网络

default

Docker 网络配置

Docker 1.9 中正式引入了所谓的 "Container Network Model",也就适合所谓的 CNM 的概念。CNM 即用于创建小型微分割的网络来使得不同组的容器之间进行相互连接。

img

Basic Usage | 基本使用

端口映射

容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P-p 参数来指定端口映射。

当使用 -P 标记时,Docker 会随机映射一个 49000~49900 的端口到内部容器开放的网络端口。

使用 docker ps 可以看到,本地主机的 49155 被映射到了容器的 5000 端口。此时访问本机的 49155 端口即可访问容器内 web 应用提供的界面。

$ sudo docker run -d -P training/webapp python app.py
$ sudo docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESbc533791f3f5 training/webapp:latest python app.py 5 seconds ago Up 2 seconds 0.0.0.0:49155->5000/tcp nostalgic_morse

同样的,可以通过 docker logs 命令来查看应用的信息。

$ sudo docker logs -f nostalgic_morse
* Running on http://0.0.0.0:5000/10.0.2.2 - - [23/May/2014 20:16:31] "GET / HTTP/1.1" 200 -10.0.2.2 - - [23/May/2014 20:16:31] "GET /favicon.ico HTTP/1.1" 404 -

-p (小写的)则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort

  • 映射所有接口地址

使用 hostPort:containerPort 格式本地的 5000 端口映射到容器的 5000 端口,可以执行

$ sudo docker run -d -p 5000:5000 training/webapp python app.py

此时默认会绑定本地所有接口上的所有地址。

  • 映射到指定地址的指定端口

可以使用 ip:hostPort:containerPort 格式指定映射使用一个特定地址,比如 localhost 地址 127.0.0.1

$ sudo docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
  • 映射到指定地址的任意端口

使用 ip::containerPort 绑定 localhost 的任意端口到容器的 5000 端口,本地主机会自动分配一个端口。

$ sudo docker run -d -p 127.0.0.1::5000 training/webapp python app.py

还可以使用 udp 标记来指定 udp 端口

$ sudo docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py
  • 查看映射端口配置

使用 docker port 来查看当前映射的端口配置,也可以查看到绑定的地址

$ docker port nostalgic_morse 5000127.0.0.1:49155.

注意:

  • 容器有自己的内部网络和 ip 地址(使用 docker inspect 可以获取所有的变量,Docker 还可以有一个可变的网络配置。)

  • -p 标记可以多次使用来绑定多个端口

例如

$ sudo docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py

注意,网络映射的操作只会在 run 命令中起作用,如果已经运行了一个容器,需要重新设置其网络映射情况,请使用 commit 将容器转化为镜像之后再创建新的容器。

容器的连接(linking )系统是除了端口映射外,另一种跟容器中应用交互的方式。

该系统会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。

自定义容器命名

连接系统依据容器的名称来执行。因此,首先需要自定义一个好记的容器命名。

虽然当创建容器的时候,系统默认会分配一个名字。自定义命名容器有 2 个好处:

  • 自定义的命名,比较好记,比如一个 web 应用容器我们可以给它起名叫 web

  • 当要连接其他容器时候,可以作为一个有用的参考点,比如连接 web 容器到 db 容器

使用 --name 标记可以为容器自定义命名。

$ sudo docker run -d -P --name web training/webapp python app.py

使用 docker ps 来验证设定的命名。

$ sudo docker ps -lCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESaed84ee21bde training/webapp:latest python app.py 12 hours ago Up 2 seconds 0.0.0.0:49154->5000/tcp web

也可以使用 docker inspect 来查看容器的名字

$ sudo docker inspect -f "{{ .Name }}" aed84ee21bde/web

注意:容器的名称是唯一的。如果已经命名了一个叫 web 的容器,当你要再次使用 web 这个名称的时候,需要先用docker rm 来删除之前创建的同名容器。

在执行 docker run 的时候如果添加 --rm 标记,则容器在终止后会立刻删除。注意,--rm-d 参数不能同时使用。

容器互联

使用 --link 参数可以让容器之间安全的进行交互。

下面先创建一个新的数据库容器。

$ sudo docker run -d --name db training/postgres

删除之前创建的 web 容器

$ docker rm -f web

然后创建一个新的 web 容器,并将它连接到 db 容器

$ sudo docker run -d -P --name web --link db:db training/webapp python app.py

此时,db 容器和 web 容器建立互联关系。

--link 参数的格式为 --link name:alias,其中 name 是要链接的容器的名称,alias是这个连接的别名。

使用 docker ps 来查看容器的连接

$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES349169744e49 training/postgres:latest su postgres -c '/usr About a minute ago Up About a minute 5432/tcp db, web/dbaed84ee21bde training/webapp:latest python app.py 16 hours ago Up 2 minutes 0.0.0.0:49154->5000/tcp web

可以看到自定义命名的容器,db 和 web,db 容器的 names 列有 db 也有 web/db。这表示 web 容器链接到 db 容器,web 容器将被允许访问 db 容器的信息。

Docker 在两个互联的容器之间创建了一个安全隧道,而且不用映射它们的端口到宿主主机上。在启动 db 容器的时候并没有使用 -p-P 标记,从而避免了暴露数据库端口到外部网络上。

Docker 通过 2 种方式为容器公开连接信息:

  • 环境变量

  • 更新 /etc/hosts 文件

使用 env 命令来查看 web 容器的环境变量

$ sudo docker run --rm --name web2 --link db:db training/webapp env. . .DB_NAME=/web2/dbDB_PORT=tcp://172.17.0.5:5432DB_PORT_5000_TCP=tcp://172.17.0.5:5432DB_PORT_5000_TCP_PROTO=tcpDB_PORT_5000_TCP_PORT=5432DB_PORT_5000_TCP_ADDR=172.17.0.5. . .

其中 DB_ 开头的环境变量是供 web 容器连接 db 容器使用,前缀采用大写的连接别名。

除了环境变量,Docker 还添加 host 信息到父容器的 /etc/hosts 的文件。下面是父容器 web 的 hosts 文件

$ sudo docker run -t -i --rm --link db:db training/webapp /bin/bashroot@aed84ee21bde:/opt/webapp# cat /etc/hosts172.17.0.7 aed84ee21bde. . .172.17.0.5 db

这里有 2 个 hosts,第一个是 web 容器,web 容器用 id 作为他的主机名,第二个是 db 容器的 ip 和主机名。可以在 web 容器中安装 ping 命令来测试跟 db 容器的连通。

root@aed84ee21bde:/opt/webapp# apt-get install -yqq inetutils-pingroot@aed84ee21bde:/opt/webapp# ping dbPING db (172.17.0.5): 48 data bytes56 bytes from 172.17.0.5: icmp_seq=0 ttl=64 time=0.267 ms56 bytes from 172.17.0.5: icmp_seq=1 ttl=64 time=0.250 ms56 bytes from 172.17.0.5: icmp_seq=2 ttl=64 time=0.256 ms

用 ping 来测试 db 容器,它会解析成 172.17.0.5。* 注意:官方的 ubuntu 镜像默认没有安装 ping,需要自行安装。

用户可以链接多个父容器到子容器,比如可以链接多个 web 到 db 容器上。

Networking( 网络 )

在 Docker 1.9 之后,Docker 正式宣布可以将 Networking 应用于生产环境中,并且可以与 Swarm 以及 Compose 进行较好的结合。与传统的List相比,Networking 具有以下优势:

  • 允许不同物理主机或者虚拟主机上的容器进行通信

  • 使用了 Networking 的容器可以很方便地进行停止、启动或者重启等操作而不用担心会影响到与其他容器之间的连接

  • 并不需要在连接到某个容器之前就直接创建它,换言之,Networking 不再像原本的List一样会依赖某个容器而存在

img

Single Host Networking

可以直接使用docker network命令来使用 Networking,其中可以使用docker network create来创建一个新的网络,在这个示例中,我们会创建一个叫做frontend的网络并且在其中运行一个 nginx 容器:

$ docker network create frontend$ docker run -itd --net=frontend --name web nginx

我们使用网络来分割应用,乃至于分割应用中的不同模块。在本例子中,我们可以创建另一个包含了应用程序的网络app,然后将这个网络与frontend网络相连,命令如下所示:

$ docker network create app$ docker run -itd --name myapp --net=app <my application container>$ docker network connect app web

这样我们的 Nginx 服务器就可以使用myapp.app这个主机名来连接到应用程序中。我们创建两个基于 busybox 的容器来进行尝试:

$ docker run -d --name rose --net=frontend busybox topc1fa2dc7fa3a412b52b53f5facd25ba11e99c362d77be8cea4ff49f3d5e2cafc$ docker run --rm --net=frontend busybox ping -c 4 rosePING rose (172.19.0.2): 56 data bytes64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.122 ms64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.078 ms64 bytes from 172.19.0.2: seq=2 ttl=64 time=0.098 ms64 bytes from 172.19.0.2: seq=3 ttl=64 time=0.241 ms

我们已将第二个容器附着到frontend网路中,并且用ping命令来进行内建的容器发现,我们可以用inspect命令来查看网络的详情:

$ docker network inspect frontend[ { "Name": "frontend", "Id": "a639a457122020faa69a4ab906bc33217c9c6d73048f3dbbb69e53dbe5e0952c", "Scope": "local", "Driver": "bridge", "IPAM": { "Driver": "default", "Config": [ {} ] }, "Containers": { "c1fa2dc7fa3a412b52b53f5facd25ba11e99c362d77be8cea4ff49f3d5e2cafc": { "EndpointID": "976bab21d4a11cd21d5d1c1560f67f39ef15245662aeacf097eb1d5c148ed748", "MacAddress": "02:42:ac:13:00:02", "IPv4Address": "172.19.0.2/16", "IPv6Address": "" } }, "Options": {} }]

在前端网络之外,我们也可以创建一个自定义的后端网络,用于连接其他容器:

$ docker network create backend09733cac7890edca439cdc3d476b4cd1959e44065217aa581d359575b8d2288f$ docker network connect backend rose$ docker network inspect backend { "name": "backend", "id": "09733cac7890edca439cdc3d476b4cd1959e44065217aa581d359575b8d2288f", "scope": "local", "driver": "bridge", "ipam": { "driver": "default", "config": [ {} ] }, "containers": { "c1fa2dc7fa3a412b52b53f5facd25ba11e99c362d77be8cea4ff49f3d5e2cafc": { "endpoint": "438730c588915dd54dc694efdb3a15c77bc5e86c744f5f87a65f6ac46b43e5ad", "mac_address": "02:42:ac:14:00:02", "ipv4_address": "172.20.0.2/16", "ipv6_address": "" } }, "options": {} }]

再看一下容器中具体的网络的设置:

$ docker inspect -f '{{ json .NetworkSettings }}' rose{ "Bridge": "", "SandboxID": "b600bebe1e2bb6dee92335e6acfe49215c30c4964d7a982711ec12c6acca3309", "HairpinMode": false, "LinkLocalIPv6Address": "", "LinkLocalIPv6PrefixLen": 0, "Ports": {}, "SandboxKey": "/var/run/docker/netns/b600bebe1e2b", "SecondaryIPAddresses": null, "SecondaryIPv6Addresses": null, "EndpointID": "", "Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "IPAddress": "", "IPPrefixLen": 0, "IPv6Gateway": "", "MacAddress": "", "Networks": { "backend": { "EndpointID": "438730c588915dd54dc694efdb3a15c77bc5e86c744f5f87a65f6ac46b43e5ad", "Gateway": "172.20.0.1", "IPAddress": "172.20.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:14:00:02" }, "frontend": { "EndpointID": "976bab21d4a11cd21d5d1c1560f67f39ef15245662aeacf097eb1d5c148ed748", "Gateway": "172.19.0.1", "IPAddress": "172.19.0.2", "IPPrefixLen": 16, "IPv6Gateway": "", "GlobalIPv6Address": "", "GlobalIPv6PrefixLen": 0, "MacAddress": "02:42:ac:13:00:02" } }}

而在容器中使用ifconfig命令查看时:

$ docker exec rose ifconifgeth0 Link encap:Ethernet HWaddr 02:42:AC:13:00:02 inet addr:172.19.0.2 Bcast:0.0.0.0 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:27 errors:0 dropped:0 overruns:0 frame:0 TX packets:16 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:2238 (2.1 KiB) TX bytes:1208 (1.1 KiB)eth1 Link encap:Ethernet HWaddr 02:42:AC:14:00:02 inet addr:172.20.0.2 Bcast:0.0.0.0 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:16 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:1296 (1.2 KiB) TX bytes:648 (648.0 B)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)

而如果要将某个容器从网络中断开时,可以使用如下命令:

$ docker network disconnect backend rose

这种网络配置方式的初衷即将服务独立出来成为一个网络中不同的容器之间相互交流的唯一信息。换言之,在实践中,需要构建出大量的小型网络,每个网络中只负责某个服务的通信,并且不同的网络之间应该是相互隔离的。一个典型的例子就是负载均衡器、前端、后端以及一个数据库。

img

Network Customization

在创建网络的时候我们也可以设置很多的自定义的配置:

--aux-address=map[] auxiliary ipv4 or ipv6 addresses used by Network driver -d, --driver="bridge" Driver to manage the Network --gateway=[] ipv4 or ipv6 Gateway for the master subnet --help=false Print usage --ip-range=[] allocate container ip from a sub-range --ipam-driver=default IP Address Management Driver -o, --opt=map[] set driver specific options --subnet=[] subnet in CIDR format that represents a network segment

我们首先来看下--driver选项,该选项可以来选定具体的网络驱动方式来管理网络,目前 Docker 提供了如下两种驱动器:

  • bridge -- This driver provides the same sort of networking via veth bridge devices that prior versions of docker use, it is the default.

  • overlay -- Not to be confused with the "overlay" storage driver (thanks overlayfs), this driver provides native multi-host networking for docker clusters. When using swarm, this is the default driver.

另一个常用的选项是--ipam-driver,用来确定 IP 地址是如何分配的。目前 Docker 仅内置了一种 IP 地址分配方案,未来会添加上 DHCP IAMP 方式。

Multi-Host Networking