Certified Kubernetes Application Developer (CKAD) #19 Ingress と NetworkPolicy
#18 では Service で Pod のまとまりに安定した入口を作りました。しかし Service だけでは 2 つ足りません。1 つは 複数のサービスを 1 つの外部入口からホストとパスで振り分ける L7 ルーティング で、もう 1 つは クラスター内の Pod 同士の通信を、誰が誰に話しかけられるかという単位で制御する ことです。前者が Ingress、後者が NetworkPolicy です。
この記事では、外部から入ってくるトラフィックをホストとパスを基準にルーティングする Ingress と、Pod 間通信をホワイトリストでロックする NetworkPolicy を実技の観点から扱います。どちらも CKAD の Services and Networking ドメインに属しており、マニフェストのフィールドを手に馴染ませることが核心です。ネットワーキングの基礎が曖昧なら K8s 中級 #3 を先に見ておくとよいです。
Ingress: 1 つの入口から L7 ルーティング #
Service の LoadBalancer タイプはサービスごとに外部 IP を 1 つずつ確保します。サービスが増えるほど外部 IP が増え、コストも増えます。Ingress は たった 1 つの入口で HTTP/HTTPS リクエストをホスト名と URL パスで振り分け て内部の Service に転送します。L4 で動作する Service と違い、Ingress は L7 で HTTP ヘッダーの Host とパスを見てルーティングします。
host と path ベースのルーティング #
Ingress の核心フィールドは rules です。各ルールは host (省略可能) と http.paths 配列を持ち、各 path は path 文字列、pathType、そしてトラフィックを受け取る backend を指定します。backend は対象 Service の名前とポートを指します。
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80shop.example.com/api に入ってきたリクエストは api-svc へ、それ以外のパスは web-svc へ行きます。1 つのホストの中でパスごとに別のサービスにトラフィックを振り分ける構造です。
pathType: Prefix と Exact #
pathType はパスをどう照合するかを決めます。CKAD で空欄にしやすい必須フィールドです。
| pathType | 照合方式 |
|---|---|
Prefix | URL パス要素単位の接頭辞照合。/api は /api、/api/v1 に照合 |
Exact | パスが正確に一致するときのみ照合。大文字小文字を区別 |
ImplementationSpecific | コントローラの実装に委任 |
実務ではほとんど Prefix を使います。pathType を省略するとマニフェストが拒否されたり意図と違う動作をしたりするので、path ごとに必ず埋める必要があります。
IngressClass と defaultBackend #
クラスターには複数種類の Ingress Controller があり得るので、どのコントローラがこの Ingress を処理するかを spec.ingressClassName で指定します。以前は kubernetes.io/ingress.class アノテーションを使いましたが、今は ingressClassName フィールドが標準です。
spec:
ingressClassName: nginxどのルールにも照合しないリクエストを受ける既定のバックエンドは spec.defaultBackend で置けます。指定しなければ照合しないリクエストはコントローラの既定の 404 に落ちます。
TLS: secret 参照で HTTPS 終端 #
Ingress は spec.tls で HTTPS を終端します。証明書と鍵は kubernetes.io/tls タイプの Secret に入れ、Ingress はその Secret 名を参照します。
spec:
tls:
- hosts:
- shop.example.com
secretName: shop-tlshosts に書いたホストに入ってくる HTTPS リクエストに shop-tls Secret の証明書を適用します。Secret 自体は kubectl create secret tls shop-tls --cert=tls.crt --key=tls.key であらかじめ作っておきます。
Ingress Controller がなければ動作しない #
Ingress は ルールを書いた仕様にすぎず、それ自体では何もしません。 実際にトラフィックを受け取ってルーティングするのは ingress-nginx や Traefik のような Ingress Controller です。コントローラは Ingress オブジェクトを監視し、そのルールどおりにリバースプロキシを構成します。したがってクラスターに Ingress Controller がインストールされていなければ、Ingress をいくら作っても外部トラフィックは入ってきません。CKAD 環境には通常コントローラがあらかじめインストールされているので、kubectl get ingressclass で利用可能なクラス名をまず確認し、その名前を ingressClassName に入れます。
NetworkPolicy: Pod 間通信をホワイトリストでロックする #
既定状態の Kubernetes クラスターは すべての Pod が互いに自由に通信 します。どの Pod でも他の Pod の IP に接続できる all-allow が既定値です。NetworkPolicy はこの開いた通信にファイアウォールルールをかけるリソースです。
核心の動作は 1 文で要約できます。NetworkPolicy が 1 つも選択しない Pod は依然として all-allow ですが、いずれかの NetworkPolicy が選択した Pod はそのポリシーに明記されたトラフィックのみ許可します。 つまりポリシーが付いた瞬間、その Pod はホワイトリスト方式に切り替わります。
podSelector と policyTypes #
NetworkPolicy の適用対象は spec.podSelector で選びます。このセレクターに照合する、同じネームスペースの Pod がポリシーの適用対象です。
spec.policyTypes は、このポリシーが入ってくるトラフィック (Ingress) を制御するか、出ていくトラフィック (Egress) を制御するか、両方かを決めます。
spec:
podSelector:
matchLabels:
app: db
policyTypes:
- Ingress
- EgresspolicyTypes に Ingress があれば、入ってくる接続は spec.ingress ルールに明記された出所だけが許可され、明記されていない出所はすべて遮断されます。Egress も同じ方式で出ていく接続を制御します。
ingress.from と egress.to #
許可する出所と宛先は 3 つのセレクターで表現します。
| セレクター | 意味 |
|---|---|
podSelector | 同じネームスペース内で label で選んだ Pod |
namespaceSelector | label で選んだネームスペース全体 |
ipBlock | CIDR 範囲 (cidr) と例外 (except) で指定した IP 帯域 |
ingress.from の下にこれらのセレクターを並べると、その出所から来るトラフィックのみ許可されます。ここに YAML のインデント 1 つが意味を変える落とし穴 があります。1 つの from 項目の中に podSelector と namespaceSelector を一緒に置くと 両方を満たす (AND) 必要があり、- で項目を分けると 2 つのうちどちらか一方を満たす (OR) だけでよくなります。
# AND: 指定ネームスペース内の、指定 label の Pod のみ
ingress:
- from:
- namespaceSelector:
matchLabels:
team: backend
podSelector:
matchLabels:
app: web
# OR: 指定ネームスペース全体、または同じ ns の指定 label の Pod
ingress:
- from:
- namespaceSelector:
matchLabels:
team: backend
- podSelector:
matchLabels:
app: webポート制限 #
ingress と egress ルールには ports を加えて許可ポートを狭められます。from や to で出所を選び、ports でその出所がアクセスできるポートまで制限する形です。
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 5432app: web Pod から来る TCP 5432 接続のみ許可し、残りは遮断します。
default deny パターン #
最もよく出題されるパターンが、ネームスペース全体に既定の遮断をかける default deny です。podSelector を空の値 ({}) にするとネームスペースの すべての Pod が対象になり、ingress ルールを空にすると入ってくるトラフィックが全部遮断されます。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: prod
spec:
podSelector: {}
policyTypes:
- IngresspolicyTypes に Ingress だけがあり spec.ingress がないので、prod ネームスペースのすべての Pod へ入ってくる接続が全部遮断されます。ここに個別の許可ポリシーを追加で付けてホワイトリストを広げていくのが標準の運用方式です。出ていくトラフィックまで遮断するには policyTypes に Egress を加え、egress を空にします。
CNI が NetworkPolicy をサポートする必要がある #
NetworkPolicy も Ingress と同じく仕様にすぎず、これを実際に施行するのはクラスターの CNI プラグイン です。Calico や Cilium のような CNI は NetworkPolicy をサポートしますが、一部の単純なネットワークプラグインはポリシーを無視します。つまり NetworkPolicy を作っても CNI がサポートしなければ通信がそのまま開いていることがあるので、試験環境ではポリシー適用後に実際に遮断されるか必ず確認します。
実戦 YAML 例 #
Ingress: host + path + TLS #
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ingress
namespace: shop
spec:
ingressClassName: nginx
tls:
- hosts:
- shop.example.com
secretName: shop-tls
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80shop.example.com に入ってくる HTTPS リクエストを shop-tls 証明書で終端し、/api は api-svc:8080 へ、残りは web-svc:80 へルーティングします。
NetworkPolicy: 特定の label からのみ ingress 許可 + default deny #
まずネームスペース全体を遮断する default deny を敷き、その上に app: web Pod から来る DB アクセスのみ開けるポリシーを載せます。
# 1) 既定の遮断
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: shop
spec:
podSelector: {}
policyTypes:
- Ingress
---
# 2) web から db への 5432 のみ許可
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-to-db
namespace: shop
spec:
podSelector:
matchLabels:
app: db
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 5432app: db Pod は両方のポリシーに選択されますが、NetworkPolicy は和集合で動作するので、結果的に app: web Pod から来る TCP 5432 のみ許可 され、それ以外のすべての ingress は遮断されます。
デバッグ: ポリシーが実際に遮断するか確認する #
NetworkPolicy は適用しても結果がすぐ目に見えないので、自分で接続を試みて遮断されるかを確認する必要があります。一時的な Pod を立ち上げて対象の Service や Pod へ接続をかける方式が最も速いです。
# ポリシー適用前: 接続が成功すれば正常
k run probe --image=busybox --rm -it --restart=Never -- \
wget -qO- --timeout=3 db-svc:5432
# ポリシー適用後: 許可されていない出所ならタイムアウトで遮断される
k run probe --image=busybox --rm -it --restart=Never -- \
wget -qO- --timeout=3 db-svc:5432許可された label を持つ Pod から送れば通り、そうでない Pod から送るとタイムアウトになります。ポリシーが意図どおりに動作しないとき確認する箇所は次のとおりです。
kubectl get networkpolicy -n <ns>でポリシーが正しいネームスペースにあるか確認kubectl describe networkpolicy <name>でpodSelectorが意図した Pod を選ぶか確認- 対象 Pod と出所 Pod の label がセレクターと正確に一致するか確認
from項目のインデントで AND/OR が入れ替わっていないか確認- 通信が最後まで遮断されないなら CNI が NetworkPolicy をサポートするか確認
Ingress 側が動作しないときは kubectl describe ingress <name> でルールと backend Service が正しく接続されているか見て、kubectl get ingressclass で指定したクラスが実際に存在するか確認します。backend Service の名前やポートのタイプミスが最もよくある原因です。
試験ポイント #
- Ingress は L7 ルーティング です。
rules.hostとhttp.pathsでホストとパスごとに backend Service へトラフィックを振り分けます。 pathTypeは必須 です。ほとんどPrefix、正確な照合が必要なときだけExactを使います。ingressClassNameでコントローラを指定 します。kubectl get ingressclassで名前をまず確認します。- TLS は
kubernetes.io/tlsSecret をspec.tls.secretNameで参照 します。Secret はkubectl create secret tlsで作ります。 - Ingress は Ingress Controller がなければ動作 しません。仕様だけではトラフィックは流れません。
- NetworkPolicy が選択した Pod はホワイトリストに切り替わり ます。ポリシーがなければ all-allow が既定です。
- default deny は
podSelector: {}+policyTypesで作ります。ingress/egressを空にして全部遮断します。 from項目の中のセレクターは AND、-で分けた項目は OR です。インデント 1 つが意味を変えます。- NetworkPolicy は CNI がサポートしてはじめて施行 されます。適用後に実際に遮断されるか確認します。
まとめ #
この記事で押さえたこと:
- Ingress 。host/path ルーティング、
pathType(Prefix/Exact)、rules/backend(service+port)、ingressClassName、defaultBackend、TLS Secret 参照、そして Ingress Controller への依存 - NetworkPolicy 。all-allow 既定値、ポリシーが付いた Pod のホワイトリストへの切り替え、
podSelector/policyTypes、ingress.from/egress.toの 3 つのセレクター、ポート制限 - default deny パターン とその上に許可ポリシーを積む運用方式、CNI への依存とデバッグ手順
Service から始めて、Ingress で外部入口を、NetworkPolicy で内部通信制御を仕上げました。2 つのリソースのセレクター構文をもう一度固めたいなら K8s 中級 #7 で label とセレクターの動作を復習するとよいです。
次へ: 試験のコツと時間管理 #
ドメイン別の知識は #18 と #19 で仕上がりました。残るは その知識を 2 時間以内に点数に変える運用力 です。
#20 試験のコツと時間管理、よく間違えるパターン では、作業ごとの時間配分、部分点を最大化する解答順序、kubectl explain と dry-run を活用した速度戦略、そして context 切り替えの漏れや pathType の漏れのように受験者が繰り返し間違えるパターンをまとめて整理します。