목록에서 사라진 컨테이너부터 다시 찾는다
docker compose up -d가 성공 메시지를 냈는데 서비스가 보이지 않는다면, 가장 먼저 restart policy를 추가할 일이 아닙니다. 기본 docker ps는 실행 중인 컨테이너만 보여 주므로 정상적으로 끝난 프로세스와 오류로 종료된 프로세스가 모두 목록에서 사라집니다. 종료된 컨테이너를 포함한 상태, 마지막 로그, Engine이 기록한 state를 같은 시점에 모아야 합니다.
컨테이너의 수명은 내부의 주 프로세스 수명과 연결됩니다. 초기화 스크립트가 백그라운드 프로세스만 띄우고 끝났거나, 배치 작업이 할 일을 마쳤다면 컨테이너 종료 자체는 정상일 수 있습니다. 반대로 애플리케이션이 구성 오류로 종료됐다면 재시작 횟수를 늘려도 같은 오류가 더 빠르게 반복될 뿐입니다.
변경 전에 확보할 네 종류의 기록
| 기록 | 명령에서 볼 항목 | 의미 |
|---|---|---|
| Compose 상태 | 서비스, 상태, 생성 시각 | 다른 프로젝트 이름의 컨테이너를 보고 있지 않은지 확인합니다. |
| 종료 state | ExitCode, Error, OOMKilled | 프로세스 종료와 Engine 생성 실패를 나눕니다. |
| 로그 | 마지막 메시지와 타임스탬프 | 구성 오류가 먼저인지 종료가 먼저인지 맞춥니다. |
| 실행 정의 | Entrypoint, Cmd, 렌더링된 command | 이미지 기본값이 Compose에서 덮였는지 확인합니다. |
명령을 실행하기 전에 출력 범위를 정합니다. 전체 Entrypoint·Cmd, 환경값, 애플리케이션 로그에는 토큰이 들어갈 수 있습니다. 먼저 아래 최소 상태만 확인하고, 상세 로그는 접근 제한 파일에 보존해 비밀값을 제거한 뒤 공유합니다. config --quiet도 오류 진단을 출력할 수 있으므로 공개 세션에서 실행하지 않습니다.
docker compose ps -a
docker compose config --quiet
docker inspect --format 'status={{.State.Status}} exit={{.State.ExitCode}} oom={{.State.OOMKilled}} error_present={{if .State.Error}}true{{else}}false{{end}} health={{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' <CONTAINER>
로그를 수집하기 전에 down이나 rm을 실행하면 가장 유용한 컨테이너 state를 잃을 수 있습니다. 컨테이너 이름을 모르면 docker compose ps -a 결과에서 대상 서비스와 연결된 이름을 확인합니다.
종료 결과를 세 갈래로 분리한다
- ExitCode가 0이고 Error가 비어 있음: 주 프로세스가 정상 종료한 경우입니다. 웹 서버처럼 계속 실행돼야 하는 서비스라면 entrypoint가 실제 서버를 foreground로 유지하는지 확인합니다. 단발성 migration이나 백업 작업이라면 종료를 장애로 취급하지 않습니다.
- ExitCode가 0이 아니고 애플리케이션 로그가 있음: 로그에 나온 설정, 권한, 연결 실패를 먼저 수정합니다. restart policy는 원인을 가리지 않도록 진단 동안 제한합니다.
- State.Error가 있고 애플리케이션 로그가 없음: 프로세스가 시작되기 전에 Engine 또는 runtime 단계에서 실패했을 가능성이 큽니다. mount 경로, 실행 파일, 권한, 포트 할당과 daemon 로그를 확인합니다.
OOMKilled가 true이면 애플리케이션 메시지만으로 결론 내리지 않습니다. 메모리 제한, 호스트 메모리 압박, 종료 시각을 함께 확인합니다. 특정 종료 코드만 보고 OOM이라고 단정하면 수동 kill이나 다른 signal 경로를 놓칠 수 있습니다.
healthcheck 실패와 프로세스 종료는 다른 상태입니다. healthcheck는 실행 중 컨테이너를 healthy 또는 unhealthy로 평가하며, 주 프로세스가 종료된 컨테이너를 유지하지 않습니다. unhealthy만으로 종료 원인을 healthcheck에 돌리지 말고 종료 시각과 health 로그를 비교합니다. Docker Engine restart policy는 프로세스 종료에 반응하며, 외부 오케스트레이터나 감시 도구는 별도 동작을 추가할 수 있습니다.
attached 실행으로 첫 실패를 잡는다
기존 state를 보존한 뒤 같은 서비스만 attached 모드로 실행하면 시작 순서와 첫 오류를 바로 볼 수 있습니다. 데이터 마이그레이션이 자동 실행되는 이미지라면 먼저 백업하고, 운영 데이터에 영향을 주지 않는 환경에서 재현해야 합니다.
docker compose up --no-deps app
위 attached 명령이 실행 중이면 같은 터미널에서 다음 명령을 실행할 수 없습니다. 상태 조회는 두 번째 터미널에서 하거나 attached 실행이 끝난 뒤 수행합니다. 전체 config 출력 역시 비밀값을 포함할 수 있어 접근 제한 파일에만 보관합니다.
Compose 파일에서 command나 entrypoint를 임시로 바꾸기 전에는 docker compose config에서 현재 값을 저장합니다. 수정 뒤에는 단순 start가 아니라 컨테이너 재생성이 필요합니다.
--no-deps는 의존 서비스를 함께 시작하지 않도록 범위를 제한합니다. 의존 서비스가 내려가 있다면 연결 오류가 재현 조건 자체에서 생길 수 있으므로 먼저 상태를 확인하세요. attached 실행 중 Ctrl+C는 이 실행이 시작한 컨테이너를 중지할 수 있어 단순 로그 보기와 같지 않습니다. 운영 프로세스를 유지한 채 관찰만 하려면 docker compose logs -f --tail 200 app를 사용합니다.
기록은 서비스 | 컨테이너 ID | 종료 시각 | ExitCode | OOMKilled | 첫 오류 순서로 맞춥니다. 재생성 전후 ID가 다른 로그를 한 사건으로 섞지 않습니다. Entrypoint, Cmd와 애플리케이션 로그에는 토큰이 들어갈 수 있으므로 외부 공유본에는 값 대신 실행 파일 이름과 오류 종류만 남깁니다. 이 글의 분기표는 공식 동작을 바탕으로 한 판단 기준이며 특정 이미지에서 얻은 실측 결과표는 아닙니다.
수정과 롤백
- 구성 문법이 실패하면 먼저
docker compose config --quiet가 통과하도록 수정합니다. - 이미지 기본 entrypoint를 덮은 경우 변경 이유를 확인하고, 불필요한 override만 제거합니다.
- 서비스 프로세스가 daemonize한다면 애플리케이션이 지원하는 foreground 옵션을 사용합니다. 로그를 붙잡기 위한
tail -f는 실제 프로세스 상태를 숨기므로 해결책으로 두지 않습니다. - 수정 후 대상 서비스만 재생성하고 state와 로그를 다시 수집합니다.
수정 뒤 더 빨리 종료되거나 데이터 초기화가 시작되면 즉시 이전 Compose 파일과 환경 파일을 복원합니다. 볼륨 삭제를 동반하는 명령은 이 진단 절차에 포함하지 않습니다. 컨테이너를 다시 만드는 것과 영구 데이터를 지우는 것은 별개의 작업입니다.
같이 확인할 글
공식 문서 및 확인 기준
- Docker Compose up
- docker compose ps reference – Compose 컨테이너 상태와 전체 목록 확인
- docker compose logs reference – 타임스탬프와 마지막 로그 수집
- docker compose config reference – 실제 적용되는 서비스 모델 검증
- Docker restart policies – 종료 조건과 restart policy의 역할
- Compose services reference – command, entrypoint, 서비스 정의 기준
이 글에 남아 있던 이전 검증 기록
검증 수준: 공식 문서 대조
Docker가 기록하는 컨테이너 state와 Compose 명령의 범위를 공식 문서로 확인했습니다. 특정 이미지의 entrypoint는 재현하지 않았으므로 이미지별 foreground 옵션은 해당 프로젝트 문서를 따라야 합니다.