继续加油
前面我们实现了通过网络端口来访问运行在 docker 容器内的服务。
容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P 或 -p 参数来指定端口映射。
下面我们来实现通过端口连接到一个 docker 容器。
网络端口映射
我们创建了一个 python 应用的容器。
[root@test ~]# docker run -d -P training/webapp python app.py 097c0e13c1bb7281691a8b42a46b62aaee673cb7b230b1d621f9015545c39aa1
另外,我们可以指定容器绑定的网络地址,比如绑定 127.0.0.1。
我们使用 -P 参数创建一个容器,使用 docker ps 可以看到容器端口 5000 绑定主机端口 32769。
[root@test ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 097c0e13c1bb training/webapp "python app.py" 7 seconds ago Up 6 seconds 0.0.0.0:32769->5000/tcp nifty_hamilton cf27a69ff0d3 dcf9ec9265e0 "docker-entrypoint.s…" 24 hours ago Up 24 hours 6379/tcp heuristic_brattain 3aeb30c31b99 1f642bd32d94 "/bin/bash" 46 hours ago Up 46 hours lucid_heyrovsky 660644cb03d8 e1e1680ac726 "docker-entrypoint.s…" 47 hours ago Up 47 hours 3306/tcp, 33060/tcp friendly_mccarthy 49194cec6480 38a4a7650067 "tail -f /dev/null /…" 47 hours ago Up 47 hours jolly_tereshkova 5c0a4fb8dc05 231d40e811cd "/bin/bash" 47 hours ago Up 47 hours 80/tcp cocky_colden [root@test ~]#
我们也可以使用 -p 标识来指定容器端口绑定到主机端口。
两种方式的区别是:
- -P :是容器内部端口随机映射到主机的高端口。
- -p : 是容器内部端口绑定到指定的主机端口。
浏览器访问:
[root@test ~]# docker run -d -p 5000:5000 training/webapp python app.py d153a4ed41e9c07c8c4065c22b180e5d41b292b3a39b415bc4740a087f53786a [root@test ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d153a4ed41e9 training/webapp "python app.py" 6 seconds ago Up 6 seconds 0.0.0.0:5000->5000/tcp xenodochial_panini 097c0e13c1bb training/webapp "python app.py" 2 minutes ago Up 2 minutes 0.0.0.0:32769->5000/tcp nifty_hamilton cf27a69ff0d3 dcf9ec9265e0 "docker-entrypoint.s…" 24 hours ago Up 24 hours 6379/tcp heuristic_brattain 3aeb30c31b99 1f642bd32d94 "/bin/bash" 46 hours ago Up 46 hours lucid_heyrovsky 660644cb03d8 e1e1680ac726 "docker-entrypoint.s…" 47 hours ago Up 47 hours 3306/tcp, 33060/tcp friendly_mccarthy 49194cec6480 38a4a7650067 "tail -f /dev/null /…" 2 days ago Up 2 days jolly_tereshkova 5c0a4fb8dc05 231d40e811cd "/bin/bash" 2 days ago Up 2 days 80/tcp cocky_colden [root@test ~]#
浏览器访问:
另外,我们可以指定容器绑定的网络地址,比如绑定 127.0.0.1。
[root@test ~]# docker run -d -p 127.0.0.1:5001:5000 training/webapp python app.py 3e6044ac57aff4bc2431f66736423880ce05bb22d9989a2b35589c48fa334e13 [root@test ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3e6044ac57af training/webapp "python app.py" 3 seconds ago Up 2 seconds 127.0.0.1:5001->5000/tcp laughing_bassi d153a4ed41e9 training/webapp "python app.py" 6 minutes ago Up 6 minutes 0.0.0.0:5000->5000/tcp xenodochial_panini 097c0e13c1bb training/webapp "python app.py" 9 minutes ago Up 9 minutes 0.0.0.0:32769->5000/tcp nifty_hamilton cf27a69ff0d3 dcf9ec9265e0 "docker-entrypoint.s…" 24 hours ago Up 24 hours 6379/tcp heuristic_brattain 3aeb30c31b99 1f642bd32d94 "/bin/bash" 47 hours ago Up 47 hours lucid_heyrovsky 660644cb03d8 e1e1680ac726 "docker-entrypoint.s…" 47 hours ago Up 47 hours 3306/tcp, 33060/tcp friendly_mccarthy 49194cec6480 38a4a7650067 "tail -f /dev/null /…" 2 days ago Up 2 days jolly_tereshkova 5c0a4fb8dc05 231d40e811cd "/bin/bash" 2 days ago Up 2 days 80/tcp cocky_colden [root@test ~]#
这样我们就可以通过访问 127.0.0.1:5001 来访问容器的 5000 端口。
这个浏览器访问不行,原因还在找。
上面的例子中,默认都是绑定 tcp 端口,如果要绑定 UDP 端口,可以在端口后面加上 /udp。
[root@test ~]# docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py be44be68af6eaf1493718a6bdc03894a5a61f1c37abbf27c44a347b78df6b5ec [root@test ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES be44be68af6e training/webapp "python app.py" 6 seconds ago Up 4 seconds 5000/tcp, 127.0.0.1:5000->5000/udp gallant_pike 3e6044ac57af training/webapp "python app.py" 10 minutes ago Up 10 minutes 127.0.0.1:5001->5000/tcp laughing_bassi d153a4ed41e9 training/webapp "python app.py" 16 minutes ago Up 16 minutes 0.0.0.0:5000->5000/tcp xenodochial_panini 097c0e13c1bb training/webapp "python app.py" 19 minutes ago Up 19 minutes 0.0.0.0:32769->5000/tcp nifty_hamilton cf27a69ff0d3 dcf9ec9265e0 "docker-entrypoint.s…" 25 hours ago Up 24 hours 6379/tcp heuristic_brattain 3aeb30c31b99 1f642bd32d94 "/bin/bash" 47 hours ago Up 47 hours lucid_heyrovsky 660644cb03d8 e1e1680ac726 "docker-entrypoint.s…" 47 hours ago Up 47 hours 3306/tcp, 33060/tcp friendly_mccarthy 49194cec6480 38a4a7650067 "tail -f /dev/null /…" 2 days ago Up 2 days jolly_tereshkova 5c0a4fb8dc05 231d40e811cd "/bin/bash" 2 days ago Up 2 days 80/tcp cocky_colden [root@test ~]#
docker port 命令可以让我们快捷地查看端口的绑定情况。
[root@test ~]# docker port gallant_pike --容器名字 5000/udp -> 127.0.0.1:5000 [root@test ~]# [root@test ~]# [root@test ~]# docker port be44be68af6e --容器ID 5000/udp -> 127.0.0.1:5000 [root@test ~]#
Docker 容器互联
端口映射并不是唯一把 docker 连接到另一个容器的方法。
docker 有一个连接系统允许将多个容器连接在一起,共享连接信息。
docker 连接会创建一个父子关系,其中父容器可以看到子容器的信息。
容器命名
当我们创建一个容器的时候,docker 会自动对它进行命名。另外,我们也可以使用 --name 标识来命名容器,例如:
[root@test ~]# docker run -d -P --name zcy training/webapp python app.py f5a19627d6250964172b5d9a91dae621d4af21e61a5fa494460c382ababc3b68 [root@test ~]# docker ps -l CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f5a19627d625 training/webapp "python app.py" 18 seconds ago Up 17 seconds 0.0.0.0:32770->5000/tcp zcy [root@test ~]#
新建网络
下面先创建一个新的 Docker 网络。
[root@test ~]# docker network create -d bridge test-net e018c428af76d1a5c59f8eff9378ce879994d01410b67869e37be9e108e12e2d [root@test ~]# [root@test ~]# docker network ls NETWORK ID NAME DRIVER SCOPE 043149dce669 bridge bridge local 13ab05d065ce host host local e1c4373b7768 none null local e018c428af76 test-net bridge local [root@test ~]#
参数说明:
-d:参数指定 Docker 网络类型,有 bridge、overlay。
其中 overlay 网络类型用于 Swarm mode,在本小节中你可以忽略它。
[root@test ~]# ifconfig br-e018c428af76: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255 ether 02:42:6d:1c:33:bc txqueuelen 0 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 4 bytes 216 (216.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
连接容器
运行一个容器并连接到新建的 test-net 网络:
[root@test ~]# docker run -itd --name test1 --network test-net 231d40e811cd /bin/bash a02f3a413518f96c054892bc1638a7498078b0680f905f46d1737bef17ad86fa [root@test ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a02f3a413518 231d40e811cd "/bin/bash" 20 seconds ago Up 19 seconds 80/tcp test1
打开新的终端,再运行一个容器并加入到 test-net 网络:
[root@test ~]# docker run -itd --name test2 --network test-net 231d40e811cd /bin/bash 33f86cb5e16bead47f8d5362f75e34f0b79562daf2d2278b6cefb8b1406d66d8 [root@test ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 33f86cb5e16b 231d40e811cd "/bin/bash" 11 seconds ago Up 11 seconds 80/tcp test2
下面通过 ping 来证明 test1 容器和 test2 容器建立了互联关系。
如果 test1、test2 容器内中无 ping 命令,则在容器内执行以下命令安装 ping(即学即用:可以在一个容器里安装好,提交容器到镜像,在以新的镜像重新运行以上俩个容器)。
apt-get update apt install iputils-ping[root@test ~]# docker exec -it test1 /bin/bash root@a02f3a413518:/# ping bash: ping: command not found root@a02f3a413518:/# apt-get update Get:2 http://deb.debian.org/debian buster InRelease [122 kB] Get:3 http://deb.debian.org/debian buster-updates InRelease [49.3 kB] Get:4 http://deb.debian.org/debian buster/main amd64 Packages [7908 kB] Get:1 http://security-cdn.debian.org/debian-security buster/updates InRelease [65.4 kB] Get:5 http://security-cdn.debian.org/debian-security buster/updates/main amd64 Packages [165 kB] Get:6 http://deb.debian.org/debian buster-updates/main amd64 Packages [5792 B] Fetched 8315 kB in 9s (961 kB/s) Reading package lists... Done root@a02f3a413518:/# apt install iputils-ping Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: libcap2 libcap2-bin libpam-cap The following NEW packages will be installed: iputils-ping libcap2 libcap2-bin libpam-cap 0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded. Need to get 104 kB of archives. After this operation, 315 kB of additional disk space will be used. Do you want to continue? [Y/n] Y Get:1 http://deb.debian.org/debian buster/main amd64 libcap2 amd64 1:2.25-2 [17.6 kB] Get:2 http://deb.debian.org/debian buster/main amd64 iputils-ping amd64 3:20180629-2 [43.0 kB] Get:3 http://deb.debian.org/debian buster/main amd64 libcap2-bin amd64 1:2.25-2 [28.8 kB] Get:4 http://deb.debian.org/debian buster/main amd64 libpam-cap amd64 1:2.25-2 [14.3 kB] Fetched 104 kB in 1s (153 kB/s) debconf: delaying package configuration, since apt-utils is not installed Selecting previously unselected package libcap2:amd64. (Reading database ... 7203 files and directories currently installed.) Preparing to unpack .../libcap2_1%3a2.25-2_amd64.deb ... Unpacking libcap2:amd64 (1:2.25-2) ... Selecting previously unselected package iputils-ping. Preparing to unpack .../iputils-ping_3%3a20180629-2_amd64.deb ... Unpacking iputils-ping (3:20180629-2) ... Selecting previously unselected package libcap2-bin. Preparing to unpack .../libcap2-bin_1%3a2.25-2_amd64.deb ... Unpacking libcap2-bin (1:2.25-2) ... Selecting previously unselected package libpam-cap:amd64. Preparing to unpack .../libpam-cap_1%3a2.25-2_amd64.deb ... Unpacking libpam-cap:amd64 (1:2.25-2) ... Setting up libcap2:amd64 (1:2.25-2) ... Setting up libcap2-bin (1:2.25-2) ... Setting up libpam-cap:amd64 (1:2.25-2) ... debconf: unable to initialize frontend: Dialog debconf: (No usable dialog-like program is installed, so the dialog based frontend cannot be used. at /usr/share/perl5/Debconf/FrontEnd/Dialog.pm line 76.) debconf: falling back to frontend: Readline debconf: unable to initialize frontend: Readline debconf: (Can't locate Term/ReadLine.pm in @INC (you may need to install the Term::ReadLine module) (@INC contains: /etc/perl /usr/local/lib/x86_64-linux-gnu/perl/5.28.1 /usr/local/share/perl/5.28.1 /usr/lib/x86_64-linux-gnu/perl5/5.28 /usr/share/perl5 /usr/lib/x86_64-linux-gnu/perl/5.28 /usr/share/perl/5.28 /usr/local/lib/site_perl /usr/lib/x86_64-linux-gnu/perl-base) at /usr/share/perl5/Debconf/FrontEnd/Readline.pm line 7.) debconf: falling back to frontend: Teletype Setting up iputils-ping (3:20180629-2) ... Processing triggers for libc-bin (2.28-10) ... root@a02f3a413518:/# ping Usage: ping [-aAbBdDfhLnOqrRUvV64] [-c count] [-i interval] [-I interface] [-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos] [-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option] [-w deadline] [-W timeout] [hop1 ...] destination Usage: ping -6 [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface] [-l preload] [-m mark] [-M pmtudisc_option] [-N nodeinfo_option] [-p pattern] [-Q tclass] [-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option] [-w deadline] [-W timeout] destination root@a02f3a413518:/#
提示:test2容器操作一样,安装一下即可。
在 test1 容器输入以下命令:
[root@test ~]# docker exec -it test1 /bin/bash root@a02f3a413518:/# ping test2 PING test2 (172.18.0.3) 56(84) bytes of data. 64 bytes from test2.test-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.162 ms 64 bytes from test2.test-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.073 ms 64 bytes from test2.test-net (172.18.0.3): icmp_seq=3 ttl=64 time=0.072 ms 64 bytes from test2.test-net (172.18.0.3): icmp_seq=4 ttl=64 time=0.074 ms 64 bytes from test2.test-net (172.18.0.3): icmp_seq=5 ttl=64 time=0.072 ms 64 bytes from test2.test-net (172.18.0.3): icmp_seq=6 ttl=64 time=0.073 ms 64 bytes from test2.test-net (172.18.0.3): icmp_seq=7 ttl=64 time=0.071 ms ^C --- test2 ping statistics --- 7 packets transmitted, 7 received, 0% packet loss, time 9ms rtt min/avg/max/mdev = 0.071/0.085/0.162/0.032 ms root@a02f3a413518:/#
同理在 test2 容器也会成功连接到:
[root@test ~]# docker exec -it test2 /bin/bash root@33f86cb5e16b:/# ping test1 PING test1 (172.18.0.2) 56(84) bytes of data. 64 bytes from test1.test-net (172.18.0.2): icmp_seq=1 ttl=64 time=0.136 ms 64 bytes from test1.test-net (172.18.0.2): icmp_seq=2 ttl=64 time=0.089 ms 64 bytes from test1.test-net (172.18.0.2): icmp_seq=3 ttl=64 time=0.072 ms 64 bytes from test1.test-net (172.18.0.2): icmp_seq=4 ttl=64 time=0.084 ms 64 bytes from test1.test-net (172.18.0.2): icmp_seq=5 ttl=64 time=0.072 ms ^C --- test1 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4ms rtt min/avg/max/mdev = 0.072/0.090/0.136/0.025 ms root@33f86cb5e16b:/#
这样,test1 容器和 test2 容器建立了互联关系。
如果你有多个容器之间需要互相连接,推荐使用 Docker Compose,后面会介绍。
配置 DNS
我们可以在宿主机的 /etc/docker/daemon.json 文件中增加以下内容来设置全部容器的 DNS:
如果没有daemon.json文件的话,可以手动加上。
[root@test ~]# ll /etc/docker/ total 4 -rw------- 1 root root 244 Oct 16 11:17 key.json [root@test ~]# [root@test ~]# [root@test ~]# touch /etc/docker/daemon.json [root@test ~]# chmod 755 /etc/docker/daemon.json [root@test ~]# [root@test ~]# [root@test ~]# vim /etc/docker/daemon.json { "dns" : [ "114.114.114.114", "8.8.8.8" ] } [root@test ~]# service docker restart Redirecting to /bin/systemctl restart docker.service [root@test ~]# [root@test ~]# docker run -it --rm 231d40e811cd cat etc/resolv.conf --说明:--rm Automatically remove the container when it exits nameserver 114.114.114.114 nameserver 8.8.8.8 [root@test ~]#
完成。