这个系列告诉你如何开始使用基础设施即代码(IaC)。目的是通过教程和代码实例帮助开发者建立对IaC的深刻理解。
- 第1部分:创建一个Kubernetes集群
- 第02部分:构建Docker镜像并部署到Kubernetes上
- 第03部分:用CI/CD自动部署
在这篇文章中,我将演示如何创建持续集成和部署(CI/CD)管道,使本系列第一部分和第二部分中涉及的TerraformIaC部署自动化。下面是我们在这篇文章中要完成的事情的快速列表。
- 为项目建立一个新的CircleCI .config.yml文件
- 配置新的工作和工作流程
- 自动执行Terraform代码以创建谷歌Kubernetes引擎(GKE)集群并部署应用程序
注意。 在你浏览本部分教程之前,请确保你已经完成了第一部分先决条件部分的所有操作。
我们将首先快速解释什么是CI/CD,并回顾本系列教程的前两期内容。然后你可以开始学习本代码库中包含的CircleCI .config.yml文件 。
持续集成和持续部署
CI/CD管道帮助开发人员和团队实现构建和测试过程的自动化。CI/CD创造了有价值的反馈回路,提供了一个近乎实时的软件开发过程的状态。CI/CD自动化还提供一致的流程执行和准确的结果。这有助于优化这些流程并促进速度的提高。用CI/CD精简开发实践正在成为团队中的普遍做法。了解如何整合和自动化重复的任务是建立有价值的CI/CD管道的关键。
在第一部分和第二部分中,我们使用Terraform创建了一个新的GKE集群和相关的Kubernetes对象,以部署、执行和服务一个应用程序。这些Terraform命令是由我们的终端手动执行的。当你在开发Terraform代码或修改它时,这很有效,但我们想自动执行这些命令。有很多方法可以实现自动化,但我们将专注于如何从CI/CD管道中做到这一点。
什么是CircleCI管线?
CircleCI管道是您在项目上触发工作时运行的全套流程。管道包括您的工作流程,这反过来协调您的工作。这都是在你的项目配置文件中定义的。在本教程的下一节,我们将定义一个CI/CD管道来构建我们的项目。
在CircleCI上设置项目
在我们开始为这个项目建立一个config.yml 文件之前,我们需要将项目添加到CircleCI。如果你不熟悉这个过程,你可以使用这里的设置CircleCI指南。一旦你完成了设置CircleCI的部分,就停在那里,这样我们就可以配置项目级环境变量。
项目级环境变量
本管道中的一些工作将需要访问认证凭证,以便在目标服务上执行命令。在本节中,我们将为一些工作定义所需的凭证,并演示如何将它们作为项目级环境变量输入CircleCI。 对于每个变量,在Name字段中输入EnVar Name: ,在Value字段中输入凭证。下面是我们的管道将需要的证书及其值的列表。
- **EnVar 名称。**TF_CLOUD_TOKEN -**Value:**本地.terraformrc文件的Base64编码值,该文件承载了Terraform云用户令牌。
- **EnVar Name:**DOCKER_LOGIN -值。 Docker Hub用户名
- **EnVar Name:**DOCKER_PWD -值。 Docker Hub密码
- **EnVar Name:**GOOGLE_CLOUD_KEYS -值。 GCP凭证JSON文件的Base64编码值
一旦上述所有环境变量到位,我们就可以开始在config.yml file 中构建我们的管道。
CircleCI的config.yml
config.yml文件是你定义CI/CD相关工作的地方,以便处理和执行。在本节中,我们将为我们的管道定义工作和工作流程。
在编辑器中打开.circleci/.config.yml 文件,删除其内容并粘贴此代码。
version: 2.1
jobs:
version: 键指定运行此管道时要使用的平台功能。jobs: 键代表我们将为该管道定义的单个作业列表。接下来,我们将创建我们的管道将执行的作业。
作业 - run_tests。
我鼓励你熟悉这个CircleCI参考文档中的特殊键、能力和功能,这应该有助于你获得平台的经验。下面是我们即将讨论的工作中每个键的大致轮廓和解释。
- docker。是一个键,代表我们的工作将在其中执行的运行时间
- 图像。是一个键,代表该作业要使用的Docker容器
- 步骤。是一个键,代表在工作中运行的可执行命令的列表或集合。
- checkout:是一个键,是一个特殊的步骤,用于将源代码检出到配置的路径。
- 运行。是一个键,用于调用所有的命令行程序
- store_test_results:是一个键,代表一个特殊步骤,用于上传和存储构建的测试结果。
- 路径。是指包含JUnit XML或Cucumber JSON测试元数据文件子目录的路径(绝对值,或相对于你的
working_directory)。
- 路径。是指包含JUnit XML或Cucumber JSON测试元数据文件子目录的路径(绝对值,或相对于你的
- store_artifacts:是一个键,代表一个步骤,用于存储工件(例如,日志、二进制文件等),以便在网络应用中或通过API提供。
- 路径。是主容器中用于保存作业工件的目录的路径
CI/CD的一个有价值的好处是能够对新写的代码执行自动测试。通过在每次修改代码时执行测试,它有助于识别代码中已知和未知的错误。
我们的下一步是在config.yml 文件中定义一个新作业。将以下内容粘贴到该文件中。
run_tests:
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Install npm dependencies
command: |
npm install --save
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
- store_test_results:
path: test-results
下面是我们刚刚添加的内容的明细。
- **docker:和image:**键指定了我们在这个作业中使用的执行器和Docker镜像。
- **command:
npm install --save**密钥安装应用程序中使用的应用程序依赖项 - 名称。运行单元测试执行自动测试,并将其保存到本地目录中,名为
test-results/ - **store_test_results:**是一个特殊的命令,可以将
test-results/目录中的结果保存并钉在CircleCI中的构建上。
这项工作作为一个单元测试功能。它有助于识别代码中的错误。如果其中任何一个测试失败,整个管道构建将失败,并提示开发人员修复错误。我们的目标是让所有的测试和工作都能通过。接下来,我们将创建一个作业,建立一个Docker镜像,并将其推送到Docker Hub注册中心。
工作 - build_docker_image
在本系列的第二部分中,我们手动创建了一个Docker镜像并将其推送到Docker Hub注册中心。在这个工作中,我们将使用自动化来完成这项任务。将此代码块附加到config.yml 文件中。
build_docker_image:
docker:
- image: circleci/node:12
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker image
command: |
export TAG=0.2.<< pipeline.number >>
export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME
docker build -t $DOCKER_LOGIN/$IMAGE_NAME -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push $DOCKER_LOGIN/$IMAGE_NAME
build_docker_image 工作是非常简单明了的。你已经遇到了它使用的大部分CircleCI YAML键,所以我将直接跳到name: Build Docker Image 命令块。
export TAG=0.2.<< pipeline.number >>行定义了一个本地环境变量,使用pipeline.number值,将Docker标签值与正在执行的管道编号联系起来export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME定义了我们将用于命名Docker镜像的变量docker build -t $DOCKER_LOGIN/$IMAGE_NAME -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .使用我们之前设置的项目级变量和我们指定的本地环境变量的组合来执行Docker构建命令。echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin验证我们的Docker Hub凭证以访问平台docker push $DOCKER_LOGIN/$IMAGE_NAME,将新的docker镜像上传到Docker Hub注册处。
这一切看起来和感觉都很熟悉,因为这些都是你在第二部分中手动运行的命令。在这个例子中,我们添加了环境变量的命名位。接下来,我们将建立一个作业来执行Terraform代码,建立一个GKE集群。
工作 - gke_create_cluster
在这个作业中,我们将自动执行在part03/iac_gke_cluster/ 目录中找到的Terraform代码。将这个代码块附加到config.yml 文件中,然后保存。
gke_create_cluster:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Create GKE Cluster
command: |
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
cd part03/iac_gke_cluster/
terraform init
terraform plan -var credentials=$HOME/gcloud_keys -out=plan.txt
terraform apply plan.txt
在这个代码块中需要注意的是执行器Docker镜像image: ariv3ra/terraform-gcp:latest 。这是我建立的一个镜像,它同时安装了Google SDK和Terraform CLI。如果我们不使用这个,我们就需要在这个作业中添加安装步骤,以便每次安装和配置这些工具。environment: CLOUDSDK_CORE_PROJECT: cicd-workshops 键也是一个重要的元素。这设置了我们以后要执行的gcloud cli 命令所需的环境变量值。
代码块中使用的其他元素。
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc是一个解码 值的命令,它创建了Terraform所需的 文件,以访问各自Terraform云工作区的状态数据。$TF_CLOUD_TOKEN./terraformrcecho $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys是一个解码 值的命令,它创建了 所需的 文件来访问GCP。$GOOGLE_CLOUD_KEYSglcoud cligcloud_keysgcloud auth activate-service-account --key-file ${HOME}/gcloud_keys是一个命令,使用我们之前解码和生成的 文件授权访问GCP。gcloud_keys
其余的命令是带有-var 参数的terraform cli 命令,这些参数指定并覆盖了各自Terraformvariables.tf 文件中定义的变量的default 值。一旦terraform apply plan.txt 执行,这个作业将创建一个新的GKE集群。
工作 - gke_deploy_app
在这个作业中,我们将自动执行在part03/iac_kubernetes_app/ 目录中找到的Terraform代码。将这个代码块追加到config.yml文件中,然后保存。
gke_deploy_app:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Deploy App to GKE
command: |
export CLUSTER_NAME="cicd-workshops"
export TAG=0.2.<< pipeline.number >>
export DOCKER_IMAGE="docker-image=${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}:$TAG"
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"
cd part03/iac_kubernetes_app
terraform init
terraform plan -var $DOCKER_IMAGE -out=plan.txt
terraform apply plan.txt
export ENDPOINT="$(terraform output endpoint)"
mkdir -p /tmp/gke/ && echo 'export ENDPOINT='${ENDPOINT} > /tmp/gke/gke-endpoint
- persist_to_workspace:
root: /tmp/gke
paths:
- "*"
下面是这个作业代码块的重要元素和一些我们之前没有讨论过的新元素。
export CLUSTER_NAME="cicd-workshops"定义了一个变量,用来保存我们要部署到的GCP项目的名称。gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"是一个命令,用于从我们在之前的工作中创建的GKE集群中检索 。kubeconfigterraform plan -var $DOCKER_IMAGE -out=plan.txt是一条命令,用于覆盖Terraform 文件中定义的各个变量的 值。variables.tfdefaultexport ENDPOINT="$(terraform output endpoint)"将Terraform命令产生的输出 值分配给本地环境变量,该变量将被保存到一个文件中,并持久化到endpointCircleCI工作区。然后,它可以被检索,从一个附加的CircleCI工作空间,并在后续的工作中使用。
工作 - gke_destroy_cluster
这个作业是我们将为这个管道建立的最后一个作业。它基本上会摧毁我们在之前的CI/CD作业中建立的所有资源和基础设施。作为测试的一部分,短暂的资源被用于烟雾测试、集成测试、性能测试和其他类型。 当这些构造不再需要时,执行destroy命令的作业对于摆脱这些构造是非常好的。
在这个作业中,我们将自动执行在part03/iac_kubernetes_app/ 目录中的Terraform代码。将这个代码块附加到config.yml文件中,然后保存。
gke_destroy_cluster:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Destroy GKE Cluster
command: |
export CLUSTER_NAME="cicd-workshops"
export TAG=0.2.<< pipeline.number >>
export DOCKER_IMAGE="docker-image=${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}:$TAG"
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
cd part03/iac_kubernetes_app
terraform init
gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"
terraform destroy -var $DOCKER_IMAGE --auto-approve
cd ../iac_gke_cluster/
terraform init
terraform destroy -var credentials=$HOME/gcloud_keys --auto-approve
这个作业代码块的重要元素是terraform destroy -var credentials=$HOME/gcloud_keys --auto-approve 命令。这个命令,执行Terraform命令,销毁所有分别在part03/iac_gke_cluster 和part03/iac_kubernetes_app/ 目录下用Terraform代码创建的资源。
现在,我们已经定义了管道中的所有工作,我们准备创建CircleCI工作流程,这将协调工作如何在管道中执行和处理。
创建CircleCI工作流程
我们的下一步是创建工作流程,定义如何执行和处理工作。把工作流程看作是一个有序的工作列表。你可以使用工作流来指定何时和如何执行这些工作。将这个工作流代码块附加到config.yml 文件中。
workflows:
build_test:
jobs:
- run_tests
- build_docker_image
- gke_create_cluster
- gke_deploy_app:
requires:
- run_tests
- build_docker_image
- gke_create_cluster
- approve-destroy:
type: approval
requires:
- gke_create_cluster
- gke_deploy_app
- gke_destroy_cluster:
requires:
- approve-destroy
这个代码块代表了我们管道的工作流定义。以下是这个代码块中的内容。
workflows:键指定一个工作流元素build_test:代表这个工作流程的名称/标识符jobs:键代表在config.yml文件中定义的要执行的工作列表。
在这个列表中,你指定你想在这个管道中执行的工作。这里是我们的工作列表。
- run_tests
- build_docker_image
- gke_create_cluster
- gke_deploy_app:
requires:
- run_tests
- build_docker_image
- gke_create_cluster
- approve-destroy:
type: approval
requires:
- gke_create_cluster
- gke_deploy_app
- gke_destroy_cluster:
requires:
- approve-destroy
run_tests,build_docker_image, 和gke_create_cluster 工作流的作业是平行或并发运行的,与gke_deploy_app: 项目不同的是,它有一个requires: 键。作业默认是并行运行的,所以你必须用一个requires: key和一个在启动指定的作业前必须完成的作业列表,明确要求任何依赖的作业名称。把requires: 键看作是在其他作业的成功上建立依赖关系。这些键让你分割和控制你的管道的执行。
approve-destroy: 项目指定了一个具有手动批准步骤的工作。它需要人工干预,必须有人批准执行工作流工作列表中的下一个工作。下一个工作,gke_destroy_cluster: ,在执行之前取决于approval-destroy: 工作的完成情况。它破坏了管道中先前执行的作业所创建的所有资源。
完整的.config.yml文件
一个完整的config.yml ,基于这个帖子,可以在项目代码repo中的.circleci/ 目录下找到。它在这里供你查阅。
version: 2.1
jobs:
run_tests:
docker:
- image: circleci/node:12
steps:
- checkout
- run:
name: Install npm dependencies
command: |
npm install --save
- run:
name: Run Unit Tests
command: |
./node_modules/mocha/bin/mocha test/ --reporter mochawesome --reporter-options reportDir=test-results,reportFilename=test-results
- store_test_results:
path: test-results
- store_artifacts:
path: test-results
build_docker_image:
docker:
- image: circleci/node:12
steps:
- checkout
- setup_remote_docker:
docker_layer_caching: false
- run:
name: Build Docker image
command: |
export TAG=0.2.<< pipeline.number >>
export IMAGE_NAME=$CIRCLE_PROJECT_REPONAME
docker build -t $DOCKER_LOGIN/$IMAGE_NAME -t $DOCKER_LOGIN/$IMAGE_NAME:$TAG .
echo $DOCKER_PWD | docker login -u $DOCKER_LOGIN --password-stdin
docker push $DOCKER_LOGIN/$IMAGE_NAME
gke_create_cluster:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Create GKE Cluster
command: |
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
cd part03/iac_gke_cluster/
terraform init
terraform plan -var credentials=$HOME/gcloud_keys -out=plan.txt
terraform apply plan.txt
gke_deploy_app:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Deploy App to GKE
command: |
export CLUSTER_NAME="cicd-workshops"
export TAG=0.2.<< pipeline.number >>
export DOCKER_IMAGE="docker-image=${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}:$TAG"
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"
cd part03/iac_kubernetes_app
terraform init
terraform plan -var $DOCKER_IMAGE -out=plan.txt
terraform apply plan.txt
export ENDPOINT="$(terraform output endpoint)"
mkdir -p /tmp/gke/
echo 'export ENDPOINT='${ENDPOINT} > /tmp/gke/gke-endpoint
- persist_to_workspace:
root: /tmp/gke
paths:
- "*"
gke_destroy_cluster:
docker:
- image: ariv3ra/terraform-gcp:latest
environment:
CLOUDSDK_CORE_PROJECT: cicd-workshops
steps:
- checkout
- run:
name: Destroy GKE Cluster
command: |
export CLUSTER_NAME="cicd-workshops"
export TAG=0.2.<< pipeline.number >>
export DOCKER_IMAGE="docker-image=${DOCKER_LOGIN}/${CIRCLE_PROJECT_REPONAME}:$TAG"
echo $TF_CLOUD_TOKEN | base64 -d > $HOME/.terraformrc
echo $GOOGLE_CLOUD_KEYS | base64 -d > $HOME/gcloud_keys
gcloud auth activate-service-account --key-file ${HOME}/gcloud_keys
cd part03/iac_kubernetes_app
terraform init
gcloud container clusters get-credentials $CLUSTER_NAME --zone="us-east1-d"
terraform destroy -var $DOCKER_IMAGE --auto-approve
cd ../iac_gke_cluster/
terraform init
terraform destroy -var credentials=$HOME/gcloud_keys --auto-approve
workflows:
build_test:
jobs:
- run_tests
- build_docker_image
- gke_create_cluster
- gke_deploy_app:
requires:
- run_tests
- build_docker_image
- gke_create_cluster
- approve-destroy:
type: approval
requires:
- gke_create_cluster
- gke_deploy_app
- gke_destroy_cluster:
requires:
- approve-destroy
结论
祝贺你!你刚刚完成了本系列的第三部分,并通过建立一个新的config.yml 文件,使用Terraform执行IaC资源,提升了你的经验。这篇文章解释并演示了config.yml 文件中的一些关键元素以及与CircleCI平台相关的内部概念。
在这个系列中,我们涵盖了许多概念和技术,如Docker、GCP、Kubernetes、Terraform和CircleCI,并包括一些实践经验。我们还涵盖了如何连接你的项目来使用CircleCI,并利用Terraform代码来测试你在目标部署环境中的应用。这个系列旨在增加你对重要的DevOps概念、技术以及它们如何一起工作的知识。
我鼓励你自己和你的团队进行试验;改变代码,增加新的Terraform提供者,重新配置CI/CD工作和管道。互相挑战,利用你所学到的知识和团队想出的其他想法,完成发布目标。通过实验,你将学到比任何博客文章所能教给你的更多。
感谢你对这个系列的关注。我希望你觉得它很有用。请随时在Twitter@punkdata上提出反馈意见。
以下资源将帮助你扩展你的知识。