一覧CL-12-1 · Kubernetesの接続と保存

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/null

webと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

1
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/null
pod/web created
service/web created
pod/client created
2
python3 inspect.py dns
name=web.lesson.svc.cluster.local
DNS_A_matches_Service_ClusterIP=True
3
python3 inspect.py endpoints 1 python3 inspect.py http allowed
ready_endpoints=1
target_ports=[8080]
HTTP=allowed
body=ok
4
kubectl patch service web --type=merge -p '{"spec":{"selector":{"app":"missing"}}}' python3 inspect.py endpoints 0 python3 inspect.py dns
service/web patched
ready_endpoints=0
target_ports=[]
name=web.lesson.svc.cluster.local
DNS_A_matches_Service_ClusterIP=True
5
kubectl patch service web --type=merge -p '{"spec":{"selector":{"app":"web"}}}' python3 inspect.py endpoints 1 python3 inspect.py http allowed
service/web patched
ready_endpoints=1
target_ports=[8080]
HTTP=allowed
body=ok
6
kubectl apply -f deny.json python3 inspect.py http blocked python3 inspect.py endpoints 1
networkpolicy.networking.k8s.io/web-ingress created
HTTP=blocked
ready_endpoints=1
target_ports=[8080]
7
kubectl apply -f allow.json python3 inspect.py http allowed
networkpolicy.networking.k8s.io/web-ingress configured
HTTP=allowed
body=ok
8
kubectl label pod client role- python3 inspect.py http blocked
pod/client unlabeled
HTTP=blocked
9
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

演習完了