把WordPress打包进Docker镜像并不难,真正需要花心思的是数据放在哪里。容器设计上更偏向无状态,容器一旦删除,可写层里的所有改动都会跟着消失。WordPress网站的数据恰好分散在MySQL数据库、上传目录、主题插件和配置文件几个地方,如果这些内容没有映射到容器外部,一次误删容器或者镜像升级就可能造成整站无法恢复。

一、为什么容器化WordPress需要单独处理持久化
Docker镜像由多个只读层叠加而成,运行容器时会在最上面加一个可写层。应用运行过程中产生的文件修改、上传、日志默认都写在这个可写层里。如果使用docker rm删除容器,可写层会被直接清理,不会留下任何痕迹。WordPress的媒体库文件、新安装的主题和插件、用户上传的附件都属于这类动态数据。
对于数据库来说,官方MySQL镜像虽然把数据目录放在了容器内部,但同样受可写层影响。容器重建后,数据表、文章、评论都会丢失。因此,容器化WordPress的持久化至少要覆盖两部分:MySQL的数据目录和WordPress的wp-content目录。前者保存结构化数据,后者保存文件型资源,两者缺一不可。
除了这两个目录,站点配置文件wp-config.php如果包含自定义数据库前缀、密钥盐值、调试开关等,也建议一并持久化。否则每次重新部署都要手动修改配置,统一性会变差。理解了这些数据边界,后面的卷映射方案就不会遗漏关键路径。
二、使用Docker Compose完成单机持久化部署
单机环境推荐用Docker Compose编排WordPress和MySQL两个服务,同时声明命名数据卷。命名卷由Docker统一管理,在Linux下通常位于/var/lib/docker/volumes,即使容器被删除,卷里的数据也会保留。下面是一个可以直接落地的docker-compose.yml配置。
version: "3.8"
services:
db:
image: mysql:8.0
container_name: wp_db
restart: unless-stopped
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wp_user
MYSQL_PASSWORD: wp_pass_123
MYSQL_ROOT_PASSWORD: root_pass_123
volumes:
- db_data:/var/lib/mysql
networks:
- wp_net
wordpress:
image: wordpress:6.4
container_name: wp_app
restart: unless-stopped
depends_on:
- db
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wp_user
WORDPRESS_DB_PASSWORD: wp_pass_123
volumes:
- wp_content:/var/www/html/wp-content
networks:
- wp_net
volumes:
db_data:
wp_content:
networks:
wp_net:
driver: bridge
上面的配置中,db_data卷挂载到MySQL数据目录,wp_content卷挂载到WordPress的内容目录。容器重建后,只要这两个卷还在,数据就不会丢失。访问http://127.0.0.1:8080即可完成WordPress安装。
如果希望把数据放在更明确的位置,也可以改用bind mount。比如把宿主机上的/srv/wordpress/wp-content目录挂载到容器内。bind mount的优势是路径直观、方便用宿主机工具直接查看,但在Linux下如果宿主机目录权限配置不当,容易出现容器无法写入的情况。命名卷则避免了这类权限问题,是入门和小规模部署的首选。
需要说明的是,MySQL官方镜像的数据目录在8.0版本存在初始化权限细节,如果直接沿用旧版本挂载方式,可能遇到Access denied for user 'root'@'localhost'。但只要按照上面的环境变量声明并坚持使用命名卷,通常不会出现这类兼容问题。生产环境还建议给数据库密码使用Docker secrets或者环境变量文件,避免明文写入Compose文件。
三、备份与恢复:防止卷本身成为单点故障
持久化卷解决了容器删除丢数据的问题,但卷本身也可能被误删,或者宿主机磁盘损坏导致数据丢失。因此,必须为数据库和wp-content目录制定备份策略。数据库备份使用mysqldump导出SQL文件,文件备份直接用tar打包命名卷内容。
下面命令演示如何备份MySQL数据库和WordPress文件。数据库通过docker exec执行mysqldump,文件卷则用一个临时Alpine容器把卷内容打包到宿主机当前目录。
# 备份MySQL数据库 docker exec wp_db mysqldump -u root -proot_pass_123 wordpress > wp_backup_$(date +%F).sql # 备份wp-content卷 docker run --rm -v wp_content:/data -v $(pwd):/backup alpine tar czf /backup/wp_content_$(date +%F).tar.gz -C /data .
恢复时先启动MySQL容器,把SQL文件导入数据库,再恢复文件卷。注意SQL备份最好在数据库低峰期进行,否则可能出现事务不一致。对于较大站点,可以使用mysqldump --single-transaction参数保证InnoDB表的一致性。恢复指令如下。
# 恢复数据库 docker exec -i wp_db mysql -u root -proot_pass_123 wordpress < wp_backup.sql # 恢复wp-content卷 docker run --rm -v wp_content:/data -v $(pwd):/backup alpine sh -c "rm -rf /data/* && tar xzf /backup/wp_content.tar.gz -C /data"
除了手工备份,生产环境应当把备份脚本加入cron定时任务。例如每天凌晨执行一次,并保留最近7天的备份文件。同时建议把备份文件同步到对象存储或异地服务器,避免同一块物理磁盘出问题时连备份一起丢掉。
四、多节点部署和高可用场景的持久化选择
当WordPress需要运行在多台Docker主机上时,本地命名卷无法被其他节点访问,此时需要引入共享存储。NFS是最常见的方案之一。把NFS共享目录挂载到所有宿主机,再通过bind mount或Docker NFS卷驱动映射到容器内即可。
Docker支持使用local卷驱动配合NFS选项创建共享卷。示例命令如下,其中192.168.1.100是NFS服务器地址,/srv/wordpress是共享目录。
# 创建NFS类型的Docker卷 docker volume create --driver local \ --opt type=nfs \ --opt o=addr=192.168.1.100,rw,nfsvers=4 \ --opt device=:/srv/wordpress \ wp_shared
如果运行在云平台上,更推荐使用云厂商提供的块存储或文件存储。块存储可以挂载到单台虚拟机作为数据库目录,性能较好;文件存储则适合多节点共享wp-content。在Kubernetes集群中,这些能力被抽象成PersistentVolume和PersistentVolumeClaim,配合StorageClass可以实现动态分配。
Kubernetes部署WordPress时,数据库通常使用StatefulSet管理,文件资源使用ReadWriteMany类型的PVC。下面是一个简化的PVC定义,它通过StorageClassnfs-client申请共享存储。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-content-pvc
spec:
accessModes:
- ReadWriteMany
storageClassName: nfs-client
resources:
requests:
storage: 20Gi
选择持久化方案时,要考虑访问模式和性能。ReadWriteOnce只允许一个节点读写,适合数据库;ReadWriteMany允许多个节点同时访问,适合wp-content。NFS虽然接入简单,但高并发下性能一般;Ceph、GlusterFS等分布式存储则更适合大规模集群,不过维护复杂度也明显上升。
五、部署中的常见问题与排查方法
第一次访问WordPress时如果出现数据库连接错误,先确认depends_on已经配置,且MySQL容器正在运行。由于MySQL初始化需要时间,即使使用了depends_on,也可能在数据库就绪前WordPress就尝试连接。可以在WordPress服务中添加healthcheck并等待数据库健康状态,或者使用depends_on的condition: service_healthy来确保顺序。
另一个高频问题是文件目录权限。如果使用bind mount挂载宿主机目录,宿主机上的目录属主和容器内运行用户不一致,会导致无法写入。解决办法是先创建目录并设置合适的属主,例如chown -R 1000:1000 /srv/wordpress/wp-content,或者直接使用命名卷避开该问题。
容器被删除后如果发现数据卷也跟着消失,通常是手动执行了docker rm -v命令。-v参数会同时删除匿名卷和未使用的命名卷。日常操作中删除容器时保留默认行为即可,不要在不确定的情况下加-v。定期检查docker volume ls和docker system df也能帮助及时发现异常。

