문제 해결: 일반적인 Docker 컨테이너 오류 빠르게 진단하기
Docker 컨테이너는 탄력적으로 설계되었지만, 시작 실패는 개발 수명 주기의 피할 수 없는 부분입니다. 컨테이너가 갑자기 종료될 때, 배포 속도를 유지하기 위해서는 근본 원인을 신속하게 이해하는 것이 가장 중요합니다. 이러한 실패는 종종 0이 아닌 종료 코드만으로 표시되어 이해하기 어렵습니다.
이 가이드는 필수 Docker 명령어 툴킷을 사용하는 전문가 수준의 문제 해결 방법론을 제공합니다. 우리는 docker ps, docker logs, docker inspect를 활용하여 가장 빈번한 컨테이너 시작 문제를 신속하게 식별하고 해결할 수 있도록 구조화된 진단 과정을 안내하며, 이를 통해 추측에 의존하지 않고 실행 가능한 해결책을 적용할 수 있습니다.
1단계: 초기 분류 및 상태 평가
모든 컨테이너 실패를 진단하는 첫 번째 단계는 현재 및 최근 상태를 파악하는 것입니다. 기본 docker ps 명령어는 실행 중인 컨테이너만 표시하므로, 컨테이너가 시작 직후 종료된 경우에는 도움이 되지 않습니다.
docker ps -a를 사용하여 실패 찾기
초기 분류를 위한 핵심 명령어는 docker ps -a (실행 중이거나 중지된 모든 컨테이너 나열)입니다. 이 명령어를 통해 중지된 컨테이너의 상태, 종료 코드 및 생성 시간을 확인할 수 있습니다.
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d3f4b5c6e7a my-app:latest "/usr/bin/start.sh" 5 minutes ago Exited (127) 3 minutes ago web-service
d8c9a0b1c2d3 nginx:latest "nginx -g 'daemon..." 10 minutes ago Up 8 minutes 80/tcp active-proxy
주요 상태 표시기:
- Exited (0): 컨테이너가 정상적으로 의도한 대로 종료되었습니다 (종종 배치 작업 완료 후). 진단은 보통 최소한으로 이루어집니다.
- Exited (0이 아님): 실패가 발생했습니다. 일반적인 0이 아닌 코드(1, 126, 127)는 프로세스 충돌, 파일을 찾을 수 없음, 권한 오류와 같은 심각한 문제를 나타냅니다.
- Created: 컨테이너가 생성되었지만 시작되지 않았거나, 상태가 업데이트되기에는 시작이 너무 빨리 실패했습니다.
2단계: 컨테이너 로그로 심층 분석
컨테이너 ID 또는 이름을 알게 되면, 진단을 위한 가장 귀중한 도구는 로깅 메커니즘입니다. Docker는 컨테이너의 기본 프로세스에서 표준 출력(stdout) 및 표준 오류(stderr) 스트림을 캡처합니다.
과거 로그 검색
docker logs 명령어를 사용하여 실패한 컨테이너에서 캡처된 모든 출력을 검색하십시오. 이 출력에는 컨테이너를 중단시킨 정확한 오류 메시지(예: 스택 추적, 구성 오류 또는 파일 누락 경고)가 포함되어 있는 경우가 많습니다.
# 실패한 컨테이너의 로그 검색
$ docker logs web-service
# --- 로그 출력 예시 ---
Standardizing environment...
Error: Configuration file not found at /etc/app/config.json
Application initialization failed. Exiting.
고급 로그 필터링 팁:
| 명령어 옵션 | 목적 | 예시 |
|---|---|---|
-f, --follow |
실시간으로 로그 스트리밍 (컨테이너가 빠르게 시작하고 충돌할 때 유용). | docker logs -f web-service |
--tail N |
로그의 마지막 N줄만 표시. | docker logs --tail 50 web-service |
-t, --timestamps |
각 로그 항목에 타임스탬프 표시 (이벤트 상관 관계 분석에 유용). | docker logs -t web-service |
--since |
특정 시간 또는 기간(예: 1h, 15m) 이후에 생성된 로그 표시. |
docker logs --since 15m web-service |
모범 사례: 실패 직후에는 항상 로그를 확인하십시오. 로그가 비어 있다면, 메인 애플리케이션 프로세스가 시작되기도 전에 실패가 발생한 것으로, 종종 Docker의
ENTRYPOINT또는CMD구성 자체에 문제가 있음을 나타냅니다.
3단계: docker inspect로 상태 및 구성 분석
로그만으로는 충분하지 않을 때(예: 일반적인 오류만 표시되거나 아무것도 표시되지 않을 때), 컨테이너의 내부 구성 및 실행 환경을 분석해야 합니다.
전체 상태 객체 검토
docker inspect는 네트워크 설정부터 리소스 제한, 그리고 결정적으로 최종 상태 및 오류 메시지에 이르기까지 컨테이너에 대한 모든 것을 자세히 설명하는 포괄적인 JSON 객체를 제공합니다.
$ docker inspect web-service
출력에서 다음 주요 JSON 경로에 집중하십시오:
1. 상태 정보
이 섹션에는 실패 시간 및 시스템 수준 오류 메시지(해당하는 경우)를 포함한 상세 종료 정보가 들어 있습니다.
...
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 127,
"Error": "", // 종종 비어 있지만, 커널 수준 메시지를 포함할 수 있습니다.
"StartedAt": "2023-10-26T14:30:00.123456789Z",
"FinishedAt": "2023-10-26T14:30:00.223456789Z"
},
...
2. 엔트리포인트 및 명령어
컨테이너가 코드 127(명령어를 찾을 수 없음) 또는 126(명령어를 실행할 수 없음)으로 종료된 경우, Config 또는 State 섹션 아래의 Path와 Args를 확인하여 기본 프로세스가 올바르게 지정되었는지, 그리고 이미징 내에 해당 경로가 존재하는지 확인하십시오.
...
"Config": {
"Entrypoint": [
"/usr/bin/start.sh"
],
"Cmd": [
"--mode=production"
],
...
3. 마운트 및 볼륨
누락된 파일이나 권한 오류로 인해 애플리케이션이 실패한 경우, Mounts 섹션을 확인하여 호스트 볼륨이 올바르게 매핑되었는지, 접근 가능한지, 그리고 필요한 권한을 가지고 있는지 확인하십시오.
4단계: 일반적인 시작 실패 시나리오 및 해결책
로그와 검사 데이터를 결합하면 실패를 분류하고 목표에 맞는 해결책을 적용할 수 있습니다.
시나리오 1: 포트 이미 할당됨 (바인딩 오류)
이 오류는 매핑하려는 호스트 포트(-p 8080:80)가 다른 프로세스(다른 컨테이너 또는 호스트 머신에서 실행 중인 프로세스)에 의해 이미 사용 중일 때 발생합니다.
진단: 컨테이너가 즉시 시작되지 않거나, 로그에 bind: address already in use와 같은 오류가 표시되는 경우가 많습니다.
해결:
1. 충돌하는 프로세스 또는 컨테이너를 중지합니다.
2. 호스트 포트 매핑을 변경합니다 (예: -p 8081:80).
시나리오 2: 명령어를 찾을 수 없음 (종료 코드 127)
이는 Docker 런타임이 ENTRYPOINT 또는 CMD 지시문에 지정된 명령어를 실행할 수 없음을 의미합니다.
진단: docker logs (비어 있을 수 있음)를 확인하고 docker inspect를 사용하여 Config 섹션을 검증합니다.
해결:
1. 실행 파일 경로가 올바른지 확인합니다 (예: 단순히 app이 아닌 /usr/local/bin/app).
2. 이미지 내에 실행 파일이 존재하는지 확인합니다. 이미지 파일 시스템을 검사하기 위해 임시 디버깅 컨테이너를 실행해야 할 수도 있습니다:
# 실패한 명령어를 재정의하여 이미지를 임시로 실행
$ docker run -it --entrypoint /bin/bash my-app:latest
# 이제 컨테이너 내부에서 확인: ls -l /usr/bin/start.sh
시나리오 3: 권한 거부됨 (종료 코드 126 또는 볼륨 오류)
일반적으로 컨테이너 사용자가 필요한 파일, 디렉터리 또는 볼륨 마운트 지점에 접근할 권한이 없을 때 발생합니다.
진단: 로그에 Permission denied 또는 cannot open file과 같은 오류가 표시됩니다.
해결:
1. 볼륨 권한: 호스트 마운트(-v /host/data:/container/data)를 사용하는 경우, 호스트 폴더가 컨테이너가 실행되는 사용자 ID(종종 UID 1000 또는 root)에 대해 읽기/쓰기 권한을 가지고 있는지 확인하십시오.
2. 엔트리포인트 권한: ENTRYPOINT에 지정된 스크립트가 Dockerfile 내에서 실행 가능 플래그가 설정되어 있는지 확인하십시오 (RUN chmod +x /path/to/script).
시나리오 4: 메모리 부족 (OOMKilled)
이는 커널이 과도한 메모리 소비로 인해 컨테이너의 메인 프로세스를 종료시키는 시스템 수준의 실패입니다.
진단: docker ps -a에서 STATUS Exited (137)을 확인하거나 docker inspect [id]를 실행하여 State 객체에서 "OOMKilled": true 필드를 찾으십시오.
해결:
1. -m 플래그를 사용하여 컨테이너의 메모리 제한을 늘리십시오 (예: --memory 2g).
2. 메모리 사용량을 줄이도록 애플리케이션을 최적화하십시오.
요약 및 다음 단계
효율적인 Docker 문제 해결은 구조화된 접근 방식에 달려 있습니다: docker ps -a로 실패를 평가하는 것부터 시작하고, docker logs를 주요 조사 도구로 사용하며, docker inspect는 더 깊은 구성 및 환경 문제에 사용하십시오. 종료 코드의 의미를 이해하고 컨테이너 상태 내에서 어디를 봐야 할지 알면, 일반적인 시작 실패를 해결하는 데 소요되는 시간을 크게 줄일 수 있습니다.
추가 조치:
- 문제가 이미지와 관련된 경우, Dockerfile에 임시 디버깅 단계(예: 환경 변수 출력)를 포함하여 이미지를 다시 빌드하십시오.
- 로그가 부족한 경우, 컨테이너의 초기화를 일시적으로
bash또는sh를 사용하도록 전환하여 파일 시스템을 수동으로 탐색하고 환경 내에서 명령어를 테스트하십시오.