개요
Dockerfile은 컨테이너 이미지를 만드는 기준이 되는 파일이며, 작성 방식에 따라 이미지의 품질이 달라진다.
비효율적인 Dockerfile은 이미지 용량 증가, 빌드 시간 지연, 보안 취약점 노출, 유지보수 어려움으로 이어질 수 있다.
따라서 Dockerfile 작성 시에는 이미지 경량화, 캐시 최적화, 명확한 명령어 구성, 불필요한 요소 제거, 보안 강화를 고려해야 한다.
Dockerfile Best Practices는 이러한 원칙을 기반으로 더 안정적이고 효율적인 컨테이너 환경을 만들기 위한 실무 기준이라고 볼 수 있다.
Dockerfile Best Practices 핵심 선요약
- 속도 최적화
→ 캐시 활용 + 변경 적은 레이어 먼저 구성 + .dockerignore로 불필요 파일 제거 - 이미지 경량화
→ Alpine/Distroless 사용 + 멀티스테이지 빌드로 불필요 요소 제거 - 보안 강화
→ root 사용자 금지 + 민감정보 미포함 + 최소 권한 원칙 적용
빌드 속도 최적화 (Build Speed)
빌드 속도 최적화의 핵심: "캐시 무효화를 최소화"
Dockerfile의 모든 명령어(RUN, COPY, ADD)는 하나의 레이어(Layer)를 생성합니다.
캐시 히트(Cache Hit): Docker는 빌드 시, 이전에 실행한 명령어와 내용이 동일하면 캐시된 레이어를 재사용
캐시 무효화(Cache Invalidation): 특정 레이어가 변경되면 (예: COPY하는 파일이변경됨), 그 이후의 모든 레이어 캐시가 무효화되어 해당 명령부터 다시 실행됨
1. 명령어 순서 최적화
자주 변경되는 내용을 Dockerfile의 가장 아래쪽에 배치한다

핵심: 의존성 파일 먼저 → install → 소스코드 나중
2. 불필요한 파일 제외 (.dockerignore)
.dockerignore 파일을 사용하여 빌드 컨텍스트(Build Context)에서 불필요한 파일을 제외한다
# Git
.git
.gitignore
# Node.js
node_modules
npm-debug.log
# OS
.DS_Store
# Secrets
.env
*.key
- 빌드 속도 향상: Docker 데몬으로 전송되는 데이터 양이 줄어듬.
- 보안: .git, .env 파일, AWS/GCP 키 파일 등 민감 정보가 이미지에 실수로 포함되는 것을 원천 차단
- 캐시 효율 증가: 로그 파일처럼 자주 변경되지만 빌드에 필요 없는 파일로인해 COPY 레이어 캐시가 무효화되는 것을 방지
3. 멀티-스테이지 빌드 (Multi-Stage Builds)
멀티-스테이지 빌드란?
Dockerfile 안에서 여러 개의 이미지 단계를 만들어 ‘빌드 단계’와 ‘실행 단계’를 완전히 분리하는 기술

멀티-스테이지 빌드 예제
멀티 스테이지 빌드는 ‘빌드 단계’와 ‘실행 단계’를 명확히 분리한다
# build 단계
FROM node:18 AS build
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
# run 단계
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
- 빌드 단계 (build stage)
- 소스 코드 복사
- npm install
- npm run build 수행
- dist 폴더 생성
- “완성된 결과물(dist)”만 사용함
- 실행 단계 (run stage)
- Nginx와 같은 초경량 이미지 사용
- 빌드 단계에서 만든 dist만 복사
- 빌드 도구(Node.js) 불필요
- 최종 이미지가 매우 작고 빠름
특히 컴파일 언어는 멀티스테이지 빌드를 사용해 JDK, Go SDK, 컴파일러(gcc 등) 같은 빌드 도구와 소스 코드를 제거함으로써 이미지 크기와 보안 리스크를 최소화해야 한다.
4. 최소한의 기본 이미지(Base Image) 사용
Docker Base Image 비교
| 이미지 | 크기 | 특징 | 사용 목적 |
| ubuntu:latest | 약 180MB+ | 전체 OS 패키지 포함, 무겁고 불필요 파일 많음 | 일반적인 개발/테스트 환경 |
| debian:slim | 약 80MB | Ubuntu보다 경량화된 OS | 기본적인 서버 환경 |
| alpine:latest | 약 5MB | 매우 가벼운 리눅스 배포판 | 경량 이미지, 빠른 배포 |
| distroless | 거의 0MB 수준 | 쉘/패키지 관리자 없음, 런타임만 포함 | 보안 강화된 운영 환경 |
| scratch | 0MB | 완전 빈 이미지 | 정적 바이너리 실행 전용 |
- 무거움 → ubuntu → debian → alpine → distroless → scratch (가벼움)
- 보안성 → distroless / scratch가 가장 높음
5. RUN 명령어 통합 및 정리
하나의 RUN 명령어 내에서 패키지 설치와 정리를 동시에 수행한다.
만약 RUN 명령어가 분리되면, 중간 레이어에 불필요한 파일이 영구적으로 저장됩니다.
# ❌ 나쁜 예 (레이어 분리로 비효율)
RUN apt-get update
RUN apt-get install -y curl
RUN rm -rf /var/lib/apt/lists/*
# ✅ 좋은 예 (하나의 레이어로 최적화)
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
6. 루트(Root) 사용자로 실행 금지
위험성
- 컨테이너는 기본적으로 root 권한으로 실행
- 컨테이너 탈출(Breakout) 발생 시
→ 호스트 시스템까지 root 권한 탈취 가능 - 즉, 전체 시스템 침해로 이어질 수 있는 치명적 리스크
해결 방법
- USER 명령어를 사용하여
→ 비루트(non-root) 사용자 생성 및 전환 - 최소 권한 원칙 적용으로 보안 강화
7. CMD와 ENTRYPOINT 명확히 구분
CMD = 기본 명령(기본 옵션 포함)
- 컨테이너가 실행될 때 기본으로 실행할 명령 또는 인자
- docker run에서 덮어쓰기가 쉬움
- “기본값 제공자” 역할
ENTRYPOINT = 고정 실행 파일
- 컨테이너가 항상 실행해야 하는 주 실행 파일
- docker run에서 전달하는 인자는 ENTRYPOINT의 인자(args)로 들어감
- “메인 실행 파일 고정” 역할
# 실행할 명령 고정
ENTRYPOINT ["python", "app.py"]
# 기본 인자 제공 (docker run 시 덮어쓸 수 있음)
CMD ["--mode=production"]
8. 민감 정보 관리
Dockerfile에 비밀번호, 토큰, 키 파일 등 민감 정보를 절대 하드코딩하지않는다
# ❌ 잘못된 방법 (이미지에 비밀정보 포함됨)
ENV API_KEY=your-secret-key
COPY config.json /app/config.json
ARG SECRET_KEY=your-secret-key # docker history로 확인 가능
# ----------------------------------------
# ✅ 안전한 방법
# 1️⃣ Build-time (빌드 시에만 사용, 이미지에 저장되지 않음)
# BuildKit 사용 필요
# docker build --secret id=mysecret,src=secret.txt .
# Dockerfile 내부:
# RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
# ----------------------------------------
# 2️⃣ Run-time (컨테이너 실행 시 주입)
# 환경 변수 방식
# docker run -e API_KEY=your-secret-key my-app
# 파일 마운트 방식
# docker run -v ./secret.json:/app/secret.json my-app
# Kubernetes 환경
# Secret 리소스를 사용하여 주입
핵심:비밀정보는 "이미지에 넣지 말고" 실행 시 주입한다
최종 요약 (체크리스트)
- [속도] 의존성 파일(package.json 등)을 먼저 복사하여 캐시 효율을 극대화했는가?
- [속도] .dockerignore를 적용해 빌드 컨텍스트를 최소화했는가?
- [크기] 목적에 맞는 최소 베이스 이미지(alpine, distroless)를 선택했는가?
- [크기] 빌드와 실행 단계를 분리하는 멀티스테이지 빌드를 적용했는가?
- [크기] 패키지 설치와 정리(install && rm)를 하나의 레이어로 묶었는가?
- [보안] 컨테이너를 non-root 사용자로 실행하도록 설정했는가?
- [보안] API 키, 설정 파일 등 민감 정보가 이미지에 포함되지 않도록 분리했는가?
- [관리] ENTRYPOINT와 CMD를 역할에 맞게 구분하여 설계했는가?
본 글은 강의 내용을 기반으로 학습한 내용을 정리한 글입니다.
https://skilleat.com/class/?idx=14
Docker Essentials - 감 잡히는 컨테이너 & 도커 : 스킬잇
기술을 가장 쉽고 맛있게 배우는 곳, SkillEat. 클라우드, 쿠버네티스, IT 개념부터 실습까지, 스킬을 맛있게 먹어치우세요!
skilleat.com
'Docker' 카테고리의 다른 글
| 5. Docker의 데이터 관리: Volume 완전 이해 (0) | 2026.05.04 |
|---|---|
| 4-1. 도커 허브(Docker Hub)란? (0) | 2026.04.15 |
| 3-1. [실습] 나만의 유틸리티 이미지 만들기 (Alpine 버전) / no cache관련 (0) | 2026.04.15 |
| 3. Dockerfile의 기본 구조 (1) | 2026.04.15 |
| 2-3. [실습] 이미지 관리 및 정리 실습 (0) | 2026.04.15 |