Linux Advent Calendar 21日目を震えながら書きます。 @udzura です。普段は昨日紹介した Haconiwa などのようにLinuxコンテナランタイムやRubyを書いたりしています。正確には、雰囲気で書いています。
今日は、LinuxでOverlay networkを実現するミドルウェア、flannelの簡単な使い方と、コンテナとの連携の仕方、そしてvxlanのパケットの様子などのメモを書いていこうと思います。
flannelとは
CoreOS社の開発している simple and easy
な Overlay networkのためのミドルウェアです。
もともとはCoreOS(現Container Linux)自身で使うために開発されていた記憶があるのですが、現在はKubernetes と連携するためというところが第一のゴールのようで、普通はkubectlコマンド経由でセットアップするようです。
今回は、挙動などを把握して色々実験するためにも、手動でコマンドを叩きつつ組んでみます。
環境
Vagrantを使って漠然と3台を立てます。
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/zesty64"
config.vm.provider "virtualbox" do |vbox|
vbox.cpus = 2
end
config.vm.define 'flannel-1' do |c|
c.vm.hostname = 'flannel-1'
c.vm.network "private_network", ip: "192.168.254.10"
end
config.vm.define 'flannel-2' do |c|
c.vm.hostname = 'flannel-2'
c.vm.network "private_network", ip: "192.168.254.11"
end
config.vm.define 'flannel-3' do |c|
c.vm.hostname = 'flannel-3'
c.vm.network "private_network", ip: "192.168.254.12"
end
end
$ vagrant up
ディストロは Ubuntu Zesty 64bit, カーネルは linux-image-generic 4.10.0.37.37
だそうです。
動作に必要な etcd のセットアップ
flannelはサブネットなどの情報を、etcdをバックエンドとして保持するので、先にインストールしてクラスタを組んでおく必要があります。etcd自体はパッケージのもので十分です。
$ sudo apt install etcd
ホスト flannel-[1,2,3]
について、 /etc/default/etcd
ファイルを以下のようにします。
ETCD_NAME="flannel-1"
ETCD_INITIAL_CLUSTER="flannel-1=http://192.168.254.10:2380,flannel-2=http://192.168.254.11:2380,flannel-3=http://192.168.254.12:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_LISTEN_PEER_URLS="http://192.168.254.10:2380,http://192.168.254.10:7001"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.254.10:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.254.10:2380"
ETCD_NAME="flannel-2"
ETCD_INITIAL_CLUSTER="flannel-1=http://192.168.254.10:2380,flannel-2=http://192.168.254.11:2380,flannel-3=http://192.168.254.12:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_LISTEN_PEER_URLS="http://192.168.254.11:2380,http://192.168.254.11:7001"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.254.11:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.254.11:2380"
ETCD_NAME="flannel-3"
ETCD_INITIAL_CLUSTER="flannel-1=http://192.168.254.10:2380,flannel-2=http://192.168.254.11:2380,flannel-3=http://192.168.254.12:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_LISTEN_PEER_URLS="http://192.168.254.12:2380,http://192.168.254.12:7001"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.254.12:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.254.12:2380"
この状態で、可能なら3つターミナルを立ててそれぞれsystemctl経由で再起動すればOKです。うまくいかない場合は /var/lib/etcd/default/*
にあるデータを消していきましょう。
$ sudo systemctl restart etcd
正直etcdのクラスタ組むのが結構大変ですが、本筋ではないのでさらっと作りました...。 こちらのQiita記事は参考になりました。
flannel のインストールと起動
etcdが入れば、どこのホストからでもいいので、先にflanneldが利用する設定を入れます。
$ etcdctl set /coreos.com/network/config \
'{ "Network": "10.254.0.0/16", "Backend": {"Type": "vxlan"}}'
その後、それぞれのホストで以下のようにざっくりフランネルさんたちをを立ち上げます。
$ wget https://github.com/coreos/flannel/releases/download/v0.9.1/flanneld-amd64 && \
chmod a+x ./flanneld-amd64 && \
sudo nohup sh -c './flanneld-amd64 -iface enp0s8 1>/var/log/flanneld.log 2>&1' &
するとそれぞれのホストに flannel.1
なるデバイスが生えます。それぞれのIPは、/24の範囲かつ10.254.0.0/16
のサブネットワークのはず。
6: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default
link/ether 5e:19:70:99:41:64 brd ff:ff:ff:ff:ff:ff
inet 10.254.1.0/32 scope global flannel.1
valid_lft forever preferred_lft forever
inet6 fe80::5c19:70ff:fe99:4164/64 scope link
valid_lft forever preferred_lft forever
割り当たっている様子は以下のようにも確認できます。
ubuntu@flannel-3:~$ etcdctl ls /coreos.com/network/subnets/
/coreos.com/network/subnets/10.254.67.0-24
/coreos.com/network/subnets/10.254.1.0-24
/coreos.com/network/subnets/10.254.69.0-24
なんかIPがかぶってる、などの場合 /var/run/flannel/subnet.env
というファイルを消すといいかもしれない。
このvxlanデバイスは、各ホストでお互いに疎通出来ます。pingを打ったりして満足しましょう。
ubuntu@flannel-1:~$ ip route
default via 10.0.2.2 dev enp0s3
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15
10.254.1.0/24 via 10.254.1.0 dev flannel.1 onlink <- !!
10.254.69.0/24 via 10.254.69.0 dev flannel.1 onlink <- !!
192.168.254.0/24 dev enp0s8 proto kernel scope link src 192.168.254.10
ubuntu@flannel-1:~$ ping 10.254.69.0
PING 10.254.69.0 (10.254.69.0) 56(84) bytes of data.
64 bytes from 10.254.69.0: icmp_seq=1 ttl=64 time=0.399 ms
64 bytes from 10.254.69.0: icmp_seq=2 ttl=64 time=0.469 ms
コンテナにくっつける
コンテナランタイムにDockerを使う場合、ひとまずこちらもパッケージで入れてしまい、以下のように設定を変えます。
$ sudo apt install docker.io
# ExecStart を置き換えるのでDrop-Inはうまく動かず、Unitファイルを直接上書き作成...
$ sudo vim /etc/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network.target docker.socket firewalld.service
Requires=docker.socket
[Service]
Type=notify
EnvironmentFile=/run/flannel/subnet.env
ExecStart=/usr/bin/dockerd --bip $FLANNEL_SUBNET --mtu $FLANNEL_MTU -H fd:// $DOCKER_OPTS
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
TimeoutStartSec=0
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker.service
dockerdがこんな様子のオプションで立ち上がっていれば成功で、ブリッジもできています。
/usr/bin/dockerd --bip 10.254.69.1/24 --mtu 1450 -H fd://
\_ containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock
ubuntu@flannel-3:~$ ip a s docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:51:f1:38:f5 brd ff:ff:ff:ff:ff:ff
inet 10.254.1.1/24 scope global docker0
valid_lft forever preferred_lft forever
適当な軽めのイメージに sh -l
で入って、相互にpingしたりして遊べます。
$ sudo docker run -ti debian:latest /bin/sh -l
## flannel-1
# ip a s eth0
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 02:42:0a:fe:43:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.254.67.2/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:aff:fefe:4302/64 scope link
valid_lft forever preferred_lft forever
## flannel-3
# ip a s eth0
8: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
link/ether 02:42:0a:fe:01:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.254.1.2/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:aff:fefe:102/64 scope link
valid_lft forever preferred_lft forever
# ping 10.254.67.2
PING 10.254.67.2 (10.254.67.2): 56 data bytes
64 bytes from 10.254.67.2: icmp_seq=0 ttl=62 time=0.651 ms
64 bytes from 10.254.67.2: icmp_seq=1 ttl=62 time=0.537 ms
64 bytes from 10.254.67.2: icmp_seq=2 ttl=62 time=0.411 ms
もちろん自分でbridgeを作り、vethを引っ張って自作Linux Namespaceで相互pingも出来ます。楽しいですね。
パケットキャプチャをしてみる
だいぶ長くなったので、最後に実際にこのように作ったOverlay networkについてもう少しだけ(本当に少しだけ)詳しく眺めてみようと思います。
ネットワーク...眺める... ということで安易ですがtcpdumpを使います。
「ホストをまたいで」、あるコンテナから別のコンテナにpingを送り続けておきます。
# ping 10.254.1.2
この状態で、対向側のホストにtcpdumpを仕掛けます。この時、vxlanデバイスやブリッジを監視すると、普通に流れているのが見えます。
ubuntu@flannel-3:~$ sudo tcpdump -i flannel.1 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on flannel.1, link-type EN10MB (Ethernet), capture size 262144 bytes
11:42:34.197889 IP flannel-1 > 10.254.1.2: ICMP echo request, id 13, seq 41, length 64
11:42:34.197942 IP 10.254.1.2 > flannel-1: ICMP echo reply, id 13, seq 41, length 64
11:42:35.199681 IP flannel-1 > 10.254.1.2: ICMP echo request, id 13, seq 42, length 64
11:42:35.199733 IP 10.254.1.2 > flannel-1: ICMP echo reply, id 13, seq 42, length 64
11:42:36.200875 IP flannel-1 > 10.254.1.2: ICMP echo request, id 13, seq 43, length 64
11:42:36.200922 IP 10.254.1.2 > flannel-1: ICMP echo reply, id 13, seq 43, length 64
11:42:37.202760 IP flannel-1 > 10.254.1.2: ICMP echo request, id 13, seq 44, length 64
11:42:37.202823 IP 10.254.1.2 > flannel-1: ICMP echo reply, id 13, seq 44, length 64
11:42:38.208945 IP flannel-1 > 10.254.1.2: ICMP echo request, id 13, seq 45, length 64
11:42:38.209012 IP 10.254.1.2 > flannel-1: ICMP echo reply, id 13, seq 45, length 64
ですが、その上におそらく通るであろうデバイス、Vagrantのプライベートネットワークに割り振られた enp0s8
を見てみても、icmpのパケットが流れません。
ubuntu@flannel-3:~$ sudo tcpdump -i enp0s8 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
^C
0 packets captured
0 packets received by filter
0 packets dropped by kernel
どうすれば見えるかというと、udpの8472番を覗いてみると何かが流れています。
ubuntu@flannel-3:~$ sudo tcpdump -i enp0s8 udp port 8472
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s8, link-type EN10MB (Ethernet), capture size 262144 bytes
11:46:35.783729 IP flannel-1.48158 > flannel-3.8472: OTV, flags [I] (0x08), overlay 0, instance 1
IP flannel-1 > 10.254.1.2: ICMP echo request, id 13, seq 282, length 64
11:46:35.783881 IP flannel-3.49529 > flannel-1.8472: OTV, flags [I] (0x08), overlay 0, instance 1
IP 10.254.1.2 > flannel-1: ICMP echo reply, id 13, seq 282, length 64
11:46:36.784927 IP flannel-1.48158 > flannel-3.8472: OTV, flags [I] (0x08), overlay 0, instance 1
IP flannel-1 > 10.254.1.2: ICMP echo request, id 13, seq 283, length 64
11:46:36.785069 IP flannel-3.49529 > flannel-1.8472: OTV, flags [I] (0x08), overlay 0, instance 1
IP 10.254.1.2 > flannel-1: ICMP echo reply, id 13, seq 283, length 64
11:46:37.786588 IP flannel-1.48158 > flannel-3.8472: OTV, flags [I] (0x08), overlay 0, instance 1
IP flannel-1 > 10.254.1.2: ICMP echo request, id 13, seq 284, length 64
11:46:37.786677 IP flannel-3.49529 > flannel-1.8472: OTV, flags [I] (0x08), overlay 0, instance 1
IP 10.254.1.2 > flannel-1: ICMP echo reply, id 13, seq 284, length 64
11:46:38.788530 IP flannel-1.48158 > flannel-3.8472: OTV, flags [I] (0x08), overlay 0, instance 1
IP flannel-1 > 10.254.1.2: ICMP echo request, id 13, seq 285, length 64
udpなのに ICMP echo request
と出ていて、vxlanなのでホストをまたぐ時にもともとのICMPイーサネットフレームをUDP/IPでカプセル化してくれているわけですが、tcpdumpでその中身を見せているというわけですね。
正直、 flannel がどのようにvxlanバックエンドをやっていっているかは 中井さんの記事 の方が詳細でわかりやすく、パケットのヘッダがどんな感じに追加されてカプセル化されているかは こちらの記事 が詳しいので、この記事はflannelのセットアップ手順でしかなかった...。
そんな感じなのですが、KubernetesやDocker SwarmでOverlay network - つまり、 L2 を L3 までで実現するネットワークを使っている方もいらっしゃると思い、その技術は具体的にはどういう感じで動いているのか、少し親しみが持てたのなら幸いに思います。僕は今度RFC読みます。
Enjoy networking!!!