通过 Spinnaker 和 Google Kubernetes Engine 实现持续交付流水线

本教程介绍如何使用 Google Kubernetes Engine (GKE)、Cloud Source Repositories、Cloud Build 和 Spinnaker 创建持续交付流水线。创建示例应用后,您可以配置这些服务以自动执行构建、测试和部署。修改应用代码时,更改会触发持续交付流水线,以自动重建、重新测试和重新部署新版本。

流水线架构

下图展示了持续交付流水线的架构。

面向开发者和用户的流水线架构。

为保证向用户持续交付应用更新,您需要一种能够以可靠的方式构建、测试和更新软件的自动化流程。代码更改应自动流经包含工件创建、单元测试、功能测试和生产部署的流水线。在某些情况下,您希望代码更新仅应用于部分用户,以便在将其推送到整个用户群之前进行“实战演练”。如果其中某个 Canary 版本不能令人满意,那么您的自动程序必须能够快速回滚软件更改。

利用 GKE 和 Spinnaker,您可以创建稳健的持续交付流程,以确保您的软件在完成开发和验证后尽快发布。尽管快速迭代是您的最终目标,但您必须首先确保每个应用修订版本均经过一系列自动化的验证,之后才成为生产发布的候选版本。通过自动化审核给定的更改后,您还可以手动验证应用,并执行进一步的预发行测试。

在您的团队确定应用已准备好投入生产之后,您的某名团队成员可以批准其进行生产部署。

应用交付流水线

在本教程中,您将构建如下图所示的持续交付流水线。

应用交付流水线的架构。

该流水线的简要步骤如下:

  1. 开发者更改代码并将其推送到代码库。

  2. Cloud Build 检测更改,构建 Docker 映像,测试映像,并将映像推送到 Spinnaker。

  3. Spinnaker 检测映像,将映像部署到 Canary,并测试 Canary 部署。手动批准后,Spinnaker 将映像部署到生产环境中。

目标

  • 启动 Cloud Shell、创建 GKE 集群并配置身份和用户管理方案,完成环境设置。
  • 下载示例应用,创建 Git 代码库,并将其上传到 Cloud Source Repositories。
  • 使用 Helm 将 Spinnaker 部署到 GKE。
  • 构建 Docker 映像。
  • 创建触发器,在应用发生更改时创建 Docker 映像。
  • 将 Spinnaker 流水线配置为以可靠且持续的方式将您的应用部署到 GKE。
  • 部署代码更改、触发流水线,并观察更改发布到生产环境。

费用

本教程使用 Google Cloud Platform (GCP) 的计费组件,包括:

  • GKE
  • Cloud Load Balancing
  • Cloud Build

您可使用价格计算器根据您的预计使用情况来估算费用。

GCP 新用户可能有资格免费试用

准备工作

  1. 登录您的 Google 帐号。

    如果您还没有 Google 帐号,请注册新帐号

  2. 在 GCP Console 的项目选择器页面上,选择或创建 GCP 项目。

    转到项目选择器页面

  3. 确保您的 Google Cloud Platform 项目已启用结算功能。 了解如何确认您的项目已启用结算功能

  4. 启用GKE、Cloud Build 和 Cloud Source Repositories API。

    启用 API

设置环境

在本部分中,您将配置完成本教程所需的基础架构和身份。

启动 Cloud Shell 实例并创建 GKE 集群

您可以从 Cloud Shell 运行本教程中的所有终端命令。

  1. 打开 Cloud Shell:

    打开 Cloud Shell

  2. 使用以下命令创建一个 GKE 集群,部署 Spinnaker 和示例应用:

    gcloud config set compute/zone us-central1-f
    
    gcloud container clusters create spinnaker-tutorial \
        --machine-type=n1-standard-2
    

配置身份和访问权限管理

您可以创建 Cloud Identity and Access Management (Cloud IAM) 服务帐号,以将权限委托给 Spinnaker,从而允许它在 Cloud Storage 中存储数据。Spinnaker 将其流水线数据存储在 Cloud Storage 中,以确保可靠性和弹性。如果您的 Spinnaker 部署意外失败,您可以在几分钟内创建相同的部署,并且可以访问与原始数据相同的流水线数据。

  1. 创建服务帐号:

    gcloud iam service-accounts create  spinnaker-account \
        --display-name spinnaker-account
    
  2. 将服务帐号电子邮件地址和您的当前项目 ID 存储在环境变量中,以供后续命令使用:

    export SA_EMAIL=$(gcloud iam service-accounts list \
        --filter="displayName:spinnaker-account" \
        --format='value(email)')
    export PROJECT=$(gcloud info --format='value(config.project)')
    
  3. storage.admin 角色绑定到您的服务帐号:

    gcloud projects add-iam-policy-binding \
        $PROJECT --role roles/storage.admin --member serviceAccount:$SA_EMAIL
    
  4. 下载服务帐号密钥。您稍后在安装 Spinnaker 并将密钥上传到 GKE 时需要用到此密钥。

    gcloud iam service-accounts keys create spinnaker-sa.json --iam-account $SA_EMAIL
    

设置 Cloud Pub/Sub 以触发 Spinnaker 流水线

  1. 创建 Cloud Pub/Sub 主题,以接收 Container Registry 的通知。此命令可能会失败,并显示“项目中已存在该资源”(Resource already exists in the project) 错误,这表示系统已经为您创建了该主题。

    gcloud beta pubsub topics create projects/$PROJECT/topics/gcr
    
  2. 创建 Spinnaker 可以读取的订阅,以接收推送映像的相关通知。

    gcloud beta pubsub subscriptions create gcr-triggers \
        --topic projects/${PROJECT}/topics/gcr
    
  3. 向 Spinnaker 服务帐号授予读取 gcr-triggers 订阅的权限。

    export SA_EMAIL=$(gcloud iam service-accounts list \
        --filter="displayName:spinnaker-account" \
        --format='value(email)')
    gcloud beta pubsub subscriptions add-iam-policy-binding gcr-triggers \
        --role roles/pubsub.subscriber --member serviceAccount:$SA_EMAIL
    

使用 Helm 部署 Spinnaker

在本部分中,您将使用 HelmCharts 代码库部署 Spinnaker。Helm 是一个程序包管理器,可用于配置和部署 Kubernetes 应用

安装 Helm

  1. 下载并安装 helm 二进制文件:

    wget https://storage.googleapis.com/kubernetes-helm/helm-v2.10.0-linux-amd64.tar.gz
    
  2. 将文件解压缩到本地系统:

    tar zxfv helm-v2.10.0-linux-amd64.tar.gz
    
    cp linux-amd64/helm .
    
  3. 向 Helm 的服务器端 Tiller 授予集群管理员角色:

    kubectl create clusterrolebinding user-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value account)
    kubectl create serviceaccount tiller --namespace kube-system
    kubectl create clusterrolebinding tiller-admin-binding --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
    
  4. 授予 Spinnaker cluster-admin 角色,使其能够在所有命名空间中部署资源:

    kubectl create clusterrolebinding --clusterrole=cluster-admin --serviceaccount=default:default spinnaker-admin
    
  5. 初始化 Helm 以在集群中安装 Tiller:

    ./helm init --service-account=tiller
    ./helm update
  6. 运行以下命令,验证 Helm 已正确安装:如果已正确安装 Helm,则客户端和服务器都将显示 v2.10.0

    ./helm version
    Client: &version.Version{SemVer:"v2.10.0", GitCommit:"9ad53aac42165a5fadc6c87be0dea6b115f93090", GitTreeState:"clean"}
    Server: &version.Version{SemVer:"v2.10.0", GitCommit:"9ad53aac42165a5fadc6c87be0dea6b115f93090", GitTreeState:"clean"}
    

配置 Spinnaker

  1. 为 Spinnaker 创建一个存储分区以存储其流水线配置:

    export PROJECT=$(gcloud info \
        --format='value(config.project)')
    export BUCKET=$PROJECT-spinnaker-config
    gsutil mb -c regional -l us-central1 gs://$BUCKET
  2. 创建配置文件 (spinnaker-config.yaml),描述 Spinnaker 应如何安装:

    export SA_JSON=$(cat spinnaker-sa.json)
    export PROJECT=$(gcloud info --format='value(config.project)')
    export BUCKET=$PROJECT-spinnaker-config
    cat > spinnaker-config.yaml <<EOF
    gcs:
      enabled: true
      bucket: $BUCKET
      project: $PROJECT
      jsonKey: '$SA_JSON'
    
    dockerRegistries:
    - name: gcr
      address: https://gcr.io
      username: _json_key
      password: '$SA_JSON'
      email: 1234@5678.com
    
    # Disable minio as the default storage backend
    minio:
      enabled: false
    
    # Configure Spinnaker to enable GCP services
    halyard:
      spinnakerVersion: 1.10.2
      image:
        tag: 1.12.0
      additionalScripts:
        create: true
        data:
          enable_gcs_artifacts.sh: |-
            \$HAL_COMMAND config artifact gcs account add gcs-$PROJECT --json-path /opt/gcs/key.json
            \$HAL_COMMAND config artifact gcs enable
          enable_pubsub_triggers.sh: |-
            \$HAL_COMMAND config pubsub google enable
            \$HAL_COMMAND config pubsub google subscription add gcr-triggers \
              --subscription-name gcr-triggers \
              --json-path /opt/gcs/key.json \
              --project $PROJECT \
              --message-format GCR
    EOF
    

部署 Spinnaker 图表

  1. 使用 Helm 命令行界面和您的配置集来部署图表:此命令通常需要五到十分钟才能完成。

    ./helm install -n cd stable/spinnaker -f spinnaker-config.yaml --timeout 600 \
        --version 1.1.6 --wait
  2. 命令完成后,在 Cloud Shell 中运行以下命令,设置到 Spinnaker 界面的端口转发:

    export DECK_POD=$(kubectl get pods --namespace default -l "cluster=spin-deck" \
        -o jsonpath="{.items[0].metadata.name}")
    kubectl port-forward --namespace default $DECK_POD 8080:9000 >> /dev/null &
    
  3. 如需打开 Spinnaker 界面,请点击 Cloud Shell 中的网页预览,然后点击在端口 8080 上预览 (Preview on port 8080)。

    网页预览下拉列表(已选择预览)。

  4. 您将看到欢迎屏幕,随即显示 Spinnaker 界面:

    显示有“Hello”的欢迎屏幕。

    Spinnaker 首页。

构建 Docker 映像

在本部分中,您将配置 Cloud Build 以检测应用源代码的更改、构建 Docker 映像,随后将该映像推送到 Container Registry。

创建存储源代码的代码库

  1. 在 Cloud Shell 中,下载示例源代码:

    wget https://gke-spinnaker.storage.googleapis.com/sample-app-v2.tgz
    
  2. 解压缩源代码:

    tar xzfv sample-app-v2.tgz
    
  3. 将目录更改为包含源代码的目录:

    cd sample-app
    
  4. 为代码库中的 Git 提交设置用户名和电子邮件地址。将 [EMAIL_ADDRESS] 替换为您的 Git 电子邮件地址,并将 [USERNAME] 替换为您的 Git 用户名。

    git config --global user.email "[EMAIL_ADDRESS]"
    git config --global user.name "[USERNAME]"
    
  5. 对存储源代码的代码库执行初始提交:

    git init
    git add .
    git commit -m "Initial commit"
    
  6. 创建一个代码库来托管您的代码:

    gcloud source repos create sample-app
    git config credential.helper gcloud.sh
    
  7. 将新创建的代码库添加为远程代码库:

    export PROJECT=$(gcloud info --format='value(config.project)')
    git remote add origin https://source.developers.google.com/p/$PROJECT/r/sample-app
    
  8. 将您代码推送到新代码库的 master 分支:

    git push origin master
  9. 检查您能否在控制台中查看自己的源代码:

    转到“源代码”页面

配置构建触发器

下图展示了您在本部分构建的触发器。

Spinnaker 工作流

您需要配置 Cloud Build,使其在您每次将 Git 标记推送到源代码库时构建并推送 Docker 映像。Cloud Build 会自动检查您的源代码,通过代码库中的 Dockerfile 构建 Docker 映像,并将该映像推送到 Container Registry。

  1. 在 GCP Console 的 Cloud Build 部分中,点击构建触发器

    转到“构建触发器”页面

  2. 选择 Cloud Source Repository,然后点击继续

  3. 从列表中选择新创建的 sample-app 代码库,然后点击继续

  4. 设置以下触发器设置:

    • 名称sample-app-tags
    • 触发器类型:标记
    • 标记(正则表达式)v.*
    • 构建配置cloudbuild.yaml
    • cloudbuild.yaml 位置cloudbuild.yaml
  5. 点击创建触发器

    创建触发器时的触发器设置。

从现在开始,每当您将以字母“v”为前缀的 Git 标记推送到存储源代码的代码库时,Cloud Build 都会自动将您的应用构建为 Docker 映像,并将其推送到 Container Registry。

准备 Kubernetes 清单以便在 Spinnaker 中使用

Spinnaker 需要访问您的 Kubernetes 清单以将其部署到您的集群。本部分将创建一个 Cloud Storage 存储分区,在 Cloud Build 中的 CI 过程中,系统将使用您的清单填充该存储分区。在您的清单显示在 Cloud Storage 中后,Spinnaker 即可在流水线执行期间下载并应用这些清单。

  1. 创建存储分区。

    export PROJECT=$(gcloud info --format='value(config.project)')
    gsutil mb -l us-central1 gs://$PROJECT-kubernetes-manifests
    
  2. 在存储分区上启用版本控制,以保证您可以获得清单的历史记录。

    gsutil versioning set on gs://$PROJECT-kubernetes-manifests
    
  3. 在 Kubernetes 部署清单中设置正确的 GCP 项目 ID:

    sed -i s/PROJECT/$PROJECT/g k8s/deployments/*
    
  4. 将更改提交到代码库:

    git commit -a -m "Set project ID"
    

构建您的映像

使用以下步骤推送您的第一个映像:

  1. 转到 Cloud Shell 中的源代码文件夹。
  2. 创建一个 Git 标记:

    git tag v1.0.0
  3. 推送标记:

    git push --tags
  4. Cloud Build 中,点击构建记录以检查是否已触发构建。如果没有,请验证在上一部分中是否正确配置了触发器。

    转到“构建记录”

    构建记录

配置部署流水线

现在您的映像已在自动构建,接下来您需要将它们部署到 Kubernetes 集群。

下图展示了部署流水线步骤。

配置部署流水线。

您可以部署到规模缩减的环境以进行集成测试。集成测试通过后,您必须手动批准更改以将代码部署到生产服务。

安装 spin CLI 以管理 Spinnaker

Spin 是一种用于管理 Spinnaker 应用和流水线的命令行实用程序。

  1. 下载最新版本的 spin

    curl -LO https://storage.googleapis.com/spinnaker-artifacts/spin/1.5.2/linux/amd64/spin
    
  2. spin 转化为可执行文件。

    chmod +x spin
    

创建部署流水线

  1. 使用 spin 在 Spinnaker 中创建应用。

    ./spin application save --application-name sample \
                            --owner-email example@example.com \
                            --cloud-providers kubernetes \
                            --gate-endpoint http://localhost:8080/gate
    

接下来,您要创建持续交付流水线。在本教程中,流水线配置为检测带有前缀为“v”的标记的 Docker 映像到达了您的 Container Registry。

  1. 在 Cloud Shell 的新标签页中,在源代码目录中运行以下命令,以将示例流水线上传到 Spinnaker 实例:

    export PROJECT=$(gcloud info --format='value(config.project)')
    sed s/PROJECT/$PROJECT/g spinnaker/pipeline-deploy.json > pipeline.json
    ./spin pipeline save --gate-endpoint http://localhost:8080/gate -f pipeline.json
    

查看流水线执行情况

您刚刚创建的配置使用新的带标记映像的推送通知来触发 Spinnaker 流水线。在上一步中,您将标记推送到 Cloud Source Repositories,后者触发 Cloud Build 以构建映像并将其推送到 Container Registry。现在,您可以检查已触发的流水线。

  1. 点击流水线返回“流水线”页面。

  2. 点击详细信息以查看有关流水线进度的更多信息。此部分显示部署流水线的状态及其步骤。蓝色步骤表示正在运行的步骤,绿色步骤是已成功完成的步骤,红色步骤则是已失败的步骤。点击一个阶段以查看有关它的详细信息。

    3 到 5 分钟后,集成测试阶段完成,流水线需要手动批准才能继续部署。

  3. 将光标悬停在推送至生产环境 (Push to production) 上,然后点击继续

    您的发布将继续进入到生产前端和后端部署。操作会在几分钟后完成。

  4. 如需查看应用,请在 Spinnaker 界面选择基础架构 > 负载平衡器

    基础架构下拉列表(已选择“负载平衡器”)。

  5. 在负载平衡器列表中向下滚动,然后在 sample-frontend-production 下点击默认

    默认负载平衡器。

  6. 在右侧的“详细信息”窗格中向下滚动,然后点击 Ingress IP 处的剪贴板按钮复制应用的 IP 地址。Spinnaker 界面的 Ingress IP 链接默认使用 HTTPS,但应用配置为使用 HTTP。

    包含应用 IP 地址的“详细信息”窗格。

  7. 将地址粘贴到浏览器中以查看应用的生产版本。

    应用的生产版本。

    现在,您已手动触发流水线以构建、测试和部署您的应用。

通过代码更改触发流水线

在本部分中,您将通过执行代码更改、推送 Git 标记以及观察响应中的流水线运行情况来对流水线执行端到端的测试。通过推送以“v”开头的 Git 标记,您可以触发 Cloud Build 构建新的 Docker 映像并将其推送到 Container Registry。Spinnaker 检测到以“v”开头的新映像标记,并触发流水线以将该映像部署到 Canary 版、运行测试,然后将相同的映像发布到部署中的所有 pod。

  1. 将应用的颜色从橙色更改为蓝色:

    sed -i 's/orange/blue/g' cmd/gke-info/common-service.go
    
  2. 标记您的更改并将其推送到存储源代码的代码库:

    git commit -a -m "Change color to blue"
    git tag v1.0.1
    git push --tags
    
  3. Cloud Build 构建记录中,您会看到新版本。

  4. 点击流水线以观察流水线开始部署映像。

  5. 观察 Canary 部署。在部署暂停后,等待其发布到生产,开始刷新包含应用的标签。您的四个后端正在运行该应用的早期版本,而只有一个后端正在运行 Canary 版。大约每刷新十次,您就应该会看到应用的新蓝色版本。

  6. 测试完成后,返回 Spinnaker 标签并批准部署。

  7. 流水线完成后,您的应用类似于下图所示。版本字段的值现在是 v1.0.1

    已更新应用的生产版本。

    至此,您已成功将应用发布到整个生产环境!

  8. (可选)您可以通过还原先前的提交来回滚此更改。回滚会添加一个新标记 (v1.0.2),并通过用于部署 v1.0.1 的相同流水线将标记推回:

    git revert v1.0.1
    git tag v1.0.2
    git push --tags

清理

为避免因本教程中使用的资源而导致您的 Google Cloud Platform 帐号产生费用,请执行以下操作:

  1. 删除 Spinnaker 安装:

    ../helm delete --purge cd
    
  2. 删除示例应用服务:

    kubectl delete -f k8s/services
    
  3. 删除服务帐号 IAM 绑定:

    export SA_EMAIL=$(gcloud iam service-accounts list \
        --filter="displayName:spinnaker-account" --format='value(email)')
    export PROJECT=$(gcloud info --format='value(config.project)')
    gcloud projects remove-iam-policy-binding $PROJECT --role roles/storage.admin --member serviceAccount:$SA_EMAIL
    
  4. 删除服务帐号:

    export SA_EMAIL=$(gcloud iam service-accounts list \
        --filter="displayName:spinnaker-account" --format='value(email)')
    gcloud iam service-accounts delete $SA_EMAIL
    
  5. 删除 GKE 集群:

    gcloud container clusters delete spinnaker-tutorial --zone=us-central1-f
    
  6. 删除代码库:

    gcloud source repos delete sample-app
    
  7. 删除存储分区:

    export PROJECT=$(gcloud info --format='value(config.project)')
    export BUCKET=$PROJECT-spinnaker-config
    gsutil -m rm -r gs://$BUCKET
    
  8. 删除容器映像:

    export PROJECT=$(gcloud info --format='value(config.project)')
    gcloud container images delete gcr.io/$PROJECT/sample-app:v1.0.0
    gcloud container images delete gcr.io/$PROJECT/sample-app:v1.0.1
    
  9. 如果您在上面的可选回滚步骤中创建了 v1.0.2,请删除该容器映像:

    gcloud container images delete gcr.io/$PROJECT/sample-app:v1.0.2
    

后续步骤