二、Docker网络

1、容器的网络通信

前面已经介绍,Linux已经支持6种名称空间:UTS、MOUNT、IPC、PID、USER、NET

这里先假定使用宿主机的网卡,假设一个主机上有4块网卡,当其中一个网卡给了其中一个名称空间使用,那么这块网卡就不能提供给其他的名称空间使用了(一个设备一般只能属于一个命名空间),假设当前的名称空间数量超过了物理网卡的数量,此时就需要用到虚拟网卡模拟一组设备来使用

Linux内核级支持两种级别设备的模拟,一种是二层设备、一种是三层设备;这里说二层的,工作在链路层,能封装物理报文在各网络设备间实现报文转发的组件,而这个功能完全可以在Linux之上利用内核当中对二层虚拟设备的支持创建虚拟网卡,同时,这样的虚拟网卡很独特,每一个网络设备是成对出现的,可以模拟为一根网线的两头,其中一头可以插在一个主机之上,另一头可以插在交换机上,实现了一个主键连接到了一个交换机上

如上,在一台主机上,建立了4个容器,通过内核模拟了交换机使C1和C2之间通过S1交换机进行连接,C3和C4通过S2交换机进行连接,如果他们属于不同的网段,中间还需要通过一个路由器进行连接,路由器实现转发可以直接通过Linux内核完成,这里可以单独建立一个容器,就专门用来做数据转发的功能,如上这种仅仅是在同一台主机之上的通信,如果现在还有一个容器,在另外的一台主机之上,此时要建立通信该如何呢?

这里存在有多种方式

  • 方式一:将C1的虚拟网卡直接和物理机的网卡进行桥接,将物理网卡作为交换机来使用,绕开了这个虚拟交换机,但是这样操作的话,代价比较大,所有的容器进行桥接,都处在同一个平面网络中,很容易产生风暴,这样在隔离和管理上会非常不容易
  • 方式二:使用NAT模式,首先容器内设定的网关都到虚拟交换机S1或S2,之后宿主机上判断目标地址不是本机,需要经过物理网卡进行转发,进行一次SNAT(最后的包能够回来);同样该包到了另外一台物理机上的时候,并不能找到其内部的容器,这个时候需要DNAT,通过宿主机上的某个端口访问,以达到访问内部容器的某个端口目的,通过这样的方式是需要经由两级NAT的,这样的效率不高

显然上面两种方式都不是很好,这里出现了一种新的网络模式:叠加网络(Overlay Network ),这种方式是采用了两级三层封装,来实现数据包传输的

在上面的 Overlay network 中,用一个IP承载另一个IP,这就就称作隧道,这个和LVS中的原理是一样的

2、Docker网络介绍

在docker中,是已经提供了三种网络模式了

2.1、bridge

桥接,这里的桥接并不是指物理桥接,而是指NAT桥,这种模式是docker创建容器时候默认使用的

上面可以看到,有一个 docker0,这里的 docker0 就是一个软交换机(注意:这里是交换机),它也可以当网卡使用(既可以扮演二层的交换设备,也可以扮演二层的网卡设备),如果不给它地址,它就是交换机,如果给了它地址,它既可以是交换机,也可以是网卡

随后我们启动每一个容器,就可以给这个容器自动分配一段网卡的设备,一半在容器中,一半在 docker0 上

如上,使用 ifconfig 可以看到最后有一个 vethf6115dd 的信息,这个就是容器那端的设备,这个设备是和 docker0 进行连接的

这里可以使用 brctl show 命令进行查看

前面说到 docker 默认使用到的是 NAT 桥,此时可以在物理机上查看 iptables 规则

在docker的这种结构下,对于同一台主机创建的不同容器之间(属于同一网段),之间的通信,可以直接通过容器的IP进行(一个小的局域网),同样因为宿主机存在有 docker0,所以宿主机访问容器也是可以直接通过容器IP进行访问的,但是对于一个外部的主机想要访问这台宿主机下的容器时候,就需要用DNAT了,通过在宿主机上进行端口映射,使得外部主机能够访问到容器中的内容

但是这样的方式下,是存在一定问题的,如果宿主机下存在有多个web服务的容器,此时要通过外部进行访问的时候,如何进行DNAT呢?其实这种模式下没有很好的方法

这里换种方式进行思考,容器之间通过的是 Namespaces 进行资源隔离,那如果将两个容器的网络命名空间进行共享呢?

这样的情况下,不同的容器之间的UTS、Net、IPC是共享的,外部进行访问的时候就能够正常进行了

小结:桥接式容器一般拥有两个接口:一个环回接口和一个连接至主机上某桥设备的以太网接口;docker daemon 启动时默认会创建一个名为docker0的网络桥,并且创建的容器为桥接式容器,其以太网接口接至docker0(--network bridge 即为将容器接口添加至docker0桥);docker0桥为NAT桥,因此,桥接式容器可通过此桥接口访问外部网络,但防火墙规则阻止了一切从外部网络访问桥接式容器的请求

2.2、host

Docker使用了Linux的Namespaces技术进行了资源隔离,如PID Namespaces隔离进程,Mount Namespace 隔离文件系统,Network Namespace隔离网络等,一个 Network Namespace提供一份独立的网络环境,包括网卡、路由、iptables规则等都与其他的 Network Namespace 隔离

host 模式类似于Vmware 的桥接模式,与宿主机在统一网络中,但没有独立IP地址,一个 Docker 容器一般会分配一个独立的 Network Namespace。但如果启动容器的时候使用 host 模式,那么这个容器将不会获得一个独立的 Network Namespace,而是和宿主机共用一个 Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口

如下,容器和主机在相同的网络命名空间下面,使用相同的网络协议栈,容器可以直接使用主机的所有网络接口

2.3、none

在Docker中的第三种模式就是 none ,在这样的模式下,可以理解所创建的容器是一个孤岛,容器和宿主机之间没有“网线”连接,那么这样的容器主要用来干什么呢?比如启动一个容器,专门用来进行数据处理

小结:不参与网络通信,运行于此类容器中的进程仅能访问本地回环接口;仅适用于进程无需网络通信的场景中,例如备份、进程诊断及各种离线任务等

2.4、两个名称空间通信

对于 Network Namespace,其实完全可以手动去操作命名空间的,可以通过使用 ip 命令

使用该命令需要安装 iproute

在 ip 的命令中间,存在有 netns 的功能

对于ip netns 存在以下的功能

采用如下的方式添加网络命名空间

到此时,仅仅只是创建了两个网络名称空间,并没有将其内部的网卡进行激活

此时也只需要创建网卡对的方式对这些网络命名空间进行配置即可(使用到的命令就是 ip link

按照上面的指示进行创建,如下

上面的命令表示的就是创建了名称为 veth1.1 和 名称为 veth1.2 的两个网卡,并且两张网卡是进行连接的,类型为 veth

此时这个“网卡设备”还是在当前系统的名称空间中,现在要将这个设备挪到前面创建的 网络名称空间 中,同样使用到的是 ip link 命令

通过上面的方式,就将 veth1.2 挪到了ns1的名称空间中间去了,此时在当前shell中进行查看,就没有 veth1.2 的设备了(一个设备只能属于一个名称空间)

此时网络设备已经进入到了 ns1 的名称空间中了,在命名空间 ns1 中间也是可以进行改名的

此时将主机名称空间中的 veth1.1 分配IP

同样,将创建的名称空间 ns1 中的网卡也激活,也放到同样的网段中,这样应该就能够进行通信

如上,可以看到,两个IP之间能够正常进行通信了,同样的方式,将主机上的 veth1.1设备挪到ns2中,ns1和ns2两个名称空间也是可以进行通信的

通过上面的实验,就是手动的为两个命名空间创建了虚拟网卡,并进行了通信

3、Docker容器网络实现

默认情况下bridge网络

使用上面的方式启动的容器,可以看到没有指定使用的网络类型,这种情况下,默认使用的就是 bridge 网络

下面指定使用的就是 bridge 

可以看到,和没有指定的效果是一样的

下面指定使用的是 none

通过这样的方式创建就是一个封闭式容器

下面指定使用的是host

此时可以看到,使用到的就是宿主机的网卡信息

除了上面的操作内容外,在启动容器的时候,还能够指定一些参数完成一些特殊的功能

4、开放容器应用为外部访问

docker0 为NAT桥,因此容器一般获得的是私有网络地址;可以将容器想象为宿主机NAT服务背后的主机;如果开放容器或其上的服务为外部网络访问,需要在宿主机上为其定义DNAT规则,docker run 命令使用 -p 选项即可是实现端口映射,无须手工添加规则

-p 选项的适用格式

  • -p <containerPort>
    • 将指定的容器端口映射至主机所有地址的一个动态端口
  • -p <hostPort>:<containerPort>
    • 将容器端口 containerPort 映射到主机端口 hostPort
  • -p <ip>::<containerPort>
    • 将容器端口 containerPort 映射至主机指定ip的动态端口
  • -p <ip>:<hostPort>:<containerPort>
    • 将容器端口 containerPort 映射至主机指定ip的端口hostPort

动态端口 指随机端口,具体的映射结果可以使用 docker port 命令查看

除了上面这样的方式进行暴露端口外,还可以使用 -P 参数,这个参数是指暴露容器中所有的开放的端口(在镜像中声明的端口),例如 nginx 肯定启动80端口,但是当nginx作为服务启动的时候,并不会直接暴露这个端口,这个时候,可以使用 -P 参数(不用指定需要暴露的端口),也是可以将端口暴露出来的(这个在Dockerfile制作的时候会有相关的指令)

5、联盟式容器

联盟式容器是指使用某个已存在容器的网络接口的容器,接口被联盟内的各容器共享使用;因此,联盟式容器批次间完全无隔离

联盟式容器彼此间虽然共享同一个网络名称空间,但其它名称空间如User、Mount、等还是隔离的

联盟式容器彼此间存在端口冲突的可能性,因此,通常只会在多个容器上程序需要程序loopback接口相互通信,或对某已存的容器的网络属性进行监控时才使用这种模式的网络模型

6、docker0 网段设定

如果想要自定义docker0桥的网络属性信息,需要进行修改的是 /etc/docker/daemon.json 文件

核心选项为bip,即 bridge ip 之意,用于指定docker0桥本身的IP地址;其他选项可通过此地址计算得出

7、Docker的远程控制

前面讲到 Docker 是C/S 架构的,所以正常来说,应该是能够远程进行管理docker容器的,但是docker默认情况下是没有开启远程管理的;其默认仅监听 Unix Socket 格式的地址,/var/run/docker.sock;如果使用TCP套接字,可以采用如下的方式

这里注意,可能修改 /etc/docker/daemon.json文件之后存在有启动报错的,一般是由如下的可能
在./etc/docker/daemon.json中添加"hosts":["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]启动失败;
原因是 docker 的 socket 配置hosts出现了冲突,需要注意的是配置host也能重启但是不生效;
解决方法:
centos中找到/usr/lib/systemd/system/docker.service,将其中的ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock修改成ExecStart=/usr/bin/dockerd;
再执行systemctl reset-failed docker.service && systemctl restart docker;

在进行远程管理的时候,加上 -H 或者 --host 参数,后面跟上主机名及对应的端口号就行了

8、创建自定义桥

 通过 docker network ls 可以看到已有的网络,其实也是创建别的桥的

如上,这样就创建了一个名为 mybr0 的桥

此时通过创建的这个桥启动容器

这里注意,这里的新的网段和前面的网段不是同一个网段,之间默认情况下是不能通信的

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值