์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿณ Docker์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปจํ…Œ์ด๋„ˆ ์šด์˜ ์ „๋žต: ์‹ค์ „ ๊ฐ€์ด๋“œ

๐Ÿณ Docker์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปจํ…Œ์ด๋„ˆ ์šด์˜ ์ „๋žต: ์‹ค์ „ ๊ฐ€์ด๋“œ

์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ •์ ์ด๊ณ  ํšจ์œจ์ ์ธ DB ์šด์˜ ๋…ธํ•˜์šฐ

๐ŸŽฏ ์ด ๊ธ€์„ ์ฝ์œผ๋ฉด ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋“ค

Docker ํ™˜๊ฒฝ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์šด์˜ํ•˜๋Š” ๊ฑด ์ƒ๊ฐ๋ณด๋‹ค ๊นŒ๋‹ค๋กœ์šด ์ผ์ด์•ผ. ๋‹จ์ˆœํžˆ ์ปจํ…Œ์ด๋„ˆ๋งŒ ๋„์šฐ๋ฉด ๋? ์ฒœ๋งŒ์—! ๋ฐ์ดํ„ฐ ์˜์†์„ฑ, ๋ฐฑ์—… ์ „๋žต, ์„ฑ๋Šฅ ์ตœ์ ํ™”, ๋ณด์•ˆ ์„ค์ •๊นŒ์ง€... ๊ณ ๋ คํ•ด์•ผ ํ•  ๊ฒŒ ํ•œ๋‘ ๊ฐ€์ง€๊ฐ€ ์•„๋‹ˆ์ง€. ์ด ๊ธ€์—์„œ๋Š” ์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๊ฒ€์ฆ๋œ DB ์ปจํ…Œ์ด๋„ˆ ์šด์˜ ์ „๋žต์„ ์นœ๊ตฌ์—๊ฒŒ ์„ค๋ช…ํ•˜๋“ฏ ์‰ฝ๊ณ  ์žฌ๋ฏธ์žˆ๊ฒŒ ํ’€์–ด๋ณผ๊ฒŒ! ๐Ÿš€

๐Ÿค” ์™œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ปจํ…Œ์ด๋„ˆ๋กœ ์šด์˜ํ• ๊นŒ?

์ „ํ†ต์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” "์ ˆ๋Œ€ ์ปจํ…Œ์ด๋„ˆ๋กœ ์šด์˜ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค"๋Š” ๋ถˆ๋ฌธ์œจ์ด ์žˆ์—ˆ์–ด. ๋ฐ์ดํ„ฐ์˜ ์˜์†์„ฑ๊ณผ ์•ˆ์ •์„ฑ์ด ์ตœ์šฐ์„ ์ด์—ˆ์œผ๋‹ˆ๊นŒ. ํ•˜์ง€๋งŒ ์š”์ฆ˜์€ ์ƒํ™ฉ์ด ๋งŽ์ด ๋‹ฌ๋ผ์กŒ์ง€!

์ปจํ…Œ์ด๋„ˆ DB์˜ ์žฅ์ ๋“ค:

โšก ๋น ๋ฅธ ํ™˜๊ฒฝ ๊ตฌ์ถ•: ๋ช‡ ๋ถ„ ์•ˆ์— ๋ณต์žกํ•œ DB ํด๋Ÿฌ์Šคํ„ฐ๋„ ๊ตฌ์„ฑ ๊ฐ€๋Šฅ
๐Ÿ”„ ์ผ๊ด€๋œ ํ™˜๊ฒฝ: ๊ฐœ๋ฐœ/์Šคํ…Œ์ด์ง•/ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์„ ๋™์ผํ•˜๊ฒŒ ์œ ์ง€
๐Ÿ“ฆ ๋ฒ„์ „ ๊ด€๋ฆฌ: DB ๋ฒ„์ „์„ ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋กœ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ด€๋ฆฌ
๐ŸŽฏ ๋ฆฌ์†Œ์Šค ๊ฒฉ๋ฆฌ: CPU, ๋ฉ”๋ชจ๋ฆฌ ๋“ฑ ๋ฆฌ์†Œ์Šค๋ฅผ ์ •๋ฐ€ํ•˜๊ฒŒ ์ œ์–ด
๐Ÿ”ง ์‰ฌ์šด ๋ณต์ œ: ํ…Œ์ŠคํŠธ์šฉ DB๋ฅผ ์ˆœ์‹๊ฐ„์— ๋ณต์ œ ๊ฐ€๋Šฅ
โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ
ํ•˜์ง€๋งŒ ๋ชจ๋“  ์ƒํ™ฉ์— ์ปจํ…Œ์ด๋„ˆ DB๊ฐ€ ์ •๋‹ต์€ ์•„๋‹ˆ์•ผ. ๋Œ€๊ทœ๋ชจ ํŠธ๋žœ์žญ์…˜์ด ๋ฐœ์ƒํ•˜๋Š” ๊ธˆ์œต๊ถŒ์ด๋‚˜, ๊ทน๋„์˜ ์•ˆ์ •์„ฑ์ด ์š”๊ตฌ๋˜๋Š” ์‹œ์Šคํ…œ์—์„œ๋Š” ์—ฌ์ „ํžˆ ๋ฒ ์–ด๋ฉ”ํƒˆ์ด๋‚˜ ์ „์šฉ VM์„ ์„ ํ˜ธํ•˜๊ธฐ๋„ ํ•ด. ์ƒํ™ฉ์— ๋งž๋Š” ์„ ํƒ์ด ์ค‘์š”ํ•˜์ง€!
Docker DB ์šด์˜ ๊ตฌ์กฐ Docker ํ˜ธ์ŠคํŠธ PostgreSQL ์ปจํ…Œ์ด๋„ˆ ๐Ÿ˜ ๋ณผ๋ฅจ: /var/lib/postgresql ํฌํŠธ: 5432 MySQL ์ปจํ…Œ์ด๋„ˆ ๐Ÿฌ ๋ณผ๋ฅจ: /var/lib/mysql ํฌํŠธ: 3306 Redis ์ปจํ…Œ์ด๋„ˆ โšก ๋ณผ๋ฅจ: /data ํฌํŠธ: 6379

๐Ÿ’พ ๋ฐ์ดํ„ฐ ์˜์†์„ฑ ์ „๋žต: ๋ณผ๋ฅจ์ด ๋‹ต์ด๋‹ค

์ปจํ…Œ์ด๋„ˆ์˜ ๊ฐ€์žฅ ํฐ ํŠน์ง•์ด ๋ญ๋ƒ๊ณ ? ๋ฐ”๋กœ "ํœ˜๋ฐœ์„ฑ"์ด์•ผ. ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ญ์ œํ•˜๋ฉด ๊ทธ ์•ˆ์˜ ๋ฐ์ดํ„ฐ๋„ ํ•จ๊ป˜ ์‚ฌ๋ผ์ง€์ง€. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ด๊ฑด ์žฌ์•™์ด๋‚˜ ๋งˆ์ฐฌ๊ฐ€์ง€์•ผ! ๐Ÿ˜ฑ

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ์—๊ฒŒ๋Š” Docker Volume์ด ํ•„์š”ํ•ด. ๋ณผ๋ฅจ์€ ์ปจํ…Œ์ด๋„ˆ์™€ ๋…๋ฆฝ์ ์œผ๋กœ ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ์•ผ.

๐Ÿ“Œ ๋ณผ๋ฅจ ํƒ€์ž… ๋น„๊ต

๋ณผ๋ฅจ ํƒ€์ž… ํŠน์ง• ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค ์„ฑ๋Šฅ
Named Volume Docker๊ฐ€ ๊ด€๋ฆฌํ•˜๋Š” ๋ณผ๋ฅจ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ, ๋ฐฑ์—… ์šฉ์ด โญโญโญโญโญ
Bind Mount ํ˜ธ์ŠคํŠธ ๋””๋ ‰ํ† ๋ฆฌ ์ง์ ‘ ๋งˆ์šดํŠธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ, ์„ค์ • ํŒŒ์ผ ๊ณต์œ  โญโญโญโญ
tmpfs Mount ๋ฉ”๋ชจ๋ฆฌ์— ์ž„์‹œ ์ €์žฅ ์บ์‹œ, ์ž„์‹œ ๋ฐ์ดํ„ฐ โญโญโญโญโญ

๐Ÿ”ง ์‹ค์ „ ๋ณผ๋ฅจ ์„ค์ • ์˜ˆ์ œ

PostgreSQL ๋ณผ๋ฅจ ์„ค์ • (docker-compose.yml)
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 ์ปจํ…Œ์ด๋„ˆ ๋งŒ๋“ค๊ธฐ

์ปจํ…Œ์ด๋„ˆ๋ผ๊ณ  ํ•ด์„œ ๋ณด์•ˆ์„ ์†Œํ™€ํžˆ ํ•˜๋ฉด ํฐ์ผ ๋‚˜! ํŠนํžˆ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ๋ฏผ๊ฐํ•œ ์ •๋ณด์˜ ๋ณด๊ณ ๋‹ˆ๊นŒ ๋”์šฑ ์‹ ๊ฒฝ ์จ์•ผ ํ•ด. ์‹ค์ œ๋กœ ๋งŽ์€ ๋ณด์•ˆ ์‚ฌ๊ณ ๊ฐ€ ์ž˜๋ชป ์„ค์ •๋œ ์ปจํ…Œ์ด๋„ˆ DB์—์„œ ๋ฐœ์ƒํ–ˆ๊ฑฐ๋“ . ๐Ÿ˜ฐ

๐Ÿ›ก๏ธ ํ•„์ˆ˜ ๋ณด์•ˆ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

1. ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ

์ ˆ๋Œ€๋กœ 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
2. ๋„คํŠธ์›Œํฌ ๊ฒฉ๋ฆฌ

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  # ์™ธ๋ถ€ ์ธํ„ฐ๋„ท ์ ‘๊ทผ ์ฐจ๋‹จ
3. ์ฝ๊ธฐ ์ „์šฉ ๋ฃจํŠธ ํŒŒ์ผ์‹œ์Šคํ…œ

์ปจํ…Œ์ด๋„ˆ์˜ ๋ฃจํŠธ ํŒŒ์ผ์‹œ์Šคํ…œ์„ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ์•…์„ฑ ์ฝ”๋“œ๊ฐ€ ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฑธ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์–ด.
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์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ํ›จ์”ฌ ์•ˆ์ „ํ•ด!
๋ณด์•ˆ ๊ณ„์ธต ๊ตฌ์กฐ ๐Ÿฆน ์™ธ๋ถ€ ๊ณต๊ฒฉ์ž ์ฐจ๋‹จ! ๋ฐฉํ™”๋ฒฝ ๐Ÿ”ฅ ํฌํŠธ ์ œํ•œ IP ํ•„ํ„ฐ๋ง ํ—ˆ์šฉ Docker ๋„คํŠธ์›Œํฌ ์•ฑ ์ปจํ…Œ์ด๋„ˆ ๐Ÿ“ฑ DB ์ปจํ…Œ์ด๋„ˆ ๐Ÿ—„๏ธ ์•”ํ˜ธํ™”๋œ ๋ณผ๋ฅจ ๐Ÿ”’ ์™ธ๋ถ€์—์„œ๋Š” DB์— ์ง์ ‘ ์ ‘๊ทผ ๋ถˆ๊ฐ€, ์•ฑ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ†ตํ•ด์„œ๋งŒ ํ†ต์‹ 

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™”: ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ธ DB ์ปจํ…Œ์ด๋„ˆ

์ปจํ…Œ์ด๋„ˆ๋กœ DB๋ฅผ ์šด์˜ํ•œ๋‹ค๊ณ  ํ•ด์„œ ์„ฑ๋Šฅ์„ ํฌ๊ธฐํ•  ์ˆœ ์—†์ง€! ์˜ฌ๋ฐ”๋ฅธ ์„ค์ •๊ณผ ํŠœ๋‹์œผ๋กœ ๋ฒ ์–ด๋ฉ”ํƒˆ์— ๊ทผ์ ‘ํ•œ ์„ฑ๋Šฅ์„ ๋‚ผ ์ˆ˜ ์žˆ์–ด. ์‹ค์ œ๋กœ ์ž˜ ํŠœ๋‹๋œ ์ปจํ…Œ์ด๋„ˆ DB๋Š” ์ผ๋ฐ˜ VM๋ณด๋‹ค ์˜คํžˆ๋ ค ๋น ๋ฅผ ์ˆ˜๋„ ์žˆ๊ฑฐ๋“ ! ๐Ÿš€

๐ŸŽฏ ๋ฆฌ์†Œ์Šค ์ œํ•œ ๋ฐ ํ• ๋‹น

CPU์™€ ๋ฉ”๋ชจ๋ฆฌ ์ตœ์  ์„ค์ •

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

๐Ÿ’ฟ ์Šคํ† ๋ฆฌ์ง€ ๋“œ๋ผ์ด๋ฒ„ ์„ ํƒ

Docker์˜ ์Šคํ† ๋ฆฌ์ง€ ๋“œ๋ผ์ด๋ฒ„๋Š” ์„ฑ๋Šฅ์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์ณ. DB์ฒ˜๋Ÿผ I/O๊ฐ€ ๋งŽ์€ ์›Œํฌ๋กœ๋“œ์—์„œ๋Š” ํŠนํžˆ ์ค‘์š”ํ•˜์ง€!
๋“œ๋ผ์ด๋ฒ„ ์„ฑ๋Šฅ ์•ˆ์ •์„ฑ ์ถ”์ฒœ ์‹œ๋‚˜๋ฆฌ์˜ค
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:

๐Ÿ”„ ๋ฐฑ์—… ๋ฐ ๋ณต๊ตฌ ์ „๋žต

"๋ฐฑ์—…์ด ์—†์œผ๋ฉด ๋ฐ์ดํ„ฐ๋„ ์—†๋‹ค"๋Š” ๋ง์ด ์žˆ์–ด. ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ๋Š” ๋”์šฑ ์ฒ ์ €ํ•œ ๋ฐฑ์—… ์ „๋žต์ด ํ•„์š”ํ•ด. ์ปจํ…Œ์ด๋„ˆ๋Š” ์–ธ์ œ๋“  ์‚ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์œผ๋‹ˆ๊นŒ! ๐Ÿ˜จ

๐Ÿ“ฆ ๋ฐฑ์—… ๋ฐฉ๋ฒ• ๋น„๊ต

1. ๋ณผ๋ฅจ ๋ฐฑ์—… (์ถ”์ฒœ)

๊ฐ€์žฅ ๊ฐ„๋‹จํ•˜๊ณ  ์•ˆ์ „ํ•œ ๋ฐฉ๋ฒ•. 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"
2. ๋…ผ๋ฆฌ์  ๋ฐฑ์—… (pg_dump, mysqldump)

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/
3. ์Šค๋ƒ…์ƒท ๋ฐฑ์—… (๊ณ ๊ธ‰)

์Šคํ† ๋ฆฌ์ง€ ๋ ˆ๋ฒจ์—์„œ ์Šค๋ƒ…์ƒท์„ ์ƒ์„ฑ. ๊ฐ€์žฅ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์ด์ง€๋งŒ ์ธํ”„๋ผ ์ง€์›์ด ํ•„์š”ํ•ด.
# 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๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐฑ์—…์„ ์ž๋™ํ™”ํ•˜๊ณ , ๋ฐฑ์—… ์„ฑ๊ณต/์‹คํŒจ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๋ฐฑ์—… ์ž๋™ํ™” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜๋„ ์žˆ์ง€!
๋ฐฑ์—… ๋ฐ ๋ณต๊ตฌ ํ”„๋กœ์„ธ์Šค ํ”„๋กœ๋•์…˜ DB ๐Ÿ—„๏ธ ์‹ค์‹œ๊ฐ„ ์šด์˜ ๋ฐฑ์—… ๋ฐฑ์—… ์Šคํ† ๋ฆฌ์ง€ ๐Ÿ’พ ์•ˆ์ „ํ•œ ๋ณด๊ด€ ๋ณต์ œ ํด๋ผ์šฐ๋“œ ๋ฐฑ์—… โ˜๏ธ ์›๊ฒฉ ๋ณด๊ด€ โš ๏ธ ์žฅ์•  ๋ฐœ์ƒ ์‹œ ๋ณต๊ตฌ ํ”„๋กœ์„ธ์Šค ์†์ƒ๋œ DB โŒ ๋ฐ์ดํ„ฐ ์†์‹ค ๋ณต๊ตฌ ์š”์ฒญ ๋ฐ์ดํ„ฐ ๋ณต์› ๋ณต๊ตฌ๋œ DB โœ… ์ •์ƒ ์šด์˜ ๋ณต๊ตฌ ์‹œ๊ฐ„ ๋ณผ๋ฅจ ๋ฐฑ์—…: 5-10๋ถ„ ๋…ผ๋ฆฌ์  ๋ฐฑ์—…: 30-60๋ถ„ ์Šค๋ƒ…์ƒท: 1-3๋ถ„

๐Ÿ“Š ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๋กœ๊น…

์ปจํ…Œ์ด๋„ˆ DB๋ฅผ ์šด์˜ํ•˜๋ฉด์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒŒ ๋ญ๋ƒ๊ณ ? ๋ฐ”๋กœ ๊ฐ€์‹œ์„ฑ(Visibility)์ด์•ผ! ๋ญ”๊ฐ€ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒผ์„ ๋•Œ ๋น ๋ฅด๊ฒŒ ํŒŒ์•…ํ•˜๊ณ  ๋Œ€์‘ํ•˜๋ ค๋ฉด ์ œ๋Œ€๋กœ ๋œ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๋กœ๊น… ์‹œ์Šคํ…œ์ด ํ•„์ˆ˜์•ผ. ๐Ÿ”

๐Ÿ“ˆ ๋ชจ๋‹ˆํ„ฐ๋ง ์Šคํƒ ๊ตฌ์„ฑ

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) ๊ตฌ์„ฑ

ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋‹จ์ผ ์žฅ์• ์ (Single Point of Failure)์„ ์—†์• ๋Š” ๊ฒŒ ์ค‘์š”ํ•ด. DB ์ปจํ…Œ์ด๋„ˆ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์•ผ! ๋ ˆํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ํด๋Ÿฌ์Šคํ„ฐ๋ง์œผ๋กœ ๊ณ ๊ฐ€์šฉ์„ฑ์„ ํ™•๋ณดํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿ’ช

๐Ÿ” 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๋กœ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๊ณ , ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ๋„ ๋Š˜๋ ธ๋”๋‹ˆ ํ•ด๊ฒฐ๋์ง€. ๋ ˆํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์„ฑํ•  ๋•Œ๋Š” ์Šฌ๋ ˆ์ด๋ธŒ ์„œ๋ฒ„์˜ ์„ฑ๋Šฅ๋„ ๋งˆ์Šคํ„ฐ์™€ ๋น„์Šทํ•˜๊ฒŒ ๋งž์ถฐ์•ผ ํ•ด!

๐Ÿš€ ์‹ค์ „ ๋ฐฐํฌ ์‹œ๋‚˜๋ฆฌ์˜ค

์ด๋ก ์€ ์ถฉ๋ถ„ํžˆ ๋ฐฐ์› ์œผ๋‹ˆ, ์ด์ œ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ๋ฐฐํฌํ•˜๋Š”์ง€ ๋‹จ๊ณ„๋ณ„๋กœ ์•Œ์•„๋ณด์ž! ์ฒ˜์Œ๋ถ€ํ„ฐ ๋๊นŒ์ง€ ๋”ฐ๋ผ ํ•˜๋ฉด ํ”„๋กœ๋•์…˜ ๋ ˆ๋ฒจ์˜ DB ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์šด์˜ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ. ๐ŸŽฏ

๐Ÿ“‹ ๋ฐฐํฌ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

1๋‹จ๊ณ„: ํ™˜๊ฒฝ ์ค€๋น„
# 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
2๋‹จ๊ณ„: ์„ค์ • ํŒŒ์ผ ์ž‘์„ฑ
# 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
3๋‹จ๊ณ„: Docker Compose ํŒŒ์ผ ์ž‘์„ฑ
# 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
4๋‹จ๊ณ„: ๋ฐฑ์—… ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ
# 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
5๋‹จ๊ณ„: ๋ฐฐํฌ ๋ฐ ๊ฒ€์ฆ
# 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 ps๋กœ ํ™•์ธํ•˜๋ฉด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ณ„์† Restarting ์ƒํƒœ
# ๋กœ๊ทธ ํ™•์ธ
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: ๋””์Šคํฌ ๊ณต๊ฐ„ ๋ถ€์กฑ

์ฆ์ƒ: "No space left on device" ์—๋Ÿฌ ๋ฐœ์ƒ
# 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: ์„ฑ๋Šฅ ์ €ํ•˜

์ฆ์ƒ: ์ฟผ๋ฆฌ ์‘๋‹ต ์‹œ๊ฐ„์ด ๋А๋ ค์ง€๊ณ  CPU ์‚ฌ์šฉ๋ฅ ์ด ๋†’์Œ
# 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 ์ „๋ฌธ๊ฐ€์˜ ์ปจ์„คํŒ…์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์ง€!
ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ํ”Œ๋กœ์šฐ์ฐจํŠธ ๋ฌธ์ œ ๋ฐœ์ƒ 1. ๋กœ๊ทธ ํ™•์ธ docker logs ์ปจํ…Œ์ด๋„ˆ ์ƒํƒœ ์›์ธ ํŒŒ์•…? Yes 2. ํ•ด๊ฒฐ ์‹œ๋„ ์„ค์ • ์ˆ˜์ • ๋ฆฌ์†Œ์Šค ์กฐ์ • No 3. ์ถ”๊ฐ€ ์กฐ์‚ฌ ๋ฉ”ํŠธ๋ฆญ ๋ถ„์„ ์ „๋ฌธ๊ฐ€ ์ƒ๋‹ด 4. ๊ฒ€์ฆ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•ด๊ฒฐ๋จ? Yes ์™„๋ฃŒ No

๐ŸŒŸ ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค ์ด์ •๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ DB ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์šด์˜ํ•  ๋•Œ ๊ผญ ์ง€์ผœ์•ผ ํ•  ๋ฒ ์ŠคํŠธ ํ”„๋ž™ํ‹ฐ์Šค๋ฅผ ์ •๋ฆฌํ•ด๋ณผ๊ฒŒ! ๐Ÿ“

โœจ ํ•ต์‹ฌ ์›์น™ 10๊ฐ€์ง€

1. ๋ฐ์ดํ„ฐ ์˜์†์„ฑ ํ™•๋ณด

โ€ข Named Volume ์‚ฌ์šฉ
โ€ข ์ •๊ธฐ์ ์ธ ๋ฐฑ์—… (์ตœ์†Œ ์ผ 1ํšŒ)
โ€ข ๋ฐฑ์—… ๊ฒ€์ฆ ํ”„๋กœ์„ธ์Šค ๊ตฌ์ถ•
โ€ข ์˜คํ”„์‚ฌ์ดํŠธ ๋ฐฑ์—… (ํด๋ผ์šฐ๋“œ ์Šคํ† ๋ฆฌ์ง€ ํ™œ์šฉ)
2. ๋ณด์•ˆ ๊ฐ•ํ™”

โ€ข ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ๋ฏผ๊ฐ ์ •๋ณด ๊ด€๋ฆฌ
โ€ข ๋„คํŠธ์›Œํฌ ๊ฒฉ๋ฆฌ (๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ๋งŒ ์‚ฌ์šฉ)
โ€ข ์ตœ์†Œ ๊ถŒํ•œ ์›์น™ ์ ์šฉ
โ€ข ์ •๊ธฐ์ ์ธ ๋ณด์•ˆ ์—…๋ฐ์ดํŠธ
โ€ข SSL/TLS ์•”ํ˜ธํ™” ํ†ต์‹ 
3. ์„ฑ๋Šฅ ์ตœ์ ํ™”

โ€ข ์ ์ ˆํ•œ ๋ฆฌ์†Œ์Šค ํ• ๋‹น (CPU, ๋ฉ”๋ชจ๋ฆฌ)
โ€ข ์Šคํ† ๋ฆฌ์ง€ ๋“œ๋ผ์ด๋ฒ„ ์ตœ์ ํ™”
โ€ข ์บ์‹ฑ ๋ ˆ์ด์–ด ๊ตฌ์ถ• (Redis ๋“ฑ)
โ€ข ์ธ๋ฑ์Šค ์ตœ์ ํ™”
โ€ข ์ฟผ๋ฆฌ ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง
4. ๊ณ ๊ฐ€์šฉ์„ฑ ๊ตฌ์„ฑ

โ€ข ๋ ˆํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค์ • (๋งˆ์Šคํ„ฐ-์Šฌ๋ ˆ์ด๋ธŒ)
โ€ข ๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ ๊ตฌ์ถ•
โ€ข ์ž๋™ ํŽ˜์ผ์˜ค๋ฒ„ ๋ฉ”์ปค๋‹ˆ์ฆ˜
โ€ข ํ—ฌ์Šค์ฒดํฌ ์„ค์ •
โ€ข ์žฌ์‹œ์ž‘ ์ •์ฑ… (restart: unless-stopped)
5. ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋กœ๊น…

โ€ข ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘ (Prometheus + Grafana)
โ€ข ์ค‘์•™ ์ง‘์ค‘์‹ ๋กœ๊น… (ELK Stack, Loki)
โ€ข ์•Œ๋ฆผ ์‹œ์Šคํ…œ ๊ตฌ์ถ•
โ€ข ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์„ฑ
โ€ข ์ •๊ธฐ์ ์ธ ๋กœ๊ทธ ๋ถ„์„
6. ๋ฒ„์ „ ๊ด€๋ฆฌ

โ€ข ์ด๋ฏธ์ง€ ํƒœ๊ทธ ๋ช…์‹œ (latest ์‚ฌ์šฉ ๊ธˆ์ง€)
โ€ข ์—…๊ทธ๋ ˆ์ด๋“œ ์ „ ํ…Œ์ŠคํŠธ
โ€ข ๋กค๋ฐฑ ๊ณ„ํš ์ˆ˜๋ฆฝ
โ€ข ๋ณ€๊ฒฝ ์‚ฌํ•ญ ๋ฌธ์„œํ™”
7. ๋ฆฌ์†Œ์Šค ์ œํ•œ

โ€ข CPU ๋ฐ ๋ฉ”๋ชจ๋ฆฌ ์ œํ•œ ์„ค์ •
โ€ข ๋””์Šคํฌ I/O ์ œํ•œ
โ€ข ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ ๊ด€๋ฆฌ
โ€ข ์ปค๋„ฅ์…˜ ํ’€ ํฌ๊ธฐ ์กฐ์ •
8. ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ ๊ตฌ์ถ•

โ€ข ํ”„๋กœ๋•์…˜๊ณผ ๋™์ผํ•œ ๊ตฌ์„ฑ
โ€ข ์ •๊ธฐ์ ์ธ ์žฌํ•ด ๋ณต๊ตฌ ํ›ˆ๋ จ
โ€ข ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ์‹ค์‹œ
โ€ข ๋ถ€ํ•˜ ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰
9. ๋ฌธ์„œํ™”

โ€ข ์•„ํ‚คํ…์ฒ˜ ๋‹ค์ด์–ด๊ทธ๋žจ
โ€ข ์šด์˜ ๋งค๋‰ด์–ผ ์ž‘์„ฑ
โ€ข ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๊ฐ€์ด๋“œ
โ€ข ๋ณ€๊ฒฝ ์ด๋ ฅ ๊ด€๋ฆฌ
10. ์ž๋™ํ™”

โ€ข CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•
โ€ข ๋ฐฑ์—… ์ž๋™ํ™”
โ€ข ๋ชจ๋‹ˆํ„ฐ๋ง ์ž๋™ํ™”
โ€ข ์•Œ๋ฆผ ์ž๋™ํ™”
โ€ข ์Šค์ผ€์ผ๋ง ์ž๋™ํ™”

๐ŸŽ“ ํ•™์Šต ๋ฆฌ์†Œ์Šค ๋ฐ ์ปค๋ฎค๋‹ˆํ‹ฐ

Docker์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์šด์˜์€ ๊ณ„์† ๋ฐœ์ „ํ•˜๋Š” ๋ถ„์•ผ์•ผ. ์ตœ์‹  ์ •๋ณด๋ฅผ ์–ป๊ณ  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฆฌ์†Œ์Šค๋“ค์„ ์†Œ๊ฐœํ• ๊ฒŒ!
๋ฆฌ์†Œ์Šค ์„ค๋ช… ๋งํฌ
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์˜ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด! ์‹ค์‹œ๊ฐ„ ์ปจ์„คํŒ…๋ถ€ํ„ฐ ์‹œ์Šคํ…œ ๊ตฌ์ถ•๊นŒ์ง€ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ๊ฑฐ๋ž˜ํ•  ์ˆ˜ ์žˆ์ง€. ํŠนํžˆ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ ๊ตฌ์ถ•์ด๋‚˜ ์„ฑ๋Šฅ ํŠœ๋‹ ๊ฐ™์€ ์ „๋ฌธ์ ์ธ ์ž‘์—…์€ ๊ฒฝํ—˜์ž์˜ ๋„์›€์„ ๋ฐ›๋Š” ๊ฒŒ ์‹œ๊ฐ„๊ณผ ๋น„์šฉ์„ ์ ˆ์•ฝํ•˜๋Š” ์ง€๋ฆ„๊ธธ์ด์•ผ! ๐Ÿš€

๐ŸŽฏ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์—ฌ๊ธฐ๊นŒ์ง€ Docker ํ™˜๊ฒฝ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์šด์˜ํ•˜๋Š” ์ „๋žต์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์–ด! ์ฒ˜์Œ์—๋Š” ๋ณต์žกํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ํ•˜๋‚˜์”ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ์ ์šฉํ•˜๋‹ค ๋ณด๋ฉด ์–ด๋А์ƒˆ ํ”„๋กœ๋•์…˜ ๋ ˆ๋ฒจ์˜ ์‹œ์Šคํ…œ์„ ์šด์˜ํ•˜๊ณ  ์žˆ๋Š” ์ž์‹ ์„ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋  ๊ฑฐ์•ผ. ๐Ÿ˜Š

ํ•ต์‹ฌ ํฌ์ธํŠธ ์š”์•ฝ:

๐Ÿณ ๋ฐ์ดํ„ฐ ์˜์†์„ฑ: Named Volume๊ณผ ์ •๊ธฐ ๋ฐฑ์—…์œผ๋กœ ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ
๐Ÿ” ๋ณด์•ˆ: ๋„คํŠธ์›Œํฌ ๊ฒฉ๋ฆฌ์™€ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ๋กœ ์•ˆ์ „์„ฑ ํ™•๋ณด
โšก ์„ฑ๋Šฅ: ๋ฆฌ์†Œ์Šค ์ตœ์ ํ™”์™€ ์บ์‹ฑ์œผ๋กœ ๋น ๋ฅธ ์‘๋‹ต ์†๋„
๐Ÿ”„ ๊ณ ๊ฐ€์šฉ์„ฑ: ๋ ˆํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ์œผ๋กœ ๋ฌด์ค‘๋‹จ ์„œ๋น„์Šค
๐Ÿ“Š ๋ชจ๋‹ˆํ„ฐ๋ง: ์‹ค์‹œ๊ฐ„ ๋ฉ”ํŠธ๋ฆญ ์ˆ˜์ง‘๊ณผ ์•Œ๋ฆผ์œผ๋กœ ์„ ์ œ์  ๋Œ€์‘
๐Ÿ› ๏ธ ์ž๋™ํ™”: CI/CD์™€ ์Šคํฌ๋ฆฝํŠธ๋กœ ์šด์˜ ํšจ์œจ์„ฑ ๊ทน๋Œ€ํ™”

๊ธฐ์–ตํ•ด! ์™„๋ฒฝํ•œ ์‹œ์Šคํ…œ์€ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š์•„. ์ž‘์€ ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ ์ง„์ ์œผ๋กœ ๊ฐœ์„ ํ•ด ๋‚˜๊ฐ€๋Š” ๊ฒŒ ์ค‘์š”ํ•ด. ๊ทธ๋ฆฌ๊ณ  ํ˜ผ์ž ๊ณ ๋ฏผํ•˜์ง€ ๋ง๊ณ , ์ปค๋ฎค๋‹ˆํ‹ฐ๋‚˜ ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์„ ์ ๊ทน ํ™œ์šฉํ•ด์„œ ๊ฒฝํ—˜์„ ๊ณต์œ ํ•˜๊ณ  ๋ฐฐ์›Œ๋‚˜๊ฐ€์ž! ๐Ÿ’ช

Docker์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปจํ…Œ์ด๋„ˆ ์šด์˜, ์ด์ œ ์ž์‹ ๊ฐ ์ƒ๊ฒผ์ง€? ์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์šด์˜๋˜๊ธธ ๋ฐ”๋ž„๊ฒŒ! ํ™”์ดํŒ…! ๐ŸŽ‰

๐ŸŒŸ ๋” ๋งŽ์€ ์ง€์‹์„ ์ฐพ๊ณ  ๊ณ„์‹ ๊ฐ€์š”?

์žฌ๋Šฅ๋„ท์˜ '์ง€์‹์ธ์˜ ์ˆฒ'์—์„œ ๋‹ค์–‘ํ•œ IT ์ „๋ฌธ ์ง€์‹์„ ๋งŒ๋‚˜๋ณด์„ธ์š”!

ํ”„๋กœ๊ทธ๋ž˜๋ฐ, ์„œ๋ฒ„ ๊ด€๋ฆฌ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ตœ์ ํ™” ๋“ฑ
์‹ค๋ฌด์— ๋ฐ”๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒ€์ฆ๋œ ์ง€์‹์ด ๊ฐ€๋“ํ•ฉ๋‹ˆ๋‹ค.

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.