LabHub
배우기 러닝패스 코스

Docker基礎

生成・起動・終了と終了コード

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

이 실습은 진짜 VM 에서 돕니다

이 상자는 파드가 아니라 KubeVirt 가 띄운 가상머신입니다. 리눅스 커널이 따로 돌고 systemd 가 실제로 서비스를 관리하며, docker 는 흉내가 아니라 진짜 도커 엔진입니다. docker run 으로 띄운 컨테이너는 실제로 프로세스가 되고 docker execdocker logs 도 그대로 동작합니다.

예전에는 이 실습이 파드 안에서 돌았습니다. 커널 권한을 전부 내려놓은 상자라 컨테이너를 띄우는 단계가 막혀 있었고, 그래서 이미지 아카이브를 직접 풀어 보는 우회로 배웠습니다. 이제 우회가 필요 없습니다.

알아 둘 것이 둘 있습니다.

목표

컨테이너의 상태 전이를 손으로 만들어 보고, 종료코드 143 · 137 · 42 를 각각 의도적으로 재현해 그 의미를 몸으로 익힙니다.

왜 중요한가

운영에서 가장 자주 받는 신고는 "컨테이너가 죽었어요"입니다. 그런데 죽는 방식은 최소 세 가지이고, 각각 조치가 완전히 다릅니다. 앱이 스스로 끝냈으면 코드를 봐야 하고, SIGTERM 을 받고 끝냈으면 정상 배포이며, SIGKILL 로 죽었으면 앱 로그에는 아무것도 남지 않습니다. 종료코드는 이 셋을 구분하는 가장 싼 신호입니다. 128 을 넘는 값은 128 + 신호번호 라는 규칙 하나만 알면 143 = 128+15 = SIGTERM, 137 = 128+9 = SIGKILL 로 바로 읽힙니다.

단계

  1. /root/dk2 를 만들고, alpine:3.20 으로 dk-life 라는 컨테이너를 생성만 합니다. 명령은 sleep 600 입니다.
  2. dk-life 를 시작해 running 상태로 만듭니다.
  3. alpine:3.20 으로 dk-trap 을 백그라운드 실행하되, SIGTERM 을 받으면 caught-term 을 출력하고 종료코드 143 으로 끝나게 합니다. 그런 다음 정상적으로 정지시킵니다.
  4. alpine:3.20 으로 dk-notrap 을 백그라운드 실행하고(명령은 sleep 600), 유예 시간을 2초만 주고 정지시킵니다. 종료코드가 137 이 되어야 합니다.
  5. alpine:3.20 으로 dk-fail 을 실행해 종료코드 42 로 끝나게 하고, 그 값을 /root/dk2/exit42.txt 에 숫자만 적습니다.
  6. dk-restart 컨테이너를 재시작 정책 on-failure, 최대 3회 로 띄웁니다.
  7. 컨테이너 안에서 프로세스 목록을 뽑아 /root/dk2/pid1.txt 로 저장합니다. PID 열이 함께 보여야 합니다. 이 실습 상자 자체가 컨테이너입니다. 여기서 ps -e -o pid,ppid,comm 을 돌리면 PID 1 이 이 컨테이너의 주 명령이라는 것과, 보이는 프로세스가 손에 꼽을 정도라는 것을 동시에 확인할 수 있습니다.
  8. /root/dk2/lifecycle.mddk-trap, dk-notrap, dk-fail 세 컨테이너의 이름과 종료코드를 한 줄에 하나씩 적습니다.

참고

실행하지 않고 만들기만 하기

/root/dk2 를 만들고, alpine:3.20 으로 dk-life 라는 컨테이너를 생성만 합니다. 명령은 sleep 600 입니다.

run 은 만들고 바로 시작합니다. 만들기만 하는 별도의 하위 명령이 있습니다. 이 상태에서는 아직 프로세스가 없습니다.

만들어 둔 컨테이너 시작하기

dk-life 를 시작해 running 상태로 만듭니다.

생성과 시작이 나뉘어 있는 이유를 생각해 보세요. 시작하면 State.Pid 에 실제 프로세스 번호가 들어옵니다.

SIGTERM 을 받아 정상 종료하기

alpine:3.20 으로 dk-trap 을 백그라운드 실행하되, SIGTERM 을 받으면 caught-term 을 출력하고 종료코드 143 으로 끝나게 합니다. 그런 다음 정상적으로 정지시킵니다.

셸에서 신호 핸들러를 등록하는 내장 명령이 있습니다. 핸들러 안에서 원하는 종료코드로 끝내면 그 값이 그대로 컨테이너 종료코드가 됩니다.

핸들러가 없으면 무슨 일이 생기나

alpine:3.20 으로 dk-notrap 을 백그라운드 실행하고(명령은 sleep 600), 유예 시간을 2초만 주고 정지시킵니다. 종료코드가 137 이 되어야 합니다.

핸들러를 등록하지 않은 PID 1 은 SIGTERM 을 무시합니다. 유예 시간을 짧게 주면 기다리지 않고 결과를 볼 수 있습니다.

애플리케이션이 낸 종료코드

alpine:3.20 으로 dk-fail 을 실행해 종료코드 42 로 끝나게 하고, 그 값을 /root/dk2/exit42.txt 에 숫자만 적습니다.

신호로 죽은 것과 앱이 스스로 끝난 것은 종료코드 범위가 다릅니다. 128 보다 작은 값은 앱이 직접 낸 값입니다.

실패했을 때만 다시 띄우기

dk-restart 컨테이너를 재시작 정책 on-failure, 최대 3회 로 띄웁니다.

재시작 정책은 네 가지가 있습니다. 실패한 경우에만, 그것도 횟수를 제한해서 다시 띄우는 값을 고르세요.

컨테이너 안의 프로세스 목록

컨테이너 안에서 프로세스 목록을 뽑아 /root/dk2/pid1.txt 로 저장합니다. PID 열이 함께 보여야 합니다. 이 실습 상자 자체가 컨테이너입니다. 여기서 ps -e -o pid,ppid,comm 을 돌리면 PID 1 이 이 컨테이너의 주 명령이라는 것과, 보이는 프로세스가 손에 꼽을 정도라는 것을 동시에 확인할 수 있습니다.

컨테이너 안에서 프로세스를 나열하면 목록이 아주 짧고 1번이 무엇인지 바로 보입니다. 호스트에서 실행하면 안 됩니다.

종료코드 표 만들기

/root/dk2/lifecycle.mddk-trap, dk-notrap, dk-fail 세 컨테이너의 이름과 종료코드를 한 줄에 하나씩 적습니다.

세 컨테이너의 종료코드를 실제 inspect 로 확인해 표에 적으세요. 각 값이 왜 그 값인지도 함께 적어 두면 나중에 도움이 됩니다.