1

1、扩展知识:Linux Namespace

        Namespace(命名空间、名字空间)是 Linux 内核用来隔离内核资源的方式。通过 Namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与他们自己相关的资源,这两拨进程根本就感觉不到对方的存在。具体实现方式是把一个或者多个进程的相关资源指定在同一个 Namespace 中。

        Linux Namespace 是对全局系统资源的一种封装隔离。使得处于不同 Namespace 的进程拥有独立的全局系统资源,改变一个 Namespace 中的系统资源只会影响当前 Namespace 里的进程,对其他 Namespace 中的进程没有影。

        可能绝大多数的使用者和我一样,是在使用 docker 后才开始了解 linux 的 namespace 技术的。实际上,Linux 内核实现 namespace 的一个主要目的就是实现轻量级虚拟化(容器)服务。在同一个 namespace 下的进程可以感知彼此的变化,而对外界的进程一无所知。这样就可以让容器中的进程产生错觉,认为自己置身于一个独立的系统中,从而达到隔离的目的。也就是说 linux 内核提供的 namespace 技术为 docker 等容器技术的出现和发展提供了基础条件。

        目前,Linux内核里面实现了7种不同类型的namespace。

名称        宏定义             隔离内容
Cgroup      CLONE_NEWCGROUP   Cgroup root directory (since Linux 4.6)
IPC         CLONE_NEWIPC      System V IPC, POSIX message queues (since Linux 2.6.19)
Network     CLONE_NEWNET      Network devices, stacks, ports, etc. (since Linux 2.6.24)
Mount       CLONE_NEWNS       Mount points (since Linux 2.4.19)
PID         CLONE_NEWPID      Process IDs (since Linux 2.6.24)
User        CLONE_NEWUSER     User and group IDs (started in Linux 2.6.23 and completed in Linux 3.8)
UTS         CLONE_NEWUTS      Hostname and NIS domain name (since Linux 2.6.19)

下面简要介绍一个以上不同类型的命名空间的作用:

  • IPC:用于隔离进程间通讯所需的资源( System V IPC, POSIX message queues),PID命名空间和IPC命名空间可以组合起来用,同一个IPC名字空间内的进程可以彼此看见,允许进行交互,不同空间进程无法交互
  • Network:Network Namespace为进程提供了一个完全独立的网络协议栈的视图。包括网络设备接口,IPv4和IPv6协议栈,IP路由表,防火墙规则,sockets等等。一个Network Namespace提供了一份独立的网络环境,就跟一个独立的系统一样。
  • Mount:每个进程都存在于一个mount Namespace里面,mount Namespace为进程提供了一个文件层次视图。如果不设定这个flag,子进程和父进程将共享一个mount Namespace,其后子进程调用mount或umount将会影响到所有该Namespace内的进程。如果子进程在一个独立的mount Namespace里面,就可以调用mount或umount建立一份新的文件层次视图。
  • PID::linux通过命名空间管理进程号,同一个进程,在不同的命名空间进程号不同!进程命名空间是一个父子结构,子空间对于父空间可见。
  • User:用于隔离用户
  • UTS:用于隔离主机名

2、Docker 网络概述

        Docker 容器之间的隔离是通过 Namespace 实现的,Docker 容器之间的网络隔离便是通过 Network Namespace 来实现的,每一份 Network Namespace 都提供了一份独立的 网络系统,包括网卡、路由、防火墙规则等与其他 Network Namespace 相互隔离。

        Docker 启动后默认分配一个独立的 Network Namespace ,类型为 bridge ,可使用如下命令查看:

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
fb7b7a0e2541        bridge              bridge              local

        这个 bridge 的名称默认为 docker0,可使用 ifconfig 或者 ip address 来查看详细信息

$ ifconfig  docker0
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.18.0.1  netmask 255.255.0.0  broadcast 172.18.255.255
        inet6 fe80::42:53ff:fe55:43d1  prefixlen 64  scopeid 0x20<link>
        ether 02:42:53:55:43:d1  txqueuelen 0  (Ethernet)
        RX packets 6345  bytes 393570 (384.3 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6868  bytes 41194953 (39.2 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

        Docker 还有两种网络类型,分别为 Host 和 None,如果启动 Docker 时,指定了网络类型为 Host 模式,则不会分配一个独立的 Network Namespace ,而是和主机共享一个共同的 Network Namespace;如果指定为 None 模式,则Docker 不会与外界进行通信。

        使用 bridge 模式的网络类型时,通过端口映射的方法,来实现网络通信,你可以使用 docker network inspect bridge 来查看这个 bridge 的详细信息。

        这里需要介绍两个 docker run 时的参数,-p-P

-p,--publish list,Publish容器的一个指定的端口到主机端口
-P,--publish-all,Publish容器的所有公开的端口到主机的随机端口

        你可以分别使用这两个参数进行测试,当你使用 docker run -P 时,你可以使用 docker ps 或者其他命令来查看随机映射的端口,下面以 -p 进行演示:

docker run --name test1_nginx  -d -p 8080:80 nginx:latest
        强调一下,-p 前面的端口是主机的端口,后端的端口对应容器的端口,不要搞错了,这个我经常搞错,🤦‍♂️

3、自定义 Docker 网桥

        前文提到,启动 Docker 后,默认创建一个名为 docker0 的网桥,但是官方不建议使用默认的这个 docker0 网桥,而是建议使用自定义(自创建)网络。

        创建自定义网络使用命令 docker create ,具体参数如下

[root@small ~]# docker network create --help
Usage:    docker network create [OPTIONS] NETWORK
Create a network
Options:
      --attachable           Enable manual container attachment
      --aux-address map      Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
      --config-from string   The network from which copying the configuration
      --config-only          Create a configuration only network
  -d, --driver string        Driver to manage the Network (default "bridge")
      --gateway strings      IPv4 or IPv6 Gateway for the master subnet
      --ingress              Create swarm routing-mesh network
      --internal             Restrict external access to the network
      --ip-range strings     Allocate container ip from a sub-range
      --ipam-driver string   IP Address Management Driver (default "default")
      --ipam-opt map         Set IPAM driver specific options (default map[])
      --ipv6                 Enable IPv6 networking
      --label list           Set metadata on a network
  -o, --opt map              Set driver specific options (default map[])
      --scope string         Control the network's scope
      --subnet strings       Subnet in CIDR format that represents a network segment

3.1、创建自定义网络

# 创建网络
$ docker network create \
  --driver=bridge \
  --subnet=172.28.0.0/16 \
  --ip-range=172.28.5.0/24 \
  --gateway=172.28.5.254 \
  my-net

# 解释
  --driver=            # 网络类型,默认为bridge
  --subnet=            # 子网
  --ip-range=          # ip地址范围
  --gateway=           # 网关
  my-net               # 网络名称

查看 Docker 网络

# 查看所有网络
$ docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    fb7b7a0e2541        bridge              bridge              local
    93d7965ac414        host                host                local    
    3c6f04a1e42c        my-net              bridge              local
    69be969894e3        none                null                local
    
# 查看创建的网络的情况
$ docker network inspect my-net
...
 {
 "Name": "my-net",
 "Config": [
 {
 "Subnet": "172.28.0.0/16",
 "IPRange": "172.28.5.0/24",
 "Gateway": "172.28.5.254"
 }
...

# 查看默认网桥的详细信息
$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "fb7b7a0e25417a2946bd879bd05992be067eb15e51bf8658808b4225c97647da",
        "Created": "2019-12-21T10:06:43.475651149+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.18.0.0/16"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "4e28d1aa47d7fcf66d3f02af108f57271edba2dae7780634138d6cc5b56e8245": {
                "Name": "test1_nginx",
                "EndpointID": "c3122141281a2a05f392b132e4c9df0e9afd223a1bdac4e193f352ce53055c1b",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            },
            "596d8d06c84fdc87ffa471b38925406bc1a1dd563438281a4351fb8554a4d075": {
                "Name": "test2_nginx",
                "EndpointID": "4b9ad904506c942d10df946411445022b432df45975ef69f4aa4225c0472e777",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
            },
            "d2429364fbe0c5524e5852b561c29a547b7bff2f01eb37ae45caee42a5c89a69": {
                "Name": "test3_nginx",
                "EndpointID": "ac281704c974f979c1d07b843ede61e911d558f33dc0be639a089f0fad72c191",
                "MacAddress": "02:42:ac:12:00:04",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
            }
        },
        "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 容器,用来跑 Nginx web 服务,接下来我们看一下这三个容器具体的网络情况,因为之前没有刚才创建的my-net网络,所有所有容器是跑在名称为 bridge 的网络下的

# 查看已经启动的容器
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
d2429364fbe0        nginx               "nginx -g 'daemon of…"   5 minutes ago       Up 5 minutes        0.0.0.0:8082->80/tcp   test3_nginx
596d8d06c84f        nginx               "nginx -g 'daemon of…"   6 minutes ago       Up 6 minutes        0.0.0.0:8081->80/tcp   test2_nginx
4e28d1aa47d7        nginx:latest        "nginx -g 'daemon of…"   About an hour ago   Up About an hour    0.0.0.0:8080->80/tcp   test1_nginx

# 查看某个容器的情况,你可以在输出的信息中看到如下网络相关信息
$ docker inspect test1_nginx
...
 "Gateway": "172.18.0.1",
 "GlobalIPv6Address": "",
 "GlobalIPv6PrefixLen": 0,
 "IPAddress": "172.18.0.2",
...

3.2、将正在运行的容器连接到网络 my-net

# 将正在运行的容器连接到网络 my-net
$ docker network connect my-net test1_nginx

# 查看切换后的容器网络状况,可见,这种方式不会影响默认的bredeg网络
$ docker inspect test1_nginx
...
  "Networks": {
                "bridge": {
                    "Gateway": "172.18.0.1",
                    "IPAddress": "172.18.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:12:00:02",
                    "DriverOpts": null
                },
                "my-net": {
                    "Gateway": "172.28.5.254",
                    "IPAddress": "172.28.5.0",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:1c:05:00",
                    "DriverOpts": {}
                }
...
注意:以上方式并没有因为给容器配置了新的 my-net 网络而影响到旧的 bridge 网络,你可以进入容器查看具体情况,此时这个 test1_nginx 容器是有两块网卡的。

3.3、启动容器时将其连接到某个网络

启动新的容器时,指定其运行的网络。

# --ip 指定一个具体的IP地址
$ docker run --name=test4_nginx -itd --network=my-net --ip=172.28.5.10 nginx

# 查看容器情况,你可以在输出信息中找到网络相关内容
$ docker inspect test4_nginx
            "Networks": {
                "my-net": {
                    "Gateway": "172.28.5.254",
                    "IPAddress": "172.28.5.10",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:1c:05:0a",
                    "DriverOpts": null
                }

你也可以进入容器安装ping或者curl等命令检查各个容器之间的网络连接情况,这时,test1_nginxtest2_nginxtest3_nginx 是相通的,test4_nginx 仅与 test1_nginx 相通。

3.4、断开容器的某个网络

断开容器名为 bridge 的网络,容器必须是运行中的

# 断开容器名为 test1_nginx 的 bridge 网络
$ docker network disconnect bridge test1_nginx

# 查看断开后的容器的网络情况
docker inspect test1_nginx

参考资料1:

https://www.cnblogs.com/sparkdev/p/9365405.html

https://www.jianshu.com/p/2a14fe583cdf

https://www.cnblogs.com/bakari/p/10613710.html

参考资料2:

https://www.imooc.com/video/14623

参考链接3:

https://docs.docker.com/engine/reference/commandline/network_create/#specify-advanced-options


Liu_wt
35 声望3 粉丝

业余选手,多多指教!