Bootstrap

mydocker

Docker容器特点

  1. 轻量级:在同一台宿主机上的容器共享系统 Kerel ,这使得它们可以迅速启动而且占用内存极少。镜像是以分层文件系统构造的,这可以让它们共享相同的文件,使得磁盘使用率和镜像下载速度得到提高。
  2. 开放:Docker 容器基于开放标准,这使得 Docker 容器可以运行在主流 Linux 发行版和 Windows 操作系统上。
  3. 安全:容器将各个应用程序隔离开来,这给所有的应用程序提供了一层额外的安全防护。

容器和虚拟机比较

容器和虚拟机同样有着资源隔离和分配的优点,但是由于其架构的不同,容器比虚拟机更加便携和高效

  • 虚拟机包含用户的程序,必要的函数库和整个客户操作系统,所有的这些差不多需要占用好几个 GB 的空间。

虚拟机的架构图:
在这里插入图片描述

  • 容器包含用户的程序和所有的依赖,但是容器之间是共享 Kermel 的。各个容器在宿主机上互相隔离,并且在用户态下运行。Docker 容器不和任何基础设施绑定,它可以运行在任何电脑、IDC 和云上。

Docker 容器的架构图:
在这里插入图片描述

Linux Namespace

概念

在 Linux 操作系统中,Namespace(命名空间)是 Kernel 的一个功能,是一种用来隔离系统资源的技术。它使得多个进程可以共享同一个系统,但每个进程的环境、资源和视图是相互隔离的。它可以隔离一系列的系统资源,比如PID(Process ID)、User ID、Network等。
这种隔离为容器技术(如 Docker)提供了基础,使得多个容器能够在同一台物理主机上运行而互不干扰

虚拟PID:
在这里插入图片描述

Linux中的七种命名空间类型

1. PID Namespace(进程ID命名空间)

  • 功能:隔离进程的 PID(进程标识符)。每个 PID 命名空间有自己的进程编号空间,进程可以在其命名空间内获得不同的 PID。
  • 作用:允许同一台机器上的不同命名空间中的进程拥有相同的 PID。例如,容器内的进程可以有 PID 1(通常是容器的 init 进程),而容器外的进程可能有不同的 PID。
  • 常见用途:容器化环境中,确保容器内部的进程 PID 不会与主机或其他容器的进程 PID 冲突。

2. Mount Namespace(挂载命名空间)

  • 功能:隔离挂载点,使得每个命名空间的进程看到不同的文件系统结构。挂载命名空间使得进程可以在不同的命名空间中看到不同的目录结构。

    在Mount Namespace 中调用mount()和 umount() 仅仅只会影响当前 Namespace内的文件系统,而对全局的文件系统是没有影响的。

  • 作用:允许不同的命名空间拥有独立的挂载目录,进程可以在自己的命名空间内挂载、卸载文件系统,而不影响其他命名空间。
  • 常见用途:容器化技术中,隔离容器内的文件系统和宿主机或其他容器的文件系统。容器可以有独立的 /proc、/sys、/dev 等挂载点。

3. Network Namespace(网络命名空间)

  • 功能:隔离网络资源,包括网络接口、路由表、端口、IP 地址等。每个网络命名空间可以拥有自己的网络设备、IP 地址、端口空间等。

    Namespace 可以让每个容器拥有自己独立的(虚拟的)网络设备,而且容器内的应用可以绑定到自己的端口,每个Namespace 内的端口都不会互相冲突。在宿主机上搭建网桥后,就能很方便地实现容器之间的通信,而且不同容器上的应用可以使用相同的端口。

  • 作用:允许多个命名空间中的进程拥有独立的网络环境。例如,容器内的进程可以有独立的网络栈,包括不同的 IP 地址、网络接口、路由表等。
  • 常见用途:容器化环境中的网络隔离。每个容器可以拥有独立的网络接口、IP 地址、DNS 配置等。
  1. IPC Namespace(进程间通信命名空间)
  • 功能:用来隔离进程间通信资源,如消息队列、信号量和共享内存。每一个 IPC Namespac 有自己的 System V IPC 和 POSIX message queue。每个命名空间中的进程只能看到属于该命名空间的 IPC 资源。
  • 作用:允许不同命名空间中的进程使用自己的 IPC 资源,而不会干扰其他命名空间中的进程。
  • 常见用途:容器内部的进程可以使用不同的共享内存和信号量,而不会与主机或其他容器中的进程发生冲突。

5. UTS Namespace(UNIX时间共享命名空间)

  • 功能:主要用来隔离主机名(nodename)和域名(domainname)两个系统标识。每个命名空间中的进程可以拥有不同的主机名和域名设置,允许有自己的 hostname。
  • 作用:允许容器或虚拟环境拥有自己的主机名和域名,而不依赖于宿主机的设置。
  • 常见用途:容器化环境中,确保每个容器可以有独立的主机名和域名,而不与宿主机或其他容器共享。

6. User Namespace(用户命名空间)

  • 功能:隔离用户和组的标识符。每个命名空间中的进程可以映射不同的 UID(用户标识符)和 GID(组标识符),从而使得容器内的进程可以运行在不同的用户权限下。
  • 作用:允许容器内的进程以非特权用户身份运行,即使这些进程在宿主机上可能是特权用户(如 root)。这可以增强安全性。
  • 常见用途:增强容器安全性,容器内的进程可以以 UID 1000 运行,而宿主机上的对应用户 UID 是 0(root),从而降低安全风险。

7. Cgroup Namespace(控制组命名空间)

  • 功能:隔离控制组(cgroup),控制组用于管理进程的资源(如 CPU、内存、磁盘 I/O 等)的分配。
  • 作用:每个命名空间中的进程可以拥有自己的控制组,允许对资源使用进行独立管理。
  • 常见用途:用于容器和虚拟化环境中,管理不同命名空间中的进程的资源使用,防止某个进程消耗过多的资源影响其他进程。

在这里插入图片描述

Linux Cgroups

概念

Linux Cgroups(Control Groups)提供了对一组进程及将来子进程的资源限制、控制和统计的能力,这些资源包括 CPU、内存、存储、网络等。通过Cgroups,可以方便地限制某个进程的资源占用,并且可以实时地监控进程的监控和统计信息

Cgroups中的3个组件

  • Ocgroup
    是对进程分组管理的一种机制,一个cgroup包含一组进程,并可以在这个cgroup上增加 Linux subsystem 的各种参数配置,将一组进程和一组 subsystem的系统参数关联起来。
  • Osubsystem
    是一组资源控制的模块,一般包含如下几项。
  • blkio 设置对块设备(比如硬盘)输入输出的访问控制。
  • cpu 设置 cgroup 中进程的 CPU 被调度的策略。
  • cpuacct 可以统计 cgroup 中进程的CPU占用。
  • cpuset 在多核机器上设置 cgroup 中进程可以使用的CPU和内存(此处内存仅使用于NUMA 架构)。
  • devices 控制cgroup 中进程对设备的访问。
  • freezer 用于挂起(suspend)和恢复(resume)cgroup中的进程。
  • memory 用于控制 cgroup 中进程的内存占用。
  • net_cls 用于将 cgroup 中进程产生的网络包分类,以便Linux的 tc(trafic controller) 可以根据分类区分出来自某个 cgroup 的包并做限流或监控。
  • net_prio 设置corn程产的网流量的优先级。
  • ns 这个 subsystem 比较特殊,它的作用是使 cgroup 中的进程在新的Namespace 中fork新进程(NEWNS)时,创建出一个新的 cgroup,这个 cgroup 包含新的 Namespace 中的进程。

每个 subsystem 会关联到定义了相应限制的 cgroup 上,并对这个 cgroup 中的进程做相应的限制和控制。

  • Ohierarchy
    Ohierarchy的功能是把一组 cgroup 串成一个树状的结构,一个这样的树便是一个 hierarchy,通过这种树状结构,Cgroups 可以做到继承。比如,系统对一组定时的任务进程通过 cgroup1 限制了CPU的使用率,然后其中有一个定时dump日志的进程还需要限制磁盘IO,为了避免限制了磁盘IO之后影响到其他进程,就可以创建 cgroup2,使其继承于 cgroup1 并限制磁盘的IO,这样 cgroup2 便继承了 cgroup1 中对CPU 使用率的限制,并且增加了磁盘I〇的限制而不影响到 cgroup1 中的其他进程。

3个组件相互的关系

Cgroups是凭借这三个组件的相互协作实现的。

  • 系统在创建了新的 hierarchy之后,系统中所有的进程都会加入这个hierarchy的cgroup根节点,这个 cgroup根节点是hierarchy默认创建的。
  • 一个 subsystem 只能附加到一个 hierarchy 上面。
  • 一个 hierarchy 可以附加多个 subsystem 。
  • 一个进程可以作为多个 cgroup 的成员,但是这些 cgroup 必须在不同的 hierarchy 中。
  • 一个进程fork出子进程时,子进程是和父进程在同一个 cgroup 中的,也可以根据需要将其移动到其他 cgroup 中。

Kernel接口

Cgroups 中的 hierarchy 是一种树状的组织结构,Kernel 为了使对 Cgroups 的配置更直观,是通过一个虚拟的树状文件系统配置 Cgroups 的,通过层级的目录虚拟出 cgroup树。

1. 创建并挂载一个 hierarchy(cgroup树)

~ mkdir cgroup-test # 创建一个hierarchy 挂载点
~ sudo mount -t cgroup -o none,name=cgroup-test cgroup-test ./cgroup-test # 挂载一个hierarchy
~ ls ./cgroup-test # 挂载后就可以看到系统在这个目录下生成了一些默认文件

在这里插入图片描述
这些文件就是这个 hierarchy 中 cgroup 根节点的配置项。

hierarchy中cgroup根节点的配置项含义

cgroup.clone_children,cpuset 的 subsystem 会读取这个配置文件,如果这个值是1(默认是0),子 cgroup 才会继承父cgroup的 cpuset 的配置。

cgroup.procs 是树中当前节点 cgroup 中的进程组ID,现在的位置是在根节点,这个文件中会有现在系统中所有进程组的 ID。

notify_on releaserelease agent 会一起使用。notify_on release 标识当这个 cgroup 最后一个进程退出的时候是否执行了 release_agent;releaseagent 则是一个路径,通常用作进程退出之后自动清理掉不再使用的 cgroup。

tasks 标识该 cgroup 下面的进程 ID,如果把一个进程ID写到 tasks 文件中,便会将相应的进程加入到这个 cgroup中。

2. 创建两个子 cgroup

# 创建刚刚创建好的hierarchy上cgroup根节点中扩展出的两个子cgroup
[cgroup-test] sudo mkdir cgroup-1 # 创建子cgroup "cgroup-1"
[cgroup-test] sudo mkdir cgroup-2 # 创建子cgroup "cgroup-2"

cgroup-test:
在这里插入图片描述
cgroup-1:
在这里插入图片描述
cgroup-2:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到,在一个 cgroup 的目录下创建文件夹时,Kernel会把文件夹标记为这个 cgroup 的子 cgroup,它们会继承父 cgroup 的属性。

3. 在 cgroup 中添加和移动进程

一个进程在一个 Cgroups 的 hierarchy 中,只能在一个 cgroup 节点上存在,系统的所有进程都会默认在根节点上存在,可以将进程移动到其他 cgroup 节点,只需要将进程ID写到移动到的 cgroup 节点的 tasks 文件中即可。
在这里插入图片描述
可以看到,当前的19696进程已经被加到 cgroup-test:/cgroup-1 中了。

4. 通过 subsystem 限制 cgroup 中进程的资源

在上面创建 hierarchy的时候,这个hierarchy并没有关联到任何的subsystem,所以没办法通过那个hierarchy中的cgroup节点限制进程的资源占用,其实系统默认已经为每个subsystem 创建了一个默认的 hierarchy,比如memory 的 hierarchy。
在这里插入图片描述
可以看到, /sys/fs/cgroup/memory 目录便是挂在了 memorysubsystem 的 hierarchy 上。下面,就通过在这个 hierarchy 中创建 cgroup,限制如下进程占用的内存。
在这里插入图片描述

Docker 使用 Cgroups

Docker 是通过Cgroups 实现容器资源限制和监控的,下面以一个实际的容器实例来看一下 Docker 是如何配置 Cgroups 的。
在这里插入图片描述
在这里插入图片描述

Docker通过为每个容器创建cgroup,并通过cgroup去配置资源限制和资源监控。

Union File System

Union File System,简称 UnionFS,是一种为Linux、FreeBSD 和 NetBSD 操作系统设计的把其他文件系统联合到一个联合挂载点的文件系统服务。它使用branch把不同文件系统的文件和目录“透明地”覆盖,形成一个单一一致的文件系统。
这些 branch 或者是 read-only 的,或者是 read-write 的,所以当对这个虚拟后的联合文件系统进行写操作的时候,系统是真正写到了一个新的文件中。看起来这个虚拟后的联合文件系统是可以对任何文件进行操作的,但是其实它并没有改变原来的文件,这是因为 unionfs 用到了一个重要的资源管理技术,叫写时复制

写时复制

写时复制(copy-0n-write,简称CoW),也叫隐式共享,是一种对可修改资源实现高效复制的资源管理技术。它的思想是,如果一个资源是重复的,但没有任何修改,这时并不需要立即创建一个新的资源,这个资源可以被新旧实例共享。创建新资源发生在第一次写操作,也就是对资源进行修改的时候。通过这种资源共享的方式,可以显著地减少未修改资源复制带来的消耗,但是也会在进行资源修改时增加小部分的开销。

举例
Knoppix,一个用于Linux演示、光盘教学和商业产品演示的 Linux发行版,就是将一个CD-ROM或DVD和一个存在于可读写设备(比如,U盘)上的叫作 knoppix.img 的文件系统联合起来的系统。这样,任何对CD/DVD上文件的改动都会被应用在U盘上,而不改变原来的CD/DVD上的内容。

AUFS

AUFS,英文全称是 Advanced Multi-Layered Unifcation Filesystem 。AUFS 完全重写了早期的 UnionFS 1.x,其主要目的是为了可靠性和性能,并且引入了一些新的功能,比如可写分支的负载均衡。AUFS的一些实现已经被纳入 UnionFS 2.x 版本。

image layer 和 AUFS

每一个Docker image都是由一系列read-onlylayer 组成的。image layer 的内容都存储在Docker hosts filesystem的/var/lib/dockeraufs/dif目录下。而/var/lib/docker/aufs/layers目录,则存储着 image layer 如何堆栈这些 layer 的 metadata。

;