블로그Docker Compose 실전 가이드 — WordPress + MySQL + phpMyAdmin 한 번에 구성하기

Docker Compose 실전 가이드 — WordPress + MySQL + phpMyAdmin 한 번에 구성하기

“로컬에서는 되는데 서버에서는 안 돼요”의 80%는 환경 설정 차이에서 옵니다. PHP 버전이 다르고, MySQL 설정이 다르고, 누군가의 맥에만 깔린 라이브러리가 있어서… Docker Compose는 이런 환경 불일치를 docker-compose.yml 한 파일로 통일해 줍니다.

이 글에서는 Docker Compose의 기본 문법과, 실제 바로 써먹을 수 있는 WordPress + MySQL + phpMyAdmin 구성을 다룹니다. 예제는 그대로 복사해서 로컬 PC나 Ubuntu 서버에서 테스트할 수 있습니다.


Docker Compose란?

여러 개의 Docker 컨테이너를 YAML 파일 하나로 선언적으로 정의하고 한 번에 실행하는 도구입니다. docker run을 5번 치는 대신 docker compose up 하나면 끝나죠.

  • 선언적 구성 — 원하는 상태를 YAML로 정의, 명령형 스크립트 불필요
  • 네트워크 자동 구성 — 같은 compose 안의 서비스끼리는 서비스명으로 통신
  • 볼륨·환경변수 관리.env 파일과 연동해 비밀값 분리
  • 버전 관리 가능 — YAML 자체를 Git에 올려 팀원과 환경 공유

참고: 예전의 docker-compose(하이픈 붙은 별도 바이너리)는 docker compose (띄어쓰기, Docker CLI 플러그인)로 통합되었습니다. Docker Desktop 또는 최신 Docker Engine을 쓰면 별도 설치가 필요 없습니다.


docker-compose.yml 핵심 문법

services:
  웹:
    image: nginx:alpine           # 사용할 이미지
    ports:
      - "8080:80"                 # 호스트포트:컨테이너포트
    volumes:
      - ./html:/usr/share/nginx/html   # 호스트경로:컨테이너경로
    environment:
      - TZ=Asia/Seoul
    depends_on:
      - 디비                       # 디비가 먼저 뜬 뒤에 시작
    restart: unless-stopped

  디비:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_PASS}
    volumes:
      - db-data:/var/lib/mysql    # 명명된 볼륨(영속성)

volumes:
  db-data:
역할
services실행할 컨테이너들을 정의
image사용할 Docker 이미지 이름과 태그
buildDockerfile로 이미지를 직접 빌드
ports포트 매핑 (호스트:컨테이너)
volumes파일 공유/영속화 (바인드 마운트 or 명명된 볼륨)
environment컨테이너에 주입할 환경변수
depends_on시작 순서 제어 (준비 상태까지는 보장하지 않음)
restart재시작 정책 (no / always / unless-stopped / on-failure)

실전 — WordPress + MySQL + phpMyAdmin 한 번에

로컬에서 WordPress 테마·플러그인 개발할 때 가장 많이 쓰는 스택입니다. 아래 2개 파일만 있으면 1분 안에 접속 가능한 환경이 뜹니다.

.env 파일

MYSQL_ROOT_PASSWORD=supersecret
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=wppass

docker-compose.yml

services:
  db:
    image: mysql:8.0
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - db-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  wordpress:
    image: wordpress:latest
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: ${MYSQL_USER}
      WORDPRESS_DB_PASSWORD: ${MYSQL_PASSWORD}
      WORDPRESS_DB_NAME: ${MYSQL_DATABASE}
    volumes:
      - ./wp-content:/var/www/html/wp-content  # 테마/플러그인 개발용

  phpmyadmin:
    image: phpmyadmin:latest
    depends_on:
      - db
    restart: unless-stopped
    ports:
      - "8081:80"
    environment:
      PMA_HOST: db
      PMA_USER: root
      PMA_PASSWORD: ${MYSQL_ROOT_PASSWORD}

volumes:
  db-data:

실행

# 백그라운드로 시작
docker compose up -d

# 로그 실시간 확인
docker compose logs -f wordpress

# 상태 확인
docker compose ps

브라우저에서 http://localhost:8080 (워드프레스) / http://localhost:8081 (phpMyAdmin) 접속하면 바로 씁니다. 중지할 땐 docker compose down. 데이터까지 완전 초기화는 docker compose down -v.


자주 쓰는 명령어 정리

# 전체 서비스 시작 / 중지
docker compose up -d
docker compose down
docker compose restart

# 특정 서비스만 재시작
docker compose restart wordpress

# 실행 중 컨테이너에 접속 (bash 진입)
docker compose exec wordpress bash
docker compose exec db mysql -u root -p

# 로그 확인 (최근 100줄)
docker compose logs --tail=100 wordpress

# 이미지 강제 재빌드 (로컬 Dockerfile 수정 후)
docker compose up -d --build

# 이미지 업데이트 (최신 pull + 재생성)
docker compose pull
docker compose up -d

# 특정 서비스만 한시적으로 실행
docker compose run --rm wordpress wp core version

# 볼륨·네트워크까지 완전 초기화
docker compose down -v --remove-orphans

운영에서 자주 놓치는 포인트 5가지

1. depends_on은 “시작 순서”만 보장

DB 컨테이너가 뜨는 것과, DB가 쿼리를 받을 준비가 된 것은 다릅니다. 초기 부팅이 10~30초 걸리기 때문에 앱이 먼저 연결을 시도하면 실패합니다. healthcheck + depends_on.condition: service_healthy 조합이 정석입니다.

2. 바인드 마운트 vs 명명된 볼륨

종류예시언제 쓸까
바인드 마운트./app:/var/www/html개발 중 코드 실시간 반영
명명된 볼륨db-data:/var/lib/mysqlDB처럼 Docker가 관리할 영속 데이터

DB를 바인드 마운트로 잡으면 권한 문제로 시작이 안 되는 경우가 많습니다. DB 데이터는 명명된 볼륨을 권장합니다.

3. .env 파일은 반드시 .gitignore

DB 비밀번호·API 키가 들어 있습니다. 실수로 공개 저장소에 올라가면 몇 분 안에 bot이 탈취합니다. .env.example만 커밋하고 실제 값은 로컬에만 두세요.

4. 포트 충돌

로컬에서 다른 서비스가 이미 8080을 쓰고 있으면 Error: port is already allocated가 뜹니다. lsof -i :8080로 누가 잡고 있는지 확인하거나, compose에서 8090:80처럼 다른 호스트 포트로 매핑하세요.

5. 이미지 태그를 latest로 두지 마세요

운영에서는 wordpress:latest가 어느 날 메이저 업데이트되면서 DB 마이그레이션이 깨질 수 있습니다. 반드시 wordpress:6.7처럼 버전 고정 후 의도적으로 업그레이드 테스트하는 습관이 필요합니다.


FAQ

Q. 운영 서버에서도 docker compose로 띄워도 되나요?

단일 서버 + 중소 트래픽이라면 전혀 문제없습니다. 이 사이트(nalkkul.com)도 바로 이런 방식으로 여러 내부 도구를 운영합니다. 여러 서버로 확장되는 시점에서는 Swarm / Kubernetes 같은 오케스트레이션 도구를 검토하세요.

Q. docker compose에서 백업은 어떻게 하나요?

DB는 mysqldump를 cron으로 돌리고, 볼륨은 docker run --rm -v db-data:/data -v $(pwd):/backup alpine tar czf /backup/db-$(date +%F).tar.gz /data 같은 식으로 아카이빙합니다. cron 설정법은 cron 작업 스케줄링 가이드 참고.

Q. 컨테이너 하나가 메모리를 너무 많이 써요.

deploy.resources.limits로 제한할 수 있습니다. 다만 이 키는 Swarm 모드에서만 강제되므로, 단일 노드에서는 mem_limit을 쓰세요.

services:
  wordpress:
    image: wordpress:6.7
    mem_limit: 512m
    cpus: 1.0

마무리

Docker Compose는 “환경을 코드로 관리한다”는 개념을 가장 낮은 학습 비용으로 체험할 수 있는 도구입니다. 이 글의 WordPress + MySQL + phpMyAdmin 예제만 돌려 봐도, 로컬에 직접 PHP·MySQL을 설치하던 시절로 돌아가기 어려워집니다. 처음에는 공식 이미지 조합부터 시작해 보고, 익숙해지면 Dockerfile로 커스텀 이미지를 빌드하는 단계로 확장하세요.

이 글이 도움이 되셨다면 공유해주세요!

댓글 남기기

날꿀닷컴 도구 안내