《菜农升职记》之 Docker网络
大家好呀,我是小菜~
本文主要介绍
docker 网络
微信公众号已开启,小菜良记,没关注的同学们记得关注哦!
刚结束完一个需求的小菜农刚想打开公众号《菜农曰》看看博客划划水,叮叮又响了!
“小菜农,这个测试环境的服务器连接信息,你登上去通过docker部署下你刚刚提交的分支测试下哈!”
导师程立给小菜农发来了消息
"docker是个啥玩意?之前不都是直接在服务器找个目录上传然后 nohup java -jar ... &
一套组合拳下来吗?"
小菜农顿时又自闭了,自从来到了职场真是处处碰壁!想想在学校还是三好学生真是唏嘘不已!
“等等!我记得公众号《菜农曰》写了一篇关于 docker 的,赶紧翻出来看看”
许久之后,小菜农方从文章中回过神来,“这下不得拿捏了!”。他打开服务器输入 docker ps
试了下指令
“有点意思啊!”他按照文章中的教程开始部署自己的应用项目。先是编写了自己应用的 dockerfile,然后通过 docker build
构建出自己的镜像,最后执行 docker run
来运行自己的镜像,一整套下来一气呵成,行云流水!然后便在浏览器测试了下自己的功能内容,测试通过后。小菜农便陷入了思考,docker 是一个容器,可以内嵌一个操作系统,每个容器之间是相互隔离的,那为啥我可以从外界访问容器内的内容?容器之间是如何通信的?又怎么创建自定义网络?
几个问题想的小菜农头都大了!放弃是不可能放弃的,没准这以后可以成为自己升职加薪,走上人生巅峰的垫脚石之一呢!从这里可以看出小菜农还是很有斗志的嘛~
docker 容器在安装的时候会自动在 host 上创建三个网络,小菜农看到了这句话,那要怎么查看这三个网络了,通过help
指令看看会不会有所发现?他快速地在终端敲入docker --help
“哦豁,果然有!network
不就是我想要的吗!”。机智的小菜农开始顺藤摸瓜,继而敲入docker network --help
“ ls
可以查看docker 的网络,那我来试试”
终端上正如小菜农所料,出现了他想要看的内容。“bridge、host、none?” 小菜农还是有些网络基础的,前两个看名称都有点熟悉,但是 none 是个啥玩意?什么都没有的网络吗?
。小菜农通过一番查询,没想到还真是,不由得佩服命名的开发者,这可真让人顾名思义~ 查询发现,挂在这个网络下的容器除了 lo,没有任何网卡
我们启动容器是通过 docker run
来执行的,这个指令后面我们可以附加参数,通过 --network = none
就可以指定该容器是使用 none 网络~
“那么这样一个封闭的网络有啥用,都上不了网”,小菜农嘀咕道
没错,就是要上不了网
,有些特殊的应用场景需要将网络封闭起来,往往这种场景意味着安全性要求很高,因此需要进行隔离,这种情况就可以放到 none 网络防止被人侵入。
“既然是特殊场景才需要,那我目前应该还接触不到特殊场景,pass pass!让我康康 host 是什么网络”
小菜农知道了可以通过 --network
指定网络类型,他试着启动了一个网络类型为host 的nginx来查看
小菜农看到这个网络配置不禁陷入了沉思,这怎么似曾相似?他将终端命令行往上滚了几页,终于发现为啥眼熟了!
原来他在上面的时候曾经输入 ip l
查看了宿主机的网络,你可以发现这两个配置是一模一样的!那就说明连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与宿主机的配置完全一样。那这样子的话直接使用 Docker host 网络的最大好处就是性能
了。如果容器对网络传输效率有较高要求的时候,就可以使用 host 网络,也相当于是使用宿主机的网络,那么这样做的缺陷是什么?那就是端口会冲突
,在宿主机上已经使用的网络,容器就不能再使用了!
小菜农觉得应该没那么简单,又查了下 host 网络的使用场景,不一会就有所发现了,使用 host 网络的另一个用途就是可以让容器直接配置 host 网络
,比如某些需要跨 host 的网络解决方案,其本身也是以容器的方式运行的,这些方案需要对网络进行配置,比如管理 iptables 之类的场景。
“真是丈母娘看郎,越看越爱~ ”小菜农已经有点迷上了docker,越深入才发现越有意思。“还剩下 bridge 网络类型,让我康康!”
bridge 是一种桥接模式的网络,桥接顾名思义就是一端在这里另一端在那里。小菜农又大胆猜测了,那肯定就是一端在容器上,一端在宿主机上!接下来便进行了验证
小菜农想起了一个命令 brctl
,该命令brctl 可用于设置、维护和检查linux内核中的以太网网桥配置。
无法使用 brctl 命令的童鞋可以使用以下命令安装
yum install -y bridge-utils
他通过brctl show
查看宿主机的网桥配置,发现有个命为 docker0 的网桥,然后他通过启动一个容器继续观察网桥变化:
小菜农发现 interface
的内容发生了变化!有一个新的网络接口 veth9b75794 挂在了 docker 0 上,而 veth9b75794 就是刚刚创建容器的网卡,小菜农紧接着查看了下刚创建容器的网络配置
容器中有个一个 eth0@if67
网卡,小菜农会心一笑
“嘿嘿,被我发现了吧,要不是我会点网络,还真被这忽悠住了。刚刚上面查看到的网络接口是 veth9b75794,而这里是 eth0@if67,换成刚接粗网络的童鞋肯定一头雾水,这两个怎么不一样!实际上 eth0@if67 和 veth9b75794 是一对 veth pair
,veth pair
是一种成对出现的特殊网络配置,而这就是我上面猜测的那样,网桥就是可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0@if67)在容器中,另一头(veth9b75794)挂在网桥docker0上,其效果就是将eth0@if67也挂在了docker0上”
而 172.17.0.2/16
这个 IP 也就是宿主机配置的,小菜农查看了下docker 的bridge配置
发现有个bridge网络配置的subnet就是172.17.0.0/16,并且网关是172.17.0.1。那么这个网关就是 docker 0!小菜农在宿主机输入 ifconfig'
进行验证
而这时,小菜农的脑子里浮现了一张图
到这里,小菜农已经熟悉了 docker 的三种网络类型的情况,便开始思考如何可以自定义网络,肯定存在很多场景用户需要根据业务来创建自定义网络来满足需求。在上面小菜农想要查询docker的网络配置时有输入过命令 docker network --help
,输出结果的时候小菜农有点印象其中好像有个 create 的命令,赶紧又输入一遍进行确认:
果然,docker 已经支持用户创建自定义网络。很快呀!小菜农便在终端输入了创建网络的命令:
眼尖的小菜农一眼就发现了不对劲,创建网络的时候并没有指定 DRIVER
呀!而这个时候创建出来的网络驱动默认就是 bridge
,那就说明是否还可以创建其他网络驱动的网络?小菜农再一次进行了尝试,但这次结果确认小菜农碰壁了
“难道只能创建 bridge 驱动类型的网络吗?” 小菜农嘀咕。依旧不死心便上了某搜索引擎进行结果查找。查找后发现原来Docker提供了三种 user-defined 的网络驱动,分别是 bridge
、overlay
和macvlan
。overlay
和macvlan
用于创建跨主机的网络!
“跨主机?那就是集群咯!目前导师分配给我的服务器只有一台,看来没法验证了,只能等后续权限大点再验证了,嘿嘿!”,既然无法验证其他两种,那就先将 bridge
搞明白~ 小菜农坏笑了起来!
上面已经创建了一个bridge
驱动类型的网络,那宿主机的网络配置应该发生了变化,小菜农输入了 brctl show
进行查看:
果然这个时候多了一个名为 br-76c202387b0c
的网桥,通过ifconfig
可以发现网段也已经分配了一个:
而这个应该就是新网络的网关了~ 小菜农真是越来越熟练起来,看来学习真能使人自信!
一切都如小菜农所料,172.18.0.1
就是 Docker 自动分配的 IP 网段
“但是如果自定义的网络多了起来,分配的网段肯定也是随之增加,那由 docker 分配的网段肯定是随机,不方便记忆,能不能自己指定 IP 网段”,小菜农突发奇想,输入 docker network create --help
会不会有所发现?
看到这结果,小菜农忍不住笑了起来,真有我的!这 subnet
和 gateway
不就是我想要的吗!~
这不就成了吗!而且宿主机上也有了新的网络,网段为 172.10.0.0/16
,网关为 172.10.0.1
既然创建了自己的网络,小菜农顿时成就感满满,这不得赶紧启动一个容器来试试自己的网络!
可以看到使用自己配置网络启动的容器,分配到的 IP 地址是 172.10.0.2/16
,“既然容器的 IP 都是docker 自动从 subnet中分配的,那我能不能指定一个静态IP呢?”小菜农又提出了疑问,那么只能再祭出老套路 --help
进行解决了!小菜农手脚麻利的敲下 docker run --help | grep 'ip'
查看结果
小菜农笑容逐渐变态,没想到自己掌握了 help
神器!看来可以通过 --ip xxx
来指定自己想要的IP,敲命令的手法逐渐娴熟起来,这不,一会功夫结果就出来了:
到目前为止,小菜农已经启动了三个容器
而网络之间的拓扑关系也忽然而出,小菜农连忙动手画了起来,以免忘记!
小菜农看着图发呆了一会,这图其实已经挺清晰了,nginx-custom
和 nginx-ip
之间肯定能够互相连通,小菜农验证一下猜想
结果是对的,那 nginx-default
是否能和其他两个容器相通呢?验证一下就知道了!
由此可见同一网络中的容器、网关之间是相通的,而属于不同网桥之间的容器是不能互相通信的。难道这样子就结束了吗?两个不同网络之间的容器难道真的不能相通了?小菜农不甘心就这样放弃,盯着屏幕一阵发呆,“有了!加个路由的话,那么两个网络之间应该就能通信了吧!” 如果 host 上对每个网络都只有一条路由,同时操作系统上开启了 ip forwarding,那么 host 就会形成一个路由器,挂接在不同网桥上的网络就能够通信!
小菜农通过ip r
查看宿主机上的路由表
可以看到 172.17.0.0/16
和 172.10.0.0/16
两个路由都已经定义好了,下一步就需要看下 ip forwarding 是否已经开启,小菜农快速的敲下指令 sysctl net.ipv4.ip_forward
可以看到 ip forwarding 也已经开启了。那这个时候只需要为 nginx-default
容器添加一块 c-custom
的网卡就可以通信了!万事俱备,只欠东风~
小菜农依旧通过熟悉的配方查看添加网卡的步骤
了解之后便开始动手操作
“nice,没有出错,这下应该可以通信了吧!” 小菜农嘀嘀咕咕,紧张的手颤抖的心继续敲下 ping 命令进行验证
瞪大了眼,第一次这么有成就感,没想到真的被自己折腾出来了!小菜农激动的差点站了起来,恨不得大声笑出来,但他知道,这只是自己成长的第一步,自己已经成功的跨出去了!平复下了激动的心,得出一个自己刚刚实验出的结论
两个容器要能通信,就必须要有属于同一个网络的网卡,当满足这一条件后,容器就可以通过 ip 进行交互了
结论来之不易,小菜农赶紧在自己的小本本上记录下来!尝到甜头的小菜农继续在网上进行搜索,看是否存在其他可以在容器间通信的方式,毕竟用 IP 进行通信还是属于比较基础的~ 很快,小菜农便找到了答案,有的! 容器间可以通过是那种方式进行通信
IP Docker DNS Server joined
IP 通信自己已经实验过了,接下来就要尝试其他两种方式。Docker DNS Server ?看解释是通过 容器名的方式
,在上面创建的容器,小菜农都已经指定了容器名,接下来只要进行实验就行了
果然是可以的!可以通过容器名进行访问,那就意味这可以不用记忆那么长一串的 IP 了!
前两种方式都已经实验过了,看到 joined
小菜农又有点懵了,加入 ? 小菜农继续往下看了解释
joined 容器是另一种实现容器间通信的方式
joined 容器非常特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信
“还真神奇,docker中处处充满了惊喜呀!”,小菜农随后便进行了尝试
这边值得注意的是,两个容器共享同一个网络栈,那么端口就不能冲突,因此启动的时候需要分离端口
到这里,小菜农不由得心满意足了,刚要退出服务器开始继续工作,却被自己的网页吸引过去了,网页上开着正是小菜农刚刚启动的docker项目。“咦,外界是怎么与容器进行连接的?” 小菜农再次提出了疑问,容器如何访问外界?,外界又如何访问容器? 看来还是得继续学习(划水)呀!
小菜农再次切换到服务器页面,尝试从容器内进行访问外界
其实不用尝试,小菜农也清楚,容器肯定能访问到外界,不然外部接口如何调用~ 但是为什么能访问到呢?小菜农查看了宿主机的 iptables 规则
可以看到有这么两条规则,一条是 docker 0,一条是小菜农刚刚自定义的,那它是什么意思呢?小菜农又犯迷糊了,看了旁边的肖老大,这不有现成的大佬可以问吗!肖老大有些好奇,小菜农这是接到啥需求了,怎么会问到这个问题,不过也没多想,新人总要照顾好的不是!便开始巴拉巴拉解释起来,小菜农也听得云里雾里,“这样说明白了吗?”。“啊?哦!差不多知道这个意思了,嘿嘿,谢谢肖老大”,“不客气不客气,下次遇到不会记得还来找我哈”,肖老大略微粗犷的嗓门,让小菜农倍感温馨!总结了下肖老大的解释:如果网桥 docker 0 接收到来自 172.17.0.0/16 网段的外出包,就会把它交给 MASQUERADE 处理,而 MASQUERADE 的处理方式就是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换 NAT。
既然如此,那么通过抓包应该就可以看清楚网络地址转化的过程,小菜农打算使用 tcpdump
来做一次简易抓包查看下结果,首先他起了两个终端,一个终端通过 ping www.baidu.com
来发送网络包,一个包通过 tcpdump -i docker0 -n icmp
抓取网络信息
172.17.0.2
是 nginx-default 容器的 IP,通过转包可以发现该 IP 地址是往 180.101.49.11
发送数据包,到这里小菜农完成了第一步,接受继续通过 tcpdump -i eth0 -n icmp
查看宿主机的网络流出信息
“啊哈!果然变了,这个时候ping包的原地址,变成了 ehp0 的 IP 172.26.19.234
”
弄清楚了 容器内部访问外界 后,那么接下来就是搞清楚 外界如果访问容器
小菜农记得刚刚自己启动容器后,在 PORTS
那里有显示端口,便在浏览器试了下就可以顺利访问了!小菜农又起了一个容器进行实验
启动容器后,便可以利用 32771 这个端口号进行访问,而32771这个端口是宿主机自动分配的,小菜农又试了下自己指定端口访问
指定端口后,通过自己指定的端口也是可以访问的,而这其中的缘由,小菜农也通过查询发现是通过 docker-proxy
实现的
每一个映射的端口,host 都会启动一个 docker-proxy 进程来处理访问容器的流量。
到这里,小菜农就搞懂了 Docker 中的网络使用,但是小菜农眉头一皱,今天玩的都是单机,那么在集群中网络又是如何访问呢?
不要空谈,不要贪懒,和小菜一起做个吹着牛X做架构
的程序猿吧~点个关注做个伴,让小菜不再孤单。咱们下文见!
今天的你多努力一点,明天的你就能少说一句求人的话!
我是小菜,一个和你一起变强的男人。
💋
微信公众号已开启,小菜良记,没关注的同学们记得关注哦!