在 GKE Windows 容器中使用 Windows 身份验证部署 ASP.NET 应用

本教程介绍如何创建将 IIS 与集成式 Windows 身份验证结合使用的 ASP.NET Web 应用,以及如何使用 Windows 容器将其部署到具有已加入网域的 Windows Server 节点的 Google Kubernetes Engine (GKE) 集群。此配置适用于在 Google Cloud 上的 Windows 容器中部署 ASP.NET 应用,以便应用可以向其他 Windows 资源进行身份验证。本教程还介绍了如何在 Active Directory 中创建群组代管式服务帐号 (gMSA),以及如何在 GKE 中配置 Web 应用部署以使用它。

本教程适用于系统管理员。其假定您熟悉 Active Directory 并具有使用 Google Kubernetes Engine (GKE) 的经验。

目标

  • 创建具有已加入网域的 Windows Server 节点的 GKE 集群,并对该集群进行配置以支持 Active Directory gMSA。
  • 构建和部署将 IIS 与集成式 Windows 身份验证结合使用的 ASP.NET Web 应用容器映像。

费用

本教程使用 Google Cloud 的以下收费组件:

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

准备工作

  1. 按照为虚拟机配置 Active Directory 以自动加入网域教程中的步骤创建加入 Active Directory 网域的 Cloud Run 服务。

  2. 如果您要在不同于创建虚拟机所在的 Google Cloud 项目中运行本教程来测试自动网域加入,请在您的 Cloud 项目中执行有关为项目启用自动网域加入功能的步骤。

    完成另一个教程后,您将拥有一个新的 Cloud Run 服务,并且其网址会输出到 PowerShell 窗口($RegisterUrl 变量的值)。请记下该服务的地址,因为您在本教程中会用到它。

  3. 确保您已启用适用于 Compute Engine、Google Kubernetes Engine、Cloud Build、Container Registry 和 Resource Manager API 的 API:

    启用 API

    完成本教程后,您可以删除您创建的资源以避免继续计费。如需了解详情,请参阅清理

简介

在已加入网域的 Windows Server 中运行的基于 Windows 的应用通常使用 Microsoft Active Directory (AD) 身份对用户和应用进行身份验证。这方面的常见用例如下:

  • 创建使用集成式 Windows 身份验证 的 ASP.NET Web 应用,以在 Active Directory 用户尝试登录该 Web 应用时对这些用户进行身份验证。
  • 创建使用服务器的 Active Directory 计算机帐号通过网络访问资源(例如远程 SMB 共享或远程 Microsoft SQL Server 实例)的应用。

Windows 容器无法加入网域,因此没有 Active Directory 中的计算机帐号。因此,在 Windows 容器上运行的 ASP.NET Web 应用无法通过集成的 Windows 身份验证对 Active Directory 用户进行身份验证,因此无法访问网络中的安全资源。如需详细了解 ASP.NET 如何访问受保护的资源,请参阅 Microsoft 文档中的应用池身份

Windows 容器可以使用 Active Directory 群组代管式服务帐号 (gMSA) 身份访问网络中的 Active Directory 和其他安全资源(例如文件共享和 SQL Server 实例),而不是使用计算机帐号。如需了解详情,请参阅 Microsoft 文档中的群组代管式服务帐号概览

以下架构图展示了本教程中使用的资源:

GKE 上已加入 Active Domain 网域的 Windows 容器。

下图展示了以下元素:

  • 开发虚拟机。在本教程中,您将创建一个 Windows 虚拟机,用于构建 ASP.NET Web 应用容器映像和创建 gMSA。
  • GKE 集群和节点。本教程中的 GKE 集群有一个 Linux 节点池和一个 Windows Server 节点池,其用途如下:
    • Linux 节点运行仅在 Linux 操作系统上运行的系统组件,例如 GKE 指标服务器。
    • Windows Server 节点用于托管 Windows Server 容器,并已加入 Active Directory 网域。
  • Active Directory 基础架构。如需将 GKE Windows 节点加入网域,请首先按照配置 Active Directory 以使虚拟机自动加入网域教程操作。在该教程中,您将创建一个 Cloud Run 服务,该服务负责在 Active Directory 中注册新计算机(实例),并为每个新实例提供临时密码,供实例用于完成网域加入过程。Windows Server 节点池中的每个新实例都会调用 Cloud Run 服务,以将自身加入 Active Directory 网域。
  • 网络负载均衡器。当本地用户打开其浏览器并浏览到 ASP.NET Web 应用时,流量会通过网络负载均衡器。负载均衡器是在您为 Web 应用创建 GKE LoadBalancer 服务时由 GKE 创建的。用户还会将其 Active Directory 凭据传递给 Web 应用,以向 Web 应用进行身份验证。

创建基础架构

完成相关教程后,您将为当前教程创建基础架构组件,其中包括:

  • 具有 ASP.NET Web 应用容器映像的 Windows Server 虚拟机。
  • 具有 Windows Server 节点池的 GKE 集群。
  • 为 GKE Pod 提供对 Active Directory 的访问权限的防火墙规则。
  • GKE 集群中的网络钩子,用于处理部署中 gMSA 资源的配置和填充。

创建开发虚拟机

您创建的 Windows Server 容器映像必须与您在其中构建容器映像的虚拟机的 Windows Server 版本匹配。此版本还必须与 GKE Window Server 节点的 Windows Server 版本匹配。在不同版本的 Windows Server 中创建容器映像或运行容器会导致错误。如需详细了解 Windows 容器的兼容性要求,请参阅匹配容器主机版本和容器映像版本

本教程将长期服务渠道 (LTSC) 2019 版本的 Windows Server 用于虚拟机、GKE 中的 Windows Server 节点以及容器映像。如需了解详情,请参阅 Windows Server 版本和 GKE 版本之间的版本映射

  1. 在 Cloud Console 中,激活 Cloud Shell。

    激活 Cloud Shell

    Cloud Shell 会话随即会在 Cloud Console 的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Cloud SDK 的 Shell 环境,其中包括 gcloud 命令行工具以及已为当前项目设置的值。该会话可能需要几秒钟时间来完成初始化。

  2. 如果 PowerShell 已打开,请输入 exit 将其关闭。
  3. 为网络和子网名称以及 Active Directory 服务网址设置环境变量:

    export NETWORK_NAME=NETWORK-NAME
    export SUBNETWORK_NAME=SUBNETWORK-NAME
    export AD_JOIN_SERVICE_URL=AD-JOIN-SERVICE-URL
    

    请替换以下内容:

    • NETWORK-NAME:要在其中部署虚拟机的 VPC 网络。
    • SUBNETWORK-NAME:要在其中部署虚拟机的子网。
    • AD-JOIN-SERVICE-URL:您在准备工作部分中部署的 Cloud Run 服务的网址。
  4. 为当前环境设置 Google Cloud 项目 ID 和区域:

    gcloud config set project PROJECT-ID
    gcloud config set compute/zone ZONE-NAME
    

    请替换以下内容:

    • PROJECT-ID:您的 Google Cloud 项目 ID。
    • ZONE-NAME:要在其中部署所有虚拟机的可用区。为缩短延迟时间,我们建议您在其中部署了加入 Active Directory 网域的 Cloud Run 服务的区域中选择可用区。
  5. 为开发虚拟机创建服务帐号:

    export SERVICE_ACCOUNT_NAME=dev-vm
    export SERVICE_ACCOUNT_EMAIL=$SERVICE_ACCOUNT_NAME@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com
    
    gcloud iam service-accounts create $SERVICE_ACCOUNT_NAME \
        --display-name="Development VM Service Account"
    
  6. 为该服务帐号授予对 Container Registry 的访问权限

    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
        --member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
        --role="roles/storage.admin"
    
    gsutil iam ch serviceAccount:$SERVICE_ACCOUNT_EMAIL:objectAdmin \
        gs://artifacts.$GOOGLE_CLOUD_PROJECT.appspot.com
    
  7. 为该服务帐号授予对 GKE 的访问权限:

    gcloud projects add-iam-policy-binding $GOOGLE_CLOUD_PROJECT \
        --member="serviceAccount:$SERVICE_ACCOUNT_EMAIL" \
        --role="roles/container.admin"
    

    该服务帐号会被授予 container.admin 角色,因为此角色既有权在项目中创建 GKE 集群,又有权管理集群中的资源(包括基于角色的访问权限控制 (RBAC) 资源)。需要具有 RBAC 资源才能控制哪个 Pod 有权使用 gMSA。

  8. 创建新的 Windows Server 虚拟机:

    gcloud compute instances create gmsa-dev-vm \
        --image-project windows-cloud \
        --image-family windows-2019-core-for-containers \
        --machine-type n1-standard-2 \
        --boot-disk-type=pd-ssd \
        --boot-disk-size=100GB \
        --network $NETWORK_NAME \
        --subnet $SUBNETWORK_NAME \
        --service-account=$SERVICE_ACCOUNT_EMAIL \
        --scopes https://www.googleapis.com/auth/cloud-platform \
        --metadata sysprep-specialize-script-ps1="iex((New-Object System.Net.WebClient).DownloadString('$AD_JOIN_SERVICE_URL')); Add-WindowsFeature RSAT-AD-PowerShell"
    

    范围 https://www.googleapis.com/auth/cloud-platform 允许实例访问所有 Google Cloud API,具体取决于为实例的服务帐号定义的 IAM 角色。

    虚拟机是使用外部 IP 地址创建的,使其能够与互联网通信。您需要虚拟机具有互联网访问权限,以便它可以下载多个实用程序(例如 gitkubectl),并且可以从 GitHub 下载 ASP.NET Web 应用。

    sysprep 阶段,新实例会加入 Active Directory 网域,使您能够使用网域帐号远程访问该实例。sysprep 命令中的脚本还会为 Active Directory 安装 PowerShell 模块。

创建 GKE 集群

  1. 在 Cloud Shell 中,为 GKE 集群的名称设置环境变量:

    export GKE_CLUSTER_NAME=cluster-1
    
  2. 创建 GKE 集群:

    gcloud container clusters create $GKE_CLUSTER_NAME \
        --release-channel rapid \
        --network $NETWORK_NAME \
        --subnetwork $SUBNETWORK_NAME \
        --enable-ip-alias
    

    --release-channel 参数设置为 rapid 会使用最新 Kubernetes 版本部署 GKE 集群。--enable-ip-alias 参数会启用别名 IP 地址。别名 IP 是 Windows Server 节点所必需的。

在 GKE 中创建 Windows Server 节点池

通过 CLI 创建新的 GKE 集群时,系统会使用 Linux 节点池创建该集群。如需在 GKE 上使用 Windows Server,您需要创建 Windows Server 节点池。

GKE 集群必须至少具有一个 Linux 节点来运行集群的内部(系统)容器。无法创建仅包含 Windows Server 节点的 GKE 集群。

  1. 在 Cloud Shell 中,为 Windows 服务器节点池的名称设置环境变量:

    export NODE_POOL_NAME=windows-server-pool
    
  2. 创建 GKE Windows Server 节点池:

    gcloud container node-pools create $NODE_POOL_NAME \
        --cluster $GKE_CLUSTER_NAME \
        --machine-type n1-standard-2 \
        --image-type WINDOWS_LTSC \
        --num-nodes 2 \
        --no-enable-autoupgrade \
        --metadata sysprep-specialize-script-ps1="iex((New-Object System.Net.WebClient).DownloadString('$AD_JOIN_SERVICE_URL'))"
    

    sysprep-specialize-script-ps1 元数据密钥是一个内置密钥,指向在 GCESysprep 步骤期间执行的 PowerShell 脚本步骤,然后实例将首次启动。

    iex cmdlet 会从您部署到 Cloud Run 的加入 Active Directory 网域的服务下载 PowerShell 脚本。然后,它会运行该脚本以将新实例加入 Active Directory 网域。

    --no-enable-autoupgrade 参数可停用池中所有节点的节点自动升级功能。这是因为升级节点的 Windows 映像可能会导致节点的 Windows Server 版本与 Pod 的 Windows Server 版本之间不兼容。如需了解详情,请参阅升级 Windows Server 节点池

    创建每个节点后,domain-join PowerShell 脚本会将节点加入网域。

  3. 等待几分钟,直到节点池创建完成,然后运行以下命令以验证所有节点是否已加入网域:

    kubectl get node \
    -l cloud.google.com/gke-nodepool=$NODE_POOL_NAME \
    -o custom-columns=":metadata.name" --no-headers \
        | xargs -I{} gcloud compute instances get-serial-port-output {} --port 1 \
        | grep "sysprep-specialize-script-ps1:.*success" --ignore-case
    

    如果节点已加入网域,则输出会如下所示:

    timestamp GCEMetadataScripts: sysprep-specialize-script-ps1: Successfully registered computer account.
    timestamp GCEMetadataScripts: sysprep-specialize-script-ps1: Computer successfully joined to domain
    
    Specify --start=152874 in the next get-serial-port-output invocation to get only the new output starting from here.
    

    如果您想查看完整的脚本输出,请从 grep 命令中移除 .*success

为 GKE Pod 授予对 Active Directory 的访问权限

您需要创建一条防火墙规则,以允许 GKE 集群的 Pod 使用以下协议访问您的网域控制器:

  • Kerberos(UDP/88、TCP/88)
  • NTP (UDP/123)
  • RPC(TCP/135、TCP/49152-65535)
  • LDAP(UDP/389、TCP/389)
  • SMB(UDP/445、TCP/445)
  • LDAP GC (TCP/3268)
  • Active Directory Web 服务 (TCP/9389)

您可以根据已分配给网域控制器的服务帐号应用规则,也可以像本教程那样使用网络标记来应用规则。如需详细了解 Active Directory 相关端口,请参阅关于 Active Directory 端口和协议要求以及跨防火墙使用 Active Directory 的文档。

如果您使用的是 Managed Service for Microsoft Active Directory(代管式 Microsoft AD),则可以跳过此步骤。

  1. 在 Cloud Shell 中,获取 GKE 集群的 Pod IP 地址范围:

    CLUSTER_IP_RANGE=`gcloud container clusters describe cluster-1 --format="value(clusterIpv4Cidr)"`
    
  2. 创建一条防火墙规则,以便为 GKE Pod 提供对 Active Directory 的访问权限:

    gcloud compute firewall-rules create allow-gke-pods-to-ad \
        --network $NETWORK_NAME \
        --allow udp:88,tcp:88,udp:123,tcp:135,tcp:49152-65535,udp:389,tcp:389,udp:445,tcp:445,tcp:3268,tcp:9389 \
        --source-ranges=$CLUSTER_IP_RANGE \
        --target-tags DC-TAG
    

    DC-TAG 替换为分配给您的网域控制器虚拟机的网络标记。

配置 GKE 以支持使用 gMSA

如需在 Windows Server 节点中使用 gMSA,您需要在 Active Directory 中创建 gMSA 对象,在 GKE 中创建匹配的 gMSA 资源,并启用新创建的 Pod 以提取其 gMSA 凭据。

  1. 在 Cloud Shell 中,下载并运行 gMSA 网络钩子脚本:

    export K8S_GMSA_DEPLOY_DOWNLOAD_REV=8aba3cc755ecf5327c799af395286f2f03371aa0
    curl https://raw.githubusercontent.com/kubernetes-sigs/windows-gmsa/$K8S_GMSA_DEPLOY_DOWNLOAD_REV/admission-webhook/deploy/deploy-gmsa-webhook.sh -o deploy-gmsa-webhook.sh && chmod +x deploy-gmsa-webhook.sh
    
    ./deploy-gmsa-webhook.sh --file ./gmsa-webhook.yml --overwrite
    rm -drf gmsa-webhook-certs
    

    该脚本会将 gMSA 自定义资源定义 (CRD) 清单添加到 GKE 集群,并部署网络钩子以将 gMSA 规范提供给 Pod。您现在可以将 gMSA 规范存储在集群中,并为 Pod 和容器配置 gMSA。

    如需详细了解 Kubernetes 和 gMSA,请参阅为 Windows Pod 和容器配置 GMSA

您的 GKE 集群现已准备好运行需要使用 gMSA 的 Windows 应用。例如,您可以在 Windows Server 节点中运行 ASP.NET Web 应用。您可以配置应用,让用户使用 Windows 身份验证登录,或者让应用使用 Pod 的 gMSA 访问远程网络共享或 SQL Server 数据库。

与 Active Directory 集成

接下来,您将在 Active Directory 中为 ASP.NET Web 应用创建 gMSA,配置 gMSA 的使用方式和使用者,然后将其配置添加到 GKE。

登录并启动 PowerShell

  1. 连接到 gmsa-dev-vm 虚拟机。
  2. 使用有权创建 gMSA 的 Active Directory 帐号登录 Windows。

    您的帐号需要是 Domain Admins 群组的成员,或者必须能够创建 msDS-GroupManagedServiceAccount 对象。如需了解详情,请参阅预配群组代管式服务帐号

    如果您使用代管式 Microsoft AD,则您的帐号需要是 Cloud Service Managed Service Account Administrators 群组的成员。如需了解详情,请参阅创建群组代管式服务帐号

  3. 打开 PowerShell:

    PowerShell
    

创建 KDS 根密钥

创建 gMSA 之前,您必须确保 Active Directory 网域控制器具有密钥分发服务 (KDS) 根密钥。Active Directory 使用 KDS 根密钥为 gMSA 生成密码。

如果您使用的是代管式 Microsoft AD,则可以跳过以下步骤,因为代管式 Microsoft AD 会在您创建网域时创建 KDS 根密钥。

  1. gmsa-dev-vm 上,检查 Active Directory 是否已具有 KDS 根密钥:

    Get-KdsRootKey
    

    此命令会显示密钥 ID(如果存在)。

  2. 如果您未在响应中收到密钥 ID,请创建密钥:

    Add-KdsRootKey -EffectiveTime ((get-date).addhours(-10))
    

创建 gMSA

创建 gMSA 时,您需要提供有权访问 gMSA 的计算机的名称。为保证安全性,建议您仅向在其中运行应用的实例授予对 gMSA 的权限。创建已加入网域的 Windows Server 节点池时,系统会为该节点池的计算机创建新的 Active Directory 群组。该群组的名称与 GKE 为节点池创建的代管式实例组 (MIG) 的名称匹配。

  1. 在 PowerShell 中,为 Google Cloud 项目 ID、集群名称、Windows 节点池名称、gMSA 名称和 AD 域名设置变量:

    $ProjectId = "PROJECT-ID"
    $GkeClusterName = "cluster-1"
    $PermittedNodePool = "windows-server-pool"
    $GmsaName = "WebApp-01"
    $AdDomain = (Get-ADDomain).DNSRoot
    

    PROJECT-ID 替换为您的 Google Cloud 项目 ID。

  2. kubectl 工具设置集群配置:

    gcloud config set project $ProjectId
    gcloud config set compute/zone "ZONE-NAME"
    

    ZONE-NAME 替换为在其中部署 GKE 集群的可用区。

  3. 检索为节点池创建的 Active Directory 群组的域名:

    $IntanceGroupUri = gcloud container node-pools describe $PermittedNodePool `
        --cluster $GkeClusterName `
        --format="value(instanceGroupUrls)"
    $InstanceGroupName=([System.Uri]$intanceGroupUri).Segments[-1]
    $GroupDN=(Get-ADGroup -Filter "name -eq '$InstanceGroupName'")
    
    Write-Host $GroupDN.DistinguishedName
    
  4. 创建 gMSA:

    New-ADServiceAccount -Name $GmsaName `
    -DNSHostName "$GmsaName.$AdDomain" `
    -PrincipalsAllowedToRetrieveManagedPassword $GroupDN
    
  5. 验证是否已创建 gMSA:

    Get-ADServiceAccount -Identity $GmsaName
    

    如果已创建 gMSA,则输出会如下所示:

    DistinguishedName : CN=WebApp01,CN=Managed Service Accounts,DC=corp,DC=example,DC=com
    Enabled           : True
    Name              : WebApp01
    ObjectClass       : msDS-GroupManagedServiceAccount
    ObjectGUID        : 5afcff45-cf15-467d-aaeb-d65e53288253
    SamAccountName    : WebApp01$
    SID               : S-1-5-21-780151012-601164977-3226406772-2103
    UserPrincipalName :
    

将 gMSA 添加到 GKE

如需在 Kubernetes 集群中使用 gMSA,您需要在 Kubernetes 中创建 gMSA 资源,并配置哪些命名空间和帐号有权使用该 gMSA 资源。

  1. gmsa-dev-vm 上的 PowerShell 中安装 git 工具:

    Install-Script -Name Install-Git -Force
    Install-Git.ps1
    $env:Path += ";c:\program files\git\bin"
    
  2. 安装 kubectl 工具。

    $version = (Invoke-WebRequest -UseBasicParsing -Uri "https://dl.k8s.io/release/stable.txt").Content
    $uri = "https://dl.k8s.io/release/$version/bin/windows/amd64/kubectl.exe"
    New-Item -Type Directory $env:ProgramFiles\kubectl
    Start-BitsTransfer -Source $uri -Destination $env:ProgramFiles\kubectl\
    $env:Path += ";$env:ProgramFiles\kubectl"
    
  3. 使用 GKE 集群的凭据初始化 kubectl 工具:

    gcloud container clusters get-credentials $GkeClusterName
    
  4. 创建 gMSA 凭据规范文件:

    Install-Module CredentialSpec -Force
    $GmsaName = $GmsaName.ToLower()
    $CredSpecFile = Join-Path $env:TEMP "$GmsaName-credspec.json"
    New-CredentialSpec -AccountName $GmsaName -Path $CredSpecFile
    
    $CredentialsSpec=@{
    "apiVersion" = "windows.k8s.io/v1alpha1";
    "kind" = "GMSACredentialSpec";
    "metadata" = @{"name" = $GmsaName}
    "credspec" = (Get-Content $CredSpecFile | ConvertFrom-Json)
    }
    
    $CredentialsSpec | ConvertTo-Json -Depth 5 | Set-Content $CredSpecFile
    

    Kubernetes 中 GMSACredentialSpec 资源的名称必须使用小写字符

    脚本会更改 $GmsaName 变量的大写字母,以符合此限制。

    脚本会显示一条警告消息,表示测试代管式服务帐号失败,这在意料之内。您的开发虚拟机不是分配给 gMSA 的群组的成员,因此无法从虚拟机测试 gMSA。该警告消息不会阻止命令生成 gMSA 凭据规范。

  5. 将 gMSA 凭据规范添加到 GKE 集群:

    kubectl apply -f $CredSpecFile
    
  6. 克隆 GitHub 代码库:

    git clone https://github.com/GoogleCloudPlatform/gke-aspnet-gmsa.git
    cd gke-aspnet-gmsa
    
  7. 将 gMSA RBAC 对象添加到集群:

    kubectl apply -f gmsa-rbac-webapp-01.yaml
    

部署和使用 Web 应用

接下来,您将构建 Web 应用和容器映像,将新的容器映像部署到 GKE 集群,并在浏览器中打开 Web 应用来验证 Web 应用是否可以使用 gMSA。

构建和部署 ASP.NET Web 应用

  1. gmsa-dev-vm 上的 PowerShell 中,为注册表位置、注册表名称和映像标记设置变量:

    $RegistryLocation = "STORAGE-REGION.gcr.io"
    $ProjectsRegistry = "$RegistryLocation/$ProjectId"
    $ImageTag = "$ProjectsRegistry/test-gmsa:latest"
    

    STORAGE-REGION 替换为要在其中存储容器映像的位置。支持的值包括 useuasia。选择靠近您在其中创建开发虚拟机和 GKE 集群的区域的位置。

  2. 构建容器映像:

    docker build -t $ImageTag -f Dockerfile-WINDOWS_LTSC .
    
  3. 将容器映像推送到 Container Registry:

    gcloud auth configure-docker $RegistryLocation --quiet
    docker push $ImageTag
    
  4. 下载应用的 YAML 文件,并使用 gMSA 配置进行更新:

    $ApplicationYaml = Join-Path $env:TEMP "gmsa-test-webapp-01.yaml"
    
    (Get-Content gmsa-test-webapp-01.yaml.template) `
    -Replace '\${registry_path}',$ProjectsRegistry | `
    Set-Content $ApplicationYaml
    
  5. 将 Web 应用部署到 GKE 集群:

    kubectl apply -f $ApplicationYaml
    

验证 ASP.NET Web 应用是否正在运行

Web 应用使用 LoadBalancer 服务在互联网上公开。您需要等待 Pod 和服务完成部署,然后才能浏览到 Web 应用。部署 Pod 可能需要几分钟时间,因为 Windows Server Core 容器映像很大(Web 应用映像大于 7 GB),并且节点下载映像并创建容器也需要一些时间。

  1. 检查 Pod 的状态:

    kubectl get pods --selector=app=gmsa-test-webapp-01
    

    重复该命令,直到输出显示 Pod 状态为正在运行

    NAME                                   READY     STATUS    RESTARTS   AGE
    gmsa-test-webapp-01-76c6d64975-zrtgq   1/1       Running   0          28s
    

    如果 Pod 的状态仍为待处理并且未更改为正在创建容器正在运行,则检查 Windows 节点的来源映像,确保它是 Windows Server 2019。您还可以查看版本映射表,了解 GKE 版本与 Windows Server 版本之间的匹配关系。如果版本不匹配,请使用与节点的 Windows Server 版本匹配的基础容器映像更新 Dockerfile-WINDOWS_LTSC 文件,然后重复构建和部署 ASP.NET Web 应用的步骤。

  2. 检查服务的状态:

    kubectl get service --selector=app=gmsa-test-webapp-01
    

    重复运行该命令,直到输出显示服务具有外部 IP 地址:

    NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)        AGE
    gmsa-test-webapp-01    LoadBalancer   10.44.2.112   external-ip    80:32233/TCP   17s
    
  3. 记下输出中 external-ip 的值;您稍后需要用到此值。

在 ASP.NET Web 应用中运行初步测试

Pod 目前正在运行,可通过网络负载均衡器从互联网进行访问。接下来,您将运行初步测试,以验证容器是否已成功部署以及它是否有权使用 gMSA。

  1. 在浏览器中,转到 http://EXTERNAL-IP 以查看 gMSA 测试 Web 应用。

    EXTERNAL-IP 替换为您在上一过程中获得的 IP 地址。

  2. 滚动到预检检查 (Preflight Checks) 部分,然后点击运行预检检查 (Run Preflight Checks) 按钮,以验证所有测试是否都已通过。

    如果测试通过,则输出会如下所示:

    [PASS]  Active Directory RSAT PowerShell Module Installed
    
    [PASS]  IIS Document Root found
            C:\inetpub\wwwroot\
    
    [PASS]  PowerShell Scripts Folder found
            C:\inetpub\wwwroot\Powershell\
    
    [PASS]  Container Diagnostic Script found
            C:\inetpub\wwwroot\Powershell\\containerDiag.ps1
    
    [PASS]  Domain Diagnostic Script found
            C:\inetpub\wwwroot\Powershell\\domainDiag.ps1
    
    [RES]   Result: PASS   All checks passed! Please proceed to run the different tests.
    
  3. 滚动到容器信息部分,然后点击运行脚本按钮。验证您是否看到有关容器和节点的信息,并且未显示任何错误。

在 Windows 容器中使用 gMSA

现在,您可以通过在 Web 应用中运行多项测试来验证 gMSA 设置是否正常工作。每个测试会将 gMSA 用于不同的用途。如果所有测试都成功,则表示您正确配置了 gMSA。

验证 gMSA 容器配置

  • 滚动到网域连接部分,在帐号名框中输入 gMSA 的名称 (WebApp-01),然后点击运行脚本。等待几秒钟,让测试完成。

    输出内容类似如下:

    *****   C O N T A I N E R   D I A G N O S T I C S   *****
    
    [INFO]  Starting script execution at 01-05-2021-13:53:11
    
    [INFO]  Using gMSA: WebApp-01
    
    [PASS]  gMSA Account found in Active Directory
            CN=WebApp01,CN=Managed Service Accounts,DC=corp,DC=example,DC=com
    
    [PASS]  This Container (gmsa-test-webapp01-5bc485b8d5-9lbb7) is running on a GKE Windows Node that is authorized to use WebApp01
    
    [INFO]  Script execution complete at 01-05-2021-13:53:12
    
    *****      E N D   O F   D I A G N O S T I C S      *****
    

    脚本使用两个 PowerShell cmdlet 测试对 gMSA 的访问权限:

    • Get-ADServiceAccount:此 cmdlet 检索 gMSA 的相关信息。如果此 cmdlt 运行成功,则容器正在使用有效的 gMSA 运行。
    • Test-ADServiceAccount:此 cmdlet 测试它是否可以检索 gMSA 凭据。如果 cmdlt 运行成功,则容器正在被许可访问 gMSA 凭据的 Windows Server 节点中运行。

让用户使用 Windows 身份验证登录

  1. 在页面的顶部导航栏中,点击登录
  2. 当系统提示您输入凭据时,请输入网域用户名和密码。
  3. 如果您看到包含帐号信息的安全页面,且系统未提示您输入凭据,则浏览器会使用您当前身份让您自动登录。

    在您经过身份验证后,您会看到安全页面。请确保看到以下三个部分:

    • 用户信息:显示所使用的身份验证的用户名和类型。
    • 群组:显示您所属的群组的列表。系统会从 Active Directory 中检索列表中的群组名称。
    • 用户声明:显示在登录过程中 Active Directory 提供的用户声明的列表。群组成员资格声明显示 Active Directory 群组 SID,而不是其名称。

除了支持集成式 Windows 身份验证之外,ASP.NET Web 应用还可以在调用远程服务器时使用其 gMSA 进行身份验证。使用 gMSA 时,Web 应用以及 Windows 容器中运行的任何其他应用都可以访问网络中需要 Windows 身份验证的资源,例如 SQL Server 实例和基于 SMB 的网络共享。

问题排查

如果您在设置过程中或在测试 Web 应用时遇到任何错误消息,请参阅以下问题排查页面:

有关生产应用的其他注意事项

您遵循的说明旨在为本教程提供最佳路径。对于生产环境,您可能会对某些流程进行更改,以使结果更可靠,如以下部分所述。

Windows Server 节点池注意事项

如果您计划部署自己的应用来使用 gMSA,并且该应用支持客户端会话,则我们建议您在节点池中至少创建两个节点。借助多个节点,您可以使用进程外会话存储来验证应用是否可以正确处理分布式会话。

在本教程中,您将创建一个 Windows Server 节点池来托管您的应用。但在某些情况下,您可能希望在集群中创建多个 Windows Server 节点池 - 例如,一个使用 HDD 永久性磁盘的节点池和一个使用 SSD 永久性磁盘的节点池。如果您需要将应用部署到多个节点池,请在使用 New-ADServiceAccount cmdlet 创建 gMSA 时,向 PrincipalsAllowedToRetrieveManagedPassword 参数提供一组 Active Directory 群组对象。

gMSA 和服务主体名称 (SPN) 注意事项

如果您的应用要求您使用 Kerberos 对用户进行身份验证(例如,为了支持身份委托),则需要使用自定义 DNS 访问您的应用,并使用 服务主体名称 (SPN) 配置 gMSA。例如,如果您的负载均衡器通过 https://my-web-app/ 在 GKE 上公开应用,则您需要通过以下方式之一创建一个名为 HTTP/my-web-app 的 SPN:

  • 对于新的 gMSA,请使用所需的 SPN 创建 gMSA。例如:

    New-ADServiceAccount -Name $GmsaName `
    -DNSHostName "$GmsaName.$AdDomain" `
    -PrincipalsAllowedToRetrieveManagedPassword $Groups `
    -ServicePrincipalNames "HTTP/my-web-app", "HTTP/my-web-app.$AdDomain"
    
  • 对于现有 gMSA,请调用 Set-ADServiceAccount 以向 gMSA 添加所需的 SPN。例如:

    Set-ADServiceAccount $GmsaName -ServicePrincipalNames @{Add="HTTP/my-web-app", "HTTP/my-web-app.$AdDomain"}
    

根据 DNS 配置,您可能还需要为 HTTP/www.my-web-appHTTP/www.my-web-app.$AdDomain 创建 SPN。

对于非 HTTP 协议(例如配置了 TCP 绑定和 Windows 身份验证的 WCF 服务),您可能需要创建其他类型的 SPN,例如 HOST/ SPN。

选择 IIS 应用池身份

ASP.NET Web 应用在 IIS Web 服务器的 Windows 中运行。在 IIS 中,您可以配置共享相同进程的 Web 应用群组。此群组称为应用池。每个应用池都托管在一个名为 w3wp 的专用进程中。IIS 应用池提供进程配置(不论进程是 32 位还是 64 位),并且还提供进程的身份。在 Windows 容器中运行 Web 应用时,请设置应用池的进程身份以使用内置网络服务帐号。

Windows 容器不需要 IIS 同样支持的本地应用池身份帐号。该应用池身份帐号由 IIS 创建,用于在同一 IIS 实例上运行多个 Web 应用时强制执行本地安全边界。对于 Windows 容器(其中每个 Web 应用都托管在单独的容器中),无需在容器内创建安全边界,因为容器本身可提供安全边界。

即使应用池身份配置为使用网络服务帐号,如果应用向需要身份验证的外部资源发出请求,应用也会使用您为 Windows 容器配置的 gMSA 进行身份验证。

清除数据

为避免因本教程中使用的资源导致您的 Google Cloud 帐号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

逐个移除资源

如果您希望保留 Google Cloud 项目,但不想删除为本教程创建的 Google Cloud 资源,则可以逐个移除这些资源。

还原 Active Directory 更改

  1. 连接到开发虚拟机,然后以对 Active Directory 网域拥有管理员权限的用户身份登录。
  2. gmsa-dev-vm 虚拟机中,如果 PowerShell 尚未打开,请将其打开:

    PowerShell
    
  3. 删除 gMSA:

    Remove-ADServiceAccount -Identity "WebApp-01" -Confirm:$false
    

删除云资源

  1. 在 Cloud Console 中,激活 Cloud Shell。

    激活 Cloud Shell

  2. 初始化 gcloud 环境:

    gcloud config set project PROJECT-ID
    gcloud config set compute/zone ZONE-NAME
    

    PROJECT-ID 替换为您的 Google Cloud 项目 ID,并将 ZONE-NAME 替换为您在其中部署 GKE 集群和开发虚拟机的可用区。

  3. 删除开发虚拟机:

    gcloud compute instances delete gmsa-dev-vm --quiet
    
  4. 删除服务帐号:

    gcloud iam service-accounts delete dev-vm@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com --quiet
    
  5. 删除 GKE 集群:

    gcloud container clusters delete cluster-1 --quiet
    
  6. 如果您为 Active Directory 控制器创建了防火墙规则,请将其删除:

    gcloud compute firewall-rules delete allow-gke-pods-to-ad --quiet
    
  7. 删除 Web 应用映像:

    gcloud container images delete STORAGE-REGION.gcr.io/$GOOGLE_CLOUD_PROJECT/test-gmsa --force-delete-tags --quiet
    

    STORAGE-REGION 替换为您选择用于存储容器映像的位置。支持的值包括 useuasia

为完成删除,请按照为虚拟机配置 Active Directory 以自动加入网域中的清理步骤进行操作。

后续步骤