使用 GitHub Actions 工作流将 .NET 应用部署到 Google Kubernetes Engine


本教程介绍了如何使用 GitHub Actions 工作流,将使用 .NET 的 ASP.NET Model-View-Controller (MVC) Web 应用部署到 Google Kubernetes Engine (GKE)。

本教程适用于具备 Microsoft .NET、GitHub Actions 和 GKE 基础知识的开发者和 DevOps 工程师。您还需要有一个 GitHub 帐号来执行本教程。

目标

将使用 .NET 6.0 并在 Linux 上运行的 ASP.NET Core Web 应用部署到 Google Kubernetes Engine

下图显示了将 ASP.NET MVC Web 应用部署到 Google Kubernetes Engine (GKE) 的 GitHub Actions 工作流。

GitHub Actions 工作流的概念图

本教程介绍了如何完成以下任务来实现目标:

  • 创建一个 GitHub 代码库。
  • 配置身份验证
  • 部署 GKE 集群和 Artifact Registry 代码库
  • 创建 GitHub Actions 工作流

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

  3. 确保您的 Google Cloud 项目已启用结算功能

  4. 启用 Artifact Registry and Google Kubernetes Engine API。

    启用 API

  5. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

  6. 确保您的 Google Cloud 项目已启用结算功能

  7. 启用 Artifact Registry and Google Kubernetes Engine API。

    启用 API

创建一个 GitHub 代码库。

在 GitHub 帐号中创建 dotnet-docs-samples 代码库的分支。必须执行此步骤,因为我们要使用 GitHub Actions 进行构建。

  1. 转到 dotnet-docs-samples GitHub 代码库。
  2. 点击创建分支 (Fork)。
  3. 创建新的分支页面上,进行以下设置:

    • 所有者 - 您的 GitHub 帐号
    • 代码库名称 - dotnet-docs-samples
  4. 点击创建分支

配置身份验证

准备您的 Google Cloud 项目,以便 GitHub Actions 可以对 Google Cloud 项目中的资源进行身份验证和访问。

创建工作负载身份联合池和提供方

如需允许 GitHub Actions 对 Google Cloud 进行身份验证并部署到 GKE,请使用工作负载身份联合。通过使用工作负载身份联合,您无需为 GitHub Actions 工作负载存储和管理服务帐号密钥

使用工作负载身份联合需要工作负载身份池和提供方。我们建议您使用专用项目来管理工作负载身份池和提供方。 为简单起见,在此教程中,为简单起见,请在 GKE 集群所在的项目中创建池和提供程序:

  1. 打开 Google Cloud 控制台。

  2. 在 Google Cloud 控制台中,激活 Cloud Shell。

    激活 Cloud Shell

    Cloud Shell 会话随即会在 Google Cloud 控制台的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Google Cloud CLI 且已为当前项目设置值的 Shell 环境。该会话可能需要几秒钟时间来完成初始化。

  3. 创建新的工作负载身份池:

    gcloud iam workload-identity-pools create github-actions \
        --location="global" \
        --description="GitHub Actions tutorial" \
        --display-name="GitHub Actions"
    
  4. 将 GitHub Actions 添加为工作负载身份池提供方:

    gcloud iam workload-identity-pools providers create-oidc github-actions-oidc \
        --location="global" \
        --workload-identity-pool=github-actions \
        --issuer-uri="https://token.actions.githubusercontent.com/" \
        --attribute-mapping="google.subject=assertion.sub"
    

创建服务帐号

  1. 在 Cloud Shell 中,创建一个服务帐号,供 GitHub Actions 用来发布 Docker 映像并部署到 GKE:

    SERVICE_ACCOUNT=$(gcloud iam service-accounts create github-actions-workflow \
      --display-name "GitHub Actions workflow" \
      --format "value(email)")
    
  2. 向服务帐号授予 Artifact Registry Writer 角色 (roles/artifactregistry.writer),以允许 GitHub 操作推送到 Artifact Registry:

    gcloud projects add-iam-policy-binding $(gcloud config get-value core/project) \
      --member serviceAccount:$SERVICE_ACCOUNT \
      --role roles/artifactregistry.writer
    
  3. 向服务帐号授予 Google Kubernetes Engine Developer 角色 (roles/container.developer),以允许 GitHub 操作推送到 Artifact Registry:

    gcloud projects add-iam-policy-binding $(gcloud config get-value core/project) \
      --member serviceAccount:$SERVICE_ACCOUNT \
      --role roles/container.developer
    

允许 GitHub Actions 工作流使用服务帐号

允许 GitHub Actions 工作流模拟和使用服务帐号:

  1. 初始化一个环境变量,其中包含 GitHub Actions 工作流所使用的主题。主题与唯一标识 GitHub 代码库和分支的用户名类似:

    SUBJECT=repo:OWNER/dotnet-docs-samples:ref:refs/heads/main
    

    OWNER 替换为您的 GitHub 用户名。

  2. 向主题授予模拟服务帐号的权限:

    PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value core/project) --format='value(projectNumber)')
    
    gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT \
      --role=roles/iam.workloadIdentityUser \
      --member="principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/github-actions/subject/$SUBJECT"
    

部署 GKE 集群和 Artifact Registry 代码库

  1. 为 Docker 映像创建代码库:

    gcloud artifacts repositories create clouddemo \
      --repository-format=docker \
      --location=us-central1
    
  2. 允许 Compute Engine 默认服务帐号访问该代码库:

    gcloud projects add-iam-policy-binding $(gcloud config get-value core/project) \
      --member=serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
      --role=roles/artifactregistry.reader
    
  3. 创建集群:

    gcloud container clusters create clouddemo-linux \
      --enable-ip-alias \
      --zone us-central1-a
    

    此命令可能需要几分钟才能完成。

  4. 获取集群的项目名称和项目编号:

    echo "Project ID: $(gcloud config get-value core/project)"
    echo "Project Number: $(gcloud projects describe $(gcloud config get-value core/project) --format=value\(projectNumber\))"
    

    您稍后需要这些值。

创建 GitHub Actions 工作流

您现在可以使用 GitHub Actions 设置持续集成。对于推送到 Git 代码库的每项提交,GitHub Actions 工作流都会构建代码并将构建工件打包到 Docker 容器中。然后将容器发布到 Artifact Registry。

该代码库已包含以下 Dockerfile:

#
# Copyright 2020 Google LLC
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

FROM mcr.microsoft.com/dotnet/aspnet:6.0
EXPOSE 8080

#------------------------------------------------------------------------------
# Copy publishing artifacts.
#------------------------------------------------------------------------------

WORKDIR /app
COPY CloudDemo.MvcCore/bin/Release/net6.0/publish/ /app/

ENV ASPNETCORE_URLS=http://0.0.0.0:8080

#------------------------------------------------------------------------------
# Run application in Kestrel.
#------------------------------------------------------------------------------

ENTRYPOINT ["dotnet", "CloudDemo.MvcCore.dll"]

该代码库还包含一个 Kubernetes 清单:

#
# Copyright 2020 Google LLC
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

apiVersion: v1
kind: Service
metadata:
  name: clouddemo-netcore
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: clouddemo-netcore
  type: NodePort

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: clouddemo-netcore
spec:
  defaultBackend:
    service:
      name: clouddemo-netcore
      port:
        number: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: clouddemo-netcore
spec:
  replicas: 2
  selector:
    matchLabels:
      app: clouddemo-netcore
  template:
    metadata:
      labels:
        app: clouddemo-netcore
    spec:
      containers:
      - name: clouddemo-netcore
        image: CLOUDDEMO_IMAGE
        ports:
          - containerPort: 8080
        livenessProbe:      # Used by deployment controller
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:     # Used by Ingress/GCLB
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 3
          periodSeconds: 5
        resources:
          limits:
            memory: 1024Mi
          requests:
            memory: 256Mi

创建一个 GitHub Actions 工作流,以便执行以下操作:

  • 使用工作负载身份联合和您之前创建的服务帐号向 Google Cloud 进行身份验证。
  • 构建 Docker 映像并将其部署到 Artifact Registry。
  • 将 Docker 映像部署到 GKE。

如需创建 GitHub Actions 工作流,请执行以下操作:

  1. GitHub 上,转到 dotnet-docs-samples 代码库的分支。
  2. 依次点击添加文件 > 创建新文件
  3. 为文件命名文本字段中,输入以下名称:

    .github/workflows/deploy-gke.yaml
    
  4. 将以下代码复制到该文件中:

    name: Build and Deploy to GKE
    
    on:
      push:
        branches:
          - main
    
    env:
      PROJECT_ID: PROJECT_ID
      PROJECT_NUMBER: PROJECT_NUMBER
    
      CLUSTER: clouddemo-linux
      CLUSTER_ZONE: us-central1-a
    
      REPOSITORY: clouddemo
      REPOSITORY_REGION: us-central1
    
      IMAGE: clouddemo
    
    jobs:
      build:
        runs-on: ubuntu-latest
        permissions:
          id-token: write
          contents: read
    
        steps:
        - name: Checkout
          uses: actions/checkout@v3
    
        #
        # Authenticate to Google Cloud using workload identity federation
        #
        - id: 'auth'
          name: 'Obtain access token by using workload identity federation'
          uses: 'google-github-actions/auth@v0'
          with:
            create_credentials_file: true
            token_format: access_token
            workload_identity_provider: projects/${{ env.PROJECT_NUMBER }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions-oidc
            service_account: github-actions-workflow@${{ env.PROJECT_ID }}.iam.gserviceaccount.com
    
        - name: Connect to Artifact Registry
          run: |-
            echo ${{ steps.auth.outputs.access_token }} | docker login -u oauth2accesstoken --password-stdin https://${{ env.REPOSITORY_REGION }}-docker.pkg.dev
    
        - name: Connect to GKE
          uses: google-github-actions/get-gke-credentials@v0
          with:
            cluster_name: ${{ env.CLUSTER }}
            location: ${{ env.CLUSTER_ZONE }}
    
        #
        # Build the .NET code
        #
        - name: Build solution
          run: |-
            dotnet publish applications/clouddemo/netcore/CloudDemo.MvcCore.sln \
                --configuration Release \
                --framework net6.0
    
        #
        # Build the Docker image and push it to Artifact Registry
        #
        - name: Create image tag
          run: echo "IMAGE_TAG=${{ env.REPOSITORY_REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE }}:$GITHUB_SHA" >> $GITHUB_ENV
    
        - name: Lock image version in deployment.yaml
          run: sed -i 's|CLOUDDEMO_IMAGE|${{ env.IMAGE_TAG }}|g' applications/clouddemo/netcore/deployment.yaml
    
        - name: Build Docker image
          run: docker build --tag "${{ env.IMAGE_TAG }}" applications/clouddemo/netcore
    
        - name: Publish Docker image to Google Artifact Registry
          run: docker push "${{ env.IMAGE_TAG }}"
    
        #
        # Deploy to GKE
        #
        - name: Deploy to GKE
          run: kubectl apply -f applications/clouddemo/netcore/deployment.yaml
        

    替换以下值:

    • PROJECT_ID:包含 GKE 集群的项目的 ID。
    • PROJECT_NUMBER:包含 GKE 集群的项目的编号。
  5. 提交新文件部分中,输入提交消息(例如 Add workflow),然后点击提交新文件

  6. 点击菜单中的操作,然后等待工作流完成。

    工作流可能需要几分钟才能完成。

  7. 在 Google Cloud 控制台中,刷新 Service 和 Ingress 页面。

    转到“Service 和 Ingress”

  8. 前往 Ingress 标签页。

  9. 找到 clouddemo 集群的 Ingress 服务,并等待其状态切换为正常。这可能需要几分钟。

  10. 打开同一行的前端列中的链接。负载均衡器需要几分钟才能使用。因此,CloudDemo 应用最初可能无法加载。负载均衡器准备就绪后,您会看到包含自定义标题的 CloudDemo 应用,这次在生产集群中运行。

清理

完成本教程后,您可以清理您创建的资源,让它们停止使用配额,以免产生费用。以下部分介绍如何删除或关闭这些资源。

删除 GitHub 代码库

如需删除 GitHub 代码库,请参阅删除代码库。 删除代码库会导致所有源代码更改丢失。

删除项目

若要避免产生费用,最简单的方法是删除您为本教程创建的项目。

    删除 Google Cloud 项目:

    gcloud projects delete PROJECT_ID

后续步骤