zookeeper.service: Failed to locate executable /opt/zookeeper/bin/zkServer.sh: Permission denied
zookeeper.service: Failed at step EXEC spawning /opt/zookeeper/bin/zkServer.sh: Permission denied
zookeeper.service: Control process exited, code=exited, status=203/EXEC
sudo -u zookeeper /opt/zookeeper/bin/zkServer.sh 를 손으로 실행하면 되는데 systemd 로만 실패하는 경우가 있다. 원인은 셋이다.
실행 권한. 가장 단순한 경우다.
ls -l /opt/zookeeper/bin/zkServer.sh
sudo chmod +x /opt/zookeeper/bin/zkServer.sh
경로 전체의 접근 권한. systemd 는 User= 로 지정한 계정으로 실행 파일을 찾는다. 중간 디렉터리 어느 하나라도 그 계정에 x 권한이 없으면 실행 파일 자체는 읽을 수 있어도 도달하지 못한다. 심볼릭 링크로 걸어 둔 경우에는 링크가 가리키는 실제 경로까지 확인한다.
namei -l /opt/zookeeper/bin/zkServer.sh
SELinux. RHEL 계열은 기본이 Enforcing 이다. /opt 아래에 풀어 놓은 파일은 systemd 가 실행할 수 있는 타입 라벨을 갖고 있지 않아 거부될 수 있다. 손으로 실행하면 되는데 systemd 만 실패하는 전형적인 패턴이다.
getenforce
sudo ausearch -m avc -ts recent | tail -20
임시로 sudo setenforce 0 후 기동해 보고, 이것으로 뜨면 SELinux 가 원인이다. 끄고 쓰는 대신 라벨을 붙여 준다.
sudo semanage fcontext -a -t bin_t "/opt/apps/apache-zookeeper-3.9.6-bin/bin(/.*)?"
sudo restorecon -Rv /opt/apps/apache-zookeeper-3.9.6-bin/bin
sudo setenforce 1
심볼릭 링크가 아니라 실제 디렉터리 경로에 라벨을 붙여야 한다. 링크에 붙여도 효과가 없다.
zkServer.sh 는 인자에 따라 동작이 다르다. start 는 백그라운드로 떨어지고 start-foreground 는 전경에 머무른다. systemd 의 Type 은 여기에 맞춰야 한다.
ExecStart |
Type |
비고 |
|---|---|---|
zkServer.sh start |
forking |
PIDFile 을 함께 지정한다 |
zkServer.sh start-foreground |
simple |
PIDFile 이 필요 없다 |
start 를 쓰면서 Type=simple 로 두면 systemd 가 셸 스크립트의 종료를 서비스 종료로 보고 곧바로 실패나 재시작으로 처리한다. 반대로 start-foreground 를 쓰면서 Type=forking 으로 두면 systemd 가 fork 를 기다리다 타임아웃이 난다.
Type=simple + start-foreground 쪽이 다루기 쉽다. PID 파일 경로가 어긋날 여지가 없고, 프로세스 상태를 systemd 가 직접 본다. 로그를 journald 로 모을 수도 있다.
[Unit]
Description=Apache ZooKeeper
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=zookeeper
Group=zookeeper
Environment=ZOOCFGDIR=/opt/zookeeper/conf
Environment=ZOO_LOG_DIR=/var/log/zookeeper
ExecStart=/opt/zookeeper/bin/zkServer.sh start-foreground
Restart=on-failure
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
PIDFile 을 쓰는 Type=forking 구성은 ZooKeeper 의 서비스 등록 절에 있다. 둘 중 하나를 고르고 섞지 않는다.
systemctl status zookeeper
journalctl -xeu zookeeper
journalctl 에 아무 ZooKeeper 로그가 없고 systemd 메시지만 있다면 프로세스가 뜨기 전에 실패한 것이다. 위의 203/EXEC 계열이다. ZooKeeper 자체 로그가 남아 있다면 설정 문제이므로 로그 파일을 본다.
tail -100 /var/log/zookeeper/zookeeper*.log
흔한 설정 문제는 myid 파일이 없거나(dataDir 아래에 있어야 한다), dataDir 에 쓰기 권한이 없거나, clientPort 가 이미 쓰이고 있는 경우다. 앙상블에서는 다른 노드가 아직 뜨지 않아 리더 선출이 끝나지 않는 동안 로그에 연결 실패가 반복되는데, 이것은 정상이고 과반이 뜨면 해소된다.
systemctl status zookeeper
echo srvr | nc localhost 2181
/opt/zookeeper/bin/zkServer.sh status
Mode: leader 또는 Mode: follower 가 나오면 앙상블이 구성된 것이다. Error contacting service 가 나오면 아직 과반이 모이지 않았거나 clientPort 가 다르다. 4글자 명령은 4lw.commands.whitelist 에 등록된 것만 응답한다.