Ingress での HTTP(S) 負荷分散のための複数の SSL 証明書の使用

概要

HTTP(S) ロードバランサでは、クライアントの SSL / TLS 接続を終了してから、ポッド間でリクエストの負荷分散が行われます。Ingress による HTTP(S) ロードバランサを構成する際には、最大 10 個の TLS 証明書をクライアントに提示するようにロードバランサを構成できます。

ロードバランサでは、TLS handshake のドメイン名に基づいて、Server Name Indication(SNI)を使用してクライアントに提示する証明書が決定されます。クライアントで SNI が使用されていないか、いずれの証明書の共通名(CN)とも一致しないドメイン名が使用されている場合は、Ingress で最初に表示される証明書が使用されます。次の図は、リクエストで使用されているドメイン名に応じて、複数の異なるバックエンドにトラフィックを送信するロードバランサを示しています。

Ingress システムでの複数の SSL 証明書の使用を示す図

Ingress 用の証明書を指定するには、次のいずれかの手段を使用できます。

  • Kubernetes Secrets

  • 以前に Google Cloud Platform プロジェクトにアップロードした事前共有証明書。

  • Google マネージド SSL 証明書。マネージド証明書は、ワイルドカードを使用していない単一のドメインをサポートします。使用方法については、マネージド証明書のページをご覧ください。

同じ Ingress で複数のメソッドを使用できます。これにより、メソッド間でのダウンタイムのない移行が可能になります。

最小 GKE バージョン

Ingress で事前共有証明書を使用するか、複数の証明書を指定する場合は、GKE バージョン 1.10.2 以降が必要です。

全体像

このトピックのステップの概要は次のとおりです。

  1. Deployment を作成します。

  2. Service を作成します。

  3. 2 つの証明書ファイルと 2 つの鍵ファイルを作成します。

  4. Secret または事前共有証明書のいずれかを使用する Ingress を作成します。Ingress を作成すると、GKE により HTTP(S) ロードバランサが作成されて構成されます。

  5. HTTP(S) ロードバランサをテストします。

始める前に

このタスクの準備として、次の手順を行います。

  • Google Kubernetes Engine API が有効になっていることを確認します。
  • Google Kubernetes Engine API の有効化
  • Cloud SDK がインストール済みであることを確認します。
  • デフォルトのプロジェクト ID を設定します。
    gcloud config set project [PROJECT_ID]
  • ゾーンクラスタを使用する場合は、デフォルトのコンピューティング ゾーンを設定します。
    gcloud config set compute/zone [COMPUTE_ZONE]
  • リージョン クラスタを使用する場合は、デフォルトのコンピューティング リージョンを設定します。
    gcloud config set compute/region [COMPUTE_REGION]
  • gcloud を最新バージョンに更新します。
    gcloud components update

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: "gcr.io/google-samples/hello-app:2.0"
        env:
        - name: "PORT"
          value: "50001"
      - name: hello-again
        image: "gcr.io/google-samples/node-hello:1.0"
        env:
        - name: "PORT"
          value: "50002"

この Deployment は 3 つのポッドで構成され、各ポッドは 2 つのコンテナで構成されています。一方のコンテナでは hello-app を実行し、TCP ポート 50001 をリッスンしています。もう一方のコンテナでは node-hello を実行し、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 ラベルを持つポッドがこの Service のメンバーであることを指定しています。したがって、前のステップで作成した Deployment のポッドは Service のメンバーです。

Service マニフェストの ports フィールドは ServicePort オブジェクトの配列です。クライアントのリクエストが my-first-port で Service に送信された場合、そのリクエストはポート 50001 でメンバーポッドのいずれかに転送されます。クライアントのリクエストが my-second-port で Service に送信された場合、そのリクエストはポート 50002 でメンバーポッドのいずれかに転送されます。

マニフェストを my-mc-service.yaml という名前のファイルにコピーして、Service を作成します。

kubectl apply -f my-mc-service.yaml

証明書と鍵を作成する

このページの演習を行うには、それぞれ対応する鍵とともに 2 つの証明書が必要になります。各証明書には、所有しているドメイン名と同じ共通名(CN)を設定する必要があります。共通名として適切な値が設定された 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_NAME]"

ここで、[FIRST_DOMAIN_NAME] は所有しているドメイン名または架空ドメイン名です。

たとえば、ロードバランサに your-store.example からのリクエストを処理させるものとします。この場合、証明書署名リクエストは次のようになります。

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

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_NAME]"

ここで、[SECOND_DOMAIN] は、所有しているもう 1 つのドメイン名または架空ドメイン名です。

たとえば、ロードバランサに your-experimental-store.example からのリクエストを処理させるものとします。この場合、証明書署名リクエストは次のようになります。

openssl req -new -key test-ingress-2.key -out test-ingress-2.csr \
    -subj "/CN=your-experimental-store.example"

2 つ目の証明書を作成します。

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

独自の証明書と鍵の作成方法の詳細については、秘密鍵と署名済み証明書を取得するをご覧ください。

これで、2 つの証明書ファイルと 2 つの鍵ファイルの作成が終了しました。このタスクの残りのステップでは、次のプレースホルダを使用して、ドメイン、証明書ファイル、鍵ファイルを参照します。

  • [FIRST_CERT_FILE] は、1 つ目の証明書ファイルのパスです。

  • [FIRST_KEY_FILE] は、1 つ目の証明書に対応する鍵ファイルのパスです。

  • [SECOND_CERT_FILE] は、2 つ目の証明書ファイルのパスです。

  • [SECOND_KEY_FILE] は、2 つ目の証明書に対応する鍵ファイルのパスです。

  • [FIRST_DOMAIN] は、所有しているドメイン名または架空ドメイン名です。

  • [SECOND_DOMAIN] は、所有している 2 つ目のドメイン名または 2 つ目の架空ドメイン名です。

Ingress 用の証明書を指定する

次のステップは Ingress オブジェクトの作成です。Ingress マニフェストでは、次の 2 つの手段のいずれかを使用してロードバランサに証明書を提供できます。

  • Secret
  • 事前共有証明書

[SECRETS] タブまたは [PRE-SHARED CERTS] タブを選択して、2 つの手段のいずれかを選択します。

Secret

Secret を作成する

1 つ目の証明書と鍵を保持する Secret を作成します。

kubectl create secret tls my-first-secret \
  --cert [FIRST_CERT_FILE] --key [FIRST_KEY_FILE]

2 つ目の証明書と鍵を保持する Secret を作成します。

kubectl create secret tls my-second-secret \
  --cert [SECOND_CERT_FILE] --key [SECOND_KEY_FILE]

Ingress を作成する

Ingress のマニフェストは次のとおりです。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-mc-ingress
spec:
  tls:
  - secretName: my-first-secret
  - secretName: my-second-secret
  rules:
  - host: [FIRST_DOMAIN]
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-first-port
  - host: [SECOND_DOMAIN]
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-second-port

マニフェストを my-mc-ingress.yaml という名前のファイルにコピーします。[FIRST_DOMAIN][SECOND_DOMAIN] は、所有しているドメイン名または架空ドメイン名に置き換えてください。

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:
  my-first-secret terminates
  my-second-secret terminates
Rules:
  Host              Path  Backends
  ----              ----  --------
  your-store.example
                     my-mc-service:my-first-port (<none>)
  your-experimental-store.example
                     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 Platform プロジェクトで証明書リソースを作成します。

gcloud compute ssl-certificates create test-ingress-1 \
--certificate [FIRST_CERT_FILE] --private-key [FIRST_KEY_FILE]

ここで

Google Cloud Platform プロジェクトで 2 つ目の証明書リソースを作成します。

gcloud compute ssl-certificates create test-ingress-2 \
--certificate [SECOND_CERT_FILE] --private-key [SECOND_KEY_FILE]

ここで

  • [SECOND_CERT_FILE] は、2 つ目の証明書ファイルです。

  • [SECOND_KEY_FILE] は、2 つ目の鍵ファイルです。

証明書リソースを表示します。

gcloud compute ssl-certificates list

出力には、test-ingres-1 および test-ingress-2 という名前の証明書リソースが存在することが示されます。

NAME                CREATION_TIMESTAMP
test-ingress-1      2018-11-03T12:08:47.751-07:00
test-ingress-2      2018-11-03T12:09:25.359-07:00

事前共有証明書リソースをアノテーションにリストした Ingress のマニフェストは次のとおりです。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-psc-ingress
  annotations:
    ingress.gcp.kubernetes.io/pre-shared-cert: "test-ingress-1,test-ingress-2"
spec:
  rules:
  - host: [FIRST_DOMAIN]
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-first-port
  - host: [SECOND_DOMAIN]
    http:
      paths:
      - backend:
          serviceName: my-mc-service
          servicePort: my-second-port

マニフェストを 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 が test-ingress-1 および test-ingress-2 という名前の事前共有証明書に関連付けられていることが示されます。また、出力には、ロードバランサの外部 IP アドレスも示されます。

Name:             my-psc-ingress
Address:          203.0.113.2
...
Rules:
  Host              Path  Backends
  ----              ----  --------
  your-store.example
                     my-mc-service:my-first-port (<none>)
  your-experimental-store.example
                     my-mc-service:my-second-port (<none>)
Annotations:
  ...
  ingress.gcp.kubernetes.io/pre-shared-cert:    test-ingress-1,test-ingress-2
  ...
  ingress.kubernetes.io/ssl-cert:               test-ingress-1,test-ingress-2
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

ロードバランサをテストする

GKE によるロードバランサの構成が終了するまで 5 分程度待ちます。

このステップを行うには、2 つのドメイン名を所有していて、かつ両方のドメイン名によって HTTP(S) ロードバランサの外部 IP アドレスが解決される必要があります。

1 つ目のドメイン名を使用してリクエストをロードバランサに送信します。

curl -v https://[FIRST_DOMAIN]

出力には、TLS handshake で 1 つ目の証明書が使用されたことが示されます。1 つ目のドメインが your-store.example である場合、出力は次のようになります。

...
*   Trying 203.0.113.1...
...
* Connected to your-store.example (203.0.113.1) port 443 (#0)
...
* TLSv1.2 (IN), TLS handshake, Certificate (11):
...
* Server certificate:
*  subject: CN=your-store.example
...
> Host: your-store.example
...
<
Hello, world!
Version: 2.0.0
...

2 つ目のドメイン名を使用してリクエストをロードバランサに送信します。

curl -v https://[SECOND_DOMAIN]

出力には、TLS handshake で 2 つ目の証明書が使用されたことが示されます。2 つ目のドメインが your-experimental-store.example である場合、出力は次のようになります。

...
*   Trying 203.0.113.1...
...
* Connected to your-experimental-store.example (203.0.113.1) port 443 (#0)
...
* Server certificate:
*  subject: CN=your-experimental-store.example
...
> Host: your-experimental-store.example
...
Hello Kubernetes!

Ingress オブジェクトの hosts フィールド

IngressSpec には、IngressTLS オブジェクトの配列である tls フィールドがあります。各 IngressTLS オブジェクトには、hosts フィールドと SecretName フィールドがあります。GKE では、hosts フィールドは使用されません。GKE では Secret 内の証明書から共通名(CN)が読み取られます。共通名がクライアント リクエストのドメイン名と一致すると、ロードバランサによって該当する証明書がクライアントに提示されます。

証明書提示の仕組み

ロードバランサでは、次のルールに従って証明書が選択されます。

  • Secret と事前共有証明書の両方が Ingress のリストに存在する場合は、Secret を無視して事前共有証明書のリストが使用されます。

  • 共通名(CN)がクライアント リクエストのドメイン名と一致する証明書が存在しない場合は、プライマリ証明書が提示されます。

  • tls ブロックに複数の Secret がリストされている場合は、リスト内の 1 つ目の Secret 内の証明書がプライマリ証明書になります。

  • アノテーションに複数の事前共有証明書がリストされている場合は、リスト内の 1 つ目の証明書がプライマリ証明書になります。

トラブルシューティング

無効な 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'

次のステップ

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Kubernetes Engine のドキュメント