NGINX 第 7 层负载均衡参考实现

Google Distributed Cloud (GDC) 网闸隔离配置提供内置的代管式第 4 层 (L4) 负载均衡器,但许多企业应用需要高级第 7 层 (L7) 功能,例如基于主机的路由、集中式 TLS 管理和复杂的流量分配。过去,这是通过 Ingress API 实现的,该 API 现在在 Kubernetes 社区中被视为功能冻结。

此参考架构提供自行管理的第 7 层负载均衡解决方案。通过在 GDC 标准集群上部署热门的 NGINX Gateway Fabric 开源控制器,客户可以将 L7 流量无缝路由到混合环境。此架构使用 TLS 终结 (HTTPRoute),根据服务器名称指示 (SNI) 将流量路由到内置容器化 Pod 和托管在外部虚拟机上的应用。

架构

GDC 网闸隔离配置上的 NGINX 第 7 层负载均衡架构图。

该解决方案的关键组件包括:

  • 客户端:发起 HTTPS 请求以与应用交互的实体。
  • GDC 标准集群:GDC 提供了一种内置方式来创建 Kubernetes Vanilla 集群。在此解决方案中,集群将托管 L7 负载均衡器及其控制器,以及外部虚拟机的工作负载和无头服务
  • GDC L4 负载均衡器:内置 L4 负载均衡器作为入口点,将 TCP/443 流量直接分发到运行 Gateway 控制器的 Kubernetes Pod。
  • 网关控制器:在标准集群中运行的 NGINX Operator。它们会监控 Gateway API 资源(例如 Gateway 和 HTTPRoute),并动态更新底层代理。该解决方案将使用 Nginx Gateway Fabric。
  • Gateway(具有 HTTPRoute):标准化的 Kubernetes 资源,用于定义物理监听端口 (443) 以及具有 TLS 终结的基于 SNI 的主机路由规则。
  • 容器化工作负载 (Pod):使用常规 Kubernetes Service 在内部公开的标准 Kubernetes 部署。
  • 基于虚拟机的工作负载(外部):托管在项目网络上的外部虚拟机上的工作负载,通过无头 Kubernetes Service 和包含虚拟机直接 IP 的自定义端点向代理公开。
  • Harbor Registry:一个私有 Container Registry,用于在经过网闸隔离的环境中存储和提供代理和应用映像。

在 Standard 集群中,您需要创建四个命名空间:

  • 托管 Nginx Gateway Fabric 资源的 nginx-gateway 命名空间:

    nginx-gateway 命名空间资源。

  • 托管 Gateway 资源的 load-balancer 命名空间:

    负载均衡器命名空间资源。

  • vm-app 命名空间托管外部虚拟机 IP vm-app、无头服务、指向外部 IP 的 EndpointSlice 以及 Gateway 的 HTTPRoute:

    vm-app 命名空间资源。

  • hello-app 命名空间托管 Deployment、Service、HTTPRoute 和 Gateway,用于演示容器工作负载:

    hello-app 命名空间资源。

准备工作

在部署此解决方案之前,请确保您已满足以下前提条件:

  • 所需软件:helm、docker、kubectl
  • CLI 登录和本地设置:从 GDC 控制台下载 gdcloud CLI,并在本地设置环境。

    export USER_NAME="USER_NAME"
    export PROJECT_ID="PROJECT_ID"
    export ZONE="ZONE"
    export ORG_NAME="ORG_NAME"
    export GDC_URL="GDC_URL"
    
    gdcloud components install gdcloud-k8s-auth-plugin
    
    gdcloud config set core/organization_console_url \
      https://console.$ORG_NAME.$ZONE.$GDC_URL
    gdcloud config set core/zone $ZONE
    gdcloud config set core/project ${PROJECT_ID}
    
    gdcloud auth login # use --login-config-cert option in case of TLS error
    
  • 项目设置:在 GDC 网闸隔离配置环境中创建一个项目,用于存放资源。

    gdcloud projects create $PROJECT_ID
    
  • IAM 角色:为您的用户授予集群管理员和标准集群管理员角色,以便管理 Kubernetes 资源;并授予 Harbor 实例管理员角色,以便推送映像。

    # Grant standard cluster and cluster admin roles
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=cluster-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=standard-cluster-admin
    
    # Grant Harbor instance admin role
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=harbor-instance-admin
    

创建标准集群

本部分将指导您在 GDC 网闸隔离配置环境中设置标准 Kubernetes 集群。标准集群为部署各种工作负载(包括 Nginx Gateway Controller 和自定义应用)提供了灵活而强大的基础。以下步骤将确保您的集群已正确配置,并且可供后续部署访问。

  1. 通过运行以下命令来确定可用的虚拟机映像类型:

    gdcloud compute machine-types list
    
  2. 为集群工作器节点选择合适的机器类型。对于本教程,建议使用至少具有 4 个 vCPU 的机器类型。

    export MACHINE_TYPE="MACHINE_TYPE"
    
  3. 获取管理 API 服务器 kubeconfig 并设置别名:

    export CLUSTER_NAME="CLUSTER_NAME"
    
    KUBECONFIG=kubeconfig-admin.yaml gdcloud clusters \
      get-credentials ${ORG_NAME}-admin
    
    alias km="kubectl --kubeconfig kubeconfig-admin.yaml"
    
  4. 创建具有 2 个工作器节点的标准集群:

    km create -f - <<EOF
    apiVersion: cluster.gdc.goog/v1
    kind: Cluster
    metadata:
      name: ${CLUSTER_NAME}
      namespace: ${PROJECT_ID}
    spec:
      nodePools:
      - machineTypeName: ${MACHINE_TYPE}
        nodeCount: 2
        name: ${CLUSTER_NAME}-node-pool
    EOF
    

    如需详细了解可用选项,请参阅文档。

    标准集群创建过程最多可能需要 60 分钟才能完成。如需检查状态,请使用以下命令:

    km get clusters/${CLUSTER_NAME} \
      -n ${PROJECT_ID} \
      --watch
    

    集群准备就绪后,输出应显示“正在运行”状态,如下所示:

    NAME         STATE     K8S VERSION
    my-cluster   Running   1.30.12-gke.300
    
  5. 集群准备就绪后,检索其凭据:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \
      get-credentials ${CLUSTER_NAME} \
      --standard \
      --project ${PROJECT_ID} \
      --zone ${ZONE}
    
  6. 创建一个别名,以便在本指南的其余部分中使 kubectl 命令更加简洁。此别名将用于与标准集群进行交互:

    alias kk="kubectl --kubeconfig kubeconfig-${CLUSTER_NAME}.yaml"
    
  7. 为控制器、“hello-app”演示容器化应用和基于虚拟机的演示应用创建命名空间:

    kk create namespace load-balancer
    kk create namespace hello-app
    kk create namespace vm-app
    

创建并集成 Harbor Registry

Harbor 是一个容器映像注册表,在 GDC 网闸隔离配置中提供内置支持。本部分将引导您完成将 Harbor Registry 与标准集群集成的步骤,包括配置凭证和 Secret 以实现安全地拉取和推送映像。

  1. 在项目中创建 Harbor 实例。
  2. 在 Harbor 实例中创建 Harbor 项目。
  3. 设置环境变量:

    export HARBOR_INSTANCE_NAME="HARBOR_INSTANCE_NAME"
    export HARBOR_INSTANCE_URL="HARBOR_INSTANCE_URL"
    export HARBOR_PROJECT="HARBOR_INSTANCE_PROJECT"
    export IMAGE_PULL_SECRET_NAME="harbor-secret"
    
  4. 使用机器人账号登录 Harbor 实例:

    docker --config=./docker login ${HARBOR_INSTANCE_URL}
    
  5. 在标准集群中创建 Secret:

    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n load-balancer
    
    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n hello-app
    

部署演示容器化应用

本部分详细介绍了如何在 GDC 网闸隔离配置 Kubernetes 集群中部署演示版容器化应用 (hello-app)。您将创建必要的 Kubernetes Deployment 和 Service 资源,以运行 hello-app 并在集群内部公开它,从而为使用 L7 负载均衡器进行访问做好准备。

  1. 将示例容器化应用的示例映像上传到 Harbor:

    docker pull gcr.io/google-samples/hello-app:1.0 \
      --platform linux/amd64
    docker tag gcr.io/google-samples/hello-app:1.0 \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
    
  2. 在标准集群中部署以下清单:

    cat << EOF > hello-app.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello-app
      namespace: hello-app
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: hello-app
      template:
        metadata:
          labels:
            app: hello-app
        spec:
          containers:
          - name: hello-server
            image: ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/hello-app:1.0
            ports:
            - containerPort: 8080
          imagePullSecrets:
          - name: ${IMAGE_PULL_SECRET_NAME}
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: hello-app
      namespace: hello-app
    spec:
      type: ClusterIP
      selector:
        app: hello-app
      ports:
        - protocol: TCP
          port: 80
          targetPort: 8080
    EOF
    
    kk apply -f hello-app.yaml
    

然后验证 Deployment 和 Service 是否存在:

kk get svc,deploy -n hello-app

在虚拟机中部署演示应用

本部分详细介绍了如何在 Kubernetes 集群外部的虚拟机 (VM) 中部署演示应用。通过在虚拟机上设置 HTTP 服务器,您将模拟负载均衡器可以公开的外部应用,从而展示其管理集群内外资源流量的能力。

首先,为演示应用创建一个虚拟机

  1. 在网络浏览器中打开 GDC 控制台。
  2. 选择您在其中创建标准 Kubernetes 集群的项目。
  3. 打开菜单,然后点击虚拟机。
  4. 点击创建实例。
  5. 将虚拟机命名为 vm-workload。2 vCPU 映像足以满足示例要求。
  6. 对于启动磁盘映像,请选择预装了 Python 的 Ubuntu 22.04 发行版。
  7. 点击创建。
  8. 等待几分钟,直到虚拟机准备就绪。
  9. 建立与虚拟机的 SSH 连接:
    1. 在 GDC 控制台中,点击该虚拟机。
    2. 点击通过 SSH 连接。

连接到 SSH 控制台后,运行以下命令:

mkdir ~/simple-server
cd ~/simple-server
echo 'Welcome to my VM!' > index.html
python3 -m http.server --bind 0.0.0.0 8080 &

如需将流量路由到虚拟机,请创建无头 Service(不含选择器)。此地址将使用 EndpointSlice 资源手动映射到虚拟机的内部 IP 地址。

kk apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: vm-app-svc
  namespace: vm-app
spec:
  ports:
  - protocol: TCP
    port: 443
    targetPort: 443
EOF

通过运行以下命令获取 vm-workload 虚拟机的 IP 地址

gdcloud compute instances list --project ${PROJECT_ID} \
  | grep workload-vm | awk '{print $3}'

输出将是设置 EndpointSlice 资源所需的虚拟机的 IP 地址。

创建资源 EndpointSlice,该资源将连接到虚拟机应用无选择器 Service,并指定应将流量路由到的虚拟机的 IP。

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: vm-app-endpoints
  namespace: vm-app
  labels:
    kubernetes.io/service-name: vm-app-svc
addressType: IPv4
ports:
  - port: 8080
endpoints:
  - addresses:
    - "VM_IP"
    conditions:
      ready: true

创建自签名证书

本部分将引导您完成为应用生成自签名 TLS 证书的过程。为方便起见,本指南使用自签名证书,但在生产环境中,您必须使用生产级证书,如可选:使用可用于生产用途的证书中所述。这些证书对于在 Nginx 网关上启用 HTTPS 终止至关重要,可确保客户端与负载均衡器之间针对容器化和基于虚拟机的工作负载进行加密通信。

  1. 为容器化应用创建证书:

    openssl req -x509 -newkey rsa:2048 -nodes \
      -keyout tls-containerized.key \
      -out tls-containerized.crt \
      -subj "/CN=k8s-app.example.com" \
      -days 365
    
    kk create secret tls tls-containerized \
      --namespace load-balancer \
      --key tls-containerized.key \
      --cert tls-containerized.crt
    
    kk create secret tls tls-containerized \
      --namespace hello-app \
      --key tls-containerized.key \
      --cert tls-containerized.crt
    
  2. 为虚拟机应用创建证书:

    openssl req -x509 -newkey rsa:2048 -nodes \
      -keyout tls-vm.key \
      -out tls-vm.crt \
      -subj "/CN=vm-app.example.com" \
      -days 365
    
    kk create secret tls tls-vm \
      --namespace load-balancer \
      --key tls-vm.key \
      --cert tls-vm.crt
    
    kk create secret tls tls-vm \
      --namespace vm-app \
      --key tls-vm.key \
      --cert tls-vm.crt
    

部署 NGINX

本部分概述了部署 Nginx Gateway Controller 的步骤,包括安装 Gateway API 自定义资源定义 (CRD)、使用 Helm 设置 Nginx Gateway Fabric,以及在标准集群中验证安装。这可为高级第 7 层路由准备好基础设施。

安装 Gateway API CRD

Gateway API 要求在部署控制器之前在集群中安装自定义资源定义 (CRD)。我们将使用 Gateway API 项目中的官方实验性自定义资源定义 (CRD)(本指南使用 1.2.0 版)。

  1. 安装实验性 CRD:

    kk apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/experimental-install.yaml
    
  2. 运行 kk get crd gateways.gateway.networking.k8s.io,验证 CRD 是否已成功安装。

安装 NGINX Gateway Fabric

使用 Helm 部署 NGINX Gateway Fabric 控制器。此控制器将监控 Gateway API 资源,并配置 NGINX 来处理流量。

  1. 设置 NGINX 映像标记:

    helm template nfg oci://ghcr.io/nginx/charts/nginx-gateway-fabric | grep "image:" # get the tag of the nginx-gateway-fabric (in following case 2.4.2)
    
    export NGINX_TAG="2.4.2"
    
  2. 拉取 NGINX Gateway Fabric 和 NGINX 映像,然后将其推送到 Harbor 注册表:

    docker pull ghcr.io/nginx/nginx-gateway-fabric:${NGINX_TAG}
    docker tag ghcr.io/nginx/nginx-gateway-fabric:${NGINX_TAG} \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx-gateway-fabric:${NGINX_TAG}
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx-gateway-fabric:${NGINX_TAG}
    
    docker pull nginx:1.27.3
    docker tag nginx:1.27.3 \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx:1.27.3
    docker --config=./docker push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx:1.27.3
    
  3. 创建 nginx-gateway 命名空间并添加 Harbor 映像拉取 Secret:

    kk create namespace nginx-gateway
    
    kk create secret docker-registry ${IMAGE_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker/config.json \
      -n nginx-gateway
    
  4. 使用 Helm 部署 NGINX Gateway Fabric,并指向 Harbor 注册表中的映像:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml helm upgrade --install ngf \
      oci://ghcr.io/nginx/charts/nginx-gateway-fabric \
      --create-namespace -n nginx-gateway \
      --set nginxGateway.image.tag="${NGINX_TAG}" \
      --set nginxGateway.image.repository="${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx-gateway-fabric" \
      --set nginx.image.repository="${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/nginx" \
      --set nginxGateway.serviceAccount.imagePullSecret="${IMAGE_PULL_SECRET_NAME}" \
      --set nginx.imagePullSecret="${IMAGE_PULL_SECRET_NAME}"
    

    Kubernetes 部署使用提供的 Secret (${IMAGE_PULL_SECRET_NAME}) 从 Harbor 安全地拉取映像,该 Secret 包含 Harbor 机器人账号的凭据。

  5. 验证 GatewayClass 资源是否已被接受:

    kk get gatewayclass
    

    您应该会看到 nginx 与 ACCEPTED = True 一起列出。

创建网关实例

定义监听端口 443 的逻辑负载均衡器实例。我们会将其配置为 Terminate 模式,这意味着网关将执行 TLS 终结,并在流量通过之前对其进行解密。

创建并应用 gateway.yaml:

cat << EOF > gateway.yaml 
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: my-gateway
  namespace: load-balancer
spec:
  gatewayClassName: nginx
  listeners:
  - name: https-k8s-workload
    hostname: "k8s-app.example.com"
    port: 443
    protocol: HTTPS
    allowedRoutes: 
      namespaces: 
        from: All
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: tls-containerized 
  - name: https-vm-workload
    hostname: "vm-app.example.com"
    port: 443
    protocol: HTTPS
    allowedRoutes: 
      namespaces: 
        from: All
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: tls-vm 
EOF

kk apply -f gateway.yaml

通过检查 PROGRAMMED 是否为 True 来验证 Gateway 是否已成功部署:

kk get gateway my-gateway --n load-balancer

验证代管式 GDC L4 负载均衡器是否已作为 Service 与 Gateway 一起部署

kk get services -n load-balancer

检查 Nginx 服务是否已创建并配置,输出应如下所示:

NAME               TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)         AGE
my-gateway-nginx   LoadBalancer   10.252.19.148   10.200.32.43   443:30649/TCP   45h

定义 L7 路由逻辑 (HTTPRoute)

将 HTTPRoute 绑定到网关,以定义应如何根据请求的主机名 (SNI) 分配流量。

创建并应用 routing.yaml:

cat << EOF > routing.yaml 
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: k8s-http-route
  namespace: hello-app
spec:
  parentRefs:
  - name: my-gateway 
    namespace: load-balancer
  hostnames:
  - "k8s-app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: hello-app 
      namespace: hello-app
      port: 80          
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: vm-http-route
  namespace: vm-app
spec:
  parentRefs:
  - name: my-gateway
    namespace: load-balancer
  hostnames:
  - "vm-app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: vm-app-svc  
      namespace: vm-app
      port: 443       
EOF

kk apply -f routing.yaml   

检索负载均衡器 IP

运行命令以获取 Nginx 负载均衡器的 IP

kk get services/my-gateway-nginx \
  -n load-balancer \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}'

在验证对应用的访问权限时,您需要用到这些信息。此项称为 LOAD_BALANCER_IP。

创建客户端虚拟机

按照相关步骤创建客户端虚拟机

  1. 在网络浏览器中打开 GDC 控制台。
  2. 打开菜单,然后点击虚拟机。
  3. 点击创建实例。
  4. 创建一个名为 client 的虚拟机,选择小型机器类型,然后选择预装了 curl 的 Rocky Linux 或 Ubuntu。
  5. 点击创建。
  6. 等待几分钟,直到虚拟机准备就绪。
  7. 虚拟机准备就绪后,建立与虚拟机的 SSH 连接:
    1. 在 GDC 控制台中,点击该虚拟机。
    2. 点击通过 SSH 连接。

验证已验证访问权限和路由

如需测试路由,请从客户端虚拟机执行 curl 命令。您可以使用这两个应用定义的域名和负载均衡器的 IP 地址连接到这两个应用。

通过在 curl 中传递 --resolve 标志,您可以强制域名解析为 GDC 网闸隔离配置 L4 负载均衡器的 IP 地址。请注意,我们传递了 -k 标志来信任自签名证书。

测试 Kubernetes 容器化应用:

curl -k --resolve k8s-app.example.com:443:$LOAD_BALANCER_IP https://k8s-app.example.com -v

测试外部虚拟机应用:

curl -k --resolve vm-app.example.com:443:$LOAD_BALANCER_IP https://vm-app.example.com -v

如果配置正确,Gateway 控制器将无缝充当 TLS 终结器,并将流量传递到目标。

可选:使用可用于生产用途的证书

本部分介绍了如何利用 GDC 网闸隔离配置 CA Service 创建私有根证书授权机构 (CA)、为工作负载颁发签名证书,以及安全地更新 GDC 网闸隔离配置标准集群和客户端虚拟机。

本部分概述了如何使用 GDC 网闸隔离配置 CA Service 创建私有根证书授权机构 (CA),并为您的应用颁发有效证书。通过在客户端虚拟机上安装此根 CA,您可以验证 TLS 终结是否可与可信证书无缝搭配使用,而无需绕过 SSL 警告(例如,使用 curl -k)。

授予必要权限并获取凭证

如需管理 CA Service 和颁发证书,您的用户需要在项目中拥有适当的 IAM 角色。

  1. 授予 certificate-authority-service-admin 和 certificate-requester 角色:

    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member=user:${USER_NAME} \
      --role=certificate-authority-service-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member=user:${USER_NAME} \
      --role=certificate-requester
    
  2. 获取 Management API 服务器的凭据:

    gdcloud clusters get-credentials ${ORG_NAME}-admin
    

创建根 CA

您将在项目命名空间内的 Management API 服务器中创建证书授权机构。

  1. 应用 CertificateAuthority 资源:

    km apply -f - <<EOF
    apiVersion: pki.security.gdc.goog/v1
    kind: CertificateAuthority
    metadata:
      name: my-root-ca
      namespace: ${PROJECT_ID}
    spec:
      caProfile:
        commonName: "My Root CA"
        duration: 87600h # 10 years
        keyAlgorithm: RSA_2048
        maxChainLength: 1
      caType: ROOT
      keyLocation: HSM
      rotationPolicy:
        cronTime: 0 0 1 1 *
    EOF
    
    km -n ${PROJECT_ID} get \
    certificateauthority.pki.security.gdc.goog/my-root-ca -ojson \
    | jq -r '
    .status.conditions[] | select( .type as $id | "Ready" | index($id)) .status'
    

颁发和部署证书

CA 准备就绪后,您将为容器化应用和基于虚拟机的应用请求证书。这些请求在 Management API 服务器中发生,并且生成的密钥必须移至标准集群。

为这两个网域创建请求:

km apply -f - <<EOF
apiVersion: pki.security.gdc.goog/v1
kind: CertificateRequest
metadata:
  name: tls-containerized-req
  namespace: ${PROJECT_ID}
spec:
  certificateAuthorityRef:
    name: my-root-ca
    namespace: ${PROJECT_ID}
  certificateConfig:
    subjectConfig:
      commonName: "k8s-app.example.com"
      dnsNames:
      - "k8s-app.example.com"
  signedCertificateSecret: tls-containerized-signed
---
apiVersion: pki.security.gdc.goog/v1
kind: CertificateRequest
metadata:
  name: tls-vm-req
  namespace: ${PROJECT_ID}
spec:
  certificateAuthorityRef:
    name: my-root-ca
    namespace: ${PROJECT_ID}
  certificateConfig:
    subjectConfig:
      commonName: "vm-app.example.com"
      dnsNames:
      - "vm-app.example.com"
  signedCertificateSecret: tls-vm-signed
EOF

请稍等片刻,等待证书签发。当 Ready 条件为 True 时,您可以验证它们是否已准备就绪:

km get certificaterequests -n ${PROJECT_ID}

更新标准集群

如果您按照本指南的前面部分操作,则标准集群中会有自签名 Secret。您必须先删除这些版本,然后才能创建新的签名版本:

kk delete secret tls-containerized -n load-balancer
kk delete secret tls-vm -n load-balancer

kk delete secret tls-containerized -n hello-app
kk delete secret tls-vm -n vm-app

现在,从 Management API 服务器中提取已签名的证书,并在标准集群中创建新的 Secret。

km get secret -n ${PROJECT_ID} tls-containerized-signed \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > tls-containerized.crt

km get secret -n ${PROJECT_ID} tls-containerized-signed \
  -o jsonpath='{.data.tls\.key}' \
  | base64 -d > tls-containerized.key

kk create secret tls tls-containerized \
  --namespace load-balancer \
  --key tls-containerized.key \
  --cert tls-containerized.crt

kk create secret tls tls-containerized \
  --namespace hello-app \
  --key tls-containerized.key \
  --cert tls-containerized.crt

km get secret -n ${PROJECT_ID} tls-vm-signed \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > tls-vm.crt

km get secret -n ${PROJECT_ID} tls-vm-signed \
  -o jsonpath='{.data.tls\.key}' \
  | base64 -d > tls-vm.key

kk create secret tls tls-vm \
  --namespace load-balancer \
  --key tls-vm.key \
  --cert tls-vm.crt

kk create secret tls tls-vm \
  --namespace vm-app \
  --key tls-vm.key \
  --cert tls-vm.crt

负载均衡器会自动获取并刷新新密钥。

配置客户端信任

如需验证设置,您需要让客户端虚拟机信任新的根 CA。

将根 CA 证书提取到文件中:

km get secret -n ${PROJECT_ID} my-root-ca-secret \
  -o jsonpath='{.data.tls\.crt}' \
  | base64 -d > my-root-ca.crt

将证书转移到客户端虚拟机。(您可以复制 my-root-ca.crt 的内容,并将其粘贴到客户端虚拟机上的文件中)。

在客户端虚拟机上,更新受信任证书存储区。

如果 client 虚拟机是 Ubuntu:

sudo cp my-root-ca.crt /usr/local/share/ca-certificates/
sudo chmod 644 /usr/local/share/ca-certificates/my-root-ca.crt
sudo update-ca-certificates

如果 client 虚拟机是 Rocky Linux:

sudo cp my-root-ca.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust

验证访问权限

现在,您可以使用 curl 访问应用,而无需使用 -k 标志。连接将完全受信任。

测试 Kubernetes 容器化应用:

curl -v --resolve k8s-app.example.com:443:LOAD_BALANDER_IP https://k8s-app.example.com

测试虚拟机应用:

curl -v --resolve vm.example.com:443:LOAD_BALANDER_IP https://vm-app.example.com

如果成功,您将立即看到应用输出,而不会出现任何 SSL 证书警告。