容器的共享网络模型
1. 创建相关实验资源。
- 在体验实验室,单击创建资源。
- (可选)在实验室⻚⾯左侧导航栏中,单击云产品资源列表,可查看本次实验资源相关信息(例如IP地址、⽤户信息等)。 说明:资源创建过程需要1~3分钟。
2. Host网络模型简介
在上一个实验中我们学习了docker中的bridge网络模式。在bridge网络模式下,容器和宿主机网络隔离。不同网络下的容器具有不同的网络地址,这种网络模式的优点是架构比较清晰,资源具有必要的隔离。
但是这种架构的缺点是容器和容器,容器和宿主机之间的网络通讯的数据包需要在不同的虚拟网卡之间传输。这种传输需要通过操作系统的底层驱动,在网络数据量大的时候会对系统资源造成消耗。同时在当容器和外网互相访问时,需要使用NAT/DNAT修改网络数据包的地址字段。这也会对系统资源造成消耗,并且对容器的网络吞吐造成影响。
因此当我们需要在容器中部署对外网网络吞吐量很大,或者对网络延迟比较敏感的服务时,采用Bridge模式并不是最好的选择,这种情况下我们可以选择Host网络模式。
- Host网络模式
首先我们来看Docker中的Host网络模式的架构。在Host网络模式下,容器在创建之时,并不会创建虚拟网卡。而是共享使用宿主机的默认网卡,因此Host模式下,容器网卡的网络地址和宿主机网卡的网络地址一致。
在了解了Host网络模式之后,我们来看如何创建Host模式的容器。在上一个实验中我们学过可以通过docker network ls命令查看Docker网络。细心的同学可能会观察到,在Docker的默认网络中就包含一个host网络。这个网络的驱动使用的就是host。
因此当我们创建创建容器时,只需要使用--network=host参数,加入这个网络容器就会设置成Host网络模式。
docker network ls -f 'name=host' docker run -itd --name host1 --network=host busybox
- 获得宿主机网卡信息
接下来我们来验证Host网络模式中的容器和宿主机的网络设置。我们首先再宿主机中使用ip addr命令查看网卡IP。在宿主机中我们可以找到默认网卡eth0,和docker默认Bridge网络docker0。
ip addr
- 获得容器网卡配置
接下来我们通过docker exec在容器中执行ip addr。我们会发现容器和宿主机的网卡完全一致。我们尝试ping docker0网卡,发现也可以ping通。这也就验证了Host网络模式下,容器的网卡和宿主机属于共享状态。
docker exec host1 ip addr ping [docker0 IP] -c1
3. Host模型的python案例
在上一个小节中我们学习了Host网络模型的基本用法,在本小节中我们来看一个Host共享网络的案例。我们创建一个Python容器并启动一个Http服务。然后将容器连接到Host网络。然后再上一个小节中创建的host1容器中访问Python服务。
- 创建Host容器
Host网络模型和Bridge模型一样,支持多个容器接入。我们创建一个接入Host网络的Python容器host2,并在创建容器的时候通过bash -c "python -m http.server 端口号"启动python内置http服务的命令。
docker run -itd --name host2 --network=host python \ bash -c "python -m http.server 8000"
- 查询网络端口
在Host网络模型中,为了保证网络传输效率,宿主机和所有的容器都使用了相同的网卡配置。因此在Host模型中,无论是在宿主机中或是容器中启动的服务绑定了端口。端口占用会对宿主机和所有容器生效。
接下来我们在宿主机中也启动一个python内置的http服务,服务绑定端口8001。然后再容器和宿主机中通过netstat命令查看端口占用情况。会发现无论是宿主机还是容器host1,端口8000和8001都被占用了。
nohup python3 -m http.server 8001 & netstat -tunple | grep 800 docker exec host1 netstat -tunple | grep 800
- 访问容器服务
由于容器和宿主机都绑定了相同的网卡,因此即使是在服务中启动的服务,也不需要端口映射就可以在宿主机中访问。Host模式的这种设计简化了容器服务的发布流程,同时也提高了网络吞吐的效率。不过需要注意的是,由于容器和宿主机共享了网卡。因此我们要小心的分配宿主机和容器中服务的端口,以避免端口绑定冲突。
curl 127.0.0.1:8000 | head -n1
4. Container网络模型
在上面两个小节中我们接了Docker中的一种共享网络模型Host。这种模式主要用于灵活的在宿主机网卡上发布应用,或者提高网络吞吐效率。除了这种共享网络模式之外,Docker还提供了另一种共享网络模式Container。接下来我们来学习这种网络模式的特点和用法。
- 创建容器
Host网络模式下,新创建的容器共享使用宿主机的网卡。而在Container网络模式和Host模式类似,只不过新创建的容器,共享的是另一个容器的网卡。
当我们希望创建一个使用Container网络模式的容器时,我们需要在docker run中使用--network container:[已有容器名]的参数格式。
docker run -itd --name container1 busybox docker run -itd --name container2 --network=container:container1 busybox
- 查看地址
Container网络模型的容器创建完毕后,我可以在两个容器中使用ip addr命令查看网卡信息,会发现两块网卡的信息完全一致
docker exec container1 ip addr docker exec container2 ip addr
- 创建python服务
接下来我们再利用这块网卡创建一个名为python1的python容器,并在容器上启动一个python的http服务。由于docker run命令的参数较长,在此我们可以使用\分割符将命令分成几行输入。
docker run -itd --name container3 \ --network=container:container1 python \ bash -c "python -m http.server 8000"
- 访问服务
在服务启动后,由于三个容器使用了相同的网卡,因此我们在container1容器使用127.0.0.1网络地址就可以访问到另一个容器中的服务。
由于在busybox容器中没有curl命令,这种情况下我们可以使用nc命令来访问http服务。具体访问命令解释如下
- 首先我们使用exec命令进入容器的控制台。
- 在控制台中我们使用printf命令构建一个HTTP访问请求
- 然后通过|符号将请求发送给nc命令
- 在使用nc命令时,我们将IP和端口作为两个参数传入
执行后,命令返回了HTTP应答的全部信息。这也就验证了在container模型下,容器可以向访问本机服务一样,跨容器进行服务访问。
docker exec -it container1 sh
printf "GET / HTTP/1.1\r\n\r\n" | nc 127.0.0.1 8000
执行exit命令退出容器控制台。
5. Container模型的redis案例
上一个小节中我们学习了Container模式的基本用法。相比较DNS而言,使用这种方式网络地址管理会更加的方便,同时由于不同的容器共享了一块网卡,网络传输的效率也有明显的提升。
在容器的使用实践时,我们在一个容器中一般只部署一个组件。对于一些由多个组件构成的服务,我们一般会使用Container网络模式。将不同的组件发布到通一个网卡上。这样不同的组件就可以向部署在同一个系统上一样使用。
接下来我们来学习一个redis和Python两个组件组成的服务的部署案例。
- 启动redis容器
redis是一个上手容易,使用简单的内存存储服务组件。通常被用来当作web服务的缓存组件来使用。首先我们来创建一个名为redis1的redis容器。当容器创建之后,我们可以通过redis-cli命令进入redis的控制台。
docker run -d --name redis1 redis docker exec -it redis1 redis-cli
- 初始化redis数据
在redis中数据的存储结构采用的是key/value结构,在控制台中我们可以通过set [key] [value]命令存储数据。数据存储后可以使用get [key]命令读取数据。
set name aliyun set year 2000 get name get year exit
- 安装Python的redis工具
接下来我们再启动一个名为python1的python容器,并和redis1容器共享网卡。在容器创建成功后,我们在容器中使用pip install命令安装访问redis的工具。
docker run -itd --name python1 --network=container:redis1 python docker exec python1 pip install redis
- 通过Python脚本调用redis
接下来我们在python1容器中执行python命令,进入命令行模式。在命令行模式中,我们可以一条一条的输入命令。并观察命令的执行结果。
首先我们使用import redis命令加载访问工具,加载完成后,使用redis.Redis()命令创建到redis服务的连接。在创建连接时虽然python服务和redis服务不在同一个容器中,但是由于我们共享了网卡,我们还是可以使用127.0.0.1的网络地址来访问不同容器的服务。
连接创建之后,我们就可以使用keys(*)命令列出redis中所有的key,然后我们继续使用get([key名字])的命令获得key对应的value。我们发现通过pyton容器获得数据和在redis-cli中获得的数据是一致的。
docker exec -it python1 python import redis r = redis.Redis(host='127.0.0.1',port=6379,db=0) print(r.keys('*')) print(r.get('name')) print(r.get('year')) exit()
实验链接:https://developer.aliyun.com/adc/scenario/4e2e9b022d364acaa18aad01fcd750d8