Dockerfile
Dockerfile은 컨테이너 이미지를 만들기 위한 텍스트 기반 빌드 명세입니다. 베이스 이미지 선택, 파일 복사, 의존성 설치, 실행 명령까지 이미지에 들어갈 모든 것을 순서대로 선언합니다. docker build 명령이 이 파일을 읽고 각 명령어를 레이어 단위로 실행해 최종 이미지를 만들어 냅니다.
▶아키텍처 다이어그램
🔄 프로세스 다이어그램점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다
애플리케이션을 서버에 배포하려면 런타임, 라이브러리, 설정 파일, 환경 변수가 정확히 맞아야 합니다. 수동으로 서버에 접속해 패키지를 설치하고 설정을 고치는 방식은 사람마다 순서가 다르고, 실수가 끼어들고, 나중에 어떤 상태로 만들었는지 추적이 안 됩니다. 개발 환경에서는 되던 것이 운영 서버에서 안 되는 일이 반복되고, 새 팀원이 합류하면 환경 셋업에 하루 이상이 걸리기도 합니다. 서버가 한두 대일 때는 어찌어찌 되지만, 서비스가 늘고 서버가 수십 대로 늘어나면 수동 설정은 유지 가능한 방식이 아닙니다.
서버 구성을 코드로 관리하려는 시도는 Dockerfile 이전에도 있었습니다. 셸 스크립트로 설치 순서를 기록하거나, 구성 관리 도구로 서버 상태를 선언하는 방식이 쓰였습니다. 하지만 셸 스크립트는 실행 환경에 따라 결과가 달라질 수 있었고, 구성 관리 도구는 이미 돌아가는 서버의 상태를 맞추는 데 초점이 있어서 깨끗한 빌드를 보장하기 어려웠습니다. Dockerfile이 가져온 변화는 빌드의 출발점을 항상 알려진 베이스 이미지로 고정하고, 거기서부터 명령어 하나하나를 레이어로 쌓아 올리는 구조를 만든 것입니다. 덕분에 같은 Dockerfile은 누가, 어디서 실행하든 동일한 결과를 내는 불변 이미지를 만들 수 있게 됐습니다. 이 재현성이 CI/CD 파이프라인과 컨테이너 오케스트레이션의 전제가 되면서, Dockerfile은 컨테이너 빌드의 사실상 표준 명세로 자리잡았습니다.
Dockerfile의 각 명령어는 이미지 레이어 하나에 대응합니다. FROM으로 출발점이 되는 베이스 이미지를 지정하면, 그 위에 COPY로 소스코드와 설정 파일을 복사하고, RUN으로 패키지 설치나 빌드 명령을 실행합니다. 이 과정에서 생긴 파일 시스템 변화가 각각 하나의 읽기 전용 레이어로 저장됩니다. 레이어 구조가 중요한 이유는 캐시 때문입니다. docker build는 명령어와 그 입력이 이전 빌드와 같으면 해당 레이어를 다시 실행하지 않고 캐시를 재사용합니다. 그래서 변경이 잦은 소스코드 복사는 뒤쪽에, 변경이 드문 의존성 설치는 앞쪽에 배치하는 것이 빌드 시간을 줄이는 핵심 전략입니다. 마지막으로 CMD나 ENTRYPOINT는 이 이미지로 컨테이너를 실행할 때 어떤 프로세스를 시작할지 선언합니다. 빌드 시점에 실행되는 RUN과 달리, CMD는 컨테이너가 뜰 때 실행됩니다.
기본 구조
# 1. 베이스 이미지
FROM node:20-alpine
# 2. 작업 디렉토리 설정
WORKDIR /app
# 3. 의존성 파일 먼저 복사 (캐시 활용)
COPY package.json package-lock.json ./
RUN npm ci
# 4. 소스코드 복사
COPY . .
# 5. 빌드
RUN npm run build
# 6. 포트 선언 및 실행 명령
EXPOSE 3000
CMD ["node", "dist/server.js"]package.json을 먼저 복사하고 npm ci를 실행한 뒤에 소스코드를 복사하는 순서가 핵심입니다. 소스코드만 바뀌었을 때 의존성 설치 레이어는 캐시에서 재사용되므로 빌드가 빨라집니다.
CMD vs ENTRYPOINT
# CMD — 기본 실행 명령 (docker run 인자로 덮어쓸 수 있음)
CMD ["npm", "start"]
# ENTRYPOINT — 고정 실행 명령 (인자가 뒤에 붙음)
ENTRYPOINT ["node", "server.js"]
# 조합 — ENTRYPOINT가 고정, CMD가 기본 인자
ENTRYPOINT ["node"]
CMD ["server.js"]CMD는 컨테이너 실행 시 기본 명령을 제공하지만 docker run에서 다른 명령으로 교체할 수 있습니다. ENTRYPOINT는 항상 실행되는 고정 진입점이고, CMD와 조합하면 기본 인자를 분리할 수 있습니다.
Dockerfile과 Docker Compose는 둘 다 컨테이너 환경을 코드로 정의한다는 공통점이 있지만, 다루는 범위가 다릅니다. Dockerfile은 하나의 이미지를 어떻게 만들 것인가에 집중합니다. 어떤 베이스 이미지에서 출발해 어떤 파일을 넣고 어떤 명령을 실행해 최종 이미지를 완성하는지가 Dockerfile의 관심사입니다. Docker Compose는 이미 만들어진 이미지들을 어떻게 함께 실행할 것인가를 다룹니다. 웹 서버, 데이터베이스, 캐시를 한 번에 띄우고 네트워크와 볼륨을 연결하는 것이 Compose의 역할입니다. Dockerfile과 셸 스크립트도 비교되곤 합니다. 셸 스크립트로도 서버 환경을 설정할 수 있지만, 실행하는 시점의 OS 상태에 따라 결과가 달라질 수 있습니다. Dockerfile은 FROM으로 출발점을 고정하기 때문에 실행 환경의 차이를 제거합니다.
Dockerfile은 개발자가 로컬에서 검증한 빌드 규칙을 CI와 운영 환경까지 그대로 전달하는 연결점입니다. 같은 Dockerfile로 이미지를 만들면 어떤 파일과 의존성, 시작 명령으로 실행할지가 문서가 아니라 실제 산출물에 고정됩니다. 실무에서 중요한 판단은 어느 단계가 자주 바뀌는지, 어떤 도구를 최종 이미지에 남길지입니다. 캐시를 살릴 수 있게 레이어 순서를 잡고, 운영에 불필요한 빌드 도구는 최종 이미지 밖으로 밀어내야 배포 속도와 추적성이 함께 좋아집니다. 그래서 Dockerfile은 단순한 문법 파일이라기보다 빌드 재현성과 배포 책임 경계를 결정하는 설계 문서에 가깝습니다.