まずはこちらをご覧ください
お分かりいただけただろうか?
-
instance-1
がmaster,instance-2
がnodeのk8sクラスタ上で -
kubectl create -f
でyamlを投げた- yamlの中身は**
instance-2
のIPをexternalIP
に持つService**
- yamlの中身は**
投げたdestroy.yaml
apiVersion: v1
kind: Service
metadata:
name: destroy
spec:
ports:
- port: 6666
externalIPs:
- 10.128.0.9 # ip of instance-2
- すると**
instance-2
が落ちた** instance-2
にSSHするとなぜかinstance-1
にログインした
一見普通のyamlなのになぜ?
解説
この現象は次の要素を組み合わせると起きます
- kube-proxy mode に IPVS modeを指定したKubernetesクラスタ
- externalIPにノードのIPを指定したサービスを作る
kube-proxy mode, IPVS mode, externalIPについて説明してから、
なぜその組み合わせがノード障害を起こすのか見ていきます
kube-proxy modeとは?
- Kubernetesdでは、Serviceをトラフィックの受け口として複数Podへのネットワークの振り分けが実現できます
- クライアントはServiceのIPを介することで、Podにアクセスできます
- この振り分けの実現方法を指定するのが kube-proxy mode です。
- デフォルトではiptablesを使うiptables proxy modeです
- IPVSというLinux Kernelのロードバランサ機能を用いるのがIPVS proxy mode です
- v1.11でGAになりました
IPVS modeの挙動
IPVSは次の2つのコンポーネントを組み合わせてロードバランスを実現します
- Virtual Server(IPVSサーバー): トラフィックの受け口
- Real Server: トラフィックの転送先
IPVSでServiceのトラフィックの振り分けを実装することを考えると、
Real ServerにはPodを指定すればよいですが、IPVSサーバーに相当するものがありません。
そこで、KubernetesではノードをIPVSサーバーにしてしまいます。kube-ipvs0
というダミーのインターフェース(NIC)を作り、
ServiceのIPはそのインターフェースに割り当てます
ExternalIPとは
- デフォルトではServiceにはKubernetes内部でのみ使用可能なIPアドレスであるClusterIPだけが自動で割り当てられます
- Kubernetes外部からはPodにアクセスできません
- 外部からアクセス可能なIPをサービスに紐づけるのがExternalIPです
- nodeのIPを指定することで、nodeのIPを介してPodへアクセスできるようになります
以上でやっと、冒頭の障害はなぜ起きるのかを説明できる準備ができました
冒頭の障害はなぜ起きるのか
- destroy.ymlを投げると、クラスタは次の状態になっています
- 作成するサービスのExternalIPに
instance-2
のIPを指定しています - ipvsモードでは、サービスのipをノードの
kube-ipvs0
インターフェースに割り当てます - そのため、
instance-1
のkube-ipvs0
がExternalIPであるinstance-2
のIPを持ってしまいます-
instance-1
→instance-2
の通信はすべてkube-ipvs0
に吸収され、instance-1
に戻ってしまいます- 通信不能で
instance-2
がdown
とKubernetesが判断 -
instance-2
にsshするとinstance-1
にログイン
- 通信不能で
-
- 作成するサービスのExternalIPに
補足
自分のクラスタのkube-proxy modeの設定を確認する方法
- masterで次を実行すると確認できます
kubectl get configmap kube-proxy -n kube-system -o yaml | grep 'mode:'
IPVSモードは必要?
大規模環境では有効です
- iptablesモードは次の理由から、大規模なクラスタでは重くなってしまいます
- Podやサービスが増えるほどにiptablesのルールが大量に増える
- パケットの転送はiptablesのテーブルを辿りながらマッチするルールを探索することになり、ルールが多いほど転送も重くなる(シーケンシャルリスト)
- そのためIPVSモードが導入されました。
- IPVSはハッシュテーブルのため、iptablesのような負荷は起きません