MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Docker容器化网络配置与管理

2022-05-116.2k 阅读

一、Docker网络基础概念

  1. 容器网络模型(CNM)
    • Docker使用容器网络模型(CNM)来管理容器网络。CNM由三个主要组件构成:Sandbox(沙箱)、Endpoint(端点)和Network(网络)。
    • Sandbox:每个容器都有自己的网络沙箱,包含了容器的网络配置,如网卡、路由表、DNS配置等。这就像是一个独立的网络环境,容器内的应用在这个环境中进行网络通信。例如,当你启动一个基于Ubuntu镜像的容器时,该容器内部有自己独立的网络命名空间,就像在一个独立的虚拟机中一样拥有自己的网络配置。
    • Endpoint:Endpoint是容器连接到网络的接口。一个容器可以有多个Endpoint,连接到不同的网络。当容器创建时,Docker会为其创建一个默认的Endpoint,并连接到默认的网络。例如,当你运行docker run -it ubuntu时,Docker会为这个Ubuntu容器创建一个Endpoint,并连接到bridge网络(默认网络类型)。
    • Network:Network定义了一组相互连接的Endpoint集合。容器通过Endpoint连接到网络,从而实现相互通信。Docker提供了几种内置的网络类型,如bridgehostnone等,每种网络类型都有其特定的用途和特点。
  2. Docker网络驱动
    • bridge驱动:这是Docker默认的网络驱动。它在宿主机上创建一个虚拟网桥(例如docker0),容器通过虚拟网卡连接到这个网桥上。容器之间可以通过IP地址相互通信,并且容器可以通过NAT(网络地址转换)访问外部网络。例如,以下是查看宿主机上docker0网桥信息的命令:
ip link show docker0
  • host驱动:使用host驱动时,容器直接使用宿主机的网络栈,容器不会有自己独立的网络命名空间。这意味着容器的网络配置与宿主机完全相同,容器内的应用监听的端口直接暴露在宿主机上。比如,如果你在容器内启动一个监听80端口的Web服务器,在宿主机上直接访问80端口就可以访问到容器内的Web应用。使用示例如下:
docker run -it --network=host ubuntu
  • none驱动none驱动为容器提供一个最小化的网络栈,容器只有lo回环接口,没有其他网络配置。这种网络类型适用于不需要网络通信或者需要自定义网络配置的场景。例如,对于一些只进行本地计算且不需要网络访问的容器,可以使用none驱动。启动命令如下:
docker run -it --network=none ubuntu
  • overlay驱动overlay驱动主要用于在多个Docker主机之间创建分布式网络,实现跨主机容器之间的通信。它通过VXLAN(虚拟可扩展局域网)技术在多个主机之间建立隧道,使不同主机上的容器看起来像是在同一个局域网内。这种驱动常用于Docker Swarm集群环境中。要使用overlay驱动,首先需要创建一个overlay网络:
docker network create -d overlay my - overlay - net

然后可以将容器连接到这个overlay网络:

docker run -it --network=my - overlay - net ubuntu

二、Docker容器内网络配置

  1. 容器内网络接口配置
    • 在容器内部,可以像在普通Linux系统中一样配置网络接口。例如,查看容器内网络接口信息,可以在容器内执行以下命令:
ip addr
  • 如果需要为容器添加额外的网络接口,可以使用docker network connect命令。首先创建一个新的网络,比如名为my - new - net的桥接网络:
docker network create -d bridge my - new - net

然后运行一个容器,例如my - container

docker run -d --name my - container ubuntu

接着将容器连接到新创建的网络:

docker network connect my - new - net my - container

此时,进入容器内再次执行ip addr命令,会看到新添加的网络接口。 2. 容器内DNS配置

  • Docker容器默认使用宿主机的DNS配置。可以通过--dns选项在运行容器时指定自定义的DNS服务器。例如,要使用Google的公共DNS服务器(8.8.8.8和8.8.4.4),运行容器的命令如下:
docker run -it --dns=8.8.8.8 --dns=8.8.4.4 ubuntu
  • 在容器内部,DNS配置信息存储在/etc/resolv.conf文件中。如果容器内的应用需要解析域名,会根据这个文件中的DNS服务器地址进行查询。
  1. 容器内路由配置
    • 容器内的路由表可以通过ip route命令查看。默认情况下,容器的默认路由通常指向宿主机上的虚拟网桥(对于bridge网络)。例如,在基于bridge网络的容器内,执行ip route命令可能会看到类似如下的输出:
default via 172.17.0.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2

这里172.17.0.1是宿主机上docker0网桥的IP地址,是容器的默认网关。如果需要在容器内添加自定义路由,可以使用ip route add命令。例如,要添加一条到192.168.1.0/24网络的路由,下一跳为172.17.0.1,命令如下:

ip route add 192.168.1.0/24 via 172.17.0.1

三、Docker宿主机网络配置与管理

  1. 宿主机网络接口与Docker网桥
    • 当Docker安装在宿主机上时,会创建一个默认的网桥,通常是docker0。可以通过以下命令查看docker0网桥的详细信息:
brctl show docker0
  • 宿主机上的容器通过虚拟网卡(例如veth对)连接到docker0网桥上。veth对是一种虚拟网络设备,一端连接在容器内,另一端连接在宿主机的网桥上。例如,在宿主机上执行ip link命令,可以看到类似vethXXXXXX的设备,这就是连接容器的虚拟网卡。
  • 如果需要自定义Docker网桥的配置,可以在Docker守护进程启动时通过配置文件进行设置。在Linux系统中,Docker的配置文件通常位于/etc/docker/daemon.json。例如,要修改docker0网桥的子网,可以在daemon.json中添加如下内容:
{
    "bip": "192.168.100.1/24"
}

然后重启Docker服务使配置生效:

sudo systemctl restart docker
  1. 宿主机网络地址转换(NAT)配置
    • Docker默认使用SNAT(源网络地址转换)来实现容器访问外部网络。在宿主机上,iptables规则用于配置NAT。可以通过以下命令查看与Docker相关的iptables规则:
sudo iptables -t nat -L -n
  • 通常会看到类似如下的规则:
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        anywhere

这表示从172.17.0.0/16docker0网桥的默认子网)发出的所有流量在通过宿主机的网络接口时会进行地址伪装,将源IP地址转换为宿主机的IP地址,从而使容器能够访问外部网络。

  • 如果需要自定义NAT规则,例如允许特定的容器使用宿主机的IP地址直接访问外部网络(而不是通过NAT),可以通过修改iptables规则来实现。但需要谨慎操作,因为错误的配置可能会导致网络安全问题。例如,假设容器的IP地址为172.17.0.5,要使其直接使用宿主机IP访问外部网络,可以添加如下iptables规则:
sudo iptables -t nat -A POSTROUTING -s 172.17.0.5 -j SNAT --to - source <宿主机IP地址>
  1. 宿主机端口映射与容器访问
    • Docker支持将宿主机的端口映射到容器内的端口,这样可以通过宿主机的IP地址和映射端口来访问容器内的应用。例如,要将宿主机的8080端口映射到容器内的80端口,可以在运行容器时使用-p选项:
docker run -d -p 8080:80 nginx
  • 此时,在浏览器中访问http://宿主机IP:8080就可以访问到容器内运行的Nginx服务器。端口映射的原理是通过iptablesPREROUTING链来实现的。在宿主机上执行iptables -t nat -L -n命令,可以看到类似如下的规则:
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       tcp  --  anywhere             anywhere             tcp dpt:8080 to:172.17.0.2:80

这里将发往宿主机8080端口的TCP流量转发到容器的80端口(假设容器的IP地址为172.17.0.2)。

  • 还可以使用-P选项让Docker随机分配宿主机端口映射到容器的公开端口。例如:
docker run -d -P nginx

然后可以通过docker port命令查看具体的端口映射情况:

docker port <容器ID或名称>

四、Docker自定义网络创建与管理

  1. 创建自定义桥接网络
    • 除了使用默认的bridge网络,还可以创建自定义的桥接网络。自定义桥接网络可以有不同的子网、DNS配置等,方便对容器网络进行更精细的管理。创建自定义桥接网络的命令如下:
docker network create -d bridge --subnet=192.168.200.0/24 --gateway=192.168.200.1 my - custom - bridge

这里创建了一个名为my - custom - bridge的桥接网络,子网为192.168.200.0/24,网关为192.168.200.1

  • 创建好自定义桥接网络后,可以将容器连接到这个网络。例如,运行一个新的容器并连接到my - custom - bridge网络:
docker run -d --name my - custom - container --network=my - custom - bridge ubuntu
  1. 创建自定义overlay网络
    • 如前文所述,overlay网络用于跨主机容器通信。在创建overlay网络之前,需要确保多个Docker主机之间能够相互通信,并且都配置了相同的overlay网络驱动。假设在一个Docker Swarm集群环境中创建overlay网络:
docker network create -d overlay --attachable my - overlay - net
  • --attachable选项表示非Swarm服务的容器也可以连接到这个网络。然后可以在不同的主机上运行容器并连接到这个overlay网络:
docker run -d --name my - overlay - container1 --network=my - overlay - net ubuntu
docker run -d --name my - overlay - container2 --network=my - overlay - net ubuntu

这样,my - overlay - container1my - overlay - container2就可以通过overlay网络进行通信,即使它们位于不同的主机上。 3. 管理自定义网络

  • 可以使用docker network ls命令查看所有的Docker网络,包括自定义网络:
docker network ls
  • 要查看某个自定义网络的详细信息,可以使用docker network inspect命令。例如,查看my - custom - bridge网络的详细信息:
docker network inspect my - custom - bridge
  • 如果需要删除自定义网络,可以使用docker network rm命令。例如,删除my - custom - bridge网络:
docker network rm my - custom - bridge

但需要注意的是,只有当没有容器连接到该网络时才能删除成功。如果有容器连接,需要先断开容器与网络的连接:

docker network disconnect my - custom - bridge <容器ID或名称>

五、Docker容器间网络通信与隔离

  1. 容器间通过同一网络通信
    • 当多个容器连接到同一个网络时,它们可以通过IP地址相互通信。例如,创建一个自定义桥接网络my - comm - net
docker network create -d bridge my - comm - net

然后运行两个容器container1container2并连接到这个网络:

docker run -d --name container1 --network=my - comm - net ubuntu
docker run -d --name container2 --network=my - comm - net ubuntu

container1内,可以通过ping命令测试与container2的连通性。首先获取container2的IP地址,在宿主机上执行:

docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container2

假设得到的IP地址为172.18.0.3,在container1内执行:

ping 172.18.0.3

如果网络配置正确,应该可以看到ping通的结果。

  • 容器间也可以通过容器名称进行通信,前提是容器所在的网络配置了DNS服务,能够解析容器名称。例如,在container1内,可以使用ping container2来测试与container2的连通性。
  1. 容器网络隔离
    • Docker通过网络命名空间实现容器网络隔离。每个容器都有自己独立的网络命名空间,这意味着容器之间的网络配置相互独立,不会相互干扰。例如,两个容器即使使用相同的端口号监听,也不会冲突,因为它们在不同的网络命名空间中。
    • 不同网络的容器之间默认是隔离的,无法直接通信。例如,一个容器连接到my - net1网络,另一个容器连接到my - net2网络,这两个容器之间不能直接通过IP地址通信,除非进行额外的网络配置,如设置路由或者使用网络策略。
    • 网络策略:Docker支持使用网络策略来进一步控制容器间的网络访问。网络策略基于标签(label)来定义规则。例如,首先给容器添加标签:
docker run -d --name container - with - label --label security=high --network=my - net ubuntu

然后创建一个网络策略文件net - policy.json,内容如下:

{
    "Name": "my - net - policy",
    "Driver": "bridge",
    "Ingress": true,
    "Rules": [
        {
            "Action": "allow",
            "Protocol": "tcp",
            "Port": 80,
            "Source": {
                "Tag": "security=high"
            }
        }
    ]
}

接着使用以下命令创建网络策略:

docker network create -d bridge --ipam - driver default --config - from net - policy.json my - net - with - policy

这样,只有标签为security=high的容器才能访问连接到my - net - with - policy网络且监听80端口的容器。

六、Docker容器与外部网络的交互

  1. 容器访问外部网络
    • 如前文所述,Docker通过SNAT(源网络地址转换)使容器能够访问外部网络。容器发出的流量经过宿主机的网络接口时,源IP地址被转换为宿主机的IP地址。例如,在容器内执行ping www.baidu.com命令,容器的流量会通过宿主机的网络接口发送出去,外部网络看到的源IP地址是宿主机的IP地址。
    • 如果需要容器使用特定的网络接口访问外部网络,可以在运行容器时使用--network - alias选项结合自定义网络来实现。例如,假设宿主机有两个网络接口eth0eth1,要让容器通过eth1访问外部网络,可以创建一个自定义网络并指定--gatewayeth1的IP地址所在子网的网关:
docker network create -d bridge --subnet=192.168.1.0/24 --gateway=192.168.1.1 my - external - net

然后运行容器并连接到这个网络:

docker run -d --name my - external - container --network=my - external - net ubuntu
  1. 外部网络访问容器
    • 实现外部网络访问容器主要通过端口映射。如前面提到的,使用-p选项可以将宿主机的端口映射到容器内的端口。例如,将宿主机的8080端口映射到容器内的80端口,外部网络可以通过http://宿主机IP:8080访问容器内的Web应用。
    • 对于一些复杂的场景,如需要从外部网络通过域名访问容器内的服务,可以结合DNS服务器进行配置。首先在宿主机上配置一个DNS服务器(如Bind9),然后将容器的服务域名解析到宿主机的IP地址和映射端口。例如,假设容器内运行一个Web服务,映射端口为8080,在DNS服务器的配置文件中添加如下记录:
web - service.example.com. IN A <宿主机IP地址>

这样,外部网络就可以通过http://web - service.example.com:8080访问容器内的Web服务。 3. 容器与VPN网络的集成

  • 在某些场景下,容器需要访问VPN网络内的资源。一种实现方式是在宿主机上配置VPN连接,然后让容器通过宿主机的VPN网络访问资源。例如,在宿主机上使用OpenVPN客户端连接到VPN网络:
sudo openvpn /etc/openvpn/client.ovpn

然后,运行容器并配置容器使用宿主机的网络栈(使用host网络驱动):

docker run -it --network=host ubuntu

这样,容器内的应用就可以访问VPN网络内的资源。另一种方式是在容器内直接配置VPN客户端,这种方式需要在容器镜像中安装VPN客户端软件,并且需要处理好VPN配置文件的挂载等问题。例如,将VPN配置文件挂载到容器内,然后在容器内启动VPN客户端:

docker run -it -v /path/to/client.ovpn:/etc/openvpn/client.ovpn ubuntu openvpn /etc/openvpn/client.ovpn

七、故障排查与性能优化

  1. 网络故障排查
    • 容器间通信故障:如果容器间无法通信,首先检查容器是否连接到同一个网络。可以使用docker network inspect命令查看容器的网络连接情况。例如:
docker network inspect my - network - name

确认容器的IP地址是否在同一子网内,并且容器的防火墙设置是否允许通信。在容器内可以使用iptables -L命令查看防火墙规则。如果需要,添加允许通信的规则,例如:

iptables -A INPUT -p tcp --dport 80 -j ACCEPT
  • 容器与外部网络通信故障:若容器无法访问外部网络,检查宿主机的NAT配置是否正确。使用iptables -t nat -L命令查看NAT规则。同时,检查宿主机的网络连接是否正常,例如可以在宿主机上执行ping命令测试外部网络连通性。如果容器使用自定义网络,检查自定义网络的网关配置是否正确。
  • 端口映射故障:当外部网络无法通过映射端口访问容器内服务时,检查iptablesPREROUTING链规则是否正确。使用iptables -t nat -L命令查看规则。例如,确保有类似DNAT tcp -- anywhere anywhere tcp dpt:8080 to:172.17.0.2:80这样的规则(假设容器IP为172.17.0.2,映射端口为8080)。同时,检查容器内的服务是否正常运行并监听正确的端口,可以在容器内使用netstat -tlnp命令查看端口监听情况。
  1. 网络性能优化
    • 调整MTU值:MTU(最大传输单元)是网络中能够传输的最大数据包大小。在Docker网络中,默认的MTU值可能不是最优的。可以通过修改docker0网桥或者自定义网络的MTU值来优化性能。例如,修改docker0网桥的MTU值为9000(需要宿主机网络支持):
sudo ifconfig docker0 mtu 9000

对于自定义网络,可以在创建网络时指定MTU值:

docker network create -d bridge --mtu 9000 my - optimized - net
  • 使用高性能网络驱动:对于一些对网络性能要求较高的场景,可以考虑使用SR - IO(单根I/O虚拟化)技术或者DPDK(数据平面开发套件)等高性能网络驱动。例如,在支持SR - IO的服务器上,可以将物理网卡的VF(虚拟功能)设备直接分配给容器使用,提高网络性能。但这种配置相对复杂,需要服务器硬件和操作系统的支持。
  • 优化容器内网络配置:在容器内部,关闭不必要的网络服务和功能,减少网络资源的占用。例如,如果容器不需要DNS服务发现,可以在/etc/systemd/network目录下创建一个配置文件,禁用DNS服务发现:
[Network]
DHCP=yes
DNS=off

然后重启网络服务:

sudo systemctl restart systemd - networkd