10日で覚えるKubernetesDay 5: Serviceによるネットワーキング
books.chapter 510日で覚えるKubernetes

Day 5: Serviceによるネットワーキング

今日学ぶこと

  • PodのIPアドレスが不安定な問題とServiceの解決策
  • Service の4つのタイプ(ClusterIP、NodePort、LoadBalancer、ExternalName)
  • サービスディスカバリとDNS
  • 実践的なService構成

なぜServiceが必要なのか

Docker書籍では、Docker Composeでサービス名を使ってコンテナ間通信を行いました。Kubernetesでも同様の仕組みが必要ですが、追加の課題があります。

flowchart TB
    subgraph Problem["課題"]
        P1["Pod A\nIP: 10.0.0.5"]
        P2["Pod B\nIP: 10.0.0.6"]
        P3["Pod C\nIP: 10.0.0.7"]
    end
    subgraph After["再作成後"]
        P4["Pod A'\nIP: 10.0.0.12"]
        P5["Pod B'\nIP: 10.0.0.15"]
        P6["Pod C'\nIP: 10.0.0.18"]
    end
    Problem -->|"Podが再作成されると\nIPが変わる!"| After
    style Problem fill:#ef4444,color:#fff
    style After fill:#f59e0b,color:#fff
問題 説明
IPが不安定 Podは再作成のたびにIPアドレスが変わる
複数Pod レプリカが複数ある場合、どのPodにアクセスすべきか
ロードバランシング 複数Podにトラフィックを分散したい

Serviceはこれらの問題を解決する安定したアクセスポイントを提供します。


Serviceの仕組み

ServiceはラベルセレクターでPodを選択し、安定したIPアドレスとDNS名を提供します。

flowchart TB
    CLIENT["クライアント"] --> SVC["Service\nweb-service\nClusterIP: 10.96.0.100"]
    SVC --> P1["Pod 1\n10.0.0.5"]
    SVC --> P2["Pod 2\n10.0.0.6"]
    SVC --> P3["Pod 3\n10.0.0.7"]
    style SVC fill:#3b82f6,color:#fff
    style P1 fill:#22c55e,color:#fff
    style P2 fill:#22c55e,color:#fff
    style P3 fill:#22c55e,color:#fff

Serviceの主な役割:

  • 安定したIPアドレス(ClusterIP)の提供
  • DNS名の提供(例: web-service.default.svc.cluster.local
  • ロードバランシング(複数Podへのトラフィック分散)

Serviceのタイプ

1. ClusterIP(デフォルト)

クラスタ内部からのみアクセス可能な仮想IPを割り当てます。

apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: ClusterIP
  selector:
    app: web
  ports:
    - port: 80          # Serviceのポート
      targetPort: 80    # Podのポート
      protocol: TCP
kubectl apply -f web-service.yaml

kubectl get services
# NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
# web-service   ClusterIP   10.96.0.100    <none>        80/TCP    30s

2. NodePort

ClusterIPに加え、各ノードの特定ポートでもアクセスできるようにします。

apiVersion: v1
kind: Service
metadata:
  name: web-nodeport
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080    # 30000-32767の範囲
flowchart TB
    EXT["外部アクセス\nhttp://NodeIP:30080"] --> NODE["ノード\nポート 30080"]
    NODE --> SVC["Service\nポート 80"]
    SVC --> P1["Pod 1"]
    SVC --> P2["Pod 2"]
    style EXT fill:#8b5cf6,color:#fff
    style SVC fill:#3b82f6,color:#fff

3. LoadBalancer

クラウドプロバイダーのロードバランサーを自動的にプロビジョニングします。

apiVersion: v1
kind: Service
metadata:
  name: web-lb
spec:
  type: LoadBalancer
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
kubectl get services
# NAME     TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)        AGE
# web-lb   LoadBalancer   10.96.0.101    203.0.113.10   80:31234/TCP   60s

4. ExternalName

外部サービスへのDNSエイリアスを作成します。

apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: db.example.com

Serviceタイプの比較

タイプ アクセス元 外部IP ユースケース
ClusterIP クラスタ内部のみ なし 内部マイクロサービス間通信
NodePort ノードIP経由で外部から なし(ノードIP使用) 開発・テスト
LoadBalancer 外部ロードバランサー あり 本番環境での外部公開
ExternalName クラスタ内部 - 外部サービスへのDNS参照
flowchart TB
    subgraph External["外部アクセス"]
        LB["LoadBalancer"]
        NP["NodePort"]
    end
    subgraph Internal["内部アクセス"]
        CIP["ClusterIP"]
        EN["ExternalName"]
    end
    LB -->|"本番環境"| POD["Pods"]
    NP -->|"開発・テスト"| POD
    CIP -->|"マイクロサービス間"| POD
    EN -->|"外部サービス参照"| EXTDB["外部DB"]
    style External fill:#ef4444,color:#fff
    style Internal fill:#3b82f6,color:#fff

サービスディスカバリ

DNS による名前解決

Kubernetesクラスタ内にはCoreDNSが動作しており、Serviceを自動的にDNS登録します。

<service-name>.<namespace>.svc.cluster.local
# 同じNamespace内からのアクセス
curl http://web-service

# 別のNamespaceからのアクセス
curl http://web-service.default.svc.cluster.local

# 短縮形(同じNamespace)
curl http://web-service.default

環境変数による検出

Podが起動する際、同じNamespaceのServiceの情報が環境変数として注入されます。

# Pod内で確認
env | grep WEB_SERVICE
# WEB_SERVICE_SERVICE_HOST=10.96.0.100
# WEB_SERVICE_SERVICE_PORT=80

DNS方式のほうが柔軟で推奨されます。


実践: WebアプリとDBの構成

Docker Composeで構築していたようなWebアプリ + データベースの構成をKubernetesで作りましょう。

1. データベースのDeploymentとService

# db-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:17
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              value: myapp
            - name: POSTGRES_USER
              value: admin
            - name: POSTGRES_PASSWORD
              value: secret123
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-service
spec:
  type: ClusterIP
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432

2. WebアプリのDeploymentとService

# web-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: web
          image: nginx:1.27
          ports:
            - containerPort: 80
          env:
            - name: DATABASE_HOST
              value: postgres-service
            - name: DATABASE_PORT
              value: "5432"
---
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080
flowchart TB
    EXT["外部アクセス\n:30080"] --> WEBSVC["web-service\nNodePort"]
    WEBSVC --> W1["Web Pod 1"]
    WEBSVC --> W2["Web Pod 2"]
    WEBSVC --> W3["Web Pod 3"]
    W1 --> DBSVC["postgres-service\nClusterIP"]
    W2 --> DBSVC
    W3 --> DBSVC
    DBSVC --> DB["PostgreSQL Pod"]
    style WEBSVC fill:#3b82f6,color:#fff
    style DBSVC fill:#8b5cf6,color:#fff
    style DB fill:#22c55e,color:#fff

デプロイと確認

# 全リソースを作成
kubectl apply -f db-deployment.yaml
kubectl apply -f web-deployment.yaml

# 確認
kubectl get deployments
kubectl get services
kubectl get pods

# Web Podからデータベースに接続できるか確認
kubectl exec -it deployment/web-app -- curl -s postgres-service:5432 || echo "Connection attempted"

Endpoints

ServiceはEndpointsオブジェクトを通じてPodのIPアドレスを追跡します。

# Endpointsの確認
kubectl get endpoints web-service
# NAME          ENDPOINTS                                   AGE
# web-service   10.0.0.5:80,10.0.0.6:80,10.0.0.7:80       5m

# Podが追加/削除されると自動的に更新される

まとめ

概念 説明
Service Podへの安定したアクセスポイントを提供
ClusterIP クラスタ内部のみ。デフォルトのServiceタイプ
NodePort ノードのポートで外部公開。開発・テスト向け
LoadBalancer クラウドLBを利用して外部公開。本番向け
サービスディスカバリ DNS名でServiceにアクセスできる仕組み
Endpoints ServiceがPodのIPを追跡するオブジェクト

重要ポイント

  1. PodのIPは不安定。Serviceを通じて安定したアクセスを確保する
  2. クラスタ内通信にはClusterIP、外部公開にはNodePortまたはLoadBalancerを使う
  3. DNS名(<service-name>.<namespace>)でService間通信が可能

練習問題

問題1: 基本

Redis用のDeployment(replicas: 1)とClusterIP Serviceを作成してください。ポートは6379です。

問題2: マルチサービス

フロントエンド(nginx、replicas: 3、NodePort)とバックエンド(httpd、replicas: 2、ClusterIP)のDeploymentとServiceを作成し、フロントエンドPodからバックエンドServiceにcurlでアクセスできることを確認してください。

チャレンジ問題

Serviceを作成せずにPodのIPアドレスだけで通信する場合と、Serviceを使う場合の違いを実験してください。Podを削除・再作成した場合に何が起こるか観察しましょう。


参考リンク


次回予告: Day 6では「ストレージとデータ永続化」について学びます。Docker書籍で学んだVolumeの概念をKubernetesでどう扱うか、PersistentVolumeとPersistentVolumeClaimの仕組みを理解しましょう。