除了自动创建的网络,还可以创建自定义网络。Docker提供三种自定义网络驱动: bridge /overlay / macvlan ,其中 overlaymacvlan 用于创建跨主机的网络。

注意:建议使用自定义的网络来控制哪些容器可以相互通信,可以自动DNS解析容器名称到IP地址

除了自动创建的网络还可以创建自定义网络。Docker提供三种自定义网络驱动:

  • bridge
  • overlay
  • macvlan

其中 overlaymacvlan 用于创建跨主机的网络。

注意:建议使用自定义的网络来控制哪些容器可以相互通信,可以自动DNS解析容器名称到IP地址

自定义创建bridge网络#

通过 bridge 驱动创建类似于默认的 bridge 网络(自定义网桥中会自己分配ip地址和网关地址)。

创建自定义网桥

[root@centos-01 ~]# docker network create --driver bridge my-net
c62a2e3d6ad5dece71a470cde488ad9c41c24fac9380689f43905ffbd70d4389

[root@centos-01 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
br-c62a2e3d6ad5		8000.0242ae4b8680	no		
docker0		8000.0242cbf135ea	no		

[root@centos-01 ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
f6524186651f   bridge    bridge    local
ed7ffc7437dd   host      host      local
c62a2e3d6ad5   my-net    bridge    local
fa66bc1a17f4   none      null      local

看到新增网桥 br-c62a2e3d6ad5c62a2e3d6ad5 是新建网桥 my-net 的短id,docker network inspect  看下 my-net 的配置信息:

[root@centos-01 ~]# docker network inspect my-net 
[
    {
        "Name": "my-net",
        "Id": "c62a2e3d6ad5dece71a470cde488ad9c41c24fac9380689f43905ffbd70d4389",
        "Created": "2021-06-18T11:16:55.17530733+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]

这里 172.19.0.0/16Docker 自动分配的IP网段,网关为 172.19.0.1 ,在 my-net 对应的网桥br-c62a2e3d6ad5

如果想要指定IP网段。只需在创建网段时指定 --subnet  和 -gateway 参数即可,命令如下:

docker network create --driver bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my-net

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

[root@centos-01 ~]# docker run -d --network=my-net --name nginx  nginx
2104ba150f62d9ea552d09b05ccb0b376db10ce054b50908d16361a639826b84

查看容器配置:

容器分配到的IP为 172.19.0.2 到目前为止,这里的IP是docker自动从 subnet 中分配的,如果想要指定一个静态IP可以通过 --ip 指定。

这里有个问题需要注意:

[root@centos-01 ~]# docker run -d --network=my-net --ip 172.19.0.110 --name nginx  nginx
5506e7ca82c8252727d8e7e4ae2c25ae1e3e5276fcf031112a17702ac4194e77
docker: Error response from daemon: user specified IP address is supported only when connecting to networks with user configured subnets.

Docker中只有使用 --subnet 创建的网络才能指定静态IP。因此在使用 docker-compose 或者 docker run 命令创建容器时,如果需要指定可以使用 :

  • –subnet  指定 IP 段
  • –gateway 指定网关
  • my-net  指定网桥名称

同网桥下容器通信#

nginx1

[root@centos-01 ~]# docker run -d --name nginx1 --network=my-net nginx
96a448f4c29fef3038abdd91410d7f9264ef3620cb1a80974bcb63694e61953c

[root@centos-01 ~]# docker exec -it nginx1 /bin/bash
root@96a448f4c29f:/# ping 172.19.0.3
PING 172.19.0.3 (172.19.0.3) 56(84) bytes of data.
64 bytes from 172.19.0.3: icmp_seq=1 ttl=64 time=0.175 ms
64 bytes from 172.19.0.3: icmp_seq=2 ttl=64 time=0.052 ms
64 bytes from 172.19.0.3: icmp_seq=3 ttl=64 time=0.044 ms

nginx2:

[root@centos-01 ~]# docker run -d --name nginx2 --network=my-net nginx
8d47f51c8138e0ef935f3e8a7447c16a655ce21f18e87a0af0bc6e5ea2daaa21

[root@centos-01 ~]# docker exec -it nginx2 /bin/bash
root@8d47f51c8138:/# ping 172.19.0.2
PING 172.19.0.2 (172.19.0.2) 56(84) bytes of data.
64 bytes from 172.19.0.2: icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from 172.19.0.2: icmp_seq=2 ttl=64 time=0.064 ms
64 bytes from 172.19.0.2: icmp_seq=3 ttl=64 time=0.049 ms

结果可知:同一网络中的容器、网关之间都是可以通信的。

容器跨网桥通信#

这里有个问题是 my-net 与默认的 bridge 网络是否可以通信,正常来说两个网络属于不同的网桥应该不能通信,测试一下:

[root@centos-01 ~]# docker ps -a
1CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
8d47f51c8138   nginx     "/docker-entrypoint.…"   32 minutes ago   Up 32 minutes   80/tcp    nginx2
96a448f4c29f   nginx     "/docker-entrypoint.…"   32 minutes ago   Up 32 minutes   80/tcp    nginx1

[root@centos-01 ~]# docker run -d --name nginx3 nginx
220cc3010771f7a3b0c778d4d5668baa860e969e8d5a6cde830290b31dad89fd

[root@centos-01 ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS     NAMES
220cc3010771   nginx     "/docker-entrypoint.…"   3 seconds ago    Up 2 seconds    80/tcp    nginx3
8d47f51c8138   nginx     "/docker-entrypoint.…"   32 minutes ago   Up 32 minutes   80/tcp    nginx2
96a448f4c29f   nginx     "/docker-entrypoint.…"   32 minutes ago   Up 32 minutes   80/tcp    nginx1

[root@centos-01 ~]# brctl show
bridge name	bridge id		STP enabled	interfaces
br-c62a2e3d6ad5		8000.0242ae4b8680	no		veth0a1e1ba
							                            veth1069d0c
docker0		8000.0242cbf135ea	no		veth3147348

[root@centos-01 ~]# docker inspect nginx3

新建容器 nginx3 但不指定网桥所以会使用默认的 bridge 网络,结果显示 br-c62a2e3d6ad5 上绑定了两个虚拟网卡 veth0a1e1baveth1069d0c 对应 nginx1nginx2 , docker0 上绑定了 veth3147348 ,对应 ngnix3 ,分配的IP地址是 172.17.0.2 也可以看出来不在一个网段。

[root@centos-01 ~]# docker exec -it nginx1 /bin/bash
root@96a448f4c29f:/# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.

结果可知:不同网段的确是 ping 不通的。

思考:“不同的网络如果加上路由是否可以通信”,如果host上对每个网络都有一条路由,同时操作系统上打开了 ip forwarding ,host就成了一个路由器,挂接在不同网桥上的网络就能够相互通信。试试看。

查看路由表:

[root@centos-01 ~]# ip r
default via 192.168.126.67 dev ens33 proto static metric 100 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
172.19.0.0/16 dev br-c62a2e3d6ad5 proto kernel scope link src 172.19.0.1 
192.168.126.0/24 dev ens33 proto kernel scope link src 192.168.126.143 metric 100 

172.17.0.0/16 和 172..16.0/24 两个网络的路由都定义好了。再看看 ip forwarding :

[root@centos-01 ~]# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1

这里看到路由转发也是开启的,条件都满足的情况下为什么还不行呢,再看看 iptables 

这里看到原因是因为: iptables DROP掉了网桥 dockero 与 br-c62a2e3d6ad5 之间双向的流量。从规则命名 DOCKER-ISOLATION 可知 docker 在设计上就是要隔离不同的 netwrok, 这里可以通过使用 docker network connect 命令添加一块网卡来实现不同网桥容器通信:

[root@centos-01 ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED        STATUS        PORTS     NAMES
220cc3010771   nginx     "/docker-entrypoint.…"   34 hours ago   Up 34 hours   80/tcp    nginx3
8d47f51c8138   nginx     "/docker-entrypoint.…"   34 hours ago   Up 34 hours   80/tcp    nginx2
96a448f4c29f   nginx     "/docker-entrypoint.…"   34 hours ago   Up 34 hours   80/tcp    nginx1

[root@centos-01 ~]# docker network connect my-net 220cc3010771 #将nginx3连接到my-net中
[root@centos-01 ~]# docker exec -it nginx1 /bin/bash
root@96a448f4c29f:/# ping 172.19.0.4
PING 172.19.0.4 (172.19.0.4) 56(84) bytes of data.
64 bytes from 172.19.0.4: icmp_seq=1 ttl=64 time=0.130 ms
64 bytes from 172.19.0.4: icmp_seq=2 ttl=64 time=0.046 ms
64 bytes from 172.19.0.4: icmp_seq=3 ttl=64 time=0.060 ms

此时发现 nginx1 已经可以访问到 nginx3 了。

端口映射#

容器访问外网是通过 iptables 的 SNAT 实现的,docker容器在启动的时候,如果不指定端口映射参数,容器外部无法通过网络访问容器内的网络应用和服务。需要设置端口映射,也可以使用 Dockerfile 文件中的 EXPOSE 指令来配置。

端口映射使用 -p 、 -P 来实现

  • -p 容器内部端口绑定到指定的主机端口
  • -P 容器内部端口随机映射到主机的高端口

格式指定ip:指定宿主机端口:指定容器端口/IP:HOSTPORT:CONTAINERPORT

这适用于将容器端口映射到指定地址的指定端口:

[root@wangpengliang ~]# docker run -it -d -p 127.0.0.1:5000:5000 --name redis redis #将容器的5000端口映射到指定地址127.0.0.1的5000端口上
c2179a906b05e210c42eb6561cf76285f3391e703d5db249a154e06d4d025f28
[root@wangpengliang ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                NAMES
4927cb5dfe67   redis     "docker-entrypoint.s…"   49 seconds ago   Up 48 seconds   127.0.0.1:5000->5000/tcp, 6379/tcp   redis

格式指定ip、宿主机随机端口、指定容器端口 /IP::CONTAINERPORT

这适用于将容器端口映射到指定地址的任意端口:

[root@wangpengliang ~]# docker run -it -d -p 127.0.0.1::5000 --name redis redis   #将容器的5000端口映射到指定地址127.0.0.1的任意端口上
c19e6b8f249db67b2f50455d35159f718a1d68e6efd02c106f0148bae14ba496
[root@wangpengliang ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                 NAMES
c19e6b8f249d   redis     "docker-entrypoint.s…"   2 seconds ago   Up 2 seconds   6379/tcp, 127.0.0.1:49153->5000/tcp   redis

格式不指定ip、指定宿主机端口、指定容器端口/HOSTPORT:CONTAINERPORT

这适用于将容器指定端口映射到宿主机的指定端口:

[root@wangpengliang ~]# docker run -it -d -p 80:8000 --name redis redis          #将容器的8000端口映射到宿主机的80端口上
3ad63ffd90665413e644cd16f2eb7d404fcb14bb509eee05f67e0db8f1c067a4
[root@wangpengliang ~]# docker ps -a
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                             NAMES
3ad63ffd9066   redis     "docker-entrypoint.s…"   5 seconds ago   Up 4 seconds   6379/tcp, 0.0.0.0:80->8000/tcp, :::80->8000/tcp   redis

绑定 UDP 端口#

默认情况下 -p-P 绑定的都是 tcp 协议端口,如果要绑定 udp 协议端口,只能使用 -p 参数,且在最后添加 /udp 字符串。

[root@wangpengliang ~]# docker run -d -p 127.0.0.1:5553:5000/udp jcdemo/flaskapp
6aa30aa070a6e77f0d3f8653df69c654edf6e8bb68cea475aefbc68f6f7f9572

绑定多个端口#

多次使用 -p 参数可以映射多个端口。

[root@wangpengliang ~]# docker run -d -p 5552:5000  -p 5551:5001 jcdemo/flaskapp
fa116ae4f5c19d82d9d4f40560c3219c85540a21d88f7fa999b60382ab57524a

查看映射端口#

docker port container_ID #容器ID
#结果输出
80/tcp -> 0.0.0.0:800

跨主机网络解决方案#

TODO