容器环境下的持续集成最佳实践

2019年12月14日 阅读数:26
这篇文章主要向大家介绍容器环境下的持续集成最佳实践,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

640?wx_fmt=jpeg

本文结合 Drone + GitFlow + Kubernetes 介绍一些容器环境下持续集成、持续发布方面的实践经验。文章将展现从 Hello World 到单人单分支手动发布到团队多分支 GitFlow 工做流再到团队多分支 semantic-release 语义化发布最后到通知 Kubernetes 全自动发布,如何从零开始一步一步搭建 CI 将团队开发、测试、发布的流程所有自动化的过程,最终能让开发人员只须要认真提交代码就能够完成平常的全部 DevOps 工做。云原生(Cloud Native)是伴随的容器技术发展出现的的一个词,最先出自 Pivotal 公司(即开发了 Spring 的公司)的一本技术小册子 Migrating to Cloud-Native Application Architectures, 其中定义了云原生应用应当具有的一些特质,如无状态、可持续交付、微服务化等。随后云原生概念被广为引用,并围绕这一律念由数家大厂牵头,成立了 CNCF 基金会来推动云原生相关技术的发展,主要投资并孵化云原生生态内的若干项目,包括了如 Kubernetes / etcd / CoreDNS 等耳熟能详的重要项目。而这张大大的云原生版图仍然在不断的扩展和完善。
从我的理解来讲,传统的应用因为年代久远,更多会考虑单机部署、进程间通讯等典型的“单机问题”,虽然也能工做在容器下,但因为历史包袱等缘由,架构上已经很难作出大的调整。而“云原生”的应用大都是从一开始就为容器而准备,不多考虑在单机环境下使用,有些甚至没法脱离容器环境工做;考虑的场景少,从而更轻量,迭代更快。好比 etcd 之于 ZooKeeper, Traefik 之于 Nginx 等,相信只要在容器环境下实现一次一样的功能,就能强烈的体会到云原生应用所特有的便捷之处。
在 CNCF 的版图下,持续集成与持续交付(Continuous Integration & Delivery)板块一直缺乏一个钦定的主角,虽然也不乏 Travis CI、GitLab、Jenkins 这样的知名项目,但最能给人云原生应用感受的,应该仍是 Drone 这个项目,本文将围绕 Drone 结合 GitFlow 及 Kubernetes 介绍一些容器环境下持续集成、持续发布(CI/CD)方面的实践经验。
主流 CI/CD 应用对比

640?wx_fmt=png


以前我也介绍过基于 Travis CI 的一些持续集成实践[1]。后来通过一些比较和调研,最终选择了 Drone 做为主力 CI 工具。截止本文,团队已经使用 Drone 有 2 年多的时间,从 v0.6 一路用到如今即将发布的 v1.0,虽然也踩了很多坑,但总的来讲 Drone 仍是能够知足大部分需求,并以不错的势头在完善和发展的。
下面这张表总结了主流的几个 CI/CD 应用的特色: 
640?wx_fmt=jpeg
Travis CI 和 CircleCI 是目前占有率最高的两个公有云 CI,易用性上相差无几,只是收费方式有差别。因为不支持私有部署,若是并行的任务量一大,按进程收费其实并不划算;并且因为服务器位置的缘由,若是推送镜像到国内,速度很不理想。
GitLab CI 虽然好用,但和 GitLab 是深度绑定的,咱们的代码托管在 GitHub,总体迁移代码库的成本太大,放弃。
Jenkins 做为老牌劲旅,也是目前市场占有率最高的 CI,几乎能够覆盖全部 CI 的使用场景,因为使用 Java 编写,配置文件使用 Groovy 语法,很是适合 Java 为主语言的团队。Jenkins 显然是能够知足咱们须要的,只是团队并不是 Java 为主,又已经习惯了使用 YAML 书写 CI 配置,抱着尝鲜的心态,将 Jenkins 做为了保底的选择。
综上,最终选择 Drone 的结论也就不可贵出了,Drone 即开源,又能够私有化部署,同时做为云原生应用,官方提供了针对 Docker、Docker Swarm、Kubernetes 等多种容器场景下的部署方案,针对不一样容器场景还有特别优化,好比在 Docker Swarm 下 Drone 是以 Agent 方式运行 CI 任务的,而在 Kubernetes 下则经过建立 Kubernetes Job 来实现,显然充分利用了容器的优点所在,这也是 Drone 优于其余 CI 应用之处。我的还以为 Drone 的语法是全部 CI 中最容易理解和掌握的,因为 Drone 每个步骤都是运行一个 Docker 容器,本地模拟或调试也很是容易。
一句话概况 Drone,能够将其看作是能够支持私有化部署的开源版 CircleCI,而且目前仍然没有看到有其余主打这个定位的 CI 工具,所以我的认为 Drone 是 CI/CD 方面云原生应用头把交椅的有力竞争者。
容器环境下一次规范的发布应该包含哪些内容

640?wx_fmt=png


技术选型完成后,我想首先演示一下最终的成果,但愿能直观的体现出 CI 对自动化效率起到的提高,不过这就涉及到一个问题:在容器环境下,一次发布应该包含哪些内容,其中有哪些部分是能够被 CI 自动化完成的。这个问题虽然每家公司各不相同,不过按经验来讲,容器环境下一次版本发布一般包含这样一个 Checklist:
  • 代码的下载构建及编译html

  • 运行单元测试,生成单元测试报告及覆盖率报告等node

  • 在测试环境对当前版本进行测试android

  • 为待发布的代码打上版本号nginx

  • 编写 ChangeLog 说明当前版本所涉及的修改git

  • 构建 Docker 镜像github

  • 将 Docker 镜像推送到镜像仓库docker

  • 在预发布环境测试当前版本数据库

  • 正式发布到生产环境npm


看上去很繁琐对吗,若是每次发布都须要人工去处理上述的全部内容,不只容易出错,并且也没法应对 DevOps 时代一天至少数次的发布频率,那么下面就来使用 CI 来解决全部问题吧。
CI 流程演示

640?wx_fmt=png


为了对 CI 流程有最直观的认识,我建立了一个精简版的 GitHub 项目 AlloVince/drone-ci-demo[2] 来演示完整的流程,同时项目对应的 CI 地址是 cloud.drone.io/AlloVince/drone-ci-demo,项目自动构建的 Docker 镜像会推送到 docker registry 的 allovince/drone-ci-demo。为了方便说明,假设这个项目的核心文件只有 index.html 一个静态页面。
单人开发模式
目前这个项目背后的 CI 都已经配置部署好,假设我是这个项目的惟一开发人员,如何开发一个新功能并发布新版本呢?
  • Clone 项目到本地, 修改项目代码, 如将 Hello World 改成 Hello World V2。json

  • git add .,而后书写符合约定的 Commit 并提交代码, git commit -m "feature: hello world v2”。

  • 推送代码到代码库git push,等待数分钟后,开发人员会看到单元测试结果,GitHub 仓库会产生一次新版本的 Release,Release 内容为当前版本的 ChangeLog, 同时线上已经完成了新功能的发布。


虽然在开发者看来,一次发布简单到只需 3 个指令,但背后通过了以下的若干次交互,这是一次发布实际产生交互的时序图,具体每一个环节如何工做将在后文中详细说明。 
640?wx_fmt=jpeg
多人开发模式
一个项目通常不止一个开发人员,好比我是新加入这个项目的成员,在这个 Demo 中应该如何上线新功能呢?一样很是简单:
  1. Clone 项目到本地,建立一个分支来完成新功能的开发, git checkout -b feature/hello-world-v3。在这个分支修改一些代码,好比将Hello World V2修改成Hello World V3

  2. git add .,书写符合规范的 Commit 并提交代码, git commit -m "feature: hello world v3”

  3. 将代码推送到代码库的对应分支, git push origin feature/hello-world

  4. 若是功能已经开发完毕,能够向 Master 分支发起一个 Pull Request,并让项目的负责人 Code Review

  5. Review 经过后,项目负责人将分支合并入主干,GitHub 仓库会产生一次新版本的 Release,同时线上已经完成了新功能的发布。


这个流程相比单人开发来多了 2 个环节,很适用于小团队合做,不只强制加入了 Code Review 把控代码质量,同时也避免新人的不规范行为对发布带来影响。实际项目中,能够在 GitHub 的设置界面对 Master 分支设置写入保护,这样就从根本上杜绝了误操做的可能。固然若是团队中都是熟手,就无需如此谨慎,每一个人均可以负责 PR 的合并,从而进一步提高效率。  640?wx_fmt=jpeg
GitFlow 开发模式
在更大的项目中,参与的角色更多,通常会有开发、测试、运维几种角色的划分;还会划分出开发环境、测试环境、预发布环境、生产环境等用于代码的验证和测试;同时还会有多个功能会在同一时间并行开发。可想而知 CI 的流程也会进一步复杂。
能比较好应对这种复杂性的,首选 GitFlow 工做流, 即经过并行两个长期分支的方式规范代码的提交。而若是使用了 GitHub,因为有很是好用的 Pull Request 功能,能够将 GitFlow 进行必定程度的简化,最终有这样的工做流: 
640?wx_fmt=jpeg
  • 以 dev 为主开发分支,Master 为发布分支

  • 开发人员始终从 dev 建立本身的分支,如 feature-a

  • feature-a 开发完毕后建立 PR 到 dev 分支,并进行 code review

  • review 后 feature-a 的新功能被合并入 dev,若有多个并行功能亦然

  • 待当前开发周期内全部功能都合并入 dev 后,从 dev 建立 PR 到 master

  • dev 合并入 Master,并建立一个新的 Release


上述是从 Git 分支角度看代码仓库发生的变化,实际在开发人员视角里,工做流程是怎样的呢。假设我是项目的一名开发人员,今天开始一期新功能的开发:
  1. Clone 项目到本地,git checkout dev。从 dev 建立一个分支来完成新功能的开发, git checkout -b feature/feature-a。在这个分支修改一些代码,好比将Hello World V3修改成Hello World Feature A

  2. git add .,书写符合规范的 Commit 并提交代码, git commit -m "feature: hello world feature A"

  3. 将代码推送到代码库的对应分支, git push origin feature/feature-a:feature/feature-a

  4. 因为分支是以 feature/ 命名的,所以 CI 会运行单元测试,并自动构建一个当前分支的镜像,发布到测试环境,并自动配置一个当前分支的域名如 test-featue-a.avnpc.com

  5. 联系产品及测试同窗在测试环境验证并完善新功能

  6. 功能经过验收后发起 PR 到 dev 分支,由 Leader 进行 code review

  7. Code Review 经过后,Leader 合并当前 PR,此时 CI 会运行单元测试,构建镜像,并发布到测试环境

  8. 此时 dev 分支有可能已经积累了若干个功能,能够访问测试环境对应 dev 分支的域名,如 test.avnpc.com,进行集成测试。

  9. 集成测试完成后,由运维同窗从 Dev 发起一个 PR 到 Master 分支,此时会 CI 会运行单元测试,构建镜像,并发布到预发布环境

  10. 测试人员在预发布环境下再次验证功能,团队作上线前的其余准备工做

    运维同窗合并 PR,CI 将为本次发布的代码及镜像自动打上版本号并书写 ChangeLog,同时发布到生产环境。

  11. 由此就完成了上文中 Checklist 所需的全部工做。虽然描述起来看似冗长,但不难发现实际做为开发人员,并无任何复杂的操做,流程化的部分所有由 CI 完成,开发人员只须要关注本身的核心任务:按照工做流规范,写好代码,写好 Commit,提交代码便可。


接下来将介绍这个以 CI 为核心的工做流,是如何一步步搭建的。
Step by Step 构建 CI 工做流

640?wx_fmt=png


Step.0:基于 Kubernetes 部署 Drone v1.0.0
以 GitHub 为例,截止本文完成时间(2019 年 3 月 28 日), Drone 刚刚发布了第一个正式版本 v1.0.0。官方文档已经提供了分别基于 Docker、Kubernetes 的 Drone 部署说明,不过比较简略,所以这里给出一个相对完整的配置文件。
首先须要在 GitHub 建立一个 Auth App,用于 repo 的访问受权。应用建立好以后,会获得 Client ID 和 Client Secret 。同时 Authorization callback URL 应填写 Drone 服务对应域名下的 /login,如 https://ci.avnpc.com/login。
Drone 支持 SQLite、MySQL、Postgres、S3 等多种后端存储,主要用于记录 build logs 等文本信息,这些信息并非特别重要,且咱们的 CI 有可能作迁移,所以我的更推荐使用 SQLite。
而在 Kubernetes 环境下,SQLite 更适合用挂载 NAS 的方式供节点使用,所以首先将存储的部分独立为文件 drone-pvc.yml,能够根据实际状况配置 nfs.path 和 nfs.server:
 
 
  1. kubectl apply -f drone-pvc.yaml


Drone 的配置主要涉及两个镜像:
  • drone/kubernetes-secrets 加密数据服务,用于读取 Kubernetes 的 secrets

  • drone/drone:1.0.0-rc.6 就是 Drone 的 server 端,因为在 Kubernetes 下 Drone 利用了 Job 机制,所以不须要部署 Agent。


这部分配置较长,能够直接参考示例 drone.yaml[3]。
主要涉及到的配置项包括:

  • Drone/kubernetes-secrets 镜像中


    • SECRET_KEY:数据加密传输所用的 key,可使用 openssl rand -hex 16 生成一个


  • Drone/Drone镜像中


    • DRONEKUBERNETESENABLED:开启 Kubernetes 模式

    • DRONEKUBERNETESNAMESPACE:Drone 所使用的 Namespace, 这里使用 default

    • DRONEGITHUBSERVER:GitHub 服务器地址,通常为 https://github.com

    • DRONEGITHUBCLIENT_ID:上文建立 GitHub Auth App 获得的 Client ID

    • DRONEGITHUBCLIENT_SECRET:上文建立 GitHub Auth App 获得的 Client Secret

    • DRONESERVERHOST:Drone 服务所使用的域名

    • DRONESERVERPROTO:http 或 https

    • DRONEDATABASEDRIVER:Drone 使用的数据库类型,这里为 SQLite3

    • DRONEDATABASEDATASOURCE:这里为 SQLite 数据库的存放路径

    • DRONESECRETSECRET:对应上文的 SECRET_KEY

    • DRONESECRETENDPOINT:加密数据服务的地址,这里经过 Kubernetes Service 暴露,无需修改


最后部署便可:
 
 
  1. kubectl apply -f drone.yaml


部署后首次登陆 Drone 就会跳转到 GitHub Auth App 进行受权,受权完毕后能够看到全部能读写的 Repo,选择须要开启 CI 的 Repo,点击 ACTIVATE 便可。 若是开启成功,在 GitHub Repo 的 Settings > Webhooks 下能够看到 Drone 的回调地址。
Step.1:Hello World for Drone
在正式开始搭建工做流以前,首先能够测试一下 Drone 是否可用。Drone 默认的配置文件是 .drone.yml, 在须要 CI 的 repo 根目录下建立.drone.yml, 内容以下,提交并git push到代码仓库便可触发 Drone 执行 CI。
 
 
  1. kind: pipeline

  2. name: deploy


  3. steps:

  4. - name: hello-world

  5. image: docker

  6. commands:

  7. - echo "hello world"


Drone v1 的语法主要参考的 Kubernetes 的语法,很是直观,无需阅读文档也能够知道,咱们首先定义了一个管道(Pipeline),管道由若干步骤(step)组成,Drone 的每一个步骤是都基于容器实现的,所以 Step 的语法就回到了咱们熟悉的 Docker,一个 Step 会拉取 image 定义的镜像,而后运行该镜像,并顺序执行 commands 定义的指令。
在上例中,Drone 首先 clone git repo 代码到本地,而后根据 .drone.yml 所定义的,拉取 Docker 的官方镜像,而后运行该进行并挂载 git repo 的代码到 /drone/src 目录。
在 Drone 的界面中,也能够清楚的看到这一过程。  640?wx_fmt=png
本阶段对应:
  • 代码部分:https://github.com/AlloVince/drone-ci-demo/tree/hello-world

  • Drone 构建记录:https://cloud.drone.io/AlloVince/drone-ci-demo/1

  • Docker 镜像:无


Step.2:单人工做流,自动化单元测试与 Docker 镜像构建
有了 Hello World 的基础,接下来咱们尝试将这个工做流进行扩充。
为了方便说明,这里假设项目语言为 JavaScript,项目内新增了 test/index.js 文件用于模拟单元测试,通常在 CI 中,只要程序的返回值为 0,即表明运行成功。这个文件中咱们仅仅输出一行 Log Unit test passed用于模拟单元测试经过。
咱们但愿将代码打包成 Docker 镜像,根目录下增长了 Dockerfile 文件,这里直接使用 Nginx 的官方镜像,构建过程只有 1 行 COPY index.html /usr/share/nginx/html/, 这样镜像运行后能够经过 http 请求看到 index.html 的内容。
至此咱们能够将工做流改进为:
  • 当 Master 分支接收到 push 后,运行单元测试

  • 当 GitHub 发布一次 Release, 构建 Docker 镜像,并推送到镜像仓库


对应的 Drone 配置文件以下:
 
 
  1. kind: pipeline

  2. name: deploy


  3. steps:

  4. - name: unit-test

  5. image: node:10

  6. commands:

  7. - node test/index.js

  8. when:

  9. branch: master

  10. event: push

  11. - name: build-image

  12. image: plugins/docker

  13. settings:

  14. repo: allovince/drone-ci-demo

  15. username: allovince

  16. password:

  17. from_secret: DOCKER_PASSWORD

  18. auto_tag: true

  19. when:

  20. event: tag


虽然比 Hello World 复杂了一些,可是可读性仍然很好,配置文件中出现了几个新概念:
Step 运行条件, 即 when 部分,上例中展现了当代码分支为 Master,且收到一个 push;以及当代码被标记 tag 这两种状况。Drone 还支持 repo、运行结果等不少其余条件,能够参考 Drone Conditions[4] 文档。
Plugin 插件,上例中用于构建和推送镜像的是 plugins/docker 这个 Plugin, 一个 Plugin 本质上仍然是一个 Docker 镜像,只是按照 Drone 的规范接受特定的输入,并完成特定的操做。因此彻底能够将 Plugin 看作一个没法更改 command 的 Docker 镜像。
Docker 这个 Plugin 由 Drone 官方提供,用于 Docker 镜像的构建和推送,具体的用法能够查看 Docker 插件的文档[5]。例子中演示的是将镜像推送到私有仓库,若是不作特殊配置,镜像将被推送到 Docker 的官方仓库。
此外 Docker 插件还有一个很方便的功能,若是设置 auto_tag: true,将根据代码的版本号自动规划 Docker 镜像的标签,如代码版本为1.0.0,将为 Docker 镜像打三个标签 1, 1.0, 1.0.0。若是代码版本号不能被解析,则镜像标签为 latest。
目前 Drone 的插件已经有不少,能够覆盖主流的云服务商和常见的工做流,而且本身制做插件的成本也不高。
Secret 加密数据,镜像仓库的用户名和密码都属于敏感信息,所以可使用 fromsecret 获取加密数据。一条加密数据就是一个 key / value 对,如上例中的 DOCKERPASSWORD 就是咱们本身定义的加密数据 key。即使加密数据在 log 中被打印,UI 也只能看到 *。加密数据的 value 须要提早保存好,保存的方式有 3 种:
  • 经过 Drone UI 界面中, repo -> Settings -> Secrets 添加,所添加的加密数据将保存在 Drone 的数据库中,仅能在当前 repo 中使用。

  • 经过 Drone cli 加密后保存在 .drone.yml 文件中, 使用范围仅限 yaml 文件内

  • 经过 Kubernetes 保存为 Kubernetes Secret,称为 External Secrets,全部的 repo 均可以共享。若是是团队使用的话,这种保存方式显然是最方便的,但也要注意安全问题,所以 External Secrets 还支持 repo 级别的权限管理, 能够只让有当前 repo 写入权限的人才能使用对应 secret。


这个阶段对应:
  • 代码仓库:https://github.com/AlloVince/drone-ci-demo/tree/single-person

  • push 时触发的 Drone CI:https://cloud.drone.io/AlloVince/drone-ci-demo/4

  • Release 时触发的 Drone CI:https://cloud.drone.io/AlloVince/drone-ci-demo/5

  • Release 后 CI 构建的 Docker 镜像:allovince/drone-ci-demo:latest


Step.3:GitFlow 多分支团队工做流
上面的工做流已经基本能够应付单人的开发了,而在团队开发时,这个工做流还须要一些扩展。不须要引入 Drone 的新功能,只须要在上文基础上根据分支作一点调整便可。
首先保证单元测试位于 steps 的第一位,而且限定团队工做的分支,在 push 和 pull_request 时,都能触发单元测试。
 
 
  1. - name: unit-test

  2. image: node:10

  3. commands:

  4. - node test/index.js

  5. when:

  6. branch:

  7. include:

  8. - feature/*

  9. - master

  10. - dev

  11. event:

  12. include:

  13. - push

  14. - pull_request


而后根据 GitFlow 的流程对于不一样的分支构建 Docker 镜像并打上特定标签,以 feature 分支为例,下面的配置约定了当分支名知足 feature/*,并收到 push 时,会构建 Docker 镜像并打标签,标签名称为当前分支名去掉 feature/。如分支 feature/readme, 对应 Docker 镜像为 allovince/drone-ci-demo:readme,考虑到 feature 分支通常都出于开发阶段,所以新的镜像会覆盖旧的。配置以下:
 
 
  1. - name: build-branch-image

  2. image: plugins/docker

  3. settings:

  4. repo: allovince/drone-ci-demo

  5. username: allovince

  6. password:

  7. from_secret: DOCKER_PASSWORD

  8. tag:

  9. - ${DRONE_BRANCH##feature/}

  10. when:

  11. branch: feature/*

  12. event: push


镜像的 Tag 处再也不使用自动方式,其中 DRONE_BRANCH 是 Drone 的内置环境变量(Environment),对应当前的分支名。##feature/ 是执行了一个字符串的替换操做(Substitution)。更多的环境变量和字符串操做均可以在文档中找到。
以此类推,能够查看这个阶段的完整 .drone.yml ,此时咱们的工做流示例以下:
  • 团队成员从 dev 分支 checkout 本身的分支 feature/readme

  • 向feature/readme提交代码并 push, CI 运行单元测试,构建镜像allovince/drone-ci-demo:readme

  • 功能开发完成后,团队成员向 dev 分支 发起 pull request , CI 运行单元测试

  • 团队其余成员 merge pull request, CI 运行单元测试,构建镜像allovince/drone-ci-demo:test

  • 运维人员从 dev 向 master 发起 pull request,CI 运行单元测试,并构建镜像allovince/drone-ci-demo:latest

  • 运维人员 merge pull request, 并 Release 新版本 pre-0.0.2, CI 构建镜像allovince/drone-ci-demo:pre-0.0.2


可能细心的同窗会发现 dev -> Master 的 pull request 时,构建镜像失败了,这是因为 Drone 出于安全考虑限制了在 pull request 时默认没法读取加密数据,所以没法获得 Docker Registry 密码。若是是私有部署的话,能够在 Repo Settings 中勾选 Allow Pull Requests,此处就能够构建成功。
Step.4:语义化发布
上面基本完成了一个支持团队协做的半自动 CI 工做流,若是不是特别苛刻的话,彻底能够用上面的工做流开始干活了。
不过基于这个工做流工做一段时间,会发现仍然存在痛点,那就是每次发布都要想一个版本号,写 ChangeLog,而且人工去 Release。
标记版本号涉及到上线后的回滚,追溯等一系列问题,应该是一项严肃的工做,其实如何标记早已有比较好的方案,即语义化版本。在这个方案中,版本号一共有 3 位,形如 1.0.0,分别表明:
  • 主版本号:当你作了不兼容的 API 修改

  • 次版本号:当你作了向下兼容的功能性新增

  • 修订号:当你作了向下兼容的问题修正


虽然有了这个指导意见,但并无很方便的解决实际问题,每次发布要搞清楚代码的修改究竟是不是向下兼容的,有哪些新的功能等,仍然要花费不少时间。
而语义化发布(Semantic Release)就能很好的解决这些问题。
语义化发布的原理很简单,就是让每一次 Commit 所附带的 Message 格式遵照必定规范,保证每次提交格式一致且都是能够被解析的,那么进行 Release 时,只要统计一下距离上次 Release 全部的提交,就分析出本次提交作了何种程度的改动,并能够自动生成版本号、自动生成 ChangeLog 等。
语义化发布中,Commit 所遵照的规范称为约定式提交(Conventional Commits)。好比 Node.js、 Angular、Electron 等知名项目都在使用这套规范。
语义化发布首先将 Commit 进行分类,经常使用的分类(Type)有:
  • feat:新功能

  • fix:BUG 修复

  • docs:文档变动

  • style:文字格式修改

  • refactor:代码重构

  • perf:性能改进

  • test:测试代码

  • chore:工具自动生成


每一个 Commit 能够对应一个做用域(Scope),在一个项目中做用域通常能够指不一样的模块。
当 Commit 内容较多时,能够追加正文和脚注,若是正文起始为BREAKING CHANGE,表明这是一个破坏性变动。
如下都是符合规范的 Commit:
 
 
  1. feat:增长重置密码功能

 
 
  1. fix(邮件模块):修复邮件发送延迟BUG

 
 
  1. featAPI):API重构


  2. BREAKING CHANGEAPI v3上线,API v1中止支持


有了这些规范的 Commit,版本号如何变化就很容易肯定了,目前语义化发布默认的规则以下:
 
 
  1. Commit 版本号变动

  2. BREAKING CHANGE 主版本号

  3. feat 次版本号

  4. fix / perf 修订号


所以在 CI 部署 semantic-release 以后,做为开发人员只须要按照规范书写 Commit 便可,其余的都由 CI 完成。
具体如何将语义化发布加入 CI 流程中呢, semantic-release 是 js 实现的,若是是 js 的项目,能够直接在 package.json 中增长配置项,而对于任意语言的项目,推荐像 Demo 中同样,在根目录下增长 配置文件release.config.js。这个配置目的是为了禁用默认开启的 npm 发布机制,能够直接套用。
semantic-release 要执行 GitHub Release,所以咱们须要在 CI 中配置本身的 Personal access tokens 让 CI 有 GitHub Repo 的读写权限, 能够经过 GitHub 点击本身头像 -> Settings -> Developer settings -> Personal access tokens -> Generate new token 生成一个 Token。 而后在 Drone 的 repo 设置界面新增一个 Secret, key 为 GITHUB_TOKEN, value 填入刚生成的 Token。
最后在 .drone.yml 中增长这样一段就能够了。
 
 
  1. - name: semantic-release

  2. image: gtramontina/semantic-release:15.13.3

  3. environment:

  4. GITHUB_TOKEN:

  5. from_secret: GITHUB_TOKEN

  6. entrypoint:

  7. - semantic-release

  8. when:

  9. branch: master

  10. event: push


来再次模拟一下流程,feature 分支部分与上文相同:
  • 从 dev 向 master 发起 pull request,CI 运行单元测试,并构建镜像allovince/drone-ci-demo:latest

  • merge pull request,CI 会执行单元测试并运行 semantic-release , 运行成功的话能看到 GitHub 新增 Release v1.0.0

  • GitHub Release 再次触发CI 构建生产环境用 Docker 镜像allovince/drone-ci-demo:1.0.0


最终咱们能获得这样一个赏心悦目的 Release。 
640?wx_fmt=png
Step.5:Kubernetes 自动发布
Docker 镜像推送到仓库后,咱们还剩最后一步就能够完成全自动发布的闭环,即通知 Kubernetes 将镜像发布到生产环境。这一步实现比较灵活,由于不少云服务商在容器服务都会提供 Trigger 机制,通常是提供一个 URL,只要请求这个 URL 就能够触发容器服务的发布。Demo 中咱们使用更为通用的方法,就是将 kubectl 打包为容器,以客户端调用 Kubernetes 集群 Master 节点 API(kube-apiserver)的形式完成发布。
假设咱们在生产环境下 drone-ci-demo 项目的 Kubernetes 发布文件以下:
 
 
  1. ---

  2. apiVersion: extensions/v1beta1

  3. kind: Deployment

  4. metadata:

  5. name: ci-demo-deployment

  6. namespace: default

  7. spec:

  8. replicas: 1

  9. template:

  10. spec:

  11. containers:

  12. - image: allovince/drone-ci-demo

  13. name: ci-demo

  14. restartPolicy: Always


对应 .drone.yml 中增长 step 以下。这里使用的插件是 honestbee/drone-kubernetes, 插件中 kubectl 链接 API 使用的是证书 + Token 的方式鉴权,所以须要先得到证书及 Token, 已经受权的 Token 保存于 Kubernetes Secret,能够经过 kubectl get secret [ your default secret name ] -o yaml | egrep 'ca.crt:|token:' 得到并配置到 Drone 中,注意插件要求 Token 是明文的,须要 base64 解码一下:echo [ your token ] | base64 -d && echo ''。
 
 
  1. - name: k8s-deploy

  2. image: quay.io/honestbee/drone-kubernetes

  3. settings:

  4. kubernetes_server:

  5. from_secret: KUBERNETES_SERVER

  6. kubernetes_cert:

  7. from_secret: KUBERNETES_CERT

  8. kubernetes_token:

  9. from_secret: KUBERNETES_TOKEN

  10. namespace: default

  11. deployment: ci-demo-deployment

  12. repo: allovince/drone-ci-demo

  13. container: ci-demo

  14. tag:

  15. - ${DRONE_TAG}

  16. when:

  17. event: tag


在示例中,能够看到在语义化发布以后 CI 会将新版本的 Docker 镜像自动发布到 Kubernetes,这里为了演示仅打印了指令并未实际运行。至关于运行了以下的指令:
 
 
  1. kubectl -n default set image deployment/ci-demo-deployment ci-demo=allovince/drone-ci-demo:v1.0.2


因为自动发布的环节势必要接触到生产服务器,须要格外注意安全问题,首推的方式固然是将 CI 和 Kubernetes 集群放于同一内网中,同时可使用 Kubernetes 的 RBAC 权限控制,为自动发布单首创建一个用户,并删除没必要要的权限。
后话

640?wx_fmt=png


总结一下,本文展现了从 Hello World 到单人单分支手动发布再到团队多分支 GitFlow 工做流再到团队多分支 semantic-release 语义化发布最后到 通知 Kubernetes 全自动发布,如何从零开始一步一步搭建 CI 将团队开发、测试、发布的流程所有自动化的过程,最终能让开发人员只须要认真提交代码就能够完成平常的全部 DevOps 工做。
最终 Step 的完成品能够适配以前的全部 Step,若是不太在乎实现细节的话,能够在此基础上稍做修改,直接使用。
然而写好每个 Commit 这个看似简单的要求,其实对于大多数团队来讲并不容易作到,在实施过程当中,常常会遇到团队成员不理解为何要重视 Commit 规范,每一个 Commit 都要深思熟虑是否过于吹毛求疵等等疑问。
以 Commit 做为 CI 的核心,我的认为主要会带来如下几方面的影响:
  1. 一个好的 Commit,表明着开发人员对当前改动之于整个系统的影响,有很是清楚的认识,代码的修改到底算 feat 仍是 fix ,何时用 BREAKING CHANGE 等都是要仔细斟酌的,每一个 Commit 都会在 ChangeLog 里“留底”,从而约束团队不随意提交未经思考的代码,提升代码质量。

  2. 一个好的 Commit 也表明开发人员有能力对所实现功能进行精细的划分,一个分支作的事情不宜过多,一个提交也应该专一于只解决一个问题,每次提交(至少是每次 push)都应该保持系统可构建、可运行、可测试,若是能坚持作到这些,对于合并代码时的冲突解决,以及集成测试都有很大帮助。

  3. 因为每次发布能清楚的看到全部关联的 Commit 以及 Commit 的重要程度,那么线上事故的回滚也会很是轻松,回滚到哪一个版本,回滚后哪些功能会受到影响,只要看 CI 自动生成的 Release 记录就一目了然。若是没有这些,回滚误伤到预期外的功能从而引起连锁反应的惨痛教训,可能不少运维都有过相似经历吧。


所以 CI 自动化实际上是锦上添花而非雪中送炭,若是团队本来就无视规范,Commit 全是空白或者没有任何意义的单词,分支管理混乱,发布困难,奢望引入一套自动化 CI 来能解决全部这些问题,无疑是不现实的。而只有本来就重视代码质量,有必定规范意识,再经过自动化 CI 来监督约束,团队在 CI 的帮助下代码质量提升,从而有机会进一步改进 CI 的效率,才能造成良心循环。
愿天下再也不有难发布的版本。
Q&A

640?wx_fmt=png


Q:Kubernetes 上主流的 CI/CD 方案是啥?A:其实这无关 Kubernetes,从市场占有率来看,前三名分别是:
  1. Jenkins

  2. JetBrains TeamCity

  3. CircleCI


来源:https://www.datanyze.com/market-share/ci
Q:GitLab 自带的 CI 与 Jenkins 和 GitLab 结合的 CI,该如何选择?想知道更深层次的理解。
A:仍是要结合本身团队的实际状况作选择。从成熟度来讲,确定是 Jenkins 用户最多,成熟度最高,缺点是侧重 Java,配置相对繁琐。GitLab 自带的 CI 相对简单,能够用 yaml,和 GitLab 结合的最好,但功能确定没有 Jenkins 全面。若是是小团队新项目,GitLab CI 又已经能够知足需求的话,并不须要上 Jenkins,若是是较大的团队,又是偏 Java 的,我的更偏向 Jenkins。
Q:Jenkins 若是不想运行在 Kubernetes 里面,该怎么和 Kubernetes 集成?
A:从 CI 的流程来讲,CI 应用是否是跑在 Kubernetes 的并不重要,CI 只要能访问代码库,有权限在生产环境发布,是否是跑在容器里从效果来讲其实没有区别,只是用 Kubernetes 部署 Jenkins 的话,运维的一致性比较好,运维团队不用额外花时间维护一套物理机的部署方案。
Q:Kubernetes 的回滚方案是回滚代码,重作镜像,仍是先切流量,后作修改?
A:代码必定是打包到镜像里的,镜像的版本就是代码的版本,因此必定是切镜像。至于回滚操做自己,Kubernetes 已经内置了不少滚动发布(Rolling update)的策略,不管是发新版本仍是回滚版本,均可以作到用户无感知。
Q:镜像大到几 G 的话如何更新部署,有什么好的实践呢,以及如何回滚?
A:几个要点:
  1. 镜像仓库部署在内网,镜像推拉走内网,几个 G 的镜像传输起来也只是秒级别的

  2. 镜像构建时将不常常变更的部分划分到 1 层,由于 Docker 镜像是分层的,这样每次拉镜像能够利用到 Docker 的缓存传输的只有镜像发生变化的部分

  3. 选择 alpine 这样尽可能小的镜像


回滚若是发生在相邻的版本,通常物理机上是有缓存的,速度都很快。
Q:Drone 开放 API 服务吗?这样方便其余系统集成。
A:能够调整一下思路,直接把须要的功能作成镜像在 Drone 里调用就行了。
Q:若是有 Drone 的 Server怎么作高可用?
A:Drone serve r用 Kubernetes 部署的话自己只起到了一个任务调度的做用,很难会遇到性能瓶颈。真的有性能问题能够尝试水平扩展 Drone server,共享同一数据库。
相关连接:

  1. https://avnpc.com/pages/android-auto-deploy-workflow-on-travis-ci

  2. https://github.com/AlloVince/drone-ci-demo

  3. https://github.com/AlloVince/drone-ci-demo/blob/master/kubernetes/drone.yaml

  4. https://docs.drone.io/user-guide/pipeline/conditions/

  5. http://plugins.drone.io/drone-plugins/drone-docker/


做者博客:https://avnpc.com/pages/drone-gitflow-kubernetes-for-cloud-native-ci


Service Mesh入门与进阶实战培训

640?wx_fmt=png


Service Mesh入门与进阶实战培训将于2019年4月12日在北京开课,3天时间带你系统学习Service Mesh,学习效果很差能够继续学习。本次培训包括:Istio介绍、核心功能、使用场景、安装与配置、升降级,Envoy介绍、架构、内部实现,Istio控制面板,Mixer核心功能与规则、监控、工做原理,Pilot介绍与配置,Istio安全,主要资源配置,策略配置,遥测,落地实践,传统微服务架构改造,Istio运维等,点击下面图片查看具体详情。
640?wx_fmt=jpeg