docker概念

虽然Kubernetes 在 v1.20 版本之后将不支持 Docker作为容器运行时Container Runtime(Docker的多层封装和调用,导致其在可维护性上略逊一筹),但是现在Docker还是Kubernetes默认的容器运行时(Container Runtime),在使用为 Kubernetes 创建的Container Runtime Interface(CRI)的运行时,Docker 生成的镜像可以继续在你的集群中与所有CRI实现一起工作。后续可以切换至与之兼容的容器运行时containerd、CRI-O等。

为什么要使用Docker而不是其它虚拟化技术?

  • 容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。
  • Docker 容器应用直接运行于宿主内核,无需启动完整的操作系统,可以做到秒级启动。
  • Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性。
  • Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。
  • Docker 确保了执行环境的一致性,使得应用的迁移更加容易。
  • Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也非常简单。

Docker Engine简介

Docker Engine 可以通过Docker Desktop在macOS和Windows10上使用,也可以使用静态二进制在各种Linux上使用。

Docker Engine作为一个client-server应用:

  • 一个长期运行的守护进程服务端 dockerd。Docker守护进程 (dockerd) 监听Docker API的请求和管理Docker对象,比如镜像,容器,网络,数据卷。守护进程并且可以与其他守护进程通信去管理Docker服务。
  • Docker API,通过指定接口Docker客户端能与 Docker守护进程进行通信,使守护程序完成了构建、运行和分发Docker容器等工作。.
  • 命令行界面(CLI, command line interface) 客户端 dockerdocker是docker用户与docker交互的一个主要途径,当你使用命令(比如docker run), 客户端发送这些命令至守护进程dockerd。docker命令使用 Docker API。Docker客户端可以和多个守护进程通信。
    docker组件流图.png

Docker Engine版本

Docker Engine分为 CE 和 EE 两大版本。CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月。
Docker CE 分为 stable, test, 和 nightly 三个更新通道。每六个月发布一个 stable 版本:
Stable为您提供一般可用性(GA, general availability)的最新版本。年-月发布是从与主分支的发布分支生成的
test 提供在GA之前准备好进行测试的预发布。
Nightly提供下一个主要版本的最新内容。


Docker组件

docker client

docker client是一个客户端工具,将用户的请求发送给 docker daemon(dockerd)

dockerd

dockerd是docker守护进程(Docker daemon)。是对容器相关操作的api的最上层封装,将请求发送给 containerd 。

 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

containerd

containerd 是一个守护进程,它管理容器生命周期,提供了在一个节点上执行容器和管理镜像的最小功能集。dockerd操作实际调用的还是containerd的api接口(gRPC方式实现),containerd是dockerd和runc之间交流的一个中间组件。

containerd-shim

containerd-shim 是一个是容器的运行时载体,允许runc在创建&运行容器之后退出,每启动一个容器都会启动一个新的containerd-shim进程,他直接通过指定的三个参数:容器id,boundle目录(容器的文件系统和一个 config.json文件)来调用runc的api创建一个容器(比如创建容器:最后拼装的命令如下:runc create )。用containerd-shim作为容器的父进程,而不是直接用containerd作为容器的父进程,是为了防止当containerd挂掉的时候导致容器失败。runC 启动完容器后本身会直接退出,containerd-shim 则会成为容器进程的父进程,负责收集容器进程的状态,上报给 containerd,并在容器中 pid 为 1 的进程退出后接管容器中的子进程进行清理,确保不会出现僵尸进程,这样就不需要containerd来wait子进程。

containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/274f1a80e3946d74354a449954874ae47e4b5dbcb1313966e82d33826ddee3a7 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc -systemd-cgroup

runc

runc是一个命令行工具端,他根据oci(开放容器组织, Open Container Initiative)的标准来创建和运行容器。具体参考:https://www.jianshu.com/p/2f6296190049


创建一个docker容器的步骤

docker启动容器.gif
  1. docker cli发送创建容器命令至Docker daemon;
  2. Docker daemon 仍然不能帮我们创建容器,转而请求 containerd 创建一个容器;
  3. containerd 收到请求后,并不会自己直接去操作容器,而是创建一个叫做 containerd-shim 的进程,让 containerd-shim 去操作容器。这是因为容器进程需要一个父进程来做诸如收集状态,维持 stdin 等 fd 打开等工作。而假如这个父进程就是 containerd,那每次 containerd 挂掉或升级,整个宿主机上所有的容器都得退出了。而引入了 containerd-shim 就规避了这个问题(containerd 和 shim 并不是父子进程关系);
  4. 我们知道创建容器需要做一些设置 namespaces 和 cgroups,挂载 root filesystem 等等操作,而这些事该怎么做已经有了公开的规范了,那就是 OCI(Open Container Initiative,开放容器标准)。它的一个参考实现叫做 runC。于是,containerd-shim 在这一步需要调用 runC 这个命令行工具,来启动容器;
  5. runC 启动完容器后本身会直接退出,containerd-shim 则会成为容器进程的父进程,负责收集容器进程的状态,上报给 containerd,并在容器中 pid 为 1 的进程退出后接管容器中的子进程进行清理,确保不会出现僵尸进程。

查看相关组件版本

[ ~]# docker version
Client: Docker Engine - Community
 Version:           19.03.12
 API version:       1.40
 Go version:        go1.13.10
 Git commit:        48a66213fe
 Built:             Mon Jun 22 15:46:54 2020
 OS/Arch:           linux/amd64
 Experimental:      false
​
Server: Docker Engine - Community
 Engine:
 Version:          19.03.12
 API version:      1.40 (minimum version 1.12)
 Go version:       go1.13.10
 Git commit:       48a66213fe
 Built:            Mon Jun 22 15:45:28 2020
 OS/Arch:          linux/amd64
 Experimental:     false
 containerd:
 Version:          1.2.13
 GitCommit:        7ad184331fa3e55e52b890ea95e65ba581ae3429
 runc:
 Version:          1.0.0-rc10
 GitCommit:        dc9208a3303feef5b3839f4323d9beb36df0a9dd
 docker-init:
 Version:          0.18.0
 GitCommit:        fec3683</pre>

Docker底层技术

Namespaces

Docker使用namespaces为容器的工作空间做隔离. 当你运行一个容器, Docker 会发该容器创建一系列的namespace。这些namespace提供了一层隔离。一个容器运行在一个单独的命名空间中,并且其访问被限制在当前命名空间内 。

Docker Engine在Linux上使用下列namespace:

  • pid namespace: 进程隔离 (PID: Process ID),"chroot"进程树,提供进程隔离能力。PID namespace是划分那些一个进程可以查看并与之交互的PID的方式。当我们创建一个新的PID namespace时,第一个进程的PID会被赋值为1。当该进程退出时,内核会杀死当前namespace内的其他进程。
  • net namespace: 管理网络接口 (NET: Networking),提供网络隔离能力。Network Namespace 可以让每个容器拥有自己独立的(虚拟的)网络设备,而且容器内的应用可以绑定到自己的端口,每个 Namespace 内的端口都不会互相冲突。在宿主机上搭建网桥后,就能很方便地实现容器之间的通信,而且不同容器上的应用可以使用相同的端口 。
  • The ipc namespace: 管理IPC资源的访问 (IPC: InterProcess Communication),提供进程间通信的隔离能力。
  • The mnt namespace: 管理文件系统挂载点 (MNT: Mount),提供磁盘挂载点和文件系统的隔离能力
  • The uts namespace: 隔离内核和版本标识符 (UTS: Unix Timesharing System),主要用来完成对容器HOSTNAME和domain的隔离,同时保存内核名称、版本、以及底层体系结构类型等信息。使得用户在容器中查看到的信息是当前容器的系统、版本,不同于主机的,内核通过uts_namespace对当前系统中多个容器的这些信息进行统一管理,每一个容器对应有一个自己的uts_namespace,用来隔离容器的内核名称、版本等信息,不同容器查看到的都是属于自己的信息,相互间不能查看。

Control groups

Docker Engine运行在Linux上也依赖control groups (cgroups). cgroup将应用程序限制为一组特定的资源。 Control groups允许Docker Engine将可用的硬件资源(比如cpu,内存,磁盘和网络IO)共享给容器,并有选择地实施限制和约束。 例如,您可以限制特定容器可用的内存。


Union file systems

Union file systems(或UnionFS)是通过创建layer来操作文件系统,使其非常轻便且快速。 Docker Engine使用UnionFS为容器提供基本组成块(building blocks)。 Docker Engine可以使用多个UnionFS变体,包括aufs,fuse-overlayfs,btrfs,zfs,vfs,devicemapper和overlay2(当前推荐)。

  • overlay2 is the preferred storage driver, for all currently supported Linux distributions, and requires no extra configuration.
  • aufs was the preferred storage driver for Docker 18.06 and older, when running on Ubuntu 14.04 on kernel 3.13 which had no support for overlay2.
  • fuse-overlayfs is preferred only for running Rootless Docker on a host that does not provide support for rootless overlay2. On Ubuntu and Debian 10, the fuse-overlayfs driver does not need to be used overlay2 works even in rootless mode. See Rootless mode documentation.
  • devicemapper is supported, but requires direct-lvm for production environments, because loopback-lvm, while zero-configuration, has very poor performance. devicemapper was the recommended storage driver for CentOS and RHEL, as their kernel version did not support overlay2. However, current versions of CentOS and RHEL now have support for overlay2, which is now the recommended driver.
  • The btrfs and zfs storage drivers are used if they are the backing filesystem (the filesystem of the host on which Docker is installed). These filesystems allow for advanced options, such as creating “snapshots”, but require more maintenance and setup. Each of these relies on the backing filesystem being configured correctly.
  • The vfs storage driver is intended for testing purposes, and for situations where no copy-on-write filesystem can be used. Performance of this storage driver is poor, and is not generally recommended for production use.

storage driver所支持的操作系统

对于Docker Engine - Community,仅测试了一些配置,并且你的操作系统的内核可能不支持每一个storage driver。一般来说, 以下配置适用于最新版本的Linux发行版:

Linux distribution Recommended storage drivers Alternative drivers
Docker Engine - Community on Ubuntu overlay2 or aufs (for Ubuntu 14.04 running on kernel 3.13) overlay¹, devicemapper², zfs, vfs
Docker Engine - Community on Debian overlay2 (Debian Stretch), aufs or devicemapper (older versions) overlay¹, vfs
Docker Engine - Community on CentOS overlay2 overlay¹, devicemapper², zfs, vfs
Docker Engine - Community on Fedora overlay2 overlay¹, devicemapper², zfs, vfs

¹) The overlay storage driver is deprecated, 将在未来的版本中移除。 推荐用户将其迁移至overlay2。
²) The devicemapper storage driver is deprecated, 将在未来的版本中移除。推荐用户将其迁移至overlay2。

storage drivers所支持的文件系统

Storage driver Supported backing filesystems
overlay2, overlay xfs with ftype=1, ext4
fuse-overlayfs any filesystem
aufs xfs, ext4
devicemapper direct-lvm
btrfs btrfs
zfs zfs
vfs any filesystem

根据工作负载选型

除其他事项外,每个存储驱动都有其自己的性能特征,这使其或多或少地适合于不同的工作负载。考虑以下概括:

  • overlay2, aufs以及overlay都是操作于文件层而不是块(block)层。它使用内存更效率, 但是在写密集型write-heavy工作负载下容器的可写层可能会增长很大。
  • 块层Block-level存储驱动,比如devicemapper, btrfs和zfs在写密集型write-heavy workloads的情况下表现的更好 (虽然不如Docker volumes一样好)。
  • 对于大量的小型写入,容器包含大量的层或者深层文件系统 , overlay也许性能更优于overlay2,但是会使用更多的inode,这将导致inode耗尽。
  • btrfs和zfs 需要大量的内存。
  • zfs对于高密集high-density工作负载型(比如 PaaS)是一个好的选择

当前使用的storage driver以及backing filesystem,可以使用docker info查看

[ ~]# docker info
 Server Version: 19.03.12
 Storage Driver: overlay2
  Backing Filesystem: xfs
  Supports d_type: true
  Native Overlay Diff: true
 Cgroup Driver: systemd
 Runtimes: runc
 Default Runtime: runc
 Init Binary: docker-init

devicemapper与overlayFS

devicemapper

devicemapper 驱动使用专用于Docker的块设备,并操作于块级别而非文件级别。可以通过向Docker主机添加物理存储来扩展这些设备,并且它们的性能要好于在操作系统(OS)级别上使用文件系统devicemapper要求lvm2device-mapper-persistent-data已经安装。且其loop-lvm模式仅使用于配置direct-lvm模式之前的测试工作。生产环境使用devicemapper存储驱动一定要使用direct-lvm模式。 direct-lvm模式将使用块设备创建thin pool。这比loop-lvm模式使用回环设备(loopback devices)更快、能更有效的使用系统资源以及其块设备能按需增长。
当你使用devicemapper驱动启动Docker时,其与镜像和容器层相关的所有对象都存储在/var/lib/docker/devicemapper/目录中,这些对象由一个或多个块级设备支持,而不是回环设备(loopback devices)(仅测试)或物理磁盘。

  • 基本设备(base device)是最低级别的对象,这就是thin pool本身,你可以使用docker info查看它。 它包含一个文件系统。 此基本设备是每个镜像和容器层的起点。 基本设备是Device Mapper实现细节,而不是Docker层。

  • 关于基本设备和每个镜像、容器层的元数据使用JSON格式存储在/var/lib/docker/devicemapper/metadata/ 。这些层都是写时复制copy-on-write快照, 这意味着它们是空的,直到它们脱离其父层。

  • 每个容器的可写层都挂载在/var/lib/docker/devicemapper/mnt/。每个只读镜像层和每个停止的容器都存在一个空目录。
    每个镜像层都是其下一层的快照。 每个镜像的最低层是基本设备的快照,基本设备存在于池中。 当你运行容器时,它是容器所基于的镜像的快照。 以下示例显示了两个正在运行的容器的Docker主机。 第一个是ubuntu容器,第二个是busybox容器。


    two_dm_container.jpg
OverlayFS

如果你要使用OverlayFS, 那应该使用overlay2而不是overlay驱动, 因为overlay2的inode利用率更高。 为了使用overlay2, 你的内核版本应该是 4.0或以上, RHEL或CentOS使用内核应该是 3.10.0-514或以上。
下图是OverlayFS是如何将Docker镜像和Docker容器分层。容器层是lowerdir,容器层是upperdir,统一视图通过merged目录显示,该目录实际上是容器的挂载点。 该图显示了Docker构造如何映射到OverlayFS构造。

overlay_constructs.jpg


Container format

Docker Engine将namespaces, control groups和UnionFS组合到一个称为容器格式的包装器中。 默认容器格式为libcontainer。 将来,Docker可以通过与BSD Jails或Solaris Zones等技术集成来支持其他容器格式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容

  • 本章内容 ◆ Docker简介◆ Docker 镜像与制作◆ Docker 数据管理◆ Docker 网络◆ Do...
    Liang_JC阅读 752评论 0 0
  • Docker是基于容器的应用开发,部署和运行平台 高性能:相比传统虚拟机,不需要hepervisor的额外负载,而...
    wangdy12阅读 1,564评论 0 0
  • docker概念 镜像与容器 docker镜像是一个统一文件系统,由一系列只读层堆叠而成。容器则是在只读层堆顶加上...
    羽色云烟阅读 709评论 0 0
  • docker最早是LXC(Linux Container)的二次封装发行,后来使用的是Libcontainer技术...
    OOM_Killer阅读 639评论 0 0
  • 一. 容器与虚拟机的区别 和虚拟机相比,容器更加轻量级。因为运行在相同宿主机上的容器共享一个操作系统,可以节省大量...
    睡不醒的大橘阅读 206评论 0 0