使用systemd ( systemctl ) 控制和托管zookeeper

网上有很多同学都贴过CentOS 7环境中使用systemd管理zookeeper,贴出来的systemd service配置文件也是五花八门,简单的抄到自己的环境里,大多都不能正常运行。本文按照需求描述,根据对zookeeper的启动文件和systemd.service的参数项的分析,来编写一个比较完整的zookeeper服务配置文件。

1. 需求描述:

使用systemd控制和托管zookeeper服务。

  1. 开机自动启动zookeeper
  2. zookeeper进程因故障退出后能自动重新启动
  3. 使用systemctl start|stop|restart|status zookeeper控制zookeeper启停。
  4. 使用普通账户运行zookeeper

本文zookeeper配置样例的环境条件,如下:

操作系统 CentOS 7
JAVA安装目录 /opt/java/jdk1.8.0_192-amd64
zookeeper安装目录 /opt/zookeeper
zookeeper配置文件 /opt/zookeeper/conf/zoo.cfg
zoo.cfg中的数据文件路径 # 快照(snapshot)文件路径
dataDir=/data/zookeeper/data
# 事务日志(datalog)文件路径
dataLogDir=/data/zookeeper/datalogs
pid文件 默认值,dataDir目录下zookeeper_server.pid,即/data/zookeeper/data/zookeeper_server.pid

2. 完整的zookeeper服务配置文件

先给出完整的zookeeper服务配置文件,方便一眼就懂的同学直接拿走。

[Unit]
Description=Zookeeper Service unit Configuration
After=network.target

[Service]
Type=forking
Environment=JAVA_HOME=/opt/java/jdk1.8.0_192-amd64
ExecStart=/opt/zookeeper/bin/zkServer.sh start /opt/zookeeper/conf/zoo.cfg
ExecStop=/opt/zookeeper/bin/zkServer.sh stop
PIDFile=/data/zookeeper/data/zookeeper_server.pid
KillMode=none
User=ibase
Group=ibase
Restart=on-failure
[Install]
WantedBy=multi-user.target

3. 操作步骤

/usr/lib/systemd/system目录中,创建zookeeper.service文件,填入以下内容:

[Unit]
Description=Zookeeper Service unit Configuration
After=network.target

[Service]
Type=forking
Environment=JAVA_HOME=/opt/java/jdk1.8.0_192-amd64
ExecStart=/opt/zookeeper/bin/zkServer.sh start /home/ibase/application/zookeeper-cmpv2/conf/zoo.cfg
ExecStop=/opt/zookeeper/bin/zkServer.sh stop
PIDFile=/data/zookeeper/data/zookeeper_server.pid
KillMode=none
User=ibase
Group=ibase
Restart=on-failure
[Install]
WantedBy=multi-user.target

执行以下命令重载unit配置文件

systemctl deamon-reload

将zookeeper服务加入开机启动项

systemctl enable zookeeper

执行systemctl enable zookeeper.service命令时,zookeeper.service的一个符号链接,就会放在/etc/systemd/system目录下面的multi-user.target.wants子目录之中。

使用systemctl命令启动zookeeper

systemctl start zookeeper

4. 启停测试

[root@n01 ~]# systemctl start zookeeper
[root@n01 ~]# systemctl status zookeeper -l
 zookeeper.service - Zookeeper
   Loaded: loaded (/usr/lib/systemd/system/zookeeper.service; disabled; vendor preset: disabled)
   Active: active (running) since 四 2020-09-03 17:36:43 CST; 3s ago
  Process: 19019 ExecStart=/opt/zookeeper/bin/zkServer.sh start /opt/zookeeper/conf/zoo.cfg (code=exited, status=0/SUCCESS)
 Main PID: 19029 (java)
    Tasks: 71
   CGroup: /system.slice/zookeeper.service
           └─19029 /usr/java/jdk1.8.0_192-amd64/bin/java -Dzookeeper.log.dir=/logs/zookeeper -Dzookeeper.root.logger=INFO,ROLLINGFILE -cp /opt/zookeeper/bin/../zookeeper-server/target/classes:/opt/zookeeper/bin/../build/classes:/opt/zookeeper/bin/../zookeeper-server/target/lib/*.jar:/opt/zookeeper/bin/../build/lib/*.jar:/opt/zookeeper/bin/../lib/slf4j-log4j12-1.7.25.jar:/opt/zookeeper/bin/../lib/slf4j-api-1.7.25.jar:/opt/zookeeper/bin/../lib/netty-3.10.6.Final.jar:/opt/zookeeper/bin/../lib/log4j-1.2.17.jar:/opt/zookeeper/bin/../lib/jline-0.9.94.jar:/opt/zookeeper/bin/../lib/audience-annotations-0.5.0.jar:/opt/zookeeper/bin/../zookeeper-3.4.14.jar:/opt/zookeeper/bin/../zookeeper-server/src/main/resources/lib/*.jar:/opt/zookeeper/bin/../conf: -server -Xms4096m -Xmx4096m -XX:MaxNewSize=256m -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false org.apache.zookeeper.server.quorum.QuorumPeerMain /opt/zookeeper/conf/zoo.cfg

9月 03 17:36:42 n01.sers systemd[1]: Starting Zookeeper...
9月 03 17:36:42 n01.sers zkServer.sh[19019]: ZooKeeper JMX enabled by default
9月 03 17:36:42 n01.sers zkServer.sh[19019]: Using config: /opt/zookeeper/conf/zoo.cfg
9月 03 17:36:43 n01.sers zkServer.sh[19019]: Starting zookeeper ... STARTED
9月 03 17:36:43 n01.sers systemd[1]: Started Zookeeper.
[root@n01 ~]# systemctl stop zookeeper
[root@n01 ~]# systemctl status zookeeper -l
 zookeeper.service - Zookeeper
   Loaded: loaded (/usr/lib/systemd/system/zookeeper.service; disabled; vendor preset: disabled)
   Active: inactive (dead)

9月 03 17:36:02 n01.sers systemd[1]: Stopped Zookeeper.
9月 03 17:36:42 n01.sers systemd[1]: Starting Zookeeper...
9月 03 17:36:42 n01.sers zkServer.sh[19019]: ZooKeeper JMX enabled by default
9月 03 17:36:42 n01.sers zkServer.sh[19019]: Using config: /opt/zookeeper/conf/zoo.cfg
9月 03 17:36:43 n01.sers zkServer.sh[19019]: Starting zookeeper ... STARTED
9月 03 17:36:43 n01.sers systemd[1]: Started Zookeeper.
9月 03 17:36:55 n01.sers systemd[1]: Stopping Zookeeper...
9月 03 17:36:55 n01.sers zkServer.sh[19805]: ZooKeeper JMX enabled by default
9月 03 17:36:55 n01.sers zkServer.sh[19805]: Using config: /opt/zookeeper/bin/../conf/zoo.cfg
9月 03 17:36:55 n01.sers systemd[1]: Stopped Zookeeper.

5. 解释说明

5.1. zookeeper自身启停命令

在编写systemd Service unit Configuration文件之前,先看一下zookeeper自身启停命令,如下:

#启动命令,启动zookeeper进程
<zookeeper安装目录>/bin/zkServer.sh start <zookeeper配置文件路径>
#停止命令,停止zookeeper进程
<zookeeper安装目录>/bin/zkServer.sh stop
#重启命令,重新启动zookeeper进程
<zookeeper安装目录>/bin/zkServer.sh restart <zookeeper配置文件路径>
#查看状态命令,显示zookeeper节点的角色,leader/follower
<zookeeper安装目录>/bin/zkServer.sh status <zookeeper配置文件路径>

由于zookeeper是Java程序,因此运行启停命令前,需要设置JAVA_HOME环境变量。

zkServer.sh为shell脚本,接受start|stop|restart|status等常用参数。
start处理逻辑:

    echo  -n "Starting zookeeper ... "
    if [ -f "$ZOOPIDFILE" ]; then
      if kill -0 `cat "$ZOOPIDFILE"` > /dev/null 2>&1; then
         echo $command already running as process `cat "$ZOOPIDFILE"`. 
         exit 0
      fi
    fi
    nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
    -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
    if [ $? -eq 0 ]
    then
      case "$OSTYPE" in
      *solaris*)
        /bin/echo "${!}\\c" > "$ZOOPIDFILE"
        ;;
      *)
        /bin/echo -n $! > "$ZOOPIDFILE"
        ;;
      esac
      if [ $? -eq 0 ];
      then
        sleep 1
        echo STARTED
      else
        echo FAILED TO WRITE PID
        exit 1
      fi
    else
      echo SERVER DID NOT START
      exit 1
    fi
    ;;

stop处理逻辑:

    echo -n "Stopping zookeeper ... "
    if [ ! -f "$ZOOPIDFILE" ]
    then
      echo "no zookeeper to stop (could not find file $ZOOPIDFILE)"
    else
      $KILL -9 $(cat "$ZOOPIDFILE")
      rm "$ZOOPIDFILE"
      echo STOPPED
    fi
    exit 0
    ;;

restart处理逻辑:

    shift
    "$0" stop ${@}
    sleep 3
    "$0" start ${@}
    ;;

5.2. systemd的Service unit Configuration文件配置项

可以通过在CentOS 7中通过man systemd.directive和man systemd.service命令查看,也可以通过阮一峰的blog查看,都比较详细,本文不再一一赘述,重点解释本文使用的配置项。
阮一峰blog地址:
http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html

  • 标识Type
    由于zookeeper自身的启动命令是shell脚本启动jar,属于forking一类。
Type字段/指令

simple(默认值):ExecStart字段启动的进程为主进程
forking:ExecStart字段将以fork()方式启动,此时父进程将会退出,子进程将成为主进程
oneshot:类似于simple,但只执行一次,Systemd 会等它执行完,才启动其他服务
dbus:类似于simple,但会等待 D-Bus 信号后启动
notify:类似于simple,启动结束后会发出通知信号,然后 Systemd 再启动其他服务
idle:类似于simple,但是要等到其他任务都执行完,才会启动该服务。一种使用场合是为让该服务的输出,不与其他服务的输出相混合
  • 定义环境变量。
    由于zookeeper为JAVA程序,必须在环境变量中指定JAVA_HOME,指向正确的JDK安装目录/opt/java/jdk1.8.0_192-amd64。查看zkServer.sh脚本可以知道,只需要指定了JAVA_HOME变量,不需要修改PATH变量,zookeeper即可运行。
Environment=JAVA_HOME=/opt/java/jdk1.8.0_192-amd64
  • 定义启停命令。
    直接使用zkServer.sh start|stop等命令填入service配置文件。无需再在外面包裹一层。

ExecStart,启动命令
ExecStop,停止命令

  • 定义 Systemd 如何停止服务。
    先看zkServer.sh stop的执行逻辑,首先检查PID文件,然后执行kill -9 (kill -SIGKILL)命令停止进程。

再看systemd停止服务(systemctl stop)的执行逻辑,由KillMode配置决定。

KillMode字段/指令:

control-group(默认值):当前控制组里面的所有子进程,都会被杀掉
process:只杀主进程
mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
none:没有进程会被杀掉,只是执行服务的 stop 命令。

KillMode,默认为control-group。对于service而言,在执行完stop命令后,对控制组中仍然运行的进程将由systemd执行kill命令,依次发送SIGTERM/SIGHUB、SIGKILL信号。

这里有一个定时器配置项,TimeoutStopSec,单位为秒,也可以用"5min 20s"这样的格式,设置为0表示禁用。默认为DefaultTimeoutStopSec,查看man systemd-system.conf,可以看到此值为90s。

两个用途:

  1. 如果设置了ExecStop,调用ExecStopin命令后,service超过该配置项时间后仍未停止,systemd向service进程发送SIGTERM信号。如果没有设置ExecStop,直接发送SIGTERM信号。
  2. SIGTERM信号后,超过该配置项时间后service仍未停止,systemd向service进程发送SIGKILL信号。

综上分析,ExecStop如果设置了zkServer.sh stop (kill -SIGKILL),那么systemd的KillMode可以直接设置为none,不需要做什么了。

  • PIDFILE字段/指令
    对于forking类型的进程,指明PID文件路径。

  • 设置zookeeper运行用户

User=normaluser
Group=normalgroup
  • 自动恢复
    Restart=on-failure

注意,通过systemd(例如systemctl stop|restart命令)来关闭的service,不受此配置约束。

该配置包含多个可选项,对于zookeeper,本文只讨论always和on-failure。首先通过man systemd.service查看这两个可选项的差别。

Restart Settings/Exit causes always on-failure
Clean exit code or signal X
Unclean exit code X X
Unclean Signal X X
Timeout X X
Watchdog X X

两者的区别在于,如果zookeeper进程exit code为0,或者被SIGHUP, SIGINT, SIGTERM or SIGPIPE这4个信号关闭,on-faiure不会重启该进程,always会。

这里我们关注的一件事是,通过直接执行./zkServer.sh stop来关闭zookeeper进程,这种情况下systemd是否会重启zookeeper进程。

分析zkServer.sh的stop处理逻辑源码,可以看到是向zookeeper进程发送信号9来关闭的,所以无论是设置为always还是on-failure,通过./zkServer.sh stop关闭zookeeper进程,systemd都依然会重启zookeeper。

所以如果想不触发systemd重启逻辑,一种方法是通过systemctl stop来调用./zkServer.sh stop来关闭,另一种方法是不要使用./zkServer.sh stop,而是通过向zookeeper进程发送信号15来关闭(kill $pid)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352