目錄html
前面咱們的實驗環境中只有一個 docker host,全部的容器都是運行在這一個 host 上的。但在真正的環境中會有多個 host,容器在這些 host 中啓動、運行、中止和銷燬,相關容器會經過網絡相互通訊,不管它們是否位於相同的 host。node
對於這樣一個 multi-host 環境,咱們將如何高效地進行管理呢?
咱們面臨的第一個問題是:爲全部的 host 安裝和配置 docker。linux
在前面咱們手工安裝了第一個 docker host,步驟包括:git
可見步驟仍是挺多的,對於多主機環境手工方式效率低且不容易保證一致性,針對這個問題,docker 給出的解決方案是 Docker Machine。github
用 Docker Machine 能夠批量安裝和配置 docker host,這個 host 能夠是本地的虛擬機、物理機,也能夠是公有云中的雲主機。golang
實驗環境web
node1 192.168.2.110 Centos 7.5docker
host1 192.168.2.120 Centos 7.5shell
host2 192.168.2.130 Centos 7.5數據庫
在node1上部署docker-machine經過dockermachine在其他兩個host上部署docker
安裝方法很簡單,執行以下命令:
curl -L https://github.com/docker/machine/releases/download/v0.9.0/docker-machine-`uname -s`-`uname -m` >/tmp/docker-machine && chmod +x /tmp/docker-machine && sudo cp /tmp/docker-machine /usr/local/bin/docker-machine
[root@host1 ~]# curl -L https://github.com/docker/machine/releases/download/v0.9.0/docker-machine-`uname -s`-`uname -m` >/tmp/docker-machine % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 617 0 617 0 0 551 0 --:--:-- 0:00:01 --:--:-- 551 100 24.1M 100 24.1M 0 0 85576 0 0:04:55 0:04:55 --:--:-- 85442 [root@host1 ~]# chmod +x /tmp/docker-machine [root@host1 ~]# sudo cp /tmp/docker-machine /usr/local/bin/docker-machine
下載的執行文件被放到 /usr/local/bin 中,執行docker-mahine version
驗證命令是否可用:
[root@host1 ~]# docker-machine version docker-machine version 0.9.0, build 15fd4c7
爲了獲得更好的體驗,咱們能夠安裝 bash completion script,這樣在 bash 可以經過 tab
鍵補全 docker-mahine
的子命令和參數。安裝方法是從https://github.com/docker/machine/tree/master/contrib/completion/bash下載 completion script:
將其放置到 /etc/bash_completion.d
目錄下。而後將以下代碼添加到$HOME/.bashrc
:
[root@host1 bash_completion.d]# PS1='[\u@\h \W$(__docker_machine_ps1)]\$ '
#下載completion script:docker-machine-prompt.bash docker-machine-wrapper.bash docker-machine.bash 將其放在/etc/bash_completion.d目錄下 #命令爲: [root@host1 bash_completion.d]# scripts=( docker-machine-prompt.bash docker-machine-wrapper.bash docker-machine.bash ); for i in "${scripts[@]}"; do sudo wget https://raw.githubusercontent.com/docker/machine/v0.13.0/contrib/completion/bash/${i} -P /etc/bash_completion.d; done
其做用是設置 docker-machine 的命令行提示符,不過要等到部署完其餘兩個 host 才能看出效果。
若提示command not found 命令未找到,
更改~/.bashrc,在bashrc中添加如下三行:
source /etc/bash_completion.d/docker-machine-wrapper.bash source /etc/bash_completion.d/docker-machine-prompt.bash source /etc/bash_completion.d/docker-machine.bash
從新source下bashrc
source /root/.bashrc
對於 Docker Machine 來講,術語 Machine
就是運行 docker daemon 的主機。「建立 Machine」 指的就是在 host 上安裝和部署 docker。先執行 docker-machine ls
查看一下當前的 machine:
[root@host1 bash_completion.d]# docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
當前尚未 machine,接下來咱們建立第一個 machine: host1 - 192.168.56.120。
建立 machine 要求可以無密碼登陸遠程主機,因此須要先經過以下命令將 ssh key 拷貝到 192.168.56.120: ssh-copy-id 192.168.56.120 [root@host1 bash_completion.d]# ssh-copy-id 192.168.2.120 /bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub" The authenticity of host '192.168.2.120 (192.168.2.120)' can't be established. ECDSA key fingerprint is SHA256:e+1rZSlP7YYndeivr7Hjikf3D+Yg9it2KBNxmpjpkQ8. ECDSA key fingerprint is MD5:a8:ab:5e:ac:55:e5:d6:fe:bb:1a:9d:41:19:d5:de:7a. Are you sure you want to continue connecting (yes/no)? yes /bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys root@192.168.2.120's password: Permission denied, please try again. root@192.168.2.120's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh '192.168.2.120'" and check to make sure that only the key(s) you wanted were added.
由於咱們是往普通的 Linux 中部署 docker,因此使用 generic
driver,其餘 driver 能夠參考文檔 https://docs.docker.com/machine/drivers/。
--generic-ip-address
指定目標系統的 IP,並命名爲 host1
。命令執行過程以下:
[root@node1 ~]# docker-machine create --driver=generic --generic-ip-address=192.168.2.120 host1 Creating CA: /root/.docker/machine/certs/ca.pem Creating client certificate: /root/.docker/machine/certs/cert.pem Running pre-create checks... Creating machine... #ssh登錄到遠程主機 (host1) No SSH key specified. Assuming an existing key at the default location. Waiting for machine to be running, this may take a few minutes... Detecting operating system of created instance... Waiting for SSH to be available... Detecting the provisioner... #安裝docker Provisioning with centos... #拷貝證書 Copying certs to the local machine directory... Copying certs to the remote machine... #配置docker daemon Setting Docker configuration on the remote daemon... Checking connection to Docker... #啓動docker Docker is up and running! To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env host1 [root@node1 ~]# docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS host1 - generic Running tcp://192.168.2.120:2376 v18.09.0 [root@node1 ~]# docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS host1 - generic Running tcp://192.168.2.120:2376 v18.09.0
已經能看到 host1 了。 咱們能夠登陸到 host1 查看 docker daemon 的具體配置 /etc/systemd/system/docker.service。
[root@host1 ~]# vim /etc/systemd/system/docker.service [Unit] Description=Docker Application Container Engine After=network.target [Service] Type=notify ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --storage-driver devicemapper --tlsverify --tlscacert /etc/docker/ca.pem --tlscert /etc/docker/server.pem --tlskey /etc/docker/server-key.pem --label provider=generic ExecReload=/bin/kill -s HUP MountFlags=slave LimitNOFILE=infinity LimitNPROC=infinity LimitCORE=infinity TimeoutStartSec=0 Delegate=yes KillMode=process Environment= [Install] WantedBy=multi-user.target
-H tcp://0.0.0.0:2376
使 docker daemon 接受遠程鏈接。--tls*
對遠程鏈接啓用安全認證和加密。同時咱們也看到 hostname 已經設置爲 host1
:
使用一樣的方法建立 host2:
[root@node1 ~]# docker-machine create --driver=generic --generic-ip-address=192.168.2.130 host2 Running pre-create checks... Creating machine... (host2) No SSH key specified. Assuming an existing key at the default location. Waiting for machine to be running, this may take a few minutes... Detecting operating system of created instance... Waiting for SSH to be available... Detecting the provisioner... Provisioning with centos... Copying certs to the local machine directory... Copying certs to the remote machine... Setting Docker configuration on the remote daemon... Checking connection to Docker... Docker is up and running! To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env host2 [root@node1 ~]# docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS host1 - generic Running tcp://192.168.2.120:2376 v18.09.0 host2 - generic Running tcp://192.168.2.130:2376 v18.09.0
用 docker-machine
建立 machine 的過程很簡潔,很是適合多主機環境。除此以外,Docker Machine 也提供了一些子命令方便對 machine 進行管理。其中最經常使用的就是無需登陸到 machine 就能執行 docker 相關操做。
咱們前面學過,要執行遠程 docker 命令咱們須要經過 -H
指定目標主機的鏈接字符串,好比:
docker -H tcp://192.168.2.120:2376 ps
Docker Machine則讓這個過程更簡單
docker-machine env host1 直接訪問host1的全部環境變量
[root@node1 ~]# docker-machine env host1 export DOCKER_TLS_VERIFY="1" export DOCKER_HOST="tcp://192.168.2.120:2376" export DOCKER_CERT_PATH="/root/.docker/machine/machines/host1" export DOCKER_MACHINE_NAME="host1" # Run this command to configure your shell: # eval $(docker-machine env host1) [root@node1 ~]# docker-machine env host2 export DOCKER_TLS_VERIFY="1" export DOCKER_HOST="tcp://192.168.2.130:2376" export DOCKER_CERT_PATH="/root/.docker/machine/machines/host2" export DOCKER_MACHINE_NAME="host2" # Run this command to configure your shell: # eval $(docker-machine env host2)
根據提示,執行 eval $(docker-machine env host1)
:
[root@node1 ~]# eval $(docker-machine env host1) [root@node1 ~ [host1]]# [root@node1 ~ [host1]]#
而後,就能夠看到命令行提示符已經變了,其緣由是咱們以前在$HOME/.bashrc
中配置了 PS1='[\u@\h \W$(__docker_machine_ps1)]\$ '
,用於顯示當前 docker host。
[root@node1 ~ [host1]]# docker run --name w1 -itd busybox Unable to find image 'busybox:latest' locally latest: Pulling from library/busybox 57c14dd66db0: Pull complete Digest: sha256:7964ad52e396a6e045c39b5a44438424ac52e12e4d5a25d94895f2058cb863a0 Status: Downloaded newer image for busybox:latest 0dada375dee242eca50ab534cf39efe44cd5c502c4bc19e9888b94092faebcf2 docker: Error response from daemon: OCI runtime create failed: rootfs (/var/lib/docker/devicemapper/mnt/6f719b81e2a0c68c48629815696efb4e596575694176f424941d06545164569d/rootfs) does not exist: unknown. [root@node1 ~ [host1]]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
執行 eval $(docker-machine env host2)
切換到 host2:
[root@node1 ~ [host1]]# eval $(docker-machine env host2) [root@node1 ~ [host2]]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
介紹幾個有用的 docker-machine 子命令:
docker-machine upgrade
更新 machine 的 docker 到最新版本,能夠批量執行:
[root@node1 ~ [host2]]# docker-machine upgrade host1 host2 Waiting for SSH to be available... Waiting for SSH to be available... Detecting the provisioner... Detecting the provisioner... Upgrading docker... Upgrading docker... Restarting docker... Restarting docker...
docker-machine config
查看 machine 的 docker daemon 配置:
[root@node1 ~]# docker-machine config host1 --tlsverify --tlscacert="/root/.docker/machine/machines/host1/ca.pem" --tlscert="/root/.docker/machine/machines/host1/cert.pem" --tlskey="/root/.docker/machine/machines/host1/key.pem" -H=tcp://192.168.2.120:2376 [root@node1 ~]# docker-machine config host2 --tlsverify --tlscacert="/root/.docker/machine/machines/host2/ca.pem" --tlscert="/root/.docker/machine/machines/host2/cert.pem" --tlskey="/root/.docker/machine/machines/host2/key.pem" -H=tcp://192.168.2.130:2376
docker-machine scp
能夠在不一樣 machine 之間拷貝文件,好比:
docker-machine scp host1:/tmp/a host2:/tmp/b
docker跨主機網絡方案包括:
docker 網絡是一個很是活躍的技術領域,不斷有新的方案開發出來,那麼要問個很是重要的問題了:
如此衆多的方案是如何與 docker 集成在一塊兒的?
答案是:libnetwork 以及 CNM。
libnetwork & CNM
libnetwork 是 docker 容器網絡庫,最核心的內容是其定義的 Container Network Model (CNM),這個模型對容器網絡進行了抽象,由如下三類組件組成:
Sandbox
Sandbox 是容器的網絡棧,包含容器的 interface、路由表和 DNS 設置。 Linux Network Namespace 是 Sandbox 的標準實現。Sandbox 能夠包含來自不一樣 Network 的 Endpoint。
Endpoint
Endpoint 的做用是將 Sandbox 接入 Network。Endpoint 的典型實現是 veth pair,後面咱們會舉例。一個 Endpoint 只能屬於一個網絡,也只能屬於一個 Sandbox。
Network
Network 包含一組 Endpoint,同一 Network 的 Endpoint 能夠直接通訊。Network 的實現能夠是 Linux Bridge、VLAN 等
libnetwork CNM 定義了 docker 容器的網絡模型,按照該模型開發出的 driver 就能與 docker daemon 協同工做,實現容器網絡。docker 原生的 driver 包括 none、bridge、overlay 和 macvlan,第三方 driver 包括 flannel、weave、calico 等。
爲支持容器跨主機通訊,Docker 提供了 overlay driver,使用戶能夠建立基於 VxLAN 的 overlay 網絡。VxLAN 可將二層數據封裝到 UDP 進行傳輸,VxLAN 提供與 VLAN 相同的以太網二層服務,可是擁有更強的擴展性和靈活性。有關 VxLAN 更詳細的內容可參考 CloudMan 在《天天5分鐘玩轉 OpenStack》中的相關章節。
Docerk overlay 網絡須要一個 key-value 數據庫用於保存網絡狀態信息,包括 Network、Endpoint、IP 等。Consul、Etcd 和 ZooKeeper 都是 Docker 支持的 key-vlaue 軟件,咱們這裏使用 Consul。
node1 192.168.2.110 host1 192.168.2.120 host2 192.168.2.130
最簡單的方式是以容器方式運行 Consul:
在node1上部署支持的組件 consul
[root@node1 ~]# docker run -d -p 8500:8500 -h consul --name consul progrium/consul -server -bootstrap
容器啓動後,能夠經過 http://192.168.2.110:8500 訪問 Consul。
接下來修改 host1 和 host2 的 docker daemon 的配置文件/etc/systemd/system/docker.service.d/10-machine.conf
。
[root@host1 ~]# vim /etc/systemd/system/docker.service.d/10-machine.conf [Service] ExecStart= ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --storage-driver overlay2 --tlsverify --tlscacert \ /etc/docker/ca.pem --tlscert /etc/docker/server.pem --tlskey /etc/docker/server-key.pem --label provider=generic \ --cluster-store=consul://192.168.2.110:8500 --cluster-advertise=ens33:2376 Environment=
重啓 docker daemon。
systemctl daemon-reload
systemctl restart docker.service
host1 和 host2 將自動註冊到 Consul 數據庫中。
http://192.168.2.110:8500/ui/#/dc1/kv/docker/nodes/
在 host1 中建立 overlay 網絡 ov_net1:
[root@host1 ~]# docker network create -d overlay ov_net1 c82b3b1be3e8035d0088952ff2e2ab42d323496dcde08bd813aaea72f09b991a [root@host1 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 9e1bc9528540 bridge bridge local 12e0fcdac081 host host local 797eee7fca29 none null local c82b3b1be3e8 ov_net1 overlay global
-d overlay
指定 driver 爲 overaly
注意到 ov_net1
的 SCOPE 爲 global,而其餘網絡爲 local。在 host2 上查看存在的網絡:
[root@host2 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE a8b54548ffa0 bridge bridge local 146fe14636bd host host local 3ef7d580564a none null local c82b3b1be3e8 ov_net1 overlay global
host2 上也能看到 ov_net1。這是由於建立 ov_net1 時 host1 將 overlay 網絡信息存入了 consul,host2 從 consul 讀取到了新網絡的數據。以後 ov_net 的任何變化都會同步到 host1 和 host2。
docker network inspect
查看 ov_net1 的詳細信息:
"IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "10.0.0.0/24", "Gateway": "10.0.0.1" } ] },
IPAM 是指 IP Address Management,docker 自動爲 ov_net1 分配的 IP 空間爲 10.0.0.0/24。
在ov_net1網絡上建立運行容器
[root@host1 ~]# docker run --name bbox1 -itd --network ov_net1 busybox 99e3ce359a1ae41667f7b09b544a4a8c6e3009b18da40cd96832b8aedb76dc7d [root@host1 ~]# docker exec bbox1 ip r default via 172.18.0.1 dev eth1 10.0.0.0/24 dev eth0 scope link src 10.0.0.2 172.18.0.0/16 dev eth1 scope link src 172.18.0.2
bbox1 有兩個網絡接口 eth0 和 eth1。eth0 IP 爲 10.0.0.2,鏈接的是 overlay 網絡 ov_net1。eth1 IP 172.18.0.2,容器的默認路由是走 eth1,eth1 是哪兒來的呢?
其實,docker 會建立一個 bridge 網絡 「docker_gwbridge」,爲全部鏈接到 overlay 網絡的容器提供訪問外網的能力。
[root@host1 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 9e1bc9528540 bridge bridge local a4d35bce9b5a docker_gwbridge bridge local 12e0fcdac081 host host local 797eee7fca29 none null local c82b3b1be3e8 ov_net1 overlay global
查看網絡
[root@host1 ~]# docker network inspect docker_gwbridge [ { "Name": "docker_gwbridge", "Id": "a4d35bce9b5a03ef3c3cbaf8139873aad139b7c6ca222cbb59d72cfa05c96675", "Created": "2018-12-17T16:12:45.850674561+08:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "99e3ce359a1ae41667f7b09b544a4a8c6e3009b18da40cd96832b8aedb76dc7d": { "Name": "gateway_1c1435110045", "EndpointID": "f876cab800aecfc9eaf4a8573b662ef3305891a8c64eab52a2fefefc858b059e", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.enable_icc": "false", "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.name": "docker_gwbridge" }, "Labels": {} } ]
並且此網絡的網關就是網橋 docker_gwbridge 的 IP 172.18.0.1
[root@host1 ~]# ifconfig docker_gwbridge docker_gwbridge: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255 inet6 fe80::42:61ff:fe3d:13fa prefixlen 64 scopeid 0x20<link> ether 02:42:61:3d:13:fa txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
這樣容器 bbox1 就能夠經過 docker_gwbridge 訪問外網。
[root@host1 ~]# docker exec bbox1 ping -c 3 www.baidu.com PING www.baidu.com (163.177.151.109): 56 data bytes 64 bytes from 163.177.151.109: seq=0 ttl=55 time=12.698 ms 64 bytes from 163.177.151.109: seq=1 ttl=55 time=11.510 ms 64 bytes from 163.177.151.109: seq=2 ttl=55 time=12.060 ms --- www.baidu.com ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 11.510/12.089/12.698 ms
若是外網要訪問容器,可經過主機端口映射,好比:
docker run -p 80:80 -d --net ov_net1 --name web1 httpd
在 host2 中運行容器 bbox2:
[root@host2 ~]# docker run --name bbox2 -itd --network ov_net1 busybox 1e004591c73ffc6e76098a70fa26ca6db7acf8d386bcc7dbdcce872f741404a5 [root@host2 ~]# docker exec bbox2 ip r default via 172.18.0.1 dev eth1 10.0.0.0/24 dev eth0 scope link src 10.0.0.3 172.18.0.0/16 dev eth1 scope link src 172.18.0.2 [root@host2 ~]# docker exec bbox2 ping -c 2 bbox1 PING bbox1 (10.0.0.2): 56 data bytes 64 bytes from 10.0.0.2: seq=0 ttl=64 time=5.838 ms 64 bytes from 10.0.0.2: seq=1 ttl=64 time=0.657 ms
bbox2 IP 爲 10.0.0.3,能夠直接 ping bbox1:
可見 overlay 網絡中的容器能夠直接通訊,同時 docker 也實現了 DNS 服務。
docker 會爲每一個 overlay 網絡建立一個獨立的 network namespace,其中會有一個 linux bridge br0,endpoint 仍是由 veth pair 實現,一端鏈接到容器中(即 eth0),另外一端鏈接到 namespace 的 br0 上。
br0 除了鏈接全部的 endpoint,還會鏈接一個 vxlan 設備,用於與其餘 host 創建 vxlan tunnel。容器之間的數據就是經過這個 tunnel 通訊的。
要查看 overlay 網絡的 namespace 能夠在 host1 和 host2 上執行 ip netns
(請確保在此以前執行過 ln -s /var/run/docker/netns /var/run/netns
),能夠看到兩個 host 上有一個相同的 namespace :1-c82b3b1be3,
[root@host2 ~]# ln -s /var/run/docker/netns/ /var/run/netns [root@host2 ~]# ip netns c6bd1da0cdcf (id: 2) 1-c82b3b1be3 (id: 1) 535be03f749a (id: 0) [root@host1 ~]# ln -s /var/run/docker/netns/ /var/run/netns [root@host1 ~]# ip netns 1c1435110045 (id: 1) 1-c82b3b1be3 (id: 0)
這就是 ov_net1 的 namespace,查看 namespace 中的 br0 上的設備。
[root@host1 ~]yum install -y bridge-utils [root@host1 ~]# ip netns exec 1-c82b3b1be3 brctl show bridge name bridge id STP enabled interfaces br0 8000.be0804033d22 no veth0 vxlan0
查看 vxlan1 設備的具體配置信息可知此 overlay 使用的 VNI爲256
9: vxlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master br0 state UNKNOWN mode DEFAULT group default link/ether ca:6b:e3:85:45:b9 brd ff:ff:ff:ff:ff:ff link-netnsid 0 promiscuity 1 vxlan id 256
不一樣的 overlay 網絡是相互隔離的
咱們建立第二個 overlay 網絡 ov_net2 並運行容器 bbox3。
[root@host1 ~]# docker network create -d overlay ov_net2 9dce57a6c3430c29c214787cdee62b81d5c2af1509b0712864946d62b9fc01d5 [root@host1 ~]# docker run --name bbox3 -itd --network ov_net2 busybox e247d50629401d9a1c2e3fc5a2b852cb3bc99cb08d1afaf9c0312a025d08d8e2 [root@host1 ~]# docker exec -it bbox3 ip r default via 172.18.0.1 dev eth1 10.0.1.0/24 dev eth0 scope link src 10.0.1.2 172.18.0.0/16 dev eth1 scope link src 172.18.0.3
bbox3 分配到的 IP 是 10.0.1.2,嘗試 ping bbox1(10.0.0.2)
[root@host1 ~]# docker exec -it bbox3 ping -c 2 10.0.0.2 PING 10.0.0.2 (10.0.0.2): 56 data bytes --- 10.0.0.2 ping statistics --- 2 packets transmitted, 0 packets received, 100% packet loss
ping 失敗,可見不一樣 overlay 網絡之間是隔離的。即使是經過 docker_gwbridge 也不能通訊。若是要實現 bbox3 與 bbox1 通訊,能夠將 bbox3 也鏈接到 ov_net1。
[root@host1 ~]# docker network connect ov_net1 bbox3 [root@host1 ~]# docker exec -it bbox3 ping -c 2 10.0.0.2 PING 10.0.0.2 (10.0.0.2): 56 data bytes 64 bytes from 10.0.0.2: seq=0 ttl=64 time=0.423 ms 64 bytes from 10.0.0.2: seq=1 ttl=64 time=0.127 ms --- 10.0.0.2 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.127/0.275/0.423 ms
除了 overlay,docker 還開發了另外一個支持跨主機容器網絡的 driver:macvlan。
macvlan 自己是 linxu kernel 模塊,其功能是容許在同一個物理網卡上配置多個 MAC 地址,即多個 interface,每一個 interface 能夠配置本身的 IP。macvlan 本質上是一種網卡虛擬化技術,Docker 用 macvlan 實現容器網絡就不奇怪了。
macvlan 的最大優勢是性能極好,相比其餘實現,macvlan 不須要建立 Linux bridge,而是直接經過以太 interface 鏈接到物理網絡。
咱們會使用 host1 和 host2 上單獨的網卡 ens33建立 macvlan。爲保證多個 MAC 地址的網絡包均可以從 ens33 經過,咱們須要打開網卡的混雜模式。
[root@host1 ~]# ip link set ens33 promisc on
在 host1 和 host2 中建立 macvlan 網絡 mac_net1
[root@host1 ~]# docker network create -d macvlan \ > --subnet=172.16.100.0/24 \ > --gateway=172.16.100.1 \ > -o parent=ens33 mac_net1 2cb22bc8ead6b896e2b2e8e8a2e4f35ec26a61937ede424c7ea1e42d5be24c2b
注意:在 host2 中也要執行相同的命令。
① -d macvlan
指定 driver 爲 macvlan。
② macvlan 網絡是 local 網絡,爲了保證跨主機可以通訊,用戶須要本身管理 IP subnet。
③ 與其餘網絡不一樣,docker 不會爲 macvlan 建立網關,這裏的網關應該是真實存在的,不然容器沒法路由。
④ -o parent
指定使用的網絡 interface。
在 host1 中運行容器 bbox1 並鏈接到 mac_net1。
[root@host1 ~]# docker run --name bbox1 -itd --ip=172.16.100.10 --network mac_net1 busybox 090490a6ea47d773ae865620766cb8f959f68320375568688d8f81a1257362dc
因爲 host1 中的 mac_net1 與 host2 中的 mac_net1 本質上是獨立的,爲了不自動分配形成 IP 衝突,咱們最好經過 --ip
指定 bbox1 地址爲 172.16.100.10
在 host2 中運行容器 bbox2,指定 IP 172.16.86.11。
驗證 bbox1 和 bbox2 的連通性。
root@host2 ~]# docker exec bbox2 ping -c 2 172.16.100.10 PING 172.16.100.10 (172.16.100.10): 56 data bytes 64 bytes from 172.16.100.10: seq=0 ttl=64 time=8.227 ms 64 bytes from 172.16.100.10: seq=1 ttl=64 time=0.618 ms --- 172.16.100.10 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 0.618/4.422/8.227 ms
bbox2 可以 ping 到 bbox1 的 IP 172.16.86.10,但沒法解析 「bbox1」 主機名。
可見 docker 沒有爲 macvlan 提供 DNS 服務,這點與 overlay 網絡是不一樣的。
macvlan 不依賴 Linux bridge,brctl show
能夠確認沒有建立新的 bridge。
查看一下容器 bbox1 的網絡設備:
oot@host1 ~]# docker exec bbox1 ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 22: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:ac:10:64:0a brd ff:ff:ff:ff:ff:ff
除了 lo,容器只有一個 eth0,請注意 eth0 後面的 @if2
,這代表該 interface 有一個對應的 interface,其全局的編號爲 2
。根據 macvlan 的原理,咱們有理由猜想這個 interface 就是主機的 ens33,確認以下:
[root@host1 ~]# ip link show ens33 2: ens33: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 link/ether 00:0c:29:92:32:12 brd ff:ff:ff:ff:ff:ff
可見,容器的 eth0 就是 ens33 經過 macvlan 虛擬出來的 interface。容器的 interface 直接與主機的網卡鏈接,這種方案使得容器無需經過 NAT 和端口映射就能與外網直接通訊(只要有網關),在網絡上與其餘獨立主機沒有區別。
macvlan 會獨佔主機的網卡,也就是說一個網卡只能建立一個 macvlan 網絡,不然會報錯:
但主機的網卡數量是有限的,如何支持更多的 macvlan 網絡呢?
好在 macvlan 不只能夠鏈接到 interface(如ens33),也能夠鏈接到 sub-interface(如ens33.xxx)。
VLAN 是現代網絡經常使用的網絡虛擬化技術,它能夠將物理的二層網絡劃分紅多達 4094 個邏輯網絡,這些邏輯網絡在二層上是隔離的,每一個邏輯網絡(即 VLAN)由 VLAN ID 區分,VLAN ID 的取值爲 1-4094。
Linux 的網卡也能支持 VLAN(yum install vlan
),同一個 interface 能夠收發多個 VLAN 的數據包,不過前提是要建立 VLAN 的 sub-interface。
好比但願ens33 同時支持 VLAN10 和 VLAN20,則需建立 sub-interfaceens33.10 和ens33.20。
在交換機上,若是某個 port 只能收發單個 VLAN 的數據,該 port 爲 Access 模式,若是支持多 VLAN,則爲 Trunk 模式,因此接下來實驗的前提是:
ens33 要接在交換機的 trunk 口上。虛擬機測試跳過
配置Vlan (host1 host2都要配置)
[root@host1 network-scripts]# pwd /etc/sysconfig/network-scripts [root@host1 network-scripts]# vim ifcfg-ens33.10 DEVICE=ens33.10 BOOTPROTO=none ONBOOT=yes IPADDR=192.168.2.211 PREFIX=24 NETWORK=192.168.2.0 VLAN=yes [root@host1 network-scripts]# vim ifcfg-ens33.20 DEVICE=ens33.20 BOOTPROTO=none ONBOOT=yes IPADDR=192.168.2.311 PREFIX=24 NETWORK=192.168.2.0 VLAN=yes
建立VLAN網卡(host1 host2都要配置)
[root@host1 network-scripts]# ip link add link ens33 name ens33.10 type vlan id 10 root@host1 network-scripts]# systemctl restart network [root@host1 network-scripts]# ip a ...... 23: ens33.10@ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link/ether 00:0c:29:92:32:12 brd ff:ff:ff:ff:ff:ff inet 192.168.2.211/24 brd 192.168.2.255 scope global noprefixroute ens33.10 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fe92:3212/64 scope link [root@host1 network-scripts]# ip link add link ens33 name ens33.20 type vlan id 20 [root@host1 network-scripts]# systemctl restart network
建立 macvlan 網絡:(host1 host2都要配置)
[root@host1 network-scripts]# docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=ens33.10 mac_net10 86eb515908d06ddabab7c2a5ed1313c6a7ad821df10c1585804e678137b46ea2 [root@host1 network-scripts]# docker network create -d macvlan --subnet=172.16.20.0/24 --gateway=172.16.20.1 -o parent=ens33.20 mac_net20 8cf792fe4f85730027796a6eb2d0a02d39893a7462d3bdab023182c58fd78dec
在 host1 中運行容器:
[root@host1 ~]# docker run --name bbox1 -itd --ip=172.16.10.10 --network mac_net10 busybox WARNING: IPv4 forwarding is disabled. Networking will not work. 6f58a52b1dcb643074d56294505089b0bc98838809a2bf66a0be44f02a2a51cd [root@host1 ~]# docker run --name bbox2 -itd --ip=172.16.20.10 --network mac_net20 busybox WARNING: IPv4 forwarding is disabled. Networking will not work. 83774612f7947b8cd019ebcfb7dde2af0f40043340d7c1fcb7bd865609c65adf docker: Error response from daemon: network mac_net20 not found.
在 host2 中運行容器:
[root@host2 ~]# docker run --name bbox3 -itd --ip=172.16.10.11 --network mac_net10 busybox WARNING: IPv4 forwarding is disabled. Networking will not work. 2ea31530a8c05d37d7b50aba54be467625b9258c5bd0ac658540dda69b3c4751 [root@host2 ~]# docker run --name bbox4 -itd --ip=172.16.20.11 --network mac_net20 busybox WARNING: IPv4 forwarding is disabled. Networking will not work. 80a04f49f098fba413957fa95d6256d717e4a8756e4da712407393d7a699c123
驗證 macvlan 之間的連通性。
[root@host1 ~]# docker exec bbox1 ping -c 2 172.16.10.11 PING 172.16.10.11 (172.16.10.11): 56 data bytes 64 bytes from 172.16.10.11: seq=0 ttl=64 time=8.106 ms 64 bytes from 172.16.10.11: seq=1 ttl=64 time=0.687 ms
bbox1 能 ping 通 bbox3,bbox2 能 ping 通 bbox4。即:同一 macvlan 網絡能通訊。
bbox1 沒法 ping 通 bbox2 和 bbox4。即:不一樣 macvlan 網絡之間不能通訊。但更準確的說法應該是:不一樣 macvlan 網絡不能 在二層上 通訊。在三層上能夠經過網關將 macvlan 連通,下面咱們就啓用網關。
咱們會將 Host 192.168.2.110配置成一個虛擬路由器,設置網關並轉發 VLAN10 和 VLAN20 的流量。固然也可使用物理路由器達到一樣的效果。首先確保操做系統 IP Forwarding 已經啓用。
[root@host1 ~]# sysctl -w net.ipv4.ip_forward=1 net.ipv4.ip_forward = 1
在node1配置Vlan
[root@node1 network-scripts]# pwd /etc/sysconfig/network-scripts [root@host1 network-scripts]# vim ifcfg-ens33.10 DEVICE=ens33.10 BOOTPROTO=none ONBOOT=yes PREFIX=24 VLAN=yes [root@host1 network-scripts]# vim ifcfg-ens33.20 DEVICE=ens33.20 BOOTPROTO=none ONBOOT=yes PREFIX=24 VLAN=yes
將網關 IP 配置到
[root@host1 network-scripts]#ip link add link ens33 name ens33.10 type vlan id 10 [root@host1 network-scripts]#ip link add link ens33 name ens33.20 type vlan id 20 [root@host1 network-scripts]#systemctl restart network
開啓VLAN
[root@node1 network-scripts]#ifconfig ens33.10 172.16.10.1 netmask 255.255.255.0 up [root@host1 network-scripts]#ifconfig ens33.20 172.16.20.1 netmask 255.255.255.0 up
設置iptables路由轉發
root@node1 ~]# iptables -t nat -A POSTROUTING -o ens33.10 -j MASQUERADE [root@node1 ~]# iptables -t nat -A POSTROUTING -o ens33.20 -j MASQUERADE [root@node1 ~]# iptables -A FORWARD -i ens33.10 -o ens33.20 -m state --state RELATED,ESTABLISHED -j ACCEPT [root@node1 ~]# iptables -A FORWARD -i ens33.20 -o ens33.10 -m state --state RELATED,ESTABLISHED -j ACCEPT [root@node1 ~]# iptables -A FORWARD -i ens33.10 -o ens33.20 -j ACCEPT [root@node1 ~]# iptables -A FORWARD -i ens33.20 -o ens33.10 -j ACCEPT
建立 macvlan 網絡:(host1 host2都要配置)
[root@node1 network-scripts]# docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=ens33.10 mac_net10 86eb515908d06ddabab7c2a5ed1313c6a7ad821df10c1585804e678137b46ea2 [root@node1 network-scripts]# docker network create -d macvlan --subnet=172.16.20.0/24 --gateway=172.16.20.1 -o parent=ens33.20 mac_net20 8cf792fe4f85730027796a6eb2d0a02d39893a7462d3bdab023182c58fd78dec
如今 host1 上位於 mac_net10 的 bbox1 已經能夠與 host2 上位於 mac_net20 的 bbox4 通訊了。
macvlan 網絡的連通和隔離徹底依賴 VLAN、IP subnet 和路由,docker 自己不作任何限制,用戶能夠像管理傳統 VLAN 網絡那樣管理 macvlan。
flannel 是 CoreOS 開發的容器網絡解決方案。flannel 爲每一個 host 分配一個 subnet,容器今後 subnet 中分配 IP,這些 IP 能夠在 host 間路由,容器間無需 NAT 和 port mapping 就能夠跨主機通訊。
每一個 subnet 都是從一個更大的 IP 池中劃分的,flannel 會在每一個主機上運行一個叫 flanneld 的 agent,其職責就是從池子中分配 subnet。爲了在各個主機間共享信息,flannel 用 etcd(與 consul 相似的 key-value 分佈式數據庫)存放網絡配置、已分配的 subnet、host 的 IP 等信息。
數據包如何在主機間轉發是由 backend 實現的。flannel 提供了多種 backend,最經常使用的有 vxlan 和 host-gw
192.168.2.110 node1 backend
192.168.2.120 host1 flanel
192.168.2.130 host2 flanel
etcd 部署在 192.168.2.110,host1 和 host2 上運行 flanneld,首先安裝配置 etcd。
在node1 配置腳本
[root@node1 ~]# cd /server/scripts/ [root@node1 scripts]# vim etcd.sh #!/bin/sh ETCD_VER=v2.3.7 DOWNLOAD_URL=https://github.com/coreos/etcd/releases/download curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz mkdir -p /tmp/test-etcd && tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/test-etcd --strip-components=1 cp /tmp/test-etcd/etcd* /usr/local/bin/
該腳本從 github 上下載 etcd 的可執行文件並保存到 /usr/local/bin/,啓動 etcd 並打開 2379 監聽端口。
[root@node1 scripts]# etcd -listen-client-urls http://192.168.2.110:2379 -advertise-client-urls http://192.168.2.110:2379
測試 etcd 是否可用:
[root@node1 ~]# etcdctl --endpoint=192.168.2.110:2379 set foo "hello" hello [root@node1 ~]# etcdctl --endpoint=192.168.2.110:2379 get foo hello
能夠正常在 etcd 中存取數據了。
flannel 沒有現成的執行文件可用,必須本身 build,最可靠的方法是在 Docker 容器中 build。不過用於作 build 的 docker 鏡像託管在 gcr.io,國內可能沒法直接訪問,爲方便你們,cloudman把它 mirror 到了 docker hub,構建步驟以下:
[root@node1 ~]# docker pull cloudman6/kube-cross:v1.6.2-2 [root@node1 ~]# docker tag cloudman6/kube-cross:v1.6.2-2 gcr.io/google_containers/kube-cross:v1.6.2-2
[root@node1 ~]# git clone https://github.com/coreos/flannel.git 正克隆到 'flannel'... remote: Enumerating objects: 363, done. remote: Counting objects: 100% (363/363), done. remote: Compressing objects: 100% (290/290), done. remote: Total 25600 (delta 83), reused 212 (delta 62), pack-reused 25237 接收對象中: 100% (25600/25600), 45.50 MiB | 2.37 MiB/s, done. 處理 delta 中: 100% (9187/9187), done.
[root@node1 ~]# cd flannel/ [root@node1 flannel]# make dist/flanneld-amd64 if [ "qemu-amd64-static" = "qemu-amd64-static" ]; then \ wget -O dist/qemu-amd64-static https://github.com/multiarch/qemu-user-static/releases/download/v3.0.0/qemu-x86_64-static; \ elif [ "qemu-amd64-static" = "qemu-arm64-static"]; then \ wget -O dist/qemu-arm64-static https://github.com/multiarch/qemu-user-static/releases/download/v3.0.0/qemu-aarch64-static; \ else \ wget -O dist/qemu-amd64-static https://github.com/multiarch/qemu-user-static/releases/download/v3.0.0/qemu-amd64-static; \ fi --2018-12-18 12:29:32-- https://github.com/multiarch/qemu-user-static/releases/download/v3.0.0/qemu-x86_64-static 正在解析主機 github.com (github.com)... 13.229.188.59, 13.250.177.223, 52.74.223.119 正在鏈接 github.com (github.com)|13.229.188.59|:443... 已鏈接。 已發出 HTTP 請求,正在等待迴應... 302 Found 位置:https://github-production-release-asset-2e65be.s3.amazonaws.com/47342812/d4478d00-cffb-11e8-8a74-22686fad33ae?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20181218%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20181218T042934Z&X-Amz-Expires=300&X-Amz-Signature=6ef4ed517480ee30e58b7f6874a05dc3a6c26b015beb11c478e756941deb1048&X-Amz-SignedHeaders=host&actor_id=0&response-content-disposition=attachment%3B%20filename%3Dqemu-x86_64-static&response-content-type=application%2Foctet-stream [跟隨至新的 URL] --2018-12-18 12:29:34-- https://github-production-release-asset-2e65be.s3.amazonaws.com/47342812/d4478d00-cffb-11e8-8a74-22686fad33ae?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20181218%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20181218T042934Z&X-Amz-Expires=300&X-Amz-Signature=6ef4ed517480ee30e58b7f6874a05dc3a6c26b015beb11c478e756941deb1048&X-Amz-SignedHeaders=host&actor_id=0&response-content-disposition=attachment%3B%20filename%3Dqemu-x86_64-static&response-content-type=application%2Foctet-stream 正在解析主機 github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)... 52.216.82.248 正在鏈接 github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)|52.216.82.248|:443... 已鏈接。 已發出 HTTP 請求,正在等待迴應... 200 OK 長度:4443728 (4.2M) [application/octet-stream] 正在保存至: 「dist/qemu-amd64-static」 100%[=======================================================>] 4,443,728 1.90MB/s 用時 2.2s 2018-12-18 12:29:38 (1.90 MB/s) - 已保存 「dist/qemu-amd64-static」 [4443728/4443728]) # valid values for ARCH are [amd64 arm arm64 ppc64le s390x] docker run -e CGO_ENABLED=1 -e GOARCH=amd64 \ -u 0:0 \ -v /root/flannel/dist/qemu-amd64-static:/usr/bin/qemu-amd64-static \ -v /root/flannel:/go/src/github.com/coreos/flannel:ro \ -v /root/flannel/dist:/go/src/github.com/coreos/flannel/dist \ golang:1.10.3 /bin/bash -c '\ cd /go/src/github.com/coreos/flannel && \ make -e dist/flanneld && \ mv dist/flanneld dist/flanneld-amd64' Unable to find image 'golang:1.10.3' locally 1.10.3: Pulling from library/golang 55cbf04beb70: Pull complete 1607093a898c: Pull complete 9a8ea045c926: Pull complete d4eee24d4dac: Pull complete 9c35c9787a2f: Pull complete 6a66653f6388: Pull complete 102f6b19f797: Pull complete Digest: sha256:3c54fa85d6262d2ef7695ee2f8793f1f4f9809ce4a08ca2e213235ef4cfdcb66 Status: Downloaded newer image for golang:1.10.3 go build -o dist/flanneld \ -ldflags '-s -w -X github.com/coreos/flannel/version.Version=v0.10.0-69-g39af3d7e -extldflags "-static"' # github.com/coreos/flannel /tmp/go-link-965670900/000022.o: In function `mygetgrouplist': /workdir/go/src/os/user/getgrouplist_unix.go:15: warning: Using 'getgrouplist' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking /tmp/go-link-965670900/000021.o: In function `mygetgrgid_r': /workdir/go/src/os/user/cgo_lookup_unix.go:38: warning: Using 'getgrgid_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking /tmp/go-link-965670900/000021.o: In function `mygetgrnam_r': /workdir/go/src/os/user/cgo_lookup_unix.go:43: warning: Using 'getgrnam_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking /tmp/go-link-965670900/000021.o: In function `mygetpwnam_r': /workdir/go/src/os/user/cgo_lookup_unix.go:33: warning: Using 'getpwnam_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking /tmp/go-link-965670900/000021.o: In function `mygetpwuid_r': /workdir/go/src/os/user/cgo_lookup_unix.go:28: warning: Using 'getpwuid_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking /tmp/go-link-965670900/000004.o: In function `_cgo_f7895c2c5a3a_C2func_getaddrinfo': /tmp/go-build/cgo-gcc-prolog:46: warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
[root@node1 flannel]# scp dist/flanneld-amd64 192.168.2.120:/usr/local/bin/flanneld flanneld-amd64 100% 34MB 21.2MB/s 00:01 [root@node1 flannel]# scp dist/flanneld-amd64 192.168.2.130:/usr/local/bin/flanneld flanneld-amd64 100% 34MB 22.2MB/s 00:01
將 flannel 網絡的配置信息保存到 etcd
先將配置信息寫到文件 flannel-config.json 中,內容爲:
[root@node1 flannel]# vim flannel-config.json { "Network": "10.2.0.0/16", "SubnetLen": 24, "Backend": { "Type": "vxlan" } }
Network
定義該網絡的 IP 池爲 10.2.0.0/16
。SubnetLen
指定每一個主機分配到的 subnet 大小爲 24 位,即10.2.X.0/24
。Backend
爲 vxlan
,即主機間經過 vxlan 通訊,後面咱們還會討論host-gw
。將配置存入 etcd:
[root@node1 flannel]# etcdctl --endpoint=192.168.2.110:2379 set /docker-test/network/config < flannel-config.json { "Network": "10.2.0.0/16", "SubnetLen": 24, "Backend": { "Type": "vxlan" } }
/docker-test/network/config
是此 etcd 數據項的 key,其 value 爲 flannel-config.json 的內容。key 能夠任意指定,這個 key 後面會做爲 flanneld 的一個啓動參數。執行 etcdctl get
確保設置成功
在 host1 和 host2 上執行以下命令:
[root@host1 ~]# flanneld -etcd-endpoints=http://192.168.2.110:2379 -iface=ens33 -etcd-prefix=/docker-test/network #ens33被選做與外部通訊的interface I1218 13:53:33.082312 9063 main.go:527] Using interface with name ens33 and address 192.168.2.120 I1218 13:53:33.082516 9063 main.go:544] Defaulting external address to interface address (192.168.2.120) I1218 13:53:33.085850 9063 main.go:244] Created subnet manager: Etcd Local Manager with Previous Subnet: None I1218 13:53:33.085886 9063 main.go:247] Installing signal handlers I1218 13:53:33.093993 9063 main.go:386] Found network config - Backend type: vxlan I1218 13:53:33.094100 9063 vxlan.go:120] VXLAN config: VNI=1 Port=0 GBP=false DirectRouting=false #識別 flannel 網絡池 10.2.0.0/16。 I1218 13:53:33.311792 9063 local_manager.go:234] Picking subnet in range 10.2.1.0 ... 10.2.255.0 # 分配的 subnet 爲 10.2.19.0/24。 I1218 13:53:33.315619 9063 local_manager.go:220] Allocated lease (10.2.19.0/24) to current node (192.168.2.120) I1218 13:53:33.317893 9063 main.go:317] Wrote subnet file to /run/flannel/subnet.env I1218 13:53:33.317920 9063 main.go:321] Running backend. I1218 13:53:33.323052 9063 vxlan_network.go:60] watching for new subnet leases I1218 13:53:33.325031 9063 main.go:429] Waiting for 22h59m59.953184611s to renew lease I1218 13:53:33.380670 9063 iptables.go:145] Some iptables rules are missing; deleting and recreating rules I1218 13:53:33.380727 9063 iptables.go:167] Deleting iptables rule: -s 10.2.0.0/16 -j ACCEPT I1218 13:53:33.390787 9063 iptables.go:167] Deleting iptables rule: -d 10.2.0.0/16 -j ACCEPT I1218 13:53:33.396709 9063 iptables.go:155] Adding iptables rule: -s 10.2.0.0/16 -j ACCEPT I1218 13:53:33.424823 9063 iptables.go:155] Adding iptables rule: -d 10.2.0.0/16 -j ACCEPT
-etcd-endpoints
指定 etcd url。
-iface
指定主機間數據傳輸使用的 interface。
-etcd-prefix
指定 etcd 存放 flannel 網絡配置信息的 key。
flanneld 啓動後,host1 內部網絡會發生一些變化:
flannel.1
被建立,並且配置上 subnet 的第一個 IP 10.2.19.0。[root@host1 ~]# ip addr show flannel.1 17: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default link/ether 3a:de:9e:a8:2f:ea brd ff:ff:ff:ff:ff:ff inet 10.2.19.0/32 scope global flannel.1 valid_lft forever preferred_lft forever inet6 fe80::38de:9eff:fea8:2fea/64 scope link valid_lft forever preferred_lft forever [root@host1 ~]# ip route default via 192.168.2.1 dev ens33 proto static metric 100 10.2.54.0/24 via 10.2.54.0 dev flannel.1 onlink 172.16.1.0/24 dev ens37 proto kernel scope link src 172.16.1.120 metric 101 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 172.18.0.0/16 dev docker_gwbridge proto kernel scope link src 172.18.0.1 192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.120 metric 100
host2 輸出相似,主要區別是 host2 的 subnet 爲 10.2.54.0/24:
目的地址爲 flannel 網絡 10.2.19.0/24 的數據包都由 flannel.1 轉發。
[root@host2 ~]# ip addr show flannel.1 8: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UNKNOWN group default link/ether d2:06:eb:04:17:62 brd ff:ff:ff:ff:ff:ff inet 10.2.54.0/32 scope global flannel.1 valid_lft forever preferred_lft forever inet6 fe80::d006:ebff:fe04:1762/64 scope link valid_lft forever preferred_lft forever [root@host2 ~]# ip route default via 192.168.2.1 dev ens33 proto static metric 100 10.2.19.0/24 via 10.2.19.0 dev flannel.1 onlink 169.254.0.0/16 dev ens33.10 scope link metric 1004 169.254.0.0/16 dev ens33.20 scope link metric 1005 172.16.1.0/24 dev ens37 proto kernel scope link src 172.16.1.130 metric 101 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 172.18.0.0/16 dev docker_gwbridge proto kernel scope link src 172.18.0.1 192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.130 metric 100
編輯 host1 的 Docker 配置文件/etc/systemd/system/docker.service.d/10-machine.conf,設置 --bip
和 --mtu
。
[root@host1 docker.service.d]# pwd /etc/systemd/system/docker.service.d [root@host1 docker.service.d]# cp 10-machine.conf 10-machine.conf.bak [root@host1 docker.service.d]# vim 10-machine.conf [Service] ExecStart= ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --storage-driver overlay2 --tlsverify --tlscacert \ /etc/docker/ca.pem --tlscert /etc/docker/server.pem --tlskey /etc/docker/server-key.pem --label provider=generic \ --bip=10.2.19.1/24 --mtu=1450 Environment=
這兩個參數的值必須與 /run/flannel/subnet.env 中 FLANNEL_SUBNET
和FLANNEL_MTU
一致。
[root@host1 ~]# cat /run/flannel/subnet.env FLANNEL_NETWORK=10.2.0.0/16 FLANNEL_SUBNET=10.2.19.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=false
重啓 Docker daemon。
[root@host1 docker.service.d]# systemctl daemon-reload [root@host1 docker.service.d]# systemctl restart docker
docker 會將10.2.19.1配置到Linux bridge docker0上,並添加10.2.19.0/24的路由
[root@host1 docker.service.d]# ip r default via 192.168.2.1 dev ens33 proto static metric 100 10.2.19.0/24 dev docker0 proto kernel scope link src 10.2.19.1
host2 配置相似:
[root@host2 ~]# cd /etc/systemd/system/docker.service.d/ [root@host2 docker.service.d]# cp 10-machine.conf 10-machine.conf.bak [root@host2 docker.service.d]# vim 10-machine.conf [Service] ExecStart= ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --storage-driver overlay2 --tlsverify --tlscacert \ /etc/docker/ca.pem --tlscert /etc/docker/server.pem --tlskey /etc/docker/server-key.pem --label provider=generic \ --bip=10.2.54.1/24 --mtu=1450 Environment=
[root@host2 ~]# cat /run/flannel/subnet.env FLANNEL_NETWORK=10.2.0.0/16 FLANNEL_SUBNET=10.2.54.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=false
[root@host2 docker.service.d]# systemctl daemon-reload [root@host2 docker.service.d]# systemctl restart docker [root@host2 docker.service.d]# ip r default via 192.168.2.1 dev ens33 proto static metric 100 10.2.19.0/24 via 10.2.19.0 dev flannel.1 onlink 10.2.54.0/24 dev docker0 proto kernel scope link src 10.2.54.1
可見:flannel 沒有建立新的 docker 網絡,而是直接使用默認的 bridge 網絡。同一主機的容器經過 docker0 鏈接,跨主機流量經過 flannel.1 轉發。
在 host1 中運行容器 bbox1並查看IP:
在 host2 中運行容器 bbox2並查看IP:
[root@host1 ~]# docker run --name bbox1 -itd busybox ea50e600956f0dba7a321913b01b0977e45c34ee694be1a17b5329c6395024aa [root@host1 ~]# docker exec bbox1 ip r default via 10.2.19.1 dev eth0 10.2.19.0/24 dev eth0 scope link src 10.2.19.2 [root@host2 ~]# docker run --name bbox2 -itd busybox 0aa6b6f1461707d9f15b49e66dd240f7611ed4f831e450f7ce18b365cb9a13b0 [root@host2 ~]# docker exec bbox2 ip r default via 10.2.54.1 dev eth0 10.2.54.0/24 dev eth0 scope link src 10.2.54.2
bbox1 和 bbox2 的 IP 分別爲10.2.19.2 10.2.54.2
測試 bbox1 和 bbxo2 的連通性:
[root@host1 ~]# docker exec bbox1 ping -c 3 10.2.54.2 PING 10.2.54.2 (10.2.54.2): 56 data bytes 64 bytes from 10.2.54.2: seq=0 ttl=62 time=6.290 ms 64 bytes from 10.2.54.2: seq=1 ttl=62 time=0.828 ms 64 bytes from 10.2.54.2: seq=2 ttl=62 time=0.738 ms --- 10.2.54.2 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 0.738/2.618/6.290 ms
bbox1 可以 ping 到位於不一樣 subnet 的 bbox2,經過 traceroute
分析一下 bbox1 到 bbox2 的路徑。
[root@host1 ~]# docker exec bbox1 traceroute 10.2.54.2 traceroute to 10.2.54.2 (10.2.54.2), 30 hops max, 46 byte packets 1 bogon (10.2.19.1) 0.033 ms 0.023 ms 0.021 ms 2 bogon (10.2.54.0) 2.005 ms 0.611 ms 2.684 ms 3 bogon (10.2.54.2) 1.844 ms 1.881 ms 1.843 ms
1)bbox1和bbox2不是一個subnet 數據包發送給默認網關10.2.19.1(docker0)
2)根據host1的路由表,數據包會發給flannel.1
3)flannel.1將數據包封裝成VxLAN,經過ens33發送給host2
3)host2收到解封,發現數據包目的地址爲10.2.54.2,根據路由表將數據包發送給flannel.1 經過docker0到達bbox2
另外,flannel 是沒有 DNS 服務的,容器沒法經過 hostname 通訊。
flannel 爲每一個主機分配了獨立的 subnet,但 flannel.1 將這些 subnet 鏈接起來了,相互之間能夠路由。本質上,flannel 將各主機上相互獨立的 docker0 容器網絡組成了一個互通的大網絡,實現了容器跨主機通訊。flannel 沒有提供隔離。
由於 flannel 網絡利用的是默認的 bridge 網絡,因此容器與外網的連通方式與 bridge 網絡同樣,即:
flannel 支持多種 backend,前面咱們討論的是 vxlan backend,host-gw 是 flannel 的另外一個 backend。
與 vxlan 不一樣,host-gw 不會封裝數據包,而是在主機的路由表中建立到其餘主機 subnet 的路由條目,從而實現容器跨主機通訊。要使用 host-gw 首先修改 flannel 的配置 flannel-config.json:
[root@node1 flannel]# cp flannel-config.json flannel-config.json.bak [root@node1 flannel]# vim flannel-config.json { "Network": "10.2.0.0/16", "SubnetLen": 24, "Backend": { "Type": "host-gw" } }
Type
用 host-gw
替換原先的 vxlan
。更新 etcd 數據庫:
[root@node1 flannel]# etcdctl --endpoint=192.168.2.110:2379 set /docker-test/network/config < flannel-config.json { "Network": "10.2.0.0/16", "SubnetLen": 24, "Backend": { "Type": "host-gw" } }
Ctrl+C 掉以前 host1 和 host2 的 flanneld 進程並重啓。
host1上輸入以下
[root@host1 ~]# flanneld -etcd-endpoints=http://192.168.2.110:2379 -iface=ens33 -etcd-prefix=/docker-test/network I1218 14:44:37.393295 11092 main.go:527] Using interface with name ens33 and address 192.168.2.120 I1218 14:44:37.393941 11092 main.go:544] Defaulting external address to interface address (192.168.2.120) I1218 14:44:37.394544 11092 main.go:244] Created subnet manager: Etcd Local Manager with Previous Subnet: 10.2.19.0/24 I1218 14:44:37.394572 11092 main.go:247] Installing signal handlers I1218 14:44:37.400960 11092 main.go:386] Found network config - Backend type: host-gw #flanneld 檢查到原先已分配的 subnet 10.2.19.0/24,重用之 I1218 14:44:37.405021 11092 local_manager.go:147] Found lease (10.2.19.0/24) for current IP (192.168.2.120), reusing I1218 14:44:37.407613 11092 main.go:317] Wrote subnet file to /run/flannel/subnet.env I1218 14:44:37.407644 11092 main.go:321] Running backend. I1218 14:44:37.419064 11092 route_network.go:53] Watching for new subnet leases I1218 14:44:37.420677 11092 main.go:429] Waiting for 22h59m59.97814884s to renew lease I1218 14:44:37.422758 11092 route_network.go:85] Subnet added: 10.2.54.0/24 via 192.168.2.130 #flanneld 從 etcd 數據庫中檢索到 host2 的 subnet 10.2.54.0/24,但由於其 type=vxlan,當即忽略。 W1218 14:44:37.422806 11092 route_network.go:88] Ignoring non-host-gw subnet: type=vxlan #再次發現 subnet 10.2.17.0/24,將其加到路由表中。此次沒有忽略 subnet 的緣由是此時咱們在 host2 上重啓了 flanneld,根據當前 etcd 的配置使用 host-gw backend I1218 14:44:39.939055 11092 route_network.go:85] Subnet added: 10.2.54.0/24 via 192.168.2.130 W1218 14:44:39.939498 11092 route_network.go:102] Replacing existing route to 10.2.54.0/24 via 10.2.54.0 dev index 17 with 10.2.54.0/24 via 192.168.2.130 dev index 2.
查看 host1 的路由表,增長了一條到 10.2.19.0/24 的路由,網關爲 host2 的 IP 192.168.2.120。
[root@host1 ~]# ip route default via 192.168.2.1 dev ens33 proto static metric 100 10.2.19.0/24 dev docker0 proto kernel scope link src 10.2.19.1 10.2.54.0/24 via 192.168.2.130 dev ens33 172.16.1.0/24 dev ens37 proto kernel scope link src 172.16.1.120 metric 101 172.18.0.0/16 dev docker_gwbridge proto kernel scope link src 172.18.0.1 192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.120 metric 100
相似的,host2 啓動 flanneld 時會重用 subnet 10.2.54.0/24,並將 host1 的 subnet 10.2.19.0/24 添加到路由表中,網關爲 host1 IP 192.168.2.110。
從 /run/flannel/subnet.env 能夠看到 host-gw 使用的 MTU 爲 1500:
[root@host1 ~]# cat /run/flannel/subnet.env FLANNEL_NETWORK=10.2.0.0/16 FLANNEL_SUBNET=10.2.19.1/24 FLANNEL_MTU=1500 FLANNEL_IPMASQ=false
這與 vxlan MTU=1450 不一樣,因此應該修改 docker 啓動參數 --mtu=1500
並重啓 docker daemon。
[root@host1 ~]# vim /etc/systemd/system/docker.service.d/10-machine.conf [Service] ExecStart= ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --storage-driver overlay2 --tlsverify --tlscacert \ /etc/docker/ca.pem --tlscert /etc/docker/server.pem --tlskey /etc/docker/server-key.pem --label provider=generic \ --bip=10.2.19.1/24 --mtu=1500 Environment=
下面對 host-gw 和 vxlan 這兩種 backend 作個簡單比較。
weave 是 Weaveworks 開發的容器網絡解決方案。weave 建立的虛擬網絡能夠將部署在多個主機上的容器鏈接起來。對容器來講,weave 就像一個巨大的以太網交換機,全部容器都被接入這個交換機,容器能夠直接通訊,無需 NAT 和端口映射。除此以外,weave 的 DNS 模塊使容器能夠經過 hostname 訪問。
weave 不依賴分佈式數據庫(例如 etcd 和 consul)交換網絡信息,每一個主機上只需運行 weave 組件就能創建起跨主機容器網絡。咱們會在 host1 和 host2 上部署 weave 並實踐 weave 的各項特性。
weave 安裝很是簡單,在 host1 和 host2 上執行以下命令:
[root@host1 ~]# curl -L git.io/weave -o /usr/local/bin/weave % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 0 0 0 0 0 0 0 0 --:--:-- 0:00:02 --:--:-- 0 0 0 0 595 0 0 177 0 --:--:-- 0:00:03 --:--:-- 581k 100 52227 100 52227 0 0 8402 0 0:00:06 0:00:06 --:--:-- 38772 [root@host1 ~]# chmod a+x /usr/local/bin/weave
在host1中啓動weave
在 host1 中執行 weave launch
命令,啓動 weave 相關服務。weave 的全部組件都是以容器方式運行的,weave 會從 docker hub 下載最新的 image 並啓動容器。
weave 運行了三個容器:
weave
是主程序,負責創建 weave 網絡,收發數據 ,提供 DNS 服務等。
weaveplugin
是 libnetwork CNM driver,實現 Docker 網絡。
weaveproxy
提供 Docker 命令的代理服務,當用戶運行 Docker CLI 建立容器時,它會自動將容器添加到 weave 網絡。
[root@host1 ~]# weave launch 2.5.0: Pulling from weaveworks/weave 605ce1bd3f31: Pull complete 18e9c1482d54: Pull complete 20978932838c: Pull complete 4738e62f8d03: Pull complete 68add50beeee: Pull complete Digest: sha256:3a6086f15bf1f68092e372bfbb08d2d3679cf8a2b0f501ceb11c2fccd06a4b03 Status: Downloaded newer image for weaveworks/weave:2.5.0 latest: Pulling from weaveworks/weavedb 9b0681f946a1: Pull complete Digest: sha256:c280cf4e7208f4ca0d2514539e0f476dd12db70beacdc368793b7736de023d8d Status: Downloaded newer image for weaveworks/weavedb:latest Unable to find image 'weaveworks/weaveexec:2.5.0' locally 2.5.0: Pulling from weaveworks/weaveexec 605ce1bd3f31: Already exists 18e9c1482d54: Already exists 20978932838c: Already exists 4738e62f8d03: Already exists 68add50beeee: Already exists c10a1d502a6f: Pull complete bec5b671028d: Pull complete 0467a09afdc2: Pull complete ade22b35f72f: Pull complete Digest: sha256:425c74052faaf6e76525f5a088a584a44353fb04fa51f6d800644e0acd64fce1 Status: Downloaded newer image for weaveworks/weaveexec:2.5.0 16a9df891e0091dfc06563f66c1c0c542b8f76c697049f74a214a978e488653f [root@host1 ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE weaveworks/weavedb latest 4ac51c93545a 6 weeks ago 698B weaveworks/weaveexec 2.5.0 6568ae41694a 6 weeks ago 166MB weaveworks/weave 2.5.0 a5fd9a080afc 6 weeks ago 111MB busybox latest 59788edf1f3e 2 months ago 1.15MB
weave 會建立一個新的 Docker 網絡 weave
:
driver 爲 weavemesh
,IP 範圍 10.32.0.0/12
。
[root@host1 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 07644a612080 bridge bridge local a4d35bce9b5a docker_gwbridge bridge local 12e0fcdac081 host host local 2cb22bc8ead6 mac_net1 macvlan local ad9f7224b03d mac_net20 macvlan local 797eee7fca29 none null local 2d6fa187b80f weave weavemesh local [root@host1 ~]# docker network inspect weave [ { "Name": "weave", "Id": "2d6fa187b80fb2ca5ca072bec52b01c5370de2c1a420b143169d23299c64bfee", "Created": "2018-12-18T15:07:13.669491609+08:00", "Scope": "local", "Driver": "weavemesh", "EnableIPv6": false, "IPAM": { "Driver": "weavemesh", "Options": null, "Config": [ { "Subnet": "10.32.0.0/12" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": { "works.weave.multicast": "true" }, "Labels": {} } ]
weave 已經安裝配置完畢.
在 host1 中運行容器 bbox1:
[root@host1 ~]# eval $(weave env) [root@host1 ~]# docker run --name bbox1 -itd busybox 81e6e68e09921da4363f87274ef2a75a376a49dc53a9dbd8e3fbc8c385b927f0
首先執行 eval $(weave env)
很重要,其做用是將後續的 docker 命令發給 weave proxy 處理。若是要恢復以前的環境,可執行 eval $(weave env --restore)
。
查看一下當前容器 bbox1 的網絡配置:
[root@host1 ~]# docker exec -it bbox1 ip address 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether 02:42:0a:02:13:02 brd ff:ff:ff:ff:ff:ff inet 10.2.19.2/24 brd 10.2.19.255 scope global eth0 valid_lft forever preferred_lft forever 30: ethwe@if31: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1376 qdisc noqueue link/ether 32:ca:97:e1:98:7a brd ff:ff:ff:ff:ff:ff inet 10.32.0.1/12 brd 10.47.255.255 scope global ethwe valid_lft forever preferred_lft forever
bbox1有兩個網絡接口eth0和ethwe,其中eth0鏈接的就是默認的bridge網絡,即docker0。
如今重點分析ethwe,看分配的ip和名字猜想ethwe和weave相關,ethwe@if31告訴咱們與ethwe對應的是編號31的interface,從host1的ip link命令找出該interface
[root@host1 ~]# ip link |grep 31 31: vethwepl12678@if30: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue master weave state UP mode DEFAULT group default
vethwepl12678和ethwe是一丟veth pair,並且 vethwepl12678掛載host1的Linux bridge weave上
[root@host1 ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.02423482878d no veth5568f50 docker_gwbridge 8000.024266baa276 no weave 8000.86413d6585b5 no vethwe-bridge vethwepl12678
除了 vethwepl12678,weave 上還掛了一個 vethwe-bridge
,這是什麼?讓咱們更深刻的分析一下,查看 ip -d link
輸出:
這裏出現了多個新 interface:
① vethwe-bridge
與 vethwe-datapath
是 veth pair。
② vethwe-datapath
的父設備(master)是 datapath
。
③ datapath
是一個 openvswitch。
④ vxlan-6784
是 vxlan interface,其 master 也是 datapath
,weave 主機間是經過 VxLAN 通訊的。
weave 網絡包含兩個虛擬交換機:Linux bridge weave
和 Open vSwitch datapath
,veth pair vethwe-bridge
和 vethwe-datapath
將兩者鏈接在一塊兒。
weave
和 datapath
分工不一樣,weave
負責將容器接入 weave 網絡,datapath
負責在主機間 VxLAN 隧道中並收發數據。
在host1內再運行一個容器 bbox2。
[root@host1 ~]# docker run --name bbox2 -itd busybox 7f7dbf50a7d69ad65e35e7802e2b95e4712b7b14729f27da025f8a410749619b
weave DNS 爲容器建立了默認域名 weave.local
,bbox1 可以直接經過 hostname 與 bbox2 通訊。
[root@host1 ~]# docker exec bbox1 hostname bbox1.weave.local [root@host1 ~]# docker exec bbox1 ping -c 3 bbox2 PING bbox2 (10.32.0.2): 56 data bytes 64 bytes from 10.32.0.2: seq=0 ttl=64 time=0.240 ms 64 bytes from 10.32.0.2: seq=1 ttl=64 time=0.133 ms 64 bytes from 10.32.0.2: seq=2 ttl=64 time=0.133 ms --- bbox2 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 0.133/0.168/0.240 ms
首先在host2 執行以下命令:
[root@host2 ~]# weave launch 192.168.2.120
這裏必須指定 host1 的 IP 192.168.2.120
,這樣 host1 和 host2 才能加入到同一個 weave 網絡。
運行容器 bbox3:
[root@host2 ~]# eval $(weave env) [root@host2 ~]# docker run --name bbox3 -itd busybox c4583ee7ddca631a6997d34a268d3437836f752a8501afada020418219da2c58
bbox3 可以直接 ping bbox1 和 bbox2。
[root@host2 ~]# docker exec bbox3 ping -c 3 bbox1 PING bbox1 (10.32.0.1): 56 data bytes 64 bytes from 10.32.0.1: seq=0 ttl=64 time=6.829 ms 64 bytes from 10.32.0.1: seq=1 ttl=64 time=1.001 ms 64 bytes from 10.32.0.1: seq=2 ttl=64 time=0.898 ms --- bbox1 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 0.898/2.909/6.829 ms
bbox一、bbox2 和 bbox3 的 IP 分別爲 10.32.0.1/十二、10.32.0.2/12 和 10.44.0.0/12,注意掩碼爲 12 位,實際上這三個 IP 位於同一個 subnet 10.32.0.0/12。經過 host1 和 host2 之間的 VxLAN 隧道,三個容器邏輯上是在同一個 LAN 中的,固然能直接通訊了。
[root@host2 ~]# docker exec bbox3 ip route default via 10.2.54.1 dev eth0 10.2.54.0/24 dev eth0 scope link src 10.2.54.2 10.32.0.0/12 dev ethwe scope link src 10.44.0.0 224.0.0.0/4 dev ethwe scope link
流程以下:
一、host2 weave查詢到目的地主機 將數據經過VxLAN發送給host1 。
二、host1 weave 接收到數據,根據目的IP將數據轉發給bbox1
默認配置下,weave 使用一個大 subnet(例如 10.32.0.0/12),全部主機的容器都從這個地址空間中分配 IP,由於同屬一個 subnet,容器能夠直接通訊。若是要實現網絡隔離,能夠經過環境變量 WEAVE_CIDR
爲容器分配不一樣 subnet 的 IP,
本人測試未成功,net和ip均無效
[root@host1 ~]# docker run -e WEAVE_CDIR=net:10.32.2.0/24 -it busybox [root@host1 ~]# docker run -e WEAVE_CDIR=ip:10.32.2.8/24 -it busybox
weave 是一個私有的 VxLAN 網絡,默認與外部網絡隔離。外部網絡如何才能訪問到 weave 中的容器呢?
答案是:
要將主機加入到 weave,執行 weave expose
。
[root@host1 ~]# weave expose 10.32.0.3
這個 IP 10.32.0.3
會被配置到 host1 的 weave 網橋上。
[root@host1 ~]# ip addr show weave 22: weave: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue state UP group default qlen 1000 link/ether 86:41:3d:65:85:b5 brd ff:ff:ff:ff:ff:ff inet 10.32.0.3/12 brd 10.47.255.255 scope global weave valid_lft forever preferred_lft forever inet6 fe80::8441:3dff:fe65:85b5/64 scope link valid_lft forever preferred_lft forever
weave 網橋位於 root namespace,它負責將容器接入 weave 網絡。給 weave 配置同一 subnet 的 IP 其本質就是將 host1 接入 weave 網絡。 host1 如今已經能夠直接與同一 weave 網絡中的容器通訊了,不管容器是否位於 host1。
在 host1 中 ping 同一主機的 bbox1:
[root@host1 ~]# ping -c 3 10.32.0.1 PING 10.32.0.1 (10.32.0.1) 56(84) bytes of data. 64 bytes from 10.32.0.1: icmp_seq=1 ttl=64 time=0.511 ms 64 bytes from 10.32.0.1: icmp_seq=2 ttl=64 time=0.074 ms 64 bytes from 10.32.0.1: icmp_seq=3 ttl=64 time=0.084 ms --- 10.32.0.1 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2001ms rtt min/avg/max/mdev = 0.074/0.223/0.511/0.203 ms
ping host2上的bbox3
[root@host1 ~]# ping -c 3 10.44.0.0 PING 10.44.0.0 (10.44.0.0) 56(84) bytes of data. 64 bytes from 10.44.0.0: icmp_seq=1 ttl=64 time=2.28 ms 64 bytes from 10.44.0.0: icmp_seq=2 ttl=64 time=0.780 ms 64 bytes from 10.44.0.0: icmp_seq=3 ttl=64 time=0.691 ms --- 10.44.0.0 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2003ms rtt min/avg/max/mdev = 0.691/1.253/2.288/0.732 ms
接下來要讓其餘非 weave 主機訪問到 bbox1 和 bbox3,只需將網關指向 host1。例如在 node1 192.168.2.110 上添加以下路由:
[root@node1 ~]# ip route add 10.32.0.0/12 via 192.168.2.120 [root@node1 ~]# ip route default via 192.168.2.1 dev ens33 proto static metric 100 10.32.0.0/12 via 192.168.2.120 dev ens33 172.16.1.0/24 dev ens37 proto kernel scope link src 172.16.1.110 metric 101 172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.110 metric 100 [root@node1 ~]# ping -c 3 10.44.0.0 PING 10.44.0.0 (10.44.0.0) 56(84) bytes of data. 64 bytes from 10.44.0.0: icmp_seq=1 ttl=63 time=1.62 ms 64 bytes from 10.44.0.0: icmp_seq=2 ttl=63 time=2.52 ms 64 bytes from 10.44.0.0: icmp_seq=3 ttl=63 time=1.08 ms --- 10.44.0.0 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 4008ms rtt min/avg/max/mdev = 1.087/1.585/2.521/0.497 ms
經過上面的配置咱們實現了外網到 weave 這個方向的通訊,反方向呢?
其實答案很簡單:由於容器自己就掛在默認的 bridge 網絡上,docker0 已經實現了 NAT,因此容器無需額外配置就能訪問外網。
10.32.0.0/12 是 weave 網絡使用的默認 subnet,若是此地址空間與現有 IP 衝突,能夠經過 --ipalloc-range
分配特定的 subnet。
weave launch --ipalloc-range 10.2.0.0/16
不過請確保全部 host 都使用相同的 subnet。
Calico 是一個純三層的虛擬網絡方案,Calico 爲每一個容器分配一個 IP,每一個 host 都是 router,把不一樣 host 的容器鏈接起來。與 VxLAN 不一樣的是,Calico 不對數據包作額外封裝,不須要 NAT 和端口映射,擴展性和性能都很好。
與其餘容器網絡方案相比,Calico 還有一大優點:network policy。用戶能夠動態定義 ACL 規則,控制進出容器的數據包,實現業務需求。
Calico 依賴 etcd 在不一樣主機間共享和交換信息,存儲 Calico 網絡狀態。咱們將在 node1 192.168.2.110 上運行 etcd。
Calico 網絡中的每一個主機都須要運行 Calico 組件,提供容器 interface 管理、動態路由、動態 ACL、報告狀態等功能。
啓動etcd
etcd 安裝配置詳細方法請參考 flannel 章節.
在node1 運行命令啓動etcd:
[root@node1 ~]# etcd -listen-client-urls http://192.168.2.110:2379 -advertise-client-urls http://192.168.2.110:2379
修改 host1 和 host2 的 Docker daemon 配置文件 /etc/systemd/system/docker.service.d/10-machine.conf, 鏈接 etcd:
[root@host1 ~]# !vim vim /etc/systemd/system/docker.service.d/10-machine.conf [Service] ExecStart= ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 -H unix:///var/run/docker.sock --storage-driver overlay2 --tlsverify --tlscacert \ /etc/docker/ca.pem --tlscert /etc/docker/server.pem --tlskey /etc/docker/server-key.pem --label provider=generic \ --cluster-store=etcd://192.168.2.110:2379 Environment= [root@host1 ~]# systemctl daemon-reload [root@host1 ~]# systemctl restart docker
host1 和 host2 下載 calicoctl:
[root@host1 ~]# wget -O /usr/local/bin/calicoctl https://github.com/projectcalico/calicoctl/releases/download/v1.6.5/calicoctl [root@host1 ~]# chmod +x /usr/local/bin/calicoctl [root@host1 ~]# calicoctl --version calicoctl version v1.6.5, build 614fcf12
在 host1 和 host2 上啓動 calico:
建立calico配置文件#### 注意格式和縮進
[root@host1 ~]# cat /etc/calico/calicoctl.cfg apiVersion: v1 kind: calicoApiConfig metadata: spec: datastoreType: "etcdv2" etcdEndpoints: "http://192.168.2.110:2379"
啓動
[root@host1 ~]# calicoctl node run --node-image=quay.io/calico/node:v2.6.12 -c /etc/calico/calicoctl.cfg Running command to load modules: modprobe -a xt_set ip6_tables Enabling IPv4 forwarding Enabling IPv6 forwarding Increasing conntrack limit Removing old calico-node container (if running). Running the following command to start calico-node: docker run --net=host --privileged --name=calico-node -d --restart=always -e CALICO_LIBNETWORK_ENABLED=true -e ETCD_ENDPOINTS=http://192.168.2.110:2379 -e NODENAME=host1 -e CALICO_NETWORKING_BACKEND=bird -v /var/log/calico:/var/log/calico -v /var/run/calico:/var/run/calico -v /lib/modules:/lib/modules -v /run:/run -v /run/docker/plugins:/run/docker/plugins -v /var/run/docker.sock:/var/run/docker.sock quay.io/calico/node:v2.6.12 Image may take a short time to download if it is not available locally. Container started, checking progress logs. 2018-12-19 03:09:54.698 [INFO][8] startup.go 173: Early log level set to info 2018-12-19 03:09:54.698 [INFO][8] client.go 202: Loading config from environment 2018-12-19 03:09:54.698 [INFO][8] startup.go 83: Skipping datastore connection test 2018-12-19 03:09:54.704 [INFO][8] startup.go 259: Building new node resource Name="host1" 2018-12-19 03:09:54.704 [INFO][8] startup.go 273: Initialise BGP data 2018-12-19 03:09:54.707 [INFO][8] startup.go 467: Using autodetected IPv4 address on interface ens37: 172.16.1.120/24 2018-12-19 03:09:54.707 [INFO][8] startup.go 338: Node IPv4 changed, will check for conflicts 2018-12-19 03:09:54.710 [INFO][8] etcd.go 430: Error enumerating host directories error=100: Key not found (/calico) [15] 2018-12-19 03:09:54.711 [INFO][8] startup.go 530: No AS number configured on node resource, using global value 2018-12-19 03:09:54.713 [INFO][8] etcd.go 105: Ready flag is now set 2018-12-19 03:09:54.715 [INFO][8] client.go 133: Assigned cluster GUID ClusterGUID="28d6ce342fb54ad69e6edb9d752e16d4" 2018-12-19 03:09:54.733 [INFO][8] startup.go 419: CALICO_IPV4POOL_NAT_OUTGOING is true (defaulted) through environment variable 2018-12-19 03:09:54.733 [INFO][8] startup.go 659: Ensure default IPv4 pool is created. IPIP mode: off 2018-12-19 03:09:54.735 [INFO][8] startup.go 670: Created default IPv4 pool (192.168.0.0/16) with NAT outgoing true. IPIP mode: off 2018-12-19 03:09:54.736 [INFO][8] startup.go 419: FELIX_IPV6SUPPORT is true (defaulted) through environment variable 2018-12-19 03:09:54.736 [INFO][8] startup.go 626: IPv6 supported on this platform: true 2018-12-19 03:09:54.736 [INFO][8] startup.go 419: CALICO_IPV6POOL_NAT_OUTGOING is false (defaulted) through environment variable 2018-12-19 03:09:54.736 [INFO][8] startup.go 659: Ensure default IPv6 pool is created. IPIP mode: off 2018-12-19 03:09:54.738 [INFO][8] startup.go 670: Created default IPv6 pool (fd80:24e2:f998:72d6::/64) with NAT outgoing false. IPIP mode: off 2018-12-19 03:09:54.767 [INFO][8] startup.go 131: Using node name: host1 2018-12-19 03:09:55.081 [INFO][12] client.go 202: Loading config from environment Starting libnetwork service Calico node started successfully
啓動過程以下:
① 設置主機網絡,例如 enable IP forwarding。
② 下載並啓動 calico-node 容器,calico 會以容器的形式運行(與 weave 相似)。
③ 鏈接 etcd。
④ calico 啓動成功
查看calico運行狀態:
[root@host1 ~]# calicoctl node status Calico process is running. IPv4 BGP status +--------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+-------------------+-------+----------+-------------+ | 172.16.1.130 | node-to-node mesh | up | 03:18:14 | Established | +--------------+-------------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found. [root@host2 scripts]# calicoctl node status Calico process is running. IPv4 BGP status +--------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+-------------------+-------+----------+-------------+ | 172.16.1.120 | node-to-node mesh | up | 03:18:15 | Established | +--------------+-------------------+-------+----------+-------------+ IPv6 BGP status No IPv6 peers found.
建立calico網絡
在 host1 或 host2 上執行以下命令建立 calico 網絡 cal_ent1:
[root@host1 ~]# docker network create --driver calico --ipam-driver calico-ipam cal_net1 d0760b57695c3dbcf5cb69571984f909456502f69c9a13030305905da68fb4dd
--driver calico
指定使用 calico 的 libnetwork CNM driver。
--ipam-driver calico-ipam
指定使用 calico 的 IPAM driver 管理 IP。
calico 爲 global 網絡,etcd 會將 cal_net 同步到全部主機。
[root@host1 ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 963caa36a8d1 bridge bridge local d0760b57695c cal_net1 calico global 12e0fcdac081 host host local 797eee7fca29 none null local 3a1e0d8a2730 weave weavemesh local
在 host1 中運行容器 bbox1 並鏈接到 cal_net1:
[root@host1 ~]# docker run --name bbox1 --net cal_net1 -itd busybox 19d88eb8ee187ef9a0e1592b6ae9f59dafa7481d6e4eea9a6e25c8ca30b316b1
查看 bbox1 的網絡配置。
[root@host1 ~]# docker exec bbox1 ip address 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 26: cali0@if27: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff inet 192.168.119.2/32 brd 192.168.119.2 scope global cali0 valid_lft forever preferred_lft forever
cali0
是 calico interface,分配的 IP 爲 192.168.119.2
。cali0 對應 host1 編號 27
的 interface cali08a3cd4c842
。
[root@host1 ~]# ip a ...... 27: cali08a3cd4c842@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether c2:3d:a6:92:fe:b8 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::c03d:a6ff:fe92:feb8/64 scope link valid_lft forever preferred_lft forever
host1 將做爲 router 負責轉發目的地址爲 bbox1 的數據包。
[root@host1 ~]# ip route default via 192.168.2.1 dev ens33 proto static metric 100 10.2.19.0/24 dev docker0 proto kernel scope link src 10.2.19.1 172.16.1.0/24 dev ens37 proto kernel scope link src 172.16.1.120 metric 101 192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.120 metric 100 blackhole 192.168.119.0/26 proto bird 192.168.119.2 dev cali08a3cd4c842 scope link
全部發送到 bbox1 的數據都會發給 cali08a3cd4c842
,由於 cali08a3cd4c842
與 cali0
是一對 veth pair,bbox1 可以接收到數據。
接下來咱們在 host2 中運行容器 bbox2,也鏈接到 cal_net1:
[root@host2 scripts]# docker run --name bbox2 --net cal_net1 -itd busybox ac8faffa86318a830397a8030ca136386fec0063d75e050426a08444bfdcbced [root@host2 scripts]# docker exec bbox2 ip address 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 20: cali0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff inet 192.168.183.66/32 brd 192.168.183.66 scope global cali0 valid_lft forever preferred_lft forever [root@host2 scripts]# ip route default via 192.168.2.1 dev ens33 proto static metric 100 10.2.54.0/24 dev docker0 proto kernel scope link src 10.2.54.1 172.16.1.0/24 dev ens37 proto kernel scope link src 172.16.1.130 metric 101 192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.130 metric 100 192.168.119.0/26 via 172.16.1.120 dev ens37 proto bird blackhole 192.168.183.64/26 proto bird 192.168.183.66 dev calia8c668b6de2 scope link
bbox2的IP爲192.168.183.66 host2主機增長了兩條路由:
一、目的地址爲host1容器subnet192.168.119.0/26的路由
二、目的地址爲本地bbox2的192.168.183.66的路由
一樣 host1也自動添加了 192.168.183.64/26的路由
[root@host1 ~]# ip route default via 192.168.2.1 dev ens33 proto static metric 100 10.2.19.0/24 dev docker0 proto kernel scope link src 10.2.19.1 172.16.1.0/24 dev ens37 proto kernel scope link src 172.16.1.120 metric 101 192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.120 metric 100 blackhole 192.168.119.0/26 proto bird 192.168.119.2 dev cali08a3cd4c842 scope link 192.168.183.64/26 via 172.16.1.130 dev ens37 proto bird
測試一下bbox1和bbox2的連通性
[root@host1 ~]# docker exec bbox1 ping -c 3 bbox2 PING bbox2 (192.168.183.66): 56 data bytes 64 bytes from 192.168.183.66: seq=0 ttl=62 time=6.818 ms 64 bytes from 192.168.183.66: seq=1 ttl=62 time=0.879 ms 64 bytes from 192.168.183.66: seq=2 ttl=62 time=0.773 ms --- bbox2 ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 0.773/2.823/6.818 ms
ping 成功,
數據包流程爲
一、bbox1的數據包從cal0發出
[root@host1 ~]# docker exec bbox1 ip route default via 169.254.1.1 dev cali0 169.254.1.1 dev cali0 scope link
二、數據通過 veth pair到達host1,查看路由表數據由ens37 發給host2(192.168.2.130 172.16.1.130 爲內網IP地址)
[root@host1 ~]# ip route default via 192.168.2.1 dev ens33 proto static metric 100 10.2.19.0/24 dev docker0 proto kernel scope link src 10.2.19.1 172.16.1.0/24 dev ens37 proto kernel scope link src 172.16.1.120 metric 101 192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.120 metric 100 blackhole 192.168.119.0/26 proto bird 192.168.119.2 dev cali08a3cd4c842 scope link 192.168.183.64/26 via 172.16.1.130 dev ens37 proto bird
三、host2收到數據包,根據路由表發送給,進而經過veth pair到達bbox2
[root@host2 scripts]# ip route default via 192.168.2.1 dev ens33 proto static metric 100 10.2.54.0/24 dev docker0 proto kernel scope link src 10.2.54.1 172.16.1.0/24 dev ens37 proto kernel scope link src 172.16.1.130 metric 101 192.168.2.0/24 dev ens33 proto kernel scope link src 192.168.2.130 metric 100 192.168.119.0/26 via 172.16.1.120 dev ens37 proto bird blackhole 192.168.183.64/26 proto bird 192.168.183.66 dev calia8c668b6de2 scope link
接下來咱們看看不一樣 calico 網絡之間的連通性。
建立 cal_net2。
[root@host2 scripts]# docker network create --driver calico --ipam-driver calico-ipam cal_net2 2b7e049df6cd8b0ea5d346d1aa80500a524b1ee14a9c0e9c6faa7b9ef5128e2d
在host1中運行容器bbox3,鏈接到cal_net2
[root@host1 ~]# docker run --name bbox3 --net cal_net2 -itd busybox 157cf44e2b18e4c2b023805241818f34d444bb9bc0cc122f002e59ec8da8ae6e [root@host1 ~]# docker exec bbox3 ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 28: cali0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff inet 192.168.119.3/32 brd 192.168.119.3 scope global cali0 valid_lft forever preferred_lft forever
calico給bbix分配的ip爲 192.168.119.3
驗證bbox1和bbox3的連通性
[root@host1 ~]# docker exec bbox3 ping -c 3 192.168.119.2 PING 192.168.119.2 (192.168.119.2): 56 data bytes --- 192.168.119.2 ping statistics --- 3 packets transmitted, 0 packets received, 100% packet loss
雖然 bbox1 和 bbox3 都位於 host1,並且都在一個 subnet 192.168.119.0/26,但它們屬於不一樣的 calico 網絡,默認不能通行。
calico 默認的 policy 規則是:容器只能與同一個 calico 網絡中的容器通訊。
calico 的每一個網絡都有一個同名的 profile,profile 中定義了該網絡的 policy。咱們具體看一下 cal_net1 的 profile:
[root@host1 ~]# calicoctl get profile cal_net1 -o yaml - apiVersion: v1 kind: profile metadata: name: cal_net1 tags: - cal_net1 spec: egress: - action: allow destination: {} source: {} ingress: - action: allow destination: {} source: tag: cal_net1
1)name: cal_net1 :命名爲cal_net1 這就是calico網絡cal_net1的prifile
2)- cal_net1: 爲 profile 添加一個 tag cal_net1
。注意,這個 tag 雖然也叫 cal_net1
,其實能夠隨便設置,這跟上面的 name: cal_net1
沒有任何關係。此 tag 後面會用到。
3) egress:對從容器發出的數據包進行控制,當前沒有任何限制。
4) ingress: 對進入容器的數據包進行限制,當前設置是接收來自 tag cal_net1
的容器,根據第 ① 步設置咱們知道,實際上就是隻接收本網絡的數據包,這也進一步解釋了前面的實驗結果。
既然這是默認 policy,那就有方法定製 policy,這也是 calico 較其餘網絡方案最大的特性。
Calico 可以讓用戶定義靈活的 policy 規則,精細化控制進出容器的流量,下面咱們就來實踐一個場景:
cal_web
並部署一個 httpd 容器 web1
。cal_net2
中的容器訪問 web1
的 80 端口。首先建立cal_web
[root@host1 ~]# docker network create --driver calico --ipam-driver calico-ipam cal_web 741b5fded82ffba3edac7d94ed405e533cfcc63b121bcbed3c892bf0d71cac85
在 host1 中運行容器 web1,鏈接到 cal_web:
[root@host1 ~]# docker run --name web1 --net cal_web -d httpd 17eccfdf171de1355deef178fd33fd0e3a2cb3ec4fcee7945f8bf949c52c9b3f [root@host1 ~]# docker exec -it web1 /bin/sh # cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 192.168.119.17 17eccfdf171d [root@host1 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 17eccfdf171d httpd "httpd-foreground" 4 minutes ago Up 4 minutes
web1的ip爲192.168.119.17
目前 bbox3 還沒法訪問 web1 的 80 端口。
[root@host1 ~]# docker exec bbox3 wget 192.168.119.17 Connecting to 192.168.119.17 (192.168.119.17:80) wget: can't connect to remote host (192.168.119.17): Connection timed out
建立 policy 文件 web.yml,內容爲:
[root@host1 ~]# vim web.yml - apiVersion: v1 kind: profile metadata: name: cal_web spec: ingress: - action: allow protocol: tcp source: tag: cal_net2 destination: ports: - 80
profile 與 cal_web 網絡同名,cal_web 的全部容器(web1)都會應用此 profile 中的 policy。
ingress 容許 cal_net2 中的容器(bbox3)訪問。
只開放 80 端口。
應用該 policy。
[root@host1 ~]# calicoctl apply -f web.yml Successfully applied 1 'profile' resource(s)
如今bbox3已經能夠訪問web1的http服務
不過 ping 仍是不行,由於只放開了 80 端口。
[root@host1 ~]# docker exec bbox3 wget 192.168.119.17 Connecting to 192.168.119.17 (192.168.119.17:80) index.html 100% |********************************| 45 0:00:00 ETA [root@host1 ~]# docker exec bbox3 ping -c 3 192.168.119.17 PING 192.168.119.17 (192.168.119.17): 56 data bytes --- 192.168.119.17 ping statistics --- 3 packets transmitted, 0 packets received, 100% packet loss
上面這個例子比較簡單,不過已經向咱們展現了 calico 強大的 policy 功能。經過 policy,能夠動態實現很是複雜的容器訪問控制。有關 calico policy 更多的配置,可參看官網文檔 http://docs.projectcalico.org/v2.0/reference/calicoctl/resources/policy。
咱們沒有特別配置,calico 會爲自動爲網絡分配 subnet,固然咱們也能夠定製。
首先定義一個 IP Pool
[root@host1 ~]# vim ipPool.yml - apiVersion: v1 kind: ipPool metadata: cidr: 17.2.0.0/16 [root@host1 ~]# calicoctl create -f ipPool.yml Successfully created 1 'ipPool' resource(s)
用此 IP Pool 建立 calico 網絡。
[root@host1 ~]# docker network create --driver calico --ipam-driver calico-ipam --subnet=17.2.0.0/16 my_net c09c90a736872401a991f2431bcf7275c5ba51e3c1e271b466d00e24d3f924e7
此時運行容器將分配到指定 subnet 中的 IP。
[root@host1 ~]# docker run --net my_net -it busybox / # ip address 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 62: cali0@if63: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff inet 17.2.119.0/32 brd 17.2.119.0 scope global cali0 valid_lft forever preferred_lft forever
固然也能夠經過 --ip
爲容器指定 IP,但必須在 subnet 範圍以內。
[root@host1 ~]# docker run --net my_net --ip 17.2.3.11 -it busybox [root@host1 ~]# docker exec b3c485a2e81a ip address 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 68: cali0@if69: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff inet 17.2.3.11/32 brd 17.2.3.11 scope global cali0 valid_lft forever preferred_lft forever
咱們將從以下幾個方面比較,你們能夠根據不一樣場景選擇最合適的方案。
網絡模型
採用何種網絡模型支持 multi-host 網絡?
Distributed Store
是否須要 etcd 或 consul 這類分佈式 key-value 數據庫存儲網絡信息?
IPMA
如何管理容器網絡的 IP?
連通與隔離
提供怎樣的網絡連通性?支持容器間哪一個級別和哪一個類型的隔離?
性能
性能比較。
跨主機網絡意味着將不一樣主機上的容器用同一個虛擬網絡鏈接起來。這個虛擬網絡的拓撲結構和實現技術就是網絡模型。
Docker overlay 如名稱所示,是 overlay 網絡,創建主機間 VxLAN 隧道,原始數據包在發送端被封裝成 VxLAN 數據包,到達目的後在接收端解包。
Macvlan 網絡在二層上經過 VLAN 鏈接容器,在三層上依賴外部網關鏈接不一樣 macvlan。數據包直接發送,不須要封裝,屬於 underlay 網絡。
Flannel 咱們討論了兩種 backend:vxlan 和 host-gw。vxlan 與 Docker overlay 相似,屬於 overlay 網絡。host-gw 將主機做爲網關,依賴三層 IP 轉發,不須要像 vxlan 那樣對包進行封裝,屬於 underlay 網絡。
Weave 是 VxLAN 實現,屬於 overlay 網絡。
Docker Overlay、Flannel 和 Calico 都須要 etcd 或 consul。Macvlan 是簡單的 local 網絡,不須要保存和共享網絡信息。Weave 本身負責在主機間交換網絡配置信息,也不須要 Distributed Store。
Docker Overlay 網絡中全部主機共享同一個 subnet,容器啓動時會順序分配 IP,能夠經過 --subnet
定製此 IP 空間。
Macvlan 須要用戶本身管理 subnet,爲容器分配 IP,不一樣 subnet 通訊依賴外部網關。
Flannel 爲每一個主機自動分配獨立的 subnet,用戶只須要指定一個大的 IP 池。不一樣 subnet 之間的路由信息也由 Flannel 自動生成和配置。
Weave 的默認配置下全部容器使用 10.32.0.0/12 subnet,若是此地址空間與現有 IP 衝突,能夠經過 --ipalloc-range
分配特定的 subnet。
Calico 從 IP Pool(可定製)中爲每一個主機分配本身的 subnet。
同一 Docker Overlay 網絡中的容器能夠通訊,但不一樣網絡之間沒法通訊,要實現跨網絡訪問,只有將容器加入多個網絡。與外網通訊能夠經過 docker_gwbridge 網絡。
Macvlan 網絡的連通或隔離徹底取決於二層 VLAN 和三層路由。
不一樣 Flannel 網絡中的容器直接就能夠通訊,沒有提供隔離。與外網通訊能夠經過 bridge 網絡。
Weave 網絡默認配置下全部容器在一個大的 subnet 中,能夠自由通訊,若是要實現隔離,須要爲容器指定不一樣的 subnet 或 IP。與外網通訊的方案是將主機加入到 weave 網絡,並把主機看成網關。
Calico 默認配置下只容許位於同一網絡中的容器之間通訊,但經過其強大的 Policy 可以實現幾乎任意場景的訪問控制。
性能測試是一個很是嚴謹和複雜的工程,這裏咱們只嘗試從技術方案的原理上比較各方案的性能。
最樸素的判斷是:Underlay 網絡性能優於 Overlay 網絡。
Overlay 網絡利用隧道技術,將數據包封裝到 UDP 中進行傳輸。由於涉及數據包的封裝和解封,存在額外的 CPU 和網絡開銷。雖然幾乎全部 Overlay 網絡方案底層都採用 Linux kernel 的 vxlan 模塊,這樣能夠儘可能減小開銷,但這個開銷與 Underlay 網絡相比仍是存在的。因此 Macvlan、Flannel host-gw、Calico 的性能會優於 Docker overlay、Flannel vxlan 和 Weave。
Overlay 較 Underlay 能夠支持更多的二層網段,能更好地利用已有網絡,以及有避免物理交換機 MAC 表耗盡等優點,因此在方案選型的時候須要綜合考慮。