[도커 30강] 19강. 이미지 태그 전략과 버전 관리

[도커 30강] 19강. 이미지 태그 전략과 버전 관리

도커를 팀 단위로 운영하다 보면, 기술적으로 더 어려운 문제보다 먼저 발목을 잡는 것이 있습니다. 바로 “이 컨테이너가 정확히 어떤 버전인지”를 누구도 자신 있게 말하지 못하는 상황입니다. 개발자는 latest를 믿고 배포했는데 운영 서버에는 지난주 이미지가 떠 있고, 장애가 나서 롤백하려고 보니 어떤 태그가 안전한 버전인지 기록이 없습니다. 이 문제는 Docker 명령어 숙련도보다 태그 전략과 버전 관리 규칙이 있는지 없는지에서 갈립니다.

이번 강의에서는 이미지 태깅을 단순 문법이 아니라, 배포 추적성과 장애 대응력을 높이는 운영 설계로 다룹니다. 핵심은 한 줄입니다. 사람이 읽기 쉬운 태그(의미 버전)와 시스템이 추적하기 쉬운 태그(Git SHA)를 함께 운용하라. 여기에 환경별 승격(promotion) 규칙까지 더하면 “어떤 코드가 어디에 배포됐는지”를 명확하게 설명할 수 있게 됩니다.


핵심 개념

  • Docker 이미지 태그(tag)는 “별명”입니다. 같은 이미지 digest에 여러 태그를 붙일 수 있고, 반대로 같은 태그가 시간이 지나며 다른 이미지 digest를 가리킬 수도 있습니다.
  • latest는 최신 보장 태그가 아닙니다. 단지 “마지막으로 latest로 push된 이미지”를 뜻합니다. 따라서 배포 기준 태그로 단독 사용하면 재현성이 무너집니다.
  • 실무에서는 보통 2트랙 태그를 씁니다.
    • 사람 중심: 1.4.2, 2026.02.17, release-2026w08
    • 추적 중심: git-a1b2c3d, sha-<commit>
  • 최종 신뢰 기준은 태그가 아니라 digest(sha256:...)입니다. 태그는 편의용 이름이고, digest는 불변 식별자입니다.
  • 운영 안정성을 높이려면 “빌드 → 검증 → 스테이징 태그 → 운영 태그” 승격 규칙을 정해 태그 이동을 통제해야 합니다.

기본 사용

예제 1) 태그와 digest 차이를 눈으로 확인하기

먼저 같은 이미지를 여러 태그로 관리할 수 있다는 점을 직접 확인해보겠습니다.

mkdir -p ~/docker100/lesson19
cd ~/docker100/lesson19

cat > Dockerfile <<'DOCKER'
FROM alpine:3.20
RUN echo "lesson19" > /version.txt
CMD ["sh", "-c", "cat /version.txt && sleep 3600"]
DOCKER

docker build -t devlab/demo-app:1.0.0 .
docker tag devlab/demo-app:1.0.0 devlab/demo-app:stable
docker tag devlab/demo-app:1.0.0 devlab/demo-app:git-abc1234

docker images | grep 'devlab/demo-app'
docker inspect --format='{{index .RepoDigests 0}}' devlab/demo-app:1.0.0

설명:

  • 1.0.0, stable, git-abc1234는 태그 이름만 다를 뿐, 동일 이미지를 가리킬 수 있습니다.
  • 실제 불변 식별자는 RepoDigests에 보이는 digest입니다.
  • 운영 사고 분석 시 “그때 어떤 digest를 배포했는지”를 남겨야 정확한 재현이 가능합니다.

예제 2) latest 의존을 줄이고 버전+커밋 태그를 동시 푸시하기

CI에서 가장 추천하는 기본 패턴입니다. 한 번 빌드한 이미지를 여러 태그로 push해 의미와 추적성을 함께 확보합니다.

cd ~/docker100/lesson19

APP_IMAGE="registry.example.com/devlab/myapp"
APP_VERSION="1.3.0"
GIT_SHA="$(git rev-parse --short HEAD 2>/dev/null || echo localdev)"

docker build -t ${APP_IMAGE}:${APP_VERSION} .
docker tag ${APP_IMAGE}:${APP_VERSION} ${APP_IMAGE}:git-${GIT_SHA}

# 선택: 최신 릴리스 별칭이 필요할 때만 부여
# docker tag ${APP_IMAGE}:${APP_VERSION} ${APP_IMAGE}:stable

docker push ${APP_IMAGE}:${APP_VERSION}
docker push ${APP_IMAGE}:git-${GIT_SHA}
# docker push ${APP_IMAGE}:stable

설명:

  • 배포 기록에 versiongit-sha를 함께 남기면, 기능 릴리스 추적과 코드 추적을 동시에 해결할 수 있습니다.
  • stable 같은 움직이는 태그는 운영 편의에 유용하지만, 반드시 고정 태그/커밋 태그와 함께 써야 안전합니다.
  • 로컬 개발 환경에서 latest를 쓰더라도, CI/CD 및 운영 매니페스트에는 고정 태그 사용을 권장합니다.

예제 3) 스테이징에서 검증 후 운영 태그로 승격하기

중요한 포인트는 “운영용 이미지를 새로 빌드하지 않는 것”입니다. 스테이징에서 검증한 동일 이미지를 태그 승격으로 운영에 반영해야 재현성이 확보됩니다.

APP_IMAGE="registry.example.com/devlab/myapp"
CANDIDATE_TAG="git-a1b2c3d"
PROD_TAG="1.3.0"

# 1) 스테이징은 candidate 태그로 배포
kubectl set image deployment/myapp myapp=${APP_IMAGE}:${CANDIDATE_TAG} -n staging

# 2) 테스트 통과 후 동일 digest를 운영 태그로 승격
docker pull ${APP_IMAGE}:${CANDIDATE_TAG}
docker tag ${APP_IMAGE}:${CANDIDATE_TAG} ${APP_IMAGE}:${PROD_TAG}
docker push ${APP_IMAGE}:${PROD_TAG}

# 3) 운영은 PROD_TAG로 배포
kubectl set image deployment/myapp myapp=${APP_IMAGE}:${PROD_TAG} -n prod

설명:

  • 스테이징과 운영에서 서로 다른 빌드를 쓰면 “스테이징 통과했는데 운영 실패”가 발생하기 쉽습니다.
  • 승격 방식은 동일 산출물을 환경만 바꿔 적용하므로 검증 신뢰도가 높습니다.
  • 운영 태그를 사람이 읽기 쉬운 릴리스 번호로 유지하면 커뮤니케이션(장애 공유, 릴리즈 노트)도 쉬워집니다.

예제 4) Compose에서 태그를 환경변수로 통제하기

개발/스테이징/운영이 각각 다른 태그를 바라보게 하되, 파일 자체는 최대한 공통화하는 패턴입니다.

cat > compose.yaml <<'YAML'
services:
  app:
    image: registry.example.com/devlab/myapp:${APP_TAG}
    ports:
      - "8080:8080"
YAML

# 개발
APP_TAG=git-local docker compose up -d

# 스테이징
APP_TAG=git-a1b2c3d docker compose up -d

# 운영
APP_TAG=1.3.0 docker compose up -d

설명:

  • 인프라 코드(Compose/K8s 매니페스트)와 배포 버전(태그)을 분리하면 변경 관리가 단순해집니다.
  • 운영에서 APP_TAG=latest 같은 설정은 지양하고, 반드시 고정 태그를 주입하세요.
  • 릴리즈 자동화 시 APP_TAG 값을 배포 로그와 함께 저장하면 롤백 대응 시간이 크게 줄어듭니다.

자주 하는 실수

실수 1) 운영 배포를 latest 하나로 통일

  • 원인: “편해서” 혹은 “항상 최신이면 좋지 않나”라는 판단.
  • 문제: 배포 시점마다 다른 이미지가 당겨져 재현이 불가능해지고, 장애 원인 추적이 매우 어려워집니다.
  • 해결: 운영은 고정 태그(버전/커밋)만 허용하고, latest는 개발 편의 용도로만 제한합니다.

실수 2) 빌드마다 태그 규칙이 팀원마다 다름

  • 원인: 규칙 문서 부재, CI 템플릿 미정의.
  • 문제: 어떤 이미지는 v1.2.0, 어떤 이미지는 release-final, 어떤 이미지는 test2처럼 혼란이 누적됩니다.
  • 해결: 태그 네이밍 표준을 팀 규칙으로 고정하고 CI에서 강제합니다.

실수 3) 운영 태그를 덮어쓰기

  • 원인: 급한 핫픽스 시 기존 태그 재사용.
  • 문제: 같은 태그가 서로 다른 digest를 가리키며 감사 추적(audit)과 롤백 신뢰가 깨집니다.
  • 해결: 운영 릴리스 태그는 immutable 정책으로 관리하고, 새 수정은 새 태그를 발급합니다.

실수 4) 태그는 남겼지만 배포 이력과 연결이 안 됨

  • 원인: 배포 로그/변경이력 시스템과 이미지 태그가 분리.
  • 문제: 장애 시 “이 서버에 뭐가 올라갔지?”를 수동 추적해야 합니다.
  • 해결: 배포 파이프라인에서 환경, 태그, digest, 커밋, 실행자, 시간을 함께 기록하도록 자동화합니다.

실무 패턴

태그 전략은 기술보다 “팀의 운영 습관”에 더 가깝습니다. 아래 패턴을 적용하면 규모가 커져도 흔들림이 줄어듭니다.

  • 패턴 1: 2중 태깅 기본화
    모든 빌드에 semver 태그와 git-sha 태그를 함께 발급합니다. 예: 1.3.0 + git-a1b2c3d.

  • 패턴 2: 승격 중심 배포
    운영 배포는 “새 빌드”가 아니라 “검증된 이미지 승격”으로 처리합니다. 즉, 스테이징 통과한 digest를 운영 태그로 재지정합니다.

  • 패턴 3: 환경별 허용 태그 정책

    • dev: 자유 태그 허용(실험 가능)
    • staging: 커밋 태그 중심
    • prod: 릴리즈 태그/고정 digest만 허용
  • 패턴 4: 보존 정책(lifecycle) 운영
    레지스트리 저장공간 절약을 위해 오래된 임시 태그를 정리하되, 운영 이력과 연결된 태그는 보존 기간을 길게 둡니다.

  • 패턴 5: 문서와 자동화 동기화
    “문서에 적힌 규칙”과 “CI 실제 동작”이 다르면 규칙은 무용지물입니다. 릴리즈 스크립트에 태그 규칙을 코드로 박아 넣으세요.

실무에서 가장 중요한 결론은 이것입니다. 태그 전략은 사고 대응 속도를 결정하는 인프라다. 평소에는 잘 보이지 않지만, 장애가 발생했을 때 잘 설계된 태그 체계는 원인 파악과 롤백을 몇 분 안에 끝내게 해줍니다. 반대로 전략 없는 태깅은 장애를 장기화시키는 숨은 비용이 됩니다.

오늘의 결론

한 줄 요약: 도커 태그는 이름표가 아니라 배포 추적 체계이며, 버전 태그와 커밋 태그를 함께 운영해야 재현성과 롤백 신뢰를 확보할 수 있다.

연습문제

  1. 현재 프로젝트의 이미지 태깅 규칙을 점검해 latest 의존 구간을 찾고, 이를 버전 태그 + git 태그 체계로 바꿔보세요.
  2. 스테이징 검증 후 운영 승격 시 “재빌드 금지” 원칙을 CI에 반영해보세요. (동일 digest 승격 확인 포함)
  3. 배포 로그에 환경/태그/digest/커밋/시간을 자동 저장하도록 스크립트를 작성해보세요.

이전 강의 정답

18강 연습문제 해설:

  • 멀티스테이지 전환의 핵심은 빌더와 런타임 분리입니다. 변경 전/후 비교 시 이미지 크기뿐 아니라 빌드 시간, 레지스트리 push/pull 시간도 함께 측정해야 실제 개선폭을 확인할 수 있습니다.
  • 빌드 전용 도구(컴파일러, 디버거, 패키지 캐시)가 런타임에 남지 않았는지 docker history, 컨테이너 내부 파일 검사로 확인해야 합니다. 단순히 “멀티스테이지를 썼다”는 사실만으로 안전이 보장되지는 않습니다.
  • --target debug--target prod를 분리하면 개발 생산성과 운영 보안을 동시에 확보할 수 있습니다. CI에서는 prod 타깃만 배포 가능하도록 게이트를 두고, debug 이미지는 내부 진단 용도로 제한하는 것이 바람직합니다.

실습 환경/재현 정보

  • OS: macOS 15+ (Apple Silicon) 또는 Ubuntu 22.04+
  • Docker: Engine/Desktop 25.x 이상
  • 레지스트리: Docker Hub, GHCR, ECR 등 OCI 호환 레지스트리
  • 실행 순서:
    1. 샘플 이미지 빌드 및 다중 태그 부여
    2. 버전/커밋 태그 동시 push
    3. 스테이징 배포 후 검증
    4. 동일 digest를 운영 태그로 승격
    5. 배포 이력에 태그·digest·커밋 기록
  • 재현 체크:
    • 운영 배포에 latest가 사용되지 않는가?
    • 릴리즈 태그와 커밋 태그가 항상 쌍으로 존재하는가?
    • 스테이징 통과 이미지와 운영 이미지 digest가 동일한가?
    • 장애 시 특정 태그로 즉시 롤백 가능한가?