오늘은 42서울에서 inception 과제에 대한 동료 평가를 하던 도중 우연히 알게 된 정보들이 있어 공유하려 한다. 개인적으로 필자가 굉장히 궁금해 하던 부분 중 하나였는데, 풀리게 되어 그것도 하나의 이유가 되겠다. 결론부터 이야기하면
docker-compose init 설정은 컨테이너의 메인 프로세스가 되는 PID(Process ID)가 1번인 프로세스가 SIGTERM 종료 신호를 처리하지 못할 경우 필요하다.
그럼 그렇게 필요하게 된 배경에 대해 이야기하도록 하겠다.
# docker-compose.yml
vsftpd:
build:
context: ./requirements/bonus/vsftpd
dockerfile: Dockerfile
image: vsftpd
pull_policy: never
container_name: vsftpd
tty: true
ports:
- "21:21"
- "10000-10005:10000-10005"
volumes:
- wordpress:/var/www/wordpress
networks:
- inception_net
env_file:
- ./.env
restart: on-failure
위는 docker compose 파일의 문제가 되는 vsftpd를 서비스하는 컨테이너에 대한 코드 부분을 발췌한 것이다. 이는 아래 Dockerfile의 코드를 통해 docker 이미지를 만들고 컨테이너에 담아 실행하게 되는데 그 내용은 다음과 같다.
# vsftpd Dockerfile
FROM debian:11.9
COPY entrypoint.sh /entrypoint.sh
RUN apt update && apt upgrade -y && apt install aptitude -y && \
aptitude install vsftpd dumb-init -y && \
mkdir -p /etc/vsftpd && mv /etc/vsftpd.conf /etc/vsftpd && \
mkdir -p /var/run/vsftpd/empty && \
usermod -u 1000 www-data && groupmod -g 1000 www-data && \
chmod +x /entrypoint.sh && \
chmod +x /usr/bin/dumb-init && \
touch /etc/vsftpd/user_list
COPY conf/vsftpd.conf /etc/vsftpd/vsftpd.conf
EXPOSE 21
ENTRYPOINT ["/entrypoint.sh"]
CMD ["vsftpd", "/etc/vsftpd/vsftpd.conf"]
위 코드가 vsftpd 컨테이너에 들어갈 이미지를 생성하는 Dockerfile 코드이다. 보는 바와 같이 vsftpd 컨테이너를 실행하면 vsftpd가 실행되도록 설정이 되어있다.
위의 경우 컨테이너를 생성하고 종료시켰을 때, 어떤 문제가 생기는지 확인해보도록 하겠다.

위와 같이 docker ps라는 명령어로 실행중인 컨테이너들을 보았을 때 vsftpd 이미지를 사용하는 컨테이너가 정상적으로 실행됨음 알 수 있다. 그리고 나서 한번 컨테이너를 전체적으로 종료해보았다.

잉?? 그림에서 볼 수 있듯 vsftpd 컨테이너만 한참을 서성이다. 10초가 지난 후에 종료가 되었다. 이상하게 느껴져 docker ps -a명령어로 컨테이너의 종료상태를 확인해보았다.

역시 우려하였던 대로 vsftpd 컨테이너만 137번이란 이상한 종료코드로 무언가 다른 컨테이너와 달리 종료되었음을 확인할 수 있었다. 이를 확인해보니
137번 종료 코드는 SIGKILL에 의해 강제 종료되었을 때 나타나는 코드라고 한다. 따라서, 프로세스가 강제 종료되기 때문에 정리될 시간을 확보하지 못해 내부적으로 데이터 손실이나 문제가 발생할 수 있다.
라고 한다. 즉, SIGKILL은 SIGTERM이라는 외부에서 프로세스 종료에 대한 처리를 보냈는데, 해당 시그널을 컨테이너의 최상단 부모 프로세스가 처리하지 못할 때 발생하는 신호라는 것이다.

위 그림에서 보이는 바와 같이 vsftpd 컨테이너의 1번 프로세스는 vsftpd 프로세스가 차지하고 있었고, 이 프로세스가 SIGTERM 신호를 처리하지 못하여 생기는 문제였다. 그렇다면 어떻게 해야될까?
그렇다. 다른 방법이 더 있는지 모르겠으나, 해당 부모 프로세스는 다른 프로세스로 교체하면 된다. 단 1줄의 docker compose 파일 코드로 말이다. 다음은 수정한 docker-compose 파일 코드내용이다.
vsftpd:
build:
context: ./requirements/bonus/vsftpd
dockerfile: Dockerfile
image: vsftpd
pull_policy: never
container_name: vsftpd
tty: true
ports:
- "21:21"
- "10000-10005:10000-10005"
volumes:
- wordpress:/var/www/wordpress
networks:
- inception_net
env_file:
- ./.env
restart: on-failure
init: true
위와 같이 마지막에 init: true 만 추가해주었다. 어떤 변화가 있는지 다시 컨테이너를 생성 및 실행하여 내부를 들여다 보자.

다시 실행해 보니 vsftpd가 아닌 무언가 다른 docker-init이란 프로세스가 그 앞을 차지하고 있다. 그렇다면, 이렇게 된 상태에서 컨테이너를 종료하면 어떻게 될까? 다음은 그 결과이다.

위 그림과 같이 다른 컨테이너들과 비슷한 종료에 소용되는 시간을 기록하였다. 무언가 아까의 10초와는 확연히 달라진 모습이다. 그렇다면 종료코드는 어떨까?

종료 코드는 여전히 다른 컨테이너들과 다른 143을 나타내고 있다. 하지만, 이는 아까 137, SIGKILL로 인한 종료와는 완전히 다른 모습을 나타낸다.
143번 종료코드는 SIGTERM에 의해 종료되었음을 의미한다.
필자가 알아본 바로는 한줄로 요약하면 프로세스를 신호를 보낸 즉시 강제 종료하느냐, 프로세스 정리를 통해 정상종료하느냐이다.
즉, Docker는 컨테이너 종료 명령을 받게 되면 각 컨테이너에 SIGTERM이라는 신호를 보낸다. 그런데 문제는 해당 컨테이너의 부모 프로세스가 이 신호를 제일 먼저 받게되는데, 해당 프로세스를 차지하는 어플리케이션이 신호를 처리하는 로직이 내부에 없다면, 10초정도 기다리다가 SIGKILL신호를 통해 강제종료 하는 것이다.
여기서 docker compose init설정이 필요하다. 해당 설정을
true값으로 설정해 놓게되면, docker가 docker-init이라는 아주 가벼운 프로세스를 해당 컨테이너의 1번 프로세스, 즉 부모 프로세스로 설정해둔다.
즉, SIGTERM이나 이런 외부 시그널을 처리하지 못하는 어플리케이션을 대신하여 부모 프로세스역할을 해주는 것이다. 따라서, 이렇게 되면 컨테이너의 운영체제는 SIGTERM 신호를 처리할 수가 있게되고, 내부 어플리케이션도 운영체제에 의해 정상종료되는 것이다.
다소 설명도 길었고, 적절히 이해되었는지 모르겠다. 하지만, 배포과정에서 해당 문제로 인해 컨테이너의 데이터가 유실되거나 문제가 생기는 것을 방지하는데 docker compose의 init 설정은 아주 유용하게 사용되리라 생각하여 공유하게 되었다.
부디 비슷한 문제로 난관을 겪는 이들에게 도움이 되었으면 한다.