CTFd 删站事故复盘
本删站事故发生于 2022 年 11 月 25 日。
事故缘由
2022 年 6 月起,我负责打理学校实验室的 CTFd 站点。
站点使用了 Docker Compose 来部署应用,再使用 Nginx 负责反代。
2022 年 11 月 25 日,由于服务器使用的是 CentOS 8 的操作系统,已经 EoL 了。我想解决服务器上一团糟的依赖地狱还有源镜像的问题。
事故过程
由于现存的 docker 是由宝塔安装的,并不是跟随 docker-ce 的最新版本,于是我选择使用 yum --allowerasing upgrade
加入官方源进行覆盖安装。这时 yum 使用了旧的宝塔版本覆盖了我后来引入的官方源。再次覆盖安装回去即可。
截止目前都没有问题,只是导致服务发生了分钟级别无关紧要的离线罢了。
这时,由于宝塔面板 docker 的一些前端bug。我重启了下防火墙,并再次重装了 docker.
紧接着,站点发生了 502 Bad Gateway.
思考了下,这是由于 Nginx 无法与容器通信导致的网络问题。果断使用 docker logs <CTFd 容器 ID> | tail
查看了下最近的几条日志。发现是无法连接 MariaDB 数据库,关闭防火墙后解决。
这时,使用 docker compose up -d
启动服务时会提示 strconv.Atoi: parsing "": invalid syntax
错误,上 StackOverflow 搜索后发现了一篇帖子。
https://stackoverflow.com/questions/73948802/strconv-atoi-parsing-invalid-syntax
敲完了 docker compose down --remove-orphans
命令后马上觉得不对,往下翻发现了……
这里已经意识到发生惨剧了,立马关闭了所有容器。尽量减少操作以防 Docker 有什么 GC 机制。
发生了什么
https://docs.docker.com/engine/reference/commandline/compose_down/
Name, shorthand | Description |
---|---|
--remove-orphans |
Remove containers for services not defined in the Compose file. |
--remove-orphans
会删除 compose 文件中未定义的容器。
使用 docker ps
看了下,只剩一个 CodiMD 的容器了……
该做什么呢
万幸的是,CTFd 在建设的时候就使用了 Volume 将目录映射到宿主机目录,所以不出意外的话 MariaDB, Redis 和 CTFd 的数据是都在的。只是重新部署服务的事情了。
先将仅存的备份做一份镜像,然后再次 docker compose up -d
(-d
为后台启动。同时可以酌情使用 --no-recreate
以不重新创建已经停止的容器)
网络问题
CTFd 默认会配置端口转发,可能会启动失败,建议先清空 iptables chains 再重启 docker 服务。
sudo iptables -t filter -F
sudo iptables -t filter -X
sudo systemctl restart docker
然后服务恢复正常了。
类似的故事
Before the Accident
容器备份
可以使用 docker commit
创建容器的快照
docker commit -p <容器 ID> container-backup
命令会生成一个作为Docker镜像的容器快照,可以通过 docker images
查看创建的Docker镜像。为了备份该快照,我们有两个选择,一个是我们可以登录进 Docker 注册中心,并推送该镜像;另一个是我们可以将 Docker 镜像打包成tar包备份,以供今后使用。
国内访问速度慢的可怜,所以建议选择本地备份,这也是绝大多数人的选择。要完成该操作,我们需要运行以下 docker save
命令。
docker save -o /Volumes/work/docker/backup/nginx-backup.tar nginx-backup
容器恢复
在注册中心推送了这些 Docker 镜像,那么我们仅仅需要把那个 Docker 镜像拉回本地。
docker pull arunpyasi/container-backup:test
将这些 Docker 镜像作为 tar 包文件备份到了本地,那么我们只要使用 docker load
tar 包的备份路径以加载该镜像。
docker load -i ~/container-backup.tar
现在,为了确保这些Docker镜像已经加载成功,我们来运行 docker images 命令。
docker images
# 镜像被加载后,用加载的镜像去运行容器。
docker run -d -p 80:80 nginx-backup