深入了解Docker(1) 环境搭建

目录

深入了解Docker(1) 环境搭建
深入了解Docker(2) - Cli命令1
深入了解Docker(3) - Cli命令2

写在前面

本文档特点:

  • 最新版本命令和参数的解析
  • 对相关重要知识进行扩展
  • 素材全部来自官网、互联网和已出版书籍

本文档用于有一点Docker基础,想要深入了解的人(当然包括我自己)。

邮箱:jim_jb@163.com

深入了解Docker学习笔记,计划包括以下内容:

  • Docker命令
  • Docker原理
  • Docker部分源代码分析
  • Docker API
  • Docker运维工具

1.1 在 CentOS 7 中安装 Docker

# yum-config-manager --add-repo
# yum install -y yum-utils device-mapper-persistent-data lvm2
# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# yum list docker-ce --showduplicates | sort -r
# yum -y install docker-ce

# systemctl start docker
# systemctl enable docker

1.2 第一个运行的Docker

# docker version
# docker --help
# docker run hello-world

1.2.1 容器镜像的位置

当 docker-cli 从镜像仓库中pull镜像到本机,保存在 /var/lib/docker/containers 目录下,目录名称就是镜像ID。
/var/lib/docker/image/overlay2/repositories.json 文件中,保存了镜像文件的描述。

1.2.2 修改容器镜像的保存位置

有两种方式能够修改容器镜像保存位置

  • 将新的卷,软链接ln -s到 /var/lib/docker 目录
  • 修改后台守护进程的启动参数

详细说明可以参考这篇文章 点击查看

为了让json更容易阅读,可以安装jq工具格式化json文本
# yum install jq
# echo repositories.json | jq
更多jq工具的使用,请查看jq --help 或查看更多关于jq的 使用说明

1.3 Docker 基本命令

# docker --help ps      # 查看 docker ps 命令的帮助说明
# docker ps -a          # 显示容器的详细信息
# docker image ls       # 查看本地镜像

[root@51yunwei overlay2]# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              bf756fb1ae65        4 months ago        13.3kB

基于ubuntu镜像,create&start命名为ubuntu的容器

# docker run -itd --name ubuntu ubuntu /bin/bash    
# docker exec -it ubuntu /bin/bash  # 运行一个容器,和本地tty绑定

1.4 Docker文件系统介绍

内容来自 Docker文件系统总结 一文。

一个典型的 Linux 系统要能运行的话,它至少需要两个文件系统:

  • boot file system (bootfs):包含 boot loader 和 kernel。用户不会修改这个文件系统。实际上,在启动(boot)过程完成后,整个内核都会被加载进内存,此时 bootfs 会被卸载掉从而释放出所占用的内存。同时也可以看出,对于同样内核版本的不同的 Linux 发行版的 bootfs 都是一致的。
  • root file system (rootfs):包含典型的目录结构,包括 /dev, /proc, /bin, /etc, /lib, /usr, and /tmp 等再加上要运行用户应用所需要的所有配置文件,二进制文件和库文件。这个文件系统在不同的Linux 发行版中是不同的。而且用户可以对这个文件进行修改。


    image

Linux系统在启动时,roofs 首先会被挂载为只读模式,然后在启动完成后被修改为读写模式,随后它们就可以被修改了。

  • 所有 Docker 容器都共享主机系统的 bootfs 即 Linux 内核;
  • 每个容器有自己的 rootfs,它来自不同的 Linux 发行版的基础镜像,包括 Ubuntu,Debian 和 SUSE 等;
  • 所有基于一种基础镜像的容器都共享这种 rootfs。

1.5 Docker 使用的文件系统驱动

Docker最开始采用AUFS作为文件系统,也得益于AUFS分层的概念,实现了多个Container可以共享同一个image。但由于AUFS未并入Linux内核,且只支持Ubuntu,考虑到兼容性问题,在Docker 0.7版本中引入了存储驱动。目前,Docker支持AUFS、Btrfs、Device mapper、OverlayFS、ZFS五种存储驱动。

就如Docker官网上说的,没有单一的驱动适合所有的应用场景,要根据不同的场景选择合适的存储驱动,才能有效的提高Docker的性能。

小技巧:安装tree工具,查看目录

# yum install tree
# tree -L 2 image/overlay2/
image/overlay2/
├── distribution
│   ├── diffid-by-digest
│   └── v2metadata-by-diffid
├── imagedb
│   ├── content
│   └── metadata
├── layerdb
│   ├── mounts
│   ├── sha256
│   └── tmp
└── repositories.json

1.5.1 写时复制和写时分配

写时复制(CoW)

  • 所有Docker文件驱动都用到的技术——写时复制(CoW)。CoW就是copy-on-write,表示只在需要写时才去复制,这个是针对已有文件的修改场景。比如基于一个image启动多个Container,如果为每个Container都去分配一个image一样的文件系统,那么将会占用大量的磁盘空间。而CoW技术可以让所有的容器共享image的文件系统,所有数据都从image中读取,只有当要对文件进行写操作时,才从image里把要写的文件复制到自己的文件系统进行修改。所以无论有多少个容器共享同一个image,所做的写操作都是对从image中复制到自己的文件系统中的复本上进行,并不会修改image的源文件,且多个容器操作同一个文件,会在每个容器的文件系统里生成一个复本,每个容器修改的都是自己的复本,相互隔离,相互不影响。使用CoW可以有效的提高磁盘的利用率。

写时分配(allocate-on-demand)

  • 而写时分配是用在原本没有这个文件的场景,只有在要新写入一个文件时才分配空间,这样可以提高存储资源的利用率。比如启动一个容器,并不会为这个容器预分配一些磁盘空间,而是当有新文件写入时,才按需分配新空间。

1.5.2 五种文件系统对比

(1) AUFS

AUFS(AnotherUnionFS)是一种Union FS,是文件级的存储驱动。AUFS能透明覆盖一或多个现有文件系统的层状文件系统,把多层合并成文件系统的单层表示。简单来说就是支持将不同目录挂载到同一个虚拟文件系统下的文件系统。这种文件系统可以一层一层地叠加修改文件。无论底下有多少层都是只读的,只有最上层的文件系统是可写的。当需要修改一个文件时,AUFS创建该文件的一个副本,使用CoW将文件从只读层复制到可写层进行修改,结果也保存在可写层。在Docker中,底下的只读层就是image,可写层就是Container。结构如下图所示:


aufs

(2) Overlay

Overlay是Linux内核3.18后支持的,也是一种Union FS,和AUFS的多层不同的是Overlay只有两层:一个upper文件系统和一个lower文件系统,分别代表Docker的镜像层和容器层。当需要修改一个文件时,使用CoW将文件从只读的lower复制到可写的upper进行修改,结果也保存在upper层。在Docker中,底下的只读层就是image,可写层就是Container。结构如下图所示:


overlay

(3) Device mapper

Device mapper是Linux内核2.6.9后支持的,提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略。前面讲的AUFS和OverlayFS都是文件级存储,而Device mapper是块级存储,所有的操作都是直接对块进行操作,而不是文件。Device mapper驱动会先在块设备上创建一个资源池,然后在资源池上创建一个带有文件系统的基本设备,所有镜像都是这个基本设备的快照,而容器则是镜像的快照。所以在容器里看到文件系统是资源池上基本设备的文件系统的快照,并不有为容器分配空间。当要写入一个新文件时,在容器的镜像内为其分配新的块并写入数据,这个叫用时分配。当要修改已有文件时,再使用CoW为容器快照分配块空间,将要修改的数据复制到在容器快照中新的块里再进行修改。Device mapper 驱动默认会创建一个100G的文件包含镜像和容器。每一个容器被限制在10G大小的卷内,可以自己配置调整。结构如下图所示


devicemapper

(4) Btrfs

Btrfs被称为下一代写时复制文件系统,并入Linux内核,也是文件级级存储,但可以像Device mapper一直接操作底层设备。Btrfs把文件系统的一部分配置为一个完整的子文件系统,称之为subvolume 。那么采用 subvolume,一个大的文件系统可以被划分为多个子文件系统,这些子文件系统共享底层的设备空间,在需要磁盘空间时便从底层设备中分配,类似应用程序调用 malloc()分配内存一样。为了灵活利用设备空间,Btrfs 将磁盘空间划分为多个chunk 。每个chunk可以使用不同的磁盘空间分配策略。比如某些chunk只存放metadata,某些chunk只存放数据。这种模型有很多优点,比如Btrfs支持动态添加设备。用户在系统中增加新的磁盘之后,可以使用Btrfs的命令将该设备添加到文件系统中。Btrfs把一个大的文件系统当成一个资源池,配置成多个完整的子文件系统,还可以往资源池里加新的子文件系统,而基础镜像则是子文件系统的快照,每个子镜像和容器都有自己的快照,这些快照则都是subvolume的快照。


btrfs

当写入一个新文件时,为在容器的快照里为其分配一个新的数据块,文件写在这个空间里,这个叫用时分配。而当要修改已有文件时,使用CoW复制分配一个新的原始数据和快照,在这个新分配的空间变更数据,变结束再更新相关的数据结构指向新子文件系统和快照,原来的原始数据和快照没有指针指向,被覆盖。

(5) ZFS

ZFS 文件系统是一个革命性的全新的文件系统,它从根本上改变了文件系统的管理方式,ZFS 完全抛弃了“卷管理”,不再创建虚拟的卷,而是把所有设备集中到一个存储池中来进行管理,用“存储池”的概念来管理物理存储空间。过去,文件系统都是构建在物理设备之上的。为了管理这些物理设备,并为数据提供冗余,“卷管理”的概念提供了一个单设备的映像。而ZFS创建在虚拟的,被称为“zpools”的存储池之上。每个存储池由若干虚拟设备(virtual devices,vdevs)组成。这些虚拟设备可以是原始磁盘,也可能是一个RAID1镜像设备,或是非标准RAID等级的多磁盘组。于是zpool上的文件系统可以使用这些虚拟设备的总存储容量。


ZFS

下面看一下在Docker里ZFS的使用。首先从zpool里分配一个ZFS文件系统给镜像的基础层,而其他镜像层则是这个ZFS文件系统快照的克隆,快照是只读的,而克隆是可写的,当容器启动时则在镜像的最顶层生成一个可写层。如下图所示:


ZFS-1

当要写一个新文件时,使用按需分配,一个新的数据快从zpool里生成,新的数据写入这个块,而这个新空间存于容器(ZFS的克隆)里。
当要修改一个已存在的文件时,使用写时复制,分配一个新空间并把原始数据复制到新空间完成修改。

1.5.3 overlay和overlay2的区别

小技巧:一次创建多个子目录
$ mkdir -p $HOME/test/{bin,lib64,lib}

1.5.4 五种存储驱动的对比及适应场景

文件系统 特点 优点 缺点 适用场景
AUFS 未并入内核主线,文件级存储 Docker的第一个驱动,历史长,社区支持强,稳定,有大量的生产实践 层次多,做写时复制操作时,如果比较大且层级较低,速度会慢 适合大并发但少IO的场景
Overlay 内核主线,文件级存储 只有两层 无论修改的内容多少都会复制整个文件,对大文件的修改要比小文件消耗更多的实践 适合大并发但少IO的场景
DeviceMapper 内核主线,块级存储 无论大文件小文件,修改时只复制要修改的块,不会复制整个文件 不支持共享存储,当多个容器读同一个文件时需要复制多个副本,磁盘占用大,大量容器启停,容易导致磁盘溢出 适合IO密集型场景
Btrfs 内核主线,文件级存储 支持类似DeviceMapper一样的直接操作底层设备,允许动态添加设备 不支持共享存储,当多个容器读同一个文件时需要复制多个副本 不适合在高密度容器的PaaS化平台使用
ZFS 所有设备集中到存储池中统一管理 支持多个容器共享内存中的缓存块,适合大内存环境 Cow特性导致碎片化问题更加严重,文件在硬盘上的物理地址变得不再连续,顺序读的性能会变差 适合PaaS和高密度场景

AUFS VS Overlay

AUFS和Overlay都是联合文件系统,但AUFS有多层,而Overlay只有两层,所以在做写时复制操作时,如果文件比较大且存在比较低的层,则AUSF可能会慢一些。而且Overlay并入了linux kernel mainline,AUFS没有,所以可能会比AUFS快。但Overlay还太年轻,要谨慎在生产使用。而AUFS做为docker的第一个存储驱动,已经有很长的历史,比较的稳定,且在大量的生产中实践过,有较强的社区支持。目前开源的DC/OS指定使用Overlay。

Overlay VS Device mapper

Overlay是文件级存储,Device mapper是块级存储,当文件特别大而修改的内容很小,Overlay不管修改的内容大小都会复制整个文件,对大文件进行修改显示要比小文件要消耗更多的时间,而块级无论是大文件还是小文件都只复制需要修改的块,并不是整个文件,在这种场景下,显然device mapper要快一些。因为块级的是直接访问逻辑盘,适合IO密集的场景。而对于程序内部复杂,大并发但少IO的场景,Overlay的性能相对要强一些。

Device mapper VS Btrfs Driver VS ZFS

Device mapper和Btrfs都是直接对块操作,都不支持共享存储,表示当有多个容器读同一个文件时,需要生成多个复本,所以这种存储驱动不适合在高密度容器的PaaS平台上使用。而且在很多容器启停的情况下可能会导致磁盘溢出,造成主机不能工作。Device mapper不建议在生产使用。Btrfs在docker build可以很高效。
ZFS最初是为拥有大量内存的Salaris服务器设计的,所在在使用时对内存会有影响,适合内存大的环境。ZFS的COW使碎片化问题更加严重,对于顺序写生成的大文件,如果以后随机的对其中的一部分进行了更改,那么这个文件在硬盘上的物理地址就变得不再连续,未来的顺序读会变得性能比较差。ZFS支持多个容器共享一个缓存块,适合PaaS和高密度的用户场景。

1.5.5 几种文件系统性能对比

  • AUFS在读的方面性能相比Overlay要差一些,但在写的方面性能比Overlay要好。
  • device mapper在512M以上文件的读写性能都非常的差,但在512M以下的文件读写性能都比较好。
  • btrfs在512M以上的文件读写性能都非常好,但在512M以下的文件读写性能相比其他的存储驱动都比较差。
  • ZFS整体的读写性能相比其他的存储驱动都要差一些。

详细测试用例及测试结果数据,可以 查看Docker文件系统对比测试

小技巧:可以使用 iozone。iozone(官方网站)是一个文件系统基准测试工具,对不同操作系统的不同文件系统进行读写性能进行测试,可以测试 Read, write, re-read,re-write, read backwards, read strided, fread, fwrite, random read, pread, mmap, aio_read, aio_write 等等不同的模式下的硬盘的性能。
详细介绍可以参考 百度百科-词条iozone

推荐阅读更多精彩内容