Ver3.0 ๐ณ Docker์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปจํ ์ด๋ ์ด์ ์ ๋ต: ์ค์ ๊ฐ์ด๋

๐ณ Docker์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปจํ ์ด๋ ์ด์ ์ ๋ต: ์ค์ ๊ฐ์ด๋
์ปจํ ์ด๋ ํ๊ฒฝ์์ ์์ ์ ์ด๊ณ ํจ์จ์ ์ธ DB ์ด์ ๋ ธํ์ฐ
Docker ํ๊ฒฝ์์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ด์ํ๋ ๊ฑด ์๊ฐ๋ณด๋ค ๊น๋ค๋ก์ด ์ผ์ด์ผ. ๋จ์ํ ์ปจํ ์ด๋๋ง ๋์ฐ๋ฉด ๋? ์ฒ๋ง์! ๋ฐ์ดํฐ ์์์ฑ, ๋ฐฑ์ ์ ๋ต, ์ฑ๋ฅ ์ต์ ํ, ๋ณด์ ์ค์ ๊น์ง... ๊ณ ๋ คํด์ผ ํ ๊ฒ ํ๋ ๊ฐ์ง๊ฐ ์๋์ง. ์ด ๊ธ์์๋ ์ค์ ํ๋ก๋์ ํ๊ฒฝ์์ ๊ฒ์ฆ๋ DB ์ปจํ ์ด๋ ์ด์ ์ ๋ต์ ์น๊ตฌ์๊ฒ ์ค๋ช ํ๋ฏ ์ฝ๊ณ ์ฌ๋ฏธ์๊ฒ ํ์ด๋ณผ๊ฒ! ๐
๐ค ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ปจํ ์ด๋๋ก ์ด์ํ ๊น?
์ปจํ ์ด๋ DB์ ์ฅ์ ๋ค:
โก ๋น ๋ฅธ ํ๊ฒฝ ๊ตฌ์ถ: ๋ช ๋ถ ์์ ๋ณต์กํ DB ํด๋ฌ์คํฐ๋ ๊ตฌ์ฑ ๊ฐ๋ฅ
๐ ์ผ๊ด๋ ํ๊ฒฝ: ๊ฐ๋ฐ/์คํ ์ด์ง/ํ๋ก๋์ ํ๊ฒฝ์ ๋์ผํ๊ฒ ์ ์ง
๐ฆ ๋ฒ์ ๊ด๋ฆฌ: DB ๋ฒ์ ์ ์ด๋ฏธ์ง ํ๊ทธ๋ก ๋ช ํํ๊ฒ ๊ด๋ฆฌ
๐ฏ ๋ฆฌ์์ค ๊ฒฉ๋ฆฌ: CPU, ๋ฉ๋ชจ๋ฆฌ ๋ฑ ๋ฆฌ์์ค๋ฅผ ์ ๋ฐํ๊ฒ ์ ์ด
๐ง ์ฌ์ด ๋ณต์ : ํ ์คํธ์ฉ DB๋ฅผ ์์๊ฐ์ ๋ณต์ ๊ฐ๋ฅ
ํ์ง๋ง ๋ชจ๋ ์ํฉ์ ์ปจํ ์ด๋ DB๊ฐ ์ ๋ต์ ์๋์ผ. ๋๊ท๋ชจ ํธ๋์ญ์ ์ด ๋ฐ์ํ๋ ๊ธ์ต๊ถ์ด๋, ๊ทน๋์ ์์ ์ฑ์ด ์๊ตฌ๋๋ ์์คํ ์์๋ ์ฌ์ ํ ๋ฒ ์ด๋ฉํ์ด๋ ์ ์ฉ VM์ ์ ํธํ๊ธฐ๋ ํด. ์ํฉ์ ๋ง๋ ์ ํ์ด ์ค์ํ์ง!
๐พ ๋ฐ์ดํฐ ์์์ฑ ์ ๋ต: ๋ณผ๋ฅจ์ด ๋ต์ด๋ค
๊ทธ๋์ ์ฐ๋ฆฌ์๊ฒ๋ Docker Volume์ด ํ์ํด. ๋ณผ๋ฅจ์ ์ปจํ ์ด๋์ ๋ ๋ฆฝ์ ์ผ๋ก ์กด์ฌํ๋ ๋ฐ์ดํฐ ์ ์ฅ์์ผ.
๐ ๋ณผ๋ฅจ ํ์ ๋น๊ต
| ๋ณผ๋ฅจ ํ์ | ํน์ง | ์ฌ์ฉ ์๋๋ฆฌ์ค | ์ฑ๋ฅ |
|---|---|---|---|
| Named Volume | Docker๊ฐ ๊ด๋ฆฌํ๋ ๋ณผ๋ฅจ | ํ๋ก๋์ ํ๊ฒฝ, ๋ฐฑ์ ์ฉ์ด | โญโญโญโญโญ |
| Bind Mount | ํธ์คํธ ๋๋ ํ ๋ฆฌ ์ง์ ๋ง์ดํธ | ๊ฐ๋ฐ ํ๊ฒฝ, ์ค์ ํ์ผ ๊ณต์ | โญโญโญโญ |
| tmpfs Mount | ๋ฉ๋ชจ๋ฆฌ์ ์์ ์ ์ฅ | ์บ์, ์์ ๋ฐ์ดํฐ | โญโญโญโญโญ |
๐ง ์ค์ ๋ณผ๋ฅจ ์ค์ ์์
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: my-postgres
environment:
POSTGRES_USER: dbuser
POSTGRES_PASSWORD: securepassword
POSTGRES_DB: myapp
volumes:
# Named Volume ์ฌ์ฉ (๊ถ์ฅ)
- postgres_data:/var/lib/postgresql/data
# ์ด๊ธฐํ ์คํฌ๋ฆฝํธ
- ./init-scripts:/docker-entrypoint-initdb.d
# ์ปค์คํ
์ค์ ํ์ผ
- ./postgresql.conf:/etc/postgresql/postgresql.conf
ports:
- "5432:5432"
restart: unless-stopped
# ๋ฆฌ์์ค ์ ํ
deploy:
resources:
limits:
cpus: '2'
memory: 4G
reservations:
cpus: '1'
memory: 2G
volumes:
postgres_data:
driver: local
driver_opts:
type: none
o: bind
device: /data/postgres
Named Volume์ ์ฌ์ฉํ ๋๋ ๋ฐ๋์ ๋ฐฑ์ ์ ๋ต์ ํจ๊ป ์๋ฆฝํด์ผ ํด.
docker volume inspect ๋ช
๋ น์ด๋ก ๋ณผ๋ฅจ์ ์ค์ ๊ฒฝ๋ก๋ฅผ ํ์ธํ๊ณ , ํด๋น ๊ฒฝ๋ก๋ฅผ ์ ๊ธฐ์ ์ผ๋ก ๋ฐฑ์
ํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ์์ฑํ๋ ๊ฒ ์ข์!
๐ ๋ณด์ ์ค์ : ์์ ํ DB ์ปจํ ์ด๋ ๋ง๋ค๊ธฐ
๐ก๏ธ ํ์ ๋ณด์ ์ฒดํฌ๋ฆฌ์คํธ
์ ๋๋ก docker-compose.yml์ ๋น๋ฐ๋ฒํธ๋ฅผ ํ๋์ฝ๋ฉํ์ง ๋ง! Docker Secrets๋ ํ๊ฒฝ ๋ณ์ ํ์ผ์ ์ฌ์ฉํด์ผ ํด.
# .env ํ์ผ ์ฌ์ฉ (๋ฐ๋์ .gitignore์ ์ถ๊ฐ!)
POSTGRES_PASSWORD=your_secure_password_here
POSTGRES_USER=dbadmin
DB_NAME=production_db
# docker-compose.yml์์ ์ฐธ์กฐ
services:
postgres:
image: postgres:15-alpine
env_file:
- .env
# ๋๋ Docker Secrets ์ฌ์ฉ (Swarm ๋ชจ๋)
secrets:
- db_password
- db_user
secrets:
db_password:
file: ./secrets/db_password.txt
db_user:
file: ./secrets/db_user.txt
DB ์ปจํ ์ด๋๋ ์ธ๋ถ์ ์ง์ ๋ ธ์ถํ์ง ๋ง๊ณ , ์ ํ๋ฆฌ์ผ์ด์ ์ปจํ ์ด๋์๋ง ํต์ ํ๋๋ก ์ค์ ํด์ผ ํด.
version: '3.8'
services:
app:
image: myapp:latest
networks:
- frontend
- backend
ports:
- "80:8080"
postgres:
image: postgres:15-alpine
networks:
- backend # ๋ฐฑ์๋ ๋คํธ์ํฌ๋ง ์ฐ๊ฒฐ
# ports๋ฅผ ๋ช
์ํ์ง ์์ผ๋ฉด ์ธ๋ถ ์ ๊ทผ ๋ถ๊ฐ
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # ์ธ๋ถ ์ธํฐ๋ท ์ ๊ทผ ์ฐจ๋จ
์ปจํ ์ด๋์ ๋ฃจํธ ํ์ผ์์คํ ์ ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ์ค์ ํ๋ฉด ์ ์ฑ ์ฝ๋๊ฐ ํ์ผ์ ์์ ํ๋ ๊ฑธ ๋ฐฉ์งํ ์ ์์ด.
services:
postgres:
image: postgres:15-alpine
read_only: true # ๋ฃจํธ ํ์ผ์์คํ
์ฝ๊ธฐ ์ ์ฉ
tmpfs:
- /tmp
- /var/run/postgresql
volumes:
- postgres_data:/var/lib/postgresql/data
ํ ๋ฒ์ ๊ฐ๋ฐ ์๋ฒ์์ DB ํฌํธ๋ฅผ 0.0.0.0:5432๋ก ๋ฐ์ธ๋ฉํ๋ค๊ฐ ๋ด๋ค์ ๋ฌด์ฐจ๋ณ ๋์ ๊ณต๊ฒฉ์ ๋ฐ์ ์ ์ด ์์ด. ๋ก๊ทธ๋ฅผ ๋ณด๋ ์ค๊ตญ, ๋ฌ์์ ๋ฑ ์ ์ธ๊ณ์์ ์ ์ ์๋๊ฐ ๋ค์ด์ค๋๋ผ๊ณ ! ๋คํํ ๊ฐ๋ ฅํ ๋น๋ฐ๋ฒํธ๋ฅผ ์ฌ์ฉํ์ง๋ง, ๊ทธ ์ดํ๋ก๋ ์ ๋ DB ํฌํธ๋ฅผ ์ธ๋ถ์ ๋ ธ์ถํ์ง ์์. ํ์ํ๋ฉด SSH ํฐ๋๋ง์ด๋ VPN์ ์ฌ์ฉํ๋ ๊ฒ ํจ์ฌ ์์ ํด!
โก ์ฑ๋ฅ ์ต์ ํ: ๋น ๋ฅด๊ณ ํจ์จ์ ์ธ DB ์ปจํ ์ด๋
๐ฏ ๋ฆฌ์์ค ์ ํ ๋ฐ ํ ๋น
DB ์ปจํ ์ด๋์๋ ์ถฉ๋ถํ ๋ฆฌ์์ค๋ฅผ ํ ๋นํด์ผ ํ์ง๋ง, ๋ฌด์ ํ์ผ๋ก ์ฃผ๋ฉด ๋ค๋ฅธ ์๋น์ค์ ์ํฅ์ ์ค ์ ์์ด.
services:
mysql:
image: mysql:8.0
deploy:
resources:
limits:
cpus: '4' # ์ต๋ 4์ฝ์ด ์ฌ์ฉ
memory: 8G # ์ต๋ 8GB ๋ฉ๋ชจ๋ฆฌ
reservations:
cpus: '2' # ์ต์ 2์ฝ์ด ๋ณด์ฅ
memory: 4G # ์ต์ 4GB ๋ณด์ฅ
# MySQL ํนํ ์ค์
command: >
--max-connections=500
--innodb-buffer-pool-size=4G
--innodb-log-file-size=512M
--innodb-flush-log-at-trx-commit=2
--innodb-flush-method=O_DIRECT
๐ฟ ์คํ ๋ฆฌ์ง ๋๋ผ์ด๋ฒ ์ ํ
| ๋๋ผ์ด๋ฒ | ์ฑ๋ฅ | ์์ ์ฑ | ์ถ์ฒ ์๋๋ฆฌ์ค |
|---|---|---|---|
| overlay2 | โญโญโญโญโญ | โญโญโญโญโญ | ํ๋ก๋์ ํ๊ฒฝ (๊ถ์ฅ) |
| btrfs | โญโญโญโญ | โญโญโญโญ | ์ค๋ ์ท ๊ธฐ๋ฅ ํ์์ |
| zfs | โญโญโญโญ | โญโญโญโญโญ | ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ์ต์ฐ์ |
| devicemapper | โญโญโญ | โญโญโญ | ๋ ๊ฑฐ์ ์์คํ |
ํ๋ก๋์ ํ๊ฒฝ์์๋ ๋ก์ปฌ ๋ณผ๋ฅจ๋ณด๋ค ์ ์ฉ ์คํ ๋ฆฌ์ง ์๋ฃจ์ ์ ์ฌ์ฉํ๋ ๊ฒ ์ข์. NFS, GlusterFS, Ceph ๊ฐ์ ๋ถ์ฐ ์คํ ๋ฆฌ์ง๋, AWS EBS, Azure Disk ๊ฐ์ ํด๋ผ์ฐ๋ ์คํ ๋ฆฌ์ง๋ฅผ ํ์ฉํ๋ฉด ์ฑ๋ฅ๊ณผ ์์ ์ฑ์ ๋์์ ์ก์ ์ ์์ง!
๐ฅ ์บ์ฑ ์ ๋ต
# Redis๋ฅผ ์บ์ ๋ ์ด์ด๋ก ํ์ฉ
version: '3.8'
services:
app:
image: myapp:latest
depends_on:
- redis
- postgres
environment:
CACHE_URL: redis://redis:6379
DATABASE_URL: postgresql://postgres:5432/mydb
redis:
image: redis:7-alpine
command: >
redis-server
--maxmemory 2gb
--maxmemory-policy allkeys-lru
--save "" # ์์์ฑ ๋นํ์ฑํ (์์ ์บ์์ฉ)
volumes:
- redis_data:/data
postgres:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_SHARED_BUFFERS: 2GB
POSTGRES_EFFECTIVE_CACHE_SIZE: 6GB
volumes:
redis_data:
postgres_data:
๐ ๋ฐฑ์ ๋ฐ ๋ณต๊ตฌ ์ ๋ต
๐ฆ ๋ฐฑ์ ๋ฐฉ๋ฒ ๋น๊ต
๊ฐ์ฅ ๊ฐ๋จํ๊ณ ์์ ํ ๋ฐฉ๋ฒ. Docker ๋ณผ๋ฅจ ์์ฒด๋ฅผ ๋ฐฑ์ ํ๋ ๊ฑฐ์ผ.
#!/bin/bash
# PostgreSQL ๋ณผ๋ฅจ ๋ฐฑ์
์คํฌ๋ฆฝํธ
BACKUP_DIR="/backup/postgres"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
VOLUME_NAME="postgres_data"
# ๋ณผ๋ฅจ์ tar๋ก ์์ถ
docker run --rm \
-v ${VOLUME_NAME}:/data \
-v ${BACKUP_DIR}:/backup \
alpine \
tar czf /backup/postgres_${TIMESTAMP}.tar.gz -C /data .
# 30์ผ ์ด์ ๋ ๋ฐฑ์
์ญ์
find ${BACKUP_DIR} -name "postgres_*.tar.gz" -mtime +30 -delete
echo "๋ฐฑ์
์๋ฃ: postgres_${TIMESTAMP}.tar.gz"
DB ์์ฒด์ ๋ฐฑ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ. ๋ณต๊ตฌ๊ฐ ์ ์ฐํ๊ณ ๋ฒ์ ๊ฐ ํธํ์ฑ์ด ์ข์.
#!/bin/bash
# PostgreSQL ๋
ผ๋ฆฌ์ ๋ฐฑ์
BACKUP_DIR="/backup/postgres/dumps"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# pg_dump ์คํ
docker exec my-postgres pg_dump -U dbuser -Fc myapp > \
${BACKUP_DIR}/myapp_${TIMESTAMP}.dump
# ๋๋ ๋ชจ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐฑ์
docker exec my-postgres pg_dumpall -U postgres > \
${BACKUP_DIR}/all_databases_${TIMESTAMP}.sql
# S3์ ์
๋ก๋ (์ ํ์ฌํญ)
aws s3 cp ${BACKUP_DIR}/myapp_${TIMESTAMP}.dump \
s3://my-backup-bucket/postgres/
์คํ ๋ฆฌ์ง ๋ ๋ฒจ์์ ์ค๋ ์ท์ ์์ฑ. ๊ฐ์ฅ ๋น ๋ฅด๊ณ ํจ์จ์ ์ด์ง๋ง ์ธํ๋ผ ์ง์์ด ํ์ํด.
# ZFS ์ค๋
์ท ์์
zfs snapshot tank/docker/volumes/postgres_data@backup_$(date +%Y%m%d_%H%M%S)
# Btrfs ์ค๋
์ท ์์
btrfs subvolume snapshot /var/lib/docker/volumes/postgres_data \
/backup/postgres_snapshot_$(date +%Y%m%d_%H%M%S)
โป๏ธ ๋ณต๊ตฌ ํ๋ก์ธ์ค
1. ํ์ฌ ์ปจํ ์ด๋ ์ค์ง
2. ๊ธฐ์กด ๋ฐ์ดํฐ ๋ฐฑ์ (ํน์ ๋ชจ๋ฅด๋๊น!)
3. ๋ณต๊ตฌํ ๋ฐฑ์ ํ์ผ ๊ฒ์ฆ
4. ํ ์คํธ ํ๊ฒฝ์์ ๋จผ์ ๋ณต๊ตฌ ํ ์คํธ
5. ํ๋ก๋์ ๋ณต๊ตฌ ์คํ
# ๋ณผ๋ฅจ ๋ฐฑ์
์์ ๋ณต๊ตฌ
docker run --rm \
-v postgres_data:/data \
-v /backup/postgres:/backup \
alpine \
sh -c "cd /data && tar xzf /backup/postgres_20240115_120000.tar.gz"
# ๋
ผ๋ฆฌ์ ๋ฐฑ์
์์ ๋ณต๊ตฌ
docker exec -i my-postgres pg_restore -U dbuser -d myapp < \
/backup/postgres/dumps/myapp_20240115_120000.dump
# ๋๋ SQL ํ์ผ์์ ๋ณต๊ตฌ
docker exec -i my-postgres psql -U postgres < \
/backup/postgres/dumps/all_databases_20240115_120000.sql
cron์ด๋ systemd timer๋ฅผ ์ฌ์ฉํด์ ๋ฐฑ์ ์ ์๋ํํ๊ณ , ๋ฐฑ์ ์ฑ๊ณต/์คํจ๋ฅผ ๋ชจ๋ํฐ๋งํ๋ ์์คํ ์ ๊ตฌ์ถํ๋ ๊ฒ ์ข์. ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๋ฐฑ์ ์๋ํ ์คํฌ๋ฆฝํธ๋ฅผ ๊ณต์ ํ๊ฑฐ๋ ์ ๋ฌธ๊ฐ์ ๋์์ ๋ฐ์ ์๋ ์์ง!
๐ ๋ชจ๋ํฐ๋ง๊ณผ ๋ก๊น
๐ ๋ชจ๋ํฐ๋ง ์คํ ๊ตฌ์ฑ
version: '3.8'
services:
# ํ๋ก๋์
DB
postgres:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
labels:
- "prometheus.io/scrape=true"
- "prometheus.io/port=9187"
# PostgreSQL Exporter (Prometheus์ฉ)
postgres-exporter:
image: prometheuscommunity/postgres-exporter
environment:
DATA_SOURCE_NAME: "postgresql://postgres:${DB_PASSWORD}@postgres:5432/postgres?sslmode=disable"
ports:
- "9187:9187"
depends_on:
- postgres
# Prometheus
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "9090:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
# Grafana
grafana:
image: grafana/grafana:latest
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
GF_INSTALL_PLUGINS: grafana-piechart-panel
depends_on:
- prometheus
volumes:
postgres_data:
prometheus_data:
grafana_data:
๐ ๋ก๊น ์ ๋ต
์ฌ๋ฌ ์ปจํ ์ด๋์ ๋ก๊ทธ๋ฅผ ํ๊ณณ์ ๋ชจ์์ ๊ด๋ฆฌํ๋ฉด ๋ฌธ์ ์ถ์ ์ด ํจ์ฌ ์ฌ์์ ธ!
# docker-compose.yml์ ๋ก๊น
๋๋ผ์ด๋ฒ ์ค์
services:
postgres:
image: postgres:15-alpine
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
labels: "service,environment"
env: "POSTGRES_VERSION"
labels:
service: "postgres"
environment: "production"
# Loki (๋ก๊ทธ ์์ง)
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/local-config.yaml
- loki_data:/loki
command: -config.file=/etc/loki/local-config.yaml
# Promtail (๋ก๊ทธ ์ ์ก)
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
DB ์ฑ๋ฅ ๋ฉํธ๋ฆญ:
โข ์ฟผ๋ฆฌ ์๋ต ์๊ฐ (Query Response Time)
โข ์ด๋น ํธ๋์ญ์ ์ (TPS)
โข ์ปค๋ฅ์ ํ ์ฌ์ฉ๋ฅ
โข ์บ์ ํํธ์จ
โข ๋์คํฌ I/O ๋๊ธฐ ์๊ฐ
์ปจํ ์ด๋ ๋ฉํธ๋ฆญ:
โข CPU ์ฌ์ฉ๋ฅ
โข ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ฅ
โข ๋คํธ์ํฌ I/O
โข ๋์คํฌ I/O
โข ์ปจํ ์ด๋ ์ฌ์์ ํ์
๐ ๊ณ ๊ฐ์ฉ์ฑ(HA) ๊ตฌ์ฑ
๐ PostgreSQL ๋ ํ๋ฆฌ์ผ์ด์
version: '3.8'
services:
# ๋ง์คํฐ DB
postgres-master:
image: postgres:15-alpine
environment:
POSTGRES_USER: replicator
POSTGRES_PASSWORD: ${REPLICATOR_PASSWORD}
POSTGRES_DB: myapp
volumes:
- postgres_master_data:/var/lib/postgresql/data
- ./master-init.sh:/docker-entrypoint-initdb.d/init.sh
command: >
postgres
-c wal_level=replica
-c max_wal_senders=3
-c max_replication_slots=3
-c hot_standby=on
networks:
- db_network
# ์ฌ๋ ์ด๋ธ DB 1
postgres-slave1:
image: postgres:15-alpine
environment:
POSTGRES_USER: replicator
POSTGRES_PASSWORD: ${REPLICATOR_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres_slave1_data:/var/lib/postgresql/data
- ./slave-init.sh:/docker-entrypoint-initdb.d/init.sh
depends_on:
- postgres-master
command: >
bash -c "
until pg_basebackup -h postgres-master -D /var/lib/postgresql/data/pgdata -U replicator -v -P -W; do
echo 'Waiting for master to be ready...'
sleep 5
done
echo 'standby_mode = on' > /var/lib/postgresql/data/pgdata/recovery.conf
echo 'primary_conninfo = \"host=postgres-master port=5432 user=replicator password=${REPLICATOR_PASSWORD}\"' >> /var/lib/postgresql/data/pgdata/recovery.conf
postgres
"
networks:
- db_network
# ์ฌ๋ ์ด๋ธ DB 2
postgres-slave2:
image: postgres:15-alpine
environment:
POSTGRES_USER: replicator
POSTGRES_PASSWORD: ${REPLICATOR_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres_slave2_data:/var/lib/postgresql/data
depends_on:
- postgres-master
networks:
- db_network
# PgPool (๋ก๋ ๋ฐธ๋ฐ์)
pgpool:
image: bitnami/pgpool:latest
environment:
PGPOOL_BACKEND_NODES: 0:postgres-master:5432,1:postgres-slave1:5432,2:postgres-slave2:5432
PGPOOL_SR_CHECK_USER: replicator
PGPOOL_SR_CHECK_PASSWORD: ${REPLICATOR_PASSWORD}
PGPOOL_ENABLE_LDAP: no
PGPOOL_POSTGRES_USERNAME: postgres
PGPOOL_POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
PGPOOL_ADMIN_USERNAME: admin
PGPOOL_ADMIN_PASSWORD: ${PGPOOL_ADMIN_PASSWORD}
ports:
- "5432:5432"
depends_on:
- postgres-master
- postgres-slave1
- postgres-slave2
networks:
- db_network
volumes:
postgres_master_data:
postgres_slave1_data:
postgres_slave2_data:
networks:
db_network:
driver: bridge
๐ฏ MySQL ํด๋ฌ์คํฐ ๊ตฌ์ฑ
version: '3.8'
services:
# MySQL ๋ง์คํฐ
mysql-master:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
volumes:
- mysql_master_data:/var/lib/mysql
- ./master.cnf:/etc/mysql/conf.d/master.cnf
command: >
--server-id=1
--log-bin=mysql-bin
--binlog-do-db=myapp
networks:
- db_network
# MySQL ์ฌ๋ ์ด๋ธ 1
mysql-slave1:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- mysql_slave1_data:/var/lib/mysql
- ./slave.cnf:/etc/mysql/conf.d/slave.cnf
command: >
--server-id=2
--relay-log=mysql-relay-bin
--log-bin=mysql-bin
--binlog-do-db=myapp
depends_on:
- mysql-master
networks:
- db_network
# MySQL ์ฌ๋ ์ด๋ธ 2
mysql-slave2:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- mysql_slave2_data:/var/lib/mysql
- ./slave.cnf:/etc/mysql/conf.d/slave.cnf
command: >
--server-id=3
--relay-log=mysql-relay-bin
--log-bin=mysql-bin
--binlog-do-db=myapp
depends_on:
- mysql-master
networks:
- db_network
# ProxySQL (๋ก๋ ๋ฐธ๋ฐ์)
proxysql:
image: proxysql/proxysql:latest
volumes:
- ./proxysql.cnf:/etc/proxysql.cnf
ports:
- "6033:6033"
- "6032:6032"
depends_on:
- mysql-master
- mysql-slave1
- mysql-slave2
networks:
- db_network
volumes:
mysql_master_data:
mysql_slave1_data:
mysql_slave2_data:
networks:
db_network:
driver: bridge
ํ๋ฒ์ ๋ง์คํฐ-์ฌ๋ ์ด๋ธ ๊ตฌ์กฐ์์ ๋ ํ๋ฆฌ์ผ์ด์ ์ง์ฐ(Replication Lag)์ด ์ฌ๊ฐํ๊ฒ ๋ฐ์ํ ์ ์ด ์์ด. ๋ง์คํฐ์์๋ ๋ฐ์ดํฐ๊ฐ ์ ๋ฐ์ดํธ๋๋๋ฐ, ์ฌ๋ ์ด๋ธ์์ ์ฝ์ผ๋ฉด ์๋ ๋ฐ์ดํฐ๊ฐ ๋์ค๋ ๊ฑฐ์ผ! ๐ฑ
์์ธ์ ์ฌ๋ ์ด๋ธ ์๋ฒ์ ๋์คํฌ I/O ์ฑ๋ฅ์ด ๋ถ์กฑํ๋ ๊ฑฐ์์ด. ๊ฒฐ๊ตญ SSD๋ก ์ ๊ทธ๋ ์ด๋ํ๊ณ , ๋คํธ์ํฌ ๋์ญํญ๋ ๋๋ ธ๋๋ ํด๊ฒฐ๋์ง. ๋ ํ๋ฆฌ์ผ์ด์ ๊ตฌ์ฑํ ๋๋ ์ฌ๋ ์ด๋ธ ์๋ฒ์ ์ฑ๋ฅ๋ ๋ง์คํฐ์ ๋น์ทํ๊ฒ ๋ง์ถฐ์ผ ํด!
๐ ์ค์ ๋ฐฐํฌ ์๋๋ฆฌ์ค
๐ ๋ฐฐํฌ ์ฒดํฌ๋ฆฌ์คํธ
# 1. Docker ๋ฐ Docker Compose ์ค์น ํ์ธ
docker --version
docker-compose --version
# 2. ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ ์์ฑ
mkdir -p ~/docker-db/{config,scripts,backup,logs}
cd ~/docker-db
# 3. ํ๊ฒฝ ๋ณ์ ํ์ผ ์์ฑ
cat > .env << EOF
# Database
POSTGRES_USER=dbadmin
POSTGRES_PASSWORD=$(openssl rand -base64 32)
POSTGRES_DB=production_db
# Replication
REPLICATOR_PASSWORD=$(openssl rand -base64 32)
# Monitoring
GRAFANA_PASSWORD=$(openssl rand -base64 32)
# Backup
BACKUP_SCHEDULE="0 2 * * *"
BACKUP_RETENTION_DAYS=30
EOF
# 4. .env ํ์ผ ๊ถํ ์ค์
chmod 600 .env
# postgresql.conf (์ปค์คํ
์ค์ )
cat > config/postgresql.conf << EOF
# ์ฐ๊ฒฐ ์ค์
max_connections = 200
shared_buffers = 4GB
effective_cache_size = 12GB
maintenance_work_mem = 1GB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 10485kB
min_wal_size = 1GB
max_wal_size = 4GB
# ๋ก๊น
logging_collector = on
log_directory = 'pg_log'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_statement = 'mod'
log_duration = on
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
# ๋ ํ๋ฆฌ์ผ์ด์
wal_level = replica
max_wal_senders = 10
max_replication_slots = 10
hot_standby = on
EOF
# pg_hba.conf (์ ๊ทผ ์ ์ด)
cat > config/pg_hba.conf << EOF
# TYPE DATABASE USER ADDRESS METHOD
local all all trust
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
host all all 172.16.0.0/12 md5
host replication replicator 172.16.0.0/12 md5
EOF
# docker-compose.yml (ํ๋ก๋์
๊ตฌ์ฑ)
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: production-postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
PGDATA: /var/lib/postgresql/data/pgdata
volumes:
- postgres_data:/var/lib/postgresql/data
- ./config/postgresql.conf:/etc/postgresql/postgresql.conf
- ./config/pg_hba.conf:/etc/postgresql/pg_hba.conf
- ./scripts:/docker-entrypoint-initdb.d
- ./backup:/backup
command: postgres -c config_file=/etc/postgresql/postgresql.conf
ports:
- "127.0.0.1:5432:5432"
networks:
- db_network
deploy:
resources:
limits:
cpus: '4'
memory: 8G
reservations:
cpus: '2'
memory: 4G
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
labels:
- "com.example.description=Production PostgreSQL Database"
- "com.example.backup=enabled"
postgres-exporter:
image: prometheuscommunity/postgres-exporter
container_name: postgres-exporter
restart: unless-stopped
environment:
DATA_SOURCE_NAME: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}?sslmode=disable"
ports:
- "127.0.0.1:9187:9187"
networks:
- db_network
depends_on:
postgres:
condition: service_healthy
backup:
image: postgres:15-alpine
container_name: postgres-backup
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
BACKUP_SCHEDULE: ${BACKUP_SCHEDULE}
BACKUP_RETENTION_DAYS: ${BACKUP_RETENTION_DAYS}
volumes:
- ./backup:/backup
- ./scripts/backup.sh:/backup.sh
entrypoint: /bin/sh
command: -c "crond -f -l 2"
networks:
- db_network
depends_on:
postgres:
condition: service_healthy
volumes:
postgres_data:
driver: local
driver_opts:
type: none
o: bind
device: /data/postgres
networks:
db_network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
EOF
# scripts/backup.sh
cat > scripts/backup.sh << 'EOF'
#!/bin/sh
set -e
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backup"
POSTGRES_HOST="postgres"
echo "Starting backup at ${TIMESTAMP}"
# ์ ์ฒด ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐฑ์
pg_dumpall -h ${POSTGRES_HOST} -U ${POSTGRES_USER} | gzip > \
${BACKUP_DIR}/full_backup_${TIMESTAMP}.sql.gz
# ๊ฐ๋ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฐฑ์
(์ปค์คํ
ํฌ๋งท)
pg_dump -h ${POSTGRES_HOST} -U ${POSTGRES_USER} -Fc ${POSTGRES_DB} > \
${BACKUP_DIR}/${POSTGRES_DB}_${TIMESTAMP}.dump
# ์ค๋๋ ๋ฐฑ์
์ญ์
find ${BACKUP_DIR} -name "*.sql.gz" -mtime +${BACKUP_RETENTION_DAYS} -delete
find ${BACKUP_DIR} -name "*.dump" -mtime +${BACKUP_RETENTION_DAYS} -delete
echo "Backup completed successfully"
# ๋ฐฑ์
๊ฒ์ฆ
if [ -f "${BACKUP_DIR}/full_backup_${TIMESTAMP}.sql.gz" ]; then
SIZE=$(du -h "${BACKUP_DIR}/full_backup_${TIMESTAMP}.sql.gz" | cut -f1)
echo "Backup size: ${SIZE}"
else
echo "ERROR: Backup file not found!"
exit 1
fi
EOF
chmod +x scripts/backup.sh
# crontab ์ค์ (๋ฐฑ์
์ปจํ
์ด๋์ฉ)
cat > scripts/crontab << EOF
${BACKUP_SCHEDULE} /backup.sh >> /var/log/backup.log 2>&1
EOF
# 1. ์ค์ ๊ฒ์ฆ
docker-compose config
# 2. ์ปจํ
์ด๋ ์์
docker-compose up -d
# 3. ๋ก๊ทธ ํ์ธ
docker-compose logs -f postgres
# 4. ํฌ์ค์ฒดํฌ ํ์ธ
docker-compose ps
# 5. DB ์ ์ ํ
์คํธ
docker exec -it production-postgres psql -U ${POSTGRES_USER} -d ${POSTGRES_DB}
# 6. ๋ฐฑ์
ํ
์คํธ
docker exec postgres-backup /backup.sh
# 7. ๋ชจ๋ํฐ๋ง ํ์ธ
curl http://localhost:9187/metrics
- โ ์ปจํ ์ด๋๊ฐ ์ ์์ ์ผ๋ก ์คํ ์ค์ธ๊ฐ?
- โ ํฌ์ค์ฒดํฌ๊ฐ ํต๊ณผํ๋๊ฐ?
- โ ๋ณผ๋ฅจ์ด ์ฌ๋ฐ๋ฅด๊ฒ ๋ง์ดํธ๋์๋๊ฐ?
- โ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ์ ์์ธ๊ฐ?
- โ ๋ฐฑ์ ์ด ์ ์์ ์ผ๋ก ์คํ๋๋๊ฐ?
- โ ๋ชจ๋ํฐ๋ง ๋ฉํธ๋ฆญ์ด ์์ง๋๋๊ฐ?
- โ ๋ก๊ทธ๊ฐ ์ ์์ ์ผ๋ก ๊ธฐ๋ก๋๋๊ฐ?
๐ง ํธ๋ฌ๋ธ์ํ : ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค
โ ๋ฌธ์ 1: ์ปจํ ์ด๋๊ฐ ๊ณ์ ์ฌ์์๋จ
# ๋ก๊ทธ ํ์ธ
docker logs production-postgres
# ์ผ๋ฐ์ ์ธ ์์ธ๋ค:
# 1. ๊ถํ ๋ฌธ์
sudo chown -R 999:999 /data/postgres
# 2. ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ
# docker-compose.yml์์ ๋ฉ๋ชจ๋ฆฌ ์ ํ ์กฐ์
deploy:
resources:
limits:
memory: 8G # ๋ ํฐ ๊ฐ์ผ๋ก ๋ณ๊ฒฝ
# 3. ์ค์ ํ์ผ ์ค๋ฅ
# postgresql.conf ๋ฌธ๋ฒ ๊ฒ์ฌ
docker run --rm -v $(pwd)/config:/config postgres:15-alpine \
postgres --config-file=/config/postgresql.conf -C shared_buffers
# 4. ํฌํธ ์ถฉ๋
# ์ด๋ฏธ ์ฌ์ฉ ์ค์ธ ํฌํธ ํ์ธ
sudo netstat -tlnp | grep 5432
# ๋ค๋ฅธ ํฌํธ๋ก ๋ณ๊ฒฝํ๊ฑฐ๋ ๊ธฐ์กด ํ๋ก์ธ์ค ์ข
๋ฃ
โ ๋ฌธ์ 2: ๋์คํฌ ๊ณต๊ฐ ๋ถ์กฑ
# 1. ๋์คํฌ ์ฌ์ฉ๋ ํ์ธ
df -h
docker system df
# 2. ์ฌ์ฉํ์ง ์๋ ๋ฆฌ์์ค ์ ๋ฆฌ
docker system prune -a --volumes
# 3. ๋ก๊ทธ ํ์ผ ์ ๋ฆฌ
docker exec production-postgres sh -c "find /var/lib/postgresql/data/pg_log -name '*.log' -mtime +7 -delete"
# 4. ์ค๋๋ ๋ฐฑ์
์๋ ์ญ์
find /backup -name "*.sql.gz" -mtime +30 -delete
# 5. WAL ํ์ผ ์ ๋ฆฌ (PostgreSQL)
docker exec production-postgres psql -U postgres -c "SELECT pg_switch_wal();"
docker exec production-postgres psql -U postgres -c "CHECKPOINT;"
# 6. ๋ณผ๋ฅจ ํฌ๊ธฐ ๋ชจ๋ํฐ๋ง ์คํฌ๋ฆฝํธ
cat > scripts/monitor_disk.sh << 'EOF'
#!/bin/bash
THRESHOLD=80
USAGE=$(df -h /data/postgres | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $USAGE -gt $THRESHOLD ]; then
echo "WARNING: Disk usage is ${USAGE}%"
# ์๋ฆผ ์ ์ก (Slack, Email ๋ฑ)
fi
EOF
โ ๋ฌธ์ 3: ์ฑ๋ฅ ์ ํ
# 1. ํ์ฌ ์คํ ์ค์ธ ์ฟผ๋ฆฌ ํ์ธ (PostgreSQL)
docker exec production-postgres psql -U postgres -c "
SELECT pid, age(clock_timestamp(), query_start), usename, query
FROM pg_stat_activity
WHERE query != '<IDLE>' AND query NOT ILIKE '%pg_stat_activity%'
ORDER BY query_start desc;
"
# 2. ๋๋ฆฐ ์ฟผ๋ฆฌ ์ฐพ๊ธฐ
docker exec production-postgres psql -U postgres -c "
SELECT query, calls, total_time, mean_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
"
# 3. ์ธ๋ฑ์ค ํ์ธ
docker exec production-postgres psql -U postgres -d myapp -c "
SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY schemaname, tablename;
"
# 4. ํ
์ด๋ธ ํต๊ณ ์
๋ฐ์ดํธ
docker exec production-postgres psql -U postgres -d myapp -c "ANALYZE;"
# 5. ์ปค๋ฅ์
ํ ์ค์ ํ์ธ
docker exec production-postgres psql -U postgres -c "
SELECT count(*) as connections, state
FROM pg_stat_activity
GROUP BY state;
"
# 6. ๋ฆฌ์์ค ์ฌ์ฉ๋ ์ค์๊ฐ ๋ชจ๋ํฐ๋ง
docker stats production-postgres
โ ๋ฌธ์ 4: ๋ฐ์ดํฐ ์์ค ๋๋ ์์
# 1. ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ๊ฒ์ฌ (PostgreSQL)
docker exec production-postgres psql -U postgres -d myapp -c "
SELECT datname, pg_database_size(datname)
FROM pg_database;
"
# 2. ํ
์ด๋ธ ์์ ๊ฒ์ฌ
docker exec production-postgres psql -U postgres -d myapp -c "
SELECT schemaname, tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
"
# 3. ๋ฐฑ์
์์ ๋ณต๊ตฌ
# ๋จผ์ ํ์ฌ ์ํ ๋ฐฑ์
docker exec production-postgres pg_dump -U postgres -Fc myapp > emergency_backup.dump
# ์ต์ ๋ฐฑ์
์์ ๋ณต๊ตฌ
docker exec -i production-postgres pg_restore -U postgres -d myapp -c < /backup/myapp_20240115_020000.dump
# 4. PITR (Point-In-Time Recovery) ์ค์
# postgresql.conf์ ์ถ๊ฐ
archive_mode = on
archive_command = 'cp %p /backup/archive/%f'
restore_command = 'cp /backup/archive/%f %p'
# 5. ํน์ ์์ ์ผ๋ก ๋ณต๊ตฌ
docker exec production-postgres psql -U postgres -c "
SELECT pg_create_restore_point('before_disaster');
"
ํธ๋ฌ๋ธ์ํ ๋ณด๋ค ์ค์ํ ๊ฑด ๋ฌธ์ ๋ฅผ ์๋ฐฉํ๋ ๊ฑฐ์ผ! ์ ๊ธฐ์ ์ธ ๋ฐฑ์ , ๋ชจ๋ํฐ๋ง, ํฌ์ค์ฒดํฌ๋ฅผ ํตํด ๋ฌธ์ ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ๊ณ ๋์ํ๋ ๊ฒ ํต์ฌ์ด์ผ. ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๊ฒฝํ ๋ง์ DBA๋ DevOps ์ ๋ฌธ๊ฐ์ ์ปจ์คํ ์ ๋ฐ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ง!
๐ ๋ฒ ์คํธ ํ๋ํฐ์ค ์ด์ ๋ฆฌ
โจ ํต์ฌ ์์น 10๊ฐ์ง
โข Named Volume ์ฌ์ฉ
โข ์ ๊ธฐ์ ์ธ ๋ฐฑ์ (์ต์ ์ผ 1ํ)
โข ๋ฐฑ์ ๊ฒ์ฆ ํ๋ก์ธ์ค ๊ตฌ์ถ
โข ์คํ์ฌ์ดํธ ๋ฐฑ์ (ํด๋ผ์ฐ๋ ์คํ ๋ฆฌ์ง ํ์ฉ)
โข ํ๊ฒฝ ๋ณ์๋ก ๋ฏผ๊ฐ ์ ๋ณด ๊ด๋ฆฌ
โข ๋คํธ์ํฌ ๊ฒฉ๋ฆฌ (๋ด๋ถ ๋คํธ์ํฌ๋ง ์ฌ์ฉ)
โข ์ต์ ๊ถํ ์์น ์ ์ฉ
โข ์ ๊ธฐ์ ์ธ ๋ณด์ ์ ๋ฐ์ดํธ
โข SSL/TLS ์ํธํ ํต์
โข ์ ์ ํ ๋ฆฌ์์ค ํ ๋น (CPU, ๋ฉ๋ชจ๋ฆฌ)
โข ์คํ ๋ฆฌ์ง ๋๋ผ์ด๋ฒ ์ต์ ํ
โข ์บ์ฑ ๋ ์ด์ด ๊ตฌ์ถ (Redis ๋ฑ)
โข ์ธ๋ฑ์ค ์ต์ ํ
โข ์ฟผ๋ฆฌ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง
โข ๋ ํ๋ฆฌ์ผ์ด์ ์ค์ (๋ง์คํฐ-์ฌ๋ ์ด๋ธ)
โข ๋ก๋ ๋ฐธ๋ฐ์ ๊ตฌ์ถ
โข ์๋ ํ์ผ์ค๋ฒ ๋ฉ์ปค๋์ฆ
โข ํฌ์ค์ฒดํฌ ์ค์
โข ์ฌ์์ ์ ์ฑ (restart: unless-stopped)
โข ๋ฉํธ๋ฆญ ์์ง (Prometheus + Grafana)
โข ์ค์ ์ง์ค์ ๋ก๊น (ELK Stack, Loki)
โข ์๋ฆผ ์์คํ ๊ตฌ์ถ
โข ๋์๋ณด๋ ๊ตฌ์ฑ
โข ์ ๊ธฐ์ ์ธ ๋ก๊ทธ ๋ถ์
โข ์ด๋ฏธ์ง ํ๊ทธ ๋ช ์ (latest ์ฌ์ฉ ๊ธ์ง)
โข ์ ๊ทธ๋ ์ด๋ ์ ํ ์คํธ
โข ๋กค๋ฐฑ ๊ณํ ์๋ฆฝ
โข ๋ณ๊ฒฝ ์ฌํญ ๋ฌธ์ํ
โข CPU ๋ฐ ๋ฉ๋ชจ๋ฆฌ ์ ํ ์ค์
โข ๋์คํฌ I/O ์ ํ
โข ๋คํธ์ํฌ ๋์ญํญ ๊ด๋ฆฌ
โข ์ปค๋ฅ์ ํ ํฌ๊ธฐ ์กฐ์
โข ํ๋ก๋์ ๊ณผ ๋์ผํ ๊ตฌ์ฑ
โข ์ ๊ธฐ์ ์ธ ์ฌํด ๋ณต๊ตฌ ํ๋ จ
โข ์ฑ๋ฅ ํ ์คํธ ์ค์
โข ๋ถํ ํ ์คํธ ์ํ
โข ์ํคํ ์ฒ ๋ค์ด์ด๊ทธ๋จ
โข ์ด์ ๋งค๋ด์ผ ์์ฑ
โข ํธ๋ฌ๋ธ์ํ ๊ฐ์ด๋
โข ๋ณ๊ฒฝ ์ด๋ ฅ ๊ด๋ฆฌ
โข CI/CD ํ์ดํ๋ผ์ธ ๊ตฌ์ถ
โข ๋ฐฑ์ ์๋ํ
โข ๋ชจ๋ํฐ๋ง ์๋ํ
โข ์๋ฆผ ์๋ํ
โข ์ค์ผ์ผ๋ง ์๋ํ
๐ ํ์ต ๋ฆฌ์์ค ๋ฐ ์ปค๋ฎค๋ํฐ
| ๋ฆฌ์์ค | ์ค๋ช | ๋งํฌ |
|---|---|---|
| Docker ๊ณต์ ๋ฌธ์ | ๊ฐ์ฅ ์ ํํ๊ณ ์ต์ ์ ๋ณด | docs.docker.com |
| PostgreSQL ์ํค | PostgreSQL ์ด์ ๊ฐ์ด๋ | wiki.postgresql.org |
| MySQL ๋ฌธ์ | MySQL ๋ ํผ๋ฐ์ค ๋งค๋ด์ผ | dev.mysql.com/doc |
| Stack Overflow | Q&A ์ปค๋ฎค๋ํฐ | stackoverflow.com |
| Reddit r/docker | Docker ์ปค๋ฎค๋ํฐ | reddit.com/r/docker |
| ์ฌ๋ฅ๋ท | ์ ๋ฌธ๊ฐ ์๋ด ๋ฐ ์ง์ ๊ณต์ | jaenung.net |
Docker์ DB ์ด์์ ์ด๋ ค์์ ๊ฒช๊ณ ์๋ค๋ฉด, ์ฌ๋ฅ๋ท์์ ๊ฒฝํ ๋ง์ DevOps ์์ง๋์ด๋ DBA์ ๋์์ ๋ฐ์ ์ ์์ด! ์ค์๊ฐ ์ปจ์คํ ๋ถํฐ ์์คํ ๊ตฌ์ถ๊น์ง ๋ค์ํ ์ฌ๋ฅ์ ๊ฑฐ๋ํ ์ ์์ง. ํนํ ํ๋ก๋์ ํ๊ฒฝ ๊ตฌ์ถ์ด๋ ์ฑ๋ฅ ํ๋ ๊ฐ์ ์ ๋ฌธ์ ์ธ ์์ ์ ๊ฒฝํ์์ ๋์์ ๋ฐ๋ ๊ฒ ์๊ฐ๊ณผ ๋น์ฉ์ ์ ์ฝํ๋ ์ง๋ฆ๊ธธ์ด์ผ! ๐
๐ฏ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
ํต์ฌ ํฌ์ธํธ ์์ฝ:
๐ณ ๋ฐ์ดํฐ ์์์ฑ: Named Volume๊ณผ ์ ๊ธฐ ๋ฐฑ์ ์ผ๋ก ๋ฐ์ดํฐ ๋ณดํธ
๐ ๋ณด์: ๋คํธ์ํฌ ๊ฒฉ๋ฆฌ์ ํ๊ฒฝ ๋ณ์ ๊ด๋ฆฌ๋ก ์์ ์ฑ ํ๋ณด
โก ์ฑ๋ฅ: ๋ฆฌ์์ค ์ต์ ํ์ ์บ์ฑ์ผ๋ก ๋น ๋ฅธ ์๋ต ์๋
๐ ๊ณ ๊ฐ์ฉ์ฑ: ๋ ํ๋ฆฌ์ผ์ด์ ๊ณผ ๋ก๋ ๋ฐธ๋ฐ์ฑ์ผ๋ก ๋ฌด์ค๋จ ์๋น์ค
๐ ๋ชจ๋ํฐ๋ง: ์ค์๊ฐ ๋ฉํธ๋ฆญ ์์ง๊ณผ ์๋ฆผ์ผ๋ก ์ ์ ์ ๋์
๐ ๏ธ ์๋ํ: CI/CD์ ์คํฌ๋ฆฝํธ๋ก ์ด์ ํจ์จ์ฑ ๊ทน๋ํ
๊ธฐ์ตํด! ์๋ฒฝํ ์์คํ ์ ์ฒ์๋ถํฐ ๋ง๋ค์ด์ง์ง ์์. ์์ ๊ฒ๋ถํฐ ์์ํด์ ์ ์ง์ ์ผ๋ก ๊ฐ์ ํด ๋๊ฐ๋ ๊ฒ ์ค์ํด. ๊ทธ๋ฆฌ๊ณ ํผ์ ๊ณ ๋ฏผํ์ง ๋ง๊ณ , ์ปค๋ฎค๋ํฐ๋ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์ ์ ๊ทน ํ์ฉํด์ ๊ฒฝํ์ ๊ณต์ ํ๊ณ ๋ฐฐ์๋๊ฐ์! ๐ช
Docker์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ปจํ ์ด๋ ์ด์, ์ด์ ์์ ๊ฐ ์๊ฒผ์ง? ์ฌ๋ฌ๋ถ์ ํ๋ก์ ํธ๊ฐ ์ฑ๊ณต์ ์ผ๋ก ์ด์๋๊ธธ ๋ฐ๋๊ฒ! ํ์ดํ ! ๐
๐ ๋ ๋ง์ ์ง์์ ์ฐพ๊ณ ๊ณ์ ๊ฐ์?
์ฌ๋ฅ๋ท์ '์ง์์ธ์ ์ฒ'์์ ๋ค์ํ IT ์ ๋ฌธ ์ง์์ ๋ง๋๋ณด์ธ์!
ํ๋ก๊ทธ๋๋ฐ, ์๋ฒ ๊ด๋ฆฌ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ต์ ํ ๋ฑ
์ค๋ฌด์ ๋ฐ๋ก ์ ์ฉํ ์ ์๋ ๊ฒ์ฆ๋ ์ง์์ด ๊ฐ๋ํฉ๋๋ค.
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.