news 2026/10/14 4:36:01

CentOS Stream上Docker Compose部署WordPress

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CentOS Stream上Docker Compose部署WordPress

忙完一个容器化改造项目,我顺手把阿里云 CentOS Stream 上构建 WordPress 的完整过程整理出来。这次不是演示环境的玩具方案,是实际部署并运行了一段日子的组合:Docker Compose 管理四个容器,Nginx 做 Web 入口,PHP 单独走 FPM 进程,数据库用 MariaDB,站点数据全部落在卷上。整套东西跑得很安静,升级和回滚动动手就能完成。文章会把配置细节、踩坑记录和排查思路一次说清楚,适合准备上容器、又不想被服务器重装反复折腾的个人站长和中小团队参考。

1. 容器化前的架构思考:为什么这套组合值得抄作业

1.1 选 CentOS Stream 而不是其他发行版的关键原因

先说系统。阿里云公共镜像列表里现在还能选到老牌的 CentOS 7,但那个版本官方早已停止维护,继续用等于把一个没人补丁的系统暴露在公网,出了问题连安全更新都等不到。CentOS Stream 是滚动发布的发行版,位置介于 Fedora 和 RHEL 之间,本质上就是 RHEL 下一个小版本的预演,阿里云镜像里直接有 CentOS Stream 9,装出来就是干净的 dnf 体系,用惯 yum 的人也能无缝过渡。

对容器化部署来说,Stream 的内核和新工具链与 Docker 配合得更好,cgroup v2、overlay2 这些现代存储驱动在上面开箱即用。用老版本 CentOS 7 跑 Docker,默认存储驱动还能跑,但内核特性落后,遇到新版本镜像有时会出现兼容性问题,排查起来相当被动。另外 Stream 的软件包更新节奏比固定版本快,PHP、Nginx、MariaDB 这些基础组件通过官方源就能拿到比较新的版本,不用为安全修复等太久。

必须提醒一点:Stream 的滚动特性意味着它不是那种"装完三年不动"的固定发行版。生产环境里我会在镜像层面把版本锁死,比如固定 WordPress 镜像的 tag,同时给系统定期做 dnf update 并保留 yum history 记录,这样即使滚动更新带来问题,也能快速回滚。个人站点或者中小项目这么用很省心,只要别把"滚动"理解为"随手升到最新"就行。

1.2 PHP-FPM 与 Nginx 拆分开的运行逻辑

WordPress 官方提供了两种常见的镜像思路:一种是 wordpress:apache 全家桶,Apache 和 PHP 都在一个容器里,docker run 就能起,适合快速验证;另一种是 wordpress:phpX.Y-fpm 加独立 Nginx 容器,这也是我推荐的生产组合。拆开之后,Web 服务器和 PHP 解释器是独立进程,Nginx 负责静态文件、Gzip 压缩、SSL 终止和 HTTP/2,PHP-FPM 专职跑 PHP 脚本,各自资源占用清晰可见。

很多新手问为什么不直接用现成的 wordpress:apache,我的回答是:能用,但它把 Web 服务和 PHP 绑死在一个进程生命周期里,改 Nginx 配置要进容器操作,高并发时 PHP 和静态文件的压力互相影响,排障时日志也混在一起。容器化的价值本身就是隔离和单一职责,把每一层拆干净,后面升级 PHP 版本、更换 Web 服务器、接入 CDN 都会轻松很多。这也是整套架构最大的设计取舍。

我做个直观对比,方便你判断自己适合哪种方案:

对比项wordpress:apache 全家桶php-fpm + nginx 拆分
启动复杂度低,一个容器中,三个容器
静态资源处理Apache 负责Nginx 更擅长并发静态文件
配置灵活度改 Apache 配置可独立调 Nginx 和 PHP
高并发扩展整体扩容可单独扩展 Web 或 PHP
排障清晰度日志混在一起各容器日志独立

1.3 弄懂镜像、数据卷、容器三层关系

这里值得多花点篇幅讲清楚一个心智模型,因为大部分容器化事故都源于对这三层的误解。镜像层是只读的,里面装着 PHP 源码、扩展和默认配置;容器启动后在镜像之上加一个可写层,运行时的改动都存在这里;数据卷独立于容器生命周期,挂在容器的某个目录上,比如数据库的 /var/lib/mysql 或者 WordPress 的 /var/www/html。容器删除重建,只有数据卷还保留着,这也是容器化之后"迁移等于换台机器拉镜像起容器再挂上同一个卷"的原因。

实际操作中,我把 wp-content 和数据库目录都用独立卷管理,配置文件用 compose 里的 environment 变量注入,不把敏感信息写进镜像。这样做的直接好处是:镜像可以随意删除、升级、回滚,站点数据却始终在卷上,不会因为一次 docker compose down 就全部丢失。刚开始用容器的人最怕"数据没了",只要守住"容器可以随便删,数据必须在卷上"这条底线,整个容器化的安全感就建立起来了。

2. 阿里云 ECS 初始化:能从零复制的完整步骤

2.1 实例规格与安全组怎么配

ECS 实例规格方面,我对大多数 WordPress 项目的建议是 2 核 4 GB 起步,系统盘 40 GB 以上,数据盘单独挂一块。1 核 1 GB 的机器跑最小化 WordPress 也不是完全不行,一个低流量个人博客加上对象缓存可能扛得住,但容器化之后 MySQL 和 PHP-FPM 都有常驻内存开销,内存一紧张就容易触发 OOM,体验会很差。2 核 4 GB 对应的成本可控,对个人站也友好,是性价比比较平衡的档位。

买机器的时候有两处最容易出错。第一,公网 IP 和带宽一定别漏选,很多新手买了台没有公网 IP 的实例,登录后发现连不上,只能在控制台里再折腾绑定。第二,安全组默认只放行 22 端口,容器起来后你会发现 80 访问不通,一定要提前在安全组规则里加一条:协议 TCP,端口 80 和 443,源地址 0.0.0.0/0。数据库的 3306 端口永远不要对外开放,容器内部走的是 Docker 网络,宿主机根本不需要暴露这个端口。

端口用途安全组建议
22SSH 管理限定来源 IP,不要全开放
80HTTP 访问0.0.0.0/0
443HTTPS 访问0.0.0.0/0
3306MySQL不开放

2.2 系统优化:时区、Swap 和内核对齐

第一次以 root 登录后,我习惯先把三件事做完。第一件是更新系统并安装基础工具,dnf update 之后装 vim、git、wget、unzip,这一步能避免后续安装过程中因为缺工具卡壳。第二件是设置时区和主机名,timedatectl set-timezone Asia/Shanghai 会让 PHP 的 date 函数、MySQL 的时间记录都统一在本地时区,不用在业务层再换算。第三件是检查内核的文件系统支持,容器存储驱动要跑 overlay2,需要内核具备 overlay 模块。

sudo dnf update -y sudo dnf install -y vim git wget unzip sudo hostnamectl set-hostname wp-prod sudo timedatectl set-timezone Asia/Shanghai cat /proc/filesystems | grep overlay free -h

内存只有 2 GB 的话,建议再加一个 2 GB 的 swap 文件。容器里的 MySQL 对内存比较敏感,有时候瞬时内存峰值一来,没有 swap 兜底就会直接 OOM 全站 502,有 swap 至少能给进程一个缓冲。做法是先用 fallocate 生成文件,mkswap 格式化,swapon 启用,再写进 /etc/fstab 保证重启后依然生效。我见过太多人忽略 swap,等站点流量一大就翻车,这个环节别跳过。

sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab swapon --show

2.3 装好 Docker 和 Compose 插件

CentOS Stream 9 上装 Docker 首选官方仓库,不要用系统自带的 podman 或老版本 docker 包,很多发行版里 docker 命令装出来可能是别的工具,特别容易让人困惑。官方源的安装流程比较固定,先加 repo,再装 docker-ce、docker-ce-cli、containerd.io 和 docker-compose-plugin 四个包,最后启动并设置开机自启。

sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin sudo systemctl enable --now docker docker version docker compose version

如果从阿里云内网拉 Docker Hub 镜像较慢,可以在 /etc/docker/daemon.json 里配置 registry-mirrors,填入阿里云容器镜像服务的加速地址。这一步不是必须的,但实测对镜像拉取速度的提升非常明显,值得加上。配置完记得 systemctl restart docker,再用 docker info 确认 Registry Mirrors 字段已经生效。

安装完成后顺手执行 docker info,看看 Storage Driver 是不是 overlay2,Cgroup 版本是 v2 还是 v1。CentOS Stream 9 默认 cgroup v2,新版 Docker 支持得很好,即使有提示也不影响本教程的示例运行。

3. 编排文件与核心参数逐个解读

3.1 docker-compose.yml 完整示例

我把生产环境用的 compose 文件精简成了一个可直接参考的版本,四个服务:db(MariaDB)、php(WordPress PHP-FPM)、web(Nginx)、certbot(签发证书用,后面章节会用到)。文件放项目目录 /opt/wp 下,下文所有相对路径都基于这个目录。

version: "3.8" networks: wp_net: driver: bridge volumes: wp_data: db_data: services: db: image: mariadb:10.11 container_name: wp-db restart: unless-stopped environment: MARIADB_DATABASE: wordpress MARIADB_USER: wpuser MARIADB_PASSWORD: "REPLACE_WITH_STRONG_PASSWORD" MARIADB_ROOT_PASSWORD: "REPLACE_WITH_ROOT_PASSWORD" volumes: - db_data:/var/lib/mysql networks: - wp_net command: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 10s timeout: 5s retries: 5 start_period: 30s php: image: wordpress:php8.2-fpm-alpine container_name: wp-php restart: unless-stopped depends_on: db: condition: service_healthy environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_USER: wpuser WORDPRESS_DB_PASSWORD: "REPLACE_WITH_STRONG_PASSWORD" WORDPRESS_DB_NAME: wordpress WORDPRESS_CONFIG_EXTRA: | define('WP_MEMORY_LIMIT', '256M'); define('WP_DEBUG', false); volumes: - wp_data:/var/www/html - ./php/conf.d:/usr/local/etc/php/conf.d:ro networks: - wp_net web: image: nginx:1.27-alpine container_name: wp-web restart: unless-stopped depends_on: - php ports: - "80:80" - "443:443" volumes: - wp_data:/var/www/html - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/ssl:/etc/nginx/ssl:ro - ./logs/nginx:/var/log/nginx networks: - wp_net certbot: image: certbot/certbot container_name: wp-certbot restart: "no" volumes: - ./nginx/ssl:/etc/letsencrypt:rw - wp_data:/var/www/html:rw networks: - wp_net

版本号字段 3.8 是对老版本 Compose 的兼容写法,新的 docker compose 插件已经不太在意这个字段,保留只是多一行提示,没有实际影响。环境变量里把数据库名、用户、密码和 WordPress 的对应配置写在同一处,PHP 容器会自动根据这些变量生成 wp-config.php,不需要手工改文件。这是我强烈推荐的配置方式,配置和代码分离,迁移时只需改环境变量就能换一套参数。

3.2 php-fpm 容器的上传大小与内存限制

WordPress 官方镜像自带常用的 PHP 扩展,比如 mysqli、gd、zip、imagick 等,默认配置对一般博客足够。但有两个参数是很多人都会踩坑的:上传大小和内存限制。默认的上传限制只有 40M 左右,如果要传大主题包、大附件图或者备份文件,会被 PHP 直接拒绝。解决办法是在宿主机的 ./php/conf.d 目录下放一个自定义 ini 文件,比如 zz-uploads.ini,挂载进容器的 conf.d 目录。

[PHP] upload_max_filesize = 64M post_max_size = 64M max_execution_time = 120 memory_limit = 256M [opcache] opcache.enable = 1 opcache.memory_consumption = 128 opcache.max_accelerated_files = 10000 opcache.validate_timestamps = 0

这里有个容易忽略的点:opcache.validate_timestamps 设为 0 后,PHP 不会频繁检查文件时间戳,性能会好一些,但代价是修改 PHP 文件后需要 reload PHP 进程才能生效,配合容器化的"改代码重新构建镜像"心智模型其实很合适。如果你还在调试主题函数或者写自定义插件,建议先保持 validate_timestamps = 1,避免改完代码半天不生效的困惑。另外 memory_limit 256M 是 WordPress 后台跑大量插件扫描时的安全值,前台一般几十 M 就够。

3.3 Nginx 站点配置:fastcgi 转发才是关键

Nginx 容器里默认没有站点配置,需要在 ./nginx/conf.d 下新建一个默认站点文件,比如 default.conf。我的做法是先把 HTTP 的 server 块写好,让站点先跑起来,等证书签发成功后再补齐 HTTPS。下面的配置是这个文件的核心部分:root 指向共享卷里的 /var/www/html,try_files 把请求交给 index.php 处理,PHP 请求通过 fastcgi_pass 转发到 php:9000 这个地址。

server { listen 80; server_name example.com www.example.com; root /var/www/html; index index.php index.html; client_max_body_size 64m; location / { try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } location ~ /\. { deny all; } location = /favicon.ico { log_not_found off; access_log off; } }

新手最容易在这段配置上翻车的是 fastcgi_pass。容器网络里 Nginx 访问 PHP 不是 localhost:9000,而是 compose 服务名加端口组成的 php:9000,这是 Docker 内置 DNS 自动解析的结果。如果你写成 localhost:9000,请求会直接 502。另一个常见问题是 SCRIPT_FILENAME 参数,如果漏掉或者写错,PHP-FPM 找不到脚本文件,同样会返回 502。这个参数一定要基于 $document_root 拼接,确保最终指向 /var/www/html/index.php 这样的真实路径。

3.4 数据卷共享与权限踩坑说明

php 和 web 两个容器都挂载了同一个 wp_data 卷,这个卷承载了 WordPress 的全部站点文件,PHP-FPM 要往里写上传内容,Nginx 要从中读静态资源。这里隐藏着一个非常典型的权限问题:官方 wordpress:php8.2-fpm-alpine 镜像里 PHP 进程以 www-data 用户运行,uid 是 82;而 nginx:alpine 镜像里 Nginx worker 进程以 nginx 用户运行,uid 是 101。

用命名卷管理时,卷目录的属主会被第一个初始化它的容器设置好,通常是 www-data,目录权限 755、文件权限 644 对 Nginx 来说可读,所以大多数情况下没问题。但如果你把 wp_data 改成宿主机 bind mount,比如挂载 /data/wp/html,宿主机目录属主是 root,Nginx worker 写 wp-content/cache 这类目录时就会因为没有写权限而报错。解法有两个:一是继续用命名卷,省心;二是手动执行 chown -R 82:82 /data/wp/html,把宿主目录属主改成和 PHP 容器一致,同时注意别让 Nginx 容器去覆盖这个属主设置。这个坑很多教程不提,实际部署中遇到的人却不少。

4. 部署验证和故障排查

4.1 首次启动流程与健康检查

compose 文件里我特意在 db 服务上加了健康检查,php 的 depends_on 用 condition: service_healthy 等数据库真正就绪。直接 depends_on 只保证容器启动顺序,数据库容器启动成功不代表 MySQL 已经能接收连接,MySQL 初始化需要十几秒,如果 PHP 在这期间急着连数据库,就会报连接失败。healthcheck 是这段逻辑的关键:

healthcheck: test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] interval: 10s timeout: 5s retries: 5 start_period: 30s

首次启动时,在 /opt/wp 目录下执行 docker compose up -d,然后 docker compose ps 观察每个服务的状态。db 的 STATUS 会从 starting 变成 healthy,php 和 web 也会在依赖满足后陆续起来。看到三个容器都处于 Up 状态后,访问 http://服务器公网IP,应该能进入 WordPress 经典的安装界面。注意这时只需填写站点标题、管理员用户名、密码和邮箱,数据库那部分信息不用填,因为它已经被环境变量注入到 wp-config.php 里了。

很多人第一次安装 WordPress 时看到数据库信息表单会习惯性填一遍,其实在这一套容器化环境下填了反而没用,wp-config.php 已经由 compose 的环境变量生成好了。安装完成后建议立刻做两件事:一是登录后台把固定链接改成伪静态格式,让文章 URL 更友好;二是上传一张测试图片,验证 PHP 写卷和 Nginx 读卷的链路都正常。

4.2 数据库连接失败的几种场景

最常见的错误提示是 Error establishing a database connection,大类原因就那么几个。第一个是数据库还没就绪,PHP 连得太早,这就是我加健康检查的原因,把 depends_on 配上 service_healthy 基本能杜绝。第二个是环境变量不一致,compose 里 MARIADB_USER 和 WORDPRESS_DB_USER 必须一致,密码也一样,某个地方多一个空格都会连不上。第三个是容器网络问题,如果 php 容器创建时没有加进 wp_net,或者你手工 docker run 了一个没接网络的容器,服务名 db 自然解析不到。

排查时可以进 php 容器直接验证网络连通性:docker compose exec php sh -lc 'getent hosts db',如果能解析出 172.x.x.x 的内网地址,说明网络正常;再用 docker compose logs php 看启动日志,有没有明确的 SQLSTATE 报错。SQLSTATE[HY000] [2002] Connection refused 一般是网络或端口问题,SQLSTATE[HY000] [1045] Access denied 则是账号密码问题,看到 1045 就优先去核对密码,大多数时候能省下不少排查时间。

4.3 502、上传失败和权限问题的处理

502 Bad Gateway 在拆分开的架构里九成是 PHP 进程出了问题。先 docker compose ps 看 php 容器是否还在运行,如果挂了就看 docker compose logs php 的 OOM 记录;如果进程正常,大概率是 fastcgi_pass 写错或者 SCRIPT_FILENAME 配置不对。我调试时习惯先临时把 Nginx 的 error_log 调成 notice 级别,然后请求一个 PHP 页面,配合 docker compose logs web 看有没有出现 upstream 的 connect() failed 或者 primary script unknown 这类关键行。

上传失败则分两层排查。如果上传的是大文件,先检查 PHP 的 upload_max_filesize 和 post_max_size,再看 Nginx 的 client_max_body_size,三层里任何一层低于文件大小都会被拒绝。注意 Nginx 默认 client_max_body_size 只有 1m,很多人改了 PHP 参数却忘了 Nginx 这层,传 20 MB 的文件照样失败。如果上传的是特定文件类型被拒绝,比如某些插件包提示文件类型不受支持,那是 WordPress 的 mime 类型表在作怪,官方镜像默认带了 fileinfo 扩展,一般不用额外处理。

权限问题大多表现为后台能登录但插件无法安装、上传目录不可写、某些页面出现 500。这些场景下先确认 wp_data 卷的属主和权限:docker compose exec php ls -la /var/www/html 看看目录属主是不是 www-data,wp-content/uploads 是否存在且可写。如果用了 bind mount,大概率就是属主不对,按前面说的 chown -R 82:82 处理一遍就能解决。

4.4 备份、恢复和版本升级实录

备份是容器化方案里最容易被忽视但最值得做扎实的环节。我个人习惯把备份拆成两部分:数据库和站点文件。数据库用 mysqldump 导出 SQL,站点文件用 tar 打包 wp_data 卷。下面两条命令配合 crontab 每周执行,备份文件放到独立的数据盘目录里,再同步到对象存储或者另一台机器,做到异地留存。

docker compose exec -T db sh -lc 'exec mysqldump -uwpuser -p"$MARIADB_PASSWORD" wordpress' > /data/backup/db_$(date +%F).sql docker run --rm -v wp_wp_data:/data -v /data/backup:/backup alpine tar czf /backup/wp_$(date +%F).tar.gz -C /data .

这里的卷名需要注意:compose 里定义的卷实际全名会带上项目目录前缀,比如在 /opt/wp 目录下启动,卷名通常是 wp_wp_data。最稳妥的办法是 docker volume ls 查看实际名字,别靠猜。恢复时,SQL 文件用 docker compose exec -T db mysql -uwpuser -p... wordpress < backup.sql 导回去,wp 的 tar 包用 docker run -v wp_wp_data:/data 解压回去,然后重启 php 容器清掉 opcache。恢复完进后台验证一遍上传目录和插件状态,整个流程十分钟内能完成。

升级 WordPress 核心和 PHP 版本时,步骤是先备份,再 docker compose pull php,最后 docker compose up -d php。因为数据都在卷上,容器重建等于换了一个新镜像实例,站点文件不受影响,opcache 因为 validate_timestamps=0 可能需要 reload。升级前先看插件和主题是否兼容新 PHP 版本,WordPress 官方对 PHP 8.2 支持得很稳,但个别老插件可能不兼容,建议先在测试环境跑一遍再上生产。

5. HTTPS 配置与生产环境加固

5.1 certbot 签发证书与自动续期

站点跑通后第一件事就是上 HTTPS。在我的 compose 文件里已经预留了 certbot 服务,签发时用 webroot 模式,因为 Nginx 已经在 80 端口服务,standalone 模式需要单独占用 80 会有冲突。执行命令前确认域名解析已经指向服务器公网 IP,且安全组放行了 80 端口。

docker compose run --rm certbot certonly \ --webroot -w /var/www/html \ -d example.com -d www.example.com \ --email ops@example.com \ --agree-tos --no-eff-email

签发成功后,证书文件在 ./nginx/ssl/live/example.com/ 下,接下来把 default.conf 升级成 HTTPS 版本:443 端口的 server 块加上 ssl 指令和 fullchain.pem、privkey.pem 的路径,同时保留一个 80 端口的 server 块做 301 跳转。续期也很简单,给 certbot 配一个 cron 任务,每月执行一次续期和 reload,避免证书过期导致浏览器大面积告警。我在生产环境用下面的 cron 跑了很长时间,没有出过续期失败。

# crontab -e 0 3 1 * * docker compose -f /opt/wp/docker-compose.yml run --rm certbot renew --quiet && docker compose exec -T web nginx -s reload

5.2 安全组、SSH 与 WordPress 层防护

容器化部署不等于自动化安全,三层防护都要做。第一层是安全组,前面说过 22、80、443 之外不要开多余端口,SSH 源地址最好限定为常用 IP,别放 0.0.0.0/0 裸奔。第二层是系统,把 sshd 的密码登录关掉,改用密钥登录,端口如果业务允许可以换到非常用端口,配合暴力破解防护工具。第三层是 WordPress 本身,登录页加限速、关闭 XML-RPC、隐藏版本号、定期更新插件主题,这些用成熟的安全插件就能覆盖。

还有一个容易被忽略的点是 WordPress 数据库账号的权限。compose 里 wpuser 只给了 wordpress 库的完整权限,并没有赋全局权限,这是镜像初始化时默认的行为。如果你手工修改了数据库账号,记得遵循最小权限原则,别用一个 root 级别的账号跑应用。容器的 restart: unless-stopped 策略能保证机器重启后自动拉起服务,但也要定期看 docker compose ps 状态,别让异常退出的容器默默躺在那。

5.3 性能优化清单与后续扩展

跑顺之后可以按需做性能优化,我的优化清单按性价比排序:启用 Nginx Gzip 压缩静态资源;给站点接对象缓存插件配 Redis,把数据库查询缓存命中率拉上来;日志轮转用 logrotate 别让 Nginx 日志无限膨胀;MySQL 的 innodb_buffer_pool_size 根据内存调到 512M 左右,避免数据库频繁走磁盘。

后续扩展的方向也比较明确:想提升静态资源分发能力,就把 CDN 接上,静态文件由 CDN 边缘节点响应;想让 WordPress 支持多站点多语言,可以在 compose 里扩展出独立的 php 服务跑不同站点,共用一套 Nginx 做域名路由;想上容器编排平台的话,这套 compose 的拆分逻辑也能自然过渡到 Deployment 加 Service,数据卷换成 PVC 即可。容器化的真正价值就在这里:跑起来只是开始,后面每一个扩展方向都有清晰的路径。

写到这里,我把在这套组合上积累的体会再补两句。容器化 WordPress 最被人低估的不是技术难度,而是它把"环境一致性"这个老问题彻底解决了,本地能跑的东西,生产环境一定也能跑,差别只剩数据和流量。我现在的习惯是:任何一次配置改动都先写进 compose 文件再重建容器,而不是 docker exec 进去改完就跑,这样即使几个月后机器重装,凭一份配置和备份卷也能在半小时内把站点原样拉起来。最后再说一句真心话:把备份做好、把权限想清楚,比研究任何性能参数都值钱,别等丢过数据才明白。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 4:35:59

多阶段构建实战:从1.6GB到30MB的Docker镜像优化指南

我记得第一次认真面对镜像臃肿这件事&#xff0c;是因为一套前后端服务的生产部署&#xff1a;前端是 Vue 打包出来的静态资源&#xff0c;后端是 Go 写的接口服务&#xff0c;按我当时的“直觉”写了一个 Dockerfile&#xff0c;把源码、依赖、构建工具一股脑全塞进镜像里&…

作者头像 李华
网站建设 2026/10/14 4:35:59

批量转双层PDF:OCRmyPDF+Tesseract多语言识别实战指南

上个月我接了一个整理扫描件的活儿&#xff0c;三千多页混合语种材料&#xff0c;中文、英文、繁体&#xff0c;还夹着几页日文表格。靠肉眼翻&#xff0c;三天都翻不完&#xff0c;更别提要从里面快速定位某一条内容。这个场景下&#xff0c;唯一靠谱的办法就是做双层PDF——给…

作者头像 李华
网站建设 2026/10/14 4:35:52

手游自然流量增长逻辑:基于2026商店算法的全套ASO优化体系拆解

在手游存量竞争时代&#xff0c;流量结构的健康度直接决定产品生命周期长短。现阶段&#xff0c;单纯依赖付费投流的增长模式弊端持续凸显&#xff1a;流量成本逐年递增、用户精准度参差不齐、流量增量完全受制于营销预算&#xff0c;绝大多数中小手游产品难以依靠买量实现长线…

作者头像 李华
网站建设 2026/10/14 4:35:46

Unity运行时加载FBX/OBJ模型:TriLib插件实战与避坑指南

简介&#xff1a;这份源码工程面向Unity开发者与AR/VR、游戏工具链方向的技术人员&#xff0c;解决运行时动态导入并预览3D模型的实际需求。工程基于TriLib 2.3.7插件&#xff0c;在Unity 2021.3.27与Standard Render Pipeline下实现选择模型、加载并即时预览的完整流程&#x…

作者头像 李华
网站建设 2026/10/14 4:35:25

云原生模型训练演进:从裸机到声明式调度

十年前&#xff0c;我和一位同事蹲在机房里&#xff0c;对着一台刚装好驱动、刚刚好能跑上前一版实验的GPU服务器&#xff0c;连试三次都失败。问题出在系统里的某个动态库被另一种软件包悄悄覆盖了。当时我们开玩笑说&#xff0c;“训练深度学习模型&#xff0c;三分练模型&am…

作者头像 李华
网站建设 2026/10/14 4:35:25

百度地图微信小程序JSAPI实战:从环境配置到路线规划避坑指南

简介&#xff1a;这份资源是面向微信小程序开发者的百度地图 JavaScript API 接口包&#xff0c;适合需要在社交、旅游、打车等场景中嵌入地理位置服务的初中级开发者。压缩包共35个文件&#xff0c;约28KB&#xff0c;以js脚本、wxss样式、json配置、wxml模板为主&#xff0c;…

作者头像 李华