๐Ÿณ ๋„์ปค ์ด๋ฏธ์ง€ ์ตœ์ ํ™” ์™„์ „ ๊ฐ€์ด๋“œ: 1.22GB์—์„œ 57MB๊นŒ์ง€ โ€“ 95% ์šฉ๋Ÿ‰ ์ ˆ๊ฐ ๋น„๊ฒฐ

sangjinsuยท2025๋…„ 6์›” 15์ผ

A Step-by-Step Guide to Docker Image Optimisation: Reduce Size by Over 95%

์‹œ์ž‘: ์ถฉ๊ฒฉ์ ์ด์—ˆ๋˜ ์ดˆ๊ธฐ ๊ฒฝํ—˜

โ€œNode.js ์•ฑ์„ ์ฒ˜์Œ ํ”„๋กœ๋•์…˜์— Docker๋กœ ๋ฐฐํฌํ–ˆ์„ ๋•Œ, ์ด๋ฏธ์ง€ ํฌ๊ธฐ๊ฐ€ ๋ฌด๋ ค 1.2GB์˜€์Šต๋‹ˆ๋‹ค. ๋‹จ ๋ช‡ ํ‚ฌ๋กœ๋ฐ”์ดํŠธ์งœ๋ฆฌ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ์— ๋ง์ด์ฃ .โ€

๋ถˆํ•„์š”ํ•˜๊ฒŒ ํฐ ์ด๋ฏธ์ง€๋Š” ์ €์žฅ ๊ณต๊ฐ„๋ฟ ์•„๋‹ˆ๋ผ, ๋ฐฐํฌ ์ง€์—ฐ, ๋น„์šฉ ์ฆ๊ฐ€, ๋ณด์•ˆ ์œ„ํ—˜๊นŒ์ง€ ์ดˆ๋ž˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ช‡ ์ผ๊ฐ„์˜ ์‹œํ–‰์ฐฉ์˜ค๋ฅผ ๊ฑฐ์ณ, ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์ตœ๋Œ€ 95% ์ค„์ด๋Š” ๊ธฐ๋ฒ•๋“ค์„ ํ„ฐ๋“ํ–ˆ์Šต๋‹ˆ๋‹ค.

โ€œ์ˆ˜์ฒœ ๊ฐœ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฐฐํฌํ•  ๋•Œ๋Š” ๋ชจ๋“  ๋ฉ”๊ฐ€๋ฐ”์ดํŠธ๊ฐ€ ์ค‘์š”ํ•˜๋‹ค.โ€

์ด ๊ธ€์—์„œ๋Š” ๊ทธ ์—ฌ์ •์„ ๋‹จ๊ณ„๋ณ„๋กœ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.


1๋‹จ๊ณ„: ๋น„๋Œ€ํ•œ ๊ธฐ๋ณธ Dockerfile

FROM node:latest
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]

์ด๋ ‡๊ฒŒ ๋นŒ๋“œํ•œ ๊ฒฐ๊ณผ: 1.22GB

ํ…Œ์ŠคํŠธ๋Š” ์„ฑ๊ณตํ–ˆ์ง€๋งŒ, ์ด๋ฏธ์ง€๊ฐ€ ๋„ˆ๋ฌด ๋น„๋Œ€ํ•ฉ๋‹ˆ๋‹ค.


2๋‹จ๊ณ„: slim ๋ฒ ์ด์Šค ์ด๋ฏธ์ง€ ์‚ฌ์šฉ

FROM node:slim
...
  • ํ•„์š” ์—†๋Š” ํŒจํ‚ค์ง€ ์ œ๊ฑฐ
  • ๋ณด์•ˆ ์ทจ์•ฝ์  ๊ฐ์†Œ
  • ๋นŒ๋“œ/ํ’€(run) ์†๋„ ํ–ฅ์ƒ

๊ฒฐ๊ณผ: ์•ฝ 345MB ๊ฐ์†Œ, 71% ์ ˆ๊ฐ


3๋‹จ๊ณ„: Alpine ์ด๋ฏธ์ง€๋กœ ์ถ”๊ฐ€ ๊ฐ๋Ÿ‰

FROM node:alpine
...
  • musl libc, BusyBox ๋“ฑ์œผ๋กœ ๊ตฌ์„ฑ๋œ ์ดˆ๊ฒฝ๋Ÿ‰ ๋ฆฌ๋ˆ…์Šค
  • ๋ฒ ์ด์Šค ์ด๋ฏธ์ง€ ์•ฝ 5MB

๊ฒฐ๊ณผ: ์ด๋ฏธ์ง€ ์•ฝ 258MB, ์ด 78% ๊ฐ์†Œ

๋‹จ, Alpine์˜ libc ํ˜ธํ™˜์„ฑ ์ด์Šˆ๋กœ ์ผ๋ถ€ ๋„ค์ดํ‹ฐ๋ธŒ ๋ชจ๋“ˆ์ด ๋ถˆ์•ˆ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


4๋‹จ๊ณ„: ๋ฉ€ํ‹ฐ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ + .dockerignore

.dockerignore

node_modules
npm-debug.log
tests
coverage
Dockerfile
.dockerignore
.env
*.md

Dockerfile

FROM node:alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .

FROM node:alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/*.js ./
...
  • ๋นŒ๋“œ ๋„๊ตฌ์™€ ํ…Œ์ŠคํŠธ/๊ฐœ๋ฐœ ํŒŒ์ผ ์ œ์™ธ
  • ๋Ÿฐํƒ€์ž„์— ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ํฌํ•จ

๊ฒฐ๊ณผ: ์ด๋ฏธ์ง€ ์•ฝ 178MB, 85% ์ ˆ๊ฐ


5๋‹จ๊ณ„: Distroless ์ด๋ฏธ์ง€ ์ ์šฉ

FROM node:22-alpine AS builder
...
FROM gcr.io/distroless/nodejs22
WORKDIR /app
COPY --from=builder /app /app
CMD ["index.js"]
  • ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์ž์™€ ์‰˜ ์—†์Œ โ†’ ๊ณต๊ฒฉ ํ‘œ๋ฉด ๊ฐ์†Œ
  • ๋ถˆํ•„์š”ํ•œ OS ๋ ˆ์ด์–ด ์ œ๊ฑฐ

๊ฒฐ๊ณผ: ์ด๋ฏธ์ง€ 143MB, 88.2% ์ ˆ๊ฐ


6๋‹จ๊ณ„: ์ •์  ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ํŒจํ‚ค์ง•

Dockerfile

FROM node:alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
RUN npm install -g pkg
COPY . .
RUN pkg --targets node16-alpine-x64 index.js -o app

FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/app .
EXPOSE 3000
CMD ["./app"]
  • Node ๋Ÿฐํƒ€์ž„ ํฌํ•จ ๋‹จ์ผ ์‹คํ–‰ ํŒŒ์ผ ์ƒ์„ฑ
  • scratch or minimal Alpine ์‚ฌ์šฉ ๊ฐ€๋Šฅ

๊ฒฐ๊ณผ: ์ด๋ฏธ์ง€ ์•ฝ 57MB, ์›๋ณธ์˜ 4.7%

์ฃผ์˜: ๋™์  ๋ชจ๋“ˆ ๋กœ๋”ฉ ๋“ฑ ๋ณต์žกํ•œ ๊ตฌ์กฐ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


์ถ”๊ฐ€: Docker Slim ๋„๊ตฌ ์†Œ๊ฐœ

  • ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ ์‚ฌ์šฉ๋œ ๋ถ€๋ถ„๋งŒ ์ถ”์  ํ›„ ์ด๋ฏธ์ง€์—์„œ ์ œ๊ฑฐ
  • ๊ฒฐ๊ณผ: 1.22GB โ†’ 123MB

์ฃผ์˜: ํŠธ๋ ˆ์ด์‹ฑ ์•ˆ ๋œ ์˜ˆ์™ธ ๊ฒฝ๋กœ์˜ ํŒŒ์ผ์ด ๋ˆ„๋ฝ๋˜๋ฉด ํ”„๋กœ๋•์…˜ ์˜ค๋ฅ˜ ๊ฐ€๋Šฅ


๊ทธ ์™ธ ๊ธฐ๋ฒ• & ๋„๊ตฌ

  1. ๋ ˆ์ด์–ด ์ตœ์ ํ™”: RUN ๋ช…๋ น์€ ๋ฌถ์–ด์„œ ์ ๊ณ  ์บ์‹œ ์ œ๊ฑฐ
  2. BuildKit ์บ์‹œ ๋งˆ์šดํŠธ: --mount=type=cache,target=/root/.npm
  3. ๋ถ„์„ ๋„๊ตฌ
    • dive: ์ด๋ฏธ์ง€ ๋ ˆ์ด์–ด ์‹œ๊ฐ ํƒ์ƒ‰
    • Docker Scout: ๊ณต์‹ ์ด๋ฏธ์ง€ ๋ถ„์„/SBOM
    • Buildpacks: ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค ๊ธฐ๋ฐ˜ ์ž๋™ ์ด๋ฏธ์ง€ ๋นŒ๋“œ

โœ… ์ตœ์ข… ์š”์•ฝ

๋‹จ๊ณ„์ด๋ฏธ์ง€ ํฌ๊ธฐ
๊ธฐ๋ณธ1.22โ€ฏGB
Slimโ€“345โ€ฏMB
Alpine~258โ€ฏMB
๋ฉ€ํ‹ฐ์Šคํ…Œ์ด์ง€~178โ€ฏMB
Distroless~143โ€ฏMB
Static ๋ฐ”์ด๋„ˆ๋ฆฌ~57โ€ฏMB
Dockerโ€‘Slim~123โ€ฏMB

โœจ ๋งˆ๋ฌด๋ฆฌ

1.22GB์—์„œ 57MB๊นŒ์ง€, ๋‹จ์ˆœ ์ €์žฅ ์ ˆ๊ฐ์ด ์•„๋‹Œ:

  • ๋ฐฐํฌ ์†๋„ ํ–ฅ์ƒ
  • ์ธํ”„๋ผ ๋น„์šฉ โ†“
  • ๊ณต๊ฒฉ ํ‘œ๋ฉด ์ตœ์†Œํ™”
  • ์ž๋™ํ™”ยทํ™•์žฅ์„ฑ ๊ฐ•ํ™”

๊ฐ ๋‹จ๊ณ„๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์œ ์šฉํ•˜๋ฉฐ, ๋ชจ๋“  ๋‹จ๊ณ„์˜ ๋„์ž…์ด ๋ถˆ๊ฐ€๋Šฅํ•ด๋„ ์ผ๋ถ€๋งŒ ์ ์šฉํ•ด๋„ ํฐ ํšจ๊ณผ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ปจํ…Œ์ด๋„ˆ ์ตœ์ ํ™” ์—ฌ์ •์€ ๊พธ์ค€ํ•œ ํ•™์Šต๊ณผ ๊ฐœ์„ ์˜ ๋ฐ˜๋ณต์ด๋ฉฐ, ํˆฌ์žํ•œ๋งŒํผ์˜ ๊ฐ€์น˜๋ฅผ ํ™•์‹คํ•˜๊ฒŒ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ“”ย ํ•œ ๋ฒˆ์ฏค ์ƒ๊ฐํ•ด๋ณด๋Š” ์ฃผ์ œ๋“ค


1. ๐Ÿ”’ ์ปจํ…Œ์ด๋„ˆ ๋ณด์•ˆ์˜ โ€œShift-Leftโ€ ์ „๋žต

๐ŸŽฏ ์™œ ์ค‘์š”ํ•œ๊ฐ€?

๊ฐœ๋ฐœ ์ดˆ๊ธฐ ๋‹จ๊ณ„๋ถ€ํ„ฐ ๋ณด์•ˆ์„ ๋‚ด์žฌํ™”(Shiftโ€‘Left)ํ•˜๋ฉด, ๋ฐฐํฌ ์ „ํ›„์— ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ณด์•ˆ ์ด์Šˆ๋ฅผ ์‚ฌ์ „์— ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹คย  .

โœ… ์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ

  1. ์ž๋™ํ™”๋œ ์ทจ์•ฝ์  ์Šค์บ”
    • CI/CD ํŒŒ์ดํ”„๋ผ์ธ์— ์ด๋ฏธ์ง€ยท์˜์กด์„ฑยทIaC ์Šค์บ” ๋„๊ตฌ(SAST, DAST, SCA) ํ†ตํ•ฉ .
    • ์˜ˆ: SonarQube, Trivy, Snyk, Dependency-Check.
  2. ๋ณด์•ˆ ๊ฒŒ์ดํŠธ ์„ค์ •
    • ์ทจ์•ฝ์  ์ˆ˜์ค€์— ๋”ฐ๋ผ ๋นŒ๋“œ ์‹คํŒจ ์กฐ๊ฑด ์„ค์ •
    • ๋ณด์•ˆ ๊ด€๋ จ ์ •์ฑ…(as code) ์ ์šฉ .
  3. ๊ถŒํ•œ ์ตœ์†Œํ™”
    • ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€๋Š” ๋ฐ˜๋“œ์‹œ root ๊ถŒํ•œ ์—†์ด ์‹คํ–‰
    • distroless, scratch ์ด๋ฏธ์ง€, ๋ถˆํ•„์š”ํ•œ ์…ธ/ํŒจํ‚ค์ง€ ์ œ๊ฑฐ.
  4. ๋Ÿฐํƒ€์ž„ ๋ชจ๋‹ˆํ„ฐ๋ง & ๊ฐ์‚ฌ
    • Falco, Clair, Dockle ๋“ฑ์˜ ๋„๊ตฌ๋ฅผ ์ด์šฉํ•ด ์‹คํ–‰ ์‹œ ์œ„ํ˜‘ ํƒ์ง€
    • ๋ณด์•ˆ ๋กœ๊ทธ ๋ฐ ์ด์ƒ ํ–‰๋™ ์‹ค์‹œ๊ฐ„ ์ˆ˜์ง‘.
  5. Shift-Left ํ…Œ์ŠคํŠธ ๋ฌธํ™”
    • ๊ฐœ๋ฐœ ์ดˆ๊ธฐ API ๋ณด์•ˆ ํ…Œ์ŠคํŠธ(Dynamic ์ทจ์•ฝ์„ฑ) ํ†ตํ•ฉ .
    • ๋ณด์•ˆํŒ€, ๊ฐœ๋ฐœํŒ€, ์šด์˜ํŒ€ ๊ฐ„ ํ˜‘์—… ์ฆ์ง„.

โœ… ์‹ค๋ฌด ํŒ

  • PR๋งˆ๋‹ค ์ž๋™ ์Šค์บ” ๊ฑธ๊ธฐ.
  • ๋ณด์•ˆ ๊ฒฝ๋ณด๋Š” ์Šฌ๋ž™/์ด์Šˆ ํŠธ๋ž˜์ปค ์•Œ๋ฆผ.
  • ๊ฐœ๋ฐœํŒ€์—๊ฒŒ ๊ฒฝ๊ณ  ๋ฌธํ™”๋ฅผ ์กฐ์„ฑํ•˜๊ณ , ์ˆ˜๋™ ์žฌ๋ฐฐํฌ ์š”๊ตฌ๋ฅผ ์ตœ์†Œํ™”.

2. ๐Ÿš€ ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์ „๋žต & ๋„คํŠธ์›Œํฌ ์ตœ์ ํ™”

๐ŸŽฏ ์™œ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋‚˜?

์ด๋ฏธ์ง€ ์ตœ์ ํ™”๋กœ ์ปจํ…Œ์ด๋„ˆ ์ž์ฒด๋Š” ๊ฐ€๋ฒผ์›Œ์กŒ์ง€๋งŒ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‹ค์šดํƒ€์ž„ ์—†์ด ๋ฐฐํฌํ•˜๊ณ , ๋„คํŠธ์›Œํฌ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ์—ฌ์ „ํžˆ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ› ๏ธ ์ฃผ์š” ๋ฐฐํฌ ์ „๋žต

  • Blue-Green Deployment
    • ๋‘ ํ™˜๊ฒฝ: โ€œBlueโ€(ํ˜„ํ–‰) / โ€œGreenโ€(์‹ ๋ฒ„์ „)์„ ๋ณ‘๋ ฌ ์šด์šฉ .
    • DNS๋‚˜ ๋กœ๋“œ๋ฐธ๋Ÿฐ์„œ๋กœ ํŠธ๋ž˜ํ”ฝ ์ „ํ™˜ โ†’ ์ฆ‰๊ฐ์  ๋กค๋ฐฑ ๊ฐ€๋Šฅ.
    • ๋‹จ์ : ๋ฆฌ์†Œ์Šค ์ด์ค‘ ์‚ฌ์šฉ, DB ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹œ ๋™๊ธฐํ™” ์ฃผ์˜.
  • Canary Deployment
    • ์ƒˆ ๋ฒ„์ „์„ ์†Œ์ˆ˜์˜ ํŠธ๋ž˜ํ”ฝ(์˜ˆ: 1%, 10%)์—๋งŒ ์„œ์„œํžˆ ๋…ธ์ถœ .
    • ์ธ๊ทธ๋ ˆ์Šค ์„ค์ •์œผ๋กœ ํŠธ๋ž˜ํ”ฝ ๋ถ„ํ•  + ๋ชจ๋‹ˆํ„ฐ๋ง ์‹ค์‹œ.
    • ์ ์ง„์  ํ™•์‚ฐ์œผ๋กœ ์•ˆ์ •์„ฑโ†‘, ์˜ค๋ฅ˜ ์‹œ ๋น ๋ฅธ ํ†ต์ œ ๊ฐ€๋Šฅ.
  • ํ˜ผํ•ฉ ์ „๋žต
    • ๋ฐ์ดํ„ฐ์„ผํ„ฐ๋ณ„๋กœ Blue-Green ์‹œํ–‰ ํ›„, Canary ๋ฐฉ์‹์œผ๋กœ ํ™•๋Œ€ ๋ฐฐํฌ .

๐Ÿ”ง ๋„คํŠธ์›Œํฌ ์ž๋™ํ™” ๋ฐ ์ตœ์ ํ™”

  • DNS ๋ผ์šฐํŒ…
    • Routeโ€ฏ53, Azure Traffic Manager ๋“ฑ์œผ๋กœ ์ง€๋ฆฌ/๊ฐ€์ค‘์น˜ ๊ธฐ๋ฐ˜ ํŠธ๋ž˜ํ”ฝ ๋ถ„๋ฆฌ .
    • ๋กค๋ฐฑ, ํŠธ๋ž˜ํ”ฝ ์Šค์œ„์นญ ์ž๋™ํ™”์— ์œ ๋ฆฌ.
  • ์ธ๊ทธ๋ ˆ์Šค/์„œ๋น„์Šค ๋งค๋‹ˆ์ € ์„ค์ •
    • Nginx Ingress ํ˜น์€ Service Mesh์—์„œ nginx.ingress.kubernetes.io/canary-weight ๋“ฑ ์–ด๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ .
    • ์„œ๋น„์Šค ์ด๋ฆ„(label)์œผ๋กœ Blue/Green ํฌ๋“œ ๋ผ์šฐํŒ….
  • ์ž๋™ํ™” ๋„๊ตฌ
    • Argo Rollouts ๊ธฐ๋ฐ˜ ๋ฐฐํฌ ํ”Œ๋กœ์šฐ ๊ตฌ์ถ• .
    • CI/CD(Terraform + Route53) + Argo๋ฅผ ์—ฐ๋™ํ•ด ์™„์ „ ๋ฌด์ค‘๋‹จ, ๊ฐ€์ค‘์น˜ ์กฐ์ ˆ ๊ฐ€๋Šฅ ๋ฐฐํฌ ํ™˜๊ฒฝ ๊ตฌ์ถ•.

โœ… ์š”์•ฝ ์ •๋ฆฌ

์ฃผ์ œ๊ตฌ์„ฑํšจ๊ณผ
Shiftโ€‘Left ๋ณด์•ˆ์ด๋ฏธ์ง€/์ฝ”๋“œ ์Šค์บ”, ๊ถŒํ•œ ์ตœ์†Œํ™”, ๋Ÿฐํƒ€์ž„ ๋ชจ๋‹ˆํ„ฐ๋ง, PR ๊ธฐ๋ฐ˜ ๋ณด์•ˆ ๊ฒŒ์ดํŠธ์ดˆ๊ธฐ ๋ณด์•ˆ ๊ฒฐํ•จ ์˜ˆ๋ฐฉ + ์šด์˜ ์ค‘ ๋ฆฌ์Šคํฌ ์ตœ์†Œํ™”
๋ฌด์ค‘๋‹จ ๋ฐฐํฌ + ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ ๊ด€๋ฆฌBlueโ€‘Green / Canary ์ „๋žต, DNS/Ingress ์ž๋™ํ™”, Argo Rollouts์ค‘๋‹จ ์—†๋Š” ๋ฐฐํฌ, ๊ฐ€์‹œ์„ฑ ํ™•๋ณด, ๋น ๋ฅธ ๋กค๋ฐฑ ๋Œ€์‘ ๊ฐ€๋Šฅ
profile
๊ฐœ๋ฐœ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋Š”, ์ด์Šˆ๋ฅผ ์ค„์ด๋Š” ํ™˜๊ฒฝ์„ ๋งŒ๋“ค๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€