Service

Kubernetes で Pod 群への安定したネットワークアクセスを提供する抽象化リソース

Kubernetesネットワーク
Service」の技術書を見る →

Service とは

Service は Kubernetes のリソースで、動的に変化する Pod 群に対して安定した IP アドレスと DNS 名を提供する。Pod は作成・削除のたびに IP が変わるが、Service は固定の ClusterIP を持ち、ラベルセレクタで対象の Pod にトラフィックを振り分ける。

Service の種類

種類は 4 つあり、type を省略した場合は ClusterIP になる。

タイプ届く範囲実現のしかた注意する点
ClusterIPクラスター内部のみノード上の規則で宛先を書き換え外部からは届かない
NodePort全ノードの IP + 割り当てポートClusterIP に加え、全ノードが同じ番号を待ち受けポートは --service-node-port-range の範囲 (既定 30000-32767) から採られる
LoadBalancer外部NodePort に加え、外部のロードバランサーを払い出し何が払い出されるかはクラウド側のコントローラー次第
ExternalName内部から外部の名前へDNS が CNAME を返すだけ転送も代理もしないため名前の食い違いが起きる

前の 3 つは積み増しの関係にある。LoadBalancer を作れば、内部からは ClusterIP として、ノード経由なら NodePort としても同じ Pod 群に届く。

ExternalName だけは仕組みが根本的に違う。my-service.prod.svc.cluster.local を引いたクライアントに外部の名前の CNAME を返して終わりで、トラフィックは Service を通らない。そのため相手が HTTP なら Host ヘッダーはクラスター内の名前のまま外部サーバーへ届き、TLS なら接続した名前に一致する証明書を出せない。外部の HTTPS エンドポイントを ExternalName で包むと、この食い違いで動かないことが多い。

ClusterIP (デフォルト)

selector に一致する Pod が振り分け先になり、受け口の port と Pod 側の targetPort を対応づける。

apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  type: ClusterIP
  selector:
    app: order-service  # このラベルを持つ Pod にトラフィックを振り分け
  ports:
    - port: 80          # Service のポート
      targetPort: 3000  # Pod のポート

クラスター内の他の Pod から http://order-service でアクセスできる。Kubernetes の内部 DNS が order-service.default.svc.cluster.local を解決する。

LoadBalancer (外部公開)

定義は ClusterIP に type を足すだけで、外部のロードバランサー自体はクラウド側のコントローラーが作る。

apiVersion: v1
kind: Service
metadata:
  name: api-gateway
spec:
  type: LoadBalancer
  selector:
    app: api-gateway
  ports:
    - port: 443
      targetPort: 8080

何が払い出されるかは、クラスターに入れたコントローラーで変わる。2026 年 8 月時点の Amazon EKS のドキュメントでは、Kubernetes 本体に同梱のロードバランサーコントローラーは既定で Classic Load Balancer を作り、今後は重大な不具合修正のみを受ける状態と説明されている。推奨されているのは別途導入する AWS Load Balancer Controller (バージョン 2.7.2 以降) で、こちらは Classic Load Balancer を作らず NLB (Network Load Balancer) を作る。ただし上の定義のままでは同梱側が処理してしまうため、NLB を作らせるには注釈が必要になる。

metadata:
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: external
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip

aws-load-balancer-type: external が「同梱のコントローラーではなく AWS Load Balancer Controller が扱う」という指定である。nlb-target-type: ip にすると Pod の IP を直接ターゲットにできるため、ノードを経由する分のホップが減る。「EKS なら type: LoadBalancer と書けば NLB ができる」と覚えたまま注釈を省くと、意図と違う種類のロードバランサーが立ち上がる。

Service の仕組み

Service の IP は、どこかのプロセスが実際に待ち受けているアドレスではない。パケットが出ていく手前でノード上の規則が宛先を書き換えることで成立している。

[クライアント]Service (ClusterIP: 10.0.0.1:80)
                    ↓ kube-proxy がトラフィックを振り分け
                [Pod A (10.0.1.1:3000)]
                [Pod B (10.0.1.2:3000)]
                [Pod C (10.0.1.3:3000)]

この書き換え規則を各ノードで維持するのが kube-proxy である。Pod が追加・削除されるとエンドポイントの一覧が更新され、規則もそれに追従する。

規則の書き方 (proxy mode) は Linux では移り変わってきた。2026 年 8 月時点の Kubernetes ドキュメントでは iptables mode が既定で、その後継として nftables mode が用意されている。nftables mode は 1.36 の時点でも比較的新しく、すべてのネットワークプラグインと組み合わせられるとは限らない。IPVS mode は v1.35 で非推奨になった。ここまではいずれも Linux ノード向けで、Windows ノードでは kernelspace mode が使われる。古い資料にある「iptables か IPVS の二択」という説明は、現在の選定の材料としては使えない。

Headless Service

負荷分散も単一の IP も要らない場合は、clusterIPNone を明示する。

spec:
  clusterIP: None  # Headless Service

仮想 IP を持たないため、kube-proxy による書き換えも起きない。セレクタを持つ headless Service では、クラスター DNS が Service 名に対して対象 Pod の IP を並べた A / AAAA レコードを返し、どの Pod へつなぐかはクライアント側が決める。StatefulSet の DB クラスターのように、レプリカを 1 台ずつ名前で指したい場合に使う。

Ingress との関係

Service はレイヤー 4 (TCP/UDP) のロードバランシングを提供する。レイヤー 7 (HTTP) のルーティング (パスベース、ホストベース) が必要な場合は Ingress を使い、Ingress が Service にトラフィックを振り分ける。

[外部][Ingress] → /api → [api-service][Pod]
                   → /web → [web-service][Pod]

ただし、これから設計するなら Ingress で止めない方がよい。2026 年 8 月時点の Kubernetes ドキュメントでは、Ingress API は凍結されたと明記されている。一般提供のまま削除の予定も無いが、以後の開発は行われず変更も入らない。プロジェクトが推奨しているのは Gateway API である。レイヤー 4 の Service の上にレイヤー 7 の経路制御を載せるという層の関係は変わらないので、載せる側を Ingress から差し替える形になる。

つまずきやすい点

  • 名前は引けるのにつながらない場合、まず疑うのは selector のラベルと Pod 側のラベルの不一致である。一致する Pod が 1 つも無くても Service の作成と名前解決は成功するため、エンドポイントが空のまま接続だけが失敗する。
  • porttargetPort の取り違えも同じ見え方になる。Pod が実際に待ち受けている番号は targetPort の側である。
  • NodePort で外部公開を済ませてしまうと、ノードの増減や入れ替えのたびに宛先の管理が必要になる。外部から常時受けるなら、ロードバランサーや経路制御の層に任せる。

PodDeploymentIngress と合わせて押さえると、クラスター内の通信経路を一通り追えるようになる。

この記事は役に立ちましたか?

関連用語

関連する記事