微服务因为船小好调头, 可以很方便快速地更新升级, 所以它才会大行其道
理论篇
从持续集成到持续交付, 工程师一直在下面这个循环里周而复始, 目的无非一个 -- 尽可能快速地交付有价值的产品给用户.
反模式
- 不到最后一刻, 没人知道软件产品最终长成什么样, 功能是否满足要求, 性能是否理想
- 软件开发过程中是不能交付的, 正在开发的的功能存在于代码库的一个单独的分支中, 直到经过很多测试才能合并到主分支中, 并发布产品包, 由运维人员最终部署到产品线上
正模式
- 每次提交都在主分支上, 都会触发系统构建并运行一系列全自动化测试, 一旦通过即部署到 alpha/beta 站点上, 由产品负责人负责验收, 决定是否部署到产品环境中
- 由 Feature toggle 来控制是否正式开放给全部或部分客户使用, 在正式发布产品之前, 会开放给一部分内部用户体验, 根据用户反馈, 功能和性能测试结果决定是否开放给外部客户
从持续集成到持续交付
持续集成(CI)是一种开发实践,是指开发人员每天多次将代码整合到共享的代码库中, 然后通过自动化构建验证每次提交,使团队能够及早发现问题。
Martin Fowler 总结了持续集成的几条最佳实践
- 维护单一代码库
- 自动化构建
- 使构建能够自我测试
- 每个人都可每天多次提交到主线
- 每一个提交都应该在集成机器上构建主分支
- 立即修复破碎的构建
- 保持构建速度
- 在生产环境相同的产品环境中测试
- 任何人都要以轻松获得最新的可执行文件
- 每个人都可以看到发生了什么
- 自动部署
而持续续交付(CD)是指通过自动化在比较短的一个个迭代中快速发布软件的做法,允许团队更频繁地交付可以工作的软件产品。 其重点--持续整合,内置测试,持续监控和分析反馈都指向软件行业的整体趋势:提高应变能力。持续交付是持续集成的扩展和目标。
如果你的测试持续在运行,并且你充分信任您的测试可以提供足够的质量保证,这样你就可以在任何时候发布软件产品。 持续交付并不总是意味着交付,它代表了一种哲学和承诺,确保您的代码始终处于发布就绪状态。
度量开发与交付的关键指标有两个:
发布频率
微服务部署并运行于产线上的频率, 通常用次/天, 次/周, 次/月来衡量, 频率不是越高越好, 为了保持产品的稳定性, 不必强求每天在产线上部署发布, 根据需要可以随时部署, 从持续改进的角度出发, 每周和每月是比较合适的频率.
它取决于上述所述的代码质量度量指标, 还取决于编译和打包速度, 测试和速度和效率, 以及部署和配置和速度等下级度量指标发布周期
发布周期是指代码从提交到代码仓库到发布到产线所经历的时间, 这个时间取决于在开发中过程中所经过的那些关键步骤所花费的时间, 这是几年前我在为一个项目所画的构建流水线图
我在两年前做的一个项目中画的持续交付的流程图:
- 提交代码到开发分支:
在本地构建并通过单元测试之后, 可以将代码提交到代码仓库的开发分支中
- 提交代码到开发分支:
- 自动构建:
包括编译, 链接, 打包, 在一些大型项目, 这个速度往往需要几十分钟以上
- 自动构建:
- 自动代码检查
各种语言都有各自的代码静态检查工具, C++ 的 CppCheck, CppLint, Java
的CheckStyle, PMD, FindBug, Python 的Pylint, Pyflakes, Pycodestyle 等工具.
如果检查出严重的错误, 并立即终止构建流水线, 检查结果会生成检查报告
- 自动代码检查
- 自动执行单元测试
确保这次提交不会造成其他模块的单元测试失败, 任何一条单元测试不通过, 也会终止构建流水线, 我们也会在这个阶段执行一个不依赖于外部环境的API 测试. 测试的结果会生成测试报告,
- 自动执行单元测试
- 自动生成文档
利用 Doxygen, Javadoc, Sphinx 之类的自动文档生成工具, 分析代码并生成 API 和帮助文档
- 自动生成文档
- 自动部署在测试环境
利用Puppet , Ansible 之类的部署脚本将软件包部署到测试环境, 注入预设的配置变量并启动服务
- 自动部署在测试环境
- 自动执行功能测试
在测试环境中, 自动运行所有需要依赖外部环境的 API 和 E2E(端到端)的测试, 如果有一条测试不通过, 也会终止构建流水线, 自动测试的结果会生成报告
- 自动执行功能测试
- 将代码合并入主分支
这个点可以设置一个批准环节, 由产品负责人决定是否立即合并到主分支, 如果改动太大, 自动化测试覆盖率不够, 开发人员信心不足的话, 可以暂时不并入主分支, 在开发分支添加更多测试
- 将代码合并入主分支
- 自动运行性能测试
性能测试分为基准性能指标测试和压力测试, 各种压测参数需要事先设定, 性能测试会生成性能测试和剖析报告, 包括吞吐量(TPS), 最耗时模块和函数, 以及对系统资源消耗的各种指标
- 自动运行性能测试
- 探索性测试
指望自动化测试把一切潜在问题找出来往往是不现实的, 手工进行一些探索性测试在发布在产线之前是必不可少的
- 探索性测试
- 自动部署到产品环境上
这个环节当然也是需要产品负责人批准的, 由产品负责人根据测试报告来决定何时部署到产线上.
- 自动部署到产品环境上
每次构建,分析,测试和部署都必须有详细的记录和报告,有利于追溯,分析和改进持续交付的流程
工具篇
Jenkins
现在的持续集成工具中, jenkins 是事实上的标准, 尤其它的 pipeline 插件已经定义了这些标准过程
https://hub.docker.com/_/jenkins/
让我们利用 Docker 先来构建一个 Jenkins
- Dockerfile
FROM jenkins:2.46.3
MAINTAINER Walter Fan
USER root
RUN mkdir /var/log/jenkins
RUN mkdir /var/cache/jenkins
RUN chown -R jenkins:jenkins /var/log/jenkins
RUN chown -R jenkins:jenkins /var/cache/jenkins
USER jenkins
ENV JAVA_OPTS="-Xmx1596m"
- 构建 jenkins docker image
docker build -t jenkins-image .
- 启动 jenkins 容器
docker run --restart always -v /workspace/jenkins:/var/jenkins_home -p 8080:8080 -p 50000:50000 --name=jenkins-container -d jenkins-image
Jenkins 非常强大, 使用方法也很庞杂,这里不做赘述, 另外再做详述. 重点谈谈它的 Pipeline 插件.
我们说的部署流水线, 主要就是通过它来完成的, 步骤如下
- 配置代码仓库
Branch Sources 无非就是 git 或 svn 的代码仓库地址, 用户名和密码, 分支名
- 定义配置文件
Build Configuration : Jenkinsfile
Jenkinsfile是重点, 它包含了大多数流水线的设置. 其他的没有什么之处
流水线提供一系列可扩展的工具, 以 流水线领域专用语言(Pipeline DSL) 代码描述了从简单到复杂的交付流程
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'make'
}
}
stage('Test'){
steps {
sh 'make check'
junit 'reports/**/*.xml'
}
}
stage('Deploy') {
steps {
sh 'make publish'
}
}
}
其他利器
docker
应用程序发布 docker image ,直接部署 docker image,可以省去大量环境配置
sonar
代码静态检查之利器,还有非java语言的 cppcheck,lint, pylint 等等
EKL
Elastic Search, Kibana, Logstash 服务器Log 收集和分析三件宝
参考资料
- 微软的持续集成与集成交付: https://docs.microsoft.com/en-us/aspnet/aspnet/overview/developing-apps-with-windows-azure/building-real-world-cloud-apps-with-windows-azure/continuous-integration-and-continuous-delivery
- 如何持续集成: https://www.atlassian.com/continuous-delivery/how-to-get-to-continuous-integration
- https://wiki.jenkins-ci.org/display/JENKINS/Distributed+builds#Distributedbuilds-Havemasterlaunchslaveagentviassh
- https://jenkins.io/doc/book/pipeline/