docker-网络基础

时间:2023-03-08 18:28:31

网络

Docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 host 的网络

Docker 安装时会自动在 host 上创建三个网络,

 ⚡ root@bogon  /home  docker network ls
NETWORK ID NAME DRIVER SCOPE
4232d0b89e85 bridge bridge local
90b3cebedfb6 host host local
c1a576af27d2 none null local
⚡ root@bogon  /home 

none 网络

挂在这个网络下的容器除了 lo,没有其他任何网卡。容器创建时,可以通过 --network=none 指定使用 none 网络。

 ⚡ root@bogon  /home  docker run -it --network=none busybox
Unable to find image 'busybox:latest' locally
Trying to pull repository docker.io/library/busybox ...
latest: Pulling from docker.io/library/busybox
8c5a7da1afbc: Pull complete
Digest: sha256:cb63aa0641a885f54de20f61d152187419e8f6b159ed11a251a09d115fdff9bd
Status: Downloaded newer image for docker.io/busybox:latest
/ # 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:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / #

一些对安全性要求高并且不需要联网的应用可以使用 none 网络。

host 网络

连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。可以通过 --network=host 指定使用 host 网络

 ✘ ⚡ root@bogon  /home  docker run -it --network=host busybox
/ # ip l
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 00:0c:29:0f:09:89 brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue
link/ether 02:42:66:21:3f:9d brd ff:ff:ff:ff:ff:ff
21: veth3e15456@if20: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
link/ether c2:02:4f:c0:9e:5d brd ff:ff:ff:ff:ff:ff
27: vethed6909e@if26: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master docker0
link/ether f2:56:d6:65:38:18 brd ff:ff:ff:ff:ff:ff

在容器中可以看到 host 的所有网卡,并且连 hostname 也是 host 的

直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。

bridge 网络

Docker 安装时会创建一个 命名为 docker0 的 linux bridge。如果不指定--network,创建的容器默认都会挂到 docker0 上

 ⚡ root@bogon  /home  brctl  show
bridge name bridge id STP enabled interfaces
docker0 8000.024266213f9d no veth3e15456
vethed6909e

其中veth3e15456 vethed6909e就是创建容器的虚拟网卡。

容器里的配置

⚡ root@bogon  /home  docker exec -it 6b9f16f7de01 bash
root@6b9f16f7de01:/usr/local/apache2# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group defau
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.4/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:4/64 scope link
valid_lft forever preferred_lft forever
root@6b9f16f7de01:/usr/local/apache2#

​ vethed6909e和eth0@if29是一对veth pair。veth pair 是一种成对出现的特殊网络设备, 想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0@if34)在容器中,另一头(veth28c57df)挂在网桥 docker0 上,其效果就是将 eth0@if34 也挂在了 docker0 上

eth0@if29 已经配置了 IP 172.17.0.4

通过 docker network inspect bridge 看一下 bridge 网络的配置信息:

docker-网络基础

bridge 网络配置的 subnet 就是 172.17.0.0/16,并且网关是 172.17.0.1。

docker-网络基础容器创建时,docker 会自动从 172.17.0.0/16 中分配一个 IP,这里 16 位的掩码保证有足够多的 IP 可以供容器使用

user-defined 网络

1.创建

 ✘ ⚡ root@bogon  ~  docker network create --driver bridge my_net
57fd498eab6a3202f2bac97ad91f6770dae73b9c3690b5a1a72c100ef5f9a060

创建一个user-defind 网络

新增了一个网桥 br-57fd498eab6a,这里 57fd498eab6a 正好新建 bridge 网络 my_net 的短 id。

 ⚡ root@bogon  ~  brctl show
bridge name bridge id STP enabled interfaces
br-57fd498eab6a 8000.0242b1c813c6 no
docker0 8000.024266213f9d no veth3e15456
vethb35268d
vethed6909e

执行 docker network inspect 查看一下 my_net 的配置信息:

 ⚡ root@bogon  ~  docker network inspect my_net
[
{
"Name": "my_net",
"Id": "57fd498eab6a3202f2bac97ad91f6770dae73b9c3690b5a1a72c100ef5f9a060",
"Created": "2018-08-05T11:47:52.338929045-04: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,
"Containers": {},
"Options": {},
"Labels": {}
}
]

另外指定 子网和网关创建,如下

docker network create --driver bridge --subnet 192.168.0.0/24 --gateway 192.168.0.10 my_net2

2.使用

容器要使用新的网络,需要在启动时通过 --network 指定

⚡ root@bogon  ~  docker run -it --network=my_net2 busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1
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
32: eth0@if33: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:c0:a8:00:01 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/24 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:c0ff:fea8:1/64 scope link
valid_lft forever preferred_lft forever

指定子网创建

docker run -it --network=my_net2 --ip 192.168.0.100 busybox

只有使用 --subnet 创建的网络才能指定静态 IP

容器之间的连通性

1.查看路由

 ⚡ root@bogon  ~  ip r
default via 192.168.246.2 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.18.0.0/16 dev br-57fd498eab6a proto kernel scope link src 172.18.0.1
192.168.0.0/24 dev br-7119530cd62a proto kernel scope link src 192.168.0.10
192.168.246.0/24 dev ens33 proto kernel scope link src 192.168.246.138

2.查看防火墙

 ⚡ root@bogon  ~  iptables-save
# Generated by iptables-save v1.4.21 on Sun Aug 5 12:22:14 2018
*nat
:PREROUTING ACCEPT [7:1070]
:INPUT ACCEPT [7:1070]
:OUTPUT ACCEPT [94:7140]
:POSTROUTING ACCEPT [94:7140]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 192.168.0.0/24 ! -o br-7119530cd62a -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-57fd498eab6a -j MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i br-7119530cd62a -j RETURN
-A DOCKER -i br-57fd498eab6a -j RETURN
-A DOCKER -i docker0 -j RETURN
COMMIT
# Completed on Sun Aug 5 12:22:14 2018
# Generated by iptables-save v1.4.21 on Sun Aug 5 12:22:14 2018
*filter
:INPUT ACCEPT [1124:81571]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [746:87877]
:DOCKER - [0:0]
:DOCKER-ISOLATION - [0:0]
-A FORWARD -j DOCKER-ISOLATION
-A FORWARD -o br-7119530cd62a -j DOCKER
-A FORWARD -o br-7119530cd62a -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i br-7119530cd62a ! -o br-7119530cd62a -j ACCEPT
-A FORWARD -i br-7119530cd62a -o br-7119530cd62a -j ACCEPT
-A FORWARD -o br-57fd498eab6a -j DOCKER
-A FORWARD -o br-57fd498eab6a -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i br-57fd498eab6a ! -o br-57fd498eab6a -j ACCEPT
-A FORWARD -i br-57fd498eab6a -o br-57fd498eab6a -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER-ISOLATION -i br-57fd498eab6a -o br-7119530cd62a -j DROP
-A DOCKER-ISOLATION -i br-7119530cd62a -o br-57fd498eab6a -j DROP
-A DOCKER-ISOLATION -i docker0 -o br-7119530cd62a -j DROP
-A DOCKER-ISOLATION -i br-7119530cd62a -o docker0 -j DROP
-A DOCKER-ISOLATION -i docker0 -o br-57fd498eab6a -j DROP
-A DOCKER-ISOLATION -i br-57fd498eab6a -o docker0 -j DROP
-A DOCKER-ISOLATION -j RETURN
COMMIT
# Completed on Sun Aug 5 12:22:14 2018

可以看到

-A DOCKER-ISOLATION -i docker0 -o br-7119530cd62a -j DROP
-A DOCKER-ISOLATION -i br-7119530cd62a -o docker0 -j DROP
-A DOCKER-ISOLATION -i docker0 -o br-57fd498eab6a -j DROP
-A DOCKER-ISOLATION -i br-57fd498eab6a -o docker0 -j DROP

实现网络的隔离

3.docker连接网络

docker network connect my_net2 6b9f16f7de01
✘ ⚡ root@bogon  ~  docker exec -it  amazing_lovelace bash
root@6b9f16f7de01:/usr/local/apache2# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
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
28: eth0@if29: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.4/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:4/64 scope link
valid_lft forever preferred_lft forever
40: eth1@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:c0:a8:00:01 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.1/24 scope global eth1
valid_lft forever preferred_lft forever
inet6 fe80::42:c0ff:fea8:1/64 scope link
valid_lft forever preferred_lft forever

容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信。

1.IP通信

如上一节

2.Docker DNS Server通信

启动时用 --name 为容器命名

只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的

示例:

docker run -it --network=my_net2 --name=bbox1 busybox

docker run -it --network=my_net2 --name=bbox2 busybox

/ # ping bbox1
PING bbox1 (192.168.0.2): 56 data bytes
64 bytes from 192.168.0.2: seq=0 ttl=64 time=0.062 ms
64 bytes from 192.168.0.2: seq=1 ttl=64 time=0.068 ms

3.joined 通信

可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信。

1,创建一个http的容器

docker run -it --name web1 httpd

2.创建一个容器使用web1 的网络

docker run -it --network=container:web1 busybox

然后发现 web1的网络和busybox的网络完全一致

busybox 可以直接用 127.0.0.1 访问 web1 的 http 服务

容器访问外部

docker-网络基础

1.查看主机路由

 ✘ ⚡ root@bogon  ~  ip r
default via 192.168.246.2 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
172.18.0.0/16 dev br-57fd498eab6a proto kernel scope link src 172.18.0.1
192.168.0.0/24 dev br-7119530cd62a proto kernel scope link src 192.168.0.10
192.168.246.0/24 dev ens33 proto kernel scope link src 192.168.246.138

2.查看nat表

 ✘ ⚡ root@bogon  ~  iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N DOCKER
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 192.168.0.0/24 ! -o br-7119530cd62a -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o br-57fd498eab6a -j MASQUERADE
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A DOCKER -i br-7119530cd62a -j RETURN
-A DOCKER -i br-57fd498eab6a -j RETURN
-A DOCKER -i docker0 -j RETURN
⚡ root@bogon  ~ 

-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE

来自 172.17.0.0/16 网段的包,目标地址是外网(! -o docker0),就把它交给 MASQUERADE 处理。而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)

3.抓包分析

抓docker

 ? root@bogon ? ~ ? tcpdump -i docker0 -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
03:21:28.362520 IP 172.17.0.2 > 13.107.21.200: ICMP echo request, id 1792, seq 133, length 64
03:21:28.400419 IP 13.107.21.200 > 172.17.0.2: ICMP echo reply, id 1792, seq 133, length 64
03:21:29.363317 IP 172.17.0.2 > 13.107.21.200: ICMP echo request, id 1792, seq 134, length 64
03:21:29.402380 IP 13.107.21.200 > 172.17.0.2: ICMP echo reply, id 1792, seq 134, length 64

docker0 收到 busybox 的 ping 包,源地址为容器 IP 172.17.0.2

抓ens33

⚡ root@bogon  ~  tcpdump -i ens33 -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
03:22:30.405175 IP 192.168.246.138 > 13.107.21.200: ICMP echo request, id 1792, seq 195, length 64
03:22:30.442453 IP 13.107.21.200 > 192.168.246.138: ICMP echo reply, id 1792, seq 195, length 64
03:22:31.405493 IP 192.168.246.138 > 13.107.21.200: ICMP echo request, id 1792, seq 196, length 64

ping 包的源地址变成了 ens33 的 IP 192.168.246.138

busybox 发送 ping 包:172.17.0.2 > www.bing.com。

docker0 收到包,发现是发送到外网的,交给 NAT 处理。

NAT 将源地址换成 enp0s3 的 IP:192.168.246.138 > www.bing.com。

ping 包从 ens33 发送出去,到达 www.bing.com。

外部访问内部

使用端口映射的方式

docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时通过-p参数映射端口:

 ⚡ root@bogon  ~  docker run -d -p 80 httpd
f16876575a9bd742eaeab3b809685d257c51593a491f8b746490a3e2b22d664f ⚡ root@bogon  ~  docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f16876575a9b httpd "httpd-foreground" 2 minutes ago Up 2 minutes 0.0.0.0:32768->80/tcp nostalgic_mccarthy ⚡ root@bogon  ~  curl 0.0.0.0:32768
<html><body><h1>It works!</h1></body></html>

指定绑定端口

 ⚡ root@bogon  ~  docker run -d -p 8080:80  httpd
026c43829faa0a5a9956dd76e029ad39943c9c74739026558634e9b3d9ad9e0d
⚡ root@bogon  ~  docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
026c43829faa httpd "httpd-foreground" 3 seconds ago Up 2 seconds 0.0.0.0:8080->80/tcp tender_hawking
f16876575a9b httpd "httpd-foreground" 8 minutes ago Up 8 minutes 0.0.0.0:32768->80/tcp nostalgic_mccarthy

每一个映射的端口,host 都会启动一个 docker-proxy 进程来处理访问容器的流量

 ⚡ root@bogon  ~  ps -ef | grep docker-proxy
root 1584 1 0 03:17 ? 00:00:02 /usr/bin/dockerd-current --add-runtime docker-runc=/usr/libexec/docker/docker-runc-current --default-runtime=docker-runc --exec-opt native.cgroupdriver=systemd --userland-proxy-path=/usr/libexec/docker/docker-proxy-current --init-path=/usr/libexec/docker/docker-init-current --seccomp-profile=/etc/docker/seccomp.json --selinux-enabled --log-driver=journald --signature-verification=false --storage-driver overlay2
root 2126 1584 0 03:26 ? 00:00:00 /usr/libexec/docker/docker-proxy-current -proto tcp -host-ip 0.0.0.0 -host-port 32768 -container-ip 172.17.0.2 -container-port 80
root 2362 1584 0 03:35 ? 00:00:00 /usr/libexec/docker/docker-proxy-current -proto tcp -host-ip 0.0.0.0 -host-port 8080 -container-ip 172.17.0.3 -container-port 80
root 2525 1407 0 03:36 pts/0 00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn docker-proxy

docker-网络基础