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


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

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

目标

将使用 .NET Framework 4 并在 Windows 上运行的 ASP.NET MVC 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. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

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

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

    启用 API

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  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-windows \
      --enable-ip-alias \
      --zone us-central1-a
    
    gcloud container node-pools create clouddemo-windows-pool \
      --cluster=clouddemo-windows \
      --image-type=WINDOWS_LTSC_CONTAINERD \
      --no-enable-autoupgrade \
      --machine-type=n1-standard-2 \
      --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/windows/servercore/iis:windowsservercore-ltsc2019
EXPOSE 80
SHELL ["powershell", "-command"]

#------------------------------------------------------------------------------
# Add LogMonitor so that IIS and Windows logs are emitted to STDOUT and can be
# picked up by Docker/Kubernetes.
#
# See https://github.com/microsoft/windows-container-tools/wiki/Authoring-a-Config-File
# for details.
#------------------------------------------------------------------------------

ADD https://github.com/microsoft/windows-container-tools/releases/download/v1.1/LogMonitor.exe LogMonitor/
ADD LogMonitorConfig.json LogMonitor/

#------------------------------------------------------------------------------
# Copy publishing artifacts to webroot.
#------------------------------------------------------------------------------

ADD CloudDemo.Mvc/bin/Release/PublishOutput/ c:/inetpub/wwwroot/

#------------------------------------------------------------------------------
# Configure IIS using the helper functions from deployment.ps1.
#------------------------------------------------------------------------------

ADD deployment.ps1 /
RUN . /deployment.ps1; \
	Install-Iis; \
	Register-WebApplication -AppName "CloudDemo"; \
	Remove-Item /deployment.ps1

#------------------------------------------------------------------------------
# Run IIS, wrapped by LogMonitor.
#------------------------------------------------------------------------------

ENTRYPOINT ["C:\\LogMonitor\\LogMonitor.exe", "C:\\ServiceMonitor.exe", "w3svc"]

该代码库还包含一个 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-net4
  annotations:
    cloud.google.com/neg: '{"ingress": false}' # Disable NEG

spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: clouddemo-net4
  type: NodePort

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

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: clouddemo-net4
spec:
  replicas: 2
  selector:
    matchLabels:
      app: clouddemo-net4
  template:
    metadata:
      labels:
        app: clouddemo-net4
    spec:
      nodeSelector:
        kubernetes.io/os: windows
      containers:
      - name: clouddemo-net4
        image: CLOUDDEMO_IMAGE
        ports:
          - containerPort: 80
        livenessProbe:      # Used by deployment controller
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 120
          periodSeconds: 5
        readinessProbe:     # Used by Ingress/GCLB
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 10
          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-windows
      CLUSTER_ZONE: us-central1-a
    
      REPOSITORY: clouddemo
      REPOSITORY_REGION: us-central1
    
      IMAGE: clouddemo
    
    jobs:
      build:
        runs-on: windows-2019  # Matches WINDOWS_LTSC in GKE
        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: Setup MSBuild
          uses: microsoft/setup-msbuild@v1
    
        - name: Setup NuGet
          uses: NuGet/setup-nuget@v1
    
        - name: Restore NuGet packages
          run: nuget restore applications/clouddemo/net4/CloudDemo.Mvc.sln
    
        - name: Build solution
          run: msbuild /t:Rebuild /p:Configuration=Release /p:DeployOnBuild=true /p:PublishProfile=FolderProfile /nologo applications/clouddemo/net4/CloudDemo.Mvc.sln
    
        #
        # 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 }}:$env:GITHUB_SHA" >> $env:GITHUB_ENV
    
        - name: Lock image version in deployment.yaml
          shell: pwsh
          run: (Get-Content applications\clouddemo\net4\deployment.yaml) -replace "CLOUDDEMO_IMAGE","${{ env.IMAGE_TAG }}" | Out-File -Encoding ASCII applications\clouddemo\net4\deployment.yaml
    
        - name: Build Docker image
          run: docker build --tag "${{ env.IMAGE_TAG }}" applications/clouddemo/net4
    
        - 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/net4/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

后续步骤