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?

【図解ハンズオン】ブラウザからアクセスしたい! — NodePort Service でクラスタ外部に公開する

0
Posted at

Kubernetes の ClusterIP Service を使えば、Pod 間の通信は安定します。安定した IP、ロードバランシング、DNS 名。クラスタ内部の通信はこれで十分です。

でも、ここで1つ壁にぶつかります。

「開発中のアプリをブラウザで確認したいとき、どうすればいいの?」

ClusterIP はクラスタの「内部」からしかアクセスできません。ブラウザで動作確認したい? チームメンバーにデモを見せたい? ClusterIP では無理です。

この「外部からアクセスしたい」を解決するのが NodePort Service です。

この記事では、NodePort Service を作成してブラウザからアクセスするまでをハンズオンで体験します。さらに、ExternalName Service の紹介と、4種類の Service の使い分けも整理します。

この記事を読み終わる頃には、

  • NodePort Service の YAML を自分で書ける
  • ClusterIP と NodePort の関係(NodePort は ClusterIP の機能を含む)を理解している
  • ExternalName Service の存在と用途を知っている
  • 4種類の Service の使い分けが頭に入っている

状態を目指します。

TL;DR

やること 使うコマンド / 概念 ポイント
ClusterIP の限界を確認する クラスタ外部からアクセスできない
NodePort Service の YAML を書く type: NodePort / nodePort ClusterIP との差分は2行だけ
NodePort Service を作成する kubectl apply -f ClusterIP の機能 + 外部公開
ブラウザからアクセスする minikube service ノードの IP + ポート経由でアクセス
3つのポートの関係を理解する port / targetPort / nodePort どれが何を指すか
ExternalName Service を知る type: ExternalName クラスタ内から外部 DNS へ参照
4種類の Service を使い分ける 用途に応じた選択基準

対象読者

  • ClusterIP Service の仕組み(selector / ロードバランシング / DNS 名)を理解している方
  • 「開発中のアプリをブラウザで確認したい」と思っている方
  • 「ClusterIP と NodePort って何が違うの?」と疑問に思っている方

この記事は minikube が起動済み であることを前提としています。
minikube のインストールがまだの方は、公式ドキュメントを参考にセットアップを済ませてから読み進めてください。

Step 1: ClusterIP の壁を振り返る — なぜ NodePort が必要なのか

いきなり NodePort の YAML を書く前に、なぜ NodePort が必要なのか を整理しましょう。

ClusterIP でできたこと / できなかったこと

ClusterIP でできたこと ClusterIP でできなかったこと
安定した IP / DNS 名で Pod にアクセス クラスタ外部からのアクセス
ロードバランシング ブラウザからの動作確認
Pod が再作成されても通信継続 チームメンバーへのデモ

ClusterIP は「クラスタ内部の通信」のプロですが、クラスタの外からは一切アクセスできません。この壁を越えるのが NodePort です。

┌──────────────────────────────────────────────────────────────────┐
│                                                                  │
│  【ClusterIP の世界】                                              │
│                                                                  │
│    クラスタ内部:                                                   │
│      curl Pod → Service → Pod-A, Pod-B, Pod-C  ✓                 │
│                                                                  │
│    クラスタ外部:                                                   │
│      ブラウザ → ??? → Service  ✗ アクセスできない!                   │
│                                                                  │
│  【NodePort の世界】                                               │
│                                                                  │
│    クラスタ外部:                                                   │
│      ブラウザ → ノードIP:30007 → Service → Pod-A, Pod-B, Pod-C  ✓  │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

NodePort Service は ClusterIP の「上位互換」です。ClusterIP の全機能(安定した IP、ロードバランシング、DNS 名)に加えて、外部公開の機能が追加されます。NodePort を作ると ClusterIP の機能も使えます。

Step 2: ハンズオン — NodePort Service の YAML を書いて動かす

では、実際に NodePort Service を作ってみましょう。

┌────────────────────────────────────────────────────────────────┐
│                                                                │
│  【これからやること】                                             │
│                                                                │
│  ① nginx の Deployment を作成する                               │
│  ② nginx-nodeport-svc.yaml を書く                              │
│  ③ kubectl apply -f で Service を作成する                       │
│  ④ kubectl get service で ClusterIP も割り当てられていることを確認  │
│                                                                │
│  ClusterIP の YAML との差分はたった2行だけ!                       │
│                                                                │
└────────────────────────────────────────────────────────────────┘

環境を準備する

まず nginx の Deployment を作成します。以下の内容で nginx-deployment.yaml を作成してください。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.27.0
          ports:
            - containerPort: 80
kubectl apply -f nginx-deployment.yaml

Pod が3つ Running になっていることを確認します。

kubectl get pods

nginx-nodeport-svc.yaml を作成する

以下の内容でファイルを作成してください。

apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
  labels:
    app: nginx
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30007
      protocol: TCP

ClusterIP の YAML との差分

ClusterIP Service の YAML を知っていれば、見覚えがあるはずです。

項目 ClusterIP NodePort
type ClusterIP NodePort
nodePort なし 30007(30000-32767 の範囲)
それ以外 同じ 同じ

差分は typenodePort の2行だけです。 selector / port / targetPort の知識はそのまま活きます。

kubectl apply -f で作成する

kubectl apply -f nginx-nodeport-svc.yaml

Service の情報を確認する

kubectl get service
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP        1d
nginx-nodeport   NodePort    10.96.78.200    <none>        80:30007/TCP   10s

2つのポイントに注目してください。

  • TYPENodePort になっている
  • PORT(S)80:30007/TCP — Service のポート 80 と nodePort 30007 が対応している
  • CLUSTER-IP も割り当てられている — NodePort は ClusterIP の機能を含んでいる

3つのポートの関係

NodePort Service には3つのポートが登場します。混乱しやすいので、ここで整理しておきましょう。

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│  ブラウザ → ノードIP:30007 → Service:80 → Pod:80                │
│              (nodePort)       (port)       (targetPort)      │
│                                                              │
│  nodePort:   クラスタ外部からアクセスするためのポート               │
│              ノード上で開く。30000-32767 の範囲                  │
│                                                              │
│  port:       Service が受け付けるポート                         │
│              クラスタ内部からのアクセス用                         │
│                                                              │
│  targetPort: Pod 側のポート                                    │
│              コンテナの containerPort と一致させる               │
│                                                              │
└──────────────────────────────────────────────────────────────┘

nodePort を省略すると、Kubernetes が 30000-32767 の範囲からランダムに割り当てます。開発時は明示的に指定する方が、毎回ポートを確認する手間が省けて扱いやすいです。

NodePort Service には ClusterIP も割り当てられます。つまり、クラスタ内部からは ClusterIP / DNS 名でアクセスでき、クラスタ外部からは nodePort でもアクセスできる。1つの Service で内部と外部の両方をカバーしています。

Step 3: ブラウザからアクセスする — 外部公開を体験する

ここからが、この記事の感動ポイントです。実際にブラウザからクラスタ内の Pod にアクセスしてみましょう。

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│  【これからやること】                                           │
│                                                              │
│  ① minikube service コマンドで URL を取得する                   │
│  ② ブラウザでアクセスする → nginx の Welcome ページが表示!        │
│  ③ クラスタ内部からも引き続きアクセスできることを確認する             │
│                                                              │
│  ついにクラスタの外から Pod にアクセスします                        │
│                                                              │
└──────────────────────────────────────────────────────────────┘

minikube service で URL を取得する

minikube service nginx-nodeport --url

URL が表示されます(例: http://127.0.0.1:52345)。この URL をコピーしてください。

minikube を Mac / Windows で使っている場合、<ノード IP>:<nodePort> で直接アクセスできない場合があります。これは minikube が VM や Docker コンテナの中で動いているためです。minikube service コマンドがポートフォワーディングを設定してくれます。Linux で直接 minikube を動かしている場合は、ノード IP + nodePort で直接アクセスできます。

ブラウザでアクセスする

コピーした URL をブラウザで開いてください。

nginx の Welcome ページが表示されます!

クラスタの外(ブラウザ)から、クラスタ内の Pod にアクセスできました。ClusterIP ではできなかったことが、NodePort なら実現できます。

クラスタ内部からもアクセスできることを確認する

NodePort は ClusterIP の上位互換です。クラスタ内部からも、Service 名でアクセスできることを確認しましょう。

kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -- curl nginx-nodeport

nginx の Welcome ページが返ってきます。外部からも内部からも、1つの Service でアクセスできています。

NodePort は開発・テスト用途には便利ですが、本番環境では以下の理由で推奨されません。

  • ポート範囲が 30000-32767 に限定される(80 や 443 は使えない)
  • ノードの IP を知る必要がある
  • セキュリティ的に全ノードでポートが開く

本番環境では LoadBalancer Service や Ingress を使うのが一般的です。

NodePort は全ノードで同じポートが開きます。複数ノードのクラスタでは、どのノードの IP でアクセスしても、そのノード上に対象の Pod がなくても、Kubernetes が別ノードの Pod にルーティングしてくれます。例えば control plane ノードの IP でアクセスしても、worker ノード上の Pod にリクエストが届きます。つまり「どのノードに入口があるか」を気にする必要はありません。

Step 4: ExternalName Service — 外部への参照(紹介)

ここまで ClusterIP と NodePort を扱ってきました。どちらも「外部や内部 → Service → Pod」という方向の通信でした。

ここで、方向が逆 の Service を1つ紹介します。

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│  【ClusterIP / NodePort】                                    │
│    外部 or 内部 → Service → Pod(クラスタ内部の Pod にアクセス)   │
│                                                              │
│  【ExternalName】                                            │
│    クラスタ内の Pod → Service → 外部 DNS(google.com など)      │
│    ↑ 方向が逆!                                                │
│    Service は Pod の前に立つのではなく、外部への参照を提供する       │
│                                                              │
└──────────────────────────────────────────────────────────────┘

ExternalName とは

ExternalName Service は、クラスタ内から外部の DNS 名を参照するための Service です。Pod の前に立つ「受付窓口」ではなく、外部サービスへの「エイリアス」として機能します。

YAML の例

以下の内容で my-external-svc.yaml を作成してください。

apiVersion: v1
kind: Service
metadata:
  name: my-external-svc
spec:
  type: ExternalName
  externalName: google.com

ClusterIP や NodePort の YAML と比べると、selectorports もありません。DNS の CNAME レコードを返すだけのシンプルな Service です。

軽いハンズオン

# Service を作成する
kubectl apply -f my-external-svc.yaml
# Service の情報を確認する
kubectl get service
NAME              TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
my-external-svc   ExternalName   <none>       google.com    <none>    10s

CLUSTER-IP が <none> で、EXTERNAL-IP に DNS 名 google.com が表示されています。このService は IP を持たず、DNS の参照だけを提供します。

# クラスタ内から Service 名で外部にアクセスできることを確認する
kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -- curl -s -o /dev/null -w "%{http_code}" my-external-svc

外部サイトにアクセスできることが確認できます。

# 片付ける
kubectl delete -f my-external-svc.yaml

ExternalName は selectorports も不要です。DNS の CNAME レコードを返すだけのシンプルな仕組みです。使用頻度は ClusterIP / NodePort に比べて低いですが、外部 API のエンドポイントをクラスタ内の Service 名で抽象化したいときに使えます。例えば、外部の DB エンドポイントが変わっても、ExternalName の externalName を変更するだけで済みます。

ExternalName は DNS の CNAME レコードを返すだけであり、HTTP/HTTPS のプロトコル変換は行いません。例えば HTTPS が必要な外部サービスに HTTP で curl すると、404 や予期しないレスポンスが返る場合があります。ExternalName が解決するのは「名前の参照」であって、プロトコルの問題は別途対処が必要です。

Step 5: 4種類の Service を使い分ける — まとめ

ここまでで ClusterIP、NodePort、ExternalName の3つを体験しました。LoadBalancer も含めた4種類を整理して、いつ何を使うかを頭に入れておきましょう。

使い分けの表

種類 いつ使う? アクセス方向 本番利用
ClusterIP Pod 間の内部通信 クラスタ内 → Pod ✓ 内部通信のデフォルト
NodePort 開発・テスト時の外部公開 クラスタ外 → Pod △ 開発用。本番は非推奨
LoadBalancer 本番環境の外部公開 クラスタ外 → Pod ✓ クラウド環境で使用
ExternalName 外部 API への参照 Pod → クラスタ外 ✓ 外部サービスのエイリアス

Service の全体像

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│  クラスタ外部                                                  │
│    │                                                         │
│    ├─ LoadBalancer ──→ Service ──→ Pod (本番の外部公開)       │
│    ├─ NodePort ──────→ Service ──→ Pod (開発の外部公開)       │
│    │                                                         │
│  クラスタ内部                                                  │
│    │                                                         │
│    ├─ ClusterIP ─────→ Service ──→ Pod (内部通信)            │
│    │                                                         │
│    └─ ExternalName ──→ Service ──→ 外部 DNS (外部参照)        │
│                                                              │
└──────────────────────────────────────────────────────────────┘

実務での使用頻度は、ClusterIP > LoadBalancer > NodePort ≒ ExternalName です。まずは ClusterIP と NodePort を確実に使えるようになっておけば、基本的なシナリオはカバーできます。

Step 6: お片付け

ハンズオンで作成したリソースを削除しましょう。

# NodePort Service を削除する
kubectl delete -f nginx-nodeport-svc.yaml

# ExternalName Service を削除する(Step 4 で削除済みならスキップ)
kubectl delete -f my-external-svc.yaml --ignore-not-found

# Deployment を削除する
kubectl delete -f nginx-deployment.yaml

# クリーンな状態を確認する
kubectl get pods
kubectl get service

まとめ

お疲れさまでした!今回のハンズオンの全体フローを振り返りましょう。

┌──────────────────────────────────────────────────────────────┐
│                                                              │
│  Step 1: ClusterIP の壁                                       │
│    │     外部からアクセスできない                                │
│    ▼                                                         │
│  Step 2: NodePort の YAML を書く                              │
│    │     ClusterIP との差分は type と nodePort の2行だけ        │
│    ▼                                                         │
│  Step 3: ブラウザからアクセス                                   │
│    │     外部から Pod にアクセスできた!                         │
│    ▼                                                         │
│  Step 4: ExternalName の紹介                                  │
│    │     方向が逆。外部 DNS への参照                             │
│    ▼                                                         │
│  Step 5: 4種類の使い分け                                       │
│    │     用途に応じて選択する                                   │
│    ▼                                                         │
│  Step 6: お片付け                                             │
│                                                              │
│  → Service の基本は完了!次のステップは                           │
│    LoadBalancer(クラウド編)や Ingress(HTTP ルーティング)       │
│                                                              │
└──────────────────────────────────────────────────────────────┘

今回使ったコマンド一覧

コマンド 用途
kubectl apply -f <file> Deployment / Service を作成・更新
kubectl get service Service の一覧を表示
kubectl get pods Pod の一覧を表示
kubectl run ... --rm -it -- curl <url> 一時的な curl Pod でリクエストを送る
minikube service <name> --url minikube 環境で NodePort Service の URL を取得
kubectl delete -f <file> ファイルで指定したリソースを削除

今回の学びのポイント

ポイント 内容
NodePort ClusterIP の上位互換。ノードのポートを開いて外部からアクセス可能にする
3つのポート nodePort(外部用)、port(Service 用)、targetPort(Pod 用)
ポート範囲 30000-32767。本番では 80/443 が使えないため非推奨
minikube service Mac / Windows では minikube のポートフォワーディングが必要
全ノードで開く どのノードの IP でアクセスしても、Kubernetes が適切な Pod にルーティングする
ExternalName 外部 DNS への参照(CNAME)。方向が逆
使い分け 内部通信は ClusterIP、開発の外部公開は NodePort、本番は LoadBalancer

この記事が誰かのためになれば幸いです!

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?