Ritolabo
  1. Home
  2. Kubernetes
  3. Service を使って Kubernetes の Pod にアクセスする

Service を使って Kubernetes の Pod にアクセスする

  • 公開日
  • カテゴリ:Kubernetes
  • タグ:Kubernetes,学習メモ
Service を使って Kubernetes の Pod にアクセスする

Pod は1つずつ自分の IP を持つ。ただし Pod は作り直されることがあり、そのとき IP も変わる。Pod の IP を宛先にしていると、Pod が入れ替わったときに届かなくなる。

Service は、複数の Pod に対する変わらない宛先を定義するリソース。Pod が入れ替わっても、Service の名前と IP は変わらない。

この記事では、Service をローカルのクラスタに作り、クラスタの中からアクセスして、Pod が入れ替わったときの動きを確認する。

この記事でやること:

  • Pod を作り直したときの IP の変化
  • Service の YAML の確認
  • Service と EndpointSlice の確認
  • クラスタの中から Service へのアクセス
  • アクセスが届いた Pod の確認
  • Pod が入れ替わったときの動き
  • Service の削除

contents

  1. 環境
  2. Pod の IP は変わる
  3. Service の YAML
  4. Service を作る
    1. EndpointSlice
    2. Service・EndpointSlice・Pod の関係
    3. Service や Deployment はどこにあるか
  5. クラスタの中からアクセスする
    1. Service の名前でアクセスする
    2. Service の IP でアクセスする
  6. アクセスはどの Pod に届いたか
  7. Pod が入れ替わったときの動き
  8. Service を消す
  9. Service の種類
  10. 用語メモ
  11. 参考 URL

環境

kind で作ったクラスタ(Control Plane 1台 + Worker 2台)を使う。(作り方は「Kubernetes の Pod とは?コンテナを動かす最小単位を整理する」を参照)

  • 環境: macOS(Apple Silicon)、Docker Desktop 29.8.0、kind v0.33.0、Kubernetes v1.37.0、kubectl v1.36.1

Pod の IP は変わる

nginx の Pod を3つ動かす Deployment を用意する。(Deployment については「Kubernetes の Deployment で Pod を管理する」を参照)

# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.16.1
          ports:
            - containerPort: 80
$ kubectl apply -f nginx-deployment.yaml

# => deployment.apps/nginx-deployment created

$ kubectl get pods -o wide

NAME                                READY   STATUS    RESTARTS   AGE   IP           NODE                NOMINATED NODE   READINESS GATES
nginx-deployment-76df5b8bb8-52kxv   1/1     Running   0          6s    10.244.1.2   k8s-study-worker    <none>           <none>
nginx-deployment-76df5b8bb8-mhjqh   1/1     Running   0          6s    10.244.1.3   k8s-study-worker    <none>           <none>
nginx-deployment-76df5b8bb8-pkrcd   1/1     Running   0          6s    10.244.2.2   k8s-study-worker2   <none>           <none>

Pod は1つずつ別の IP を持っている。ここで Pod を1つ消す。

$ kubectl delete pod nginx-deployment-76df5b8bb8-52kxv

# => pod "nginx-deployment-76df5b8bb8-52kxv" deleted from default namespace

$ kubectl get pods -o wide

NAME                                READY   STATUS    RESTARTS   AGE   IP           NODE                NOMINATED NODE   READINESS GATES
nginx-deployment-76df5b8bb8-mhjqh   1/1     Running   0          48s   10.244.1.3   k8s-study-worker    <none>           <none>
nginx-deployment-76df5b8bb8-pkrcd   1/1     Running   0          48s   10.244.2.2   k8s-study-worker2   <none>           <none>
nginx-deployment-76df5b8bb8-t4jvl   1/1     Running   0          4s    10.244.2.3   k8s-study-worker2   <none>           <none>
  • 消した Pod(...-52kxv、IP 10.244.1.2)はなくなった
  • 代わりに作られた Pod(...-t4jvl)は、名前も IP(10.244.2.3)も別
  • 置かれた Node も、k8s-study-worker から k8s-study-worker2 に変わっている

10.244.1.2 を宛先にしていた場合、Pod が入れ替わった時点で届かなくなる。また、Pod は3つあるので、どの Pod にアクセスするかをアクセスする側が選ぶ必要もある。

Service の YAML

# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
場所意味
metadata.nameService の名前。クラスタの中からは、この名前を宛先にしてアクセスできる
spec.selectorどの label が付いた Pod を届け先にするかの指定
spec.ports.portService が受けるポート
spec.ports.targetPort届け先の Pod のポート。省略すると port と同じ値

selector の app: nginx は、Deployment の template.metadata.labels で Pod に付けている label と同じもの。

nginx-deployment.yaml                    nginx-service.yaml
─────────────────────                    ──────────────────
template:                                spec:
  metadata:                                selector:
    labels:                                  app: nginx
      app: nginx
  • Service の YAML には、Pod の名前も IP も書かない
  • 書くのは「app: nginx の label が付いた Pod」という条件だけ
  • type(Service の種類)を書かなかった場合は ClusterIP になる

Service を作る

$ kubectl apply -f nginx-service.yaml

# => service/nginx-service created

$ kubectl get services

NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubernetes      ClusterIP   10.96.0.1      <none>        443/TCP   3d9h
nginx-service   ClusterIP   10.96.28.227   <none>        80/TCP    17s
  • nginx-service が作った Service。kubernetes は最初からあるもの
  • CLUSTER-IP(10.96.28.227)が、Service に割り当てられた IP。Pod の IP(10.244.x.x)とは別の範囲になっている
  • TYPE の ClusterIP は、クラスタの中からだけアクセスできる種類

EndpointSlice

Service を作ると、EndpointSlice が自動で作られる。Service の selector に一致する Pod への参照(IP)を持つリソース。

$ kubectl get endpointslices

NAME                  ADDRESSTYPE   PORTS   ENDPOINTS                          AGE
kubernetes            IPv4          6443    172.21.0.4                         3d9h
nginx-service-bf75h   IPv4          80      10.244.2.3,10.244.1.3,10.244.2.2   2m12s

$ kubectl get pods -o wide

NAME                                READY   STATUS    RESTARTS   AGE     IP           NODE                NOMINATED NODE   READINESS GATES
nginx-deployment-76df5b8bb8-mhjqh   1/1     Running   0          5m57s   10.244.1.3   k8s-study-worker    <none>           <none>
nginx-deployment-76df5b8bb8-pkrcd   1/1     Running   0          5m57s   10.244.2.2   k8s-study-worker2   <none>           <none>
nginx-deployment-76df5b8bb8-t4jvl   1/1     Running   0          5m13s   10.244.2.3   k8s-study-worker2   <none>           <none>

nginx-service-bf75h の ENDPOINTS に並ぶ3つの IP は、3つの Pod の IP と一致している。

Service・EndpointSlice・Pod の関係

図を描画しています…

Node と Deployment を加えると、次のようになる。

図を描画しています…
  • 実線は Service 経由のアクセスの向き、点線は Deployment(ReplicaSet)が Pod を作って数を保つ関係
  • Node の上にあるのは Pod だけ。Service、EndpointSlice、Deployment、ReplicaSet は、どの Node にも属していない
  • Service は、届け先の Pod がどの Node にあるかを区別しない。2つの Node にある3つの Pod が、同じ Service の届け先になっている
  • 同じ Pod を、Deployment は「数を保つ対象」として、Service は「届け先」として扱っている。どちらも app: nginx の label で Pod を選んでいる
リソース担当誰が作るか
Service宛先(名前、IP、ポート)と、届け先を選ぶ条件(selector)自分で定義する。IP は自動で割り当てられる
EndpointSlice届け先の Pod の IP の一覧コントロールプレーンが自動で作り、更新する
Podコンテナを動かすDeployment(ReplicaSet)が作る

Service や Deployment はどこにあるか

Service や Deployment は、動いているプログラムではなく、YAML に書いた内容が記録されたデータ(オブジェクト)。Control Plane で動いている etcd に保存されている。

k8s-study-control-plane(Control Plane)
  ├─ etcd                      クラスタのすべてのデータの保存場所
  │    ├─ Service        nginx-service
  │    ├─ EndpointSlice  nginx-service-bf75h
  │    ├─ Deployment     nginx-deployment
  │    ├─ ReplicaSet     nginx-deployment-76df5b8bb8
  │    └─ Pod            nginx-deployment-76df5b8bb8-mhjqh など
  ├─ kube-apiserver
  └─ kube-controller-manager

k8s-study-worker / k8s-study-worker2(Worker)
  └─ Pod のコンテナ(nginx)
リソースetcd に保存されるデータNode の上で動くもの
Serviceありなし
EndpointSliceありなし
Deploymentありなし
ReplicaSetありなし
Podありあり(コンテナ)
  • Pod もオブジェクトとして etcd に保存される。Pod だけが Node の上にあるのは、実際に動かすコンテナを持つため
  • Service 用のコンテナが、どこかの Node で動いているわけではない
  • 各 Node では kube-proxy が動いている。kube-proxy は Service と EndpointSlice の追加・削除を監視し、Service の IP とポート宛ての通信を届け先のどれかに転送するように、自分の Node を設定する
  • Service の IP は、特定のホストが応答しているものではなく、仮想 IP として扱われる

(Control Plane のコンポーネントについては「Kubernetes のアーキテクチャを整理する:Control Plane と Node の構成要素」を参照)

クラスタの中からアクセスする

ClusterIP の Service には、クラスタの中からしかアクセスできない。クラスタの中に作業用の Pod を作り、その中からアクセスする。

$ kubectl run tmp --image=busybox:1.36 --restart=Never -- sleep 3600

# => pod/tmp created

$ kubectl get pod tmp

NAME   READY   STATUS    RESTARTS   AGE
tmp    1/1     Running   0          5s
部分意味
kubectl run tmpYAML を使わずに、tmp という名前の Pod を1つ作る
--image=busybox:1.36使う image。busybox は、基本的なコマンドをまとめた小さな image
--restart=Neverコンテナが終了しても、起動し直さない
-- sleep 3600コンテナで実行するコマンド。Pod を動かしたままにしておくためのもの

STATUS が Running になったら、kubectl exec で Pod の中のシェルを起動する。

$ kubectl exec -it tmp -- sh

/ #

ここから先、/ # で始まる行は Pod の中で実行したコマンド。

(busybox:1.28 を使ったときは、シェルや wget が exit code 139 で異常終了することがあった。busybox:1.36 では起きていない)

Service の名前でアクセスする

busybox には curl が入っていないので、wget を使う。宛先は Service の名前。

/ # wget -qO- http://nginx-service

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
(以下省略)

nginx のページが返ってくる。宛先に指定したのは nginx-service という名前だけで、Pod の名前や IP は使っていない。

  • クラスタの中で定義された Service には、DNS 名が割り当てられる
  • Service の DNS 名は、Service の IP(CLUSTER-IP)に名前解決される
  • 名前だけで届くのは、アクセスする Pod と Service が同じ namespace にある場合。別の namespace の Service は Service の名前.namespace の名前 で指定する

Service の IP でアクセスする

CLUSTER-IP を宛先にしても、同じ結果になる。

/ # wget -qO- http://10.96.28.227

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
(以下省略)

アクセスはどの Pod に届いたか

続けて、Pod の中から6回アクセスする。

/ # for i in 1 2 3 4 5 6; do wget -qO /dev/null http://nginx-service; done
/ # exit

ここまでのアクセスは、名前で1回、IP で1回、for で6回の合計8回。どの Pod に届いたかは、nginx のアクセスログで確認できる。

$ kubectl logs -l app=nginx --prefix
部分意味
-l app=nginxapp: nginx の label が付いた Pod すべてのログを出す。Pod ごとに最後の 10 行まで
--prefix行の先頭に、どの Pod のどのコンテナのログかを付ける

次の出力は、作業用の Pod(IP は 10.244.1.7)からのアクセスの行を抜き出したもの。

[pod/nginx-deployment-76df5b8bb8-t4jvl/nginx] 10.244.1.7 - - [29/Sep/2026:12:08:21 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
[pod/nginx-deployment-76df5b8bb8-t4jvl/nginx] 10.244.1.7 - - [29/Sep/2026:12:08:49 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
[pod/nginx-deployment-76df5b8bb8-t4jvl/nginx] 10.244.1.7 - - [29/Sep/2026:12:08:49 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
[pod/nginx-deployment-76df5b8bb8-t4jvl/nginx] 10.244.1.7 - - [29/Sep/2026:12:08:49 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
[pod/nginx-deployment-76df5b8bb8-t4jvl/nginx] 10.244.1.7 - - [29/Sep/2026:12:08:49 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
[pod/nginx-deployment-76df5b8bb8-mhjqh/nginx] 10.244.1.7 - - [29/Sep/2026:12:08:49 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
[pod/nginx-deployment-76df5b8bb8-pkrcd/nginx] 10.244.1.7 - - [29/Sep/2026:12:06:47 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
[pod/nginx-deployment-76df5b8bb8-pkrcd/nginx] 10.244.1.7 - - [29/Sep/2026:12:08:49 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
Pod届いた回数
...-t4jvl5
...-mhjqh1
...-pkrcd2
  • 同じ宛先へのアクセスが、3つの Pod に分かれて届いている
  • 届いた回数は均等ではない
  • kube-proxy の iptables モードでは、届け先の Pod は既定でランダムに選ばれる(このクラスタの kube-proxy のモードは確認していない)

Pod が入れ替わったときの動き

Service がある状態で、Pod を1つ消す。

$ kubectl delete pod nginx-deployment-76df5b8bb8-t4jvl

# => pod "nginx-deployment-76df5b8bb8-t4jvl" deleted from default namespace

$ kubectl get pods -o wide

NAME                                READY   STATUS    RESTARTS   AGE     IP           NODE                NOMINATED NODE   READINESS GATES
nginx-deployment-76df5b8bb8-mhjqh   1/1     Running   0          18m     10.244.1.3   k8s-study-worker    <none>           <none>
nginx-deployment-76df5b8bb8-pkrcd   1/1     Running   0          18m     10.244.2.2   k8s-study-worker2   <none>           <none>
nginx-deployment-76df5b8bb8-wf8x7   1/1     Running   0          8s      10.244.1.8   k8s-study-worker    <none>           <none>
tmp                                 1/1     Running   0          4m49s   10.244.1.7   k8s-study-worker    <none>           <none>

$ kubectl get endpointslices

NAME                  ADDRESSTYPE   PORTS   ENDPOINTS                          AGE
kubernetes            IPv4          6443    172.21.0.4                         3d9h
nginx-service-bf75h   IPv4          80      10.244.1.3,10.244.2.2,10.244.1.8   15m

$ kubectl get services

NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubernetes      ClusterIP   10.96.0.1      <none>        443/TCP   3d9h
nginx-service   ClusterIP   10.96.28.227   <none>        80/TCP    15m
消す前消した後
Service の名前nginx-servicenginx-service
Service の IP10.96.28.22710.96.28.227
EndpointSlice の届け先10.244.2.3、10.244.1.3、10.244.2.210.244.1.3、10.244.2.2、10.244.1.8
  • 消した Pod の IP(10.244.2.3)が EndpointSlice から外れ、新しい Pod の IP(10.244.1.8)が入っている
  • EndpointSlice は同じもの(名前が nginx-service-bf75h のまま)で、中身だけが更新されている
  • Service の名前と IP は変わっていない
  • 作業用の Pod tmp(10.244.1.7)は app: nginx の label を持たないので、届け先に入っていない

Kubernetes は、Service の selector に一致する Pod の集合が変わるたびに、その Service の EndpointSlice を更新する。アクセスする側は、Service の名前だけを宛先にしていればよい。

Service を消す

Service と Deployment は別々のリソースなので、それぞれ消す。作業用の Pod も消す。

$ kubectl delete -f nginx-service.yaml

# => service "nginx-service" deleted from default namespace

$ kubectl delete -f nginx-deployment.yaml

# => deployment.apps "nginx-deployment" deleted from default namespace

$ kubectl delete pod tmp

# => pod "tmp" deleted from default namespace

$ kubectl get services

NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   3d9h

$ kubectl get endpointslices

NAME         ADDRESSTYPE   PORTS   ENDPOINTS    AGE
kubernetes   IPv4          6443    172.21.0.4   3d9h

Service を消すと、EndpointSlice(nginx-service-bf75h)も一緒に消えている。

Service の種類

今回使った ClusterIP のほかにも、Service には種類がある。

種類内容
ClusterIPクラスタ内部の IP で Service を公開する。クラスタの中からだけアクセスできる。type を指定しない場合の既定
NodePort各 Node の IP の、決まったポートで Service を公開する。ポートは既定で 30000〜32767 の範囲から割り当てられる
LoadBalancer外部のロードバランサーを使って、Service をクラスタの外に公開する

用語メモ

用語読み方意味
Serviceサービス複数の Pod に対する宛先を定義するリソース。名前と IP を持つ
ClusterIPクラスターアイピーService の種類の1つ。クラスタの中からだけアクセスできる。Service に割り当てられる IP もこう呼ばれる
EndpointSliceエンドポイントスライスService の届け先(Pod の IP)の一覧を持つリソース
selectorセレクターどの label が付いたリソースを対象にするかの指定
labelラベルリソースに付ける キー: 値 の形の札
kube-proxyキューブプロキシ各 Node で動き、Service 宛ての通信を Pod に転送する設定を行うコンポーネント
etcdエトセディークラスタのすべてのデータの保存場所として使われるキーバリューストア

まとめ

  • Pod の IP は、Pod が作り直されると変わる
  • Service は、複数の Pod に対する宛先を定義するリソース。名前と IP を持ち、Pod が入れ替わっても変わらない
  • Service や Deployment は、動いているプログラムではなく、Control Plane の etcd に保存されたデータ。Node の上で動くのは Pod のコンテナ
  • 届け先の Pod は、selector に書いた label で決まる。Service の YAML に Pod の名前や IP は書かない
  • Service を作ると EndpointSlice が自動で作られる。Pod が入れ替わると、EndpointSlice の中身も自動で更新される
  • クラスタの中からは、Service の名前を宛先にしてアクセスできる
  • Service へのアクセスは、複数の Pod に分かれて届く
  • type を指定しない Service は ClusterIP で、クラスタの中からだけアクセスできる

参考 URL


[Prev] Kubernetes の Deployment で Pod を管理する

Author

rito

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級