[Kubernetes][Network] Project Calicoのネットワークをコンテナ外に出す

コンテナネットワークの基本的な考え

Kubernetesに限らず、Dockerを使用する環境において、ネットワークは「コンテナホスト内のプライベートIP」を構成し、コンテナはコンテナホストの所有するIPアドレスを使用してNATを行い、外に出るというのが基本的な考えだったりします。

Kubernetesで使用されるオーバーレイネットワークも基本的にはその流れに沿っていて、WeaveもFlannelもコンテナホスト環境内においては共通したL2ネットワークが構成されますが、外に出るときはやっぱりNATで外に出る形になります。外からコンテナへアクセスするには、Serviceリソースを構成し、そこでNodeIPを指定する事が多いように思います。

Project Calicoのデフォルト構成

我が家では、CNIプラグインとして、Project Calicoを使用しています。これはホストエージェントがiBGPルーターとして構成される一風変わったネットワークコンポーネントになりますが、これも基本的には先述した構成に沿っています。

基本的にはコンテナはNAT越えをすることになる

また、デフォルト設定ではホスト間ネットワークを構成するCaliconodeは互いをフルメッシュでiBGP接続を行っていて、相互経路交換を行っています。ホスト間がセグメントをまたがる場合は、IPIP接続を行って1hop通信構成を維持する形になっています。

今回狙う構成

今回やろうとしているのは、本来外に出ることはないコンテナネットワークのIPを、コンテナ外からアクセスできるようにしようというものです。

VyOSをルートリフレクタとして構成する

Project CalicoではiBGPが使用されていると書きましたが、つまりはこの経路をコンテナ外側のルーターに引き渡すことができれば、外から内側に対するアクセスが可能になります。

また、iBGPのピアをフルメッシュ型からスター型にすることで、BGPピアの数を削減させ、運用管理上の負担を軽減させることが可能です。ルートを集約・展開する中継デバイスを「ルートリフレクタ」と言うそうです。

今回、このルートリフレクタを行うデバイスとして、VyOSを採用することにしました。本当はSEIL/x86を使用したかったのですが、悲しいことにSEIL/x86はeBGPにしか対応しておらず、iBGP非対応だったのです(しかも機能キー購入後に気づくという悲しいお話)。

iBGPで集約した経路は、OSPFに乗せてコンテナ外のネットワークデバイスに流します。今回、確認対象として、コアL3スイッチを使うことにしました。

VyOS側の作業

VyOS側では、AS番号の設定・ピア接続設定を実施します。今回AS番号はプライベートサイトのみの構成になることから、プライベートAS番号を(AS64512以降)使います。当環境では、AS64515を使用しています。

# edit protocols bgp 64515
# set parameters router-id 192.168.100.41
# set neighbor 192.168.100.132 remote-as 64515
# set neighbor 192.168.100.172 remote-as 64515
# set neighbor 192.168.100.173 remote-as 64515
# set neighbor 192.168.100.174 remote-as 64515
# set neighbor 192.168.100.175 remote-as 64515

# set neighbor 192.168.100.132 route-reflector-client
# set neighbor 192.168.100.172 route-reflector-client
# set neighbor 192.168.100.173 route-reflector-client
# set neighbor 192.168.100.174 route-reflector-client
# set neighbor 192.168.100.175 route-reflector-client

# set network 192.168.0.0/16
# set network 10.5.0.0/24
# set network 10.5.1.0/24

# commit
# save

アドレス192.168.100.132はKubernetesのMasterノード、192.168.100.17[2345]はMinionノードです。192.168.0.0/16および10.5.0.0/24、10.5.1.0/24はコンテナ外の自宅ネットワークであることを指しています。

設定が完了したら commit -> save を行い、設定を反映・保存します。

Project Calico側の設定

Project Calico側では、 calicoctl と言うコマンドラインを使用します。当環境では、calicoctlバイナリをMasterノードに仕込んでいますので、これを使用します。

calicoctl は kubectl によく似ており、リソース作成時はYAMLファイルを作成して、それをコマンド適用すると言うスタイルを取っています。状態確認もこれまたkubectl同様にgetコマンドを使用することで、情報を取得することが可能です。

まず、以下YAMLファイルを作成します。

【AS_Create.yaml:AS番号の変更

apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default
spec:
  logSeverityScreen: Info
  nodeToNodeMeshEnabled: false
  asNumber: 64515

【createPeer.yaml:iBGPピア接続設定】

apiVersion: projectcalico.org/v3
kind: BGPPeer
metadata:
  name: bgppeer-zenmai
spec:
  peerIP: 192.168.100.41
  asNumber: 64515

これらファイルを用いて、リソースを作成します。

# calicoctl create -f AS_Create.yaml
Successfully created 1 'BGPConfiguration' resource(s)
# calicoctl create -f createPeer.yaml 
Successfully created 1 'BGPPeer' resource(s)

この時点で、iBGPピアが確立された状態になります。

■Project Calico上の構成を確認する
# calicoctl get nodes -o wide
NAME                  ASN       IPV4                 IPV6                                        
acalanatha            (64515)   192.168.100.174/24   2403:bd80:c102:100:b76f:eb70:523c:e6de/64   
bhaisa.bluecore.net   (64515)   192.168.100.175/24   2403:bd80:c102:100:7998:9c56:a9a3:dc98/64   
mahsth                (64515)   192.168.100.173/24   2403:bd80:c102:100:1d6b:e1c3:98c7:5ede/64   
maitreya              (64515)   192.168.100.172/24   2403:bd80:c102:100:9244:9b33:606a:e003/64   
rahu                  (64515)   192.168.100.132/24   2403:bd80:c102:100:5743:c6a8:6343:c74/64    

# calicoctl get bgpConfiguration -o wide
NAME      LOGSEVERITY   MESHENABLED   ASNUMBER   
default   Info          false         64515      

# calicoctl get bgpPeer -o wide
NAME             PEERIP           NODE       ASN     
bgppeer-zenmai   192.168.100.41   (global)   64515   

■Kubernetesマスターノードの経路情報を確認する
# ip r
default via 192.168.100.254 dev ens160 proto static metric 100 
blackhole 10.240.26.192/26 proto bird 
10.240.26.219 dev calief7197cf894 scope link 
10.240.26.223 dev cali4477e889399 scope link 
10.240.54.192/26 via 192.168.100.175 dev ens160 proto bird 
10.240.162.128/26 via 192.168.100.173 dev ens160 proto bird 
10.240.232.192/26 via 192.168.100.174 dev ens160 proto bird 
10.240.239.0/26 via 192.168.100.172 dev ens160 proto bird 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
192.168.100.0/24 dev ens160 proto kernel scope link src 192.168.100.132 metric 100 

■Kubernetes minionノードの経路情報を確認する
# ip r
default via 192.168.100.254 dev ens160 proto static metric 100 
10.240.26.192/26 via 192.168.100.132 dev ens160 proto bird 
10.240.54.192/26 via 192.168.100.175 dev ens160 proto bird 
10.240.162.128/26 via 192.168.100.173 dev ens160 proto bird 
10.240.232.192/26 via 192.168.100.174 dev ens160 proto bird 
blackhole 10.240.239.0/26 proto bird 
10.240.239.39 dev calibcd49511076 scope link 
10.240.239.40 dev cali83831ae1e85 scope link 
10.240.239.41 dev cali552f5ab0c2d scope link 
10.240.239.42 dev calief912e1e91a scope link 
10.240.239.43 dev cali6e8d0b98ce1 scope link 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
192.168.100.0/24 dev ens160 proto kernel scope link src 192.168.100.172 metric 100 

VyOS側の状態を見てみると以下のような状態になっていました。

$ show ip bgp summary
BGP router identifier 192.168.100.41, local AS number 64515
IPv4 Unicast - max multipaths: ebgp 1 ibgp 1
RIB entries 13, using 1248 bytes of memory
Peers 5, using 22 KiB of memory

Neighbor        V    AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
192.168.100.132 4 64515     182     172        0    0    0 02:39:09        1
192.168.100.172 4 64515     184     172        0    0    0 02:39:08        1
192.168.100.173 4 64515     184     172        0    0    0 02:39:08        1
192.168.100.174 4 64515     184     172        0    0    0 02:39:08        1
192.168.100.175 4 64515     185     172        0    0    0 02:39:08        1

Total number of neighbors 5

$ show ip bgp
BGP table version is 0, local router ID is 192.168.100.41
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
              r RIB-failure, S Stale, R Removed
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*>i10.240.26.192/26 192.168.100.132               100      0 i
*>i10.240.54.192/26 192.168.100.175               100      0 i
*>i10.240.162.128/26
                    192.168.100.173               100      0 i
*>i10.240.232.192/26
                    192.168.100.174               100      0 i
*>i10.240.239.0/26  192.168.100.172               100      0 i

Total number of prefixes 5

OSPFの設定(VyOS側)

続いて、VyOS側でOSPFの設定を行います。当方のサーバセグメントはOSPFで経路制御を行っており、エリアはbackboneエリアとなっています。これを踏まえて以下の通り設定しました。

# set protocols ospf area 0.0.0.0 network 10.240.0.0/16
# set protocols ospf area 0.0.0.0 network 192.168.100.0/24

# set protocols ospf parameters route-id 192.168.100.41

# set protocols ospf redistribute bgp
# set protocols ospf redistribute connected

# commit
# save

その後の経路情報を確認します。コアL3スイッチの経路は以下のようになりました。

# show ip route
Codes: C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route

Gateway of last resort is 192.168.100.4 to network 0.0.0.0

     103.0.0.0/29 is subnetted, 1 subnets
O       103.247.181.136 [110/11] via 192.168.100.4, 00:05:55, Vlan10
C    192.168.90.0/24 is directly connected, Vlan30
S    192.168.42.0/24 [1/0] via 192.168.100.132
O E2 192.168.230.0/24 [110/1] via 192.168.100.1, 00:05:55, Vlan10
O E2 192.168.231.0/24 [110/1] via 192.168.100.1, 00:05:55, Vlan10
C    192.168.200.0/24 is directly connected, Vlan100
     10.0.0.0/8 is variably subnetted, 12 subnets, 4 masks
S       10.5.254.240/28 [1/0] via 192.168.100.3
O E2    10.240.54.192/26 [110/20] via 192.168.100.175, 00:05:50, Vlan10
O IA    10.5.1.0/24 [110/11] via 192.168.100.4, 00:05:55, Vlan10
O       10.5.0.0/24 [110/201] via 192.168.100.3, 00:05:55, Vlan10
O E2    10.240.239.0/26 [110/20] via 192.168.100.172, 00:05:50, Vlan10
O E2    10.240.26.192/26 [110/20] via 192.168.100.132, 00:05:57, Vlan10
O E2    10.81.234.0/24 [110/20] via 192.168.100.3, 00:06:02, Vlan10
O E2    10.240.232.192/26 [110/20] via 192.168.100.174, 00:05:57, Vlan10
O E2    10.240.162.128/26 [110/20] via 192.168.100.173, 00:05:57, Vlan10
S       10.5.254.0/25 [1/0] via 192.168.100.3
O E2    10.255.0.0/24 [110/20] via 192.168.100.3, 00:06:02, Vlan10
O E2    10.242.2.0/24 [110/10] via 192.168.100.4, 00:06:02, Vlan10
O E2 192.168.239.0/24 [110/1] via 192.168.100.1, 00:06:02, Vlan10
C    192.168.220.0/24 is directly connected, Vlan20
C    192.168.100.0/24 is directly connected, Vlan10
O*E2 0.0.0.0/0 [110/1] via 192.168.100.4, 00:06:02, Vlan10

10.240.0.0/16のうち、いくつかの26bitセグメントが経路伝搬されていることがわかります。VyOS側の経路情報を見てみます。

$ show ip route
Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
       I - ISIS, B - BGP, > - selected route, * - FIB route

O   0.0.0.0/0 [110/1] via 192.168.100.4, 11:09:44
S>* 0.0.0.0/0 [1/0] via 192.168.100.254, eth0
O>* 10.5.0.0/24 [110/210] via 192.168.100.3, eth0, 11:09:45
O>* 10.5.1.0/24 [110/20] via 192.168.100.4, eth0, 11:09:45
O>* 10.81.234.0/24 [110/20] via 192.168.100.3, eth0, 11:09:44
B>* 10.240.26.192/26 [200/0] via 192.168.100.132, eth0, 11:45:32
B>* 10.240.54.192/26 [200/0] via 192.168.100.175, eth0, 11:45:31
B>* 10.240.162.128/26 [200/0] via 192.168.100.173, eth0, 11:45:31
B>* 10.240.232.192/26 [200/0] via 192.168.100.174, eth0, 11:45:31
B>* 10.240.239.0/26 [200/0] via 192.168.100.172, eth0, 11:45:31
O>* 10.242.2.0/24 [110/10] via 192.168.100.4, eth0, 11:09:44
O>* 10.255.0.0/24 [110/20] via 192.168.100.3, eth0, 11:09:44
O>* 103.247.181.136/29 [110/20] via 192.168.100.4, eth0, 11:09:45
C>* 127.0.0.0/8 is directly connected, lo
O>* 192.168.42.0/24 [110/20] via 192.168.100.132, eth0, 11:09:44
O>* 192.168.90.0/24 [110/11] via 192.168.100.254, eth0, 11:09:45
O   192.168.100.0/24 [110/10] is directly connected, eth0, 11:09:45
C>* 192.168.100.0/24 is directly connected, eth0
O>* 192.168.200.0/24 [110/11] via 192.168.100.254, eth0, 11:09:45
O>* 192.168.220.0/24 [110/11] via 192.168.100.254, eth0, 11:09:45
O>* 192.168.230.0/24 [110/1] via 192.168.100.1, eth0, 11:09:44
O>* 192.168.231.0/24 [110/1] via 192.168.100.1, eth0, 11:09:44
O>* 192.168.239.0/24 [110/1] via 192.168.100.1, eth0, 11:09:44

お互いに経路交換はできてそうです。

VyOSはなにげに面白い

久々にVyOSを触った(当時はまだVyattaと言われてた)のですが、勘を取り戻すのにだいぶ苦労したものの、今回はかなり楽しめました。CLIはJuniper系に似てて、どちらかというと私は苦手としているのですが、思った以上にすんなりととっつけた感じです。

機能的にもかなり多彩なので、おすすめといえばおすすめです。こういうルートリフレクタのような内部用途で使うデバイスとしては結構良いんじゃないかなーなんて思ったり。

今回、IPv4に限定してネットワーク構成変更を行ったのですが、そのうち現在NAT66で外出ししているIPv6に関しても、同じ様な対応をとってみたいなーとか思ったり。併せて、ルートリフレクタの冗長構成なんかを撮ってみようかとかいろいろ考えています。