上一篇文章讲了如何搭建高性能的 Composer 服务,本篇来聊聊如何搭配 CI 进行使用,让研发效率有一定的保障。

写在前面

本文以 GitLab Runner 中最简单通用的 shell模式为例,其他 CI 系统类似,酌情修改脚本即可。考虑到持续集成过程中需要进行资源隔离,我们使用工具容器作为持续集成环境。

定义阶段

在使用 CI 前,我们需要先拆分阶段,一般来说,基于 Composer 的项目存在三个阶段:

  • 更新代码:composer install 阶段
  • 部署代码:sync release 阶段
  • 重启服务:reload/restart service 阶段

如果写成 yml 配置,可以这样描述:

定义完毕阶段后,需要详细描述每阶段做的事情。

先从 update 阶段开始说起。

更新代码

为了方便描述,项目结构简单定义为下面这样。

更新代码最简单的方案便是进入项目目录,执行 composer i ,等待项目安装完毕了。然而这样会导致两个问题:

  • CI 构建机需要安装并维护 composer,构建机器越多,管理成本越高
  • 不同项目必须使用同一份配置,构建机的缓存不能够独立管理

所以如果使用固定配置构建的工具镜像,搭配“即用即丢”模式来用做构建执行环境,上面的问题就迎刃而解了。

使用上面的方案,搭配 部署令牌 ,除了解决常规依赖的获取外,也不难解决下面这种类型的软件包的获取。

一切似乎很美好,但是如果涉及到下面这类仓库,上面的方案就失灵了。

使用 Git 获取 SSH 协议的仓库数据,需要配置 SSH KEY。解决的方案也不难,为上面的 docker 命令加一些额外的参数:

为了追求简单优雅,我们可以将上面的代码进一步优化,改为用 docker-compose ,像是下面这样。

你会发现这里多映射了一个 known_hosts 文件。由于 SSH 设计上防止中间人攻击,需要验证服务端的指纹,所以,我们需要将服务端指纹保存下来,否则当 composer 容器访问代码仓库服务器的时候,会因为下面的错误而中止仓库 Clone 。

这里通过 ssh-keyscan 来生成签名文件:ssh-keyscan gitlab.lab.com > known_hosts ,文件内容会类似下面:

使用 compose 来描述内容,最后的 CI 脚本可以简化为:

部署代码

部署代码可以做的简单些,比如直接用 rsync ,可以开启 - I 选项保证一致性,或者老老实实 ansible ,如果追求一步到位,把代码打包镜像分发也没有问题。先以 rsync 为例,ansible 足够再写一篇啦。

生成一枚 SSH 密钥用于部署,然后项目仓库/代码主机中配置信任该密钥,rsync 使用的话,手段就灵活多了:

  • 使用 CI 变量储存 / 使用配置服务API获取
  • 预先分配 KEY 到 CI 程序用户 .ssh 目录中

和上面一样同样考虑将工具打包为镜像使用:

上面的 Dockerfile 演示了如何构建一个完全独立于系统,大小只有 10MB 的 Rsync 工具包。

使用命令也很简单,和传统的 Rsync 别无二致,只是前面加上了 docker run 命令:

同样的,如果你愿意的话,这个命令也可以重写为更优雅一些的 compose 脚本。

此外,如果你想部署多台服务器,想将上一步的“过程产物”共享,需要定义缓存目录,本例中可以这样配置:

重启服务

重启/重载服务其实没有什么难的,如果业务需要平滑重启,可以将脚本配置成串行任务:

  • 重启服务第一批
  • 重启服务第二批

前端不论是 Nginx 还是负载均衡服务,会根据服务可用情况帮助你平滑更新线上应用。

此外,如果你的服务部署更新后,需要触发其他项目的构建过程,可以使用 GitLab API:pipeline trigger,调用手段很多,最简单的莫过于使用 curl 进行调用,放在当前项目 CI 脚本的合适位置即可:

上面的变量可以在 GitLab CI 帮助文档和项目配置中获得,在此就不赘述了。

最后

这里给出一套简单的参考配置,里面演示了如何使用不同的命令来进行项目部署:

先写到这里吧。

—EOF