docker 容器网络配置

一、容器网络模型

容器网络实质上也是由 docker 为应用程序所创造的虚拟环境的一部分,它能让应用从宿主机操作系统的网络环境中独立出来,形成容器自有的网络设备、IP 协议栈、端口套接字、IP 路由表、防火墙等等与网络相关的模块。

在 docker 网络中,有三个比较核心的概念:沙盒(Sandbox)、网络(Network)、端点(Endpoint)。

docker 容器网络配置_第1张图片

  1. 沙盒提供了容器的虚拟网络栈,也就是端口套接字、IP 路由表、防火墙等的内容。隔离了容器网络与宿主机网络,形成了完全独立的容器网络环境。
  2. 网络可以理解为 docker 内部的虚拟子网,网络内的参与者相互可见并能够进行通讯。docker 的这种虚拟网络也是于宿主机网络存在隔离关系的,其目的主要是形成容器间的安全通讯环境。
  3. 端点是位于容器或网络隔离墙之上的洞,其主要目的是形成一个可以控制的突破封闭的网络环境的出入口。当容器的端点与网络的端点形成配对后,就如同在这两者之间搭建了桥梁,就能够进行数据传输了。

这三者形成了容器网络模型(Container Network Model)。

二、docker 网络模型

容器网络模型是给容器引擎提供的一套标准的网络对接范式,而在 docker 中,实现这套范式的是 docker 封装的 libnetwork 模块。

而 docker 对于网络的具体实现,在发展过程中也逐渐抽象形成了统一的抽象定义。通过这些抽象定义,便可以对 docker 网络的实现方式进行不同的变化。

目前 docker 官方为我们提供了五种 Docker 网络驱动,分别是:bridge driver、host driver、overlay driver、macvlan driver、none driver。

  1. bridge : docker 容器的默认网络驱动,简而言之其就是通过网络桥接来实现网络通讯 (网桥网络的实现可以基于硬件,也可以基于软件)。
  2. overlay : 借助 docker 集群模块 docker swarm 来搭建的跨 docker daemon 网络,可以通过它搭建跨物理主机的虚拟网络,进而让不同物理机中运行的容器感知不到多个物理机的存在。
  3. host : 容器与主机享受相同的 network namespace,在这种情况下,访问主机端口就能访问容器。
  4. macvlan :docker 主机网卡接口逻辑上分为多个子接口,每个子接口标识一个 vlan。容器接口直接连接 docker 主机网卡接口,通过路由策略转发到另一台 docker 主机。
  5. none : 禁用所有网络。通常与自定义网络驱动程序一起使用。此模式下创建容器是不会为容器配置任何网络参数的,如:容器网卡、IP、通信路由等,全部需要自己去配置。

三、容器互联

由于 Docker 提倡容器与应用共生的轻量级容器理念,所以容器中通常只包含一种应用程序;要让一个容器连接到另外一个容器,可以在使用 docker createdocker run 命令创建容器时通过 --link 选项进行配置。

例如,这里创建一个 MySQL 容器,将 Web 应用的容器连接到这个 MySQL 容器上,打通两个容器间的网络,实现它们之间的网络互通:

$ sudo docker run -d --name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=yes mysql
$ sudo docker run -d --name webapp --link mysql webapp:latest

此时,mysql 与 webapp 之间的网络已经打开了,webapp 使用 jdbc 连接 mysql 时,只需要将容器的网络命名填入到连接地址中,就可以访问需要连接的容器了,那么 url 可以这么写 :

String url = "jdbc:mysql://mysql:3306/webapp";

连接地址中的 mysql 就就像是域名,docker 会将其指向 MySQL 容器的 ip 地址。这样有一个好处就是从开发环境切换到测试环境的时候不需要频繁切换 ip 地址了。

四、开放端口

需要注意的是,虽然容器间的网络打通了,但并不意味着可以任意访问被连接容器中的任何服务。docker 为容器网络增加了一套安全机制,只有容器自身允许的端口,才能被其他容器所访问。

docker 开放端口可以通过镜像定义,也可以在创建容器时通过 --expose 参数定义,可以指定多个端口:

$ sudo docker run -d --name redis --expose 6379 --expose 26379 redis:5.0.7

docker ps 的结果中的 PORTS 就是容器暴露给其他容器访问的端口:

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
93aa5326931f        redis:5.0.7         "docker-entrypoint.s…"   31 seconds ago      Up 30 seconds       6379/tcp, 26379/tcp   redis

可以看到,前面在名称为 redis 的容器开放的端口 6379 与 26379 是成功开放了的,在连接到 redis 容器后,只能对这两个端口进行访问。

容器开放端口类似打开容器的防火墙,具体能不能通过这个端口访问容器中的服务,还需要容器中的应用监听并处理来自这个端口的请求。

五、通过别名连接

纯粹的通过容器名来打开容器间的网络通道缺乏一定的灵活性,在 docker 里还支持连接时使用别名来摆脱容器名的限制。

$ sudo docker run -d --name webapp --link mysql:database webapp:latest

在这里使用 --link : 的形式,连接到 MySQL 容器,并设置它的别名为 database。需要在 Web 应用中使用 MySQL 连接时,就可以使用 database 来代替连接地址了。

$ String url = "jdbc:mysql://database:3306/webapp";

六、管理网络

容器能够互相连接的前提是两者同处于一个网络中(这里的网络是指容器网络模型中的网络)。

网络可以理解为 docker 所虚拟的子网,而容器网络沙盒可以看做是虚拟的主机,只有当多个主机在同一子网里时,才能互相看到并进行网络数据交换。

当启动 docker 服务时,它会创建一个默认的 bridge 网络,而创建的容器在不专门指定网络的情况下都会连接到这个网络上。所以前面之所以能够把 webapp 容器连接到 mysql 容器上,其原因是两者都处于 bridge 这个网络上。

通过 docker inspect 命令查看容器,可以在 Network 部分看到容器网络相关的信息 :

$ sudo docker inspect redis
[
    {
    ......
        "NetworkSettings": {
            ......
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "57cba05dde080f4d84a3e100cce1fc568fb3cf7d04ae0706f6cd7d945bbd2f50",
                    "EndpointID": "900184a27a54f27517b85c5c39ef2114ccd3b3131c67d1ebcfe772f779f7ef09",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]

这里我们能够看到 mysql 容器在 bridge 网络中所分配的 ip 地址,其自身的端点、mac 地址,bridge 网络的网关地址等信息。

在没有明确指定容器网络时,容器都会连接到这个网络中。

七、创建网络

在 docker 里,可以创建网络,形成自己定义虚拟子网的目的。

在 docker 命令行里与网络相关的命令都以 docker network 开头,其中创建网络的命令是 docker network create

$ sudo docker network create -d bridge my-bridge-name

通过 -d 可以为新网络指定驱动的类型,其值可以是 bridge、host、overlay、maclan、none,也可以是其他网络驱动插件所定义的类型。这里使用的是 bridge driver(当不指定网络驱动时,docker 也会默认采用 bridge driver 作为网络驱动)。

通过 docker network ls 或者 docker network list 可以查看 docker 中已经存在的网络 :

$ sudo docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
57cba05dde08        bridge              bridge              local
3a00cd261b29        host                host                local
3974f46bcd21        my-bridge-name      bridge              local
aefd8de0db63        none                null                local

之后在创建容器时,可以通过 --network 来指定容器所加入的网络,一旦这个参数被指定,容器便不会默认加入到 bridge 网络中了(但是仍然可以通过 --network bridge 让其加入)。

 sudo docker run -d --name nginx --network my-bridge-name nginx:1.17.6

通过 docker inspect 查看容器的网络:

root@dreamboy:~# sudo docker inspect nginx
[
    {
        "Id": "c708e06fdb3268cf807d36dec72ebe7d9ac1e02182f3ccaebd9472209367d99f",
        "Created": "2019-12-05T07:59:52.316873323Z",
        "Path": "nginx",
        "Args": [
            "-g",
            "daemon off;"
        ],
        "State": {
            ......
            "Networks": {
                "my-bridge-name": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "c708e06fdb32"
                    ],
                    "NetworkID": "3974f46bcd2114b369d1f2e14e087e4fb850f829ca96bb20989c209506ac23ac",
                    "EndpointID": "119dce14105a7291613f2506efcc571b514f77c8957e8b6ddfd855d48e5768d1",
                    "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-bridge-name 这个网络了。

八、端口映射

前面都是容器直接通过 docker 网络进行的互相访问,在实际使用中,还有一个非常常见的需求,就是在容器外通过网络访问容器中的应用。最简单的一个例子,外部客户端访问容器内的 web 应用。

在 docker 中,有端口映射的功能,通过 docker 端口映射功能可以把容器的端口映射到宿主操作系统的端口上,当从外部访问宿主操作系统的端口时,数据请求就会自动发送给与之关联的容器端口。

在创建容器时通过 -p 或者 --publish 选项实现端口映射

$ sudo docker run -d --name nginx -p 8081:80 -p 443:443 nginx:1.17.6

使用端口映射选项的格式是 -p ::,其中:ip 是宿主操作系统的监听 ip,可以用来控制监听的网卡,默认为 0.0.0.0,也就是监听所有网卡。host-port 和 container-port 分别表示映射到宿主操作系统的端口和容器的端口,这两者是可以不一样的,可以将容器的 80 端口映射到宿主操作系统的 8081 端口,传入 -p 8081:80 即可。

执行命令后,就可以在容器列表里看到端口映射的配置:

$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS                                        NAMES
adac71332d9d        nginx:1.17.6        "nginx -g 'daemon of…"   About a minute ago   Up About a minute   0.0.0.0:443->443/tcp, 0.0.0.0:8081->80/tcp   nginx

打印的结果里用符号 -> 标记了端口的映射关系。

你可能感兴趣的:(docker,docker)