Kubernetesのサービス公開とルーティング:ClusterIP, NodePort, LoadBalancerの違いとIngress徹底活用
やあ、みんな! 今日はKubernetes(k8s)の世界に足を踏み入れたばかりの君たちに、アプリケーションを外部に公開するための超重要トピック、「サービス公開とルーティング」について、僕が現場で培ってきた経験を惜しみなく伝授しようと思う。
「k8sって、コンテナを色々動かせるのはわかったけど、どうやって外からアクセスさせるの?」って疑問、きっと持ってるよね? 実は、k8sにはいくつかサービスを公開する方法があって、それぞれに得意なこと、苦手なことがあるんだ。それを理解しないまま進むと、思わぬところでハマってしまうことも……。
でも、心配しないで! この記事を読み終わる頃には、君も自信を持ってk8sのサービス公開ができるようになるはず。しかも、ただ「動く」だけじゃなくて、「なぜそう動くのか」までしっかり理解できるようになるから、応用力も格段にアップするはずだよ。
この記事では、以下の内容を、まるで隣で一緒に作業しているかのように、優しく丁寧に解説していくね。
1. Kubernetes内部ネットワークの仕組み:これが全ての基本!
2. サービスタイプの違い(ClusterIP / NodePort / LoadBalancer):それぞれの特徴と使い分け
3. Ingressコントローラー(Nginx等)の導入とルーティング設定:本番運用で必須の強力な武器
4. TLS/SSL証明書の導入手順:安全な通信を実現しよう
さあ、君たちのk8sライフが劇的に変わる、このエキサイティングな旅に、早速出発しよう!
—
1. Kubernetes内部ネットワークの仕組み:これが全ての基本!
まず、k8sのサービス公開を理解するには、その内部ネットワークの仕組みを知ることが不可欠なんだ。これは、k8sの「サービス」という概念の根幹に関わる部分だから、じっくりと腰を据えて見ていこう。
コンテナ間の通信:PodのIPアドレス
k8sでは、アプリケーションの最小単位は「Pod」と呼ばれる。そして、各PodにはユニークなIPアドレスが割り当てられるんだ。まるで、それぞれのPodが独立した小さなコンピューターみたいだね。
- Podは独立したネットワークを持つ: 各Podは、それぞれ独自のIPアドレスを持ち、他のPodとはこのIPアドレスを通じて直接通信できる。
- PodのIPアドレスは一時的: Podは、再起動やスケールアウト/インによって、IPアドレスが変わることがある。これは、Podが「使い捨て」のコンテナであるというk8sの思想とも合致している。
Pod間の通信を抽象化する「Service」
ここで、登場するのがk8sの「Service」というリソースだ。PodのIPアドレスは変動するから、直接PodのIPアドレスを指定して通信するのは現実的じゃない。そこで、Serviceが「PodのIPアドレスの変動を吸収し、固定されたIPアドレスとDNS名を提供する」という役割を担うんだ。
Serviceは、ラベルセレクターを使って、どのPod群にトラフィックをルーティングするかを定義する。
apiVersion: v1
kind: Service
metadata:
name: my-app-service # Serviceの名前
spec:
selector:
app: my-app # このラベルを持つPodにトラフィックをルーティングする
ports:
- protocol: TCP
port: 80 # Serviceが公開するポート
targetPort: 8080 # Podがリッスンしているポート
このService定義を見ると、`my-app-service` という名前で、`app: my-app` というラベルを持つPod群に対して、Serviceのポート`80`へのアクセスを、Podのポート`8080`へ転送してくれることがわかるね。
ServiceのIPアドレス:ClusterIP
Serviceにデフォルトで割り当てられるIPアドレスのことを「ClusterIP」と呼ぶ。このClusterIPは、k8sクラスタ内部からのみアクセス可能な、仮想的なIPアドレスなんだ。
- クラスタ内部専用: ClusterIPは、k8sクラスタの外からは直接アクセスできない。
- 固定のIPアドレスとDNS名: ClusterIPを持つServiceは、クラスタ内で固定のIPアドレスとDNS名(例: `my-app-service.default.svc.cluster.local`)を持つ。これにより、クラスタ内の他のPodから、常に安定してこのServiceへアクセスできるようになる。
ここまでが、k8s内部ネットワークの基本的な仕組みだ。この「Pod」「Service」「ClusterIP」の関係性をしっかり理解することが、次のステップへの確実な一歩となるよ。
—
2. サービスタイプの違い(ClusterIP / NodePort / LoadBalancer):それぞれの特徴と使い分け
さて、k8s内部の通信の基本がわかったところで、いよいよアプリケーションを「外部」に公開する方法について見ていこう。Serviceには、その公開方法を決定する「Type」という属性があるんだ。ここでは、代表的な3つのTypeを見ていくよ。
1. ClusterIP: クラスタ内部からのアクセスのみ(デフォルト)
これは、先ほども説明した、ServiceのデフォルトのTypeだ。
- 特徴:
- クラスタ内部でのみアクセス可能な仮想IPアドレス(ClusterIP)を割り当てる。
- クラスタ外からは直接アクセスできない。
- ユースケース:
- マイクロサービス間の通信(例: フロントエンドからバックエンドAPIへのアクセス)。
- データベースやキャッシュなど、クラスタ内部で完結するサービス。
- メリット: セキュリティが高く、クラスタ内部の連携に最適。
- デメリット: 外部からのアクセスには使えない。
2. NodePort: 各ノードのIPアドレスとポートで公開
NodePortは、k8sクラスタの各ノード(Worker Node)に、指定したポートを公開し、そのポートにアクセスがあった場合に、Serviceへトラフィックを転送するTypeだ。
- 特徴:
- k8sクラスタの各ノードのIPアドレスと、指定された「NodePort」(通常30000~32767の範囲)でサービスが公開される。
- 外部から `[ノードIPアドレス]:[NodePort]` でアクセスできる。
- 設定例:
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort # TypeをNodePortに指定
selector:
app: my-app
ports:
- protocol: TCP
port: 80 # Serviceのポート(クラスタ内部で使われる)
targetPort: 8080 # Podがリッスンしているポート
# nodePort: 30080 # 省略すると自動で割り当てられる
- ユースケース:
- 開発環境での手軽な公開。
- 小規模なサービスで、外部ロードバランサーを導入するほどではない場合。
- メリット: 外部ロードバランサーなしで、手軽に外部公開できる。
- デメリット:
- 各ノードのIPアドレスとNodePortを管理する必要があり、運用が煩雑になる。
- ノード障害が発生した場合、そのノードにアクセスできなくなる(単一障害点になりやすい)。
- IPアドレスが長くなりがちで、覚えにくい。
- ポート番号の範囲が限られている。
3. LoadBalancer: クラウドプロバイダーのロードバランサーを利用
LoadBalancer Typeは、k8sが動作しているクラウド環境(AWS, GCP, Azureなど)のマネージドロードバランサーを利用して、サービスを公開する最も一般的な方法だ。
- 特徴:
- クラウドプロバイダーが提供するロードバランサー(例: AWS ELB, GCP Load Balancer)が自動的にプロビジョニングされる。
- ロードバランサーには、グローバルにアクセス可能なIPアドレスが割り当てられる。
- ロードバランサーは、クラスタ内の複数のノードにトラフィックを分散してくれる。
- 設定例:
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer # TypeをLoadBalancerに指定
selector:
app: my-app
ports:
- protocol: TCP
port: 80 # Serviceのポート
targetPort: 8080 # Podがリッスンしているポート
- ユースケース:
- 本番環境で、可用性の高い外部公開が必要な場合。
- スケーラブルなサービスを公開したい場合。
- メリット:
- クラウドプロバイダーのロードバランサー機能(ヘルスチェック、SSL終端など)を利用できる。
- 可用性が高く、単一障害点になりにくい。
- グローバルIPアドレスでアクセスできるため、管理が容易。
- デメリット:
- クラウドプロバイダーごとに課金が発生する。
- プロビジョニングに時間がかかる場合がある。
- IPアドレスの数が限られる場合がある(特にIPv4)。
どれを選ぶ?:使い分けのポイント
- クラスタ内部でのみ使うなら: `ClusterIP` (デフォルト)
- 手軽に試したい、開発環境なら: `NodePort`
- 本番環境で安定した公開をしたいなら: `LoadBalancer`
しかし、`LoadBalancer` Typeにも、いくつか課題がある。それは、「1つのServiceで1つのロードバランサーがプロビジョニングされる」という点だ。つまり、多数のアプリケーションを公開しようとすると、その数だけロードバランサーが作られ、コストがかさんでしまう可能性があるんだ。
そこで、登場するのが、次の「Ingress」という強力な仕組みなんだ!
—
3. Ingressコントローラー(Nginx等)の導入とルーティング設定:本番運用で必須の強力な武器
「Ingress」は、k8sクラスタへの外部トラフィックを管理するためのAPIリソースだ。Service Typeの`LoadBalancer`が「1 Service = 1 LoadBalancer」だったのに対し、Ingressは「1つのロードバランサーで、複数のServiceへトラフィックをルーティングする」ことを可能にする。まるで、巨大なビルの一つの入り口から、様々なテナント(Service)へ案内してくれるコンシェルジュのような存在だね。
Ingressの仕組み:コントロールプレーンとデータプレーン
Ingressを理解するには、まず「Ingressコントローラー」の存在が重要になる。
- Ingressリソース: どのホスト名やパスへのアクセスを、どのServiceに転送するか、といったルーティングルールを定義するYAMLファイル。
- Ingressコントローラー: Ingressリソースの設定を読み取り、実際にトラフィックのルーティングを行うソフトウェア。一般的には、Nginx、HAProxy、Traefikなどが使われる。Ingressコントローラーは、通常、`LoadBalancer` TypeのServiceとしてデプロイされることが多い。
つまり、
1. ユーザーが `example.com/app1` にアクセス。
2. 外部ロードバランサー(IngressコントローラーがデプロイされたService Type)がトラフィックを受け取る。
3. Ingressコントローラーが、Ingressリソースの設定を見て、`example.com/app1` へのアクセスは `app1-service` に転送すべきだと判断。
4. Ingressコントローラーが、`app1-service` にトラフィックを転送。
という流れになる。
Nginx Ingress Controllerの導入
ここでは、最もポピュラーなNginx Ingress Controllerを例に、導入手順を見ていこう。
前提:
- `kubectl` コマンドが使えること。
- `k8s` クラスタが構築済みであること。
1. Namespaceの作成:
まず、Ingress Controller用のNamespaceを作成する。
kubectl create namespace ingress-nginx
2. Nginx Ingress Controllerのインストール:
Helm(k8sのパッケージマネージャー)を使うのが一番簡単で一般的だ。Helmがインストールされていない場合は、先にインストールしておこう。
Helmリポジトリの更新
helm repo update
Nginx Ingress Controllerのインストール
helm install ingress-nginx ingress-nginx/ingress-nginx \
–namespace ingress-nginx \
–set controller.replicaCount=2 \ # 冗長化のためレプリカ数を2に
–set controller.nodeSelector.”kubernetes\.io/os”=linux \ # Linuxノードで動かす場合
–set controller.metrics.enabled=true # メトリクスを有効にする場合
インストールが完了すると、`ingress-nginx` NamespaceにPodやServiceが作成される。
インストールされたか確認
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
`ingress-nginx-controller` という名前のServiceが、`LoadBalancer` Typeで作成されているはずだ。このServiceのExternal IPアドレスが、外部からのアクセスポイントになる。
3. サンプルアプリケーションのデプロイ:
テスト用に、簡単なWebアプリケーションを2つデプロイしてみよう。
`app1.yaml`:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app1-deployment
spec:
replicas: 2
selector:
matchLabels:
app: app1
template:
metadata:
labels:
app: app1
spec:
containers:
- name: app1
image: nginxdemos/hello # 簡単なWebサーバーイメージ
ports:
- containerPort: 80
`app1-service.yaml`:
apiVersion: v1
kind: Service
metadata:
name: app1-service
spec:
selector:
app: app1
ports:
- protocol: TCP
port: 80
targetPort: 80
`app2.yaml`:
apiVersion: apps/v1
kind: Deployment
metadata:
name: app2-deployment
spec:
replicas: 2
selector:
matchLabels:
app: app2
template:
metadata:
labels:
app: app2
spec:
containers:
- name: app2
image: nginxdemos/hello
ports:
- containerPort: 80
`app2-service.yaml`:
apiVersion: v1
kind: Service
metadata:
name: app2-service
spec:
selector:
app: app2
ports:
- protocol: TCP
port: 80
targetPort: 80
これらのファイルを適用する。
kubectl apply -f app1.yaml
kubectl apply -f app1-service.yaml
kubectl apply -f app2.yaml
kubectl apply -f app2-service.yaml
4. Ingressリソースの作成:
いよいよ、Ingressリソースを作成して、ルーティングルールを定義する。
`my-ingress.yaml`:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
# Nginx Ingress Controllerを使うことを明示
# kubernetes.io/ingress.class: “nginx” # 古いバージョン用
# nginx.ingress.kubernetes.io/rewrite-target: / # 必要に応じてリライト設定
spec:
rules:
# app1.example.com へのアクセスを app1-service にルーティング
- host: app1.example.com
http:
paths:
- path: /
pathType: Prefix # Prefixマッチング
backend:
service:
name: app1-service # 転送先のService名
port:
number: 80 # 転送先のServiceポート番号
# app2.example.com へのアクセスを app2-service にルーティング
- host: app2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app2-service
port:
number: 80
このIngressリソースを適用する。
kubectl apply -f my-ingress.yaml
5. 動作確認:
IngressコントローラーのServiceのExternal IPアドレスを確認しよう。
kubectl get svc -n ingress-nginx
ここで取得したIPアドレスを、ローカルの`/etc/hosts`ファイルに追記するか、DNS設定で `app1.example.com` および `app2.example.com` をそのIPアドレスに紐づける。
ローカルでのhostsファイル編集例:
(Linux/macOSの場合)
/etc/hosts ファイルに以下を追加 (IPアドレスは実際の値に置き換える)
これで、ブラウザで `http://app1.example.com` にアクセスするとapp1の、`http://app2.example.com` にアクセスするとapp2のWebサーバーが表示されるはずだ!
Ingressを使うことで、一つの外部IPアドレスで複数のアプリケーションを、ホスト名やパスごとに柔軟にルーティングできるようになった。これは、本番環境で多くのサービスを公開する際に、コスト効率と管理効率を劇的に改善してくれる、まさに必須の技術なんだ。
—
4. TLS/SSL証明書の導入手順:安全な通信を実現しよう
現代のWebサービスでは、HTTPSによる暗号化通信は必須だ。k8sでIngressを使ってアプリケーションを公開している場合、TLS/SSL証明書を導入してHTTPS化するのは、Ingressコントローラーの得意技の一つなんだ。
TLS/SSL証明書の管理:Secretリソース
k8sでは、証明書のような機密情報を「Secret」というリソースで管理する。TLS/SSL証明書をSecretとして保存することで、アプリケーションやIngressコントローラーが安全にそれを利用できるようになるんだ。
1. 証明書の準備:
まずは、利用したいTLS/SSL証明書(公開鍵証明書ファイル `tls.crt` と秘密鍵ファイル `tls.key`)を用意する。
- 本番環境: Let’s Encryptなどの認証局から取得した正規の証明書を使う。
- 開発/テスト環境: 自己署名証明書や、ローカル開発用の証明書(mkcertなど)でもOK。
2. Secretリソースの作成:
証明書ファイルをk8sのSecretとして作成する。`tls` というキーで証明書と秘密鍵を格納するのが一般的だ。
証明書ファイル (tls.crt) と秘密鍵ファイル (tls.key) がカレントディレクトリにあるとする
kubectl create secret tls my-tls-secret \
–namespace <アプリケーションをデプロイしているNamespace> \
–cert=tls.crt \
–key=tls.key
3. IngressリソースでのTLS設定:
作成したSecretをIngressリソースで参照するように設定する。
`my-ingress-tls.yaml` (先ほどのIngress設定にTLS部分を追加):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress-tls
annotations:
# Nginx Ingress Controllerを使用
nginx.ingress.kubernetes.io/ssl-redirect: “true” # HTTPリクエストをHTTPSにリダイレクトする場合
spec:
tls:
# TLS設定を適用するホスト名とSecret名を指定
- hosts:
- app1.example.com # TLSを適用したいホスト名
- app2.example.com
secretName: my-tls-secret # 作成したSecretリソースの名前
rules:
- host: app1.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app1-service
port:
number: 80
- host: app2.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app2-service
port:
number: 80
このIngressリソースを適用する。
kubectl apply -f my-ingress-tls.yaml
4. 動作確認:
再度、ブラウザで `https://app1.example.com` や `https://app2.example.com` にアクセスしてみよう。
- 正規の証明書の場合: ブラウザのアドレスバーに鍵マークが表示され、安全なHTTPS通信ができていることが確認できるはずだ。
- 自己署名証明書の場合: ブラウザで「安全ではありません」といった警告が表示されるが、これは証明書が信頼されていないためであり、通信自体は暗号化されている。開発環境でのテストとしては十分だ。
Let’s Encrypt との連携 (cert-manager)
本番環境では、Let’s Encryptのような無料の証明書を自動で取得・更新するのが一般的だ。k8sでは、`cert-manager` というツールを導入することで、このプロセスを完全に自動化できる。
1. `cert-manager` をk8sクラスタにインストールする。
2. `Issuer` または `ClusterIssuer` リソースを作成し、Let’s EncryptのACMEチャレンジ設定を行う。
3. IngressリソースのAnnotationで `cert-manager.io/cluster-issuer` などを指定する。
これで、Ingressリソースを適用するだけで、証明書の取得、更新、そしてIngressへの適用まで、全て自動で行ってくれるようになるんだ。これは、運用負荷を劇的に減らしてくれる、まさに魔法のような仕組みだよ。
—
まとめ:k8sのサービス公開はIngressで決まり!
お疲れ様! 今日は、Kubernetesでアプリケーションを外部に公開するための、ClusterIP、NodePort、LoadBalancerといった基本的なサービスタイプから、本番運用で必須となるIngressの活用、そしてTLS/SSL証明書の導入まで、じっくりと解説してきたよ。
- ClusterIP: クラスタ内部通信の基本。
- NodePort: 手軽だが、本番には向かない。
- LoadBalancer: シンプルだが、コストや管理の課題も。
- Ingress: 複数のServiceを効率的に公開する、本番運用のデファクトスタンダード。
- TLS/SSL: cert-managerと組み合わせることで、HTTPS化も自動化できる。
特にIngressは、ルーティングの柔軟性、コスト効率、そしてTLS証明書の自動管理といった多くのメリットをもたらしてくれる。これをマスターすれば、君のk8sでのサービス公開スキルは、一気にプロフェッショナルレベルに引き上げられるはずだ。
最初は少し複雑に感じるかもしれないけれど、一つずつ試していけば、きっと理解できるはず。この知識を活かして、君のアプリケーションを世界に羽ばたかせてあげよう!
もし、もっと深く知りたいことや、疑問に思ったことがあれば、いつでも気軽に聞いてくれ。君のk8sエンジニアとしての成長を、心から応援しているよ!