惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
大猫的无限游戏
大猫的无限游戏
L
LINUX DO - 热门话题
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
博客园_首页
MongoDB | Blog
MongoDB | Blog
V
V2EX
GbyAI
GbyAI
量子位
Microsoft Azure Blog
Microsoft Azure Blog
有赞技术团队
有赞技术团队
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
B
Blog
Microsoft Security Blog
Microsoft Security Blog
S
SegmentFault 最新的问题
O
OpenAI News
N
News and Events Feed by Topic
博客园 - Franky
爱范儿
爱范儿
Forbes - Security
Forbes - Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V2EX - 技术
V2EX - 技术
Application and Cybersecurity Blog
Application and Cybersecurity Blog
N
News and Events Feed by Topic
N
News | PayPal Newsroom
Schneier on Security
Schneier on Security
Cloudbric
Cloudbric
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Recent Commits to openclaw:main
Recent Commits to openclaw:main
人人都是产品经理
人人都是产品经理
P
Privacy International News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog RSS Feed
阮一峰的网络日志
阮一峰的网络日志
D
DataBreaches.Net
Last Week in AI
Last Week in AI
罗磊的独立博客
Spread Privacy
Spread Privacy
Recent Announcements
Recent Announcements
The Cloudflare Blog
Google DeepMind News
Google DeepMind News
AWS News Blog
AWS News Blog
The Register - Security
The Register - Security
Y
Y Combinator Blog
J
Java Code Geeks
I
Intezer

蛮荆

如何获取更多的免费服务器 Kubernetes 调度器队列 - 设计与实现 Kubernetes 调度器 - 核心流程 Kubernetes Networking Model & CNI Kubernetes 控制器管理总结 Kubernetes CronJob 设计与实现 Kubernetes Job 设计与实现 Kubernetes HPA 设计与实现 Kubernetes Deployment 滚动更新实现原理 Kubernetes GC 设计与实现 Kubernetes Pod 驱逐 - 设计与实现 Kubernetes Daemonset 设计与实现 Kubernetes ReplicaSet 设计与实现 Kubernetes EndPoint 设计与实现 Kubernetes Informer 设计与实现 降本增效之应用优化 (三) 日志存储与检索 Kubernetes Pod 设计与实现 - 创建流程 Kubernetes 探针设计与实现 Unix 编程艺术名句摘录 Kubernetes - CRI 概述 Golang 编译速度为什么这么快? Kubernetes Pod 设计与实现 - Pause 容器 Kubernetes - kube-proxy 代理模式工程优化 Kubernetes 应用最佳实践 - 优雅关闭长连接 Kubernetes Service 类型和会话亲和性 Kubernetes 为什么需要 Ingress Kubernetes 架构 - 控制平面和数据平面 降本增效之应用优化 (二) 大报表 Go 语言如何获取 CPU 利用率 降本增效之应用优化 (一) Redis 业务规则引擎演变过程简述 微服务中的熔断算法 漏桶算法和令牌桶算法 jsonparser 为什么比标准库的 encoding/json 快 10 倍 ? zap 高性能设计与实现 HTTP Router 算法演进 fastcache 高性能设计与实现 Web 常见的三个安全问题 ants Code Reading Go 线程安全 map 方案选型 布隆过滤器 死锁、活锁、饥饿、自旋锁 sync.Pool Code Reading Go 内存管理概述 Go netpoll Code Reading goroutine 泄漏与检测 time/Timer Code Reading GMP Scheduler Code Reading Go channel 的 15 条规则和底层实现 为什么 Linux “一切皆文件” context.Context Code Reading runtime/HACKING.md Goland 最佳实践 互联网开发与金庸武学 为什么 Redis 6.0 引入多线程模型? Kubernetes 应用最佳实践 - 金丝雀发布 容器中如何正确配置 GOMAXPROCS ? singleflight Code Reading sync.Map Code Reading sync.Cond Code Reading sync.WaitGroup Code Reading sync.RWMutex Code Reading sync.Mutex Code Reading sync.Once Code Reading Go 无锁编程 sync/atomic Code Reading goroutine 交替打印奇偶数 GODEBUG Go 并发模式 Go 汇编 UUID 通用技术选型 Kubernetes 应用最佳实践 - 水平自动伸缩 Go 高性能 Tips fasthttp 为什么比标准库 net/http 快 10 倍 ? 技术文章配图指南 ChatGPT 初体验 Docker 网络原理概览 iptables 的五表五链 Kubernetes 应用最佳实践 - 亲和性和污点容忍度 Go 的反射与三大定律 Docker 官方提供的最佳实践 Go 语言内置的设计模式 HTTP1 到 HTTP3 的工程优化 Kubernetes 应用最佳实践 - Sidecar 模式 Kubernetes 应用最佳实践 - init 容器和钩子函数 为什么 recover 必须在 defer 中调用? 为什么 defer 的执行顺序和注册顺序不同? Go map 设计与实现 Go 切片扩容底层实现 Go 语言中的零拷贝 Go Delve 云原生和边缘计算简介 Kubernetes Pod 服务质量等级 Kubernetes 应用最佳实践 - 探针 Kubernetes 应用最佳实践 - 资源请求和限制 CDN 原理 Kubernetes 应用最佳实践 - 开篇 缓存策略和模式 Go 内存模型 Kubernetes 核心概念
Docker 基础支撑技术概览
2023-02-07 · via 蛮荆

2023-02-07 Docker Cloud Native

概述

Docker 容器本质上是宿主机上的进程

Docker 通过 Namespaces 实现了资源隔离,通过 CGroups 实现了资源限制,通过 UnionFS 实现了高效的文件 (镜像) 操作。

图片来源: https://medium.com/@goyalsaurabh66/docker-basics-cb006b9be243

Namespaces

命名空间 (namespace) 是一种 内核级别的资源隔离机制,用于确保运行在同一操作系统上的各进程互不干扰, 和平时业务代码中的 namespace 或者 package 概念类似,单个 命名空间 中的进程只能看到该 命名空间 中的相关信息,例如:

  • 主机名称
  • 网络资源
  • 用户信息
  • 文件系统
  • 进程关系

7 种资源类型命名空间

名称 系统调用参数 隔离内容
IPC CLONE_NEWIPC 主机名和域名
Network CLONE_NEWNET 网络设备、网络栈、端口等
Mount CLONE_NEWNS 挂载点 (文件系统)
PID CLONE_NEWPID 进程 ID
User CLONE_NEWUSER 用户和用户组 ID
UTS CLONE_NEWUTS 主机名和域名
CGroups CLONE_NEWCGROUP CGroups root directory

以上 7 种命名空间基本覆盖了一个进程运行所需要的独立环境,换句话说,一个进程不属于某一个资源类型的命名空间,而是属于每种资源类型的一个命名空间。

proc 目录

Linux 中每个进程都有一个 /proc/$pid/ns 的目录,里面保存了该进程所在对应 命名空间 的链接。

以笔者机器中的 MySQL 进程为例:

$ ls -l /proc/17042/ns/
total 0
lrwxrwxrwx 1 xxx xxx 0 Feb 11 21:18 ipc -> ipc:[4026532808]
lrwxrwxrwx 1 xxx xxx 0 Feb 11 21:18 mnt -> mnt:[4026532806]
lrwxrwxrwx 1 xxx xxx 0 Feb 11 21:18 net -> net:[4026532812]
lrwxrwxrwx 1 xxx xxx 0 Feb 11 21:18 pid -> pid:[4026532810]
lrwxrwxrwx 1 xxx xxx 0 Feb 11 21:18 user -> user:[4026531837]
lrwxrwxrwx 1 xxx xxx 0 Feb 11 21:18 uts -> uts:[4026532807]

每个文件都是对应的 命名空间 文件描述符,方括号里面的值是 命名空间 的 inode,如果两个进程所属 命名空间 一样, 那么它们的 inode 列表是一样的,反之亦然。如果某个 命名空间 中没有进程了,它会被自动删除,不需要手动删除。 但有个例外,如果 命名空间 对应的文件已经被某个应用进程打开,那么该 命名空间 是不会被删除的,这个特性可以使某个 命名空间 常驻, 便于后续往里面添加进程。

系统调用 API

  • clone() : 实现线程的系统调用,用来创建一个新的进程,并设置它的 命名空间
  • unshare() : 将进程脱离某个 命名空间
  • setns() : 将进程加入某个 命名空间

图片来源: https://bikramat.medium.com/namespace-vs-cgroup-60c832c6b8c8

通过 命名空间 为进程实现了资源隔离,但是 命名空间 无法提供物理资源的使用限制,比如 CPU 或者 内存,如果一台主机上面多个运行的容器中, 有一个容器疯狂抢占内存资源,那么其他容器都会受到影响。如何对多个容器进行物理资源限制,就要用到接下来介绍的 CGroups

CGroups

CGroup 全称 Linux Control Group, 是 Linux 内核的一个功能,用来 限制、控制与分离一个进程组群的资源(如 CPU、内存、磁盘输入输出等), 除此之外,还可以为资源设置权重、计算使用量、操控进程启动和停止等。

主要功能

CGroup 从设计之初使命就很明确,为进程提供资源控制,它主要的功能包括:

  • 资源限制 (Resource Limitation):可以对进程组使用的资源总额进行限制,如设定进程运行时使用内存的上限,一旦超过这个配额就发出 OOM(Out of Memory)
  • 优先级分配(Prioritization):通过分配的 CPU 时间片数量及硬盘 IO 带宽大小,相当于控制了进程运行的优先级
  • 资源统计 (Accounting): 可以统计系统的资源使用量,如 CPU 使用时长、内存用量等,非常适用于计费功能
  • 进程控制 (Control):可以对进程组执行挂起、恢复等操作

常见的操作有:

  • 隔离一个进程集合(比如:Nginx 的所有进程),并限制他们所消费的资源,比如绑定 CPU 的核数
  • 为一组进程分配足够使用的内存
  • 为一组进程分配相应的网络带宽和磁盘存储限制
  • 限制访问某些设备(通过设置设备的白名单)

图片来源: https://blog.devgenius.io/container-namespace-introduction-6a1e26f8707a

核心概念

  • task:任务,对应于系统中运行的一个实体,一般是指进程
  • subsystem:子系统,具体的资源控制器,控制某个特定的资源使用,比如 CPU 子系统可以控制 CPU 时间,内存子系统可以控制内存使用量
  • CGroup:控制组,一组任务和子系统的关联关系,表示对这些任务进行怎样的资源管理策略。一个任务可以加入某个 CGroup,也可以从某个 CGroup 迁移到另外一个 CGroup
  • hierarchy:层级树,一系列 CGroup 组成的树形结构。每个节点都是一个 CGroup,CGroup 可以有多个子节点,子节点默认会继承父节点的属性,系统中可以有多个 hierarchy

subsystem (子资源系统)

subsystem 实际上就是 CGroup 的资源控制系统,每种 subsystem 独立地控制一种资源。

  • Block IO(blkio):限制块设备(磁盘、SSD、USB 等)的 IO 速率
  • CPU Set(cpuset):限制任务能运行在哪些 CPU 核上
  • CPU Accounting(cpuacct):生成 CGroup 中任务使用 CPU 的报告
  • CPU (CPU):限制调度器分配的 CPU 时间
  • Devices (devices):允许或者拒绝 CGroup 中任务对设备的访问
  • Freezer (freezer):挂起或者重启 CGroup 中的任务
  • Memory (memory):限制 CGroup 中任务使用内存的量,并生成任务当前内存的使用情况报告
  • Network Classifier(net_cls):为 cgroup 中的报文设置特定的 classid 标志,这样 tc 等工具就能根据标记对网络进行配置
  • Network Priority (net_prio):对每个网络接口设置报文的优先级
  • perf_event:识别任务的 CGroup 成员,可以用来做性能分析

本质上来说,CGroup 是内核附加在程序上的一系列钩子(hooks),通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的

UnionFS

UnionFS 将不同物理位置的目录合并到同一个目录中。

图片来源: https://www.linuxjournal.com/article/7714

DockerUnionFS 的设计理念扩展到了容器的镜像管理功能,并通过不同的存储驱动来管理镜像和容器文件。

AUFS

AUFS 的全称是 Advanced Multi-layered unification filesytem,即 UnionFS 的升级版,它能够提供更优秀的性能和效率。 和 UnionFS 一样,它的 主要功能是:将多个目录合并为一个目录

多个目录怎样结合成一个目录呢?具体的读写操作如下:

  • 默认情况下,最上层的目录为读写层,只能有一个;下面可以有一个或者多个只读层
  • 读文件,打开文件的时候使用了 O_RDONLY 选项:从最上面一个开始往下逐层去找,打开第一个找到的文件,读取其中的内容
  • 写文件,打开文件时用了 O_WRONLY 或者 O_RDWR 选项
  • 如果在最上层找到了该文件,直接打开,否则,从上往下开始查找,找到文件后,把文件复制到最上层,然后再打开这个 copy(所以,如果要读写的文件很大,这个过程耗时会很久)
  • 删除文件:在最上层创建一个 whiteout 文件,.wh.<origin_file_name>,就是在原来的文件名字前面加上 .wh.

图片来源: https://coolshell.cn/articles/17061.html

上面的图片非常形象地展现了合并的过程,每一个镜像层都是建立在另一个镜像层之上的,除了顶层的镜像层可读写外,其他的镜像层都是只读的, 所有的镜像都建立在一些底层基础镜像上面,比如 Kernel, Golang 等,这种合并过程通过组合的方式提供了非常大的灵活性, 只读的镜像层通过共享能够减小镜像文件体积,减少磁盘的占用空间。

AUFS 作为联合文件系统,能够将不同目录中的层联合(Union)到同一个目录中,这些目录在 AUFS 中称作分支,整个联合的过程被称为联合挂载(Union Mount):

图片来源: https://docs.docker.com/storage/storagedriver/aufs-driver/

当我们使用 docker run 命令创建镜像时,会在镜像的最上层添加一个可写的层,也就是容器层,所有对于运行时容器的修改其实都是对这个容器读写层的修改。 容器和镜像的区别就在于,所有的镜像都是只读的,而每一个容器就等于镜像加上一个可读写的层,也就是同一个镜像可以对应多个容器

图片来源: https://docs.docker.com/storage/storagedriver/

各发行版推荐存储驱动

AUFS 只是 Docker 使用的存储驱动的其中一种,除了 AUFS 之外,Docker 还支持了不同的存储驱动。在最新版的 Docker 中,overlay2 取代 AUFS 成为了推荐存储驱动。

Linux 发行版 推荐存储驱动 备选存储驱动
Ubuntu overlay2 overlay, devicemapper, aufs, zfs, vfs
Debian overlay2 overlay, devicemapper, aufs, vfs
CentOS overlay2 overlay, devicemapper, zfs, vfs
Fedora overlay2 overlay, devicemapper, zfs, vfs
SLES 15 overlay2 overlay, devicemapper, vfs
RHEL overlay2 overlay, devicemapper, vfs

查看当前存储驱动

$ docker info | grep -i storage

Storage Driver: overlay2

小结

本文简单介绍了 Docker 背后的基础支撑技术,没有深入探索底层 API 和具体实践,感兴趣的读者可以参考扩展阅读文章列表自己动手打造 “山寨版 Docker” :-)

Reference