2026年5月30日

2026年5月30日

KubernetesのCert ManagerでLet’s Encrypt証明書を自動取得・自動更新する手順

Kubernetes で HTTPS の証明書を自動で取得・更新するには、cert-manager をインストールし、Let's Encrypt を使う ClusterIssuer を作って、Ingress にアノテーションを1行足すのが基本の流れです。この記事では、インストールからステージング環境での試験発行、本番への切り替え、証明書が発行されないときに Certificate から Challenge までをたどる確認方法までを説明します。

cert-manager が行っていること

cert-manager は、証明書の発行と更新を担当するコントローラーです。クラスターにいくつかのカスタムリソースを追加し、それらを監視して次のように動きます。

  • Issuer / ClusterIssuer: どこから証明書をもらうか(Let's Encrypt などの ACME サーバー、自前の CA など)を定義します。Issuer は1つの Namespace 内だけ、ClusterIssuer はクラスター全体で使えます。
  • Certificate: 「このドメインの証明書を、この Secret に保存しておいてほしい」という要求です。
  • CertificateRequest / Order / Challenge: 発行の途中経過です。ACME の場合、Order が注文、Challenge がドメインの所有確認にあたります。

発行された証明書と秘密鍵は、Certificate で指定した名前の Secret(kubernetes.io/tls 型)に保存され、Ingress コントローラーはその Secret を読んで HTTPS を提供します。更新も cert-manager が自動で行い、既定では有効期間の3分の2が過ぎたころに新しい証明書を取り直します。

事前に確認しておくこと

  • ドメインの DNS がクラスターの入口を向いていること。 HTTP-01 方式では、Let's Encrypt がインターネットからそのドメインの80番ポートにアクセスして確認します。
  • Ingress コントローラーが動いていること。 以下の例では IngressClass 名を nginx としています。自分の環境の名前は kubectl get ingressclass で確認してください。なお、Kubernetes コミュニティ版の ingress-nginx は保守終了が告知されており、Gateway API などへの移行が案内されています。cert-manager は Gateway API にも対応していますが、設定方法が異なるため、移行する場合は公式ドキュメントを参照してください(2026年10月現在)。
  • Helm が使えること。

手順1:cert-manager をインストールする

公式の Helm チャートを使うのが一般的です。

helm repo add jetstack https://charts.jetstack.io --force-update

helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true

crds.enabled=true は、カスタムリソース定義(CRD)も一緒にインストールする指定です。古いチャートでは installCRDs=true という名前でした。推奨されるインストール方法やチャートの置き場所は版によって変わることがあるため、インストール前に公式ドキュメントの手順を確認し、バージョンは --version で固定しておくと、後で同じ環境を再現できます。

インストール後、3つの Pod が Running になっていることを確認します。

kubectl get pods -n cert-manager

cert-manager、cert-manager-cainjector、cert-manager-webhook の3つです。webhook が準備完了になる前に Issuer を作ろうとすると、webhook に接続できないというエラーで弾かれることがあります。その場合は少し待ってから作り直します。

手順2:ステージング用の ClusterIssuer を作る

Let's Encrypt の本番環境には発行回数の制限(レート制限)があります。設定ミスのまま試行を繰り返すと制限にかかり、しばらく発行できなくなるため、最初はステージング環境で試します。ステージングの証明書はブラウザに信頼されませんが、発行の流れを確かめるには十分です。

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-staging-account-key
    solvers:
      - http01:
          ingress:
            ingressClassName: nginx

privateKeySecretRef は、Let's Encrypt のアカウント用の秘密鍵を保存する Secret の名前です。ここで作られるのはアカウントの鍵で、サイトの証明書の鍵とは別物です。email には連絡を受け取れるアドレスを書きます。Let's Encrypt は期限切れ通知メールを2025年に終了しているため、期限の監視は後述する方法で別に用意してください。

kubectl apply -f clusterissuer-staging.yaml
kubectl get clusterissuer letsencrypt-staging

READY が True になれば、ACME サーバーへのアカウント登録は成功しています。

手順3:Ingress にアノテーションを付けて発行する

最も簡単なのは、Ingress に cert-manager.io/cluster-issuer アノテーションと tls の指定を書く方法です。cert-manager がこれを見つけて、対応する Certificate を自動で作ります。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
  namespace: production
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - www.example.com
      secretName: www-example-com-tls
  rules:
    - host: www.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

適用後、Certificate の状態を確認します。

kubectl get certificate -n production

READY が True になり、secretName の Secret ができていれば発行は完了です。

Ingress を使わない場合や、1つの証明書を複数の用途で使う場合は、Certificate リソースを直接書くこともできます。

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: www-example-com
  namespace: production
spec:
  secretName: www-example-com-tls
  dnsNames:
    - www.example.com
  issuerRef:
    name: letsencrypt-staging
    kind: ClusterIssuer

手順4:本番の Issuer に切り替える

ステージングで発行できたら、本番用の ClusterIssuer を作ります。手順2の YAML の name、server、privateKeySecretRef.name を次のように変えます。

  • name: letsencrypt-prod
  • server: https://acme-v02.api.letsencrypt.org/directory
  • privateKeySecretRef.name: letsencrypt-prod-account-key

Ingress のアノテーションを letsencrypt-prod に変えたら、ステージングの証明書が入った Secret を削除すると、本番の証明書で取り直されます。Secret を残したままだと、更新時期が来るまでステージングの証明書が使われ続けることがあります。削除の対象は証明書の Secret(例では www-example-com-tls)だけで、アカウント鍵の Secret ではない点に注意してください。

証明書が発行されないときの確認方法

発行は Certificate → CertificateRequest → Order → Challenge の順に進みます。どこで止まっているかを上から順に見ていきます。

kubectl get certificate,certificaterequest,order,challenge -n production
kubectl describe challenge -n production

多くの場合、原因は Challenge の describe の Events や Status に書かれています。よくある原因を、起きやすい順に挙げます。

  1. DNS がまだクラスターを向いていない。 dig www.example.com で、Ingress コントローラーの外部 IP(またはロードバランサーの名前)が返るかを確認します。DNS を変えた直後は反映待ちのこともあります。
  2. 80番ポートに外から届かない。 HTTP-01 の確認は http://<ドメイン>/.well-known/acme-challenge/<トークン> へのアクセスで行われます。ファイアウォールやクラウドのセキュリティグループ、ロードバランサーの設定で80番が閉じていないか、HTTP から HTTPS への強制リダイレクトがこのパスを妨げていないかを確認します。
  3. IngressClass の指定が合っていない。 Issuer の solver の ingressClassName と、実際に動いている Ingress コントローラーのクラス名が違うと、確認用の一時的な Ingress が処理されません。
  4. レート制限にかかった。 Order の状態にレート制限のエラーが出ていれば、時間を置くしかありません。ステージングで先に試す理由がここにあります。
  5. ワイルドカード証明書を HTTP-01 で取ろうとしている。 *.example.com は HTTP-01 では発行できず、DNS-01 方式が必要です。

cert-manager のコマンドラインツール cmctl を入れておくと、cmctl status certificate で状態をまとめて確認したり、cmctl renew で手動更新を試したりできます。

ワイルドカード証明書と DNS-01

DNS-01 方式は、DNS に TXT レコードを書き込んで所有を証明します。80番を開けられない社内向けのサービスや、ワイルドカード証明書に使います。cert-manager は Route 53、Cloudflare、Google Cloud DNS などに対応しており、solver に DNS プロバイダーの認証情報を指定します。

    solvers:
      - dns01:
          cloudflare:
            apiTokenSecretRef:
              name: cloudflare-api-token
              key: api-token

API トークンは DNS の編集に必要な最小限の権限で発行し、Secret として cert-manager の Namespace に置きます(ClusterIssuer の場合)。トークンの値をマニフェストに直接書いて Git に入れないでください。Route 53 の場合は、アクセスキーを Secret に置くより、IAM ロールをサービスアカウントに割り当てる方法(EKS の IRSA など)が推奨されます。

更新が止まっていないかを監視する

自動更新は便利ですが、DNS の変更やファイアウォールの設定変更で、いつの間にか更新に失敗していることがあります。Let's Encrypt は証明書の有効期間を段階的に短くする方針を発表しており、更新の失敗に気づくまでの余裕は今後さらに小さくなります(2026年10月現在、具体的な日程は Let's Encrypt の告知を確認してください)。

  • cert-manager は Prometheus 形式のメトリクスを出しており、証明書の有効期限(certmanager_certificate_expiration_timestamp_seconds)を使って、期限が近い証明書にアラートを出せます。
  • 外部からの監視として、openssl s_client -connect www.example.com:443 -servername www.example.com で実際に配信されている証明書の期限を確認する仕組みを別に持っておくと、クラスター内部の監視が止まった場合にも気づけます。

まとめ

  • cert-manager は Helm でインストールし、3つの Pod が Running になってから Issuer を作ります。
  • 最初はステージングの ClusterIssuer で発行を試し、成功してから本番に切り替えます。切り替え後は証明書の Secret を削除して取り直します。
  • Ingress に cert-manager.io/cluster-issuer アノテーションと tls を書けば、Certificate は自動で作られます。
  • 発行されないときは Certificate → CertificateRequest → Order → Challenge の順にたどり、DNS、80番ポート、IngressClass を確認します。
  • 期限切れ通知メールは終了しているため、メトリクスや外部監視で更新の失敗に気づける仕組みを用意します。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲