Dockerfile

2025. 6. 13. 15:02·Infra/Docker

Docker를 사용하다 보면 기존에 제공되는 이미지만으로는 우리의 요구사항을 충족하기 어려운 경우가 많습니다. 특정 애플리케이션을 실행하거나, 개발 환경에 맞는 커스텀 설정이 필요할 때가 그런 상황입니다. Dockerfile은 이러한 문제를 해결해주는 강력한 도구로, 우리만의 맞춤형 Docker 이미지를 생성할 수 있게 해줍니다.

Dockerfile의 개념과 필요성

Docker 이미지 커스터마이징의 한계

Docker Hub에서 제공하는 기본 이미지들은 범용적으로 설계되어 있어 특정 프로젝트의 요구사항을 완벽하게 만족하기 어렵습니다. 예를 들어, Node.js 이미지를 다운로드받아도 우리의 애플리케이션 코드는 포함되어 있지 않고, 필요한 의존성 패키지도 설치되어 있지 않습니다.

매번 컨테이너를 실행할 때마다 수동으로 파일을 복사하고 패키지를 설치하는 것은 비효율적일 뿐만 아니라 휴먼 에러가 발생할 가능성도 높습니다. 이런 반복적인 작업을 자동화하고 표준화하는 것이 Dockerfile의 핵심 가치입니다.

Dockerfile이 제공하는 해결책

Dockerfile은 Docker 이미지를 만들게 해주는 스크립트 파일입니다. 텍스트 파일에 일련의 명령어들을 작성하면, Docker가 이를 순차적으로 실행하여 우리가 원하는 환경이 구성된 이미지를 자동으로 생성해줍니다.

이는 마치 요리 레시피와 같은 개념입니다. 재료(베이스 이미지)부터 시작해서 단계별 조리 과정(각종 명령어들)을 거쳐 최종적으로 완성된 요리(커스텀 이미지)를 만들어내는 것입니다.

베이스 이미지 설정하기

FROM 명령어의 역할

모든 Dockerfile은 FROM 명령어로 시작됩니다. 이 명령어는 우리가 만들고자 하는 이미지의 기반이 될 베이스 이미지를 지정합니다.

FROM openjdk:17-jdk

이 한 줄만으로도 OpenJDK 17이 설치된 Linux 환경을 얻을 수 있습니다. FROM 명령어는 특정 초기 이미지를 기반으로 추가적인 설정을 할 수 있게 해주며, 이때 '특정 초기 이미지'가 바로 베이스 이미지입니다.

태그 명시의 중요성

베이스 이미지를 선택할 때는 태그를 명시적으로 지정하는 것이 좋습니다.

FROM node:20-alpine

태그를 생략하면 자동으로 latest 태그가 적용되는데, 이는 예상치 못한 버전 변경으로 인한 호환성 문제를 야기할 수 있습니다. alpine 태그는 필수 기능만을 가지는 경량화된 버전으로, 이미지 크기를 줄이고 보안을 강화하는 데 도움이 됩니다.

첫 번째 이미지 빌드해보기

간단한 Dockerfile을 작성해서 이미지를 빌드해보겠습니다.

FROM openjdk:17-jdk

# 컨테이너가 바로 종료되지 않도록 하는 임시 명령어
ENTRYPOINT ["/bin/bash", "-c", "sleep 500"]

이미지 빌드는 docker build 명령어를 사용합니다.

docker build -t my-jdk17-server .

여기서 -t 옵션은 태그를 지정하는 것이고, 마지막의 .은 Dockerfile이 존재하는 디렉토리 경로를 의미합니다. 현재 디렉토리에 Dockerfile이 있으므로 .을 사용했습니다.

파일 복사와 애플리케이션 배포

COPY 명령어로 파일 전달하기

실제 애플리케이션을 배포하려면 호스트 컴퓨터에 있는 소스 코드나 빌드 파일을 컨테이너로 복사해야 합니다. 이때 사용하는 것이 COPY 명령어입니다.

COPY [호스트 컴퓨터의 파일 경로] [컨테이너 내부 경로]

기본적인 파일 복사부터 시작해보겠습니다.

FROM ubuntu

# my-app 디렉토리의 모든 내용을 컨테이너의 /my-app/ 디렉토리에 복사
COPY my-app /my-app/

ENTRYPOINT ["/bin/bash", "-c", "sleep 500"]

슬래시(/)를 명시하는 것은 "디렉토리로 복사하겠다"는 의도를 명확히 표현하는 방법입니다.

와일드카드와 선택적 복사

특정 패턴의 파일들만 복사하고 싶을 때는 와일드카드를 사용할 수 있습니다.

FROM ubuntu

# 모든 텍스트 파일을 복사
COPY *.txt /text-files/

ENTRYPOINT ["/bin/bash", "-c", "sleep 500"]

.dockerignore로 불필요한 파일 제외하기

모든 파일을 복사할 때 특정 파일이나 폴더를 제외하고 싶다면 .dockerignore 파일을 활용할 수 있습니다.

readme.txt
node_modules
.git

이렇게 설정하면 COPY 명령어 실행 시 해당 파일들은 무시됩니다.

FROM ubuntu

# 전체 복사 (단, .dockerignore에 명시된 파일들은 제외)
COPY ./ /

ENTRYPOINT ["/bin/bash", "-c", "sleep 500"]

컨테이너 실행 명령어 설정

ENTRYPOINT의 역할과 중요성

ENTRYPOINT는 컨테이너가 생성되고 최초로 실행할 때 수행되는 명령어를 지정합니다. 이는 컨테이너의 주 프로세스가 되며, 이 프로세스가 종료되면 컨테이너도 함께 종료됩니다.

ENTRYPOINT ["java", "-jar", "/app.jar"]

컨테이너 종료 문제 해결하기

자주 마주치는 문제 중 하나는 컨테이너가 예상보다 빨리 종료되는 현상입니다. 이는 ENTRYPOINT로 지정된 프로세스가 완료되면 컨테이너가 자동으로 종료되기 때문입니다.

디버깅을 위해 다음과 같은 방법을 사용할 수 있습니다.

FROM ubuntu

# hello 출력 후 바로 종료 (logs로 확인 가능)
ENTRYPOINT ["/bin/bash", "-c", "echo hello"]

또는 일정 시간 동안 컨테이너를 유지하려면 다음과 같이 할 수 있습니다.

FROM ubuntu

# 500초 동안 컨테이너 유지
ENTRYPOINT ["/bin/bash", "-c", "sleep 500"]

이미지 빌드 과정에서의 명령 실행

RUN 명령어의 특성

RUN 명령어는 이미지 생성 과정에서 필요한 명령어를 실행할 때 사용합니다. ENTRYPOINT와의 가장 큰 차이점은 실행 시점입니다.

FROM ubuntu

# 이미지 빌드 시점에 git 설치
RUN apt update && apt install -y git

ENTRYPOINT ["/bin/bash", "-c", "sleep 500"]

RUN과 ENTRYPOINT의 실행 시점 비교

이 두 명령어의 차이점을 명확히 이해하는 것이 중요합니다.

  • RUN과 COPY: 이미지 생성(빌드) 시 실행되며, 결과는 이미지에 저장됩니다
  • ENTRYPOINT와 CMD: 이미지를 기반으로 컨테이너를 실행할 때 실행됩니다

따라서 패키지 설치나 환경 설정은 RUN으로, 애플리케이션 실행은 ENTRYPOINT로 처리하는 것이 적절합니다.

작업 디렉토리 관리

WORKDIR의 필요성

컨테이너 내부에서 작업할 때 기존 시스템 파일들과 우리의 애플리케이션 파일들이 뒤섞이면 관리가 어려워집니다. WORKDIR 명령어를 사용하면 명확한 작업 공간을 설정할 수 있습니다.

FROM ubuntu

# /my-dir을 작업 디렉토리로 설정
WORKDIR /my-dir

# 현재 디렉토리의 모든 파일이 /my-dir 내부로 복사됨
COPY . .

ENTRYPOINT ["/bin/bash", "-c", "sleep 500"]

WORKDIR로 작업 디렉토리를 전환하면 그 이후에 등장하는 모든 RUN, CMD, ENTRYPOINT, COPY, ADD 명령문은 해당 디렉토리를 기준으로 실행됩니다.

포트 문서화

EXPOSE 명령어는 컨테이너 내부에서 어떤 포트에 프로그램이 실행되는지를 문서화합니다. 실제 포트 매핑은 컨테이너 실행 시점에 결정되지만, EXPOSE를 통해 개발자들에게 어떤 포트를 사용하는지 알려줄 수 있습니다.

# 컨테이너가 3000번 포트를 사용함을 명시
EXPOSE 3000

실전 애플리케이션 배포 사례

Spring Boot 애플리케이션 배포

Spring Boot 프로젝트를 Docker로 배포하는 과정을 살펴보겠습니다.

FROM openjdk:17-jdk

# 빌드된 JAR 파일을 컨테이너로 복사
COPY build/libs/*SNAPSHOT.jar app.jar

ENTRYPOINT ["java", "-jar", "/app.jar"]

배포 과정은 다음과 같습니다.

# Spring Boot 애플리케이션 빌드
./gradlew clean build

# Docker 이미지 빌드
docker build -t hello-server .

# 컨테이너 실행
docker run -d -p 8080:8080 hello-server

Node.js 애플리케이션 배포

Nest.js나 Next.js 같은 Node.js 기반 애플리케이션도 유사한 방식으로 배포할 수 있습니다.

FROM node:20-alpine

WORKDIR /app

# 소스 코드 복사
COPY . .

# 이미지 빌드 시점에 의존성 설치
RUN npm install

# 애플리케이션 빌드
RUN npm run build

# 포트 정보 문서화
EXPOSE 3000

# 애플리케이션 실행
ENTRYPOINT ["npm", "run", "start"]

.dockerignore 파일로 불필요한 파일들을 제외합니다.

node_modules
.git
.next

정적 웹사이트 배포

Nginx를 사용한 정적 웹사이트 배포는 매우 간단합니다.

FROM nginx

# HTML, CSS 파일들을 Nginx 웹 루트로 복사
COPY ./ /usr/share/nginx/html

HTML 파일을 준비하고 이미지를 빌드합니다.

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>My Web Page</h1>
</body>
</html>
# 이미지 빌드
docker build -t my-web-server .

# 컨테이너 실행 (80번 포트로 매핑)
docker run -d -p 80:80 my-web-server

마무리

Dockerfile을 마스터하면 개발 환경의 일관성을 보장하고, 배포 과정을 자동화할 수 있습니다. FROM으로 시작해서 각종 설정과 파일 복사를 거쳐 ENTRYPOINT로 마무리하는 일련의 과정을 통해, 어떤 종류의 애플리케이션이든 Docker 이미지로 패키징할 수 있습니다.

'Infra > Docker' 카테고리의 다른 글

Docker Compose 2  (3) 2025.06.17
Docker Compose 1  (7) 2025.06.16
Docker Volume  (4) 2025.06.09
자주 사용되는 Docker CLI  (6) 2025.06.07
Docker 기본 개념  (2) 2025.06.05
'Infra/Docker' 카테고리의 다른 글
  • Docker Compose 2
  • Docker Compose 1
  • Docker Volume
  • 자주 사용되는 Docker CLI
leve68
leve68
leve68 님의 블로그 입니다.
  • leve68
    leve68
    leve68
  • 전체
    오늘
    어제
    • 분류 전체보기
      • BackEnd
        • Spring Framework
        • Database
      • FrontEnd
        • JavaScript
        • Vue
      • Infra
        • Docker
        • CI CD
      • CS
        • Algorithm
      • Project
        • Web
  • 인기 글

  • 태그

    DATABASE
    sql
    MyBatis
    compose
    SSAFY
    spring security
    Spring
    springboot
    MySQL
    docker
  • 링크

    • github
    • portfolio
  • 최근 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.3
leve68
Dockerfile
상단으로

티스토리툴바