nginx-ingressを複数系統で構築する (with cert-manager/dns-01チャレンジ)


悲しみに終わった前回(nginx-ingress/kube-legoを複数系統で構築する )に対しての本命。

DEPRECATEDなkube-legoに代わって同じ開発元による後継、cert-managerを使う。

TL; DR

cert-manager/DNS-01チャレンジ方式を使うという選択さえしてしまえば、
nginx-ingressを複数系統で構築することにもはや嵌りそうな罠もなにもなかった。

DNS-01最高

NGINX Ingress ControllerだろうがGCE Ingress Controllerだろうが、
複数系統だろうがそうでなかろうが、とにかくHTTP-01に比べてDNS-01最高

(HTTP-01と違って使える環境の制約があるけど)

やったこと

NGINX Ingress Controllerを使うと、
各IngressでそのコントローラのService(type: LoadBalancer)を共有する形になり、GKEでいうとTCP LoadBalancerを共有し、IPも同じになる模様。

(それによるデメリットがどれほどかは明確にわかってない)

それを避けるために複数のNGINX Ingress Controllerをデプロイして系統を分ける。

その際、cert-managerのDNS-01チャレンジ方式を使ってLet's EncryptのSSL証明書の管理を自動化。

cert-managerでは Issuer(namespaceごとに配備)を使うかClusterIssuer(namespace無しでcluster全体に配備)を使うか選べる。
複数namespaceを一括で賄いたいのでClusterIssuerを使った。

やっぱり基本的にHelmを使う。

環境

  • Host: GKE
  • Master version/Node version: 1.8.5-gke.0
  • Helm: v2.8.0
  • chart: stable/nginx-ingress:0.9.1
  • chart: github.com/jetstack/cert-manager:v0.2.3
  • image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.10.2
  • image: quay.io/jetstack/cert-manager-controller:v0.2.3

構成

  • NGINX Ingress Controller

    • namespace: ingress-nginx
  • cert-manager

    • namespace: kube-system
      • ClusterIssuerが生成するSecretがkube-system namespaceに入るのを回避できなかったのでcert-manager namespaceにデプロイするのは諦めた
  • 通常の系統

    • nginx-ingress
  • 別系統(社内用系統なイメージ)

    • nginx-ingress-inhouse
  • cert-managerは共通で一つ

手順

(helmのセットアップ手順は省略)

namespace作成

# ingress-nginx
curl https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/namespace.yaml \
  | kubectl apply -f -

必須ではないけど、公式の命名に準じる。

nginx-ingressのセットアップ

helm install stable/nginx-ingress \
  --name nginx-ingress \
  --namespace ingress-nginx \
  --set controller.replicaCount=2

replicaCount は任意で。

nginx-ingress-inhouseのセットアップ

helm install stable/nginx-ingress \
  --name nginx-ingress-inhouse \
  --namespace ingress-nginx \
  --set controller.ingressClass=nginx-inhouse \
  --set controller.replicaCount=2

ingressClass をデフォルトのnginxとは別名に指定する。

cert-managerのセットアップ

GKE で TLS 証明書を自動管理(cert-manager DNS-01 編) がとてもわかりやすいのでそちらを参照。

kube-lego/HTTP-01のときと違ってNGINX Ingress Controllerが2つだろうとcert-managerには関係がないので、セットアップの仕方に特に変化はない。

上の記事の手順の中で唯一、 Issuer ではなく ClusterIssuer が使いたかったのでそこだけ変更

$ kubectl apply -f - << EOF
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    # The ACME server URL
    server: https://acme-v01.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: [email protected]
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-prod
    # Enable the HTTP-01 challenge provider
    http01: {}
    # ACME dns-01 provider configurations
    dns01:
      # Here we define a list of DNS-01 providers that can solve DNS challenges
      providers:
      - name: prod-dns
        clouddns:
          # A secretKeyRef to a the google cloud json service account
          serviceAccountSecretRef:
            name: clouddns-service-account
            key: cert-manager-key.json
          # The project in which to update the DNS zone
          project: apstndb-sandbox
EOF
issuer "letsencrypt-prod" created

nginx-ingress系統にingress追加

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: web-nginx
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: web
          servicePort: 80
  tls:
  - hosts:
    - example.com
    secretName: web-tls

name, namespace, serviceName, servicePort などは適宜命名。
example.com は要変更。
secretName にはcert-managerのCertificateリソースで生成したSecretを指定する。

kubernetes.io/ingress.classnginxなので主系統のnginx-ingressに捕捉されて処理される。

nginx-ingress-inhouse系統にingress追加

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: admin-nginx-inhouse
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx-inhouse
spec:
  rules:
  - host: admin.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: admin-service
          servicePort: 80
  tls:
  - hosts:
    - admin.example.com
    secretName: admin-tls

kubernetes.io/ingress.classnginx-inhouseなのでnginx-ingress-inhouseに捕捉される。

おわり

kube-legoではHTTP-01チャレンジ方式しか使えずIngress側も駆使して証明書取得していたのに比べると、
DNS-01チャレンジではIngressは無関係になって、単に準備されたSecretを使うだけになる。

そのおかげで、NGINX Ingress Controllerが幾つあろうとcert-managerは一つで済み、
設定にも難しいことは特になくなった。