
이번에는 docker 의 기초기능에 이어서 docker의 고급 기능에 대해서 알아보고자 한다.
docker build 명령어를 실행하면 도커 이미지가 생성된다. 이때 코드 및 환경은 Read-Only로 저장되는데, 그럼 데이터는 어떻게 관리할까?
도커 이미지는 여러 개의 이미지 레이어(Image Layer) 로 구성되고, 컨테이너가 실행될 때 최상단에 쓰기 가능한 컨테이너 레이어(Container Layer) 가 추가된다.
Container
├── Container Layer ← Writable
├── Image Layer
├── Image Layer ← Non-Writeable
├── Image Layer
└── Base Layer
| 종류 | 설명 |
|---|---|
| Application | Code와 환경 (종속성). Read-Only로 저장되며 이미지와 컨테이너에 포함 |
| 임시 App 데이터 | 컨테이너 내부에서 생성되는 데이터 (변수, 임시 파일 등). 메모리에 저장되고 컨테이너 실행 중에만 존재 |
| Permanent App 데이터 | 컨테이너가 종료되어도 남아있어야 하는 데이터. 데이터베이스, 로그 파일, 사용자 업로드 파일 등 |
💡 Permanent App 데이터를 다루려면 볼륨(Volume) 이 필요하다.
볼륨 = 우리가 지정한 폴더
볼륨은 호스트 머신에서 폴더들로 구성되고, 컨테이너가 생성 또는 시작될 때 마운트(Mount, mapped) 된다.
Dockerfile에서 COPY와 VOLUME의 차이점을 명확히 구분해야 된다.
| 명령어 | 설명 |
|---|---|
COPY | 파일을 이미지에 복사 |
VOLUME | 호스트의 파일을 컨테이너에 마운트 |
💡 마운트란? 다른 컴퓨터에 있는 디렉토리를 내 컴퓨터로 가져와서 사용하는 것
이름을 가지고 있는 볼륨으로, Dockerfile에서는 생성할 수 없고 명령어로 생성해야 된다.
# 볼륨 생성
docker volume create <volume_name>
# 볼륨 목록 확인
docker volume ls
# 볼륨 삭제
docker volume rm <volume_name>
컨테이너 생성 시 Named Volume 연결:
docker run -v <volume_name>:<directory> <image_name>
이름이 없는 볼륨으로 임시 볼륨이라고도 한다.
볼륨은 도커 데몬이 관리하기 때문에 호스트와 컨테이너 간의 데이터 공유가 어렵다. 이럴 때 Bind Mount를 쓰면 된다.
Bind Mount 생성:
docker run -v <host_directory>:<container_directory> <image_name>
$(pwd):/app"%cd%":/app| 구분 | Volume | Bind Mount |
|---|---|---|
| 관리 주체 | Docker 데몬 | 사용자 (호스트) |
| 경로 지정 | 볼륨 이름 | 호스트 절대 경로 |
| 주요 용도 | DB, 영구 데이터 저장 | 코드, 실시간 업데이트 |
| 컨테이너 삭제 시 | Named: 유지 / Anonymous: 삭제 | 호스트 파일 유지 |
Docker Host
├── Volumes → Docker Area (Docker 관리)
└── Bind Mounts → Host File System (사용자 관리)
데이터 유지가 필요한가?
├── Yes
│ ├── 호스트 경로를 직접 지정하고 싶다 → Bind Mount
│ └── Docker가 관리해도 된다
│ ├── 이름이 필요하다 → Named Volume
│ └── 임시로만 사용 → Anonymous Volume
└── No → 컨테이너 레이어에 저장 (컨테이너 삭제 시 사라짐)
개발 환경에서 코드 변경 사항을 실시간 반영하려면 Bind Mount를,
운영 환경에서 DB나 업로드 파일 같은 영구 데이터 보관에는 Named Volume을 주로 쓰면 된다.
| 구분 | 설명 | 사용 범위 |
|---|---|---|
| ARG | Dockerfile 내에서 사용되는 변수 | 이미지 빌드 시에만 사용, CMD나 코드에서는 접근 불가 |
| ENV | Dockerfile 내에서 사용되는 환경 변수 | 컨테이너 실행 시에도 사용 가능, CMD나 코드에서 접근 가능 |
💡 ARG와 ENV를 함께 사용하면 이미지를 유연하게 빌드하고 컨테이너도 실행할 수 있다.
실행 시 환경 변수 설정:
docker run -e (variable_name)=(value) (image_name)
Dockerfile에서 ENV 설정:
FROM python
WORKDIR /app
COPY . /app
ENV NUM 1000
CMD ["python", "main.py"]
빌드 시 ARG 설정:
docker build --build-arg (variable_name)=(value) .
Dockerfile에서 ARG + ENV 함께 사용:
FROM python
ARG DEFAULT_NUM=1000
WORKDIR /app
COPY . /app
ENV NUM $DEFAULT_NUM
CMD ["python", "main.py"]
ARG로 빌드 시 기본값을 지정하고, 그 값을 ENV에 넘겨서 컨테이너 실행 시에도 쓸 수 있어!
네트워크 생성:
docker network create (network_name)
# 예시
docker network create my-network
MySQL과 Python 앱 컨테이너가 서로 통신하는 예시이다.
1. 네트워크 생성
docker network create my-network
2. MySQL 컨테이너 실행
docker run --name mysql-container --network my-network \
-e MYSQL_ROOT_PASSWORD=1234 \
-e MYSQL_DATABASE=testdb \
-e MYSQL_USER=testuser \
-e MYSQL_PASSWORD=1234 \
-d mysql:latest
3. Python 컨테이너 실행
docker run --name python-container --network my-network \
-e MYSQL_HOST=mysql-container \
-e MYSQL_DATABASE=testdb \
-e MYSQL_USER=testuser \
-e MYSQL_PASSWORD=1234 \
python-app
💡
--network옵션으로 같은 네트워크에 묶으면, Python 컨테이너에서mysql-container라는 이름만으로 MySQL에 접근할 수 있다!
IP를 직접 쓸 필요 없이 컨테이너 이름으로 통신이 가능해진다.
도커 컴포즈 (Docker Compose)
다중 컨테이너 환경(예: MySQL + Python)을 운영할 때 다음과 같은 문제가 생긴다 :
docker run 명령어를 실행해야한다.Docker Compose = 여러 컨테이너를 한 번에 build, run, stop할 수 있도록 도와주는 도구
docker-compose up을 통해 모든 컨테이너를 쉽게 시작, 정지 및 관리할 수 있다.| 내용 |
|---|
| Docker Compose는 도커 데몬이 실행 중인 환경에서만 사용 가능 |
| Docker Compose는 Dockerfile을 대체하는 것이 아님 |
| Docker Compose는 이미지와 컨테이너를 대체하는 것도 아님 |
| 여러 컨테이너를 하나의 호스트에서 관리하는 데 초점을 둔 도구이다. |
docker-compose.yaml 파일의 기본 구조는 각 서비스(컨테이너)를 정의하고 관리하기 위한 상위 레벨 요소들로 구성된다.
services: # 여러 컨테이너를 정의
컨테이너명:
...
volumes: # 사용할 볼륨 정의
networks: # 컨테이너 간 통신을 위한 네트워크 정의
| 옵션 | 설명 |
|---|---|
image | 컨테이너를 실행할 때 사용할 도커 이미지 지정 |
container_name | 컨테이너의 이름 지정 (미지정 시 Compose가 자동으로 생성) |
context | 이미지를 빌드할 때 사용할 Dockerfile의 경로 지정 |
args | Dockerfile 내에서 사용할 빌드 인수 정의 (ARG 명령어 활용) |
build | 컨테이너 이미지를 직접 빌드할 때 사용. Dockerfile 경로나 빌드 컨텍스트를 지정하여 이미지를 생성 |
ports | 컨테이너 내부와 호스트 시스템 간의 포트 매핑. "호스트포트:컨테이너포트" 형태 |
environment | 컨테이너 실행 시 필요한 환경 변수 지정. KEY=VALUE 형식으로 작성 |
volumes | 호스트와 컨테이너 간의 파일 시스템을 공유하는 볼륨을 연결 |
networks | 컨테이너가 연결될 네트워크를 지정. 다른 서비스와의 통신을 위해 네트워크에 연결 |
depends_on | 한 서비스가 다른 서비스에 의존하는 경우, 해당 서비스가 먼저 실행될 수 있도록 지정 |
docker-compose.yaml로 지정해야 Docker Compose가 자동으로 인식한다.#로 주석 작성 가능# 모든 서비스를 시작 (백그라운드 실행은 -d 옵션 추가)
docker-compose up
docker-compose up -d
# 현재 실행 중인 모든 컨테이너를 중지
docker-compose stop
# 현재 실행 중인 모든 컨테이너를 중지하고 네트워크와 함께 삭제
docker-compose down
# 실행 중인 컨테이너의 로그를 확인 (특정 서비스 로그만 보려면 서비스명 추가)
docker-compose logs
docker-compose logs
환경 변수를 docker-compose.yaml 안에 직접 작성하는 대신, .env 파일로 분리해서 관리할 수 있다.
.env 파일에 정의된 환경 변수를 불러올 수 있어KEY=VALUE 형식으로 저장한 파일을 사용한다.services:
web:
env_file:
- .env
💡 비밀번호나 API 키 같은 민감한 정보는
.env파일로 분리하고.gitignore에 추가하는 게 좋은 습관이다!
services:
# MySQL DB
db:
image: mysql:8.0
container_name: mysql_db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: 1234
MYSQL_DATABASE: mysql_db
MYSQL_USER: testdb
MYSQL_CHARSET: utf8mb4
MYSQL_COLLATION: utf8mb4_unicode_ci
ports:
- "3306:3306"
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "--password=1234"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# Spring Boot App (또는 Flask App)
app:
build:
context: .
dockerfile: Dockerfile
container_name: myapp
restart: unless-stopped
depends_on:
- db
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/mysql_db?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: 1234
JWT_SECRET: your_jwt_secret_key
volumes:
mysql-data:
depends_on: db를 설정하면 MySQL이 먼저 실행된 후에 앱 컨테이너가 실행된다.
docker-compose.yaml 하나로...
├── 여러 컨테이너를 한 번에 관리
├── 네트워크 자동 생성 및 연결
├── 볼륨 설정
├── 환경 변수 관리 (.env 파일 활용)
└── 컨테이너 의존성 순서 지정 (depends_on)
→ docker-compose up -d 한 줄이면 끝!