k8s Service完全ガイド:ClusterIP, NodePort, LoadBalancerを理解する

はい、承知いたしました。Kubernetes ServiceのClusterIP, NodePort, LoadBalancerタイプに焦点を当てた、約5000語の詳細なガイド記事を作成します。


k8s Service完全ガイド:ClusterIP, NodePort, LoadBalancerを理解する

はじめに:なぜKubernetes Serviceが必要なのか?

マイクロサービスアーキテクチャが普及し、アプリケーションは小さく分割された複数のサービスとして開発・運用されることが一般的になりました。Kubernetesのようなコンテナオーケストレーションプラットフォームは、これらのサービスを構成するコンテナ(Podとして実行)のデプロイ、スケーリング、管理を自動化します。

しかし、Kubernetes上でPodを運用する際には、いくつかの課題が発生します。

  1. PodのEphemeral性(短命性): PodはKubernetesによって柔軟にスケールイン/アウトされたり、クラッシュした場合に再起動されたり、ローリングアップデートによって置き換えられたりします。そのたびに、PodのIPアドレスは変更される可能性があります。クライアントや他のサービスが、常に変動するPodのIPアドレスを直接知っていると、通信が破綻してしまいます。
  2. Podの発見(Discovery): クライアントや他のサービスは、目的の機能を提供するPodがどこにあるのか(どのIPアドレスとポートで待機しているのか)を知る必要があります。動的に生成・消滅するPodをどのように発見すれば良いでしょうか?
  3. 負荷分散: 同じ機能を提供するPodが複数ある場合(ReplicaSetやDeploymentで管理されている場合)、それらのPodにトラフィックを均等に分散させる仕組みが必要です。
  4. 外部からのアクセス: クラスター内部で動作するPodに、外部インターネットや企業ネットワークからアクセスさせたい場合があります。

これらの課題を解決するために、KubernetesはServiceという抽象化レイヤーを提供します。Serviceは、一連のPodに対して安定したネットワークエンドポイント(IPアドレスとポート)を提供し、それらのPodへのトラフィックをルーティング・負荷分散する役割を担います。

本記事では、Kubernetes Serviceの基本的な概念から、最も一般的で重要な3つのタイプ、すなわちClusterIPNodePortLoadBalancerについて、その仕組み、ユースケース、設定方法、メリット・デメリットを詳細に解説します。さらに、関連する高度な概念や、Serviceを選択する際の考慮事項、トラブルシューティングについても触れ、Kubernetes Serviceを完全に理解するための手引きとなることを目指します。

Kubernetes Serviceとは?

Kubernetes Serviceは、特定の機能を提供する一連のPodを論理的にグループ化し、そのグループに対して安定したネットワークアクセス手段を提供するリソースです。Serviceは、Podのように実行されるコンテナを持つわけではありません。Serviceは、特定のPod群へのトラフィックをどのように発見し、ルーティングし、負荷分散するかを定義する抽象化です。

Serviceが対象とするPod群は、通常、Label Selectorによって指定されます。Podを作成する際に付与するラベル(例: app: my-web-app, version: v1)をServiceの定義で指定することで、Serviceはそのラベルを持つすべてのPodを自動的にバックエンドとして認識します。Podが作成されたり削除されたりしてIPアドレスが変わっても、ServiceはLabel Selectorに基づいて常に最新のPodリスト(正確にはEndpointsオブジェクト)を把握し、トラフィックをルーティングします。

Serviceは通常、以下の要素で定義されます。

  • Label Selector (spec.selector): このServiceがトラフィックをルーティングする対象となるPod群を指定します。指定されたラベルを持つすべてのPodが、このServiceのバックエンドとなります。
  • Ports (spec.ports):
    • port: Service自体のポートです。クライアントはこのポートにアクセスします。
    • targetPort: Serviceがトラフィックを転送する先のPodのポートです。Pod内でアプリケーションが実際にリッスンしているポートを指定します。
    • protocol: トラフィックのプロトコル(TCP, UDP, SCTP)。デフォルトはTCPです。
  • Type (spec.type): このServiceがどのような方法で公開されるかを指定します。本記事で詳しく解説するClusterIP, NodePort, LoadBalancerなどがこれにあたります。
  • ClusterIP (spec.clusterIP): ClusterIPタイプの場合に自動的に割り当てられる、クラスター内部でのみ到達可能な仮想IPアドレスです。通常はKubernetesによって自動的に割り当てられます。
  • NodePort (spec.nodePort): NodePortタイプの場合に、各Kubernetesノード上で開放されるポートです。外部からのアクセスはこのポートを経由してServiceに到達します。通常はKubernetesによって自動的に割り当てられます。

Serviceは、クライアント(他のPodや外部ユーザー)とバックエンドのPodの間に入り、PodのIPアドレスの変動を隠蔽し、安定したインターフェースを提供します。

Serviceの主要なタイプ

Kubernetesは、Serviceの公開方法に応じていくつかのタイプを提供しています。ここでは最も一般的な3つのタイプ、ClusterIPNodePortLoadBalancerについて掘り下げていきます。

1. ClusterIP

概要:

ClusterIPはServiceのデフォルトのタイプです。このタイプのServiceを作成すると、Kubernetesクラスター内部でのみ到達可能な仮想IPアドレス (Cluster IP) が割り当てられます。このIPアドレスはクラスター内部のPodや他のServiceからアクセスできますが、クラスター外部からは直接アクセスできません

ClusterIP Serviceは、主にクラスター内部のサービス間通信に使用されます。例えば、フロントエンドサービスがバックエンドAPIサービスと通信する場合などに利用されます。ClusterIPは安定しており、バックエンドのPodがスケールしたり再起動したりしても変化しません。

仕組み:

ClusterIP Serviceの仕組みは、主にKubernetesコンポーネントであるkube-proxyによって実現されます。

  1. ServiceとEndpointsの作成: Serviceを作成すると、Kubernetes APIサーバーはServiceオブジェクトを保存します。同時に、Serviceのselectorに一致するPodのIPアドレスとポートのリストを含むEndpointsオブジェクトが自動的に作成・更新されます。
  2. kube-proxyの監視: クラスター内の各ノードで実行されているkube-proxyは、Kubernetes APIサーバーを監視し、ServiceとEndpointsオブジェクトの変更を検知します。
  3. ローカルルーティングルールの設定: kube-proxyは、これらの情報に基づいて、そのノード上で動作するコンテナからのトラフィックをルーティングするためのネットワークルールを設定します。これは通常、LinuxのiptablesまたはIPVS (IP Virtual Server) を使用して行われます。
    • iptablesモード: kube-proxyは、ServiceのCluster IPとポート宛てのトラフィックを捕捉し、対応するEndpoints(バックエンドPodのIPとポート)の中から一つにランダムに(またはラウンドロビンで)転送するためのiptablesルールを設定します。これはDestination Network Address Translation (DNAT) を利用して行われます。
    • IPVSモード: IPVSモードはより高性能で、大規模なクラスターに適しています。kube-proxyは、Cluster IPとポートに対してIPVS仮想サーバーを設定し、バックエンドPodをそのリアルサーバーとして登録します。IPVSは様々な負荷分散アルゴリズム(ラウンドロビン、最小コネクションなど)をサポートします。
  4. トラフィックのルーティング: クラスター内のPodがServiceのCluster IPとポートにアクセスしようとすると、ノードのネットワークスタックがkube-proxyによって設定されたiptables/IPVSルールを捕捉し、トラフィックは自動的にバックエンドのPodのいずれかに転送されます。

kube-proxyがEndpointを常に監視しているため、バックエンドPodが追加・削除されても、ルーティングルールは自動的に更新され、Serviceは常に最新のPodリストにトラフィックをルーティングできます。

ユースケース:

  • クラスター内部のマイクロサービス間通信: 最も一般的なユースケースです。例えば、WebサーバーPodがAPIサーバーPodと通信する場合など。
  • 内部データベースへのアクセス: アプリケーションPodがクラスター内でホストされているデータベースPodにアクセスする場合。
  • デフォルトのServiceタイプ: 外部に公開する必要がないPod群に対しては、セキュリティとシンプルさの観点からClusterIPがデフォルトの選択肢となります。

設定例 (YAML):

yaml
apiVersion: v1
kind: Service
metadata:
name: my-internal-service
spec:
selector:
app: my-app
tier: backend
ports:
- protocol: TCP
port: 80 # Serviceのポート
targetPort: 8080 # バックエンドPodのポート
type: ClusterIP # ClusterIPタイプであることを明示 (省略時はデフォルトでClusterIP)

上記の例では、app: my-appかつtier: backendというラベルを持つPod群に対して、my-internal-serviceという名前のServiceを定義しています。このServiceはクラスター内部の仮想IPを持ち、Serviceのポート80に到達したTCPトラフィックを、バックエンドPodのポート8080に転送します。

このServiceを作成すると、Kubernetesは自動的にCluster IPを割り当てます。他のPodは、Service名 my-internal-service (またはそのCluster IP) とポート80を使用して、このServiceにアクセスできます。KubernetesのDNS addon (CoreDNSなど) により、Service名はクラスター内部で解決可能になります。

メリット:

  • シンプルで理解しやすい: 最も基本的なServiceタイプです。
  • セキュリティが高い: クラスター内部からのみアクセス可能なため、外部からの不要なアクセスを防ぎます。
  • 安定した内部エンドポイント: Podの変更に関わらず、Cluster IPとポートは安定しています。
  • デフォルトの挙動: typeフィールドを省略した場合のデフォルトであるため、手軽に利用できます。

デメリット:

  • 外部からのアクセス不可: クラスター外部から直接アクセスすることはできません。外部公開するには、他のServiceタイプやIngressと組み合わせて使用する必要があります。

注意点:

  • Cluster IPは仮想的なIPであり、ネットワークインターフェースに直接割り当てられているわけではありません。ノード内部のルーティングルールによって機能します。
  • 通常、Service名はクラスター内部DNSによって解決されるため、PodはCluster IPアドレスを直接知らなくても、Service名でアクセスできます。例えば、ping my-internal-service や、HTTPリクエストのURLとしてhttp://my-internal-service/api のように使用できます。

2. NodePort

概要:

NodePort Serviceは、ClusterIP Serviceの上に構築されるタイプです。このタイプを指定すると、Kubernetesクラスター内の各ノードの特定ポート (nodePort) が開放され、そのポート宛てのトラフィックがService(そしてバックエンドのPod)に転送されます。

NodePort Serviceを使用すると、クラスター外部から任意のノードのIPアドレスと開放されたnodePort を使用してServiceにアクセスできるようになります。例えば、http://<ノードのIPアドレス>:<nodePort> の形式でアクセス可能です。

NodePortはClusterIPの上に構築されるため、NodePort Serviceは対応するClusterIPも持ちます。ノードポート宛てのトラフィックは、まずそのノード上で捕捉され、対応するClusterIPとポートにルーティングされた後、通常通りバックエンドのPodに転送されます。

仕組み:

  1. ClusterIP Serviceの作成: NodePort Serviceを作成すると、まず対応するClusterIP Serviceが内部的に作成されます。
  2. NodePortの割り当て: Kubernetesは、デフォルトで30000-32767の範囲から未使用のポートを自動的に割り当て、それをnodePortとして使用します。YAML定義で特定のポートを指定することも可能ですが、推奨されません(ポート衝突の可能性があるため)。
  3. kube-proxyによるルール設定: クラスター内の各ノードで実行されているkube-proxyは、NodePort Serviceの情報を監視し、各ノードのnodePort宛てのトラフィックを、対応するClusterIPとServiceポートに転送するためのネットワークルール(iptables/IPVS)を設定します。
  4. 外部からのアクセス: クラスター外部のクライアントは、任意のノードのIPアドレスと、Serviceに割り当てられたnodePortを使用してアクセスします。
  5. トラフィックのルーティング: クライアントからのトラフィックがノードのnodePortに到達すると、kube-proxyによって設定されたルールにより、そのトラフィックはServiceのCluster IPとServiceポートに転送されます。そこからはClusterIP Serviceの場合と同様に、バックエンドのPodのいずれかにルーティングされます。このルーティングは、トラフィックを受け取ったノード上のバックエンドPodだけでなく、クラスター内の他のノード上のバックエンドPodにも転送可能です(External Traffic Policyの設定による)。

ユースケース:

  • 開発/テスト環境での簡易な外部公開: クラウドプロバイダーのロードバランサーを用意する手間をかけずに、ローカルネットワークや特定の環境からアプリケーションにアクセスしたい場合に便利です。
  • デモやPOC (Proof of Concept): Kubernetesクラスターの機能を簡単に外部に見せたい場合に使用できます。
  • ロードバランサーのバックエンド: クラウドロードバランサーやオンプレミスのロードバランサーのバックエンドとして、各ノードのnodePortを指定する構成(後述のLoadBalancerタイプと組み合わせて利用されることが多い)が可能です。

設定例 (YAML):

yaml
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
selector:
app: my-web-app
ports:
- protocol: TCP
port: 80 # Serviceのポート
targetPort: 80 # バックエンドPodのポート
# nodePort: 30080 # 任意指定する場合はこの行を追加 (非推奨)
type: NodePort

上記の例では、app: my-web-appというラベルを持つPod群に対して、my-nodeport-serviceという名前のNodePort Serviceを定義しています。このServiceは、Kubernetesによって自動的に割り当てられたnodePortを使用し、クラスター内の各ノードのそのポートを開放します。外部クライアントは、任意のノードのIPアドレスと割り当てられたnodePortを使って、Serviceのポート80、そしてバックエンドPodのポート80にアクセスできます。

割り当てられたnodePortを確認するには、kubectl get svc my-nodeport-serviceコマンドを使用します。出力には、PORT(S)列に 80:30080/TCP のようにServiceポートと割り当てられたnodePortが表示されます(30080は例)。

メリット:

  • 外部からアクセス可能: ClusterIPとは異なり、クラスター外部からServiceにアクセスできます。
  • 設定が簡単: ServiceのtypeNodePortに設定するだけで基本的な外部公開が可能です。
  • クラウドプロバイダーに依存しない: どのような環境のKubernetesクラスターでも利用できます(ベアメタル、仮想マシンなど)。

デメリット:

  • 利用可能なポート範囲が限られる: nodePortに割り当てられるポートは、通常30000-32767の範囲に限定されます。
  • 各ノードのIPアドレスを知る必要がある: アクセスするには、少なくともいずれかのノードのIPアドレスを知っている必要があります。ノードが追加/削除されると、利用可能なIPアドレスリストが変わる可能性があります。
  • ノード障害の影響を受ける: 特定のノードにアクセスしている間にそのノードがダウンすると、アクセスできなくなります。高可用性を実現するためには、外部のロードバランサーと組み合わせて、複数のノードのnodePortにトラフィックを分散させる必要があります。
  • セキュリティリスク: クラスター内の各ノードの特定のポートが外部に公開されるため、注意が必要です。

注意点:

  • YAMLでnodePortを明示的に指定することは可能ですが、ポートの衝突を避けるため、通常はKubernetesに自動割り当てさせるのが推奨されます。
  • NodePort Serviceは、多くのクラウド環境において、LoadBalancer Serviceの基盤として内部的に使用されます。クラウドプロバイダーのロードバランサーは、各ノードのNodePortにトラフィックを転送するように構成されることが一般的です。

3. LoadBalancer

概要:

LoadBalancer Serviceは、クラウドプロバイダーのロードバランサーと連携して、アプリケーションを外部に公開するためのServiceタイプです。このタイプを指定すると、Kubernetesは利用しているクラウドプロバイダー(AWS, GCP, Azureなど)に対して、外部ロードバランサーを作成するように要求します。

プロビジョニングされたロードバランサーには、外部からアクセス可能な安定したIPアドレスが割り当てられます。この外部IPアドレス宛てのトラフィックは、クラウドプロバイダーのロードバランサーによって、Kubernetesクラスター内のバックエンドPod(通常はNodePort Serviceを介して)にルーティング・負荷分散されます。

LoadBalancerタイプは、本番環境で広く利用される、高可用性、スケーラビリティ、信頼性に優れた外部公開手段です。

仕組み:

LoadBalancer Serviceの仕組みは、NodePort Serviceとクラウドプロバイダーのインフラストラクチャが組み合わさって実現されます。

  1. NodePort Serviceの内部作成: LoadBalancer Serviceを作成すると、Kubernetesは通常、対応するNodePort Serviceを内部的に作成します。これにより、クラスター内の各ノードで特定のnodePortが開放されます。
  2. クラウドプロバイダーAPIとの連携: Kubernetesのコントロールプレーン(特にkube-controller-managerの一部であるCloud Controller Manager)は、LoadBalancerタイプのServiceが作成されたことを検知します。
  3. ロードバランサーのプロビジョニング: Cloud Controller Managerは、利用しているクラウドプロバイダーのAPIを呼び出し、外部ロードバランサーをプロビジョニングするように要求します。このロードバランサーは、NodePort Serviceによって開放された各ノードのnodePortをバックエンドターゲットとして設定するように構成されます。
  4. 外部IPの割り当て: クラウドプロバイダーはロードバランサーを作成し、外部からアクセス可能なIPアドレス(またはDNS名)を割り当てます。この情報(外部IP)は、Kubernetes Serviceオブジェクトのstatus.loadBalancer.ingressフィールドに反映されます。
  5. 外部からのアクセス: クラスター外部のクライアントは、プロビジョニングされたロードバランサーの外部IPアドレスと、Serviceに指定したポート(通常はServiceポート)を使用してアクセスします。
  6. トラフィックのルーティング: クライアントからのトラフィックはまずクラウドプロバイダーのロードバランサーに到達します。ロードバランサーは、設定されたバックエンド(通常は各ノードのnodePort)の中から健全なノードを選択し、そのノードのnodePortにトラフィックを転送します。ノードに到達したトラフィックは、NodePort Serviceの仕組みによってServiceのCluster IPとServiceポートにルーティングされ、最終的にバックエンドのPodのいずれかに到達します。

この仕組みにより、たとえ一部のノードやPodがダウンしても、ロードバランサーが健全性チェックを行い、トラフィックを正常なバックエンドにのみ転送することで、高可用性が実現されます。

ユースケース:

  • 本番環境でのアプリケーション外部公開: 高可用性、スケーラビリティ、信頼性が求められるWebアプリケーションやAPIサービスの外部公開。
  • 安定した外部IPアドレスの提供: 外部からのアクセスポイントとして、固定されたIPアドレスを提供したい場合。

設定例 (YAML):

yaml
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
selector:
app: my-web-app
tier: frontend
ports:
- protocol: TCP
port: 80 # クライアントがロードバランサーにアクセスするポート
targetPort: 80 # バックエンドPodのポート
type: LoadBalancer
# loadBalancerIP: "YOUR_EXTERNAL_IP" # 特定の静的IPを割り当てる場合 (プロバイダーによる)

上記の例では、app: my-web-appかつtier: frontendというラベルを持つPod群に対して、my-loadbalancer-serviceという名前のLoadBalancer Serviceを定義しています。Kubernetesはクラウドプロバイダーにロードバランサーの作成を要求し、外部IPが割り当てられます。クライアントは割り当てられた外部IPのポート80にアクセスすることで、バックエンドのPodに到達できます。

割り当てられた外部IPを確認するには、kubectl get svc my-loadbalancer-serviceコマンドを使用します。出力のEXTERNAL-IP列に表示されます。プロビジョニングには時間がかかる場合があります。

メリット:

  • 高可用性とスケーラビリティ: クラウドプロバイダーのロードバランサーがトラフィック分散と健全性チェックを行うため、高い可用性とスケーラビリティが実現されます。
  • 安定した外部IPアドレス: 外部からのアクセスポイントとして固定されたIPアドレス(またはDNS名)が提供されます。
  • 設定がシンプル(K8s側): KubernetesのYAML定義としてはtype: LoadBalancerを指定するだけでよく、複雑な設定はクラウドプロバイダー側で自動的に行われます。

デメリット:

  • コストがかかる: クラウドプロバイダーのロードバランサーは通常、使用量に応じて料金が発生します。
  • クラウドプロバイダーに依存: このタイプのServiceは、クラウド環境でKubernetesを運用している場合にのみネイティブに利用できます(ベアメタル環境などでは代替手段が必要です)。
  • プロビジョニングに時間がかかる: ロードバランサーの作成・設定には時間がかかる場合があります。

注意点:

  • ベアメタル環境やオンプレミス環境など、クラウドプロバイダーのロードバランサーが利用できない環境では、MetalLBのようなサードパーティのソフトウェアロードバランサーを利用することで、LoadBalancerタイプのServiceをエミュレートできます。
  • クラウドプロバイダーによっては、spec.loadBalancerIPフィールドを指定することで、特定の静的外部IPアドレスをServiceに割り当てることができます。ただし、これはプロバイダー固有の機能であり、すべての環境で利用できるわけではありません。

Serviceの高度な概念と関連事項

Headless Service (clusterIP: None)

ClusterIP Serviceを作成する際に、spec.clusterIP: None を指定すると、Headless Serviceが作成されます。Headless ServiceにはCluster IPが割り当てられません。その代わりに、KubernetesのDNSは、Service名に対してバックエンドPodのIPアドレスリストを返します。

仕組み:

  • kube-proxyによるロードバランシングは行われません。
  • DNSクエリに対して、Headless Service名に対するAレコードとして、Serviceのセレクターに一致するすべてのバックエンドPodのIPアドレスが返されます。
  • クライアントは受け取ったPod IPアドレスリストから、自分で接続先のPodを選択する必要があります。

ユースケース:

  • StatefulSet: StatefulSetで管理されるPodは、それぞれが固有のIDと安定したネットワークIDを持つことが期待されます。Headless Serviceと組み合わせることで、各Podに対して pod-name.headless-service-name.namespace.svc.cluster.local のような安定したDNS名が割り当てられ、他のPodやクライアントは個々のPodに直接アクセスできます。
  • カスタムの負荷分散ロジック: アプリケーション側で独自の負荷分散やサービスディスカバリを行いたい場合(例: ZooKeeper, Cassandraなどの分散システム)。

設定例:

yaml
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
selector:
app: my-stateful-app
ports:
- protocol: TCP
port: 9042
targetPort: 9042
clusterIP: None # これがHeadless Serviceの指定

Session Affinity (spec.sessionAffinity)

デフォルトでは、Serviceはリクエストごとに異なるバックエンドPodにトラフィックを分散させます。しかし、特定のクライアントからの連続するリクエストを常に同じバックエンドPodにルーティングしたい場合があります(例: ステートフルなアプリケーション)。これを実現するのがSession Affinityです。

spec.sessionAffinity: ClientIP を設定すると、クライアントのIPアドレスに基づいて、そのクライアントからのトラフィックが常に同じバックエンドPodに転送されるようになります。

注意点:

  • Client IPベースのSession Affinityは、途中にNATやプロキシがある場合(例: NodePortやLoadBalancerを経由する場合、あるいはクラスター外部からアクセスする場合)に正しく機能しないことがあります。特にNodePortやLoadBalancer Serviceの場合、各ノードのIPアドレスやロードバランサーのIPアドレスがクライアントIPとして見えてしまう可能性があります。
  • これを解決するためには、spec.externalTrafficPolicy: Local を設定して、ソースIPを維持する(後述)必要があります。ただし、この設定はトラフィック分散に影響を与える可能性があります。

External Traffic Policy (spec.externalTrafficPolicy)

NodePort ServiceやLoadBalancer Serviceを経由してクラスター外部からトラフィックが到達した際に、そのトラフィックをどのようにバックエンドPodにルーティングするかを制御します。

  • Cluster (デフォルト): 外部からのトラフィックは、まずトラフィックを受け取ったノード(External peer node)に到達します。そこからkube-proxyのルールによって、クラスター内のどのノード上にいるバックエンドPodにも転送されます。この場合、バックエンドPodから見ると、接続元のIPアドレスは通常、External peer nodeのIPアドレスになります(元のクライアントIPは失われる)。これは負荷分散の均一性を保証しますが、元のクライアントIPを知ることができません。
  • Local: 外部からのトラフィックは、トラフィックを受け取ったノードに到達します。そこからkube-proxyのルールによって、そのノード上にいるバックエンドPodにのみ転送されます。この場合、バックエンドPodから見ると、接続元のIPアドレスは元のクライアントのIPアドレスになります(ソースIPが維持される)。ただし、トラフィックを受け取ったノードにバックエンドPodが存在しない場合、そのトラフィックはルーティングされず、到達できません。これはトラフィックの分散が不均一になる可能性を意味します。

ユースケース:

  • Cluster: ソースIPを維持する必要がなく、トラフィックをクラスター全体で均一に分散させたい場合。
  • Local: バックエンドPodでクライアントの元のIPアドレスが必要な場合(例: アクセスログ記録、IPベースの認証/制限)、またはSession Affinity (ClientIP) を外部アクセスで機能させたい場合。ただし、トラフィックの分散が偏る可能性があることを考慮する必要があります。

Ingressとの関係性

Serviceは、主にL4 (TCP/UDP) レベルでのトラフィックルーティングと負荷分散を行います。Podへのトラフィックは、ServiceのCluster IPやNodePort、あるいはLoadBalancer IPを経由して、バックエンドPodのIPアドレスとポートに転送されます。

一方、Ingressは、クラスター外部からのL7 (HTTP/HTTPS) トラフィックを管理するためのAPIオブジェクトです。Ingressは、ホスト名やパスに基づいてトラフィックを異なるServiceにルーティングしたり、SSL/TLS終端を提供したり、名前ベースの仮想ホスティングを実現したりします。

Ingress自体はトラフィックを処理する機能を持っておらず、Ingress Controller(Nginx Ingress, Traefik, GCE/AWS Load Balancer Controllerなど)と組み合わせて使用されます。Ingress ControllerはIngressリソースを監視し、その定義に基づいて外部ロードバランサー、プロキシサーバー、またはGateway APIなどのトラフィック処理インフラストラクチャを設定します。

Ingressは、着信したHTTP/HTTPSトラフィックをどのServiceにルーティングするかを定義します。つまり、IngressはトラフィックをServiceに転送し、ServiceがさらにトラフィックをバックエンドのPodに転送する、という連携が行われます。

なぜIngressが必要か?

LoadBalancer Serviceで外部公開する場合、通常はServiceごとに1つのロードバランサーが必要になります。これはコストがかかりますし、HTTPヘッダーやパスに基づく複雑なルーティング設定はできません。

Ingressを使用すると、1つの外部IPアドレス(Ingress Controllerに割り当てられたLoadBalancer Serviceなど)で複数のServiceを公開できます。Ingressルールによって、例えば example.com/app1 へのアクセスは app1-service に、example.com/app2 へのアクセスは app2-service にルーティングするといった柔軟な設定が可能になります。また、SSL/TLS証明書の管理や終端もIngress Controllerで行えるため、バックエンドPodはHTTPで動作させることができ、証明書管理の手間が軽減されます。

Serviceタイプの選び方:シナリオ別判断基準

どのServiceタイプを選択すべきかは、アプリケーションの要件とKubernetesクラスターの環境によって異なります。以下に、一般的なシナリオと推奨されるServiceタイプを示します。

  1. クラスター内部のサービス間通信:

    • 推奨: ClusterIP
    • 理由: 外部からのアクセスは不要であり、ClusterIPが最もシンプルで安全な選択肢です。Podの変動を隠蔽し、安定した内部アクセスポイントを提供します。
  2. 開発/テスト目的での簡易的な外部公開:

    • 推奨: NodePort
    • 理由: クラウドロードバランサーを用意する手間なく、すぐに外部からアクセス可能なエンドポイントを得られます。ただし、高可用性やスケーラビリティ、固定IPは期待できません。
  3. 本番環境での高可用性・スケーラビリティを伴う外部公開 (HTTP/HTTPS以外):

    • 推奨: LoadBalancer
    • 理由: クラウドプロバイダーのロードバランサーが、健全性チェックに基づいたトラフィック分散と高可用性を提供します。FTPやカスタムTCP/UDPプロトコルなど、L7ルーティングが不要な場合に適しています。
  4. 本番環境での高可用性・スケーラビリティを伴う外部公開 (HTTP/HTTPS) と複雑なルーティング:

    • 推奨: Ingress (Ingress Controller + Service)
    • 理由: ホスト名/パスベースのルーティング、SSL/TLS終端、単一IPでの複数Service公開など、L7トラフィック管理の高度な要件に対応できます。Ingressは通常、LoadBalancer Serviceタイプを持つIngress Controllerによって外部公開されます。
  5. 個々のPodに直接アクセスしたい、またはアプリケーション側でサービスディスカバリを行いたい:

    • 推奨: Headless Service (clusterIP: None)
    • 理由: Cluster IPを持たず、DNSがPodのIPアドレスリストを返します。StatefulSetや特定の分散システムに適しています。
  6. 外部公開しつつ、バックエンドPodでクライアントの元のIPアドレスが必要:

    • 推奨: NodePort または LoadBalancer + spec.externalTrafficPolicy: Local
    • 理由: Localポリシーを設定することでソースIPを維持できます。ただし、トラフィック分散が不均一になるリスクがあります。この要件がない場合は、通常Clusterポリシー(デフォルト)が推奨されます。

これらの基準は一般的なものであり、特定のKubernetesディストリビューションやクラウドプロバイダーの機能(例えば、LoadBalancer Serviceの追加設定オプションなど)によって、最適な選択肢が異なる場合もあります。

Serviceのトラブルシューティング

Serviceに関する問題が発生した場合、以下のステップで調査を進めることができます。

  1. Serviceオブジェクト自体を確認する:

    • kubectl get svc <service-name>: Serviceのタイプ、ClusterIP、Ports、External-IP (LoadBalancerの場合)、NodePort (NodePortの場合) などを確認します。
    • kubectl describe svc <service-name>: Serviceの詳細情報を確認します。特に重要なのは以下の点です。
      • Selector: このServiceがどのPodを対象にしているかを確認します。これが正しくないと、バックエンドPodが見つかりません。
      • Endpoints: このServiceに紐づけられているバックエンドPodのIPアドレスとポートのリストを確認します。ここに想定しているPodのIPアドレスとtargetPortが表示されているか? Endpointsが空の場合は、SelectorがPodに一致していないか、一致するPodが起動していない可能性があります。
      • Events: Serviceに関連するイベント(LoadBalancerの作成失敗など)が表示されていないか確認します。
  2. バックエンドPodを確認する:

    • kubectl get pods -l <selector-labels>: Serviceのselectorに指定したラベルを持つPodがリストアップされるか確認します。
    • kubectl get pods -o wide -l <selector-labels>: PodのIPアドレスや、どのノードで実行されているかを確認します。Endpointsに表示されているIPアドレスと一致するか確認します。
    • kubectl describe pod <pod-name>: バックエンドPodの詳細を確認します。PodがRunning状態であるか、指定したtargetPortでアプリケーションがリッスンしているか(コンテナログなどで確認)を確認します。
    • kubectl logs <pod-name>: アプリケーションが正常に起動し、指定されたtargetPortでリッスンしているか、エラーが発生していないかログで確認します。
  3. Pod間の通信を確認する (ClusterIPの場合):

    • 同じクラスター内の別のPodから、Service名またはCluster IPとServiceポートへの疎通を確認します。例えば、busyboxなどのデバッグPodを起動し、ping <service-name>telnet <service-name> <service-port> を実行してみます。
  4. 外部からの通信を確認する (NodePort, LoadBalancerの場合):

    • NodePort: 外部から、任意のノードのIPアドレスと割り当てられたnodePortに対してアクセスしてみます (curl http://<node-ip>:<node-port>). 特定のノードでしかアクセスできないか、他のノードでもアクセスできるか確認します。
    • LoadBalancer: 外部から、ServiceのEXTERNAL-IPとServiceポートに対してアクセスしてみます (curl http://<external-ip>:<service-port>). External-IPがまだ割り当てられていない場合は、しばらく待つか、kubectl describe svcのEventsを確認します。
    • Firewall/Security Groups: クラウドプロバイダーのセキュリティグループや、各ノードのファイアウォール設定が、必要なポート(NodePortやLoadBalancerが使用するポート)をブロックしていないか確認します。
  5. kube-proxyのログを確認する:

    • 各ノード上のkube-proxyのログを確認することで、ルーティングルールの設定に関するエラーや警告がないか確認できます。ログの場所はKubernetesのセットアップ方法によって異なります(Systemd service logs, Docker logs, Containerd logsなど)。
  6. ServiceポートとtargetPortのマッピングを確認する:

    • Service定義のportsセクションで、port(Serviceポート)とtargetPort(Podポート)が正しくマッピングされているか、そしてバックエンドPod内のアプリケーションが実際にtargetPortでリッスンしているかを確認します。

これらのステップを順に進めることで、Serviceが正しく設定されているか、バックエンドPodが正常に動作しているか、そしてネットワーク経路上の問題がないかを特定できます。

まとめ

Kubernetes Serviceは、動的に変化するPod群に対する安定したネットワークインターフェースを提供し、Kubernetes上でのアプリケーション運用において不可欠な要素です。本記事では、その中でも特に重要な3つのタイプ、ClusterIP, NodePort, LoadBalancerについて深く掘り下げてきました。

  • ClusterIP: クラスター内部からのみアクセス可能な仮想IPアドレスを提供し、マイクロサービス間の通信に最適です。最もシンプルでセキュアなServiceタイプです。
  • NodePort: 各ノードの特定ポートを開放し、クラスター外部から任意のノードIPとNodePortでアクセスできるようにします。簡易な外部公開や開発/テスト環境に適していますが、高可用性や固定IPが必要な本番環境には単体では不向きです。
  • LoadBalancer: クラウドプロバイダーのロードバランサーと連携し、外部からアクセス可能な安定したIPアドレスと高可用性・スケーラビリティを提供します。本番環境での外部公開において広く利用されますが、コストがかかり、クラウドプロバイダーに依存します。

さらに、Headless Service、Session Affinity、External Traffic Policyといった高度な概念、そしてIngressとの連携についても解説しました。これらの概念を理解することで、より要件に合った方法でServiceを構成し、Kubernetes上のアプリケーションを効果的に公開・運用できるようになります。

Kubernetes Serviceの選択と設定は、アプリケーションの可用性、スケーラビリティ、セキュリティに直接影響します。本記事が、これらのServiceタイプとその仕組み、そして適切な選択方法について、読者の理解を深める一助となれば幸いです。Kubernetesの進化とともにServiceの機能も拡充される可能性がありますので、常に最新のドキュメントを参照することも重要です。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール