Kubernetes Kubernetes

Service:短命な Pod のための安定したネットワーキング

Pod に安定したアドレスを与え、クラスターの内外に公開し、誰が誰と通信できるかを制限します。

14 min learn.level.中級

すべての Pod は独自の IP アドレスを持ちますが、Pod は現れては消え、そのアドレスも一緒に消えます。Service は、変化し続ける Pod の集合の前に、安定したアドレスを提供します。このガイドでは、前のガイドの web Deployment を基に、Service、クラスターの外への公開、Ingress、ネットワークポリシーを説明します。

問題:Pod の IP は変わる

kubectl get pods -l app=web -o wide
kubectl delete pod POD_NAME
kubectl get pods -l app=web -o wide

代わりの Pod は、異なる IP を持ち、別のノード上にあることもあります。古いアドレスをハードコードしていたものは、すべて壊れ、レプリカが現れては消える中で、アドレスを手作業で追跡することはできません。

ClusterIP の Service を作成する

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80

selector は、Deployment で見たのと同じラベルを使って、Service を支える Pod を選びます。port は Service がリッスンするポートで、targetPort は転送先の Pod のポートで、コンテナのポートと一致している必要があります。type を設定していないため、これは ClusterIP の Service です。クラスターの内部からのみ、安定した仮想 IP で到達できます。

kubectl apply -f service.yaml
kubectl get service web
kubectl get endpointslices -l kubernetes.io/service-name=web

EndpointSlice は、現在 Service の背後にある Pod の IP を一覧表示します。Deployment をスケールアップまたはダウンすると自動的に更新されますが、Service 自体のアドレスは変わりません。(古いクラスターやチュートリアルでは、同じ情報のために kubectl get endpoints web を使います。最近のバージョンでは、その API は EndpointSlice に置き換えられ、非推奨になっています。)

クラスター内では、Service には DNS 名も付きます。同じ namespace からは web、どこからでも web.NAMESPACE.svc.cluster.local です。使い捨ての Pod からテストします。

kubectl run tmp --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://web

nginx のウェルカムページが表示されるはずです。各ノードでは、kube-proxy が、Service の準備完了の Pod に接続を分散させるルールを維持しています。一般的な iptables モードでは、これは厳密なラウンドロビンではなく、実質的にランダムで、readiness チェックに失敗した Pod はローテーションから外されます。

Service をクラスターの外に公開する

Service のタイプはさらに 2 つあり、それぞれが前のものの上に構築されています。

  • NodePort は、さらにすべてのノードでポート(30000 〜 32767)を開きます。ノードの IP に到達できるものは何でも、そのポートで Service に到達できます。
  • LoadBalancer は、さらにそれらのノードポートを指す外部ロードバランサーを、環境に要求します。クラウドプロバイダーでは自動的に行われます。ベアメタルでは、MetalLB のようなものが必要です。
apiVersion: v1
kind: Service
metadata:
  name: web-public
spec:
  type: NodePort
  selector:
    app: web
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080

nodePort を省略すると、Kubernetes が空いているものを選びます。Docker ドライバーを使った minikube では、ノードの IP にノート PC から直接到達できないことが多いため、minikube に開いてもらいます。

minikube service web-public --url

minikube の LoadBalancer の Service には、別のターミナルで minikube tunnel を実行します。

Ingress で HTTP をルーティングする

Service ごとに LoadBalancer を持つとコストがかさみ、しかも生のトラフィックしか処理できません。Ingress を使うと、単一のエントリポイントを通じて、ホストとパスで多くの Service に HTTP(S) トラフィックをルーティングし、そこで TLS を終端できます。Ingress はルールを記述するだけです。それに基づいて動作するには、クラスターで動作している ingress コントローラーが必要です(minikube では minikube addons enable ingress)。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  ingressClassName: nginx
  rules:
    - host: web.example.local
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80

web.example.local へのリクエストは、web Service に送られます。ホストの証明書は、cert-manager のようなツールで自動化できます。

NetworkPolicy でトラフィックを制限する

デフォルトでは、すべての Pod が他のすべての Pod と通信できます。NetworkPolicy は、Pod を選択し、許可されるトラフィックを列挙します。いずれかのポリシーが ingress について Pod を選択すると、明示的に許可されていないものはすべて拒否されます。まず、namespace 内のすべての受信トラフィックを拒否することから始めます。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
    - Ingress

空の podSelector は、namespace 内のすべての Pod を選択します。そして、必要なものだけを許可します。たとえば、role: client ラベルを持つ Pod から web Pod へのトラフィックです。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-client-to-web
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: client
      ports:
        - protocol: TCP
          port: 80

ネットワークポリシーは namespace に属し、クラスターのネットワークプラグイン(たとえば Calico や Cilium)がサポートしている場合にのみ適用されます。一部の最小限のローカル構成は、オブジェクトを受け付けても、黙って無視します。そのため、拒否された接続が実際にブロックされていることをテストしてください。

人気のガイド