Docker Compose入门实战:在Ubuntu 24.04上部署Nginx + MySQL + WordPress(2026)

📝 651 字 · ☕ 2 分钟阅读

前言:为什么你需要 Docker Compose?

如果你曾经在 VPS 上手动装过 LNMP/LEMP 环境,你一定记得那种痛——Nginx、MySQL、PHP 一个一个装,配置文件改来改去,apt 源版本不对、依赖冲突、端口被占、跑起来了又不知道哪里挂掉。换个服务器?重来一遍。升级系统?心惊胆战。

Docker Compose 就是解决这个问题的。 它让你把一整个应用栈(Nginx + MySQL + PHP/WordPress)写进一个 YAML 文件,一条命令全部启动,删掉也是一条命令全部清理干净。环境隔离、版本锁定、迁移丝滑——这就是容器编排的力量。

这篇文章我在 Ubuntu 24.04 上实操了一遍,从零开始用 Docker Compose 部署一个完整的 Nginx + MySQL + WordPress 栈。我会把每一步的命令和输出都贴出来,让你可以跟着敲。

前置条件

  • 一台 Ubuntu 24.04 服务器(我用的是阿里云 2C4G,系统刚装好的)
  • 有 sudo 权限的用户
  • 域名指向服务器 IP(我用 demo.devlearn.club 做演示)
  • SSH 能连上就行,不需要装任何东西

第一步:安装 Docker 和 Docker Compose

Ubuntu 24.04 的官方源里 Docker 版本偏旧,我习惯用 Docker 官方源安装。一条龙脚本拿走:

$ sudo apt update && sudo apt install -y ca-certificates curl
$ sudo install -m 0755 -d /etc/apt/keyrings
$ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
$ sudo chmod a+r /etc/apt/keyrings/docker.asc

$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

$ sudo apt update
$ sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

验证安装:

$ docker --version
Docker version 28.0.4, build b8034c0

$ docker compose version
Docker Compose version v2.36.0

注意:Ubuntu 24.04 用的是新版的 docker compose(中间空格,不是 docker-compose 带连字符)。新版是 Docker CLI 的插件,不用单独装 docker-compose 了。如果你在网上看到旧教程用连字符版本,直接替换成空格版本就行。

把当前用户加到 docker 组,免得每次都要 sudo:

$ sudo usermod -aG docker $USER
$ newgrp docker

第二步:创建项目目录结构

$ mkdir -p ~/wordpress-docker/{nginx,wordpress,mysql-data}
$ cd ~/wordpress-docker

目录说明:

  • nginx/ — 放 Nginx 配置文件
  • wordpress/ — WordPress 文件挂载目录
  • mysql-data/ — MySQL 数据持久化目录

第三步:编写 docker-compose.yml

这是整个项目的核心。三个服务:Nginx 做反向代理、MySQL 做数据库、WordPress 跑 PHP。用自定义网络把它们连在一起:

$ cat > docker-compose.yml << 'EOF'
services:
  db:
    image: mysql:8.0
    container_name: wp_mysql
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: MyStr0ng!Pass2024
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wpuser
      MYSQL_PASSWORD: WpP@ssw0rd2024
    volumes:
      - ./mysql-data:/var/lib/mysql
    networks:
      - wp_net

  wordpress:
    image: wordpress:php8.3-fpm-alpine
    container_name: wp_app
    restart: unless-stopped
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wpuser
      WORDPRESS_DB_PASSWORD: WpP@ssw0rd2024
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - ./wordpress:/var/www/html
    depends_on:
      - db
    networks:
      - wp_net

  nginx:
    image: nginx:alpine
    container_name: wp_nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx:/etc/nginx/conf.d
      - ./wordpress:/var/www/html
    depends_on:
      - wordpress
    networks:
      - wp_net

networks:
  wp_net:
    driver: bridge
EOF

几个关键点:

  • WordPress 镜像选了 php8.3-fpm-alpine,因为 Nginx 需要 PHP-FPM 来处理 PHP 请求,Apache 版 WordPress 自带 web server 反而用不上。alpine 镜像体积小(不到 100MB),启动快。
  • depends_on 不等于等待就绪。 它只保证启动顺序,不保证 MySQL 已经准备好接受连接。实际生产环境建议加 healthcheck,我后面会提。
  • 数据持久化靠 volumes。 不用 volumes 的话,容器删了数据就没了。mysql-data 和 wordpress 目录都映射到宿主机,随便折腾容器,数据不丢。
  • 密码别用我这份, 改一个强密码。

第四步:配置 Nginx

创建 Nginx 的 server block 配置,让它把请求转发给 PHP-FPM(WordPress 容器):

$ cat > nginx/wordpress.conf << 'EOF'
server {
    listen 80;
    server_name demo.devlearn.club;

    root /var/www/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ /\.ht {
        deny all;
    }
}
EOF

fastcgi_pass wordpress:9000; 这行的 wordpress 是 docker-compose.yml 里定义的服务名,Docker 的内部 DNS 会自动解析成容器 IP。这就是 Compose 网络的好处——不用记 IP,直接用服务名互相访问。

第五步:启动整个栈

$ docker compose up -d

[+] Running 4/4
 ✔ Network wordpress-docker_wp_net    Created     0.0s
 ✔ Container wp_mysql                 Started     0.5s
 ✔ Container wp_app                   Started     1.2s
 ✔ Container wp_nginx                 Started     1.8s

检查容器状态:

$ docker compose ps

NAME        IMAGE                              STATUS          PORTS
wp_mysql    mysql:8.0                          Up 2 minutes    3306/tcp
wp_app      wordpress:php8.3-fpm-alpine        Up 2 minutes    9000/tcp
wp_nginx    nginx:alpine                       Up 2 minutes    0.0.0.0:80->80/tcp

三个容器全部 Up,端口映射正常。这时候打开浏览器访问你的域名,应该看到 WordPress 安装界面了。

第六步:完成 WordPress 安装

浏览器打开 http://你的域名,看到 WordPress 经典的五分钟安装界面。选语言、填站点信息、设置管理员账户,一路下一步就完了。

安装完成后你应该能在后台愉快地写文章了。等等——你现在还没有 HTTPS?没错,目前只配了 80 端口。生产环境必须上 SSL,但 HTTPS 配置我专门写过一篇 Let’s Encrypt SSL 证书自动续期教程,配合 Certbot + Nginx 一条龙搞定,这里不展开了。

日常管理命令速查

# 查看日志
$ docker compose logs -f nginx

# 重启某个服务
$ docker compose restart wordpress

# 停止所有服务
$ docker compose down

# 停止并删除数据卷(危险!)
$ docker compose down -v

# 重新构建并启动
$ docker compose up -d --build

# 进入容器内 shell
$ docker compose exec wordpress sh

踩坑记录

我在实际部署时踩了几个坑,记录一下省得你重复掉:

坑一:MySQL 8.0 认证方式变更。 WordPress 默认用 mysql_native_password 连接,但 MySQL 8.0 默认用 caching_sha2_password。不过官方的 WordPress 镜像已经处理好了这个问题,如果用第三方的 WordPress 镜像或者自己 build,可能需要手动加 --default-authentication-plugin=mysql_native_password 参数。

坑二:Nginx 502 Bad Gateway。 访问站点看到 502,99% 是 fastcgi_pass 写错了。检查 nginx 配置里 fastcgi_pass 后面的服务名是不是 docker-compose.yml 里的 service name,端口是不是 9000。

坑三:文件权限。 WordPress 容器内部以 www-data 用户运行,如果你在宿主机上编辑 wordpress/ 目录下的文件,可能会遇到权限问题。解决方法:

$ sudo chown -R 33:33 wordpress/   # 33 是容器内 www-data 的 UID

进阶:加健康检查

前面提过 depends_on 不保证服务就绪。如果你遇到 WordPress 容器启动时 MySQL 还没准备好(概率不高但确实存在),可以在 docker-compose.yml 里给 db 服务加 healthcheck:

db:
  image: mysql:8.0
  healthcheck:
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
    interval: 10s
    timeout: 5s
    retries: 5

然后在 wordpress 服务里把 depends_on 改成条件依赖:

depends_on:
  db:
    condition: service_healthy

常见问题 FAQ

Q: 我在国内 VPS 上拉 Docker 镜像特别慢怎么办?

配置国内镜像加速器。编辑 /etc/docker/daemon.json
{"registry-mirrors": ["https://mirror.ccs.tencentyun.com"]}
然后 sudo systemctl restart docker。截至 2026 年,国内可用的加速器推荐 腾讯云、阿里云(需注册获取专属地址)和 DaoCloud。Docker Hub 官方镜像在中国大陆已不可用。

Q: 我要给 WordPress 升级或装插件,容器重启后会丢失吗?

不会。因为 ./wordpress:/var/www/html 这个 volume 映射把 WordPress 文件持久化到了宿主机。你通过后台安装的插件、主题、上传的媒体文件都存在宿主机磁盘上,容器删了重建也不丢。同理,MySQL 数据在 ./mysql-data 目录下。

Q: 怎么给这个 WordPress 加上 HTTPS / SSL 证书?

推荐用 Certbot + Let’s Encrypt 免费证书。具体步骤我在另一篇文章里写过——Let’s Encrypt SSL 证书自动续期配置教程。简单说就是在宿主机上安装 Certbot,生成证书后挂在 Nginx 容器里,然后修改 nginx/wordpress.conf 加上 443 端口的 server block。配合 cron 定时任务自动续期,一劳永逸。

总结

用 Docker Compose 部署 WordPress 比传统方式有几个明显优势:

  • 一条命令起docker compose up -d,不用一个一个装软件
  • 环境隔离 — PHP、MySQL、Nginx 各跑各的容器,版本互不干扰
  • 迁移简单 — 把整个项目目录打包拷到另一台服务器,docker compose up -d 就能跑
  • 清理干净docker compose down -v 删得干干净净,不留垃圾

如果你之前一直用宝塔面板或者手动编译安装,强烈建议试试 Docker Compose。搭过一次你就回不去了。这玩意儿和 VPS 安全加固 配合起来,一台干净的 Ubuntu VPS 配上 Docker Compose,就是你的最佳生产环境底座。

下一篇我计划写 systemctl 服务管理完全指南,把 systemd 的原理、常用命令、定时器替代 crontab 都讲透。感兴趣的话留意更新。
🔗 相关阅读:MySQL慢查询优化实战:一条SQL从8秒干到0.03秒的全过程复盘 — 搭好MySQL只是第一步,性能优化才是重头戏。

📤 分享这篇文章