[도커 30강] 20강. docker exec/cp로 운영 중 디버깅하기

[도커 30강] 20강. docker exec/cp로 운영 중 디버깅하기

운영 중인 컨테이너에서 장애가 나면 가장 먼저 드는 생각은 “일단 들어가서 확인해보자”입니다. 이때 많이 쓰는 명령이 docker execdocker cp입니다. 둘 다 강력하지만, 잘못 쓰면 서비스 상태를 더 꼬이게 만들 수 있습니다. 특히 운영 환경에서는 “빠르게 확인”만큼이나 “흔적을 남기고 안전하게 빠져나오기”가 중요합니다.

이번 강의에서는 docker exec/cp를 단순 명령어 소개로 끝내지 않고, 운영 디버깅에서 안전하게 쓰는 절차로 정리합니다. 핵심은 세 가지입니다. 첫째, 컨테이너 내부를 바꾸기 전에 현재 상태를 먼저 기록할 것. 둘째, 가능한 읽기 중심(read-only) 진단부터 시작할 것. 셋째, 임시 조치와 영구 수정(이미지/코드 반영)을 반드시 분리할 것.


핵심 개념

  • docker exec는 “실행 중 컨테이너 안에서 추가 프로세스 실행”입니다. 컨테이너를 재시작하지 않고 확인할 수 있어 즉각 대응에 유용합니다.
  • docker cp는 “호스트와 컨테이너 간 파일 복사”입니다. 로그/설정/덤프를 꺼낼 때 매우 유용하지만, 반대로 파일을 넣을 때는 운영 상태를 바꾸는 행위라는 점을 인식해야 합니다.
  • 운영 디버깅의 우선순위는 보통 다음 순서가 안전합니다: 메타데이터 확인(inspect) → 로그 확인(logs) → 읽기 진단(exec) → 증거 수집(cp) → 최소 범위 임시 조치.
  • exec로 들어가서 수정한 내용은 이미지에 반영되지 않습니다. 컨테이너 재생성 시 사라질 수 있으므로, 원인 해결은 결국 Dockerfile/애플리케이션 코드/배포 설정으로 귀결되어야 합니다.
  • 팀 단위 운영에서는 “누가, 언제, 어떤 exec/cp를 수행했는지”를 기록하는 운영 습관이 사고 재발 방지에 큰 영향을 줍니다.

기본 사용

예제 1) 서비스 영향 최소화하며 컨테이너 상태 진단하기

아래 흐름은 운영 장애 대응의 기본 루틴입니다. 먼저 로그와 프로세스를 확인하고, 그다음 필요 시에만 셸로 진입합니다.

# 1) 대상 컨테이너 식별
CONTAINER=$(docker ps --filter "name=myapp" --format '{{.Names}}' | head -n 1)
echo "target=$CONTAINER"

# 2) 최근 로그 확인
docker logs --tail 200 "$CONTAINER"

# 3) 메타데이터/환경 변수 일부 확인
docker inspect "$CONTAINER" --format '{{.Config.Image}} {{.State.Status}} {{.State.StartedAt}}'

# 4) 비대화형 exec로 읽기 진단
docker exec "$CONTAINER" sh -lc 'id; uname -a; ps aux | head -n 20'

설명:

  • docker logsdocker inspect는 먼저 확인해야 할 1차 정보입니다. 많은 문제는 여기서 이미 단서를 얻습니다.
  • docker exec ... sh -lc '...' 형태는 명령을 한 번에 실행하고 빠져나와서, 불필요한 대화형 세션보다 추적이 쉽습니다.
  • 진단 초기에는 파일 수정 명령(sed -i, rm, mv)을 피하고 읽기 명령(cat, ls, grep) 위주로 진행하는 것이 안전합니다.

예제 2) 대화형 진입이 필요할 때 안전하게 들어가기

라이브 디버깅이 필요하면 -it를 쓰되, 먼저 “무엇을 볼지”를 정한 뒤 들어가야 불필요한 변경을 줄일 수 있습니다.

CONTAINER=myapp-web-1

# 셸이 있는지 확인 후 진입
docker exec -it "$CONTAINER" sh

# 컨테이너 내부에서 읽기 위주 점검
pwd
ls -al /app
cat /app/config/app.yaml
env | sort | grep -E 'APP_|DB_|REDIS_'

# 애플리케이션 포트 리스닝 상태 확인
ss -lntp || netstat -lntp

설명:

  • 어떤 베이스 이미지는 bash가 없고 sh만 있습니다. 습관적으로 bash부터 치면 “명령 없음”으로 시간을 낭비할 수 있습니다.
  • 대화형 세션에서 확인할 항목을 미리 정리하면, 운영 중 우발적 변경 위험이 줄어듭니다.
  • 환경변수 확인 시 비밀값 노출 가능성이 있으므로, 터미널 기록/스크린샷/공유 채널에 그대로 올리지 않도록 주의하세요.

예제 3) docker cp로 증거 수집(로그/설정/덤프)하기

장애 분석의 핵심은 “증거 확보”입니다. 운영 컨테이너에서 필요한 파일을 꺼내 로컬에서 분석하면 서비스 영향 없이 조사 범위를 넓힐 수 있습니다.

CONTAINER=myapp-web-1
mkdir -p ~/incident-2026-02-17

# 1) 컨테이너 -> 호스트: 로그/설정 파일 추출
docker cp "$CONTAINER":/app/logs/error.log ~/incident-2026-02-17/error.log
docker cp "$CONTAINER":/app/config/app.yaml ~/incident-2026-02-17/app.yaml

# 2) 추출 파일 무결성 확인
ls -lh ~/incident-2026-02-17
shasum -a 256 ~/incident-2026-02-17/*

# 3) 필요 시 호스트 -> 컨테이너
# docker cp ./temporary-hotfix.conf "$CONTAINER":/app/config/hotfix.conf

설명:

  • docker cp는 컨테이너가 살아 있는 상태에서도 파일을 꺼낼 수 있어 장애 분석에 매우 효율적입니다.
  • 복사한 파일에 해시를 남기면 “어떤 시점의 어떤 파일을 분석했는지” 추적성이 좋아집니다.
  • 운영 중 파일 주입(호스트→컨테이너)은 마지막 수단입니다. 임시 조치 후에는 반드시 이미지/코드에 영구 반영하세요.

예제 4) 임시 핫픽스 후 반드시 영구 수정으로 이어가기

운영에서 exec로 문제를 완화했다면, 거기서 끝나면 안 됩니다. 동일 이슈가 재배포 시 재발합니다.

# (임시) 운영 컨테이너에서 빠른 완화 조치 수행
CONTAINER=myapp-web-1
docker exec "$CONTAINER" sh -lc 'cp /app/config/app.yaml /app/config/app.yaml.bak && sed -i "s/timeout: 3/timeout: 10/" /app/config/app.yaml'

# 서비스 상태 점검
docker logs --tail 100 "$CONTAINER"

# (영구) 소스 저장소에서 동일 변경 반영 + 이미지 재빌드
git checkout -b fix/timeout-incident
# 코드/설정 수정 후
# git commit -am "Increase timeout to 10 for upstream latency spike"
# docker build -t registry.example.com/devlab/myapp:1.4.1 .
# docker push registry.example.com/devlab/myapp:1.4.1

설명:

  • 임시 변경 전 백업 파일을 남기면 롤백이 쉬워집니다.
  • 운영 완화 조치와 영구 수정은 한 세트입니다. 영구 수정이 없으면 다음 배포 때 다시 장애가 납니다.
  • 사건 회고 문서에는 “임시 조치 내용, 영구 반영 PR/커밋, 재발 방지 항목”까지 묶어 기록하세요.

자주 하는 실수

실수 1) 로그 확인 없이 바로 exec로 들어감

  • 원인: “일단 들어가 보면 알겠지”라는 습관.
  • 문제: 원인 추적보다 탐색 시간이 길어지고, 불필요한 명령 실행이 늘어납니다.
  • 해결: 항상 logs/inspect를 먼저 보고 가설을 세운 뒤 exec를 최소 범위로 사용하세요.

실수 2) 운영 컨테이너 안에서 패키지 설치로 문제를 해결하려 함

  • 원인: 긴급 상황에서 apt install, apk add로 즉석 대응.
  • 문제: 이미지 정의와 런타임 상태가 달라져 재현 불가 상태가 됩니다.
  • 해결: 임시 완화는 하되, 즉시 Dockerfile/코드에 반영하고 새 이미지로 교체 배포하세요.

실수 3) docker cp로 파일 주입 후 변경 이력 미기록

  • 원인: “급해서 일단 고침” 후 문서화 누락.
  • 문제: 팀원이 동일 컨테이너를 기준으로 잘못된 가정을 하게 됩니다.
  • 해결: 파일 주입 시점, 대상 경로, 이유, 복구 방법을 운영 로그(티켓/위키)에 즉시 남기세요.

실수 4) 디버깅 중 민감정보를 그대로 공유

  • 원인: 환경변수/설정 파일 전체를 캡처해서 채팅방 공유.
  • 문제: 토큰/비밀번호 유출로 2차 사고 발생 가능.
  • 해결: 공유 전 마스킹 규칙 적용(예: ***), 필요 최소 범위만 전달하세요.

실무 패턴

운영 현장에서 exec/cp를 안정적으로 쓰려면 개인 요령보다 팀 표준이 필요합니다. 아래 패턴은 작은 팀에서도 바로 적용 가능합니다.

  • 패턴 1: 진단 체크리스트 고정
    장애 발생 시 순서를 고정합니다. 예: (1) 컨테이너 식별, (2) 최근 로그 200줄, (3) 상태 inspect, (4) 비대화형 exec, (5) 필요 시 대화형 exec. 이 순서만 지켜도 실수율이 크게 줄어듭니다.

  • 패턴 2: 읽기 진단 우선 원칙
    운영 중 최초 10~15분은 읽기 전용 점검만 허용하고, 쓰기 변경은 승인된 담당자만 수행합니다. 빠른 진단과 안정성 균형에 효과적입니다.

  • 패턴 3: 임시 변경 라벨링
    컨테이너 내부 임시 변경을 했다면, 이슈 트래커에 “TEMP-FIX” 태그를 붙이고 종료 전까지 “영구 수정 PR 링크”를 의무 첨부합니다.

  • 패턴 4: 증거 수집 표준화
    docker cp로 가져온 파일은 사고 번호 디렉터리(incident-YYYYMMDD-<id>)에 보관하고, 해시/수집 시각/수집자를 함께 기록합니다. 이후 회고와 재현 실험이 쉬워집니다.

  • 패턴 5: 재배포로 정상화
    임시 완화가 끝나면 “수정된 이미지로 재배포”를 정상화 완료 조건으로 둡니다. 운영 컨테이너 수작업 상태를 오래 유지하면 다음 장애 때 더 큰 혼란이 생깁니다.

결국 운영 디버깅의 목적은 “지금 당장 불을 끄는 것”과 “다시 불이 안 나게 만드는 것” 두 가지입니다. docker exec/cp는 전자에는 매우 강하지만, 후자를 완성하려면 코드/이미지/배포 파이프라인까지 연결해야 진짜 해결이 됩니다.

오늘의 결론

한 줄 요약: docker exec/cp는 운영 디버깅의 핵심 도구지만, 읽기 진단 우선·증거 수집·영구 수정 반영까지 묶어야 안전한 실무 대응이 된다.

연습문제

  1. 현재 서비스 컨테이너 하나를 선택해, logs → inspect → 비대화형 exec 순서로 진단 체크리스트를 직접 실행하고 결과를 문서화해보세요.
  2. docker cp로 로그/설정 파일을 수집하는 스크립트를 작성하고, 파일 해시와 수집 시각을 자동 기록하도록 개선해보세요.
  3. 운영에서 임시 설정 변경이 발생했다고 가정하고, 동일 변경을 Dockerfile/코드에 영구 반영해 새 이미지 태그로 재배포하는 절차를 작성해보세요.

이전 강의 정답

19강 연습문제 해설:

  • latest 의존 구간 제거의 핵심은 “운영 배포 태그 고정”입니다. 실무에서는 버전 태그 + git 커밋 태그를 함께 발급해 사람이 읽는 릴리스 정보와 시스템 추적 정보를 동시에 확보하는 방식이 가장 안정적입니다.
  • 스테이징 검증 후 운영 승격에서는 재빌드하지 않고 동일 digest를 승격해야 합니다. 이렇게 해야 스테이징에서 통과한 산출물과 운영 산출물이 정확히 같다는 것을 보장할 수 있습니다.
  • 배포 로그 자동화 시에는 최소한 환경, 태그, digest, 커밋, 배포 시각을 함께 저장해야 장애 분석과 롤백이 빨라집니다. 이 값들이 분리되어 있으면 사고 시 수동 추적 시간이 크게 늘어납니다.

실습 환경/재현 정보

  • OS: macOS 15+ 또는 Ubuntu 22.04+
  • Docker 버전: Engine/Desktop 25.x 이상
  • 실습 컨테이너: Nginx, Python/FastAPI, Node.js 등 임의 서비스 1개
  • 실행 순서:
    1. 대상 컨테이너 식별 및 상태 확인
    2. 로그와 inspect로 1차 진단
    3. docker exec로 읽기 중심 점검
    4. docker cp로 로그/설정 증거 수집
    5. 필요 시 임시 완화 후 코드/이미지에 영구 반영
  • 재현 체크:
    • 운영 디버깅이 체크리스트 순서대로 수행되었는가?
    • 임시 변경 사항이 문서화되었는가?
    • 영구 수정 PR/커밋/이미지 태그가 연결되었는가?
    • 동일 문제가 재배포 후에도 재발하지 않는가?