本页面介绍如何为 Google Kubernetes Engine (GKE) 集群中的 Ingress 资源配置多个 SSL 证书。
概览
如果您想要接受来自客户端的 HTTPS 请求,则应用负载均衡器必须具有证书,这样才能向客户端证明其身份。负载均衡器还必须具有私钥才能完成 HTTPS 握手。
当负载均衡器接受来自客户端的 HTTPS 请求时,客户端与负载均衡器之间的流量使用 TLS 进行加密。但是,负载均衡器可终止 TLS 加密,并将未加密的请求转发给应用。通过 Ingress 配置 HTTP(S) 应用负载均衡器时,可以将负载均衡器配置为最多向客户端提供 10 个 TLS 证书。
负载均衡器使用服务器名称指示 (SNI),根据 TLS 握手中的域名确定要向客户端提供哪个证书。如果客户端不使用 SNI,或者客户端使用的域名与其中一个证书中的公用名 (CN) 不匹配,则负载均衡器会使用 Ingress 中列出的第一个证书。
下图展示了负载均衡器根据请求中使用的域名将流量发送到不同后端:
您可以使用以下方法为应用负载均衡器提供 SSL 证书:
- Google 管理的 SSL 证书。如需了解如何使用它们,请参阅托管式证书页面。
您自己管理的 Google Cloud SSL 证书。SSL 证书使用您上传到 Google Cloud 项目的预共享证书。
Kubernetes Secret。Secret 中包含您自行创建的证书和密钥。您将 Secret 的名称添加到 Ingress 清单的
tls
字段。
您可以在同一 Ingress 中使用多个方法。这可实现各方法之间的无停机时间迁移。
概览
以下是本文档中的步骤概览:
创建 Deployment
创建 Service
创建 2 个证书文件和 2 个密钥文件或 2 个
ManagedCertificate
对象。您必须在部署负载均衡器的项目和命名空间中配置这些证书。创建使用 Secret 或预共享证书的 Ingress。当您创建 Ingress 时,GKE 会创建并配置应用负载均衡器。
测试应用负载均衡器。
准备工作
在开始之前,请确保您已执行以下任务:
- 启用 Google Kubernetes Engine API。 启用 Google Kubernetes Engine API
- 如果您要使用 Google Cloud CLI 执行此任务,请安装并初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行
gcloud components update
以获取最新版本。
- 您必须拥有两个域名。域名长度不得超过 63 个字符。
限制
- 只有使用外部应用负载均衡器的 GKE Ingress 支持 Google 管理的证书。Google 管理的证书不支持第三方 Ingress 控制器。
- 对于内部应用负载均衡器,您必须在 Ingress 清单中停用 HTTP。对于外部负载均衡器,您不需要这样做。
- 您不得手动更改或更新应用负载均衡器的配置。这意味着您不得修改负载均衡器的任何组件,包括目标代理、网址映射和后端服务。您所做的任何更改都会被 GKE 覆盖。
创建 Deployment
将以下清单保存为
my-mc-deployment.yaml
:apiVersion: apps/v1 kind: Deployment metadata: name: my-mc-deployment spec: selector: matchLabels: app: products department: sales replicas: 3 template: metadata: labels: app: products department: sales spec: containers: - name: hello image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0" env: - name: "PORT" value: "50001" - name: hello-again image: "us-docker.pkg.dev/google-samples/containers/gke/hello-app:2.0" env: - name: "PORT" value: "50002"
此清单描述了具有三个 Pod 的 Deployment。每个 Pod 都有两个容器。一个容器运行
hello-app:1.0
并监听 TCP 端口 50001。另一个容器运行hello-app:2.0
并监听 TCP 端口 50002。将清单应用到您的集群:
kubectl apply -f my-mc-deployment.yaml
创建一个 Service
将以下清单保存为
my-mc-service.yaml
:apiVersion: v1 kind: Service metadata: name: my-mc-service spec: type: NodePort selector: app: products department: sales ports: - name: my-first-port protocol: TCP port: 60001 targetPort: 50001 - name: my-second-port protocol: TCP port: 60002 targetPort: 50002
此清单描述了具有以下字段的 Service:
selector
:指定任何具有app: products
标签和department: sales
标签的 Pod 都是此 Service 的成员。ports
:指定当客户端向my-first-port
上的 Service 发送请求时,GKE 会将请求转发到端口 50001 上的某个成员 Pod。当客户端将请求发送到my-second-port
上的 Service 时,GKE 会将请求转发到端口 50002 上的某个成员 Pod。
将清单应用到您的集群:
kubectl apply -f my-mc-service.yaml
创建证书和密钥
要完成本页中的练习,您需要两个证书,每个证书都有一个相应的密钥。每个证书的公用名 (CN) 必须与您拥有的域名一致。
您可以手动创建这些证书,也可以使用 Google 管理的证书。如果您的两个证书文件的公用名都有适当的值,可跳转至下一部分。
用户管理的证书
创建第一个密钥:
openssl genrsa -out test-ingress-1.key 2048
创建第一个证书签名请求:
openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \ -subj "/CN=FIRST_DOMAIN"
将
FIRST_DOMAIN
替换为您拥有的域名,例如example.com
。创建第一个证书:
openssl x509 -req -days 365 -in test-ingress-1.csr -signkey test-ingress-1.key \ -out test-ingress-1.crt
创建第二个密钥:
openssl genrsa -out test-ingress-2.key 2048
创建第二个证书签名请求:
openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \ -subj "/CN=SECOND_DOMAIN"
将
SECOND_DOMAIN
替换为您拥有的另一个域名,例如examplepetstore.com
。创建第二个证书:
openssl x509 -req -days 365 -in test-ingress-2.csr -signkey test-ingress-2.key \ -out test-ingress-2.crt
如需详细了解证书和密钥,请参阅 SSL 证书概览。
您现在有了 2 个证书文件和 2 个密钥文件。
其余任务使用以下占位符来引用您的网域、证书和密钥:
FIRST_CERT_FILE
:第一个证书文件的路径。FIRST_KEY_FILE
:与第一个证书对应的密钥文件的路径。FIRST_DOMAIN
:您拥有的一个域名。FIRST_SECRET_NAME
:包含第一个证书和密钥的 Secret 的名称。SECOND_CERT_FILE
:第二个证书文件的路径。SECOND_KEY_FILE
:与第二个证书对应的密钥文件的路径。SECOND_DOMAIN
:您拥有的另一个域名。SECOND_SECRET_NAME
:包含第二个证书和密钥的 Secret 的名称。
Google 管理的证书
如需创建 Google 管理的证书,您必须将 ManagedCertificate
对象添加到 Ingress 的命名空间。您可以使用以下模板为网域定义证书:
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: FIRST_CERT_NAME
spec:
domains:
- FIRST_DOMAIN
---
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: SECOND_CERT_NAME
spec:
domains:
- SECOND_DOMAIN
替换以下内容:
FIRST_CERT_NAME
:第一个ManagedCertificate
对象的名称。FIRST_DOMAIN
:您拥有的第一个网域。SECOND_CERT_NAME
:第二个ManagedCertificate
对象的名称。SECOND_DOMAIN
:您拥有的第二个网域。
ManagedCertificate
对象的名称不同于其创建的实际证书的名称。您只需知道 ManagedCertificate
对象的名称,以在 Ingress 中使用它们。
为 Ingress 指定证书
下一步是创建 Ingress 对象。在 Ingress 清单中,您可以使用以下某种方法为负载均衡器提供证书:
- Secret
- 预共享证书
- Google 管理的证书
密钥
创建包含第一个证书和密钥的 Secret:
kubectl create secret tls FIRST_SECRET_NAME \ --cert=FIRST_CERT_FILE \ --key=FIRST_KEY_FILE
创建包含第二个证书和密钥的 Secret:
kubectl create secret tls SECOND_SECRET_NAME \ --cert=SECOND_CERT_FILE \ --key=SECOND_KEY_FILE
创建 Ingress
将以下清单保存为
my-mc-ingress.yaml
:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-mc-ingress spec: tls: - secretName: FIRST_SECRET_NAME - secretName: SECOND_SECRET_NAME rules: - host: FIRST_DOMAIN http: paths: - pathType: ImplementationSpecific backend: service: name: my-mc-service port: number: 60001 - host: SECOND_DOMAIN http: paths: - pathType: ImplementationSpecific backend: service: name: my-mc-service port: number: 60002
将
FIRST_DOMAIN
和SECOND_DOMAIN
替换为您拥有的域名,例如example.com
和examplepetstore.com
。将清单应用到您的集群:
kubectl apply -f my-mc-ingress.yaml
描述 Ingress:
kubectl describe ingress my-mc-ingress
输出类似于以下内容:
Name: my-mc-ingress Address: 203.0.113.1 ... TLS: FIRST_SECRET_NAME terminates SECOND_SECRET_NAME terminates Rules: Host Path Backends ---- ---- -------- FIRST_DOMAIN my-mc-service:my-first-port (<none>) SECOND_DOMAIN my-mc-service:my-second-port (<none>) Annotations: ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ADD 3m loadbalancer-controller default/my-mc-ingress Normal CREATE 2m loadbalancer-controller ip: 203.0.113.1
此输出显示两个 Secret 都与 Ingress 相关联。它还显示了负载均衡器的外部 IP 地址。 如果未设置外部 IP 地址,请等待几分钟,然后重试该命令。
预共享证书
创建证书:
gcloud compute ssl-certificates create FIRST_CERT_NAME \ --certificate=FIRST_CERT_FILE \ --private-key=FIRST_KEY_FILE
替换以下内容:
FIRST_CERT_NAME
:您的第一个证书的名称。FIRST_CERT_FILE
:您的第一个证书文件。FIRST_KEY_FILE
:您的第一个密钥文件。
创建第二个证书:
gcloud compute ssl-certificates create SECOND_CERT_NAME \ --certificate=SECOND_CERT_FILE \ --private-key=SECOND_KEY_FILE
替换以下内容:
SECOND_CERT_NAME
:您的第二个证书的名称。SECOND_CERT_FILE
:您的第二个证书文件。SECOND_KEY_FILE
:您的第二个密钥文件。
查看您的证书资源:
gcloud compute ssl-certificates list
输出类似于以下内容:
NAME CREATION_TIMESTAMP FIRST_CERT_NAME 2018-11-03T12:08:47.751-07:00 SECOND_CERT_NAME 2018-11-03T12:09:25.359-07:00
创建 Ingress
将以下清单保存为
my-psc-ingress.yaml
:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-psc-ingress annotations: ingress.gcp.kubernetes.io/pre-shared-cert: "FIRST_CERT_NAME,SECOND_CERT_NAME" spec: rules: - host: FIRST_DOMAIN http: paths: - pathType: ImplementationSpecific backend: service: name: my-mc-service port: number: 60001 - host: SECOND_DOMAIN http: paths: - pathType: ImplementationSpecific backend: service: name: my-mc-service port: number: 60002
将
FIRST_DOMAIN
和SECOND_DOMAIN
替换为您的域名。此清单描述了一个在注解中列出预共享证书资源的 Ingress。
将清单应用到您的集群:
kubectl apply -f my-psc-ingress.yaml
描述 Ingress:
kubectl describe ingress my-psc-ingress
输出类似于以下内容:
Name: my-psc-ingress Address: 203.0.113.2 ... Rules: Host Path Backends ---- ---- -------- FIRST_DOMAIN my-mc-service:my-first-port (<none>) SECOND_DOMAIN my-mc-service:my-second-port (<none>) Annotations: ... ingress.gcp.kubernetes.io/pre-shared-cert: FIRST_CERT_NAME,SECOND_CERT_NAME ... ingress.kubernetes.io/ssl-cert: FIRST_CERT_NAME,SECOND_CERT_NAME Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ADD 2m loadbalancer-controller default/my-psc-ingress Normal CREATE 1m loadbalancer-controller ip: 203.0.113.2
输出结果显示 Ingress 与名为
FIRST_CERT_NAME
和SECOND_CERT_NAME
的预共享证书相关联。它还显示了负载均衡器的外部 IP 地址。如果未设置外部 IP 地址,请等待几分钟,然后重试该命令。
Google 管理的证书
创建 Ingress
将以下清单保存为
my-gmc-ingress.yaml
:apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-gmc-ingress annotations: networking.gke.io/managed-certificates: "FIRST_CERT_NAME,SECOND_CERT_NAME" spec: rules: - host: FIRST_DOMAIN http: paths: - pathType: ImplementationSpecific backend: service: name: my-mc-service port: number: 60001 - host: SECOND_DOMAIN http: paths: - pathType: ImplementationSpecific backend: service: name: my-mc-service port: number: 60002
将
FIRST_DOMAIN
和SECOND_DOMAIN
替换为您的域名。此清单描述了一个在注解中列出预共享证书资源的 Ingress。
将清单应用到您的集群:
kubectl apply -f my-gmc-ingress.yaml
描述 Ingress:
kubectl describe ingress my-gmc-ingress
输出类似于以下内容:
Name: my-gmc-ingress Address: 203.0.113.2 ... Rules: Host Path Backends ---- ---- -------- FIRST_DOMAIN my-mc-service:my-first-port (<none>) SECOND_DOMAIN my-mc-service:my-second-port (<none>) Annotations: ... ingress.gcp.kubernetes.io/pre-shared-cert: mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4 ... ingress.kubernetes.io/ssl-cert: mcrt-a6e41ce4-2b39-4334-84ce-867ff543c424,mcrt-bbff4116-f014-4800-a43a-4095bffeb4f4 networking.gke.io/managed-certificates: FIRST_CERT_NAME,SECOND_CERT_NAME Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ADD 2m loadbalancer-controller default/my-gmc-ingress Normal CREATE 1m loadbalancer-controller ip: 203.0.113.2
输出结果显示 Ingress 与名为
FIRST_CERT_NAME
和SECOND_CERT_NAME
的代管式证书相关联。 GKE 会自动使用您使用ManagedCertificate
对象创建的 Google 管理的证书填充ingress.gcp.kubernetes.io/pre-shared-cert
和ingress.kubernetes.io/ssl-cert
注解。输出还显示了负载均衡器的外部 IP 地址。如果未设置外部 IP 地址,请等待几分钟,然后重试该命令。
测试负载均衡器
请等待 5 分钟左右,让 GKE 完成负载均衡器的配置。
如果您使用了 Google 管理的证书,则完成配置可能需要相当长的时间,因为系统需要预配证书并验证给定网域的 DNS 配置。如需测试负载均衡器,您必须拥有两个域名,并且两个域名都必须解析外部应用负载均衡器的外部 IP 地址。
使用您的第一个域名向负载均衡器发送请求:
curl -v https://FIRST_DOMAIN
您可能需要使用
curl -k
选项来执行不安全的 SSL 传输,以便curl
接受自签名证书。输出类似于以下内容:
... * Trying 203.0.113.1... ... * Connected to FIRST_DOMAIN (203.0.113.1) port 443 (#0) ... * TLSv1.2 (IN), TLS handshake, Certificate (11): ... * Server certificate: * subject: CN=FIRST_DOMAIN ... > Host: FIRST_DOMAIN.com ... Hello, world! Version: 1.0.0 ...
此输出显示您的第一个证书用于 TLS 握手。
使用您的第二个域名向负载均衡器发送请求:
curl -v https://SECOND_DOMAIN
输出类似于以下内容:
... * Trying 203.0.113.1... ... * Connected to SECOND_DOMAIN (203.0.113.1) port 443 (#0) ... * Server certificate: * subject: CN=SECOND_DOMAIN ... > Host: SECOND_DOMAIN ... Hello, world! Version: 2.0.0
此输出显示您的第二个证书用于 TLS 握手。
Ingress 对象的 hosts 字段
IngressSpec 具有一个 tls
字段,它是一组 IngressTLS 对象。每个 IngressTLS
对象都有一个 hosts
字段和 SecretName
字段。在 GKE 中,不使用 hosts
字段。GKE 在 Secret 中读取证书的公用名 (CN)。如果公用名与客户端请求中的域名匹配,则负载均衡器向客户端提供所匹配的证书。
提供哪种证书?
负载均衡器根据以下规则选择证书:
如果 Ingress 中同时列出了 Secret 和预共享证书,则预共享证书优先于 Secret。换句话说,仍然包含 Secret,但首先提供预共享证书。
如果所有证书的公用名 (CN) 与客户端请求中的域名均不匹配,则负载均衡器将提供主证书。
对于
tls
块中列出的 Secret,主证书位于列表中的第一个 Secret 中。对于注释中列出的预共享证书,主证书是列表中的第一个证书。
证书轮替最佳做法
如果要轮替 Secret 或预共享证书的内容,请遵循以下最佳实践:
- 使用其他名称创建包含新证书数据的新 Secret 或预共享证书。按照前面提供的说明,将此资源(以及现有资源)关联到 Ingress。对更改感到满意后,您可以从 Ingress 中移除旧证书。
- 如果您不想中断流量,则可以从 Ingress 中移除旧资源,预配名称相同但内容不同的新资源,然后将其重新关联到 Ingress。
问题排查
如果指定的 Secret 无效或不存在,则会发生 Kubernetes 事件错误。您可以按如下方式检查 Kubernetes 事件的 Ingress:
kubectl describe ingress
输出类似于以下内容:
Name: my-ingress
Namespace: default
Address: 203.0.113.3
Default backend: hello-server:8080 (10.8.0.3:8080)
TLS:
my-faulty-Secret terminates
Rules:
Host Path Backends
---- ---- --------
* * my-service:443 (10.8.0.3:443)
Events:
Error during sync: cannot get certs for Ingress default/my-ingress:
Secret "my-faulty-ingress" has no 'tls.crt'
后续步骤
- 阅读 GKE 网络概览。
- 了解如何使用静态 IP 地址配置域名。
- 如果应用在不同区域的多个 GKE 集群上运行,请配置多集群 Ingress,以将流量路由到离用户最近区域的集群。