暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Docker数据卷管理和资源限制

牧羊人的方向 2021-03-03
351

本文简要介绍了Docker容器对数据卷管理以及CPU、内存和IO等系统资源限制方式


1、Docker数据卷管理

数据卷是Docker容器保存数据的方式,跟bind mounts方式相比,有以下优势:

  1. 数据卷更容易备份迁移

  2. 使用Docker CLI或者Docker API对数据卷进行管理

  3. 数据卷可以在多个容器间共享和复用

  4. Volumes driver可以将卷内容存储在远端主机上

  5. 可以使用Docker填充新的数据卷内容

  6. 数据卷内容存在Docker容器之外,并不会增加容器本身大小

1.1 创建和管理卷

1)创建卷

[root@tango-01 ~]# docker volume create my-volume
my-volume

2)List卷

[root@tango-01 ~]# docker volume ls
DRIVER VOLUME NAME
local my-volume

3)Inspect volume查看卷详细信息

[root@tango-01 ~]# docker volume inspect my-volume
[
{
"CreatedAt": "2020-10-06T20:25:50+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
"Name": "my-volume",
"Options": {},
"Scope": "local"
}
]

4)Remove volume

[root@tango-01 ~]#  docker volume rm my-volume
my-volume

5)使用-v选项添加一个数据卷

[root@tango-01 docker]# docker run -d  --name voltest   -v my-vol1:/data nginx:latest
966f73c1e518adfd115b5bb3321effc9b5692a10a64a2dbf992e533465dc516c
[root@tango-01 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
966f73c1e518 nginx:latest "/docker-entrypoint." 6 seconds ago Up 5 seconds 80/tcp voltest
[root@tango-01 docker]# docker volume ls
DRIVER VOLUME NAME
local my-vol1

创建数据卷绑定到到新建容器,新建容器中会创建/data数据卷,如果卷不存在,Docker会创建一个新的volume。

>>挂载时创建卷<<

1)挂载卷

[root@tango-01 docker]# docker run -d -p 80:80 -v data:/usr/share/nginx/html nginx:latest
66bc9c834237cc2f0bf47e63a7f3a2eb0ce51a6689309a6f740dc268403a44ff
[root@tango-01 docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
66bc9c834237 nginx:latest "/docker-entrypoint." 6 seconds ago Up 6 seconds 0.0.0.0:80->80/tcp mystifying_banzai

容器内站点目录: usr/share/nginx/html

2)在宿主机写入数据,查看

[root@tango-01 ]# echo "hello tango" >/data/index.html
[root@tango-01 ]# curl 192.168.112.10
hello tango

3)设置共享卷,使用同一个卷启动一个新的容器

[root@tango-01 ]# docker run -d -p 8080:80 -v data:/usr/share/nginx/html nginx:latest
b2cb700cd9937674e6d08326ede94932855d59d327c90427ba027903d9c90e61
[root@tango-01 ]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b2cb700cd993 nginx:latest "/docker-entrypoint." 6 seconds ago Up 4 seconds 0.0.0.0:8080->80/tcp charming_wright
66bc9c834237 nginx:latest "/docker-entrypoint." About a minute ago Up About a minute 0.0.0.0:80->80/tcp mystifying_banzai
[root@tango-01 ]# curl 192.168.112.10:8080
hello tango

4)查看卷列表

[root@tango-01 ]# docker volume ls
DRIVER VOLUME NAME
local 4a77c195a70ff39c151ad75367d41f7db1f48de06e0ebb0333189724ca6c389a
local 73cca56105d152363bf63789bb3093443ec239559029caffc66da1a78257f200
local 548de7d0ba3d0a8c85a4b25e1234ba1b2a5891068a3062858825389a3ee4fc42
local my-vol1

>>创建卷后挂载<<

1)使用卷创建

[root@tango-01 ]# docker run -d -p 9080:80 -v my-vol1:/usr/share/nginx/html nginx:latest
43be86f4ec307e509c36d4145de3cd874c7d8adacff53e32f2a994953b86f7a0

2)宿主机测试

[root@tango-01 ]# echo 'hello tango' > var/lib/docker/volumes/my-vol1/_data/index.html
[root@tango-01 ]# curl 192.168.112.10:9080
hello tango

3)设置卷

[root@tango-01 ]# docker run  -d  -P  --volumes-from 43be86f4ec30 nginx:latest
5acebbac45d74890819ab36aa38241f2d5fcd0673d7624bf4a55894d3e11efcf
[root@tango-01 ]#
[root@tango-01 ]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5acebbac45d7 nginx:latest "/docker-entrypoint." 5 seconds ago Up 4 seconds 0.0.0.0:32768->80/tcp upbeat_einstein
43be86f4ec30 nginx:latest "/docker-entrypoint." 9 minutes ago Up 8 minutes 0.0.0.0:9080->80/tcp elated_lederberg
b2cb700cd993 nginx:latest "/docker-entrypoint." 15 minutes ago Up 14 minutes 0.0.0.0:8080->80/tcp charming_wright
66bc9c834237 nginx:latest "/docker-entrypoint." 16 minutes ago Up 16 minutes 0.0.0.0:80->80/tcp mystifying_banzai
[root@tango-01 ]# curl 192.168.112.10:32768
hello tango

1.2 备份、恢复或迁移数据卷

1)数据卷备份

[root@tango-01 ]# docker run -v testdata --name voltest ubuntu bin/bash
[root@tango-01 ]# docker run --rm --volumes-from voltest -v $(pwd):/backup ubuntu tar cvf backup/test.tar testdata
tar: Removing leading `/' from member names
/testdata/

启动一个新的容器并且从voltest容器中挂载卷,然后挂载当前目录到容器中为backup,并备份test卷中所有的数据为test.tar,执行完成之后删除容器--rm,此时备份就在当前的目录下,名为test.tar。

注意:后面的/data是数据卷的目录路径(即数据卷创建时在容器里的路径)

2)数据卷恢复或迁移

可以恢复给同一个容器或者另外的容器,新建容器并解压备份文件到新的容器数据卷

[root@tango-01 /]# docker run -v testdata --name voltest1 ubuntu /bin/bash
[root@tango-01 /]# docker run --rm --volumes-from voltest1 -v $(pwd):/backup ubuntu bash -c "cd testdata && tar xvf backup/test.tar --strip 1"

恢复之前的文件到新建卷中,执行完后自动删除容器

>>数据卷备份恢复操作实战<<

1)先创建一个容器voltest,包含两个数据卷/var/volume1和/var/volume2(这两个目录是在容器里的数据卷路径)

[root@tango-01 /]# docker run -t -i -v var/volume1 -v var/volume2 --name voltest ubuntu bin/bash
root@881631b3f8c5:

根据Docker的数据持久化之数据卷容器可知,上面创建的voltest数据卷容器挂载了/var/volume1和/var/volume2两个目录。

2)在数据卷里写些数据,以供测试

root@881631b3f8c5:/# cd var/volume1
root@881631b3f8c5:/var/volume1# echo "test1" > test1
root@881631b3f8c5:/var/volume1# echo "test11" > test11
root@881631b3f8c5:/var/volume1# echo "test111" > test111
root@881631b3f8c5:/var/volume1# ls
test1 test11 test111
root@881631b3f8c5:/var/volume1# cd var/volume2
root@881631b3f8c5:/var/volume2# echo "test2" > test2
root@881631b3f8c5:/var/volume2# echo "test22" > test22
root@881631b3f8c5:/var/volume2# echo "test222" > test222
root@881631b3f8c5:/var/volume2# ls
test2 test22 test222

3)进行数据卷的备份操作

为了利用数据卷容器备份,使用--volumes-from标记来创建一个加载voltest容器卷的容器,并从主机挂载当前目录到容器的/backup目录。并备份voltest卷中的数据,执行完成之后删除容器--rm,此时备份就在当前的目录下了。

a) 备份voltest容器中的/var/volume1数据卷数据

[root@tango-01 ]# docker run -i -t --volumes-from voltest -v $(pwd):/backup ubuntu tar cvf backup/backup1.tar var/volume1
tar: Removing leading `/' from member names
/var/volume1/
/var/volume1/test1
/var/volume1/test11
/var/volume1/test111

b) 备份voltest容器中的/var/volume2数据卷数据

[root@tango-01 ]# docker run -i -t --volumes-from voltest -v $(pwd):/backup ubuntu tar cvf backup/backup2.tar var/volume2
tar: Removing leading `/' from member names
/var/volume2/
/var/volume2/test2
/var/volume2/test22
/var/volume2/test222

c) 备份voltest容器中的/var/volume1和/var/volume2数据卷数据

[root@tango-01 ]# docker run -i -t --volumes-from voltest -v $(pwd):/backup ubuntu tar cvf backup/backup.tar var/volume1 var/volume2
tar: Removing leading `/' from member names
/var/volume1/
/var/volume1/test1
tar: Removing leading `/' from hard link targets
/var/volume1/test11
/var/volume1/test111
/var/volume2/
/var/volume2/test2
/var/volume2/test22
/var/volume2/test222
[root@tango-01 ]# ls -l
total 68
-rw-r--r-- 1 root root 10240 Oct 6 22:04 backup1.tar
-rw-r--r-- 1 root root 10240 Oct 6 22:05 backup2.tar
-rw-r--r-- 1 root root 10240 Oct 6 22:06 backup.tar

这样,数据卷容器中的数据就备份完成了

4)恢复数据给同一个容器

a) 为了测试效果,先删除数据卷

[root@tango-01 ]# docker start voltest
voltest
[root@tango-01 ]# docker attach voltest
root@881631b3f8c5:/# ls var/volume1
test1 test11 test111
root@881631b3f8c5:/# ls var/volume2
test2 test22 test222
root@881631b3f8c5:/# rm -rf var/volume1
rm: cannot remove '/var/volume1': Device or resource busy
root@881631b3f8c5:/# rm -rf var/volume2
rm: cannot remove '/var/volume2': Device or resource busy
root@881631b3f8c5:/# ls var/volume1
root@881631b3f8c5:/# ls var/volume2

b) 进行数据卷恢复,恢复数据卷中的所有数据:

[root@tango-01 /]# docker run --rm --volumes-from voltest -v $(pwd):/backup ubuntu tar xvf backup/backup.tar -C 
var/volume1/
var/volume1/test1
var/volume1/test11
var/volume1/test111
var/volume2/
var/volume2/test2
var/volume2/test22
var/volume2/test222

注意-C后面的路径,这个路径表示将数据恢复到容器里的路径。命令中用"/",即表示将backup.tar中的数据解压到容器的/路径下。后面跟什么路径,就解压到这个路径下,因此这里用"/"。

c) 再次到容器里查看,发现数据卷里的数据已经恢复了

[root@tango-01 ]# docker start voltest
voltest
[root@tango-01 ]# docker attach voltest
root@881631b3f8c5:/# ls var/volume1
test1 test11 test111
root@881631b3f8c5:/# ls var/volume2
test2 test22 test222

5)恢复数据给另外的容器,新建容器并解压备份文件到新的容器数据卷

即新建一个容器voltest1,将上面备份的数据卷数据恢复到这个新容器里

[root@tango-01 ]# docker run -t -i -v var/volume1 -v var/volume2 --name voltest1 ubuntu bin/bash
root@7acb459cbe1c:/# ls var/volume1
root@7acb459cbe1c:/# ls var/volume2

恢复数据到新的容器voltest1中:

[root@tango-01 /]# docker run --rm --volumes-from voltest1 -v $(pwd):/backup ubuntu tar xvf backup/backup.tar -C 
var/volume1/
var/volume1/test1
var/volume1/test11
var/volume1/test111
var/volume2/
var/volume2/test2
var/volume2/test22
var/volume2/test222
[root@tango-01 ]# docker start voltest1
voltest1
[root@tango-01 ]# docker attach voltest1
root@7acb459cbe1c:/# ls var/volume1
test1 test11 test111
root@7acb459cbe1c:/# ls var/volume2
test2 test22 test222

注:新容器创建时挂载的数据卷路径最好是和之前备份的数据卷路径一致

6)新建容器挂载的数据卷只是备份数据卷的一部分,那么恢复的时候也只是恢复一部分数据。如下,新容器创建时只挂载/var/volume1

[root@tango-01 ]# docker run -t -i -v var/volume1 --name voltest2 ubuntu bin/bash
root@e2c453c81390:/# ls var/volume1
root@e2c453c81390:/# ls var/volume2
ls: cannot access '/var/volume2': No such file or directory

恢复数据到新的容器voltest2中:

[root@tango-01 ]# docker run --rm --volumes-from voltest2 -v $(pwd):/backup ubuntu tar xvf backup/backup.tar -C 
var/volume1/
var/volume1/test1
var/volume1/test11
var/volume1/test111
var/volume2/
var/volume2/test2
var/volume2/test22
var/volume2/test222
root@e2c453c81390:/# ls var/volume1
test1 test11 test111

查看容器,发现只恢复了/var/volume1的数据,/var/volume2数据没有恢复,因为没有容器创建时没有挂载这个。

7)如果新容器创建时挂载的数据卷目录跟之前备份的路径不一致

[root@tango-01 ]# docker run -t -i -v var/volume3 --name voltest3 ubuntu bin/bash
root@06bfbfc830a7:/# ls var/volume3

如果解压时-C后面跟的路径不是容器挂载的容器,那么数据恢复不了,如下

[root@tango-01 /]# docker run --rm --volumes-from voltest3 -v $(pwd):/backup ubuntu tar xvf backup/backup.tar -C 
var/volume1/
var/volume1/test1
var/volume1/test11
var/volume1/test111
var/volume2/
var/volume2/test2
var/volume2/test22
var/volume2/test222

发现容器内数据没有恢复

root@06bfbfc830a7:/# ls var/volume3
root@06bfbfc830a7:/#

但是如果解压时-C后面跟的是容器挂载的路径,数据就能正常恢复

[root@tango-01 ]# docker run --rm --volumes-from voltest3 -v $(pwd):/backup ubuntu tar xvf backup/backup.tar -C var/volume3
var/volume1/
var/volume1/test1
var/volume1/test11
var/volume1/test111
var/volume2/
var/volume2/test2
var/volume2/test22
var/volume2/test222

发现容器内数据已经恢复了

root@06bfbfc830a7:/# ls var/volume3
var
root@06bfbfc830a7:/# ls var/volume3/var
volume1 volume2
root@06bfbfc830a7:/# ls /var/volume3/var/volume1
test1 test11 test111
root@06bfbfc830a7:/# ls /var/volume3/var/volume2
test2 test22 test222
root@06bfbfc830a7:/#

2、Docker容器资源限制

2.1 Namespace资源隔离

Docker使用Linux namespace技术实现容器间的资源隔离

>>以PID namespace为例<<

1)启动一个容器

[root@tango-01 /]# docker run -it  --name pidtest ubuntu /bin/bash

2)查看容器中的进程id(可以看到/bin/sh的pid=1)

root@e9fcfb4c6f17:/# ps
PID TTY TIME CMD
1 pts/0 00:00:00 bash
8 pts/0 00:00:00 ps

3)查看宿主机中的该/bin/sh的进程id

[root@tango-01 /]# ps -ef|grep ubuntu
root 3079 1671 0 14:48 pts/0 00:00:00 docker run -it --name pidtest ubuntu /bin/bash
root 3146 2387 0 14:49 pts/1 00:00:00 grep --color=auto ubuntu

可以看到,在Docker里最开始执行的/bin/sh,就是这个容器内部的第1号进程(PID=1),而在宿主机上看到它的PID=3709。这就意味着,前面执行的/bin/sh,已经被Docker隔离在了一个跟宿主机完全不同的世界当中。这就是Docker在启动一个容器(创建一个进程)时使用了PID namespace

2.2 Cgroup资源限制

Cgroup是Control Groups的缩写,是Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如 cpu、memory、磁盘IO等等) 的机制。Docker 通过内核的 cgroups 来做容器的资源限制;包括CPU、内存、磁盘三大方面。

2.2.1 内存限制

Docker 提供的内存限制功能有以下几点:

  • 容器能使用的内存和交换分区大小。

  • 容器的核心内存大小。

  • 容器虚拟内存的交换行为。

  • 容器内存的软性限制。

  • 是否杀死占用过多内存的容器。

  • 容器被杀死的优先级

执行docker run命令时能使用的和内存限制相关的所有选项如下:

2.2.2 CPU限制

docker run命令和CPU限制相关的所有选项如下:

其中--cpuset-cpus用于设置容器可以使用的 vCPU 核。-c,--cpu-shares用于设置多个容器竞争 CPU 时,各个容器相对能分配到的 CPU 时间比例。--cpu-period和--cpu-quata用于绝对设置容器能使用 CPU 时间。

>>CPU限制例子<<

1)配置控制组

[root@tango-01 cgroup]# cd /sys/fs/cgroup/cpu
[root@tango-01 cpu]# mkdir testcpulimit
[root@tango-01 cpu]# cd testcpulimit/
[root@tango-01 testcpulimit]# ls
cgroup.clone_children cgroup.procs cpuacct.usage cpu.cfs_period_us cpu.rt_period_us cpu.shares notify_on_release
cgroup.event_control cpuacct.stat cpuacct.usage_percpu cpu.cfs_quota_us cpu.rt_runtime_us cpu.stat tasks
[root@tango-01 testcpulimit]# cat /sys/fs/cgroup/cpu/testcpulimit/cpu.cfs_quota_us
-1
[root@tango-01 testcpulimit]# cat /sys/fs/cgroup/cpu/testcpulimit/cpu.cfs_period_us
100000

创建的这个目录testcpulimit就称为一个“控制组”,操作系统会在新创建的目录下,自动生成该子系统对应的资源限制文件。可以看到testlimit控制组里的CPU quota还没有任何限制(即:-1),CPU period则是默认的100000us。

2)配置一个只能使用30% cpu的限制,即长度为cfs_period的一段时间内,只能被分配到总量为cfs_quota的CPU时间。

[root@tango-01 testcpulimit]# echo 30000 > /sys/fs/cgroup/cpu/testcpulimit/cpu.cfs_quota_us
[root@tango-01 testcpulimit]# cat /sys/fs/cgroup/cpu/testcpulimit/cpu.cfs_quota_us
30000

3)在容器中执行脚本

[root@tango-01 /]# docker run -it  --name cputest ubuntu /bin/bash
root@be59c323f3ad:/#
root@be59c323f3ad:/# while : ; do : ; done &
[1] 10
root@be59c323f3ad:/# top
top - 07:24:47 up 1:04, 0 users, load average: 0.08, 0.44, 0.31
Tasks: 3 total, 2 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 30.0 us, 0.3 sy, 0.0 ni, 69.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 976.3 total, 136.2 free, 440.4 used, 399.7 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 362.4 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
10 root 20 0 4232 748 180 R 30.3 0.1 3:39.46 bash
1 root 20 0 4232 2208 1640 S 0.0 0.2 0:00.13 bash
11 root 20 0 6100 1812 1292 R 0.0 0.2 0:00.07 top

4)使用cgroup限制该进程的cpu

[root@tango-01 testcpulimit]# echo 3564 > /sys/fs/cgroup/cpu/testcpulimit/tasks
[root@tango-01 testcpulimit]#
[root@tango-01 testcpulimit]# top
top - 15:23:21 up 1:03, 2 users, load average: 0.38, 0.59, 0.34
Tasks: 130 total, 2 running, 128 sleeping, 0 stopped, 0 zombie
%Cpu(s): 29.6 us, 0.3 sy, 0.0 ni, 70.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 999696 total, 138896 free, 378192 used, 482608 buff/cache
KiB Swap: 2097148 total, 2097148 free, 0 used. 370456 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3564 root 20 0 4232 748 180 R 29.9 0.1 3:13.66 bash

看到使用刚才创建的testcpulimit控制组,将cpu被限制到了30%左右

2.2.3 磁盘IO配额控制

相对于CPU和内存的配额控制,docker对磁盘IO的控制相对不成熟,大多数都必须在有宿主机设备的情况下使用。主要包括以下参数:

>>磁盘IO配额控制示例<<

1)blkio-weight

要使–blkio-weight生效,需要保证IO的调度算法为CFQ。可以使用下面的方式查看:

[root@tango-01 /]# cat /sys/block/sda/queue/scheduler
noop [deadline] cfq

使用下面的命令创建两个–blkio-weight值不同的容器:

[root@tango-01 /]# docker run -ti --rm --blkio-weight 10 ubuntu
[root@tango-01 /]# docker run -ti --rm --blkio-weight 1000 ubuntu

在容器中同时执行下面的dd命令,进行测试:

time dd if=/dev/zero of=test.out bs=1M count=1024 oflag=direct

最终输出如下所示:

##--blkio-weight 10
[root@tango-01 /]# docker run -ti --rm --blkio-weight 10 ubuntu
root@8f5bd8c7b853:/# time dd if=/dev/zero of=test.out bs=1M count=1024 oflag=direct
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 10.6883 s, 100 MB/s

real 0m10.690s
user 0m0.001s
sys 0m0.633s
root@8f5bd8c7b853:/#
##--blkio-weight 1000
[root@tango-01 testcpulimit]# docker run -ti --rm --blkio-weight 1000 ubuntu
root@0248a935fa36:/# time dd if=/dev/zero of=test.out bs=1M count=1024 oflag=direct
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 9.74697 s, 110 MB/s

real 0m9.839s
user 0m0.001s
sys 0m0.673s
root@0248a935fa36:/#

2)device-write-bps

使用下面的命令创建容器,并执行命令验证写速度的限制。

docker run -tid name disk1 device-write-bps /dev/sda:1mb ubuntu

通过dd来验证写速度,输出如下图示:

[root@tango-01 /]# docker run -tid --name disk1 --device-write-bps /dev/sda:1mb ubuntu
33c0cac7c8966e2d16850f9239b2e354c169a275199ed2c5900dfe1959232c19
[root@tango-01 /]# docker exec -ti disk1 bash
root@33c0cac7c896:/# dd if=/dev/zero of=test.out bs=1M count=100 oflag=direct
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 100.003 s, 1.0 MB/s

可以看到容器的写磁盘速度被成功地限制到了1MB/s。


参考资料

  1. https://docs.docker.com/storage/volumes/

  2. https://www.cnblogs.com/kevingrace/p/6238195.html

  3. https://www.cnblogs.com/zhuochong/p/9728383.html

  4. https://blog.csdn.net/songcf_faith/article/details/82749011

  5. https://blog.csdn.net/songcf_faith/article/details/82748987

文章转载自牧羊人的方向,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论