浅记持续交付

定义

持续交付是开发人员,如何将一个好的点子,以最快的速度交付给用户的方法。

持续集成:开发反复提交-构建-测试的过程;

持续部署:可交付的产品快速且安全地交给用户使用的一套方法和系统;

通过统一标准、规范流程、工具化、自动化等的方式,影响着整个研发周期。

人(组织文化)+事(流程)+物(架构)。

DevOps是一种鼓励协作的研发文化,它的概念比持续交付更宽泛,持续交付是DevOps的一种工具和技术实现。

实践

拿Java中常用的maven来举例:

maven的原则:

最短路径优先,如果A依赖C,A依赖B,B依赖C,那么maven会使用A依赖的C的版本;

第一声明优先,如果A依赖B和C,B和C都依赖了D,那么maven会使用B依赖的版本。

实践:

  • 不要在生产环境使用SNAPSHOT版本的包;
  • 在父模块使用dependencyManagment来统一版本;
  • 对于一组依赖的控制,使用BOM来进行统一管理;
  • 使用properties来减少重复劳动;
  • 不要在生产环境中使用mvn install命令,因为这个命令会导致真正需要的包被覆盖;
  • 禁止变更了代码不改版本号就上传到仓库。

集成分支上线后回滚,

1
2
3
4
5
6
git fetch origin  
$ git checkout master
$ git reset --hard V0529 # 把本地的 master 分支的指针回退到 V0529,此时暂存区 (index) 里就指向 V0529 里的内容了。
$ git reset --soft origin/master # --soft 使得本地的 master 分支的指针重新回到 V05javascript:;30,而暂存区 (index) 变成 V0529 的内容。
$ git commit -m "rollback to V0529" # 把暂存区里的内容提交,这样一来新生成的 commit 的内容和 V0529 相同。
$ git push origin master # 远端的 master 也被回滚。

推动规范的完成:

  • 代码以及依赖规范;
  • 命名规范;
  • 开发规范;
  • 配置规范;
  • 部署规范;
  • 安全规范;
  • 测试规范。

环境构建流水线:

可以通过OpenStack进行物理机和虚拟机的初始化工作,然后通过自动化的配置管理工具(Puppet、Chef、Ansible、SaltStack)安装一些基础软件,例如JDK和Tomcat等。如果采用资源池的方式,可以根据平时机器的使用情况预先分派一个资源池,省的从paas平台申请机器时还需要等待初始化。

云计算的三种模式:

  • SaaS:软件即服务,提供用户所需要的一切应用;
  • PaaS:平台即服务,提供了一个大的框架,包括中间件、操作系统、软件运行时环境等;
  • Iaas:基础架构即服务,只提供网络,存储,虚拟化技术等底层,其他需要用户自助。

应用部署流水线:

  • 单应用部署流水化;
  • 应用部署并行;
  • 流水线的容错机制:错误中断或者优先完成正常的,最后将错误的交给用户解决。

容器技术

统一了软件环境和软件代码。

  • 交付结果一致;
  • 交付自动化;
  • 交付个性化;
  • 交付版本控制。

构建提速

私有仓库

  • createrepo:搭建CentOS的yum仓库;
  • Nexus:Java的maven仓库;
  • cnpm:NodeJS的npm仓库;
  • pypiserver:Python的pip仓库;
  • gitlab:代码仓库;
  • Harbor:Docker镜像仓库。

Maven调优

  • 合适堆内存;
  • -Dmaven.test.skip=true跳过单元测试(-DskipTests命令还会编译测试文件);
  • 构建过程异步检查和测试(Enforce检查,框架依赖检查,sona检查,单元测试,集成测试);
  • 增量代码扫描;
  • 发布阶段不使用snapshot版本的依赖;
  • 使用-T 2C进行并行构建;
  • -pl参数局部构建;
  • 正确使用clean,在改了类型名或者删除了某些类的情况下可以使用该参数。

构建检测

maven enforcer

一款maven插件,使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<build>		
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<executions>
<execution>
<id>enforce</id>
<configuration>
<rules>
<requireMavenVersion>
<version>3.5</version>
</requireMavenVersion>
<requireJavaVersion>
<version>1.8</version>
</requireJavaVersion>
<requireOS>
<family>Linux</family>
</requireOS>
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

通过继承pom的形式,定义公司或者bu的统一依赖管理。

参考携程的做法:

1
2
3
4
5
6
corp pom
ctrip pom/qunar pom
bu pom
product pom
project parent pom
subject module pom

对于一组依赖的控制,可以使用bom来统一管理依赖的公共组件。

参考enforcer插件的官网https://maven.apache.org/enforcer/enforcer-rules/index.html

以及扩展插件:http://www.mojohaus.org/extra-enforcer-rules/

CI工具

  • Travis CI:基于Github的CI托管解决方案之一;
  • Circle CI:基于Github,Bitbucket的云端持续集成方案;
  • Jenkins CI:自动化驱动编译、测试、交付、部署。

容器镜像构建

  • 选择合适的Base镜像
  • 较少不必要的镜像层的产生
  • 充分利用指令的缓存

Docker out Docker:在物理机上安装一个Docker daemon,所有的容器都在物理上运行;

Docker in Docker:在容器里安装一个Docker daemon,需要--privileged参数取得真正的Root命令,docker官方提供了一个docker:dind镜像可以直接使用。

灰度发布

  1. 蓝绿发布:先增加一套新的集群,发布版本到新集群并验证,此时不介入外部流量,等验证通过以后再把流量引入新服务集群,等待一段时间没有异常以后,老版本服务集群下线。
  2. 滚动发布:从旧的集群中选出一批部署新应用,进行验证,验证完毕以后接入流量,然后依次重复部署;
  3. 金丝雀发布:从集群中挑选特定服务器或一小批符合条件的用户,对其进行版本更新验证,随后逐步更新。

发布系统

一张页面:展示尽可能详细和精准的数据和内容;

两个时态:

  1. 发布中:发布中内容,处理过程、结果、耗时、当前情况;
  2. 未发布时:历史发布进程,集群、服务器具体版本的情况;

两个按钮:

  1. 开始发布
  2. 中断发布
  3. 局部有错误时:中断或重试发布
  4. 发布被暂停时:中断或继续发布

三种发布结果:

  1. 成功
  2. 失败
  3. 中断

四类操作选择:

  1. 开始发布
  2. 停止发布
  3. 发布回退
  4. 发布重试

五个步骤:

  1. markdown-拉出集群,不接入新流量
  2. downland-下载代码包
  3. install-停止服务、替换代码、重启服务
  4. verify-启动检查、程序预热(异步)
  5. markup-拉回集群、接入流量

六个页面产物:

  1. 集群
  2. 示例
  3. 发布日志
  4. 发布历史
  5. 发布批次
  6. 发布操作

ctrip-deploy-system-architecture.png

携程发布系统架构

Roll Engine:发布引擎,用于创建发布批次,按批次粒度实施部署策略,异步调用Salt Master服务,真正用于部署任务的是agent

监控

类型:

  1. 用户侧监控-访问速度和结果
  2. 网络监控-CDN与核心网络监控
  3. 业务监控-核心业务指标波动
  4. 应用监控-服务调用链、P99、错误量、JVM
  5. 系统监控-基础设置、虚拟机、操作系统

静态代码检查

SonarQube

提倡开发人员在开发环境中执行静态检查。

服务端环境:Sonar服务+CheckStyle插件

开发端环境:Idea-SonarLint插件

maven命令:mvn org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar -f ./pom.xml -Dsonar.host.url=sonar 服务器地址 -Dsonar.login= 账号名称 -Dsonar.password= 账号密码 -Dsonar.profile= 检查规则的集合 -Dsonar.global.exclusions= 排除哪些文件 -Dsonar.branch= 检查的分支

集成GitLab:在Merge Request中增加Sonar环节,包括检查状态和结果等。

破坏性测试

  1. 严格设计和执行破坏性测试的手段和过程;
  2. 权衡破坏性测试的量和度。

混沌工程

步骤:

  1. 正常系统行为的可测量数据-稳定态
  2. 对照组:假定对照组-实验组都是稳定态
  3. 引入真实世界变量-断网、磁盘损坏、服务器崩溃
  4. 对比对照组和实验组,找出系统弱点

高级原则:

  1. 真实场景
  2. 生产环境
  3. 自动化连续实现
  4. 最小爆破半径

Chaos Monkey

Netflix对于混沌工程的先驱实践,随机杀死一些服务制造混乱

测试

Mock

  1. 测试用例独立
  2. 提升测试执行速度
  3. 提高测试用例准备效率-无需考虑依赖端情况

基于对象和类的Mock:Mockito或者EasyMock,在运行时为每一个被Mock的对象或类动态生成一个代理对象适合DAO层数据库操作和复杂逻辑

基于微服务的Mock:Weir Mock和Mock Server,模拟API、http形式对象:

  1. 标记被代理的类或对象,或声明被代理的服务
  2. 通过Mock框架定制代理的行为
  3. 调用代理,从而获得预期的结果

回放

方案一:在统一的SLB上做统一的拦截和复制转发处理,容易产生生产链路的故障

方案二:在集群中新增一台服务器,启动一个软交换,由该软交换负责复制和转发用户请求

移动APP

静态检查

  • Clang Static Analyzer:被Xcode集成
  • OCLint
  • Infer

Android热修复:

百川hHotFix、美团Robust、手机QQ空间、微信Thinker

IOS热修复:

  1. Rollout.io、JSPatch、DynamicCocoa,只针对IOS
  2. React Native(FaceBook)、Weex(Alibaba),跨平台热更新
  3. Wax、Hyrid,前者用Lua语言,比较适合游戏,Hybrid主要面向H5

Jenkins实践

Jenkins Pipeline:运行在Jenkins上的一个工作流框架,支持将原先运行在一个或多个节点的任务通过一个Groovy脚本串联起来,以实现之前单个任务难以完成的复杂工作流

  1. 安装Jenkins
  2. 配置Jenkins对GitLab的访问权限-配置Jenkins钥匙-凭据-系统-全局凭据-添加凭据,添加私钥
  3. 配置gitlab或者github的公钥
  4. 安装GitLab plugin
  5. 选择GitLab API Token,将GitLab的/profile/personal_access_tokens这个api生成的access token保存到GitLab API Token

创建Jenkins Pipeline任务:

  1. GitLab项目-settings—>intergrations—>Merge request events

  2. settings—>Merge Request—>Only allow Merge Requests to be merge if the pipeline succeeds

  3. 贴入代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    node {
    def mvnHome

    # 修改 Merge Request 的状态,并 checkout 代码
    stage('Preparation') { // for display purposes
    mvnHome = tool 'M3'
    updateGitlabCommitStatus name: 'build', state: 'running'
    checkout scm
    }

    # 执行 Maven 命令对项目编译和打包
    stage('Build') {
    echo 'Build Start'
    // Run the maven build
    sh "'${mvnHome}/bin/mvn' -Dmaven.test.skip=true clean package"
    }

    # 启动 sonar 检查,允许 junit 单元测试,获取编译产物,并更新 Merge request 的状态
    stage('Results') {
    // Run sonar
    sh “'${mvnHome}/bin/mvn' org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar”
    junit '**/target/surefire-reports/TEST-*.xml'
    archive 'target/*.war'
    updateGitlabCommitStatus name: 'build', state: 'success'
    }
    }

    对于Docker镜像,在上面的检查结束以后,还需要在检查以后安装Centos软件以及搭建Tomcat等容器运行环境。

    对于IOS应用,需要指定应用在Mac Slave的Jenkins中进行编译构建。

企业级持续交付方案推荐:

代码管理:GitLab

构建打包:Jenkins

自动部署:Ansible

Web管理界面:Ansible Tower

交付平台:Spinnaker

坚持原创、技术分享。请作者喝杯茶吧!