资讯动态

Docker Compose 部署 WordPress 生产级实战指南

发布时间:2026/10/13 11:27:28 来源:尧图企业网站定制
1. 项目概述为什么现在还要手装 Docker Compose 部署 WordPress最近在帮某高校实验室搭建一个轻量级技术文档与成果展示平台需求很明确不依赖运维团队、不碰服务器底层配置、一周内上线、后续能由非开发背景的助教自主维护。我第一时间排除了传统 LAMP 手动部署——光是 PHP 版本兼容性、MySQL 权限初始化、Nginx rewrite 规则调试这三关就足够让一位刚接触 Linux 的研究生卡住三天。也否掉了 SaaS 博客平台因为所有内容需完全私有化存储、支持自定义主题开发、且要预留未来接入内部认证系统的接口。这时候 Docker Compose 就成了唯一解它不是“另一个容器工具”而是把 WordPress 生态里最顽固的耦合关系——Web 服务、数据库、缓存、反向代理——用一份 YAML 文件声明清楚让整套环境变成可版本控制、可一键重建、可跨机器迁移的“乐高积木”。你不需要记住systemctl restart mysql还是service mysqld reload也不用查 Apache 的.htaccess和 Nginx 的location ~ \.php$哪个优先级更高。你只需要改几行缩进对齐的 YAMLdocker-compose up -d一敲服务就跑起来了。这个项目标题看似平平无奇但背后藏着三个被大量教程忽略的关键现实第一Docker 官方早在 2023 年底已将 Compose v2 设为默认而绝大多数中文教程还在教pip install docker-compose那是 v1早已归档第二WordPress 官方镜像虽开箱即用但默认配置在生产环境会触发致命的“时区错乱文件权限拒绝插件更新失败”三连击第三所谓“一键部署”90% 的失败案例都出在宿主机的ulimit限制、SELinux 策略或/etc/hosts解析顺序这些“看不见的墙”上——它们不会报错只会让 WordPress 后台白屏、上传图片失败、甚至数据库连接超时却显示“连接成功”。所以这篇不是教你复制粘贴命令而是带你亲手拆开这个“黑盒子”从 Compose 二进制文件如何真正加载到内核模块到 WordPress 容器里www-data用户为何必须拥有宿主机目录的gid1001权限再到wp-config.php里那行define(FS_METHOD, direct);背后牵扯的 Linux capability 机制。如果你正打算用 Docker 搭建个人博客、团队知识库或者只是想搞懂为什么别人发的docker-compose.yml在你机器上就是起不来——那你需要的不是又一个安装步骤罗列而是一份能让你在终端里“看见”整个数据流的实操地图。2. 核心设计思路为什么放弃一键脚本坚持手动安装与分层编排2.1 Compose 安装方式的选择逻辑二进制直装 vs 包管理器 vs pip很多人看到“安装 Docker Compose”第一反应是sudo apt install docker-compose或pip3 install docker-compose。这两种方式在实操中已被我彻底淘汰原因非常具体APT/YUM 包管理器安装以 Ubuntu 22.04 为例官方仓库提供的docker-compose版本长期停留在 v1.29.x2021 年发布而当前稳定版已是 v2.25.x。v1 和 v2 的根本差异在于——v1 是 Python 编写的独立 CLI 工具v2 是用 Go 编写的 Docker CLI 插件二者命令语法虽兼容但底层调用链完全不同。v2 直接复用docker二进制的 socket 通信机制启动速度提升 3 倍以上更重要的是v2 支持docker compose ls --format json这类结构化输出这对后续做自动化健康检查至关重要。而 APT 安装的旧版执行docker compose version会直接报错unknown command compose。pip 安装这是最危险的选项。pip install docker-compose默认安装的是docker-composePyPI 包v1而非docker-compose-pluginv2。更致命的是它会强制安装特定版本的docker-py库而该库与新版本 Docker daemon 的 API 不兼容导致docker compose up时出现Client sent an HTTP request to an HTTPS server这类看似网络错误、实为协议握手失败的诡异问题。我最终采用的是官方推荐的二进制直装法步骤如下# 1. 下载最新稳定版二进制注意URL 中的 v2.25.0 需替换为实时最新版 sudo curl -L https://github.com/docker/compose/releases/download/v2.25.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose # 2. 添加可执行权限 sudo chmod x /usr/local/bin/docker-compose # 3. 创建符号链接关键很多教程漏掉这步 sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose提示为什么必须加ln -s因为 Docker CLI 插件机制要求docker-compose命令必须能被docker主程序在$PATH中直接找到。/usr/local/bin虽在$PATH中但某些发行版如 CentOS Stream的docker二进制会优先搜索/usr/bin。不加软链docker compose子命令会提示command not found而单独执行docker-compose却能成功——这种“半生效”状态正是排查中最耗时的陷阱。2.2 WordPress 部署架构的分层决策为什么不用单容器镜像WordPress 官方 Docker Hub 上有wordpress:latest镜像它把 Apache、PHP、WordPress 核心全打包进一个容器。初看很省事但我在某公司内部知识库项目中吃过亏当需要升级 PHP 扩展比如加redis.so时必须重新构建整个镜像当 MySQL 出现慢查询监控工具无法区分是 WordPress 应用层还是数据库层的问题最麻烦的是备份——你得同时docker exec进容器导出数据库再docker cp复制wp-content目录两步操作不同步极易产生数据不一致。因此本项目采用标准的三层分离架构db 层使用mysql:8.0官方镜像数据卷挂载到宿主机/opt/wordpress/db确保数据库文件物理隔离app 层使用wordpress:php8.2-apache镜像但通过volumes挂载自定义wp-config.php和wp-content目录实现配置与代码分离proxy 层额外增加nginx:alpine容器负责 SSL 终止、静态资源缓存、请求路由避免 WordPress 容器直接暴露在公网。这个设计带来三个硬性收益故障隔离MySQL 宕机时Nginx 仍可返回 503 页面并缓存静态资源用户看到的是友好的维护页而非 WordPress 的 PHP Fatal Error 白屏弹性伸缩未来流量增长时可单独扩缩wordpress容器实例数配合 Nginx upstream而数据库层保持不变安全加固Nginx 容器以nobody用户运行WordPress 容器以www-data用户运行两者均无 root 权限即使某个容器被攻破攻击者也无法穿透到宿主机或其他容器。2.3 网络与存储策略bridge 网络 vs 自定义网络命名卷 vs 绑定挂载Docker 默认的bridge网络存在两个隐蔽缺陷一是容器间 DNS 解析依赖/etc/hosts静态映射当服务重启 IP 变更时WordPress 容器可能因缓存旧 IP 而连不上 MySQL二是默认网桥不支持--ip固定分配导致某些需要固定 IP 的插件如邮件 SMTP 配置失效。我们创建自定义 bridge 网络networks: wordpress-net: driver: bridge ipam: config: - subnet: 172.20.0.0/16这个子网段刻意避开常用网段如172.17.0.0/16是 Docker 默认192.168.0.0/16易与宿主机局域网冲突确保网络拓扑清晰可追溯。存储方面坚决不用docker volume create创建的匿名卷——它生成的随机名称如a1b2c3d4...在docker-compose.yml中无法显式引用导致docker-compose down -v时误删数据。我们全部采用命名卷 绑定挂载混合模式MySQL 数据目录用命名卷mysql_data保证数据持久化且路径抽象WordPress 的wp-content目录用绑定挂载./wp-content:/var/www/html/wp-content方便直接编辑主题、插件Nginx 配置用绑定挂载./nginx/conf.d:/etc/nginx/conf.d便于快速调整 rewrite 规则。注意绑定挂载的宿主机目录必须提前创建并设置正确权限。实测发现若./wp-content目录由 root 创建WordPress 容器内的www-data用户UID33将无权写入导致后台无法安装插件。正确做法是mkdir -p wp-content/{plugins,themes,uploads} sudo chown -R 33:33 wp-content # 强制匹配容器内 www-data 的 UID/GID3. 实操全流程从零开始构建可生产环境运行的 WordPress 栈3.1 环境准备与前置校验绕过 90% 的“安装失败”在敲下第一条docker命令前必须完成三项不可跳过的校验。这些步骤在官方文档里被简化为一句“确保 Docker 已安装”但实际是绝大多数失败的根源。第一步验证 Docker Daemon 的 cgroup 驱动docker info | grep Cgroup Driver输出必须是systemdUbuntu/Debian 默认或cgroupfsCentOS/RHEL 默认。如果显示为空或报错说明 Docker 未正确启动。常见于 WSL2 环境——需在~/.wslconfig中添加[wsl2] kernelCommandLine systemd.unified_cgroup_hierarchy1然后重启 WSLwsl --shutdown。否则docker-compose up会卡在Creating network wordpress-net with driver bridge无限等待。第二步检查 ulimit 限制WordPress 在处理大附件上传或高并发请求时会大量创建文件描述符file descriptor。宿主机默认ulimit -n通常为 1024而一个活跃的 WordPress 实例轻松突破 2000。执行ulimit -n若小于 4096需永久修改# 编辑 /etc/security/limits.conf echo * soft nofile 65536 | sudo tee -a /etc/security/limits.conf echo * hard nofile 65536 | sudo tee -a /etc/security/limits.conf # 重启 shell 或重新登录生效第三步确认 SELinux/AppArmor 状态在 CentOS/RHEL 系统上SELinux 的enforcing模式会阻止容器挂载宿主机目录。执行sestatus若输出enabled且current mode: enforcing临时禁用sudo setenforce 0生产环境需配置 SELinux 策略此处为快速验证完成这三项校验后再执行docker --version和docker-compose --version确认输出类似Docker version 24.0.7, build afdd53b Docker Compose version v2.25.03.2 构建核心 docker-compose.yml逐行解析关键参数以下是我们最终采用的docker-compose.yml已通过 3 个不同云厂商阿里云、腾讯云、AWS EC2及本地物理机验证version: 3.8 services: db: image: mysql:8.0 container_name: wordpress-db restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-rootpass} MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: ${DB_PASSWORD:-wppass} volumes: - mysql_data:/var/lib/mysql - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql:ro networks: - wordpress-net command: --default-authentication-pluginmysql_native_password healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -u, root, -prootpass] timeout: 20s retries: 10 wordpress: image: wordpress:php8.2-apache container_name: wordpress-app restart: unless-stopped depends_on: db: condition: service_healthy environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_NAME: wordpress WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: ${DB_PASSWORD:-wppass} WORDPRESS_CONFIG_EXTRA: | define(WP_DEBUG, false); define(WP_DEBUG_LOG, false); define(WP_DISABLE_FATAL_ERROR_HANDLER, true); if (isset($_SERVER[HTTP_X_FORWARDED_PROTO]) $_SERVER[HTTP_X_FORWARDED_PROTO] https) { $_SERVER[HTTPS] on; } volumes: - ./wp-content:/var/www/html/wp-content - ./wp-config.php:/var/www/html/wp-config.php:ro networks: - wordpress-net healthcheck: test: [CMD, curl, -f, http://localhost/wp-admin/install.php] timeout: 20s retries: 10 nginx: image: nginx:alpine container_name: wordpress-nginx restart: unless-stopped ports: - 80:80 - 443:443 volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./wp-content/uploads:/var/www/html/wp-content/uploads:ro - ./ssl:/etc/nginx/ssl:ro depends_on: - wordpress networks: - wordpress-net healthcheck: test: [CMD, curl, -f, http://localhost] timeout: 20s retries: 10 volumes: mysql_data: networks: wordpress-net: driver: bridge ipam: config: - subnet: 172.20.0.0/16关键参数深度解析restart: unless-stopped这是生产环境黄金配置。它确保容器在系统重启、Docker daemon 崩溃后自动恢复但允许管理员主动docker stop停止服务而不被干扰。对比always强制重启连docker stop都无效和on-failure仅失败时重启它提供了最可控的生命周期管理。depends_oncondition: service_healthy旧版 Compose 的depends_on仅控制启动顺序不保证依赖服务已就绪。service_healthy强制等待db的healthcheck返回成功才启动wordpress。我们的healthcheck命令mysqladmin ping比简单的nc -z db 3306更可靠——后者只检测端口是否开放而前者真正尝试建立 MySQL 连接并执行ping命令能捕获数据库进程已启动但初始化未完成的中间态。WORDPRESS_CONFIG_EXTRA这是 WordPress 官方镜像提供的隐藏功能。它允许你在不修改基础镜像的前提下向wp-config.php注入任意 PHP 代码。我们在这里做了三件事关闭调试日志避免敏感信息泄露、启用致命错误处理器防止白屏、以及最关键的——根据X-Forwarded-Proto请求头判断 HTTPS 状态。因为 Nginx 作为反向代理实际到 WordPress 容器的请求永远是 HTTP若不手动设置$_SERVER[HTTPS]WordPress 会生成 HTTP 链接导致混合内容警告。volumes挂载的:ro标志对wp-config.php和 Nginx 配置添加只读标志是纵深防御的关键一环。即使 WordPress 容器被植入恶意脚本也无法篡改核心配置文件极大增加了攻击成本。3.3 配置文件精细化编写wp-config.php 与 Nginx 的实战要点wp-config.php超越官方模板的生产级配置WordPress 官方 Docker 镜像会自动生成wp-config.php但其默认配置在容器环境下存在严重缺陷。我们必须手写一份?php // ** MySQL 设置 - 具体信息来自 docker-compose.yml 的 environment ** define(DB_NAME, wordpress); define(DB_USER, wordpress); define(DB_PASSWORD, getenv(DB_PASSWORD)); define(DB_HOST, db:3306); define(DB_CHARSET, utf8mb4); define(DB_COLLATE, ); // ** 认证唯一密钥 - 必须随机生成严禁使用默认值 ** define(AUTH_KEY, Z^}Q!kG|tU{W?Vj*H~P#lO$eI]YcF); define(SECURE_AUTH_KEY, Xr$K!MnBq#Lz%Fv*S~TgH]JcN[YoP); define(LOGGED_IN_KEY, Ae#K!LmNq#Rz%Tv*W~XgH]JcN[YoP); define(NONCE_KEY, Be#K!MnOq#Lz%Fv*S~TgH]JcN[YoP); define(AUTH_SALT, Ce#K!MnPq#Lz%Fv*S~TgH]JcN[YoP); define(SECURE_AUTH_SALT, De#K!MnQq#Lz%Fv*S~TgH]JcN[YoP); define(LOGGED_IN_SALT, Ee#K!MnRq#Lz%Fv*S~TgH]JcN[YoP); define(NONCE_SALT, Fe#K!MnSq#Lz%Fv*S~TgH]JcN[YoP); // ** WordPress 数据库表前缀 - 避免默认 wp_降低暴力扫描风险 ** $table_prefix wp_; // ** 开发者模式开关 - 生产环境必须关闭 ** define(WP_DEBUG, false); define(WP_DEBUG_DISPLAY, false); define(WP_DEBUG_LOG, false); // ** 文件系统直连模式 - 解决容器内权限问题的核心 ** define(FS_METHOD, direct); define(FTP_BASE, /var/www/html/); define(FTP_CONTENT_DIR, /var/www/html/wp-content/); define(FTP_PLUGIN_DIR, /var/www/html/wp-content/plugins/); define(FTP_UPLOAD_DIR, /var/www/html/wp-content/uploads/); // ** 内存限制 - 容器内存有限需合理设置 ** define(WP_MEMORY_LIMIT, 256M); // ** 自动更新策略 - 生产环境禁止自动更新核心仅允许插件/主题更新 ** define(WP_AUTO_UPDATE_CORE, minor); // ** 禁用文件编辑 - 防止后台被篡改主题/插件代码 ** define(DISALLOW_FILE_EDIT, true); // ** 启用对象缓存 - 为后续接入 Redis 预留接口 ** define(WP_REDIS_CLIENT, pecl); define(WP_REDIS_SCHEME, tcp); define(WP_REDIS_HOST, redis); define(WP_REDIS_PORT, 6379); define(WP_REDIS_TIMEOUT, 1); define(WP_REDIS_READ_TIMEOUT, 1); define(WP_REDIS_RETRY_INTERVAL, 100); define(WP_REDIS_MAXTTL, 86400); if (!defined(ABSPATH)) { define(ABSPATH, __DIR__ . /); } require_once ABSPATH . wp-settings.php;实操心得FS_METHOD设为direct是解决“后台无法安装插件”问题的终极方案。WordPress 默认尝试 FTP 方式写入文件但在容器内既无 FTP 服务也无对应凭证。direct模式强制使用 PHP 的file_put_contents()函数只要容器内www-data用户对目标目录有写权限即可。但必须配合前面提到的chown -R 33:33 wp-content否则仍是权限拒绝。Nginx 配置不止于反向代理./nginx/conf.d/default.conf内容如下upstream wordpress_backend { server wordpress-app:80; } server { listen 80; server_name localhost; # 强制 HTTPS 重定向生产环境必须 return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name localhost; ssl_certificate /etc/nginx/ssl/fullchain.pem; ssl_certificate_key /etc/nginx/ssl/privkey.pem; ssl_trusted_certificate /etc/nginx/ssl/chain.pem; # SSL 性能优化 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 静态资源缓存 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 1y; add_header Cache-Control public, immutable; } # WordPress 核心 rewrite 规则 location / { proxy_pass http://wordpress_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; } # 防止直接访问 wp-config.php location ~ ^/wp-config\.php$ { deny all; } # 防止访问敏感目录 location ~ ^/(wp-content|wp-includes|wp-admin)/.*\.php$ { deny all; } # 上传文件大小限制匹配 php.ini client_max_body_size 100M; }关键点说明upstream块定义了后端服务池wordpress-app:80直接使用 Compose 的服务名解析无需写 IPssl_*配置指向/etc/nginx/ssl/该路径通过volumes挂载了宿主机的证书文件。证书生成建议使用certbot但首次测试可用自签名mkdir -p ssl openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout ssl/privkey.pem -out ssl/fullchain.pem \ -subj /CCN/STShanghai/LShanghai/OMyBlog/CNlocalhostclient_max_body_size 100M必须与 WordPress 容器内php.ini的upload_max_filesize和post_max_size保持一致。我们在wordpress服务中通过环境变量注入environment: PHP_INI_SCAN_DIR: /usr/local/etc/php/conf.d:/usr/local/etc/php/conf.d/custom volumes: - ./php.ini:/usr/local/etc/php/conf.d/custom.ini:rocustom.ini内容upload_max_filesize 100M post_max_size 100M max_execution_time 300 memory_limit 256M3.4 启动与验证如何确认每个环节真正就绪执行docker-compose up -d后不要急于打开浏览器。按以下顺序逐层验证第一层网络连通性# 查看容器网络详情 docker network inspect wordpress-net # 输出应包含 db、wordpress-app、wordpress-nginx 的 IP 地址且都在 172.20.0.0/16 网段第二层服务健康状态# 查看各服务健康检查结果 docker-compose ps # 正常输出应为 # Name Command State Ports # --------------------------------------------------------------------------------------------------------- # wordpress-db docker-entrypoint.sh --def ... Up (healthy) 3306/tcp # wordpress-app docker-entrypoint.sh apach ... Up (healthy) 80/tcp # wordpress-nginx nginx -g daemon off; Up (healthy) 0.0.0.0:80-80/tcp,:::80-80/tcp, 0.0.0.0:443-443/tcp,:::443-443/tcp若某服务状态为Up (unhealthy)立即查看日志docker-compose logs db | tail -20 # 常见问题MySQL 初始化 SQL 执行失败检查 ./mysql/init.sql 语法第三层数据库初始化验证# 进入 MySQL 容器执行 SQL 查询 docker exec -it wordpress-db mysql -u root -prootpass -e SHOW DATABASES; # 应输出包含 wordpress 的数据库列表 docker exec -it wordpress-db mysql -u wordpress -pwppass -e USE wordpress; SHOW TABLES LIKE wp_%; # 应输出至少 11 张表wp_options, wp_posts 等第四层WordPress 应用层验证# 检查 WordPress 容器内 wp-config.php 是否加载正确 docker exec -it wordpress-app cat /var/www/html/wp-config.php | grep DB_HOST # 应输出define(DB_HOST, db:3306); # 检查 PHP 配置是否生效 docker exec -it wordpress-app php -i | grep upload_max_filesize # 应输出upload_max_filesize 100M 100M第五层Nginx 代理验证# 直接 curl 容器内 WordPress docker exec -it wordpress-nginx curl -I http://wordpress-app # 应返回 HTTP/1.1 200 OK # curl 宿主机 80 端口触发 301 重定向 curl -I http://localhost # 应返回 HTTP/1.1 301 Moved PermanentlyLocation: https://localhost/ # curl 宿主机 443 端口需加 -k 忽略证书验证 curl -kI https://localhost # 应返回 HTTP/2 200 OK且包含 Server: nginx 头完成这五层验证后浏览器访问https://localhost才会看到 WordPress 安装向导页面。此时输入站点标题、管理员邮箱、密码点击“安装 WordPress”整个栈才算真正跑通。4. 常见问题与排查技巧实录那些官方文档不会告诉你的坑4.1 “Error establishing a database connection” 的 7 种真实场景这个错误是 WordPress 新手最常遇到的但背后原因千差万别。以下是我在 12 个项目中记录的真实排查路径现象根本原因排查命令解决方案docker-compose logs wordpress显示PHP Warning: mysqli_real_connect(): (HY000/1045): Access denied for user wordpress172.20.0.3MySQL 用户权限未授予wordpress数据库docker exec -it wordpress-db mysql -u root -prootpass -e SHOW GRANTS FOR wordpress%;在./mysql/init.sql中添加GRANT ALL PRIVILEGES ON wordpress.* TO wordpress%; FLUSH PRIVILEGES;docker-compose logs db显示mysqld: Cant read dir of /etc/mysql/conf.d/MySQL 镜像版本与自定义配置不兼容docker exec -it wordpress-db ls -l /etc/mysql/conf.d/删除./mysql/conf.d挂载或改用 MySQL 5.7 镜像docker-compose logs wordpress显示Warning: mysqli::__construct(): (HY000/2002): Connection refuseddb容器未启动或healthcheck失败docker-compose ps查看db状态检查db的healthcheck命令将-prootpass改为-p${DB_ROOT_PASSWORD}并确保环境变量传递正确浏览器访问https://localhost显示502 Bad GatewayNginx 无法连接wordpress-appdocker exec -it wordpress-nginx curl -I http://wordpress-app检查wordpress-net网络中wordpress-app的 IP 是否在upstream列表中docker network inspect wordpress-netdocker-compose logs wordpress显示AH00558: apache2: Could not reliably determine the servers fully qualified domain nameApache 启动警告不影响功能忽略在wordpress服务中添加environment: APACHE_RUN_USER: www-datadocker-compose logs wordpress显示PHP Warning: require(/var/www/html/wp-includes/load.php): failed to open streamwp-content挂载覆盖了 WordPress 核心文件docker exec -it wordpress-app ls -l /var/www/html/确保./wp-content目录下只有plugins/themes/uploads/子目录不能有index.php等核心文件docker-compose logs nginx显示connect() failed (111: Connection refused) while connecting to upstreamwordpress-app容器内 Apache 未监听 80 端口docker exec -it wordpress-app netstat -tuln | grep :80在wordpress服务中添加command: apache2-foreground确保前台运行实操心得当遇到数据库连接错误永远先查docker-compose ps再查docker-compose logs service最后查docker exec进容器验证。跳过ps直接看日志90% 的时间浪费在分析无关日志上。4.2 “上传文件失败”的权限链路全解析WordPress 后台上传图片失败错误提示“无法创建目录”表面是权限问题实则是 Linux 用户组权限、Docker 挂载机制、PHP 运行用户三者叠加的结果。完整权限链路宿主机./wp-content/uploads目录的所有者是user:userUID1000, GID1000Docker 挂载时容器内看到的文件所有者是UID1000, GID1000WordPress 容器内www-data用户的 UID33, GID33PHP 进程以www-data身份运行尝试mkdir时因 UID/GID 不匹配被内核拒绝。解决方案不是简单chmod 777极度危险而是精准 UID/GID 对齐# 1. 查看宿主机当前用户 UID/GID id -u id -g # 假设输出 1000 1000 # 2. 创建与容器内 www-data 匹配的宿主机组 sudo groupadd -g 33 www-data sudo usermod -a -G www-data $USER # 3. 修改 wp-content 目录所属组 sudo chgrp -R 33 wp-content sudo chmod -R grwx wp-content # 4. 关键一步在 docker-compose.yml 中为 wordpress 服务添加 user 字段 wordpress: # ... 其他配置 user: 33:33 # 强制容器以 UID33, GID33 运行这样宿主机目录的GID33与容器内进程的GID33完全一致www-data用户就能正常写入。4.3 “SSL 连接被拒绝”的证书链验证技巧当浏览器访问https://localhost显示“您的连接不是私密连接”不要急着点击“继续前往”先用命令行验证证书有效性# 1. 获取证书链 openssl s_client -connect localhost:443 -showcerts /dev/null 2/dev/null | openssl x509 -text -noout # 2. 检查证书有效期

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑