1. 网络常用命令
ip addr#查看ip地址
ip a
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:70:df:b0:c5 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:70ff:fedf:b0c5/64 scope link
valid_lft forever preferred_lft forever
如何测试端口的连通性?可以使用telnet来实现,可以检查一个服务器的端口是否打开?
telnet www.baidu.com 80
Connected to 192.168.132.128.
如何追踪路径?
tracepath
如何请求web服务?
curl www.baidu.com
2. 容器网络涉及到哪些问题?
16: eth0@if17: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::42:acff:fe11:3/64 scope link
valid_lft forever preferred_lft forever
/ # exit
[root@192 ~]# ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.129 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.095 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.097 ms
64 bytes from 172.17.0.3: icmp_seq=4 ttl=64 time=0.097 ms
为什么在本地能ping通这个地址?
为什么在本宿主机上的各个容器能够互相访问
容器的IP地址为什么是这样分配的?
3. 容器间的通信网络问题
为什么容器间可以互相通信?
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:70:df:b0:c5 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:70ff:fedf:b0c5/64 scope link
valid_lft forever preferred_lft forever
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
当选定了这个网络之后,如果有容器试图加入这个网络的话,那么就会从这个具有16个主机位的网段中获取一个IP地址,然后执行分配
"Containers": {
"61eb0057c21dd80a9cf10a58d6da600d388617b70513b90d1ecc1b2f55a79049": {
"Name": "box1",
"EndpointID": "216ba94af6bd325c74fbed195c24d8a752fcc2b4321fba2f133dfda9fc755130",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"6b81c358b2160057f0674859e2330550354ac8263441843869eb1ebbf0f40c77": {
"Name": "redis",
"EndpointID": "de81d4aeb2101a4e18f8c7b84991b0bfeccb74c1ea9dc34bfd22ba9b60f14bd5",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
},
"720a52bc4d8e191dde31ff2a3283ea21d3651067f677f676b3583430362f693b": {
"Name": "box2",
"EndpointID": "dd926d2d1ad9d732a5633146ad6c99a79aa1c1c8ebcea73beb3bcb01605cb79b",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/16",
"IPv6Address": ""
}
},
这个bridge就类似于一个网桥/交换机,我们的容器加入到这个网络之后,就相当于连接到这个交换机上了,然后通过交换机进行通信
简单来理解一下这个图,图中的eth0均是网卡,其中容器内部的为虚拟网卡,而与外部通信的是实际宿主机上的网卡(当然也有可能是通过虚拟机技术虚拟出来的网卡),vethx称为是插槽,它就类似于docker0网桥中的一个插槽,它能够将容器中的网卡以插槽的形式插入到网桥中,然后网桥在与外部网卡进行通信,它可以显示Linux-Bridge,能够显示这个Bridge中插槽的占用情况
bridge name bridge id STP enabled interfaces
docker0 8000.024270dfb0c5 no veth26415ab
veth2b12f72
veth95b4392
4. 容器对外进行通信
[root@localhost ~]# ip route
default via 192.168.132.2 dev ens33 proto static metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
192.168.132.0/24 dev ens33 proto kernel scope link src 192.168.132.128 metric 100
在容器中ping www.baidu.com,发生了什么?
首先看到下面的图,假设使用Test1去ping www.baidu.com,然后通过插槽veth1将数据包发送到docker0,docker0中的状态机会判断当前数据包的去向
如果docker0发现数据包是去向本局域网的,那么就查找端口表中的IP地址,将数据包送达对应的端口
如果docker0发现数据包是去向外网的,它首先会查询本主机上的路由表上的默认路由所对应的网卡,将数据包发送到对应的网卡设备上,这个过程会经历一个NAT,这是因为dockerBridge形成的网络通常是一个私有网络,出得去,但是回不来,它会将原本的数据报的源地址包装成eth0的地址,比如说网桥中的网段是172.168.132.0/16,然后iptables中定义,如果你的源地址是这个网段下的话,那么就会统统包装成对应的eth0的网卡地址,然后发送出去
- 从外部来看,无论是通过容器内部往外面ping,还是说从宿主机直接往外面ping,它的源地址都会变成对外路由的地址
5. NAT的原理是什么?
NAT主要是用在路由器上的,主要是用来解决什么问题呢?用来解决IPV4数量不足的问题, 通常是通过私有网络+NAT模式来解决的
iptables可以通过软件的方式来实现ip地址的转换,
- Private to Public
- Public to Private
- Private to Private
只要设置合理的规则,就能够实现这几种映射
注意,docker容器可以连接多个网络,就相当于说一个容器中虚拟出来了多张网卡,然后这几张网卡通过插槽,插入到了对应的网桥上,通过网桥实现对应的传输
在容器内ping一个网段中的其他容器的名称,也可以互相ping通,这就相当于容器之间提供了一种DNS的机制,通过这种机制就能够实现容器网段内互相连通的形式
6. docker的端口转发是怎么回事?
思考一下,我们通常会这样启动一个nginx
docker container run -d --rm --name -p 8080:80 web nginx
其中-p 8080:80是指:宿主机会监听本地的8080端口,然后会将发送到本地的8080端口上的数据包转发到内部容器的80端口上,格式:外部端口:内部端口
而其原理是基于iptables来实现的,Docker在我们使用-p参数的时候,会添加一条规则,通常格式是:
tcp dpt:8080 to:172.167.0.2:80
它就是说做了一个地址绑定,它会将一个ip:port的数据包转发到ip:port上
EXPOSE?
它简单来说,就是给了一个备注,就是说你这个镜像在运行的时候,要暴露出去哪个端口
EXPOSE 80/udp#可以指定暴露端口的处理协议栈
EXPOSE 80/tcp
7. host网络模式是怎么回事?
简单来说就是说不再虚拟出来一张eth0网卡出来,而是和主机共享一个网络命名空间
使用场景:当容器内需要修改主机的网络配置的时候,就应当要使用这种模式
Linux中的网络命名空间
Linux的namespace(命名空间)技术是一种隔离技术,常用的Namespace有user namespace,process namespace,network namespace等
在Docker容器中,不同的容器通过Network namespace进行了隔离,也就是说不同的容器有各自的IP地址,路由表等,互不影响
- 创建bridge
[root@localhost ~]# brctl addbr mydocker0
[root@localhost ~]# brctl show
bridge name bridge id STP enabled interfaces
br-4bc785c32314 8000.0242c47294f2 no vethe25c480
vethede6974
docker0 8000.024270dfb0c5 no veth26415ab
veth2b12f72
veth95b4392
mydocker0 8000.000000000000 no
- 准备一个shell脚本
#!/bin/bash
bridge=$1
namespace=$2
addr=$3
#插槽的名字
vethA=veth-$namespace
vethB=eth00-$namespace
#创建网络命名空间
sudo ip netns add $namespace
#添加一个link,相当于用网线将插槽连起来
sudo ip link add $vethA type veth peer name $vethB
#将接口放入到namespace
sudo ip link set $vethB netns $namespace
#配置一个ip地址,然后up起来
sudo ip netns exec $namespace ip addr add $addr dev $vethB
sudo ip netns exec $namespace ip link set $vethB up
sudo ip link set $vethA up
#将接口加入进去
sudo brctl addif $bridge $vethA
大致就是创建了一个namespace1,创建一条link,将这条link的一个接口放到一个namespace1中,一个接口放到MyBridge中
接着namespace2也是做一样的操作
8. namespace
通过clone()在创建新进程的同时创建namespace
int clone(int (*child_finc)(void *))
linux是否只能有一个PID为1的进程?
上帝进程:init进程,它的PID为1,每新创建一个进程,都会使得PID+1
首先先说答案,可以有多个PID的进程,这是因为存在Namespace这样的一种进程组隔离技术
这种技术将原本的三层架构,也就是进程运行于操作系统内核之上,操作系统内核运行于硬件之上,变成了
进程运行于Namespace之上,Namespace运行于操作系统内核之上,操作系统内核运行于硬件之上
那么这也就意味着,namespace中的每一个进程组,都可以有一个PID=1的进程
Linux Namespace是什么?
八股文:Namespace机制提供了一种资源隔离和虚拟化的特性,PID,IPC,network等系统资源得不再是全局性的了,而是属于某个特定的namespace,每个namespace下的资源对于其他的namespace都是不可见的,因此从操作系统的层面上看,就会出现多个相同PID的进程,系统中可以同时存在多个相同PID的进程,由于资源属于不同的namespace,因此它们并不冲突,而是在用户层面上只能看到属于用户自己namespace下的资源,例如使用ps,命令只能够列出自己namespace下的进程
该功能对内核资源进行分区,使得一组进程看到一组资源,而另外一组进程看到另外一组的资源,Namespace的工作方式通过一组资源和进程设置在相同的Namespace而其作用,但是这些Namespace引用了不同的资源,资源可能存在于多个Namespace中,这些资源可以是进程ID、主机名、用户ID、文件名、与网络访问相关的名称和进程间通信
但是要注意,root namespace可以看到其他namespace下的进程,但是子namespace下的ID是从1开始的,也就是可能和其他的namespace中ID冲突,因此这里的话还做了一个映射,也就是说子namespace的进程号映射到rootnamespace中有自己的一个PID
基于namespace的容器技术如何保障安全性?
尽管目前namespace已经提供了非常多的资源隔离类型,但是依然有部分关键内容没有被完全隔离,其中包括有一些系统的关键性目录/sys、/proc等
由于同一宿主机上的所有容器都共享了主机的内核,攻击者可以利用一些特殊手段导致内核崩溃,比如说恶意占用大量CPU
Docker自身是基于Linux的多种Namespace
User Namespace主要设计用来做容器内用户和主机的用户隔离的,Docker从1.10版本开始,使用这个命名空间,从而实现了容器中的root用户映射到主机上的非root用户,安装扫描镜像的组件,与镜像仓库宿主机的内核应该要尽量安装最新的补丁
使用
Capabilities划分权限,在虚拟机内可以赋予用户所有权限,不使用--privileged以及其他特殊权限可以使用--cap-add参数使用资源限制
docker run --cpus=1 -m=2048m --pid-limit=1000
# 启动一个1核2G的容器
# --cpus:限制CPU的配额
# -m:限制内存的配额
# --pid-limit:限制容器的PID的个数
- 使用安全容器,安全容器内的每个容器都运行在一个单独的微型虚拟机中,拥有独立的操作系统和内核,并且有虚拟化层的安全隔离
为什么构建容器需要Namespace?
Mount Namespace:隔离不同的进程或者进程组看到的挂载点,实现容器内只能看到自己的挂载信息,在容器内的挂载操作不会影响主机的挂载目录,简单来说,就是在不同的进程中可以看到不同的挂载目录
什么叫做挂载?
挂载就是说将设备文件中的顶级目录连接到Linux根目录下的某一个目录(最好是空的目录),访问这个目录就相当于访问设备文件,就好像是windows中的快捷访问方式,本质应该是软连接
比如说,当你插入一个U盘的时候,这时候由于Linux系统和U盘分属于两个文件系统,因此通过Linux系统无法直接访问到U盘中的系统目录,因此在这种情况下Linux系统使用任何硬件设备,都必须建立一个连接,使得访问Linux下的文件就好像在访问U盘上的文件一样
unshare:是util-linux工具包中的一个工具,可以实现创建并且访问不同类型的Namespace
unshare --mount --fork /bin/bash
创建一个bash进程并且新建一个Mount Namespace
什么是PID Namespace
PID Namespace的作用是用来隔离进程的,你如何来验证?
可以通过对照法,就是一个通过unshare --pid --fork --mount-proc /bin/bash后查询当前命名空间的ps aux,就可以看到当前命名空间的所有进程信息了
什么是UTS Namespacet
它是用来隔离主机名的,它允许每个UTS Namespace拥有一个独立的主机名,可以使用hostname来查看
IPC是用来隔离进程间的通信的
PID Namespace和IPC Namespace一起使用可以实现同一个IPC Namespace内的进程彼此可以通信,不同的IPC Namespace的进程却不能够通信,当起来容器的时候,那么这时候容器就互不可见了
UserNamespace主要是用来隔离用户和用户组的
使用User Namespace可以实现进程在容器内拥有root权限,而在主机上只是普通用户,实现了用户组和用户组之间的隔离
NetNamespace用来隔离网络设备,IP地址和端口信息
NetNamespace可以让每个进程拥有自己独立的IP地址,端口和网卡信息
例如说主机的IP地址为172.16.4.1,容器内可以设置独立的IP地址为192.168.1.1
总而言之,通过Namespace之间的组合和搭配,可以实现同一主机系统中对进程ID,主机名,用户ID,文件名,网络和进程间通信等资源的隔离
9. 容器监控原理以及cAdvisor
- 容器是短期存活的,并且可以动态调度
- 容器的本质是进程,而不是一个完整的操作系统
- 由于容器非常轻量,因此其基本操作也会非常频繁
docker stats#命令
CAdvisor可以采集及其上所有运行的容器信息,提供了基础的查询界面和HTTP接口
docker run \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:rw \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--volume=/dev/disk/:/dev/disk:ro \
--publish=8080:8080 \
--detach=true \
--name=cadvisor \
--privileged=true \
google/cadvisor:latest
这些监控数据是怎么来的?
Docker是基于Namespace、CGroups和联合文件系统实现的
CGroup不仅可以用于容器资源的限制,还可以提供容器的资源使用率
CGroup的工作目录/sys/fs/cgroup下包含了Cgroups的所有内容
当我们创建一个容器的时候,就会得到一个容器的ID,这个ID作为Docker area的存储凭证,它会将容器相关信息存储在工作目录/sys/fs/cgroup/memory/docker/ID下,其中文件memory.limit_in_bytes就存储了memory中的相关信息,/sys/fs/cgroup/memory/docker/ID
通过这个例子就可以知道,它是通过读取cgroup下的文件信息来获取的
如何查看网络的数据?
sudo cat /proc/PID/net/dev
容器的监控原理其实就是定时读取Linux主机上的相关的文件并且展示给用户
10. 如何通过CGroups机制实现资源限制?
使用不同的Namespace,可以实现容器中的进程看不到其他容器的资源,但是却无法限制容器的资源使用情况。
主要是用来限制单个docker容器可使用硬件资源
什么是CGaroups?
它是内核中的一个功能,可以实现限制进程或者进程组的资源(比如说CPU、内存、磁盘IO等)
- 资源限制:限制容器的使用上限
- 优先级控制:不同的组可以有不同的资源使用优先级
- 审计:计算控制组的资源使用情况
- 控制:控制进程的挂起或者恢复
核心概念
- 子系统(subSystem):它本质上一个内核的组件,一个子系统就代表着一类资源调度控制器,比如说内存子系统可以限制内存的使用量,进程子系统,控制了进程的调度,CPU子系统控制了CPU的使用时间,它是真正实现某一个资源限制的基础
- 控制组:表示了一组进程和一组带有参数的子系统的关联关系,这个就是说,假设需要用子系统来控制某一组进程的使用情况,那么一个子系统和这一组进程构成了一个控制组,比如说使用内存子系统限制了某一组进程的内存使用量,那么内存子系统和这一组进程称为是一个控制组
- 层次树:是由一系列的控制组按照树状结构排列组成的,简单来说,就是当一个进程组作为父进程组,一个进程组作为子进程组的情况下,子进程组自动获得了父进程组的一些属性,比如说有一个控制组C1
(限制一个进程组只能使用1GB的内存),而控制组C2希望(限制一个进程组在使用1GB内存的前提下使用2GB的磁盘),那么C2就可以作为C1的子控制组,形成了一个层次树
如何使用CGroup?
- 在CPU子系统下创建测试文件
总用量 0
-rw-r--r--. 1 root root 0 4月 3 19:57 cgroup.clone_children
--w--w--w-. 1 root root 0 4月 3 19:57 cgroup.event_control
-rw-r--r--. 1 root root 0 4月 3 19:57 cgroup.procsw
-r--r--r--. 1 root root 0 4月 3 19:57 cpuacct.stat
-rw-r--r--. 1 root root 0 4月 3 19:57 cpuacct.usage
-r--r--r--. 1 root root 0 4月 3 19:57 cpuacct.usage_percpu
-rw-r--r--. 1 root root 0 4月 3 19:57 cpu.cfs_period_us
-rw-r--r--. 1 root root 0 4月 3 19:57 cpu.cfs_quota_us
-rw-r--r--. 1 root root 0 4月 3 19:57 cpu.rt_period_us
-rw-r--r--. 1 root root 0 4月 3 19:57 cpu.rt_runtime_us
-rw-r--r--. 1 root root 0 4月 3 19:57 cpu.shares
-r--r--r--. 1 root root 0 4月 3 19:57 cpu.stat
-rw-r--r--. 1 root root 0 4月 3 19:57 notify_on_release
-rw-r--r--. 1 root root 0 4月 3 19:57 tasks
- 将进程加入到cgroup中
25055 # shell的主进程
25147
这里简单讲一下原理是什么,就是创建一个cgroup,然后将你需要绑定的PID输入到这个tasks中去,然后对应地修改相关的cpu.shares等文件内容,就可以执行实现相关进程的资源限制了
11. 剖析docker组件以及底层工作原理
docker:docker是一个二进制文件,对用户可见的操作形式为docker命令,通过docker命令可以完成所有的Docker客户端和服务端的通信,具体的流程如下:
docker组件向服务端发送请求后,服务端根据请求执行具体的动作并且将结果返回给docker,docker解析服务端返回的结果,并且将结果通过命令行标准输出展示给用户
dockerd(docker daemon):dockerd用来接收客户端发送的请求,执行具体的处理任务,处理完成后将结果返回到客户端
dockerClient和DockerServer是如何通信的?
- 在同一台机器上使用UNIX套接字通信,配置格式为
unix://socker_path,默认dockered生成的socket文件路径为/var/run/docker.sock,这个文件只有root用户才能访问,这也就是为什么初始情况下只有root用户才能访问 - 通过TCP与服务端通信,配置格式为
tcp://host:port,通过设置Docker的TLS相关参数,来保证数据传输的安全 - 通过
文件描述符的方式,配置格式为fd://,这种格式一般用于systemed管理的系统中
Docker客户端和服务端的通信形式必须保持一致,否则将无法通信,如果想要通过远程的方式访问dockerd,可以使用dockerd启动的时候添加-H参数指定监听的Host和Post
docker-init:对标于Linux系统中的init进程,它可以杀死僵尸进程或者是托管孤儿进程,在容器的内部,当业务进程没有回收子进程的能力的时候,在执行docker run 启动容器的时候可以添加--init,它具体原理是:在docker所创建的进程组中,添加1号init进程
docker-proxy:主要是用来做端口映射的,当使用docker run命令启动容器的时候,如果使用了-p参数,docker proxy组件就会把容器内相应的端口映射到主机上来,底层是基于iptables实现的
具体就是说,主机会监听本机上的端口,当有数据发送到这个端口的时候,就会根据iptables中的规则,将数据报转发到对应的IP地址上
containerd(container daememon):它包含有一个后台常驻进程,默认的socket路径为/run/containerd/containerd.sock,dockerd通过unix套接字向containerd发送请求,containerd接收到请求后负责执行指令,并且将执行结果返回给dockerd
- 镜像的管理,在容器启动前,一系列的操作,包括有在本地查询是否有镜像,在远程是否有镜像,然后根据镜像创建容器层等
- 接收dockerd的请求,通过适当的参数调用
runc启动容器 - 管理存储相关资源
- 管理网络相关的资源
containerd-shim:主要作用是将containerd和真正的容器进程解耦,使用containerd-shim作为容器进程的父进程,从而实现containerd不会影响已启动的容器进程
ctr:containerd-control,它是containerd的客户端,主要用来开发和调试,在没有dockerd的环境中,ctr可以直接向containerd守护进程发送操作容器的请求
runc:容器运行时组件,命令行可以用来直接创建和运行容器
总体来说,Docker相关的组件负责发送和接收Docker请求
Containerd相关的组件负责管理容器的生命周期
runc负责真正意义上创建和启动容器
12. 剖析Docker的网络实现和Libnetwork的底层原理
Docker定义的网络模型称为是Container Network Model
沙箱:代表一系列网络堆栈的配置,其中就包括了路由信息、网络接口等网络资源的管理
接入点:代表容器的网络接口,接入点的实现通常是
Linux的veth设备对网络:一组可以互相通信的接入点,它将多接入点组成一个子网,并且多个加入点之间可以相互通信
基本网络模式
- null空网络,可以构建一个没有网络接入的容器环境,保障数据安全
出于安全的考虑,比如说计算任务,就相当于一个没有联网的电脑,在这种模式下,Docker除了为容器创建了Net Namespace外,没有创建任何的网卡、IP地址、路由等网络配置
docker run --network=none busybox
container网络模式,可以将两个容器放在同一个网络命名空间内,但是要注意,其他资源依然是隔离的
bridge桥接模式,可以打通容器和容器之间的网络通信的需求
使用这个网络模式可以实现容器和容器之间的互通,可以从一个容器直接通过容器IP访问到另外一个容器,在容器内启动的业务,可以从主机直接请求
veth:是Linux中的虚拟设备接口,都是成对出现的,可以用来连接虚拟网络设备,比如说veth可以用来连通两个Net Namespace,从而使得两个Net Namespace之间可以互相访问
Linux bridge:是一个虚拟设备,用来连接网络的设备,可以用来转发两个Net Namespace中的流量
- host主机网络模式,可以让容器内的进程共享主机网络,从而监听或者修改主机网络
Docker容器中的进程直接共享主机的网络配置,可以直接使用
13. Docker卷与持久化的底层原理
容器按照业务类型,总体可以分成:
- 无状态的:数据不需要被持久化
- 有状态的:数据需要被持久化
卷的本质是文件或者目录,它可以绕过默认的联合文件系统,直接以文件或者目录的形式存在于宿主机上
docker run -d --name=nginx --miunt
source = mybolume,
target=/usr/local/nginx/html
nginx
容器内产生的日志需要一个专门的日志采集程序去采集日志内容
- 第一步:创建一个共享日志的数据卷
docker volume create log-vol
- 第二步:启动容器,并且挂载
docker run -d --name=log-producer --mount
source = log-vol,
target=/tmp/log
busybox
启动消费者容器
docker run -it --name consumer --volumes-from log-producer busybox
怎么实现的?
在创建Docker卷的时候,Docker会将卷的数据全部放在/var/lib/docker/volumes目录下,并且在每个对应的卷目录下创建一个_data目录,然后将_data目录绑定到容器中
因此,在容器中操作卷中的目录,就是在操作这个目录(/var/lib/docker/volumes/_data)
volumes和bind mount有什么区别?
- bind mount的src是可以由用户来指定的,而
volumes则是由/var/lib/docker/volumes/下管理的 - 当
bind mount为中的src为空的时候,它会覆盖dest,而volume则是会保留原来dest中的内容 bind mount可以挂载文件或者目录,而volume只能挂载目录
14. 什么是联合文件系统?
联合文件系统(Unionfs):是一种分层的轻量级文件系统,它可以将多个目录联合挂载到同一个目录下,从而形成一个单一的文件系统,比如说,你电脑上插入了多个U盘,这各个U盘的文件系统都是各不相同的,如果你想要各自操作这些文件系统,那么就可以建立一个联合文件系统,将这多个U盘中的目录挂载到Linux的目录上,从而形成了联合文件系统
联合文件系统是Docker镜像和容器的基础,因为它可以使得Docker可以把镜像做成一个分层的结构,从而使得镜像的每一层可以被共享
每一个目录在AUFS中都被叫做是分支,在Docker中称之为层,最终呈现给用户的则是一个普通的单层文件系统,我们将多层以单一层的方式呈现出来的过程叫做联合挂载
当一个镜像没有生成容器的时候,diff文件夹:它会存储镜像的内容,每一层中都存储在以镜像层ID命名的子文件夹中,layers文件夹:存储镜像层关系的元数据,文件的内容为该层镜像的父级镜像的ID,mnt文件夹:联合挂载点目录,当没有生成容器的时候,这个目录会是空的
当一个镜像生成容器之后,diff文件夹:当容器运行的时候,会在diff目录下生成容器层,layers会增加容器层相关的元数据,mnt文件夹:容器的联合挂载点,这和容器中看到的文件内容是一致的
- 当文件在容器层存在的时候,直接从容器层中读取
- 当文件在容器层不存在的时候,从镜像层查找该文件,然后读取该文件的内容
修改流程:基于写时复制技术实现的
- 第一次修改文件,AUFS会触发写时复制操作,AUFS会首先从镜像层复制文件到容器层,然后执行对应的修改操作
- 删除文件或者目录,AUFS会创建一个特殊的文件或者文件夹,这种特殊的文件或者文件夹会阻止容器的访问
这也就意味着并不会直接删除文件,而是会通过手段来隐藏这个文件夹
15. 通过unshare()在原先进程上进行namespace的隔离
unshre运行在原来的进程上,不需要启动一个新的进程
[root@localhost ~]# unshare --fork --pid --mount-proc bash
[root@localhost ~]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.3 0.0 115676 2084 pts/0 S 20:27 0:00 bash
root 12 0.0 0.0 155448 1856 pts/0 R+ 20:27 0:00 ps aux
这样就打开了一个完全的隔离的空间,然后我们通过,就可以看到两个进程了
docker run -it busybox
ps aux