このページでは、Google Kubernetes Engine(GKE)クラスタの Ingress リソースに複数の SSL 証明書を構成する方法について説明します。
概要
クライアントからの HTTPS リクエストを受け入れるには、アプリケーション ロードバランサは証明書を持っていなければなりません。これにより、適切な送信先であることをクライアントに示すことができます。さらに、ロードバランサには、HTTPS handshake を完了するための秘密鍵も必要です。
ロードバランサがクライアントからの HTTPS リクエストを受け入れると、クライアントとロードバランサ間のトラフィックは TLS で暗号化されます。ただし、TLS 暗号化はロードバランサで終端し、アプリケーションへのリクエストの転送は暗号化なしに行われます。Ingress によるアプリケーション ロードバランサを構成する際には、最大 10 個の TLS 証明書をクライアントに提示するようにロードバランサを構成できます。
ロードバランサでは、TLS handshake のドメイン名に基づいて、Server Name Indication(SNI)を使用してクライアントに提示する証明書が決定されます。クライアントで SNI が使用されていないか、いずれの証明書の共通名(CN)とも一致しないドメイン名が使用されている場合は、Ingress で最初に表示される証明書が使用されます。
次の図は、リクエストで使用されているドメイン名に応じて、複数の異なるバックエンドにトラフィックを送信するロードバランサを示しています。
次の方法で、アプリケーション ロードバランサに SSL 証明書を提供できます。
- Google マネージド SSL 証明書。使用方法については、マネージド証明書のページをご覧ください。
自身で管理する Google Cloud SSL 証明書。SSL 証明書は、Google Cloud プロジェクトにアップロードする事前共有証明書を使用します。
Kubernetes Secret。Secret には、自身で作成した証明書と鍵が保持されます。Secret の名前を Ingress マニフェストの
tls
フィールドに追加します。
同じ Ingress で複数の方法を使用できるため、1 つの方法で問題が発生してもダウンタイムなしに別の方法に移動できます。
全体像
このドキュメントの手順の概要は次のとおりです。
Deployment を作成します。
Service を作成します。
2 つの証明書ファイルと 2 つの鍵ファイルを作成するか、2 つの
ManagedCertificate
オブジェクトを作成します。これらの証明書は、ロードバランサがデプロイされているプロジェクトと同じ Namespace で構成する必要があります。Secret または事前共有証明書のいずれかを使用する Ingress を作成します。Ingress を作成すると、GKE によってアプリケーション ロードバランサが作成されて構成されます。
アプリケーション ロードバランサをテストします。
始める前に
始める前に、次の作業が完了していることを確認してください。
- Google Kubernetes Engine API を有効にする。 Google Kubernetes Engine API の有効化
- このタスクに Google Cloud CLI を使用する場合は、gcloud CLI をインストールして初期化する。すでに gcloud CLI をインストールしている場合は、
gcloud components update
を実行して最新のバージョンを取得する。
- 2 つのドメイン名を所有している必要があります。ドメイン名は 63 文字以下にする必要があります。
制限事項
- Google マネージド証明書は、外部アプリケーション ロードバランサを使用する GKE Ingress でのみサポートされています。Google マネージド証明書は、サードパーティの Ingress コントローラをサポートしていません。
- 内部アプリケーション ロードバランサの場合は、Ingress マニフェストで HTTP を無効にする必要があります。外部ロードバランサの場合、この操作は必要ありません。
- アプリケーション ロードバランサの構成を手動で変更または更新しないでください。ターゲット プロキシ、URL マップ、バックエンド サービスなど、ロードバランサのいずれのコンポーネントも編集しないでください。変更を加えたとしても 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"
このマニフェストでは、3 つの Pod を持つ Deployment を記述しています。各 Pod には 2 つのコンテナがあります。一方のコンテナでは
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 の 1 つにリクエストを転送するように指定します。クライアントがmy-second-port
の Service にリクエストを送信すると、GKE はいずれかのメンバー Pod のポート 50002 にリクエストを転送します。
マニフェストをクラスタに適用します。
kubectl apply -f my-mc-service.yaml
証明書と鍵を作成する
このページの演習を行うには、それぞれ対応する鍵とともに 2 つの証明書が必要になります。各証明書には、所有しているドメイン名と同じ共通名(CN)を設定する必要があります。
これらの証明書を手動で作成することも、Google マネージド証明書を使用することもできます。共通名として適切な値が設定された 2 つの証明書ファイルがすでに存在する場合は、次のセクションまでスキップできます。
ユーザー マネージド証明書
1 番目の鍵を作成します。
openssl genrsa -out test-ingress-1.key 2048
1 番目の証明書署名リクエストを作成します。
openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \ -subj "/CN=FIRST_DOMAIN"
FIRST_DOMAIN
は、所有しているドメイン名(example.com
など)に置き換えます。1 番目の証明書を作成します。
openssl x509 -req -days 365 -in test-ingress-1.csr -signkey test-ingress-1.key \ -out test-ingress-1.crt
2 番目の鍵を作成します。
openssl genrsa -out test-ingress-2.key 2048
2 番目の証明書署名リクエストを作成します。
openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \ -subj "/CN=SECOND_DOMAIN"
SECOND_DOMAIN
は、所有している別のドメイン名(examplepetstore.com
など)に置き換えます。2 番目の証明書を作成します。
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
: 2 番目の証明書ファイルのパス。SECOND_KEY_FILE
: 2 番目の証明書に対応する鍵ファイルのパス。SECOND_DOMAIN
: 所有している 2 番目のドメイン名。SECOND_SECRET_NAME
: 2 番目の証明書と鍵が含まれている Secret の名前。
Google マネージド証明書
Google マネージド証明書を作成するには、Ingress の Namespace に ManagedCertificate
オブジェクトを追加する必要があります。ドメインの証明書を定義するには、次のテンプレートを使用します。
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
: 1 つ目のManagedCertificate
オブジェクトの名前。FIRST_DOMAIN
: 所有する 1 つ目のドメイン。SECOND_CERT_NAME
: 2 番目のManagedCertificate
オブジェクトの名前。SECOND_DOMAIN
: 所有する 2 番目のドメイン
ManagedCertificate
オブジェクトの名前は、実際に作成する証明書の名前とは異なります。Ingress で使用する ManagedCertificate
オブジェクトの名前を確認しておいてください。
Ingress の証明書を指定する
次のステップは Ingress オブジェクトの作成です。Ingress マニフェストで、次のいずれかの方法を使用してロードバランサの証明書を提供します。
- Secret
- 事前共有証明書
- Google マネージド証明書
Secret
1 つ目の証明書と鍵を保持する Secret を作成します。
kubectl create secret tls FIRST_SECRET_NAME \ --cert=FIRST_CERT_FILE \ --key=FIRST_KEY_FILE
2 番目の証明書と鍵を保持する 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
出力には、2 つの 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
: 最初の鍵ファイル。
2 番目の証明書を作成します。
gcloud compute ssl-certificates create SECOND_CERT_NAME \ --certificate=SECOND_CERT_FILE \ --private-key=SECOND_KEY_FILE
次のように置き換えます。
SECOND_CERT_NAME
: 2 番目の証明書の名前。SECOND_CERT_FILE
: 2 番目の証明書ファイル。SECOND_KEY_FILE
: 2 番目の鍵ファイル。
証明書リソースを表示します。
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 アドレスが設定されていない場合は、数分待ってからコマンドを再度試してください。
ロードバランサをテストする
GKE によるロードバランサの構成が終了するまで 5 分程度待ちます。
Google マネージド証明書を使用した場合、システムで証明書のプロビジョニングとドメインの DNS 構成の検証が必要になるため、構成が完了するまでに時間がかかることがあります。ロードバランサをテストするには、2 つのドメイン名を所有し、両方のドメイン名が外部アプリケーション ロードバランサの外部 IP アドレスに解決される必要があります。
1 つ目のドメイン名を使用してリクエストをロードバランサに送信します。
curl -v https://FIRST_DOMAIN
curl
が自己署名証明書を受け入れるには、curl -k
オプションを使用して、安全でない SSL 転送を実行しなければならない場合があります。出力は次のようになります。
... * 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 handshake で 1 つ目の証明書が使用されたことが示されます。
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 handshake で 2 つ目の証明書が使用されたことが示されます。
Ingress オブジェクトの hosts フィールド
IngressSpec には、IngressTLS オブジェクトの配列である tls
フィールドがあります。各 IngressTLS
オブジェクトには、hosts
フィールドと SecretName
フィールドがあります。
GKE では、hosts
フィールドは使用されません。GKE では Secret 内の証明書から共通名(CN)が読み取られます。共通名がクライアント リクエストのドメイン名と一致すると、ロードバランサによって該当する証明書がクライアントに提示されます。
証明書提示の仕組み
ロードバランサでは、次のルールに従って証明書が選択されます。
Secret と事前共有証明書の両方が Ingress のリストに存在する場合は、事前共有証明書が Secret よりも優先されます。つまり、Secret は含まれますが、事前共有証明書が最初に表示されます。
共通名(CN)がクライアント リクエストのドメイン名と一致する証明書が存在しない場合は、プライマリ証明書が提示されます。
tls
ブロックに複数の Secret がリストされている場合は、プライマリ証明書はリストの 1 つ目の Secret 内にあります。アノテーションに複数の事前共有証明書がリストされている場合は、リスト内の 1 つ目の証明書がプライマリ証明書になります。
証明書のローテーションのベスト プラクティス
Secret または事前共有証明書のコンテンツをローテーションする場合のベスト プラクティスは次のとおりです。
- 新しい証明書データが含まれる別の名前の新しい Secret または事前共有証明書を作成します。上記の手順を使用して、このリソースを(既存のリソースとともに)Ingress に接続します。変更が完了したら、Ingress から古い証明書を削除できます。
- トラフィックの中断を気にしない場合は、Ingress から古いリソースを削除し、同じ名前で内容が異なる新しいリソースをプロビジョニングして、Ingress に再接続します。
トラブルシューティング
無効な Secret または存在しない Secret を指定すると、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'
次のステップ
- GKE ネットワークの概要を読む。
- 静的 IP アドレスでドメイン名を構成する方法を学習する。
- 複数のリージョンに存在する複数の GKE クラスタで実行されるアプリケーションがある場合に、マルチクラスタ Ingress を構成してユーザーに最も近いリージョンのクラスタにトラフィックをルーティングする。