Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

42장. 도커 기초 — 이미지 · 컨테이너 · 레이어

이 장에서 말하고자 하는 것

앞 장에서 우리는 컨테이너가 무엇이고 왜 쓰는지를 봤다.

이제 그 컨테이너를 실제로 만드는 도구 Docker 의 핵심 세 개념을 잡는다.

  • 이미지(Image)
  • 컨테이너(Container)
  • 레이어(Layer)

1. 이미지와 컨테이너 — 다른 것이다

이미지   : 실행되지 않은 설계도 (붕어빵 틀)
컨테이너 : 그 설계도로 실행된 인스턴스 (붕어빵)
my-app:v1   ← 이미지 1개
  ↓ docker run
컨테이너 1, 2, 3 ...   ← 같은 이미지로 컨테이너 여러 개

이미지는 변하지 않는다. 컨테이너는 살아 있는 프로세스다.


2. Dockerfile — 이미지의 설계도

FROM node:20-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 8080
CMD ["node", "server.js"]

각 줄이 이미지에 한 단계씩 쌓아 올라간다.


3. 레이어 — 이미지의 속살

이미지는 단일 파일이 아니라 여러 레이어 의 누적이다.

[Layer A: 베이스 OS]
[Layer B: 의존성]
[Layer C: 코드]

Dockerfile의 각 명령이 한 레이어를 만든다.

이 구조 덕분에

같은 베이스 + 같은 의존성이면 캐시를 재사용한다

코드만 바꾸면 그 레이어만 다시 만들어진다.


4. 빌드 캐시를 살리는 순서

# 좋은 순서
COPY package.json package-lock.json ./
RUN npm ci
COPY . .            ← 코드 변경 시에도 npm ci 캐시 살아남
# 나쁜 순서
COPY . .
RUN npm ci          ← 코드가 조금만 바뀌어도 의존성 다시 설치

자주 안 바뀌는 명령을 위에, 자주 바뀌는 명령을 아래에


5. Multi-stage Build — 가벼운 이미지

# 빌드 단계
FROM node:20-alpine AS build
WORKDIR /app
COPY . .
RUN npm ci && npm run build

# 실행 단계
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY --from=build /app/node_modules ./node_modules
CMD ["node", "dist/server.js"]

빌드 도구는 빌드 단계에만, 최종 이미지에는 실행에 필요한 것만.

이미지가 작아지면 push · pull · 배포가 다 빨라진다


6. 우리 서비스에서 컨테이너의 표준 모양

  • alpine 또는 distroless 베이스
  • multi-stage build
  • 빌드 캐시를 살리는 COPY 순서
  • CMD 하나 (한 프로세스)
  • 비루트 사용자
  • /health 엔드포인트 또는 HEALTHCHECK

7. 직접 확인해보기 — CLI

docker build -t orders:v1 .
docker run -p 8080:8080 orders:v1
docker ps
docker logs <container-id>
docker exec -it <container-id> sh
docker history orders:v1

docker history 는 이미지가 어떻게 만들어졌는지 한눈에 본다.


8. 코드로는 이렇게 생겼다 — 운영용 Dockerfile

FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app

RUN addgroup -S app && adduser -S app -G app
USER app

COPY --from=build --chown=app:app /app/dist ./dist
COPY --from=build --chown=app:app /app/node_modules ./node_modules
COPY --from=build --chown=app:app /app/package.json ./

EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:8080/health || exit 1
CMD ["node", "dist/server.js"]

9. 이렇게 쓰면 망한다 — 안티패턴

안티패턴 1. 모든 걸 한 줄에 우겨넣는다

RUN apt-get update && apt-get install -y nodejs && git clone ... && npm ci && npm run build

레이어 캐시를 활용 못 한다.

안티패턴 2. 루트로 실행한다

기본 도커는 root로 실행. 보안 사고가 호스트까지 영향 줄 수 있다.

USER app 으로 비루트 전환

안티패턴 3. 비밀 키 · 토큰을 이미지에 박는다

ENV DB_PASSWORD=mypassword123

이미지에 남으면 어디서든 읽을 수 있다.

비밀은 Secrets Manager · Parameter Store · 환경 변수로 주입

안티패턴 4. OS 도구를 가득 깔아둔다

vim, curl, ssh 가 다 들어가면 공격 표면이 넓어진다.

실행에 필요한 것만 남기고 distroless 고려


10. 한 줄로 정리

Dockerfile은 이미지의 설계도이며,
레이어 캐시와 multi-stage 빌드를 살리는 게 곧 운영 품질이다


11. 이 장의 핵심 정리

  1. 이미지는 설계도, 컨테이너는 실행 인스턴스다.
  2. 이미지는 레이어의 누적이며, COPY/RUN 순서가 캐시를 결정한다.
  3. Multi-stage build로 빌드 도구를 최종 이미지에서 분리한다.
  4. 비루트 사용자, 작은 베이스, 비밀 제거가 보안의 기본이다.
  5. /health 엔드포인트와 HEALTHCHECK는 모든 운영 이미지에 있어야 한다.