CI/CD Overview
我们软件的价值不仅体现在我们编写的代码中,还体现在我们打包和交付代码的方式中。 除了我们编写的代码之外,我们还必须执行以下操作来满足客户的需求并增加收入:
- 创建可执行文件
- 测试可执行文件是否符合他们的规范
- 交付可执行文件
自软件开发开始以来,这通常是一个手动过程,工程师在此过程中创建可执行文件,质量保证 (QA) 团队手动运行一套测试并对其进行签名,操作人员手动将经过认证的可执行文件部署到一个生产环境。 然而,在过去的二十年里,运营和发布工程已经发展到交付过程中几乎所有步骤都可以自动化的程度。
将软件从代码带到生产执行的一系列步骤本质上是由 业务需求 。 例如,一些应用程序可能要求在部署应用程序之前运行验收和性能测试,而其他应用程序可能只需要单元和集成测试。 同样,一些应用程序可能会部署到单个生产环境中,而其他应用程序可能会打包一个可执行文件,可供无数客户下载。 尽管具体步骤会因我们的业务需求而异,但总体原则保持不变:串行和并行执行步骤。
当手动执行时,这是一个艰巨且成本高昂的过程,并且随着更多步骤的增加而变得越来越繁重。
持续集成
迈向自动化的第一步是 持续集成 (CI)。 在 CI 之前,开发人员会在应用程序的不同部分上工作并单独测试它们。 的过程中一起测试它们 大爆炸集成 。 在大多数情况下,集成从未按预期工作,并且需要数天或数周才能解决发生的任何问题。 这个代价高昂的过程每隔几周就会重复一次,直到所有所需的功能都准备好,如果在最后一刻完成,它可能会带来灾难性的后果。
为了改进这个过程,开发人员开始自动化他们的单元和集成测试,每天至少运行一次,或者如果可能的话,在每次签入存储库之后运行。 这种方法确保每天完成数十甚至数百次集成,并且集成应用程序的小部分——而不是大的、单一的块——被集成。 这个 CI 过程极大地改善了周转时间和应用程序质量,为自动化过程中的下一个逻辑步骤奠定了基础: 持续交付 (CD)。
持续交付
CI 专注于代码的集成,而 CD 则自动化了从签入到部署的整个交付过程。 CD 的核心概念是 管道 ,它表示一组有序的 阶段 ——一些是串行执行的,另一些是并行执行的。 示例管道如下所示:
在这种情况下,CD 管道从提交到存储库开始,然后启动构建。 构建完成并创建可执行文件后,可执行文件将进行单元测试。 如果所有单元测试都通过,则可执行文件进行集成测试; 如果所有集成测试都通过,则运行验收测试。 一旦所有验收测试通过,管道就会在生产环境中并行运行三个阶段:
- 用户验收测试 (UAT) – 一组手动测试,例如 UI 测试或其他验收测试,需要人工判断才能确定它们是否通过。 由于此阶段的完成需要手动签核,因此在此阶段完成之前,管道的进程被称为 门控 。
- 容量测试 ——如果应用程序在生产环境中满足其时间和容量规范,则执行性能测试。
- 暂存 ——可执行文件及其随附的配置已暂存并准备好进行部署。
如果所有这三个阶段都通过了——在手动 UAT 的情况下,当测试人员签署测试已通过时——分阶段的可执行文件和配置将部署到生产环境(或移动到面向公众的服务器,以便客户可以下载应用程序)。 虽然某些阶段可以通过按下管道中的按钮手动完成,但最好自动化尽可能多的阶段,如果不是全部的话。 完全自动化的管道允许开发人员将代码签入存储库,如果所有测试都通过,确保他们的更改以一致且可重复的方式推送到生产环境,无需任何人工干预。
CI/CD 的概念对于软件开发的进步至关重要,当我们将它们集成到我们的存储库中时,它们的业务和实际利益就会得到回报。 虽然有无数的存储库可供选择,但其中一个已成为迄今为止最受欢迎的选择:GitHub。
GitHub Actions 关键概念
GitHub 拥有超过 2 亿个存储库 等第三方工具创建 CI/CD 管道 Travis CI 和 CircleCI 。 这个外部流程在 2019 年 11 月发生了变化,当时 GitHub 宣布推出 GitHub Actions 。 GitHub Actions 是一个 CI/CD 工具,它直接集成到每个 GitHub 存储库中,利用直接驻留在存储库中的基于文本的配置文件。
GitHub Actions 中的根本概念是 工作流 ,或一系列自动化程序。 在实践中,工作流类似于管道,允许开发人员配置一系列阶段,每次触发特定事件时都可以执行这些阶段。 每个存储库都可以有任意数量的工作流,每个工作流由以下组件组成:
零件 | 描述 |
---|---|
工作 | 在同一个运行器上执行的一组步骤 默认情况下,如果一个工作流有多个作业,这些作业将并行执行,但可以通过声明一个作业依赖于另一个作业来将作业配置为串行运行。 如果作业 B 依赖于作业 A ,则作业 B 时才会执行 A 成功完成 |
步 | 由一个或多个 shell 命令或操作组成的任务 一项作业的所有步骤都在同一个运行器上执行,因此可以彼此共享数据。 |
行动 | 可以在一个步骤中执行的一组预先打包的过程 GitHub 社区已经提供了许多执行常见任务的操作,例如签出代码或上传工件。 |
事件 | 触发工作流执行的刺激 最常见的事件之一是用户将代码签入存储库。 |
赛跑者 | 在特定操作系统 (OS) 或平台上执行作业的服务器 Runners 可以由 GitHub 托管,也可以在独立服务器上。 |
这些组件之间的关系如下图所示:
在实践中,工作流程比 CD 管道更通用,但它们密切相关:
- 工作流程 = 管道
- 工作 = 阶段
- 步骤 = 组成一个阶段的一系列程序
示例工作流程
为了演示工作流程,我们可以创建一个包含多个测试的小项目。 对于这个例子,我们将使用 dzone-github-actions-refcard-example
项目 。 该项目运行一个代表性状态转移 (REST) 应用程序编程接口 (API) 应用程序,该应用程序以“Hello, world!”响应。 来自的消息 /hello
端点并且有一个单一的测试来确保端点的响应体是正确的。 要克隆存储库,请执行以下命令:
git clone git@github.com:albanoj2/dzone-github-actions-refcard-example.git git checkout code
项目准备好后,我们可以通过创建一个新的工作流 .yml
文件中 .github/workflows/
我们的 GitHub 存储库的目录——例如, .github/workflows/example.yml
. 然后我们可以配置我们的工作流程来检查我们的存储库并使用 mvn test
通过将以下内容添加到我们的命令 example.yml
文件:
name: dzone-github-actions-example on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: mvn package -DskipTests - name: Upload Artifacts uses: actions/upload-artifact@v2 with: name: jar-file path: target/github-actions-example-1.0.0.jar unit-test: needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - run: mvn test
此配置分为三个主要部分:
name
– 工作流的可选名称。on
- 执行工作流的触发器 - 在这种情况下,当提交被推送到存储库时,表示为push
. 的完整语法on
字段记录在 GitHub 操作页面的工作流语法中 。jobs
– 构成工作流程的工作。
作业字段包含两个作业: build
和 unit-test
.
build
是用于构建我们项目的工作。 注意名字 build
没有任何特殊意义,可以使用任何名称。 这 runs-on
字段表示作业将在其上执行的操作系统和环境,例如最新版本的 Ubuntu(表示为 ubuntu-latest
)。 这 steps
字段表示作业的步骤。 在这种情况下,有三个步骤:
1. 存储库 出存储库中的代码 Checkout 操作签 。 由于我们不知道将执行每个作业的运行器的状态,因此我们首先必须检查我们的存储库,然后才能访问我们的代码。 我们可以选择任何 可用的操作 来运行 uses
场地。 请参阅 工作流语法页面 。
2. 构建存储库 ——我们执行一个 shell 命令——在这种情况下, mvn package -DskipTests
- 使用 run
场地。 此命令将我们的应用程序打包到 Jar 文件中,而不运行测试(将在后续作业中执行)。 如果需要,我们还可以使用管道符运行多个 shell 命令。 例如,我们可以回显 Running a build
然后按如下方式执行构建:
- run: | echo "Running a build" mvn package -DskipTests
3. 上传工件 ——我们稍后会看到,我们需要访问在这个作业中创建的 JAR 文件(我们的可执行文件)。 为了以后存储它,我们使用 upload-artifact
操作,使用 name
字段并使用 path
场地。 请参阅 上传工件文档 。
unit-test
是用于运行我们的单元测试的工作。 这份工作类似于我们的 build
作业,而不是运行 mvn package -DskipTests
命令,我们运行 mvn test
命令。 此外,我们还添加了 needs
字段,其值只是我们的作业的名称 unit-test
取决于。 在我们的例子中,我们指定我们的 unit-test
工作取决于我们 build
工作使用 needs: build
. 配置了这种关系后,我们的 unit-test
作业只会执行一次 build
作业成功完成。 请参阅 工作流语法页面 。
当我们提交这个 example.yml
文件,GitHub 识别出工作流已配置并执行我们的工作流。 如果我们单击 GitHub 存储库中的 Actions 选项卡,我们可以看到与我们的提交对应的所有工作流运行:
如果我们点击一个工作流运行——在这里, Added Actions
— 我们可以看到该运行的管道状态以及状态和持续时间信息:
最后,如果我们点击 build
作业,我们可以看到对应于我们的执行的日志输出 build
在我们的工作 Added Actions
犯罪:
尽管 GitHub Actions 工作流的语法很简单,但它提供了创建复杂管道和执行满足业务目标所需的几乎任何程序所需的机制。
使用 GitHub Actions 进行部署
编者注:本节演示如何使用 GitHub Actions 部署到 Azure——订阅 Azure 服务(付费)的用户可以使用他们现有的帐户进行跟进。
通过构建和测试我们的应用程序,我们现在可以部署它。 有许多云提供商可供选择——每个都有自己的优点和缺点——在本节中,我们将使用 Azure。 要部署到 Azure,我们必须创建一个 Dockerfile
在我们项目的根目录下,内容如下:
FROM openjdk:11 COPY target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app.jar"]
要设置我们的 Azure 环境,我们必须完成以下步骤:
1. 导航到您的 Azure 服务门户 。
2. 创建 新订阅 。 我们将这个新订阅的 ID 引用为 <subscription_id>
.
3. 创建一个 新的容器注册表 。 我们将容器注册表 URL 引用为 <login_server>
. 在这种情况下,我们将使用 albanoj2.azurecr.io
.
4.创建一个 新的应用服务 :
- 将 Name 为唯一名称(在整个 Azure 中必须是唯一的)。 在这种情况下,我们将使用
dzone-github-actions-example
. - 将 发布 为 Docker 容器。
- 将 操作系统 为 Linux。
5. 在 Azure CLI 本地
curl -sL https://aka.ms/InstallAzureCLIDeb \ | sudo bash
6. 使用 Azure CLI 登录:
az login
7. 创建一个新的 服务主体 :
az ad sp create-for-rbac \ --role contributor \ --scopes \ /subscriptions/<subscription_id> \ --sdk-auth
此命令将输出以下内容:
{ "clientId": <client_id>, "clientSecret": <client_secret>, "subscriptionId": <subscription_id>, "tenantId": <tenent_id>, "activeDirectoryEndpointUrl": "https://login.microsoftonline.com", "resourceManagerEndpointUrl": "https://management.azure.com/", "activeDirectoryGraphResourceId": "https://graph.windows.net/", "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/", "galleryEndpointUrl": "https://gallery.azure.com/", "managementEndpointUrl": "https://management.core.windows.net/" }
8.添加一个新的秘密, AZURE_CREDENTIALS
,到我们的 GitHub 存储库,在 Settings > Secrets 下,如下所示:
- 创造一个新的秘密,
REGISTRY_USERNAME
, 与价值<client_id>
. - 创造一个新的秘密,
REGISTRY_PASSWORD
, 与价值<client_secret>
.
配置好我们的 Azure 和 GitHub 环境后,我们可以更新我们的工作流以包含一个新作业, deploy
,它将负责构建我们的 Docker 映像并将其部署到我们的 Azure 环境:
deploy: needs: unit-test runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Download JAR file uses: actions/download-artifact@v2 with: name: jar-file path: target/ - name: Login to Azure uses: azure/login@v1 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - uses: azure/docker-login@v1 with: login-server: albanoj2.azurecr.io username: ${{ secrets.REGISTRY_USERNAME }} password: ${{ secrets.REGISTRY_PASSWORD }} - run: | docker build -t albanoj2.azurecr.io/dzone-github-actions-example:${{ github.sha }} . docker push albanoj2.azurecr.io/dzone-github-actions-example:${{ github.sha }} - uses: azure/webapps-deploy@v2 with: app-name: dzone-github-actions-example images: albanoj2.azurecr.io/dzone-github-actions-example:${{ github.sha }} - name: Azure logout run: | az logout
与我们之前的工作类似, deploy
作业在 unit-test
作业,使用最新的 Ubuntu 映像执行,并从我们的存储库中签出最新的代码,但也有新的步骤:
- 下载 JAR 工件 – 由于每个作业都是独立执行的,因此我们的构建还没有运行
deploy
工作。 因此,我们必须下载我们在build
访问我们的 JAR 工件的工作。 我们使用path
字段以指定 JAR 应下载到target/
,如果我们运行构建,将包含 JAR 文件的同一目录。 - 登录 Azure—— 我们通过 Azure 进行身份验证,以便稍后在作业中运行特权命令。 请注意,
login-server
价值 (albanoj2.azurecr.io
) 对于每个用户来说都是不同的,具体取决于<login_server>
在设置我们的 Azure 环境时。 请参阅 Azure Docker 登录操作文档 。 - 构建 Docker 映像 ——我们的应用程序将作为 Docker 容器部署到 Azure,因此我们必须首先构建 Docker 映像。 一旦这个图像是使用我们的
Dockerfile
,我们只需将其推送到我们的容器注册表——在本例中,位于albanoj2.azurecr.io
. - 将 Docker 映像部署到 Azure—— 最后,我们将在上一步中创建的映像部署到 Azure 部署。 请注意,
app-name
对应于我们在设置 Azure 环境时创建的 App Service 的名称(即dzone-github-actions-example
)。 请参阅 Azure WebApps 部署操作文档 。
当我们执行这个工作流时,我们看到我们的 deploy
作业成功完成:
文章评论