疑問
Kubernetesのserviceとはなんなのか?
「Podだけだと再起動が発生した時にIPが変わるので、Service経由でアクセスするのが良い」
これは、よく言われることだが、実際にServiceはどのようにPodにリクエストを送っているのか?、また負荷分散をどのように実現しているのかを探る。
Serviceには3つのタイプがある
- ClusterIP
- クラスタ内で使用できる仮想IP
- FQDN(Fully Qualified Domain Name)が生成される
admin.namespace.svc.cluster.local
みたいなやつ
- NodePort
- クラスタ外部からもアクセスが可能
- LoadBalancer
- Kubernetesが提供しているLBではなく外部のLBを利用して負荷分散する(AWSやGoogle Cloudなど)
- NodePortを使用する方法と、ClusterIPを使用する方法がある
実験
以下のServiceを作成する。
apiVersion: v1
kind: Service
metadata:
name: clusterip-svc
spec:
type: ClusterIP
selector:
app: api-container
ports:
- protocol: TCP
port: 5555
targetPort: 5000
nodeに入り、iptablesを見ると、
# iptables -L -nv -t nat | grep lb-svc | grep KUBE-SEP
0 0 KUBE-SEP-37QFZ7WS6YCMEGGL 0 -- * * 0.0.0.0/0 0.0.0.0/0 /* default/lb-svc -> 10.244.1.3:5000 */ statistic mode random probability 0.33333333349
0 0 KUBE-SEP-6SMVGQRJXVZRRJRN 0 -- * * 0.0.0.0/0 0.0.0.0/0 /* default/lb-svc -> 10.244.1.4:5000 */ statistic mode random probability 0.50000000000
0 0 KUBE-SEP-TQN37TW44HP3RSKY 0 -- * * 0.0.0.0/0 0.0.0.0/0 /* default/lb-svc -> 10.244.2.3:5000 */
/* default/lb-svc -> 10.244.1.3:5000 */ statistic mode random probability 0.33333333349
/* default/lb-svc -> 10.244.1.4:5000 */ statistic mode random probability 0.50000000000
/* default/lb-svc -> 10.244.2.3:5000 */
という3行の記述が存在している。
iptablesとは⇩
/* default/lb-svc -> 10.244.1.3:5000 */ statistic mode random probability 0.33333333349
/* default/lb-svc -> 10.244.1.4:5000 */ statistic mode random probability 0.50000000000
/* default/lb-svc -> 10.244.2.3:5000 */
これを解説すると
- 33% の確率で
10.244.1.3
にルーティング - それ以外のリクエスト(100-33 = 約 67%)のうち 50%(= 約 33%)が
10.244.1.4
にルーティング - 残りが
10.244.2.3
にルーティング(結果的に約 33%)
という挙動となる。
結果として、均等なラウンドロビン的な動作になる。
つまり、3分の1の確率で特定のPodにリクエストが流れるという負荷分散が行われている。このようにServiceの負荷分散はiptablesを使用しているため、L3/4での負荷分散である。
(L7負荷分散についてははIngressが行っている)
ここに全てが書いています
つまり、Serviceの実態はプロセスではなくiptables?→YES
Serviceのyamlをapplyした時の挙動は?
まずはk8sの構成要素について述べる必要がある
- etcd
- KVS
- クラスタの全ての情報を保存する
- kube-apiserverからのみアクセスできる
- kube-apiserver
- etcdにアクセスしてデータを管理
- 他コンポーネントはkube-apiserverに定期的にあるべき状態を問い合わせて差分があれば更新する
- kube-scheduler
- workerに対するPodのスケジューリングを担当
- manifestに書かれているresouce.cpu、resource.limitを考慮してノードの空き状況を確認する
- kube-controller-manager
- クラスタ管理のための色々なコントローラが1つになっているコンポーネント
- レプリカの数を調整したりするコンポーネントが存在している
- kube-proxy
- https://kubernetes.io/ja/docs/concepts/overview/components/#kube-proxy
- ノードのネットワークルールを管理
- クラスタ内外からPodへの通信を可能にする
- Serviceを作成した際には、このコンポーネントがiptablesを編集する
- kubelet
- ノードで動くコンテナを監視/管理する
Serviceが作成されるまでは以下の流れである。
- Serviceのyamlをapply
- api-serverがetcdを変更
- kube-proxyがControl Planeのapi-serverを監視しており、kube-proxyがiptablesを変更する
- Podが除外/生成されても、api-serverを監視しているのでkube-proxyがiptablesを書き換えてルーティングが正しく機能し続ける
Serviceの実態がiptalbesというのは、公式ドキュメントにiptables
プロキシモード (https://kubernetes.io/ja/docs/concepts/services-networking/service/#proxy-mode-iptables ) として書かれている。
L7の負荷分散どうやってするの?という問いについてはIngressが行っている。
Ingressについてはまた別の機会に。