はじめに
Ingress 設定は、一度作れば終わりという静的な対象ではありません。TLS 証明書を更新し、タイムアウトやボディサイズ上限を調整し、レートリミットのアノテーションを追加し、新しいサービスが生まれるたびにパスを増やします。そしてこれらの変更はすべて、dev、staging、production という複数の環境で微妙に異なる値で適用されなければなりません。誰かが `kubectl edit ingress` で production を直接直した瞬間、その変更は何の記録もなく消える運命に置かれます。
だからこそ Ingress は、コードで管理されるべき代表的な対象です。コントローラー自体のデプロイ(どのバージョンを、どのリソースで、どの ConfigMap で)と、個々の Ingress マニフェスト(ホスト、パス、アノテーション、TLS)の両方を Git に置き、宣言的にクラスターへ適用すべきです。これが GitOps の核心的価値です。Git が単一の信頼できる情報源(single source of truth)となり、クラスターの状態は常に Git へ向かって収束します。
本記事では Ingress の構成管理を四つの段階に分けて扱います。第一に Helm values でコントローラーを環境ごとにデプロイすること。第二に Kustomize で Ingress マニフェストをテンプレート化し環境ごとの patch を重ねること。第三に Argo CD と Flux でデリバリーとドリフト検知を自動化すること。第四に Kyverno のようなポリシーエンジンで必須アノテーションと IngressClass を強制するゲートを立てること。2026 年現在 `Ingress` API は凍結状態で Gateway API が後継標準ですが、ここで扱う構成管理パターンは両 API に同じく適用される点も押さえておきます。
構成管理が必要なもの
Ingress 領域で Git に入れるべき対象は、大きく二つの層に分かれます。
| 層 | 対象 | 変更頻度 | 管理ツール |
| --- | --- | --- | --- |
| プラットフォーム | コントローラーのデプロイ、ConfigMap、IngressClass | 低い(四半期ごと) | Helm values |
| アプリケーション | Ingress マニフェスト(ホスト/パス/TLS/アノテーション) | 高い(常時) | Kustomize/Helm |
この二つを分離することが重要です。コントローラーはプラットフォームチームが慎重にバージョンとセキュリティパッチを管理し、Ingress マニフェストは各アプリケーションチームが自分のサービスに合わせて頻繁に変えます。両層を一つの巨大なチャートに混ぜると、変更の影響範囲が広がりレビューが難しくなります。
Git リポジトリ
├── platform/ingress-controller/ (Helm values, 環境ごと)
│ ├── values-base.yaml
│ ├── values-dev.yaml
│ └── values-prod.yaml
└── apps/<service>/ingress/ (Kustomize base + overlays)
├── base/
└── overlays/{dev,staging,prod}/
Helm でコントローラーをデプロイ
ingress-nginx、Traefik などの主要コントローラーはすべて公式 Helm チャートを提供します。要点は、環境ごとに異なる values を置きつつ、共通部分を base values にまとめることです。
base values の例です。
controller:
replicaCount: 2
ingressClassResource:
name: nginx
default: false
config:
use-forwarded-headers: "true"
proxy-body-size: "16m"
resources:
requests:
cpu: 100m
memory: 128Mi
production オーバーレイではレプリカとリソースを増やし、外部公開の方式を明示します。
controller:
replicaCount: 4
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
resources:
requests:
cpu: 500m
memory: 512Mi
config:
proxy-body-size: "64m"
デプロイは二つの values ファイルを重ねて適用します。
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace \
-f values-base.yaml -f values-prod.yaml
こうすれば production と dev の違いが小さなオーバーレイファイル一つに明確に表れ、「なぜこの環境だけボディサイズ上限が違うのか」といった問いに Git diff で即座に答えられます。
Kustomize で Ingress マニフェストをテンプレート化
個々の Ingress マニフェストは Kustomize がよく合います。共通構造を base に置き、環境ごとにホストや TLS、アノテーションだけを patch で変える方式です。
base の Ingress です。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
spec:
ingressClassName: nginx
rules:
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
base の kustomization です。
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ingress.yaml
production オーバーレイではホストと TLS を patch で変えます。
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- target:
kind: Ingress
name: web
patch: |-
- op: replace
path: /spec/rules/0/host
value: web.prod.example.com
- op: add
path: /spec/tls
value:
- hosts:
- web.prod.example.com
secretName: web-prod-tls
共通アノテーションをすべての Ingress に一括追加したい場合は `commonAnnotations` を使えます。
commonAnnotations:
team: platform
managed-by: kustomize
このパターンの利点は、base がただ一つの真実であることです。すべての環境が base を共有するので、共通ロジックを一か所で直せば全環境に一貫して反映されます。
Argo CD でデリバリーとドリフト検知
いよいよ Git に整理されたマニフェストをクラスターへ自動適用する番です。Argo CD は Application リソースで「この Git パスをこのクラスター/ネームスペースに同期せよ」を宣言します。
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: web-ingress-prod
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/infra.git
targetRevision: main
path: apps/web/ingress/overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: web
syncPolicy:
automated:
prune: true
selfHeal: true
要点は `selfHeal: true` と `prune: true` です。誰かが `kubectl edit` でクラスターの Ingress を直接変えると、Argo CD はこれをドリフトとして検知し、Git の状態に戻します。Git からリソースを消すとクラスターからも削除します。これにより「クラスター = Git」の不変式が維持されます。
Flux を使うなら Kustomization リソースで同じことをします。
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: web-ingress
namespace: flux-system
spec:
interval: 5m
path: ./apps/web/ingress/overlays/prod
prune: true
sourceRef:
kind: GitRepository
name: infra
`interval: 5m` ごとに Git とクラスターを比較し、差があれば調整します。ドリフト検知の本質は同じです。
ポリシーゲート — Kyverno で強制
GitOps だけでは「誤った Ingress が Git にマージされること」自体は防げません。そこでクラスター進入の直前にポリシーゲートを置きます。Kyverno はアドミッション Webhook として動作し、ルールに違反するリソースを拒否または自動変形します。
第一に、すべての Ingress に IngressClass を強制するポリシーです。クラスがないとどのコントローラーが処理するか曖昧になるため、これを防ぎます。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-ingress-class
spec:
validationFailureAction: Enforce
rules:
- name: check-ingress-class
match:
any:
- resources:
kinds:
- Ingress
validate:
message: "すべての Ingress は spec.ingressClassName を指定する必要があります。"
pattern:
spec:
ingressClassName: "?*"
第二に、セキュリティ上必須のアノテーション(例: SSL リダイレクトの強制)を要求するポリシーです。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-ssl-redirect
spec:
validationFailureAction: Enforce
rules:
- name: check-ssl-redirect
match:
any:
- resources:
kinds:
- Ingress
validate:
message: "Ingress には force-ssl-redirect アノテーションが必要です。"
pattern:
metadata:
annotations:
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
こうしたポリシーを置けば、誤って平文 HTTP のみを公開したり、クラスのない Ingress を作ったりすることをクラスターレベルで遮断できます。GitOps(望ましい状態の保証)とポリシーエンジン(許容可能な状態の強制)は相互補完的です。
マルチ環境・マルチクラスター
規模が大きくなると環境が複数あり、クラスターも複数あります。Argo CD は ApplicationSet で同じパターンを複数の対象に広げます。たとえばクラスター一覧を generator に置き、各クラスターに同じ Ingress オーバーレイを自動デプロイします。このときも base は共有し、クラスターごとの違いだけをオーバーレイで表現する原則は同じです。
シークレット(TLS)の管理
TLS 証明書は構成管理の厄介な部分です。平文の Secret をそのまま Git に入れることはできないからです。実務では二つのアプローチが標準です。
- **cert-manager による自動発行**: Ingress アノテーションに issuer を指定すると、cert-manager が ACME(Let's Encrypt など)で証明書を発行・更新します。証明書自体は Git になく、宣言だけを Git に置きます。
- **暗号化シークレット**: 外部シークレットマネージャーや封印された(Sealed)シークレットで暗号化した形のみを Git に置き、クラスターで復号します。
cert-manager 方式の Ingress アノテーション例です。
metadata:
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
こうすれば証明書の更新まで宣言的に管理され、期限間近のアラートに手動で対応していた作業がなくなります。
レビュープロセス
GitOps の真の価値は、変更が Pull Request を経るところから生まれます。Ingress 変更に対する推奨レビューフローです。
1. ブランチでオーバーレイ patch を修正
2. CI で kustomize build + kubeconform によりスキーマ検証
3. CI で Kyverno CLI によりポリシーを事前チェック
4. Argo CD diff を PR コメントに自動投稿(実適用前に変更をプレビュー)
5. 同僚レビュー後にマージ -> Argo CD が自動同期
要点は「適用前に見る」です。Argo CD diff を PR に貼れば、マージ結果がクラスターにどんな変化を起こすかを人が目で確認したうえで承認できます。
チェックリスト
[ ] コントローラー(プラットフォーム)と Ingress(アプリ)の構成管理を分離したか
[ ] Helm base values + 環境オーバーレイでコントローラーをデプロイするか
[ ] Kustomize base 一つをすべての環境が共有するか
[ ] Argo CD/Flux で selfHeal と prune を有効にしたか
[ ] Kyverno で IngressClass と必須アノテーションを強制するか
[ ] TLS は cert-manager または暗号化シークレットで管理するか
[ ] CI で build/スキーマ/ポリシー検証を経るか
[ ] PR に Argo CD diff を投稿し適用前に検討するか
おわりに
Ingress をコードで管理するとは、単に YAML を Git に入れることではありません。コントローラーとマニフェストを二つの層に分離し、Helm と Kustomize で環境差を明示的に表現し、Argo CD や Flux でクラスターを Git に収束させ、Kyverno で安全ガードレールを立てる、一貫したシステムを作ることです。
このシステムが定着すれば、「production Ingress を誰が、いつ、なぜ変えたのか」という問いに Git 履歴で答えられ、誤った変更はポリシーゲートとドリフト検知が防いでくれます。そしてこのパターンは `Ingress` API でも後継標準の Gateway API でも同じく適用されるため、今後 Gateway API へ移行しても構成管理体系はそのまま引き継げます。
参考資料
- Kubernetes Ingress 公式ドキュメント: https://kubernetes.io/docs/concepts/services-networking/ingress/
- ingress-nginx Helm インストールガイド: https://kubernetes.github.io/ingress-nginx/deploy/
- Helm 公式ドキュメント: https://helm.sh/docs/
- Kustomize 公式ドキュメント: https://kubectl.docs.kubernetes.io/references/kustomize/
- Argo CD 公式ドキュメント: https://argo-cd.readthedocs.io/
- Flux 公式ドキュメント: https://fluxcd.io/flux/
- Kyverno 公式ドキュメント: https://kyverno.io/docs/
- cert-manager 公式ドキュメント: https://cert-manager.io/docs/
- Gateway API 公式ドキュメント: https://gateway-api.sigs.k8s.io/
현재 단락 (1/209)
Ingress 設定は、一度作れば終わりという静的な対象ではありません。TLS 証明書を更新し、タイムアウトやボディサイズ上限を調整し、レートリミットのアノテーションを追加し、新しいサービスが生まれる...