Dockerfile 최적화 실전: 이미지 크기를 80% 줄이는 5가지 방법

터미널의 진행 표시줄이 이미 30분째 Pushing to registry에서 멈춰 있었습니다.
3.2GB.
첫 Node.js 애플리케이션의 Docker 이미지는 인터넷의 튜토리얼을 따라 Dockerfile을 한 단계씩 작성해 빌드에는 성공했지만, 크기가 이 정도일 줄은 전혀 예상하지 못했습니다. 다음 날 아침 동료가 Slack에서 물었습니다. “그 이미지에 운영체제 전체를 넣은 거 아니야? 내 노트북 저장 공간이 거의 다 찼어.”
Ubuntu 베이스 이미지 때문일까요? node_modules 때문일까요? 아니면 빌드 도구 때문일까요? 어쨌든 결과는 간단한 API 서비스의 이미지가 프로젝트 코드 전체보다 50배나 컸습니다. Docker 공식 문서와 모범 사례를 연구한 끝에 이 3.2GB짜리 괴물을 180MB로 줄였습니다. 94% 감소한 것입니다.
이 글에서는 그 과정에서 찾은 가장 효과적인 5가지 방법을 소개합니다. 어떻게 하는지만 설명하는 데 그치지 않고 왜 효과가 있는지도 살펴보겠습니다. 원리를 이해하는 것이 명령을 외우는 것보다 훨씬 중요하기 때문입니다.
먼저 Docker 이미지가 왜 이렇게 큰지 이해하기
최적화 방법을 알아보기 전에 문제의 근본 원인부터 이해해야 합니다.
Docker 이미지는 여러 레이어로 구성됩니다. 각 RUN, COPY, ADD 명령은 새로운 파일 시스템 레이어를 만듭니다. 이 레이어들이 쌓여 최종 이미지가 됩니다. 핵심은 각 레이어에는 추가만 가능하고 삭제는 불가능하다는 점입니다.
예를 들어 Dockerfile에 다음과 같이 작성했다고 해 보겠습니다.
RUN apt-get update
RUN apt-get install -y build-essential
RUN rm -rf /var/lib/apt/lists/*
겉보기에는 마지막 줄에서 apt 캐시를 삭제했습니다. 하지만 실제로 그 캐시는 두 번째 레이어에 영구적으로 저장되어 있습니다. 세 번째 레이어는 “이 파일들이 삭제되었다”고 표시할 뿐, 해당 데이터는 여전히 이미지 안에서 공간을 차지합니다.
이사할 때 방을 정리할 때마다 사진을 찍는 것과 비슷합니다. 마지막에 쓰레기를 버렸더라도 쓰레기가 찍힌 사진까지 모두 가져가야 하는 셈입니다. 다소 어리석어 보이지만 Docker의 Copy-on-Write 메커니즘은 이런 방식으로 동작합니다.
최적화하지 않은 이미지에 docker history를 실행하면 여러 레이어의 크기를 합한 값이 실제 필요한 파일 크기보다 훨씬 크다는 사실을 확인할 수 있습니다.
또 하나 놓치기 쉬운 점은 베이스 이미지 선택이 이미지 크기의 하한선을 직접 결정한다는 것입니다. ubuntu:20.04만 해도 72MB이고, node:16은 무려 1.09GB입니다. 완전한 Debian 시스템을 기반으로 하며 사용하지 않을 가능성이 큰 여러 시스템 도구까지 포함하기 때문입니다.
이 원리를 이해하면 최적화 방향도 명확해집니다. 레이어 수를 줄이고 더 가벼운 베이스 이미지를 선택하며 설치와 정리를 같은 레이어에서 완료해야 합니다.
방법 1: 적절한 베이스 이미지를 선택해 출발부터 앞서가기
베이스 이미지 선택은 집을 살 때 입지를 고르는 것과 비슷합니다. 처음부터 잘못 고르면 이후에 아무리 잘 꾸며도 한계가 있습니다.
먼저 몇 가지 수치를 비교해 보겠습니다.
node:16→ 1.09GBnode:16-slim→ 240MBnode:16-alpine→ 174MBalpine:latest→ 5.6MB
차이가 한눈에 보입니다. 당시 node:16에서 node:16-alpine으로 바꾸자 코드를 전혀 수정하지 않았는데도 이미지가 1.2GB에서 400MB로 바로 줄었습니다.
Alpine Linux란 무엇인가요?
컨테이너 환경을 위해 특별히 설계된 Linux 배포판입니다. 미니멀리즘을 지향하며 가장 핵심적인 구성 요소만 남겼습니다. 표준 glibc 대신 musl libc를 사용하고, 패키지 관리자로 apt 대신 apk를 사용합니다.
장점은 분명합니다.
- 작은 크기(5MB vs Ubuntu의 72MB)
- 높은 보안성(공격 표면 최소화)
- 빠른 시작 속도
하지만 주의할 점도 있습니다.
Alpine의 호환성 함정
musl libc를 사용하기 때문에 일부 사전 컴파일된 바이너리가 실행되지 않을 수 있습니다. 저도 한 번 겪었습니다. 프로젝트가 C++로 작성된 Node.js 네이티브 모듈에 의존하고 있었는데 Alpine에서 바로 library not found 오류가 발생했습니다. 한참 씨름한 뒤에야 libc 문제라는 것을 알아냈습니다.
따라서 실용적인 권장 순서는 다음과 같습니다.
- 먼저 Alpine 변형(
-alpine접미사)을 사용해 봅니다. - 호환성 문제가 생기면 Debian을 기반으로 하되 많은 패키지를 제거한
-slim변형으로 바꿉니다. - 그래도 해결되지 않을 때 표준 이미지를 사용합니다. 하지만 실제로 이런 경우는 많지 않습니다.
코드 수정은 아주 간단합니다.
# 최적화 전
FROM node:16
# 최적화 후
FROM node:16-alpine
이 한 줄만으로 800MB를 절약했습니다.
효과를 확인하는 방법
빌드가 끝난 뒤 다음 명령을 실행합니다.
docker images your-image-name
SIZE 열을 확인하세요. 여전히 크다면 베이스 이미지 외에도 다른 문제가 있다는 뜻이므로 계속 읽어 보세요.
방법 2: RUN 명령을 병합해 이미지 레이어 줄이기
이 방법은 이해하기 쉽지만 실제로 사용할 때 자주 놓칩니다.
앞서 설명했듯이 각 RUN 명령은 하나의 레이어를 만듭니다. 그리고 핵심은 같은 레이어 안에서만 파일 삭제가 유효하다는 점입니다.
다음은 잘못된 예입니다.
# 잘못된 예(3개 레이어 생성)
RUN apt-get update
RUN apt-get install -y python3 gcc
RUN rm -rf /var/lib/apt/lists/*
이렇게 작성하면 보통 수십 MB에 달하는 apt 캐시가 두 번째 레이어에 저장됩니다. 세 번째 레이어의 삭제 작업은 “이 파일들은 이제 없다”고 표시할 뿐, 실제 데이터는 이미지에 남습니다.
올바른 방법은 &&로 명령을 연결하는 것입니다.
# 올바른 방법(레이어 1개만 생성)
RUN apt-get update && \
apt-get install -y python3 gcc && \
rm -rf /var/lib/apt/lists/*
이렇게 하면 설치와 정리가 같은 레이어에서 완료되므로 실제로 데이터가 삭제됩니다.
백슬래시의 역할
여기서 \에 주목하세요. 긴 명령을 여러 줄로 나눠 가독성과 유지보수성을 높여 줍니다. 이 문자를 사용하지 않으면 모든 내용이 한 줄에 몰려 보기 좋지 않습니다.
병합 여부를 판단하는 방법
모든 RUN 명령을 합쳐야 하는 것은 아닙니다. 간단한 판단 기준은 다음과 같습니다.
- 병합해야 하는 경우: 설치+정리, 다운로드+압축 해제+압축 파일 삭제
- 병합하지 말아야 하는 경우: 논리적으로 관련 없는 작업, 자주 바뀌는 단계(빌드 캐시를 무효화할 수 있음)
예를 들면 다음과 같습니다.
# 좋은 레이어 구성
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
RUN npm install
RUN npm run build
시스템 의존성 설치를 한 레이어로, npm install을 한 레이어로(package.json은 자주 변경되기 때문), 빌드를 또 한 레이어로 분리합니다. 이렇게 하면 package.json을 수정하더라도 앞의 시스템 의존성 레이어는 캐시를 사용하므로 다시 실행할 필요가 없습니다.
실제 측정 결과, 한 프로젝트에서는 원래 12개였던 RUN 명령을 4개로 병합한 뒤 이미지가 520MB에서 320MB로 줄었습니다.
방법 3: 멀티 스테이지 빌드로 필요한 것만 가져가기
멀티 스테이지 빌드(Multi-stage Build)는 Docker 이미지 크기를 줄이는 가장 효과적인 도구입니다. 단연 최고입니다.
핵심 아이디어는 매우 간단합니다. 빌드와 실행을 분리하는 것입니다.
생각해 보세요. Go 프로그램을 컴파일하려면 수백 MB에 달하는 전체 Go 도구 체인이 필요하지만, 컴파일된 바이너리는 10MB에 불과할 수 있습니다. 최종 이미지에 Go 도구 체인까지 함께 넣는 것은 낭비일 뿐입니다.
멀티 스테이지 빌드는 이 문제를 해결합니다. 하나의 Dockerfile에서 여러 단계를 정의할 수 있습니다. 첫 번째 단계에서는 빌드하고, 두 번째 단계에서는 빌드 결과물만 복사합니다.
Node.js 예제를 보겠습니다.
# === 빌드 단계 ===
FROM node:16-alpine AS builder
WORKDIR /app
# 의존성 파일 복사
COPY package*.json ./
RUN npm install
# 소스 코드 복사 및 빌드
COPY . .
RUN npm run build
# === 실행 단계 ===
FROM node:16-alpine
WORKDIR /app
# 필요한 파일만 복사
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["node", "dist/index.js"]
핵심은 COPY --from=builder입니다. 첫 번째 단계(builder)의 파일을 두 번째 단계로 복사합니다. 최종 이미지에는 두 번째 단계의 내용만 포함되고, 첫 번째 단계의 모든 중간 결과물은 버려집니다.
멀티 스테이지 빌드를 사용해야 하는 경우
대표적인 사례는 다음과 같습니다.
- 컴파일 언어: Go, Rust, C++처럼 컴파일러가 필요한 언어
- 프론트엔드 프로젝트: TypeScript 컴파일, Webpack 번들링
- 빌드 도구가 필요한 프로젝트: 예를 들어 일부 라이브러리를 컴파일하는 데 gcc가 필요한 Python 프로젝트
제 Node.js 프로젝트는 TypeScript를 JavaScript로 변환하는 전형적인 사례였습니다. node_modules 안의 @types까지 포함한 소스 코드는 400MB였지만 컴파일된 dist 폴더는 2MB에 불과했습니다. 멀티 스테이지 빌드를 사용해 400MB에서 220MB로 줄였습니다.
쉽게 빠지는 함정
실행 단계에서도 “어차피 의존성을 설치해야 하니까”라고 생각하며 npm install을 실행하는 경우가 있습니다. 그렇게 하지 마세요! 그러면 devDependencies(개발 의존성)까지 설치되어 공간을 낭비합니다.
올바른 방법은 빌드 단계에서 컴파일에 필요한 개발 의존성을 포함해 npm install을 실행한 다음, 전체 node_modules를 실행 단계로 복사하는 것입니다. 또는 더 정확하게 다음과 같이 처리할 수 있습니다.
# 빌드 단계
RUN npm install
# 실행 단계
RUN npm install --production
프로덕션 의존성만 설치하면 크기를 30~40% 더 줄일 수 있습니다.
멀티 스테이지 빌드는 처음 보면 다소 복잡할 수 있지만 이해하고 나면 정말 우아한 설계라고 느끼게 됩니다. 여행 짐을 싸는 것과 같습니다. 집에서는(빌드 단계) 모든 물건을 펼쳐 놓고 정리하지만, 비행기에 탈 때는(실행 단계) 가방 속에 꼭 필요한 것만 가져갑니다.
방법 4: .dockerignore로 불필요한 파일 제외하기
.dockerignore 파일의 역할은 .gitignore와 비슷하지만 많은 사람이 이를 간과합니다.
Dockerfile에 COPY . .를 작성하면 Docker는 전체 디렉터리를 빌드 컨텍스트로 Docker daemon에 전송합니다. 프로젝트 디렉터리에 수백 MB의 node_modules, .git 기록, 테스트 파일, 로그가 있다면 이 모든 것이 전송되고 복사됩니다.
결국 사용하지 않더라도 빌드 과정이 느려지고, 이미지에 들어가서는 안 되는 파일(예: .env 파일 속 비밀 값)을 실수로 포함하기 쉽습니다.
해결 방법은 프로젝트 루트 디렉터리에 .dockerignore 파일을 만드는 것입니다.
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.env.local
README.md
.vscode
.idea
*.md
.DS_Store
coverage/
.pytest_cache/
__pycache__/
*.pyc
dist-local/
핵심 원칙
다음 항목을 추가하세요.
- 기존 의존성 디렉터리: node_modules, vendor, target 등(빌드 시 어차피 다시 설치함)
- 개발 도구 설정: .vscode, .idea, .editorconfig
- Git 관련 파일: .git, .gitignore(.git 폴더는 종종 수십 MB에 달함)
- 문서와 안내 파일: README, CHANGELOG, docs/
- 민감한 정보: .env, credentials.json, *.pem
이전에 한 프로젝트에서 .git을 제외하는 것을 깜빡해 빌드할 때마다 .git 폴더의 500MB에 달하는 이력까지 전송한 적이 있습니다. .dockerignore에 추가하자 빌드 시간이 2분에서 30초로 줄었고 이미지 크기도 상당히 감소했습니다.
실용적인 팁
어떤 파일이 복사되는지 확실하지 않다면 먼저 한 번 빌드한 뒤 컨테이너에 들어가 확인하세요.
docker run --rm -it your-image sh
ls -lah
들어가서는 안 되는 파일을 발견하면 .dockerignore에 추가하면 됩니다.
간단해 보이지만 즉각적인 효과를 얻을 수 있는 방법입니다. 특히 프론트엔드 프로젝트는 dist 디렉터리, node_modules, .cache를 합치면 쉽게 몇 GB가 됩니다.
방법 5: 패키지 관리자 캐시 정리하기
여러 패키지 관리자(npm, pip, apt, apk)는 패키지를 설치한 뒤 캐시를 남깁니다. 로컬 개발 환경에서는 이 캐시가 이후 설치 속도를 높여 주지만 Docker 이미지 안에서는 공간만 차지합니다.
문제는 많은 사람이 캐시를 정리해야 한다는 사실은 알아도 방법을 잘못 사용한다는 것입니다.
반드시 같은 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/*
패키지 관리자마다 정리 방법이 다르므로 목록으로 정리해 보겠습니다.
Node.js (npm/yarn)
# npm - 전통적인 방식
RUN npm install && \
npm cache clean --force
# npm - 더 간단한 방식(캐시 비활성화)
RUN npm install --no-cache
# yarn
RUN yarn install && \
yarn cache clean
Python (pip)
# 가장 간단한 방법: 설치할 때 캐시를 생성하지 않음
RUN pip install --no-cache-dir -r requirements.txt
# 또는 설치 후 정리
RUN pip install -r requirements.txt && \
rm -rf ~/.cache/pip
Alpine (apk)
# Alpine의 apk가 제공하는 매우 편리한 옵션
RUN apk add --no-cache package-name
# 또는 직접 정리
RUN apk add package-name && \
rm -rf /var/cache/apk/*
Debian/Ubuntu (apt)
RUN apt-get update && \
apt-get install -y package-name && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
실제 측정 데이터
같은 의존성을 사용하는 Python 프로젝트를 테스트한 결과는 다음과 같았습니다.
- 캐시를 정리하지 않음: 450MB
- 캐시 정리: 320MB
--no-cache-dir사용: 310MB(가장 깔끔함)
140MB나 차이가 납니다! 옵션 하나만 추가했을 뿐입니다.
개발 환경 vs 프로덕션 환경
한 가지 세부 사항이 있습니다. 프로덕션 환경의 이미지에서는 --production 또는 --no-dev를 사용해 꼭 필요한 패키지만 설치해야 합니다. 개발 의존성은 일반적으로 전체 크기의 30~50%를 차지합니다.
# Node.js 프로덕션 의존성만 설치
RUN npm install --production
# Python 필수 패키지만 설치(requirements.txt에서 구분)
RUN pip install --no-cache-dir -r requirements-prod.txt
이 5가지 방법을 함께 사용하면 효과는 덧셈이 아니라 곱셈처럼 커집니다. 제 3.2GB 프로젝트를 최종적으로 180MB까지 줄일 수 있었던 것도 이 방법을 모두 적용했기 때문입니다.
전체 사례: Node.js 애플리케이션 최적화 전 과정
이론을 충분히 살펴봤으니 실제 최적화 사례를 보겠습니다. 이전에 작업했던 Express API 서비스의 Dockerfile이 변화한 과정입니다.
최적화 전(1.2GB)
FROM node:16
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]
간단하고 거칠지만 이미지가 매우 큽니다.
1단계 최적화: Alpine 베이스 이미지로 변경(→ 400MB, -67%)
FROM node:16-alpine
WORKDIR /app
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/index.js"]
한 줄만 바꿨는데도 800MB를 절약했습니다.
2단계 최적화: .dockerignore 추가(→ 380MB, -5%)
.dockerignore를 만듭니다.
node_modules
.git
*.md
.env
coverage
크기 감소 폭은 작아 보이지만 빌드 속도는 훨씬 빨라졌습니다.
3단계 최적화: 멀티 스테이지 빌드(→ 220MB, -42%)
# 빌드 단계
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 실행 단계
FROM node:16-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 3000
CMD ["node", "dist/index.js"]
이 단계의 효과가 가장 컸습니다. 빌드 과정의 모든 중간 파일을 버렸기 때문입니다.
4단계 최적화: 프로덕션 의존성만 설치하고 캐시 정리(→ 180MB, -18%)
# 빌드 단계
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 실행 단계
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production --no-cache && \
npm cache clean --force
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]
최종 버전은 180MB로, 처음의 1.2GB보다 85% 작아졌습니다.
최적화 로드맵 요약
1.2GB (node:16 원본)
↓ Alpine으로 변경
400MB (-67%)
↓ .dockerignore
380MB (-5%)
↓ 멀티 스테이지 빌드
220MB (-42%)
↓ 프로덕션 의존성+정리
180MB (-18%)
────────────────
총 85% 감소
어떤 방법의 효과가 가장 좋았나요?
이 사례에서 다음과 같은 결론을 얻을 수 있습니다.
- Alpine 베이스 이미지: 효과가 즉시 나타나고 가장 쉽게 시작할 수 있음
- 멀티 스테이지 빌드: 효과가 가장 크지만 약간의 학습이 필요함
- 정리와 프로덕션 의존성: 작은 세부 최적화가 모여 큰 차이를 만듦
시간이 부족하다면 앞의 두 가지부터 적용하세요.
결론
5가지 방법을 다시 정리해 보겠습니다.
- Alpine 베이스 이미지 선택 - 시작부터 크기 줄이기
- RUN 명령 병합 - 같은 레이어에서 설치와 정리 완료하기
- 멀티 스테이지 빌드 - 런타임에 필요한 파일만 가져가기
- .dockerignore - 불필요한 파일과 민감한 정보 제외하기
- 패키지 관리자 캐시 정리 -
--no-cache계열 옵션 사용하기
이 방법들은 서로 독립적이지 않으며 함께 사용할 때 효과가 가장 좋습니다. 제 경험으로는 Alpine과 멀티 스테이지 빌드가 이미지 크기 문제의 80%를 해결하고, 나머지 20%는 정리와 제외 설정으로 해결할 수 있습니다.
지금 바로 실행하기
문제가 생길 때까지 기다렸다가 최적화하지 마세요. 기존 프로젝트 하나를 골라 이 5가지 방법을 적용해 보세요.
- Alpine으로 바꿀 수 있는지 확인합니다(대부분 가능함).
- Dockerfile에서 설치와 정리가 나뉘어 있는지 확인하고, 그렇다면 병합합니다.
- 멀티 스테이지 빌드를 추가합니다(컴파일 프로젝트라면 필수).
- .dockerignore 파일을 만듭니다.
- 패키지 관리자에
--no-cache옵션을 추가합니다.
빌드가 끝나면 docker images로 전후를 비교해 얼마나 줄었는지 확인하세요.
더 나아갈 방향
더 깊이 알아보고 싶다면 다음 주제를 살펴보세요.
- Docker BuildKit의 캐시 마운트 기능
- Distroless 이미지(Google에서 제작, Alpine보다 더 작음)
- 이미지 보안 스캔 도구(Trivy, Grype)
Dockerfile 최적화는 일회성 작업이 아니라 지속적인 개선 과정입니다. 빌드할 때마다 이미지 크기를 확인하는 습관을 들이면 자연스럽게 크기를 관리할 수 있습니다.
여러분의 이미지가 계속 더 가벼워지기를 바랍니다.
Dockerfile 최적화 전체 과정
5가지 방법으로 이미지 크기를 80% 줄여 3.2GB에서 180MB로, 총 94% 감소
⏱️ Estimated time: 1 hr
- 1
Step 1: 방법 1: Alpine 베이스 이미지 사용
Alpine 베이스 이미지의 장점:
• 작은 크기(5MB에 불과하며 Ubuntu는 200MB)
• 높은 보안성(공격 표면 최소화)
• 간단한 패키지 관리자(apk)
• 프로덕션 환경에 적합
Alpine 사용법:
• FROM ubuntu:20.04를 FROM alpine:latest로 변경
• 패키지 설치 시 apt-get 대신 apk 사용: apk add --no-cache nodejs npm
• 이미지 크기를 200MB에서 5MB로 줄여 95% 감소 - 2
Step 2: 방법 2~3: RUN 명령 병합과 멀티 스테이지 빌드
RUN 명령 병합:
• 여러 RUN 명령을 하나로 합쳐 이미지 레이어 수를 줄임
• &&로 명령을 연결하고 \로 줄을 바꿔 가독성을 높임
• 마지막에 캐시 정리:
RUN apt-get update && \
apt-get install -y nodejs npm && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
멀티 스테이지 빌드:
• 첫 번째 단계에서는 완전한 빌드 이미지로 컴파일
• 두 번째 단계에서는 최소 런타임 이미지로 실행
• 런타임에 필요한 파일만 남기고 빌드 도구는 포함하지 않음
• 이미지 크기를 크게 줄임 - 3
Step 3: 방법 4~5: .dockerignore 설정과 캐시 정리
.dockerignore 설정:
• node_modules, .git, .env, dist 등 불필요한 파일 제외
• 빌드 컨텍스트 크기를 줄여 빌드 속도 향상
캐시 정리:
• --no-cache 옵션 사용: apk add --no-cache
• apt 캐시 정리: apt-get clean && rm -rf /var/lib/apt/lists/*
• npm 캐시 정리: npm cache clean --force
• 이미지 크기 감소
FAQ
Dockerfile을 최적화하는 5가지 방법은 무엇인가요?
1) Alpine 베이스 이미지 사용(Ubuntu 200MB에서 Alpine 5MB로, 95% 감소)
2) RUN 명령 병합(이미지 레이어 수와 이미지 크기 감소)
3) 멀티 스테이지 빌드(런타임에 필요한 파일만 남기고 빌드 도구는 포함하지 않음)
4) .dockerignore 설정(node_modules, .git 등 불필요한 파일 제외)
5) 캐시 정리(apt-get clean, npm cache clean 등)
최적화 효과:
• Node.js 애플리케이션 이미지를 3.2GB에서 180MB로 줄여 94% 감소
• 1.2GB에서 180MB로 줄여 85% 감소
• 배포 시간을 30분에서 몇 분으로 단축
• 이미지 크기를 크게 줄임
Alpine 베이스 이미지가 더 좋은 이유는 무엇인가요?
• 작은 크기(5MB에 불과하며 Ubuntu는 200MB)
• 높은 보안성(공격 표면 최소화)
• 간단한 패키지 관리자(apk)
• 프로덕션 환경에 적합
Alpine 사용법:
• FROM ubuntu:20.04를 FROM alpine:latest로 변경
• 패키지 설치 시 apt-get 대신 apk 사용: apk add --no-cache nodejs npm
• 이미지 크기를 200MB에서 5MB로 줄여 95% 감소
RUN 명령은 어떻게 병합하나요?
• 여러 RUN 명령을 하나로 합쳐 이미지 레이어 수를 줄임
• &&로 명령 연결
• \로 줄을 바꿔 가독성을 높임
• 마지막에 캐시 정리:
RUN apt-get update && \
apt-get install -y nodejs npm && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
이렇게 하면 이미지 레이어 수와 이미지 크기를 줄이고 빌드 효율을 높일 수 있습니다.
Dockerfile 최적화의 모범 사례는 무엇인가요?
• Alpine 베이스 이미지 사용
• RUN 명령 병합
• 멀티 스테이지 빌드 사용
• .dockerignore 설정
• 캐시 정리
• 베이스 이미지 버전을 정기적으로 업데이트
이 방법들은 서로 독립적이지 않으며 함께 사용할 때 효과가 가장 좋습니다. 제 경험으로는 Alpine과 멀티 스테이지 빌드가 이미지 크기 문제의 80%를 해결하고, 나머지 20%는 정리와 제외 설정으로 해결할 수 있습니다.
Dockerfile 최적화는 일회성 작업이 아니라 지속적으로 개선해야 하는 과정입니다. 빌드할 때마다 이미지 크기를 확인하는 습관을 들이면 자연스럽게 크기를 관리할 수 있습니다.
3분 읽기 · 게시일: 2025년 12월 17일 · 수정일: 2026년 9월 4일
Docker 실전 가이드
검색으로 들어왔다면 같은 시리즈의 이전 글이나 다음 글로 이동하는 것이 가장 빠릅니다.
이전
Dockerfile 입문 가이드: 첫 Docker 이미지 처음부터 만들기(예제 포함)
Dockerfile 작성법과 FROM, RUN, COPY 등 핵심 명령어를 단계별로 설명하고 초보자가 자주 겪는 문제를 피하는 방법을 Node.js 실전 예제와 함께 알아봅니다.
33편 중 3편
다음
Docker 멀티 스테이지 빌드 실전: Go/Java/Rust 이미지를 GB에서 MB로 극한 경량화
Docker 멀티 스테이지 빌드를 깊이 있게 살펴보고, 실제 사례를 통해 Go 이미지는 98%, Java 이미지는 86%, Rust 이미지는 99.4% 줄이는 방법을 설명합니다. 완전한 Dockerfile 코드와 시행착오 경험도 담았습니다.
33편 중 5편



댓글
GitHub로 로그인하여 댓글을 남기세요