网络文档(两节课)
目录
- I.Docker 中的网络功能介绍(原理)
- II.Docker NAT iptables实现
- III.Docker 网络配置
- IV.自定义网桥
- V.
none
模式配置网络 - VI.创建一个点到点连接
- VII.
DNS/HOSTNAME
自定义 - VIII.docker中的容器互联系统
- IX.
pipework
I.Docker 中的网络功能介绍(原理)
1.检查是否开启路由转发
[root@daring ~]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
[root@daring ~]# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
当使用–P
(大写)标记时,Docker 会随机映射一个随机的端口到内部容器开放的网络端口。
注:-P
使用时需要指定--expose
选项或dockerfile
中用expose
指令容器要暴露的端口,指定需要对外提供服务的端口
使用 docker ps
可以看到,本地主机的32770
被映射到了容器的22端口,本地主机的32769被映射到了容器的80端口,本地主机的32768被映射到了容器的443 端口。
[root@daring ~]# docker run -d -P docker.io/jasonperma/centos6_http_ssh
18a82f52edfbf05db01c7dceea723d74fd047176912a4420d9d8e3f40bda1d60
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
18a82f52edfb docker.io/jasonperma/centos6_http_ssh "/bin/bash /run.sh" 10 seconds ago Up 3 seconds 0.0.0.0:32769->22/tcp, 0.0.0.0:32768->80/tcp sharp_ramanujan
此时访问本机的 32769端口即可访问容器内 ssh 应用。
[root@daring ~]# ssh [email protected] -p 32769
The authenticity of host '[192.168.142.165]:32769 ([192.168.142.165]:32769)' can't be established.
RSA key fingerprint is c2:70:72:11:7e:2a:6f:74:f9:88:18:c9:e5:4b:03:84.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.142.165]:32769' (RSA) to the list of known hosts.
[email protected]'s password:
注:192.168.142.165是宿主主机地址。
查看容器运行的httpd进程
[admin@18a82f52edfb ~]$ pgrep httpd
8
9
10
11
12
13
14
15
16
此时访问本机的 32768端口即可访问容器内 web 应用
-p(小写)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有
ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
注意:
- 容器有自己的内部网络和 ip 地址(使用
docker inspect
可以获取所有的变量。) -p
标记可以多次使用来绑定多个端口
2.映射所有接口地址
使用 hostPort:containerPort
格式,将本地的10111端口映射到容器的 22 端口,本地的801端口映射到容器的80端口可以执行
[root@daring ~]# docker run -d -p 10111:22 -p 801:80 centos:http
66f19ff7d4f4f449917fa9473b9d1607e5773f7e8d26ea38c960868a2a6d941d
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66f19ff7d4f4 centos:http "/bin/bash /run.sh" 49 seconds ago Up 20 seconds 443/tcp, 0.0.0.0:10111->22/tcp, 0.0.0.0:801->80/tcp drunk_noether
测试访问:
1) ssh测试:
使用xshell工具:
[admin@66f19ff7d4f4 ~]$ ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:56 errors:0 dropped:0 overruns:0 frame:0
TX packets:39 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:6384 (6.2 KiB) TX bytes:5387 (5.2 KiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
[admin@66f19ff7d4f4 ~]$ ip r
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2
测试web访问
3.映射到指定地址的指定端口
可以使用 ip:hostPort:containerPort 格式,指定映射使用一个特定地址,比如宿主机网卡配置的一个地址192.168.1.102
[root@daring ~]# docker run -dit -p 192.168.142.165:10112:22 -p 192.168.142.165:80:80 centos:http
33e8b01c7d3ec212b708eb1b04a3820a015ec055b7adf322969f409068c87145
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
33e8b01c7d3e centos:http "/bin/bash /run.sh" 4 seconds ago Up 3 seconds 192.168.142.165:80->80/tcp, 443/tcp, 192.168.142.165:10112->22/tcp tiny_goldstine
4.映射到指定地址的任意端口
使用 ip::containerPort
绑定192.168.1.102的任意端口到容器的80端口,本地主机会自动分配一个口。--name
为启动的容器指定一个容器名。
[root@daring ~]# docker run -d -p 192.168.142.165::80 --name webserver centos:http
449aa79e8dcba836f42bf1242851f0119eb003bdaa98fa1fca97e37c087f8f0d
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
449aa79e8dcb centos:http "/bin/bash /run.sh" 47 seconds ago Up 46 seconds 22/tcp, 443/tcp, 192.168.142.165:32768->80/tcp webserver
注:还可以使用 udp 标记来指定 udp 端口
# docker run -d -p 127.0.0.1:5000:5000/udp –name db4 commit:v1
[root@daring ~]# docker run -d -p 127.0.0.1:5000:5000/udp --name db4 centos:http
064cd9144bcc4145fc27f1da5afb012df2214fca390536630b30c7842e288119
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
064cd9144bcc centos:http "/bin/bash /run.sh" 2 seconds ago Up 1 seconds 22/tcp, 80/tcp, 443/tcp, 127.0.0.1:5000->5000/udp db4
5.查看映射端口配置
使用 docker port
来查看当前映射的端口配置,也可以查看到绑定的地址
[root@daring ~]# docker port webserver
80/tcp -> 192.168.142.165:32768
II.Docker NAT iptables实现
默认情况下,容器可以主动访问到外部网络的连接,但是外部网络无法访问到容器
1.容器访问外部实现
容器所有到外部网络的连接,源地址都会被 NAT
成本地系统的 IP 地址(即docker0地址)。这是使用 iptables
的源地址伪装操作实现的
查看主机的 NAT 规则
[root@daring ~]# iptables -t nat -vnL
...//省略
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1 72 MASQUERADE all -- * !docker0 172.17.0.0/16 0.0.0.0/0
88 6684 POSTROUTING_direct all -- * * 0.0.0.0/0 0.0.0.0/0
88 6684 POSTROUTING_ZONES_SOURCE all -- * * 0.0.0.0/0 0.0.0.0/0
88 6684 POSTROUTING_ZONES all -- * * 0.0.0.0/0 0.0.0.0/0
...//省略
其中,上述规则将所有源地址在 172.17.0.0/16 网段,目标地址为其他网段(外部网络)的流量动态伪装为从系统网卡发出。MASQUERADE
跟传统 SNAT
的好处是它能动态从网卡获取地址。
2.外部访问容器实现
容器允许外部访问,可以在 docker run
时候通过 -p
或-P
参数来启用,不管用那种办法,其实也是在本地的 iptable
的nat
表中添加相应的规则
使用
-P
时:
[root@daring ~]# docker run -d -P centos:http
28ec06f8fe3bc93ab42f826c1532327dc44a0030fd3cfa0e7f7dc31433120e6c
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
28ec06f8fe3b centos:http "/bin/bash /run.sh" 2 seconds ago Up 1 seconds 0.0.0.0:32770->22/tcp, 0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp sleepy_wozniak
# iptables -t nat –nvL
[root@daring ~]# iptables -t nat -nvL
...//省略
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32768 to:172.17.0.2:443
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32769 to:172.17.0.2:80
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:32770 to:172.17.0.2:22
使用 -p 80:80 时:
[root@daring ~]# docker run -d -p 192.168.142.165:80:80 centos:http
331413e4ae7f9d62d34e95876bd8067c0b0e1821a733ce6e18c108f49ae7c864
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
331413e4ae7f centos:http "/bin/bash /run.sh" 2 seconds ago Up 1 seconds 22/tcp, 443/tcp, 192.168.142.165:80->80/tcp hopeful_kowalevski
# iptables -t nat –nvL
[root@daring ~]# iptables -t nat -nvL
...//省略
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
0 0 DNAT tcp -- !docker0 * 0.0.0.0/0 192.168.142.165 tcp dpt:80 to:172.17.0.2:80
...//省略
3.docker0
网桥
Docker服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。
Docker 默认指定了 docker0 接口的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信 由于目前 Docker 网桥是 Linux 网桥,用户可以使用 brctl show 来查看网桥和端口连接信息。
[root@daring ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02427de56e4b no veth24b899a
注:brctl 命令在centos中可以使用yum install bridge-utils 来安装 每次创建一个新容器的时候,Docker 从可用的地址段中选择一个空闲的 IP 地址分配给容器的 eth0端口。使用本地主机上 docker0 接口的 IP 作为所有容器的默认网关。
[root@daring ~]# docker run -d -P centos:http
b5bb1505c35c6f3ec26f14d913a9c57a05f51ea844cbd330734f20d09e957456
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b5bb1505c35c centos:http "/bin/bash /run.sh" 3 seconds ago Up 1 seconds 0.0.0.0:32773->22/tcp, 0.0.0.0:32772->80/tcp, 0.0.0.0:32771->443/tcp small_jang
[root@daring ~]# ssh [email protected] -p 32773
The authenticity of host '[192.168.142.165]:32773 ([192.168.142.165]:32773)' can't be established.
RSA key fingerprint is 70:8d:e2:a3:b3:26:b0:91:9c:d0:df:7e:a8:de:8b:91.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.142.165]:32773' (RSA) to the list of known hosts.
[email protected]'s password:
[admin@b5bb1505c35c ~]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
20: eth0@if21: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:2/64 scope link
valid_lft forever preferred_lft forever
[admin@b5bb1505c35c ~]$ ip r
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2
[admin@b5bb1505c35c ~]$
III.Docker 网络配置
Docker 四种网络模式
docker run 创建 Docker 容器时,可以用 --net 选项指定容器的网络模式,Docker 有以下 4 种网络模式:
- host 模式,使用
--net=host
指定。 - container 模式,使用
--net=container:NAMEorID
指定。 - none 模式,使用
--net=none
指定。 - bridge 模式,使用
--net=bridge
指定,默认设置。
1.host
模式
如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。
例如,我们在192.168.1.102/24 的机器上用 host 模式启动一个含有 web 应用的 Docker 容器,监听 tcp 80 端口。当我们在容器中执行任何类似 ifconfig 命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用192.168.1.102:80 即可,不用任何 NAT 转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
启动容器前,执行pgrep http查看宿主机httpd进程
[root@daring ~]# pgrep httpd
[root@daring ~]#
上面显示结果说明宿主机没有httpd进程运行
用 host 模式启动一个含有 web 应用的 Docker 容器
[root@daring ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/jasonperma/centos6_http_ssh latest 2460b63219d7 2 days ago 291.9 MB
centos http b097bfe56b56 6 days ago 291.7 MB
docker.io/centos centos6 cf2c3ece5e41 4 weeks ago 194.6 MB
[root@daring ~]# docker run -dit --net=host centos:http
b6c903551d938f502a2a9d980a534769d0cdd8a9b124190301464411fed7f427
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b6c903551d93 centos:http "/bin/bash /run.sh" 4 seconds ago Up 3 seconds condescending_bose
[root@daring ~]# pgrep httpd
4776
4777
4778
4779
4780
4781
4782
4783
4784
用浏览器访问宿主机地址的80端口
注意防火墙:
[root@daring ~]# firewall-cmd --add-port=80/tcp
Warning: ALREADY_ENABLED
2.container
模式
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。 运行一个容器:查看容器的IP
[root@daring ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/jasonperma/centos6_http_ssh latest 2460b63219d7 2 days ago 291.9 MB
centos http b097bfe56b56 6 days ago 291.7 MB
docker.io/centos centos6 cf2c3ece5e41 4 weeks ago 194.6 MB
[root@daring ~]# docker run -it docker.io/centos:centos6
[root@1c698500f24b /]# ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:508 (508.0 b) TX bytes:508 (508.0 b)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
[root@1c698500f24b /]# [root@daring ~]#
将容器切换到后台运行:ctrl+p ctrl+q
在运行一个容器使用container模式:查看新容器的地址
[root@daring ~]# docker run -it --net=container:1c698500f24b docker.io/centos:centos6
[root@1c698500f24b /]# ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 b) TX bytes:648 (648.0 b)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
[root@1c698500f24b /]#
3.none
模式
这个模式和前两个不同。在这种模式下,Docker 容器拥有自己的Network Namespace,但是,并不为 Docker容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。
4.bridge
模式
当 docker 启动时,会在主机上创建一个 docker0 的虚拟网卡。他随机挑选 RFC1918 私有网络中的一段地址给 docker0 。比如 172.17.0.1/16,16 位掩码的网段可以拥有 65534 个地址可以使用,这对主机和容器来说应该足够了。
docker0 不是普通的网卡,他是桥接到其他网卡的虚拟网卡,容器使用它来和主机相互通信。当创建一个 docker 容器的时候,它就创建了一个对接口,当数据包发送到一个接口时,另外一个接口也可以收到相同的数据包,它们是绑在一起的一对孪生接口。这对接口在容器中那一端的的名字是 eth0 ,宿主主机端的会指定一个唯一的名字,比如 vethAQI2QT 这样的名字。 所有的 veth* 的接口都会桥接到 docker0 ,这样 docker 就创建了在主机和所有容器之间一个虚拟共享网
Docker完成以上网络配置的过程大致是这样的(总结):
1.在主机上创建一对虚拟网卡veth pair设备。veth设备总是成对出现的,它们组成了一个数据的通道,数据从一个设备进入,就会从另一个设备出来。因此,veth设备常用来连接两个网络设备。
2.Docker将
veth pair
设备的一端放在新创建的容器中,并命名为eth0
。另一端放在主机中,以veth65f9
这样类似的名字命名,并将这个网络设备加入到docker0
网桥中,可以通过brctl show
命令查看。注:brctl 工具依赖
bridge-utils
软件包[root@daring ~]# brctl show bridge name bridge id STP enabled interfaces docker0 8000.02427de56e4b no vethea01325
3.从docker0子网中分配一个IP给容器使用,并设置
docker0
的IP地址为容器的默认网关。 容器内部访问外网以及容器和主机之间的端口映射都是通过Iptables实现的,可以查看Iptables表分析。
查看当前 docker0
地址
[root@daring ~]# ifconfig docker0
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.1 netmask 255.255.0.0 broadcast 0.0.0.0
inet6 fe80::42:7dff:fee5:6e4b prefixlen 64 scopeid 0x20<link>
ether 02:42:7d:e5:6e:4b txqueuelen 0 (Ethernet)
RX packets 230 bytes 46455 (45.3 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 205 bytes 23454 (22.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
在容器运行时,每个容器都会分配一个特定的虚拟机口并桥接到 docker0
。每个容器都会配置同 docker0
ip 相同网段的专用 ip 地址,docker0
的 IP 地址被用于所有容器的默认网关。
运行一个容器:
[root@daring ~]# docker run -dit centos:http
c6cf0334ea269c41d37cb286cfdeba04268cf16544434a11c1e21c975ae77f41
查看当前运行的容器:
[root@daring ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c6cf0334ea26 centos:http "/bin/bash /run.sh" 4 seconds ago Up 4 seconds 22/tcp, 80/tcp, 443/tcp stoic_hypatia
1c698500f24b docker.io/centos:centos6 "/bin/bash" 3 minutes ago Up 3 minutes cocky_bartik
通过brctl show命令查看
[root@daring ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.02427de56e4b no vethb210cab
vethea01325
以上, docker0 扮演着stoic_hypatia和 cocky_bartik这两个容器的虚拟接口 vethxx interface 桥接的角色。
执行docker network inspect bridge查看桥接网络的详细信息
[root@daring ~]# docker network inspect bridge
[
{
"Name": "bridge",
"Id": "e558507c9c2e26602c38d49ef841cf0cf17b8b22edef6b312e8be71ef696f05a",
"Scope": "local",
"Driver": "bridge",
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16"
}
]
},
"Containers": {
"1c698500f24ba2043cc13a24b1735913fd4d9954d788f12da3cf3b0251b9e536": {
"Name": "cocky_bartik",
"EndpointID": "2e038ff3e2a670e796309de7e2d71f03d247caec69d7c1bcee19198f342b0266",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"c6cf0334ea269c41d37cb286cfdeba04268cf16544434a11c1e21c975ae77f41": {
"Name": "stoic_hypatia",
"EndpointID": "69d17d62606f50f256be4c28a42621be6d050a83783dfef7191c62d836eb6711",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
}
}
]
IV.自定义网桥
除了默认的 docker0 网桥,用户也可以指定网桥来连接各个容器。在启动 Docker 服务的时候,使用 -b BRIDGE 或 --bridge=BRIDGE 来指定使用的网桥。 Docker 允许你管理 docker0 桥接或者通过-b选项自定义桥接网卡,需要安装bridge-utils软件包。
基本步骤如下:
1.确保docker的进程是停止的
2.创建自定义网桥
3.给网桥分配特定的 ip
4.以 -b 的方式指定网桥
操作步骤 回顶部
1.如果服务已经运行,那需要先停止服务,并删除旧的网桥
[root@daring ~]# systemctl stop docker
[root@daring ~]# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
Active: inactive (dead) since Tue 2016-08-02 11:39:04 CST; 4s ago
Docs: http://docs.docker.com
Process: 1518 ExecStart=/bin/sh -c /usr/bin/docker-current daemon
...//省略
[root@daring ~]# ip link set dev docker0 down
[root@daring ~]# brctl delbr docker0
[root@daring ~]# brctl show
bridge name bridge id STP enabled interfaces
2.然后创建一个网桥 bridge0
[root@daring ~]# brctl addbr bridge0
3.给网桥分配特定的ip
[root@daring ~]# ip addr add 192.168.10.1/24 dev bridge0
[root@daring ~]# ip link set dev bridge0 up
4.查看确认网桥创建并启动
[root@daring ~]# brctl show
bridge name bridge id STP enabled interfaces
bridge0 8000.000000000000 no
或
[root@daring ~]# ip addr show bridge0
26: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN
link/ether ce:b8:f9:37:75:d3 brd ff:ff:ff:ff:ff:ff
inet 192.168.10.1/24 scope global bridge0
valid_lft forever preferred_lft forever
inet6 fe80::ccb8:f9ff:fe37:75d3/64 scope link
valid_lft forever preferred_lft forever
5.修改/etc/sysconfig/docker
文件
修改前:
OPTIONS='--selinux-enabled --log-driver=journald'
修改后:添加前面所新建的网桥
[root@daring ~]# vim /etc/sysconfig/docker
[root@daring ~]# cat /etc/sysconfig/docker | grep -v "#" | grep -v "^$"
OPTIONS='--selinux-enabled --log-driver=journald -b=bridge0'
DOCKER_CERT_PATH=/etc/docker
[root@daring ~]#
6.启动 Docker
服务
[root@daring ~]# systemctl start docker
[root@daring ~]# systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
Active: active (running) since Tue 2016-08-02 11:43:31 CST; 4s ago
Docs: http://docs.docker.com
Main PID: 5532 (sh)
...//省略
7.新建一个容器,可以看到它已经桥接到了 bridge0
上
[root@daring ~]# docker run -dit docker.io/centos:centos6
4ce36687d391c107edd824c5d7bf56491ea4cda607c211313b9efcfb6036e463
[root@daring ~]# brctl show
bridge name bridge id STP enabled interfaces
bridge0 8000.46345a536e04 no veth46c9c2b
8.进入容器,查看容器的IP
[root@daring ~]# docker attach 4ce36687d391c107edd824c5d7bf56491ea4cda607c211313b9efcfb6036e463
[root@4ce36687d391 /]# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 02:42:C0:A8:0A:02
inet addr:192.168.10.2 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::42:c0ff:fea8:a02/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 b) TX bytes:648 (648.0 b)
docker 服务启动成功并绑定容器到新的网桥,新建一个容器,你会看到它的 ip 是我们的设置的新 ip段, docker 会自动检测到它。用 brctl show 可以看到容器启动或则停止后网桥的配置变化,在容器中使用 ip a 和 ip r 来查看 ip 地址配置和路由信息。
下面通过配置一个以 --net=none 启动的容器,使他达到跟平常一样具有访问网络的权限。来介绍docker 是如何连接到容器中的。
V.none
模式配置网络
[root@daring ~]# cat noneip.sh
id=$(docker ps -q)
pid=$(docker inspect -f '{{.State.Pid}}' $id)
mkdir -p /var/run/netns
ln -s /proc/$pid/ns/net /var/run/netns/$pid
ip link add A type veth peer name B
brctl addif docker0 A
ip link set A up
ip link set B netns $pid
ip netns exec $pid ip link set dev B name eth0
ip netns exec $pid ip addr add 172.17.0.100/16 dev eth0
ip netns exec $pid ip link set eth0 up
ip netns exec $pid ip route add default via 172.17.0.1
可以用上面的脚本实现
1.启动一个运行bash容器,并指定为none模式
[root@daring ~]# docker run -it --net=none docker.io/centos:centos6 /bin/bash
[root@bae006592056 /]# ifconfig
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 b) TX bytes:0 (0.0 b)
2.再开启一个新的终端,查找这个容器的进程id
[root@daring ~]# pid=$(docker inspect -f '{{.State.Pid}}' bae006592056)
[root@daring ~]# echo $pid
6391
3.创建命名空间
[root@daring ~]# mkdir -p /var/run/netns
[root@daring ~]# ln -s /proc/$pid/ns/net /var/run/netns/$pid
[root@daring ~]# ls /var/run/netns/$pid
/var/run/netns/6391
4.检查桥接网卡的ip
和子网掩码
[root@daring ~]# ip addr show bridge0
26: bridge0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 72:b1:bb:79:33:2c brd ff:ff:ff:ff:ff:ff
inet 192.168.10.1/24 scope global bridge0
valid_lft forever preferred_lft forever
inet6 fe80::ccb8:f9ff:fe37:75d3/64 scope link
valid_lft forever preferred_lft forever
5.创建一对peer
接口 A 和 B ,绑定 A 到网桥,并启用它
[root@daring ~]# ip link add A type veth peer name B
[root@daring ~]# brctl addif bridge0 A
[root@daring ~]# ip link set A up
[root@daring ~]# ifconfig
A: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether 76:08:97:34:23:38 txqueuelen 1000 (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
bridge0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.10.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::ccb8:f9ff:fe37:75d3 prefixlen 64 scopeid 0x20<link>
ether 72:b1:bb:79:33:2c txqueuelen 0 (Ethernet)
RX packets 24 bytes 1608 (1.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 10 bytes 732 (732.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
eno16777728: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.142.165 netmask 255.255.255.0 broadcast 192.168.142.255
inet6 fe80::20c:29ff:fec4:bc5d prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:c4:bc:5d txqueuelen 1000 (Ethernet)
RX packets 8898 bytes 734816 (717.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 4605 bytes 580751 (567.1 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 0 (Local Loopback)
RX packets 4 bytes 340 (340.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 4 bytes 340 (340.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
veth1ad6672: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet6 fe80::70b1:bbff:fe79:332c prefixlen 64 scopeid 0x20<link>
ether 72:b1:bb:79:33:2c txqueuelen 0 (Ethernet)
RX packets 8 bytes 648 (648.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 8 bytes 648 (648.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
6.将B
放到容器的网络命名空间,命名为 eth0
, 配置一个空闲的 ip
[root@daring ~]# ip link add A type veth peer name B
[root@daring ~]# ip link set A netns 7004
[root@daring ~]# ip netns exec 7004 ip addr add 10.1.1.1/32 dev A
[root@daring ~]# ip netns exec 7004 ip link set A up
[root@daring ~]# ip netns exec 7004 ip route add 10.1.1.1/32 dev A
[root@daring ~]# ip link set B netns 6902
[root@daring ~]# ip netns exec 6902 ip addr add 10.1.1.2/32 dev B
[root@daring ~]# ip netns exec 6902 ip link set B up
[root@daring ~]# ip netns exec 6902 ip route add 10.1.1.1/32 dev B
自此,你又可以像平常一样使用网络了
当你退出容器后, docker 清空容器,容器的 eth0
随网络命名空间一起被摧毁, A 接口也被自动从docker0
取消注册。不用其他命令,所有东西都被清理掉了!
注意 ip netns exec
命令,它可以让我们像 root 一样配置网络命名空间。但在容器内部无法使用,因为统一的安全策略, docker 限制容器进程配置自己的网络。使用ip netns exec
可以让我们不用设置 --privileged=true
就可以完成一些可能带来危险的操作。
VI.创建一个点到点连接
默认情况下,Docker 会将所有容器连接到由 docker0 提供的虚拟子网中。 用户有时候需要两个容器之间可以直连通信,而不用通过主机网桥进行桥接。 解决办法很简单:创建一对 peer 接口,分别放到两个容器中,配置成点到点链路类型即可。
1.首先启动 2 个容器
#docker run -i -t --rm --net=none 镜像id/bin/bash
root@1f1f4c1f931a:/#
#docker run -i -t --rm --net=none镜像id/bin/bash
root@12e343489d2f:/#
[root@daring ~]# docker run -it --rm --net=none docker.io/centos:centos6 /bin/bash
[root@130ec14b3fec /]#
[root@daring ~]# docker run -it --rm --net=none docker.io/centos:centos6 /bin/bash
[root@e6e801087291 /]#
2.找到进程号,然后创建网络名字空间的跟踪文件
#docker inspect -f '{{.State.Pid}}' 1f1f4c1f931a
2989
#docker inspect -f '{{.State.Pid}}' 12e343489d2f
3004
#mkdir -p /var/run/netns
#ln -s /proc/2989/ns/net /var/run/netns/2989
#ln -s /proc/3004/ns/net /var/run/netns/3004
[root@daring ~]# docker inspect -f '{{.State.Pid}}' 130ec14b3fec
6902
[root@daring ~]# docker inspect -f '{{.State.Pid}}' e6e801087291
7004
[root@daring ~]#
3.创建一对 peer
接口,然后配置路由
#ip link add A type veth peer name B
#ip link set A netns 2989
#ip netns exec 2989 ip addr add 10.1.1.1/32 dev A
#ip netns exec 2989 ip link set A up
#ip netns exec 2989 ip route add 10.1.1.2/32 dev A
#ip link set B netns 3004
#ip netns exec 3004 ip addr add 10.1.1.2/32 dev B
#ip netns exec 3004 ip link set B up
#ip netns exec 3004 ip route add 10.1.1.1/32 dev B
现在这 2 个容器就可以相互 ping 通,并成功建立连接。点到点链路不需要子网和子网掩码
VII.DNS/HOSTNAME
自定义
Docker 没有为每个容器专门定制镜像,那么怎么自定义配置容器的主机名和 DNS 配置呢?秘诀就是它利用虚拟文件来挂载到来容器的 3 个相关配置文件。
在容器中使用 mount 命令可以看到挂载信息:注(mount
命令软件包util-linux)
# mount
...
...
这种机制可以让宿主主机 DNS 信息发生更新后,所有 Docker 容器的 dns
配置通过 /etc/resolv.conf
文件立刻得到更新。
如果用户想要手动指定容器的配置,可以利用下面的选项。
-h HOSTNAME
or --hostname=HOSTNAME
设定容器的主机名,它会被写到容器内的/etc/hostname
和 /etc/hosts
。但它在容器外部看不到,既不会在 docker ps
中显示,也不会在其他的容器的 /etc/hosts
看到。
--link=CONTAINER_NAME:ALIAS
选项会在创建容器的时候,添加一个其他容器的主机名到
/etc/hosts
文件中,让新容器的进程可以使用主机名ALIAS
就可以连接它。
--dns=IP_ADDRESS
添加 DNS 服务器到容器的/etc/resolv.conf
中,让容器用这个服务器来解析所有不在 /etc/hosts
中的主机名。
--dns-search=DOMAIN
设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host的主机时, DNS 不仅搜索 host,还会搜索 host.example.com。注意:如果没有上述最后 2 个选项,Docker 会默认用主机上的 /etc/resolv.conf
来配置容器。
具体其他选项可以查看docker run --help
帮助
VIII.docker中的容器互联系统
docker 有一个 linking 系统可以连接多个容器。它会创建一对父子关系,父容器可以看到所选择的子容器的信息。该系统会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息
自定义容器的命名
linking系统依据容器的名称来执行,因此,首先需要自定义一个好记的容器命名。当我们创建容器的时候,系统会随机分配一个名字。当然我们也可以自己来命名容器,这样做有 2 个好处:
•当我们自己指定名称的时候,比较好记,比如一个 web 应用我们可以给它起名叫 web
•当我们要连接其他容器时候,可以作为一个有用的参考点,比如连接 web 容器到 db 容器
使用 --name
标记可以为容器自定义命名。使用 docker ps
来验证设定的命名,也可以使用 docker inspect
来查看容器的名字docker inspect -f "{{ .Name }}"
容器id
注意:容器的名称是唯一的。如果已经命名了一个叫 web 的容器,当你要再次使用 web 这个名称的时候,需要先用 docker rm 来删除之前创建的同名容器。
在执行 docker run 的时候如果添加 --rm 标记,则容器在终止后会立刻删除。注意, --rm 和 -d 参数不能同时使用。
容器互联
使用--link参数可以让容器之间安全的进行交互。
下面先创建一个新的数据库容器。
docker run -dit --name dbserver镜像id
然后创建一个新的 web 容器,并将它连接到 db 容器
--link
标记的格式: --link name:alias
, name 是我们要链接的容器的名称, alias 是这个链接的别名。
使用docker ps来查看容器的连接
我们可以看到我们命名的容器, dbserver 和 web , dbserver 容器的 names 列有 dbserver 也有 web/dbserver 。这表示 web 容器链接到 db 容器,他们是一个父子关系。在这个 link 中, 2 个容器中有一对父子关系。 docker 在 2 个容器之间创建了一个安全的连接,而且不用映射dbserver容器的端口到宿主主机上。所以在启动 db 容器的时候也不用 -p 和 -P 标记。使用 link 之后我们就可以不用暴露数据库端口到网络上。
注意:你可以链接多个子容器到父容器,比如我们可以链接多个 web 到 db 容器上。
Docker 会添加子容器的 host
信息到父容器的 /etc/hosts
的文件。
下面是父容器 web 的 hosts 文件
这里有 2 个 hosts,第一个是 web 容器,web 容器用 id 作为他的主机名,第二个是 dbserver 容器的 ip 和主机名。可以在 web 容器中安装 ping 命令来测试跟dbserver容器的连通。 注意:官方的镜像默认没有安装 ping,需要自行安装,软件包名iputils
用 ping 来测试db容器,
附:在bridge模式下,连在同一网桥上的容器可以相互通信(若出于安全考虑,也可以禁止它们之间通信,方法是在DOCKER_OPTS
变量中设置--icc=false
,这样只有使用--link才能使两个容器通信)。
多台物理主机之间的容器互联(暴露容器到真实网络中)
docker 默认的桥接网卡是 docker0 。它只会在本机桥接所有的容器网卡,举例来说容器的虚拟网卡在主机上看一般叫做 vethxxx,而 docker 只是把所有这些网卡桥接在一起,如下:
这样就可以把这个网络看成是一个私有的网络,通过 nat 连接外网,如果要让外网连接到容器中,就需要做端口映射,即-p
参数。
如果在企业内部应用,或则做多个物理主机的集群,可能需要将多个物理主机的容器组到一个物理网络中来,那么就需要将这个网桥桥接到我们指定的网卡上。
主机 A 和主机 B 的网卡一都连着物理交换机的同一个 vlan 101, 这样网桥一和网桥三就相当于在同一个物理网络中了,而容器一、容器三、容器四也在同一物理网络中了,他们之间可以相互通信,而且可以跟同一 vlan 中的其他物理机器互联。 这样就直接把容器暴露到物理网络上了,多台物理主机的容器也可以相互联网了。需要注意的是,这样就需要自己来保证容器的网络安全了。 不同容器之间的通信可以借助于 pipework 这个工具 pipework是由Docker的工程师Jérôme Petazzoni开发的一个Docker网络配置工具,由200多行shell实现,方便易用。 下载地址:wget https://github.com/jpetazzo/pipework.git unzip pipework-master.zip cp -p /root/pipework-master/pipework /usr/local/bin/ 安装相应依赖软件 yum install bridge-utils -y 配置桥接网络
[root@centos7 network-scripts]# cat ifcfg-eno16777728
TYPE=Ethernet
BOOTPROTO=none
NM_CONTROLLED=no
DEFROUTE=yes
PEERDNS=yes
PEERROUTES=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_PEERDNS=yes
IPV6_PEERROUTES=yes
IPV6_FAILURE_FATAL=no
NAME=eno16777728
UUID=4778be1c-5a9a-4f0d-a6ed-e558a5adf1fc
DEVICE=eno16777728
ONBOOT=yes
BRIDGE="br0"
[root@centos7 network-scripts]# cat ifcfg-br0
DEVICE=br0
BOOTPROTO=static
NM_CONTROLLED=no
ONBOOT=yes
TYPE=Bridge
IPADDR=192.168.186.128
NETMASK=255.255.255.0
重启network服务
把 docker 的桥接指定为 br0,这样跨主机不同容器之间通过 pipework 新建 docker 容器的网卡桥接到 br0,这样跨主机容器之间就可以通信了。 CentOS 7/RHEL 7系统
#systemctl stop docker
修改/etc/sysconfig/docker
#systemctl start docker
pipework 把 docker 默认桥接指定到了 br0,则最好在创建容器的时候加上--net=none,防止自动分配的 IP 在局域网中有冲突。
使用镜像运行一个容器
注:默认不指定网卡设备名,则默认添加为 eth1
注:另外 pipework 不能添加静态路由,如果有需求则可以在 run 的时候加上 --privileged=true 权限在容器中手动添加,但这种安全性有缺陷,可以通过 ip netns 操作
访问容器提供的web服务:
使用ip netns添加静态路由,避免创建容器使用--privileged=true选项造成一些不必要的安全问题:
进入容器查看路由记录:
在其它宿主机进行相应的配置,新建容器并使用 pipework 添加虚拟网卡桥接到 br0,测试通信情况即可。 注:可以删除 docker0,直接把 docker 的桥接指定为 br0。也可以保留使用默认的配置,这样单主机容器之间的通信可以通过 docker0,而跨主机不同容器之间通过 pipework 新建 docker 容器的网卡桥接到 br0,这样跨主机容器之间就可以通信了。
扩展:
IX.pipework
pipework可以在下面用三个场景来使用和工作原理。
1.将Docker容器配置到本地网络环境中
为了使本地网络中的机器和Docker容器更方便的通信,我们经常会有将Docker容器配置到和主机同一网段的需求。这个需求其实很容易实现,我们只要将Docker容器和主机的网卡桥接起来,再给Docker容器配上IP就可以了。 下面我们来操作一下,我主机A地址为192.168.1.102/24,网关为192.168.1.1,需要给Docker容器的地址配置为192.168.1.150/24。在主机A上做如下操作: 安装pipework 下载地址:wget https://github.com/jpetazzo/pipework.git unzip pipework-master.zip cp -p /root/pipework-master/pipework /usr/local/bin/ 启动Docker容器。 docker run -itd --name test1 镜像 /bin/bash 配置容器网络,并连到网桥br0上。网关在IP地址后面加@指定。 pipework br0 test1 192.168.1.150/[email protected] 将主机enp0s3桥接到br0上,并把enp0s3的IP配置在br0上。 ip addr add 192.168.1.102/24 dev br0 ip addr del 192.168.1.102/24 dev enp0s3 brctl addif br0 enp0s3 ip route del default ip route add default via 192.168.1.1 dev br0 注:如果是远程操作,中间网络会断掉,所以放在一条命令中执行。 ip addr add 192.168.1.102/24 dev br0; \ ip addr del 192.168.1.102/24 dev enp0s3; \ brctl addif br0 enp0s3; \ ip route del default; \ ip route add default via 192.168.1.1 dev br0 完成上述步骤后,我们发现Docker容器已经可以使用新的IP和主机网络里的机器相互通信了。 进入容器内部查看容器的地址:
pipework工作原理分析 那么容器到底发生了哪些变化呢?我们docker attach到test1上,发现容器中多了一块eth1的网卡,并且配置了192.168.1.150/24的IP,而且默认路由也改为了192.168.1.1。这些都是pipework帮我们配置的。 ·首先pipework检查是否存在br0网桥,若不存在,就自己创建。
·创建veth pair设备,用于为容器提供网卡并连接到br0网桥。
·使用docker inspect找到容器在主机中的PID,然后通过PID将容器的网络命名空间链接到/var/run/netns/目录下。这么做的目的是,方便在主机上使用ip netns命令配置容器的网络。因为,在Docker容器中,我们没有权限配置网络环境。
·将之前创建的veth pair设备分别加入容器和网桥中。在容器中的名称默认为eth1,可以通过pipework的-i参数修改该名称。
·然后就是配置新网卡的IP。若在IP地址的后面加上网关地址,那么pipework会重新配置默认路由。这样容器通往外网的流量会经由新配置的eth1出去,而不是通过eth0和docker0。(若想完全抛弃自带的网络设置,在启动容器的时候可以指定--net=none) 以上就是pipework配置Docker网络的过程,这和Docker的bridge模式有着相似的步骤。事实上,Docker在实现上也采用了相同的底层机制。 通过源代码,可以看出,pipework通过封装Linux上的ip、brctl等命令,简化了在复杂场景下对容器连接的操作命令,为我们配置复杂的网络拓扑提供了一个强有力的工具。当然,如果想了解底层的操作,我们也可以直接使用这些Linux命令来完成工作,甚至可以根据自己的需求,添加额外的功能。
2.单主机Docker容器VLAN划分
pipework不仅可以使用Linux bridge连接Docker容器,还可以与OpenVswitch结合,实现Docker容器的VLAN划分。下面,就来简单演示一下,在单机环境下,如何实现Docker容器间的二层隔离。 为了演示隔离效果,我们将4个容器放在了同一个IP网段中。但实际他们是二层隔离的两个网络,有不同的广播域。 在主机A上创建4个Docker容器,test1、test2、test3、test4
docker run -itd --name test1 ubuntu /bin/bash
docker run -itd --name test2 ubuntu /bin/bash
docker run -itd --name test3 ubuntu /bin/bash
docker run -itd --name test4 ubuntu /bin/bash
将test1,test2划分到一个vlan中,vlan在mac地址后加@指定,此处mac地址省略。
pipework ovs0 test1 192.168.0.1/24 @100
pipework ovs0 test2 192.168.0.2/24 @100
将test3,test4划分到另一个vlan中
pipework ovs0 test3 192.168.0.3/24 @200
pipework ovs0 test4 192.168.0.4/24 @200
完成上述操作后,使用docker attach连到容器中,然后用ping命令测试连通性,发现test1和test2可以相互通信,但与test3和test4隔离。这样,一个简单的VLAN隔离容器网络就已经完成。 由于OpenVswitch本身支持VLAN功能,所以这里pipework所做的工作和之前介绍的基本一样,只不过将Linux bridge替换成了OpenVswitch,在将veth pair的一端加入ovs0网桥时,指定了tag。底层操作如下: ovs-vsctl add-port ovs0 veth* tag=100
实验
[root@daring ~]# docker run -itd --name test1 centos7:latest /bin/bash
64ab563717b8487bbdd1938c81f8bc4bcb58af08d4991a0365ca735370d5db25
[root@daring ~]# docker run -itd --name test2 centos7:latest /bin/bash
ec009586c05bc0065e1d8ef8a1cfab4cecb1a733990e51463e85e3d061703874
[root@daring ~]# docker run -itd --name test3 centos7:latest /bin/bash
70d06aaf1824854775b52c86dbfb2b1da201ceec976626339a92aa923dd2930c
[root@daring ~]# docker run -itd --name test4 centos7:latest /bin/bash
65b3ae48d19d5f6060743aa843d823416e737d75ccd8a04ad118b1e6438f1a56
[root@daring ~]#
[root@daring ~]# pipework ovs0 test1 192.168.0.1/24 @100
[root@daring ~]# pipework ovs0 test2 192.168.0.2/24 @100
[root@daring ~]# pipework ovs0 test3 192.168.0.3/24 @200
[root@daring ~]# pipework ovs0 test4 192.168.0.4/24 @200
[root@daring ~]# docker attach test1
[root@64ab563717b8 /]# ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
From 192.168.0.1 icmp_seq=1 Destination Host Unreachable
From 192.168.0.1 icmp_seq=2 Destination Host Unreachable
From 192.168.0.1 icmp_seq=3 Destination Host Unreachable
From 192.168.0.1 icmp_seq=4 Destination Host Unreachable
^C
--- 192.168.0.3 ping statistics ---
6 packets transmitted, 0 received, +4 errors, 100% packet loss, time 5003ms
pipe 4
[root@64ab563717b8 /]# ping 192.168.0.4
PING 192.168.0.4 (192.168.0.4) 56(84) bytes of data.
From 192.168.0.1 icmp_seq=1 Destination Host Unreachable
From 192.168.0.1 icmp_seq=2 Destination Host Unreachable
From 192.168.0.1 icmp_seq=3 Destination Host Unreachable
From 192.168.0.1 icmp_seq=4 Destination Host Unreachable
^C
--- 192.168.0.4 ping statistics ---
5 packets transmitted, 0 received, +4 errors, 100% packet loss, time 4005ms
pipe 4
[root@64ab563717b8 /]# ping 192.168.0.2
PING 192.168.0.2 (192.168.0.2) 56(84) bytes of data.
64 bytes from 192.168.0.2: icmp_seq=1 ttl=64 time=0.504 ms
64 bytes from 192.168.0.2: icmp_seq=2 ttl=64 time=0.046 ms
64 bytes from 192.168.0.2: icmp_seq=3 ttl=64 time=0.044 ms
^C
--- 192.168.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.044/0.198/0.504/0.216 ms
[root@64ab563717b8 /]#
3.多主机Docker容器的VLAN划分
上面介绍完了单主机上VLAN的隔离,下面我们将情况延伸到多主机的情况。有了前面两个例子做铺垫,这个也就不难了。为了实现这个目的,我们把宿主机上的网卡桥接到各自的OVS网桥上,然后再为容器配置IP和VLAN就可以了。我们实验环境如下,主机A和B各有一块网卡enp0s3,IP地址分别为10.10.101.105/24、10.10.101.106/24。在主机A上创建两个容器test1、test2,分别在VLAN 100和VLAN 200上。在主机B上创建test3、test4,分别在VLAN 100和VLAN 200 上。最终,test1可以和test3通信,test2可以和test4通信。 拓扑图如下所示
在主机A上
创建Docker容器
docker run -itd --name test1 ubuntu /bin/bash
docker run -itd --name test2 ubuntu /bin/bash
划分VLAN
pipework ovs0 test1 192.168.0.1/24 @100
pipework ovs0 test2 192.168.0.2/24 @200
将eth0桥接到ovs0上
ip addr add 10.10.101.105/24 dev ovs0
ip addr del 10.10.101.105/24 dev eth0;
ovs-vsctl add-port ovs0 eth0
ip route del default
ip route add default gw 10.10.101.254 dev ovs0
在主机B上
创建Docker容器
docker run -itd --name test3 ubuntu /bin/bash
docker run -itd --name test4 ubuntu /bin/bash
划分VLAN
pipework ovs0 test1 192.168.0.3/24 @100
pipework ovs0 test2 192.168.0.4/24 @200
将eth0桥接到ovs0上
ip addr add 10.10.101.106/24 dev ovs0
ip addr del 10.10.101.106/24 dev eth0;
ovs-vsctl add-port ovs0 eth0
ip route del default
ip route add default gw 10.10.101.254 dev ovs0
完成上面的步骤后,主机A上的test1和主机B上的test3容器就划分到了一个VLAN中,并且与主机A上的test2和主机B上的test4隔离(主机eth0网卡需要设置为混杂模式,连接主机的交换机端口应设置为trunk模式,即允许VLAN 100和VLAN 200的包通过)。 注:除此之外,pipework还支持使用macvlan设备、设置网卡MAC地址等功能。不过,pipework有一个缺陷,就是配置的容器在关掉重启后,之前的设置会丢失。
其中promisc表示网卡混杂模式 其他参数的含义:
参数 | 含义 |
---|---|
UP | 表示网卡开启状态 |
BROADCAST | 表示支持广播 |
promisc | 表示网卡混杂模式 |
RUNNING | 表示网卡的网线被接上 |
MULTICAST | 表示支持组播 |
MTU | 表示MaximumTrasmission Unit 最大传输单元(字节),即此接口一次所能传输的最大封包; |
RX | 表示网络由激活到目前为止接收的数据包 |
TX | 表示网络由激活到目前为止发送的数据包 |
collisions | 表示网络信号冲突的情况 |
txqueuelen | 表示传输缓冲区长度大小 |
设置网卡工作模式
#ifconfig 网卡名 promisc 设置混杂
#ifconfig 网卡名 -promisc 取消混杂
网卡工作模式有4种,分别是:
- 广播(Broadcast)模式
- 多播(Multicast)模式
- 单播模式(Unicast)
- 混杂模式(Promiscuous)
在混杂模式下的网卡能够接收一切通过它的数据,而不管该数据目的地址是否是它。如果通过程序将网卡的工作模式设置为 “混杂模式”,那么网卡将接受所有流经它的数据帧,这实际上就是Sniffer工作的基本原理:让网卡接收一切他所能接收的数据。Sniffer就是一种 能将本地网卡状态设成混杂(promiscuous)状态的软件,当网卡处于这种"混杂"方式时,它对所有遇到的每一个数据帧都 产生一个硬件中断以便提醒操作系统处理流经该物理媒体上的每一个报文包。可见,Sniffer工作在网络环境中的底层,它会拦截所有的正在网络上传送的数据,并且通过相应的软件处理,可以实时分析这些数据的内容,进而分析所处的网络状态和整体布局。
扩展:利用Weave实现跨主机容器互联