新卒インフラエンジニア向け「Kubernetes 実践教科書① CKAD アプリケーション開発編」の第18回です。第17回で fanclub-api を Helm chart にまとめ、helm install で一括デプロイできるようにしました。ただし外部からの入口はまだ無く、動作確認は kubectl exec や port-forward に頼っていました。今回は Gateway API と Traefik・cert-manager で https://fanclub.local を TLS 付きで公開し、ブラウザから CRUD を操作できる状態にします。CKAD ドメイン D5(Services and Networking)の 2026 年 NEW 必須項目です。
動作確認バージョン:Gateway API v1.4.0(standard channel)/ Traefik chart v39.0.9(Traefik v3.6.15)/ cert-manager v1.20.0 / Helm v4.1.4 / kind v0.31.0(K8s v1.35.0)/ fanclub-backend:0.3.2(据え置き)/ fanclub-frontend:0.1.0 → 1.0.0(2026-07-03 実機検証時点)
今ここマップ(全 19 回中の現在地)
現在地は第 6 部「パッケージ管理と HTTPS 公開」の第 18 回です。chart 化した fanclub-api に、外部公開の入口(HTTPS)を付けます。
- 第 1 部 コンテナと Docker(第 1〜4 回)
- 第 2 部 Kubernetes 基礎(第 5〜6 回)
- 第 3 部 アプリリソース(第 7〜11 回)
- 第 4 部 ワークロード戦略(第 12〜14 回)
- 第 5 部 セキュリティ基礎(第 15〜16 回)
- 第 6 部 パッケージ管理と HTTPS 公開(第 17〜19 回)← 今ここ
この回のゴール
- Ingress と Gateway API の違いを説明できる。
- GatewayClass / Gateway / HTTPRoute の役割を理解し、Traefik を Gateway 実装として導入できる。
- cert-manager の自己署名 CA で TLS 証明書を発行し、
https://fanclub.localをブラウザから開ける。
この回の作業はすべて、作業端末 k8s-ops に一般ユーザー developer で SSH した状態で行います。設定ファイルは developer のホーム(~)に一般ユーザーとして作成し、kubectl / helm は第6回から使ってきた同じ kubeconfig(~/.kube/config)で kind クラスタを操作します。root 権限は使いません。
Ingress との対比
Kubernetes で HTTP(S) を外部公開する従来の仕組みは Ingress でした。Ingress には次の弱点があります。
- アノテーション依存:TLS のリダイレクトやパス書き換えなど、標準フィールドに無い設定はコントローラ独自のアノテーションで書く。実装ごとに書式が違い、移植性が低い。
- 役割が 1 リソースに混在:インフラ担当・クラスタ運用・アプリ開発の関心事が 1 つの Ingress に同居し、権限分離しにくい。
- 実装差異:同じ Ingress でもコントローラ実装で挙動が変わる。
Gateway API は Ingress の後継として設計された標準 API で、役割ごとにリソースを分けます。設定は標準フィールドで表現でき、アノテーション頼みが減ります。加えて、広く使われてきた ingress-nginx は 2026 年 3 月で EOL(開発終了) になり、本シリーズでは採用しません。これも Gateway API へ移る実務上の理由です。
Gateway API のリソース
Gateway API は、関心事を 3 つのリソースに分けます。誰が管理するかが明確になる設計です。
- GatewayClass:どの実装(プロバイダ)を使うかを指定する。Traefik を入れると
traefikという GatewayClass が用意される。インフラ担当の関心事。 - Gateway:リスナー(待ち受けるポート・プロトコル・TLS 終端)を定義する。クラスタ運用者の関心事。
- HTTPRoute:ホスト名やパスで、どの Service へ転送するかを定義する。アプリ開発者の関心事。
- ReferenceGrant:Namespace をまたいで参照するとき(例: 別 Namespace の Service や Secret を指す)に、参照先側で許可を与える。本回は Gateway・HTTPRoute・Service・Secret をすべて同じ
fanclubNamespace に置くので ReferenceGrant は不要です。

Gateway API の CRD を入れる
Gateway API のリソース(GatewayClass など)は Kubernetes 標準ではなく CRD(カスタムリソース定義)で提供されます。まず v1.4.0 の standard channel を適用します。実行コマンド:
$ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml
実行結果(例):GatewayClass / Gateway / HTTPRoute / ReferenceGrant など 6 種の CRD が作られます。
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
customresourcedefinition.apiextensions.k8s.io/backendtlspolicies.gateway.networking.k8s.io created
続けて、この後使う Helm リポジトリを 2 つ登録しておきます。実行コマンド:
$ helm repo add traefik https://traefik.github.io/charts
$ helm repo add jetstack https://charts.jetstack.io
$ helm repo update
Traefik を Helm で入れる(Gateway 実装)
Gateway API は「仕様」なので、実際にトラフィックを捌く実装が要ります。本シリーズは Traefik を使います。kind 特有のはまりどころが 2 つあるので、先に値ファイルで対策を入れておきます。developer のホームに traefik-values.yaml を作成します。
providers:
kubernetesGateway:
enabled: true
kubernetesIngress:
enabled: false
gateway:
enabled: false
gatewayClass:
enabled: true
ports:
web:
port: 80
hostPort: 80
websecure:
port: 443
hostPort: 443
service:
type: ClusterIP
updateStrategy:
type: Recreate
rollingUpdate: null
securityContext:
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
runAsGroup: 65532
runAsNonRoot: true
runAsUser: 65532
値の意味と、2 つのはまりどころを説明します。
providers.kubernetesGateway.enabled: true:Traefik を Gateway API のプロバイダとして動かす。gatewayClass.enabled: trueと合わせてtraefikという GatewayClass が作られる。Ingress は使わないのでkubernetesIngress.enabled: false。gateway.enabled: false:Traefik chart 自身が作る既定の Gateway は不要(Gateway は後で chart のfanclub-gatewayを自分で作る)なので無効にする。- はまりどころ①・ポート一致:Traefik の待ち受け口(entryPoint)は既定で
websecure=:8443です。しかし後で作る Gateway のリスナーは 443 です。Gateway のリスナー port は Traefik の entryPoint と一致していないと有効になりません(PortUnavailableになる)。そこでports.web.port/ports.websecure.portを80/443に変え、非 root で低番ポートを開けるようsecurityContextにNET_BIND_SERVICEを足します。 hostPort: 80 / 443:第5回の kind 構成で「ホストの 80/443 → kind ノードの 80/443」をマッピング済みです。Traefik がノードの 80/443 にhostPortで結び付くことで、ホストからの HTTPS が Traefik に届きます。- はまりどころ②・更新の詰まり:
hostPortはノードのポートを 1 Pod が占有します。既定のローリング更新だと、新旧 2 つの Pod が同時に 80/443 を掴もうとして新 Pod が起動できません。updateStrategy.type: Recreateで「古い Pod を消してから新 Pod を作る」に変えて回避します。
この値で Traefik を install します。実行コマンド:
$ helm install traefik traefik/traefik --version 39.0.9 \
-n traefik --create-namespace -f ~/traefik-values.yaml
GatewayClass が使える状態(ACCEPTED=True)になったか確認します。実行コマンド:
$ kubectl get gatewayclass
実行結果(例):
NAME CONTROLLER ACCEPTED AGE
traefik traefik.io/gateway-controller True 20s
cert-manager で TLS 証明書を用意する
HTTPS には TLS 証明書が要ります。cert-manager を入れ、自己署名 CA(社内 CA を模した学習用の認証局)を作って、そこから fanclub.local のサーバ証明書を発行します。まず cert-manager 本体を install します(CRD も同時に入れます)。実行コマンド:
$ helm install cert-manager jetstack/cert-manager --version v1.20.0 \
-n cert-manager --create-namespace --set crds.enabled=true
次に自己署名 CA を作ります。cert-manager では、次の 3 リソースを鎖のようにつなぎます。developer のホームに ca-issuer.yaml を作成します。
selfSignedのClusterIssuer(自分で自分に署名する起点)- その起点で発行する CA 証明書(
isCA: true・秘密鍵は cert-manager Namespace の Secret に入る) - その CA を使う
ClusterIssuer(fanclub-ca-issuer・以降のサーバ証明書はここが発行する)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-bootstrap
spec:
selfSigned: {}
---
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: 87600h
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: selfsigned-bootstrap
kind: ClusterIssuer
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: fanclub-ca-issuer
spec:
ca:
secretName: fanclub-ca-secret
適用し、fanclub-ca-issuer が使える状態(READY=True)になったか確認します。実行コマンド:
$ kubectl apply -f ~/ca-issuer.yaml
$ kubectl get clusterissuer
実行結果(例):
NAME READY AGE
fanclub-ca-issuer True 5s
selfsigned-bootstrap True 5s
CA 秘密鍵の Secret(fanclub-ca-secret)を cert-manager Namespace に置いているのは、ClusterIssuer が CA を既定でこの Namespace から読むためです(別 Namespace に置くと fanclub-ca-issuer が CA を見つけられません)。サーバ証明書(fanclub-tls)そのものは、この後 chart 側の Certificate テンプレートが fanclub-ca-issuer に発行を依頼します。CA の準備はここまでです。
Frontend を最終形(v1.0.0)に仕上げる
ブラウザ公開に合わせて、Frontend を最終形にします。第8回で作った Frontend は削除操作をブラウザの確認ダイアログで行っていましたが、これを専用の削除確認画面(delete.html)に置き換え、6 画面(ホーム / 登録 / 詳細 / 編集 / 削除確認 / 検索)そろえて v1.0.0 とします。delete.html は他の画面と同じく Bootstrap と app.js の deleteMember() を呼ぶ静的ページで、削除ボタンからこの画面へ遷移するよう app.js と detail.html も更新します(差分は本シリーズ配布のアプリソースに含めています)。Frontend のソース一式は developer のホームの ~/fanclub-frontend にあり、これを更新したうえで第4回で構築した k8s-registry へビルドして push します。実行コマンド:
$ cd ~/fanclub-frontend
$ docker build -t k8s-registry:5000/fanclub-frontend:1.0.0 .
$ docker push k8s-registry:5000/fanclub-frontend:1.0.0
実行結果(例):registry のタグ一覧に 1.0.0 が増えます。
{"name":"fanclub-frontend","tags":["0.1.0","1.0.0"]}
なお、Frontend の Nginx は第8回から /api を Backend へリバースプロキシしています。この回でもその構成を維持します(後述のルーティング設計と関係します)。
chart の Gateway を有効化する
第17回の chart には、Gateway / HTTPRoute / Certificate のテンプレートを gateway.enabled のトグルで無効にした状態で仕込んであります。CRD と Traefik・cert-manager がそろった今、これを有効化します。まず 3 つのテンプレートの中身を確認します(~/fanclub-chart/templates/ にあります)。
gateway.yaml:443 で HTTPS を待ち受け、fanclub-tls Secret で TLS を終端します。
{{- if .Values.gateway.enabled }}
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: fanclub-gateway
namespace: {{ .Values.namespace }}
spec:
gatewayClassName: traefik
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: {{ .Values.gateway.host | quote }}
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: fanclub-tls
{{- end }}
certificate.yaml:先ほど作った fanclub-ca-issuer に、fanclub.local のサーバ証明書を fanclub-tls Secret として発行させます。
{{- if .Values.gateway.enabled }}
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: fanclub-tls
namespace: {{ .Values.namespace }}
spec:
secretName: fanclub-tls
dnsNames:
- {{ .Values.gateway.host | quote }}
issuerRef:
name: fanclub-ca-issuer
kind: ClusterIssuer
{{- end }}
httproute.yaml:ここがルーティング設計の要です。本アプリは /(すべてのパス)を Frontend へ通す単一ルートにします。/api は Frontend の Nginx が Backend へリバースプロキシするので、通信は Gateway → Frontend → Backend → DB の一本道になります。
{{- if .Values.gateway.enabled }}
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: fanclub-route
namespace: {{ .Values.namespace }}
spec:
parentRefs:
- name: fanclub-gateway
hostnames:
- {{ .Values.gateway.host | quote }}
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: fanclub-frontend
port: 80
{{- end }}
この「一本道」にする設計は、第16回の NetworkPolicy 設計と揃えるためです。第16回では Gateway → Frontend → Backend → DB の隣接層だけを許可する 4 層のホワイトリストを敷き、外部→Frontend の許可(allow-frontend-from-gateway)は本回で足すと予告しました。もし HTTPRoute で /api を Backend へ直接振り分けると、Gateway が Frontend と Backend の両方に通信することになり、Backend 向けの許可も別途必要になってこの設計から外れます。そこで /api は Frontend の Nginx に任せ、Gateway は Frontend だけを見る形にします。
その allow-frontend-from-gateway を、chart の ~/fanclub-chart/templates/networkpolicy.yaml(第16回の 6 枚と第17回のバッチ用 1 枚、計 7 枚がすでに入っているファイル)に追記します(gateway.enabled のときだけ有効になるよう {{- if .Values.gateway.enabled }} で囲みます)。Traefik は traefik Namespace にいるので、その Namespace(自動で付く kubernetes.io/metadata.name: traefik ラベルで指定)からの ingress を許可します。
{{- if .Values.gateway.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-from-gateway
namespace: {{ .Values.namespace }}
spec:
podSelector:
matchLabels:
app: fanclub-frontend
policyTypes: ["Ingress"]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: traefik
ports: [{protocol: TCP, port: 80}]
{{- end }}
準備ができたら、gateway.enabled=true と Frontend の新タグ 1.0.0 を指定して helm upgrade します。helm コマンドは chart を ./fanclub-chart の相対パスで指すので、developer のホーム(~)で実行します。実行コマンド:
$ helm upgrade fanclub ./fanclub-chart \
--set gateway.enabled=true --set frontend.image.tag=1.0.0
Gateway とルート、証明書ができたか確認します。実行コマンド:
$ kubectl get gateway,httproute,certificate -n fanclub
実行結果(例):Gateway が PROGRAMMED=True、Certificate が READY=True になれば、TLS 終端と経路が有効です。
NAME CLASS PROGRAMMED AGE
gateway.gateway.networking.k8s.io/fanclub-gateway traefik True 30s
NAME HOSTNAMES AGE
httproute.gateway.networking.k8s.io/fanclub-route ["fanclub.local"] 30s
NAME READY SECRET AGE
certificate.cert-manager.io/fanclub-tls True fanclub-tls 30s

ブラウザから HTTPS でアクセスする
最後に、ブラウザを動かす端末で名前解決を通します。hosts ファイル(Linux/macOS は /etc/hosts、Windows は C:\Windows\System32\drivers\etc\hosts)に、管理者権限で次の 1 行を追記します。kind と同じ端末のブラウザで開くなら 127.0.0.1 を、別の端末(例: VM 上の kind に対して手元の PC のブラウザ)から開くなら kind ホストの IP アドレスを指します(あるいは SSH ポートフォワードで手元の 443 を kind ホストの 443 へ転送します)。ここでは kind と同じ端末で開く前提で 127.0.0.1 を使います。
127.0.0.1 fanclub.local
ブラウザで https://fanclub.local を開くと、会員一覧が表示されます。証明書は自己署名 CA が発行したものなので、CA をブラウザ/OS の信頼ストアに取り込んでいない場合は警告が出ます。学習環境では警告を承認して進めるか、fanclub-ca-secret の CA 証明書を信頼ストアに取り込みます。コマンドラインで疎通だけ確かめる場合は、次のように確認できます(自己署名なので -k、社内プロキシを迂回するため --noproxy を付けます)。実行コマンド:
$ curl -k --noproxy '*' --resolve fanclub.local:443:127.0.0.1 \
https://fanclub.local/api/members
実行結果(例):会員一覧の JSON が返ります。ブラウザ →(HTTPS)→ Gateway → Frontend →(Nginx が /api を中継)→ Backend → DB の一本道が通っています。
[{"createdAt":"2026-07-03T07:36:53.911406","email":"hanako@example.com","id":1,"name":"鈴木花子","plan":"standard"},{"createdAt":"2026-07-03T07:36:53.911406","email":"ichiro@example.com","id":2,"name":"佐藤一郎","plan":"free"}]
ブラウザから会員の登録・編集・削除まで一通り操作できれば、fanclub-api は HTTPS で公開できました。
やってみよう
- Gateway API の CRD を適用し、Traefik を
~/traefik-values.yamlで install してkubectl get gatewayclassがACCEPTED=Trueになることを確認する。 - cert-manager を install し、
ca-issuer.yamlを適用してfanclub-ca-issuerがREADY=Trueになることを確認する。 - Frontend を
1.0.0でビルドして push し、helm upgrade fanclub ./fanclub-chart --set gateway.enabled=true --set frontend.image.tag=1.0.0を実行する。 kubectl get gateway,certificate -n fanclubでPROGRAMMED=True/READY=Trueを確認する。- ホストの
hostsに127.0.0.1 fanclub.localを足し、ブラウザでhttps://fanclub.localを開いて会員の登録・削除を操作する。
理解度チェック
次の各文が正しいか(○)誤りか(×)を判断してください。解答は下にまとめています。
- Gateway API は GatewayClass / Gateway / HTTPRoute に役割を分け、Ingress よりアノテーション依存が少ない。
- GatewayClass はどの実装(プロバイダ)を使うかを指定するリソースである。
- Gateway のリスナー port は、Gateway 実装(Traefik)の待ち受け口(entryPoint)と一致していなくてもよい。
- cert-manager の自己署名 CA は、selfSigned の Issuer → CA 証明書 → CA の ClusterIssuer の順に構成する。
- 同じ Namespace 内の Gateway・HTTPRoute・Service・Secret だけを参照する場合、ReferenceGrant は不要である。
- Gateway の TLS 終端で使う証明書は、Gateway と同じ Namespace の Secret で参照する。
- ingress-nginx は 2026 年 3 月で EOL となり、本シリーズでは採用しない。
解答
- 1. ○:役割分離と標準フィールドが Gateway API の要点。
- 2. ○:GatewayClass は実装プロバイダ(本回は
traefik)を指す。 - 3. ×:一致が必須。ずれると
PortUnavailableでリスナーが無効になる(本回は Traefik を 80/443 に合わせた)。 - 4. ○:selfSigned を起点に CA 証明書を発行し、その CA を使う ClusterIssuer を作る。
- 5. ○:同一 Namespace 内の参照なら ReferenceGrant は要らない。
- 6. ○:本回は
fanclub-tlsSecret を Gateway と同じfanclubNamespace に置いた。 - 7. ○:EOL のため新シリーズでは Gateway API へ移行する。
まとめ
本記事では、Gateway API で fanclub-api を HTTPS 公開しました。Ingress との違い(役割分離・標準 API・ingress-nginx の EOL)を押さえ、GatewayClass / Gateway / HTTPRoute の役割分担を確認しました。実装の Traefik は Helm で導入し、kind 特有の 2 つのはまりどころ(Gateway リスナーと entryPoint のポート一致・hostPort と Recreate 更新)を対策しました。cert-manager の自己署名 CA で fanclub-tls を発行し、Gateway で TLS を終端しました。ルーティングは / をすべて Frontend へ通し、/api は Nginx が Backend へ中継する一本道にして、第16回の NetworkPolicy 設計(allow-frontend-from-gateway で 4 層完成)と揃えました。これで https://fanclub.local をブラウザから操作できます。
次回予告
次回(第19回・最終回)は、ここまで積み上げた全 14 機構を、クリーンな kind クラスタにゼロから 120 分で通し構築する総合演習です。Namespace 分離から HTTPS 公開まで、Helm 一発デプロイを軸に第1巻を完走します。ブラウザから https://fanclub.local で CRUD 操作ができれば、第1巻の完走マイルストーン達成です。
シリーズ一覧
第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 + chart 化 + フルサイクル
- 第18回 Gateway API + HTTPRoute + Ingress 対比 + Traefik + cert-manager ← 今ここ
- 第19回 fanclub-api 全機構連携の実技総ざらい総合演習 + 第1巻完走宣言 ★
