Quick Syntax
FROM node:22-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
CMD ["npm", "start"]RUN은 빌드 중 실행되고, CMD와 ENTRYPOINT는 컨테이너 시작 시의 기본 동작을 정합니다. ENTRYPOINT는 실행 인자가 아니라는 뜻이 아니며, 필요하면 docker run --entrypoint로 명시적으로 바꿀 수 있습니다.
핵심 지시어
FROM — 베이스 이미지 선택이 이미지 크기와 보안의 시작점이다
FROM은 새 이미지의 기반이 될 베이스 이미지를 지정합니다. alpine 계열은 작고 musl libc를 쓰며, slim은 불필요한 패키지를 줄인 Debian 계열입니다. 전체 이미지(예: node:22)는 진단 도구와 호환성이 필요한 개발 환경에 유리할 수 있습니다. 운영 이미지는 크기만으로 고르지 말고 native dependency 호환성, 패치 제공, CA 인증서·시간대·사용자 계정 같은 실행 요구를 함께 확인합니다.
FROM node:22-alpine # 경량, 운영용 권장
FROM node:22-slim # Debian 기반, 호환성 좋음
FROM node:22 # 전체 이미지, 개발 편의용RUN vs CMD vs ENTRYPOINT — 빌드 시점과 런타임 시점을 구분한다
RUN은 이미지 빌드 중에 실행되는 명령입니다. npm ci, pip install, 패키지 설치처럼 이미지를 만드는 과정에 필요한 작업에 씁니다. CMD는 컨테이너가 시작될 때 기본으로 실행할 명령을 지정하며, docker run에서 다른 명령을 붙이면 덮어씌워집니다. exec form ENTRYPOINT는 CMD를 기본 인자로 받는 패턴에 쓰고, 추가 인자는 보통 CMD만 바꿉니다. 실행 파일 자체는 docker run --entrypoint로 명시적으로 교체할 수 있습니다.
# 빌드 시 의존성 설치
RUN npm ci
# 컨테이너 시작 시 기본 명령 (docker run으로 덮어씌울 수 있음)
CMD ["npm", "start"]
# ENTRYPOINT + CMD 조합 — 항상 node를 실행하되 인자만 교체 가능
ENTRYPOINT ["node"]
CMD ["server.js"]
# docker run my-app worker.js → node worker.js로 실행됨COPY와 WORKDIR — 파일 위치와 작업 디렉터리를 명확히 해야 레이어 캐시가 효율적이다
WORKDIR은 이후 모든 지시어(COPY, RUN, CMD)의 기본 작업 디렉터리를 설정합니다. 별도 지정 없이 루트(/)에 파일을 쌓으면 관리가 어려워집니다. COPY는 빌드 컨텍스트의 파일을 이미지로 복사하며, 복사 순서가 레이어 캐시 효율에 직접 영향을 줍니다. 자주 바뀌는 소스 코드는 나중에 복사해야 의존성 설치 캐시가 유지됩니다.
WORKDIR /app
# 의존성 파일 먼저 복사 (소스 변경이 캐시에 영향 없음)
COPY package.json package-lock.json ./
RUN npm ci
# 소스는 나중에 복사
COPY src/ ./src/
COPY tsconfig.json ./RUN, CMD, ENTRYPOINT는 빌드 시점과 실행 시점을 나눠서 봐야 한다
RUN은 이미지를 만드는 동안 실행되고, CMD와 ENTRYPOINT는 컨테이너가 시작될 때 영향을 줍니다. 설치 명령을 CMD에 넣으면 컨테이너가 켜질 때마다 다시 설치를 시도하게 되고, 기본 실행 명령을 RUN에 넣으면 빌드 중 한 번만 실행되고 끝납니다. 이 구분을 먼저 잡아야 Dockerfile이 자연스럽게 읽힙니다.
# 빌드 시점
RUN npm ci
# 런타임 시점
CMD ["npm", "start"]이미지 보안과 캐시
Dockerfile은 단순 셸 스크립트가 아니라 레이어 구조를 가진 빌드 정의다. RUN 명령 하나가 레이어 하나를 만들고, 중간 레이어의 파일이 최종 이미지에 포함되므로 비밀 파일을 RUN으로 복사하고 나중에 삭제해도 이미지에서 추출할 수 있다. 민감 파일은 처음부터 빌드 컨텍스트에 포함시키지 않거나 BuildKit secret을 사용해야 한다.
참고 링크
2 sources