Cassandra配置JMX
Cassandra数据库通过JMX方式对外提供监控和管理服务。本文讲解如何配置和开启Cassandra的JMX服务。
环境说明
本文是基于以下版本的系统和服务:
- cassandra 3.11.6
- openjdk 1.8.0
- mx4j 3.0.2
- Ubuntu 18.04.4 LTS
需要说明的是,当使用java 11以服务形式启动cassandra,服务启动后会进入 active(exited)
状态:
$ systemctl status cassandra
● cassandra.service - LSB: distributed storage system for structured data
Loaded: loaded (/etc/init.d/cassandra; generated)
Active: active (exited) since Wed 2020-05-13 13:48:21 CST; 4s ago
Docs: man:systemd-sysv-generator(8)
Process: 23280 ExecStop=/etc/init.d/cassandra stop (code=exited, status=0/SUCCESS)
Process: 23298 ExecStart=/etc/init.d/cassandra start (code=exited, status=0/SUCCESS)
这是因为有参数不支持导致jvm启动失败。虽然根据Cassandra 3的文档,java 1.8及以上版本都支持,但至少java 11并不支持。
Cassandra目录和文件
安装和数据目录
Cassandra安装在 /usr/share/cassandra
目录下。这个目录的子目录 /lib
下是Cassandar的依赖包,后面安装配置MX4J服务时,会用到这个目录。
Cassandra的数据文件保存在 /var/lib/cassandra
目录下。一般可以通过这个目录来备份和恢复数据。
日志文件在 /var/log/cassandra
目录下。这个目录有三种日志文件 system.log
, debug.log
和gc.log
。多数情况下,通过查看system.log
可以了解系统的运行状态和错误信息。
配置和运行脚本
在Ubuntu上通过Debian Packages方式安装Cassandra之后,Cassandra可以以服务方式启动。接下来的配置也都是基于服务方式操作Cassandra的情况。
Cassandra没有提供Systemd服务的配置,所以服务操作还是通过 /etc/init.d/
目录下的 cassandra
脚本来定义的。可以根据这个脚本来分析Cassandra服务的启动过程。
根据服务启动脚本,可以发现/etc/default/cassandra
配置脚本会用来设置运行时所需要的环境变量。比如,如果系统默认的java是不兼容的版本,就可以在这个脚本中添加JAVA_HOME
和JRE_HOME
环境变量,比如:
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export JRE_HOME=/usr/lib/jvm/java-8-openjdk-amd64/jre
注意,如果不是以系统服务的方式启动Cassandra,则这个配置脚本中设置的环境变量不会生效。
最主要的配置文件和脚本在/etc/cassandra
目录下。这个脚本会根据环境变量等参数动态的计算一些jvm启动参数。这个目录下的另外一个配置文件jvm.options
也可以用来设置jvm启动参数,但是cassandra-env.sh
脚本设置的优先级比较高,所以要注意确认jvm.options
中的设置不会被cassandra-env.sh
覆盖。本文主要使用cassandra-env.sh
脚本来设置需要的参数。
配置MX4J HttpAdaptor
Cassandra可以与MX4J HttpAdaptor集成,通过Web控制台的方式提供JMX服务访问。
为了开启MX4J HttpAdaptor服务,需要安装MX4J。MX4J可以从下载页面下载需要的版本。下载并解压后,在mx4j-3.0.2/lib
目录下找到mx4j-tools.jar
,将这个jar包安装到Cassandra的lib目录/var/lib/cassandra
下,然后重启Cassandra。
如果一切正常,可以看到system.log
中以下日志内容:
INFO [main] 2020-05-14 09:29:24,963 Mx4jTool.java:61 - mx4j successfuly loaded
日志里的拼写错误是Cassandra的代码问题,不是我弄错了。
默认情况下MX4J HttpAdaptor监听的是127.0.0.1
上的8081
端口。如果启动正常,可以通过本地浏览器访问Web控制台了:
8081
这个端口很有可能和其他服务冲突。当端口冲突时,可以在system.log
中看到以下错误信息:
WARN [main] 2020-05-14 15:52:41,847 Mx4jTool.java:70 - Could not start register mbean in JMX
java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_252]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_252]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_252]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_252]
at org.apache.cassandra.utils.Mx4jTool.maybeLoad(Mx4jTool.java:60) ~[apache-cassandra-3.11.6.jar:3.11.6]
at org.apache.cassandra.service.CassandraDaemon.setup(CassandraDaemon.java:197) [apache-cassandra-3.11.6.jar:3.11.6]
at org.apache.cassandra.service.CassandraDaemon.activate(CassandraDaemon.java:630) [apache-cassandra-3.11.6.jar:3.11.6]
at org.apache.cassandra.service.CassandraDaemon.main(CassandraDaemon.java:757) [apache-cassandra-3.11.6.jar:3.11.6]
Caused by: java.net.BindException: Address already in use (Bind failed)
at java.net.PlainSocketImpl.socketBind(Native Method) ~[na:1.8.0_252]
at java.net.AbstractPlainSocketImpl.bind(AbstractPlainSocketImpl.java:387) ~[na:1.8.0_252]
at java.net.ServerSocket.bind(ServerSocket.java:390) ~[na:1.8.0_252]
at java.net.ServerSocket.<init>(ServerSocket.java:252) ~[na:1.8.0_252]
at mx4j.tools.adaptor.PlainAdaptorServerSocketFactory.createServerSocket(PlainAdaptorServerSocketFactory.java:24) ~[mx4j-tools.jar:na]
at mx4j.tools.adaptor.http.HttpAdaptor.createServerSocket(HttpAdaptor.java:672) ~[mx4j-tools.jar:na]
at mx4j.tools.adaptor.http.HttpAdaptor.start(HttpAdaptor.java:478) ~[mx4j-tools.jar:na]
... 8 common frames omitted
另外,如果希望从远程访问,需要设置监听地址。此时,可以在/etc/default/cassandra
脚本中将这两个变量设置为期望的值,然后重启Cassandra服务:
export MX4J_ADDRESS="-Dmx4jaddress=0.0.0.0"
export MX4J_PORT="-Dmx4jport=7081"
设置成功后,可以在system.log
日志中看到mx4jaddress
和mx4jport
虚拟机参数:
INFO [main] 2020-05-14 16:04:19,007 CassandraDaemon.java:506 - JVM Arguments: [-Xloggc:/var/log/cassandra/gc.log, -ea, -XX:+UseThreadPriorities, -XX:ThreadPriorityPolicy=42, -XX:+HeapDumpOnOutOfMemoryError, -Xss256k, -XX:StringTableSize=1000003, -XX:+AlwaysPreTouch, -XX:-UseBiasedLocking, -XX:+UseTLAB, -XX:+ResizeTLAB, -XX:+UseNUMA, -XX:+PerfDisableSharedMem, -Djava.net.preferIPv4Stack=true, -XX:+UseParNewGC, -XX:+UseConcMarkSweepGC, -XX:+CMSParallelRemarkEnabled, -XX:SurvivorRatio=8, -XX:MaxTenuringThreshold=1, -XX:CMSInitiatingOccupancyFraction=75, -XX:+UseCMSInitiatingOccupancyOnly, -XX:CMSWaitDuration=10000, -XX:+CMSParallelInitialMarkEnabled, -XX:+CMSEdenChunksRecordAlways, -XX:+CMSClassUnloadingEnabled, -XX:+PrintGCDetails, -XX:+PrintGCDateStamps, -XX:+PrintHeapAtGC, -XX:+PrintTenuringDistribution, -XX:+PrintGCApplicationStoppedTime, -XX:+PrintPromotionFailure, -XX:+UseGCLogFileRotation, -XX:NumberOfGCLogFiles=10, -XX:GCLogFileSize=10M, -Xms3952M, -Xmx3952M, -Xmn600M, -XX:+UseCondCardMark, -XX:CompileCommandFile=/etc/cassandra/hotspot_compiler, -javaagent:/usr/share/cassandra/lib/jamm-0.3.0.jar, -Dcassandra.jmx.local.port=7199, -Dcom.sun.management.jmxremote.authenticate=false, -Dcom.sun.management.jmxremote.password.file=/etc/cassandra/jmxremote.password, -Djava.library.path=/usr/share/cassandra/lib/sigar-bin, -Dmx4jaddress=0.0.0.0, -Dmx4jport=7081, -XX:OnOutOfMemoryError=kill -9 %p, -Dlogback.configurationFile=logback.xml, -Dcassandra.logdir=/var/log/cassandra, -Dcassandra.storagedir=/var/lib/cassandra, -Dcassandra-pidfile=/var/run/cassandra/cassandra.pid, -XX:HeapDumpPath=/var/lib/cassandra/java_1589443456.hprof, -XX:ErrorFile=/var/lib/cassandra/hs_err_1589443456.log]
当然,直接在cassandra-env.sh
脚本设置这两个环境变量也是可以的。
安全
虽然MX4J HttpAdaptor支持用基于户名和密码的基础认证,但是Cassandra没有使用这个特性,任何人都可以访问Web控制台,所以要谨慎决定是否要开启MX4J HttpAdaptor,以及监控的地址。
配置JMX
默认情况下,Cassandra开启了JMX的本地访问,监听端口是7199
。在Cassandra所在服务器中运行JConsole, 新建连接窗口中选择 远程进程 ,在地址中填入 127.0.0.1:7199
,用户名和密码留空,然后就可以连接了:
由于默认配置下使用的不是SSL连接,JConsole会有警告信息,选择使用不安全连接就可以继续了:
连接成功后,可以看到虚拟机的运行状态以及其他JMX信息:
远程访问
开启远程访问首先需要在/etc/default/cassandra
脚本中添加以下两个环境变量:
export LOCAL_JMX=no
export JVM_OPTS="$JVM_OPTS -Djava.rmi.server.hostname=192.168.112.16"
其中,虚拟机参数java.rmi.server.hostname
用来设置JMX服务访问的域名或者地址。如果不显试设置,可能会导致远程访问失败。这个地址不一定是服务监听的地址,比如如果cassandra运行在docker容器中,通过端口映射到宿主机上,那么就需要设置为宿主机的地址或者域名。
然后创建文件/etc/cassandra/jmxremote.password
,用来设置JMX访问所需的用户名和密码,比如在文件中加入:
jmx 12345
这里jmx
是用户名,12345
是密码,两者之间用空格分隔。如果有多个用户,则每个用户一行。为了安全性,建议将这个文件设置为只有cassandra用户可以访问和修改。
如果配置成功,重启Cassandra服务可以在system.log
中的jvm参数信息中看到java.rmi.server.hostname
, cassandra.jmx.remote.port
, com.sun.management.jmxremote.rmi.port
, com.sun.management.jmxremote.authenticate
, com.sun.management.jmxremote.password.file
等的值:
INFO [main] 2020-05-18 19:11:47,721 CassandraDaemon.java:506 - JVM Arguments: [-Djava.rmi.server.hostname=192.168.112.16, -Xloggc:/var/log/cassandra/gc.log, -ea, -XX:+UseThreadPriorities, -XX:ThreadPriorityPolicy=42, -XX:+HeapDumpOnOutOfMemoryError, -Xss256k, -XX:StringTableSize=1000003, -XX:+AlwaysPreTouch, -XX:-UseBiasedLocking, -XX:+UseTLAB, -XX:+ResizeTLAB, -XX:+UseNUMA, -XX:+PerfDisableSharedMem, -Djava.net.preferIPv4Stack=true, -XX:+UseParNewGC, -XX:+UseConcMarkSweepGC, -XX:+CMSParallelRemarkEnabled, -XX:SurvivorRatio=8, -XX:MaxTenuringThreshold=1, -XX:CMSInitiatingOccupancyFraction=75, -XX:+UseCMSInitiatingOccupancyOnly, -XX:CMSWaitDuration=10000, -XX:+CMSParallelInitialMarkEnabled, -XX:+CMSEdenChunksRecordAlways, -XX:+CMSClassUnloadingEnabled, -XX:+PrintGCDetails, -XX:+PrintGCDateStamps, -XX:+PrintHeapAtGC, -XX:+PrintTenuringDistribution, -XX:+PrintGCApplicationStoppedTime, -XX:+PrintPromotionFailure, -XX:+UseGCLogFileRotation, -XX:NumberOfGCLogFiles=10, -XX:GCLogFileSize=10M, -Xms3952M, -Xmx3952M, -Xmn600M, -XX:+UseCondCardMark, -XX:CompileCommandFile=/etc/cassandra/hotspot_compiler, -javaagent:/usr/share/cassandra/lib/jamm-0.3.0.jar, -Dcassandra.jmx.remote.port=7199, -Dcom.sun.management.jmxremote.rmi.port=7199, -Dcom.sun.management.jmxremote.authenticate=true, -Dcom.sun.management.jmxremote.password.file=/etc/cassandra/jmxremote.password, -Djava.library.path=/usr/share/cassandra/lib/sigar-bin, -Dmx4jaddress=0.0.0.0, -Dmx4jport=7081, -XX:OnOutOfMemoryError=kill -9 %p, -Dlogback.configurationFile=logback.xml, -Dcassandra.logdir=/var/log/cassandra, -Dcassandra.storagedir=/var/lib/cassandra, -Dcassandra-pidfile=/var/run/cassandra/cassandra.pid, -XX:HeapDumpPath=/var/lib/cassandra/java_1589800305.hprof, -XX:ErrorFile=/var/lib/cassandra/hs_err_1589800305.log]
接下来就可以远程访问Cassandra的JMX服务了:
除了地址需要改成远程地址之后,还需要指定正确的用户名和密码。后面的操作就和上文中的例子一样了。
在上面的日志信息中,可以看到还有很多JMX相关的配置项。如果要调整这些配置,则需要修改cassandra-env.sh
脚本。比如如果希望修改JMX服务监听的端口,需要在cassandra-env.sh
脚本中找到以下部分,然后将JMX_PORT
变量的值修改为期望的端口:
# Specifies the default port over which Cassandra will be available for
# JMX connections.
# For security reasons, you should not expose this port to the internet. Firewall it if needed.
JMX_PORT="7199"
修改保存后,重启Cassandra服务,成功的话就可以看到日志中的相关信息,并用新端口访问了。
其它相关的配置可以参考cassandra-env.sh
脚本中的注释说明和Cassandra文档。