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模式:
- 停止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 | [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 |
配置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"
清除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
启动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配置成功且正常工作了。