Jusene's Blog

Docker 容器虚拟化基础

字数统计: 3.6k阅读时长: 15 min
2017/08/26 Share

Docker

Docker是一个开源的应用容器引擎,让开发者可以打包开发的应用以及依赖包到一个可移植的容器中,然后发布到任何Linux系统上,也可以实现虚拟化,如果Java可以做到“一次编译,到处运行”,而Docker则可以做到‘构建一次,在各平台运行’,Docker以其高度可扩展性,移植性,轻量型的特点,已然成为这个时代的明星产品。

那么Docker的本身技术并算不上什么新技术,它基于LXC(LinuxX Containers),使用AUFS,而这些都是已经存在很长时间并被广泛应用的技术。而Docker做的就是将这些技术重新整合,使之更加易用。相对于彻底隔离的管理程序的虚拟化,容器技术一直被认为是不安全的,因为所有的容器虚拟出来的用户空间只存在宿主机的内核空间,在资源的隔离上及安全防御等上都可能会牵一发而动全身,而Docker的成功更加依赖现代Linux kernel上的特性,Linux kernel中的NameSpace,CGroup技术的成熟,使容器和宿主机之间的隔离更加彻底,容器有独立的网络和存储栈,还拥有了自己的资源管理能力,使同一台宿主机中的多个容器可以友好地共存。

传统的虚拟化与容器虚拟化相比:

可以很清楚的看出容器更加轻量级,降低了对宿主机的开销,可以运行更多的容器。

Docker属于用户空间的虚拟化,而所依赖的内核核心特性如下:

  • NameSpace: 名称空间,内核级别,环境隔离
    PID NameSpace: Linux 2.6.24,PID隔离
    Network NameSpace: Linux 2.6.29,网络设备、网络栈、端口等网络资源隔离
    User NameSpace: Linux 3.8,用户和用户组资源隔离
    IPC NameSpace: Linux 2.6.19,信号量、消息队列和共享内存的隔离
    UTS NameSpace: Linux 2.6.19, 主机名和域名的隔离
    Mount NameSpace: Linux 2.4.19,挂载点(文件系统)隔离

  • CGroup: Linux Control Group,控制组,内核级别,限制、控制与一个进程组群的资源
    功能:

    • Resource limitation:资源限制
    • Prioritization:优先级控制
    • Accounting:审计和统计,主要为计费
    • Control:挂起进程,恢复进程

CGroup的子系统:

  • cpu:设定CPU的限制

  • cpuacct:报告cgroup中所使用的CPU资源

  • cpuset:为cgroup中的任务分配cpu和内存资源

  • memory:设定内存的使用限制

  • devices:控制cgroup中的任务对设备的访问

  • freezer:挂起或恢复cgroup中的任务

  • net_cls:(classld),使用等级级别标识符来标识网络数据包,以实现基于tc完成对不同的cgroup中产生的流量的控制

  • perf_event:使用后使用cgroup中的任务可以进行统一的性能测试

  • hugetlb:对HugeTLB系统进行限制;内存大页

  • Docker还有一个强依赖AUFS:
    内核中有一种特性UnionFS,而AUFS被称为Adanced UFS(高级UFS),简单来说就是支持将不同的目录挂载到同一个虚拟文件系统下,并实现一种layer的概念。Aufs将挂载到同一虚拟文件系统下的多个目录分别设置成read-only,read-write以及whiteout-able权限,对read-only目录只能读,而写操作只能实施在read-write目录中。重点在于,当挂载目录的时候要严格按照各目录之间的增量关系,将被增量操作的目录优先于在它基础上增量操作的目录挂载,待所有目录挂载结束了,继续挂载一个read-write目录,如此便形成了一种层次结构。

传统的Linux加载bootfs时会先将rootfs设为read-only,然后在系统自检之后将rootfs从read-only改为read-write,然后我们就可以在rootfs上进行写和读的操作了。但Docker的镜像却不是这样,它在bootfs自检完毕之后并不会把rootfs的read-only改为read-write。而是利用union mount(UnionFS的一种挂载机制)将一个或多个read-only的rootfs加载到之前的read-only的rootfs层之上。在加载了这么多层的rootfs之后,仍然让它看起来只像是一个文件系统,在Docker的体系里把union mount的这些read-only的rootfs叫做Docker的镜像。但是,此时的每一层rootfs都是read-only的,我们此时还不能对其进行操作。当我们创建一个容器,也就是将Docker镜像进行实例化,系统会在一层或是多层read-only的rootfs之上分配一层空的read-write的rootfs。看图理解下:


Aufs是Docker最初采用的文件系统,由于Aufs未能加入到Linux内核,考虑到兼容性问题,加入了Devicemapper的支持。目前,除少数版本如Ubuntu,Docker基本运行在Devicemapper基础上。

Aufs

Aufs支持将多个目录挂载到同一个虚拟目录下,CentOS系统不支持,以下是通过Ubuntu 14.04:

1
~]# mkdir aufs test1 test2
2
~]# echo 'file1' > test1/file1
3
~]# echo 'file2' > test2/file2
4
~]# mount -t aufs -o br=/tmp/test1=ro:/tmp/test2=rw none /tmp/aufs
5
mount: warning: /tmp/aufs seems to be mounted read-only.
  • -o 指定mount传递给文件系统的参数
  • br 指定需要挂载的文件夹
  • ro/rw 指定文件的权限只读和可读写
  • none 这里没有设备,用none表示
1
~]# echo 'hello' > aufs/file1
2
bash: file1: Read-only file system
3
~]# echo 'hello' > aufs/file2
4
~]# cat aufs/file2
5
hello

如果两个文件同名在挂载会出现什么呢?

1
~]# umount /tmp/aufs
2
~]# mv test1/file1 test1/file2
3
~]# mount -t aufs -o br=/tmp/test1=ro:/tmp/test2=rw none /tmp/aufs
4
mount: warning: /tmp/aufs seems to be mounted read-only.
5
~]# ls /tmp/aufs
6
file2
7
~]# cat /tmp/aufs/file2
8
file1

由此可见,在挂载的过程中,mount命令按照给出的文件夹顺序挂载,若出现同名的文件的情况下,则以先挂载的为主,其他的不挂载。这也说明了Docker镜像为什么采用增量的方式,完全是利用aufs的特性达到了节约空间的目的,使之更加轻量化。

devicemapper

Device mapper 是Linux 2.6内核中提供的一种从逻辑设备到物理设备的映射框架机制。在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略,如条带化,镜像,快照等。

在介绍devicemapper之前,我们需要知道几个关于Linux Volume的概念,主要包括snapshot(快照),thinly-provisioned snapshot(自动精简快照)。

snapshot

snapshot是lvm提供的一种特性,它可以在不中断服务运行的情况下为the origin创建一个虚拟快照:

  • 当the origin内容发生变化时,snapshot对变化的部分做一个拷贝以用来origin进行重构。
  • 因为只对变化的部分做拷贝,所以lvm的snapshot在读操作频繁而写操作不频繁的情况下占用很少一部分空间完成特定任务。
  • 当snapshot大小耗尽或者运大于实际需求时,我们可以对其大小进行调整。
  • 当对snapshot的数据进行写操作的时候,snapshot实施相应的操作,并丢弃从the origin的拷贝,以后的操作以写操作之后snapshot中数据为准
  • 在某些发行版的linux系统下,可以使用lvconvert的–merge选项将snapshot合并会the origin

对shapshot的应用如:对数据进行实时备份,利用其可读写的特性创建snapshot供测试等等。

Thin-Provisioning

Thin-Provisioning是一项利用虚拟化方法减少物理存储部署的技术,可最大限度提升存储空间利用率。下图中展示了某位用户向服务器管理员请求分配10TB的资源的情形。实际情况中这个数值往往是峰值,根据使用情况,分配2TB就已足够。因此,系统管理员准备2TB的物理存储,并给服务器分配10TB的虚拟卷。服务器即可基于仅占虚拟卷容量1/5的现有物理磁盘池开始运行。这样的“始于小”方案能够实现更高效地利用存储容量。

Thin-provisioning Snapshot

Thin-provisioning Snapshot结合了Thin-Provisioning和Snapshot两种技术,允许多个虚拟设备同时挂载到一个数据卷以达到数据共享的目的。

  • 可以将不同的snaptshot挂载到同一个the origin上,节省了磁盘空间。
  • 当多个Snapshot挂载到了同一个the origin上,并在the origin上发生写操作时,将会触发COW操作。这样不会降低效率。
  • Thin-Provisioning Snapshot支持递归操作,即一个Snapshot可以作为另一个Snapshot的the origin,且没有深度限制。
  • 在Snapshot上可以创建一个逻辑卷,这个逻辑卷在实际写操作(COW,Snapshot写操作)发生之前是不占用磁盘空间的。

Thin-Provisioning Snapshot是作为device mapper的一个target在内核中实现的,Docker的后端存储也是类似使用devicemapper的Thin-Provisioning Snapshot的实现。

devicemapper 模式

  • loop-lvm是默认的模式,它使用os层面离散的文件来构建thin pool。该模式主要是为了使Docker能够简单的‘开箱即用’而无需任何配置。但是如果在生产环境中官方不推荐使用该模式。我们在docker info中可以看见警告:
    1
    WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.

direct-lvm是Docker推荐的生产环境的推荐模式,他使用块设备来构建精简池来存放镜像和容器的数据。

配置direct-lvm模式:

  1. 停止Docker并备份

如果Docker服务已在运行且有需要保留的镜像和容器,停服务前把相关数据给备份。个人也强烈建议如果是在生产环境使用Docker的话,拿到host的第一时间就将direct-lvm模式给配置了。

1
~]# docker info
2
Containers: 5
3
 Running: 2
4
 Paused: 0
5
 Stopped: 3
6
Images: 3
7
Server Version: 1.12.6
8
Storage Driver: devicemapper
9
 Pool Name: docker-253:0-201549152-pool
10
 Pool Blocksize: 65.54 kB
11
 Base Device Size: 10.74 GB
12
 Backing Filesystem: xfs
13
 Data file: /dev/loop0
14
 Metadata file: /dev/loop1
15
 Data Space Used: 960.2 MB
16
 Data Space Total: 107.4 GB
17
 Data Space Available: 41.52 GB
18
 Metadata Space Used: 1.466 MB
19
 Metadata Space Total: 2.147 GB
20
 Metadata Space Available: 2.146 GB
21
 Thin Pool Minimum Free Space: 10.74 GB
22
 Udev Sync Supported: true
23
 Deferred Removal Enabled: false
24
 Deferred Deletion Enabled: false
25
 Deferred Deleted Device Count: 0
26
 Data loop file: /var/lib/docker/devicemapper/devicemapper/data
27
 WARNING: Usage of loopback devices is strongly discouraged for production use. Use `--storage-opt dm.thinpooldev` to specify a custom block storage device.
28
 Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
29
 Library Version: 1.02.107-RHEL7 (2015-10-14)

可见当前模式为loop-lvm

停止docker

1
~]# systemctl stop docker
  1. 分配裸设备
1
[root@INIT ~]# fdisk -l /dev/sdb
2
3
Disk /dev/sdb: 21.5 GB, 21474836480 bytes, 41943040 sectors
4
Units = sectors of 1 * 512 = 512 bytes
5
Sector size (logical/physical): 512 bytes / 4096 bytes
6
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
7
8
[root@INIT ~]# pvcreate /dev/sdb
9
  Physical volume "/dev/sdb" successfully created
10
11
[root@INIT ~]# vgcreate docker /dev/sdb
12
  Volume group "docker" successfully created
13
14
[root@INIT ~]# vgdisplay docker
15
  --- Volume group ---
16
  VG Name               docker
17
  System ID             
18
  Format                lvm2
19
  Metadata Areas        1
20
  Metadata Sequence No  1
21
  VG Access             read/write
22
  VG Status             resizable
23
  MAX LV                0
24
  Cur LV                0
25
  Open LV               0
26
  Max PV                0
27
  Cur PV                1
28
  Act PV                1
29
  VG Size               20.00 GiB
30
  PE Size               4.00 MiB
31
  Total PE              5119
32
  Alloc PE / Size       0 / 0   
33
  Free  PE / Size       5119 / 20.00 GiB
34
  VG UUID               QRvHPD-HJZ3-w0NL-fY1v-sFpH-Qqmm-b5b7Xy

创建pool

1
[root@INIT ~]# lvcreate --wipesignatures y -n thinpool docker -l 95%VG
2
  Logical volume "thinpool" created.
3
[root@INIT ~]# lvcreate --wipesignatures y -n thinpoolmeta docker -l 1%VG 
4
  Logical volume "thinpoolmeta" created.

数据LV大小为VG的95%,元数据LV大小为VG的1%,剩余的空间用来自动扩展。

将pool装换为thinpool

1
[root@INIT ~]# lvconvert -y --zero n -c 512K --thinpool docker/thinpool --poolmetadata docker/thinpoolmeta
2
  WARNING: Converting logical volume docker/thinpool and docker/thinpoolmeta to pool's data and metadata volumes.
3
  THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.)
4
  Converted docker/thinpool to thin pool.

配置thinpool

配置池的自动扩展

1
[root@INIT ~]# vim /etc/lvm/profile/docker-thinpool.profile
2
activation {
3
        thin_pool_autoextend_threshold=80
4
        thin_pool_autoextend_percent=20
5
}

应用配置变更

1
[root@INIT ~]# lvchange --metadataprofile docker-thinpool docker/thinpool
2
  Logical volume "thinpool" changed.

状态监控检查

1
[root@INIT ~]# lvs -o+seg_monitor
2
  LV       VG     Attr       LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert Monitor  
3
  home     centos -wi-ao---- 20.16g                                                              
4
  root     centos -wi-ao---- 41.29g                                                              
5
  swap     centos -wi-ao----  2.00g                                                              
6
  thinpool docker twi-a-t--- 19.00g             0.00   0.03                             monitored
  1. 配置Docker

    1
    ExecStart后加入storage相关配置参数,如果配置了$OPTIONS也可以在对应的EnvironmentFile中加入。
    2
    [root@INIT ~]# vim /etc/sysconfig/docker-storage
    3
    DOCKER_STORAGE_OPTIONS="--storage-driver=devicemapper --storage-opt=dm.thinpooldev=/dev/mapper/docker-thinpool --storage-opt dm.use_deferred_removal=true"
  2. 清除graph driver

    1
    ~]# [root@INIT ~]# rm -rf /var/lib/docker/*

    之前已提醒数据备份,因为在这里清除graphdriver会将image,Container和volume所有数据都删除。如果不删除,则会遇到以下的错误导致docker服务起不来。

    1
    Error starting daemon: error initializing graphdriver: devmapper: Base Device UUID and Filesystem verification failed: devicemapper: Error running deviceCreate (ActivateDevice) dm_task_run failed
  3. 启动docker

    1
    [root@INIT ~]# systemctl daemon-reload
    2
    [root@INIT ~]# systemctl start docker
    3
    [root@INIT ~]# docker info
    4
    Containers: 0
    5
     Running: 0
    6
     Paused: 0
    7
     Stopped: 0
    8
    Images: 0
    9
    Server Version: 1.12.6
    10
    Storage Driver: devicemapper
    11
     Pool Name: docker-thinpool
    12
     Pool Blocksize: 524.3 kB
    13
     Base Device Size: 10.74 GB
    14
     Backing Filesystem: xfs
    15
     Data file: 
    16
     Metadata file: 
    17
     Data Space Used: 20.45 MB
    18
     Data Space Total: 20.4 GB
    19
     Data Space Available: 20.38 GB
    20
     Metadata Space Used: 61.44 kB
    21
     Metadata Space Total: 213.9 MB
    22
     Metadata Space Available: 213.8 MB
    23
     Thin Pool Minimum Free Space: 2.039 GB
    24
     Udev Sync Supported: true
    25
     Deferred Removal Enabled: true
    26
     Deferred Deletion Enabled: false
    27
     Deferred Deleted Device Count: 0
    28
     Library Version: 1.02.107-RHEL7 (2015-10-14)
    29
    Logging Driver: journald
    30
    Cgroup Driver: systemd
    31
    Plugins:
    32
     Volume: local
    33
     Network: null host bridge overlay
    34
    Swarm: inactive
    35
    Runtimes: docker-runc runc
    36
    Default Runtime: docker-runc
    37
    Security Options: seccomp selinux
    38
    Kernel Version: 3.10.0-327.el7.x86_64
    39
    Operating System: CentOS Linux 7 (Core)
    40
    OSType: linux
    41
    Architecture: x86_64
    42
    Number of Docker Hooks: 2
    43
    CPUs: 2
    44
    Total Memory: 1.793 GiB
    45
    Name: INIT
    46
    ID: N36R:QGRK:Y32B:QQ5T:QXAV:M3UN:WKRS:RFJN:APCO:HEJI:SR4U:65PP
    47
    Docker Root Dir: /var/lib/docker
    48
    Debug Mode (client): false
    49
    Debug Mode (server): false
    50
    Registry: https://index.docker.io/v1/
    51
    WARNING: bridge-nf-call-iptables is disabled
    52
    WARNING: bridge-nf-call-ip6tables is disabled
    53
    Insecure Registries:
    54
     127.0.0.0/8
    55
    [root@INIT ~]# docker pull busybox
    56
    Using default tag: latest
    57
    Trying to pull repository docker.io/library/busybox ... 
    58
    latest: Pulling from docker.io/library/busybox
    59
    add3ddb21ede: Pull complete 
    60
    Digest: sha256:b82b5740006c1ab823596d2c07f081084ecdb32fd258072707b99f52a3cb8692
    61
    [root@INIT ~]# lvs
    62
      LV       VG     Attr       LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
    63
      home     centos -wi-ao---- 20.16g                                                    
    64
      root     centos -wi-ao---- 41.29g                                                    
    65
      swap     centos -wi-ao----  2.00g                                                    
    66
      thinpool docker twi-a-t--- 19.00g             0.19   0.03

可以看见thinpool的数据使用率变大了,说明direct-lvm配置成功且正常工作了。

CATALOG
  1. 1. Docker
    1. 1.1. Aufs
    2. 1.2. devicemapper
      1. 1.2.1. snapshot
      2. 1.2.2. Thin-Provisioning
      3. 1.2.3. Thin-provisioning Snapshot
      4. 1.2.4. devicemapper 模式