Cassandra数据库通过JMX方式对外提供监控和管理服务。本文讲解如何配置和开启Cassandra的JMX服务。

环境说明

本文是基于以下版本的系统和服务:

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
  • 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.logdebug.loggc.log。多数情况下,通过查看system.log可以了解系统的运行状态和错误信息。

配置和运行脚本

在Ubuntu上通过Debian Packages方式安装Cassandra之后,Cassandra可以以服务方式启动。接下来的配置也都是基于服务方式操作Cassandra的情况。

Cassandra没有提供Systemd服务的配置,所以服务操作还是通过 /etc/init.d/ 目录下的 cassandra 脚本来定义的。可以根据这个脚本来分析Cassandra服务的启动过程。

根据服务启动脚本,可以发现/etc/default/cassandra配置脚本会用来设置运行时所需要的环境变量。比如,如果系统默认的java是不兼容的版本,就可以在这个脚本中添加JAVA_HOMEJRE_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控制台了:

Cassandra配置JMX Nosql 第1张

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日志中看到mx4jaddressmx4jport虚拟机参数:

  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,用户名和密码留空,然后就可以连接了:

Cassandra配置JMX Nosql 第2张

由于默认配置下使用的不是SSL连接,JConsole会有警告信息,选择使用不安全连接就可以继续了:

Cassandra配置JMX Nosql 第3张

连接成功后,可以看到虚拟机的运行状态以及其他JMX信息:

Cassandra配置JMX Nosql 第4张

远程访问

开启远程访问首先需要在/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.hostnamecassandra.jmx.remote.portcom.sun.management.jmxremote.rmi.portcom.sun.management.jmxremote.authenticatecom.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服务了:

Cassandra配置JMX Nosql 第5张

除了地址需要改成远程地址之后,还需要指定正确的用户名和密码。后面的操作就和上文中的例子一样了。

在上面的日志信息中,可以看到还有很多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文档。

参考资料

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄