- 第18回スコープ・学習目標・今ここマップ
- Ingress の限界 — なぜ Gateway API が生まれたか
- Gateway API の 4 リソース — GatewayClass / Gateway / HTTPRoute / ReferenceGrant
- cert-manager の役割 — Issuer / Certificate による TLS 自動化
- やってみよう①: Gateway API CRD + cert-manager インストール
- Traefik を Gateway API 実装プロバイダとして使う
- やってみよう②: Traefik インストール + fanclub-api デプロイ + Gateway 作成
- Step 1: traefik リポジトリを追加する
- Step 2: Traefik を Gateway API プロバイダとしてインストールする
- Step 3: Traefik Pod と GatewayClass を確認する
- Step 4: fanclub-api を fanclub Namespace にデプロイする
- Step 5: 自己署名 ClusterIssuer を作成する
- Step 6: CA 証明書を発行する
- Step 7: CA ベースの ClusterIssuer を作成する
- Step 8: Gateway リソースを作成する
- Step 9: cert-manager が Gateway を検知したことを確認する
- 演習②のまとめ
- HTTPRoute による URL ルーティングと TLS 終端
- やってみよう③: HTTPRoute 作成 + HTTPS アクセス確認
- kind 環境での外部公開 — port-forward と extraPortMappings
- CKAD 試験頻出パターン — Gateway API の速攻設計
- 現場ヒヤリハット 2 件
- ep18 完了後の模擬アプリ状態と ep19(第1巻完走)への橋渡し
- 理解度チェック・第18回まとめ・次回予告・シリーズ一覧
第18回スコープ・学習目標・今ここマップ
動作確認バージョン: K8s v1.35 / kubectl v1.35.0 / Helm v4.1.4 / Gateway API v1.4.0 / Traefik chart 39.0.9(Traefik v3.6.15)/ cert-manager v1.20.0 / kind v0.31.0 / kindest/node:v1.35.0 / AlmaLinux 10.1(kernel 6.12.0-124.55.3.el10_1)(2026-05-16 時点・k8s-ops 実機検証)
本回は Kubernetes 実践教科書 第1巻(CKAD 対応・全 19 回)の第18回です。第6部「パッケージ管理 + HTTPS 公開」の第2回(3 回中の 2 回目)として、Kubernetes でアプリケーションを外部に HTTPS 公開する仕組みを扱います。第17回では fanclub-api を Helm Chart としてパッケージ化し、helm install 一発でデプロイできる状態にしました。
ただし、その時点では fanclub-api の Backend は ClusterIP Service で公開されているだけで、クラスタの外(ブラウザ)からアクセスする経路がありません。本回では、従来の Ingress リソースに代わる Kubernetes 標準のトラフィック制御 API である Gateway API を使って、fanclub-api を外部公開します。
Gateway API の実装プロバイダとして Traefik を Helm で導入し、cert-manager で TLS 証明書を自己署名 CA から自動発行して、HTTPS で fanclub-api にアクセスできる構成を kind 実機で組み立てます。
これにより CKAD ドメイン D5「Services and Networking」(出題比率 20%)の Competency「Use Ingress rules to expose applications」を、2026 年から CKAD の出題範囲に入った Gateway API で網羅します。
第17回からの継承状態確認:
| 項目 | 状態 | 出典 |
|---|---|---|
| kind クラスタ | kind-control-plane Ready(v1.35.0) | Lead 実機観察 |
| kind の port mapping | 6443/tcp(API Server)のみ。HTTP/HTTPS(80/443)の extraPortMappings なし | Lead 実機観察 |
| Helm | v4.1.4 導入済(/usr/local/bin/helm・ep17 演習①で導入) | ep17 完了状態 |
| CRD | 1 個(Gateway API はまだ未導入) | Lead 実機観察 |
| Namespace 一覧 | default / kube-node-lease / kube-public / kube-system / local-path-storage(合計 5 個) | ep17 完了状態 |
| default ns のリソース | ep16 完了状態の fanclub-api(生 kubectl apply 管理)+ 6 個の NetworkPolicy | ep17 完了状態を継続 |
Helm Chart ~/fanclub-charts/fanclub-api/ | ep17 演習で作成・手元に残存(Chart version 1.0.0) | ep17 完了状態 |
今ここマップ(第1巻 19 回中の現在位置):
第1部 コンテナとDocker
第1回〜第4回 [完了]
第2部 Kubernetes基礎
第5回〜第6回 [完了]
第3部 アプリリソース
第7回〜第11回 [完了]
第4部 ワークロード戦略
第12回〜第14回 [完了]
第5部 セキュリティ基礎
第15回〜第16回 [完了]
第6部 パッケージ管理 + HTTPS公開(第17〜19回)
第17回 Helm v4 基礎 + fanclub-api Helm Chart 作成 [完了]
★ 第18回 Gateway API + Traefik + cert-manager で HTTPS 公開 ← 今ここ(第6部第2回)
第19回 総合演習(第1巻完走)
第18回を終えると、以下を習得した状態になります。
- 従来の Ingress リソースが抱える課題(アノテーション依存でベンダー実装ごとに記法が異なり移植性がない・HTTP(S) 以外のプロトコルに非対応・単一リソースに全部を詰め込むため担当ロールの分離ができない)を整理し、Gateway API がそれをどう解決するか(標準 API・role-based の 4 リソース分割)を説明できる。ingress-nginx が 2026 年 3 月に EOL を迎えた背景を理解する
- Gateway API の 4 リソース(GatewayClass / Gateway / HTTPRoute / ReferenceGrant)の役割分担を理解し、それぞれを作成できる。GatewayClass はクラスタスコープ、Gateway / HTTPRoute / ReferenceGrant は Namespace スコープであることを区別できる
- Traefik を Gateway API の実装プロバイダ(GatewayClass controller)として Helm でインストールし、Traefik が提供する GatewayClass と Gateway を紐付けられる
- cert-manager の Issuer / ClusterIssuer / Certificate を理解し、外部疎通のない kind 環境向けに自己署名 CA を構成して、TLS 証明書を Secret に自動発行し、Gateway の HTTPS リスナーに適用できる
- CKAD 試験 D5 の Gateway API(2026 NEW 項目)に対応できる。HTTPRoute によるホスト名・パスベースのルーティングと、Gateway の TLS リスナーでの TLS 終端を実装でき、旧来の Ingress リソースとの対応関係も説明できる
模擬アプリ進捗(第18回):本回の演習①では Gateway API v1.4.0 の CRD をクラスタに導入し、cert-manager v1.20.0 を Helm でインストールします。演習②では Traefik chart を Helm でインストールして Gateway API の実装プロバイダにし、ep17 で作った fanclub-api Helm Chart を新 Namespace fanclub にデプロイ、自己署名 ClusterIssuer と Gateway リソースを作成します。
演習③では HTTPRoute を作成してルーティングを構成し、cert-manager が発行した TLS 証明書で HTTPS アクセスを kubectl port-forward + curl で確認します。これにより fanclub-api が https://fanclub.local で HTTPS 公開された状態になり、第1巻の HTTPS 公開マイルストーンを達成します。
設計上の注意:本回の演習は、default ns ではなく新しい 3 つの Namespace(cert-manager / traefik / fanclub)に対して実施します。default ns には ep16 で適用した default-deny-all を含む 6 個の NetworkPolicy と、生 kubectl apply で管理された既存 fanclub-api が稼働しています。
本回で扱う Gateway API・Traefik・cert-manager はクラスタ全体に関わるコンポーネントで、fanclub-api は ep17 の Helm Chart を使って新 ns fanclub にデプロイします。default ns の既存リソース・NetworkPolicy には一切触れません。また、本回の HTTPS アクセス確認は kubectl port-forward + curl --resolve 方式で行います。
理由は H2「kind 環境での外部公開」で詳しく説明しますが、本シリーズの kind クラスタは 80/443 をホストに公開する extraPortMappings を持たないため、ブラウザから直接 https://fanclub.local にアクセスできません。ブラウザアクセスの手順は、読者の環境向けの案内として同じ H2 で扱います。
Ingress の限界 — なぜ Gateway API が生まれたか
演習に入る前に、本回の中心テーマである Gateway API が「なぜ生まれたのか」を整理します。Kubernetes でアプリケーションを外部に HTTP / HTTPS 公開する仕組みとして、長年使われてきたのが Ingress というリソースです。Gateway API は、その Ingress が抱えていた課題を解決するために設計された後継 API です。
Ingress の何が課題だったかを具体的に理解しておくと、Gateway API がなぜ 4 つのリソースに分かれているのか、なぜ「ベンダー中立」を強調するのかが腑に落ちます。
Ingress とは — 外部 HTTP(S) トラフィックの入口
第8回で学んだ Service には、ClusterIP / NodePort / LoadBalancer という 3 つのタイプがあります。このうちクラスタの外からアクセスできるのは NodePort と LoadBalancer ですが、NodePort は 30000〜32767 という高いポート番号を使う制約があり、LoadBalancer はクラウド環境ごとに外部ロードバランサーを 1 つずつ作るためコストがかさみます。
複数のアプリを 1 つの IP・1 つのポート(80 / 443)で公開し、ホスト名やパスで振り分けたい、というのが Web アプリの一般的な要件です。これを担うのが Ingress でした。
Ingress リソース(networking.k8s.io/v1 グループの Ingress)は、「shop.example.com へのリクエストは shop Service へ」「example.com/api へのリクエストは api Service へ」といったルーティング規則を 1 つの YAML に書きます。ただし、Ingress リソースを書いただけではトラフィックは流れません。
実際にルーティングを実行する Ingress Controller(nginx・Traefik・HAProxy など、リバースプロキシの実体)をクラスタに別途インストールし、Ingress Controller が Ingress リソースを読んで自分の設定に反映する、という仕組みです。Ingress リソースが「ルーティングの宣言」、Ingress Controller が「ルーティングの実行者」という分担になります。
Ingress の 3 つの限界
Ingress は長く使われてきましたが、Web アプリの要件が複雑になるにつれて、3 つの限界が表面化しました。
| 限界 | 具体的な困りごと |
|---|---|
| アノテーション依存で移植性がない | Ingress リソースの仕様(spec)は HTTP ルーティングの基本しか定義していない。タイムアウト・リライト・認証・カナリアといった高度な機能は、すべてアノテーション(metadata.annotations)で指定する。そのアノテーションのキー名は Ingress Controller ごとに異なる(nginx は nginx.ingress.kubernetes.io/...、Traefik は traefik.ingress.kubernetes.io/...)。Ingress Controller を nginx から Traefik に変えると、アノテーションを全部書き換える必要がある。Kubernetes 標準のはずの Ingress リソースが、実態としてベンダー依存になっていた |
| HTTP(S) 以外のプロトコルに非対応 | Ingress リソースが扱えるのは HTTP / HTTPS のみ。TCP・UDP・gRPC を Ingress Controller 経由で公開するには、リソースの spec に書く場所がなく、ConfigMap やアノテーションでの独自拡張に頼るしかなかった。gRPC を多用するマイクロサービスや、TCP のデータベース接続を外部公開したいケースで Ingress は力不足だった |
| 担当ロールの分離ができない | Ingress リソースは「どのポートを開くか」「どの証明書を使うか」「どのホスト名をどの Service へ振り分けるか」を 1 つのリソースに全部詰め込む。実際の組織では、ポートや証明書はインフラ管理者の責任、ホスト名やパスの振り分けはアプリ開発者の責任、と分かれている。1 リソースに全部入っていると、アプリ開発者がルーティングを変えるたびにインフラ管理者の領域(ポート・証明書)にも触れることになり、権限分離(RBAC)が設計しにくかった |
これらの限界のうち、特に「アノテーション依存」は深刻でした。Ingress は Kubernetes の標準 API のはずなのに、実際に使える機能の大半が Ingress Controller のアノテーションに依存していたため、「Kubernetes 標準の書き方」というものが事実上存在しませんでした。クラスタを別の環境に移したり Ingress Controller を入れ替えたりすると、ルーティング定義をほぼ書き直すことになっていました。
ingress-nginx の 2026 年 3 月 EOL
Ingress Controller の実装として最も広く使われてきたのが ingress-nginx(Kubernetes プロジェクトが管理する nginx ベースの Ingress Controller)です。多くの入門記事や既存クラスタが ingress-nginx を採用してきました。しかし、この ingress-nginx は2026 年 3 月に EOL(End of Life・サポート終了)を迎えました。背景には、長年にわたるメンテナの不足があります。
ingress-nginx は Kubernetes コミュニティの中でも保守の負担が大きいプロジェクトで、十分な数のメンテナを確保できず、新規機能追加もセキュリティ修正も継続が困難になりました。
EOL を迎えたソフトウェアは、新しい脆弱性が見つかっても修正パッチが提供されません。本番クラスタのトラフィック入口にあたる Ingress Controller を EOL のまま使い続けるのは、セキュリティ上のリスクが大きい状態です。このため、Kubernetes コミュニティ全体が「Ingress + ingress-nginx」から「Gateway API + Gateway API 対応の実装」への移行を進めています。
本シリーズが Ingress リソースではなく Gateway API を、ingress-nginx ではなく Traefik を採用するのは、この業界の移行を反映したものです。
補足:ingress-nginx が EOL になったのは「ingress-nginx という特定の実装」であり、「Ingress リソースそのもの」が即座に廃止されたわけではありません。Ingress リソース(networking.k8s.io/v1)は今も Kubernetes に存在し、ingress-nginx 以外の Ingress Controller(Traefik も Ingress リソースを処理できます)も動きます。
ただし、Kubernetes コミュニティの開発リソースは Gateway API に集中しており、新しい機能は Gateway API 側に実装されています。今からトラフィック制御を学ぶなら Gateway API を主軸にするのが妥当です。本回も Gateway API を中心に扱いますが、CKAD 試験対策の観点では旧来の Ingress リソースの基本も押さえておく必要があり、その点は H2「CKAD 試験頻出パターン」で補足します。
Gateway API — ベンダー中立の標準 API
Gateway API は、Ingress の 3 つの限界を解決するために、Kubernetes の SIG-Network(ネットワーク領域の特別作業グループ)が設計した新しいトラフィック制御 API です。API グループは gateway.networking.k8s.io です。Gateway API の設計思想を一言でいうと「ベンダー中立・role-based・拡張可能」です。
- ベンダー中立:タイムアウト・リトライ・ヘッダ操作といった機能を、アノテーションではなくリソースの spec フィールドとして標準化した。どの実装(Traefik / Istio / Cilium など)でも同じ YAML が通じる。実装を入れ替えてもルーティング定義を書き直さなくてよい
- role-based:Ingress が 1 リソースに詰め込んでいた責務を、4 つのリソースに分割した。ポート・証明書はインフラ管理者向けの Gateway に、ホスト名・パスの振り分けはアプリ開発者向けの HTTPRoute に分けることで、RBAC での権限分離がしやすくなった
- 拡張可能:HTTP 用の HTTPRoute だけでなく、TCP 用の TCPRoute、gRPC 用の GRPCRoute なども定義されている。HTTP(S) 以外のプロトコルにも対応できる設計になっている

Gateway API は Kubernetes 本体(kube-apiserver)に最初から組み込まれているリソースではなく、CRD(Custom Resource Definition)として後からインストールする追加コンポーネントです。第15回で CRD の概念を学びましたが、Gateway API はその CRD の代表例です。本回の演習①では、この Gateway API の CRD をクラスタに導入するところから始めます。
次の H2 では、Gateway API が分割した 4 つのリソースを 1 つずつ見ていきます。
Gateway API の 4 リソース — GatewayClass / Gateway / HTTPRoute / ReferenceGrant
Gateway API は、Ingress が 1 つのリソースに詰め込んでいた責務を 4 つのリソースに分割しました。この 4 リソースの役割分担を正確に押さえておくと、本回の演習で「いま何のリソースを作っているのか」が常に明確になります。CKAD 試験でも、どのリソースがどのスコープ(クラスタ / Namespace)かは頻出の論点です。
4 リソースの役割分担
| リソース | 役割 | 担当ロール | スコープ |
|---|---|---|---|
| GatewayClass | どの実装プロバイダ(Traefik など)で Gateway を実現するかを指定する。Ingress でいう「Ingress Controller の種類」に相当 | インフラ管理者 | クラスタ |
| Gateway | リスナー(どのポートで・どのプロトコルで・どの TLS 証明書で待ち受けるか)を定義する。トラフィックの入口そのもの | クラスタ運用者 | Namespace |
| HTTPRoute | ホスト名・パスベースのルーティング規則を定義する。「fanclub.local の / へのリクエストを fanclub-api Service へ」といった振り分けを書く | アプリ開発者 | Namespace |
| ReferenceGrant | Namespace をまたいだリソース参照を許可する。HTTPRoute が別 Namespace の Service を参照するときなどに必要 | 各 Namespace 管理者 | Namespace |
Ingress(1 リソース)と Gateway API(4 リソース)の対比を図にすると、次のようになります。

GatewayClass — 実装プロバイダの指定(クラスタスコープ)
GatewayClass は、「この Gateway を、どの実装で実現するか」を指定するクラスタスコープのリソースです。Kubernetes における StorageClass(第9回で学んだ、どのストレージプロビジョナを使うかを指定するリソース)と同じ考え方です。StorageClass が「ストレージの種類」を表すように、GatewayClass は「Gateway の実装の種類」を表します。
GatewayClass の重要な性質は、多くの場合、実装プロバイダ(本回なら Traefik)が自動で作成するという点です。本回の演習②で Traefik を Helm でインストールすると、Traefik 自身が traefik という名前の GatewayClass をクラスタに登録します。利用者が手で GatewayClass を書くことは通常ありません。
GatewayClass の YAML 構造を参考までに示すと、次のようになります(実際には Traefik が自動生成するため、これを手で kubectl apply する必要はありません)。
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: traefik
spec:
controllerName: traefik.io/gateway-controller
spec.controllerName が「この GatewayClass を処理するコントローラ」を指す文字列です。Traefik の Gateway コントローラは traefik.io/gateway-controller という名前を名乗り、その名前を持つ GatewayClass だけを Traefik が処理します。GatewayClass はクラスタスコープのため、kubectl get gatewayclass に -n(Namespace 指定)は付けません。
Gateway — リスナーの定義(Namespace スコープ)
Gateway は、トラフィックの入口そのものを定義する Namespace スコープのリソースです。Gateway には リスナー(listener)を 1 つ以上定義します。リスナーは「どのポートで・どのプロトコルで・どのホスト名のために待ち受けるか」を表し、HTTPS リスナーの場合は「どの TLS 証明書を使うか」も指定します。Gateway の YAML 構造は次のとおりです。
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: fanclub-gateway
namespace: fanclub
spec:
gatewayClassName: traefik
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: fanclub.local
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: fanclub-tls
allowedRoutes:
namespaces:
from: Same
主要なフィールドを整理します。
| フィールド | 意味 |
|---|---|
spec.gatewayClassName | この Gateway をどの GatewayClass(実装)で実現するか。traefik を指定すると Traefik が処理する |
listeners[].protocol | リスナーのプロトコル。HTTP / HTTPS / TLS / TCP など |
listeners[].port | 待ち受けるポート番号。HTTPS なら通常 443 |
listeners[].hostname | このリスナーが受け付けるホスト名。fanclub.local 宛のリクエストだけを処理する |
listeners[].tls.mode | Terminate は Gateway で TLS を終端する(復号する)モード。Passthrough は復号せずバックエンドへ素通しするモード |
listeners[].tls.certificateRefs | TLS 終端に使う証明書が入った Secret の参照。本回は cert-manager が fanclub-tls Secret に証明書を入れる |
listeners[].allowedRoutes | このリスナーに HTTPRoute をアタッチできる範囲。from: Same は同じ Namespace の HTTPRoute だけを許可する |
Gateway が正しく実装プロバイダに認識されると、kubectl get gateway の PROGRAMMED 列が True になります。PROGRAMMED=True は「Gateway の設定が実装(Traefik)に反映され、トラフィックを受け付ける準備ができた」という意味です。
HTTPRoute — ルーティング規則(Namespace スコープ)
HTTPRoute は、「どのホスト名・どのパスへのリクエストを、どの Service へ振り分けるか」というルーティング規則を定義する Namespace スコープのリソースです。Ingress の spec.rules に書いていた振り分けが、Gateway API では HTTPRoute に独立しました。HTTPRoute の YAML 構造の詳細は H2「HTTPRoute による URL ルーティングと TLS 終端」で扱いますが、ここでは骨格だけ示します。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: fanclub-route
namespace: fanclub
spec:
parentRefs:
- name: fanclub-gateway
hostnames:
- fanclub.local
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: fanclub-api-fanclub-api
port: 80
spec.parentRefs が「この HTTPRoute をどの Gateway にアタッチするか」、spec.hostnames が「どのホスト名を処理するか」、spec.rules が「パスのマッチ条件(matches)と振り分け先(backendRefs)」です。HTTPRoute が Gateway に正しくアタッチされると、kubectl describe httproute の status に Accepted=True が出ます。
ReferenceGrant — Namespace 越え参照の許可(Namespace スコープ)
ReferenceGrant は、Namespace をまたいだリソース参照を明示的に許可するためのリソースです。Gateway API では、セキュリティ上の理由から、あるリソースが別の Namespace のリソースを参照することをデフォルトで禁止しています。
たとえば app Namespace の HTTPRoute が、backend Namespace の Service を backendRefs で指したい場合、参照される側(backend Namespace)に ReferenceGrant を置いて「app Namespace の HTTPRoute からの参照を許可する」と宣言しないと、ルーティングが成立しません。
ReferenceGrant の YAML 構造は次のとおりです。これは「app Namespace の HTTPRoute が、この Namespace(ReferenceGrant が置かれた Namespace)の Service を参照することを許可する」という宣言です。
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-httproute-from-app
namespace: backend
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: app
to:
- group: ""
kind: Service
spec.from が「誰からの参照を」、spec.to が「何への参照を」許可するかです。重要なのは、ReferenceGrant は参照される側の Namespace に置くという点です。「私の Namespace のリソースを、あの Namespace から参照してよい」と、リソースの持ち主が許可を出す形になっています。これにより、知らないうちに他 Namespace から自分のリソースを参照される、という事態を防げます。
本回の演習では、HTTPRoute・Gateway・fanclub-api Service をすべて同じ fanclub Namespace に置くため、ReferenceGrant は不要です。HTTPRoute が backendRefs で参照する Service が同じ Namespace にあれば、Namespace 越えの参照が発生しないため、ReferenceGrant は要りません。
ただし、ReferenceGrant は本番運用と CKAD 試験の両方で頻出の概念のため、構造を理解しておく必要があります。「HTTPRoute と振り分け先 Service が別 Namespace なら ReferenceGrant が必要」というルールは、H2「現場ヒヤリハット」で実際の事故例とともに扱います。
cert-manager の役割 — Issuer / Certificate による TLS 自動化
fanclub-api を HTTPS で公開するには、TLS 証明書が必要です。HTTPS は HTTP の通信を TLS で暗号化したもので、サーバー側が「正しいサーバーである」ことを証明する TLS 証明書を提示します。この TLS 証明書を Kubernetes 上で発行・管理・更新する仕組みが cert-manager です。本 H2 では cert-manager の役割と 3 つの主要リソースを整理します。
なぜ証明書管理に専用ツールが要るか
TLS 証明書には有効期限があります。手作業で証明書を発行して Secret に登録する運用だと、有効期限が切れる前に人間が気づいて更新しなければならず、更新漏れによる HTTPS 障害が起こりがちです。cert-manager は、証明書の発行・Secret への格納・期限が近づいたときの自動更新を、すべて Kubernetes のリソースとして宣言的に管理します。
「こういう証明書がほしい」という宣言(Certificate リソース)を置くと、cert-manager が証明書を発行して Secret に入れ、期限が近づいたら自動で更新します。
cert-manager も Gateway API と同じく、Kubernetes 本体には組み込まれていない追加コンポーネントです。CRD(Issuer / ClusterIssuer / Certificate など)とコントローラ(証明書を発行する Pod)をクラスタにインストールして使います。本回の演習①では、cert-manager を Helm でインストールします。
cert-manager の 3 つの主要リソース
| リソース | 役割 | スコープ |
|---|---|---|
| Issuer | 証明書の発行元。「どこから(自己署名 CA / Let’s Encrypt など)証明書を発行するか」を定義する | Namespace |
| ClusterIssuer | Issuer のクラスタスコープ版。クラスタ全体のどの Namespace からも使える発行元 | クラスタ |
| Certificate | 「こういう証明書がほしい」という宣言。どの Issuer から・どのホスト名(DNS 名)の証明書を・どの Secret に格納するかを書く。cert-manager がこれを見て証明書を発行する | Namespace |
Issuer と ClusterIssuer の違いは、StorageClass と PVC の関係に似た「スコープの違い」です。Issuer は特定の Namespace 内でしか使えませんが、ClusterIssuer はクラスタ全体で共有できます。本回は ClusterIssuer を使います。理由は、自己署名 CA を一度作れば、複数の Namespace の Certificate から同じ CA を参照したいケースに対応しやすいためです。
本回は自己署名 CA を使う — Let’s Encrypt ではない理由
cert-manager がもっともよく使われるのは、Let’s Encrypt という無料の認証局から、ブラウザに信頼される正式な証明書を自動取得する用途です。本番のインターネット公開サービスでは、cert-manager + Let’s Encrypt の組み合わせが定番です。しかし、本回の演習では Let’s Encrypt は使えません。理由は次のとおりです。
- Let’s Encrypt は、証明書を発行する前に「このドメインを本当にあなたが管理しているか」を検証する。検証には、対象ドメインがインターネットから到達可能で、DNS が正しく引けることが前提になる
- 本シリーズの kind 環境は
fanclub.localという、インターネット上に存在しないローカル専用のホスト名を使う。.localドメインはインターネットの DNS に登録されておらず、Let’s Encrypt の検証が通らない - kind 環境は alma-proxy の whitelist 方式の閉じたネットワークで、外部から到達できない。Let’s Encrypt の検証サーバーが kind 環境にアクセスする経路がない
そこで本回は、自己署名 CA(Certificate Authority・認証局)を cert-manager の中に作り、その自己署名 CA から fanclub-api 用の証明書を発行します。「自己署名」とは、外部の認証局に頼らず、自分で認証局を立てて自分で署名する方式です。自己署名 CA で発行した証明書は、ブラウザのデフォルトの信頼リストには入っていないため、ブラウザは「この証明書は信頼できない」という警告を出します。
これは演習環境では想定どおりの挙動で、後述する curl -k や、ブラウザでの警告承認で回避できます。本番のインターネット公開では Let’s Encrypt の正式証明書を使う、と理解しておいてください。
自己署名 CA の 2 段構成
本回の自己署名 CA は、cert-manager の標準的な作法に従って 2 段構成にします。いきなり「自己署名 ClusterIssuer」で fanclub-api の証明書を作ることもできますが、それだと「CA がない、ばらばらの自己署名証明書」になり、CA で署名された証明書の構造を学べません。本番の認証局も「CA があり、CA が個々の証明書に署名する」構造のため、その構造を再現します。
ステップ1: selfsigned-issuer(自己署名 ClusterIssuer)
└─ 自分で自分に署名する発行元
ステップ2: ca-certificate(CA 証明書の Certificate)
└─ selfsigned-issuer に発行を依頼
└─ 発行された CA 証明書を Secret に格納
(isCA: true = この証明書は CA として使える)
ステップ3: ca-issuer(CA ベースの ClusterIssuer)
└─ ステップ2 で作った CA 証明書を使う発行元
ステップ4: fanclub-api の TLS 証明書
└─ ca-issuer に発行を依頼
└─ CA で署名された証明書が fanclub-tls Secret に入る
流れを言葉でまとめると、(1) まず「自分で自分に署名する」自己署名 ClusterIssuer を作る、(2) その自己署名 ClusterIssuer で「CA として使える証明書」を 1 つ発行する、(3) その CA 証明書を発行元とする ClusterIssuer をもう 1 つ作る、(4) その CA ベースの ClusterIssuer で fanclub-api の証明書を発行する、という 4 段階です。fanclub-api の証明書は、最終的に CA で署名された証明書になります。
本回の演習②でステップ 1〜3 を、演習③でステップ 4 を実施します。
cert-manager と Gateway の連携 — Secret を介した受け渡し
cert-manager と Gateway は直接つながっているわけではなく、Secret を介して連携します。流れは次のとおりです。
- cert-manager が Certificate リソースを見て、TLS 証明書を発行し、
kubernetes.io/tlsタイプの Secret(本回ならfanclub-tls)に格納する - Gateway の HTTPS リスナーの
tls.certificateRefsが、その Secret(fanclub-tls)を参照する - Traefik(Gateway の実装)が Secret から証明書を読み込み、HTTPS リスナーの TLS 終端に使う
本回では、cert-manager の Gateway API 連携機能を使って、この一連の流れを自動化します。Gateway リソースに cert-manager.io/cluster-issuer というアノテーションを付けておくと、cert-manager が Gateway の HTTPS リスナーを検知し、必要な Certificate を自動で作成します。
利用者は Gateway にアノテーションを 1 行付けるだけで、Certificate の作成・証明書発行・Secret 格納がすべて自動で進みます。この連携の詳細は演習②・③で実機を見ながら確認します。
やってみよう①: Gateway API CRD + cert-manager インストール
演習①では、Gateway API v1.4.0 の CRD をクラスタに導入し、cert-manager v1.20.0 を Helm でインストールします。所要時間の目安は 20 分です。この演習で、本回の HTTPS 公開に必要な土台となる 2 つのコンポーネント(Gateway API の CRD と cert-manager)を整えます。
Step 1: Gateway API v1.4.0 の CRD を適用する
Gateway API は CRD として配布されています。Gateway API には「standard チャンネル」と「experimental チャンネル」の 2 種類のインストール用 YAML があります。standard チャンネルは GatewayClass / Gateway / HTTPRoute / ReferenceGrant など安定版のリソースを含み、本回はこちらを使います。実行コマンド:
$ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml
期待される実行結果:
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created
customresourcedefinition.apiextensions.k8s.io/grpcroutes.gateway.networking.k8s.io created
Gateway API の CRD 群がクラスタに登録されました。もし github.com へのアクセスが alma-proxy の whitelist に登録されておらず kubectl apply が失敗する場合は、別環境で standard-install.yaml を取得して k8s-ops に転送し、kubectl apply -f standard-install.yaml とローカルファイル指定で適用してください。CRD の適用はオブジェクトの登録だけで、外部疎通は不要です。
Step 2: Gateway API の CRD を確認する
登録された CRD を kubectl get crds で確認します。実行コマンド:
$ kubectl get crds | grep gateway.networking.k8s.io
期待される実行結果:
gatewayclasses.gateway.networking.k8s.io 2026-05-16T10:30:12Z
gateways.gateway.networking.k8s.io 2026-05-16T10:30:12Z
grpcroutes.gateway.networking.k8s.io 2026-05-16T10:30:13Z
httproutes.gateway.networking.k8s.io 2026-05-16T10:30:13Z
referencegrants.gateway.networking.k8s.io 2026-05-16T10:30:13Z
本回で使う GatewayClass / Gateway / HTTPRoute / ReferenceGrant の 4 リソースに対応する CRD が登録されています。CRD が登録されると、その CRD で定義されたリソース(kubectl get gateway など)を扱えるようになります。この段階ではまだリソースの実体は何もありません。実体は演習②以降で作ります。
Step 3: jetstack リポジトリを追加する
cert-manager は jetstack という組織が管理しています。cert-manager の Helm Chart は jetstack の公式リポジトリで配布されています。第17回で学んだ helm repo add でリポジトリを登録します。実行コマンド:
$ helm repo add jetstack https://charts.jetstack.io
$ helm repo update
期待される実行結果:
"jetstack" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "jetstack" chart repository
Update Complete. Happy Helming!
helm repo add で jetstack リポジトリを登録し、helm repo update でリポジトリのインデックス(どの Chart のどのバージョンがあるか)を最新化しました。インストールしたいバージョンが利用できるかは、helm search repo jetstack/cert-manager --versions で確認できます。本回は v1.20.0 を使います。
Step 4: cert-manager v1.20.0 を Helm でインストールする
cert-manager を新 Namespace cert-manager にインストールします。実行コマンド:
$ helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.20.0 \
--set crds.enabled=true \
--set config.apiVersion=controller.config.cert-manager.io/v1alpha1 \
--set config.kind=ControllerConfiguration \
--set config.enableGatewayAPI=true
期待される実行結果:
NAME: cert-manager
LAST DEPLOYED: Sat May 16 10:34:02 2026
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.20.0 has been deployed successfully!
コマンドのオプションを整理します。--namespace cert-manager と --create-namespace で cert-manager Namespace を作ってそこにインストールします。--version v1.20.0 で Chart のバージョンを固定します(バージョンを固定しないと、その時点の最新版が入ってしまい、教材と挙動が変わる可能性があります)。
--set crds.enabled=true は、cert-manager の CRD(Issuer / ClusterIssuer / Certificate など)を Chart のインストールと同時に適用する指定です。cert-manager は本体のコントローラと CRD で構成されており、CRD がないと Certificate リソースなどを扱えないため、この指定で CRD も一緒に入れます。
cert-manager の Gateway API 連携は明示的な有効化が必要:末尾 3 行の --set config.* が重要です。cert-manager は本回後半で、Gateway リソースに付けた cert-manager.io/cluster-issuer アノテーションを検知して TLS 証明書を自動発行します。
しかしこの「Gateway API 連携」機能は cert-manager v1.20 のデフォルトでは無効で、config.enableGatewayAPI=true を明示しないと動きません。config.apiVersion と config.kind は、その設定を渡すための ControllerConfiguration の宣言です。この 3 行を付け忘れると、後で Gateway を作っても証明書が発行されず HTTPS が機能しないため、インストール時に必ず含めます。
Step 5: cert-manager の 3 Pod が Running になることを確認する
cert-manager は 3 つの Pod(cert-manager / cert-manager-cainjector / cert-manager-webhook)で構成されます。これらが Running になることを確認します。実行コマンド:
$ kubectl get pods -n cert-manager
期待される実行結果:
NAME READY STATUS RESTARTS AGE
cert-manager-6d8f9c7b54-x2k7p 1/1 Running 0 75s
cert-manager-cainjector-7c5b9d4f88-q9wm2 1/1 Running 0 75s
cert-manager-webhook-5f7c8b9d6c-h4nzt 1/1 Running 0 75s
3 つの Pod の役割を整理します。cert-manager 本体が証明書の発行や更新を担うコントローラ、cert-manager-cainjector は CA 証明書を必要なリソースに注入する補助、cert-manager-webhook は Issuer / Certificate の YAML を API Server に保存する前に妥当性を検証する Admission Webhook(第15回で概念を学んだ Admission Controller の一種)です。
3 つすべてが 1/1 Running になっていれば、cert-manager は機能する状態です。STATUS が ContainerCreating や Pending のままなら、kubectl get pods -n cert-manager -w で監視しながら数十秒待ってください。
Step 6: cert-manager の CRD を確認する
--set crds.enabled=true で適用された cert-manager の CRD を確認します。実行コマンド:
$ kubectl get crds | grep cert-manager.io
期待される実行結果:
certificaterequests.cert-manager.io 2026-05-16T10:34:01Z
certificates.cert-manager.io 2026-05-16T10:34:01Z
challenges.acme.cert-manager.io 2026-05-16T10:34:01Z
clusterissuers.cert-manager.io 2026-05-16T10:34:01Z
issuers.cert-manager.io 2026-05-16T10:34:01Z
orders.acme.cert-manager.io 2026-05-16T10:34:01Z
本回で使う certificates.cert-manager.io(Certificate)・clusterissuers.cert-manager.io(ClusterIssuer)・issuers.cert-manager.io(Issuer)に対応する CRD が登録されています。challenges.acme / orders.acme は Let’s Encrypt などの ACME プロトコルで使う CRD で、本回の自己署名 CA では使いません。
演習①のまとめ
演習①で、本回の HTTPS 公開に必要な土台が整いました。Gateway API v1.4.0 の CRD(GatewayClass / Gateway / HTTPRoute / ReferenceGrant)がクラスタに登録され、cert-manager v1.20.0 が cert-manager Namespace で稼働しています。
次の演習②では、Gateway API の実装プロバイダとして Traefik をインストールし、fanclub-api を fanclub Namespace にデプロイして、自己署名 ClusterIssuer と Gateway を作ります。
Traefik を Gateway API 実装プロバイダとして使う
演習②に入る前に、本回が Gateway API の実装として採用する Traefik を整理します。Gateway API は「ルーティングの宣言の仕方」を標準化しただけで、実際にトラフィックを処理するソフトウェアは別途必要です。それが実装プロバイダ(GatewayClass controller)であり、本回では Traefik を使います。
Traefik とは — クラウドネイティブなリバースプロキシ
Traefik は、Go 言語で書かれたリバースプロキシ / ロードバランサです。Kubernetes との統合を前提に設計されており、Ingress Controller としても Gateway API の実装プロバイダとしても動きます。本シリーズが採用するのは Traefik v3.6.x で、Helm Chart は v39.x 系を使います。Traefik を Gateway API の実装として使うと、次の役割を担います。
- GatewayClass の登録:Traefik をインストールすると、Traefik が
traefikという名前の GatewayClass をクラスタに自動登録する - Gateway / HTTPRoute の監視:Traefik のコントローラが、
gatewayClassName: traefikを指定した Gateway と、それにアタッチされた HTTPRoute を監視する - トラフィックの実処理:Traefik の Pod が実際のリバースプロキシとして動き、外部からのリクエストを受けて HTTPRoute の規則に従いバックエンド Service へ転送する。HTTPS リスナーなら TLS 終端もここで行う
Traefik をインストールすると、traefik Namespace に Traefik の Pod(リバースプロキシの実体)と Service が作られます。この Traefik Service が、外部からのトラフィックの入口になります。本来この Service を LoadBalancer や NodePort で外部公開しますが、kind 環境での制約があるため、本回は kubectl port-forward でこの Service にアクセスします(詳細は H2「kind 環境での外部公開」)。
Gateway API プロバイダの有効化
Traefik はデフォルトでは Ingress Controller として動き、Gateway API の処理は有効になっていません。Traefik を Gateway API の実装プロバイダとして使うには、Helm の values で providers.kubernetesGateway.enabled=true を指定します。この指定により、Traefik のコントローラが Gateway / HTTPRoute を監視するようになり、GatewayClass traefik も登録されます。
もう 1 つ、本回で重要な values が gateway.enabled=false です。Traefik chart は、デフォルトで Traefik 自身の Namespace に Gateway リソースを 1 つ自動生成する設定になっています。本回は fanclub-api のための Gateway を fanclub Namespace に自分で作るため、Traefik chart が自動生成する Gateway は不要です。
gateway.enabled=false でこの自動生成を止め、Gateway は演習②で明示的に作成します。Traefik chart の values キーは chart のバージョンによって変わりうるため、実機では helm show values traefik/traefik --version <version> でキー名を確認するのが確実です。
やってみよう②: Traefik インストール + fanclub-api デプロイ + Gateway 作成
演習②では、Traefik を Gateway API の実装プロバイダとしてインストールし、ep17 の fanclub-api Helm Chart を fanclub Namespace にデプロイ、自己署名 ClusterIssuer と CA を構成し、Gateway リソースを作成します。所要時間の目安は 30 分です。
Step 1: traefik リポジトリを追加する
Traefik の Helm Chart は Traefik Labs の公式リポジトリで配布されています。実行コマンド:
$ helm repo add traefik https://traefik.github.io/charts
$ helm repo update
期待される実行結果:
"traefik" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "traefik" chart repository
...Successfully got an update from the "jetstack" chart repository
Update Complete. Happy Helming!
traefik リポジトリが追加されました。利用可能な Chart バージョンは helm search repo traefik/traefik --versions で確認できます。本回は v39 系の Chart(Traefik v3.6.x)を使います。
Step 2: Traefik を Gateway API プロバイダとしてインストールする
Traefik を traefik Namespace にインストールします。Gateway API プロバイダを有効化し、chart の Gateway 自動生成は無効化します。実行コマンド:
$ helm install traefik traefik/traefik \
--namespace traefik \
--create-namespace \
--version 39.0.9 \
--set providers.kubernetesGateway.enabled=true \
--set providers.kubernetesIngress.enabled=false \
--set gateway.enabled=false \
--set ports.websecure.port=443 \
--set securityContext.capabilities.add[0]=NET_BIND_SERVICE
期待される実行結果:
NAME: traefik
LAST DEPLOYED: Sat May 16 10:42:18 2026
NAMESPACE: traefik
STATUS: deployed
REVISION: 1
TEST SUITE: None
各オプションの意味を整理します。--version 39.0.9 は Traefik chart のバージョン固定(Traefik v3.6.15 同梱)。--set providers.kubernetesGateway.enabled=true で Traefik を Gateway API の実装プロバイダにします。--set providers.kubernetesIngress.enabled=false は旧来の Ingress プロバイダを無効化(本回は Gateway API のみ使う)。
--set gateway.enabled=false は Traefik chart 同梱の Gateway 自動生成を止める指定で、本回は自前で Gateway を作るため無効化が必須です。これを忘れると、後で entryPoint のポートを変更したときに chart 同梱 Gateway のテンプレートがエラー(port ... is not declared in ports)になります。
Gateway リスナーのポートと Traefik の entryPoint を一致させる:末尾の --set ports.websecure.port=443 と --set securityContext.capabilities.add[0]=NET_BIND_SERVICE が本回のはまりどころです。Traefik chart のデフォルトでは HTTPS 用 entryPoint websecure はコンテナ内ポート 8443 で待ち受けます。
一方、本回後半で作る Gateway リソースは標準の HTTPS ポート 443 をリスナーに指定します。Gateway API の実装プロバイダ(Traefik)は「Gateway リスナーのポート」と「自身の entryPoint のポート」が一致しないと、その Gateway を受け付けず Cannot find entryPoint for Gateway: no matching entryPoint for port 443 というエラーで Gateway が PROGRAMMED=False のままになります。
そのため ports.websecure.port=443 で Traefik の entryPoint を 443 に合わせます。さらに 1024 未満のポートは非 root プロセスでは bind できないため、Traefik コンテナに NET_BIND_SERVICE capability を付与して 443 を bind 可能にします。
Gateway API は標準 API ですが、このように実装プロバイダ固有の設定(entryPoint とリスナーのポート整合)が必要になる点は実務で押さえておく論点です。
Step 3: Traefik Pod と GatewayClass を確認する
Traefik の Pod が Running になり、GatewayClass traefik が登録されたことを確認します。実行コマンド:
$ kubectl get pods -n traefik
$ kubectl get gatewayclass
期待される実行結果:
NAME READY STATUS RESTARTS AGE
traefik-7d9c8f6b5d-2tq4w 1/1 Running 0 90s
NAME CONTROLLER ACCEPTED AGE
traefik traefik.io/gateway-controller True 90s
Traefik の Pod が 1/1 Running になり、GatewayClass traefik が ACCEPTED=True で登録されています。CONTROLLER 列の traefik.io/gateway-controller は、この GatewayClass を Traefik が処理することを示します。ACCEPTED=True は「この GatewayClass を担当するコントローラ(Traefik)がクラスタに存在し、GatewayClass を受け入れた」という意味です。
GatewayClass はクラスタスコープのため、kubectl get gatewayclass に -n は付きません。
Step 4: fanclub-api を fanclub Namespace にデプロイする
ep17 で作成した fanclub-api の Helm Chart(~/fanclub-charts/fanclub-api/)を使って、fanclub-api を新 Namespace fanclub にデプロイします。実行コマンド:
$ helm install fanclub-api ~/fanclub-charts/fanclub-api \
--namespace fanclub \
--create-namespace
期待される実行結果:
NAME: fanclub-api
LAST DEPLOYED: Sat May 16 10:46:33 2026
NAMESPACE: fanclub
STATUS: deployed
REVISION: 1
TEST SUITE: None
fanclub-api の Pod と Service が fanclub Namespace に作られます。デプロイを確認します。実行コマンド:
$ kubectl get pods,svc -n fanclub
期待される実行結果:
NAME READY STATUS RESTARTS AGE
pod/fanclub-api-fanclub-api-7f8655444b-fm7kb 1/1 Running 0 43s
pod/fanclub-api-fanclub-api-7f8655444b-zr2bw 1/1 Running 0 43s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/fanclub-api-fanclub-api ClusterIP 10.96.26.152 80/TCP 43s
fanclub-api の Pod が 1/1 Running で、fanclub-api-fanclub-api という ClusterIP Service がポート 80 で公開されています。リソース名が fanclub-api-fanclub-api になっているのは、ep17 の Chart のヘルパーテンプレートがリソース名を「<Release 名>-<Chart 名>」で組み立てるためで、Release 名 fanclub-api と Chart 名 fanclub-api が連結されています。
Pod が 2 つあるのは、ep17 の演習③で values.yaml の replicaCount を 2 にしたまま Chart を保存しているためです。ep17 で確認したとおり、この Chart は Init Container(wait-for-db)を initContainer.enabled: false で無効化しているため、PostgreSQL DB がなくても Backend が起動します。本回の主眼は HTTPS 公開のため、DB なしの構成で進めます。
この fanclub-api-fanclub-api Service(ポート 80)が、後で HTTPRoute の振り分け先になります。
Step 5: 自己署名 ClusterIssuer を作成する
cert-manager の自己署名 CA 構成を組み立てます。まず、「自分で自分に署名する」自己署名 ClusterIssuer を作ります。次の YAML を ~/fanclub-tls/selfsigned-issuer.yaml として保存します。
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
spec.selfSigned: {} は「自己署名で証明書を発行する」という指定です。中括弧だけで、追加のパラメータはありません。ClusterIssuer はクラスタスコープのため namespace は書きません。実行コマンド:
$ mkdir -p ~/fanclub-tls
$ kubectl apply -f ~/fanclub-tls/selfsigned-issuer.yaml
$ kubectl get clusterissuer selfsigned-issuer
期待される実行結果:
clusterissuer.cert-manager.io/selfsigned-issuer created
NAME READY AGE
selfsigned-issuer True 8s
READY=True は、この ClusterIssuer が証明書を発行できる状態であることを示します。自己署名 ClusterIssuer は外部とのやり取りがないため、すぐ True になります。
Step 6: CA 証明書を発行する
次に、自己署名 ClusterIssuer を使って「CA として使える証明書」を 1 つ発行します。これが本回の自己署名 CA の本体になります。次の YAML を ~/fanclub-tls/ca-certificate.yaml として保存します。
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: fanclub-ca
namespace: cert-manager
spec:
isCA: true
commonName: fanclub-local-ca
secretName: fanclub-ca-secret
duration: 8760h
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
group: cert-manager.io
主要フィールドを整理します。isCA: true が「この証明書は CA(認証局)として、他の証明書に署名できる」という指定です。secretName: fanclub-ca-secret で、発行された CA 証明書と秘密鍵を格納する Secret を指定します。duration: 8760h は有効期間 1 年(8760 時間)です。issuerRef で発行元を指定し、ここでは Step 5 で作った自己署名 ClusterIssuer を指します。
この Certificate は cert-manager の Pod が動いている cert-manager Namespace に置きます。実行コマンド:
$ kubectl apply -f ~/fanclub-tls/ca-certificate.yaml
$ kubectl get certificate fanclub-ca -n cert-manager
$ kubectl get secret fanclub-ca-secret -n cert-manager
期待される実行結果:
certificate.cert-manager.io/fanclub-ca created
NAME READY SECRET AGE
fanclub-ca True fanclub-ca-secret 12s
NAME TYPE DATA AGE
fanclub-ca-secret kubernetes.io/tls 3 12s
Certificate fanclub-ca が READY=True になり、fanclub-ca-secret という kubernetes.io/tls タイプの Secret が作られました。この Secret には CA 証明書(tls.crt)・CA の秘密鍵(tls.key)・CA 証明書(ca.crt)の 3 つのデータが入っています。次の Step で、この CA を発行元とする ClusterIssuer を作ります。
Step 7: CA ベースの ClusterIssuer を作成する
Step 6 で発行した CA 証明書を発行元とする ClusterIssuer を作ります。これが fanclub-api の証明書を発行する発行元になります。次の YAML を ~/fanclub-tls/ca-issuer.yaml として保存します。
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: ca-issuer
spec:
ca:
secretName: fanclub-ca-secret
spec.ca.secretName に、Step 6 で作った CA の Secret(fanclub-ca-secret)を指定します。これで「この CA で証明書に署名する」発行元になります。なお、CA タイプの ClusterIssuer は、参照する Secret が cert-manager の Pod が動く Namespace(cert-manager)にあることを前提とします。Step 6 で CA 証明書を cert-manager Namespace に作ったのはこのためです。実行コマンド:
$ kubectl apply -f ~/fanclub-tls/ca-issuer.yaml
$ kubectl get clusterissuer
期待される実行結果:
clusterissuer.cert-manager.io/ca-issuer created
NAME READY AGE
ca-issuer True 10s
selfsigned-issuer True 9m
2 つの ClusterIssuer が READY=True で揃いました。selfsigned-issuer は CA 証明書を発行するための発行元、ca-issuer はその CA で fanclub-api の証明書を発行するための発行元です。これで自己署名 CA の構成(H2「cert-manager の役割」のステップ 1〜3)が完成しました。fanclub-api の証明書(ステップ 4)は演習③で発行します。
Step 8: Gateway リソースを作成する
fanclub-api への HTTPS トラフィックの入口となる Gateway を作成します。次の YAML を ~/fanclub-tls/fanclub-gateway.yaml として保存します。
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: fanclub-gateway
namespace: fanclub
annotations:
cert-manager.io/cluster-issuer: ca-issuer
spec:
gatewayClassName: traefik
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: fanclub.local
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: fanclub-tls
allowedRoutes:
namespaces:
from: Same
この Gateway のポイントを整理します。
annotationsのcert-manager.io/cluster-issuer: ca-issuerが、cert-manager の Gateway API 連携を起動するアノテーション。cert-manager がこの Gateway を検知し、HTTPS リスナーのために必要な Certificate をca-issuerから自動発行するspec.gatewayClassName: traefikで、この Gateway を Traefik が処理するlistenersに HTTPS リスナーを 1 つ定義。ポート 443・ホスト名fanclub.local・TLS モードTerminate(Gateway で TLS を終端する)tls.certificateRefsでfanclub-tlsという Secret を指定。この Secret はまだ存在しないが、cert-manager がca-issuerから証明書を発行してこの Secret を作るallowedRoutes.namespaces.from: Sameで、同じfanclubNamespace の HTTPRoute だけがこの Gateway にアタッチできる
実行コマンド:
$ kubectl apply -f ~/fanclub-tls/fanclub-gateway.yaml
$ kubectl get gateway -n fanclub
期待される実行結果:
gateway.networking.k8s.io/fanclub-gateway created
NAME CLASS ADDRESS PROGRAMMED AGE
fanclub-gateway traefik True 25s
PROGRAMMED=True は、Gateway の設定が Traefik に反映され、トラフィックを受け付ける準備ができたことを示します。CLASS 列が traefik で、この Gateway を Traefik が担当していることが分かります。ADDRESS 列が空なのは、Traefik の Service が LoadBalancer タイプで、kind には外部 LoadBalancer がないため EXTERNAL-IP が割り当てられないためです(H2「kind 環境での外部公開」で詳述)。
ADDRESS が空でも、本回は kubectl port-forward でアクセスするため演習に支障はありません。なお、適用直後は PROGRAMMED が空欄のことがあり、Traefik が Gateway を処理して True になるまで数秒〜十数秒かかります。
Step 9: cert-manager が Gateway を検知したことを確認する
Gateway に cert-manager.io/cluster-issuer アノテーションを付けたため、cert-manager が Gateway を検知し、Certificate を自動で作り始めているはずです。fanclub Namespace の Certificate を確認します。実行コマンド:
$ kubectl get certificate -n fanclub
期待される実行結果:
NAME READY SECRET AGE
fanclub-tls True fanclub-tls 30s
cert-manager が fanclub-tls という Certificate を自動で作成し、READY=True になっています。この Certificate は、Gateway の tls.certificateRefs で指定した Secret 名(fanclub-tls)と同じ名前で作られ、ca-issuer(CA ベースの ClusterIssuer)から発行されています。
利用者は Gateway にアノテーションを 1 行付けただけで、Certificate の作成・証明書発行・Secret 格納が自動で進みました。次の演習③で、この証明書を使って HTTPS アクセスを確認します。
演習②のまとめ
演習②で、HTTPS 公開の構成がほぼ揃いました。Traefik が Gateway API の実装プロバイダとして稼働し、fanclub-api が fanclub Namespace にデプロイされ、自己署名 CA の ClusterIssuer 2 つ(selfsigned-issuer / ca-issuer)が用意され、Gateway fanclub-gateway が PROGRAMMED=True になりました。
cert-manager は Gateway のアノテーションを検知して fanclub-tls 証明書を自動発行しました。残るは、Gateway とバックエンドをつなぐ HTTPRoute です。次の H2 で HTTPRoute の構造を整理し、演習③で作成します。
HTTPRoute による URL ルーティングと TLS 終端
Gateway がトラフィックの入口を定義するのに対し、HTTPRoute は「入口に来たリクエストを、どのバックエンドに振り分けるか」を定義します。本 H2 では、演習③で作る HTTPRoute の構造を整理し、Gateway での TLS 終端とバックエンドへの転送の流れを確認します。
HTTPRoute の YAML 構造
HTTPRoute の主要フィールドは parentRefs / hostnames / rules の 3 つです。rules はさらに matches(マッチ条件)と backendRefs(振り分け先)に分かれます。本回で使う HTTPRoute の全量を示します。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: fanclub-route
namespace: fanclub
spec:
parentRefs:
- name: fanclub-gateway
sectionName: https
hostnames:
- fanclub.local
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: fanclub-api-fanclub-api
port: 80
各フィールドの意味を整理します。
| フィールド | 意味 |
|---|---|
parentRefs[].name | この HTTPRoute をアタッチする Gateway の名前。fanclub-gateway にアタッチする |
parentRefs[].sectionName | Gateway のどのリスナーにアタッチするか。https リスナーを指定(Gateway の listeners[].name と一致させる) |
hostnames | この HTTPRoute が処理するホスト名。fanclub.local 宛のリクエストだけを扱う |
rules[].matches[].path.type | パスのマッチ方式。PathPrefix は前方一致、Exact は完全一致 |
rules[].matches[].path.value | マッチさせるパス。/ は全パスにマッチする(前方一致のため) |
rules[].backendRefs[].name | 振り分け先の Service 名。fanclub-api-fanclub-api Service へ送る |
rules[].backendRefs[].port | 振り分け先 Service のポート。fanclub-api Service は 80 |
本回の HTTPRoute は「fanclub.local 宛のすべてのパス(/ 前方一致)を fanclub-api Service のポート 80 へ送る」というシンプルな 1 ルールです。実務では、/api は api Service、/static は static Service、というように複数の matches + backendRefs でパスごとに振り分けます。複数ルールの書き方は H2「CKAD 試験頻出パターン」で扱います。
TLS 終端の流れ — Gateway で復号、バックエンドへは平文
本回の HTTPS リクエストが、どこで暗号化され、どこで復号されるかを整理します。Gateway の HTTPS リスナーは tls.mode: Terminate を指定しており、これは「Gateway で TLS を終端する」という意味です。終端とは「暗号化された通信をそこで復号する」ことです。

流れを言葉でまとめます。クライアントは https://fanclub.local へ TLS 暗号化されたリクエストを送ります。Traefik(Gateway の実装)は fanclub-tls Secret の証明書を提示し、TLS ハンドシェイクを完了させ、暗号化された通信をそこで復号します(TLS 終端)。復号後は平文の HTTP リクエストになり、Traefik は HTTPRoute の規則に従って fanclub-api Service へ平文 HTTP で転送します。
つまり、クライアントと Gateway の間は HTTPS(暗号化)、Gateway とバックエンドの間は HTTP(平文)です。バックエンドの fanclub-api 自身は HTTP しか話さなくてよく、HTTPS 化は Gateway / Traefik が担当します。この役割分担により、個々のアプリに TLS 証明書を持たせる必要がなくなります。
Gateway とバックエンドの間が平文 HTTP であることは、kind クラスタ内部の通信なので本回の演習では問題になりません。ただし本番で「Gateway とバックエンドの間も暗号化したい」要件がある場合は、Pod 間通信を暗号化する mTLS(相互 TLS)を別途導入します。Pod 間の mTLS は第3巻(CKS)で Cilium を使って扱う内容で、本巻の範囲外です。本巻内のリンクはありません。
cert-manager が証明書を供給する流れ
TLS 終端には証明書が必要です。その証明書を cert-manager が供給する流れを、演習②・③の操作と対応づけて整理します。
- (1) Gateway に
cert-manager.io/cluster-issuer: ca-issuerアノテーションを付ける(演習② Step 8) - (2) cert-manager が Gateway を検知し、HTTPS リスナーの
certificateRefsに書かれた Secret 名(fanclub-tls)と、リスナーのhostname(fanclub.local)から、Certificatefanclub-tlsを自動生成する - (3) cert-manager が
ca-issuer(CA ベースの ClusterIssuer)に証明書発行を依頼し、CA で署名された証明書をfanclub-tlsSecret に格納する(演習② Step 9 で確認) - (4) Traefik が Gateway の
certificateRefsを見てfanclub-tlsSecret から証明書を読み込み、HTTPS リスナーの TLS 終端に使う

この一連の流れにより、利用者は Gateway にアノテーションを 1 行付けるだけで、証明書の発行・格納・利用が自動で完結します。証明書の有効期限が近づくと cert-manager が自動で更新し、Traefik が新しい証明書を読み直すため、証明書の期限切れによる HTTPS 障害も防げます。
やってみよう③: HTTPRoute 作成 + HTTPS アクセス確認
演習③では、HTTPRoute を作成してルーティングを完成させ、cert-manager が発行した証明書で HTTPS アクセスを確認します。所要時間の目安は 25 分です。この演習を終えると、fanclub-api が https://fanclub.local で HTTPS 公開された状態になります。
Step 1: HTTPRoute を作成する
Gateway とバックエンド fanclub-api をつなぐ HTTPRoute を作成します。次の YAML を ~/fanclub-tls/fanclub-route.yaml として保存します。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: fanclub-route
namespace: fanclub
spec:
parentRefs:
- name: fanclub-gateway
sectionName: https
hostnames:
- fanclub.local
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: fanclub-api-fanclub-api
port: 80
この HTTPRoute は、Gateway fanclub-gateway の https リスナーにアタッチし、fanclub.local 宛の全パスを fanclub-api-fanclub-api Service のポート 80 へ振り分けます。HTTPRoute・Gateway・fanclub-api-fanclub-api Service はすべて同じ fanclub Namespace にあるため、ReferenceGrant は不要です。実行コマンド:
$ kubectl apply -f ~/fanclub-tls/fanclub-route.yaml
期待される実行結果:
httproute.gateway.networking.k8s.io/fanclub-route created
Step 2: HTTPRoute が Accepted になることを確認する
HTTPRoute が Gateway に正しくアタッチされたかを確認します。HTTPRoute の状態は kubectl describe の status で確認します。実行コマンド:
$ kubectl get httproute -n fanclub
$ kubectl describe httproute fanclub-route -n fanclub
期待される実行結果(describe の status 部分の抜粋):
NAME HOSTNAMES AGE
fanclub-route ["fanclub.local"] 20s
Status:
Parents:
Conditions:
Type: Accepted
Status: True
Reason: Accepted
Type: ResolvedRefs
Status: True
Reason: ResolvedRefs
Controller Name: traefik.io/gateway-controller
status の Accepted=True は「HTTPRoute が Gateway に受け入れられた」、ResolvedRefs=True は「backendRefs で指定した fanclub-api Service への参照が解決できた」という意味です。両方が True なら、ルーティングが成立しています。
もし ResolvedRefs が False なら、backend の Service 名・ポート・Namespace のいずれかが間違っているか、別 Namespace 参照で ReferenceGrant が足りていない可能性があります(H2「現場ヒヤリハット」①で扱います)。
Step 3: Certificate と TLS Secret を確認する
HTTPS 通信に必要な証明書が揃っているかを確認します。演習② Step 9 で fanclub-tls Certificate が READY=True になっていることは確認済みですが、対応する TLS Secret も確認します。実行コマンド:
$ kubectl get certificate -n fanclub
$ kubectl get secret fanclub-tls -n fanclub
期待される実行結果:
NAME READY SECRET AGE
fanclub-tls True fanclub-tls 6m
NAME TYPE DATA AGE
fanclub-tls kubernetes.io/tls 2 6m
fanclub-tls Secret が kubernetes.io/tls タイプで作られ、DATA が 2(証明書 tls.crt と秘密鍵 tls.key)になっています。Gateway の HTTPS リスナーは、この Secret の証明書を使って TLS 終端を行います。Certificate が READY=True・Secret が存在、の 2 点が揃って初めて HTTPS が機能します。
Certificate が READY=False のまま HTTPS アクセスすると接続が失敗します(H2「現場ヒヤリハット」②で扱います)。
Step 4: Traefik Service を port-forward する
HTTPS アクセスを確認するため、Traefik の Service を k8s-ops の localhost にフォワードします。本シリーズの kind クラスタは 80/443 をホストに公開していないため、kubectl port-forward で Traefik Service の 443 ポートを localhost の 8443 に転送します(理由の詳細は次の H2 で扱います)。port-forward は起動したままになるコマンドのため、バックグラウンドで起動します。実行コマンド:
$ kubectl port-forward -n traefik svc/traefik 8443:443 > /tmp/pf.log 2>&1 &
$ sleep 2
$ cat /tmp/pf.log
期待される実行結果:
Forwarding from 127.0.0.1:8443 -> 8443
Forwarding from [::1]:8443 -> 8443
kubectl port-forward -n traefik svc/traefik 8443:443 は、「traefik Namespace の traefik Service のポート 443 を、k8s-ops の 127.0.0.1:8443 に転送する」という指定です。8443:443 の左がローカル(k8s-ops)のポート、右が Service 側のポートです。
HTTPS の標準ポート 443 をそのまま使わず 8443 にしているのは、443 のような 1024 未満のポートを使うには root 権限が必要なため、一般ユーザーで動かせる 8443 を選んでいます。末尾の & でバックグラウンド実行、> /tmp/pf.log 2>&1 で出力をログファイルに退避しています。Forwarding from 127.0.0.1:8443 が出れば、転送が始まっています。
Step 5: curl で HTTPS アクセスする
port-forward した経路を使って、curl で fanclub-api の /health/live エンドポイントに HTTPS アクセスします。実行コマンド:
$ curl --noproxy "*" --resolve fanclub.local:8443:127.0.0.1 https://fanclub.local:8443/health/live -k
期待される実行結果:
{"status":"UP","checks":[{"name":"fanclub-api-live","status":"UP","data":{}}]}
fanclub-api の Liveness ヘルスチェックの応答が HTTPS 経由で返ってきました。ここで使った curl の 3 つのオプションを整理します。
--resolve <host>:<port>:<ip>:指定した host:port へのアクセスを、指定した IP に向ける。ここではfanclub.local:8443へのアクセスを127.0.0.1(port-forward 先)に向けている。DNS にfanclub.localを登録しなくても名前解決をその場で上書きできる、/etc/hostsの一時版にあたる仕組み。ホスト名をfanclub.localとして送るのは、Gateway のリスナーがhostname: fanclub.local宛のリクエストだけを処理するため-k(--insecure):サーバー証明書の検証をスキップする。本回は自己署名 CA で発行した証明書を使っており、curlのデフォルトの信頼リストにこの CA は入っていない。検証すると「証明書が信頼できない」エラーになるため、-kで検証を省く。本番の正式証明書なら-kは不要--noproxy "*":すべての宛先でプロキシを経由しない指定。k8s-ops VM は alma-proxy 経由でインターネットに接続する構成のためHTTPS_PROXY環境変数が設定されており、これを付けないとlocalhost宛の通信までプロキシに送られcurl: (56) CONNECT tunnel failed, response 403エラーになる。port-forward 先のローカル接続ではプロキシを明示的にバイパスする
もしレスポンスが返らない場合、port-forward が止まっていないか(cat /tmp/pf.log でエラーがないか)、HTTPRoute が Accepted=True か、Certificate が READY=True か、を順に確認してください。
Step 6: 証明書の内容を確認する
提示されている証明書が、本回で作った自己署名 CA で署名されていることを確認します。curl -v の verbose 出力で、TLS ハンドシェイク時の証明書情報を見ます。実行コマンド:
$ curl --noproxy "*" --resolve fanclub.local:8443:127.0.0.1 https://fanclub.local:8443/health/live -k -v 2>&1 | grep -E "subject:|issuer:|SSL connection"
期待される実行結果:
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256 / X25519MLKEM768 / RSASSA-PSS
* subject:
* issuer: CN=fanclub-local-ca
出力を読み解きます。issuer: CN=fanclub-local-ca は「この証明書を署名した発行元」で、演習② Step 6 で作った CA 証明書の commonName: fanclub-local-ca と一致しています。つまり、fanclub-api の証明書は、本回で作った自己署名 CA によって署名されている、ということが確認できます。SSL connection using TLSv1.3 から、TLS 1.3 で暗号化された接続が成立していることも分かります。
subject 行は cert-manager が Gateway 連携で発行した証明書では CN を空にして SAN(Subject Alternative Name)に fanclub.local を格納する形式のため、subject: の後ろは空で表示されます。ホスト名の検証は CN ではなく SAN で行うのが現代の TLS の標準です。
Step 7: port-forward を停止する
確認が終わったら、バックグラウンドで動かしている port-forward を停止します。実行コマンド:
$ jobs
$ kill %1
$ jobs
期待される実行結果:
[1]+ Running kubectl port-forward -n traefik svc/traefik 8443:443 > /tmp/pf.log 2>&1 &
[1]+ Terminated kubectl port-forward -n traefik svc/traefik 8443:443 > /tmp/pf.log 2>&1
jobs でバックグラウンドジョブの一覧を確認し、kill %1 でジョブ番号 1 の port-forward を停止します。最後の jobs で何も表示されなければ、port-forward は停止しています。port-forward を止め忘れると、k8s-ops 上にプロセスが残り続けます。確認作業が終わったら忘れず停止してください。
Step 8: 読者環境向けのブラウザアクセス案内
ここまでの確認は CUI の curl で行いました。読者の環境でブラウザから https://fanclub.local にアクセスしたい場合は、次の準備が必要です。
- kind を 80/443 公開で作り直す:kind を自前で構築する場合、
kind create cluster --configでextraPortMappingsを指定し、コンテナの 80/443 をホストの 80/443 に公開する。ただし本シリーズの実機クラスタは 80/443 公開なしで作成済みのため、後付けはできない(詳細は次の H2) - Vagrant の port forwarding:読者が Vagrant + VirtualBox で環境を組んでいる場合、
Vagrantfileのconfig.vm.network "forwarded_port"で VM の 443 をホスト PC のポートに転送する /etc/hostsにfanclub.localを追記:ブラウザを動かすホスト PC の/etc/hosts(Windows ならC:\Windows\System32\drivers\etc\hosts)に127.0.0.1 fanclub.localを追記し、fanclub.localをローカルに名前解決させる- ブラウザの証明書警告を承認:自己署名 CA の証明書はブラウザの信頼リストにないため、「この接続は安全ではありません」という警告が出る。演習環境では想定どおりの挙動で、警告画面で「詳細設定」から続行を選ぶ。本番では Let’s Encrypt の正式証明書を使えば警告は出ない
本シリーズの実機検証環境(k8s-ops VM)は CUI サーバーでブラウザがなく、kind クラスタも 80/443 公開なしで作成済みのため、実機検証は curl で代替しています。読者が自前環境でブラウザアクセスまで試したい場合は、上記の準備を整えてください。
演習③のまとめ
演習③で、fanclub-api の HTTPS 公開が完成しました。HTTPRoute fanclub-route が Accepted=True で Gateway にアタッチされ、cert-manager が自己署名 CA で発行した証明書による TLS 終端で、https://fanclub.local への HTTPS アクセスが curl で確認できました。証明書の発行元(issuer)が本回で作った CA であることも確認しました。
次の H2 では、本演習で port-forward 方式を採った背景である kind 環境の外部公開制約を整理します。
kind 環境での外部公開 — port-forward と extraPortMappings
本回の演習③で、HTTPS アクセス確認に kubectl port-forward を使いました。本来 Web アプリは 80/443 で外部公開し、ブラウザから直接アクセスできるのが自然です。なぜ本回が port-forward 方式を採ったのか、kind 環境の外部公開の仕組みを整理します。
kind は 80/443 をデフォルトで公開しない
第5回で学んだとおり、kind(Kubernetes in Docker)は Kubernetes のノードを Docker コンテナとして動かします。Docker コンテナの中で動いているサービスにホスト(k8s-ops VM)からアクセスするには、コンテナのポートをホストのポートに公開(ポートマッピング)する必要があります。kind は、API Server のポート 6443 はデフォルトで公開しますが、HTTP / HTTPS の 80 / 443 はデフォルトで公開しません。
本シリーズの kind クラスタも、第5回で標準オプションで作成したため、port mapping は 6443 のみです。
このため、Traefik の Service を 80/443 で待ち受けるように構成しても、k8s-ops のホスト側から https://localhost でアクセスする経路がありません。コンテナ内部の 443 とホストの 443 がつながっていないためです。これが、本回の演習で kubectl port-forward を使った理由です。
extraPortMappings — kind 構築時に 80/443 を公開する
kind で 80/443 をホストに公開したい場合は、クラスタ作成時の設定ファイルで extraPortMappings を指定します。次のような設定ファイル(kind-config.yaml)で kind create cluster --config kind-config.yaml を実行すると、コンテナの 80/443 がホストの 80/443 に公開されます。
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
重要なのは、extraPortMappings はクラスタ作成時にしか指定できず、既存クラスタには後付けできないという点です。後から 80/443 を公開したい場合は、クラスタを一度削除して、extraPortMappings 付きで作り直す必要があります。
本シリーズが port-forward を選んだ理由
本シリーズの kind クラスタは第5回で 80/443 公開なしで作成し、第6回から第18回まで fanclub-api を積み上げてきました。ep18 のために 80/443 を公開しようとすると、クラスタを削除して作り直す必要があります。しかしクラスタを作り直すと、ep1〜ep17 で構築したリソース(fanclub-api・NetworkPolicy・Helm Chart の Release など)がすべて消えます。本回 1 回のブラウザアクセスのために、これまでの積み上げを失うのは割に合いません。
そこで本回は、クラスタの作り直しを避け、kubectl port-forward で Traefik Service を k8s-ops の localhost にフォワードし、curl --resolve で HTTPS アクセスを確認する方式を採りました。port-forward は extraPortMappings と違って、既存クラスタに対していつでも実行でき、確認が終わったら止められます。
なお、次の ep19(第1巻完走の総合演習)では、クリーンな kind クラスタを一から作り直して ep1〜ep18 の全機構を再構築するため、その際に extraPortMappings 付きでクラスタを作る選択肢が出てきます。本回と ep19 で役割を分け、本回は既存クラスタを壊さず port-forward で完結させます。
本番クラウド環境での外部公開
kind は開発・学習用の軽量 Kubernetes のため、外部公開に extraPortMappings や port-forward という工夫が要りました。本番のクラウド環境(EKS / GKE / AKS など)では、外部公開はもっと素直です。Traefik の Service を type: LoadBalancer にすると、クラウドが外部ロードバランサを自動でプロビジョニングし、グローバル IP が割り当てられます。
そのグローバル IP に DNS のレコード(fanclub.example.com → グローバル IP)を向ければ、ブラウザから 80/443 で直接アクセスできます。
つまり、本回で学んだ Gateway / HTTPRoute / cert-manager の構成自体はそのまま本番で通用し、変わるのは「Traefik Service をどう外部公開するか」の部分だけです。kind では port-forward、本番では LoadBalancer Service + 外部 LB、という違いです。
本番クラウドでの LoadBalancer Service や MetalLB(オンプレミスで LoadBalancer を実現する仕組み)は第2巻(CKA)で扱う内容で、本巻の範囲外です。本巻内のリンクはありません。
CKAD 試験頻出パターン — Gateway API の速攻設計
CKAD 試験は実技形式で、制限時間内に kubectl 操作や YAML 作成で課題を解きます。Gateway API は 2026 年から CKAD ドメイン D5「Services and Networking」の出題範囲に入った新しいリソースです。本 H2 では、Gateway API 関連の設問でよく問われるパターンと、速攻で解くためのコマンド・落とし穴を整理します。
頻出パターン 4 選
| # | 設問のキーワード | 解答の方針 |
|---|---|---|
| 1 | 「アプリを HTTP で外部公開せよ」 | HTTP リスナー(protocol: HTTP / port: 80)を持つ Gateway を作り、HTTPRoute でホスト名・パスをバックエンド Service に振り分ける |
| 2 | 「HTTPS で公開し TLS を終端せよ」 | Gateway に HTTPS リスナー(protocol: HTTPS / port: 443 / tls.mode: Terminate)を作り、tls.certificateRefs で証明書を入れた Secret を指定する |
| 3 | 「パスごとに別の Service へ振り分けよ」 | HTTPRoute の rules を複数書き、それぞれ matches.path と backendRefs を指定する |
| 4 | 「別 Namespace の Service を参照させよ」 | 参照される側の Namespace に ReferenceGrant を作り、HTTPRoute からの Service 参照を許可する |
パターン 3 の「パスごとに別 Service へ振り分け」の HTTPRoute の書き方を示します。rules を複数並べる構造です。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: multi-path-route
namespace: fanclub
spec:
parentRefs:
- name: fanclub-gateway
hostnames:
- fanclub.local
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: fanclub-api-fanclub-api
port: 80
- matches:
- path:
type: PathPrefix
value: /static
backendRefs:
- name: fanclub-static
port: 80
/api へのリクエストは fanclub-api Service、/static へのリクエストは fanclub-static Service へ振り分けます。rules をリストで複数書けば、パスごとの振り分けが実現できます。
速攻コマンド早見表
| やりたいこと | コマンド |
|---|---|
| GatewayClass の確認 | kubectl get gatewayclass |
| Gateway の状態確認 | kubectl get gateway -n <ns>(PROGRAMMED 列を見る) |
| Gateway の詳細・エラー確認 | kubectl describe gateway <name> -n <ns> |
| HTTPRoute の状態確認 | kubectl describe httproute <name> -n <ns>(Accepted / ResolvedRefs を見る) |
| Certificate の状態確認 | kubectl get certificate -n <ns>(READY 列を見る) |
| Certificate が Ready にならない原因調査 | kubectl describe certificate <name> -n <ns> |
| Issuer / ClusterIssuer の確認 | kubectl get clusterissuer / kubectl get issuer -n <ns> |
Gateway API には kubectl create での生成サブコマンドが用意されていません。Deployment なら kubectl create deployment で雛形を作れますが、Gateway / HTTPRoute は YAML を手書きする必要があります。本回で示した YAML 構造を覚えておき、試験では空の YAML から apiVersion / kind / spec を埋められるようにしておいてください。
落とし穴 3 選
- ReferenceGrant の置き忘れ:HTTPRoute と
backendRefsの Service が別 Namespace のとき、ReferenceGrant がないとルーティングされない。HTTPRoute の status のResolvedRefsがFalseになる。ReferenceGrant は参照される側(Service のある Namespace)に置く - Certificate が Ready にならない:Issuer / ClusterIssuer の名前のタイプミス、参照する Secret の不在などで Certificate が
READY=Falseのまま。kubectl describe certificateで原因を確認する。Certificate が Ready でないと HTTPS が機能しない - GatewayClass 名のミスマッチ:Gateway の
spec.gatewayClassNameが、実際に存在する GatewayClass 名(本回ならtraefik)と一致していないと、どの実装も Gateway を処理せずPROGRAMMEDがTrueにならない。kubectl get gatewayclassで正確な名前を確認してから Gateway に書く
旧来の Ingress リソースも押さえておく
本回は Gateway API を中心に扱いましたが、CKAD 試験対策としては、旧来の Ingress リソース(networking.k8s.io/v1 グループの Ingress)の基本も押さえておく必要があります。Gateway API は 2026 年から CKAD の出題範囲に入った新しいリソースですが、全試験回で Gateway API が安定して出題されるとは限らず、従来の Ingress リソースが出題される可能性も残っています。
受験時には、Gateway API と Ingress リソースの両方に備えておくのが安全です。Ingress リソースの基本構造を参考までに示します。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fanclub-ingress
namespace: fanclub
spec:
ingressClassName: traefik
rules:
- host: fanclub.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fanclub-api
port:
number: 80
Gateway API と対応づけると、Ingress の ingressClassName が GatewayClass、rules[].host が HTTPRoute の hostnames、rules[].http.paths が HTTPRoute の rules.matches.path + backendRefs に相当します。
Ingress は 1 リソースに全部が入る一方、Gateway API は Gateway と HTTPRoute に分かれる、という違いを意識すると、両者を行き来しやすくなります。試験で「Ingress を作成せよ」と明示されたら Ingress リソースを、「Gateway API で公開せよ」とあれば Gateway + HTTPRoute を使う、と設問の指定に従って解答してください。
現場ヒヤリハット 2 件
本回で扱った Gateway API・cert-manager が、本番運用中にどのような事故を引き起こすかを 2 件取り上げます。それぞれ「背景・事故・根本原因・解決策・本番ガードレール」の 5 点セットで整理します。どちらも Gateway API + cert-manager の運用で頻発する事故で、本回で学んだ HTTPRoute の status 確認と Certificate の状態確認が、そのまま事故防止策になります。
ヒヤリハット ①: ReferenceGrant 忘れで Namespace 越え HTTPRoute が機能しない
背景:あるチームが、Gateway API でマイクロサービスを外部公開していた。組織のルールで、Gateway はインフラチームが管理する gateway-system Namespace に置き、各アプリの HTTPRoute と Service はアプリごとの Namespace(shop / billing など)に置く、という Namespace 設計になっていた。
新しいマイクロサービス billing を公開するため、開発者は billing Namespace に HTTPRoute と Service を作り、HTTPRoute の parentRefs で gateway-system Namespace の Gateway を指定した。
事故:HTTPRoute と Service を作ったのに、billing サービスへの HTTP リクエストが 404 になり、トラフィックがまったく流れなかった。開発者は HTTPRoute の YAML を何度も見直したが、ホスト名・パス・backendRefs の Service 名はすべて正しかった。Gateway も PROGRAMMED=True で正常に見えた。原因が分からないまま時間が過ぎ、公開予定が遅れた。
根本原因:HTTPRoute が gateway-system Namespace の Gateway を parentRefs で参照していたが、その Gateway の allowedRoutes が from: Same(同じ Namespace の HTTPRoute だけ許可)になっていた。billing Namespace の HTTPRoute は gateway-system の Gateway にアタッチできず、HTTPRoute は Gateway に受け入れられていなかった。
kubectl describe httproute を見ると status の Accepted が False で、理由に「許可されていない Namespace からのアタッチ」が記録されていた。開発者は HTTPRoute の YAML 本体だけを見て、status を確認していなかった。
さらに、Gateway 側の allowedRoutes を from: All または from: Selector に変えて他 Namespace の HTTPRoute を許可する設定変更が必要だったことにも気づいていなかった。
解決策:インフラチームが Gateway の allowedRoutes を見直し、許可するアプリ Namespace を明示する from: Selector(ラベルセレクタで許可 Namespace を指定)に変更した。あわせて、HTTPRoute の backendRefs が同じ billing Namespace の Service を指していたため、この事例では ReferenceGrant は不要だったが、もし backendRefs の Service が別 Namespace だった場合は、Service のある Namespace に ReferenceGrant を置く必要があることもチームで確認した。
設定変更後、kubectl describe httproute で Accepted=True を確認してから公開した。
本番ガードレール:
- HTTPRoute を作成したら、必ず
kubectl describe httproute <name> -n <ns>で status のAcceptedとResolvedRefsを確認する。YAML 本体が正しくても status がFalseならトラフィックは流れない - HTTPRoute が別 Namespace の Gateway を
parentRefsで指す場合、その Gateway のallowedRoutesが自分の Namespace を許可しているかを確認する。許可されていなければAccepted=Falseになる - HTTPRoute の
backendRefsが別 Namespace の Service を指す場合、Service のある Namespace に ReferenceGrant を置く。ReferenceGrant がないとResolvedRefs=Falseになる - Namespace をまたぐ Gateway API 構成は、「誰の Gateway を・誰の HTTPRoute がアタッチし・誰の Service を参照するか」を設計時に図にして、必要な
allowedRoutesと ReferenceGrant を洗い出す - 本番公開前のチェックリストに「Gateway の
PROGRAMMED=True」「HTTPRoute のAccepted=TrueかつResolvedRefs=True」を必須項目として入れる
ヒヤリハット ②: cert-manager の Certificate が Ready にならず HTTPS が落ちる
背景:あるサービスが、cert-manager + Let’s Encrypt で HTTPS 証明書を自動取得していた。証明書は 90 日で期限が切れるため、cert-manager が期限の 30 日前に自動更新する設定だった。半年ほど問題なく動いていたため、チームは証明書まわりを「動いているもの」として日常的に確認しなくなっていた。
事故:ある朝、サービスへの HTTPS アクセスが突然失敗するようになった。ブラウザは「証明書の有効期限が切れています」というエラーを表示した。証明書の有効期限が切れていたため、cert-manager に自動更新を任せていたはずなのに更新されていなかった。HTTPS でアクセスできず、サービスが実質的に停止した。
根本原因:数週間前にネットワーク構成が変更され、Let’s Encrypt がドメイン所有を検証するための HTTP01 チャレンジ(特定のパスへの一時的なアクセス)が外部から到達できなくなっていた。cert-manager は期限の 30 日前に証明書の更新を試みたが、チャレンジが失敗し、Certificate は READY=False のまま更新できずにいた。
kubectl describe certificate や kubectl get challenge を見れば、チャレンジ失敗のエラーが記録されていた。しかしチームは Certificate の状態を監視しておらず、READY=False に誰も気づかないまま、古い証明書の期限切れの日を迎えてしまった。cert-manager は「更新を試みて失敗していた」のであって、何もしていなかったわけではないが、その失敗が誰にも届いていなかった。
解決策:緊急対応として、kubectl describe certificate でチャレンジ失敗の原因(外部からの到達不可)を特定し、ネットワーク構成を修正してチャレンジが通る状態に戻した。その後 kubectl delete で Certificate を作り直して即時に再発行させ、READY=True を確認してサービスを復旧させた。
事後改善として、Certificate の READY 状態と証明書の残り有効日数を監視対象にし、READY=False が一定時間続いたら、あるいは有効期限が 21 日を切ったらアラートを上げる仕組みを入れた。
本番ガードレール:
- cert-manager の Certificate の
READY状態を監視対象にする。READY=Falseが続いたらアラートを上げ、期限切れの前に人間が気づけるようにする - 証明書の残り有効日数を監視し、自動更新が想定どおり進んでいるか(期限が一定日数を切る前に更新されているか)を確認する。「自動更新だから安心」と監視を外さない
- Certificate が Ready にならないときは
kubectl describe certificateでイベントを確認する。Let’s Encrypt 利用時はkubectl get challenge/kubectl get orderでチャレンジの成否も確認する。なお、本回のように Gateway のアノテーションから証明書を自動発行させる構成では、そもそも Certificate リソース自体が作られない(kubectl get certificateが空)ことがある。その最頻原因は cert-manager の Gateway API 連携が無効(インストール時にconfig.enableGatewayAPI=trueを付け忘れた)ことなので、Certificate が見当たらないときはまず cert-manager の設定を確認する - ネットワーク構成や DNS の変更時に、cert-manager のチャレンジ経路(HTTP01 / DNS01)が影響を受けないかをチェック項目に入れる。変更後に証明書を意図的に再発行して、更新経路が生きているか確認する
- HTTPS で公開するサービスは、証明書の状態(Issuer の
READY・Certificate のREADY・残り有効日数)をダッシュボードで一覧化し、定期的に目視する運用にする
2 件のヒヤリハットに共通するのは「Gateway API も cert-manager も、リソースの YAML を作っただけでは機能が成立せず、status / READY という状態が True になって初めて動く」という性質です。HTTPRoute なら Accepted / ResolvedRefs、Certificate なら READY を、作成直後と運用中の両方で確認する。この「状態を確認する」習慣が、外部公開と HTTPS の事故防止の土台になります。
本回の演習で kubectl describe httproute や kubectl get certificate を繰り返し実行したのは、この習慣を身につけるためです。
ep18 完了後の模擬アプリ状態と ep19(第1巻完走)への橋渡し
本回で、第6部「パッケージ管理 + HTTPS 公開」の第2回として Gateway API による HTTPS 公開を扱いました。fanclub-api は https://fanclub.local で HTTPS アクセスできる状態になり、第1巻の HTTPS 公開マイルストーンを達成しました。
ep18 完了後のクラスタ状態
| リソース | 状態 |
|---|---|
| kind クラスタ | kind-control-plane Ready(v1.35.0)・port mapping は 6443 のみ(ep17 から変化なし) |
| Gateway API CRD | 新規導入(v1.4.0・GatewayClass / Gateway / HTTPRoute / ReferenceGrant・演習①) |
| cert-manager | 新規インストール(v1.20.0・cert-manager Namespace・3 Pod 稼働・演習①) |
| Traefik | 新規インストール(chart v39 系・traefik Namespace・Gateway API プロバイダ・演習②) |
| ClusterIssuer | selfsigned-issuer / ca-issuer の 2 個(自己署名 CA 構成・演習②) |
fanclub Namespace | fanclub-api(Helm Release)+ Gateway + HTTPRoute + Certificate fanclub-tls(演習②・③) |
| default ns の fanclub-api | ep16 完了状態のまま(生 YAML 管理・本回では一切触れていない) |
| default ns の NetworkPolicy | 6 個(ep16 から継続・本回では触れていない) |
Helm Chart ~/fanclub-charts/fanclub-api/ | ep17 で作成・手元に残存(本回でこの Chart を fanclub ns にデプロイ) |
本回の演習は、新しい 3 つの Namespace(cert-manager / traefik / fanclub)と、クラスタスコープの Gateway API CRD・ClusterIssuer で完結しました。default ns の既存 fanclub-api(生 kubectl apply 管理)と 6 個の NetworkPolicy は ep16 完了状態のままで、本回の影響を受けていません。
なお、本回で作った fanclub Namespace と Traefik / cert-manager は、次の ep19 でクラスタを作り直す際に一度クリアされます。
第1巻 HTTPS 公開マイルストーンの達成
第1巻の目標の 1 つに「読者がブラウザから https://fanclub.local にアクセスして fanclub-api の動作を確認できる」というマイルストーンがあります。本回で、fanclub-api を Gateway API + Traefik で外部公開し、cert-manager の自己署名 CA で TLS 証明書を発行して HTTPS 化しました。
実機検証環境では kubectl port-forward + curl で HTTPS アクセスを確認しましたが、読者が kind を extraPortMappings 付きで構築し /etc/hosts を整えれば、ブラウザから https://fanclub.local にアクセスできる構成です。第7回から積み上げてきた fanclub-api が、HTTP / HTTPS の入口を持ち、ブラウザから到達できるアプリケーションになりました。
ep19(第1巻完走)への橋渡し
第6部の 3 回(ep17〜ep19)のうち、本回で 2 回目を終えました。残るは ep19「総合演習 — fanclub-api 全機構デプロイ + CKAD 対策まとめ」で、第1巻の完走回です。ep19 では、クリーンな kind クラスタを一から作り直し、ep1〜ep18 で学んだ全機構(Docker イメージビルド・Pod・Deployment・Service・StatefulSet・ConfigMap・Secret・Probe・ResourceQuota・RBAC・SecurityContext・NetworkPolicy・Helm・Gateway API・cert-manager)を通し演習で再構築します。
本回の成果物である Gateway / HTTPRoute / ClusterIssuer の YAML と、ep17 の fanclub-api Helm Chart は、ep19 の総ざらいの素材になります。ep19 でクラスタを作り直すときに、本回で学んだ extraPortMappings を使うかどうかも含めて、第1巻で学んだ知識を 1 つの通し演習に統合します。
本回で作った ~/fanclub-tls/ の YAML(ClusterIssuer / Certificate / Gateway / HTTPRoute)と ~/fanclub-charts/fanclub-api/ の Helm Chart は、ep19 まで削除せず手元に残しておいてください。
理解度チェック・第18回まとめ・次回予告・シリーズ一覧
理解度チェック(○×形式・9 問)
問 1:Ingress リソースは、高度な機能をアノテーションで指定し、そのアノテーションのキー名が Ingress Controller ごとに異なるため、移植性が低い。
問 2:Gateway API の GatewayClass は Namespace スコープのリソースである。
問 3:HTTPRoute は、URL のホスト名・パスでルーティング先の Service を振り分ける。
問 4:cert-manager の ClusterIssuer は、クラスタ全体のどの Namespace からも使える証明書の発行元である。
問 5:Gateway の HTTPS リスナーで tls.mode: Terminate にすると、Gateway で TLS を終端し、バックエンドへは平文 HTTP で転送される。
問 6:ingress-nginx は 2026 年も長期サポートが継続している。
問 7:HTTPRoute と backendRefs の Service が別の Namespace にあるとき、ReferenceGrant が必要になる。
問 8:cert-manager の Certificate リソースは、発行された TLS 証明書を Secret に格納する。
問 9:Gateway API は Kubernetes 本体に最初から組み込まれており、CRD を追加インストールする必要はない。
解答:
| 問 | 解答 | 解説 |
|---|---|---|
| 問 1 | ○ | Ingress の spec は HTTP ルーティングの基本しか定義せず、タイムアウトやリライトなどはアノテーションで指定する。アノテーションのキー名は nginx と Traefik で異なり、Ingress Controller を変えると書き直しになる。この移植性の低さが Gateway API が生まれた理由の 1 つ |
| 問 2 | × | GatewayClass はクラスタスコープのリソース。StorageClass と同じく -n(Namespace 指定)を付けずに kubectl get gatewayclass で確認する。Namespace スコープなのは Gateway / HTTPRoute / ReferenceGrant の 3 つ |
| 問 3 | ○ | HTTPRoute は hostnames と rules.matches.path でリクエストを判定し、backendRefs で指定した Service へ振り分ける。Ingress の spec.rules に相当する部分が Gateway API では HTTPRoute に独立した |
| 問 4 | ○ | ClusterIssuer はクラスタスコープの証明書発行元で、どの Namespace の Certificate からも参照できる。Namespace スコープ版が Issuer。本回は ClusterIssuer を 2 つ(selfsigned-issuer / ca-issuer)使った |
| 問 5 | ○ | tls.mode: Terminate は Gateway で TLS を終端(復号)するモード。クライアントと Gateway の間は HTTPS、Gateway とバックエンドの間は平文 HTTP になる。バックエンドのアプリは HTTP だけ話せばよい |
| 問 6 | × | ingress-nginx はメンテナ不足により 2026 年 3 月に EOL を迎えた。EOL 後はセキュリティ修正が提供されない。Kubernetes コミュニティは Gateway API への移行を進めており、本シリーズも Gateway API + Traefik を採用している |
| 問 7 | ○ | Gateway API は Namespace 越えのリソース参照をデフォルトで禁止する。HTTPRoute の backendRefs が別 Namespace の Service を指す場合、Service のある Namespace に ReferenceGrant を置いて参照を許可する必要がある。本回の演習は同一 Namespace のため ReferenceGrant は不要だった |
| 問 8 | ○ | Certificate は「こういう証明書がほしい」という宣言で、cert-manager がそれを見て証明書を発行し、kubernetes.io/tls タイプの Secret に格納する。Gateway の tls.certificateRefs がその Secret を参照して TLS 終端に使う |
| 問 9 | × | Gateway API は Kubernetes 本体には組み込まれておらず、CRD として後からインストールする。本回は演習①で standard-install.yaml を kubectl apply して CRD を導入した。cert-manager も同様に CRD + コントローラの追加コンポーネント |
第18回まとめ
第18回では以下を実施しました。
- 従来の Ingress リソースが抱える 3 つの限界(アノテーション依存で移植性がない・HTTP(S) 以外のプロトコルに非対応・担当ロールの分離ができない)を整理し、Gateway API が標準 API・role-based の 4 リソース分割・拡張可能な設計でそれを解決する構造を明確にした。Ingress Controller として広く使われた ingress-nginx が、メンテナ不足により 2026 年 3 月に EOL を迎え、Kubernetes コミュニティが Gateway API への移行を進めている背景を整理した
- Gateway API の 4 リソース(GatewayClass / Gateway / HTTPRoute / ReferenceGrant)の役割分担とスコープを整理した。GatewayClass はクラスタスコープで実装プロバイダを指定し多くは実装が自動生成する、Gateway は Namespace スコープでリスナー(ポート・プロトコル・TLS)を定義する、HTTPRoute は Namespace スコープでホスト名・パスの振り分けを定義する、ReferenceGrant は Namespace スコープで参照される側に置き Namespace 越え参照を許可する、という対応を明確にした
- cert-manager の 3 つの主要リソース(Issuer / ClusterIssuer / Certificate)を整理した。本回は外部疎通のない kind 環境のため Let’s Encrypt ではなく自己署名 CA を使い、自己署名 ClusterIssuer → CA 証明書 → CA ベースの ClusterIssuer → fanclub-api の証明書、という 2 段構成を組み立てた。cert-manager と Gateway は Secret を介して連携し、Gateway のアノテーション 1 行で証明書の発行・格納が自動化される仕組みを示した
- 演習①で Gateway API v1.4.0 の CRD を
kubectl applyで導入し、GatewayClass / Gateway / HTTPRoute / ReferenceGrant の CRD を確認した。cert-manager v1.20.0 を jetstack リポジトリからhelm installし、3 つの Pod(cert-manager / cainjector / webhook)が Running になることと cert-manager の CRD を確認した - 演習②で Traefik chart を
providers.kubernetesGateway.enabled=trueでインストールし、Gateway API の実装プロバイダにした。GatewayClasstraefikがACCEPTED=Trueで登録されることを確認した。ep17 の fanclub-api Helm Chart を新 Namespacefanclubにデプロイし、自己署名 ClusterIssuer・CA 証明書・CA ベース ClusterIssuer を作成、Gateway リソースをcert-manager.io/cluster-issuerアノテーション付きで作成してPROGRAMMED=Trueを確認した - HTTPRoute の YAML 構造(
parentRefs/hostnames/rulesのmatchesとbackendRefs)を整理した。Gateway の HTTPS リスナーで TLS 終端すると、クライアントと Gateway の間は HTTPS・Gateway とバックエンドの間は平文 HTTP になること、cert-manager が Secret を介して証明書を供給する流れを明確にした - 演習③で HTTPRoute を作成して
Accepted=True/ResolvedRefs=Trueを確認し、cert-manager が発行したfanclub-tls証明書と TLS Secret を確認した。kubectl port-forwardで Traefik Service を localhost に転送し、curl --resolve fanclub.local:8443:127.0.0.1 https://fanclub.local:8443/health/live -kで HTTPS アクセスを確認、証明書の issuer が本回で作った自己署名 CA であることをcurl -vで確認した - kind 環境の外部公開の制約を整理した。kind は 80/443 をデフォルトで公開せず、
extraPortMappingsはクラスタ作成時にしか指定できず後付け不可であること、本シリーズは ep1〜ep17 の積み上げを失わないためクラスタを作り直さずkubectl port-forwardで HTTPS 検証する方式を採ったことを明確にした。本番クラウドでは LoadBalancer Service + 外部 LB で 80/443 を公開する - CKAD 試験 D5 の Gateway API 頻出パターン 4 選(HTTP 公開 / HTTPS の TLS 終端 / パスごとの振り分け / 別 Namespace 参照)を解答方針とともに整理し、速攻コマンド早見表と落とし穴 3 選(ReferenceGrant 置き忘れ / Certificate not Ready / GatewayClass 名のミスマッチ)を示した。Gateway API は 2026 年からの NEW 出題項目だが、旧来の Ingress リソースも出題されうるため両方に備えるべきことを補足した
- 現場ヒヤリハットを 2 件扱った。Namespace 越えの HTTPRoute で Gateway 側の
allowedRoutes不許可と ReferenceGrant の理解不足によりトラフィックが流れなかった事例、cert-manager の Certificate がチャレンジ失敗でREADY=Falseのまま放置され証明書期限切れで HTTPS が落ちた事例を、5 点セットで整理した。「YAML を作っただけでなく status / READY を確認する」習慣の重要性を再確認した - fanclub-api を
https://fanclub.localで HTTPS 公開し、第1巻の HTTPS 公開マイルストーンを達成した。本回の~/fanclub-tls/の YAML(ClusterIssuer / Certificate / Gateway / HTTPRoute)と ep17 の fanclub-api Helm Chart は、ep19(第1巻完走の総合演習)の素材として手元に残すことを明示した
次回予告
第19回「総合演習 — fanclub-api 全機構デプロイ + CKAD 対策まとめ」は、第1巻の完走回です。クリーンな kind クラスタを一から作り直し、ep1〜ep18 で学んだ全機構(Docker イメージビルド・Pod・Deployment・Service・StatefulSet・ConfigMap・Secret・Probe・ResourceQuota・RBAC・SecurityContext・NetworkPolicy・Helm・Gateway API・cert-manager)を 1 つの通し演習で再構築します。
fanclub-api をゼロからデプロイして https://fanclub.local で動作確認するところまでを総ざらいし、CKAD 試験 6 ドメインの対策ポイントを整理します。第1巻 19 回の集大成として、第7回から積み上げてきた fanclub-api を、新しいクラスタの上に一気通貫で組み立てます。
シリーズ一覧
第1部:コンテナと Docker
- 第1回 コンテナ技術概念 + Docker 環境準備
- 第2回 Docker 基本操作
- 第3回 Dockerfile + マルチステージビルド + JDK 25 / Payara Micro イメージビルド
- 第4回 コンテナレジストリ + イメージタグ戦略 + Trivy スキャン
第2部:Kubernetes 基礎
第3部:アプリリソース
- 第7回 Pod + Multi-container パターン
- 第8回 Service とネットワーキング
- 第9回 ストレージ(PVC + StatefulSet)+ PostgreSQL DB 追加
- 第10回 ConfigMap + Secret + ServiceAccount 基礎
- 第11回 Job + CronJob + DaemonSet
第4部:ワークロード戦略
- 第12回 Deployment + 3 Probe + Rolling Update + Probe デバッグ実践
- 第13回 Deployment 戦略補完(Blue/Green + Canary + Recreate)
- 第14回 ResourceQuota + LimitRange + Multi-tenant Namespace
第5部:セキュリティ基礎
第6部:パッケージ管理 + HTTPS 公開
- 第17回 Helm v4 基礎 + fanclub-api Helm Chart 作成
- 第18回 Gateway API + Traefik + cert-manager で HTTPS 公開 ← 今ここ
- 第19回 総合演習 — fanclub-api 全機構デプロイ + CKAD 対策まとめ
