Kubernetesのサービスディスカバリー入門 〜Service、Endpoint、EndpointSliceの基礎と活用〜

## はじめに

Kubernetes でアプリケーションを運用していると、「Service」や「Endpoint」という用語は頻繁に目にします。さらに近年では「EndpointSlice」の利用も増えており、

* 「Pod の IP アドレスが頻繁に変わるのに、どのようにルーティングを維持しているのか？」
* 「EndpointSlice はなぜ導入され、どんなメリットがあるのか？」

といった疑問を持たれる方も多いかと思います。

本記事では、「Service」「Endpoint」「EndpointSlice」がそれぞれどのような役割を果たし、kube-proxy と連携してどのようにネットワーク転送を実現しているのかをわかりやすく解説します。また、EndpointSlice の導入背景や、スケーラビリティ面・運用面でのメリットについてもあわせて紹介します。

## 目次

1. Serviceとは
   * Serviceが解決する問題
   * 主なServiceタイプ（ClusterIP、NodePort、LoadBalancer、ExternalName）
2. Endpointとは
   * Endpointsリソースの概要
   * 従来の課題
3. EndpointSliceとは
   * EndpointSlice導入の背景
   * EndpointSliceの特徴とメリット
   * EndpointSliceに含まれる情報
4. kube-proxyとEndpointSliceの関係
   * kube-proxyの基本的な動作
   * EndpointSlice使用のメリット
5. まとめと運用のヒント

## 1. Serviceとは

Kubernetes の Pod は一時的な存在であり、いつでも再作成・削除が行われる可能性があります。これに伴って IP アドレスも変化するため、サービス提供者への接続先が変わらないように管理するのは大変です。そこで役立つのが、Service という仕組みです。

Service は、常に変わり続ける Pod の集合に対して"安定したネットワークエンドポイント"を提供する Kubernetes の抽象化オブジェクトです。これによって、クライアントは変動する Pod の IP アドレスを意識せず、固定の IP や DNS 名を使ってアクセスできます。

### Serviceが解決する問題

もしクライアントが直接 Pod の IP アドレスに接続していたら、次のような問題に直面します。

* Pod IP の管理が複雑になる
  * Pod が再作成やスケーリングのたびに、クライアント側の設定を更新しなければならない。
* 可用性の低下
  * Pod が落ちたときに、自動的に他の正常な Pod にトラフィックを振り分ける仕組みがない。

Service は "特定のラベルを持つ Pod グループ" への入り口として安定した IP（ClusterIP）や DNS 名を提供し、これらの煩雑さを解決します。クライアントは Service の IP（または DNS）だけを指定すればよく、実際の Pod へのルーティングは Kubernetes に任せられます。

### 主なServiceタイプ

Kubernetes の Service にはいくつか種類があります。

1. ClusterIP（デフォルト）
   * クラスタ内でのみ到達できる仮想 IP（ClusterIP）を割り当てる
   * クラスタ内部（Pod や Node）からしかアクセスできない
   * Kubernetes DNS （例：service-name.namespace.svc.cluster.local）が ClusterIP を指すため、Pod 間通信をシンプルにできる

2. NodePort
   * クラスタ内の全 Node で同じポートを割り当てる（デフォルトは30000～32767）
   * Node の IP とこのポートを指定すれば、クラスタ外からアクセス可能
   * ロードバランサを使えないオンプレミス環境や開発・テスト用途でよく利用される

3. LoadBalancer
   * クラウドプロバイダ（AWS ELB、GCP LB、Azure LBなど）のマネージドロードバランサと連携
   * パブリック IP やホスト名を発行し、クラスタ内の Pod へトラフィックを振り分ける
   * 本番環境など、外部から直接アクセスが必要な場合によく使われる

4. ExternalName
   * DNS の CNAME レコードのように振る舞い、クラスタ内サービス名を外部ホストに紐づける
   * 例：my-db-service → actual-db.example.com のようにリダイレクト

### Service作成プロセス（簡略版）

1. kubectl apply -f service.yaml で Service 定義を適用
2. Kubernetes の制御プレーン（kube-controller-manager）が、Service のラベルセレクターに合致する Pod を見つけ、対応する Endpoints と EndpointSlice を自動作成・更新
3. 各 Node 上の kube-proxy がそれらの更新を監視し、Service IP/ポートから Pod へトラフィックを転送するための iptables/IPVS/eBPF ルールを設定

## 2. Endpointとは

### Endpointsリソースの概要

* Endpoints（kind: Endpoints） は、対応する Service を支える Pod の IP アドレスとポートを列挙するオブジェクトです
* kubectl get endpoints や kubectl describe endpoints <service-name> で確認できる
* IP アドレスとポートの詳細を含む subsets 配列を持つ
* 制御プレーンが自動管理し、通常はユーザーが直接触ることは少ない

### 従来の課題

1. スケーラビリティ
   * 大規模な環境では、1つの Service が数百〜数千もの Pod を抱える場合がある
   * Endpoints オブジェクトが巨大化し、Pod の増減のたびに大きなオブジェクトを更新する必要がある

2. APIサーバーの負荷増大
   * kube-proxy は API サーバーと watch で接続し、Endpoints の更新を受け取る
   * 巨大な Endpoints を頻繁にやり取りすると、API サーバー側の通信量・CPU 使用率が大きくなる

3. 部分更新のしにくさ
   * Pod の状態が1つ変わっただけでも、Endpoints 全体を更新する必要があり、効率が悪い

こうした問題を解決するために開発されたのが EndpointSlice です。

## 3. EndpointSliceとは

### EndpointSlice導入の背景

* Kubernetes 1.16 でアルファ版として導入され、1.19 以降ではデフォルトで有効化
* 単一の巨大な Endpoints オブジェクトにすべての Pod 情報を入れる非効率を解消するために設計された

### EndpointSliceの特徴とメリット

1. 分割管理
   * Pod 情報を複数の EndpointSlice リソースに分散
   * 1 つの EndpointSlice には最大100件（デフォルト）のエンドポイントを含めるなど、オブジェクトサイズを制限できる

2. 更新効率の向上
   * 変更のあった Slice だけを更新するため、Pod の増減が頻繁でも不要な大規模リソース書き換えが起きにくい

3. 豊富なメタデータ
   * nodeName、zone、ready / serving / terminating 状態などを持ち、トポロジーを意識した高度なルーティングを可能にする

4. サービスとの自動連携
   * Endpoints と同様に Service コントローラーが自動生成・更新を行い、ユーザーが直接管理する必要はほぼない

### EndpointSliceの情報例

```yaml
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: example-slice
  labels:
    kubernetes.io/service-name: my-service
addressType: IPv4
endpoints:
  - addresses:
      - 10.244.1.6
    conditions:
      ready: true
      serving: true
      terminating: false
    targetRef:
      kind: Pod
      namespace: default
      name: my-pod-6c7f79bcc5-4q5m2
    nodeName: node-1
ports:
  - name: http
    protocol: TCP
    port: 80
```

ポイント:

* addressType は IPv4 / IPv6 / FQDN が指定可能
* endpoints[] に Pod の IP と状態が含まれる
* targetRef で実際の Pod オブジェクトを参照
* nodeName / zone などのトポロジー情報でレイテンシの最適化ができる
* ports[] でポート番号・プロトコルを指定

## 4. kube-proxyとEndpointSliceの関係

### kube-proxyの基本動作

1. 監視メカニズム
   * kube-proxy は各 Node で動き、Kubernetes API を watch して Service、Endpoints、EndpointSlice の変更を受け取る

2. キャッシュ更新
   * 変更を検知すると、kube-proxy 内部のキャッシュが更新される

3. ルール作成
   * 最新のキャッシュ情報に基づき、Node 上の iptables / IPVS / eBPF ルールを構成
   * Service IP / ポート宛のパケットが、対応する EndpointSlice 内の Pod へ転送されるように設定する

### EndpointSlice使用のメリット

1. 大規模なクラスターへの対応
   * 更新が必要な Slice のみ小分けで変更されるため、API サーバーやネットワークへの負荷を抑えられる

2. トポロジー情報の活用
   * kube-proxy は nodeName や zone などを参考に、最も近いノードやゾーンへのルーティングを最適化できる

3. 更新の粒度向上
   * 頻繁に Pod がスケールイン/アウトする環境でも、必要最小限のリソースが更新されるため無駄が少ない

## 5. まとめと運用のヒント

### Service、Endpoint、EndpointSliceの関係

* Service: 安定したアクセス先として IP / DNS を提供し、Pod グループへ負荷分散を行う
* Endpoint / EndpointSlice: その裏側で実際にトラフィックを受け取る Pod の IP / ポート情報を管理する
* kube-proxy: 各 Node に配置され、EndpointSlice などの更新を反映しながらネットワーク転送ルールを動的に設定する

### デバッグ方法

Service にアクセスできないときの基本的なチェックポイントは以下のとおりです。

1. Service設定の確認
```bash
kubectl describe service <service-name>
```
* ポート定義やラベルセレクターが正しいかを確認

2. EndpointSlice の状態確認
```bash
kubectl get endpointslice -l kubernetes.io/service-name=<service-name>
```
* 該当エンドポイントの ready が true になっているか
* addressType がクラスタの IP ファミリー（IPv4 / IPv6）と合っているか

3. Pod のログやイベント
```bash
kubectl logs <pod-name>
kubectl describe pod <pod-name>
```
* Pod 側で問題が起きていないかをチェック
* それでも解決しない場合は、問題が起きている Node の kube-proxy ログを参照

### レガシーバージョンとの互換性

* Kubernetes 1.19 より前は EndpointSlice がデフォルトで有効化されていない場合がある
* バージョンが古い環境や EndpointSlice を無効化している環境では、Endpoints のみ利用している可能性がある
* 必要に応じて EndpointsMirroring コントローラーが Endpoints と EndpointSlice 間のデータを同期できる

### スケーリングの考慮

* EndpointSlice は、Pod 数が大きい（数百〜数千以上）クラスターでも性能を保ちやすい
* トポロジーに基づくルーティングを活用すると、ゾーンをまたぐ通信を減らせる（クラウドのネットワークコストやレイテンシ低減につながる）
* ただし、EndpointSlice 自体も watch イベントを発行するため、大量に Slice が生成される場合は kube-proxy のパフォーマンスや API サーバーへの影響を確認することが重要

### 運用上のポイント

* モニタリング
  * EndpointSlice の更新頻度や kube-proxy のルール更新状況をメトリクスとして監視
  * Fluentd や Logstash などのロギングツールを活用し、自動スケーリングやローリングアップデートがどの程度変更を発生させているのかを可視化すると効果的

* サービスメッシュとの組み合わせ
  * Istio や Linkerd などを導入している場合、EndpointSlice や Endpoints をさらに詳細に制御するケースがある
  * これらの仕組みを理解しておくと、トラフィックルーティングのトラブルシュートに役立つ

## 結論

Kubernetes 上でのトラフィック転送の仕組みを理解しておくことは、安定した運用を行ううえで非常に重要です。

* Service はアプリケーション（Pod 群）への安定した入口を用意します。
* Endpoint/EndpointSlice は具体的な Pod の IP とポートを管理し、よりスケーラブルなサービス運用を実現します。
* kube-proxy は各 Node で動作し、EndpointSlice の情報をもとにネットワーク転送のルールを動的に設定します。

クラスターが拡大しても、EndpointSlice により API サーバー負荷の低減や kube-proxy のスケーラビリティ向上が期待できます。ネットワーク関連のトラブルシュートやクラスター設計を最適化するためにも、ぜひ基本をしっかり押さえましょう。チーム内で知識を共有しながら、Kubernetes ネットワーキングをより深く理解して、快適な運用を実現してください。

本記事が、Kubernetes の Service / Endpoints / EndpointSlice、そして kube-proxy との連携について理解を深めるきっかけになれば幸いです。学びがより楽しく、より実践的になりますように！