Docker 작성
Docker Compose는 다중 컨테이너 애플리케이션을 정의하고 실행하기 위한 도구입니다. 모든 서비스를 설명하는 단일 YAML 파일을 사용하면 하나의 명령으로 전체 애플리케이션 스택을 시작할 수 있습니다.
Docker 및 Docker Compose 설치
Docker 엔진 설치
# Remove old versions
sudo apt remove docker docker-engine docker.io containerd runc 2>/dev/null
# Install dependencies
sudo apt update
sudo apt install ca-certificates curl gnupg -y
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker Engine (includes Compose plugin)
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
# Add current user to the docker group (no sudo needed)
sudo usermod -aG docker $USER
newgrp docker
# Verify installation
docker --version
docker compose version더 빠른 가져오기를 위해 미러 레지스트리 사용
sudo tee /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://docker.mirrors.ustc.edu.cn"
]
}
EOF
sudo systemctl restart dockerdocker-compose.yml 기본 사항
파일 구조
# docker-compose.yml
# Modern Docker Compose no longer needs the version field (it is deprecated)
services:
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html:ro
restart: unless-stopped
app:
build: ./app
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
depends_on:
- db
db:
image: postgres:16
volumes:
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
db-data:
networks:
default:
driver: bridge공통 구성 옵션
services:
myservice:
# Image
image: ubuntu:26.04
# Or build from Dockerfile
build:
context: ./app
dockerfile: Dockerfile
args:
VERSION: "1.0"
# Container name
container_name: my-app
# Port mapping (host:container)
ports:
- "8080:80"
- "127.0.0.1:3000:3000" # Local access only
# Volumes
volumes:
- ./data:/app/data # Bind mount
- app-data:/app/storage # Named volume
- /etc/localtime:/etc/localtime:ro # Sync timezone
# Environment variables
environment:
- NODE_ENV=production
- SECRET_KEY=mysecret
# Or load from file
env_file:
- .env
# Restart policy
restart: unless-stopped # always | on-failure | no
# Dependencies
depends_on:
db:
condition: service_healthy
# Health check
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Resource limits
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
# Networks
networks:
- frontend
- backend
# Logging configuration
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"공통 명령
# Start all services (detached mode)
docker compose up -d
# View running status
docker compose ps
# View logs
docker compose logs
docker compose logs -f web # Follow logs for a specific service
docker compose logs --tail=100 # Last 100 lines
# Stop all services
docker compose down
# Stop and remove volumes
docker compose down -v
# Rebuild images
docker compose build
docker compose up -d --build
# Enter a container
docker compose exec web bash
# Run a one-off command
docker compose run --rm app python manage.py migrate
# Scale services
docker compose up -d --scale web=3
# View resource usage
docker compose top
docker stats일반적인 기술 스택 예
LEMP 스택(Nginx + PHP + MySQL)
# docker-compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./src:/var/www/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- php
restart: unless-stopped
php:
image: php:8.3-fpm
volumes:
- ./src:/var/www/html
restart: unless-stopped
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
MYSQL_PASSWORD: apppass
volumes:
- mysql-data:/var/lib/mysql
restart: unless-stopped
phpmyadmin:
image: phpmyadmin:latest
ports:
- "8080:80"
environment:
PMA_HOST: mysql
depends_on:
- mysql
restart: unless-stopped
volumes:
mysql-data:워드프레스
services:
wordpress:
image: wordpress:latest
ports:
- "80:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress_pass
WORDPRESS_DB_NAME: wordpress
volumes:
- wp-content:/var/www/html
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: mariadb:11
environment:
MARIADB_ROOT_PASSWORD: root_pass
MARIADB_DATABASE: wordpress
MARIADB_USER: wordpress
MARIADB_PASSWORD: wordpress_pass
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
volumes:
wp-content:
db-data:Gitea + PostgreSQL
services:
gitea:
image: gitea/gitea:latest
ports:
- "3000:3000"
- "2222:22"
environment:
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea_pass
volumes:
- gitea-data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
depends_on:
- db
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: gitea
POSTGRES_PASSWORD: gitea_pass
POSTGRES_DB: gitea
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
gitea-data:
postgres-data:모니터링 스택(Prometheus + Grafana)
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
restart: unless-stopped
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin_pass
volumes:
- grafana-data:/var/lib/grafana
depends_on:
- prometheus
restart: unless-stopped
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--path.rootfs=/rootfs'
restart: unless-stopped
volumes:
prometheus-data:
grafana-data:.env 파일 사용
# .env file (in the same directory as docker-compose.yml)
MYSQL_ROOT_PASSWORD=secretpass
MYSQL_DATABASE=myapp
APP_PORT=8080# Reference in docker-compose.yml
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
app:
ports:
- "${APP_PORT}:80"네트워크 구성
services:
frontend:
networks:
- front-tier
backend:
networks:
- front-tier
- back-tier
db:
networks:
- back-tier
networks:
front-tier:
driver: bridge
back-tier:
driver: bridge
internal: true # Isolated network, no external access데이터 백업 및 복원
# Back up a volume
docker run --rm -v myproject_db-data:/data -v $(pwd):/backup ubuntu \
tar czf /backup/db-backup-$(date +%Y%m%d).tar.gz -C /data .
# Restore a volume
docker run --rm -v myproject_db-data:/data -v $(pwd):/backup ubuntu \
bash -c "cd /data && tar xzf /backup/db-backup-20260324.tar.gz"
# Database export (MySQL example)
docker compose exec db mysqldump -u root -p myapp > backup.sql
# Database import
docker compose exec -T db mysql -u root -p myapp < backup.sql모범 사례
- 핀 버전: 프로덕션에서는 고정 버전 태그를 사용합니다(예:
nginx:1.27).latest을 피하세요 - 별도의 환경 변수: 중요한 정보를
.env파일에 보관하고.gitignore에 추가합니다. - 상태 확인: 종속성이 준비되었는지 확인하기 위해 중요한 서비스에 대해
healthcheck을 구성합니다. - 로그 제한: 디스크 공간 소모를 방지하도록 로그 순환을 구성합니다.
- 리소스 제한:
deploy.resources을 사용하여 CPU 및 메모리를 제한합니다. - 읽기 전용 마운트: 구성 파일을 읽기 전용으로 마운트하려면
:ro을 사용하세요. - 명명된 볼륨: 영구 데이터에 대해 바인드 마운트 대신 명명된 볼륨을 사용합니다.
- 네트워크 격리: 데이터베이스 및 기타 서비스에는
internal네트워크를 사용합니다. 불필요하게 포트를 노출하지 마십시오