Docker容器连接
1、概述
Docker 提供网络服务的方法包括两种:
- 通过网络端口映射的方式,外部访问docker容器;
- 通过Docker容器间互联的方式。
2、网络端口映射
在docker容器中可以运行一些网络应用,外部想要访问docker容器内的应用,可以通过-p或-P选项来指定端口映射,两个选项的区别如下所示:
- -P: 是容器内部端口随机映射到主机的端口,其中这个随机的端口范围可以在 /proc/sys/net/ipv4/ip_local_port_range文件中查看。
- -p: 是容器内部端口绑定到指定的主机端口。
2.1 使用-P选项外部访问docker容器
# 创建一个端口随机映射到本地主机的容器
[root@localhost ~]# docker run -d -P training/webapp python app.py
fd7fe9a68463f8673fc8d73b89451d68ac2f7e7b0aeee73082cb3db45bfab476
# 查看容器信息。:::32770->5000/tcp,5000为容器端口,32770为本地主机随机映射的端口
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fd7fe9a68463 training/webapp "python app.py" 4 seconds ago Up 3 seconds 0.0.0.0:32770->5000/tcp, :::32770->5000/tcp youthful_williamson
2.2 使用-p选项外部访问docker容器
- -p 指定特定的端口绑定到一个容器上,其支持的格式有:
- hostPort:containerPort:映射本地特定端口所有ip地址到容器的特定端口;
- ip:hostPort:containerPort:映射本地特定端口的特定ip地址到容器的特定端口;
- ip::containerPort:映射本地主机特定ip地址的随机端口到容器特定的端口;
- 其中,上面的hostPort表示主机端口或本地端口;containerPort表示容器端口;ip表示主机ip地址,下面分别罗列这三种格式的使用方法:
- hostPort:containerPort
#绑定本地所有ip地址的5500端口到容器的5000端口上 [root@localhost ~]# docker run -d -p 5500:5000 training/webapp python app.py dd400ee1a85b9ae515033cba8eaaea0996036fb8178f3ae6b26688837d534420 #0.0.0.0:5500 表示本地主机所有ip地址的5500端口 [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES dd400ee1a85b training/webapp "python app.py" 15 seconds ago Up 13 seconds 0.0.0.0:5500->5000/tcp, :::5500->5000/tcp busy_booth
- ip:hostPort:containerPort
# 绑定本地端口5600上192.168.122.1的ip地址到容器的5000端口上 [root@localhost ~]# docker run -d -p 192.168.122.1:5600:5000 training/webapp python app.py ac8e008063c701a1b0f24937ff0d0ce8ce800cf8ade9bd3a2aba09f756c8192c [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ac8e008063c7 training/webapp "python app.py" 4 seconds ago Up 3 seconds 192.168.122.1:5600->5000/tcp strange_beaver
- ip::containerPort
# 绑定本地主机随机端口上的192.168.122.1 ip地址到容器的5000端口上,这里的随机分配的主机端口为32768。 [root@localhost ~]# docker run -d -p 192.168.122.1::5000 training/webapp python app.py 0d2e0590b7ede08552bd42cb11bfdd47d10ebe57208de8adf6a983a4a755f5ca [root@localhost ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0d2e0590b7ed training/webapp "python app.py" 6 seconds ago Up 5 seconds 192.168.122.1:32768->5000/tcp vigilant_borg
- hostPort:containerPort
- 同时绑定多个端口
- -p可以多次使用来绑定多个端口,例如:
[root@localhost ~]# docker run -d -p 192.168.122.1::5000 -p 3000:80 training/webapp python app.py 88ecd0c638975fca8471787be24c771643f8f939667254c5098d5c057d572fbd
- -p可以多次使用来绑定多个端口,例如:
3、容器互联
3.1 新建网络
# 创建一个新的Docker网络, 网络类型为bridge, 网络名称为 test-net
[root@localhost ~]# docker network create -d bridge test-net
#查看Docker网络信息
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
956cd42a27ba bridge bridge local
5091d8c0f9dd host host local
2dfe918dbe97 none null local
71c2d19d105e test-net bridge local
3.2 连接容器
#运行一个容器命名为test1并连接到新建的test-net网络:
[root@localhost ~]# docker run -itd --name test1 --network test-net ubuntu /bin/bash
a84040a535dd733fbd630b845ea41e856743de95694a80776617e6d25bc55d37
#运行一个容器命名为test2并连接到新建的test-net网络:
[root@localhost ~]# docker run -itd --name test2 --network test-net ubuntu /bin/bash
7dae8252369a0ef98b4014f0324d8e618513c11724dc98508b33243a5f635d2b
#查看容器运行信息
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7dae8252369a ubuntu "/bin/bash" 2 seconds ago Up 2 seconds test1
a84040a535dd ubuntu "/bin/bash" 9 seconds ago Up 8 seconds test2
[root@localhost ~]#
3.3 确认容器互联关系
- 通过 ping 来证明 test1 容器和 test2 容器建立了互联关系;如果容器内中无 ping 命令,则在容器内执行以下命令安装 ping。
apt-get update
apt install iputils-ping
- 进入容器执行ping
# 在test1容器中ping test2
[root@localhost ~]# docker exec -it test1 /bin/bash
root@7dae8252369a:/# ping test2
PING test2 (172.18.0.2) 56(84) bytes of data.
64 bytes from test2.test-net (172.18.0.2): icmp_seq=1 ttl=64 time=0.052 ms
64 bytes from test2.test-net (172.18.0.2): icmp_seq=2 ttl=64 time=0.061 ms
# 在test2容器中ping test1
[root@localhost ~]# docker exec -it test2 /bin/bash
root@a84040a535dd:/# ping test1
PING test1 (172.18.0.3) 56(84) bytes of data.
64 bytes from test1.test-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.052 ms
64 bytes from test1.test-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.059 ms
3.3 配置DNS
-
在宿主机的 /etc/docker/daemon.json 文件中增加以下内容来设置全部容器的 DNS:
{ "dns" : [ "114.114.114.114", "8.8.8.8" ] }
-
重启 docker
- 配置完dns之后,需要重启 docker 才能生效。在设置dns之后启动容器,它的DNS都会自动配置为 114.114.114.114 和 8.8.8.8。
- systemctl restart docker
-
查看容器的 DNS 是否生效
- docker run -it --rm ubuntu cat etc/resolv.conf
-
手动指定容器的配置
- 如果只想在指定的容器设置 DNS,则可以使用以下命令:docker run -it --rm -h host_ubuntu --dns=114.114.114.114 --dns-search=test.com ubuntu
参数说明:
–rm:容器退出时自动清理容器内部的文件系统。
-h HOSTNAME 或者 --hostname=HOSTNAME: 设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts。
–dns=IP_ADDRESS: 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
–dns-search=DOMAIN: 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 host.example.com。- 如果在容器启动时没有指定 --dns 和 --dns-search,Docker 会默认用宿主主机上的 /etc/resolv.conf 来配置容器的 DNS。
4、Docker 网络基础(容器间通信)
4.1 Docker内置网络模式
网络模式 | 简介 | 指定网络模式参数项 |
---|---|---|
bridge | Docker 默认使用 bridge 网络模式,创建一个名为 docker0 的虚拟网桥,并为每个容器分配一个 IP 地址。容器间可以通过 IP 地址相互通信 | –net = bridge(默认设置) |
host | 容器将不会虚拟出自己的网卡,配置自己的Ip等,而是使用宿主机的IP和端口。容器的网络配置与宿主机相同,可以通过宿主机的 IP 地址直接访问容器。 | –net = host |
container | 指定新创建容器与已存在的某个容器共享一个 Network Namespace。使它们可以直接使用 localhost 来进行通信,就像在同一台主机上运行的进程一样。容器之间的进程通过 lo 网卡设备通信 | –net container:已运行的容器名称 |
none | 新创建的容器不会创建自己的网卡和配置自己的IP,只有lo网卡。在none网络模式下,容器没有网络接口,无法与外部网络通信。该模式主要用于一些特殊场景,如只需要运行一个进程的容器或与网络无关的容器。 | –net =none |
4.1 bridge 模式
4.1.1 虚拟网桥 docker0
- docker守护进程就是通过 docker0 为docker的容器提供网络连接的各种服务。
docker0是一个Linux虚拟网桥(网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过容器IP直接容器)
- docker守护进程在一个容器启动时,实际上它要创建网络连接的两端。一端是在容器中的网络设备,而另一端是在运行docker守护进程的主机上打开一个名为veth*的一个接口,用来实现docker这个网桥与容器的网络通信。
4.1.2 查看宿主机网络设备及容器内的网络设备
- 宿主机执行 ifconfig 命令
- 容器内执行ifconfig命令
- 分别查看宿主机和容器的网卡信息时会发现,每启动一个容器,宿主机会增加一个veth**格式命名的网卡,容器内会有eth0网卡。所以可以得出结论,每启动一个容器,就会多出一对网卡,同时他们被连接到docker0上(或者自定义虚拟网卡上),而docker0(或自定义虚拟网卡)又和虚拟机之间连通。可以抽象出如下网络模型:
- 分别查看宿主机和容器的网卡信息时会发现,每启动一个容器,宿主机会增加一个veth**格式命名的网卡,容器内会有eth0网卡。所以可以得出结论,每启动一个容器,就会多出一对网卡,同时他们被连接到docker0上(或者自定义虚拟网卡上),而docker0(或自定义虚拟网卡)又和虚拟机之间连通。可以抽象出如下网络模型:
4.1.3 自定义虚拟网桥
# 1.查看网络信息
[root@localhost ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
152468cf8556 bridge bridge local
5091d8c0f9dd host host local
2dfe918dbe97 none null local
# 2.创建bridge网络
[root@localhost ~]# docker network create --driver bridge --subnet 172.18.0.0/16 --gateway 172.18.0.1 mynet
# 3.查看网络信息
docker network ls
NETWORK ID NAME DRIVER SCOPE
152468cf8556 bridge bridge local
5091d8c0f9dd host host local
2dfe918dbe97 none null local
71c2d19d105e test-net bridge local
# 4.查看新建网络详细信息
[root@localhost ~]# docker inspect test-net
[
{
"Name": "test-net",
"Id": "71c2d19d105e7374630a61367432995cafb836a88e50e18f9d66b83a17a5b057",
"Created": "2023-07-31T09:29:35.555918758+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": {}
}
]
4.1.4 Docker各个网络之间的互联
-
在没有设置的情况下,不同网络间的容器是无法进行网络连接的。不同Docker网络之间的容器需要连接的话需要把作为调用方的容器注册一个ip到被调用方所在的网络上。需要使用docker connect命令。
-
分析说明:
# 1.创建四个容器(两个使用默认docker0网络,两个使用自定义网络) [root@localhost ~]# docker run -itd --name test1 ubuntu /bin/bash [root@localhost ~]# docker run -itd --name test2 --network=test-net ubuntu /bin/bash [root@localhost ~]# docker run -itd --name test3 --network=test-net ubuntu /bin/bash [root@localhost ~]# docker run -itd --name=test5 ubuntu /bin/bash # 2.测试网络之间的容器连通性 # 容器 test1 不通 test2、test3, 但能ping通同是docker0网络的test5 [root@localhost ~]# docker exec -it test1 /bin/bash root@620e6715cb0d:/# ping test2 ping: test2: Name or service not known root@620e6715cb0d:/# ping test3 ping: test3: Name or service not known root@620e6715cb0d:/# ping 172.17.0.3 --- 172.17.0.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2004ms root@620e6715cb0d:/# ping 172.18.0.3 --- 172.18.0.2 ping statistics --- 48 packets transmitted, 0 received, 100% packet loss, time 47156ms # 容器test2通test3,但不通test1 [root@localhost ~]# docker exec -it test2 /bin/bash root@a454d400cc2a:/# ping test3 PING test3 (172.18.0.3) 56(84) bytes of data. 64 bytes from test3.test-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.047 ms 64 bytes from test3.test-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.152 ms --- test3 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1006ms rtt min/avg/max/mdev = 0.047/0.099/0.152/0.052 ms root@a454d400cc2a:/# ping test1 ping: test1: Name or service not known root@a454d400cc2a:/#
-
设置默认bridge网络的test1容器连接到自定义网络test-net
- docker network connect test-net test1
-
测试连通性
[root@localhost ~]# docker exec -it test1 /bin/bash root@620e6715cb0d:/# ping test2 PING test2 (172.18.0.2) 56(84) bytes of data. 64 bytes from test2.test-net (172.18.0.2): icmp_seq=1 ttl=64 time=0.055 ms 64 bytes from test2.test-net (172.18.0.2): icmp_seq=2 ttl=64 time=0.053 ms
-
查看test1网络信息
- 将test1连接到test-net网络后,test1容器会新增一个与test-net同网段的网卡(eth1)
root@620e6715cb0d:/# ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255 ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet) RX packets 5144 bytes 28870205 (28.8 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 3887 bytes 215608 (215.6 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.18.0.4 netmask 255.255.0.0 broadcast 172.18.255.255 ether 02:42:ac:12:00:04 txqueuelen 0 (Ethernet) RX packets 29 bytes 2154 (2.1 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 21 bytes 1498 (1.4 KB) 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 loop txqueuelen 1000 (Local Loopback) RX packets 52 bytes 3941 (3.9 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 52 bytes 3941 (3.9 KB)
- 将test1连接到test-net网络后,test1容器会新增一个与test-net同网段的网卡(eth1)
4.1.4 bridge模式的网络流程
- 容器与容器之间通讯是通过Network Namespace, bridge和veth pair这三个虚拟设备实现一个简单的二层网络,不同的namespace实现了不同容器的网络隔离让他们分别有自己的ip,通过veth pair连接到docker0网桥上实现了容器间和宿主机的互通;
- 容器与外部或者主机通过端口映射通讯是借助iptables,通过路由转发到docker0,容器通过查询CAM表,或者UDP广播获得指定目标地址的MAC地址,最后将数据包通过指定目标地址的连接在docker0上的veth pair设备,发送到容器内部的eth0网卡上;
4.1.5 bridge模式的优缺点
- 优点:
- 简单易用:桥接网络是默认的网络驱动程序,使用起来非常方便。
- 适用于单个 Docker 主机:桥接网络适用于单个 Docker 主机上的容器之间的通信。
- 可以让容器访问宿主机上的网络。
- 缺点:
- 不适用于多主机环境:桥接网络只适用于单个 Docker 主机上的容器之间的通信,不适用于多个 Docker 主机之间的通信。
4.1.6 总结
- Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
- docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。也可以在docker run的时候,通过 –network 参数选项指定为自定义的虚拟网桥。
- 网桥 docker0 创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
4.2 Host模式
4.2.1 介绍
- 容器与主机共享同一Network Namespace,共享同一套网络协议栈、路由表及iptables规则等;host 模式下,和主机上启动一个端口没有差别,也不会做端口映射,所以不能启动的服务在主机端口范围内不能冲突;
4.2.2 查看网络端口信息
```
[root@localhost ~]# netstat -nap|grep 5000
tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN 8290/python
[root@localhost ~]# ps -ef|grep 8290
root 8290 8268 0 14:44 pts/0 00:00:00 python app.py
root 8387 3781 0 14:48 pts/0 00:00:00 grep --color=auto 8290
```
4.2.3 优缺点
- 优点
- 简单易用:使用主机网络可以很容易地让容器直接访问宿主机上的网络。
- 不需要为容器分配 IP 地址:使用主机网络时,容器共享宿主机的网络命名空间,因此不需要为容器分配单独的 IP 地址。
- 缺点
- 不适用于多主机环境:主机网络只适用于单个 Docker 主机上的容器之间的通信,不适用于多个 Docker 主机之间的通信。
- 不适用于需要隔离网络的场景:使用主机网络时,容器与宿主机共享同一个网络命名空间,因此容器之间的网络隔离会变得更加困难。
4.3 Container模式
- 指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
4.4 None模式
- 使用none模式,Docker容器拥有自己的Network Namespace,但是,Docker并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
- 这种网络模式下容器只有lo回环网络,没有其他网卡。none模式可以在容器创建时通过–network=none来指定。这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
- 手动配置网络案例
#1. 以none模式启动容器
[root@localhost netns]# docker run -it --rm --net=none ubuntu /bin/bash
root@39e91a587bb7:/#
#2.在本地主机查找容器的进程id
[root@localhost netns]# docker inspect -f {{.State.Pid}} 39e9
22109
#3.为容器的进程创建对应网络命名空间
pid=22109
mkdir -p /var/run/netns
ln -s /proc/$pid/ns/net /var/run/netns/$pid
#4.查看桥接网卡的 IP 和子网掩码信息(配置容器的ip及路由信息用到)
[root@localhost netns]# ip addr show docker0
...
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
...
#5.创建一对“ veth pair ”接口
ip link add A type veth peer name B
#6.绑定A接口到网桥 dockerO
brctl addif docker0 A
#7.启用A接口
ip link set A up
#8.将B接口放到容器的网络命名空间
ip link set B netns $pid
#9.将B接口命名为 ethO
ip netns $pid ip link set dev B name eth0
#10.启动B接口(参照步骤7比对命令)
ip netns exec $pid ip link set eth0 up
#11.配置一个可用IP(桥接网卡同网段)
ip netns exec $pid ip addr add 172.17.0.3/16 dev eth0
#12.配置默认路由(docker0地址)
ip netns exec $pid ip route add default via 172.17.0.1
5、Docker 网络基础(跨主机通信)
5.1 Overlay模式
- Overlay网络允许在多个 Docker 主机之间创建网络,从而允许容器在不同的主机之间通信。
- 操作步骤
- 创建Overlay网络
docker network create --driver overlay test-overlay-net
- 继续在其他 Docker 主机上创建同名的Overlay网络
- 将所有需要通信的Docker主机加入到同一个swarm。这样,就可以在 swarm 内的所有 Docker 主机上使用同一个网络了。
- 优缺点
- 优点
- 允许容器在不同的主机之间通信:使用Overlay网络可以让位于不同 Docker 主机上的容器之间相互通信。
- 支持容器服务发现:使用Overlay网络时,Docker 提供了内置的容器服务发现功能,可以让容器自动发现其他容器,并建立连接。
- 缺点
- 配置复杂:使用Overlay网络需要配置 Docker swarm,因此比较复杂。
- 性能有一定影响:由于Overlay网络需要对网络流量进行加密和解密,因此会对网络性能产生一定的影响。
- 优点
5.2 Macvlan模式
- Macvlan 网络允许容器使用与物理主机相同的 MAC 地址,从而使得容器可以直接访问宿主机上的网络。
- 操作步骤
- 创建Macvlan网络
docker network create -d macvlan --subnet= --gateway= -o parent=<parent_interface> test-macvlan-net
- 将新创建容器连接到Macvlan网络
docker run --name container-name --network test-macvlan-net test-image
- 优缺点
- 优点
- 允许容器与外部网络通信:使用Macvlan网络可以让容器连接到物理网络上,从而可以与外部网络通信。
- 高性能:由于容器直接连接到物理网络上,因此使用物理网络时可以获得很高的网络性能。
- 缺点
- 配置复杂:使用物理网络需要对网络进行配置,包括子网、网关和物理接口等。
- 容器无法直接通信:由于每个容器都有自己的 MAC 地址和 IP 地址,因此容器之间无法直接通信。
- 优点