Amazon Web Services ブログ

Contour で EKS Ingress を保護し、Let’s Encrypt の GitOps 方法を学びます

Weaveworks の Stefan Prodan 氏によるゲスト投稿です。

Kubernetes の用語で、Ingress は、クラスターの外部からクラスター内で実行されているサービスへの HTTP(S) ルートを公開します。Ingress は、負荷分散と SSL/TLS 終了を実行しながら、Kubernetes サービスに外部から到達可能な URL を提供するように構成できます。

Kubernetes には Ingress リソースが付属しており、ELB Ingress Controller や NGINX などの Ingress 仕様を実装するコントローラがいくつかあります。Kubernetes Ingress の仕様は非常に限られているため、ほとんどのコントローラはアノテーションを使用してルーティング機能を Ingress が許可する基本範囲を超えて拡張する必要がありました。ただし、アノテーションを使用しても、名前空間間のルーティングや加重負荷分散など、解決できない制限がいくつかあります。

Contour (今後 CNCF プロジェクト) は、Envoy に基づいた最新の Ingress コントローラで、HTTPProxy という名前の新しい仕様で Ingress API の機能を拡張します。HTTPProxy API は、より豊かなユーザー体験を可能にし、マルチテナント環境での Ingress の使用制限に対処します。

HTTPProxy 仕様は、HTTP ヘッダーまたは Cookie フィルターに基づいた高度な L7 ルーティングポリシー、および Kubernetes サービス間の加重負荷分散を促進するのに十分な柔軟性を備えています。これらの機能を備えているため、Contour は、Flagger を使用した Canary リリースおよび A/B テストの自動化に適しています。

このガイドでは、GitOps パイプラインをセットアップして HTTPS を介して Kubernetes サービスを安全に公開する方法を示します。

  • Amazon EKS および Amazon Route 53
  • TLS 証明書をプロビジョニングするための、Let’s Encrypt での cert-manager
  • Ingress コントローラとしての Contour
  • GitOps オペレーターとしての Flux
  • デモウェブアプリケーションとしての podinfo

EKS クラスターを作成する

ローカルにインストールされた AWS アカウント、GitHub アカウント、gitkubectleksctl が必要です。最初に、4 つの EC2 ノードで EKS クラスターを作成します。

cat << EOF | eksctl create cluster -f -
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: my-cluster
  region: eu-west-1
nodeGroups:
  - name: controllers
    labels: { role: controllers }
    instanceType: m5.large
    desiredCapacity: 2
    iam:
      withAddonPolicies:
        certManager: true
        albIngress: true
    taints:
      controllers: "true:NoSchedule"
managedNodeGroups:
  - name: workers
    labels: { role: workers }
    instanceType: m5.large
    desiredCapacity: 2
    volumeSize: 120
EOF

上記のコマンドで、2 つのノードグループを持つ EKS クラスターを作成します。

  • コントローラノードグループには、cert-manager で DNS01 ACME チャレンジを解決するために必要な IAM ロールがあり、Contour および cert-manager と共に Envoy プロキシ DaemonSet を実行するために使用されます。
  • ワーカー管理対象ノードグループは、アプリ用として、Envoy によってクラスターの外部に公開されます。

Kustomize パッチは、セレクターとトレレーションを使用してノードグループのワークロードを固定するために使用されます。例:

# contour/node-selector-patch.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: envoy
  namespace: projectcontour
spec:
  template:
    spec:
      nodeSelector:
        role: controllers
      tolerations:
        - key: controllers
          operator: Exists

Kustomize パッチを使用して、元のマニフェストを変更しないようにします。マニフェストを更新するには、./scripts/update-manifests.sh を実行します。このスクリプトは、最新の cert-manager と Contour YAML をダウンロードし、リポジトリ内のマニフェストを上書きします。

Flux をインストールする

Flux は Kubernetes の GitOps オペレーターであり、クラスターの状態を Git リポジトリと同期させます。Flux はプルベースであり、Kubernetes 内でも実行されるため、運用環境の外部でクラスターの認証情報を公開する必要はありません。

Kubernetes YAML マニフェストを使用してクラスターの目的の状態を定義し、Kustomize を使用してカスタマイズできます。Flux は、引き続きクラスターに目的の状態を適用する制御ループを実装し、デプロイの削除やポリシーの変更などの有害なアクションに対する保護を提供します。

プラットフォームに応じて、fluxctl をインストールします。

# macOS
brew install fluxctl

# Windows
choco install fluxctl

# Linux
curl -sL https://fluxcd.io/install | sh

GitHub で、このリポジトリをフォークし、ローカルで複製します (stefanprodan を GitHub ユーザー名に置き換えます):

git clone https://github.com/stefanprodan/eks-contour-ingress
cd eks-contour-ingress

次に、次を使用して fluxcd 名前空間を作成します。

kubectl create ns fluxcd

そして、フォーク URL を指定して Flux をインストールします (そして、再び stefanprodan を GitHub ユーザー名に置き換えます):

export GHUSER="stefanprodan" && \
fluxctl install \
--git-user=${GHUSER} \
--git-email=${GHUSER}@users.noreply.github.com \
--git-url=git@github.com:${GHUSER}/eks-contour-ingress \
--git-branch=master \
--manifest-generation=true \
--namespace=fluxcd | kubectl apply -f -

Git 同期をセットアップ

Flux は起動時に SSH キーを生成し、パブリックキーを記録します。以下でパブリックキーを見つけます。

fluxctl identity --k8s-fwd-ns fluxcd

クラスターの状態を git と同期するには、パブリックキーをコピーし、GitHub リポジトリに書き込みアクセスを使用してデプロイキーを作成する必要があります。

GitHub を開き、リポジトリに移動して、[設定] > [デプロイキー] に移動し、[デプロイキーを追加] をクリックして、[書き込みアクセスを許可] のチェックをオンにし、Flux パブリックキーを貼り付けて [キーを追加] をクリックします。

数秒後、Flux はクラスターに Contour、cert-manager、podinfo をデプロイします。watch kubectl get pods --all-namespaces で同期ステータスを確認できます。

DNS を構成する

Contour の Envoy ロードバランサーの外部アドレスを取得します。

$ kubectl get -n projectcontour service envoy -o wide

NAME    TYPE           CLUSTER-IP      EXTERNAL-IP
envoy   LoadBalancer   10.100.228.53   af4726981288e11eaade7062a36c250a-1448602599.eu-west-1.elb.amazonaws.com

外部アドレスを使用して、Route53 で CNAME レコードを作成します。LB アドレス *.example.com にマップします。

host コマンドを使用して DNS 設定を確認します。

$ host podinfo.example.com

podinfo.example.com is an alias for af4726981288e11eaade7062a36c250a-1448602599.eu-west-1.elb.amazonaws.com.

Let’s Encrypt ワイルドカードの証明書を取得する

Let’s Encrypt から証明書を取得するには、ドメイン DNS レコードを制御できることを証明する特定のコンテンツを含む TXT レコードを作成して、所有権を証明する必要があります。DNS チャレンジと証明書の更新は、cert-manager と Route 53 を使用して完全に自動化できます。

次に、Let’s Encrypt DNS01 ソルバーを使用してクラスターの問題を作成します (stefanprodan を GitHub ユーザー名に置き換えます)。

export GHUSER="stefanprodan" && \
cat << EOF | tee ingress/issuer.yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
  annotations:
    fluxcd.io/ignore: "false"
spec:
  acme:
    email: ${GHUSER}@users.noreply.github.com
    privateKeySecretRef:
      name: letsencrypt-prod
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - dns01:
        route53:
          region: eu-west-1
EOF

projectcontour 名前空間で証明書を作成します (example.com をご使用のドメインに置き換えます):

export DOMAIN="example.com" && \
cat << EOF | tee ingress/cert.yaml
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: cert
  namespace: projectcontour
  annotations:
    fluxcd.io/ignore: "false"
spec:
  secretName: cert
  commonName: "*.${DOMAIN}"
  dnsNames:
  - "*.${DOMAIN}"
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
EOF

次に、GitOps の方法である git を使用して変更を適用します。

git add -A && \
git commit -m "add wildcard cert" && \
git push origin master && \
fluxctl sync --k8s-fwd-ns fluxcd

Flux は git<->cluster 調整を 5 分ごとに実行するため、上記の fluxctl sync コマンドを使用して同期を高速化します。

証明書が発行されるまで待ちます (完了するまでに最大 2 分かかります)。

$ watch kubectl -n projectcontour describe certificate

Events:
  Type    Reason        Age    From          Message
  ----    ------        ----   ----          -------
  Normal  GeneratedKey  2m17s  cert-manager  Generated a new private key
  Normal  Requested     2m17s  cert-manager  Created new CertificateRequest resource "cert-1178588226"
  Normal  Issued        20s    cert-manager  Certificate issued successfully

証明書が発行されると、cert-manager は TLS 証明書を使用してシークレットを作成します。次のようにご確認ください。

$ kubectl -n projectcontour get secrets
NAME                  TYPE                                  DATA   AGE
cert                  kubernetes.io/tls                     3      5m40s

証明書を Contour 名前空間に保存して、異なる名前空間にデプロイされた複数のアプリで再利用できるようにします。

TLS を介したサービスを公開

デモアプリ podinfo を公開し、クラスターの外部からアクセスできるようにするには、Contour の HTTPProxy カスタムリソース定義を使用します。

このために、TLS 証明書のシークレットを参照して HTTPProxy を作成します (example.com をドメインに置き換えます)。

export DOMAIN="example.com" && \
cat << EOF | tee ingress/proxy.yaml
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
  name: podinfo
  namespace: projectcontour
  annotations:
    fluxcd.io/ignore: "false"
spec:
  virtualhost:
    fqdn: podinfo.${DOMAIN}
    tls:
      secretName: cert
  includes:
    - name: podinfo
      namespace: demo
EOF

HTTPProxies には他の HTTPProxy オブジェクトを含めることができます。上記の構成には、デモ名前空間の podinfo HTTPProxy が含まれます。以下を参照してください。

$ cat podinfo/proxy.yaml
apiVersion: projectcontour.io/v1
kind: HTTPProxy
metadata:
  name: podinfo
  namespace: demo
spec:
  routes:
    - services:
        - name: podinfo
          port: 9898

クロス名前空間インクルージョンを使用して、共有 TLS ワイルドカード証明書をロードできます。

それでは、Flux が証明書を取得してクラスターを同期するために、git 経由で変更を適用しましょう。

git add -A && \
git commit -m "expose podinfo" && \
git push origin master && \
fluxctl sync --k8s-fwd-ns fluxcd

仮想ホストに対して TLS が有効になっている場合、Contour はトラフィックを安全なインターフェイスにリダイレクトします。確認するには、(ドメインで) 次を実行します。

$ curl -vL podinfo.example.com

< HTTP/1.1 301 Moved Permanently
< location: https://podinfo.example.com/

できました。 最後に、少し高度なトピックであるプログレッシブ配信パラダイムを使用してトラフィックを新機能にルーティングする方法に進みます。

プログレッシブ配信

プログレッシブ配信は、Canary デプロイ、機能フラグ、A/B テストなどの高度なデプロイパターンを包括する用語です。これらの手法を使用して、アプリ開発者と SRE チームが広範囲をきめ細かく制御できるようにすることで、本番環境で新しいソフトウェアバージョンを導入するリスクを軽減します。

Flagger と Contour の HTTPProxy を一緒に使用して、ウェブアプリの Canary リリースと A/B テストを自動化できます。Flagger を使用する場合、podinfo サービスとプロキシの定義を Canary の定義に置き換えます。Flagger は、Canary 仕様に基づいて、独自に Kubernetes ClusterIP サービスと Contour HTTPProxy を生成します。

アプリの新しいバージョンをデプロイすると、Flagger はトラフィックを徐々に Canary に移動し、同時にリクエストの成功率と平均応答時間を測定します。これらのメトリックは Envoy によって提供され、Prometheus によって収集されます。カスタムの Prometheus メトリック、受け入れテスト、負荷テストで Canary 分析を拡張し、アプリのリリースプロセスにおける検証プロセスを強化できます。

Flagger のお試しを希望する場合は、Contour プログレッシブ配信チュートリアルをご利用いただけます。

Stefan Prodan

Stefan Prodan

Stefan は、Weaveworks の開発者体験エンジニアであり、Flagger、FluxCD、Helm Operator、OpenFaaS などのクラウドネイティブプロジェクトにオープンソースを提供しています。彼はソフトウェアアーキテクト兼 DevOps コンサルタントとして働いており、企業が DevOps と SRE の動きを取り入れるように支援します。Twitter の @stefanprodan で彼をフォローすることができます。