Docker
💻

Docker

Tags
Published
Author
URL
子页面:
⛓️
常用命令
⚗️
跨CPU架构编译镜像
🦼
官方仓库 加速器配置
⏱️
DockerFile
🎙️
docker 修改镜像存储位置
🎷
常见问题处理
🪄
修改 podman 本地默认存放地址
查询镜像文件有哪些变动的方法
🎰
Docker 镜像签名

介绍

简要

  • 基于linux 内核的Cgroup, Namespace 以及UnionFS等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术,由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。
  • 最初实现的是基于LXC,从0.7以后开始去除LXC,转而使用自行开发的libcontainer, 从1.11开始,则进一步演进为使用runC 和Containerd。
  • Docker在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护,使得Docker技术比虚拟机技术更为轻便、快捷。

NameSpace 进程资源运行空间(隔离)

  • Linux Namespace 是一种Linux Kernel 提供的资源隔离
    • 系统可以为进程分配不同的Namespace;
    • 并保证不同的Namespace资源独立分配、进程彼此隔离,即不同的Namespace下的进程互不干扰。
  • 关于 namespace 的常用操作
    • 查看当前系统的namespace:
    • lsns # 展示所有的Namespace lsns -t <type> # 展示指定类型的Namespace
    • 查看某进程的namespace:
    • ls -la /proc/<pid>/ns/ #可以查看到所有类型的Namespace
    • 进入某namespace 运行命令
    • nsenter -t <pid> -n ip addr

Cgroup 进程资源管理(控制)

  • Cgroups( Control Groups)是Linux 下用于对一个或一组进程进行资源控制和监控的机制;
  • 可以对诸如CPU使用时间、内存、磁盘I/0等进程所需要的资源进行限制;
  • 不同资源的具体管理工作由相应的Cgroup子系统(Subsystem)来实现;
  • 针对不同类型的资源限制,只要将限制策略在不同的子系统上进行关联即可;
  • Cgroups 在不同的系统资源管理子系统中层级树(Hierarchy)的方式来进行组织管理,每个Cgroup 都可以包含其他的子Cgroup,因此子Cgroup能使用的资源除了受本Cgroup配置的资源参数限制,还受到父Cgroup设置的资源限制。

可配额/可度量 - Control Groups

notion image
  • Cgroup 实现了对资源的配额和度量
    • blkio: 这个子系统设置限制每个块设备的输入输出控制。列如:磁盘,光盘以及USB等等。
    • CPU: 这个子系统使用调度程序为cgroup任务提供了CPU的访问。
    • cpuacct: 产生cgroup任务的CPU资源报告。
    • cpuset: 如果是多核心的CPU,这个子系统会为cgroup任务分配单独的CPU和内存。
    • devices: 允许或拒绝cgroup任务对设备的访问。
    • freezer: 暂停和恢复cgroup任务。
    • memory: 设置每个cgroup 的内存限制以及产生内存资源报告。
    • net_cls: 标记每个网络包以供cgroup方便使用。
    • ns: 名称空间子系统。
    • pid: 进程标识子系统。

CPU 子系统

  • cpu.shares: 可出让的能获得CPU使用时间的相对值。
  • cpu.cfs_period_us: 用来配置时间周期长度,单位为us(微秒)。
  • cpu.cfs_quota_us: 用来配置当前cgroup在cfs_period_us时间内最多能使用的CPU时间数,单位为us(微秒)。
  • cpu.stat: cgroup内进程使用的CPU时间统计。
  • nr_periods: 经过cfs_period_us的时间周期数量
  • nr_throttled: 在经过的周期内,有多少次因为进程在指定的时间周期内用光了配额时间受到限制。
  • throttled_time: Cgroup中进程被限制使用CPU的总用时,单位是ns(纳秒)。

Linux 调度器

内核默认提供了5个调度器,linux内核使用struct sched_class 来对调度器进行抽象:
  • Stop 调度器,stop_sched_class: 优先级最高的调度类,可以抢占其它所有进程,不能被其他进程抢占;
  • Deadline 调度器,dl_sched_class: 使用红黑树,把进程按照绝对截止期限进行排序,选择最小进程进行调度运行;
  • RT调度器,rt_sched_class: 实时调度器,为每个优先级维护一个队列;
  • CFS 调度器,cfs_sched_class: 完全公平调度器,采用完全公平调度算法,引入虚拟运行时间概念;
  • IDE-Task调度器, idle_sched_class: 空闲调度器,每个CPU都会有一个idle线程,当没有其它进程可以调度时,调度运行idle线程。
 

CFS 调度器

  • CFS 是 completely Fair scheduler 简称,即完全公平调度器。
  • CFS 实现的主要思想是维护为任务提供处理器时间方面的平衡,这意味着应给进程分配相当数量的处理器。
  • 分给某个人的时间市区平衡时,应给失去平衡的任务分配时间,让其执行。
  • CFS 通过虚拟运行时间(vruntime)来实现平衡,维护提供给某个任务的时间量。
    • vruntime = 实际运行时间*1024 / 进程权重
  • 进程按照各自不同的速率在物理时钟节拍内前进,优先级高则权重大,其虚拟时钟比真实时钟跑得慢,但获得比较多的运行时间。
 

vruntime 红黑树

 
CFS 调度器没有将进程维护在运行队列中,而是维护了一个以虚拟运行时间为顺序的红黑树。红黑树的主要特点有:
  • 自平衡,树上没有一条路径会比其它路径长出两倍。
  • O(log n) 时间复杂度,能够在树上进行快速高效地插入或删除进程。
notion image

CFS进程调度

  • 在时钟周期开始时,调度器调用 _schedule()函数来开始调度的运行。
  • schedule() 函数调用picknext_task() 让进程调用器从就绪队列中选择一个最合适的进程Next,即红黑树最左边的节点。
  • 通过context_switch()切换到新的地址空间,从而保证Next进程运行。
  • 在时钟周期结束是,调度器调用entity_tick()函数来更新进程负载、进程状态以及vruntime(当前vruntime + 该时钟周期内运行的时间)。
  • 最后,将该进程的虚拟时间与就绪队列红黑树中最左边的调用实体的虚拟时间作比较,如果小于左边的时间,则不用触发调度,继续调度当前调度实体
notion image

cpuacct 子系统

用于统计Cgroup 及其子Cgroup 下进程的CPU的使用情况。
  • cpuacct.usage
包含该Cgroup及其子Cgroup下进程使用CPU的使用时间,单位是ns(纳秒)。
  • cpuacct.stat
包含该Cgroup及其子Cgroup下进程使用的CPU时间,以及用户态和内核态的时间。
 

Memory 子系统

  • memory.usage_in_bytes
    • cgroup下进程使用的内存,包含cgroup及其子cgroup下的进程使用的内存
  • memory.max_usage_in_bytes cgroup 下进程使用内存的最大值,包含子 cgroup 的内存使用量。
  • memory.limit_in_bytes 设置 Cgroup 下进程最多能使用的内存。如果设置为 -1,表示对该 cgroup 的内存使用不做限制。
  • memory.soft_limit_in_bytes
    • 这个限制并不会阻止进程使用超过限额的内存,只是在系统内存足够时,会优先回收超过限额的内存,使之向限定值靠拢。
  • memory.oom_control
    • 设置是否在 Cgroup 中使用 OOM(Out of Memory)Killer,默认为使用。当属于该 cgroup 的进程使用的内存超过最大的限定值时, 会立刻被 OOM Killer 处理。
 

Cgroup driver

  • systemd:
    • 当操作系统使用 systemd 作为 init system 时,初始化进程生成一个根 cgroup 目录结构并作为 cgroup 管理器。
    • systemd 与 cgroup 紧密结合,并且为每个 systemd unit 分配 cgroup。
  • cgroupfs:
    • docker 默认用 cgroupfs 作为 cgroup 驱动。
  • 存在问题:
    • 在 systemd 作为 init system 的系统中,默认并存着两套 groupdriver。
    • 这会使得系统中 Docker 和 kubelet 管理的进程被 cgroupfs 驱动管,而 systemd 拉起的服务由 systemd 驱动管,让 cgroup 管理混乱且容易在资源紧张时引发问题。
    • 因此 kubelet 会默认--cgroup-driver=systemd,若运行时 cgroup 不一致时,kubelet 会报错。

文件系统

Union FS

  • 将不同目录挂载到同一个虚拟文件系统下 (unite several directories into a single virtual filesystem) 的文件系统
  • 支持为每一个成员目录(类似Git Branch)设定 readonly、readwrite 和 whiteout-able 权限
  • 文件系统分层, 对 readonly 权限的 branch 可以逻辑上进行修改(增量地, 不影响 readonly 部分的)
  • 通常 Union FS 有两个用途, 一方面可以将多个 disk 挂到同一个目录下, 另一个更常用的就是将一个 readonly 的 branch 和一个 writeable 的 branch 联合在一起
 

Docker 的文件系统

典型的Linux文件系统组成:
  • bootfs( boot file system)
    • Bootloader - 引导加载kernel
    • Kernel - 当kernel被加载到内存中后 umount bootfs(卸载)。
  • rootfs( root file system)
    • /dev, /proc, /bin, /etc 等标准目录和文件。
    • 对于不同的linux发行版,bootfs基本是一致的,但rootfs会有差别。
    •  

Docker 启动

Linux
  • 在启动后,首先将rootfs设置为readonly,进行一系列的检查,然后将其切换为”readwrite“ 供用户使用。
Docker 启动
  • 初始化是也是将rootfs以readonly方式加载并检查,然而接下来利用union mount的方式将一个readwrite文件系统挂载在readonly的rootfs之上;
  • 并且允许再次将下层的FS设定为readonly并且向上叠加。
  • 这样一组readonly和一个writeable的结构构成了一个container的运行时态,每一个FS被称作一个FS层。

写操作

由于镜像具有共享特性,所以对容器可写层的操作需要依赖存储驱动提供的写时复制和用时分配机制,以此来 支持对容器可写层的修改,进而提高对存储和内存资源的利用率。
  • 写时复制
    • 写时复制,即 Copy-on-Write。
    • 一个镜像可以被多个容器使用,但是不需要在内存和磁盘上做多个拷贝。
    • 在需要对镜像提供的文件进行修改时,该文件会从镜像的文件系统被复制到容器的可写层的文件系统 进行修改,而镜像里面的文件不会改变。
    • 不同容器对文件的修改都相互独立、互不影响。
  • 用时分配
    • 按需分配空间,而非提前分配,即当一个文件被创建出来后,才会分配空间。

容器存储驱动

Overlay 只有两层:upper 层和 lower 层,Lower 层代表镜像层,upper 层代表容器可写层。
notion image

Docker 引擎结构

notion image