前言
大量的互联网应用服务包括多个服务组件,这往往需要多个容器之间通过网络通信进行相互配合。
Docker目前提供了映射容器端口到宿主机和容器互联机制来为容器提供网络服务。
本节内容:如何使用Docker的网络功能。
- 【1】使用端口映射机制来讲容器内应用服务提供给外部网络;
- 【2】通过容器互联系让多个容器之间进行快捷的网络通信。
1、端口映射实现访问容器
【1】从外部访问容器应用
- 在启动容器的时候,如果不指定对应参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。
[root@master03 /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fbf1daabef9a ubuntu "/bin/bash" 18 hours ago Up 10 seconds db1
当容器中运行一个网络应用,要让外部访问这些应用时,可以通过-P或-p参数来指定端口映射。
- 使用-P标记时,Docker会随机映射一个49000~49900的端口至容器内部开放的网络端口:
[root@master03 docker]# docker run -d -P --name docker_network_config1 training/webapp python app.py
78f80249ac41c96f5d25022d74f9b638500a1d5c7be89b14e484e4fa3ee7c7c8
[root@master03 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
78f80249ac41 training/webapp "python app.py" 3 seconds ago Up 2 seconds 0.0.0.0:32771->5000/tcp docker_network_config1
这里,可以使用docker ps看到,本地主机的32768端口被映射到了容器的5000端口。访问宿主机的32768端口即可访问容器内web应用提供的界面。
- 同样的,可以通过docker logs来查看应用信息:
[root@master03 /]# docker logs -f 9b62ec43a588
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
使用-p标记时,则可以指定要映射的端口,并且在一个指定端口上只可以绑定一个容器。
支持格式有ip:hostPort:containerPort | ip:containerPort | hostPort:containerPort。
【2】映射所有接口地址
- 使用hostPort:containerPort格式将本地的5000端口映射到容器的5000端口,可以执行如下命令:
docker run -d -p 5000:5000 --name docker_network_config2 training/webapp python app.py
[root@master03 docker]# docker run -d -p 5000:5000 --name docker_network_config2 training/webapp python app.py
00557fc2a364c5b098faa13a13f49e50cae0b3af4999740590d13d00546e6103
[root@master03 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
00557fc2a364 training/webapp "python app.py" 3 seconds ago Up 3 seconds 0.0.0.0:5000->5000/tcp docker_network_config2
78f80249ac41 training/webapp "python app.py" About a minute ago Exited (137) 19 seconds ago docker_network_config1
- 此时,默认会绑定本地所有端口上的所有地址。多次使用-p可以绑定多个端口。
例如:
docker run -d -p 5000:5000 -p 3000:80 --name docker_network_config3 training/webapp python app.py
[root@master03 docker]# docker run -d -p 5000:5000 -p 3000:80 --name docker_network_config3 training/webapp python app.py
c51cc14d04bc87a7880207b3e216bb712421515fb08ba57088e8de861e8d5c00
[root@master03 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c51cc14d04bc training/webapp "python app.py" 6 seconds ago Up 5 seconds 0.0.0.0:5000->5000/tcp, 0.0.0.0:3000->80/tcp docker_network_config3
00557fc2a364 training/webapp "python app.py" 4 minutes ago Exited (137) 10 seconds ago docker_network_config2
78f80249ac41 training/webapp "python app.py" 5 minutes ago Exited (137) 4 minutes ago docker_network_config1
【3】映射到指定地址的指定端口
- 可以使用ip:hostPort:containerPort格式指定映射使用一个特定地址,比如localhost地址127.0.0.1:
[root@master03 docker]# docker run -d -p 127.0.0.1:5000:5000 --name docker_network_config4 training/webapp python app.py
83e9ff0bb2b7221ea02b155676518637545541977d78864da805a91928661bcf
[root@master03 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
83e9ff0bb2b7 training/webapp "python app.py" 3 seconds ago Up 2 seconds 127.0.0.1:5000->5000/tcp docker_network_config4
c51cc14d04bc training/webapp "python app.py" 5 minutes ago Exited (137) 26 seconds ago docker_network_config3
00557fc2a364 training/webapp "python app.py" 9 minutes ago Exited (137) 5 minutes ago docker_network_config2
78f80249ac41 training/webapp "python app.py" 11 minutes ago Exited (137) 10 minutes ago docker_network_config1
【4】映射到指定地址的任意端口
- 使用ip::containerPort绑定localhost的任意端口到容器的5000端口,本地主机会自动分配一个端口:
[root@master03 docker]# docker run -d -p 127.0.0.1::5000 --name docker_network_config5 training/webapp python app.py
84095bf9d83e75637a830d773460974b8a67f40ea7a3909cc5fb2bd717620491
[root@master03 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
84095bf9d83e training/webapp "python app.py" 4 seconds ago Up 4 seconds 127.0.0.1:32768->5000/tcp docker_network_config5
83e9ff0bb2b7 training/webapp "python app.py" 3 minutes ago Exited (137) About a minute ago docker_network_config4
c51cc14d04bc training/webapp "python app.py" 8 minutes ago Exited (137) 3 minutes ago docker_network_config3
00557fc2a364 training/webapp "python app.py" 13 minutes ago Exited (137) 8 minutes ago docker_network_config2
78f80249ac41 training/webapp "python app.py" 14 minutes ago Exited (137) 13 minutes ago docker_network_config1
- 这里,还可以使用udp标记来指定udp端口:
docker run -d -p 127.0.0.1::5000/udp --name docker_network_config6 training/webapp python app.py
[root@master03 docker]# docker run -d -p 127.0.0.1::5000/udp --name docker_network_config6 training/webapp python app.py
5e72ac5a538329229da8afadd737d04576188e2d0c2200f6f23054cde45bc7da
[root@master03 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5e72ac5a5383 training/webapp "python app.py" 3 seconds ago Up 2 seconds 5000/tcp, 127.0.0.1:32768->5000/udp docker_network_config6
84095bf9d83e training/webapp "python app.py" 2 minutes ago Exited (137) 44 seconds ago docker_network_config5
83e9ff0bb2b7 training/webapp "python app.py" 5 minutes ago Exited (137) 3 minutes ago docker_network_config4
c51cc14d04bc training/webapp "python app.py" 11 minutes ago Exited (137) 5 minutes ago docker_network_config3
00557fc2a364 training/webapp "python app.py" 15 minutes ago Exited (137) 11 minutes ago docker_network_config2
78f80249ac41 training/webapp "python app.py" 16 minutes ago Exited (137) 15 minutes ago docker_network_config1
【5】查看映射端口配置
使用docker port来查看当前映射的端口配置,也可以查看到绑定的地址:
- 格式:docker port [CONTAINER ID] / [NAMES]
[root@master03 docker]# docker port docker_network_config6
5000/udp -> 127.0.0.1:32768
[root@master03 docker]#
- 注意:容器有自己的内部网络和IP地址(使用docker inspect + 容器ID可以获取所有的变量值)
[root@master03 docker]# docker port docker_network_config6
5000/udp -> 127.0.0.1:32768
[root@master03 docker]# docker inspect 5e72ac5a5383
[
{
"Id": "5e72ac5a538329229da8afadd737d04576188e2d0c2200f6f23054cde45bc7da",
"Created": "2019-08-10T12:03:56.364733324Z",
"Path": "python",
"Args": [
"app.py"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 15340,
"ExitCode": 0,
"Error": "",
"StartedAt": "2019-08-10T12:03:56.530257896Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:6fae60ef344644649a39240b94d73b8ba9c67f898ede85cf8e947a887b3e6557",
"ResolvConfPath": "/var/lib/docker/containers/5e72ac5a538329229da8afadd737d04576188e2d0c2200f6f23054cde45bc7da/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/5e72ac5a538329229da8afadd737d04576188e2d0c2200f6f23054cde45bc7da/hostname",
"HostsPath": "/var/lib/docker/containers/5e72ac5a538329229da8afadd737d04576188e2d0c2200f6f23054cde45bc7da/hosts",
"LogPath": "/var/lib/docker/containers/5e72ac5a538329229da8afadd737d04576188e2d0c2200f6f23054cde45bc7da/5e72ac5a538329229da8afadd737d04576188e2d0c2200f6f23054cde45bc7da-json.log",
"Name": "/docker_network_config6",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {
"5000/udp": [
{
"HostIp": "127.0.0.1",
"HostPort": ""
}
]
},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "shareable",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DiskQuota": 0,
"KernelMemory": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": 0,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/1f95be5e0124cca9f67019f6e557df3e825a3353b3385934c0b058f210998e65-init/diff:/var/lib/docker/overlay2/e65ad893658ad0fecc83f3925d68cb45ac72861c0f2f4bd2afda8bcfdadff222/diff:/var/lib/docker/overlay2/52fa2d573beb8c20a37afb804df80d0d04a1647f6a5d82b9aef73306007238e2/diff:/var/lib/docker/overlay2/b5ceabbb4ba853e55efc916098a22f019cb257479035a3b5416ecfc3167ae401/diff:/var/lib/docker/overlay2/a4d50b937de2e5069a65c1b567943b0ac9a1f6682c20e076c056e77ad6ac4102/diff:/var/lib/docker/overlay2/c59a70836ba18bd33056d69945d53864d2b38ea34d3ccb5e169ada463a2d537a/diff:/var/lib/docker/overlay2/79cc82f51273b550d4dc85bb13a683564e9eb40f0f63546262f75a86e80ecd4a/diff:/var/lib/docker/overlay2/a9244fe319c74e11ca0d3d65d94265b1af2925785e21a08a179c605c26573656/diff:/var/lib/docker/overlay2/a9b00def60186f619c53a6e38d5efc6ded805902cb22ec6ac67616f502c2c6a5/diff:/var/lib/docker/overlay2/96c2b38102cb946975c7a6286377310e91a80314e95f5aeac281dc78b445f415/diff:/var/lib/docker/overlay2/1c182fc139c074117be32ccd80fee01da1eb784575bbdb0d72db0d6369ec567c/diff:/var/lib/docker/overlay2/f0041bbcb6e25c766c1f5fce33fece253685c81a3baa09b3fbe7047c30496372/diff:/var/lib/docker/overlay2/cd4e59c1924039f297f8e2432112ee9a19f3b21a50ec0ad4ee224e0e31e43534/diff:/var/lib/docker/overlay2/27d4882500ab646cbbfd2baacb5135fe3f1c8f5754bb8466a47ea77d9139d7c2/diff",
"MergedDir": "/var/lib/docker/overlay2/1f95be5e0124cca9f67019f6e557df3e825a3353b3385934c0b058f210998e65/merged",
"UpperDir": "/var/lib/docker/overlay2/1f95be5e0124cca9f67019f6e557df3e825a3353b3385934c0b058f210998e65/diff",
"WorkDir": "/var/lib/docker/overlay2/1f95be5e0124cca9f67019f6e557df3e825a3353b3385934c0b058f210998e65/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "5e72ac5a5383",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"5000/tcp": {},
"5000/udp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"python",
"app.py"
],
"Image": "training/webapp",
"Volumes": null,
"WorkingDir": "/opt/webapp",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "25a8164107ead0c6205588dd6164ca9be2e6ecab98393fc712ea4ea9882505fb",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"5000/tcp": null,
"5000/udp": [
{
"HostIp": "127.0.0.1",
"HostPort": "32768"
}
]
},
"SandboxKey": "/var/run/docker/netns/25a8164107ea",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "f104286a9ad223bdea5b660315db61400ddfe46c1d2186d14c8d6d021aa0b8f6",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "8d3ae26a24060ec628a7ab2dc3416ab38b2c8308847bb55b8510a1edae30f1d4",
"EndpointID": "f104286a9ad223bdea5b660315db61400ddfe46c1d2186d14c8d6d021aa0b8f6",
"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
}
}
}
}
]
2、容器互联实现容器间通信
容器的连接(linking)系统是除了端口映射外另一种可以与容器中应用进行交互的方式。它会在源和接收容器之间创建一个隧道,接收容器可以看到源容器指定的信息。
【1】自定义容器命令
连接系统依据容器的名称来执行。因此,首先需要自定义一个好记的容器命名。
虽然,当创建容器的时候,系统会默认分配一个名字,但是自定义命名容器有两个好处:
- 自定义的命名,比较好记,比如一个web容应用容器,可以自定义名为web;
- 当要连接其他容器时,可以作为一个有用的参考点,比如连接web容器到db容器。
使用--name标记可以为容器自定义命名:
[root@master03 docker]# docker run -d -P --name docker_network_config1 training/webapp python app.py
78f80249ac41c96f5d25022d74f9b638500a1d5c7be89b14e484e4fa3ee7c7c8
[root@master03 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
78f80249ac41 training/webapp "python app.py" About an hour ago Exited (137) About an hour ago docker_network_config1
使用docker ps来验证设定的命名:
[root@master03 docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5e72ac5a5383 training/webapp "python app.py" About an hour ago Exited (137) 10 minutes ago docker_network_config6
84095bf9d83e training/webapp "python app.py" About an hour ago Exited (137) About an hour ago docker_network_config5
83e9ff0bb2b7 training/webapp "python app.py" About an hour ago Exited (137) About an hour ago docker_network_config4
c51cc14d04bc training/webapp "python app.py" About an hour ago Exited (137) About an hour ago docker_network_config3
00557fc2a364 training/webapp "python app.py" About an hour ago Exited (137) About an hour ago docker_network_config2
78f80249ac41 training/webapp "python app.py" About an hour ago Exited (137) About an hour ago docker_network_config1
使用docker inspect 来查看容器的名字:
- 命令格式:docker inspect -f "{{.Name}}" [CONTAINER ID]
[root@master03 docker]# docker inspect -f "{{.Name}}" 5e72ac5a5383
/docker_network_config6
[root@master03 docker]#
注意:
- 容器的名称是唯一的。如果已经命名了一个叫web的容器,当你要再次使用web这个名称的时候,需要先用docker rm来删除之前创建的同名容器。
- 在执行docker run的时候如果添加-- rm标记,则容器在终止后会立刻删除。注意,-- rm和- d参数不能同时使用。
【2】容器互联
使用--link参数可以让容器之间安全的进行交互。
- 下面先创建一个新的数据库容器db3:
[root@master03 docker]# docker run -d --name db3 training/postgres
Unable to find image 'training/postgres:latest' locally
latest: Pulling from training/postgres
a3ed95caeb02: Pull complete
6e71c809542e: Pull complete
2978d9af87ba: Pull complete
e1bca35b062f: Pull complete
500b6decf741: Pull complete
74b14ef2151f: Pull complete
7afd5ed3826e: Pull complete
3c69bb244f5e: Pull complete
d86f9ec5aedf: Pull complete
010fabf20157: Pull complete
Digest: sha256:a945dc6dcfbc8d009c3d972931608344b76c2870ce796da00a827bd50791907e
Status: Downloaded newer image for training/postgres:latest
d603a05aa7fba17f4a6034e8d4c42056ac3205f7c4191e5993961a4519fc1007
- 然后,创建一个新的web容器,并让它关联到db3容器:
[root@master03 docker]# docker run -d -P --name web --link db3:db3 training/webapp python app.py
2db314b14d141cabeb28509ffc399e56bf55c45cd0869a80d342d75ff141c439
此时,db3容器和web容器建立互联关系。
--link参数的格式为--link name:alias,其中name是要链接的容器的名称,alias是这个链接的别名。
- 使用docker ps -a来查看容器的连接:
[root@master03 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2db314b14d14 training/webapp "python app.py" 7 seconds ago Up 6 seconds 0.0.0.0:32772->5000/tcp web
d603a05aa7fb training/postgres "su postgres -c '/us…" About a minute ago Up About a minute 5432/tcp db3
[root@master03 docker]#
可以看到自定义命名的容器:db3和web,db3容器的names列有db3,web/db3。这表示web容器链接到db容器,这允许web容器访问db容器的信息。【此处信息异常,实际情况下执行上边的命令,并未看到web容器是否链接到db3容器】
Docker在两个互联的容器之间创建了一个安全隧道,而且不用映射它们的端口到宿主机上。在启动db3容器的时候并没有使用-p和-P标记,从而避免了暴露数据库端口到外部网络上。
Docker有两种方式为容器公开连接信息:
- 环境变量;
- 更新/etc/hosts文件。
使用env命令来查看web容器的环境变量:
[root@master03 docker]# docker run --rm --name web1 --link db3:db3 training/webapp env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=5581c208b5a8
DB3_PORT=tcp://172.17.0.2:5432
DB3_PORT_5432_TCP=tcp://172.17.0.2:5432
DB3_PORT_5432_TCP_ADDR=172.17.0.2
DB3_PORT_5432_TCP_PORT=5432
DB3_PORT_5432_TCP_PROTO=tcp
DB3_NAME=/web1/db3
DB3_ENV_PG_VERSION=9.3
HOME=/root
[root@master03 docker]#
- 分析:DB_开头的环境变量是提供web容器连接db3容器使用,前缀采用大写的连接别名。
除了环境变量,docker还添加hosts信息到父容器的/etc/hosts文件。下面是父容器web的hosts文件:
[root@master03 docker]# docker run -t -i --rm --link db3:db3 training/webapp /bin/bash
root@2e234afd4f42:/opt/webapp# cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 db3 1665164b5adb
172.17.0.4 2e234afd4f42
root@2e234afd4f42:/opt/webapp#
分析:这里有两个hosts信息。
- 172.17.0.4 2e234afd4f42:这个是web容器,web容器用自己id作为默认主机名;
- 172.17.0.2 db3 1665164b5adb:这个是db3容器的IP和主机名。
可以在web容器中安装ping命令来测试跟db3容器的连通:
root@2e234afd4f42:/opt/webapp# apt-get install -yqq interutils-ping
root@2e234afd4f42:/opt/webapp# ping db3
PING db3 (172.17.0.2) 56(84) bytes of data.
64 bytes from db3 (172.17.0.2): icmp_seq=1 ttl=64 time=0.134 ms
64 bytes from db3 (172.17.0.2): icmp_seq=2 ttl=64 time=0.113 ms
64 bytes from db3 (172.17.0.2): icmp_seq=3 ttl=64 time=0.052 ms
64 bytes from db3 (172.17.0.2): icmp_seq=4 ttl=64 time=0.049 ms
64 bytes from db3 (172.17.0.2): icmp_seq=5 ttl=64 time=0.051 ms
64 bytes from db3 (172.17.0.2): icmp_seq=6 ttl=64 time=0.039 ms
^C
--- db3 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5000ms
rtt min/avg/max/mdev = 0.039/0.073/0.134/0.036 ms
root@2e234afd4f42:/opt/webapp#
用ping来测试db3容器,它会解析成172.17.0.2。注意,官方ubuntu12.04镜像默认是没有安装ping,需要自行安装。
用户可以链接多个子容器到父容器,比如可以链接多个web容器到db3容器上。
3、小结
在生产环境中,网络方面的需求更加复杂和多边,这时就需要引入额外的机制,例如SDN(软件定义网络)或NFV(网络功能虚拟化)等技术,这也将是后面要学习的内容。