0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kubernetes③(全7回)|kind|Service / Ingress — ネットワーキングの設計思想

0
Last updated at Posted at 2026-02-18

シリーズ記事一覧

📑 目次

  1. この記事について
  2. この記事のゴール
  3. 前提条件
  4. Docker Composeではこうだった(Before)
  5. k8sではこうなる(After)
  6. ハンズオン:Serviceを作って外部からアクセスする
  7. つまずきポイント(体験談)
  8. まとめ
  9. 次回予告

1. この記事について

前回、Deploymentを使ってPodを動かし、Self-healing やスケーリングを体験しました。

チェーンレストランで言えば「店舗にスタッフを配置した」状態です。
でも、お客さんがまだ来れません
なぜなら、看板も入口もないからです。

今回は「どうやってPodにアクセスするか」= k8sのネットワーキング を調べて試してみました。

Docker Composeでは
ports: "8080:80" の1行で済んでいたアクセス設定が、k8sではなぜ複雑になるのか。
その理由がわかると、k8sの設計思想が一気に見えてきました。


2. この記事のゴール

この記事で書いていること:

  • PodのIPに直接アクセスしない理由
  • Serviceの4種類の違いの整理
  • NodePort経由でnginxにアクセスしてみた話
  • Ingressの役割と位置づけ

3. 前提条件

項目 要件
前回の完了 第2回でDeploymentを理解済み
クラスタ状態 kindクラスタが起動中
# WSL2 Ubuntu で実行

# クラスタが動いているか確認
kubectl get nodes

期待される出力:

NAME                         STATUS   ROLES           AGE   VERSION
my-first-cluster-control-plane   Ready    control-plane   ...   v1.xx.x

4. Docker Composeではこうだった(Before)

4-1. Docker Composeのポート設定

Docker Composeでは、ports の1行で外部アクセスが可能になります。

# docker-compose.yml

services:
  web:
    image: nginx:1.25
    ports:
      - "8080:80"    # ホスト8080 → コンテナ80

http://localhost:8080 で即アクセス。
シンプルで分かりやすい。

4-2. でもこの壁がある

# Docker Composeの現実
壁1 コンテナが再起動するとIPが変わる アクセス先を毎回確認?
壁2 複数コンテナへの振り分け ロードバランサーは自分で用意
壁3 「/api は APIサーバー」「/ はフロント」 URLベースの振り分けは自前で構築

🔰 Memo:
Docker Composeの ports は「1台のコンテナに直結」。
コンテナが複数台あるとき、誰が振り分けるのか?
という問題が残ることに気づきました。


5. k8sではこうなる(After)

5-1. なぜPodに直接アクセスしないのか

🍽️ 比喩:スタッフの個人携帯 vs お店の代表電話

PodにはIPアドレスがあります。
直接アクセスできます。
でも、k8sのPodは使い捨てです。

状況 何が起きるか
Self-healing 古いPodが消え、新しいPodが作られる → IPが変わる
スケールアウト 新しいPodが追加される → アクセス先が増える
ローリングアップデート 全Podが順番に入れ替わる → IPが全部変わる

これはスタッフの個人携帯に直接電話するようなもの。
退職(Pod削除)したら番号が変わってしまいます。

Service = お店の代表電話番号

スタッフ(Pod)が入れ替わっても、
お客さん(クライアント)は同じ番号に電話すれば繋がる。
誰が応対するかは、裏側で自動的に振り分けてくれる。

5-2. PodのIP問題を図解

PodのIPに直接アクセスするとどうなるか、
Service経由だとどう変わるかを比較してみましょう。

❌PodのIPに直接アクセスする場合:

✅ Service経由でアクセスする場合:

5-3. Serviceの4種類

Serviceには用途に応じた4つの種類があります。

🍽️ 電話システムに例えると、4段階の「公開範囲」があります。

種類 比喩 公開範囲 用途
ClusterIP 内線電話 クラスタ内部のみ Pod間の通信(API → DB など)
NodePort 店の直通電話 ノードのIP + 指定ポートで外部公開 開発・検証環境
LoadBalancer コールセンター クラウドLB経由で外部公開 本番環境(AWS ALB等)
ExternalName 転送電話 外部サービスへのエイリアス 外部DBや外部APIへの接続

🔰 Memo: まず覚えるのは ClusterIP と NodePort の2つ だけで十分だと感じました。
LoadBalancerはクラウド環境(EKS等)で使い、ExternalNameは特殊なケースのようです。

5-4. 4種類の構成図

4種類のServiceがクラスタ内でどう配置されるかを1枚の図で確認します。

🍽️ NodePort は ClusterIP を「内包」しています。
図のように、NodePort の中に ClusterIP が入っている構造です。
NodePort Service を作ると ClusterIP も自動で割り当てられる — 「直通電話を引いたら、内線番号も自動で付いてくる」ということです。

5-5. Ingressとは?

Serviceが「1つのアプリへのアクセス経路」なら、Ingressは「複数のServiceへの振り分け」です。

🍽️ 比喩:商業ビルの総合受付

  • /api にアクセス → APIサービスに案内(B1Fの和食店へ)
  • / にアクセス → フロントサービスに案内(2Fのカフェへ)
  • admin.example.com → 管理画面に案内(3Fの事務所へ)

🔰 Note: Ingressは今回はハンズオンしていません。
「こういう仕組みがある」と押さえておくだけにしました。
実際の構築は、EKS等のクラウド環境で試すのが効率的そうです。


6. ハンズオン:Serviceを作って外部からアクセスする

6-1. 作業ディレクトリ

# WSL2 Ubuntu で実行

mkdir -p ~/k8s-handson/service-ingress
cd ~/k8s-handson/service-ingress

6-2. Step1:Deploymentの作成(前回の復習)

まずアクセス先となるPodを用意します。

以下のファイルを作成:

📂 service-ingress/
└── nginx-deployment.yaml          ← Step 1 🆕
# nginx-deployment.yaml
# Deployment = 店長への指示書

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx            # ← このラベルが重要(Serviceと紐づく)
    spec:
      containers:
        - name: nginx
          image: nginx:1.25
          ports:
            - containerPort: 80

ポイント:app: nginx ラベルがServiceとの接続に必要
この後のStepで作るServiceは selector: { app: nginx } でPodを探します。
Deployment の template.metadata.labels.app: nginx と一致させることで、
Service → Pod のルーティングが成立します。

# ~/k8s-handson/service-ingress/ で実行

kubectl apply -f nginx-deployment.yaml
kubectl get pods

期待される出力:

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-xxxxxxxxx-xxxxx   1/1     Running   0          10s
nginx-deployment-xxxxxxxxx-yyyyy   1/1     Running   0          10s
nginx-deployment-xxxxxxxxx-zzzzz   1/1     Running   0          10s

6-3. Step2:ClusterIP Service(内線電話)

クラスタ内部からPodにアクセスするためのServiceを作ります。

以下のファイルを作成:

📂 service-ingress/
├── nginx-deployment.yaml          ✅ Step 1
└── nginx-service-clusterip.yaml   ← Step 2 🆕
# nginx-service-clusterip.yaml
# ClusterIP Service = お店の内線電話

apiVersion: v1
kind: Service
metadata:
  name: nginx-clusterip
spec:
  # ClusterIP(デフォルト。省略してもClusterIPになる)
  type: ClusterIP

  # どのPodに振り分けるか(ラベルで紐づけ)
  selector:
    app: nginx                # ← Deploymentの labels と一致させる

  # ポート設定
  ports:
    - port: 80                # Serviceが受け付けるポート
      targetPort: 80          # Pod側のポート

6-3-1. マニフェストの構造解説

YAMLの項目 比喩 意味
kind: Service 「電話回線の申請書です」 リソースの種類
type: ClusterIP 「内線電話でお願いします」 クラスタ内部のみ
selector: app: nginx 「app: nginx のタグが付いたスタッフに繋いで」 振り分け先の指定
port: 80 「代表電話の番号は80で」 Serviceのポート
targetPort: 80 「スタッフの内線番号は80で」 Podのポート
# ~/k8s-handson/service-ingress/ で実行

# Serviceを作成
kubectl apply -f nginx-service-clusterip.yaml

# Serviceの確認
kubectl get service

期待される出力:

NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP   ...
nginx-clusterip   ClusterIP   10.96.xxx.xxx   <none>        80/TCP    5s

6-3-2. 内部からアクセスしてみる

ClusterIPはクラスタ内部からのみアクセスできます。
一時的なPodを作ってcurlで確認します。

# 一時的なPodからService経由でアクセス
kubectl run curl-test \
  --image=curlimages/curl \
  --rm -it \
  --restart=Never \
  -- curl nginx-clusterip:80

期待される出力:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

Service経由でnginxにアクセスできました。

💡 タイムアウトした場合 → セクション7-1(Selectorのミスマッチ)を確認

🍽️ 内線電話(ClusterIP)で「代表番号(nginx-clusterip)」にかけたら、スタッフ(Pod)の誰かが応答してくれた、ということです。

🔰 Note: ここで大事だなと思ったのが、nginx-clusterip:80 という名前でアクセスしている点。
PodのIPアドレスは一切使っていません。
Podが再作成されてIPが変わっても、この名前は変わらないわけです。


6-4. Step3:NodePort Service(直通電話)

次に、クラスタの外側からアクセスできるServiceを作ります。

以下のファイルを作成:

📂 service-ingress/
├── nginx-deployment.yaml          ✅ Step 1
├── nginx-service-clusterip.yaml   ✅ Step 2
└── nginx-service-nodeport.yaml    ← Step 3 🆕
# nginx-service-nodeport.yaml
# NodePort Service = お店の直通電話

apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
spec:
  # NodePort(外部からアクセス可能)
  type: NodePort

  # どのPodに振り分けるか
  selector:
    app: nginx

  # ポート設定
  ports:
    - port: 80                # Service内部のポート
      targetPort: 80          # Pod側のポート
      nodePort: 30080         # 外部公開ポート(30000-32767の範囲)
ClusterIPとの違い 内容
type: NodePort 外部公開モード
nodePort: 30080 ノードの30080番ポートで外部に公開
# ~/k8s-handson/service-ingress/ で実行

# Serviceを作成
kubectl apply -f nginx-service-nodeport.yaml

# Serviceの確認
kubectl get service

期待される出力:

NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP        ...
nginx-clusterip   ClusterIP   10.96.xxx.xxx   <none>        80/TCP         2m
nginx-nodeport    NodePort    10.96.yyy.yyy   <none>        80:30080/TCP   5s

80:30080/TCP の表示に注目。
「Serviceの80番ポート」が「ノードの30080番ポート」に紐づいています。

6-4-1. kindでの外部アクセス方法

kindは Docker 内でクラスタを動かしているため、localhost:30080 では直接アクセスできません。
kubectl port-forward を使います。

# port-forward でローカルからアクセス可能にする
kubectl port-forward service/nginx-nodeport 8080:80

別のターミナルを開いて:

# 別ターミナルで実行
curl http://localhost:8080

期待される出力:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

外部からnginxにアクセスできました。

Ctrl + C でport-forwardを終了してください。

🍽️ 直通電話(NodePort)を引いたので、外部のお客さんからも注文が取れるようになりました。


6-5. Step4:Ingressの概念理解

IngressはStep2/3の「Service」のさらに前段に位置する、URLベースの振り分け(ルーティング)機能です。

6-5-1. Service と Ingress の使い分け

観点 Service Ingress
振り分け単位 ポート番号 URLパス / ホスト名
対象 1つのアプリ 複数のService
比喩 各店舗の電話番号 商業ビルの総合受付
設定例 :80 → nginx Pods /api → api-svc/ → frontend-svc
AWS連携 - ALB Ingress Controller でAWS ALBと統合

🔰 Note: Ingressのハンズオンはここでは省略しました。
kindでIngress Controller を動かすには追加設定が必要で、本質の理解から外れると判断したためです。
実務ではAWS EKS + ALB Ingress Controller の組み合わせがよく使われるようです。


6-6. 後片付け

# 全てのリソースを削除
kubectl delete -f nginx-service-nodeport.yaml
kubectl delete -f nginx-service-clusterip.yaml
kubectl delete -f nginx-deployment.yaml

# 確認
kubectl get all

7. つまずきポイント(体験談)

7-1. ServiceがPodに繋がらない(Selectorのミスマッチ)

症状: curlするとタイムアウトする。

原因: Serviceの selector と Podの labels が一致していない。

# Podのラベルを確認
kubectl get pods --show-labels

# Serviceのselectorを確認
kubectl describe service nginx-clusterip | grep Selector

対策: 両方の app: nginx が一致しているか確認。大文字小文字も区別されるので注意。

7-2. NodePortのポート番号が範囲外

症状:

The Service "nginx-nodeport" is invalid: spec.ports[0].nodePort: Invalid value: 80: provided port is not in the valid range. The range of valid ports is 30000-32767

対策: nodePort30000〜32767 の範囲内で指定してください。

7-3. kindでNodePortに直接アクセスできない

症状: curl localhost:30080 が繋がらない。

原因: kindはDockerコンテナ内でクラスタを動かしているため、ホストから直接NodePortにアクセスできません。

対策: kubectl port-forward を使うか、kind作成時に extraPortMappings を設定する方法があります。

# kind-config.yaml(クラスタ作成時に使用)
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
  - role: control-plane
    extraPortMappings:
      - containerPort: 30080
        hostPort: 30080        # ホストの30080 → kindの30080
# この設定でクラスタを作り直す場合
kind delete cluster --name my-first-cluster
kind create cluster \
  --name my-first-cluster \
  --config kind-config.yaml

8. まとめ

8-1. この記事でやったこと

# 内容 状態
1 PodのIPに直接アクセスしない理由を理解した
2 ClusterIP Serviceで内部アクセスを体験した
3 NodePort Serviceで外部アクセスを体験した
4 Ingressの役割と位置づけを理解した

8-2. ネットワーキング全体像(まとめ図)

8-3. Docker Compose → k8s 対応表

Docker Compose Kubernetes 備考
ports: "8080:80" Service(NodePort) k8sは「名前」でアクセス
コンテナのIPに直アクセス やらない PodのIPは使い捨て
(該当なし) ClusterIP 内部通信の仕組み
(該当なし) Ingress URLベースのルーティング
docker-compose.yml 1ファイル Deployment + Service + Ingress 関心事ごとにファイルを分ける

9. 次回予告

第4回:ConfigMap / Secret / Volume — 設定とデータの分離

アプリが動き、外部からアクセスもできるようになりました。
でもまだ足りないものがあります。

🍽️ レストランは開店したけど、メニュー表(設定ファイル)とレシピ(秘密情報)がスタッフの頭の中にしかない。
スタッフが入れ替わったらどうする?

次回は:

  • なぜ設定をコンテナイメージに埋め込まないのか?
  • ConfigMap で環境変数や設定ファイルを外部化
  • Secret でパスワード・APIキーを安全に管理
  • Volume でデータの永続化

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?