docker network

Docker网络和虚拟机网络一样,有很多种模式。

当安装Docker以后,会自动创建三个网络。可以使用docker network ls命令列出这些网络

$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
440aefe8afa3   bridge    bridge    local
aa8d6325580f   host      host      local
3eb717ea6240   none      null      local

常见的网络类型有以下四种 :

  • bridge:桥接模式(默认),创建一个桥接网络,内部网卡自动获取IP地址,向外访问是经过NAT(网络地址转换)。

  • host:不会创建出自己的网卡,和宿主机共享网络命名空间。即没有自己的IP,而是使用宿主机的IP和端口。

  • none:不配置网络。需要时,进行自定义配置。

  • container:容器网络连通, 和另外一个容器共享网络空间。

  • macvlan: 直接连接到物理网络

  • overlay: 允许不同宿主机上的容器相互通信

使用 ip addr 查看系统网卡, 会多出一个网卡

$ ip addr
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
        link/ether 02:42:1d:9f:bd:cd brd ff:ff:ff:ff:ff:ff
        inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
           valid_lft forever preferred_lft forever

brctl show 查看网桥,系统已经创建一个网桥

$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02421d9fbdcd       no

docker0是一个网桥,可以把其他网卡加入,它工作在二层,可以理解是一个交换机。每加入一个网卡,交换机便多一个口。

当创建一个 Docker 容器的时候,同时会创建了一对 veth pair 接口,两个接口相当于用网线直连。 这对接口其中一个在容器内;另一个被加入到docker0 网桥中,名称以 veth 开头。

容器和主机可以通信,容器之间在一个局域里,也可以相互通信。Docker 创建了在主机和所有容器之间一个虚拟二层交换网络。

           -----docker0--
          |      |      |
        veth0  veth1  veth2
----|--------|------|-----------
        |        |      |
        eth0   eth0   eth0

创建容器并运行

docker run --name bjbook -p 8000:8000 -it --rm python:3  python -m  http.server

查看网桥

brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02421d9fbdcd       no              vethd123cb3

发现网桥中增加了名称为 vethd123cb3 的网卡。

进入到容器里,查看其网卡信息

$ docker exec -it bjbook bash
$ cat /proc/net/dev
$ cat /proc/net/fib_trie

发现其网卡IP为172.17.0.2/16

网络模式

macvlan,

bridge模式

bridge模式,–net=bridge(默认)

这是docker网络的默认设置,为容器创建独立的网络名字空间,容器具有独立的网卡等所有单独的网络栈, 是最常用的使用方式。

在docker run启动容器的时候,如果不加–net参数,就默认采用这种网络模式。安装完docker,系统会自动添加一个 供docker使用的网桥docker0,我们创建一个新的容器时,容器通过DHCP获取一个与docker0同网段的IP地址, 并默认连接到docker0网桥,以此实现容器与宿主机的网络互通。

实际实现是创建veth网卡,veth网卡是一对连接在一起的网卡。然后把其中一个网卡加入网桥,另外一个放在容器中。 这样就可以将多个容器放在同一个网桥上。

在IP层是通过NAT来实现的,通过网络地址转换,可以访问容器内的网路服务。

查看网络信息:

$ docker network inspect bridge
[
        {
                "Name": "bridge",
                "Id": "47b1606e07c37288c76ad42e2798ac60d836954bdd77b2167c10633b5ba7ad1a",
                "Created": "2022-11-28T11:24:09.381326089+08:00",
                "Scope": "local",
                "Driver": "bridge",
                "EnableIPv6": false,
                "IPAM": {
                        "Driver": "default",
                        "Options": null,
                        "Config": [
                                {
                                        "Subnet": "172.17.0.0/16",
                                        "Gateway": "172.17.0.1"
                                }
                        ]
                },
                "Internal": false,
                "Attachable": false,
                "Ingress": false,
                "ConfigFrom": {
                        "Network": ""
                },
                "ConfigOnly": false,
                "Containers": {},
                "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"
                },
                "Labels": {}
        }
]

启动容器

docker run -p 8000:8000 -it --rm python:3  python -m  http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/)

容器之间互通。

实际IPTABLEs配置

$ iptables-save |grep 8000
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8000 -j ACCEPT
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 8000 -j MASQUERADE
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8000 -j DNAT --to-destination 172.17.0.2:8000

端口映射

默认是端口映射模式,启动时设置端口映射,将本机的tcp 1234端口映射到docker容器的1234端口

docker run -p 1234:1234 -d --rm zhang:v1

查看端口映射,参数为容器ID

docker port f2d8da7cb853

也可以使用iptables-save 命令查看所有的netfilter配置。

也可以通过docker inspect f2d8da7cb853 命令来查看容器的详细信息,包含了IP和端口映射信息。

也可以通过docker exec -it f2d8da7cb853 /bin/bash 命令进入容器来查看容器IP

上面没有设置docker网络, 会加入默认的docker网络空间。下面我们创建自定义的容器网络。

docker network create -d bridge mynet1

运行两个容器,并加入到mynet1网络中

docker run -it --rm --name aa1 --network mynet1 busybox sh
docker run -it --rm --name aa2 --network mynet1 busybox sh

容器名分别为aa1和aa2, 在aa1内部执行ping aa2, 可以ping通。说明自定义的容器网络, 在容器内部可以相互以主机名ping通。

查看其dns配置,和默认网络不同,其nameserver是本地的服务器 127.0.0.11

/ # cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0

容器命名为zhang1

docker run -p 9999:8000 -d –rm –name zhang1 zhang:v1

host模式

host模式,–net=host 和 –network host作用相同。

这个模式下创建出来的容器,直接使用容器宿主机的网络名字空间,例如网卡和IP都是和宿主机共用。 但容器的其他方面,如文件系统和进程等是和宿主机隔离的。

容器将不拥有自己独立的Network Namespace,即没有独立的网络环境。它和宿主机共用网卡和IP。 例如启动nginx服务,默认绑定在主机的80端口,不用创建端口映射。

$ docker run --rm -d --network host --name my_nginx nginx
    1f378ca055340e87b3072e8cb15390c9b45a2b47ce4ebc8ec6dcfdec3e0503d5

查看运行的docker

$ docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED          STATUS          PORTS                                       NAMES
1f378ca05534   nginx      "/docker-entrypoint.…"   36 seconds ago   Up 35 seconds                                               my_nginx
  • host模式优势是网络性能比较好,因为没有进行NAT。

  • 劣势是网络隔离性不好,容器和宿主机的端口存在抢占行为。

host模式运行的docker 可以理解为一个进程, 可以直接使用宿主机的IP和端口,因此也不需要设置端口映射。

container模式

指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。

新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。 这样两个容器除了网络,其他的如文件系统和进程等是隔离的。两个容器的进程可以通过 lo 网卡设备通信。

应用举例:

todo:

优势:节约的网络资源,多个容器共享网络空间。容器可以通过访问 localhost:port 来访问相同网络名字空间下的其他容器。

none模式

该模式Docker容器拥有自己的Network Namespace,但Docker容器没有任何网络配置。

这个Docker容器没有网卡、IP地址等信息。按需另外为Docker容器添加网卡、配置IP等。

none模式可以在容器创建时通过–network=none来指定。或使用 –net=none 指定。

应用举例:

例如一些计算任务,写入磁盘,不需要网络的情况下使用。

todo

创建桥接网络

创建一个桥接网络

$ docker network create --driver bridge my_bridge

查看已将创建了my_bridge

$ docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
47b1606e07c3   bridge      bridge    local
aa8d6325580f   host        host      local
4dc1bb25fc47   my_bridge   bridge    local
3eb717ea6240   none        null      local

查看多了一个网卡, 其IP地址为172.18.0.1

ifconfig
br-4dc1bb25fc47: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
                inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
                ether 02:42:cb:00:7a:7b  txqueuelen 0  (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

参考资料