Tungsten Fabricのインストール(k8s, kolla, k8s&kolla)

Tungsten Fabric のインストールをAWS上で試してみたので、その時のメモとなる。

インストール時の組み合わせとしては、
1. k8s
2. kolla openstack
3. k8s & kolla openstack
の3パターンで実施している。
※ このうち、3番目の組み合わせは、最近できた仕組みとなる

全体を通じて、環境としては、AWS 上のMarketPlaceで、"CentOS 7 (x86_64) - with Updates HVM" と記載されている、Centos7.4のインスタンスを使用している。(ami-91c4d3ed, ログインユーザー名は centos)
また、Disk は20GBで作成するようにした。

2018/5/19 追記
centos7.5が出た影響なのか、もともとのami とcontrailnightly:latest の組み合わせだとvrouter.ko の読み込みが出来なくなっていた、、
https://bugs.launchpad.net/juniperopenstack/+bug/1764537

ifconfig vhost0
docker logs vrouter_vrouter-agent_1
等で確認可能
centos7.5用のami (ami-3185744e) でk8sの手順を実施し、動作することを確認している

2018/7/22 追記
upstream の変更に合わせて記述を修正:
- instance.yaml に global_configuration の定義が必要になった (空の場合も) ので、追記
- TFインストール前に install_k8s.yaml, install_openstack.yaml を実施するようになったため、コマンドを追記
- minimum_diskGB を設定する際のパス, container名が変更になったので追記
- admin-openrc.sh の置き場所が変わったのでパスを修正

k8s

元の資料は以下となる。
https://github.com/Juniper/contrail-ansible-deployer/wiki/Contrail-microservice-installation-with-kubernetes

インスタンス種別としては、t2.large x 2 を使用した。


事前準備として、master ノード上で、以下のコマンドを発行し、パッケージをインストールしておく

# yum -y install epel-release git ansible net-tools

また、master ノードで以下のコマンドを発行し、

# ssh-keygen

できた/root/.ssh/id_rsa.pubを master ノード, slave ノード の /root/.ssh/authorized_keys に追記しておく。

※ あわせて、各ノードでntpも設定しておく。

yum -y install ntp
service ntpd start

上記が終わったら、以下のコマンドで、インストール用のplaybook を流す準備を行っていく。

# git clone https://github.com/Juniper/contrail-ansible-deployer.git
# cd contrail-ansible-deployer
# vi config/instances.yaml
provider_config:
  bms:
   ssh_user: root
   ssh_public_key: /root/.ssh/id_rsa.pub
   ssh_private_key: /root/.ssh/id_rsa
   domainsuffix: local
instances:
  bms1:
   provider: bms
   roles:
      config_database:
      config:
      control:
      analytics_database:
      analytics:
      webui:
      k8s_master:
      kubemanager:
   ip: 172.31.14.34 # masterノードのip
  bms2:
   provider: bms
   roles:
     vrouter:
     k8s_node:
   ip: 172.31.4.240 # slave ノードのip
contrail_configuration:
  CONTAINER_REGISTRY: opencontrailnightly
  CONTRAIL_VERSION: latest
  KUBERNETES_CLUSTER_PROJECT: {}
global_configuration:  ### 2018/7/22追記

この後、以下の3つのplaybook で、インストールが実施される。

ansible-playbook -i inventory/ playbooks/configure_instances.yml
※ 10分ほどかかる
ansible-playbook -e orchestrator=kubernetes -i inventory/ playbooks/install_k8s.yml  ## 2018/7/22 追記
※ 5分ほどかかる
ansible-playbook -e orchestrator=kubernetes -i inventory/ playbooks/install_contrail.yml
※ 20分ほどかかる


動作確認としては、まず、以下のコマンドで、全てのコンポーネントのステータスがactive になっていることを確認する。

# contrail-status

nodemgr: initializing (Disk for DB is too low.)

が表示された場合、以下を実施し、再度 contrail-status を実施する。

docker exec -it config_nodemgr_1 sed -i 's/#minimum_diskGB=4/minimum_diskGB=4/' /etc/contrail/contrail-config-nodemgr.conf
docker restart config_nodemgr_1
docker exec -it analyticsdatabase_nodemgr_1 sed -i 's/#minimum_diskGB=4/minimum_diskGB=4/' /etc/contrail/contrail-database-nodemgr.conf
docker restart analyticsdatabase_nodemgr_1

(2018/7/22追記)
docker exec -it config_database_nodemgr_1 sed -i 's/#minimum_diskGB=4/minimum_diskGB=4/' /entrypoint.sh
docker restart config_database_nodemgr_1
docker exec -it analytics_database_nodemgr_1 sed -i 's/#minimum_diskGB=4/minimum_diskGB=4/' /entrypoint.sh
docker restart analytics_database_nodemgr_1

上記が確認できたら、以下のようにcirrosを投入し、コンテナの起動と gw への疎通が出来ることを確認する。
k8s yaml としては以下を使用: https://github.com/tnaganawa/contrail-k8s-tutorial/tree/master/yml/1_initial

[root@ip-172-31-14-34 1_initial]# kubectl create -f cirros1.yaml 
pod "cirros1" created
[root@ip-172-31-14-34 1_initial]#
[root@ip-172-31-14-34 1_initial]# kubectl create -f cirros2.yaml 
pod "cirros2" created
[root@ip-172-31-14-34 1_initial]#
[root@ip-172-31-14-34 1_initial]# kubectl get pod -o wide
NAME      READY     STATUS    RESTARTS   AGE       IP              NODE
cirros1   1/1       Running   0          1m        10.47.255.251   ip-172-31-4-240.ap-northeast-1.compute.internal
cirros2   1/1       Running   0          12s       10.47.255.250   ip-172-31-4-240.ap-northeast-1.compute.internal
[root@ip-172-31-14-34 1_initial]# 

[root@ip-172-31-14-34 1_initial]# kubectl exec -it cirros1 sh
/ # ip -o a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1\    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
11: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue \    link/ether 02:56:17:0e:4a:4a brd ff:ff:ff:ff:ff:ff
11: eth0    inet 10.47.255.251/12 scope global eth0\       valid_lft forever preferred_lft forever
11: eth0    inet6 fe80::20a8:92ff:fe11:6d80/64 scope link \       valid_lft forever preferred_lft forever
/ # ping 10.47.255.254
PING 10.47.255.254 (10.47.255.254): 56 data bytes
64 bytes from 10.47.255.254: seq=0 ttl=64 time=0.457 ms
64 bytes from 10.47.255.254: seq=1 ttl=64 time=0.196 ms
^C
--- 10.47.255.254 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.196/0.326/0.457 ms
/ # 

合わせて、以下のようなコマンドで contrail webui までの疎通を可能にし、webui が開けることを確認する。(id/pass は admin:contrail123)

ssh -L 8143:127.0.0.1:8143 -i (awsのpem) centos@(master ノードのip)

スクリーンショットはこちら
f:id:aaabbb_200904:20180428214154p:plain

kolla openstack

元の資料は以下となる。
https://github.com/Juniper/contrail-ansible-deployer/wiki/Contrail-with-Kolla-Ocata

k8s の場合とほぼ同じなので、コマンドのみ列記していく。
インスタンス種別としては、t2.2xlarge x 1 を使用した (t2.xlarge だと、メモリ不足で一部のプロセスが起動しなかった、、)

yum -y install epel-release 
yum -y install git ansible-2.4.2.0
yum -y install ntp
service ntpd start
ssh-keygen
cd .ssh/
cat id_rsa.pub >> authorized_keys
cd
git clone http://github.com/Juniper/contrail-ansible-deployer
cd contrail-ansible-deployer
vi config/instances.yaml
(以下を記述)
provider_config:
  bms:
    ssh_pwd: root
    ssh_user: root
    domainsuffix: local
instances:
  bms1:
    provider: bms
    ip: 172.31.2.25 # ec2 インスタンスのip
    roles:
      config_database:
      config:
      control:
      analytics_database:
      analytics:
      webui:
      vrouter:
      openstack:
      openstack_compute:
contrail_configuration:
  RABBITMQ_NODE_PORT: 5673
  AUTH_MODE: keystone
  KEYSTONE_AUTH_URL_VERSION: /v3
kolla_config:
  kolla_globals:
    enable_haproxy: no
  kolla_passwords:
    keystone_admin_password: contrail123 # adminユーザーのパスワード
global_configuration:  ### 2018/7/22追記

ansible-playbook -i inventory/ playbooks/configure_instances.yml
※ 10分ほどかかる
ansible-playbook -i inventory/ playbooks/install_openstack.yml 
※ 20分ほどかかる
ansible-playbook -i inventory/ -e orchestrator=openstack playbooks/install_contrail.yml
※ 10分ほどかかる

インストールが完了したら、aws での環境用に以下の設定変更を実施する。(nested kvm が使用できないことへの対応)

vi /etc/kolla/nova-compute/nova.conf
[libvirt] に以下を追記
virt_type=qemu
cpu_mode=none

docker restart nova_compute

openstack 上にcirros を作成し、ping疎通が取れることを確認する。

yum install -y gcc python-devel wget
pip install python-openstackclient
pip install python-ironicclient

source /etc/kolla/admin-openrc.sh
source /etc/kolla/kolla-toolbox/admin-openrc.sh ## 2018/7/22追記

wget http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img
openstack image create cirros2 --disk-format qcow2 --public --container-format bare --file cirros-0.4.0-x86_64-disk.img                                      
openstack network create testvn
openstack subnet create --subnet-range 192.168.100.0/24 --network testvn subnet1
openstack flavor create --ram 512 --disk 1 --vcpus 1 m1.tiny
NET_ID=`openstack network list | grep testvn | awk -F '|' '{print $2}' | tr -d ' '`
openstack server create --flavor m1.tiny --image cirros2 --nic net-id=${NET_ID} test_vm1
openstack server create --flavor m1.tiny --image cirros2 --nic net-id=${NET_ID} test_vm2

f:id:aaabbb_200904:20180428214448p:plain
f:id:aaabbb_200904:20180428214541p:plain
f:id:aaabbb_200904:20180428214435p:plain

※ horizon/webuiへのアクセスのため、ログイン時に、以下のようなssh port forward を使用した。

ssh -L 8143:127.0.0.1:8143 -L 8080:172.31.2.25:80 -L 6080:172.31.2.25:6080 -i (awsのpem) centos@(masterノードのip)

k8s & kolla openstack

元資料は以下となる。
https://github.com/Juniper/contrail-ansible-deployer/wiki/Deployment-Example:-Contrail-and-Kubernetes-and-Openstack

元々openstack と k8s は、どちらもvxlan 等を使って、ネットワークセグメンテーションを実施しているのだが、両方を一つのcontroller で管理する、という仕組みは、(知る限りでは) 今まで、あまり出てきていなかったように思う。
ただ、仕組み上は、同時に使えた方が便利と思われるので、今後はこちらのような構成が一般的になっていくのかもしれない。

今回は、インスタンスの種別として、 t2.2xlarge x 1 (masterノード用), t2.large x 1 (slaveノード用) を使用した。

コマンドは、kolla openstack の時と同じなので、 config/instances.yaml の中身のみ記載する。
※ playbook が流れきるまで、40分程度 必要となる。

provider_config:
  bms:
    ssh_pwd: root
    ssh_user: root
    domainsuffix: local
instances:
  bms101:
    provider: bms
    ip: 172.31.15.178 # masterノードのip
    roles:
        config_database:
        config:
        control:
        analytics_database:
        analytics:
        webui:
        k8s_master:
        kubemanager:
        openstack:
  bms102:
    provider: bms
    ip: 172.31.4.212 # slaveノードのip
    roles:
        vrouter:
        k8s_node:
        openstack_compute:
global_configuration:
  CONTAINER_REGISTRY: opencontrailnightly
contrail_configuration:
  CONTAINER_REGISTRY: opencontrailnightly
  CONTRAIL_VERSION: latest
  UPGRADE_KERNEL: true
  RABBITMQ_NODE_PORT: 5673
  AUTH_MODE: keystone
  KEYSTONE_AUTH_URL_VERSION: /v3
  KEYSTONE_AUTH_ADMIN_PASSWORD: contrail123 # adminユーザーのパスワード
  CLOUD_ORCHESTRATOR: openstack
kolla_config:
  customize:
    nova.conf: |
      [libvirt]
      virt_type=qemu
      cpu_mode=none
  kolla_globals:
    enable_haproxy: "no"
    enable_ironic: "no"
    enable_swift: "no"


動作確認としても、k8s, kolla openstack 単独の場合と同じで、cirros を作成し、疎通を確かめていく。
作成後の確認結果を載せておく。

(openstack)

[root@ip-172-31-15-178 1_initial]# nova list
+--------------------------------------+------+--------+------------+-------------+---------------+
| ID                                   | Name | Status | Task State | Power State | Networks      |
+--------------------------------------+------+--------+------------+-------------+---------------+
| aa746031-2726-49fc-bcaf-83857ab80325 | vm1  | ACTIVE | -          | Running     | net1=10.1.1.3 |
| 7104659c-870f-4105-a09a-aba33daa47c8 | vm2  | ACTIVE | -          | Running     | net1=10.1.1.4 |
+--------------------------------------+------+--------+------------+-------------+---------------+
[root@ip-172-31-15-178 1_initial]# 

[root@ip-172-31-4-212 ~]# ssh cirros@169.254.0.4
The authenticity of host '169.254.0.4 (169.254.0.4)' can't be established.
ECDSA key fingerprint is SHA256:vQ1pnyAy/vAmsvl5XhF4ukwIdoNx8e/p/RaJIA2sqFw.
ECDSA key fingerprint is MD5:aa:80:67:5a:33:37:d7:7b:73:bb:25:be:b9:12:0e:e5.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '169.254.0.4' (ECDSA) to the list of known hosts.
cirros@169.254.0.4's password: 
Permission denied, please try again.
cirros@169.254.0.4's password: 
$ ip -o a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1\    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000\    link/ether 02:fe:5a:fc:ba:3d brd ff:ff:ff:ff:ff:ff
2: eth0    inet 10.1.1.3/24 brd 10.1.1.255 scope global eth0\       valid_lft forever preferred_lft forever
2: eth0    inet6 fe80::fe:5aff:fefc:ba3d/64 scope link \       valid_lft forever preferred_lft forever
$ 
$ ping 10.1.1.4
PING 10.1.1.4 (10.1.1.4): 56 data bytes
64 bytes from 10.1.1.4: seq=0 ttl=64 time=11.855 ms
64 bytes from 10.1.1.4: seq=1 ttl=64 time=2.957 ms
^C
--- 10.1.1.4 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 2.957/7.406/11.855 ms
$ 


(k8s)

[root@ip-172-31-15-178 1_initial]# kubectl get pod -o wide
NAME      READY     STATUS    RESTARTS   AGE       IP              NODE
cirros1   1/1       Running   0          36s       10.47.255.251   ip-172-31-4-212.ap-northeast-1.compute.internal
cirros2   1/1       Running   0          20s       10.47.255.250   ip-172-31-4-212.ap-northeast-1.compute.internal
[root@ip-172-31-15-178 1_initial]#

[root@ip-172-31-15-178 ~]# kubectl exec -it cirros1 sh
/ # 
/ # ping 10.47.255.250
PING 10.47.255.250 (10.47.255.250): 56 data bytes
64 bytes from 10.47.255.250: seq=0 ttl=63 time=0.552 ms
64 bytes from 10.47.255.250: seq=1 ttl=63 time=0.062 ms
^C
--- 10.47.255.250 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.062/0.307/0.552 ms
/ # 
/ # 
/ # [root@ip-172-31-15-178 ~]# 

合わせて、webui から Tungsten Fabricの状態を確認する。
※ interfaces 欄に openstack, k8s 両方のvmi が記載されていることを確認可能
f:id:aaabbb_200904:20180428214951p:plain
f:id:aaabbb_200904:20180428215002p:plain

まとめ

簡単だが、Tungsten Fabric のインストール方法についてまとめておいた。
これ以外に、ec2インスタンス作成、等も自動で実施する、cloud formationを使った仕組みも用意されている。
すぐに試したい場合には、こちらを使ってみてもよいのではなかろうか。
https://github.com/Juniper/contrail-ansible-deployer/wiki/Deployment-Example:-Click-On-Deployment-of-Contrail-and-Kubernetes-in-AWS

AWS上でのHA構成(k8s)

以下の文書に従い、Tungsten controller の 3-node 構成を試してみたので、その際のメモとなる。(k8sを使用)
https://github.com/Juniper/contrail-docker/wiki/Provision-Contrail-CNI-for-Kubernetes-with-High-Availability-(HA)

事前準備として、t2.large の ubuntu16.04.2 (メモリ8GB, ami-ea4eae8c) を5台用意し、kubeadm を使って master x 1, slave x 4 の構成を用意している.
詳しくは、以下のリンクを参照。
http://aaabbb-200904.hatenablog.jp/entry/2017/10/15/034243

その後、k8sの設定ファイルを環境に合わせて書き換え、kubectl apply で適用する。
修正内容として、以下のように、tungsten のcontroller, analytics, analyticsdb をデプロイするipを変更する、という内容となる。

# git clone https://github.com/Juniper/contrail-docker.git -b R4.0
# cd contrail-docker/kubernetes/manifests/
# vi contrail-host-ubuntu-ha.yaml
(以下の行を修正)
  config_nodes = 10.84.24.52,10.84.24.53,10.84.14.144
  controller_nodes = 10.84.24.52,10.84.24.53,10.84.14.144
  analytics_nodes = 10.84.24.52,10.84.24.53,10.84.14.144
  analyticsdb_nodes = 10.84.24.52,10.84.24.53,10.84.14.144
-
  api_server = 10.84.14.144

また、上記に加えて、tungsten の docker を起動したいslave ノードについては、以下を実行し、起動の許可を出す必要があるのだが、

# kubectl label node <node-name> opencontrail.org/controller=true

実機で試した時には、以下のエラーが発生したため、master ノードでパッケージのアップデートを行い、解消させるようにしている。

# kubectl label node 'ip-172-31-7-231' 'opencontrail.org/controller=true'
The Node "ip-172-31-7-231" is invalid: metadata.labels: Invalid value: "opencontrail.org~1controller": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName',  or 'my.name',  or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')
# 
# apt-get update kubectl
# dpkg -l | grep -i kube
ii  kubeadm                          1.7.4-00                                   amd64        Kubernetes Cluster Bootstrapping Tool
ii  kubectl                          1.10.0-00                                  amd64        Kubernetes Command Line Tool
ii  kubelet                          1.7.4-00                                   amd64        Kubernetes Node Agent
ii  kubernetes-cni                   0.6.0-00                                   amd64        Kubernetes CNI
# kubectl label node 'ip-172-31-7-231' 'opencontrail.org/controller=true'
node "ip-172-31-7-231" labeled
# 

その後、以下のコマンドで DaemonSet の定義を行うと、約10分後に、container の起動が行われた。

# kubectl apply -f contrail-host-ubuntu-ha.yaml

上手く起動すると、以下のURLで、
http://contoller-ip:8080
で、webui にログイン可能となる。
id:pass は、 admin:contrail123
f:id:aaabbb_200904:20180408003203p:plain

なお、こちらで試した際には、controller が起動した slave ノードでは vhost0 がうまく作られず、controller 以外の container が起動出来ない状態になってしまったのだが、一度 os のリブートを行うことで事象が解消し、webui からもvrouter として認識されるようになった。
f:id:aaabbb_200904:20180408003231p:plain

※ 合わせて、念のため、各ノードで以下も実施している

# apt install ntp

Tungstenで、よく確認するソースコード

schema-transformer

tungsten の動作を確認する上で、よく確認するソースコードをあげていこうと思う。

個人的に一番よく眺める、と思っているファイルは、schema-transformer 内の以下のファイルになる。
https://github.com/Juniper/contrail-controller/blob/master/src/config/schema-transformer/config_db.py

schema-transformer は、controller のなかで動いているプロセスの一つで、webui 等で指定される値と、実際にcontrail-control, vrouter 等で使用される値の間の変換を行っている。

例えば、neutronの中で、2つの仮想ネットワーク(以下、VN) 間の間の通信を許可するとき、ポリシー、ルーターなどの要素を作成して、VN間の通信を許可することになる。
ポリシー、ルーターについては、tungsten の中にも同じような設定が存在しており、こちらはcontroller の中で動いているcassandra の中に、一旦情報が保管される。

ただ、実際にcontrail-control が上記をvrouter に伝えるときには、上記の指定は、route-target の指定を使ったものに変換されている。
※ 具体的には、ポリシーは、それぞれのVNが持っているルートターゲットをお互いにインポートしあい、ルーターは、ルーターが持つ共通のroute-target に対して、各VNがroute-target のexport/import を行う、という実装になっている

上記のような変換を行う仕組みが、schema-transformer となっている。
このため、schema-transformer の動作を追うことで、tungsten の uiから指定した値がどのように使われているか、等を確認することが出来る。

※ 例として、ルーターをVNに接続した際の処理はこちら
https://github.com/Juniper/contrail-controller/blob/master/src/config/schema-transformer/config_db.py#L4165


schema-transformer は、通常のポリシー、ルーター以外に、サービスチェインを設定したときの動作も扱っている。
例えば、transparent, in-network, in-network-nat で、どのような違いがあるか、等を確認してみると、おもしろいのではなかろうか。

※ この辺りを参照
https://github.com/Juniper/contrail-controller/blob/master/src/config/schema-transformer/config_db.py#L2919

device-manager

device-manager は、controller の中のプロセスの1つで、物理ルーター、物理スイッチ等に、netconf で設定を投入するプロセスになる。

ソースコードの場所はこちらになる。
https://github.com/Juniper/contrail-controller/blob/master/src/config/device-manager/device_manager/plugins/juniper/juniper_conf.py
https://github.com/Juniper/contrail-controller/blob/master/src/config/device-manager/device_manager/plugins/juniper/mx/mx_conf.py

以前紹介した例だと、vMXを外部ルーターとして設定する場合に、以下のようなコンフィグを投入したのだが、この辺りの処理は、こちらのロジックで動いている。
https://github.com/tnaganawa/contrail-k8s-tutorial/blob/master/vmx-config/vmx-config

例えば、仮想ネットワークに'Extend To Physical Router'を設定すると、VRFの作成と、import/exportポリシーの設定が行われるのだが、その辺りの処理はこちらになる。
https://github.com/Juniper/contrail-controller/blob/master/src/config/device-manager/device_manager/plugins/juniper/mx/mx_conf.py#L247

device-manager には、上記の機能以外にも、BGPの設定を行ったり、ToRスイッチの設定を行う、といった機能もある。
https://github.com/Juniper/contrail-controller/tree/master/src/config/device-manager/device_manager/plugins/juniper/qfx

いろいろと眺めてみるとおもしろいのではなかろうか。

svc-monitor

svc-monitorも controller の中のプロセスの1つで、contrail webui等から設定された値を元に、各種の処理を行う部分になる。
こう書くとschema-transformer とあまり変わらないのだが、実際には svc-monitor では、schema-transformer とは扱う対象が異なっている。
http://juniper.github.io/contrail-vnc/architecture.html

よく出てくる項目としては以下がある。
- load balancer の設定時 (openstack lbaas, k8s service/ingress 等から設定) にhaproxy の立ち上げ等を実施
- snat router の設定時に iptables MASQUERADE のルールを設定

ソースコードの場所でいうと、load balancer 設定時の動作は、以下の場所となる。
https://github.com/Juniper/contrail-controller/tree/master/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers

例えば、haproxy の設定ファイルは、以下のようなロジックで作成されている。
https://github.com/Juniper/contrail-controller/blob/master/src/config/svc-monitor/svc_monitor/services/loadbalancer/drivers/ha_proxy/haproxy_config.py

また、router にsnatを設定した際は、compute上に linux namespace が作成され、iptables の MASQUERADEが設定されることによって、snat が実施されるのだが、その辺りの制御も、svc-monitorが担当している。
https://github.com/Juniper/contrail-controller/blob/master/src/config/svc-monitor/svc_monitor/snat_agent.py#L138

実際にcompute上で作成されるnamespace は以下で指定されているらしい。
https://github.com/Juniper/contrail-controller/blob/master/src/vnsw/opencontrail-vrouter-netns/opencontrail_vrouter_netns/vrouter_netns.py#L106

Contrailクラスタ間で直接GRE接続するには

Contrailのクラスタが複数ある場合に、クラスタ間で直接GRE接続する方法についてまとめてみる。

通常、Contrailのクラスタは1つにしたいところなのだが、2サイトに分かれる場合など、複数クラスタに分かれてしまうケースも考えられる。
この場合もBGPを使ってcontroller間で経路配布を行うことで、クラスタ間で直接、GRE接続を行うことが出来る。
https://github.com/tonyliu0592/opencontrail/wiki/Gateway-MX
ただし、別クラスタで同じサブネットを払い出す場合、IPAMは共有されないため、IPが重複する可能性があるので注意

今回は、aws上に2セットのk8sを構築し、cirros間での疎通を行ってみた。

実施内容は以下となる。
1. BGPの設定
2. 仮想ネットワークの設定
3. cirrosの作成と疎通確認

設定時の出力内容は、以下となる。
> 1. BGPの設定
configure>networking>bgp routersから、対向のcontrollerノードを、External Control Node として追加する。

f:id:aaabbb_200904:20171106011102p:plain
f:id:aaabbb_200904:20171106011115p:plain

> 2. 仮想ネットワークの設定

2については、仮想ネットワークとして、最初から存在するcluster-network を使用した。
この後、ルートターゲットを設定する必要があるのだが、今回はクラスタが分かれているために、'router', 'policy' 等によるルートターゲットインポートを行うことは出来ない。
このため、今回は明示的にvrf-target を設定(65412:11 として、両側に設定)して、経路インポートを行うようにしている。(なお、同じクラスタ内でも、ルートターゲット設定で経路インポートを行うこともできる)
f:id:aaabbb_200904:20171106011230p:plain

> 3. cirrosの作成と疎通確認

クラスタにcirrosを作成する。
※ 一部のcirrosが同じipになってしまったので、別のipが出るまで複数作成している。

root@ip-172-31-3-97:~# kubectl get pod -o wide
NAME      READY     STATUS    RESTARTS   AGE       IP              NODE
cirros1   1/1       Running   0          1m        10.47.255.252   ip-172-31-6-143
cirros2   1/1       Running   0          1m        10.47.255.251   ip-172-31-15-186
cirros3   1/1       Running   0          40s       10.47.255.250   ip-172-31-6-143
root@ip-172-31-3-97:~# 

root@ip-172-31-12-54:~# kubectl get pod -o wide
NAME      READY     STATUS    RESTARTS   AGE       IP              NODE
cirros1   1/1       Running   0          1h        10.47.255.251   ip-172-31-7-27
root@ip-172-31-12-54:~# 

172.31.12.54側のクラスタのルーティングテーブルを確認すると、10.47.255.250 への経路(172.31.6.143, 24)が、BGPで配布されてきていることがわかる。

f:id:aaabbb_200904:20171106011307p:plain

この状態で、172.31.12.54側のcirros1にログインした後、sshで10.47.255.250 にログインし、/tmp/ 以下に適当なファイルを作っておく。

root@ip-172-31-12-54:~# kubectl exec -it cirros1 sh
/ # 
/ # ssh cirros@10.47.255.250

Host '10.47.255.250' is not in the trusted hosts file.
(fingerprint md5 03:b3:c9:16:e1:37:d0:67:9d:55:89:24:22:fa:51:ac)
Do you want to continue connecting? (y/n) y
cirros@10.47.255.250's password: 
$ ip -o a
1: lo:  mtu 65536 qdisc noqueue qlen 1\    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
1: lo    inet 127.0.0.1/8 scope host lo\       valid_lft forever preferred_lft forever
1: lo    inet6 ::1/128 scope host \       valid_lft forever preferred_lft forever
15: eth0@if16:  mtu 1500 qdisc noqueue \    link/ether 02:7d:6d:99:f0:c2 brd ff:ff:ff:ff:ff:ff
15: eth0    inet 10.47.255.250/12 scope global eth0\       valid_lft forever preferred_lft forever
15: eth0    inet6 fe80::28f8:67ff:fe54:91e5/64 scope link \       valid_lft forever preferred_lft forever
$ echo aaa > /tmp/aaa
$ 

この後、172.31.3.97 (先ほどと別のk8sクラスタの、マスターサーバー) から該当のcirrosにログインし、先ほどのファイルが作成されていることを確認することが出来た。

root@ip-172-31-3-97:~# kubectl exec -it cirros3 sh
/ # cat /tmp/aaa 
aaa
/ # 

なお、今回はkubernetes間での疎通を行ったのだが、仕組み上は、k8s以外にvsphere, openstack 等との間でも経路やりとりができるはずである。
複数のオーケストレーター内でネットワーク分離を行いつつ、必要に応じて直接疎通を行う、という場合には、役に立つのではなかろうか。

kubernetes連携時の動作2

前回に引き続き、contrailとkubernetesを組み合わせた場合の動作の確認となる。
https://github.com/Juniper/contrail-controller/wiki/Kubernetes

ingress

ingress はservice と違い、L7でのロードバランスを行う仕組みとなる。
contrail 使用時は、各スレーブノード内にhaproxyが起動し、振り分けを行う動作となる。

サンプルのyamlは以下を参照。
https://github.com/tnaganawa/contrail-k8s-tutorial/tree/master/yml/3_contrail-cni-features/3_ingress

順に定義していくと、以下のように、ingresssvc に対してアタッチされる動作となる。

root@ip-172-31-3-97:~# kubectl get pod -o wide
NAME                                READY     STATUS    RESTARTS   AGE       IP              NODE
cirros-pod                          1/1       Running   1          1d        10.47.255.252   ip-172-31-6-143
nginx-deployment-1286893921-hjkth   1/1       Running   0          6m        10.47.255.252   ip-172-31-6-143
nginx-deployment-1286893921-tbkhk   1/1       Running   0          6m        10.47.255.251   ip-172-31-15-186

root@ip-172-31-3-97:~# kubectl get deployment -o wide
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINER(S)   IMAGE(S)      SELECTOR
nginx-deployment   2         2         2            2           6m        nginx          nginx:1.7.9   app=nginx-deployment

root@ip-172-31-3-97:~# kubectl get svc -o wide
NAME         CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE       SELECTOR
kubernetes   10.96.0.1              443/TCP   21d       
nginx-svc    10.97.170.21           80/TCP    6m        app=nginx-deployment

root@ip-172-31-3-97:~# kubectl get ing -o wide
NAME            HOSTS     ADDRESS         PORTS     AGE
nginx-ingress   *         10.47.255.250   80        6m

pod内にログインして、curl実行することで、urlが提供されていることが分かる。

root@ip-172-31-3-97:~# kubectl exec -it cirros-pod sh
/ # 
/ # curl 10.47.255.250
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
(snip)

なお、ルーティングテーブルを確認してみると、該当のIPは各スレーブノードへの経路が配布されており、行き先は、特定のlinux namespaceとなっている。
f:id:aaabbb_200904:20171106003752p:plain
f:id:aaabbb_200904:20171106003759p:plain

スレーブノードにログインして確認 (agentのdocker内でnetnsが作成されている) してみると、以下のようにhaproxyが起動されていることが分かる。

root@ip-172-31-15-186(agent):/# ip netns                                                                                   
RTNETLINK answers: Invalid argument
RTNETLINK answers: Invalid argument
vrouter-8e2c207e-8799-4422-8f3c-631635e67e03:aab44187-c1f9-11e7-8cc3-06b5b8f5fd22
root@ip-172-31-15-186(agent):/#    

root@ip-172-31-15-186(agent):/# ps -ef | cat
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 07:12 ?        00:00:00 /lib/systemd/systemd systemd.unit=multi-user.target
root        19     1  0 07:12 ?        00:00:00 /lib/systemd/systemd-journald
root       814     1  2 07:12 ?        00:00:37 /usr/bin/contrail-vrouter-agent
contrail   821     1  0 07:12 ?        00:00:05 /usr/bin/python /usr/bin/contrail-nodemgr --nodetype=contrail-vrouter
message+   920     1  0 07:12 ?        00:00:00 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
haproxy   2898     1  0 07:19 ?        00:00:00 haproxy -f /var/lib/contrail/loadbalancer/haproxy/aab44187-c1f9-11e7-8cc3-06b5b8f5fd22/haproxy.conf -p /var/lib/contrail/loadbalancer/haproxy/aab44187-c1f9-11e7-8cc3-06b5b8f5fd22/haproxy.pid -sf 2887
root      3451     0  0 07:34 ?        00:00:00 bash
root      3802  3451  0 07:43 ?        00:00:00 ps -ef
root      3803  3451  0 07:43 ?        00:00:00 cat
root@ip-172-31-15-186(agent):/# 

haproxy自体は各スレーブノードで起動されているようなので、スレーブノードが全て停止しない限り、ingressは継続出来るようである。

namespace

通常、kubernetes でnamespace を設定した場合、

  • kubectl の表示対象を分離する
  • kube-dnsドメイン名としてnamespace の名前が使用される

等の違いはあるが、ネットワーク的には分離されない状況となる。

contrail 使用時も、この動作に変化はないが、追加の機能として、namespace 作成時に以下のannotation を設定することで、互いに疎通が取れないnamespace を作成することが出来るようになる。
※ 内部的には namespace の名前を持つ仮想ネットワークが作成される

  annotations: {
   "opencontrail.org/isolation" : "true"
  }

yamlのサンプルは以下を参照。
https://github.com/tnaganawa/contrail-k8s-tutorial/tree/master/yml/3_contrail-cni-features/4_namespace

作成を行うと、以下のようにnamespaceが作成される。

root@ip-172-31-3-97:~# kubectl get ns -o wide
NAME          STATUS    AGE
default       Active    21d
kube-public   Active    21d
kube-system   Active    21d
myns1         Active    8m
myns2         Active    6m

root@ip-172-31-3-97:~# kubectl get pod -n myns1 -o wide
NAME           READY     STATUS    RESTARTS   AGE       IP              NODE
cirros-myns1   1/1       Running   0          2m        10.47.255.252   ip-172-31-15-186
root@ip-172-31-3-97:~# kubectl get pod -n myns2 -o wide
NAME           READY     STATUS    RESTARTS   AGE       IP              NODE
cirros-myns2   1/1       Running   0          11s       10.47.255.250   ip-172-31-6-143
root@ip-172-31-3-97:~# 

※ namespace内にpodを作る場合、以下のように、作成時にnamespaceを指定する必要があるので注意

# kubectl create -n myns1 -f cirros-myns1.yaml 
pod "cirros-myns1" created


この状態で、myns1内のpodに入り、myns2内のpodにpingを打つと、pingが飛ばず、ネットワークが分離されていることが確認できる。
複数のシステムを扱い、システム間で互いに疎通できないようにしたい場合に、活用できるのではなかろうか。

root@ip-172-31-3-97:~# kubectl exec -it -n myns1 cirros-myns1 sh
/ # ping 10.47.255.250
PING 10.47.255.250 (10.47.255.250): 56 data bytes
^C
--- 10.47.255.250 ping statistics ---
2 packets transmitted, 0 packets received, 100% packet loss

kubernetes連携時の動作1

contrail と kubernetes を組み合わせた場合の動作について、ざっくりとまとめておく。
詳細はこちらを参照。
https://github.com/Juniper/contrail-controller/wiki/Kubernetes

使用したyamlについては、こちらにまとめておく。
https://github.com/tnaganawa/contrail-k8s-tutorial/tree/master/yml/3_contrail-cni-features

pod, deployment

podは、kubernetes 内で実際にアプリの処理を行う部分で、複数のコンテナで構成される。
deploymentは、同じpodを複数まとめて立ち上げる仕組みで、スケールアウト等に用いられる。
※ 特に通常のkubernetes の動作と変わりがないので、詳しくはこちら等を参照。

https://kubernetes.io/docs/concepts/workloads/pods/pod/
https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

service

service はpod にアクセスを行うための、ロードバランサーのようなものになる。
contrail では、type: ClusterIP (pod間の通信向け) と type: LoadBalancer (外部からの通信向け) が使用できる。

contrailを使用した場合、複数のpodを持つdeployment に対してserviceを定義すると、各vrouter (外部ルーターがある場合、そちらにも) にnext-hopが2つ設定され、ECMPでの負荷分散が行われる動作になる。

実際 clusterip で定義を行ってみて、内部のpodから確認してみたときの出力は以下となる。
2台のノード上にあるdeploymentに対して、ssh 用のsvc (ip: 10.96.205.1) を定義しているのだが、この場合、contrail内のルーティングテーブルには、svc用に、2つのnext-hop(172.31.6.143, 24と172.31.15.186, 60)が定義されている。
f:id:aaabbb_200904:20171104020938p:plain


kubernetes 側から確認した場合、以下のように、deploymentに対応したpod2つと、svc 1つが定義されている状況となる。

root@ip-172-31-3-97:~# kubectl get pod -o wide
NAME                                READY     STATUS    RESTARTS   AGE       IP              NODE
cirros-deployment-899733556-cj3jp   1/1       Running   0          1h        10.47.255.246   ip-172-31-15-186
cirros-deployment-899733556-tlr0m   1/1       Running   0          1h        10.47.255.247   ip-172-31-6-143
cirros-pod                          1/1       Running   0          18s       10.47.255.252   ip-172-31-6-143
root@ip-172-31-3-97:~# 

root@ip-172-31-3-97:~# kubectl get deployment -o wide
NAME                DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINER(S)   IMAGE(S)   SELECTOR
cirros-deployment   2         2         2            2           1h        cirros         cirros     app=cirros-deployment
root@ip-172-31-3-97:~# 

root@ip-172-31-3-97:~# kubectl get svc -o wide
NAME               CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE       SELECTOR
cirros-clusterip   10.96.205.1           22/TCP    6s        app=cirros-deployment
kubernetes         10.96.0.1             443/TCP   19d       
root@ip-172-31-3-97:~# 

podにログインした後、何度かsshを発行すると、以下のように別の鍵が返ってきて、負荷分散が行われていることが分かる。

root@ip-172-31-3-97:~# kubectl exec -it cirros-pod sh
/ # ssh 10.96.205.1

Host '10.96.205.1' is not in the trusted hosts file.
(fingerprint md5 8b:31:ad:50:47:de:8d:b3:9a:3a:e6:da:b9:e5:f8:7a)
Do you want to continue connecting? (y/n) 
ssh: Connection to root@10.96.205.1:22 exited: Didn't validate host key
/ # ssh 10.96.205.1

Host '10.96.205.1' is not in the trusted hosts file.
(fingerprint md5 bb:ac:0e:ea:1e:a9:41:57:dc:e0:04:59:f7:49:b5:23)
Do you want to continue connecting? (y/n) 
ssh: Connection to root@10.96.205.1:22 exited: Didn't validate host key


ノード上でvrouterのflow をダンプしてみた結果は以下となり、 vrouter上で 10.96.205.1 向けの通信について、DNAT が発行されていることが分かる。

root@ip-172-31-15-186(agent):/# flow -l
Flow table(size 80609280, entries 629760)

Entries: Created 639 Added 639 Deleted 1020 Changed 1020 Processed 639 Used Overflow entries 0
(Created Flows/CPU: 639)(oflows 0)

Action:F=Forward, D=Drop N=NAT(S=SNAT, D=DNAT, Ps=SPAT, Pd=DPAT, L=Link Local Port)
 Other:K(nh)=Key_Nexthop, S(nh)=RPF_Nexthop
 Flags:E=Evicted, Ec=Evict Candidate, N=New Flow, M=Modified Dm=Delete Marked
TCP(r=reverse):S=SYN, F=FIN, R=RST, C=HalfClose, E=Established, D=Dead

    Index                Source:Port/Destination:Port                      Proto(V)
 -----------------------------------------------------------------------------------
   138600<=>147932       10.47.255.246:22                                    6 (2->2)
                         10.47.255.252:57490
(Gen: 1, K(nh):60, Action:N(S), Flags:, TCP:SSrEEr, QOS:-1, S(nh):60, 
 Stats:8/1458,  SPort 55502, TTL 0, Sinfo 11.0.0.0)

   147932<=>138600       10.47.255.252:57490                                 6 (2->2)
                         10.96.205.1:22   
(Gen: 1, K(nh):60, Action:N(D), Flags:, TCP:SSrEEr, QOS:-1, S(nh):65, 
 Stats:8/1022,  SPort 60991, TTL 0, Sinfo 172.31.6.143)

(snip)

※ flow -l の詳しい読み方は以下を参照。
https://www.juniper.net/documentation/en_US/contrail3.2/topics/task/configuration/vrouter-cli-utilities-vnc.html#jd0e369



type: LoadBalancer では、上記の動作に加えて、外部から疎通するためのIPが払い出される動作になるが、こちらを定義する場合、事前にexternalのネットワークから、floating-ipを取得できるようにしておく必要がある。
作業としては以下の順番で実施する。
1. configure>networking>networks から適当な仮想ネットワークを作成 (public-network1, 10.0.101.0/24 で作成) し、advanced options の external にチェックをつける。
2. configure>networking>floating ips でfloating-ipの作成を行い、取得元のvnとして、1で作成したネットワークを指定する。
3. 2で取得したfloating-ip を、contrail-kube-manager に設定する

※ 3 の設定方法は以下となる。

(マスター上で実施)
# kubectl exec -it -n kube-system contrail-kube-manager-xxxxx bash
# vi /etc/contrail/kube-manager.conf
(以下を追記)
 -public_fip_pool = {}
 +public_fip_pool = {'domain': 'default-domain', 'project': 'default', 'network': 'public-network1', 'name': 'default' }

# systemctl restart contrail-kube-manager.service

上記実施後、deployment に対して、service を定義すると、external ip としてipが払い出され、該当ipがvMXに配布されることが確認出来る。
※ 今回は 10.0.101.5 として払い出されている

root@ip-172-31-3-97:~# kubectl get svc -o wide
NAME                  CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE       SELECTOR
cirros-clusterip      10.96.205.1             22/TCP         14m       app=cirros-deployment
cirros-loadbalancer   10.101.93.103   ,10.0.101.5   22:30344/TCP   21s       app=cirros-deployment
kubernetes            10.96.0.1               443/TCP        19d       
root@ip-172-31-3-97:~#

実際にvMXに連携用の設定を入れた際の、show route は以下となった。

_contrail_public-network1-l3-1.inet.0: 5 destinations, 7 routes (5 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
  1. = Active Route, - = Last Active, * = Both
0.0.0.0/0 *[Static/5] 02:20:39 to table inet.0 10.0.101.0/24 *[Static/5] 02:20:39 Discard 10.0.101.3/32 *[BGP/170] 00:00:06, MED 100, localpref 200, from 172.31.3.97 AS path: ? validation-state: unverified, > via gr-0/0/0.32772, Push 23 10.0.101.5/32 @[BGP/170] 00:00:14, MED 100, localpref 200, from 172.31.3.97 AS path: ? validation-state: unverified, > via gr-0/0/0.32769, Push 24 [BGP/170] 00:00:39, MED 100, localpref 200, from 172.31.3.97 AS path: ? validation-state: unverified, > via gr-0/0/0.32772, Push 60 #[Multipath/255] 00:00:14, metric 100, metric2 0 via gr-0/0/0.32769, Push 24 > via gr-0/0/0.32772, Push 60

配布された経路のnext-hop は、2つのmplsラベル となっており、それぞれdeployment で作成されたpodのラベルに対応している。
このため、external-router を通過した通信が、直接別のスレーブノードに分散されて、負荷分散が行われることになる。

使用したvMXコンフィグは以下を参照(一部、multipathの設定を追加する必要があった)。
https://github.com/tnaganawa/contrail-k8s-tutorial/blob/master/vmx-config/vmx-config-k8s-ecmp-loadbalance

稼働確認用のインスタンスから確認してみたところ、こちらも、以下のように異なった鍵が返っており、外部からアクセスする場合も、負荷分散が行われていることが確認出来た。

[root@ip-172-31-9-34 ~]# ssh 10.0.101.5
The authenticity of host '10.0.101.5 (10.0.101.5)' can't be established.
RSA key fingerprint is SHA256:rbD/ry5jzyMDiqR/EYljBE0PZCG/jmBrhtSTEUvkqUs.
RSA key fingerprint is MD5:bb:ac:0e:ea:1e:a9:41:57:dc:e0:04:59:f7:49:b5:23.
Are you sure you want to continue connecting (yes/no)? 
Host key verification failed.
[root@ip-172-31-9-34 ~]# ssh 10.0.101.5
The authenticity of host '10.0.101.5 (10.0.101.5)' can't be established.
RSA key fingerprint is SHA256:pVn6/oc05FNLEpZ5djBX4+gqh2D/fiBClMQ1DbXUsK0.
RSA key fingerprint is MD5:8b:31:ad:50:47:de:8d:b3:9a:3a:e6:da:b9:e5:f8:7a.
Are you sure you want to continue connecting (yes/no)? 
Host key verification failed.
[root@ip-172-31-9-34 ~]# 

通常、service を提供する場合、haproxy等が使われる場合が多いかとは思うが、contrail を使う場合、上記のようにルーターから直接割り振りが行われるため、状況によっては、構成が簡単になるのではなかろうか。

opencontrailでのexternal-router経由のサービスチェイン

前回の続きで、今回はexternal-router(以下、vMX)を通過するトラフィックに対して、サービスチェインを構成する場合の設定となる。
http://aaabbb-200904.hatenablog.jp/entry/2017/10/29/223844

前回、前々回はnest kvmを使用するため、gcp環境を使ったのだが、今回はvMXも使いたかった関係で、ハイパーバイザとしてqemuを使用して、aws環境での構築を行った。
※ ubuntu16.04.2(ami-ea4eae8c), t2.large 3台を使用
手順は前回と同じだが、追加で、以下の作業を実施している。

1. computeにログイン
2. 以下を実施
# docker exec -it -u root nova_compute bash
 # apt-get update
 # apt-get install vim-common
 # vi /etc/nova/nova.conf
  [libvirt] の節に、
  virt_type=qemu
 を追記
# reboot

※ なお、前回発生した、169.254関係の事象は、今回は発生しなかった、、


このあと本題の、vMXを通過するトラフィックについてサービスチェインを行う場合の設定なのだが、以下の順序で設定を行う必要がある。
1. contrail 上に、ルートターゲットを持ったvn2つ (以下、vn1, vn2、ルートターゲットはそれぞれ 64512:1, 64512:2 で設定) を作成する
2. vMXに上記のルートターゲットを設定した2つのVRF, および、該当VRFに入るためのfirewall filter を定義する
3. 2で作成したvnの間でサービスチェインを設定する

なお、vMX上で該当のVRFに入る条件、および出る条件については、今回はvpcの構成上、ge-0/0/0 のみが一つのサブネットに存在するような構成にしていたため、稼働確認用のインスタンス2台 (以下、pc1, pc2) を用意し、それぞれのソースアドレスで、vn1, vn2に出入りするように設定しておいた。
※ それぞれfirewall filterと VRF内のrouting-options で設定

最終的なvmxコンフィグは以下となる。
https://github.com/tnaganawa/contrail-k8s-tutorial/blob/master/vmx-config/vmx-config-for-service-chaining

contrail-controllerのIP: 172.31.4.76
computeノードのIP: 172.31.10.155
vMX ge-0/0/0のIP: 172.31.10.57
pc1のIP: 172.31.9.34
pc2のIP: 172.31.8.198

また、サービスチェイン設定前後の変化の確認用に、上記 2, 3 のタイミングでの、vmx 内 vn1, vn2 の、ルーティングテーブルを載せておく。
2, 3を比べると、サービスチェインの効果で、vn1のルーティングテーブルがvn2にコピー、vn2のルーティングテーブルがvn1にコピーされ、コピー後のnext-hopがサービスインスタンスのmplsラベルになっていることがわかる (前回同様、該当のmplsラベル (30, 34) が、サービスインスタンスのものとなっている)

(サービスチェイン前)
vn1.inet.0: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
  1. = Active Route, - = Last Active, * = Both
10.0.1.3/32 *[BGP/170] 00:09:34, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 26 10.0.1.4/32 *[BGP/170] 00:09:34, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 30 10.0.1.5/32 *[BGP/170] 00:09:34, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 30 172.31.9.34/32 *[Static/5] 00:08:24 to table inet.0 vn2.inet.0: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
  1. = Active Route, - = Last Active, * = Both
10.0.2.3/32 *[BGP/170] 00:05:54, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 22 10.0.2.4/32 *[BGP/170] 00:05:54, MED 200, localpref 100, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 34 10.0.2.5/32 *[BGP/170] 00:05:54, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 34 172.31.8.198/32 *[Static/5] 00:05:55 to table inet.0 (サービスチェイン後) vn1.inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
  1. = Active Route, - = Last Active, * = Both
10.0.1.3/32 *[BGP/170] 00:07:49, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 26 10.0.1.4/32 *[BGP/170] 00:07:49, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 30 10.0.1.5/32 *[BGP/170] 00:07:49, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 30 10.0.2.3/32 *[BGP/170] 00:07:49, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 30 10.0.2.4/32 *[BGP/170] 00:07:49, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 30 10.0.2.5/32 *[BGP/170] 00:07:49, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 30 172.31.8.198/32 *[BGP/170] 00:04:09, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 30 172.31.9.34/32 *[Static/5] 00:06:39 to table inet.0 vn2.inet.0: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
  1. = Active Route, - = Last Active, * = Both
10.0.1.3/32 *[BGP/170] 00:04:09, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 34 10.0.1.4/32 *[BGP/170] 00:04:09, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 34 10.0.1.5/32 *[BGP/170] 00:04:09, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 34 10.0.2.3/32 *[BGP/170] 00:04:09, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 22 10.0.2.4/32 *[BGP/170] 00:04:09, MED 200, localpref 100, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 34 10.0.2.5/32 *[BGP/170] 00:04:09, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 34 172.31.8.198/32 *[Static/5] 00:04:10 to table inet.0 172.31.9.34/32 *[BGP/170] 00:04:09, MED 100, localpref 200, from 172.31.4.76 AS path: ?, validation-state: unverified > via gr-0/0/0.32771, Push 34


更に、pc1, pc2, 1-to-2 (サービスインスタンス) に対して、以下のルートを設定することで、pc1, pc2間で、1-to-2 を通過してpingが飛ぶことを確認出来た。(1-to-2 上で sysctl -w net.ipv4.ip_forward=0 で、pingが飛ばなくなることを確認)

(pc1)
ip route add 172.31.8.198/32 via 172.31.10.57

(pc2)
ip route add 172.31.9.34/32 via 172.31.10.57

(1-to-2)
ip route add 172.31.9.34/32 via 10.0.1.1
ip route add 172.31.8.198/32 via 10.0.2.1

f:id:aaabbb_200904:20171103020559p:plain


上記の例では、1つのサービスインスタンスだけを挟んでいるが、前回同様、複数のサービスインスタンスを挟むことも出来る。
また、firewall filterを工夫することで、適当なソースアドレス、行き先アドレスについてのみ、サービスを適用する、等も可能となる。
ルーターを通過するトラフィックごとに、適用するサービスを制御したい場合には、役にたつのではなかろうか。