前端工程师的CI进阶之路

2019 年 11 月 28 日

前端工程师的CI进阶之路

我在很多 Github 项目中都会使用 travis-ci 来做自动化构建任务,其中的一项主要内容就是代码提交后自动执行单元测试计算测试覆盖率。但是 travis-ci 对于其它平台(例如 Gitlab)以及公司内网仓库来说都是不支持的,所以萌生了想要找另外一款 CI 工具代替 travis-ci 的想法。


1 基于 Docker 的 CI 工具 - Drone


对比了几款 CI 产品之后,我最终选择了 Drone。它是一款使用 Go 开发的开源的 CI 自动构建平台,能够单独部署,支持常见的 Git 仓库,例如 Github, Gitlab, Bitbucket 以及 Gogs 等。Drone 主要有以下几个方面吸引我:


原生 Docker 支持


环境部署算是自动化构建比较复杂的一点了,目前最优解就是使用 docker 容器化技术打包我们的环境。原生 docker 的支持让我们不需要额外的在构建脚本中增加 docker 的命令,简单配置就可以为我们带来集成 docker 所引入的一系列的好处:环境隔离、标准化镜像。并且它的每一个插件都是一个镜像,让我们的自动构建环境模块化。


新版的 Drone 将其拆分成了 Server 端和 Agent 端,Server 用来处理任务分发,Agent 用来执行自动化构建任务。简单增加 Agent 容器实例,我们就可以非常方便的横向扩容 Drone。


PipeLine AS Code


在项目根目录有一个 .drone.yml 的文件,这个是 Drone 构建脚本的配置文件,它随项目一块进行版本管理,开发者不需要额外再去维护一个配置脚本。其实现代 CI 程序都是这么做了,这个主要是相对于 Jekins 来说的。虽然 Jekins 也有插件支持,但毕竟还是需要配置。


另外就是基于 yaml 的配置文件非常简单(当然也要视需求而定),例如一个简单的构建脚本如下:


pipeline: build:   image: node:6.6.0   commands:     - npm install --silent     - npm test
复制代码


比起 Jekins 纷繁复杂的配置,Drone 几行就搞定了构建的需求。


丰富的插件支持


这个是 Drone 的插件商店,包含了常见的一些需求插件,例如:


  • 构建后发送消息:slack, telegram, line, facebook, discord, gitter, email…

  • 构建成功后发布:npm, docker, github release, google container…

  • 构建成功后部署:AWS, Kubernetes, rsync, scp, ftp…


pipeline 中简单的增加插件镜像配置就可以支持插件,例如:


pipeline: build:   image: node:6.6.0   commands:     - npm install --silent     - npm test publish:   image: plugins/npm   secret: [ npm_username, npm_pwd, npm_email ]   username: ${NPM_USERNAME}   password: ${NPM_PWD}   email: ${NPM_EMAIL}   when:     event: [ tag ] notify:   image: appleboy/drone-telegram   secret: [ telegram_token, telegram_to ]   token: ${TELEGRAM_TOKEN}   to: ${TELEGRAM_TO}
复制代码


由于 Drone 是基于 Docker 的,甚至是所有的插件也都是一个 docker 镜像。这代表着我们可以使用任何语言来开发 Drone 插件,最后只要打包成镜像就好了。


其它 CI 工具


Drone 相对于 Jekins 来说优势比较明显,复杂的配置以及需要插件支持的构建脚本 pipeline 化都是我没考虑它的原因。不过 Jekins 其丰富的插件扩展,针对于无项目的自动化构建来说还是非常不错的。Gitlab-CI 也是一款不错的工具,不过从名字上也能看出它的缺点,就是只支持 Gitlab,而且无法扩展配置文件,对于正好在使用 Gitlab 且需求不高的同学可以试试。


2 安装配置 Drone


下面我将来介绍下如何配置安装 Drone。官方推荐使用 docker-compose 来启动服务,所以首先我们要准备好 docker 和 docker-compose。


安装 docker


docker 服务提供了桌面版,服务器版和云服务版不同操作系统的多种支持,可以在官方文档中找到对应的安装方法。这里以 Debain 服务器安装为例。Docker 支持 Debian 7.7+ 以上的系统版本, 需要 3.10 版本以上的 Linux 内核环境。


首先我们添加 docker 源的密钥:


$ sudo apt-get update$ sudo apt-get install \    apt-transport-https \    ca-certificates \    curl \    gnupg2 \    software-properties-common$ curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | sudo apt-key add -
复制代码


然后我们添加 Docker 官方源并安装 Docker:


$ sudo add-apt-repository \  "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \  $(lsb_release -cs) \  stable"$ sudo apt-get update$ sudo apt-get install docker-ce
复制代码


执行以下命令确保我们已经安装正确:


$ sudo docker run hello-world
复制代码


相比 docker 的安装,docker-compose 的安装就比较简单了。它是用 python 写的一个 docker 多镜像编排启动的工具,我们可以直接使用 pip 来安装它:


$ pip install docker-compose
复制代码


Git 仓库准备


CI 可持续构建的一个大前提是基于仓库的,所有的构建第一步都需要去仓库拉取代码。Drone 支持 Github,Gitlab,Bitbucket,Gogs, Gitea, coding 多个仓库类型,其中支持 OAuth 的仓库需要在网站申请密钥。这里以 Github 为例。在 New Oauth Application 页申请密钥,其中 callback URL 需要填写为<你的 CI 地址>/authorize。申请好后记录下你的 client id 和 client secret 以便后续使用。



创建 compose 配置文件


我们新建一个空目录防止我们的配置文件文件:


$ mkdir drone$ cd drone$ vim docker-compose.yml
复制代码


在配置文件中,我们设置 docker-compose.yml 的格式为 2 号版本,定义两种服务。


  • drone-server :使用 drone/drone:0.8.2 版本镜像,将启动监听 3800 上的主 Drone 服务容器,9000 端口来开放给 Agent。我们将在容器内挂载 /etc/drone 目录,以便 drone 可以保留数据。配置服务自动重新启动,并添加一些环境变量。

  • drone-agent:使用 drone/agent:0.8.2 版本镜像,将 docker 启动句柄挂载到容器 /var/run/docker.sock 文件中,以便 drone 可以使用 docker 来执行镜像构建任务。环境变量中需要配置 drone server 的地址以及 server 的密钥,以便于 server 进行通信。


version: '2'services: drone-server:   image: drone/drone:0.8.2   ports:     - 3800:8000     - 9000   volumes:     - /etc/drone:/var/lib/drone/   restart: always   environment:     # 是否允许注册,false 后只有在 DRONE_ADMIN 变量中指定的账户才能登录     - DRONE_OPEN=true
# Drone可公开访问的地址 - DRONE_HOST=http://ci.eming.li
# 配置 Git 仓库,只能同时使用一种仓库 # Github 仓库需要配置上问申请到的 client id 和 client secret - DRONE_GITHUB=true - DRONE_GITHUB_CLIENT=xxxxx - DRONE_GITHUB_SECRET=xxxxx
# Gogs 配置仓库地址即可 - DRONE_GOGS=false - DRONE_GOGS_URL=http://git.eming.li
# Drone Server 和 Agent 的通信密钥 - DRONE_SECRET=xxxxx drone-agent: image: drone/agent:0.8.2 command: agent restart: always depends_on: - drone-server volumes: - /var/run/docker.sock:/var/run/docker.sock environment: # 配置 SERVER 地址 - DRONE_SERVER=drone-server:9000
# 配置与 SERVER 通信的密钥,需要与 Server 配置的保持一致 - DRONE_SECRET=xxxxx
复制代码


其中的通信密钥相当于 drone 的密码,最好为一个长串的随机字串防止被破解。可以在命令行中使用以下内容生成


$ LC_ALL=C </dev/urandom tr -dc A-Za-z0-9 | head -c 65 && echo
复制代码


配置文件完成之后,我们就可以使用以下命令启动服务了:


$ docker-compose -f /etc/drone/docker-compose up
复制代码


docker-compose 会自动帮我们去下载镜像并根据配置初始化容器。一切就绪之后,我们使用 http://< host>:3800 就可以访问到 drone 了。


配置 Nginx


带端口访问总是让人不爽,老方法我们配置下 Nginx 做下反向代理就可以了,以下是具体的 nginx 配置文件示例:


map $http_upgrade $connection_upgrade {   default upgrade;   ''      close;}server {   listen 80;   server_name ci.eming.li;   set $drone_port 3800;   location / {       proxy_set_header X-Forwarded-For $remote_addr;       proxy_set_header X-Forwarded-Proto $scheme;       proxy_set_header Host $http_host;       proxy_pass http://127.0.0.1:$drone_port;       proxy_redirect off;       proxy_http_version 1.1;       proxy_buffering off;       chunked_transfer_encoding off;   }   location ~* /ws {       proxy_pass http://127.0.0.1:$drone_port;       proxy_http_version 1.1;       proxy_set_header Upgrade $http_upgrade;       proxy_set_header Connection "upgrade";       proxy_read_timeout 86400;       proxy_set_header X-Forwarded-For $remote_addr;       proxy_set_header X-Forwarded-Proto $scheme;       proxy_set_header Host $http_host;   }}
复制代码


修改 server_name 和 $drone_port 为正确的值即可,最后记得别忘了重启 Nginx 服务。这样我们就能直接使用 http://ci.eming.li 来访问 drone 了。


进程守护


刚才使用 docker-compose up 命令启动如果退出终端了之后服务就会停止,所以我们需要后台执行。我们可以直接使用 nohup 方法启动:


$ nohup docker-compose -f docker-compose.yml up &
复制代码


除了使用 nohup 之外,我们还可以使用 systemctl 来启动进程。我们创建一个 drone.service 服务文件:


$ vim /etc/systemd/system/drone.service
复制代码


复制以下内容:


[Unit]Description=Drone serverAfter=docker.service nginx.service[Service]Restart=alwaysExecStart=/usr/local/bin/docker-compose -f /etc/drone/docker-compose.yml upExecStop=/usr/local/bin/docker-compose -f /etc/drone/docker-compose.yml stop[Install]WantedBy=multi-user.target
复制代码


第一部分告诉 systemd 在 Docker 和 Nginx 可用之后启动此服务。 第二部分告诉 init 系统在发生故障时自动重新启动服务。 然后,它使用 Docker Compose 和我们之前创建的配置文件定义启动和停止 Drone 服务的命令。 最后,最后一节定义了如何使服务在启动时启动。完成后保存文件并使用如下命令启动服务:


$ systemctl start drone
复制代码


使用如下命令可以查看服务启动状态, 如状态显示为 active (running)则服务运行正常。


$ systemctl status drone
复制代码


这样我们的安装过程就结束了,访问 Drone,使用对应仓库的账户登录。如果是 Github 的话会使用 OAuth 连接到 Github 进行授权申请,如果是 Gogs 则需要输入 Gogs 的账号和密码。


3 User Guide


接下来我将来介绍重头戏——如何在项目中使用 Drone 完成自动化构建任务。首先我们要在 drone 中开启自动构建,实际上它会在对象的 Git 仓库中添加 webhooks 方便当开发者执行对应 git 操作后触发自动构建任务。



.drone.yml


在《基于 DOCKER 的 CI 工具—DRONE》一文中我们有说过它的每一个构建插件都是镜像,能够模块化的配置我们的构建任务。将构建任务理解成是一条管道,项目流经管道做各种具体的任务。以下是一个简单的管道流程,代码克隆下来后执行安装并执行测试,完成后使用使用 Telegram 通知用户。



通过编辑放置在项目的根目录下的 .drone.yml 文件,我们可以对构建流进行配置。下面就是上图构建流程的配置:


pipeline: build:   image: node:6.6.0   commands:     - npm install --silent     - npm test telegram:   image: appleboy/drone-telegram   token: XXX:XXXXX   to: 337635385   message: >     {{#success build.status}}     主人,{{repo.owner}}/{{repo.name}}第{{build.number}}次构建成功!     {{else}}     主人,{{repo.owner}}/{{repo.name}}第{{build.number}}次构建失败了,快来修理下吧。     {{/success}}
复制代码


在配置文件中我们总共定义了两个管道任务:


  • build: 我们为 build 任务指定了 node:6.6.0 镜像,并在镜像中执行 npm install --silent 命令安装依赖。最后执行 npm test 来运行单元测试脚本。

  • telegram: 我们为 telegram 任务指定了 appleboy/brone-telegram 镜像,并定义了一些镜像需要的环境参数。包括 telegram 的机器人 token,指定了消息的发送对象 to 以及消息的模板。


将其保存提交后就能正常触发任务并使用 telegram 接收消息了。



telegram


telegram 插件需要设置 bot token,没有的需要先关注 @BotFather 并输入 /netbot 命令创建一个 bot,这样你就能获取到 token 了。


而 to 参数需要配置的是发送用户的 ID,这个比较坑爹了。最开始以为是 username,输入之后一直没有反应,后来才反应过来可能是唯一 ID。不过 telegram ID 的获取比较困难,有人为此特地做了一个机器人。关注 @userinfobot 后和它发个消息它就会告诉你你自己的 ID 啦。这里有个小技巧是把别人的消息转发给它,它会给出对象的 ID。


关于 telegram 插件更多的配置可以参考:http://plugins.drone.io/appleboy/drone-telegram/


Secrets


由于我们将 token 直接写在了 .drone.yml 的配置文件中,项目其它人也都可见,这会带来一些潜在的安全问题。为此我们可以使用 drone 的 secrets 功能。我们小改一下配置文件:


telegram:   image: appleboy/drone-telegram   secrets: [ telegram_token, telegram_to ]   token: ${TELEGRAM_TOKEN}   to: ${TELEGRAM_TO}
复制代码


同时我们在网站后台添加两个名为 telegram_token 和 telegram_to 的密钥:



保存后重新执行构建任务即可,这样我们就做到了隐藏敏感信息的目的。当然其实这么做也有缺陷,因为在网站后台添加的密钥是对管道中所有的构建插件全局共享的,如果开发者在管道中增加其它插件并 echo 或者其它操作,一样也是能获取到密钥的。为此我们就需要使用 drone-cli 命令来添加密钥,它支持指定密钥的作用域镜像。


$ drone secrets add \   --repository=lizheming/qalarm \   --name=telegrame_to \   --value=337635385 \   --image=appleboy/telegram
复制代码


4 Troubleshooting


构建任务无故退出


实际使用过程中我发现我的构建任务总是莫名其妙就失败,提示我被 killed 掉,并显示 exit code 137。最开始我一直在查 137 退出码,发现就是 kill -9 的退出,不明所以。后来偶然尝试以 docker killed 为关键词搜索,才发现原来是内存爆了 docker 执行 OOM 把容器杀掉了。后来按照作者建议给机器加到 2G 的内存就没问题了。



5 总结


基本上 Drone 的流程是我想要的 CI 模式了。不过人无完人,drone 也有一些不足,主要是:


  • 不同版本的文档太多,老文档的一些功能都不支持了。

  • 社区还不够发展,找个问题比较困难。不过好在有个官方论坛,作者回复倒是挺勤快的。

  • 前端界面比较简单,很多功能还是得依靠命令行。


不过这些问题都不影响我对它的看法,相信它会越来越好。


本文转载自公众号 360 云计算(ID:hulktalk)。


原文链接:


https://mp.weixin.qq.com/s/jggTjEuqiaVqYWHNEPF3Ag


2019 年 11 月 28 日 16:191183

评论

发布
暂无评论
发现更多内容

大型互联网应用系统浅析

飞雪

架构师训练营第四周作业

努力努力再努力m

极客大学架构师训练营

<<架构师训练营>>第四周作业

0x12FD16B

一个典型的大型互联网应用系统使用了哪些技术

L001

极客大学架构师训练营

架构学习第四周作业

云峰

架构师训练营第四周作业

极客大学架构师训练营

Week 04- 作业二:学习总结

dean

极客大学架构师训练营

第四周学习总结

iHai

极客大学架构师训练营

第04周 设计系统架构 命题作业

Jaye

架构师训练营第四周 - 作业

人世间

极客大学架构师训练营

架构师训练营 第四周 学习总结

RZC

软件测试缺陷等级划分准则

海浪豆豆

软件测试

【架构师训练营】第 4周总结

花生无翼

架构学习第四周总结

云峰

第四周总结

changtai

极客大学架构师训练营

面向对象学习

一叶知秋

系统架构

eazonshaw

极客大学架构师训练营

第四周学习总结

麻辣

<<架构师训练营>>第四周总结

0x12FD16B

第四周作业

changtai

极客大学架构师训练营

大型互联网应用架构中的主要技术,与其对应的业务问题

Ph0rse

互联网系统的问题与方案 - 第四周作业

X﹏X

架构师训练营第 0 期第四周作业

无名氏

架构师训练营第四周课程总结

狂奔嘀兔纸

极客大学架构师训练营

第四周总结

AIK

大型互联网应用解决问题的技术方案和手段

AIK

从软件架构演进“看”做好事情的三条边

林昱榕

学习 架构模式 极客大学架构师训练营 架构演进 三条边

第4周作业

Week 04 学习总结

Jeremy

大型网站架构演化历程

stars

Week4-总结

龙7

前端工程师的CI进阶之路-InfoQ