总纲

从数据库、机器学习模型到前端
从数据库性能优化,机器学习模型持续监控,前端自动化测试到自动部署

在本篇中,介绍持续集成与持续交付的基本概念,帮助大家了解为什么要做持续交付,最后通过 Azure DevOps 搭建一个自己的持续交付管道
接下来三个核心篇,分别针对数据库、机器学习和网站应用三个环节,讲解持续测试与改进的方法

  • 数据库篇,学习 SQL Server 上可用的性能监控工具,学习提升数据库性能的表结构设计方式与技巧
  • 机器学习篇,学习 MLOps 的基本概念,了解如何自动化管理机器学习的生命周期,实现机器学习模型的持续改进
  • 网站应用篇,学习网站 UI 自动化测试的方式与常用工具,并通过 Selenium 实现网页的 UI 自动化测试

最后,会针对数据库、机器学习模型、微服务应用和前端应用的自动部署做更进一步的讲解 ,指导大家通过 DevOps 工具中应用所学到的优化技术

持续集成与持续交付

在软件开发生命周期的管理中,DevOps 的理念愈发流行。互联网上关于 DevOps 的定义众说纷纭,看多了不免觉得,这不就是各种软件工程理论的大杂烩吗?为了解决这个问题,我会帮助大家理清 DevOps 的核心内容,通过实际地搭建自动交付管道,来理解实施 DevOps 的核心价值。

DevOps 的核心在于指导团队利用自动化的工具,实现持续集成(Continuous Integration, CI)与持续交付(Continuous Delivery, CD),提高软件的质量和迭代交付的速度。

新代码的提交到程序的部署,一般会经历编译检查、单元测试和代码扫描、分支、打包和部署几个阶段,其中部署包括部署到测试环境、集成测试和批准后部署到生产环境。
通过持续集成,让开发人员能够更频繁地提交代码,并在最短的时间内获得代码层面的测试结果,例如可编译性、能否通过单元测试、Bug 数量、第三方库安全性和与主分支的冲突等。帮助开发人员改善自己的代码,帮助开发团队提高协作开发的效率。
通过持续交付,在最短的时间内将通过测试的代码发布到测试环境,让上层的自动化测试开始运行,测试人员可以开展测试。在测试通过后,可以容易地将应用部署至生产环境,其中需经历的测试及审批流程依公司和项目要求而定。
image.png
CI/CD 管道的构建帮助项目团队更快的提交代码,更早地发现问题,更及时地验证功能,减少了一次性上线大量新功能带来的系统风险。同时更频繁更快速的发布,有助于保持多环境部署的统一,当生产环境遇到问题时,不用因担心部署时间过长而直接在生产环境进行热修复。

因此我们发现,实现 CI/CD 的关键就是自动化,通过合适的触发机制,让编译、测试、打包和部署工作自动开展,避免因沟通和流程问题带来的时间消耗,减少人为因素带来的可靠性影响。

关于持续交付的几点建议

除了将测试、发布的流程自动化,为了更好地结合云原生应用的特征,实现更有效的持续交付,提出以下几点建议:

  1. 基础设施即代码(Infrastructure as Code, IaC),指云环境中的资源的变更,如虚拟机(或 PaaS 网站应用)的创建和配置修改,应通过脚本文件(例如 ARM Template)记录和实施;
  2. 数据库即代码(Database as Code),指数据库的任何创建和修改,应通过脚本文件(.DACPAC, .sql)记录和实施;
  3. 为确保所有的修改透明且可追踪,项目文档和上述脚本应随源代码一并提交到源代码管理(Source Code Management, SCM)工具中;
  4. 在 CI/CD 工具链中,集成静态代码扫描工具,管理项目中引入的第三方库,管理代码缺陷;
  5. 通过公共库,或在必要时搭建私有仓库(私有 nuget/NPM)来处理公共组件,处理公共依赖及版本管理问题;
  6. 将数据库等配置信息配置在环境变量中,而非写入配置文件提交到 SCM,避免机密信息泄露,同时实现一份代码一次编译多次部署。

更多关于云原生时代应用 DevOps 的概念和建议,可以参考以下文档
What is DevOps?
12 Factor APP

通过 Azure Pipeline 构建 CI/CD 管道

本文结合系列文章,以微软的 Azure DevOps(VSTS)为主要工具,带领大家搭建一个基本的 CI/CD 管道,并在后续的文章中讲解如何针对数据库、机器学习模型和网站应用做持续交付。

Azure DevOps 的优势

  1. Azure DevOps 即 Visual Studio Team Services(在线版 TFS),在国内外大厂中得到了广泛而长久的应用,功能和性能都经过了时间和实践验证;
  2. 对于使用 Office 365 的公司来说,可以非常方便地管理 Azure DevOps 中的权限;对于个人来说,可以使用微软账号(亦是登录 Win10 的账号)进行登录;
  3. Azure DevOps 的私有库支持免费添加五位开发成员,相对的 Github 是三位;
  4. Azure DevOps 整合了项目管理、代码仓库、CI/CD 管道、测试管理和私有包管理,支持丰富的第三方扩展,可以使用一个平台完成一个项目的软件生命周期管理;
  5. Azure Pipeline 搭建的管道具有极高的灵活性,获取源代码、编译、打包和部署中的任一步都可以使用其他工具替换。

搭建一个简单的 CI/CD 管道

准备示例代码

通过 Vue-CLI 新建一个 Vue 项目

  1. vue create gty-training-vuedemo
  2. # 本地编译并测试运行效果
  3. cd gty-training-vuedemo
  4. npm run serve

image.png
提交代码至 Azure DevOps 上的代码仓库(repos)

  1. git remote add origin https://{organizationName}@dev.azure.com/{organizationName}/{projectName}/_git/{repositoryName}
  2. git push -u origin --all

image.png

实现自动编译

代码提交好了,我们首先来搭建一个 CI 管道
进入 Pipelines,点击新建 Pipeline,选择代码仓库的位置和代码仓库
image.png
image.png
选择初始模板,系统会根据所选仓库中的代码类型,自动推荐合适的模板,此处我们选用空模板 Start pipeline 用于讲解

Azure pipeline 中主要通过 YAML 文件储存管道配置,一个基本的 YAML 文件包括以下几个部分
Pipeline 触发器,决定触发 CI 管道运行的条件,此处我们选用最简单的一种方式,有代码提交就触发管道运行

  1. # master 分支有代码提交时运行
  2. trigger:
  3. - master
  4. # master 和 Relase1.0 Release... 分支有代码提交时触发
  5. trigger:
  6. branches:
  7. include:
  8. - master
  9. - Release*
  10. # 定时触发,通过 CRON 表达式设置运行时间(UTC 时间)
  11. # always: false 表示 Release... 分支代码有更新才会按时触发 Pipeline
  12. schedules:
  13. - cron: "0 14 * * *"
  14. displayName: build at UTC+8 22:00
  15. branches:
  16. include:
  17. - Release*
  18. always: false
  19. # 被另一 Pipeline 触发运行
  20. resources:
  21. pipelines:
  22. - pipeline: parentPipeline # 对父级 Pipeline 的引用,可在 YAML 文件下文中使用
  23. source: Parent Pipeline # 父级 Pipeline 实际的名字(显示在 Pipelines 界面)
  24. trigger:
  25. branches:
  26. - master
  27. # 以上两种定义方式,在有代码提交时也会被触发
  28. # 若不想在代码提交时运行,可在最前面添加:
  29. trigger: none

指定编译环境

  1. # Pipeline 运行环境,最常用的为 windows-latest, ubuntu-latest, macos-latest
  2. pool:
  3. vmImage: 'ubuntu-latest'

设置全局变量

  1. # 全局变量,以该变量为例
  2. variables:
  3. environment: 'Release' # YAML 文件下文通过 $(environment) 引用

设置 Pipeline 的具体执行步骤,然后点击保存并运行

  1. steps:
  2. # 安装 NPM 并编译应用
  3. - task: NodeTool@0
  4. inputs:
  5. versionSpec: '12.x'
  6. displayName: 'Install Node.js'
  7. - script: |
  8. npm install
  9. npm run build
  10. displayName: 'npm install and build'
  11. # 发布部署文件(用于部署,并非指 Artifacts 模块)
  12. - task: CopyFiles@2
  13. inputs:
  14. SourceFolder: '$(Build.SourcesDirectory)'
  15. contents: '**/dist/**/*'
  16. targetFolder: '$(build.artifactStagingDirectory)'
  17. - task: PublishBuildArtifacts@1
  18. inputs:
  19. PathtoPublish: '$(Build.ArtifactStagingDirectory)/dist'
  20. ArtifactName: 'Vue-demo'

查看 pipeline 运行结果
image.png
点击 published 查看发布好的部署文件
image.png

实现自动部署

至此我们已经实现了代码提交时自动编译,接下来我们进入 Pipelines - Releases,点击New pipeline 开始 CD 管道的搭建,让应用自动部署到 Azure App Service
点击 Add an artifact,在 Source 处选择刚刚搭建的 CI 管道,然后点击 Add
image.png
添加后点击右上角的闪电图标,启用 Continuous deployment trigger,这样每当 CI 管道运行完成且有部署文件发布,CD 管道就会被触发运行
image.png
我们选择通过官方模板进行部署,点击 Add a stage,选择 Azure App Service Deployment
然后点击 Tasks 标签 - Stage 1,选择已连接的 Azure 订阅和要部署到的 App Service,连接 China Azure 的方式可以参考我的这篇教程
image.png
点击 Deploy Azure App Service 任务,在 Package of folder 处选择要部署的文件或文件夹,点击保存
image.png
此时已经配置好了应用的自动编译和自动部署,我们来修改一行代码并提交试试效果
image.png
一分钟后 CI 管道运行完成,应用成功编译并打包
image.png
不到半分钟后 CD 管道也运行完成,应用成功发布到了 Azure App Service
image.png
打开网站,可以看到首页的文字已经更新了,说明部署成功了
image.png

在部署这一步,如果不使用官方模板,也可以在新建 Stage 时选择 Empty Job
在编辑界面添加 FTP upload 任务,通过 FTP(S) 的方式部署应用文件,这种方式适用于更多的云服务和云上资源
image.png
类似的,如果部署到 Tomcat 服务器,可以添加 Deploy to Apache Tomcat 任务,部署文件到 webapps/ROOT 路径。

实现自动化测试

首先在 Vue 项目根目录运行如下指令,安装 jest 插件,安装后会自动生成一个示例测试

  1. vue add @vue/unit-jest

在 jest.config.js 文件中添加如下内容,开启测试覆盖率检测,并配置测试报告和覆盖率报告的格式

  1. module.exports = {
  2. reporters: [
  3. "jest-junit"
  4. ],
  5. collectCoverage: true,
  6. collectCoverageFrom: [
  7. "**/*.{js,vue}",
  8. "!**/node_modules/**"
  9. ],
  10. coverageReporters: [
  11. "cobertura"
  12. ]
  13. }

在 .gitignore 中添加两行,以免本地扫描报告被提交到代码仓库

  1. coverage
  2. junit.xml

在 CI 管道(YAML 文件)中修改和添加如下任务,然后提交代码

  1. # 编译前运行 npm run test:unit
  2. - script: |
  3. npm install
  4. npm run test:unit # 新增
  5. npm run build
  6. displayName: 'npm install and build'
  7. # 添加到编译和运行测试之后
  8. # 发布覆盖率检测报告
  9. - task: PublishCodeCoverageResults@1
  10. inputs:
  11. codeCoverageTool: 'Cobertura'
  12. summaryFileLocation: |
  13. $(System.DefaultWorkingDirectory)/**/*.xml
  14. reportDirectory: '$(System.DefaultWorkingDirectory)/coverage'
  15. failIfCoverageEmpty: false # 找不到覆盖率报告会导致 pipeline 运行失败
  16. # 发布单元测试报告
  17. - task: PublishTestResults@2
  18. inputs:
  19. testResultsFormat: 'JUnit'
  20. testResultsFiles: |
  21. $(System.DefaultWorkingDirectory)/junit.xml
  22. mergeTestResults: true
  23. failTaskOnFailedTests: true # 有单元测试未通过会导致 pipeline 运行失败

CI 管道运行结束后,可以在运行结果页面查看测试结果,点击即可查看详情
image.png
单元测试报告详情
image.png
覆盖率报告详情
image.png
进入 Overview - Dashboards 进入项目仪表板
点击 Add a widget 并选择 Test Results Trend,添加后点击齿轮图标选择对应的 CI 管道,即可在该页面看到单元测试运行结果趋势图
image.png
进一步地,可以在 CI 管道中添加第三方库扫描和代码静态扫描,检测代码引用的第三方库是否安全,检测代码中的 Bug 和漏洞,具体方法可以参考我的这两篇教程
WhiteSource Bolt 使用方法
SonarQube 安装与使用方法

总结

本篇教程梳理了 CI/CD 的基本概念,并通过 Vue 项目讲解了在 Azure DevOps 中搭建 CI/CD 管道的基本方法。Azure DevOps 是一个非常灵活的工具,具有丰富的预定义任务和扩展,可以用于搭建各种语言和框架的持续交付管道。同时,本文所涉及到的方法和理念同样适用于 Github Action, Jenkins 等 DevOps 工具。希望大家领会其中的精髓,学会举一反三,灵活应用。祝愿大家通过学习和应用 DevOps 工具赋能开发团队,助力公司的业务发展。