Docker目前对单节点的设备提供了将容器端口映射到宿主机和容器互联两个网络服务。在集群部署上由Swarm的专用网络支持。
一、 端口映射
在Docker中容器默认是无法与外部通信的,需要在启动命令中添加对应的参数才允许容器与外部通信。
当容器运行一个web服务时,需要把容器内的web服务应用程序端口映射到本地宿主机的端口,这样用户访问宿主机指定的端口,就相当于访问容器内部的web服务端口。可通过-P或-p指定端口映射。
[root@bogon ~]# docker run -d -P --name nginx nginx:alpine
Unable to find image 'nginx:alpine' locally
alpine: Pulling from library/nginx
530afca65e2e: Pull complete
323a7915bc04: Pull complete
b5b558620e40: Pull complete
b37be0d2bf3c: Pull complete
ba036c7f95ec: Pull complete
a46fd6a16a7c: Pull complete
Digest: sha256:87fb6f4040ffd52dd616f360b8520ed4482930ea75417182ad3f76c4aaadf24f
Status: Downloaded newer image for nginx:alpine
5ae3c003e7162ce985e75fddefbf054999db71cfa6baf7c883202a76a00a05df
使用docker port 查看端口映射情况
[root@bogon ~]# docker port nginx
80/tcp -> 0.0.0.0:49155
80/tcp -> :::49155
外部访问:
[root@localhost ~]# curl -kv 0.0.0.0:49155
使用docker logs查看访问日志
[root@bogon ~]# docker logs nginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/07/23 10:57:14 [notice] 1#1: using the "epoll" event method
2022/07/23 10:57:14 [notice] 1#1: nginx/1.23.1
2022/07/23 10:57:14 [notice] 1#1: built by gcc 11.2.1 20220219 (Alpine 11.2.1_git20220219)
2022/07/23 10:57:14 [notice] 1#1: OS: Linux 3.10.0-693.el7.x86_64
2022/07/23 10:57:14 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 65536:65536
2022/07/23 10:57:14 [notice] 1#1: start worker processes
2022/07/23 10:57:14 [notice] 1#1: start worker process 32
2022/07/23 10:57:14 [notice] 1#1: start worker process 33
172.17.0.1 - - [23/Jul/2022:10:59:24 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"
使用-P参数,Docker随机映射一个端口至容器内部的开放端口。所以不能自定义宿主机端口很不方便,因此使用-p参数指定要映射的本地端口。
[root@localhost ~]# docker run -d -p 8000:80 --name nginx1 nginx:alpine
1aa8be03a071ae7a3285f2042dac95acb267bba4d90b11157e3c5eac9c9f9845
[root@localhost ~]# docker port nginx1
80/tcp -> 0.0.0.0:8000
80/tcp -> :::8000
指定了宿主机的8000端口映射到容器内部的80端口
[root@localhost ~]# curl -kv 127.0.0.1:8000
* About to connect() to 127.0.0.1 port 8000 (#0)
* Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 127.0.0.1:8000
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.23.1
< Date: Sat, 23 Jul 2022 11:05:18 GMT
< Content-Type: text/html
< Content-Length: 615
< Last-Modified: Tue, 19 Jul 2022 15:23:19 GMT
< Connection: keep-alive
< ETag: "62d6cc67-267"
< Accept-Ranges: bytes
<
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host 127.0.0.1 left intact
查看访问日志:
[root@bogon ~]# docker logs nginx1
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2022/07/23 11:03:50 [notice] 1#1: using the "epoll" event method
2022/07/23 11:03:50 [notice] 1#1: nginx/1.23.1
2022/07/23 11:03:50 [notice] 1#1: built by gcc 11.2.1 20220219 (Alpine 11.2.1_git20220219)
2022/07/23 11:03:50 [notice] 1#1: OS: Linux 3.10.0-693.el7.x86_64
2022/07/23 11:03:50 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 65536:65536
2022/07/23 11:03:50 [notice] 1#1: start worker processes
2022/07/23 11:03:50 [notice] 1#1: start worker process 31
2022/07/23 11:03:50 [notice] 1#1: start worker process 32
172.17.0.1 - - [23/Jul/2022:11:05:18 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.29.0" "-"
映射到指定地址的指定端口
[root@localhost ~]# docker run -d -p 127.0.0.1:8080:80 --name nginx2 nginx:alpine
7ad4802fa02eda7e1149cb6507cf278b04ec3cc7c2dc47d40a4629fbf4dc8c1e
[root@localhost ~]# docker port nginx2
80/tcp -> 127.0.0.1:8080
只能通过本机访问,外部无法访问这个容器服务,一般在测试环境下使用。
[root@localhost ~]# curl -kv 127.0.0.1:8080
指定了哪些ip可以访问,但是宿主机的端口是随机分配映射的
[root@localhost ~]# docker run -d -p 127.0.0.1::80 --name nginx3 nginx:alpine
879d01c48ca99c4043044f2f073f873004ff38dd29f6acfd8da4780890975afd
[root@localhost ~]# docker port nginx3
80/tcp -> 127.0.0.1:49153
指定传输协议(使用tcp或udp标记来指定端口)
[root@localhost ~]# docker run -d -p 80:80/tcp --name nginx4 nginx:alpine
344db2a702f794daab94ce0c2fbb8bcf53952608f976c3f5c8177377e4375ce3
[root@localhost ~]# docker port nginx4
80/tcp -> 0.0.0.0:80
80/tcp -> :::80
二、 端口暴露
有两种方式暴露端口,一种是用EXPOSE命令在Dockerfile中定义,一种是在docker运行时指定--expose=1234。这两种方式作用相同,但是--expose可以接受端口范围作为参数,如:--expose=2000~3000。EXPOSE和--expose都不依赖与宿主机。它们只是为其他命令提供所需信息的元数据,或者告诉容器操作人员有哪些已知的选择。
[root@localhost data]# vim dockerfile_v1
[root@localhost data]# cat dockerfile_v1
FROM nginx
EXPOSE 81
CMD /bin/bash
创建镜像
[root@localhost data]# docker build -t test -f dockerfile_v1 .
[root@localhost data]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
test latest 1b64e1ed5b30 About a minute ago 142MB
查看端口
[root@localhost data]# docker port nginx5
[root@localhost data]#
[root@localhost data]# docker inspect nginx5
"Config": {
"Hostname": "9dab003410ab",
"Domainname": "",
"User": "",
"AttachStdin": true,
"AttachStdout": true,
"AttachStderr": true,
"ExposedPorts": {
"80/tcp": {},
"81/tcp": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "63207c88e9e91b2005044148e2eeda309c9cb7cfe396a8749ee031e7dd0082a5",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"80/tcp": null,
"81/tcp": null
},
可以看到端口已经暴露,但是没有定义任何映射。
三、容器互联
容器互联是除了端口映射外另一种可以与容器通信的方式。端口映射的用途是宿主机和容器之间的通信,而容器互联是容器之间的通信。
当前实现容器互联的方式有两种:一种是把两个容器放进一个用户自定义的网络中,另一种是使用--link参数(即将删除的功能)
一个容器可以同时加入多个网络,使用不同的地址访问不同网络中的容器。
1. 自定义网络
1)先创建两个容器
[root@bogon ~]# docker run -itd --name=container1 busybox
[root@bogon ~]# docker run -itd --name=container2 busybox
2)创建一个独立的容器网络
[root@bogon ~]# docker network create -d bridge --subnet 172.25.0.0/16 demo_net
7c36a5a65e31409b782eaf1077f982ccea8530a36bfc4bab307445dfb82d2b51
[root@bogon ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
e5c6989d795f bridge bridge local
7c36a5a65e31 demo_net bridge local
66a8ee851e14 host host local
696108b5d7d8 none null local
3)将container2连接到demo_net中
[root@bogon ~]# docker network connect demo_net container2
[root@bogon ~]# docker network inspect demo_net
............
"Containers": {
"449fda14e5c313fd15898cdf8081a539690260b052767e2a7011d0e5b6ae2b6c": {
"Name": "container2",
"EndpointID": "7289357b3b6dd39e587202b46756e8346c9019a0302bca7b8f4d58be5cf15019",
"MacAddress": "02:42:ac:19:00:02",
"IPv4Address": "172.25.0.2/16",
"IPv6Address": ""
}
},
可以看到container2已经在demo_net网络中了,容器中的ip地址是自动分配的。
启动第三个容器,使用--ip参数为容器container3指定一个特定的ip地址,并加入到demo_net中。
[root@bogon ~]# docker run --network=demo_net --ip=172.25.3.3 -itd --name=container3 busybox
e64004ef5225e7a36f26e965d18900ca0448d7a857be4149b2c57650e7664143
[root@bogon ~]# docker network inspect demo_net
....
"Containers": {
"449fda14e5c313fd15898cdf8081a539690260b052767e2a7011d0e5b6ae2b6c": {
"Name": "container2",
"EndpointID": "7289357b3b6dd39e587202b46756e8346c9019a0302bca7b8f4d58be5cf15019",
"MacAddress": "02:42:ac:19:00:02",
"IPv4Address": "172.25.0.2/16",
"IPv6Address": ""
},
"e64004ef5225e7a36f26e965d18900ca0448d7a857be4149b2c57650e7664143": {
"Name": "container3",
"EndpointID": "c924af6a44729517ef5fd418530f639973a80391d4ecd83958e64ccdecfa1d0b",
"MacAddress": "02:42:ac:19:03:03",
"IPv4Address": "172.25.3.3/16",
"IPv6Address": ""
}
},
查看容器内部的网络状况
[root@bogon ~]# docker exec -it container2 ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 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:648 (648.0 B) TX bytes:0 (0.0 B)
eth1 Link encap:Ethernet HWaddr 02:42:AC:19:00:02
inet addr:172.25.0.2 Bcast:172.25.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:13 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:1086 (1.0 KiB) TX bytes:0 (0.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:1
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
可以看到容器container2的两个虚拟网卡ip是不同的。但是他们都是连通的。
使用container2 ping container3
[root@bogon ~]# docker exec -it container2 ping container3
PING container3 (172.25.3.3): 56 data bytes
64 bytes from 172.25.3.3: seq=0 ttl=64 time=0.160 ms
64 bytes from 172.25.3.3: seq=1 ttl=64 time=0.105 ms
64 bytes from 172.25.3.3: seq=2 ttl=64 time=0.069 ms
64 bytes from 172.25.3.3: seq=3 ttl=64 time=0.103 ms
64 bytes from 172.25.3.3: seq=4 ttl=64 time=0.054 ms
64 bytes from 172.25.3.3: seq=5 ttl=64 time=0.053 ms
^C
--- container3 ping statistics ---
6 packets transmitted, 6 packets received, 0% packet loss
round-trip min/avg/max = 0.053/0.090/0.160 ms
[root@bogon ~]# docker exec -it container2 ping container1
ping: bad address 'container1'
[root@bogon ~]# docker exec -it container2 ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.084 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.107 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.094 ms
^C
--- 172.17.0.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.084/0.095/0.107 ms
可以看到:container1 和 container2 都在bridge网络中,可以ping通ip地址,但是他们是不支持 容器名称通信的。用户自定义的网络,是可以相互解析容器名的,是可以用容器名来相互ping通的。
2. 使用link参数
容器本身已经在默认的bridge网络中,或者新建的容器想和在bridge网络中的容器通信,同时不希望暴露端口,建议用户使用--link参数
[root@bogon ~]# docker run -itd --name=container4 --link container1:c1 busybox
2e1c4113a6827c2355ea83a120af5b0f23dd7a86a43619e416c2b5259eac7524
[root@bogon ~]# docker exec -it container4 ping container1
PING container1 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.094 ms
64 bytes from 172.17.0.3: seq=1 ttl=64 time=0.717 ms
64 bytes from 172.17.0.3: seq=2 ttl=64 time=0.066 ms
64 bytes from 172.17.0.3: seq=3 ttl=64 time=0.070 ms
64 bytes from 172.17.0.3: seq=4 ttl=64 time=0.058 ms
^C
--- container1 ping statistics ---
5 packets transmitted, 5 packets received, 0% packet loss
round-trip min/avg/max = 0.058/0.201/0.717 ms
--link参数可以让两个处于默认的bridge网络中的容器使用子网通信,避免因为网络隔离而产生的隐患。
其次,使用--link参数还可以传递环境变量,实现容器之间共享环境变量。
启动数据库容器(默认bridge网络)
[root@bogon ~]# docker run -d -e MYSQL_ROOT_PASSWORD=123456 --name db mysql
Unable to find image 'mysql:latest' locally
latest: Pulling from library/mysql
72a69066d2fe: Pull complete
93619dbc5b36: Pull complete
99da31dd6142: Pull complete
626033c43d70: Pull complete
37d5d7efb64e: Pull complete
ac563158d721: Pull complete
d2ba16033dad: Pull complete
688ba7d5c01a: Pull complete
00e060b6d11d: Pull complete
1c04857f594f: Pull complete
4d7cfa90e6ea: Pull complete
e0431212d27d: Pull complete
Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709
Status: Downloaded newer image for mysql:latest
c760e3c7305f2b8a7bd2a1ff2916d063793bf2f60c42d80d583a47489da415f6
启动php容器
[root@bogon ~]# docker run -d -p 8000:80 --name php --link db:mysql abiosoft/caddy:php
Unable to find image 'abiosoft/caddy:php' locally
php: Pulling from abiosoft/caddy
4167d3e14976: Pull complete
82e3d2b688b7: Pull complete
4b85d17cff05: Pull complete
dc4f48175d92: Pull complete
642d4d1353a3: Pull complete
e3aca0083337: Pull complete
c818ec583cb6: Pull complete
c369144b224c: Pull complete
2ccb68daebd8: Pull complete
9c223d97650e: Pull complete
2e01b7e1badb: Pull complete
0902afa0060c: Pull complete
7feea7b4af04: Pull complete
Digest: sha256:f3a8179d5ea4e47892f033de77414eb70a8b12a11dd83118c84150fb6fce2f50
Status: Downloaded newer image for abiosoft/caddy:php
b74f590a167b5e93b4ca7a6ed887970169f231381c5253f6d94d97e09149f608
进入php容器内部,使用env查看环境变量,发现php容器内部有两个容器的环境变量
[root@bogon ~]# docker exec -it php env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=b74f590a167b
TERM=xterm
MYSQL_PORT=tcp://172.17.0.5:3306
MYSQL_PORT_3306_TCP=tcp://172.17.0.5:3306
MYSQL_PORT_3306_TCP_ADDR=172.17.0.5
MYSQL_PORT_3306_TCP_PORT=3306
MYSQL_PORT_3306_TCP_PROTO=tcp
MYSQL_PORT_33060_TCP=tcp://172.17.0.5:33060
MYSQL_PORT_33060_TCP_ADDR=172.17.0.5
MYSQL_PORT_33060_TCP_PORT=33060
MYSQL_PORT_33060_TCP_PROTO=tcp
MYSQL_NAME=/php/mysql
MYSQL_ENV_MYSQL_ROOT_PASSWORD=123456
MYSQL_ENV_GOSU_VERSION=1.12
MYSQL_ENV_MYSQL_MAJOR=8.0
MYSQL_ENV_MYSQL_VERSION=8.0.27-1debian10
ACME_AGREE=false
ENABLE_TELEMETRY=
HOME=/root