使用 Ingress 在 HTTPS 負載平衡中使用多個 SSL 憑證


本頁面說明如何在 Google Kubernetes Engine (GKE) 叢集中,為 Ingress 資源設定多個 SSL 憑證。

總覽

如果要接受來自用戶端的 HTTPS 要求,應用程式負載平衡器必須要有憑證,以向用戶端證明其身分。負載平衡器還必須要有私密金鑰才能完成 HTTPS 握手。

當負載平衡器接受來自用戶端的 HTTPS 要求時,用戶端和負載平衡器之間的流量將會使用 TLS 加密。不過負載平衡器會終止 TLS 加密,並將要求在不加密的情況下轉給應用程式。透過 Ingress 設定應用程式負載平衡器時,可以設定負載平衡器向用戶端顯示最多 10 個 TLS 憑證。

負載平衡器會使用伺服器名稱指示 (SNI),依據 TLS 握手中的網域名稱判斷要向客戶呈現哪些憑證。若用戶端不使用 SNI,或者用戶端使用的網域名稱與其中一個憑證的常用名稱 (CN) 名稱不符,負載平衡器就會使用 Ingress 中列出的第一個憑證。

下圖顯示負載平衡器如何根據要求中使用的網域名稱,將流量傳送至不同後端:

「使用 Ingress 設定多個 SSL 憑證」系統圖表

您可以透過下列方法,為應用程式負載平衡器提供 SSL 憑證:

  • Google 代管的 SSL 憑證。 如需相關操作方式,請參閱代管憑證頁面。

  • Trusted Cloud by S3NS 您自行管理的 SSL 憑證。SSL 憑證會使用您上傳到 Trusted Cloud by S3NS 專案的預先共用憑證。

  • Kubernetes 密鑰。 密鑰包含您自行建立的憑證和金鑰。將 Secret 名稱新增至 Ingress 資訊清單的 tls 欄位。

您可以在同一個 Ingress 中使用多個方法,如此一來,在方法間進行遷移時就不會出現停機時間。

縱觀全局

以下概要說明本文中的步驟:

  1. 建立 Deployment。

  2. 建立 Service。

  3. 建立兩個憑證檔案和兩個金鑰檔案,或兩個 ManagedCertificate 物件。您必須在與負載平衡器部署位置相同的專案和命名空間中,設定這些憑證。

  4. 建立使用密鑰或預先共用憑證的 Ingress。 建立 Ingress 時,GKE 會建立並設定應用程式負載平衡器。

  5. 測試應用程式負載平衡器。

事前準備

開始之前,請確認你已完成下列工作:

  • 啟用 Google Kubernetes Engine API。
  • 啟用 Google Kubernetes Engine API
  • 如要使用 Google Cloud CLI 執行這項工作,請安裝初始化 gcloud CLI。如果您先前已安裝 gcloud CLI,請執行 gcloud components update,取得最新版本。
  • 您必須擁有兩個網域名稱。網域名稱的長度不得超過 63 個字元。

限制

  • Google 代管憑證僅適用於使用外部應用程式負載平衡器的 GKE Ingress。Google 代管憑證不支援第三方 Ingress 控制器。

  • 如果是內部應用程式負載平衡器,您必須在 Ingress 資訊清單中停用 HTTP。外部負載平衡器則不必執行這項操作。

  • 您不得手動變更或更新應用程式負載平衡器的設定。也就是說,您不得編輯任何負載平衡器的元件,包括目標 proxy、網址對應和後端服務。您所做的任何變更都將被 GKE 覆寫。

可建立部署作業

  1. 將下列資訊清單儲存為 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 的部署。每個 Pod 都有兩個容器。其中一個容器會執行 hello-app:1.0 並監聽 TCP 通訊埠 50001。另一個容器會執行 hello-app:2.0 並監聽 TCP 通訊埠 50002。

  2. 將資訊清單套用至叢集:

    kubectl apply -f my-mc-deployment.yaml
    

建立 Service

  1. 將下列資訊清單儲存為 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。
  2. 將資訊清單套用至叢集:

    kubectl apply -f my-mc-service.yaml
    

建立憑證和金鑰

若要進行此頁面的練習,您要有兩個憑證,每個憑證都有對應的金鑰。每一個憑證都必須有一個與您所擁有的網域相等的共用名稱 (CN)。

您可以手動建立這些憑證,也可以使用 Google 代管的憑證。

若您已有具備適當的「共用名稱」值的兩個憑證檔案,可直接前往下一節。

使用者管理的憑證

  1. 建立第一個金鑰:

    openssl genrsa -out test-ingress-1.key 2048
    
  2. 建立第一個憑證簽署要求:

    openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \
        -subj "/CN=FIRST_DOMAIN"
    

    FIRST_DOMAIN 替換為您擁有的網域名稱,例如 example.com

  3. 建立第一個憑證:

    openssl x509 -req -days 365 -in test-ingress-1.csr -signkey test-ingress-1.key \
        -out test-ingress-1.crt
    
  4. 建立第二個金鑰:

    openssl genrsa -out test-ingress-2.key 2048
    
  5. 建立第二個憑證簽署要求:

    openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \
        -subj "/CN=SECOND_DOMAIN"
    

    SECOND_DOMAIN 替換為您擁有的其他網域名稱,例如 examplepetstore.com

  6. 建立第二個憑證:

    openssl x509 -req -days 365 -in test-ingress-2.csr -signkey test-ingress-2.key \
        -out test-ingress-2.crt
    

如要進一步瞭解憑證和金鑰,請參閱「安全資料傳輸層 (SSL) 憑證總覽」。

您現在有兩個憑證檔案和兩個金鑰檔案。

其餘工作會使用下列預留位置來參照您的網域、憑證和金鑰:

  • 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 資訊清單中,可以使用下列其中一種方法來提供負載平衡器的憑證:

  • 密鑰
  • 預先共用的憑證
  • Google 代管憑證

密鑰

  1. 建立包含第一個憑證和金鑰的「密鑰」:

    kubectl create secret tls FIRST_SECRET_NAME \
        --cert=FIRST_CERT_FILE \
        --key=FIRST_KEY_FILE
    
  2. 建立包含第二個憑證和金鑰的「密鑰」:

    kubectl create secret tls SECOND_SECRET_NAME \
        --cert=SECOND_CERT_FILE \
        --key=SECOND_KEY_FILE
    

建立 Ingress

  1. 將下列資訊清單儲存為 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_DOMAINSECOND_DOMAIN 替換為您擁有的網域名稱,例如 example.comexamplepetstore.com

  2. 將資訊清單套用至叢集:

    kubectl apply -f my-mc-ingress.yaml
    
  3. 說明您的 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
    

    此結果顯示有兩個密鑰與 Ingress 關聯。輸出也會顯示負載平衡器的外部 IP 位址。如果未設定外部 IP 位址,請稍候幾分鐘,然後再次嘗試執行指令。

Pre-shared certs (預先共用的憑證)

  1. 建立憑證:

    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:您的第一個金鑰檔案。
  2. 建立第二個憑證:

    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:第二個金鑰檔案。
  3. 查看您的憑證資源:

    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

  1. 將下列資訊清單儲存為 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_DOMAINSECOND_DOMAIN 替換成您的網域名稱。

    這份資訊清單說明 Ingress,在註解中列出預先共用的憑證資源。

  2. 將資訊清單套用至叢集:

    kubectl apply -f my-psc-ingress.yaml
    
  3. 說明您的 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_NAMESECOND_CERT_NAME 的預先共用憑證有關聯。輸出也會顯示負載平衡器的外部 IP 位址。如果未設定外部 IP 位址,請稍候幾分鐘,然後再次嘗試執行指令。

Google 代管的憑證

建立 Ingress

  1. 將下列資訊清單儲存為 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_DOMAINSECOND_DOMAIN 替換成您的網域名稱。

    這份資訊清單說明 Ingress,在註解中列出預先共用的憑證資源。

  2. 將資訊清單套用至叢集:

    kubectl apply -f my-gmc-ingress.yaml
    
  3. 說明您的 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_NAMESECOND_CERT_NAME 的代管憑證有關聯。GKE 會自動使用您透過 ManagedCertificate 物件建立的 Google 代管憑證,填入 ingress.gcp.kubernetes.io/pre-shared-certingress.kubernetes.io/ssl-cert 註解。輸出也會顯示負載平衡器的外部 IP 位址。如果未設定外部 IP 位址,請稍候片刻,然後再次嘗試執行指令。

測試負載平衡器

請稍候五分鐘,等待 GKE 設定負載平衡器完成。

如果您使用 Google 代管的憑證,系統需要佈建憑證並驗證指定網域的 DNS 設定,因此完成設定的時間可能會大幅延長。

如要測試負載平衡器,您必須擁有兩個網域名稱,且這兩個網域名稱必須解析外部應用程式負載平衡器的外部 IP 位址。

  1. 使用您的第一個網域名稱傳送要求至負載平衡器:

    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 握手使用您的第一個憑證。

  2. 使用第二個網域名稱,將要求傳送至負載平衡器。

    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 物件的主機欄位。

IngressSpectls 欄位是 IngressTLS 物件的陣列。每個 IngressTLS 物件都有 hosts 欄位和 SecretName 欄位。 GKE 中不使用 hosts 欄位。GKE 會從密鑰中的憑證讀取共用名稱 (CN)。若共用名稱符合用戶端要求中的網域名稱,負載平衡器便會對用戶端提供相符的憑證。

提供的憑證有哪些?

負載平衡器會根據下列這些規則選擇憑證

  • 若密鑰和預先共用的憑證都列於 Ingress 中,預先共用的憑證會優先於密鑰。換句話說,系統仍會納入密鑰,但會優先顯示預先共用的憑證。

  • 若憑證的共用名稱 (CN) 都不符合服務用戶端要求中的網域名稱,負載平衡器便會向用戶端提供主要憑證。

  • 針對 tls 區塊中列出的密鑰,主要憑證是清單中的第一個密鑰。

  • 針對註解中列出的預先共用憑證,主要憑證為清單中的第一個憑證。

憑證輪替最佳做法

如要輪替 Secret 或預先共用憑證的內容,請參考下列最佳做法:

  • 建立含有新憑證資料的新密鑰或預先共用憑證,並使用不同名稱。按照先前提供的操作說明,將這個資源 (連同現有資源) 附加至 Ingress。確認變更沒有問題後,即可從 Ingress 移除舊憑證。
  • 如果不在意流量中斷,可以從 Ingress 移除舊資源,以相同名稱但不同內容佈建新資源,然後重新附加至 Ingress。

如要避免自行管理憑證輪替,請參閱「使用 Google 代管的 SSL 憑證」。

疑難排解

指定無效或不存在的密鑰會導致 Kubernetes 事件錯誤。 您可以查看 Ingress 的 Kubernetes 事件,如下:

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'

後續步驟