はじめに
- こんにちは、新川です。先日執筆した以下の記事「Amazon EKS + Karpenter ハンズオン」では、EKSクラスタと高性能なオートスケーラーであるKarpenter の構築手順をハンズオン形式でご紹介しました。また、EKS の重要な機能であるPod Identity、AWS Load Balancer Controller に焦点を当てたハンズオンも続編のブログに執筆しています。
[ハンズオン] EKS + Karpenter のデプロイ・トラブルシューティング演習
EKS Pod Identity:S3アクセスを例に学ぶPod単位のIAM制御
EKS と AWS Load Balancer Controller で実現するPodへのアクセス制御
- 今回の記事では、EKS のベースであるKubernetes におけるネットワークの基本であるPod 間通信と名前解決の仕組みを、学びます。また、次回公開予定の後半では、Pod 単位にファイアウォール(セキュリティグループ)を割り当て、より高度なアクセス制御を実現するハンズオンに挑戦しますので、ご期待ください!
- 本記事では、上記のハンズオンで作成したEKS と Karpenter 環境があることを前提に進めます。
Pod 間はどうやって通信するのか?
- Kubernetes では、各Pod が固有のIPアドレスを持ち、Pod 同士はこのIPアドレスを使って直接通信します。しかし、この仕組みには大きな課題があります。
Pod のIPは固定できない
- Kubernetes におけるPod は、揮発的(いつかは消える存在)な前提で設計されています。障害からの復旧や、負荷に応じたスケールアウト・スケールイン(オートスケーリング)の際、Pod は頻繁に削除が行われたり、新しいPod に置き換わったりします。
- Pod が再作成されるたびに、新しいIPアドレスが割り当てられます。そのため、特定のPod のIPアドレスを直接アプリケーションに書き込んでしまうと、そのPod が再作成された瞬間に通信ができなくなってしまいます。
CoreDNS を名前解決に使用する
- このIPアドレスが固定できない課題を解決するのが、サービスディスカバリーの仕組みです。Kubernetes では、この役割をService リソースとCoreDNS が担います。
- Service:複数のPod を論理的なグループとして束ね、そのグループに対して単一の固定的な宛先(クラスタIPという仮想IPアドレス)と名前(例:my-service)を提供します。
- CoreDNS:EKSクラスタにアドオンとして標準でインストールされているDNSサーバーです。クラスタ内の「電話帳」のような役割を果たします。
名前解決の仕組み
- アプリケーションが「http://my-service」のようなサービス名で通信を開始しようとします。
- このリクエストは、まずCoreDNS に送られます。CoreDNS は、my-service という名前から、対応するService のクラスタIP を調べ、アプリケーションに応答します。
- アプリケーションは、受け取ったクラスタIP 宛に通信を開始します。
- この仕組みのおかげで、Pod のIPアドレスがどれだけ変わっても、アプリケーションは常に固定の「サービス名」で通信を続けることができます。
PodのDNS設定
- PodがCoreDNS を使えるのは、Pod が作成される際に、Pod内の/etc/resolv.conf ファイルに、DNSサーバーとしてCoreDNS のService IP(クラスタIP)が自動で設定されるためです。
- 詳細は、Kubernetes 公式ドキュメントを参照。
FQDN による名前解決
- Kubernetes のDNS は、Service が作成されると自動的にDNSレコードを作成します。そのため、my-service.my-namespace.svc.cluster.local のようなFQDN(完全修飾ドメイン名)でアクセスできます。
- 異なるNamespace 間の通信:FQDN での指定が必須です。
- 例:curl http://my-service.my-namespace.svc.cluster.local
- 同じNamespace 内の通信:Service 名だけで名前解決が可能です。Kubernetes のDNS は、まず同じNamespace 内を探すためです。
- 例:curl http://my-service
今回の検証シナリオ
- このハンズオンのシナリオを記載します。
- サーバーPod(nginx-deployment):Nginx サーバーを起動し、リクエストを待ち受けます。
- クライアントPod(client-pod):サーバーPod に対して、Service名(nginx-service) を使って名前解決とHTTP通信を試みます。
- ゴール:client-pod からnginx-service への名前解決と通信が成功することを確認し、Kubernetes の基本的なネットワークの仕組みを理解します。
【ハンズオン】Pod間通信と名前解決の実践
手順1:サーバーとクライアントのリソースを作成する
- Deployment、Service、Pod を定義した以下のYAML を作成します。
- 以下に示すコードを使用します。(例:pod-communication-test.yaml )
- コードの内容を一部説明します。
- 「image: nicolaka/netshoot:latest」はコンテナのイメージを指定しています。nicolaka/netshoot のイメージは、Docker + Kubernetes のネットワークのトラブルシューティングに利用することが多いイメージとなります。
- 「command: [“/bin/sh”, “-c”, “sleep 3600”]」は、コンテナが起動した時にに実行するコマンドを指定しています。Linux のシェルを起動後、3600秒の待ち時間を設定しています。Pod は起動後にすぐに終了することなく、kubectl exec コマンドでコンテナに入って作業をすることが可能です。
- 「restartPolicy: Never」は、Pod 内のコンテナが終了した際の再起動ポリシーを指定しています。Never の振る舞いは、以下の通りです。
- コンテナがエラーで終了した場合、コンテナは再起動されず、Pod自体のステータスが Failed になります。
- コンテナが正常に終了した場合、コンテナは再起動されず、Pod自体のステータスは Completed になります。
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: public.ecr.aws/nginx/nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 type: ClusterIP --- apiVersion: v1 kind: Pod metadata: name: client-pod spec: containers: - name: netshoot image: nicolaka/netshoot:latest command: ["/bin/sh", "-c", "sleep 3600"] restartPolicy: Never
- EKS クラスタに、Deployment、Service、Pod のリソースをデプロイします。kubectl apply コマンドでデプロイ後、kubectl get pods コマンドでステータスを確認します。Running が確認できたら、コマンドはCtrl + c で停止します。
~ $ kubectl apply -f pod-communication-test.yaml deployment.apps/nginx-deployment created service/nginx-service created pod/client-pod created ~ $ kubectl get pods -w NAME READY STATUS RESTARTS AGE client-pod 0/1 ContainerCreating 0 4s nginx-deployment-6c644f6bd9-k2gld 1/1 Running 0 4s client-pod 1/1 Running 0 11s ^C~ $
手順2:名前解決をテストする(nslookup)
- kubectl exec コマンドでPod に接続し、オペレーションを行います。
- nslookup コマンドで名前解決ができることを確認します。
~ $ kubectl exec -it client-pod -- /bin/sh ~ # hostname client-pod ~ # ~ # nslookup nginx-service ;; Got recursion not available from 172.20.0.10 Server: 172.20.0.10 Address: 172.20.0.10#53 Name: nginx-service.default.svc.cluster.local Address: 172.20.231.164 ;; Got recursion not available from 172.20.0.10 ~ #
手順3:Pod間通信をテストする(curl)
- curl コマンドで、nginx-service のService に対し、http の疎通を行います。
- 200 OK のレスポンス、Nginx のウェルカムページ「Welcome to nginx!」が返りました。
~ # curl -v http://nginx-service * Host nginx-service:80 was resolved. * IPv6: (none) * IPv4: 172.20.231.164 * Trying 172.20.231.164:80... * Connected to nginx-service (172.20.231.164) port 80 * using HTTP/1.x > GET / HTTP/1.1 > Host: nginx-service > User-Agent: curl/8.14.1 > Accept: */* > * Request completely sent off < HTTP/1.1 200 OK < Server: nginx/1.29.1 < Date: Sat, 23 Aug 2025 08:56:27 GMT < Content-Type: text/html < Content-Length: 615 < Last-Modified: Wed, 13 Aug 2025 14:33:41 GMT < Connection: keep-alive < ETag: "689ca245-267" < Accept-Ranges: bytes <Welcome to nginx!
If you see this page, the nginx web server is successfully installed and working. Further configuration is required.
For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.Thank you for using nginx.
* Connection #0 to host nginx-service left intact ~ # ~ # ~ # exit
まとめと次回予告
- Pod からnslookup コマンドで名前解決ができたことは、CoreDNS が正常に機能していることを表します。また、curl で疎通に成功し、名前解決からPod間通信までの一連の流れが確認できました。今回のハンズオンで、Kubernetes の基本的なネットワーク機能であるServiceとCoreDNSの役割を体感いただけたのではないでしょうか。
- 今回はPod 間の通信がすべて許可されていましたが、実際のアプリケーションではPod ごとにアクセス制御が必要です。次回は、この基本を応用し、Pod 1つ1つにAWSのセキュリティグループを割り当て、より実践的なネットワークの通信制御を構築するハンズオンに挑戦します!