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を追跡するオブジェクト |
重要ポイント
- PodのIPは不安定。Serviceを通じて安定したアクセスを確保する
- クラスタ内通信にはClusterIP、外部公開にはNodePortまたはLoadBalancerを使う
- 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の仕組みを理解しましょう。