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