KubernetesにおけるDNSと証明書のセルフサービス管理を実現

NGINX | 2022年11月01日

アプリケーション開発の最終的な目標は、言うまでもなく、アプリをインターネット上に公開することです。Kubernetesは、アプリケーションへのリクエストをルーティングする仕組みとしてIngressコントローラーを提供することで、開発者にとってこのプロセスをある程度簡素化します。しかし、すべてを思いどおりにセルフサービスで進められるわけではありません。アプリのドメイン名をIngressコントローラーのIPアドレスに対応付けるDomain Name System(DNS)レコードと、HTTPSを使用した接続を保護するTLS証明書が必要です。ほとんどの組織では、開発者自身がDNSやTLSを管理していないため、それらを管理する運用部門(場合によっては複数の部門)との調整が必要になります。

運用担当者にとっても、必ずしも簡単ではありません。ほとんどの組織では、DNSレコードを更新する機会が少ないため、ビジネスルールと実際の技術的な手順のどちらについても、手順が十分に整備されていないか、まったく存在しない傾向があります。そのため、DNSレコードを追加する際は、まずドキュメントを探すか、同僚に尋ねる必要があり、最悪の場合は自力で方法を見つけなければなりません。また、社内のセキュリティルールに準拠していることを確認し、ファイアウォール向けにIngressへ適切なタグが付けられていることも確認する必要があります。

幸い、開発者と運用担当者の双方の負担を軽減する方法があります。この記事では、運用担当者がKubernetesデプロイメントを構成し、Kubernetes環境で開発者がセルフサービスでDNSレコードを更新し、TLS証明書を生成できるようにする方法を紹介します。インフラストラクチャを事前に構築しておくことで、必要なビジネス要件と技術要件をすべて満たすことができます。

概要と前提条件

このソリューションを導入すると、開発者は、Kubernetes環境で管理されているドメイン内の完全修飾ドメイン名(FQDN)を含む所定のテンプレートに従ってIngressコントローラーを作成するだけで、アプリケーションをインターネットに公開できます。Kubernetesはこのテンプレートを使用して、IngressコントローラーにIPアドレスを割り当て、FQDNをIPアドレスにマッピングするDNS Aレコードを作成し、FQDN用のTLS証明書を生成してIngressコントローラーに追加します。クリーンアップも同様に簡単です。Ingressを削除すると、DNSレコードも削除されます。

このソリューションでは、以下のテクノロジーを活用します(インストールと構成の手順については後述します)。

ソリューションを構成する前に、以下を準備してください。

  • エグレス(LoadBalancer)オブジェクトを備えたKubernetesクラウド環境。このソリューションではLinodeを使用しますが、他のクラウドプロバイダーも利用できます。
  • ホスト先がCloudflareであるドメイン名。Cloudflareを選択したのは、cert-managerでサポートされているDNSプロバイダーの1つであり、ExternalDNSにも対応しているためです(執筆時点ではベータ版)。このドメインは、本番環境やその他の重要な用途には使用しないことを強く推奨します。
  • 無料プランに含まれるCloudflare APIへのアクセス。
  • Helm(Kubernetesのインストールとデプロイに使用)。
  • kubectl(Kubernetesのコマンドラインインターフェース)。
  • 必要に応じて、K9sを使用できます。K9sは、Kubernetesをより体系的に操作できる、使いやすく設計されたテキストユーザーインターフェース(TUI)です。

また、Kubernetesの基本的な知識(マニフェストの適用方法、Helmチャートの使用方法、および出力の確認やトラブルシューティングを行うための「kubectl」コマンドの実行方法)があることを前提としています。Let’s Encryptの基本概念を理解していると役立ちますが、必須ではありません。概要については、こちらの「ブログ」をご覧ください。cert-managerの仕組みに関する知識も必要ありませんが、cert-manager(および証明書全般)とNGINX Ingress Controllerの連携に関心がある場合は、最近の投稿「Kubernetes環境における証明書管理の自動化」をご覧ください。

このソリューションはmacOSとLinuxの両方でテスト済みです。Windows Subsystem for Linux version 2(WSL2)ではテストしていませんが、問題はないと見込んでいます。

注: このソリューションは概念実証用のサンプルであり、本番環境での使用を目的としていません。特に、運用とセキュリティに関するすべてのベストプラクティスが組み込まれているわけではありません。これらのトピックについては、cert-managerおよびExternalDNSのドキュメントを参照してください。

ソリューションのデプロイ

ソリューションをデプロイするには、以下のセクションの手順に従ってください。

ソフトウェアのダウンロード

  1. Download your Cloudflare API Tokenをダウンロードします。
  2. NGINX Ingress Controllerリポジトリをクローンします:
$ git clone https://github.com/nginxinc/kubernetes-ingress.gitCloning into 'kubernetes-ingress'...
remote: Enumerating objects: 45176, done.
remote: Counting objects: 100% (373/373), done.
remote: Compressing objects: 100% (274/274), done.
remote: Total 45176 (delta 173), reused 219 (delta 79), pack-reused 44803
Receiving objects: 100% (45176/45176), 60.45 MiB | 26.81 MiB/s, done.
Resolving deltas: 100% (26592/26592), done.
  1. Kubernetesクラスターに接続できることを確認します。
$ kubectl cluster-infoKubernetes control plane is running at https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443
KubeDNS is running at https://ba35bacf-b072-4600-9a04-e04...6a3d.us-west-2.linodelke.net:443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
 
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

NGINX Ingress Controllerのデプロイ

  1. Helmを使用してNGINX Ingress Controllerをデプロイします。ここでは、3つの非標準の構成オプションを追加します。
$ helm install nginx-kic nginx-stable/nginx-ingress --namespace nginx-ingress --set controller.enableCustomResources=true --create-namespace --set controller.enableCertManager=true --set controller.enableExternalDNS=trueNAME: nginx-kic
LAST DEPLOYED: Day Mon DD hh:mm:ss YYYY
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The NGINX Ingress Controller has been installed.
  • controller.enableCustomResources – NGINXのVirtualServerおよびVirtualServerRouteカスタムリソースの作成に使用するカスタムリソース定義(CRD)をインストールするようHelmに指示します。
  • controller.enableCertManager – NGINX Ingress Controllerがcert-managerコンポーネントと通信するように構成します。
  • controller.enableExternalDNS – Ingress ControllerがExternalDNSコンポーネントと通信するように設定します。
  1. NGINX Ingress Controllerが実行中であることを確認し、EXTERNAL-IPフィールドの値を控えます。この値はNGINX Ingress ControllerのIPアドレスです(ここではwww.xxx.yyy.zzz)。読みやすいように、出力は2行に分けて表示しています。
$ kubectl get services --namespace nginx-ingressNAME                      TYPE           CLUSTER-IP      ...
nginx-kic-nginx-ingress   LoadBalancer   10.128.152.88   ... 

   ... EXTERNAL-IP       PORT(S)                      AGE
   ... www.xxx.yyy.zzz   80:32457/TCP,443:31971/TCP   3h8m

cert-managerのデプロイ

このソリューションでは、cert-managerはTLS証明書の取得時にDNS-01チャレンジタイプを使用します。そのため、ClusterIssuerリソースの作成時にCloudflare APIトークンを指定する必要があります。このソリューションでは、APIトークンはKubernetesのSecretとして指定します。

  1. Helmを使用して、cert-managerをデプロイします:
$ helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.9.1 --set installCRDs=trueNAME: cert-manager
LAST DEPLOYED: Day Mon DD hh:mm:ss YYYY
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.9.1 has been deployed successfully!
  1. Cloudflare APIトークンをKubernetes Secretとしてデプロイします。その際、次の値を置き換えてください:<your-API-token>:
$ kubectl apply -f - <<EOFapiVersion: v1 kind: Secret metadata: name: Cloudflare-api-token-secret namespace: cert-manager type: Opaque stringData: api-token: "<your-API-token>" EOF
secret/Cloudflare-api-token-secret created
  1. ClusterIssuerオブジェクトを作成し、トークンの取得先として、Cloudflare-api-token-secret(前の手順で定義)を指定します。必要に応じて、metadata.nameフィールドのexample-issuer(およびspec.acme.privateKeySecretRef.nameフィールドのexample-issuer-account-key)を別の名前に置き換えることができます。
$ kubectl apply -f - <<EOFapiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: example-issuer namespace: cert-manager spec: acme: email: example@example.com server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: example-issuer-account-key solvers: - dns01: Cloudflare: apiTokenSecretRef: name: Cloudflare-api-token-secret key: api-token EOF
clusterissuer.cert-manager.io/example-issuer created
  1. ClusterIssuerがデプロイ済みで準備が整っていることを確認します(READYフィールドの値がTrueであること)。
$ kubectl get clusterissuerNAME             READY   AGE
example-issuer   True    3h9m

ExternalDNSをデプロイ

cert-managerと同様に、ExternalDNSプロジェクトでDNSを管理するにはCloudflare API Tokenが必要です。両方のプロジェクトで同じトークンを使用できますが、同じトークンを使用する必要はありません。

  1. プロジェクト間の連携を有効にするため、NGINX Ingress Controller用のExternalDNS CRDを作成します。
$ kubectl create -f ./kubernetes-ingress/deployments/common/crds/externaldns.nginx.org_dnsendpoints.yamlcustomresourcedefinition.apiextensions.k8s.io/dnsendpoints.externaldns.nginx.org created
  1. External DNSサービス(external-dns)を作成します。マニフェストがかなり長いため、ここでは2つに分けます。前半では、アカウント、ロール、権限を設定します:
$ kubectl apply -f - <<EOFapiVersion: v1 kind: ServiceAccount metadata: name: external-dns --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: external-dns rules: - apiGroups: [""] resources: ["services","endpoints","pods"] verbs: ["get","watch","list"] - apiGroups: ["extensions","networking.k8s.io"] resources: ["ingresses"] verbs: ["get","watch","list"] - apiGroups: ["externaldns.nginx.org"] resources: ["dnsendpoints"] verbs: ["get","watch","list"] - apiGroups: ["externaldns.nginx.org"] resources: ["dnsendpoints/status"] verbs: ["update"] - apiGroups: [""] resources: ["nodes"] verbs: ["list","watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: external-dns-viewer roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: external-dns subjects: - kind: ServiceAccount name: external-dns namespace: default EOF
serviceaccount/external-dns created
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created
  1. マニフェストの2番目の部分では、ExternalDNSデプロイメントを作成します:
$  kubectl apply -f - <<EOF --- apiVersion: apps/v1 kind: Deployment metadata: name: external-dns spec: strategy: type: Recreate selector: matchLabels: app: external-dns template: metadata: labels: app: external-dns spec: serviceAccountName: external-dns containers: - name: external-dns image: k8s.gcr.io/external-dns/external-dns:v0.12.0 args: - --source=service - --source=ingress - --source=crd - --crd-source-apiversion=externaldns.nginx.org/v1 - --crd-source-kind=DNSEndpoint - --domain-filter=example.com - --provider=Cloudflare env: - name: CF_API_TOKEN value: "<your-API-token>" - name: FREE_TIER value: "true" EOF
serviceaccount/external-dns created
clusterrole.rbac.authorization.k8s.io/external-dns created
clusterrolebinding.rbac.authorization.k8s.io/external-dns-viewer created
deployment.apps/external-dns created
  • DNS管理に関するすべての書き込みおよび更新操作を管理するために、external-dnsというServiceAccountオブジェクトを作成します。
  • 必要な権限を定義するClusterRoleオブジェクト(同じく「external-dns」という名前)を作成します。
  • ClusterRoleをServiceAccountにバインドします。
  • ドメインフィルターを作成し、ExternalDNSによるドメイン管理で生じる可能性のある損害の範囲を限定します。たとえば、ステージング環境のドメイン名を指定することで、本番環境への変更を防止できます。この例では、domain-filterをexample.comに設定します。
  • Sets the CF_API_TOKEN環境変数にCloudflare API Tokenを設定します。<your-API-token>には、実際のトークンまたはトークンを含むSecretを指定します。後者の場合は、環境変数を使用してSecretをコンテナに投影する必要もあります。
  • Sets the FREE_TIER環境変数を"true"に設定します(Cloudflareの有料サブスクリプションを利用していない場合に適した設定です)。

サンプルアプリケーションのデプロイ

テストには、標準のNGINX Ingress Controllerサンプルアプリケーション「Cafe」を使用します。

  1. Cafeアプリケーションをデプロイします。
$ kubectl apply -f ./kubernetes-ingress/examples/ingress-resources/complete-example/cafe.yamldeployment.apps/coffee created
service/coffee-svc created
deployment.apps/tea created
service/tea-svc created
  1. Cafeアプリケーション用にNGINX Ingress Controllerをデプロイします。次の設定に注意してください。この手順の完了にかかる時間は、KubernetesがDNSプロバイダーのDNS APIと通信するため、プロバイダーによって大きく異なります。
$ kubectl apply -f - <<EOFapiVersion: k8s.nginx.org/v1 kind: VirtualServer metadata: name: cafe spec: host: cafe.example.com tls: secret: cafe-secret cert-manager: cluster-issuer: example-issuer externalDNS: enable: true upstreams: - name: tea service: tea-svc port: 80 - name: coffee service: coffee-svc port: 80 routes: - path: /tea action: pass: tea - path: /coffee action: pass: coffee EOF
virtualserver.k8s.nginx.org/cafe created
  • kind: VirtualServer – 標準のKubernetes Ingressリソースではなく、NGINX VirtualServerカスタムリソースを使用しています。
  • spec.host – cafe.example.comをデプロイするホストの名前に置き換えます。ホストは、ExternalDNSで管理されているドメイン内にある必要があります。
  • spec.tls.cert-manager.cluster-issuer – この記事で指定されている値を使用している場合、これは「example-issuer」です。必要に応じて、「ステップ3:cert‑managerのデプロイ」で選択した名前に置き換えてください。
  • spec.externalDNS.enable – 値を「true」に設定すると、ExternalDNSによってDNS Aレコードが作成されます。

ソリューションを検証する

  1. DNS Aレコードを確認します。特に、ANSWER SECTIONブロックでFQDN(ここではcafe.example.com)が正しいIPアドレス(www.xxx.yyy.zzz)にマッピングされていることを確認してください。
$ dig cafe.example.com 
; <<>> DiG 9.10.6 <<>> cafe.example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22633
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
 
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;cafe.example.com.		IN	A
 
;; ANSWER SECTION:
cafe.example.com.	279	IN	A	www.xxx.yyy.zzz
 
;; Query time: 1 msec
;; SERVER: 2607:fb91:119b:4ac4:2e0:xxxx:fe1e:1359#53(2607:fb91:119b:4ac4:2e0:xxxx:fe1e:1359)
;; WHEN: Day Mon DD hh:mm:ss TZ YYYY
;; MSG SIZE  rcvd: 67
  1. 証明書が有効であることを確認します(READYフィールドの値がTrueであること)。
$ kubectl get certificatesNAME          READY   SECRET        AGE
cafe-secret   True    cafe-secret   8m51s
  1. アプリケーションにアクセスできることを確認します。
$ curl https://cafe.example.com/coffeeServer address: 10.2.2.4:8080
Server name: coffee-7c86d7d67c-lsfs6
Date: DD/Mon/YYYY:hh:mm:ss +TZ-offset
URI: /coffee
Request ID: 91077575f19e6e735a91b9d06e9684cd
$ curl https://cafe.example.com/tea
Server address: 10.2.2.5:8080
Server name: tea-5c457db9-ztpns
Date: DD/Mon/YYYY:hh:mm:ss +TZ-offset
URI: /tea
Request ID: 2164c245a495d22c11e900aa0103b00f

開発者がNGINX Ingress Controllerをデプロイするとどうなるか

ソリューションを導入すると、内部ではさまざまな処理が行われます。この図は、開発者がNGINX VirtualServerカスタムリソースを使用してNGINX Ingress Controllerをデプロイした際の動作を示しています。なお、運用上の詳細の一部は省略しています。

開発者がNGINX VirtualServerカスタムリソースとともにNGINX Ingress Controllerをデプロイした場合の処理:(1)開発者がNGINX CRDを使用してVirtualServerリソースをデプロイする(2)KubernetesがNGINX Ingress Controllerを使用してVirtualServerを作成する(3)NGINX Ingress ControllerがExternalDNSを呼び出し、DNS Aレコードを作成する(4)ExternalDNSがDNSにAレコードを作成する(5)NGINX Ingress Controllerがcert-managerを呼び出し、TLS証明書をリクエストする(6)cert-managerがDNS-01チャレンジで使用するDNSレコードを追加する(7)cert-managerがLet’s Encryptに接続し、チャレンジを完了する(8)Let’s EncryptがDNSに照らしてチャレンジを検証する(9)Let’s EncryptがTLS証明書を発行する(10)cert-managerがNGINX Ingress ControllerにTLS証明書を提供する(11)NGINX Ingress ControllerがTLSで保護された外部リクエストをアプリケーションPodにルーティングする

  1. 開発者がNGINX CRDを使用してVirtualServerリソースをデプロイ
  2. KubernetesがNGINX Ingress Controllerを使用してVirtualServerを作成
  3. NGINX Ingress ControllerがExternalDNSを呼び出してDNS Aレコードを作成します
  4. ExternalDNSがDNSにAレコードを作成します
  5. NGINX Ingress Controllerがcert-managerを呼び出し、TLS証明書をリクエストします
  6. cert-managerがDNS-01チャレンジで使用するDNSレコードを追加します
  7. cert-managerがLet’s Encryptに接続し、チャレンジを完了します
  8. Let’s EncryptがDNSに対してチャレンジを検証します
  9. Let’s EncryptがTLS証明書を発行します
  10. cert-managerがTLS証明書をNGINX Ingress Controllerに提供します
  11. NGINX Ingress Controllerは、TLSで保護された外部リクエストをアプリケーションPodにルーティングします

トラブルシューティング

Kubernetesと使用しているコンポーネントは複雑なため、包括的なトラブルシューティングガイドを提供することは困難です。ただし、問題の特定に役立つ基本的な対処方法をいくつかご紹介します。

  • デプロイ済みオブジェクトの構成を検証するには、kubectl getコマンドとkubectl describeコマンドを使用します。
  • デプロイされた各種コンポーネントのログファイルを表示するには、kubectl logs <component> コマンドを使用します。
  • インストール状況の確認にはK9sを使用します。このソフトウェアは、問題を重大度に応じて黄色または赤色で強調表示し、ログやオブジェクトの詳細にアクセスするためのインターフェースを提供します。

それでも問題が解決しない場合は、NGINXCommunity Slackでお気軽にご相談ください。活発なコミュニティがあり、問題の解決に向けていつでも喜んでサポートします。

NGINX PlusベースのNGINX Ingress Controllerをお試しいただくには、今すぐ30日間の無料トライアルを開始するか、ユースケースについてご相談ください。


共有
タグ: DNS, F5 NGINX, Tech

関連するブログ記事

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
NGINX | 2024/01/18

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する

OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
NGINX | 2023/05/31

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正

Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
NGINX | 2023/02/09

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法

Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINX | 2022/05/24

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング

NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
NGINX | 2018/08/17

SELinuxでNGINXおよびNGINX Plusを使用する

Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。

NGINX PlusによるAWS Auto Scalingグループの負荷分散
NGINX | 2017/03/06

NGINX PlusによるAWS Auto Scalingグループの負荷分散

新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります