记录 ECS 部署 WordPress 二开程序的曲折 Debug 过程

Published: 2024-06-30

Tags: ECS Docker

本文总阅读量

前情信息

  • 服务部署在 AWS EKS
  • 二次开发的 WordPress 代码通过 wordproess 基础镜像封装,配置通过 Sidecar 镜像拉取,两个容器在一个 Task 内
  • WordPress 由其它团队二次开发,我协助一同事将服务容器化部署

排查过程

  1. 现象:线上二次开发的 WordPress 服务修改后的功能没有生效
  2. 猜测:通过外部机器更新的 efs 后,Wordpress 服务没有读取到最新的代码
  3. 验证:通过跳板机进到 ECS 机器节点,再通过 Docker 进到容器,此时从外部更新 efs 内文件,容器内可见修改,efs 文件更新没有问题
  4. 猜测:因更新 efs 内主题代码为手动复制,猜测复制不完全导致测试环境和正式环境的代码不一致(重点!)
  5. 验证:核对了正式环境 wp-content/.../comment.php 等文件,文件内容符合预期,初步认为正式环境代码是跟测试环境保持一致的
  6. 猜测:在 efs 内修改主题代码后,WordPress 程序没有使用最新的代码(没有 PHP 语言和 WordPress 经验,不清楚修改代码后是否实时生效)
  7. 验证:简单粗暴的修改接口返回值的 Code 为 4XX,从页面看可以立即生效,确认可以动态生效,此时认为排除了 efs 磁盘的嫌疑、也基本排除代码不一致问题
  8. 猜想:测试环境和正式环境的数据库数据不一致,导致代码逻辑走到了异常流程
  9. 验证:比对数据库表结构,字段均一致,未发现差异
  10. 猜想:基本排除数据库不一致问题,但想定位到问题,也需要看下线上代码走到了哪个条件分支导致跟测试环境结果不一样
  11. 验证:线上相关代码添加 ERROT LOG,在 ECS 机器上查看容器日志,期间引发 Container 退出,Task 重新启动,调试非常麻烦
  12. 验证:先将本地测试环境搭建起来,看是否有同样问题,同时也方便添加日志查看输出
  13. 问题:本地没有可使用的数据库,暂连接线上数据库进行访问(正式环境暂无线上用户),访问国外异常缓慢,且容易触发超时,另外本地启动的 WordPress 会修改线上数据库插件的开关,不适合再继续使用
  14. 问题:使用外包团队提供的数据库,导入数据出现问题,使用本地 MySQL 数据库问题依然存在。
  15. 解决:MySQL 版本不一致,修复测试环境日期为空的字段、或忽略目标 MySQL 的强校验,导入成功。
  16. 问题:本地 Docker Desktop 启动容器内的 Service 连不上通过 Brew 安装的本地数据库
  17. 解决:换用 ObrStack 容器程序,指定主机网络模式,同时数据库地址从 localhost 调整为 127.0.0.1,访问成功
  18. 问题:从浏览器页面上访问不到服务的页面
  19. 解决:通过主机模式没有映射,原本 -p 80:8080 应访问 80 到达服务,调整网络模式后需要访问 8080,但 8080 被本地的 Nginx 服务占用,删除相关容器后恢复访问,本地测试环境启动完成
  20. 验证:本地开发环境功能正常,未复现线上问题
  21. 猜想:问题很隐蔽,为进一步排除数据库问题,正式环境的数据库也同步到本地进行验证
  22. 验证:功能正常,彻底排除正式环境可能存在数据差异的猜想
  23. 猜想:数据库一致,猜想正式环境和测试环境代码还是存在不一致,可能存在某一个文件不一致,在之前测试时没碰到
  24. 验证:通过跳板本 ECS 节点上,导出正在运行的容器的全量代码文件,本地进行比对,发现一些文件不同!
  25. 验证:正式环境线上修改代码,功能正常(找到差异,定位就容易了)
  26. 猜想:容器构建的时候,代码没有正确打包到镜像中
  27. 验证:在 Jenkins 控制台打印了特定文件、在 ECS 节点上导出 Image 内的文件,均是正确的,再次核对容器内文件,不正确,也就是 “镜像中文件正确,容器中文件不正确”
  28. 猜想:容器启动的时候,WordPress 镜像有什么特殊逻辑,删除或覆盖了二次开发的代码文件
  29. 验证:排查 WordPress 镜像的启动脚本,还真发现了,如果 /var/www/html 目录没有文件,则从 /usr/src/wordpress 复制代码到 /var/www/html 目录,注释掉 docker-entrypoint.yaml 相关代码,/var/www/html 内的内容消失,仅剩 sidecar 拉取的 wp-config.php 及 efs 挂载的 wp-content 文件夹(此时也是一头雾水)
  30. 验证:构建镜像时,将二次开发的代码放到 /usr/src/wordpress 目录,代码果真被复制过去,问题临时解决,但是很奇怪的是为什么镜像中存在文件,在 docker-entrypoint.yaml 中走到了 “如果文件不在” 的复制逻辑呢?
  31. 猜想:WordPress Dockerfile 模版看到个 VOLUME /var/www/html 指令,可能会导致 /var/www/html 被清空
  32. 验证:基于 WordPress 基础镜像制作镜像,使用 COPY 拷贝自定义的文件到 /var/www/html 目录,启动容器后,文件依然存在,启动临时容器挂载这个匿名 volume ,可以在 volume 中查看到自定义文件,说明 WordPress 模版挂载的 VOLUME 不会影响到构建镜像时存入的文件。(对于 VOLUME 命令不理解导致的)
  33. 验证:手动从镜像启动容器自定义文件不会消失
  34. 猜想:所以还存在其他因素导致镜像中的文件在启动后消失问题,感觉此时临门一脚,但迈不出去
  35. 验证:偶然间,使用 Inspect 查看容器,发现启动容器时,/var/www/html 目录被绑定到了卷,进而发现,这个卷也被 sidecar 挂载,它拉取 wp-config.php 放到这个目录中... 至此,真相大白:因为服务容器在运行时绑定了新卷,清空了目录,这个是 ansible 调用 Playbook 部署 ECS Service / Task 脚本中配置的

另外,为什么只有正式环境有问题、手动从镜像启动容器不能复现、明明有代码,WordPress 为什么还走到了复制 src 代码的逻辑,以及换用其它如 php nginx 镜像发现目录内没有代码起不来,都串联起来了。

其实整个排查问题中,最具误导性的点在于 WordPress 复制文件夹的时候不会复制 wp-content 文件夹(如果存在),这就导致在确认 efs 文件的时候,看 wp-content 下主题的修改是更新了的就认为代码已被正确发布,但事实是非 wp-content 文件夹内的其它 php 文件,均被 wordpress 的 src 所覆盖,以至于兜了一大圈才又再次排查代码不一致问题

要点总结

  1. Dockerfile 中使用 VOLUME /dir 命令可以创建一个匿名卷,并将 /dir 目录绑定到这个卷,持久化存储这个目录的内容,后续使用这个镜像作为基础镜像创建镜像,向 /dir 目录复制内容时,也是存储在这个匿名卷中
  2. Container 容器启动时通过 -v 挂载卷,会导致目标文件夹内容丢失,本例中的问题即挂载了(/wp-config.php 到 /var/www/html),导致镜像中的文件丢失,仅剩 wp-config.php 文件
  3. WordPress 镜像有行为,当检测到 /var/www/html 目录没有 index 及 version 文件时会触发复制 src 到这个目录
  4. 容器通过 --network host 主机模式启动时,容器内的 localhost 和 127.0.0.1 地址可能不同,从容器内访问电脑上的服务,稳妥的方式是使用 127.0.0.1
  5. efs 磁盘可以同时挂载到多个 EC2 或 ECS Container 上,修改后会立即生效