キャンセル
次の結果を表示 
次の代わりに検索 
もしかして: 
cancel
8563
閲覧回数
5
いいね!
1
コメント
mnagao
Cisco Employee
Cisco Employee

 

 

はじめに

Cisco Digital Network Architecture Center (DNA Center) は Kubernetes 上の Pod で構成される Web アプリケーションです。このドキュメントバージョン 2.1.2.4 の 3-node cluster を例に Cisco DNA Center クラスタにおけるネットワーキングの概要を説明します。Keepalived Pod を用いて外部からのアクセスを VRRP master となるノードに誘導することが特徴になりますが、Cisco 独自の要素は無い標準的な実装です。sigle node cluster の場合は VRRP を使用しない構成とすることも可能で他ノードへの IPIP トンネルが存在しない以外は概ね同じ挙動になります。

 

  

Pod 間の通信

以下のような 3 node cluster を考えます。

$ maglev cluster node display
maglev-1 [main - https://kong-frontend.maglev-system.svc.cluster.local:443]
ID                                       ADDRESS            PLATFORM  
----------------------------------------------------------------------
24557023-8ec3-4567-911e-3912f4b9caa0     192.0.2.33         DN1-HW-APL
29af5be4-c538-404c-bb8c-453f9ef321c2     192.0.2.32         DN1-HW-APL
ff66b180-f884-4ec0-9a5d-07cb204929fa     192.0.2.31         DN1-HW-APL

  

DNA Center では Kubernetes の CNI (Container Network Interface) plugin として Calico を使っており、ノードをまたがる Pod 間通信は L3 ネットワーキングにより実現されます。

$ kubectl get pods -n kube-system -o wide | egrep "NAME|calico"
NAME                                       READY   STATUS    RESTARTS   AGE   IP                NODE         NOMINATED NODE   READINESS GATES
calico-kube-controllers-55c9bc4f7b-nrv2n   1/1     Running   0          18d   192.0.2.33        192.0.2.33              
calico-node-6g495                          1/1     Running   0          17d   192.0.2.31        192.0.2.31              
calico-node-jqj9b                          1/1     Running   0          17d   192.0.2.32        192.0.2.32              
calico-node-nl8lm                          1/1     Running   0          17d   192.0.2.33        192.0.2.33              

$ calicoctl get node -o wide NAME ASN IPV4 IPV6 maglev-master-192-0-2-31 (unknown) 192.0.2.31/24 maglev-master-192-0-2-32 (unknown) 192.0.2.32/24 maglev-master-192-0-2-33 (unknown) 192.0.2.33/24

  

DNA Center の初期セットアップにおいて Kubernetes のコンテナサブネットおよびサービスサブネットを指定しますが、その値は maglev cluster network display で確認できます。下記例ではコンテナサブネットが 100.103.0.0/16 であることが確認でき、Calico はこれを用いて Pod ネットワークを構成します。

$ maglev cluster network display
cluster_network:
  cluster_dns: 169.254.20.10
  cluster_hostname: ''
  cluster_subnet: 100.113.0.0/16
  cluster_vip:
  - 192.0.2.30
  - 192.168.1.30
  - 10.70.71.149
  container_subnet: 100.103.0.0/16


$ cat /etc/kubernetes/calico.yaml calico_ipv4pool_cidr: "100.103.0.0/16"
$ calicoctl get ippool -o wide NAME CIDR NAT IPIPMODE VXLANMODE DISABLED SELECTOR default-ipv4-ippool 100.103.0.0/16 true Always Never false all()

 

Calico は 3 ノードにまたがる L3 ネットワークを構成するために IPIP トンネルインターフェイス (tunl0) を各ノードに構成します。

$ calicoctl get node -o yaml
apiVersion: projectcalico.org/v3
items:
- apiVersion: projectcalico.org/v3
  kind: Node
  metadata:
    labels:
      dna_role: DNA
      kubernetes.io/hostname: 192.0.2.31
    name: maglev-master-192-0-2-31
  spec:
    bgp:
      ipv4Address: 192.0.2.31/24
      ipv4IPIPTunnelAddr: 100.103.121.192
- apiVersion: projectcalico.org/v3
  kind: Node
  metadata:
    labels:
      dna_role: DNA
      kubernetes.io/hostname: 192.0.2.32
    name: maglev-master-192-0-2-32
  spec:
    bgp:
      ipv4Address: 192.0.2.32/24
      ipv4IPIPTunnelAddr: 100.103.14.0
- apiVersion: projectcalico.org/v3
  kind: Node
  metadata:
    labels:
      dna_role: DNA
      kubernetes.io/hostname: 192.0.2.33
    name: maglev-master-192-0-2-33
  spec:
    bgp:
      ipv4Address: 192.0.2.33/24
      ipv4IPIPTunnelAddr: 100.103.119.128

// node: maglev-master-192-0-2-33 での tunnel interface の例 $ ip addr show dev tunl0 11: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1400 qdisc noqueue state UNKNOWN group default qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.0 inet 100.103.119.128/32 brd 100.103.119.128 scope global tunl0 valid_lft forever preferred_lft forever

 

割り当てられた CIDR ブロックは Calico のデフォルトである blockSize /26 に分割されて各ノードに動的に割り当てられて static routing が構成されます。下記の例 (ノード192.0.2.33 で実行) では自ノードに 3 つのブロックが割り当てられいるので、それらについては blackhole routing と表示されます。また、ノード 192.0.2.31 には 1 ブロック、ノード 192.0.2.32 には 2 ブロックが割り当てられられて tunl0 経由でルーティングされることが分かります。

Calico では BGP によるルーティングを構成することもできますが、DNA Center ではこの Static Routing のみを使用しています。

[Fri Jan 15 10:34:40 UTC] maglev@192.0.2.33 (maglev-master-192-0-2-33)
$ ip route | grep -v cali | grep 100\. 100.103.14.0/26 via 192.0.2.32 dev tunl0 proto bird onlink 100.103.14.64/26 via 192.0.2.32 dev tunl0 proto bird onlink blackhole 100.103.119.128/26 proto bird blackhole 100.103.119.192/26 proto bird blackhole 100.103.120.0/26 proto bird 100.103.121.192/26 via 192.0.2.31 dev tunl0 proto bird onlink

 

各 Endpoint (Pod の eth0) にはノードに割り当てられた /26 ブロックの中から IP 付与され、Pod に対応する veth が "cali" をプリフィックスとして作成されます。

なお、DNAC: tcpdump による特定 Pod のパケットキャプチャ に記載のように Pod の eth0 とホスト側 veth は 1:1 対応です。

$ calicoctl get workloadEndpoint --all-namespaces -o wide
NAMESPACE                  NAME                                                                                                 WORKLOAD                                                 NODE                       NETWORKS             INTERFACE         PROFILES                                                            NATS   
ai-network-analytics       maglev--master--192--0--2--33-k8s-kairos--agent--cd6cdcdd9--r5s9q-eth0                               kairos-agent-cd6cdcdd9-r5s9q                             maglev-master-192-0-2-33   100.103.120.7/32     calieff43a557fc   kns.ai-network-analytics,ksa.ai-network-analytics.default                  
app-hosting                maglev--master--192--0--2--31-k8s-postgres--2-eth0                                                   postgres-2                                               maglev-master-192-0-2-31   100.103.121.241/32   calice1285f3663   kns.app-hosting,ksa.app-hosting.managed-sa                                 
app-hosting                maglev--master--192--0--2--32-k8s-app--hosting--6b6c5fffb5--b9t6v-eth0                               app-hosting-6b6c5fffb5-b9t6v                             maglev-master-192-0-2-32   100.103.14.42/32     cali1cfee3971c8   kns.app-hosting,ksa.app-hosting.default                                    
app-hosting                maglev--master--192--0--2--32-k8s-postgres--1-eth0                                                   postgres-1                                               maglev-master-192-0-2-32   100.103.14.55/32     cali0ed6da6bc12   kns.app-hosting,ksa.app-hosting.managed-sa                                 
app-hosting                maglev--master--192--0--2--33-k8s-postgres--0-eth0                                                   postgres-0                                               maglev-master-192-0-2-33   100.103.120.0/32     cali9dedffa0eec   kns.app-hosting,ksa.app-hosting.managed-sa                                 
assurance-backend          maglev--master--192--0--2--31-k8s-rogue--service--5cff4b4977--2m6s8-eth0                             rogue-service-5cff4b4977-2m6s8                           maglev-master-192-0-2-31   100.103.121.205/32   cali1581062c544   kns.assurance-backend,ksa.assurance-backend.default                        
assurance-backend          maglev--master--192--0--2--32-k8s-nsa--webapp--b47c7fb8b--948lz-eth0                                 nsa-webapp-b47c7fb8b-948lz                               maglev-master-192-0-2-32   100.103.14.60/32     calibdbca4cb0a8   kns.assurance-backend,ksa.assurance-backend.default    
<snip> $ kubectl get pods -A -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES ai-network-analytics kairos-agent-cd6cdcdd9-r5s9q 1/1 Running 0 18d 100.103.120.7 192.0.2.33 app-hosting app-hosting-6b6c5fffb5-b9t6v 1/1 Running 0 17d 100.103.14.42 192.0.2.32 app-hosting postgres-0 3/3 Running 0 18d 100.103.120.0 192.0.2.33 app-hosting postgres-1 3/3 Running 0 17d 100.103.14.55 192.0.2.32 app-hosting postgres-2 3/3 Running 0 17d 100.103.121.241 192.0.2.31 <snip> assurance-backend nsa-webapp-b47c7fb8b-948lz 1/1 Running 0 17d 100.103.14.60 192.0.2.32 assurance-backend rogue-service-5cff4b4977-2m6s8 1/1 Running 0 17d 100.103.121.205 192.0.2.31 <snip>

 

 

以上で Calico による L3 ネットワークが構成され、Pod 間通信が成立することが分かります。

 

 

クラスタ外からの API 通信

このセクションではブラウザや外部アプリケーションからの API Gateway である Kong を介した API 通信を取り上げますが、その前に Kubernetes Service について確認します。

DNA Center の Kubernetes Service は多くが ClusterIP として実装され、外部との通信は行いません。 外部との通信は主に Kong を経由します。

$ kubectl get services -n fusion -o wide
NAME                                    TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)                               AGE   SELECTOR
aca-controller-service                  ClusterIP   100.113.6.65              17776/TCP                             18d   serviceName=aca-controller-service
apic-em-event-service                   ClusterIP   100.113.1.232             80/TCP                                18d   serviceName=apic-em-event-service
apic-em-inventory-manager-service       ClusterIP   100.113.62.239            80/TCP                                18d   serviceName=apic-em-inventory-manager-service
apic-em-jboss-ejbca                     ClusterIP   100.113.145.42            16026/TCP,16027/TCP,16029/TCP         18d   serviceName=apic-em-jboss-ejbca
apic-em-network-programmer-service      ClusterIP   100.113.97.208            17125/TCP                             18d   serviceName=apic-em-network-programmer-service
apic-em-pki-broker-service              ClusterIP   100.113.51.46             16025/TCP                             18d   serviceName=apic-em-pki-broker-service
app-policy-provisioning-service         ClusterIP   100.113.241.5             29799/TCP                             18d   serviceName=app-policy-provisioning-service
application-recognition-service         ClusterIP   100.113.227.0             31947/TCP                             18d   serviceName=application-recognition-service
cnsr-reasoner                           ClusterIP   100.113.145.166           8039/TCP                              18d   serviceName=cnsr-reasoner
 
バージョン 2.1.2.4 では Kubernetes Service を外部に expose 可能な NodePort として実装されているのが以下のサービスとなります。*-ext とのサフィックスが付いているのは主に Assurance における各 Telemetry Collector で、対応する (-ext の無い) ClusterIP タイプのサービスも存在します。
collector-snmp のようにサーバとしては機能しない (DNA Center からデバイスへの SNMP MIB polling のみ)  ために NodePort サービスの無いものもあります。
$ kubectl get services -A -o wide | grep -v ClusterIP
NAMESPACE                  NAME                                    TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)                                                                                                                                                                  AGE   SELECTOR
assurance-backend          collector-iosxe-db-ext                  NodePort    100.113.207.140           25103:25103/TCP                                                                                                                                                          18d   serviceName=collector-iosxe-db
assurance-backend          grpc-collector-ext                      NodePort    100.113.220.105           62626:32626/TCP                                                                                                                                                          18d   serviceName=grpc-collector
fusion                     sdg-service-ext                         NodePort    100.113.217.174           9991:9991/TCP                                                                                                                                                            18d   serviceName=sdg-service
fusion                     sftp-service-ext                        NodePort    100.113.152.238           2222:22/TCP                                                                                                                                                              18d   serviceName=sftp-service
maglev-system              kong-public                             NodePort    100.113.173.195           443:443/TCP,30443:30443/TCP,80:80/TCP                                                                                                                                    18d   serviceName=kong
ndp                        collector-netflow-ext                   NodePort    100.113.130.82            6007:6007/UDP                                                                                                                                                            18d   serviceName=collector-netflow
ndp                        collector-syslog-ext                    NodePort    100.113.110.179           514:514/UDP                                                                                                                                                              18d   serviceName=collector-syslog
ndp                        collector-trap-ext                      NodePort    100.113.66.94             162:162/UDP                                                                                                                                                              18d   serviceName=collector-trap
sdavc                      sdavc-server-ext                        NodePort    100.113.7.204             50000:21730/UDP                                                                                                                                                          18d   serviceName=sdavc-server
sys-ops                    disaster-recovery-service-ext           NodePort    100.113.241.3             8301:8301/TCP,8302:8302/TCP,8300:8300/TCP,8301:8301/UDP,8302:8302/UDP                                                                                                    18d   serviceName=disaster-recovery-service

$ kubectl get services -A -o wide | grep collector
assurance-backend          collector-iosxe-db                      ClusterIP   100.113.94.61             8077/TCP                                                                                                                                                                 18d   serviceName=collector-iosxe-db
assurance-backend          collector-iosxe-db-ext                  NodePort    100.113.207.140           25103:25103/TCP                                                                                                                                                          18d   serviceName=collector-iosxe-db
assurance-backend          grpc-collector                          ClusterIP   100.113.218.119           9013/TCP                                                                                                                                                                 18d   serviceName=grpc-collector
assurance-backend          grpc-collector-ext                      NodePort    100.113.220.105           62626:32626/TCP                                                                                                                                                          18d   serviceName=grpc-collector
assurance-backend          wirelesscollector                       ClusterIP   100.113.131.173           8042/TCP                                                                                                                                                                 18d   serviceName=wirelesscollector
ndp                        collector-agent                         ClusterIP   100.113.92.49             8000/TCP                                                                                                                                                                 18d   serviceName=collector-agent
ndp                        collector-ise                           ClusterIP   100.113.71.149            8005/TCP                                                                                                                                                                 18d   serviceName=collector-ise
ndp                        collector-manager                       ClusterIP   100.113.48.59             8000/TCP,8090/TCP                                                                                                                                                        18d   serviceName=collector-manager
ndp                        collector-netflow                       ClusterIP   100.113.49.126            9011/TCP                                                                                                                                                                 18d   serviceName=collector-netflow
ndp                        collector-netflow-ext                   NodePort    100.113.130.82            6007:6007/UDP                                                                                                                                                            18d   serviceName=collector-netflow
ndp                        collector-snmp                          ClusterIP   100.113.117.207           8000/TCP                                                                                                                                                                 18d   serviceName=collector-snmp
ndp                        collector-syslog                        ClusterIP   100.113.19.71             8000/TCP                                                                                                                                                                 18d   serviceName=collector-syslog
ndp                        collector-syslog-ext                    NodePort    100.113.110.179           514:514/UDP                                                                                                                                                              18d   serviceName=collector-syslog
ndp                        collector-trap                          ClusterIP   100.113.207.114           8000/TCP                                                                                                                                                                 18d   serviceName=collector-trap
ndp                        collector-trap-ext                      NodePort    100.113.66.94             162:162/UDP                                                                                                                                                              18d   serviceName=collector-trap

 

DNA Center での通信は、宛先 (TCP connection におけるサーバが DNA Center) とする通信の内、非 HTTP を含む Telemetry 通信など一部を除けば多くが REST API, GraphQL などの API 通信です。

外部からの API 通信はまずノードの物理 NIC で受信した後、上記の kong-public Service (NodePort) 経由で Kong を実装している kong Pod にルーティングされることになります。

$ kubectl get services -A -o wide | grep kong           
maglev-system              kong                                    ClusterIP   100.113.131.65            8001/TCP,8080/TCP                                                                                                                                                        18d   serviceName=kong
maglev-system              kong-frontend                           ClusterIP   100.113.201.242           443/TCP,30443/TCP,80/TCP                                                                                                                                                 18d   serviceName=kong
maglev-system              kong-public                             NodePort    100.113.173.195           443:443/TCP,30443:30443/TCP,80:80/TCP                                                                                                                                    18d   serviceName=kong

$ kubectl get pods -A -o wide | grep kong
maglev-system              kong-6ff84d57cc-92gkz                                    2/2     Running     0          18d    100.103.14.34     192.0.2.32              
maglev-system              kong-6ff84d57cc-dwtql                                    2/2     Running     0          18d    100.103.121.222   192.0.2.31              
maglev-system              kong-6ff84d57cc-mv2rj                                    2/2     Running     0          18d    100.103.119.151   192.0.2.33              

 

kong Pod が API リクエストを受け取るまでの過程はここでは説明しませんが、Service から Endpoint (Pod) へ負荷分散されていることが iptables の --mode random --probability 0.xxx という定義から分かります。

$ kubectl describe services -n maglev-system kong-public   
Name:                     kong-public
Namespace:                maglev-system
Labels:                   addon=true
                          serviceName=kong-public
Annotations:              
Selector:                 serviceName=kong
Type:                     NodePort
IP:                       100.113.173.195
Port:                     https  443/TCP
TargetPort:               443/TCP
NodePort:                 https  443/TCP
Endpoints:                100.103.119.151:443,100.103.121.222:443,100.103.14.34:443
snip

$ sudo iptables-save | grep KV42O345ZCVYHY6N
:KUBE-SEP-KV42O345ZCVYHY6N - [0:0]
-A KUBE-SEP-KV42O345ZCVYHY6N -s 100.103.119.151/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-KV42O345ZCVYHY6N -p tcp -m tcp -j DNAT --to-destination 100.103.119.151:443
-A KUBE-SVC-MUNBSCDX45GMDE34 -m statistic --mode random --probability 0.33332999982 -j KUBE-SEP-KV42O345ZCVYHY6N
-A KUBE-XLB-MUNBSCDX45GMDE34 -m comment --comment "Balancing rule 0 for maglev-system/kong-public:https" -j KUBE-SEP-KV42O345ZCVYHY6N

 

前置きが長くなりましたが、このセクションの本題の API 通信についての確認を始めます。

ここでは /api/v1/network-programmer という API に着目します。Kong に登録されたこの API の upstream_uri のホストは apic-em-network-programmer-service Service となっており、この Service 経由で apic-em-network-programmer-service Pod へ API リクエストが転送されることが分かります。

$ magctl api routes
    {
      "http_if_terminated": false, 
      "retries": 0, 
      "name": "fusion_apic-em-network-programmer-service_network-programmer", 
      "https_only": false, 
      "upstream_url": "http://apic-em-network-programmer-service.fusion.svc.cluster.local:17125/apic-em-network-programmer-service/network-programmer", 
      "strip_uri": true, 
      "created_at": 1609133194523, 
      "uris": [
        "/api/v1/network-programmer"
      ], 
      "preserve_host": false, 
      "upstream_send_timeout": 60000, 
      "upstream_read_timeout": 60000, 
      "upstream_connect_timeout": 60000, 
      "id": "67a589e9-ef56-47b0-8527-e09893397da3"
    }, 

[Fri Jan 15 05:28:32 UTC] maglev@192.0.2.31 (maglev-master-192-0-2-31) ~ $ nslookup apic-em-network-programmer-service.fusion.svc.cluster.local Server: 169.254.20.10 Address: 169.254.20.10#53 Name: apic-em-network-programmer-service.fusion.svc.cluster.local Address: 100.113.97.208
$ kubectl get services -A | grep network-programmer fusion apic-em-network-programmer-service ClusterIP 100.113.97.208 17125/TCP

$ kubectl describe services -n fusion apic-em-network-programmer-service Name: apic-em-network-programmer-service Namespace: fusion Labels: serviceName=apic-em-network-programmer-service Annotations: Selector: serviceName=apic-em-network-programmer-service Type: ClusterIP IP: 100.113.97.208 Port: default 17125/TCP TargetPort: 17125/TCP Endpoints: 100.103.119.236:17125,100.103.14.15:17125 Session Affinity: None Events:
$ kubectl get pods -n fusion -o wide | grep network-programmer apic-em-network-programmer-service-5577cd4ffc-96xjb 1/1 Running 0 18d 100.103.14.15 192.0.2.32 apic-em-network-programmer-service-5577cd4ffc-dlbnt 1/1 Running 0 18d 100.103.119.236 192.0.2.33

 

iptables を確認すると、Kong から API リクエストを受け取った apic-em-network-programmer-service Service は宛先 IP を apic-em-netwrok-programmer-service Pod のものに DNAT してパケット転送する定義であることが分かります。

3-node cluster の場合は、上記のように 2 Pod が別々のノードに実装されており、probability 0.5 で負荷分散することでパフォーマンスの向上を図っています。

$ sudo iptables-save | grep -e network-programmer -e 100\.103\.14\.15 -e 100\.103\.119\.236 -e 100\.113\.97\.208 -e MOEH6TQSY2L3VPWA -e OP4ZB2UWNO6TQGUI -e WWNMAOWQ7XLKYRU6
:KUBE-SEP-MOEH6TQSY2L3VPWA - [0:0]
:KUBE-SEP-OP4ZB2UWNO6TQGUI - [0:0]
:KUBE-SVC-WWNMAOWQ7XLKYRU6 - [0:0]
-A KUBE-SEP-MOEH6TQSY2L3VPWA -s 100.103.119.236/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-MOEH6TQSY2L3VPWA -p tcp -m tcp -j DNAT --to-destination 100.103.119.236:17125
-A KUBE-SEP-OP4ZB2UWNO6TQGUI -s 100.103.14.15/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-OP4ZB2UWNO6TQGUI -p tcp -m tcp -j DNAT --to-destination 100.103.14.15:17125
-A KUBE-SERVICES ! -s 100.103.0.0/16 -d 100.113.97.208/32 -p tcp -m comment --comment "fusion/apic-em-network-programmer-service:default cluster IP" -m tcp --dport 17125 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 100.113.97.208/32 -p tcp -m comment --comment "fusion/apic-em-network-programmer-service:default cluster IP" -m tcp --dport 17125 -j KUBE-SVC-WWNMAOWQ7XLKYRU6
-A KUBE-SVC-WWNMAOWQ7XLKYRU6 -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-MOEH6TQSY2L3VPWA
-A KUBE-SVC-WWNMAOWQ7XLKYRU6 -j KUBE-SEP-OP4ZB2UWNO6TQGUI

 

以上が、Web ブラウザを含む外部アプリケーションが DNA Center に API リクエストを行う際の通信の大まかな流れです。

API リクエストの宛先 IP は 3-node cluster のどのノードでも構いません。Kong が受け取った API リクエストは kube-proxy によって構成された iptables に従い Service 経由で DNAT されて Pod に API リクエストが届きます。

3-node cluster の場合、耐障害性を考慮するとノードの物理 IP を宛先として API リクエストを行うのであれば外部に Load Balancer が必要になり、構成が複雑になります。

この対策として、DNA Center では Keepalived を Pod として実装し、VRRP を稼働させることで VRRP master node (Kubernetes の master とは関係ありません) に VIP (Virtual IP) を持たせ、外部アプリケーションは VIP 宛に通信するという構成になっています。

 

Keepalived は keepalived Pod として各ノードに実装されています。

$ kubectl get pods -n maglev-system -o wide | grep keepalived
keepalived-lr599                       1/1     Running   0          18d   192.0.2.32        192.0.2.32              
keepalived-lsv67                       1/1     Running   0          18d   192.0.2.33        192.0.2.33              
keepalived-v24dd                       1/1     Running   0          18d   192.0.2.31        192.0.2.31              

 

Pod にログインすると Python スクリプトから Keepalived が呼び出されている状況や keepalived.conf を確認できます。

[Fri Jan 15 16:02:45 UTC] maglev@192.0.2.33 (maglev-master-192-0-2-33) ~
$ magctl service attach keepalived-lsv67 --docker
Attaching to 'maglev-system/keepalived-lsv67
root@maglev-master-192-0-2-33:/#
root@maglev-master-192-0-2-33:/# ps auxf USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 80014 0.0 0.0 18308 3304 pts/0 Ss 15:44 0:00 /bin/bash root 80057 0.0 0.0 34428 2844 pts/0 R+ 15:49 0:00 \_ ps auxf root 8 0.0 0.0 111192 50704 ? Ss 2020 7:43 /usr/bin/python /keepalived.py root 1258 0.0 0.0 31724 3608 ? S 2020 0:00 \_ /usr/sbin/keepalived --vrrp --dont-fork --log-console --log-detail --release-vips --pid /etc/keepalived/keepalived.pid & root 1259 0.0 0.0 31844 2420 ? S 2020 4:42 \_ /usr/sbin/keepalived --vrrp --dont-fork --log-console --log-detail --release-vips --pid /etc/keepalived/keepalived.pid & root 1 0.0 0.0 1024 4 ? Ss 2020 0:00 /pause root@maglev-master-192-0-2-33:/# ls /keepalived* /keepalived.py /keepalivednotify.py root@maglev-master-192-0-2-33:/# cat /etc/keepalived/keepalived.conf <snip> vrrp_instance vip_192.0.2.30 { state BACKUP interface enp10s0 virtual_router_id 97 nopreempt advert_int 1 track_interface { enp10s0 } virtual_ipaddress { 192.0.2.30 dev enp10s0 scope global } unicast_src_ip 192.0.2.33 unicast_peer { 192.0.2.31 192.0.2.32 } track_script { node_health_check } notify /keepalivednotify.py root } <snip>

 

一般的な Kubernetes Pod はプロセス/ファイルシステム/ネットワークなどの名前空間をホストから分離し、Ethernet NIC については eth0 だけが存在するという構成になりますが、ホストから分離された Pod のネットワーク名前空間で VRRP を稼働しても意味がありません。keepalived Pod ではネットワーク名前空間を分離せずホストと同じネットワークとすることでノードの VRRP master election を Pod 上の Keepalived で行うということを行っています。

root@maglev-master-192-0-2-33:/# ip add | grep eth0
root@maglev-master-192-0-2-33:/#
root@maglev-master-192-0-2-33:/# ip add | grep -e ^[0-9].*enp -e 10\.70 -e 192\.
2: enp9s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 192.168.1.33/24 brd 192.168.1.255 scope global enp9s0
    inet 192.168.1.30/32 scope global enp9s0
3: enp10s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 192.0.2.33/24 brd 192.0.2.255 scope global enp10s0
    inet 192.0.2.30/32 scope global enp10s0
4: enp1s0f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 10.70.71.145/27 brd 10.70.71.159 scope global enp1s0f0
    inet 10.70.71.149/32 scope global enp1s0f0
5: enp1s0f1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 100
root@maglev-master-192-0-2-33:/# exit
exit

[Fri Jan 15 16:02:35 UTC] maglev@192.0.2.33 (maglev-master-192-0-2-33) ~
$ ip add | grep -e ^[0-9].*enp -e 10\.70 -e 192\.
2: enp9s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 192.168.1.33/24 brd 192.168.1.255 scope global enp9s0
    inet 192.168.1.30/32 scope global enp9s0
3: enp10s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 192.0.2.33/24 brd 192.0.2.255 scope global enp10s0
    inet 192.0.2.30/32 scope global enp10s0
4: enp1s0f0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    inet 10.70.71.145/27 brd 10.70.71.159 scope global enp1s0f0
    inet 10.70.71.149/32 scope global enp1s0f0
5: enp1s0f1: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000

 

このように、VRRP を利用することで外部アプリケーションは物理 NIC IP ではなく VIP へ API リクエストを行う構成とし、ノード障害時に外部 Load Balancer 無しで API 通信サービスの継続性を確保しています。



また、データベース系のアプリケーションについては StatefulSet が使用され、Service の Cluster IP は定義されていません。これらについては DNAT ではなく DNS Round Robin による負荷分散が行われます。

(こちらは DNA Center 2.2.2.4 でのログです)

[Sun Oct 31 08:50:43 UTC] maglev@192.0.2.33 (maglev-master-192.0.2.33) ~ $ kubectl get services -A | grep -e None -e NAME NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE app-hosting postgres ClusterIP None 9200/TCP,5432/TCP,5433/TCP,8080/TCP 25d fusion postgres ClusterIP None 9200/TCP,5432/TCP,5433/TCP,8080/TCP 27d maglev-system cassandra ClusterIP None 9042/TCP,8080/TCP 27d maglev-system elasticsearch ClusterIP None 9200/TCP,9300/TCP 27d maglev-system glusterfs-brick ClusterIP None 8080/TCP,24007/TCP,24008/TCP,49152/TCP,49153/TCP,49154/TCP,49156/TCP,49157/TCP,49158/TCP,49159/TCP,49160/TCP,49161/TCP,49162/TCP,38465/TCP,38466/TCP,38467/TCP,111/TCP 27d maglev-system influxdb ClusterIP None 8083/TCP,8086/TCP,8089/TCP 27d maglev-system mongodb ClusterIP None 27017/TCP,8080/TCP 27d maglev-system rabbitmq ClusterIP None 8080/TCP,5672/TCP,4369/TCP,25672/TCP,15672/TCP 27d maglev-system workflow-worker ClusterIP None 27d maglev-system zookeeper ClusterIP None 2181/TCP,2188/TCP,3888/TCP 27d ndp elasticsearch ClusterIP None 9200/TCP,9300/TCP 27d ndp kafka ClusterIP None 9092/TCP,7203/TCP 27d ndp redis ClusterIP None 6379/TCP 27d [Sun Oct 31 08:50:46 UTC] maglev@192.0.2.33 (maglev-master-192.0.2.33) ~ $ kubectl get statefulset -A -o wide NAMESPACE NAME READY AGE CONTAINERS IMAGES app-hosting postgres 3/3 25d postgres,sidecar,proxy maglev-registry.maglev-system.svc.cluster.local:5000/postgres:1.6.84,maglev-registry.maglev-system.svc.cluster.local:5000/postgres-sidecar:1.6.65,maglev-registry.maglev-system.svc.cluster.local:5000/postgres-proxy:1.6.18 fusion postgres 3/3 27d postgres,sidecar,proxy maglev-registry.maglev-system.svc.cluster.local:5000/postgres:1.6.63,maglev-registry.maglev-system.svc.cluster.local:5000/postgres-sidecar:1.6.55,maglev-registry.maglev-system.svc.cluster.local:5000/postgres-proxy:1.6.14 maglev-system cassandra 3/3 27d cassandra,sidecar maglev-registry.maglev-system.svc.cluster.local:5000/cassandra-3.11:1.6.15,maglev-registry.maglev-system.svc.cluster.local:5000/cassandra-sidecar:1.6.31 maglev-system elasticsearch 3/3 27d sidecar,elasticsearch maglev-registry.maglev-system.svc.cluster.local:5000/elasticsearch-sidecar:1.6.52,maglev-registry.maglev-system.svc.cluster.local:5000/elasticsearch-7.0.1:1.6.28 maglev-system glusterfs-brick 3/3 27d glusterfs,sidecar maglev-registry.maglev-system.svc.cluster.local:5000/glusterfs:1.6.31,maglev-registry.maglev-system.svc.cluster.local:5000/glusterfs-sidecar:1.6.63 maglev-system influxdb 2/2 27d sidecar,proxy,influxdb maglev-registry.maglev-system.svc.cluster.local:5000/influxdb-sidecar:1.6.37,maglev-registry.maglev-system.svc.cluster.local:5000/influxdb-proxy:1.6.19,maglev-registry.maglev-system.svc.cluster.local:5000/influxdb:1.6.26 maglev-system mongodb 3/3 27d mongodb,mongo-sidecar,mongo-maglev-sidecar maglev-registry.maglev-system.svc.cluster.local:5000/mongodb-4.0.12:1.6.24,maglev-registry.maglev-system.svc.cluster.local:5000/mongo-k8s-sidecar:1.6.16,maglev-registry.maglev-system.svc.cluster.local:5000/mongodb-sidecar:1.6.90 maglev-system rabbitmq 3/3 27d rabbitmq,rabbitmq-sidecar maglev-registry.maglev-system.svc.cluster.local:5000/rabbitmq:1.6.28,maglev-registry.maglev-system.svc.cluster.local:5000/rabbitmq-sidecar:1.6.54 maglev-system zookeeper 3/3 27d zookeeper,sidecar maglev-registry.maglev-system.svc.cluster.local:5000/zookeeper:1.6.47,maglev-registry.maglev-system.svc.cluster.local:5000/zookeeper-sidecar:1.6.56 ndp elasticsearch 3/3 27d elasticsearch,sidecar maglev-registry.maglev-system.svc.cluster.local:5000/elasticsearch-7.0.1:1.6.28,maglev-registry.maglev-system.svc.cluster.local:5000/elasticsearch-sidecar:1.6.52 ndp kafka 3/3 27d sidecar,kafka,jmxtrans maglev-registry.maglev-system.svc.cluster.local:5000/kafka-sidecar:1.6.51,maglev-registry.maglev-system.svc.cluster.local:5000/kafka:1.6.22,maglev-registry.maglev-system.svc.cluster.local:5000/kafka-jmxtrans:1.6.14 ndp redis 3/3 27d redis,sidecar maglev-registry.maglev-system.svc.cluster.local:5000/redis:1.6.13,maglev-registry.maglev-system.svc.cluster.local:5000/redis-sidecar:1.6.38 [Sun Oct 31 08:50:57 UTC] maglev@192.0.2.33 (maglev-master-192.0.2.33) ~ $ [Sun Oct 31 08:51:04 UTC] maglev@192.0.2.33 (maglev-master-192.0.2.33) ~ $ kubectl get statefulset -n fusion -o wide | grep -e NAME -e postgres NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR postgres ClusterIP None 9200/TCP,5432/TCP,5433/TCP,8080/TCP 27d serviceName=postgres [Sun Oct 31 08:51:22 UTC] maglev@192.0.2.33 (maglev-master-192.0.2.33) ~ $ kubectl get pods -n fusion -o wide | grep -e NAME -e postgres NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES postgres-0 3/3 Running 0 24d 169.254.41.231 192.0.2.33 postgres-1 3/3 Running 1 25d 169.254.41.108 192.0.2.31 postgres-2 3/3 Running 4 26d 169.254.45.2 192.0.2.32 [Sun Oct 31 08:51:33 UTC] maglev@192.0.2.33 (maglev-master-192.0.2.33) ~ $ [Sun Oct 31 08:51:36 UTC] maglev@192.0.2.33 (maglev-master-192.0.2.33) ~ $ nslookup > 169.254.41.231 231.41.254.169.in-addr.arpa name = postgres-0.postgres.fusion.svc.cluster.local. > 169.254.41.108 108.41.254.169.in-addr.arpa name = postgres-1.postgres.fusion.svc.cluster.local. > 169.254.45.2 2.45.254.169.in-addr.arpa name = postgres-2.postgres.fusion.svc.cluster.local. > exit [Sun Oct 31 08:52:07 UTC] maglev@192.0.2.33 (maglev-master-192.0.2.33) ~ $ cat /etc/resolv.conf | grep -v \# nameserver 127.0.0.53 options edns0 search default.svc.cluster.local maglev-system.svc.cluster.local svc.cluster.local cluster.local local [Sun Oct 31 08:52:19 UTC] maglev@192.0.2.33 (maglev-master-192.0.2.33) ~ $ nslookup postgres.fusion.svc.cluster.local Server: 127.0.0.53 Address: 127.0.0.53#53 Name: postgres.fusion.svc.cluster.local Address: 169.254.45.2 Name: postgres.fusion.svc.cluster.local Address: 169.254.41.108 Name: postgres.fusion.svc.cluster.local Address: 169.254.41.231

 

 

DNA Center からの通信

 DNA Center は API サーバとしての通信だけではなく、SSH, NETCONF でのデバイスの Provision, SNMP MIB の polling, Webhook で外部アプリケーションに HTTP POST するなど、通信のクライアントとしても機能します。その場合は Pod からのトラフィックが SNAT されて宛先に送信されます。

 

例えば、以下の例であれば 100.103.119.178 からデバイス 192.168.202.14 への SSH (tcp/22) および NETCONF (tcp/803) が NAT されて送信されていることが分かります。

$ sudo conntrack -E -d 192.168.202.14
    [NEW] tcp      6 120 SYN_SENT src=100.103.119.178 dst=192.168.202.14 sport=33596 dport=22 [UNREPLIED] src=192.168.202.14 dst=192.168.1.33 sport=22 dport=1164
 [UPDATE] tcp      6 60 SYN_RECV src=100.103.119.178 dst=192.168.202.14 sport=33596 dport=22 src=192.168.202.14 dst=192.168.1.33 sport=22 dport=1164
 [UPDATE] tcp      6 86400 ESTABLISHED src=100.103.119.178 dst=192.168.202.14 sport=33596 dport=22 src=192.168.202.14 dst=192.168.1.33 sport=22 dport=1164 [ASSURED]
    [NEW] tcp      6 120 SYN_SENT src=100.103.119.178 dst=192.168.202.14 sport=53230 dport=830 [UNREPLIED] src=192.168.202.14 dst=192.168.1.33 sport=830 dport=22444
 [UPDATE] tcp      6 60 SYN_RECV src=100.103.119.178 dst=192.168.202.14 sport=53230 dport=830 src=192.168.202.14 dst=192.168.1.33 sport=830 dport=22444
 [UPDATE] tcp      6 86400 ESTABLISHED src=100.103.119.178 dst=192.168.202.14 sport=53230 dport=830 src=192.168.202.14 dst=192.168.1.33 sport=830 dport=22444 [ASSURED]

$ sudo conntrack -L -n | grep 192.168.202.14
tcp      6 86399 ESTABLISHED src=100.103.119.178 dst=192.168.202.14 sport=33596 dport=22 src=192.168.202.14 dst=192.168.1.33 sport=22 dport=1164 [ASSURED] mark=0 use=1
tcp      6 86395 ESTABLISHED src=100.103.119.178 dst=192.168.202.14 sport=53230 dport=830 src=192.168.202.14 dst=192.168.1.33 sport=830 dport=22444 [ASSURED] mark=0 use=1

 

送信元である 100.103.119.178 は apic-em-inventory-manager-service Pod で、その NATed IP は DNA Center の (VIP の 192.168.1.30 ではなく) 物理 NIC IP (192.168.1.33) に SNAT されていることを確認できます。

$ kubectl get pods -n fusion -o wide | grep 100.\103\.119\\.178
apic-em-inventory-manager-service-d8bb9c5c5-wwdhk       1/1     Running   0          20d   100.103.119.178   192.0.2.33              


2: enp9s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 4c:77:6d:49:82:74 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.33/24 brd 192.168.1.255 scope global enp9s0
       valid_lft forever preferred_lft forever
    inet 192.168.1.30/32 scope global enp9s0
       valid_lft forever preferred_lft forever
    inet6 fe80::4e77:6dff:fe49:8274/64 scope link 
       valid_lft forever preferred_lft forever

$ maglev cluster network display cluster_network: cluster_dns: 169.254.20.10 cluster_hostname: '' cluster_subnet: 100.113.0.0/16 cluster_vip: - 192.0.2.30 - 192.168.1.30 - 10.70.71.149 container_subnet: 100.103.0.0/16

 

その他

DNA Center は Linux 上の Web アプリケーションですので通信は Linux での設定に基づきます。 例えば、以下の例では rp_filter = 1 となっていますので Reverse Path Forwarding が RFC 3704 strict mode で設定されていることや ignore_routes_with_linkdown = 0 なので NIC が link down しても static route は無効にならない (IOS での floating static ような挙動はしない) ことなどが分かります。

$ sysctl net.ipv4.conf | grep enp9s0
net.ipv4.conf.enp9s0.accept_local = 0
net.ipv4.conf.enp9s0.accept_redirects = 0
net.ipv4.conf.enp9s0.accept_source_route = 0
net.ipv4.conf.enp9s0.arp_accept = 0
net.ipv4.conf.enp9s0.arp_announce = 0
net.ipv4.conf.enp9s0.arp_filter = 0
net.ipv4.conf.enp9s0.arp_ignore = 0
net.ipv4.conf.enp9s0.arp_notify = 0
net.ipv4.conf.enp9s0.bootp_relay = 0
net.ipv4.conf.enp9s0.disable_policy = 0
net.ipv4.conf.enp9s0.disable_xfrm = 0
net.ipv4.conf.enp9s0.drop_gratuitous_arp = 0
net.ipv4.conf.enp9s0.drop_unicast_in_l2_multicast = 0
net.ipv4.conf.enp9s0.force_igmp_version = 0
net.ipv4.conf.enp9s0.forwarding = 1
net.ipv4.conf.enp9s0.igmpv2_unsolicited_report_interval = 10000
net.ipv4.conf.enp9s0.igmpv3_unsolicited_report_interval = 1000
net.ipv4.conf.enp9s0.ignore_routes_with_linkdown = 0
net.ipv4.conf.enp9s0.log_martians = 1
net.ipv4.conf.enp9s0.mc_forwarding = 0
net.ipv4.conf.enp9s0.medium_id = 0
net.ipv4.conf.enp9s0.promote_secondaries = 0
net.ipv4.conf.enp9s0.proxy_arp = 0
net.ipv4.conf.enp9s0.proxy_arp_pvlan = 0
net.ipv4.conf.enp9s0.route_localnet = 0
net.ipv4.conf.enp9s0.rp_filter = 1
net.ipv4.conf.enp9s0.secure_redirects = 0
net.ipv4.conf.enp9s0.send_redirects = 0
net.ipv4.conf.enp9s0.shared_media = 1
net.ipv4.conf.enp9s0.src_valid_mark = 0
net.ipv4.conf.enp9s0.tag = 0
 
また、Kubernetes Pod の通信については CNI である Calio の設定にも依存します。
$ calicoctl get globalnetworkpolicy                                
NAME                             
allow-cluster-internal-ingress   
allow-gre                        
allow-nodeport                   
allow-outbound-external          
drop-other-ingress               

$ calicoctl get globalnetworkpolicy allow-cluster-internal-ingress -o yaml
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  creationTimestamp: 2020-12-28T04:29:59Z
  name: allow-cluster-internal-ingress
  resourceVersion: "37417"
  uid: 5836a1b7-48c5-11eb-88cf-4c776d498274
spec:
  applyOnForward: true
  ingress:
  - action: Allow
    destination: {}
    source:
      nets:
      - 100.113.0.0/16
      - 100.103.0.0/16
      - 192.0.2.33/32
      - 192.0.2.32/32
      - 192.0.2.31/32
  order: 10
  preDNAT: true
  selector: has(host-endpoint)
  types:
  - Ingress

 

コメント
mnagao
Cisco Employee
Cisco Employee

DNA Center 2.3.3 以降では kube-proxy の proxier は iptables から仮想 NIC kube-ipvs0 を使用する IPVS に変更になっています。sudo ipvsadm -l , sudo ipvsadm -lc などで proxy の状況を確認できます。



DNA Center 2.2.3 以前

$ kubectl logs -n kube-system kube-proxy-fq4sh | head -n 5
W0627 03:05:18.568426       6 server.go:216] WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP.
W0627 03:05:18.675890       6 server_others.go:249] Flag proxy-mode="" unknown, assuming iptables proxy
I0627 03:05:18.769741       6 server_others.go:143] Using iptables Proxier.
I0627 03:05:18.770183       6 server.go:534] Version: v1.15.3-cisco
I0627 03:05:18.818136       6 conntrack.go:52] Setting nf_conntrack_max to 2883584



DNA Center 2.3.3 以降

$ magctl service logs -r kube-proxy | head -n 5
W0725 05:20:13.636607       6 server.go:225] WARNING: all flags other than --config, --write-config-to, and --cleanup are deprecated. Please begin using a config file ASAP.
I0725 05:20:13.753922       6 node.go:136] Successfully retrieved node IP: 172.20.115.1
I0725 05:20:13.753967       6 server_others.go:259] Using ipvs Proxier.
I0725 05:20:13.754393       6 server.go:583] Version: v1.18.15-cisco
I0725 05:20:13.754775       6 conntrack.go:52] Setting nf_conntrack_max to 2883584

 

Getting Started

検索バーにキーワード、フレーズ、または質問を入力し、お探しのものを見つけましょう

シスコ コミュニティをいち早く使いこなしていただけるよう役立つリンクをまとめました。みなさんのジャーニーがより良いものとなるようお手伝いします