이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Dockerfile layer와 image는 Spring 배포 재현성에 어떤 영향을 주는가?
- 운영용 Spring Boot image를 만들 때 무엇을 image에 넣고 무엇을 runtime config로 빼야 하는가?
- image tag, digest, base image 문제를 장애 중 어떻게 확인하는가?
개요
Docker image는 Spring 애플리케이션의 실행 환경을 artifact로 고정하는 방법이다.
jar 파일만 배포하면 서버의 JDK, 파일 경로, 실행 사용자 차이가 운영 결과에 영향을 준다.
Docker image는 JRE, jar, entrypoint, 실행 사용자, 기본 filesystem을 하나의 산출물로 묶는다.
하지만 image 안에 운영 Secret이나 환경별 값을 넣으면 재현성은 좋아지는 대신 보안과 운영 유연성이 무너진다.
Layer의 의미
Dockerfile의 각 명령은 layer를 만든다.
layer는 build cache와 image size에 영향을 준다.
Spring 애플리케이션에서는 dependency layer와 application layer를 분리하면 코드 변경 시 rebuild와 pull 비용을 줄일 수 있다.
하지만 처음에는 단순하고 명확한 Dockerfile이 더 중요하다.
FROM eclipse-temurin:21-jre
WORKDIR /app
RUN groupadd --system app && useradd --system --gid app app
COPY build/libs/order-api.jar /app/order-api.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75", "-jar", "/app/order-api.jar"]이 image는 JRE와 jar, 실행 사용자를 고정한다.
profile, DB URL, Secret은 image가 아니라 실행 시점에 주입한다.
Image에 넣을 것과 빼야 할 것
image에 넣을 것:
- 실행할 jar
- 필요한 runtime
- non-root user
- entrypoint
- 기본 working directory
- health 확인에 필요한 최소 도구
image에서 빼야 할 것:
- 운영 DB password
- API token
- 환경별 endpoint
- 배포 시각마다 달라지는 값
- 서버별 로그 파일
- 업로드 파일이나 DB 데이터
운영 값을 image에 넣으면 같은 image를 dev, staging, prod에 재사용하기 어렵다.
Secret이 image layer나 registry에 남는 문제도 생긴다.
Tag와 Digest
운영 image는 tag와 digest로 추적한다.
docker image ls order-api
docker inspect order-api:2026.06.30-3f21a9c --format '{{json .RepoDigests}}'tag는 사람이 읽기 쉽지만 재사용될 수 있다.
digest는 content를 가리키므로 장애 중 더 강한 증거다.
release note에는 tag와 digest를 함께 남기는 편이 좋다.
latest만 사용하면 어떤 버전으로 rollback해야 하는지 알기 어렵다.
Base Image와 보안
base image는 JRE 버전, OS package, timezone, CA certificate에 영향을 준다.
base image가 바뀌면 애플리케이션 코드는 그대로여도 TLS 연결, DNS, font, locale, timezone 동작이 달라질 수 있다.
보안 패치 때문에 base image를 갱신해야 하지만, 갱신 자체도 release 변경이다.
base image 변경은 dependency 변경처럼 release note에 남겨야 한다.
Build Context와 .dockerignore
build context에 불필요한 파일이 들어가면 image build가 느려지고 Secret이 섞일 수 있다.
.dockerignore로 build context를 제한한다.
.git
.gradle
build/tmp
*.log
.env.env나 local dump가 build context에 들어가지 않게 해야 한다.
COPY 명령이 넓으면 의도하지 않은 파일이 image에 포함될 수 있다.
운영 체크리스트
- image tag가 Git SHA나 release 번호와 연결되는가?
- image digest를 release note에 남기는가?
- Dockerfile에 Secret이나 prod config가 없는가?
- non-root user로 실행하는가?
- base image 변경이 release note에 기록되는가?
.dockerignore가 Secret과 불필요 파일을 제외하는가?- image 안 jar version과 release note가 일치하는가?
장애 신호
- 운영에서 어떤 image digest가 실행 중인지 모른다.
- tag는 같은데 서버마다 다른 image content를 실행한다.
- base image 변경 후 TLS 인증서 검증이나 timezone이 달라진다.
- image 안에
.env나 credential 파일이 포함되어 있다. - image rebuild 없이 config를 바꿨다고 생각했지만 실제 값은 image에 박혀 있다.
- root user로 container가 실행되어 mount 파일 소유권이 꼬인다.
안전한 완화 조치
image 문제가 의심되면 새 build와 새 push를 멈추고 현재 실행 중인 digest를 확인한다.
tag 재사용이 의심되면 모든 서버의 image digest를 비교한다.
이전 image digest가 남아 있고 DB/data 변경이 호환된다면 rollback을 검토한다.
Secret이 image에 들어간 것이 확인되면 image 삭제만으로 끝나지 않는다.
registry, server cache, CI log 범위를 확인하고 Secret 회전을 함께 진행해야 한다.
인프라 담당자와 공유할 자료
서비스: order-api prod
문제 image tag: 2026.06.30-3f21a9c
digest: sha256:...
base image: eclipse-temurin:21-jre
증상: 배포 후 TLS handshake failure 증가
최근 변경: base image patch update
요청: 인스턴스별 image digest와 registry pull 시각 확인image 문제는 tag보다 digest와 build 변경점을 공유해야 좁혀진다.
실전 팁
- 운영 image는 CI에서 한 번 만들고 그대로 promotion한다.
- tag 재사용을 금지한다.
- Secret은 runtime에 주입하고 image에는 넣지 않는다.
- Dockerfile 변경은 애플리케이션 코드 변경만큼 release note에 남긴다.
- image size 최적화보다 먼저 추적성과 보안을 확보한다.
위험 신호!
latesttag만 사용한다.- 운영 서버에서 직접 image를 build한다.
- Dockerfile에
ENV DB_PASSWORD=...가 있다. .dockerignore가 없어 repository 전체가 build context로 들어간다.- root user로 container를 실행한다.
확인 질문
확인 질문
- Docker image와 container의 차이는 무엇인가?
- image는 실행 가능한 불변 산출물이고 container는 image를 실행한 프로세스와 writable layer를 가진 실행 인스턴스다.
- image digest를 확인해야 하는 이유는 무엇인가?
- 같은 tag라도 content가 달라질 수 있으므로 실제 실행 중인 image를 정확히 추적하기 위해서다.
- 운영 Secret을 image에 넣으면 안 되는 이유는 무엇인가?
- registry, layer, build cache, 서버 cache에 Secret이 남아 유출 범위가 커지고 회전 전까지 안전하게 제거하기 어렵기 때문이다.