目录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 表耗尽等优点,因此在方案选型的时候须要综合考虑。