文章目录
- Docker数据卷管理及优化
- 为什么要使用数据卷
- bind mount数据卷
- docker managed数据卷
- Data Volume Container(数据卷容器)
- bind mount数据卷 VS docker managed数据卷
- 备份与迁移数据卷
- Docker的安全优化
- Docker的资源限制
- 限制CPU的使用
- 限制CPU的使用量
- 限制CPU的优先级
- 限制内存使用
- 限制docker的磁盘io
- Docker的安全加固
- Docker默认隔离性
- 解决Docker的默认隔离性
- 容器特权
- 容器特权的白名单
- 容器编排工具Docker Compose
- Docker Compose介绍
- Docker Compose常用命令
- Docker Compose的yml文件
- 企业实例
Docker数据卷管理及优化
Docker 数据卷是一个可供容器使用的特殊目录,它绕过了容器的文件系统,直接将数据存储在宿主机上。
目的
-
数据持久化:即使容器被删除或重新创建,数据卷中的数据仍然存在,不会丢失。
-
数据共享:多个容器可以同时挂载同一个数据卷,实现数据的共享和交互。
-
独立于容器生命周期:数据卷的生命周期独立于容器,不受容器的启动、停止和删除的影响。
为什么要使用数据卷
docker分层文件系统
-
性能差
-
生命周期与容器相同
docker数据卷
-
mount到主机中,绕开分层文件系统
-
和主机磁盘性能相同,容器删除后依然保留
-
仅限本地磁盘,不能随容器迁移
docker提供了两种卷:
-
bind mount
-
docker managed volume
bind mount数据卷
-
是将主机上的目录或文件mount到容器里
-
使用直观高效,易于理解
-
使用 -v 选项指定路径
-
-v选项指定的路径,如果不存在,挂载时会自动创建
[root@docker-node1 ~]# docker run -it --rm -v /tmp/data1:/data1 \
> -v /tmp/data1:/data2:ro \
> -v /etc/passwd:/data/passwd:ro busybox
/ #
/ # tail -n 3 /data/passwd
tcpdump:x:72:72::/:/sbin/nologin
www:x:1000:1000:www:/home/www:/bin/bash
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
/ # touch /data1/file1
/ # touch /data2/file1
touch: /data2/file1: Read-only file system
/ # exit
docker managed数据卷
-
bind mount必须指定host文件系统路径,限制了移植性
-
docker managed volume 不需要指定mount源,docker自动为容器创建数据卷目录
-
默认创建的数据卷目录都在 /var/lib/docker/volumes 中
-
如果挂载时指向容器内已有的目录,原有数据会被复制到volume中
[root@docker-node1 ~]# docker run -d --name mysql -e MYSQL_ROOT_PASSWORD='lee' mysql:5.7
9781f04a2822f2e00dbce4bda72ac97c141edd3b6f23125d4829182b25057a1d
[root@docker-node1 ~]# cd /var/lib/docker/volumes/
[root@docker-node1 volumes]# touch 8dcf7d5d58cc7202cf61d5a1a2631cc71dbe777f1c848f2b36d782b614af0fcb/_data/file
[root@docker-node1 volumes]# docker exec -it mysql bash
bash-4.2# cd /var/lib/mysql
bash-4.2# ls
auto.cnf client-key.pem ib_logfile1 mysql.sock server-cert.pem
ca-key.pem file ibdata1 performance_schema server-key.pem
ca.pem ib_buffer_pool ibtmp1 private_key.pem sys
client-cert.pem ib_logfile0 mysql public_key.pem
bash-4.2# pwd
/var/lib/mysql
bash-4.2# exit
exit
# 清理未使用的docker数据卷
[root@docker-node1 ~]# docker volume prune
WARNING! This will remove anonymous local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
0a5c2ad548fa7d7dfa133a891b9c617f6e99a30c285623f91380197532de9dc2
0bc271ea4c6bcb782f43e995904eb6d639d669b6f405ea5b91f8469a2e806ef4
3e623200a07190eb275a0316d8a0a678f4f4fe8618565795709174ef939b90ff
d4d5a0421e4ed3071b6ebc526cc75a7a04449c5ec8716e1b97d33edbe465aa8b
8474cf4d194756bf9e9436420483199562dc2d58ce05d8cbeea4e1296c188cc3
a37ec89239ce78447b97820dc965ac8e1ce051187d25008cd67f5cc86a2f3491
158111130feeb06f9ec916b0b32c303897574716c51619c26a5a2d1b3ab5845d
6828fee837d6aee6cb06517f1345004b29370aa0f0eee191e880699469df0895
edd0b7e81fdd0ee6c6ce1b6132f90cc81d791699943fed503ad450bc2e32249f
21d2821d9ced61cfd953ed00a46ed550174cf31e0a637a0e6c55e6122ca096c8
ed80a9df621126a6af226db9b92e32b811422b8c27c7e4ba5a3a427a606a176e
9ad0728e274d487c6ff966da543845bed74f5f283590a126ff893e3224d98fb9
c745cf88e4b2da376fb975bd4909c7f67c48759b3a610016e398d57820e49cb8
d66545e0cf4c3bc1ccfa6e09b454230b562aab071c01f315db0ba31c8fee1602
174856cec2fa4d9fcb911a8516401a920b399cb303232f74e51a9f1db74e5630
31ba18819e23d8a7255fbb8e840e60f7cabcf9a1110e169aef4d11c046586a05
9a1eafa00e59896512f3f110938c7094642c348a7d1572ae5120b1d7ca58b21c
b2af94b4195fd52d808a863ada827afed135f3ac4b18407c65e10d7c6f080d4b
2e5ce69c10c3102133bf32ab4daaa2c8868a860b4b20f5e4ba426bdc1263b089
69a9aad2e3ff212de21ed60abbce5ffc5e7b0d9d5bb59dfe2d46672d5c756232
8d273aa5f3e0cb8d75536b14487cd0574736b009f101e876a29ab48f0d8a4d80
Total reclaimed space: 485.9MB
# 建立数据卷
[root@docker-node1 ~]# docker volume create leevol1
leevol1
[root@docker-node1 ~]# ls -l /var/lib/docker/volumes/leevol1/_data/
total 0
# 查看数据卷
[root@docker-node1 ~]# docker volume ls
DRIVER VOLUME NAME
local 0de6a749cb43a84db4e820f54ce97110df81c6e6391302d7fecee7fefb4e62bd
local 8dcf7d5d58cc7202cf61d5a1a2631cc71dbe777f1c848f2b36d782b614af0fcb
local 8f11d628e92f8d8bd09f0f37d63146f99b53b6b7a080d3ee2901651dadea0a82
local 14fe46087475cdb40ebdf9bffdfc70d323a0c31c4271f6323b596057488e8a2a
local 51bc6a34f5767b28a04024ded851d2c66213d73225e8808a0695a1e566352e3a
local leevol1
# 使用建立的数据卷
[root@docker-node1 ~]# docker run -d --name web1 -p 80:80 -v leevol1:/usr/share/nginx/html nginx
b3f06568ac420c41720a5fc6f9a7529d7a2cded50c3e5c1a0e9944c74bd28268
[root@docker-node1 ~]# cd /var/lib/docker/volumes/leevol1/_data/
[root@docker-node1 _data]# ls
50x.html index.html
[root@docker-node1 _data]# echo leevol1 > index.html
[root@docker-node1 _data]# curl 172.25.254.100
leevol1
Data Volume Container(数据卷容器)
数据卷容器(Data Volume Container)是 Docker 中一种特殊的容器,主要用于方便地在多个容器之间共享数据卷。
# 建立数据卷容器
[root@docker-node1 ~]# docker run -d --name datavol \
> -v /tmp/data1:/data1:rw \
> -v /tmp/data2:/data2:ro \
> -v /etc/resolv.conf:/etc/hosts busybox
04bc98c3a0364db743891976478fbb6e23455721ad819f9facdc5712ca800daf
# 使用数据卷容器
[root@docker-node1 ~]# docker run -it --name test --rm --volumes-from datavol busybox
/ # ls
bin data2 etc lib proc sys usr
data1 dev home lib64 root tmp var
/ # cat /etc/resolv.conf
# Generated by Docker Engine.
# This file can be edited; Docker Engine will not make further changes once it
# has been modified.
nameserver 114.114.114.114
search example.com
# Based on host file: '/etc/resolv.conf' (legacy)
# Overrides: []
/ # exit
bind mount数据卷 VS docker managed数据卷
相同点: 两者都是 host 文件系统中的某个路径
不同点:
bind mount docker managed volume volume位置 可任意指定 /var/lib/docker/volumes/… 对已有mount point影响 隐藏并替换为volume 原有数据复制到volume 是否支持单个文件 支持 不支持,只能是目录 权限控制 可设置为只读,默认为读写权限 无控制,均为读写权限 移植性 移植性弱,与host path绑定 移植性强,无需指定host目录
备份与迁移数据卷
# 备份数据卷
# 建立容器并指定使用卷到要备份的容器
[root@docker-node1 ~]# docker run --volumes-from datavol \
> -v `pwd`:/backup busybox \ #把当前目录挂在到容器中用于和容器交互保存要备份的容器
> tar zcf /backup/data1.tar.gz /data1 #备份数据到本地
tar: removing leading '/' from member names
# 数据恢复
[root@docker-node1 ~]# docker run -it --name test -v leevol1:/data1 -v `pwd`:/backup busybox /bin/sh -c "tar zxf /backup/data1.tar.gz;/bin/sh"
/ # ls
backup data1 etc lib proc sys usr
bin dev home lib64 root tmp var
# 查看数据迁移情况
/ # cd data1/
/data1 # ls
50x.html file1 index.html
/data1 #
Docker的安全优化
Docker容器的安全性,很大程度上依赖于Linux系统自身
评估Docker的安全性时,主要考虑以下几个方面:
-
Linux内核的命名空间机制提供的容器隔离安全
-
Linux控制组机制对容器资源的控制能力安全
-
Linux内核的能力机制所带来的操作权限安全
-
Docker程序(特别是服务端)本身的抗攻击性
-
其他安全增强机制对容器安全性的影响
#在rhel9中默认使用cgroup-v2 但是cgroup-v2中不利于观察docker的资源限制情况,所以推荐使用cgroup-v1
[root@docker-node1 ~]# grubby --update-kernel=/boot/vmlinuz-$(uname -r) \
--args="systemd.unified_cgroup_hierarchy=0
systemd.legacy_systemd_cgroup_controller"
# 做完这个操作需要重启系统才生效
[root@docker-node1 ~]# reboot
- 命名空间隔离的安全
- 当docker run启动一个容器时,Docker将在后台为容器创建一个独立的命名空间。命名空间提供了最基础也最直接的隔离。
- 与虚拟机方式相比,通过Linux namespace来实现的隔离不是那么彻底。
- 容器只是运行在宿主机上的一种特殊的进程,那么多个容器之间使用的就还是同一个宿主机的操作系统内核。
- 在 Linux 内核中,有很多资源和对象是不能被 Namespace 化的,比如:磁盘等等
[root@docker-node1 ~]# docker run -d --name web nginx
add5aaf32cb0893bbbfd6f98bef8cbdb1ac294222c1d9a01b125c3ac99e0207a
[root@docker-node1 ~]# docker inspect web | grep Pid
"Pid": 4962,
"PidMode": "",
"PidsLimit": null,
# 进程的namespace
[root@docker-node1 ~]# cd /proc/4962/ns/
[root@docker-node1 ns]# ls
cgroup ipc mnt net pid pid_for_children time time_for_children user uts
- 控制组资源控制的安全
- 当docker run启动一个容器时,Docker将在后台为容器创建一个独立的控制组策略集合。
- Linux Cgroups提供了很多有用的特性,确保各容器可以公平地分享主机的内存、CPU、磁盘IO等资源。
- 确保当发生在容器内的资源压力不会影响到本地主机系统和其他容器,它在防止拒绝服务攻击(DDoS)方面必不可少
# 内存资源默认没有被隔离
[root@docker-node1 ~]# docker run -it --name test busybox
/ # free -m
total used free shared buff/cache available
Mem: 1743 483 720 7 540 1095
Swap: 2064 196 1868
/ # exit
[root@docker-node1 ~]# free -m
total used free shared buff/cache available
Mem: 1743 586 782 7 539 1157
Swap: 2063 195 1868
-
内核能力机制
- 能力机制(Capability)是Linux内核一个强大的特性,可以提供细粒度的权限访问控制。
- 大部分情况下,容器并不需要“真正的”root权限,容器只需要少数的能力即可。
- 默认情况下,Docker采用“白名单”机制,禁用“必需功能”之外的其他权限。
-
Docker服务端防护
-
使用Docker容器的核心是Docker服务端,确保只有可信的用户才能访问到Docker服务。
-
将容器的root用户映射到本地主机上的非root用户,减轻容器和主机之间因权限提升而引起的安全问题。
-
允许Docker 服务端在非root权限下运行,利用安全可靠的子进程来代理执行需要特权权限的操作。这些子进程只允许在特定范围内进行操作。
-
Docker的资源限制
Linux Cgroups
的全称是 Linux Control Group
。
-
是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。
-
对进程进行优先级设置、审计,以及将进程挂起和恢复等操作。
Linux Cgroups
给用户暴露出来的操作接口是文件系统
-
它以文件和目录的方式组织在操作系统的
/sys/fs/cgroup
路径下。 -
执行此命令查看:
mount -t cgroup
[root@docker-node1 ~]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/misc type cgroup (rw,nosuid,nodev,noexec,relatime,misc)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
在 /sys/fs/cgroup 下面有很多诸如 cpuset、cpu、 memory 这样的子目录,也叫子系统。
在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录)。
控制组下面的资源文件里填上什么值,就靠用户执行 docker run 时的参数指定。
限制CPU的使用
限制CPU的使用量
[root@docker-node1 ~]# docker run -it --rm --name test --cpu-period 100000 --cpu-quota 20000 ubuntu
root@23b81a2e99eb:/# dd if=/dev/zero of=/dev/null &
[1] 9
root@23b81a2e99eb:/# top
top - 10:52:14 up 3:39, 0 user, load average: 0.08, 0.10, 0.13
Tasks: 3 total, 2 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 1.6 us, 3.1 sy, 0.0 ni, 94.9 id, 0.0 wa, 0.4 hi, 0.0 si, 0.0 st
MiB Mem : 1743.5 total, 715.4 free, 653.1 used, 540.5 buff/cache
MiB Swap: 2064.0 total, 1868.2 free, 195.8 used. 1090.4 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9 root 20 0 2736 1536 1536 R 20.3 0.1 0:02.60 dd
1 root 20 0 4588 3840 3328 S 0.0 0.2 0:00.01 bash
10 root 20 0 8848 5248 3200 R 0.0 0.3 0:00.00 top
限制CPU的优先级
# 关闭cpu的核心,当cpu都不空闲下才会出现争抢的情况,为了实验效果可以关闭一个cpu核心
[root@docker-node1 ~]# echo 0 > /sys/devices/system/cpu/cpu1/online
[root@docker-node1 ~]# cat /proc/cpuinfo
processor : 0
vendor_id : AuthenticAMD
cpu family : 25
model : 117
model name : AMD Ryzen 7 8845H w/ Radeon 780M Graphics
stepping : 2
cpu MHz : 3792.875
cache size : 1024 KB
physical id : 0
siblings : 1
core id : 0
cpu cores : 1
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 16
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl tsc_reliable nonstop_tsc cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw topoext ssbd ibrs ibpb ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 erms invpcid avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves avx512_bf16 clzero wbnoinvd arat avx512vbmi umip pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid overflow_recov succor fsrm flush_l1d
bugs : fxsave_leak sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass srso
bogomips : 7585.75
TLB size : 3584 4K pages
clflush size : 64
cache_alignment : 64
address sizes : 45 bits physical, 48 bits virtual
power management:
# 开启容器并限制资源
# 设定cpu优先级,最大为1024,值越大优先级越高
[root@docker-node1 ~]# docker run -it --rm --cpu-shares 100 ubuntu
root@52953e2f2dcc:/# dd if=/dev/zero of=/dev/null &
[1] 9
root@52953e2f2dcc:/# top
top - 10:56:33 up 3:43, 0 user, load average: 0.89, 0.27, 0.17
Tasks: 3 total, 2 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 27.1 us, 72.1 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.8 hi, 0.0 si, 0.0 st
MiB Mem : 1743.5 total, 677.8 free, 689.6 used, 542.0 buff/cache
MiB Swap: 2064.0 total, 1868.2 free, 195.8 used. 1053.9 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9 root 20 0 2736 1408 1408 R 8.9 0.1 0:33.41 dd
1 root 20 0 4588 3968 3456 S 0.0 0.2 0:00.01 bash
10 root 20 0 8848 5376 3328 R 0.0 0.3 0:00.00 top
# 同时开启另外一个容器不限制CPU的优先级
[root@docker-node1 ~]# docker run -it --rm ubuntu
root@9fd3ca09d40d:/# dd if=/dev/zero of=/dev/null &
[1] 9
root@9fd3ca09d40d:/# top
top - 10:57:18 up 3:44, 0 user, load average: 1.48, 0.52, 0.26
Tasks: 3 total, 2 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 24.4 us, 74.8 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.8 hi, 0.0 si, 0.0 st
MiB Mem : 1743.5 total, 677.8 free, 689.5 used, 542.1 buff/cache
MiB Swap: 2064.0 total, 1868.2 free, 195.8 used. 1053.9 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9 root 20 0 2736 1536 1536 R 90.4 0.1 0:57.46 dd
1 root 20 0 4588 3968 3456 S 0.0 0.2 0:00.01 bash
10 root 20 0 8848 5248 3200 R 0.0 0.3 0:00.01 top
限制内存使用
# 开启容器并限制容器使用内存大小
[root@docker-node1 ~]# docker run -d --name test --memory 200M --memory-swap 200M nginx
079ce41fd75e279a7b543e868e341602f960ecbce08f42aa558200f900f722f5
#查看容器内存使用限制
[root@docker-node1 ~]# cd /sys/fs/cgroup/memory/docker/079ce41fd75e279a7b543e868e341602f960ecbce08f42aa558200f900f722f5/
[root@docker-node1 079ce41fd75e279a7b543e868e341602f960ecbce08f42aa558200f900f722f5]# cat memory.limit_in_bytes
209715200
[root@docker-node1 079ce41fd75e279a7b543e868e341602f960ecbce08f42aa558200f900f722f5]# cat memory.memsw.limit_in_bytes
209715200
[root@docker-node1 079ce41fd75e279a7b543e868e341602f960ecbce08f42aa558200f900f722f5]# cd ..
# 测试容器内存限制,在容器中我们测试内存限制效果不是很明显,可以利用工具模拟容器在内存中写入数据
# 在系统中/dev/shm这个目录被挂在到内存中
[root@docker-node1 ~]# dnf install libcgroup-0.41-19.el8.x86_64.rpm libcgroup-tools-0.41-19.el8.x86_64.rpm -y
[root@docker-node1 cgroup]# docker run -d --name test --rm --memory 200M --memory-swap 200M nginx
1212fe76e20ed89beafef14efb6d699246a4baa97316a8b53e5166fb09531d77
[root@docker-node1 cgroup]# cgexec -g memory:docker/1212fe76e20ed89beafef14efb6d699246a4baa97316a8b53e5166fb09531d77 dd if=/dev/zero of=/dev/shm/bigfile bs=1M count=150
150+0 records in
150+0 records out
157286400 bytes (157 MB, 150 MiB) copied, 0.198961 s, 791 MB/s
[root@docker-node1 cgroup]# cgexec -g memory:docker/1212fe76e20ed89beafef14efb6d699246a4baa97316a8b53e5166fb09531d77 dd if=/dev/zero of=/dev/shm/bigfile bs=1M count=180
180+0 records in
180+0 records out
188743680 bytes (189 MB, 180 MiB) copied, 0.0927194 s, 2.0 GB/s
[root@docker-node1 cgroup]# cgexec -g memory:docker/1212fe76e20ed89beafef14efb6d699246a4baa97316a8b53e5166fb09531d77 dd if=/dev/zero of=/dev/shm/bigfile bs=1M count=200
Killed
限制docker的磁盘io
# 指定容器使用磁盘io的速率
# /dev/nvme0n1是指定系统的磁盘,30M即每秒30M数据
[root@docker-node1 ~]# docker run -it --rm \
> --device-write-bps \
> /dev/nvme0n1:30M \
> ubuntu
# 开启容器后会发现速度和设定不匹配,是因为系统的缓存机制
root@73cc5d801377:/# dd if=/dev/zero of=bigfile
^C10242379+0 records in
10242379+0 records out
5244098048 bytes (5.2 GB, 4.9 GiB) copied, 20.8394 s, 252 MB/s
root@73cc5d801377:/# dd if=/dev/zero of=bigfile bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 0.0427228 s, 2.5 GB/s
# 设定dd命令直接写入磁盘
root@73cc5d801377:/# dd if=/dev/zero of=bigfile bs=1M count=100 oflag=direct
100+0 records in
100+0 records out
104857600 bytes (105 MB, 100 MiB) copied, 3.33415 s, 31.4 MB/s
Docker的安全加固
Docker默认隔离性
# 在系统中运行容器,我们会发现资源并没有完全隔离开
# 虽然我们限制了容器的内容使用情况,但是查看到的信息依然是系统中内存的使用信息,并没有隔离开
[root@docker-node1 ~]# free -m
total used free shared buff/cache available
Mem: 1743 623 744 7 540 1119
Swap: 2063 195 1868
[root@docker-node1 ~]# docker run -it --rm --memory 200M ubuntu
root@435e5c41ba8a:/# free -m
total used free shared buff/cache available
Mem: 1743 594 773 7 541 1149
Swap: 2063 195 1868
解决Docker的默认隔离性
LXCFS 是一个为 LXC(Linux Containers)容器提供增强文件系统功能的工具。
主要功能
- 资源可见性:
- LXCFS可以使容器内的进程看到准确的CPU、内存和磁盘I/O等资源使用信息。在没有 LXCFS时,容器内看到的资源信息可能不准确,这会影响到在容器内运行的应用程序对资源的评估和管理。
- 性能监控:
- 方便对容器内的资源使用情况进行监控和性能分析。通过提供准确的资源信息,管理员和开发人员可以更好地了解容器化应用的性能瓶颈,并进行相应的优化。
# 安装lxcfs
# 在rhel9中lxcfs是被包含在epel源中,我们可以直接下载安装包进行安装
[root@docker-node1 ~]# ls
lxcfs-5.0.4-1.el9.x86_64.rpm
lxc-libs-4.0.12-1.el9.x86_64.rpm
lxc-templates-4.0.12-1.el9.x86_64.rpm
[root@docker-node1 ~]# dnf install lxc*.rpm -y
# 运行lxcfs并解决容器隔离性
[root@docker-node1 ~]# lxcfs /var/lib/lxcfs &
[1] 7049
[root@docker-node1 ~]# Running constructor lxcfs_init to reload liblxcfs
mount namespace: 4
hierarchies:
0: fd: 5: name=systemd
1: fd: 6: blkio
2: fd: 7: memory
3: fd: 8: cpu,cpuacct
4: fd: 9: net_cls,net_prio
5: fd: 10: misc
6: fd: 11: freezer
7: fd: 12: perf_event
8: fd: 13: devices
9: fd: 14: pids
10: fd: 15: rdma
11: fd: 16: cpuset
12: fd: 17: hugetlb
Kernel supports pidfds
Kernel supports swap accounting
api_extensions:
- cgroups
- sys_cpu_online
- proc_cpuinfo
- proc_diskstats
- proc_loadavg
- proc_meminfo
- proc_stat
- proc_swaps
- proc_uptime
- proc_slabinfo
- shared_pidns
- cpuview_daemon
- loadavg_daemon
- pidfds
[root@docker-node1 ~]# docker run -it -m 256M \
> -v /var/lib/lxcfs/proc/cpuinfo:/proc/cpuinfo:rw \
> -v /var/lib/lxcfs/proc/diskstats:/proc/diskstats:rw \
> -v /var/lib/lxcfs/proc/meminfo:/proc/meminfo:rw \
> -v /var/lib/lxcfs/proc/stat:/proc/stat:rw \
> -v /var/lib/lxcfs/proc/swaps:/proc/swaps:rw \
> -v /var/lib/lxcfs/proc/uptime:/proc/uptime:rw \
> ubuntu
root@7de93af1ffc5:/# free -m
total used free shared buff/cache available
Mem: 256 1 254 0 0 254
Swap: 512 0 512
容器特权
在容器中默认情况下即使我是容器的超级用户也无法修改某些系统设定,比如网络
[root@docker-node1 ~]# docker run --rm -it busybox
/ # whoami
root
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
41: eth0@if42: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
/ # ip a a 192.168.0.100/24 dev eth0
ip: RTNETLINK answers: Operation not permitted
# 这是因为容器使用的很多资源都是和系统真实主机公用的,如果允许容器修改这些重要资源,系统的稳定性会变的非常差。
# 但是由于某些需要求,容器需要控制一些默认控制不了的资源,如何解决此问题,这时我们就要设置容器特权
#如果添加了--privileged 参数开启容器,容器获得权限近乎于宿主机的root用户
[root@docker-node1 ~]# docker run --rm -it --privileged busybox
/ # id root
uid=0(root) gid=0(root) groups=0(root),10(wheel)
/ # ip a a 192.168.0.100/24 dev eth0
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
43: eth0@if44: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.0.100/24 scope global eth0
valid_lft forever preferred_lft forever
/ # fdisk -l
Disk /dev/nvme0n1: 50 GB, 53687091200 bytes, 104857600 sectors
205603 cylinders, 255 heads, 2 sectors/track
Units: sectors of 1 * 512 = 512 bytes
Device Boot StartCHS EndCHS StartLBA EndLBA Sectors Size Id Type
/dev/nvme0n1p1 * 4,4,1 1023,254,2 2048 2099199 2097152 1024M 83 Linux
/dev/nvme0n1p2 1023,254,2 1023,254,2 2099200 104857599 102758400 48.9G 8e Linux LVM
Disk /dev/dm-0: 47 GB, 50444894208 bytes, 98525184 sectors
6132 cylinders, 255 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytes
Disk /dev/dm-0 doesn't contain a valid partition table
Disk /dev/dm-1: 2064 MB, 2164260864 bytes, 4227072 sectors
263 cylinders, 255 heads, 63 sectors/track
Units: sectors of 1 * 512 = 512 bytes
Disk /dev/dm-1 doesn't contain a valid partition table
容器特权的白名单
--privileged=true
的权限非常大,接近于宿主机的权限,为了防止用户的滥用,需要增加限制,只提供给容器必须的权限。此时Docker 提供了权限白名单的机制,使用--cap-add
添加必要的权限。
# 限制容器对网络有root权限
[root@docker-node1 ~]# docker run --rm -it --cap-add NET_ADMIN busybox
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
47: eth0@if48: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 可以设定网络
/ # ip a a 192.168.0.100/24 dev eth0
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
47: eth0@if48: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet 192.168.0.100/24 scope global eth0
valid_lft forever preferred_lft forever
# 无法磁盘管理
/ # fdisk -l
/ #
容器编排工具Docker Compose
Docker Compose介绍
Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。
其是官方的一个开源项目,托管到github上。
https://github.com/docker/compose
主要功能
-
定义服务
-
使用 YAML 格式的配置文件来定义一组相关的容器服务。每个服务可以指定镜像、端口映射、环境变量、存储卷等参数。
例如,可以在配置文件中定义一个 Web 服务和一个数据库服务,以及它们之间的连接关系。
-
-
一键启动和停止
-
通过一个简单的命令,可以启动或停止整个应用程序所包含的所有容器。这大大简化了多容器
应用的部署和管理过程。
例如,使用
docker-compose up
命令可以启动配置文件中定义的所有服务,使用docker compose down
命令可以停止并删除这些服务。
-
-
服务编排
- 可以定义容器之间的依赖关系,确保服务按照正确的顺序启动和停止。例如,可以指定数据库服务必须在 Web 服务之前启动。
- 支持网络配置,使不同服务的容器可以相互通信。可以定义一个自定义的网络,将所有相关的容器连接到这个网络上。
-
环境变量管理
-
可以在配置文件中定义环境变量,并在容器启动时传递给容器。这使得在不同环境(如开发、测试和生产环境)中使用不同的配置变得更加容易。
例如,可以定义一个数据库连接字符串的环境变量,在不同环境中可以设置不同的值。
-
工作原理
- 读取配置文件
- Docker Compose 读取 YAML 配置文件,解析其中定义的服务和参数。
- 创建容器
- 根据配置文件中的定义,Docker Compose 调用 Docker 引擎创建相应的容器。它会下载所需的镜像(如果本地没有),并设置容器的各种参数。
- 管理容器生命周期
- Docker Compose 监控容器的状态,并在需要时启动、停止、重启容器。
- 它还可以处理容器的故障恢复,例如自动重启失败的容器。
Docker Compose中的管理层
- 服务 (service) 一个应用的容器,实际上可以包括若干运行相同镜像的容器实例
- 项目 (project) 由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义
- 容器(container)容器是服务的具体实例,每个服务可以有一个或多个容器。容器是基于服务定义的镜像创建的运行实例
Docker Compose常用命令
服务管理:
docker compose up
- 启动配置文件中定义的所有服务。
- 可以使用 -d 参数在后台启动服务。
- 可以使用-f 来指定yml文件
docker compose dwon
- 停止并删除配置文件中定义的所有服务以及相关的网络和存储卷。
docker compose start
- 启动已经存在的服务,但不会创建新的服务。
docker compose stop
- 停止正在运行的服务
docker compose restart
- 重启服务
服务状态查看
docker compose ps
- 列出正在运行的服务以及它们的状态,包括容器 ID、名称、端口映射等信息。
docker compose logs
- 查看服务的日志输出。可以指定服务名称来查看特定服务的日志。
其他
docker compose exec
- 在正在运行的服务容器中执行命令。
docker compose pull
- 拉取配置文件中定义的服务所使用的镜像。
docker compose config
- 验证并查看解析后的 Compose 文件内容
Docker Compose的yml文件
Docker Compose 的 YAML 文件用于定义和配置多容器应用程序的各个服务。以下是一个基本的Docker Compose YAML 文件结构及内容解释:
服务(services)
- 服务名称(service1_name/service2_name)
- 每个服务在配置文件中都有一个唯一的名称,用于在命令行和其他部分引用该服务。
- 镜像(images)
- 指定服务所使用的 Docker 镜像名称和标签。例如, image: nginx:latest 表示使用 nginx镜像的最新版本
- 端口映射(ports)
- 将容器内部的端口映射到主机的端口,以便外部可以访问容器内的服务。例如,
-"8080:80"
表示将主机的 8080 端口映射到容器内部的 80 端口。
- 将容器内部的端口映射到主机的端口,以便外部可以访问容器内的服务。例如,
- 环境变量(environment)
- 为容器设置环境变量,可以在容器内部的应用程序中使用。例如,
VAR1: value1
设置环境变量VAR1
的值为value1
- 为容器设置环境变量,可以在容器内部的应用程序中使用。例如,
- 存储卷(volumes)
- 将主机上的目录或文件挂载到容器中,以实现数据持久化或共享。例如,
-/host/data:/container/data
将主机上的/host/data
目录挂载到容器内的/container/data
路径。
- 将主机上的目录或文件挂载到容器中,以实现数据持久化或共享。例如,
- 网络(networks)
- 将服务连接到特定的网络,以便不同服务的容器可以相互通信
- 命令(command)
- 覆盖容器启动时默认执行的命令。例如,
command: python app.py
指定容器启动时运行python app.py
命令
- 覆盖容器启动时默认执行的命令。例如,
网络(networks)
- 定义 Docker Compose 应用程序中使用的网络。可以自定义网络名称和驱动程序等属性。
- 默认情况下docker compose 在执行时会自动建立网路
存储卷(volumes)
- 定义 Docker Compose 应用程序中使用的存储卷。可以自定义卷名称和存储位置等属性。
企业实例
利用容器编排完成haproxy和nginx负载均衡架构实施
[root@docker-node1 ~]# dnf install haproxy -y --downloadonly --downloaddir=/mnt
[root@docker-node1 ~]# cd /mnt/
[root@docker-node1 mnt]# rpm2cpio haproxy-2.4.22-3.el9_3.x86_64.rpm | cpio -id
[root@docker-node1 haproxy]# mkdir /var/lib/docker/volumes/conf
[root@docker-node1 haproxy]# cp haproxy.cfg /var/lib/docker/volumes/conf/
[root@docker-node1 haproxy]# cd /var/lib/docker/volumes/conf/
[root@docker-node1 conf]# ls
haproxy.cfg
[root@docker-node1 conf]# vim haproxy.cfg
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
listen webcluster
bind *:80
balance roundrobin
server web1 webserver1:80 check inter 3 fall 3 rise 5
server web2 webserver2:80 check inter 3 fall 3 rise 5
[root@docker-node1 conf]# cd
[root@docker-node1 ~]# cd test/
[root@docker-node1 test]# vim haproxy.yml
services:
web1:
image: nginx:latest
container_name: webserver1
restart: always
expose:
- 80
volumes:
- data_web1:/usr/share/nginx/html
networks:
- internel
web2:
image: nginx:latest
container_name: webserver2
restart: always
expose:
- 80
volumes:
- data_web2:/usr/share/nginx/html
networks:
- internel
haproxy:
image: haproxy:2.3
restart: always
container_name: haproxy
ports:
- "80:80"
volumes:
- /var/lib/docker/volumes/conf/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
networks:
- internel
- extrnal
networks:
internel:
driver: bridge
extrnal:
driver: bridge
volumes:
data_web1:
name: data_web1
data_web2:
name: data_web2
[root@docker-node1 test]# docker compose -f haproxy.yml up -d
[+] Running 3/0
✔ Network test_extrnal Created 0.0s
✔ Network test_internel Created 0.0s
[+] Running 7/7_web1" Created 0.0s
✔ Network test_extrnal Created 0.0s
✔ Network test_internel Created 0.0s ✔ Volume "data_web1" Created 0.0s ✔ Volume "data_web2" Created 0.0s
✔ Container webserver2 Started 0.5s
✔ Container haproxy Started 0.7s
✔ Container webserver1 Started 0.5s
[root@docker-node1 test]# echo webserver1 > /var/lib/docker/volumes/data_web1/_data/index.html
[root@docker-node1 test]# echo webserver2 > /var/lib/docker/volumes/data_web2/_data/index.html
[root@docker-node1 test]# curl 172.25.254.100
webserver1
[root@docker-node1 test]# curl 172.25.254.100
webserver2
[root@docker-node1 test]# curl 172.25.254.100
webserver1