Docker中Container的网络
一、Docker单host容器网络
查看Docker 自动在 host 上创建的3个网络
docker network ls
1、none 网络
none 网络是什么都没有的网络
挂在这个网络下的容器除了 lo,没有其他任何网卡
容器创建时,用 --network=none 指定使用 none 网络
docker run -it --network=none busybox
busybox是什么?
(1)busybox是Linux上的一个应用程序(application)
(2)它整合了许多Linux上常用的工具和命, 如rm, ls, gzip, tftp等。对于这些工具和命令,busybox中的实现可能不是最全的,但却是最常用的,因此它的特点就是短小精悍,特别适合对尺寸很敏感的嵌入式系统
(3)busybox的官方网站是http://www.busybox.net/
封闭的网络应用场景:
一些对安全性要求高并且不需要联网的应用可以使用 none 网络,如:密码生成服务
2、host 网络
连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样
通过 --network=host 指定使用 host 网络
docker run -it --network=host busybox
ip l
hostname
容器中可看到 host 所有网卡,并且 hostname 也是 host 的
- Docker host 网络最大好处是性能
如果容器对网络传输效率有较高要求,则可以选择 host 网络
要考虑端口冲突问题,Docker host 上已经使用的端口容器不能再用
B、Docker host 让容器可以直接配置 host 网路
某些跨 host 的网络解决方案,管理 iptables
二、bridge 网络
1、名叫docker0 的 linux bridge
Docker 安装时会创建一个 命名为 docker0 的 linux bridge。如果不指定--network,创建的容器默认都会挂到 docker0 上
yum -y install brctl
brctl show
docker run -d httpd
brctl show
新的网络接口 veth053fdc4 挂到了 docker0 上,veth053fdc4 是新创建容器的虚拟网卡
2、容器的网络配置
linux查看mac地址
ip addr show (ip address show 、ip addr ) 查看本机ip和额外的一些信息
ifconfig -a 其中 HWaddr 就是mac地址
cat /sys/class/net/eth0/address 查看eth0的mac地址
cat /proc/net/arp
进入容器,查看网络配置
docker exec -it e5139668ecf6 bash
容器有一个网卡 eth0
实际上容器中的 eth0和host中的 vethddb21c8@if6 是一对 veth pair
veth pair 是一种成对出现的特殊网络设备,可比作由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0)在容器中,另一头(vethddb21c8@if6)挂在网桥 docker0 上,其效果就是将 eth0 也挂在了 docker0 上
eth0配置了172.17.0.3的IP地址
3、bridge 网络的配置信息
docker network inspect bridge
bridge 网络配置的 subnet 就是 172.17.0.0/16,并且网关是 172.17.0.1
ifconfig docker0
容器创建时,docker 会自动从 172.17.0.0/16 中分配一个 IP
三、user-defined 网络
Docker 提供三种 user-defined 网络驱动:bridge, overlay 和 macvlan
overlay 和 macvlan 用于创建跨主机的网络
1、创建自定义bridge 网络
brctl show
A、自定义创建bridge网络
docker network create --driver bridge myself-net
brctl show
网桥br-c34b6df765be为新创建的,c34b6df765be是self-net网络长ID对应的短ID
c34b6df765befbd7609a6bb8007fc8d29afba3de3d4a152b95d4a96172cd8866
查看一下 self-net 的配置信息
docker network inspect self-net
[root@docker-centos ~]# docker network inspect self-net
[
{
"Name": "self-net",
"Id": "c34b6df765befbd7609a6bb8007fc8d29afba3de3d4a152b95d4a96172cd8866",
"Created": "2020-05-22T16:59:22.711670578+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
Docker自动分配的IP网段:172.18.0.0/16
指定网段,创建容器:
docker run -it --network=self-net busybox
Docker自动分配的IP地址:172.18.0.2
B、自定义创建指定子网网络
创建时需要指定参数:
--subnet
--gateway
创建指定网段的网络:
docker network create --driver bridge --subnet 172.16.128.0/24 --gateway 172.16.128.1 self-net-02
brctl show
ifconfig br-1bf920351b57
[root@docker-centos ~]# docker network inspect self-net-02
[
{
"Name": "self-net-02",
"Id": "1bf920351b57c3d9e002fc5e20367685bc51945c4098ad85aa984afb8577363f",
"Created": "2020-05-22T17:14:25.536769431+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.16.128.0/24",
"Gateway": "172.16.128.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
2、容器使用指定网络
在容器启动时,指定容器需要使用的特定网络
--network
A、指定容器使用的网络
docker run -it --network=self-net-02 busybox
ip a
容器自动分配到IP地址:172.16.128.2
B、指定容器使用的IP地址(容器使用静态IP)
--ip
docker run -it --network=self-net-02 --ip 172.16.128.88 busybox
容器的指定IP地址:172.16.128.88
注意:只有使用 --subnet 创建的网络,启动容器时才能指定静态 IP
否则,会报错!
self-net在创建时,没有用 --subnet 指定网段,所以在启动容器时,若是指定固定IP会报错:docker: Error response from daemon: user specified IP address is supported only when connecting to networks with user configured subnets.
ERRO[0000] error waiting for container: context canceled
3、自定义网络中容器间通讯
2个 busybox 容器都挂在 self-net-02 上,互通测试
172.16.128.2
172.16.128.88
docker exec -it 3eaf226a84c2 sh
ip a
ping 172.16.128.88
ping 172.16.128.1
同一网络中的容器、网关之间都是可以通信的
4、自定义网络之间容器间通讯
172.18.0.0/16段,容器IP地址:172.18.0.2,网关:172.18.0.1
172.16.128.0/24段,容器IP地址:172.16.128.2,网关:172.16.128.1
A、默认情况下:属于不通网段容器之间的网络是不通的!
docker run -it --network=self-net busybox
ping -c 2 172.16.128.1 (网关通)
ping -c 2 172.16.128.2 (容器不通)
B、如何打通不通网段之间容器的访问?
前提条件:
host 上对每个网络的都有一条路由
操作系统上打开了 ip forwarding
host 成为一个路由器,挂接在不同网桥上的网络就能够相互通信
(1) 172.16.128.0/24段 和 172.18.0.0/16段 路由定义已经存在
route -F
route -n
ip r
(2) 路由转发ip forwarding已经开启
sysctl net.ipv4.ip_forward
- 最后,查看iptables规则
iptables-save | grep DROP
iptables-save | grep DOCKER-ISOLATION-STAGE
isolation隔离
!表示除了的意思
DOCKER-ISOLATION 规则的命名,可知 docker 在设计上就是要隔离不同的 netwrok
如何让 不同网段 busybox 通信?
方法:在容器中加入需要通信的网段的网卡
给容器添加1张指定网段的网卡:
docker network connect 指定网络名称 容器ID
基本网络状况:
68694e564dc3容器 属于self-net网段
7b5e8a86db80 和 3eaf226a84c2容器 属于 self-net-02网段
现在给68694e564dc3容器添加一张self-net-02网段的网卡:
docker network connect self-net-02 68694e564dc3
进入添加网卡的容器
docker exec -it 68694e564dc3 sh
查看容器的ip地址
ip a
测试网络连通性
ping -c 2 172.16.128.2
ping -c 2 172.16.128.88
容器68694e564dc3增加了一张 self-net-02 网络的网卡eth1 ,IP地址为172.16.128.3
测试 self-net-02 网络的7b5e8a86db80 和 3eaf226a84c2容器,网络可以ping通
四、容器间的相互通信---3种
1、IP 通信
容器间通信,必须每一个容器中有属于同一个网络的网卡
方法1:容器创建时通过 --network 指定相应的网络
方法2:docker network connect 容器加入到指定网络
2、Docker DNS Server
IP 访问容器不够灵活,优化方案 docker 的 DNS 服务
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过“容器名”通信
方法:启动时用 --name 指定容器命名
使用范围:只能在 user-defined 自定义网络中使用,默认的 bridge 网络是无法使用 DNS 的
- 情景1:使用定义网络self-net-02
创建2个容器busybox-1和busybox-2
docker run -it --network=self-net-02 --name=busybox-1 busybox
docker run -it --network=self-net-02 --name=busybox-2 busybox
busybox-1 就可以直接 ping 到 busybox-2
docker restart $(docker ps -aq -f status=exited)
docker exec -it busybox-1 sh
ping busybox-2
B、情景2:使用默认bridge网络
创建2个容器busybox-3和busybox-4
docker run -it --name=busybox-3 busybox
docker run -it --name=busybox-4 busybox
busybox-3 不能 ping 通 busybox-4
docker restart $(docker ps -aq -f status=exited)
docker exec -it busybox-3 sh
ping busybox-4
容器名 busybox-4 是ping 不通的
3、joined 容器
joined 容器使n个容器共享1个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信
第一步:创建名为 web1 的 httpd 容器
docker run -d -it --name=web1 httpd
ip a
注意:docker的base镜像只包含简单的操作系统,其他命令需要自己安装
apt update && apt install -y iproute2
创建自己的新镜像:
docker ps
docker commit 18f212734fa6 httpd_ip
httpd容器的IP地址和Mac地址:
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
第二步:创建 busybox 容器并通过 --network=container:web1 指定 jointed 容器为 web1:
docker run -it --network=container:web1 --name=jointed-web1 busybox
wget 127.0.0.1
cat index.html
busybox容器的IP地址和Mac地址:
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
busybox 和 web1 的网卡 mac 地址与 IP 一样,共享相同的网络栈
busybox 可用 127.0.0.1 访问 web1 的 http 服务
joined 容器适用场景:
不同容器中的程序通过 loopback 高效快速地通信,比如 web server 与 app server
监控其他容器的网络流量,比如运行在独立容器中的网络监控程序
获取所有容器以及IP地址:
docker inspect -f '{{.Name}} - {{.NetworkSettings.IPAddress }}' $(docker ps -aq)
使用docker-compose命令为,
docker inspect -f '{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)
docker inspect --format='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -aq)
查看单个容器的IP地址:
docker inspect --format '{{ .NetworkSettings.IPAddress }}' 18f212734fa6
docker inspect 18f212734fa6
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 18f212734fa6
五、容器访问外部
当docker host可以访问Internet时,之上创建的容器默认也是可以上Internet
容器默认就能访问外网----外网指的是容器网络以外的网络
分析原理:
iptables -t nat -S
iptables -t nat -S | grep docker0
其中有1条规则:
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
规则解析:
如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理
MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)
查看地址是如何转换的:
1、docker host 的路由表
ip r
解析:
默认路由通过 ens33 发出去,所以我们要同时监控 ens33 和 docker0 上的 icmp(ping)数据包
当 httpd容器 18f212734fa6 去 ping baidu.com 时,tcpdump 输出如下:
httpd容器中安装ping命令:
apt-get update && apt-get install iputils-ping
ping -c 3 baidu.com
host 上安装tcpdump
yum -y install tcpdump
tcpdump -i docker0 -n icmp
解析:
docker0 收到 httpd容器 的 ping 包,源地址为容器 IP 172.17.0.4,交给 MASQUERADE 处理
MASQUERADE 处理--网络地址转换(NAT)之后,观察 ens33 的ping包源地址变化
tcpdump -i ens33 -n icmp
解析:
ens33 收到的 ping 包,源地址为容器 IP 192.168.233.144,即为host本机地址
NAT过程分步骤:
A、 httpd容器 发送 ping 包:172.17.0.4 > www.baidu.com
B、 docker0 收到ping包,发现是发送到外网的,交给 NAT 处理
C、 NAT 将源地址换成 ens33 的 IP:192.168.233.144 > www.baidu.com
D、 ping 包从 ens33 发送出去,到达 www.baidu.com
六、外部访问容器
docker 将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器
容器启动时,-p参数映射端口
A、动态映射到host端口
docker run -d --name=web02 -p 80 httpd_ip
docker run -d --name=web03 -p 80 httpd
查看容器在host上的映射端口:
docker port 95c6bc1f7b8f web03
80/tcp -> 0.0.0.0:32771
docker port 9a5c3e003dd6 web02
80/tcp -> 0.0.0.0:32770
httpd 容器web03的 80 端口被映射到 host 32771 上,通过 <host ip>:<32771> 访问容器的 web 服务了
httpd 容器web02的 80 端口被映射到 host 32770 上
curl 192.168.233.144:32771
curl 192.168.233.144:32770
B、映射指定到host端口
httpd容器的 80 端口映射到 host 的 8080 端口
docker run -d --name=web04 -p host端口:容器端口 httpd
docker run -d --name=web04 -p 8080:80 httpd
docker port 486e6c224c28
curl 192.168.233.144:8080
原理解析:
每一个映射的端口,host 都会启动一个 docker-proxy 进程来处理访问容器的流量
ps -ef | grep docker-proxy
A、docker-proxy 监听 host 的 8080 端口
B、当 curl 访问 192.168.233.144:8080 时,docker-proxy 转发给容器 172.17.0.8:80
C、httpd 容器响应请求并返回结果
查看httpd容器的应用信息:
docker logs -f web04
详情请见,微信公众号