🎆

KubernetesのServiceの挙動を確認する

2022/05/28に公開

目的

  • 普段、Kubernetesを触ってはいるのですが、表面的な使い方しか知らないので動きを確認してみます

環境

  • OS: Arch Linux(5.17.9-arch1-1)
  • k8sの環境: kind

ひとまず、ローカルでクラスタを立てる

  • 環境に応じてkindをインストール
  • クラスタの作成
    • $ kind create cluster --name kind-iptables --config=kind-config.yaml
    • デフォルトではノードが1台になるので、設定ファイルを用意してノードを増やしておきます
kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

クラスタの状態確認

  • $ docker ps
    • コントロールプレーンとワーカー2台が動いてます
CONTAINER ID   IMAGE                  COMMAND                  CREATED        STATUS       PORTS                       NAMES
34b28d9094db   kindest/node:v1.24.0   "/usr/local/bin/entr…"   41 hours ago   Up 2 hours                               kind-iptables-worker
7a9586854130   kindest/node:v1.24.0   "/usr/local/bin/entr…"   41 hours ago   Up 2 hours                               kind-iptables-worker2
f5915419a00a   kindest/node:v1.24.0   "/usr/local/bin/entr…"   41 hours ago   Up 2 hours   127.0.0.1:44651->6443/tcp   kind-iptables-control-plane

サンプルのリソースをデプロイ

  • $ kubectl apply -f deployment.yaml
    • レプリカ数を3にします
    • nginxが80ポートで待ち受けてます
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
  • $ kubectl apply -f svc.yaml
    • 3000ポートで待ち受けます
svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: default
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 3000
    targetPort: 80

リソースの確認

  • $ kubectl -n default get all -o wide
    • Podがkind-iptables-workerに1つ、kind-iptables-worker2に2つで動いてます
NAME                                   READY   STATUS    RESTARTS       AGE   IP           NODE                    NOMINATED NODE   READINESS GATES
pod/nginx-deployment-7dd97694d-bp8tg   1/1     Running   1 (121m ago)   15h   10.244.2.3   kind-iptables-worker2   <none>           <none>
pod/nginx-deployment-7dd97694d-lllnh   1/1     Running   1 (121m ago)   15h   10.244.2.2   kind-iptables-worker2   <none>           <none>
pod/nginx-deployment-7dd97694d-xkx5f   1/1     Running   1 (121m ago)   15h   10.244.1.2   kind-iptables-worker    <none>           <none>

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE   SELECTOR
service/kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    40h   <none>
service/nginx-svc    ClusterIP   10.96.115.121   <none>        3000/TCP   15h   app=nginx

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES         SELECTOR
deployment.apps/nginx-deployment   3/3     3            3           15h   nginx        nginx:latest   app=nginx

NAME                                         DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES         SELECTOR
replicaset.apps/nginx-deployment-7dd97694d   3         3         3       15h   nginx        nginx:latest   app=nginx,pod-template-hash=7dd97694d

クラスタのネットワークを確認

  • 各々ノード内に入ってipコマンドを使って情報を整理すると以下の図のようになっていることが確認できます
    • $ docker exec -it 7a9586854130 ip -4 a
      • インターフェースのIPアドレスを確認
    • $ docker exec -it 7a9586854130 ip r
      • ルーティングテーブルを確認
    • $ docker exec -it 7a9586854130 ip netns exec cni-a8a3f9b5-afdc-aefd-d054-00ee392bd326 ip -4 a

ルーティングの確認

  • まず、ServiceのIPアドレスを知りたいので、コントロールプレーンにあるCoreDNSに問い合わせます
    • Serviceはmy-svc.my-namespace.svc.cluster.localという形式でDNSレコードが割り当てられています
    # CoreDNSのIPアドレスを確認
    $ kubectl -n kube-system get pods -o wide -l k8s-app=kube-dns
    NAME                       READY   STATUS    RESTARTS        AGE   IP           NODE                          NOMINATED NODE   READINESS GATES
    coredns-6d4b75cb6d-hqvp2   1/1     Running   2 (4h49m ago)   43h   10.244.0.4   kind-iptables-control-plane   <none>           <none>
    coredns-6d4b75cb6d-ppnk9   1/1     Running   2 (4h49m ago)   43h   10.244.0.2   kind-iptables-control-plane   <none>           <none>
    
    # ノードに入る
    $ docker exec -it 7a9586854130 bash
    # digがないのでインストール
    $ apt update && apt install dnsutils
    $ dig nginx-svc.default.svc.cluster.local +short @10.244.0.2
    10.96.115.121
    
    • 10.96.115.121
  • ServiceのIPアドレスは分かったので、次にPodにリダイレクトしている部分を覗いてみます
    target     prot opt source               destination
    KUBE-SVC-NPX46M4PTMTKRN6Y  tcp  --  anywhere             10.96.0.1            /* default/kubernetes:https cluster IP */ tcp dpt:https
    KUBE-SVC-HL5LMXD5JFHQZ6LN  tcp  --  anywhere             10.96.115.121        /* default/nginx-svc cluster IP */ tcp dpt:3000
    KUBE-SVC-TCOU7JCQXEZGVUNU  udp  --  anywhere             10.96.0.10           /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain
    KUBE-SVC-ERIFXISQEP7F7OF4  tcp  --  anywhere             10.96.0.10           /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain
    KUBE-SVC-JD5MR3NA4I4DYORP  tcp  --  anywhere             10.96.0.10           /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
    KUBE-NODEPORTS  all  --  anywhere             anywhere             /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
    
    • $ docker exec -it 7a9586854130 iptables -L KUBE-SVC-HL5LMXD5JFHQZ6LN -t nat
      • ServiceのバックエンドとなるPodの数だけチェインがあります
    Chain KUBE-SVC-HL5LMXD5JFHQZ6LN (1 references)
    target     prot opt source               destination
    KUBE-MARK-MASQ  tcp  -- !10.244.0.0/16        10.96.115.121        /* default/nginx-svc cluster IP */ tcp dpt:3000
    KUBE-SEP-SADCJIHRQW7RJ62U  all  --  anywhere             anywhere             /* default/nginx-svc -> 10.244.1.2:80 */ statistic mode random probability 0.33333333349
    KUBE-SEP-QX4RFH7MAHSLU35M  all  --  anywhere             anywhere             /* default/nginx-svc -> 10.244.2.2:80 */ statistic mode random probability 0.50000000000
    KUBE-SEP-ZXD3C5GBUWQNCJML  all  --  anywhere             anywhere             /* default/nginx-svc -> 10.244.2.3:80 */
    
    target     prot opt source               destination
    KUBE-MARK-MASQ  all  --  10.244.2.2           anywhere             /* default/nginx-svc */
    DNAT       tcp  --  anywhere             anywhere             /* default/nginx-svc */ tcp to:10.244.2.2:80
    
  • 上記で確認した情報はdescribeで見れます
    • $ kubectl describe services nginx-svc
    Name:              nginx-svc
    Namespace:         default
    Labels:            <none>
    Annotations:       <none>
    Selector:          app=nginx
    Type:              ClusterIP
    IP Family Policy:  SingleStack
    IP Families:       IPv4
    IP:                10.96.115.121
    IPs:               10.96.115.121
    Port:              <unset>  3000/TCP
    TargetPort:        80/TCP
    Endpoints:         10.244.1.2:80,10.244.2.2:80,10.244.2.3:80
    Session Affinity:  None
    Events:            <none>
    

kube-proxyのログを確認

  • Serviceを見てiptablesを触っているのはkube-proxyです
  • Serviceの作成でどんなログが流れているか見てみます

verbosityの変更

Service作成時のログ

  • $ kubectl logs -f daemonsets/kube-proxy
    • $ kubectl apply -f svc.yaml
    • ログは長いので端折って貼ってます
    • 確かにiptablesを更新しているようです
    • これが各々ノードに載っているkube-proxyのログから見れます
I0528 11:35:33.263396       1 config.go:336] "Calling handler.OnServiceAdd"
I0528 11:35:33.263930       1 service.go:322] "Service updated ports" service="default/nginx-svc" portCount=1
I0528 11:35:33.264020       1 service.go:437] "Adding new service port" portName="default/nginx-svc" servicePort="10.96.169.188:3000/TCP"
I0528 11:35:33.264052       1 proxier.go:853] "Syncing iptables rules"
I0528 11:35:33.271804       1 config.go:245] "Calling handler.OnEndpointSliceAdd" endpoints="default/nginx-svc-kbg7l"
I0528 11:35:33.286849       1 proxier.go:1464] "Reloading service iptables data" numServices=5 numEndpoints=7 numFilterChains=4 numFilterRules=4 numNATChains=15 numNATRules=34
I0528 11:35:33.289487       1 proxier.go:820] "SyncProxyRules complete" elapsed="25.512602ms"
I0528 11:35:33.289607       1 endpointslicecache.go:358] "Setting endpoints for service port name" portName="default/nginx-svc" endpoints=[10.244.1.2:80 10.244.2.2:80 10.244.2.3:80]
I0528 11:35:33.289646       1 proxier.go:853] "Syncing iptables rules"
I0528 11:35:33.311661       1 proxier.go:1492] "Network programming" endpoint="default/nginx-svc" elapsed=0.311622221
  • $ kubectl describe service nginx-svc
Name:              nginx-svc
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.96.169.188
IPs:               10.96.169.188
Port:              <unset>  3000/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.2:80,10.244.2.2:80,10.244.2.3:80
Session Affinity:  None
Events:            <none>
  • ログに出力されたIPアドレスと同じ
1 service.go:437] "Adding new service port" portName="default/nginx-svc" servicePort="10.96.169.188:3000/TCP"

まとめ

  • デフォルトでは
  • kube-proxyがServiceを見てiptablesを更新している
  • ServiceのIPアドレスはコントロールプレーンのDNSに問い合わせている
  • iptablesが宛先をServiceから各Podに変換している

Discussion