将 TLS 用于 Azure Kubernetes 服务 (AKS) 上的入口控制器

传输层安全性 (TLS) 协议使用证书为通信、加密、身份验证和完整性提供安全性。 将 TLS 用于 AKS 上的入口控制器可以保护应用程序之间的通信,同时获得入口控制器的优势。

你可以创建自己的证书并将其与机密存储 CSI 驱动程序相集成。 或者,可以使用 cert-manager,它会自动生成并配置 Let's Encrypt 证书。 在 AKS 群集中运行两个应用程序,可通过单个 IP 地址访问其中的每个应用程序。

重要

建议将应用程序路由加载项用于 AKS 中的入口。 有关详细信息,请参阅具有应用程序路由加载项的托管 nginx Ingress

重要

Microsoft 管理或支持证书管理器,或由使用其产生的任何问题。 有关证书管理器的问题,请参阅 证书管理器故障排除 文档。

Kubernetes 有两个基于 Nginx 的开源入口控制器:一个控制器由 Kubernetes 社区 (kubernetes/ingress-nginx) 维护,另一个由 NGINX, Inc. (nginxinc/kubernetes-ingress) 维护。 本文将使用 Kubernetes 社区入口控制器。

准备阶段

  • 本文假设已设置入口控制器和应用程序。 如果需要入口控制器或示例应用程序,请参阅创建入口控制器

  • 本文使用 Helm 3支持的 Kubernetes 版本上安装 NGINX 入口控制器。 确保使用最新版本的 Helm,并且你有权访问 ingress-nginxjetstack Helm 存储库。 本文中概述的步骤可能与 Helm chart、NGINX 入口控制器或 Kubernetes 的先前版本不兼容。

  • 本文假设你有一个包含集成 Azure 容器注册表 (ACR) 的现有 AKS 群集。 有关使用集成 ACR 创建 AKS 群集的详细信息,请参阅使用 ACR 从 AKS 进行身份验证

  • 如果使用的是 Azure CLI,本文要求运行 Azure CLI 2.0.64 或更高版本。 运行 az --version 即可查找版本。 如果需要进行安装或升级,请参阅安装 Azure CLI

  • 如果使用的是 Azure PowerShell,本文要求运行 Azure PowerShell 5.9.0 或更高版本。 运行 Get-InstalledModule -Name Az 即可查找版本。 如果需要进行安装或升级,请参阅安装 Azure PowerShell

通过机密存储 CSI 驱动程序将 TLS 与你自己的证书配合使用

若要通过机密存储 CSI 驱动程序将 TLS 与你自己的证书配合使用,需要一个配置了机密存储 CSI 驱动程序的 AKS 群集,以及一个 Azure 密钥保管库实例。

有关详细信息,请参阅设置机密存储 CSI 驱动程序以启用使用 TLS 的 NGINX 入口控制器

将 TLS 与 Let's Encrypt 证书配合使用

若要将 TLS 与 Let's Encrypt 证书配合使用,需要部署 cert-manager 用于自动生成和配置 Let's Encrypt 证书。

将 Helm 图表使用的 cert-manager 映像导入 ACR

  • 使用 az acr import 将以下映像导入 ACR。

    REGISTRY_NAME=<REGISTRY_NAME>
    CERT_MANAGER_REGISTRY=quay.azk8s.cn
    CERT_MANAGER_TAG=v1.8.0
    CERT_MANAGER_IMAGE_CONTROLLER=jetstack/cert-manager-controller
    CERT_MANAGER_IMAGE_WEBHOOK=jetstack/cert-manager-webhook
    CERT_MANAGER_IMAGE_CAINJECTOR=jetstack/cert-manager-cainjector
    
    az acr import --name $REGISTRY_NAME --source $CERT_MANAGER_REGISTRY/$CERT_MANAGER_IMAGE_CONTROLLER:$CERT_MANAGER_TAG --image $CERT_MANAGER_IMAGE_CONTROLLER:$CERT_MANAGER_TAG
    az acr import --name $REGISTRY_NAME --source $CERT_MANAGER_REGISTRY/$CERT_MANAGER_IMAGE_WEBHOOK:$CERT_MANAGER_TAG --image $CERT_MANAGER_IMAGE_WEBHOOK:$CERT_MANAGER_TAG
    az acr import --name $REGISTRY_NAME --source $CERT_MANAGER_REGISTRY/$CERT_MANAGER_IMAGE_CAINJECTOR:$CERT_MANAGER_TAG --image $CERT_MANAGER_IMAGE_CAINJECTOR:$CERT_MANAGER_TAG
    

注意

还可以将 Helm 图表导入 ACR。 有关详细信息,请参阅将 Helm 图表推送和拉取到 ACR

入口控制器配置选项

可使用静态公共 IP 地址或动态公共 IP 地址配置 NGINX 入口控制器。 如果使用自定义域,则需要将 A 记录添加到 DNS 区域。 如果不使用自定义域,则可配置入口控制器 IP 地址的完全限定的域名 (FQDN)。

创建静态或动态公共 IP 地址

使用静态公共 IP 地址

可使用静态公共 IP 地址创建入口控制器。 如果删除入口控制器,静态公共 IP 地址仍存在。 如果删除 AKS 群集,IP 地址不会保留。

升级入口控制器时,必须将参数传递给 Helm 版本,以确保入口控制器服务知道将分配给它的负载均衡器。 为使 HTTPS 证书正常工作,使用 DNS 标签来配置入口控制器 IP 地址的 FQDN。

  1. 使用 az aks show 命令获取 AKS 群集的资源组名称。

    az aks show --resource-group myResourceGroup --name myAKSCluster --query nodeResourceGroup -o tsv
    
  2. 使用 az network public-ip create 命令通过静态分配方法创建一个公共 IP 地址。 以下示例在前面的步骤中获取的 AKS 群集资源组中创建名为 myAKSPublicIP 的公共 IP 地址。

    az network public-ip create --resource-group MC_myResourceGroup_myAKSCluster_chinaeast2 --name myAKSPublicIP --sku Standard --allocation-method static --query publicIp.ipAddress -o tsv
    

注意

或者,你可以在不同的资源组中创建一个 IP 地址,该资源组可以与你的 AKS 群集分开管理。 如果在其他资源组中创建 IP 地址,请确保满足以下条件:

  • AKS 群集使用的群集标识已将权限委托给资源组,例如“网络参与者”。
  • 添加 --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-resource-group"="<RESOURCE_GROUP>" 参数。 将 <RESOURCE_GROUP> 替换为 IP 地址驻留的资源组的名称。
  1. 添加 --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-dns-label-name"="<DNS_LABEL>" 参数。 可以在首次部署入口控制器时设置 DNS 标签,也可以稍后进行配置。

  2. 添加 --set controller.service.loadBalancerIP="<STATIC_IP>" 参数。 指定在前面的步骤中创建的你自己的公共 IP 地址。

    DNS_LABEL="<DNS_LABEL>"
    NAMESPACE="ingress-basic"
    STATIC_IP=<STATIC_IP>
    
    helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
      --namespace $NAMESPACE \
      --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-dns-label-name"=$DNS_LABEL \
      --set controller.service.loadBalancerIP=$STATIC_IP \
      --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz
    

有关详细信息,请参阅将静态公共 IP 地址和 DNS 标签用于 AKS 负载均衡器

使用动态公共 IP 地址

在创建时,将为入口控制器创建一个 Azure 公共 IP 地址。 此公共 IP 地址在入口控制器的寿命期内是静态的。 如果删除入口控制器,公共 IP 地址不会保留。 如果创建新的入口控制器,将为其分配新的公共 IP 地址。 输出应类似于以下示例输出。

  • 使用 kubectl get service 命令获取入口控制器的公共 IP 地址。

    # Get the public IP address for your ingress controller
    
    kubectl --namespace ingress-basic get services -o wide -w nginx-ingress-ingress-nginx-controller
    
    # Sample output
    
    NAME                                     TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                      AGE   SELECTOR
    nginx-ingress-ingress-nginx-controller   LoadBalancer   10.0.74.133   EXTERNAL_IP     80:32486/TCP,443:30953/TCP   44s   app.kubernetes.io/component=controller,app.kubernetes.io/instance=nginx-ingress,app.kubernetes.io/name=ingress-nginx
    

将 A 记录添加到 DNS 区域

如果使用自定义域,则需要将 A 记录添加到 DNS 区域。 如果不使用自定义域,可使用 FQDN 配置公共 IP 地址。

  • 使用 New-AzDnsRecordSetaz network dns record-set a add-record 将 A 记录添加到使用 NGINX 服务的外部 IP 地址的 DNS 区域。

    az network dns record-set a add-record \
        --resource-group myResourceGroup \
        --zone-name MY_CUSTOM_DOMAIN \
        --record-set-name "*" \
        --ipv4-address MY_EXTERNAL_IP
    

配置入口控制器的 FQDN

(可选)可通过设置 DNS 标签来为入口控制器 IP 地址而不是自定义域配置 FQDN。 FQDN 应遵循以下格式:<CUSTOM DNS LABEL>.<AZURE REGION NAME>.chinacloudapp.cn

重要

DNS 标签在其 Azure 位置中必须是唯一的。

可以使用以下方法之一配置 FQDN:

  • 使用 Azure CLI 或 Azure PowerShell 设置 DNS 标签。
  • 使用 Helm 图表设置来设置 DNS 标签。

有关详细信息,请参阅公共 IP 地址 DNS 名称标签

使用 Azure CLI 或 Azure PowerShell 设置 DNS 标签

请确保将 <DNS_LABEL> 替换为唯一的 DNS 标签。

# Public IP address of your ingress controller
IP="MY_EXTERNAL_IP"

# Name to associate with public IP address
DNSLABEL="<DNS_LABEL>"

# Get the resource-id of the public IP
PUBLICIPID=$(az network public-ip list --query "[?ipAddress!=null]|[?contains(ipAddress, '$IP')].[id]" --output tsv)

# Update public IP address with DNS name
az network public-ip update --ids $PUBLICIPID --dns-name $DNSLABEL

# Display the FQDN
az network public-ip show --ids $PUBLICIPID --query "[dnsSettings.fqdn]" --output tsv

使用 Helm 图表设置来设置 DNS 标签

可以使用 --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-dns-label-name" 参数将注释设置传递给 Helm 图表配置。 可以在首次部署入口控制器时设置此参数,也可以稍后进行配置。

以下示例演示如何在部署控制器后更新此设置。 请确保将 <DNS_LABEL> 替换为唯一的 DNS 标签。

DNSLABEL="<DNS_LABEL>"
NAMESPACE="ingress-basic"

helm upgrade ingress-nginx ingress-nginx/ingress-nginx \
  --namespace $NAMESPACE \
  --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-dns-label-name"=$DNSLABEL \
  --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz

安装证书管理器

NGINX 入口控制器支持 TLS 终止。 可通过多种方法为 HTTPS 检索和配置证书。 本文使用 cert-manager,它提供 Lets Encrypt 证书自动生成和管理功能。

若要安装证书管理器控制器,请使用以下命令。

# Set variable for ACR location to use for pulling images
ACR_URL=<REGISTRY_URL>

# Label the ingress-basic namespace to disable resource validation
kubectl label namespace ingress-basic cert-manager.io/disable-validation=true

# Add the Jetstack Helm repository
helm repo add jetstack https://charts.jetstack.io

# Update your local Helm chart repository cache
helm repo update

# Install the cert-manager Helm chart
helm install cert-manager jetstack/cert-manager \
  --namespace ingress-basic \
  --version=$CERT_MANAGER_TAG \
  --set installCRDs=true \
  --set nodeSelector."kubernetes\.io/os"=linux \
  --set image.repository=$ACR_URL/$CERT_MANAGER_IMAGE_CONTROLLER \
  --set image.tag=$CERT_MANAGER_TAG \
  --set webhook.image.repository=$ACR_URL/$CERT_MANAGER_IMAGE_WEBHOOK \
  --set webhook.image.tag=$CERT_MANAGER_TAG \
  --set cainjector.image.repository=$ACR_URL/$CERT_MANAGER_IMAGE_CAINJECTOR \
  --set cainjector.image.tag=$CERT_MANAGER_TAG

若要详细了解证书管理器配置,请参阅证书管理器项目

创建 CA 群集证书颁发者

在可以颁发证书之前,cert-manager 需要下列证书颁发者之一:

  • 在单个命名空间中运行的 Issuer
  • 在所有命名空间中运行的 ClusterIssuer 资源。

有关详细信息,请参阅证书管理器颁发者文档。

  1. 使用以下示例清单创建群集证书颁发者,例如 cluster-issuer.yaml。 请将 MY_EMAIL_ADDRESS 替换为你的组织的有效地址。

    apiVersion: cert-manager.io/v1
    kind: ClusterIssuer
    metadata:
      name: letsencrypt
    spec:
      acme:
        server: https://acme-v02.api.letsencrypt.org/directory
        email: MY_EMAIL_ADDRESS
        privateKeySecretRef:
          name: letsencrypt
        solvers:
        - http01:
            ingress:
              class: nginx
              podTemplate:
                spec:
                  nodeSelector:
                    "kubernetes.io/os": linux
    
  2. 使用 kubectl apply 命令应用颁发者。

    kubectl apply -f cluster-issuer.yaml --namespace ingress-basic
    

更新入口路由

需要更新入口路由,以处理发送到 FQDN 或自定义域的流量。

在以下示例中,流量按如下方式路由:

  • 发往 hello-world-ingress.MY_CUSTOM_DOMAIN 的流量将路由到 aks-helloworld-one 服务。
  • 发往 hello-world-ingress.MY_CUSTOM_DOMAIN/hello-world-two 的流量将路由到 aks-helloworld-two 服务。
  • 到 hello-world-ingress.MY_CUSTOM_DOMAIN/static 的流量会路由到静态资产的名为 aks-helloworld-one 的服务 。

注意

如果为入口控制器 IP 地址(而不是自定义域)配置了 FQDN,请使用 FQDN 而不是 hello-world-ingress.MY_CUSTOM_DOMAIN。

例如,如果 FQDN 为 demo-aks-ingress.chinaeast2.chinacloudapp.cn,请在 hello-world-ingress.yaml 中将 hello-world-ingress.MY_CUSTOM_DOMAIN 替换为 demo-aks-ingress.chinanorth2.chinacloudapp.cn。

  1. 使用以下示例 YAML 文件创建或更新 hello-world-ingress.yaml 文件。 将 spec.tls.hostsspec.rules.host 更新为在前面步骤中创建的 DNS 名称。

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: hello-world-ingress
      annotations:
        nginx.ingress.kubernetes.io/rewrite-target: /$2
        nginx.ingress.kubernetes.io/use-regex: "true"
        cert-manager.io/cluster-issuer: letsencrypt
    spec:
      ingressClassName: nginx
      tls:
      - hosts:
        - hello-world-ingress.MY_CUSTOM_DOMAIN
        secretName: tls-secret
      rules:
      - host: hello-world-ingress.MY_CUSTOM_DOMAIN
        http:
          paths:
          - path: /hello-world-one(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-one
                port:
                  number: 80
          - path: /hello-world-two(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-two
                port:
                  number: 80
          - path: /(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-one
                port:
                  number: 80
    ---
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: hello-world-ingress-static
      annotations:
        nginx.ingress.kubernetes.io/ssl-redirect: "false"
        nginx.ingress.kubernetes.io/rewrite-target: /static/$2
    spec:
      ingressClassName: nginx
      tls:
      - hosts:
        - hello-world-ingress.MY_CUSTOM_DOMAIN
        secretName: tls-secret
      rules:
      - host: hello-world-ingress.MY_CUSTOM_DOMAIN
        http:
          paths:
          - path: /static(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-one
                port:
                  number: 80
    
  2. 使用 kubectl apply 命令更新入口资源。

    kubectl apply -f hello-world-ingress.yaml --namespace ingress-basic
    

验证是否已创建证书对象

接下来,必须创建证书资源。 证书资源定义了必需的 X.509 证书。 有关详细信息,请参阅证书管理器证书

cert-manager 使用 ingress-shim(自 v0.2.2 以来随证书管理器自动部署)自动创建证书对象。 有关详细信息,请参阅 ingress-shim 文档

若要验证证书是否已成功创建,请使用 kubectl get certificate --namespace ingress-basic 命令,并验证“READY”是否为“True”。 获取输出可能需要几分钟时间。

kubectl get certificate --namespace ingress-basic

以下输出显示了证书的状态。

NAME         READY   SECRET       AGE
tls-secret   True    tls-secret   11m

测试入口配置

打开 Web 浏览器并访问 Kubernetes 入口控制器的 hello-world-ingress.MY_CUSTOM_DOMAIN 或 FQDN。 确保符合以下条件:

  • 你已重定向以使用 HTTPS。
  • 证书受信任。
  • 演示应用程序显示在 Web 浏览器中。
  • 将 /hello-world-two 添加到域的末尾,并确保显示了具有自定义标题的第二个演示应用程序。

清理资源

本文使用 Helm 来安装入口组件、证书和示例应用。 在部署 Helm 图表时,会创建若干 Kubernetes 资源。 这些资源包括 pod、部署和服务。 若要清理这些资源,可以删除整个示例命名空间,也可以删除单个资源。

删除示例命名空间以及所有资源

删除示例命名空间还会删除命名空间中的所有资源。

  • 使用 kubectl delete 命令并指定命名空间名称来删除整个示例命名空间。

    kubectl delete namespace ingress-basic
    

单独删除资源

或者,可以单独删除资源。

  1. 删除群集颁发者资源。

    kubectl delete -f cluster-issuer.yaml --namespace ingress-basic
    
  2. 使用 helm list 命令列出 Helm 版本。 查找名为“nginx”和“cert-manager”的图表,如以下示例输出中所示。

    $ helm list --namespace ingress-basic
    
    NAME                    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION
    cert-manager            ingress-basic   1               2020-01-15 10:23:36.515514 -0600 CST    deployed        cert-manager-v0.13.0    v0.13.0
    nginx                   ingress-basic   1               2020-01-15 10:09:45.982693 -0600 CST    deployed        nginx-ingress-1.29.1    0.27.0
    
  3. 使用 helm uninstall 命令卸载这些版本。 以下示例将卸载 NGINX 入口和证书管理器部署。

    $ helm uninstall cert-manager nginx --namespace ingress-basic
    
    release "cert-manager" uninstalled
    release "nginx" uninstalled
    
  4. 删除两个示例应用程序。

    kubectl delete -f aks-helloworld-one.yaml --namespace ingress-basic
    kubectl delete -f aks-helloworld-two.yaml --namespace ingress-basic
    
  5. 删除将流量定向到示例应用的入口路由。

    kubectl delete -f hello-world-ingress.yaml --namespace ingress-basic
    
  6. 删除命名空间自身。 使用 kubectl delete 命令并指定你的命名空间名称。

    kubectl delete namespace ingress-basic
    

后续步骤

本文包含 AKS 的一些外部组件。 若要详细了解这些组件,请参阅以下项目页面:

也可执行以下操作: