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 の範囲) |
| それ以外 | 同じ | 同じ |
差分は type と nodePort の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つのポイントに注目してください。
-
TYPE が
NodePortになっている -
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 と比べると、selector も ports もありません。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 は selector も ports も不要です。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 |
この記事が誰かのためになれば幸いです!