9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Kubernetes のサービスとは (3) kube-proxy とネットワーク設定

Last updated at Posted at 2022-09-25

この記事について

Kubernetes のサービスとは (2) リソース、API Server、コントローラーの続き。

Kubernetes の Service/Endpoints/EndpointSlice リソースに基づき、通信に必要なネットワーク設定を行う kube-proxy の話。
内容は大体こんな感じ:

  • kube-proxy はノードのネットワーク設定を実際に行うコンポーネント
  • Service の IP とネットワーク、ロードバランサーは実体はなく、単なる iptables 上のルールである

kube-proxy って何?

kube-proxy は Kubernetes のコンポーネントで、Service/Endpoints/EndpointSlice リソースを監視し、それらの変更(作成・更新・削除)を検知した場合にリソースに登録されている情報をもとにノードのネットワーク設定を行う。
image.png

kube-proxy は DaemonSet としてデプロイされているので各ノードで1つずつ Pod が動作している。

# DaemonSet として動作
$ kubectl get daemonset -n kube-system
NAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-proxy   2         2         2       2            2           kubernetes.io/os=linux   32h

# DaemonSet なので、kube-proxy の Pod は各ノードで実行される
$ kubectl get pod -n kube-system -o wide
NAME                             READY   STATUS    RESTARTS      AGE   IP               NODE  
coredns-565d847f94-p79gc         1/1     Running   1 (37m ago)   32h   192.168.219.71   master
coredns-565d847f94-r794g         1/1     Running   1 (37m ago)   32h   192.168.219.73   master
etcd-master                      1/1     Running   1 (37m ago)   32h   10.0.0.5         master
kube-apiserver-master            1/1     Running   1 (37m ago)   32h   10.0.0.5         master
kube-controller-manager-master   1/1     Running   1 (37m ago)   32h   10.0.0.5         master
kube-proxy-fkpfb                 1/1     Running   1 (37m ago)   32h   10.0.0.4         node01 ★これ
kube-proxy-mm4rc                 1/1     Running   1 (37m ago)   32h   10.0.0.5         master ★これ
kube-scheduler-master            1/1     Running   1 (37m ago)   32h   10.0.0.5         master

kube-proxy が行うネットワーク設定とは?

kube-proxy には3つの動作モードがあり、デフォルトは iptables を構成する iptables モードになる。つまり、iptables の設定により Service の IP やロードバランスを実現する。
他にも user-space proxy モードと IPVS モードがあるがここでは割愛する。仮想IPとサービスプロキシー にこれらのモードの説明があるので必要に応じて参照いただければ。

kube-proxy による iptables の設定

では、kube-proxy が実際にどんな設定を行うか見てみる。nginx の Deployment(レプリカ数=2)と Service(Type=ClusterIP) が以下のようにデプロイされているとする。Service の IP は 10.110.64.222 で、Pod の IP は 192.168.219.77, 192.168.196.131

# Deployment に含まれる Pod
$ kubectl get pod -l app=nginx -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP                NODE
nginx-b8c585cf6-67wkb   1/1     Running   0          21m   192.168.219.77    master
nginx-b8c585cf6-stsmr   1/1     Running   0          22m   192.168.196.131   node01

# Service
kubectl get svc nginx
NAME    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
nginx   ClusterIP  10.110.64.222   <none>        8080/TCP   32s

kube-proxy は主に NAT テーブルを操作して Service やロードバランスを実現する。上記のリソースをデプロイした後の sudo iptables -n -t nat -L を実行して関係するところを抜粋したのが以下。

# 入口となる PREROUTING チェイン
Chain PREROUTING (policy ACCEPT)
target         prot opt source      destination   
KUBE-SERVICES  all  --  0.0.0.0/0   0.0.0.0/0     /* kubernetes service portals */

# 全 Service を含むチェイン
Chain KUBE-SERVICES (2 references)
target                     prot opt source      destination   
KUBE-SVC-TCOU7JCQXEZGVUNU  udp  --  0.0.0.0/0   10.96.0.10     /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
KUBE-SVC-ERIFXISQEP7F7OF4  tcp  --  0.0.0.0/0   10.96.0.10     /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
KUBE-SVC-JD5MR3NA4I4DYORP  tcp  --  0.0.0.0/0   10.96.0.10     /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
+ KUBE-SVC-P4Q3KNUAWJVP4ILH  tcp  --  0.0.0.0/0   10.110.64.222  /* default/nginx:http cluster IP */ tcp dpt:8080
KUBE-SVC-NPX46M4PTMTKRN6Y  tcp  --  0.0.0.0/0   10.96.0.1      /* default/kubernetes:https cluster IP */ tcp dpt:443

# 個々の Service に該当するチェイン
+ Chain KUBE-SVC-P4Q3KNUAWJVP4ILH (1 references)
+ target                     prot opt source           destination    
+ KUBE-MARK-MASQ             tcp  -- !192.168.0.0/16   10.110.64.222  /* default/nginx:http cluster IP */ tcp dpt:8080
+ KUBE-SEP-5V2UTJDVBQ73MZHD  all  --  0.0.0.0/0        0.0.0.0/0      /* default/nginx:http -> 192.168.196.131:80 */ statistic mode random probability 0.50000000000
+ KUBE-SEP-6KKQPHGRLUODOJRX  all  --  0.0.0.0/0        0.0.0.0/0      /* default/nginx:http -> 192.168.219.77:80 */

# そのサービスに紐づく Endpoint(Pod の IP:Port) に該当するチェイン : 1個目
+ Chain KUBE-SEP-5V2UTJDVBQ73MZHD (1 references)
+ target          prot opt source            destination   
+ KUBE-MARK-MASQ  all  --  192.168.196.131   0.0.0.0/0     /* default/nginx:http */
+ DNAT            tcp  --  0.0.0.0/0         0.0.0.0/0     /* default/nginx:http */ tcp to:192.168.196.131:80

# そのサービスに紐づく Endpoint(Pod の IP:Port) に該当するチェイン : 2個目
+ Chain KUBE-SEP-6KKQPHGRLUODOJRX (1 references)
+ target          prot opt source           destination
+ KUBE-MARK-MASQ  all  --  192.168.219.77   0.0.0.0/0      /* default/nginx:http */
+ DNAT            tcp  --  0.0.0.0/0        0.0.0.0/0      /* default/nginx:http */ tcp to:192.168.219.77:80

ややこしく見えるかもしれないが、図にすると結構シンプルに見えると思う。Service 一覧 → Service → Endpoint と段階的に iptables のルールを追って、最後にパケットの宛先を Pod に変換している。
image.png

なお、ロードバランスは以下の部分で実現している。この場合は Pod が2つなので50%ずつ振り分けるようになっている。

Chain KUBE-SVC-P4Q3KNUAWJVP4ILH (1 references)
target                    prot opt source     destination    
...
KUBE-SEP-5V2UTJDVBQ73MZHD all  --  0.0.0.0/0  0.0.0.0/0  statistic mode random probability 0.50000000000
KUBE-SEP-6KKQPHGRLUODOJRX all  --  0.0.0.0/0  0.0.0.0/0  

statistic の設定については、https://linuxjm.osdn.jp/html/iptables/man8/iptables-extensions.8.html の説明を引用しておく。

statistic
このモジュールは統計的な条件に基づいたパケットのマッチングを行う。 二つのモードがサポートされており、 --mode オプションで設定できる。
サポートされているオプション:
--mode mode
    マッチングルールのマッチングモードを設定する。 サポートされているモードは random と nth である。
[!] --probability p
    ランダムにパケットがマッチする確率を設定する。 random モードでのみ機能する。 p は 0.0 と 1.0 の範囲でなければならない。 サポートされている粒度は 1/2147483648 である。

NodePort の場合

NodePort とは?

ここまでは ClsuterIP という種類の Service の場合で、この IP は Kubernetes クラスターの中に閉じていて外部から直接アクセスできない。クラスターの外からアクセスするには NodePort という種類の Service が必要になる。すごく概念的な図だがこんなイメージ。
image.png

NodePort は実際にはクラスターを構成する各ノード上の IP:Port(上図の例では 172.16.0.1:30037)で、イメージ的にはここから NodePort → Servcie → Pod という風にパケットが流れる感じである。

NodePort はどういう構成になる?

では NodePort の場合はどう構成されるかというと、実は大きくは変わらない。これまた iptables 上の定義に過ぎず、パケットの宛先ポートが NodePort にマッチすると結局該当の Service(ClusterIP) に行く。 Service(ClusterIP)の前段に NodePort がいるという形になる。

Service(NodePort) を作成すると自動で Service(ClusterIP) も作成されるが、それはこのような通信経路になるためと言える。

image.png

いちおう、iptables の nat テーブルに追加された部分を一部抜粋して記載しておく。

# Service 一覧のチェインに NodePort へ行くルールが追加される
Chain KUBE-SERVICES (2 references)
target                     prot opt source     destination   
...
KUBE-SVC-P4Q3KNUAWJVP4ILH  tcp  --  0.0.0.0/0  10.110.64.222  /* default/nginx:http cluster IP */ tcp dpt:8080
...
KUBE-SVC-NPX46M4PTMTKRN6Y  tcp  --  0.0.0.0/0  10.96.0.1      /* default/kubernetes:https cluster IP */ tcp dpt:443
+ KUBE-NODEPORTS             all  --  0.0.0.0/0  0.0.0.0/0      /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL

# NodePort 一覧チェイン
+ Chain KUBE-NODEPORTS (1 references)
+ target                     prot opt source     destination    
+ KUBE-EXT-P4Q3KNUAWJVP4ILH  tcp  --  0.0.0.0/0  0.0.0.0/0      /* default/nginx:http */ tcp dpt:30170

# 個々の NodePort のチェイン。 ペアとなる Service(Cluster)に行く
+ Chain KUBE-EXT-P4Q3KNUAWJVP4ILH (1 references)
+ target                     prot opt source     destination    
+ KUBE-MARK-MASQ             all  --  0.0.0.0/0  0.0.0.0/0      /* masquerade traffic for default/nginx:http external destinations */
+ KUBE-SVC-P4Q3KNUAWJVP4ILH  all  --  0.0.0.0/0  0.0.0.0/0

結局実体は?

これまでも何度か掲載した以下の図で言えば、赤色の Service Network、IP(Service の ClusterIP)、ロードバランサーは実体がなく iptables 上の定義として存在しているだけということになる。上で見たように NodePort も同様である。

image.png

続き

執筆中。。。

補足・おまけ

Pod からの iptables 設定

kube-proxy の Pod(つまりコンテナ)の中からノード側の iptables を設定できるのかという話があるが、privileged: true という特権モード(root 相当)で動いているので可能になる。

# DaemonSet のマニフェスト(一部のみ抜粋して記載)
$ kubectl get daemonset -n kube-system -o yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-proxy
  namespace: kube-system
spec:
  template:
    spec:
      containers:
      - command:
        - /usr/local/bin/kube-proxy
        - --config=/var/lib/kube-proxy/config.conf
        - --hostname-override=$(NODE_NAME)
        image: registry.k8s.io/kube-proxy:v1.25.2
        name: kube-proxy
        securityContext:
          privileged: true ★これ

kube-proxy のデバッグログ表示

マネージドの Kubernetes だと出来ない場合が多いと思うが、自前で構築した場合はログレベルの変更を変えることができる。
以下のコマンドを実行する。

kubectl edit daemonset -n kube-system kube-proxy

マニフェストが開くので、以下の行を追加して保存する。数字の 5 の部分が大きいほどより詳細なログが出る(たぶん 0 ~ 9 まで指定できる)。これだけで OK。

    spec:
      containers:
      - command:
        - /usr/local/bin/kube-proxy
        - --config=/var/lib/kube-proxy/config.conf
        - --hostname-override=$(NODE_NAME)
        - -v5  ★この行を追加

例えば、以下のようなログが見れて kube-proxy の動作を把握しやすくなる。

I0925 02:11:33.975296       1 service.go:440] "Adding new service port" portName="default/nginx:http" servicePort="10.102.246.227:8080/TCP"
I0925 02:11:33.975336       1 proxier.go:850] "Syncing iptables rules"
I0925 02:11:33.975343       1 iptables.go:467] running iptables: iptables [-w 5 -W 100000 -N KUBE-EXTERNAL-SERVICES -t filter]

iptables のチェイン名の Prefix

ソースで以下のように定義されている。

const (
	servicePortPolicyClusterChainNamePrefix = "KUBE-SVC-"
	servicePortPolicyLocalChainNamePrefix   = "KUBE-SVL-"
	serviceFirewallChainNamePrefix          = "KUBE-FW-"
	serviceExternalChainNamePrefix          = "KUBE-EXT-"
	servicePortEndpointChainNamePrefix      = "KUBE-SEP-"

	// For cleanup.  This can be removed after 1.26 is released.
	deprecatedServiceLBChainNamePrefix = "KUBE-XLB-"
)
9
10
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?