์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿณ Docker ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ด€๋ฆฌ, ์นœ๊ตฌํ•œํ…Œ ์„ค๋ช…ํ•˜๋“ฏ ์™„์ „ ์ •๋ณตํ•˜๊ธฐ

๐Ÿณ Docker ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ด€๋ฆฌ, ์นœ๊ตฌํ•œํ…Œ ์„ค๋ช…ํ•˜๋“ฏ ์™„์ „ ์ •๋ณตํ•˜๊ธฐ

์•ผ, ๋„ˆ ํ˜น์‹œ ์ด๋Ÿฐ ๊ฒฝํ—˜ ์žˆ์–ด?
๋กœ์ปฌ์—์„œ๋Š” ์ž˜ ๋Œ๋˜ ์•ฑ์„ ์ปจํ…Œ์ด๋„ˆ์— ๋„ฃ์—ˆ๋”๋‹ˆ ๊ฐ‘์ž๊ธฐ "DB ์—ฐ๊ฒฐ ์‹คํŒจ"๊ฐ€ ๋œจ๋Š” ๊ฑฐ. ์•„๋‹ˆ๋ฉด ๋” ๋ฌด์„œ์šด ๊ฑฐ, ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ฆฐ ์ด๋ฏธ์ง€ ์•ˆ์— DB ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋ฐ•ํ˜€ ์žˆ๋˜ ๊ฑฐ. ๐Ÿ˜ฑ

์ด ๋‘ ์‚ฌ๊ณ ์˜ ๊ณตํ†ต์ ์€ ๋”ฑ ํ•˜๋‚˜์•ผ. ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ด€๋ฆฌ๋ฅผ ๋Œ€์ถฉ ํ–ˆ๋‹ค๋Š” ๊ฒƒ. ์˜ค๋Š˜์€ Docker์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์–ด๋””์„œ, ์–ด๋–ป๊ฒŒ, ์–ด๋–ค ์ˆœ์„œ๋กœ ๋„ฃ๊ณ , ๋น„๋ฐ€๊ฐ’์€ ์–ด๋–ป๊ฒŒ ์ˆจ๊ธฐ๋Š”์ง€ ์ปคํ”ผ ํ•œ ์ž” ํ•˜๋ฉด์„œ ์ฐจ๊ทผ์ฐจ๊ทผ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ. ์ง„์งœ ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์จ๋จน์„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋งŒ ๊ณจ๋ผ์™”์–ด. โ˜•

ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ ์ปจํ…Œ์ด๋„ˆ๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ธธ .env ํŒŒ์ผ -e ์˜ต์…˜ ์‹œํฌ๋ฆฟ ์ปจํ…Œ์ด๋„ˆ ํ”„๋กœ์„ธ์Šค ์ด๋ฏธ์ง€๋Š” ๊ทธ๋Œ€๋กœ, ์„ค์ •๋งŒ ๋ฐ”๊ฟ” ๋ผ์šด๋‹ค

๐Ÿค” 1. ์™œ ๊ตณ์ด ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์„ค์ •์„ ๋„ฃ์–ด์•ผ ํ•ด?

์ด๊ฑด The Twelve-Factor App์ด๋ผ๋Š” ์œ ๋ช…ํ•œ ๋ฐฉ๋ฒ•๋ก ์—์„œ ๋‚˜์˜จ ์›์น™์ด์•ผ. 3๋ฒˆ์งธ ํ•ญ๋ชฉ "Config"์—์„œ "์„ค์ •์€ ์ฝ”๋“œ์™€ ๋ถ„๋ฆฌํ•ด์„œ ํ™˜๊ฒฝ(environment)์— ์ €์žฅํ•˜๋ผ"๊ณ  ๋งํ•˜๊ฑฐ๋“ .

Docker๋ž‘ ์ด๊ฒŒ ์ฐฐ๋–ก์ธ ์ด์œ ๋Š” ๊ฐ„๋‹จํ•ด. ๋„์ปค ์ด๋ฏธ์ง€๋Š” ํ•œ ๋ฒˆ ๋นŒ๋“œํ•˜๋ฉด ๋ถˆ๋ณ€(immutable)์ด์•ผ. ๊ทธ๋Ÿฐ๋ฐ ๊ฐœ๋ฐœยท์Šคํ…Œ์ด์ง•ยท์šด์˜ ํ™˜๊ฒฝ๋งˆ๋‹ค DB ์ฃผ์†Œ, API ํ‚ค, ๋กœ๊ทธ ๋ ˆ๋ฒจ์ด ๋‹ค ๋‹ค๋ฅด์ž–์•„? ํ™˜๊ฒฝ๋งˆ๋‹ค ์ด๋ฏธ์ง€๋ฅผ ๋”ฐ๋กœ ๋นŒ๋“œํ•˜๋ฉด "์Šคํ…Œ์ด์ง•์—์„œ ํ…Œ์ŠคํŠธํ•œ ๊ทธ ์ด๋ฏธ์ง€"๊ฐ€ ์šด์˜ ์ด๋ฏธ์ง€๋ž‘ ๋‹ฌ๋ผ์ง€๋Š” ๊ฑฐ์•ผ. ๊ทธ๋Ÿผ ํ…Œ์ŠคํŠธํ•œ ์˜๋ฏธ๊ฐ€ ์—†์ง€.

๐Ÿ’ก ํ•ต์‹ฌ ํ•œ ์ค„ ์š”์•ฝ
"์ด๋ฏธ์ง€๋Š” ํ•˜๋‚˜๋งŒ ๋นŒ๋“œํ•˜๊ณ , ํ™˜๊ฒฝ๋ณ€์ˆ˜๋งŒ ๊ฐˆ์•„ ๋ผ์›Œ์„œ ์–ด๋””์„œ๋“  ์‹คํ–‰ํ•œ๋‹ค." ์ด๊ฒŒ Docker ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ด€๋ฆฌ์˜ ๋Œ€์ „์ œ์•ผ.

๐Ÿงฐ 2. ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋„ฃ๋Š” ๋ฐฉ๋ฒ• ์ด์ •๋ฆฌ

Docker์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๋„ฃ๋Š” ๊ธธ์€ ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์•„. ํ•˜๋‚˜์”ฉ ๋ณด์ž.

โ‘  Dockerfile์˜ ENV

FROM node:20-alpine
ENV NODE_ENV=production \
    PORT=3000
WORKDIR /app
COPY . .
CMD ["node", "server.js"]

ENV๋กœ ์„ค์ •ํ•œ ๊ฐ’์€ ์ด๋ฏธ์ง€ ์•ˆ์— ์ €์žฅ๋ผ์„œ, ์ด ์ด๋ฏธ์ง€๋กœ ๋งŒ๋“  ๋ชจ๋“  ์ปจํ…Œ์ด๋„ˆ์— ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋“ค์–ด๊ฐ€. ๋นŒ๋“œ ์ค‘๊ฐ„์˜ RUN ๋ช…๋ น์—์„œ๋„ ์“ธ ์ˆ˜ ์žˆ๊ณ , ๋Ÿฐํƒ€์ž„์—๋„ ๋‚จ์•„ ์žˆ์–ด.
๊ทธ๋ž˜์„œ ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฐ™์€ ๋ฏผ๊ฐ ์ •๋ณด๋ฅผ ENV์— ๋„ฃ์œผ๋ฉด ์ ˆ๋Œ€ ์•ˆ ๋ผ. docker history๋‚˜ docker inspect๋กœ ๋ˆ„๊ตฌ๋‚˜ ๋ณผ ์ˆ˜ ์žˆ๊ฑฐ๋“ .

โ‘ก Dockerfile์˜ ARG (๋นŒ๋“œ ํƒ€์ž„ ์ „์šฉ)

ARG APP_VERSION=1.0.0
FROM node:20-alpine
ARG APP_VERSION
ENV APP_VERSION=${APP_VERSION}
docker build --build-arg APP_VERSION=2.1.0 -t myapp .

ARG๋Š” ๋นŒ๋“œํ•  ๋•Œ๋งŒ ์กด์žฌํ•˜๋Š” ๋ณ€์ˆ˜์•ผ. ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์‹คํ–‰๋  ๋•Œ๋Š” ์‚ฌ๋ผ์ ธ. ์œ„ ์˜ˆ์ œ์ฒ˜๋Ÿผ ENV๋กœ ์˜ฎ๊ฒจ ๋‹ด์•„์•ผ ๋Ÿฐํƒ€์ž„์—์„œ๋„ ์“ธ ์ˆ˜ ์žˆ์ง€.
์—ฌ๊ธฐ์„œ ์ž์ฃผ ํ•˜๋Š” ์ฐฉ๊ฐ์ด ํ•˜๋‚˜ ์žˆ์–ด. "ARG๋Š” ๋นŒ๋“œ ํ›„์— ์‚ฌ๋ผ์ง€๋‹ˆ๊นŒ ๋น„๋ฐ€๊ฐ’ ๋„ฃ์–ด๋„ ๋˜๊ฒ ์ง€?" โ†’ ๋…ธ๋…ธ. ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ARG ๊ฐ’์€ docker history๋กœ ๋…ธ์ถœ๋  ์ˆ˜ ์žˆ์œผ๋‹ˆ ๋น„๋ฐ€๊ฐ’์— ์“ฐ์ง€ ๋ง๋ผ๊ณ  ๊ฒฝ๊ณ ํ•ด. ๊ทธ๋ฆฌ๊ณ  FROM ์•ž์— ์„ ์–ธํ•œ ARG๋Š” FROM ์ดํ›„์—์„œ ์“ฐ๋ ค๋ฉด ์œ„์ฒ˜๋Ÿผ ๋‹ค์‹œ ARG APP_VERSION์œผ๋กœ ์„ ์–ธํ•ด์•ผ ํ•ด. ์€๊ทผ ํ—ท๊ฐˆ๋ฆฌ๋Š” ํฌ์ธํŠธ!

๐Ÿ—๏ธ ARG
๋นŒ๋“œ ์‹œ์ ์—๋งŒ ์กด์žฌ
--build-arg๋กœ ์ฃผ์ž…
๋Ÿฐํƒ€์ž„ ์ปจํ…Œ์ด๋„ˆ์—” ์—†์Œ
๋ฒ„์ „ยท๋นŒ๋“œ ์˜ต์…˜์šฉ
๐Ÿš€ ENV
๋นŒ๋“œ+๋Ÿฐํƒ€์ž„ ๋ชจ๋‘ ์กด์žฌ
์ด๋ฏธ์ง€์— ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ €์žฅ
-e๋กœ ๋ฎ์–ด์“ฐ๊ธฐ ๊ฐ€๋Šฅ
์•ฑ ๊ธฐ๋ณธ ์„ค์ •์šฉ

โ‘ข docker run -e / --env-file

# ํ•˜๋‚˜์”ฉ ๋„ฃ๊ธฐ
docker run -e DB_HOST=db.local -e LOG_LEVEL=debug myapp

# ํ˜ธ์ŠคํŠธ์— ์ด๋ฏธ ์žˆ๋Š” ๋ณ€์ˆ˜ ๊ทธ๋Œ€๋กœ ์ „๋‹ฌ (๊ฐ’ ์ƒ๋žต)
export API_TOKEN=abc123
docker run -e API_TOKEN myapp

# ํŒŒ์ผ๋กœ ํ•œ ๋ฒˆ์—
docker run --env-file ./prod.env myapp

-e VAR์ฒ˜๋Ÿผ ๊ฐ’์„ ์ƒ๋žตํ•˜๋ฉด ํ˜ธ์ŠคํŠธ ์…ธ์˜ ๊ฐ™์€ ์ด๋ฆ„ ๋ณ€์ˆ˜๋ฅผ ๋„˜๊ฒจ์ค˜. ์ด ๋ฐฉ์‹์€ ์…ธ ํžˆ์Šคํ† ๋ฆฌ์— ๊ฐ’์ด ์•ˆ ๋‚จ๋Š”๋‹ค๋Š” ์†Œ์†Œํ•œ ์žฅ์ ๋„ ์žˆ์–ด.
--env-file ํŒŒ์ผ์€ KEY=VALUE ํ•œ ์ค„์”ฉ ์“ฐ๊ณ , #์œผ๋กœ ์‹œ์ž‘ํ•˜๋Š” ์ค„์€ ์ฃผ์„์ด์•ผ. ์ฃผ์˜ํ•  ์ ์€ ๋”ฐ์˜ดํ‘œ๊ฐ€ ๋ฌธ๋ฒ•์ ์œผ๋กœ ๋ฒ—๊ฒจ์ง€์ง€ ์•Š๊ณ  ๊ฐ’์— ๊ทธ๋Œ€๋กœ ํฌํ•จ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ. PASSWORD="abc"๋ผ๊ณ  ์“ฐ๋ฉด ๋”ฐ์˜ดํ‘œ๊นŒ์ง€ ๊ฐ’์œผ๋กœ ๋“ค์–ด๊ฐ€๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์œผ๋‹ˆ, docker run์šฉ env ํŒŒ์ผ์—์„œ๋Š” ๋”ฐ์˜ดํ‘œ ์—†์ด ์“ฐ๋Š” ๊ฒŒ ์•ˆ์ „ํ•ด.

โ‘ฃ Docker Compose์˜ environment / env_file

services:
  api:
    image: myapp:latest
    environment:
      NODE_ENV: production
      DB_HOST: db
    env_file:
      - ./common.env
      - ./api.env
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_pw
    secrets:
      - db_pw

secrets:
  db_pw:
    file: ./secrets/db_password.txt

์‹ค๋ฌด์—์„  ๊ฑฐ์˜ ์ด๊ฑธ ์“ฐ๊ฒŒ ๋  ๊ฑฐ์•ผ. env_file์„ ์—ฌ๋Ÿฌ ๊ฐœ ๋‚˜์—ดํ•˜๋ฉด ์•„๋ž˜์ชฝ ํŒŒ์ผ์ด ์œ„์ชฝ ๊ฐ’์„ ๋ฎ์–ด์จ. ๊ณตํ†ต ์„ค์ • + ์„œ๋น„์Šค๋ณ„ ์„ค์ •์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ ์ข‹์ง€.

โš ๏ธ 3. ๋‹ค๋“ค ํ•œ ๋ฒˆ์”ฉ ๋‹นํ•˜๋Š” ํ•จ์ •: .env ํŒŒ์ผ์˜ ์ •์ฒด

์—ฌ๊ธฐ๊ฐ€ ์˜ค๋Š˜์˜ ๊ฟ€ํฌ์ธํŠธ์•ผ. Compose ์“ธ ๋•Œ ํ”„๋กœ์ ํŠธ ํด๋”์˜ .env ํŒŒ์ผ๊ณผ env_file:๋กœ ์ง€์ •ํ•œ ํŒŒ์ผ์€ ์—ญํ• ์ด ์™„์ „ํžˆ ๋‹ฌ๋ผ.

๐Ÿ“„ ํ”„๋กœ์ ํŠธ์˜ .env
Compose๊ฐ€ YAML ํŒŒ์ผ์„ ํ•ด์„ํ•  ๋•Œ ์“ฐ๋Š” ๋ณ€์ˆ˜
์˜ˆ: image: myapp:${TAG}์˜ TAG๋ฅผ ์ฑ„์›€
์ปจํ…Œ์ด๋„ˆ ์•ˆ์œผ๋กœ ์ž๋™ ์ „๋‹ฌ๋˜์ง€ ์•Š์Œ
๐Ÿ“ฆ env_file: ์ง€์ • ํŒŒ์ผ
๊ฐ’์ด ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๋“ค์–ด๊ฐ
์•ฑ์ด process.env ๋“ฑ์œผ๋กœ ์ฝ์Œ
YAML ์น˜ํ™˜์—๋Š” ์“ฐ์ด์ง€ ์•Š์Œ

๊ทธ๋Ÿฌ๋‹ˆ๊นŒ .env์— DB_PASSWORD=1234 ์จ๋†“๊ณ  "์™œ ์ปจํ…Œ์ด๋„ˆ์—์„œ ์•ˆ ์ฝํžˆ์ง€?" ํ•˜๊ณ  ํ•œ ์‹œ๊ฐ„ ๋‚ ๋ฆฌ๋Š” ๊ฑฐ, ์ง„์งœ ๊ตญ๋ฃฐ์ด์•ผ. ๐Ÿ˜‚ ์ปจํ…Œ์ด๋„ˆ์— ๋„ฃ๊ณ  ์‹ถ์œผ๋ฉด environment: DB_PASSWORD: ${DB_PASSWORD}์ฒ˜๋Ÿผ ๋ช…์‹œ์ ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๊ฑฐ๋‚˜, env_file: .env๋กœ ์ง€์ •ํ•ด์•ผ ํ•ด.

# .env ๋‚ด์šฉ์ด ์ œ๋Œ€๋กœ ์น˜ํ™˜๋๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์ตœ๊ณ ์˜ ๋ฐฉ๋ฒ•
docker compose config

docker compose config๋Š” ๋ณ€์ˆ˜ ์น˜ํ™˜๊นŒ์ง€ ๋๋‚œ ์ตœ์ข… ์„ค์ •์„ ๋ณด์—ฌ์ค˜. ๋””๋ฒ„๊น…ํ•  ๋•Œ ์ด๊ฑฐ ํ•˜๋‚˜๋ฉด ๋ฐ˜์€ ํ•ด๊ฒฐ๋ผ.
์ฐธ๊ณ ๋กœ Compose์˜ ๋ณ€์ˆ˜ ์น˜ํ™˜ ๋ฌธ๋ฒ•๋„ ์“ธ๋ชจ๊ฐ€ ๋งŽ์•„. ${VAR:-default}๋Š” ๊ฐ’์ด ์—†๊ฑฐ๋‚˜ ๋น„์–ด ์žˆ์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’, ${VAR:?์—๋Ÿฌ๋ฉ”์‹œ์ง€}๋Š” ๊ฐ’์ด ์—†์œผ๋ฉด ์‹คํ–‰ ์ž์ฒด๋ฅผ ๋ง‰์•„์ค˜. ํ•„์ˆ˜ ๋ณ€์ˆ˜์— :? ๋ถ™์—ฌ๋‘๋ฉด ์„ค์ • ๋ˆ„๋ฝ ์‚ฌ๊ณ ๋ฅผ ๋ฏธ๋ฆฌ ๋ง‰์„ ์ˆ˜ ์žˆ์ง€.

๐Ÿฅ‡ 4. ๋ˆ„๊ฐ€ ์ด๊ธฐ๋‚˜? ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์šฐ์„ ์ˆœ์œ„

๊ฐ™์€ ๋ณ€์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ ๊ณณ์— ์„ค์ •๋ผ ์žˆ์œผ๋ฉด ๋ญ๊ฐ€ ์ด๊ธธ๊นŒ? Docker ๊ณต์‹ ๋ฌธ์„œ ๊ธฐ์ค€ Compose์—์„œ ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์šฐ์„ ์ˆœ์œ„๋Š” ์ด๋ž˜ (์œ„๊ฐ€ ๊ฐ€์žฅ ๊ฐ•ํ•จ).

1
docker compose run -e ๋กœ CLI์—์„œ ์ง์ ‘ ์ง€์ •ํ•œ ๊ฐ’
2
environment ์†์„ฑ (์…ธ์ด๋‚˜ .env์—์„œ ์น˜ํ™˜๋œ ๊ฐ’ ํฌํ•จ)
3
env_file ์†์„ฑ์œผ๋กœ ์ง€์ •ํ•œ ํŒŒ์ผ
4
์ด๋ฏธ์ง€์˜ Dockerfile ENV ๊ฐ’

์ฆ‰ Dockerfile์˜ ENV๋Š” "๊ธฐ๋ณธ๊ฐ’", ์‹คํ–‰ํ•  ๋•Œ ๋„ฃ๋Š” ๊ฐ’์ด "์ตœ์ข… ๊ฒฐ์ •๊ถŒ์ž"์•ผ. ์ด ๊ตฌ์กฐ ๋•๋ถ„์— ์ด๋ฏธ์ง€ ํ•˜๋‚˜๋กœ ์—ฌ๋Ÿฌ ํ™˜๊ฒฝ์„ ์ปค๋ฒ„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฑฐ์ง€.
๊ทธ๋ฆฌ๊ณ  YAML ์น˜ํ™˜์šฉ ๋ณ€์ˆ˜์—์„œ๋Š” ์…ธ ํ™˜๊ฒฝ๋ณ€์ˆ˜๊ฐ€ .env ํŒŒ์ผ๋ณด๋‹ค ์šฐ์„ ํ•ด. CI์—์„œ export TAG=v2 ํ•ด๋‘๋ฉด .env์˜ TAG๋ณด๋‹ค ๋จผ์ € ์ ์šฉ๋œ๋‹ค๋Š” ๋œป!

๋ฎ์–ด์“ฐ๊ธฐ ํ”ผ๋ผ๋ฏธ๋“œ CLI -e environment env_file Dockerfile ENV (๊ธฐ๋ณธ๊ฐ’) ๊ฐ€์žฅ ๊ฐ•ํ•จ

๐Ÿ” 5. ๋น„๋ฐ€๊ฐ’(Secret)์€ ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ๋„ฃ์–ด๋„ ๋ ๊นŒ?

์†”์งํžˆ ๋งํ• ๊ฒŒ. ์†Œ๊ทœ๋ชจ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋„ฃ๋Š” ๊ฑด ํ”ํ•˜๊ณ , ๋‹น์žฅ ํฐ์ผ ๋‚˜์ง„ ์•Š์•„. ํ•˜์ง€๋งŒ ์šด์˜ ํ™˜๊ฒฝ์ด๋ผ๋ฉด ์ด์•ผ๊ธฐ๊ฐ€ ๋‹ฌ๋ผ. ํ™˜๊ฒฝ๋ณ€์ˆ˜์—๋Š” ์ด๋Ÿฐ ์•ฝ์ ์ด ์žˆ๊ฑฐ๋“ .

๐Ÿšจ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๋น„๋ฐ€๊ฐ’์„ ๋‹ค๋ฃฐ ๋•Œ์˜ ์œ„ํ—˜
โ€ข docker inspect ๊ฒฐ๊ณผ์— ํ‰๋ฌธ์œผ๋กœ ๊ทธ๋Œ€๋กœ ๋ณด์ž„
โ€ข ์•ฑ์ด ํฌ๋ž˜์‹œ ๋‚˜๋ฉด์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ „์ฒด๋ฅผ ๋กœ๊ทธ๋‚˜ ์—๋Ÿฌ ๋ฆฌํฌํŠธ์— ๋คํ”„ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Œ
โ€ข ์ž์‹ ํ”„๋กœ์„ธ์Šค์— ์ž๋™ ์ƒ์†๋จ
โ€ข ๊ฐ™์€ ํ˜ธ์ŠคํŠธ์—์„œ ๊ถŒํ•œ ์žˆ๋Š” ์‚ฌ์šฉ์ž๋Š” /proc/<pid>/environ์œผ๋กœ ํ™•์ธ ๊ฐ€๋Šฅ

๊ทธ๋ž˜์„œ Docker๋Š” ์‹œํฌ๋ฆฟ(Secrets) ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด. Compose์—์„œ secrets๋ฅผ ์“ฐ๋ฉด ๊ฐ’์ด ์ปจํ…Œ์ด๋„ˆ ์•ˆ์˜ /run/secrets/<์ด๋ฆ„> ํŒŒ์ผ๋กœ ๋งˆ์šดํŠธ๋ผ. ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋ชฉ๋ก์—๋Š” ๋‚˜ํƒ€๋‚˜์ง€ ์•Š์ง€.

์œ„ Compose ์˜ˆ์ œ์—์„œ POSTGRES_PASSWORD_FILE์„ ์“ด ๊ฑฐ ๋ดค์–ด? ๊ณต์‹ postgres, mysql ๊ฐ™์€ ์ด๋ฏธ์ง€๋Š” _FILE ์ ‘๋ฏธ์‚ฌ ๊ทœ์น™์„ ์ง€์›ํ•ด์„œ, ๋ณ€์ˆ˜ ๊ฐ’ ๋Œ€์‹  ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ์ฃผ๋ฉด ๊ทธ ํŒŒ์ผ ๋‚ด์šฉ์„ ์ฝ์–ด ๋น„๋ฐ€๋ฒˆํ˜ธ๋กœ ์จ. ๋‚ด ์•ฑ์—์„œ๋„ ๊ฐ™์€ ํŒจํ„ด์„ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด.

# Python ์˜ˆ์‹œ: _FILE ๋ณ€์ˆ˜๊ฐ€ ์žˆ์œผ๋ฉด ํŒŒ์ผ์—์„œ, ์—†์œผ๋ฉด ํ™˜๊ฒฝ๋ณ€์ˆ˜์—์„œ
import os

def get_secret(name):
    file_path = os.getenv(f"{name}_FILE")
    if file_path and os.path.exists(file_path):
        with open(file_path) as f:
            return f.read().strip()
    return os.getenv(name)

db_password = get_secret("DB_PASSWORD")

๋นŒ๋“œ ๊ณผ์ •์—์„œ ๋น„๋ฐ€๊ฐ’์ด ํ•„์š”ํ•  ๋•Œ(์˜ˆ: ํ”„๋ผ์ด๋น— ํŒจํ‚ค์ง€ ์ €์žฅ์†Œ ํ† ํฐ)๋Š” BuildKit์˜ secret mount๋ฅผ ์จ. ์ด๊ฑด ์ด๋ฏธ์ง€ ๋ ˆ์ด์–ด์— ํ”์ ์ด ์•ˆ ๋‚จ์•„.

# Dockerfile
# syntax=docker/dockerfile:1
FROM node:20-alpine
RUN --mount=type=secret,id=npm_token \
    NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci

# ๋นŒ๋“œ ๋ช…๋ น
docker build --secret id=npm_token,src=./npm_token.txt -t myapp .

๋” ํฐ ๊ทœ๋ชจ๋ผ๋ฉด Kubernetes Secret, HashiCorp Vault, AWS Secrets Manager, Azure Key Vault ๊ฐ™์€ ์ „์šฉ ์‹œํฌ๋ฆฟ ์ €์žฅ์†Œ์™€ ์—ฐ๋™ํ•˜๋Š” ๊ฒŒ ์ •์„์ด์•ผ. ๋‹ค๋งŒ Kubernetes Secret์€ ๊ธฐ๋ณธ์ ์œผ๋กœ base64 ์ธ์ฝ”๋”ฉ์ผ ๋ฟ ์•”ํ˜ธํ™”๊ฐ€ ์•„๋‹ˆ๋ผ์„œ, etcd ์ €์žฅ ์‹œ ์•”ํ˜ธํ™”(Encryption at rest)์™€ RBAC ์„ค์ •์„ ๋”ฐ๋กœ ์ฑ™๊ฒจ์•ผ ํ•ด. ์ด๊ฑฐ ๋ชจ๋ฅด๋Š” ์‚ฌ๋žŒ ๊ฝค ๋งŽ์•„!

๐Ÿ“Š 6. ํ•œ๋ˆˆ์— ๋ณด๋Š” ๋ฐฉ๋ฒ•๋ณ„ ๋น„๊ตํ‘œ

๋ฐฉ๋ฒ•์ ์šฉ ์‹œ์ ์ถ”์ฒœ ์šฉ๋„๋น„๋ฐ€๊ฐ’
Dockerfile ENV๋นŒ๋“œ+๋Ÿฐํƒ€์ž„๊ธฐ๋ณธ ์„ค์ •๊ฐ’โŒ ๊ธˆ์ง€
Dockerfile ARG๋นŒ๋“œ๋งŒ๋ฒ„์ „, ๋นŒ๋“œ ์˜ต์…˜โŒ ๊ธˆ์ง€
run -e / --env-file๋Ÿฐํƒ€์ž„ํ™˜๊ฒฝ๋ณ„ ์„ค์ •โ–ณ ๊ฐœ๋ฐœ์šฉ
Compose environment๋Ÿฐํƒ€์ž„์„œ๋น„์Šค๋ณ„ ์„ค์ •โ–ณ ๊ฐœ๋ฐœ์šฉ
Compose/Swarm secrets๋Ÿฐํƒ€์ž„(ํŒŒ์ผ)๋น„๋ฐ€๋ฒˆํ˜ธ, ํ‚คโœ… ๊ถŒ์žฅ
BuildKit --secret๋นŒ๋“œ๋งŒ๋นŒ๋“œ์šฉ ํ† ํฐโœ… ๊ถŒ์žฅ

๐Ÿ› ๏ธ 7. ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์“ฐ๋Š” ์ฒดํฌ๋ฆฌ์ŠคํŠธ

01.env๋Š” .gitignore์—, .env.example์€ ์ปค๋ฐ‹
ํ‚ค ์ด๋ฆ„๊ณผ ์„ค๋ช…๋งŒ ๋‹ด์€ ํ…œํ”Œ๋ฆฟ์„ ๊ณต์œ ํ•˜๋ฉด ํŒ€์›์ด ๋ญ˜ ์ฑ„์›Œ์•ผ ํ•˜๋Š”์ง€ ๋ฐ”๋กœ ์•Œ์•„.

02.dockerignore์—๋„ .env ์ถ”๊ฐ€
COPY . . ํ•œ ๋ฐฉ์— .env๊ฐ€ ์ด๋ฏธ์ง€๋กœ ์“ธ๋ ค ๋“ค์–ด๊ฐ€๋Š” ์‚ฌ๊ณ , ์ƒ๊ฐ๋ณด๋‹ค ํ”ํ•ด. gitignore๋งŒ ์ฑ™๊ธฐ๊ณ  dockerignore๋ฅผ ๋นผ๋จน๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„.

03์•ฑ ์‹œ์ž‘ ์‹œ ํ•„์ˆ˜ ๋ณ€์ˆ˜ ๊ฒ€์ฆ
๊ฐ’์ด ์—†์œผ๋ฉด ์• ๋งคํ•˜๊ฒŒ ๋Œ์ง€ ๋ง๊ณ  ์ฆ‰์‹œ ์ข…๋ฃŒ(fail fast)ํ•˜์ž. ์›์ธ ํŒŒ์•…์ด ํ›จ์”ฌ ๋นจ๋ผ์ ธ.

04ํ™˜๊ฒฝ๋ณ„ Compose ํŒŒ์ผ ๋ถ„๋ฆฌ

docker compose -f compose.yaml -f compose.prod.yaml up
์ฒ˜๋Ÿผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํŒŒ์ผ๋กœ ์šด์˜ ์„ค์ •๋งŒ ๋ฎ์–ด์“ฐ๊ธฐ.

05๋กœ๊ทธ์— ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์ „์ฒด ์ถœ๋ ฅ ๊ธˆ์ง€
๋””๋ฒ„๊น…ํ•œ๋‹ค๊ณ  printenv ๊ฒฐ๊ณผ๋ฅผ ๋กœ๊ทธ์— ๋‚จ๊ธฐ๋ฉด ๋น„๋ฐ€๊ฐ’์ด ๋กœ๊ทธ ์ˆ˜์ง‘ ์‹œ์Šคํ…œ์œผ๋กœ ์ค„์ค„ ์ƒˆ์–ด๋‚˜๊ฐ€.

06์ด๋ฏธ์ง€ ์Šค์บ”์œผ๋กœ ์‹œํฌ๋ฆฟ ๊ฒ€์‚ฌ
CI ๋‹จ๊ณ„์—์„œ gitleaks, trufflehog, Trivy ๊ฐ™์€ ๋„๊ตฌ๋กœ ์ฝ”๋“œ์™€ ์ด๋ฏธ์ง€ ์† ๋น„๋ฐ€๊ฐ’์„ ๊ฒ€์‚ฌํ•˜๋ฉด ๋“ ๋“ ํ•ด.

๐Ÿงช 8. ๋””๋ฒ„๊น…ํ•  ๋•Œ ์“ฐ๋Š” ๋ช…๋ น์–ด ๋ชจ์Œ

# ์‹คํ–‰ ์ค‘ ์ปจํ…Œ์ด๋„ˆ์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํ™•์ธ
docker exec mycontainer env

# ์„ค์ •๋œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋ชฉ๋ก๋งŒ ๋ฝ‘๊ธฐ
docker inspect --format '{{json .Config.Env}}' mycontainer

# Compose ์ตœ์ข… ์„ค์ •(์น˜ํ™˜ ์™„๋ฃŒ) ํ™•์ธ
docker compose config

# ํŠน์ • ์„œ๋น„์Šค์— ์ผํšŒ์„ฑ์œผ๋กœ ๋‹ค๋ฅธ ๊ฐ’ ์ฃผ๊ณ  ์‹คํ–‰
docker compose run -e LOG_LEVEL=debug api

๊ทธ๋ฆฌ๊ณ  ๊ผญ ์•Œ์•„๋‘˜ ๊ฒƒ! ํ™˜๊ฒฝ๋ณ€์ˆ˜๋Š” ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ์ •ํ•ด์ ธ. ๊ทธ๋ž˜์„œ env ํŒŒ์ผ์„ ์ˆ˜์ •ํ–ˆ๋‹ค๋ฉด docker restart๋กœ๋Š” ๋ฐ˜์˜์ด ์•ˆ ๋˜๊ณ , ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋‹ค์‹œ ๋งŒ๋“ค์–ด์•ผ ํ•ด. Compose๋ผ๋ฉด docker compose up -d๊ฐ€ ์„ค์ • ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•ด์„œ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์žฌ์ƒ์„ฑํ•ด์ค˜. "๋ถ„๋ช… ๋ฐ”๊ฟจ๋Š”๋ฐ ์™œ ๊ทธ๋Œ€๋กœ์ง€?"์˜ ๋ฒ”์ธ์€ ๋Œ€๋ถ€๋ถ„ ์ด๊ฑฐ์•ผ. ๐Ÿ•ต๏ธ

๐ŸŽฏ ๋งˆ๋ฌด๋ฆฌ: ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๊ด€๋ฆฌ๋Š” ๊ฒฐ๊ตญ '์Šต๊ด€'์ด๋‹ค

์ •๋ฆฌํ•ด๋ณด๋ฉด ์ด๋ž˜.
โ€ข ์ด๋ฏธ์ง€๋Š” ํ•˜๋‚˜, ์„ค์ •์€ ๋Ÿฐํƒ€์ž„์— ์ฃผ์ž…
โ€ข ARG๋Š” ๋นŒ๋“œ์šฉ, ENV๋Š” ๊ธฐ๋ณธ๊ฐ’์šฉ, ๋‘˜ ๋‹ค ๋น„๋ฐ€๊ฐ’ ๊ธˆ์ง€
โ€ข Compose์˜ .env๋Š” YAML ์น˜ํ™˜์šฉ์ด๋ผ๋Š” ํ•จ์ • ๊ธฐ์–ตํ•˜๊ธฐ
โ€ข ์šด์˜ ๋น„๋ฐ€๊ฐ’์€ secrets ํŒŒ์ผ ๋งˆ์šดํŠธ๋‚˜ ์ „์šฉ ์‹œํฌ๋ฆฟ ์ €์žฅ์†Œ๋กœ
โ€ข .gitignore + .dockerignore ๋‘˜ ๋‹ค ์ฑ™๊ธฐ๊ธฐ

์ฒ˜์Œ์—” ๊ท€์ฐฎ์•„ ๋ณด์—ฌ๋„ ํ•œ ๋ฒˆ ๊ตฌ์กฐ๋ฅผ ์žก์•„๋‘๋ฉด ๋ฐฐํฌ๊ฐ€ ์ง„์งœ ํŽธํ•ด์ ธ. ํ˜น์‹œ ์‚ฌ๋‚ด ์ธํ”„๋ผ์— ๋งž๋Š” DockerยทCI/CD ๊ตฌ์„ฑ์„ ์ „๋ฌธ๊ฐ€์—๊ฒŒ ๋„์›€๋ฐ›๊ณ  ์‹ถ๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ DevOps ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•œ ๊ฐœ๋ฐœ์ž๋ฅผ ์ฐพ์•„๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ๋ฐ˜๋Œ€๋กœ ์ด๋Ÿฐ ๋…ธํ•˜์šฐ๋ฅผ ์ด๋ฏธ ๊ฐ–์ท„๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์—์„œ ๋„ค ๊ธฐ์ˆ ์„ ๋‚˜๋ˆ ๋ณด๋Š” ๊ฒƒ๋„ ๋ฉ‹์ง„ ์„ ํƒ์ด๊ณ ! ๐Ÿ™Œ

์˜ค๋Š˜ ๋ฐฐ์šด ๊ฑฐ ํ•˜๋‚˜๋งŒ ๊ธฐ์–ตํ•ด๋„ ์ถฉ๋ถ„ํ•ด.
"๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ์ด๋ฏธ์ง€์— ๋„ฃ์ง€ ์•Š๋Š”๋‹ค."
์ด๊ฒƒ๋งŒ ์ง€์ผœ๋„ ๋„ˆ๋Š” ์ด๋ฏธ ์ƒ์œ„๊ถŒ ๊ฐœ๋ฐœ์ž์•ผ. ๐Ÿ˜Ž

๐Ÿ“š ์ฐธ๊ณ  ์ž๋ฃŒ
โ€ข Docker Docs - Set environment variables within your container's environment: https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/
โ€ข Docker Docs - Environment variables precedence: https://docs.docker.com/compose/how-tos/environment-variables/envvars-precedence/
โ€ข Docker Docs - Manage secrets securely in Docker Compose: https://docs.docker.com/compose/how-tos/use-secrets/
โ€ข Docker Docs - Build secrets: https://docs.docker.com/build/building/secrets/
โ€ข Dockerfile reference (ARG, ENV): https://docs.docker.com/reference/dockerfile/
โ€ข The Twelve-Factor App - III. Config: https://12factor.net/config
โ€ข Kubernetes Docs - Secrets: https://kubernetes.io/docs/concepts/configuration/secret/

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.