Serviceによる名前解決と通信制御
名前が解決できること、転送先があること、通信を許可されることを、それぞれの記録から判断します。
説明
ServiceはPodのIPとは別に接続先を提供し、selectorに一致するPodへ通信を届けます。DNS、転送先の選択、NetworkPolicyによる許可は別の条件です。IngressはHTTPのホスト名やパスからServiceへの経路を定義します。
lesson名前空間にHTTP応答を返すwebと、接続するclientを作ります。DNSとEndpointSliceを照合し、selectorの誤りと通信拒否を順に起こしてから、許可するクライアントを限定します。
名前が解決できること、転送先があること、通信を許可されることを、それぞれの記録から判断します。
1サーバーとクライアントを作る
webは8080で固定のHTTP本文okを返します。clientにはrole=clientというラベルを付け、後で許可条件に使います。
kubectl applyPodとServiceを登録する -f web.json -f service.json
kubectl run clientラベル付きクライアントを起動する --image=alpine:3.22 --image-pull-policy=Never --labels=role=client --restart=Never --command -- sleep 3600
kubectl wait両PodのReadyを待つ --for=condition=Ready pod/web pod/client --timeout=60s > /dev/nullwebとclientが準備できました。Serviceの80番をコンテナの8080へ転送する構成です。
2名前とServiceのIPを照合する
client内のnslookupの応答と、ServiceのclusterIPを比較します。名前にはService名、名前空間、svc、クラスタのドメインが含まれます。
python3 inspect.py dnsDNS応答とServiceのIPを照合するweb.lesson.svc.cluster.localのAレコードがServiceのIPと一致します。この通常のServiceで名前解決されるのは個別PodのIPではなく、Serviceに割り当てられたIPです。
3転送先と応答を確認する
EndpointSliceに入ったReadyの転送先とtargetPortを確認し、clientからGETします。
python3 inspect.py endpoints 1Readyの転送先1個とポートを読む
python3 inspect.py http allowedclientからHTTPで取得するReadyの転送先が1個、転送ポートは8080で、本文okが返ります。DNSと転送先とHTTPの三段階がつながっています。
4selectorを誤った値へ変える
Serviceのselectorをapp=missingへ変更します。webのラベルapp=webとは一致しなくなります。
kubectl patch service webPodと一致しないselectorへ変える --type=merge -p '{"spec":{"selector":{"app":"missing"}}}'
python3 inspect.py endpoints 0転送先がなくなることを確認する
python3 inspect.py dnsServiceの名前解決は残ることを確認する転送先は0個ですが、Service自体とDNSの対応は残ります。名前解決の成功だけではアプリへ届くと判断できません。
5selectorを修復する
selectorをapp=webへ戻します。
kubectl patch service webwebに一致するselectorへ戻す --type=merge -p '{"spec":{"selector":{"app":"web"}}}'
python3 inspect.py endpoints 1転送先の回復を確認する
python3 inspect.py http allowed実際の応答を確認する転送先1個とHTTP応答が戻ります。同じService名を使いながら、selectorが転送先の集合を変えています。
6受信を許可しない規則
webを選ぶIngress方向のNetworkPolicyを適用し、許可規則を空にします。ここではNetworkPolicyを実施するネットワーク機能が動いています。
kubectl apply -f deny.jsonwebへの受信を許可しない規則を作る
python3 inspect.py http blockedclientからの取得失敗を確認する
python3 inspect.py endpoints 1転送先は残っていることを確認するHTTPは届かなくなりますが、Readyの転送先は1個のままです。Podの不在ではなく、受信規則が通信を止めています。
7ラベルとポートで許可する
同じ名前空間のrole=clientを持つPodから、TCPの8080へ届く通信だけを許可します。
kubectl apply -f allow.json送信元ラベルと宛先ポートを限定する
python3 inspect.py http allowed許可されたclientの応答を確認するclientからの応答が戻ります。Serviceの80ではなく、宛先Podで使う8080を規則へ指定しています。
8送信元ラベルを外す
同じclientからroleラベルを外します。IPが同じでも、許可するPodの集合から外れます。
kubectl label pod client role-許可条件に使うラベルを外す
python3 inspect.py http blockedラベル変更後の拒否を確認する同じclientからの取得が失敗します。送信元のラベル条件が評価されていることを確認できました。
9Ingressの定義と実装を分ける
HTTPのlearn.example.testをService/webの80番へ送るIngressを作ります。ingressClassNameは処理を担当するコントローラーのクラスを指定します。
kubectl apply -f ingress.jsonHTTP経路の宣言を保存する
kubectl get ingress web担当クラスとServiceへの対応を読む -o jsonpath='{.spec.ingressClassName}{"\n"}{.spec.rules[0].host}{" -> "}{.spec.rules[0].http.paths[0].backend.service.name}{":"}{.spec.rules[0].http.paths[0].backend.service.port.number}{"\n"}'設定はlesson-controllerとweb:80を示します。このクラスタにはそのコントローラーを配置していないため、定義の保存を外部HTTP公開の成功とは扱えません。
まとめ
DNS・EndpointSlice・HTTP応答を順に調べると、失敗箇所を絞れます。
NetworkPolicyの受信許可はPodとポートを対象にします。
Ingressの保存と、コントローラーによる経路の実装は別です。
操作履歴と解説を振り返れます。
Kubernetes 1.37.0 / kind 0.33.0 / Linux arm64
kubectl apply -f web.json -f service.json
kubectl run client --image=alpine:3.22 --image-pull-policy=Never --labels=role=client --restart=Never --command -- sleep 3600
kubectl wait --for=condition=Ready pod/web pod/client --timeout=60s > /dev/nullpod/web created service/web created pod/client created
python3 inspect.py dnsname=web.lesson.svc.cluster.local DNS_A_matches_Service_ClusterIP=True
python3 inspect.py endpoints 1
python3 inspect.py http allowedready_endpoints=1 target_ports=[8080] HTTP=allowed body=ok
kubectl patch service web --type=merge -p '{"spec":{"selector":{"app":"missing"}}}'
python3 inspect.py endpoints 0
python3 inspect.py dnsservice/web patched ready_endpoints=0 target_ports=[] name=web.lesson.svc.cluster.local DNS_A_matches_Service_ClusterIP=True
kubectl patch service web --type=merge -p '{"spec":{"selector":{"app":"web"}}}'
python3 inspect.py endpoints 1
python3 inspect.py http allowedservice/web patched ready_endpoints=1 target_ports=[8080] HTTP=allowed body=ok
kubectl apply -f deny.json
python3 inspect.py http blocked
python3 inspect.py endpoints 1networkpolicy.networking.k8s.io/web-ingress created HTTP=blocked ready_endpoints=1 target_ports=[8080]
kubectl apply -f allow.json
python3 inspect.py http allowednetworkpolicy.networking.k8s.io/web-ingress configured HTTP=allowed body=ok
kubectl label pod client role-
python3 inspect.py http blockedpod/client unlabeled HTTP=blocked
kubectl apply -f ingress.json
kubectl get ingress web -o jsonpath='{.spec.ingressClassName}{"\n"}{.spec.rules[0].host}{" -> "}{.spec.rules[0].http.paths[0].backend.service.name}{":"}{.spec.rules[0].http.paths[0].backend.service.port.number}{"\n"}'ingress.networking.k8s.io/web created lesson-controller learn.example.test -> web:80
演習完了