本ページでは、Istio にカスタム Ingress ゲートウェイをデプロイする方法と、cert-manager を使用して証明書を管理する方法について説明します。

Istio は、複数のカスタム Ingress ゲートウェイをサポートし、異なるポートを介してメッシュのエッジで着信接続を処理し、異なるロードバランサーを使用して異なるトラフィックを分離します。 cert-manager は、Kubernetes Secret リソースに保存されている署名キーペアを使用して証明書を取得するために使用できます。

前提条件

  • Istio が Kubernetes クラスターにデプロイされている。

    Kubernetes 用 Alibaba Cloud Container Service で Istio の迅速なデプロイがサポートされている。 Container Service コンソールを使用して「Kubernetes クラスターの作成」と「Istio を Kubernetes クラスターにデプロイする」ができる。

  • CloudShell」を使用して Kubernetes クラスターに接続できる。
  • クラスターの管理を容易にするために、secretName にゲートウェイ名を含めることを推奨します。 具体的には、secretName は、istio-{Gateway Name}-certs または istio-{Gateway Name}-ca-cert とする必要があります。

証明書マネージャーの有効化

  1. デフォルトの Istio 構成では、cert-manager は証明書管理に対して有効になっていません。 Istio のインストール後に、cert-manager を有効にできます。
  2. Container Service コンソール にログインします。
  3. 左側のナビゲーションウィンドウで、 [サービスメッシュ] > [Istio の管理] の順にクリックして管理ページに移動します。
  4. 右上の [更新] をクリックします。 表示されたダイアログボックスで、パラメーターを変更します。 certManager.enabled の値を true に設定し、[更新] をクリックします。
    パラメーター ”certManager” が見つからない場合は、YAML 形式の最初の行として追加します。
    yaml

署名キーペアの生成

CA 発行元は、署名キーペアを自動的に作成、管理しません。 次のいずれかの方法を使用して、署名キーペアを取得します。
  • すでに独自のキーペアがある場合は、プライベートキーと証明書に、それぞれ ca.keyca.crt という名前を付ける必要があります。
  • 署名キーペアがない場合は、次の手順で、x509 タイプのキーと証明書を生成します。
    1. 次のコマンドを実行して CA キーを生成します。
      $ docker run -it -v $(pwd):/export frapsoft/openssl genrsa -out /export/ca.key 2048
    2. 次のコマンドを実行して自己署名付き証明書を生成します。
      $ docker run -it -v $(pwd):/export frapsoft/openssl req -x509 -new -nodes -key /export/ca.key -subj "/CN=myexample.com" -days 3650 -reqexts v3_req -extensions v3_ca -out /export/ca.crt

署名キーペアをシークレットとして保存

次のコマンドを実行して、署名キーペアを含むシークレットをデフォルトの名前空間に作成します。
kubectl create namespace myexample
kubectl create secret tls istio-myexample-customingressgateway-ca-certs \
   --cert=ca.crt \
   --key=ca.key \
   --namespace=myexample
  • このキーペアを使用して署名付き証明書を生成する発行者を作成します。 発行者がキーペアを参照できるようにするには、キーペアを Kubernetes シークレットリソースに保存します。
  • 発行者が名前空間にスコープされ、自分の名前空間内でのみシークレットを参照できます。 したがって、キーペアが発行者と同じ名前空間にあることを確認します。

シークレットを参照する発行者の作成

次の内容に従って、 certmanager-secret-issuer.yaml ファイルを作成します。 次に、 kubectl apply -n myexample -f certmanager-secret-issuer.yaml コマンドを実行して、署名付き証明書を生成するためのキーペアを使用する発行者を作成します。
apiVersion: certmanager.k8s.io/v1alpha1
kind: Issuer
metadata:
  name: ca-issuer
  namespace: myexample
spec:
  ca:
    secretName: istio-myexample-customingressgateway-ca-certs

署名付き証明書の取得

これで、必要な証明書を指定する次の証明書リソースが作成されます。 発行者を使用して証明書を取得するには、発行者と同じ名前空間に証明書リソースを作成する必要があります。 この例に示すように、発行者は名前空間に分けられます。 複数の名前空間で署名キーペアを再利用する場合は、ClusterIssuer を使用します。 まず、次のコマンドを実行してドメイン名 myexample.com の証明書を作成します 。

次の内容に従って、 myexample-certificate.yaml ファイルを作成します。 次に、 kubectl apply -n myexample -f myexample-certificate.yaml コマンドを実行して署名付き証明書を作成します。
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: myexample-certificate
  namespace: myexample
spec:
  secretName: istio-myexample-customingressgateway-certs
  issuerRef:
    name: ca-issuer
    # ClusterIssuer タイプの発行者を参照できます。 デフォルトでは、名前空間の発行者のみ使用します。
    kind: Issuer
  commonName: myexample.com
  organization:
  - MyExample CA
  dnsNames:
  - myexample.com
  - www.myexample.com

証明書リソースが作成されたら、cert-manager で、ca-issuer 発行者を使用して証明書の取得が試みられます。 試行が成功すると、証明書リソースと同じ名前空間 (myexample) にある istio-myexample-customingressgateway-certs という名前のシークレットリソース内に証明書が保存されます。

証明書とキーの確認

commonName フィールドは既に指定されているため、myexample.com は、証明書の共通名になります。 dnsNames リストの共通名とすべての要素は、サブジェクトの別名 (SAN) になります。 共通名が指定されていない場合、dnsNames リストの最初の要素が共通名として使用され、dnsNames リストにある要素もすべて SAN になります。

証明書が作成されたら、次のコマンドを実行して証明書が取得されているかどうかを確認します。

次のように myexample-certificate 証明書を確認します。

kubectl describe certificate myexample-certificate
Name:         myexample-certificate
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"certmanager.k8s.io/v1alpha1","kind":"Certificate","metadata":{"annotations":{},"name":"myexample-certificate","namespace":"...
API Version:  certmanager.k8s.io/v1alpha1
Kind:         Certificate
....
Spec:
  Common Name:  myexample.com
  Dns Names:
    myexample.com
    www.myexample.com
  Issuer Ref:
    Kind:  Issuer
    Name:  ca-issuer
  Organization:
    MyExample CA
  Secret Name:  istio-myexample-customingressgateway-certs
Status:
  Conditions:
    ....
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
....
Events:
  Type    Reason      Age   From          Message
  ----    ------      ----  ----          -------
  Normal  CertIssued  78s   cert-manager  Certificate issued successfully
			

最後の行に Certificate issued successfully と表示されれば、証明書は正常に作成されています。

kubectl get secret istio-myexample-customingressgateway-certs -o yaml コマンドを実行して、発行が成功したかどうかを確認することもできます。 成功すると、base64 でエンコードされた署名付き TLS キーペアが表示されます。

証明書が取得されたら、cert-manager でその有効性が常に確認され、有効期限が切れそうになると証明書の更新が試みられます。 たとえば、証明書の Not After フィールドが現在時刻 + 30 日 未満の場合、cert-manager では、証明書の有効期限が近づいていると見なされます。 CA ベースの発行者の場合、cert-manager で証明書が発行され、Not After フィールドに現在時刻 + 365 日と設定されます 。

カスタムゲートウェイのデプロイ

ゲートウェイは、着信または発信用 HTTP/TCP 接続を受信するメッシュのエッジで動作するロードバランサーを表します。 Kubernetes 用 Alibaba Cloud Container Service では、CRD を介したカスタムゲートウェイの管理がサポートされます。 次の YAML コンテンツで、カスタム Ingress ゲートウェイを定義します。

apiVersion: istio.alibabacloud.com/v1beta1
kind: IstioGateway
metadata:  
  labels:    
    controller-tools.k8s.io: "1.0"  
  name: "myexample-customingressgateway"  
spec:  
  maxReplicas: 5  
  minReplicas: 1  
  ports:  
  - name: status-port    
    port: 15020    
    targetPort: 15020  
  - name: http2    
    port: 80    
    targetPort: 80  
  - name: https    
    port: 443    
    targetPort: 0  
  - name: tls    
    port: 15443    
    targetPort: 15443  
  replicaCount: 1  
  serviceType: LoadBalancer  
  serviceAnnotations:    
    service.beta.kubernetes.io/alicloud-loadbalancer-address-type: internet  
secretName にゲートウェイ名が含まれていることを確認します。 具体的には、secretName は、istio-{Gateway Name}-certs または istio-{Gateway Name}-ca-certs とする必要があります。

次の内容に従って、myexample-customingressgateway.yaml ファイルを作成します。 次に、kubectl apply -n myexample -f myexample-customingressgateway.yaml コマンドを実行して、署名付き証明書を作成します。

内部サービスの定義

この例での内部サービスは、NGINX に基づいて実装されています。 まず、NGINX サーバー用構成ファイルを作成します。 ドメイン myexample.com の内部サービスを例にとります。 "Welcome to myexample.com! This is one custom Istio Ingress Gateway powered by cert-manager!" とステータスコード 200 を直接返すように、要求されたルートパスを定義します。

myexample-nginx.conf の具体的な内容は次のとおりです。
events {
}

http {
  log_format main '$remote_addr - $remote_user [$time_local]  $status '
  '"$request" $body_bytes_sent "$http_referer" '
  '"$http_user_agent" "$http_x_forwarded_for"';
  access_log /var/log/nginx/access.log main;
  error_log  /var/log/nginx/error.log;

  server {
    listen 80;

    location / {
        return 200 'Welcome to myexample.com! This is one custom Istio Ingress Gateway powered by cert-manager!' ;
        add_header Content-Type text/plain;
    }
  }
}
  1. 次のコマンドを実行して Kubernetes ConfigMap を作成し、Nginx サーバーの構成を保存します。
    kubectl create configmap myexample-nginx-configmap --from-file=nginx.conf=./myexample-nginx.conf
  2. 次のコマンドを実行して、名前空間を default に設定し、自動サイドカー注入を開始します。
    kubectl label namespace default istio-injection=enabled
    自動サイドカー注入のラベル付けは、IngressGateway が作成された後にのみ行うようにします。 これにより、ラベルが IngressGateway に自動的に注入されなくなります。 自動注入を開始せず、手動注入を使用してプロセスを完了することもできます。 詳細は、「 Manual injection」をご参照ください。
  3. 次の内容に従って、myexampleapp.yaml ファイルを作成します。 次に、kubectl apply -f myexampleapp.yaml コマンドを実行して、ドメイン名 myexample.com の内部サービスを作成します。
    apiVersion: v1
    kind: Service
    metadata:
      name: myexampleapp
      labels:
        app: myexampleapp
    spec:
      ports:
      - port: 80
        protocol: TCP
      selector:
        app: myexampleapp
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: myexampleapp
    spec:
      selector:
        matchLabels:
          app: myexampleapp
      replicas: 1
      template:
        metadata:
          labels:
            app: myexampleapp
        spec:
          containers:
          - name: nginx
            image: nginx
            ports:
            - containerPort: 80
            volumeMounts:
            - name: nginx-log
              mountPath: /etc/config
              readOnly: true
          volumes:
          - name: nginx-log
            configMap:
              name: myexample-nginx-configmap
    
    EOF

カスタムゲートウェイ構成オブジェクトの作成

次の内容に従って、 istio-myexample-customingressgateway.yaml ファイルを作成します。 次に、 kubectl apply -n myexample -f istio-myexample-customingressgateway.yaml コマンドを実行して、Istioカスタムゲートウェイ構成オブジェクトを作成します。
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  annotations:
  name: istio-myexample-customingressgateway
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - '*.myexample.com'
    port:
      name: http
      number: 80
      protocol: HTTP
    tls:
      httpsRedirect: true
  - hosts:
    - '*.myexample.com'
    port:
      name: https
      number: 443
      protocol: HTTPS
    tls:
      mode: SIMPLE
      privateKey: /etc/istio/myexample-customingressgateway-certs/tls.key
      serverCertificate: /etc/istio/myexample-customingressgateway-certs/tls.crt

VirtualService の作成

次の内容に従って、 istio-myexample-customvirtualservice.yaml ファイルを作成します。 次に、 kubectl apply -n myexample -f istio-myexample-customvirtualservice.yaml コマンドを実行して、istio-myexample-customingressgateway に接続する VirtualService を作成します。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: istio-myexample-customvirtualservice
spec:
  hosts:
  - "www.myexample.com"
  gateways:
  - istio-myexample-customingressgateway
  http:
  - route:
    - destination:
        host: myexampleapp.default.svc.cluster.local
        port:
          number: 80
				

ゲートウェイを使用したサービスへのアクセス

  1. ドメイン名 myexample.com を例にとります。 kubectl get svc -n myexample -l istio = myexample-customingressgateway コマンドを実行して、対応するカスタムゲートウェイサービスのパブリックIPアドレスを取得します。
    NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
    istio-myexample-customingressgateway   LoadBalancer   172.19.XX.XX   106.14.XX.XX    ....   11m
    環境変数 INGRESS_HOSTSECURE_INGRESS_PORT を実際の環境のアドレス値に変更します。これらの値は、手順 1 の EXTERNAL-IP 値で見つかります。
    INGRESS_HOST=106.14.XX.XX
    SECURE_INGRESS_PORT=443
  2. 次のコマンドを実行して、istio-ingressgateway Pod で証明書と秘密キーが正しくロードされたかどうかを確認します。
    kubectl exec -it -n myexample $(kubectl -n myexample get pods -l istio=myexample-customingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/myexample-customingressgateway-certs

    tls.crt と tls.key が両方ともこのディレクトリに保存されているはずです。

  3. 次のコマンドを実行して、Ingress ゲートウェイ証明書のサブジェクトフィールドが正しいかどうかを確認します。
    kubectl exec -i -n myexample $(kubectl get pod -l istio=myexample-customingressgateway  -n myexample -o jsonpath='{.items[0].metadata.name}')  -- cat /etc/istio/myexample-customingressgateway-certs/tls.crt | openssl x509 -text -noout | grep 'Subject:'
            Subject: O=MyExample CA, CN=myexample.com
  4. 次のコマンドを実行して、Ingress ゲートウェイプロキシが証明書に正しくアクセスできるかどうかを確認します。
    kubectl exec -ti $(kubectl get po -l istio=myexample-customingressgateway -n myexample -o jsonpath={.items[0]..metadata.name}) -n myexample -- curl  127.0.0.1:15000/certs
    {
        "ca_cert": "",
        "cert_chain": "...."
    }

これで、cert-manager を使用してカスタム Ingress ゲートウェイをデプロイするためのすべての手順が完了しました。

期待される結果

次のコマンドを実行して、HTTPS 経由で myexample.com サービスにアクセスします。
curl -k -H Host:www.myexample.com --resolve www.myexample.com:443:106.14.XX.XX  https://www.myexample.com
Welcome to myexample.com! This is one custom Istio Ingress Gateway powered by cert-manager!