1. Storage Driver
1.1 AUFS for ubuntu which has no overlay2
1.2 overlayfs
overlay: image分层比较多,促使inode非常大
各个镜像层中,每下一层中的文件以硬链接的方式出现在它的上一层中,以此类推,最终挂载overlayfs的lower dir为最上层镜像层目录imager layer N。与此同时,容器的writable dir作为upper dir,挂载成为容器的rootfs,由于是采用硬链接来串联每个镜像层,导致每个硬链接下目录文件会产生额外的inode,可能导致inode值超过设置。
Overlayfs2 采用多lower层的支持, 解决了inode过多的问题
overlay2: always recommend if driver is in kernel
Overlay2的挂载方式比Overlay的要简单许多,它基于内核overlayfs的Multiple lower layers特性实现,不在需要硬链接,直接将镜像层的各个目录设置为overlayfs的各个lower layer即可(Overlayfs最多支持500层lower dir),对比Overlay Driver将减少inode的使用
1.3 Devicemapper good for RHEL without overlay2 driver
loopback-lvm: bad performance but zero config
direct-lvm: production
1.4 btrfs if host filesystem is btrfs
1.5 vfs for testing purpose, performance is poor
2. 容器启动过程
2.1 image解压,形成一个联合FS, 挂载到指定容器目录 /var/lib/docker/devicemapper/mnt/<container_id>
2.2 docker通过内核申请mnt pid utc network等namespace
2.3 容器namespace是继承父进程的文件系统的,于是给容器目录 挂载特殊目录以及文件例如proc /etc/hosts /etc/hostnames
2.4 容器namespace里面挂载-v所指定的volune到容器目录
2.5 在容器namespace中挂载的任何操作,不影响容器外挂载点。 但是容器仍然与父进程共享非以上挂载点的文件系统
2.6 通过chroot指定容器目录为根目录,隔离容器与外界的FS
2.7 容器naspeace下, 配置网路信息
2.8 容器执行指定命令
3. Network
3.1 none
该模式关闭了容器的网络功能,在以下两种情况下是有用的:容器并不需要网络
3.2 host
与宿主机在同一个网络中,但没有独立IP地址。这个容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
3.3 container
这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信。
3.4 bridge
容器使用独立network Namespace,并连接到docker0虚拟网卡(默认模式)。通过docker0网桥以及Iptables nat表配置与宿主机通信;bridge模式是Docker默认的网络设置,此模式会为每一个容器分配Network Namespace、设置IP等,并将一个主机上的Docker容器连接到一个虚拟网桥上。
bridge使用于单个docker host的容器互联, 不适用于多个docker hosts上的container互联,请用overlay
3.5 overlay
The overlay network driver creates a distributed network among multiple Docker daemon hosts. This network sits on top of (overlays) the host-specific networks, allowing containers connected to it to communicate securely when encryption is enabled. Docker transparently handles routing of each packet to and from the correct Docker daemon host and the correct destination container.
需要backend store应用来提供服务发现和DNS解析功能 例如etcd
如果docker host 创建或加入swarm, 则不需要第三方backend,swarm实现其功能
创建一个overlay type的网络(bridge),默认不提供gateway容器连接到docker-gwbridge上,用于访问overlay以外的网络。
连接到overlay网络的容器,会有2块网络,一个连接到overlay的网络, 另外一个连接到docker-gwbridge的bridge上。
连在docker-gwbridge上的容器是不同通过bridge访问的,default的setting是icc=false
overlay 网络内的路由走overlay那块网卡
访问overlay以外的网络,走gwbridge的网关,这个网关可以通过host上的ip命令查看。
3.6 macvlan
当容器需要直连入物理网络时,可以使用Macvlan。Macvlan本身不创建网络,接下来就是在这张物理网卡上创建虚拟网卡,并为虚拟网卡指定MAC地址,实现一卡多用,在物理网络看来,每张虚拟网卡都是一个单独的接口。如果各个macvlan需要存在三层路由,用IPVlan代替MACVlan
从长远来看bridge网络与overlay网络是更好的选择,原因就是虚拟网络应该与物理网络隔离而不是共享。
3.6.1 bridge 模式
Macvlan网络流量直接使用宿主机物理网卡。容器使用docker主机物理网络。网段,掩码,网关都是用所指定网口的物理网络
3.6.2 802.1q trunk bridge模式
Macvlan网络流量使用Docker动态创建的802.1q子接口,对于路由与过虑,这种模式能够提供更细粒度的控制。指定网口虚拟出子网口去对应相应的物理vlan
4. Linux Namespace
Docker利用Linux Namespace内核技术实现容器的资源隔离。
宿主机有的资源容器进程都可以享有,但彼此之间是隔离的,同样,不同容器进程之间使用资源也是隔离的,这样,彼此之间进行相同的操作,都不会互相干扰,安全性得到保障
为了在分布式的环境下进行通信和定位,容器必须要有独立的 IP、端口和路由等,这就需要对网络进行隔离。同时容器还需要一个独立的主机名以便在网络中标识自己。接下来还需要进程间的通信、用户权限等的隔离。最后,运行在容器中的应用需要有进程号(PID),自然也需要与宿主机中的 PID 进行隔离。也就是说这六种隔离能力是实现一个容器的基础
4.1 uts
隔离hostname和域名, 为每个container有不同的hostname, 相互不影响。在网络上就可以被视为一个独立的节点,在容器中对 hostname 的命名不会对宿主机造成任何影响
4.2 mount
mount namespace 可以实现不同mount 命名空间的进程看到的文件系统层次不一样。也就是说,不同的容器,以及容器与主机之间,可以出现不同目录结构;
mount()和umount()系统调用的影响不再是全局的而只影响其调用进程指向的名称空间。所以容器A里面mount了xxx到目录yyy,容器B也看不见,当然主机的yyy 目录也不会指向xxx
4.3 network
在逻辑上是网络堆栈的一个副本,它有自己的路由、防火墙规则和网络设备network namespace 可以创建相互独立的网络栈,从而实现网络的隔离.
4.4 PID
PID namespace完成的是进程号的隔离一个进程进入新创建的PID namespace,这个进程的进程号变为1。
在Linux里面1号进程非常特殊,init进程的PID是1,容器化后,应该每个容器都有一个1以及由1衍生的子进程和子进程的子进程,1号进程用于管理器子进程的监控‘回收等特性
4.5 IPC
隔离IPC, 每个container 有自己的IPC资源。例如共享内存 信号量,共享队列。IPC相关知识 可以google
4.6 user
同样一个用户的 user ID 和 group ID 在不同的 user namespace 中可以不一样。一个用户可以在一个 user namespace 中是普通用户,但在另一个 user namespace 中是超级用户。
User namespace 可以嵌套(目前内核控制最多32层),除了系统默认的 user namespace 外,所有的 user namespace 都有一个父 user namespace,每个 user namespace 都可以有零到多个子 user namespace
5. Linux cgroups
Linux 系统中,一切皆文件。Linux 将 cgroups 实现成了文件系统,方便用户使用。mount -t cgroup, 用户可以通过读写文件的方式对cgroup进行配置。
cgroup被内核load起来后, 会在/sys/fs/cgroup下建立一些默认的hierarchy(root group), 例如cpu, memory,cpu下有cpu相关的subsystem, memory下有memory相关的subsystem。
docker会在每个root hierarchy为docker建立一个cgroup,每个容器会在docker的cgroup下载建立子cgroup。
子层次继承父层次的属性。
资源限制:cgroups 可以对任务使用的资源总额进行限制,如设定应用运行时使用内存的上限,一旦超过这个配额就发出 OOM(Out of Memory)提示。
优先级分配:通过分配的 CPU 时间片数量和磁盘 IO 带宽大小,实际上就相当于控制了任务运行的优先级。
资源统计:cgroups 可以统计系统的资源使用,如 CPU 使用时长、内存用量等,这个功能非常适用于计费。
任务控制:cgroups 可以对任务执行挂起、恢复等操作。
https://www.infoq.cn/article/docker-kernel-knowledge-cgroups-resource-isolation/
https://www.cnblogs.com/sammyliu/p/5886833.html
6.1 Task
任务就是系统的一个进程。一个任务可以是多个 cgroup 的成员,但是这些 cgroup 必须在不同的层级
6.2 cgroup
控制族群就是一组按照某种标准划分的进程。Cgroups 中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用 cgroups 以控制族群为单位分配的资源,同时受到 cgroups 以控制族群为单位设定的限制
系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的 cgroup。
docker 会为docker 在每个hierarchy下创建一个docker的cgroup, 每个container会在docker的cgroup下创建自己的cgroup。
6.3 Hierarchy
控制族群可以组织成 hierarchical 的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性
cgroup的内核会默认创建多个hierarchy(root cgroup),每个hierarchy会加入相关的subsystem, 加入的subsystem种类决定了这个hierarchy下的cgroup所拥有的subsystem。用户可以在某个hierarchy下创建新的cgroup,cgroup里面会自动继承hierarchy的subsystem。
hierarchy是个抽象的概念,没有实际的文件目录和它对应。他决定这个root cgroup下控制能力(subsystem)
一个hierarchy可以附加多个subsystem
6.4 subsystem
一个子系统就是一个资源控制器,比如 cpu 子系统就是控制 cpu 时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。一个subsystem最多只能附加到一个hierarchy(root hierarchy)
Block IO
CPU
Memory
Device
网络流量限制
net_cls 和 tc 一起使用可用于限制进程发出的网络包所使用的网络带宽。当使用 cgroups network controll net_cls 后,指定进程发出的所有网络包都会被加一个 tag,然后就可以使用其他工具比如 iptables 或者 traffic controller(TC)来根据网络包上的 tag 进行流量控制
Docker没有直接的命令去设置网络流量,可以根据上述方法自己开发
7. Linux chroot
容器启动时,通过 chroot 命令切换根目录的挂载点,从而隔离文件系统。
docker 启动一个container,images extract出来的联合文件系统,为作为容器的根目录的挂载点。这样不同的imgae的创建的容器,根目录层次结构/context理论上是不同的,即使相同image创建的容器,有着相同的根目录层次结构,但由于extract出来到不同路径,container的根目录不会相互影响,也不会和host的文件系统产生冲突,起到隔离的效果。