Docker学习笔记(四)-docker中的网络与存储
前言
要了解docker的网络和存储,首先需要知道docker的资源隔离机制—namespace,让某个特定的全局系统资源通过抽象方法使namespace 中的进程看起来拥有它们自己的隔离的全局系统资源实例(The purpose of each namespace is to wrap a particular global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource. )。Linux 内核从版本 2.4.19 开始陆续引入了 namespace 的概念。通过Network namespace 每个容器用有其独立的网络设备,IP 地址,IP 路由表,/proc/net 目录,端口号等等。
网络namespace
在linxu环境下我们可以通过ip a
命令查看网络,通过ip netns list
可以查看宿主机的网络namespace情况。
每启动一个新的容器,都会创建对应的namespace,不同namespace之间通过veth pair可以互相联通,如图:
我们假设宿主机有两个网络接口eth0、eth1,test1和test2是两个独立的namespace,二者可以通过veth pair这个桥梁进行互联,而Docker中的容器之间的连接类似,在了解docker网络机制之前,我们可以先还原一下不同的namespace之间连接建立的过程:
开始之前需要了解ip命令:
Ip命令的语法:ip [OPTIONS] OBJECT [COMMAND [ARGUMENTS]]
OPTIONS
:
OPTIONS是一些修改ip行为或者改变其输出的选项常用的有:
-V,-Version: 打印ip的版本并退出。
-s,-stats,-statistics: 输出更为详尽的信息。如果这个选项出现两次或者多次,输出的信息将更为详尽。
-o,-oneline 对每行记录都使用单行输出,回行用字符代替。如果你需要使用wc、grep等工具处理ip的输出,会用到这个选项。
-r,-resolve 查询域名解析系统,用获得的主机名代替主机IP地址.OBJECT
:
OBJECT是要操作的对象,如:
link: 网络设备
address: 一个设备的协议(IP或者IPV6)地址
neighbour: ARP或者NDISC缓冲区条目
route: 路由表条目
rule: 路由策略数据库中的规则
maddress: 多播地址
mroute: 多播路由缓冲区条目
netns:管理网络空间
下面我们还原一下不同的namespace之间连接建立的过程:
1. 首先创建两个不同的网络namespace
ip netns add test1
ip netns add test2
ip netns list # 可以查看当前的网络namespace
2. 添加veth pair
ip link add veth-test1 type veth peer name veth-test2
### 查看当前的ip设备情况
ip link
可以看到我们添加的veth对信息:
3. 设置veth-test1 veth-test2的namespace
ip link set veth-test1 netns test1
ip link set veth-test2 netns test2
### 查看test2的网络设备(test1同理)
ip netns exec test2 ip link
此时我们相当于给test1和test2之间建立了一条veth pair通道,通过这个通道连接了两个“容器”,但是仔细看它们的状态是DOWN,接下来我们需要将其up起来
4. 给veth-test1 veth-test2配置ip地址
ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1
ip netns exec test1 ip addr add 192.168.1.2/24 dev veth-test2
### 查看ip地址配置情况(test1同理)
ip netns exec test2 ip a
这里可以看到ip信息:
5. 将veth-test1 veth-test2启动
ip netns exec test1 ip link set dev veth-test1 up
ip netns exec test2 ip link set dev veth-test2 up
再次查看状态发现已经启动:
6. 查看两个“容器”互通情况,通过test2去ping test1,可以ping通
ip netns exec test2 ping 192.168.1.1
以上我们还原了两个namespace之间进行通信建立的过程。
容器中的网络模式
在docker中主要有三种网络模式,我们通过docker network ls
可以查看:
通过ip a
命令我们可以查看当前的网络(我的是阿里云):
可以看到除了lo回环网口和eth0外,这里还有一个docker0,这个接口可以理解为docker容器与外部通信的桥梁,从容器角度看,docker0 对于运行在同一个主机上的各个容器来说是二层交换机的角色,docker0 自身也具有MAC地址,并且绑定了IP,因此在容器中还可以作为默认网关存在。
(1)bridge网络
在容器启动的时候,会在外部创建一个vethxxxxxa(可以通过ip a
查看),这个veth与外部主机通过桥接方式(docker0)进行连接,容器内部也会创建vethxxxxxb(可以进入容器内部后通过ip a
查看),vethxxxxxb和外部vethxxxxxa组成一对veth pair进行通信,如图:
这样就解决了容器之间的通信,那么容器又是怎么访问外网的呢?答案是NAT(网络地址转换),如图:
通过网络地址转换技术从宿主机的eth0网口就可以连接外网了,关于NAT不是本文重点,可以参考网上资料了解。(有时候我们不需要记住容器的ip,这样会带来很多额外的工作,通过link命令可以将容器通过容器名称这种容易记住的方式进行网络的连接)
docker run -d --name test2 --link test1 xxx/xxx
这样在test2中可以不用使用具体的ip而直接使用test1就可以进行连接(相当于添加一条dns记录)
我们也可以创建自己的bridge:
docker network create -d bridge mybridge
在创建容器的时候就可以通过–network指定我们创建的bridge,默认使用docker0的:
docker run -d --name test3 --network mybridge xxx/xxx
通过connect
命令也可以实现:
docker network connect mybridge test3
(2)host网络
host网络主要给我们提供了类似端口转发(映射)的功能,使我们将容器服务暴露给外部端口,实现通过访问外部端口可以访问容器内部服务的能力:
docker run -d -p 80:8080 --name test4 xxx/xxx
通过-p
参数我们将容器的8080端口映射到了宿主机的80端口,访问宿主机的80端口就相当于访问容器内部服务。
(3)none网络
不能通过外部访问,一般用来存储密码等安全性较高的东西,只能通过本地访问
(4)多机器通信
前面说的都是在同一个linux机器下容器之间的通信,那么在不同的机器下不同的容器如何通信呢,如图:
目前主要有以下几种通信方式:
- 使用openvswitch 搭建 xvlan协议隧道
- 将多个物理机的容器组到一个物理网络,这需要在每台机器上创建自己的网桥br0,然后将docker默认网桥绑定到br0
- 使用docker的swarm集群
- 使用docker的overlay网络
以后会再深入研究这几种方式。
容器的存储
container是可读可写的,但是image是只读的,我们在容器中的数据如果不想丢失,需要进行持久化存储,目前docker的持久化存储主要通过Data Volume。
Volume类型有两种:
- 受管理的data Volume,由docker后台自动创建
- 绑定挂载的Volume,由用户指定
有两种持久化方案:
1.基于本地文件系统的Volume。
在执行docker create或docker run时,通过-v
参数将主机的目录作为容器的数据卷,这部分功能基于本地文件系统的volume管理,
更好的方式是使用bind mouting挂载的方式进行数据同步(无论在容器内外修改文件都会改变,相当于操作同一个目录),命令如下:
docker run -v /home/xxx:/root/yyy
2.基于plugin的Volume。
使用第三方存储,比如NAS,aws