このページでは、Ingress で内部負荷分散と外部負荷分散を構成する際に複数の SSL 証明書を使用する方法を説明します。
概要
クライアントから HTTPS リクエストを受け入れるには、内部または外部の HTTP(S) ロードバランサに証明書を設置し、その正当性をクライアントに証明する必要があります。さらに、ロードバランサには、HTTPS handshake を完了するための秘密鍵も必要です。
ロードバランサがクライアントからの HTTPS リクエストを受け入れると、クライアントとロードバランサ間のトラフィックは TLS で暗号化されます。ただし、TLS 暗号化はロードバランサで終端し、アプリケーションへのリクエストの転送は暗号化なしに行われます。Ingress による HTTP(S) ロードバランサを構成する際には、最大 10 個の TLS 証明書をクライアントに提示するようにロードバランサを構成できます。
ロードバランサでは、TLS handshake のドメイン名に基づいて、Server Name Indication(SNI)を使用してクライアントに提示する証明書が決定されます。クライアントで SNI が使用されていないか、いずれの証明書の共通名(CN)とも一致しないドメイン名が使用されている場合は、Ingress で最初に表示される証明書が使用されます。次の図は、リクエストで使用されているドメイン名に応じて、複数の異なるバックエンドにトラフィックを送信するロードバランサを示しています。
次の 3 つの方法のいずれかを使用して、HTTPS ロードバランサに SSL 証明書を提供できます。
Google マネージド SSL 証明書。使用方法については、マネージド証明書のページをご覧ください。
ご自身で管理している Google Cloud SSL 証明書。以前に Google Cloud プロジェクトにアップロードされた事前共有証明書を使用します。
Kubernetes Secrets。Secret では、ご自身で作成した証明書と鍵が保持されます。Secret を使用するには、その名前を Ingress マニフェストの
tls
フィールドに追加します。
同じ Ingress で複数の方法を使用できるため、1 つの方法で問題が発生してもダウンタイムなしに別の方法に移動できます。
最小 GKE バージョン
Ingress で事前共有証明書を使用する、または複数の証明書を指定するには、GKE バージョン 1.10.2 以降が必要です。
前提条件
このページの演習を行うには、2 つのドメイン名を所有している必要があります。Google Domains または他の登録事業者を使用できます。
全体像
このトピックのステップの概要は次のとおりです。
Deployment を作成します。
Service を作成します。
2 つの証明書ファイルと 2 つの鍵ファイルを作成するか、2 つの
ManagedCertificate
オブジェクトを作成します。これらの証明書は、ロードバランサがデプロイされているプロジェクトと名前空間で構成します。Secret または事前共有証明書のいずれかを使用する Ingress を作成します。Ingress を作成すると、GKE により HTTP(S) ロードバランサが作成されて構成されます。
HTTP(S) ロードバランサをテストします。
始める前に
作業を始める前に、次のことを確認してください。
- Google Kubernetes Engine API が有効になっていることを確認します。 Google Kubernetes Engine API の有効化
- Google Cloud CLI がインストールされていることを確認します。
- 次のいずれかの方法で、プロジェクトにデフォルトの Google Cloud CLI 設定をセットアップします。
- プロジェクトのデフォルトの設定全般を確認する場合は、
gcloud init
を使用します。 gcloud config
を使用して、プロジェクト ID、ゾーン、リージョンを個別に設定します。-
gcloud init
を実行して、次の操作を行います。gcloud init
リモート サーバーで SSH を使用している場合は、
--console-only
フラグを指定して、コマンドがブラウザを起動しないようにします。gcloud init --console-only
- Google Cloud アカウントを使用できるように、gcloud CLI の承認手順を行います。
- 新しい構成を作成するか、既存の構成を選択します。
- Google Cloud プロジェクトを選択します。
- デフォルトの Compute Engine ゾーンを選択します。
- デフォルトの Compute Engine リージョンを選択します。
- デフォルトのプロジェクト ID を設定します。
gcloud config set project PROJECT_ID
- デフォルトの Compute Engine リージョン(例:
us-central1
)を設定します。gcloud config set compute/region COMPUTE_REGION
- デフォルトの Compute Engine ゾーン(例:
us-central1-c
)を設定します。gcloud config set compute/zone COMPUTE_ZONE
gcloud
を最新バージョンに更新します。gcloud components update
gcloud init
gcloud config
デフォルトの場所を設定することで、gcloud CLI のエラー(One of [--zone, --region] must be supplied: Please specify location
など)を防止できます。
Deployment を作成する
Deployment のマニフェストは次のとおりです。
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"
この Deployment は 3 つの Pod で構成され、各 Pod は 2 つのコンテナで構成されています。一方のコンテナでは hello-app:1.0
を実行し、TCP ポート 50001 をリッスンしています。もう一方のコンテナでは hello-app:2.0
を実行し、TCP ポート 50002 をリッスンしています。
このマニフェストを my-mc-deployment.yaml
という名前のファイルにコピーし、次のようにして Deployment を作成します。
kubectl apply -f my-mc-deployment.yaml
Service を作成する
Service のマニフェストは次のとおりです。
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 のメンバーであることを示しています。したがって、前のステップで作成した Deployment の Pod は Service のメンバーです。
Service マニフェストの ports
フィールドは ServicePort オブジェクトの配列です。クライアントのリクエストが my-first-port
で Service に送信された場合、そのリクエストはポート 50001 でメンバー Pod のいずれかに転送されます。クライアントのリクエストが my-second-port
で Service に送信された場合、そのリクエストはポート 50002 でメンバー Pod のいずれかに転送されます。
マニフェストを my-mc-service.yaml
という名前のファイルにコピーして、Service を作成します。
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
ドメインからのリクエストを処理するとします。証明書署名リクエストは次のようになります。openssl req -new -key test-ingress-1.key -out test-ingress-1.csr \ -subj "/CN=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
ドメインからのリクエストを処理するとします。証明書署名リクエストは次のようになります。openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \ -subj "/CN=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 番目のドメイン
Ingress 用の証明書を指定する
次のステップは Ingress オブジェクトの作成です。Ingress マニフェストで、次のいずれかの方法を使用してロードバランサの証明書を提供します。
- Secret
- 事前共有証明書
- Google マネージド証明書
使用する方法のタブを選択してください。
Secret
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 を作成する
Ingress のマニフェストを次に示します。
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
マニフェストを
my-mc-ingress.yaml
という名前のファイルにコピーします。FIRST_DOMAIN
とSECOND_DOMAIN
は、example.com
やexamplepetstore.com
などの所有しているドメイン名に置き換えてください。Ingress を作成します。
kubectl apply -f my-mc-ingress.yaml
Ingress を作成すると、GKE Ingress コントローラにより HTTP(S) ロードバランサが作成されます。GKE によってロードバランサに外部 IP アドレスが割り当てられるまで待ちます。
Ingress の説明を表示します。
kubectl describe ingress my-mc-ingress
出力には、2 つの Secret が Ingress に関連付けられていることが示されます。また、出力には、ロードバランサの外部 IP アドレスも示されます。
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
事前共有証明書
事前共有証明書の作成
Google Cloud プロジェクトで証明書リソースを作成します。
gcloud compute ssl-certificates create FIRST_CERT_NAME \ --certificate FIRST_CERT_FILE --private-key FIRST_KEY_FILE
次のように置き換えます。
FIRST_CERT_NAME
: 最初の証明書の名前。FIRST_CERT_FILE
: 1 つ目の証明書ファイルです。FIRST_KEY_FILE
: 最初の鍵ファイル。
Google Cloud プロジェクトで 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
出力には
FIRST_CERT_NAME
とSECOND_CERT_NAME
という名前の証明書リソースがあることが示されます。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
GKE Ingress の作成
事前共有証明書リソースをアノテーションにリストした Ingress のマニフェストは次のとおりです。
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
マニフェストを
my-psc-ingress.yaml
という名前のファイルにコピーします。FIRST_DOMAIN
とSECOND_DOMAIN
をドメイン名に置き換えます。Ingress を作成します。
kubectl apply -f my-psc-ingress.yaml
GKE によってロードバランサに外部 IP アドレスが割り振られるまで待ちます。
Ingress の説明を表示します。
kubectl describe ingress my-psc-ingress
出力には、Ingress が
FIRST_CERT_NAME
とSECOND_CERT_NAME
という名前の事前共有証明書に関連付けられていることが示されています。また、ロードバランサの外部 IP アドレスも示されます。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
Google マネージド証明書
GKE Ingress の作成
事前共有証明書リソースをアノテーションにリストした Ingress のマニフェストは次のとおりです。
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
マニフェストを
my-gmc-ingress.yaml
という名前のファイルにコピーします。FIRST_DOMAIN
とSECOND_DOMAIN
をドメイン名に置き換えます。Ingress を作成します。
kubectl apply -f my-gmc-ingress.yaml
GKE によってロードバランサに外部 IP アドレスが割り振られるまで待ちます。
Ingress の説明を表示します。
kubectl describe ingress my-gmc-ingress
出力には、Ingress が
FIRST_CERT_NAME
とSECOND_CERT_NAME
という名前のマネージド証明書に関連付けられていることが示されています。GKE は、ManagedCertificate
オブジェクトで作成した Google マネージド証明書を参照するようにingress.gcp.kubernetes.io/pre-shared-cert
アノテーションとingress.kubernetes.io/ssl-cert
アノテーションを自動的に埋め込みます。出力には、外部ロードバランサの IP アドレスも含まれています。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
ロードバランサをテストする
GKE によるロードバランサの構成が終了するまで 5 分程度待ちます。Google マネージド証明書の場合、システムで証明書のプロビジョニングとドメインの DNS 構成の検証が必要になるため、構成が完了するまでに時間がかかる場合があります。
このステップを行うには、2 つのドメイン名を所有していて、かつ両方のドメイン名によって HTTP(S) ロードバランサの外部 IP アドレスが解決される必要があります。
1 つ目のドメイン名を使用してリクエストをロードバランサに送信します。
curl -v https://FIRST_DOMAIN
出力には、TLS handshake で 1 つ目の証明書が使用されたことが示されます。1 番目のドメインが example.com
で、出力は次のようになります。
...
* Trying 203.0.113.1...
...
* Connected to example.com (203.0.113.1) port 443 (#0)
...
* TLSv1.2 (IN), TLS handshake, Certificate (11):
...
* Server certificate:
* subject: CN=example.com
...
> Host: example.com
...
Hello, world!
Version: 1.0.0
...
2 番目のドメイン名を使用してリクエストをロードバランサに送信します。
curl -v https://SECOND_DOMAIN
出力には、TLS handshake で 2 つ目の証明書が使用されたことが示されます。2 番目のドメインが examplepetstore.com
で、出力は次のようになります。
...
* Trying 203.0.113.1...
...
* Connected to examplepetstore.com (203.0.113.1) port 443 (#0)
...
* Server certificate:
* subject: CN=examplepetstore.com
...
> Host: examplepetstore.com
...
Hello, world!
Version: 2.0.0
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 に再接続します。
証明書のローテーションを自分で管理しないようにするには、Google マネージド SSL 機能をご覧ください。
トラブルシューティング
無効な 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 ネットワークの概要を確認する。
Ingress を使用した HTTP(S) 負荷分散を構成する方法についてのチュートリアルを行う。
静的 IP アドレスとドメイン名を構成する方法を学習する。
複数のリージョンに存在する複数の GKE クラスタで実行されるアプリケーションがある場合は、複数クラスタ Ingress を構成してユーザーに最も近いリージョンのクラスタにトラフィックをルーティングする。