【クラウドネイティブ|Kubernetesをゼロから学ぶ】20. Service proxy kube-proxy コンポーネントの詳細解説

この記事はコラム「k8sをゼロから学ぶ」に収録されています
前回の記事:Kubernetesのコア技術 サービス実戦クリックジャンプ

ここに画像の説明を挿入

kube-proxy コンポーネントの紹介

Kubernetes サービスは、アプリケーションが外部の世界にサービスを提供する方法を抽象化するだけです. 実際のアプリケーションは Pod 内のコンテナーで実行されます. リクエストは kubernetes ノードに対応する nodePort に送信されます. nodePort のリクエストはさらにどのように提供されますか?バックグラウンド サービス Pod についてはどうでしょうか。これは kube-proxy によって実現されます

kube-proxy は k8s の各 Node ノードにデプロイされ、Kubernetes のコア コンポーネントです. サービスを作成すると、kube-proxy はいくつかのルールを iptables に追加して、ルーティングと負荷分散機能を実装します. k8s1.8 より前の kube-proxy は、デフォルトで iptables モードを使用し、各ノードの iptables ルールによってサービスの負荷分散を実現していましたが、サービス数の増加に伴い、線形検索マッチングによる iptables モード、フルアップデート等の特性上、性能が著しく低下します。k8s の 1.8 バージョン以降、kube-proxy は IPVS モードを導入しました. IPVS モードは iptables と同様に Netfilter に基づいていますが、ハッシュ テーブルを使用します. したがって、サービスの数が一定の規模に達すると、ハッシュ テーブルの速度の利点はルックアップが表示されるので、サービスのサービス パフォーマンスが向上します。

kubectl get pods -n kube-system -o wide

サービスはポッドのグループのサービス抽象化であり、ポッドのグループの LB に相当し、対応するポッドへのリクエストの分散を担当します。このサービスは、一般にクラスター IP と呼ばれるこの LB の IP を提供します。kube-proxy の役割は、主にサービスの実装を担当し、具体的には、ポッドからサービスへの内部アクセスと、ノード ポートからサービスへの外部アクセスを実現します。

1. kube-proxy は実際には、クラスター内の Pod からサービスへのアクセスやクラスター外のサービスへのアクセスなど、サービスを管理するためのアクセス エントリです。

2. kube-proxy は、サービスのエンドポイントを管理します. サービスは、クラスタ IP とも呼ばれる仮想 IP を公開します. クラスタ内のクラスタ IP:ポートにアクセスすることで、対応するサービスの Pod にアクセスできますクラスター。

kube-proxy の 3 つの動作モード

1. ユーザー空間方式:

ここに画像の説明を挿入

クライアント Pod がサーバー Pod にアクセスする場合、まずカーネル空間のサービス iptables ルールにリクエストを送信し、次に指定されたソケットでリッスンしている kube-proxy のポートにリクエストを転送します。リクエストを処理し、指定されたサーバー ポッドにリクエストを分散した後、リクエストはカーネル空間のサービス ip に転送され、サービス iptables は各ノードのサーバー ポッドにリクエストを転送します。

このモードには大きな問題があります. クライアントはまずカーネル空間に入り, kube-proxy にアクセスするためにユーザー空間に入ります. kube-proxy パッケージが完成した後, カーネル空間の iptables に入ります.その後、iptables.Userspace pods のルールに従って各ノードに配布します。ユーザー空間とカーネル空間の間でやり取りする必要があるため、非常に非効率的です。Kubernetes バージョン 1.1 より前は、ユーザー空間がデフォルトのプロキシ モデルでした。

2.iptables メソッド:

ここに画像の説明を挿入

クライアント IP が要求すると、ローカル カーネル サービス IP を直接要求し、iptables のルールに従って各ポッドに要求を直接転送します. 転送を完了するために iptable NAT が使用されるため、無視できないパフォーマンスの損失もあります. また、クラスター内に数万のサービス/エンドポイントがある場合、ノード上の iptables ルールは非常に大きくなり、パフォーマンスはさらに低下します. iptables プロキシ モードは Kubernetes バージョン 1.1 によって導入され、デフォルトになりました.バージョン 1.2 以降のタイプ。

3.ipvs メソッド:

ここに画像の説明を挿入

Kubernetes は、バージョン 1.9-alpha から ipvs プロキシ モードを導入し、バージョン 1.11 以降のデフォルトです。クライアントのリクエストがカーネル空間に到着すると、ipvs のルールに従って各 Pod に直接配信されます。kube-proxy は Kubernetes サービス オブジェクトとエンドポイントを監視し、netlink インターフェイスを呼び出してそれに応じて ipvs ルールを作成し、ipvs ルールを Kubernetes サービス オブジェクトとエンドポイント オブジェクトと定期的に同期して、ipvs 状態が期待どおりであることを確認します。サービスにアクセスすると、トラフィックはバックエンド ポッドの 1 つにリダイレクトされます。iptables と同様に、ipvs は netfilter のフック機能に基づいていますが、基礎となるデータ構造としてハッシュ テーブルを使用し、カーネル空間で動作します。これは、ipvs がトラフィックをより高速にリダイレクトし、プロキシ ルールを同期する際のパフォーマンスが向上することを意味します。さらに、ipvs は、次のような負荷分散アルゴリズムのオプションをさらに提供します。

rr:轮询调度  

lc:最小连接数  

dh:目标哈希  

sh:源哈希  

sed:最短期望延迟  

nq:不排队调度 

サービス バックエンド Pod が変更され、ラベル セレクターがもう 1 つの Pod に適応する場合、適応された情報はすぐに apiserver に反映され、kube-proxy は etc の情報の変更を監視し、すぐにそれを変換できる必要があります。 ipvs または iptables のルールは、すべて動的かつリアルタイムであり、ポッドの削除についても同じことが言えます。

ここに画像の説明を挿入

上記にかかわらず、kube-proxy は apiserver によって etcd に書き込まれた Pod に関する最新のステータス情報を watch を介して監視し、Pod リソースが削除または新規作成されたことを検出すると、これらの変更をすぐに iptables または ipvs ルールに反映します。 Clinet Pod リクエストをサーバー Pod にスケジュールするときに、iptables と ipvs がサーバー Pod が存在しないという状況が発生しないようにします。k8s1.11 以降、サービスはデフォルトで ipvs ルールを使用します. ipvs がアクティブ化されていない場合、iptables ルールを使用するようにダウングレードされます.

kube-proxy によって生成された iptables ルールの分析

1. サービスのタイプは ClusterIp、iptables ルール分析

k8s で作成されたサービスには IP アドレスがありますが、サービスの IP は仮想であり、物理マシン上には存在しません. iptables または ipvs ルールにあります.

[root@k8smaster service]# kubectl apply -f pod_test.yaml 
[root@k8smaster service]# kubectl apply -f service_test.yaml 
[root@k8smaster node]# kubectl get svc -l run=my-nginx
NAME       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.105.254.244   <none>        80/TCP    15s
[root@k8smaster node]# kubectl get pods -l run=my-nginx -o wide
NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE  
my-nginx-5898cf8d98-5trvw   1/1     Running   0          40s   10.244.1.5   k8snode2   <none>          
my-nginx-5898cf8d98-phfqr   1/1     Running   0          40s   10.244.1.4   k8snode2   <none>          
[root@k8smaster node]# iptables -t nat -L | grep 10.105.254.244
KUBE-MARK-MASQ  tcp  -- !10.244.0.0/16        10.105.254.244       /* default/my-nginx: cluster IP */ tcp dpt:http
KUBE-SVC-BEPXDJBUHFCSYIC3  tcp  --  anywhere             10.105.254.244       /* default/my-nginx: cluster IP */ tcp dpt:http

[root@k8smaster node]# iptables -t nat -L | grep KUBE-SVC-BEPXDJBUHFCSYIC3
KUBE-SVC-BEPXDJBUHFCSYIC3  tcp  --  anywhere             10.105.254.244       /* default/my-nginx: cluster IP */ tcp dpt:http			#把service关联的pod做了转发
Chain KUBE-SVC-BEPXDJBUHFCSYIC3 (1 references)		

[root@k8smaster node]# iptables -t nat -L | grep 10.244.1.5
KUBE-MARK-MASQ  all  --  10.244.1.5           anywhere             /* default/my-nginx: */
DNAT       tcp  --  anywhere             anywhere             /* default/my-nginx: */ tcp to:10.244.1.5:80
#DNAT转发 kubesvc接收请求,在过滤podip的时候有个mark也会标记ip,然后做了一个dnat转发到10.244.1.5:80这个pod上

#通过上面可以看到之前创建的 service,会通过 kube-proxy 在 iptables 中生成一个规则,来实现流量路由,有一系列目标为 KUBE-SVC-xxx 链的规则,每条规则都会匹配某个目标 ip 与端口。也就是说访问某个 serivce的ip和端口请求会由 KUBE-SVC-xxx 链来通过DNAT转发到对应的podip和端口上。

2. サービスのタイプは nodePort、iptables ルール分析

[root@k8smaster node]# kubectl apply -f pod_nodeport.yaml 
deployment.apps/my-nginx-nodeport created
[root@k8smaster node]# kubectl apply -f service_nodeport.yaml 
service/my-nginx-nodeport created

[root@k8smaster node]# kubectl get pods -l run=my-nginx-nodeport -o wide
NAME                                 READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED
my-nginx-nodeport-5fccbb754b-m4csx   1/1     Running   0          34s   10.244.1.7   k8snode2   <none>      
my-nginx-nodeport-5fccbb754b-rg48l   1/1     Running   0          34s   10.244.1.6   k8snode2   <none>
[root@k8smaster node]# kubectl get svc -l run=my-nginx-nodeport 
NAME                TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
my-nginx-nodeport   NodePort   10.105.58.82   <none>        80:30380/TCP   39s
 
[root@k8smaster node]# iptables -t nat -S | grep 30380 
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport:" -m tcp --dport 30380 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport:" -m tcp --dport 30380 -j KUBE-SVC-6JXEEPSEELXY3JZG
#一个是mark链一个是svc 在访问物理机ip和端口,访问会先经过这两个链

[root@k8smaster node]# iptables -t nat -S | grep KUBE-SVC-6JXEEPSEELXY3JZG
-N KUBE-SVC-6JXEEPSEELXY3JZG
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/my-nginx-nodeport:" -m tcp --dport 30380 -j KUBE-SVC-6JXEEPSEELXY3JZG
-A KUBE-SERVICES -d 10.105.58.82/32 -p tcp -m comment --comment "default/my-nginx-nodeport: cluster IP" -m tcp --dport 80 -j KUBE-SVC-6JXEEPSEELXY3JZG
-A KUBE-SVC-6JXEEPSEELXY3JZG -m comment --comment "default/my-nginx-nodeport:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-36FBCF7ZW3VDH33Q
-A KUBE-SVC-6JXEEPSEELXY3JZG -m comment --comment "default/my-nginx-nodeport:" -j KUBE-SEP-K2MGI3AJIGBK3IJ5
#会通过iptables的probability机制有0.50的概率进入KUBE-SEP-36FBCF7ZW3VDH33Q这个链,剩下50%还是最后那个GBK3IJ5这个链

[root@k8smaster node]# iptables -t nat -S | grep KUBE-SEP-36FBCF7ZW3VDH33
-N KUBE-SEP-36FBCF7ZW3VDH33Q
-A KUBE-SEP-36FBCF7ZW3VDH33Q -s 10.244.1.6/32 -m comment --comment "default/my-nginx-nodeport:" -j KUBE-MARK-MASQ
-A KUBE-SEP-36FBCF7ZW3VDH33Q -p tcp -m comment --comment "default/my-nginx-nodeport:" -m tcp -j DNAT --to-destination 10.244.1.6:80
-A KUBE-SVC-6JXEEPSEELXY3JZG -m comment --comment "default/my-nginx-nodeport:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-36FBCF7ZW3VDH33Q

#-A KUBE-SEP-36FBCF7ZW3VDH33Q -p tcp -m comment --comment "default/my-nginx-nodeport:" -m tcp -j DNAT --to-destination 10.244.1.6:80 是做了一个dnat把请求给10.244.1.6这个pod的80端口了 下面可以看到ip是相同的
[root@k8smaster node]# kubectl get pods -l run=my-nginx-nodeport -o wide
NAME                                 READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED
my-nginx-nodeport-5fccbb754b-m4csx   1/1     Running   0          8m24s   10.244.1.7   k8snode2   <none>    
my-nginx-nodeport-5fccbb754b-rg48l   1/1     Running   0          8m24s   10.244.1.6   k8snode2   <none>    

 
[root@k8smaster node]# iptables -t nat -S | grep KUBE-SEP-K2MGI3AJIGBK3IJ5
-N KUBE-SEP-K2MGI3AJIGBK3IJ5
-A KUBE-SEP-K2MGI3AJIGBK3IJ5 -s 10.244.1.7/32 -m comment --comment "default/my-nginx-nodeport:" -j KUBE-MARK-MASQ
-A KUBE-SEP-K2MGI3AJIGBK3IJ5 -p tcp -m comment --comment "default/my-nginx-nodeport:" -m tcp -j DNAT --to-destination 10.244.1.7:80
-A KUBE-SVC-6JXEEPSEELXY3JZG -m comment --comment "default/my-nginx-nodeport:" -j KUBE-SEP-K2MGI3AJIGBK3IJ5
#也是一个dnat,把请求分给另外一个pod,通过这两个链50%的概率来转发到两个pod上

最後に書く

作成するのは簡単ではありません。コンテンツが役立つと思われる場合は、3 つのリンクをフォローしてサポートしてください。間違いがあればコメントで指摘していただければ修正します!
現在更新中のシリーズ:ゼロからk8sを学ぶ ご覧
いただきありがとうございます 記事には個人的な理解が混在しています 誤りがある場合は、私に連絡して指摘してください〜
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_45400861/article/details/126850632