개요
오늘 포스팅해 볼 내용은 init system, 그중에서도 dumb-init을 사용해서 SIGTERM을 전파하는 방법에 대해서 알아보려고 한다. 사실 dumb-init을 이번에 처음 안 것은 아니고 2년 전에도 비슷하게 포스팅을 한 적이 있다. 그런데 이번에도 비슷한 이슈를 다루며 자세히 적어두면 좋을 것 같다고 생각하여 작성하게 되었다.
내가 관리하고 있는 rails 프로젝트에는 entry point가 아래와 같이 구성이 되어 있다.
ENTRYPOINT ["sh", "-c", "export DD_AGENT_HOST=$(curl <http://169.254.169.254/latest/meta-data/local-ipv4>); rails"]
ECS 환경에서 Datadog을 사용하려면 이렇게 사용해야 한다. export 명령어를 사용하기 때문에 sh -c가 붙게 되고, 여기에서 별 다른 설정을 하지 않으면 프로세스가 아래와 같이 뜨게 된다.
PID TTY TIME CMD
1 ? 00:00:00 sh
8 ? 00:37:32 ruby
여기에서 문제가 발생하게 되는데, 1번 PID가 sh로 설정되면 SIGTERM 신호를 제대로 전파해주지 않게 된다. 실제로 이렇게 떠있는 컨테이너에 docker kill ~ --signal=SIGTERM 명령어를 보내보면 아무런 일도 일어나지 않는다.
그래서 문제가 되는게 ECS의 경우에 새 배포를 적용하기 시작하면 PID 1번 프로세스에 SIGTERM signal을 전송하고 StopTimeout 시간만큼 기다리다 SIGKILL Signal을 전송한다. (자세한 내용은 아래 블로그 내용 참고)
When a task is stopped, a SIGTERM signal is sent to each container’s entry process, usually PID 1. After a timeout has lapsed, the process will be sent a SIGKILL signal. By default, there is a 30 second delay between the delivery of SIGTERM and SIGKILL signals. This value can be adjusted by updating the ECS task parameter stopTimeout, or with EC2 Container Instances by setting the ECS agent environment variable ECS_CONTAINER_STOP_TIMEOUT. Processes that don’t exit before the timeout expires will be terminated abruptly upon receipt of the SIGKILL signal.
https://aws.amazon.com/ko/blogs/containers/graceful-shutdowns-with-ecs/
근데 PID 1번 프로세스인 sh은 기본적으로 SIGTERM 명령어를 전파하지 않고 무시한다. 그렇게 되면 ruby 입장에서는 Graceful Shutdown을 하기도 전에 갑자기 SIGKILL signal을 맞고 죽어버리는 것이다. 이걸 실제로 확인하려면 sh가 PID 1번인 컨테이너를 중지시키면 확인할 수 있다.
중지된 컨테이너를 확인해보면 Exit code가 137(137-128=9), SIGKILL을 맞고 죽었다. 이렇게 되면 기존 리소스를 정리할 시간도 없이 강제종료 되는 것이기 때문에 5XX 에러가 발생하거나 뜻하지 않은 작업 유실이 일어날 수 있다.
그래서 이를 방지하기 위해 exec 커맨드를 사용하거나, tini나 dumb-init 같은 dedicated process manager를 사용한다. 이번 포스팅에서는 그중에서도 dumb-init을 사용하여 문제를 해결하는 방법에 대해 알아보려고 한다. (나머지 방법에 대해서는 AWS 블로그 글에 잘나와있으니 참고하시길…)
dumb-init?
tini나 dumb-init이나 가볍게 사용할 수 있는 init system이기 때문에 뭘 사용해도 상관이 없다. 둘 다 활발하게 maintain 되고 있는 프로젝트도 아니고 dumb-init이 signal을 rewriting 해주는 등의 추가 기능을 조금 더 제공해주긴 하지만 솔직히 읽어봤을 땐 미묘한 차이라서 … 😑 그냥 아무거나 써도 무방할 것 같다.
아무튼 dumb-init에 대해 간단히 소개하면 위의 문제점을 해결하기에 적격인 init system으로, dumb-init을 사용하면 dumb-init이 1번 PID로 실행되게 된다. 직접 사용해보면 이해가 바로 될 정도로 단순하다. Dockerfile에 apt install dumb-init을 추가하고 맨 앞에 dumb-init 명령어만 추가해 주면 된다. 난 아래 같이 Entry Point에는 dumb-init을, CMD에는 실행 커맨드를 추가했다.
ENTRYPOINT ["dumb-init", "--"]
CMD ["sh", "-c", "export DD_AGENT_HOST=$(curl <http://169.254.169.254/latest/meta-data/local-ipv4>); rails"]
이렇게 설정하고 다시 실행하면 프로세스가 조금 달라지는데, PID 1번이 sh가 아니라 dumb-init으로 실행된다.
PID TTY TIME CMD
1 ? 00:00:00 dumb-init
7 ? 00:00:00 sh
9 ? 00:00:04 bundle
이 상태에서는 SIGTERM이 잘 전달되는지 확인하기 위해 아까와 같이 컨테이너를 중지시켜보았다.
이번에는 아까와 다르게 Exit code가 143(143-128=15), SIGTERM을 맞고 종료된 것을 확인할 수 있다. 이렇게 하면 애플리케이션 단에서도 Graceful Shutdown이 가능해지기 때문에 훨씬 안전하게 컨테이너를 종료할 수 있다
'DevOps > AWS' 카테고리의 다른 글
AWS VPC Endpoint (Interface, Gateway) (0) | 2025.04.12 |
---|---|
Secret Manager를 사용해서 Spring Boot 프로젝트의 property값 관리하기(2) - EC2 (0) | 2021.10.29 |
Secret Manager를 사용해서 Spring Boot 프로젝트의 property값 관리하기(1) - 로컬 환경 (2) | 2021.10.15 |
Github Action과 AWS CodeDeploy를 사용하여 Spring Boot 프로젝트 CI/CD 파이프라인 구축하기 (3) | 2021.08.28 |
AWS 해킹 시 대처 방법, 해킹 방지하기 (0) | 2021.08.04 |