도커에서는 한 컨테이너에 하나의 프로세스(서비스)를 띄우는 것을 권장하고 있다. 그러나 때때로 비용절감, 프로세스 모니터링 등의 이유로 한 컨테이너에 다수의 프로세스를 띄워야하는 경우가 있다. 이때 어떻게 띄워야 안정적으로 컨테이너를 운용할 수 있을까?
entrypoint 파일에 다음과 같이 작성되어 있다고 가정하자.
# entrypoint.sh
...
start_process1 &
start_process2 &
PID="$!"
wait "$PID"
위와 같은 경우 process2 의 ID 가 PID
에 할당되고 wait
을 통해 해당 프로세스가 종료되기를 기다릴 것이다. 즉, process1, process2 가 띄워지고 process2 가 종료되기까지 컨테이너는 계속 running 이다.
그럼 wait
에 의해 기다리고 있던 동작이 끝나면서 컨테이너는 exit 0 을 반환하며 내려갈 것이다.
process1 이 꺼지더라도 컨테이너는 오직 process2 를 기다리고 있기 때문에 종료되지 않는다.
설명에 앞서 한가지 조건을 걸자. 해당 서비스를 담당하는 타겟그룹에는 process2 에 대한 health check 가 걸려있다.
첫 번째, 메모리 초과등의 이유로 process2 가 꺼진다면 컨테이너가 내려가 health check 에 걸려 unhealthy 상태가 되고, ECS 는 해당 테스크를 내린 뒤 새로운 테스크로 교체할 것이다. 즉, process2 가 종료되어 서비스가 동작안한다면 ECS 에서 재배포를 할테니 크리티컬한 이슈까지는 발생안할 것이다.
두 번째, process1 이 꺼지더라도 process2 가 올라간 채 컨테이너는 정상적으로 running 상태를 유지하고 있을테니 health check 에 걸릴 이유가 없다. 즉, process1 이 꺼지더라도 정상적으로 띄워지고 있는 거처럼 보이는 것이다. 만약 process1 이 백그라운드 처리 등 중요한 서비스를 담당하는 프로세스라면 이는 서비스에 큰 영향을 미칠 것이다.
위와 같은 이유로 한 컨테이너에 다수의 프로세스를 띄울 때 이를 안정적으로 관리하는 툴이 필요하다. 이때 주로 사용하는 것이 supervisord
툴이다. 해당 툴 사용법은 간단하다. supervisord 로 sidekiq 을 관리하는 예제 코드는 다음과 같다.
# conf.d/sidekiq.conf 파일
[program:sidekiq]
command=bundle exec sidekiq -i 0 -C config/sidekiq.yml # 명령어
directory=/home/deploy/app
user=deploy
startsecs=10
autostart=false
autorestart=true # 프로세스가 꺼지면 자동으로 재시작해줄 것
startretries=3 # 3번 이상 실패하면 재시작 안하도록
redirect_stderr=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
# supervisord.conf 파일
[include]
files=conf.d/*.conf # 프로그램 conf 파일들을 import 해주고
[unix_http_server]
file=/var/run/supervisor.sock
[supervisord]
logfile=/var/log/supervisor/supervisord.log # supervisor 의 로그파일
pidfile=/var/run/supervisord.pid
childlogdir=/var/log/supervisor # 프로세스들의 로그 파일들이 위치할 폴더 경로
autostart=false
nodaemon=true
environment=...
[rpcinterface:supervisor] # 요거 설정을 안하면 이슈가 발생함.
supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl] # supervisorctl 커맨드를 사용하기 위해 설정
serverurl=unix:///var/run/supervisor.sock
# ROLE
[group:sidekiq] # 내가 설정한 sidekiq 프로그램을 등록
programs=sidekiq
...
이제 위 supervisord 를 적용한 컨테이너에서 sudo supervisorctl start sidekiq
명령어를 치면 conf.d/sidekiq.conf 에서 설정한 값 그대로 sidekiq 프로세스가 띄워진다. 그리고 sudo supervisorctl status
를 통해 해당 프로세스가 up
, down
상태인지 파악도 할 수 있다. supervisord 에서 프로세스를 다음과 같이 state 관리를 한다고 한다. 참고하면 좋을 거 같다.
아까 들었던 예시에서 문제가 됐던 점은 background + wait 방법으로 처리하다 보니 process2 만 관리가 된다는 것이였다. 그러나 supervisord 를 사용하면 supervisor 가 띄운 모든 child process 들을 관리를 해주니, 아까처럼 process1 이 메모리 초과 등의 이유로 내려가면 supervisor 가 다시 띄워준다.
도커 공식 문서 에서도 supervisor 를 이용하여 멀티 프로세스를 띄우는 방법을 소개하고 있으니 이를 참고하면 좋을 거 같다.
http://supervisord.org/
https://docs.docker.com/config/containers/multi-service_container/