| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |
- PHP
- dhgrp
- SQL문법
- 디자인패턴
- Tool Calling
- docker
- linux 권한
- 객체복제
- 소프트웨어설계
- 데코레이터패턴
- 객체지향
- MCP
- LLM
- mcp client
- Agent Loop
- 데이터베이스
- PHP객체지향
- MySQL
- docker network
- 어댑터패턴
- Laravel
- SQL기초
- SQL
- ai agent
- mcp server
- OOP
- 인덱스
- 라라벨
- 매직메서드
- ai 에이전트
- Today
- Total
개발블로그
Dockerfile 본문
개념
개념
Docker Image를 어떻게 만들 것인지 정의한 설정 파일
Application 실행에 필요한 환경을 코드 형태로 작성해두고,
Docker은 Dockerfile의 명령을 순서대로 실행하여 Image를 생성함
Application의 실행 환경을 코드로 정의하여 동일한 Image를 반복해서 만들 수 있게 하는 것
의의
직접 Server나 Container 내부에 들어가서 PHP, Nginx, Library 등을 하나씩 설치하면 환경을 다시 만들 때 동일한 작업을 반복해야 함.
=> Dockerfile에 필요한 환경을 정의하면 동일한 환경을 반복해서 생성할 수 있다
예시
예를들어 PHP Application을 실행하려면 다음과 같은 환경이 필요할 수 있음
=> PHP, PHP Extension, Application Source Code, 설정 File, Library, 실행 Command
이를 Dockerfile에 정의한다
Dockerfile
↓
Docker가 명령을 순서대로 실행
↓
PHP와 필요한 Extension 설치
↓
Source Code 복사
↓
Docker Image 생성
명령어
| 명령어 | 의미 | 옵션 | 예시 |
| FROM 기본 이미지 [AS name ] | 기본 이미지 선택 (어떤 환경에서 시작할 것인가) ex) PHP가 없는 환경에서 모든 것을 처음부터 설치(X) PHP 8.3 FPM이 준비된 Image를 기반으로 새로운 Image 구성 (O) |
FROM php:8.3-fpm ex) FROM node:22 AS builder = node:22 Image를 기반으로 새로운 작업 공간을 하나 만들고 그 공간 이름을 builder라고 하겠다. |
|
| RUN | 이미지 빌드 중 명령 실행 | RUN apt-get update | |
| COPY | 파일 복사 (내 프로젝트 파일을 이미지 안으로 복사 : Host의 파일 => Image 내부로) - 필요한 파일만 복사할 수도 있음 ex) 현재 Directory가 다음과 같다면 Project ├─ index.php ├─ composer.json └─ src/ => COPY ... 실행 후 Image 내부 /var/www/html ├─ index.php ├─ composer.json └─ src/ • 단, 개발환경에서는 volumn을 더 많이 씀 ⇒ 내 pc 코드 수정이 컨테이너 안에도 바로 반영됨. |
--from = builder => 앞 Stage에서 만든 File을 현재 Stage로 가져옴. |
COPY . . - 필요한 파일만 복사 COPY composer.json composer.lock ./ |
| ADD | COPY와 비슷하지만 압축 해제나 URL 처리 같은 추가 기능이 있음. • 일반적인 프로젝트 파일 복사는 보통 COPY를 더 많이 씀 |
||
| WORKDIR | 작업 디렉터리 설정 → 이후 명령어들은 이 위치를 기준으로 실행됨. |
WORKDIR /app | |
| CMD | 컨테이너가 시작될 때 기존으로 실행할 명령어 • 기본 실행 명령 |
CMD ["php-fpm"] | |
| ENTRYPOINT | CMD와 비슷하게 컨테이너 시작 명령을 정함. • 반드시 실행되는 시작점 - Container에서 실행할 기본 Program 자체를 고정하는 용도로 사용할 수 있다. |
ENTRYPOINT ["php"] | |
| EXPOSE | 컨테이너가 사용할 포트 표시 • 실제로 포트를 외부에 열어주는 명령이 아님 ⇒ 실제로 내 PC에서 접속하려면 Compose나 docker run에서 ports를 설정해야 함. |
EXPOSE 8080 | |
| ENV | 환경변수 설정 단, Password, API Key 같은 Secret을 Dockerfile에 직접 넣는 용도로 사용하면 안됨 |
ENV APP_ENV=production | |
| ARG | Image Build 과정에서 사용할 변수 정의 | ARG PHP_VERSION=8.3 |
Dockerfile 설계
개념
단순히 Application이 실행되도록 명령어를 나열하는 것이 아니라,
작고, 빠르고, 재현 가능하며, 안전한 Docker Image를 만들도록 Dockerfile의 구조와 명령 순서를 결정하는 것
실행 가능
+
Build가 빠름
+
Image가 불필요하게 크지 않음
+
환경을 재현할 수 있음
+
민감한 정보가 포함되지 않음
+
보안상 불필요한 권한이 없음
+
유지보수하기 쉬움
고려할 사항
적절한 Base Image 선택
Base Image는 최종 Image의 크기, 보안, Package 설치 방법 등에 영향을 준다.
| 고려사항 | 내용 |
| Image 크기 | 불필요하게 큰 Image인지 |
| 필요한 Library | Application에서 필요한 Package를 지원하는지 |
| 호환성 | 사용하는 Runtime / Extension과 문제없는지 |
| 관리 편의성 | Package 설치와 문제 해결이 쉬운지 |
| 보안 | 오래되거나 지원 종료된 Image인지 |
Docker Layer를 고려해 명령 순서 설계
Docker Image는 Dockerfile의 명령을 실행하면서 여러 Layer로 구성됨
이전 Build에서 만들어진 Layer를 다시 사용할 수 있는 경우 Cache를 사용
=> 자주 변경되지 않는 작업은 위쪽에, 자주 변경되는 작업은 아래쪽에 배치하는 것이 일반적
ex)
잘못된 예)
COPY . .
RUN composer install
Source Code 하나만 수정되어도 COPY .. Layer가 변경됨.
그럼 이후의 RUN composer install도 다시 실행될 가능성이 높음.
보완)
COPY composer.json composer.lock ./
RUN composer install
COPY . .
의존성을 정의하는 File를 먼저 복사함
Souce Code만 변경되었다면 composer.json이나 composer.lock이 변경되지 않았으므로 기존 Dependency 설치 Layer를 재사용할 수 있음
필요한 File만 Image에 포함
.dockerigonre 를 이용하여 필요 없는 파일 제외
장점
- Build Context 감소
- Image 크기 감소
- Build 속도 개선
- .env 같은 민감한 File이 Image에 들어가는 실수 방지
하나의 RUN에서 불필요한 Layer 줄이기
서로 관련있는 명령은 하나의 RUN으로 묶음
(명령어 마다 각각 별도의 Layer가 생성되기 때문)
+
특히 Package 설치 Cache까지 정리하면 최종 Image에 불필요한 데이터가 남는 것을 줄일 수 있다.
ex)
RUN apt-get update
RUN apt-get install -y git
RUN apt-get install -y unzip
위의 명령을 개선
RUN apt-get update \
&& apt-get install -y \
git \
unzip \
&& rm -rf /var/lib/apt/lists/*
Build 단계와 실행 단계를 구분
Build 단계 : Application을 실행할 수 있는 결과물을 만드는 데 필요한 환경
실행(Runtime) 단계 : 만들어진 결과물을 실제로 실행하는 데 필요한 환경
ex) React + TypeScript 프로젝트
[dockerfile]
# Node 환경 하나 만들기 : node:22 Image를 기반으로 새로운 작업 공간 하나 만들고, 그 이름을 builder라고 하기
FROM node:22 AS builder
# 거기에 Source 코드를 만든다, 그리고 현재 프로젝트 파일을 builder Stage에 넣음
WORKDIR /app
COPY . .
# npm install
RUN npm install
# npm run build를 해서 dist를 만들기
RUN npm run build
# 이제 Nginx 환경을 새로 하나 만들기
FROM nginx:alpine
# 앞에서 만든 dist만 Nginx 환경으로 가져오고 마지막 Nginx 환경을 최종 Docker Image로 사용
COPY --from=builder /app/dist /usr/share/nginx/html
---------------------------------------------------------------------------------------------------
builder Stage
┌─────────────────┐
│ Node.js │
│ npm │
│ Vite │
│ Source │
│ │
│ dist ────────────────┐
└─────────────────┘ │
│ COPY
↓
Runtime Stage
┌─────────────────┐
│ Nginx │
│ │
│ dist │
└─────────────────┘
Dependency 버전을 명확하게 관리
명확한 Version을 지정해서 환경 재현성을 높이기
ex)
FROM php:latest
=> latest는 시간이 지나면서 실제 Image가 변경될 수 있다.
FROM php:8.3-fpm
Build 시점과 Container 실행 시점을 구분
RUN => Image를 Build할 때 실행 됨
CMD => Container가 실행될 때 실행 됨
따라서 해당 작업이 Image를 만드는 데 필요한 작업인지, Container가 시작될 때 필요한 작업인지를 구분해야 함.
Application 설정을 Image에 직접 고정하지 않기
민감한 값은 Image에 포함하지 않는 것이 좋음.
대신 실행 시 환경변수 등을 통해 전달함
Dockerfile
→ Application을 실행할 수 있는 공통 환경
Environment Variable
→ 실행 환경별 설정
으로 분리하는 것이 좋음.
하나의 Image를 여러 환경에서 사용할 수 있음.
가능하면 Root가 아닌 User로 실행
보안을 고려한다면 Application 실행에 필요한 권한만 가진 User를 사용하는 것이 좋음.
Container의 Main Process를 명확하게 설정
[ Main Process ]
Container가 시작될 때 가장 중심이 되어 실행되는 Process
=> Container 안에 여러 Process가 존재할 수는 있지만, 그중 Container의 생명주기를 대표하는 중심 Process가 존재
- 리눅스 기준으로는 보통 이 Process가 PID 1이 됨
ex) CMD ["php-fpm"]
=> Container가 실행될 때 php-fpm이 Main Process 역할을 함.
[ Container 실행 상태 ]
Container는 하나의 Main Process를 기준으로 실행 상태가 관리됨
Container 시작
↓
Main Process 실행
↓
Main Process 실행 중
↓
Container 실행 중
↓
Main Process 종료
↓
Container 종료
- Main Process가 종료되면 Container도 종료됨
ex) CMD ["echo", "hello"]
=> Container 시작 → echo hello 실행 → hello 출력 → echo 종료 → Container 종료
- Web Server를 Main Process로 사용하는 이유
=> Server Process (ex PHP-FPM, Node.js Server, Nginx)는 실행된 후 Request를 기다리면서 계속 동작함.
ex) CMD ["php-fpm"]
=> Container 시작 → php-fpm 실행 → Request 대기 → 계속 실행 → Container 유지
=> Dockerfile에서는 Container가 시작될 때 실행할 핵심 Process를 명확하게 지정해야 함.
[ 고려사항 ]
- Container가 시작되면 무슨 Process를 실행할 것인가?
- 그 Process가 계속 실행되는가?
- Docker의 종료 Signal을 제대로 받을 수 있는가?
- Main Process가 종료되었을 때 Container도 정상적으로 종료되는가?
[ 핵심 ]
- 실제 Application Server를 Main Process로 실행
- Server는 Background가 아니라 Foreground에서 실행
=> 일반적인 Server 환경에서는 Program을 Background로 실행하기도 함.
하지만 Container에서는 Main Process가 Background로 빠지고 원래 Process가 종료되면,
Container가 종료될 수 있음.
ex) CMD ["nginx", "-g", "daemon off;"]
: daemon off;는 Nginx를 Background로 보내지 않고 Foreground에서 실행 - Docker의 종료 Sinal을 Applciation이 직접 받을 수 있도록 구성
: docker stop → SIGTERM → Main Process → 정리 작업 실행 → Process 종료 → Container 종료
- CMD는 Exec Form을 사용하는 것이 좋음 (Shell Form 지양)
ex) CMD ["node", "server.js"] => Node Process가 직접 Main Process가 됨
ex) CMD node server.js => Shell이 중간에 존재함. 이 경우 Docker의 종료 Signal이 Application
에 직접 전달되지 않을 수 있음
'STUDY > Docker' 카테고리의 다른 글
| Windows + WSL 2에서 Docker 설치 및 설정하기 (Docker Desktop 설치 안함) (0) | 2026.08.27 |
|---|---|
| Docker 명령어 모음 (0) | 2026.08.26 |
| Docker (0) | 2026.08.26 |
| Docker Volumn과 데이터 영속성 (0) | 2026.08.26 |
| Docker Container의 Port와 Network 통신 구조 (0) | 2026.08.26 |