devin0910

全干工程师

Docker Network入门用法

在Docker中,默认情况下容器与容器、容器与外部宿主机的网络是隔离开来的。当你安装Docker的时候,docker会创建一个桥接器docker0,通过它才让容器与容器之间、与宿主机之间通信。

 

ifconfig docker0

docker0   Link encap:Ethernet  HWaddr 02:42:db:c4:96:d3  
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          inet6 addr: fe80::42:dbff:fec4:96d3/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:648 (648.0 B)

Docker安装的时候默认会创建三个不同的网络。你可以通过docker network ls命令查看这些网络

docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
f65bddc829ad        bridge              bridge              local
887f3f66f5dc        host                host                local
7d7c2584672c        none                null                local

None Network

网络模式为none的,即不为Docker Container创建任何的网络环境。一旦Docker Container采用了none网络模式,那么容器内部就只能使用loopback网络设备,不会再有其他的网络资源。

 docker run -it --rm --network=none ubuntu:14.04  ifconfig

 lo       Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

Host Network

如果你在创建容器的时候使用--network=host选项,那么容器会使用宿主机的网络,容器与宿主机的网络并没有隔离。使用这种网络类型的好处就是网络性能很好,基本上跟宿主机的网络一样,它很大的弊端就是不安全。你可以在容器中更改宿主机的网络,如果你的程序是用root用户运行的,有可能会通过Docker容器来控制宿主机的网络。当我们在容器中执行类似ifconfig命令查看网络环境是,看到的都是宿主机上的信息。

docker run -it --network=host nginx

curl -i localhost

这里我们指定--network=host启动一个运行nginx的容器,然后在宿主机的命令行中请求localhost,在控制台中会输出nginx访问日志,其实这里并没有像以前那样通过-p-P来暴露容器80端口。

Bridge Network

桥接网络是默认的网络类型,我们可以使用下面的命令来查看默认的网络配置信息:

docker inspect bridge

[
    {
        "Name": "bridge",
        "Id": "f65bddc829adfd9b0df91a71ef31acc52b3f450f6c13cac0a5460ded2dddf55f",
        "Created": "2017-08-07T07:04:56.06541016Z",
        "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,
        "Containers": {
            "c3ff1cd9bbe6f65a861be0134b50d4239cf4f271da5f546775de3c25c84cb311": {
                "Name": "thirsty_banach",
                "EndpointID": "31e4f25acdbbdc186bb7cb7be57a34e980315f101746f085ab72c74cb65d56e1",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/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": {}
    }
]

这儿桥接的网络名为docker0。当我们启动一个容器的时候,每个容器会有它自己的虚拟网络接口连接到docker0,并获得一个IP地址。上面的 Containers 项里有所有连接到这个网络的容器,可以看到这个容器的ip地址。

端口转发

如果Docker容器不是使用none网络模式,默认是不需要再做任何配置就可以访问网络。但是,外部网络在默认情况下是不能访问容器的服务的。

docker run -d -P --expose 443 nginx

-P选项Docker会把Dockerfile中的通过EXPOSE指令或--expose选项暴露的端口随机映射到临时端口。默认的nginx镜像的Dockerfile中会 EXPOSE 80

查看端口映射关系

docker port 7101c41b0ca2

80/tcp -> 0.0.0.0:32773
443/tcp -> 0.0.0.0:32772

许多Linux内核预设的临时端口范围为32768 ~ 61000,你可以通过cat /proc/sys/net/ipv4/ip_local_port_range查看临时端口范围。

Docker会在宿主机的iptables里添加NAT规则,使得宿主机外的网络能通过宿主机ip:port使用容器提供的服务。

iptables -t nat -L

DNAT       tcp  --  anywhere         anywhere       tcp dpt:32772 to:172.17.0.2:443
DNAT       tcp  --  anywhere         anywhere       tcp dpt:32773 to:172.17.0.2:80

你也可以通过-p选项指定端口映射关系:

docker run -d -p 8080:80 nginx

这儿指定了宿主机的8080端口映射到容器的80端口。

docker ps

CONTAINER ID   IMAGE   COMMAND                  CREATED        STATUS        PORTS                 NAMES
deca43a61f37   nginx   "nginx -g 'daemon ..."   3 seconds ago  Up 1 second   0.0.0.0:8080->80/tcp  happy_austin

PORTS 这一列列出了端口映射关系。

连接两个不同的容器示例

docker run --rm -it -v /home/vagrant/test.php:/data/test.php --link redis-server:redis  php /bin/bash

--link会在hosts文件中添加redis服务对应的ip地址

cat /etc/hosts

172.17.0.2	redis 41b5db2d1282 redis-server

test.php测试redis服务的连接,简单地set键名为hello的值为world,然后从redis里重新获取值并打印:

$redis = new Redis;

$redis->connect('redis');

$redis->set('hello', 'world');

echo $redis->get('hello');

这里connect函数里的参数 redis 实际上是上面提到的 172.17.0.2

由于默认php cli容器没有安装redis扩展,需要自己手动安装,把下面的命令输入到控制台中:

pecl install redis           // 安装redis扩展

docker-php-ext-enable redis  // 开启redis支持

运行php /data/test.php,打印出 world

现在我们能简单的利用Docker来开发应用,了解如何挂载文件,如何开放容器里的服务,如何与容器进行交互等。最后的例子中我们在官方的php镜像上,安装了redis扩展,但是这个过程比较繁琐。如何自动构建一个满足应用需求的容器? Docker file 作为记录构建容器步骤的描述语言能很好的记录整个容器的构建过程。

留言