Docker 로그·모니터링·트러블슈팅 — 운영 서버 장애 대응 가이드
서버가 터졌다. 가장 먼저 치는 명령어는? docker logs다.
"어제까지 잘 되던 서버가 갑자기 죽었어요." 개발자라면 누구나 듣기 싫어하는 말이다. 하지만 Docker 환경이라면 이야기가 조금 다르다. 운영체제(OS) 전체를 뒤질 필요 없이, 문제가 발생한 격리된 컨테이너만 타겟팅해서 파헤치면 되기 때문이다.
운영 서버에서 장애가 발생했을 때, 당황하지 않고 체계적으로 원인을 찾아가는 트러블슈팅 프로세스와 필수 명령어들을 알아보자.
장애 대응 프로세스 — 순서가 중요하다
문제가 터졌을 때 아무렇게나 명령어를 쳐보는 것은 시간 낭비다. 아래의 흐름을 머릿속에 넣어두자.
- 상태 확인 (
docker ps): 녀석이 아직 살아있는가? 아니면 계속 재시작(Restarting) 중인가? - 로그 분석 (
docker logs): 죽기 직전에 무슨 유언(에러 메시지)을 남겼는가? - 리소스 점검 (
docker stats): CPU나 메모리를 혼자 다 먹고 폭주하고 있지는 않은가? - 내부 진입 (
docker exec/inspect): 설정 파일이 꼬였는가? 볼륨 마운트가 잘못되었는가?
docker logs — 원인 분석의 시작
컨테이너가 출력하는 표준 출력(stdout)과 표준 에러(stderr)를 보여준다. 애플리케이션 코드가 내뿜는 에러 스택 트레이스를 가장 먼저 확인하는 곳이다.
실시간 모니터링 (-f, --follow)
지금 막 에러가 발생하고 있다면, 로그를 실시간으로 스트리밍해서 봐야 한다. tail -f와 같은 역할이다.
docker logs -f my-backend
최근 로그만 보기 (--tail)
컨테이너가 몇 달째 켜져 있었다면 로그가 기가바이트(GB) 단위일 수 있다. 그냥 docker logs를 치면 터미널이 멈출지도 모른다. 가장 최근의 에러만 빠르게 스캔하자.
docker logs --tail 100 my-backend
시간대별 필터링 (--since, --until)
"오늘 새벽 2시쯤에 장애 알럿이 울렸어요"라는 제보를 받았다면? 해당 시간대의 로그만 잘라내서 볼 수 있다.
# 특정 시간 이후의 로그만 보기
docker logs --since "2026-05-27T02:00:00" my-backend
# 타임스탬프를 함께 출력해서 정확한 시간 확인하기
docker logs -t --tail 50 my-backend
docker stats — 리소스 병목 찾기
로그에 별다른 에러가 없는데 서비스가 느리거나 서버가 뻗었다면, 십중팔구 리소스 고갈 문제다.
실시간 모니터링 (CPU/MEM/NET/IO)
전체 컨테이너들의 리소스 사용량을 표 형태로 실시간 갱신해서 보여준다. 리눅스의 top 명령어와 비슷하다.
docker stats
- MEM USAGE / LIMIT: 현재 사용 중인 메모리와 할당된 최대 메모리. 이 비율이 90%를 넘어가고 계속 오르기만 한다면 메모리 누수(Memory Leak)를 의심해야 한다.
- CPU %: 100%를 찍고 내려오지 않는 녀석이 있다면, 무한 루프에 빠졌거나 연산이 과도한 것이다.
스냅샷 모드 (--no-stream)
보고서를 작성하거나 스크립트에서 현재 상태만 딱 한 번 가져와야 할 때 쓴다.
docker stats --no-stream
docker inspect — 설정과 상태 깊이 파기
컨테이너의 모든 메타데이터(IP 주소, 마운트된 볼륨, 환경 변수 등)를 JSON 형태로 보여주는 현미경 같은 명령어다. "분명히 설정을 제대로 줬는데 왜 안 되지?" 싶을 때 확인한다.
JSON 출력에서 원하는 값만 뽑기
출력 결과가 너무 방대하기 때문에, --format (또는 -f) 옵션으로 필요한 값만 쏙 빼먹는 것이 실무 꿀팁이다.
# 컨테이너의 내부 IP 주소만 확인하고 싶을 때
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-app
# 마운트된 볼륨 경로가 제대로 잡혔는지 확인할 때
docker inspect -f '{{json .Mounts}}' my-app
# 컨테이너가 지금까지 몇 번이나 재시작했는지 볼 때 (CrashLoopBackOff 의심)
docker inspect -f '{{.RestartCount}}' my-app
docker top / docker diff — 프로세스와 파일 변경 추적
docker exec로 굳이 안에 들어가지 않고도 내부 상태를 살짝 엿볼 수 있는 명령어들이다.
docker top
컨테이너 안에서 어떤 프로세스들이 돌고 있는지 호스트(운영 서버) 관점에서 보여준다. Nginx 컨테이너라면 마스터 프로세스와 워커 프로세스들이 어떻게 떠 있는지 볼 수 있다.
docker top my-nginx
docker diff
컨테이너가 처음 실행된 이후에 파일 시스템에 어떤 변경(A: 추가, C: 변경, D: 삭제)이 일어났는지 추적한다. 해킹당해서 이상한 파일이 심어졌거나, 임시 파일이 계속 쌓이고 있는지 확인할 때 유용하다.
docker diff my-app
docker events — 시스템 이벤트 실시간 모니터링
도커 데몬(서버 전체)에서 일어나는 이벤트를 실시간으로 스트리밍한다. 누군가 컨테이너를 죽이고 살리는지, OOM(Out of Memory)으로 컨테이너가 죽었는지 추적할 수 있다.
# 서버에서 발생하는 도커 이벤트 실시간 감시
docker events
# OOM으로 죽은 이벤트만 필터링해서 찾기
docker events --filter 'event=oom'
실전 트러블슈팅 시나리오 3가지
배운 명령어들을 조합해서 실무에서 자주 만나는 상황들을 해결해보자.
시나리오 1: 컨테이너가 켜지자마자 자꾸 죽어요
docker ps를 쳐보니 상태가 계속 Restarting (1)이다.
docker logs my-app을 쳐서 치명적인 에러(예: DB 접속 실패, 포트 충돌)가 있는지 확인한다.docker inspect my-app으로 환경 변수(DB 비밀번호 등)가 오타 없이 제대로 들어갔는지 검사한다.
시나리오 2: 서버 응답이 너무 느려요
docker stats를 켜두고 CPU나 메모리를 100% 가까이 치고 있는 컨테이너를 찾는다.- 범인을 찾았다면
docker top 범인_컨테이너를 쳐서 내부의 어떤 프로세스가 문제인지 확인한다. docker logs --tail 200 범인_컨테이너를 통해 트래픽이 몰렸는지, 무한 루프 에러가 났는지 확인한다.
시나리오 3: 코드를 바꿨는데 적용이 안 돼요
docker exec -it my-app /bin/bash로 컨테이너 안에 들어간다./app(소스 폴더)로 이동해서cat이나less로 바뀐 코드가 제대로 복사/마운트되었는지 확인한다.- 볼륨 마운트 문제라면
docker inspect my-app으로 호스트 경로와 컨테이너 경로가 올바르게 매핑되었는지 재확인한다.
📝 정리
장애가 났을 때 당황해서 컨테이너부터 재시작해버리면, 왜 죽었는지 원인(증거)을 영영 날려버릴 수 있다. 항상 증거를 먼저 수집하는 습관을 들이자.
- [x]
docker logs --tail 100 -f: 에러 로그 빠르게 훑어보기 - [x]
docker stats: CPU/메모리 병목 지점 찾기 - [x]
docker inspect: 숨겨진 설정값(IP, 환경변수, 볼륨) 현미경으로 파헤치기 - [x]
docker events: 서버 전체에서 일어나는 은밀한 이벤트(OOM 등) 감시하기
지금까지 컨테이너를 띄우고, 문제를 해결하는 방법을 알아봤다. 하지만 컨테이너를 오래 운영하다 보면 반드시 마주치는 거대한 적이 있다. 바로 디스크 용량 부족이다. 다음 3편(이미지·볼륨·네트워크 관리)에서는 No space left on device 에러를 막고 운영 서버를 깔끔하게 유지하는 청소 및 관리 비법을 알아보자.