文章

Docker实验四:Docker 容器互联

实验四 Docker 容器互联

1 网络端口映射

首先,创建了一个 Python Web 应用的容器。

1
2
3
cd ~/course/docker/webapp/webapp
docker run -d -P training/webapp python app.py
docker ps -l

通过 -P 参数创建一个容器,使用 docker ps 可以看到容器端口 5000 绑定主机端口 49153。 使用地址:localhost:49153访问容器内的Web服务。

也可以使用 -p 指定容器端口所绑定的主机端口。

两种方式的区别是:

  • -P :容器内部端口随机映射到主机端口。
  • -p :容器内部端口绑定到指定的主机端口。
1
2
docker run -d -p 80:5000 training/webapp python app.py
docker ps -l

通过chrome浏览器访问容器服务,由于80是Web服务的默认端口,因此可以直接输入IP地址访问服务。 注意:本地的IP地址可以直接用localhost代替。

另外,可以指定容器绑定的网络地址,比如绑定 127.0.0.1。

1
2
docker run -d -p 127.0.0.1:5001:5000 training/webapp python app.py
docker ps -l

这样就可以通过访问 127.0.0.1:5001 来访问容器的 5001 端口。

上面的例子中,默认都是绑定 tcp 端口,如果要绑定 UDP 端口,可以在端口后面加上 /udp。 例如:docker run -d -p IP:端口:Docker端口/udp example/example_app python example.py -P 和 -p 选项使得在容器外部通过宿主机可以访问容器内部的服务,例如对于主机 H 和容器 A,通过 -P 或 -p 进行端口映射后, 可以通过主机 H 的 IP 地址和所映射的端口号来访问服务。 对于容器B,如果容器 B 到主机 H 的网络是互通的,也可以通过 H 的IP地址和端口号来访问服务。 下面演示上述访问过程,验证操作包括以下几部分:

  • 查看宿主机的IP地址
  • 使用Ubuntu镜像启动容器作为容器 B
  • 为容器A安装wget工具,需要分别执行:apt-get updateapt-get install wget两条命令
  • 使用命令 wget IP:32771 获取容器 A 内的Web服务的页面 index.html 。
  • 使用命令 cat index.html 查看页面内容 。

上述步骤的操作过程如下:

通过查询本机的ip为192.168.52.131(由于使用的是虚拟机,所以会是内网IP)

1
2
3
4
5
6
7
8
9
docker run -itd ubuntu #启动容器B
docker ps -l
docker exec -it e0c7e0884bf6 /bin/bash
apt-get update
apt-get install wget
apt install iputils-ping #安装ping命令
ping 192.168.52.131
wget 192.168.52.131
cat index.html

可以看到,

  • 容器B可以正常访问容器A中的服务,即文件index.html可以正常下载。
  • cat index.html 也正确地输出了容器A中的Web服务的首页内容。

然而,上述操作存在的问题是,容器A在访问容器B最好的方式是直接访问,而不是先绕到宿主机上,然后再绕到容器A上。同时,假如容器A中的服务只被容器B使用,将容器A的服务暴露在宿主机上也不是一种安全的做法。

因此,需要一种,在容器B内直接访问容器A中的服务的方法,而且最好可以使用容器名称,而不是IP地址。

事实上,Docker提供了一种简便的容器互联的方式:–link

容器的互联是一种让多个容器中的应用进行快速交互的方式。它会在源和接收容器之间创建连接关系,接收容器可以通过容器名快速访问到源容器,而不用指定具体的IP地址。

连接系统依据容器的名称来执行。因此,首先需要自定义一个好记的容器命名。虽然当创建容器的时候,系统默认会分配一个名字,但自定义命名容器有两个好处:

  • 自定义的命名,比较好记,一目了然,使用–name标记可以为容器自定义命令。
  • 当要连接其他容器时候(即便重启),也可以使用容器名而不用改变

注: 容器的名称是唯一的。如果已经命名了一个叫web的容器,当再次使用web这个名字的时候,需要先docker rm命令删除之前创建的同名容器。 使用–link 参数可以让容器之间安全的进行交互。–link参数的格式为: –link name:alias 其中,name是要链接的容器的名称,alias是别名。 接下来,使用–link的方式实现容器B到容器A的访问,

1
2
3
4
5
6
7
8
9
docker run -d --name web training/webapp python app.py
docker run -itd --link web:pyweb ubuntu
docker ps -l
docker exec -it fc185e95a931 /bin/bash
apt-get update
apt-get install wget -y
apt install iputils-ping -y #安装ping命令
wget pyweb:5000
cat index.html

在上述过程中,首先使用training/webapp创建了容器web(作为容器A),然后使用ubuntu镜像创建了容器B,在容器B内安装wget。 安装完毕后,访问容器A的web服务,可以看到正确地获取到了index.html文件,且文件中的内容正确。 Docker相当于在两个互联的容器之间创建了一个虚拟通道,而且不用映射他们的端口到宿主主机上。在启动db容器的时候并没有使用-p和-P标记,从而避免了暴露数据库服务端口到外部网络上。

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

  • 更新环境变量;
  • 更新/etc/hosts文件。

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

1
docker run --rm --name test2 --link web:pyweb ubuntu env

其中PYWEB_PORT开头的环境变量是提供给 test2 容器连接 web 容器使用的环境变量。前缀采用大写的连接别名。 上述命令中的–rm表示容器运行完毕后,删除容器。 除了环境变量,Docker还添加host信息到 test2 容器的/etc/hosts的文件中。

1
docker run --rm --name test2 --link web:pyweb ubuntu cat /etc/hosts

这里有两个host信息,一个是 test2 容器,test2 容器用自己的id 176bd8985190 作为默认主机名,另一个是 web 容器的IP和主机名(226377a53e4d)。

可以连接多个容器到一个容器,例如有容器A1、A2、A3,可以通过–link A1:A1 –link A2:A2 –link A3:A3将A1~A3都连接到容器B。

3 Docker 网络

接下来介绍如何使用Docker网络实现多个容器之间的互联互通。

将学习以下内容:

  • 如何创建Docker网络。
  • 如何将容器连接网络。
  • 如何为容器配置DNS。

3.1 新建网络

下面先创建一个新的 Docker 网络。

1
docker network create -d bridge mynet

参数说明:

  • -d:参数指定 Docker 网络类型,有 bridge、overlay。

其中 overlay 网络类型用于 Swarm mode,本实验中可以先不考虑 overlay 模式。

使用 docker network ls 可查看当前的Docker网络列表。可以看到mynet已成功创建,mynet的网络ID为:55f6a336932e。

3.2 连接容器

运行两个ubuntu容器并连接到新建的 mynet 网络:

1
2
3
docker run -itd --name test1 --network mynet ubuntu 
docker run -itd --name test2 --network mynet ubuntu 
docker ps

下面通过 ping 来证明 test1 容器和 test2 容器建立了互联关系。

由于test1和test2容器中没有ping命令,可以先制作一个安装了ping和wget这两个工具的Ubuntu镜像,操作过程如下:

1
2
3
4
5
6
7
8
9
10
11
docker run -itd --name test --network mynet ubuntu 
docker exec -it test /bin/bash
apt-get update -qqy
apt-get install iputils-ping -qqy
apt-get install wget -qqy
which ping
which wget
exit
docker ps -l
docker commit 2a839a2cb497 jojo/ubuntu_net:v1.0 
docker images

jojo/ubuntu_net就是集成了ping和wget的新镜像。

接着使用jojo/ubuntu_net创建两个容器test1和test2,并连接到mynet,然后测试是否互通。

操作命令如下:

1
2
3
4
5
6
7
docker run -itd --name test1 --network mynet jojo/ubuntu_net:v1.0 
docker run -itd --name test2 --network mynet jojo/ubuntu_net:v1.0 
docker exec -it test1 /bin/bash
ping test2 
exit
docker exec -it test2 /bin/bash
ping test1

由此可见,连接到mynet的test1和test2之间的网络是互通的。因此,test1 容器和 test2 容器建立了互联关系。

注意:在ping命令执行过程中,按CTRL+C可退出执行。

如果有多个容器之间需要互相连接,推荐使用 Docker Compose,后续实验会详细介绍。

3.3 设定网段和IP

创建网络时,可以指定网段和子网掩码。

首先把之前的网络和容器清除了。

1
2
3
docker stop $(docker ps -aq)
docker rm $(docker ps -aq)
docker network rm mynet

创建网络,指定网段和掩码。然后,创建容器,并手工指定IP地址:

1
2
3
docker network create --subnet=172.19.0.0/16 mynet
docker run -itd --name test1 --network mynet --ip 172.19.1.1 jojo/ubuntu_net:v1.0
docker run -itd --name test2 --network mynet --ip 172.19.1.2 jojo/ubuntu_net:v1.0 

设定mynet的网段为:172.19.0.0/16,为test1和test2分别分配的ip为172.19.1.1和172.19.1.2。

分别查看test1和test2的ip,并测试连通性。

1
2
3
4
5
6
7
8
docker exec -it test1 /bin/bash 
hostname -I
ping -c 3 test2 
exit
docker exec -it test2 /bin/bash
hostname -I
ping -c 3 test1
exit

可以通过 hostname -I 查看ip,也可以通过ifconfig查看,使用ifconfig时会找不到命令的错误。可以通过命令 apt-get install net-tools 安装网络工具后解决。

可以看出,test1和test2的地址为设定的地址,而且可以互通。

3.4 配置 DNS

如果需要为指定的容器设置 DNS,则可以使用以下命令:

1
2
3
docker run -it --rm --hostname=ubuntu_test jojo/ubuntu_net:v1.0 cat /etc/hosts
docker run -it --rm --dns=114.114.114.114 jojo/ubuntu_net:v1.0 cat /etc/resolv.conf
docker run -it --rm --dns=114.114.114.114 --dns=8.8.8.8 --hostname=ubuntu_test jojo/ubuntu_net:v1.0 cat /etc/resolv.conf

参数说明:

  • –hostname=HOSTNAME:设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts。也可以使用 -h HOSTNAME 。
  • –dns=IP_ADDRESS:添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。

如果在容器启动时没有指定 –dns ,Docker 会默认用宿主主机上的 /etc/resolv.conf 来配置容器的 DNS。

本文由作者按照 CC BY 4.0 进行授权