Docker 高级操作
文章目录
Docker 高级操作
1. 容器的进程
2. 命名空间
3. chroot
4. cgroups
4.1 进程的CPU统计信息
4.2 进程的内存配置
4.3 如何配置cgroups?
5. Seccomp / AppArmor
6. Capabilities
7. 容器镜像
8. 创建空镜像
9. 不使用Dockerfile创建镜像
1. 容器的进程
$ docker run -d --name=db redis:alpine $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e2edc005ea6e redis:alpine "docker-entrypoint.s…" 31 seconds ago Up 30 seconds 6379/tcp db #Docker容器启动一个名为redis-server的进程。在主机上,我们可以看到所有正在运行的进程,包括由Docker启动的进程。 $ ps aux | grep redis-server 999 1099 0.3 1.1 29156 11316 ? Ssl 08:50 0:00 redis-server *:6379 docker top db UID PID PPID C STIME TTY TIME CMD 999 1099 1083 0 08:50 ? 00:00:00 redis-server *:6379 #将列出所有子进程。 $ pstree -c -p -A $(pgrep dockerd) dockerd(679)-+-docker-containe(717)-+-docker-containe(1083)-+-redis-server(1099)-+-{bio_aof_fsync}(1134) | | | |-{bio_close_file}(1133) | | | |-{bio_lazy_free}(1135) | | | `-{jemalloc_bg_thd}(1136) | | |-{docker-containe}(1084) | | |-{docker-containe}(1085) | | |-{docker-containe}(1086) | | |-{docker-containe}(1087) | | |-{docker-containe}(1088) | | `-{docker-containe}(1089) | |-{docker-containe}(718) | |-{docker-containe}(719) | |-{docker-containe}(720) | |-{docker-containe}(721) | |-{docker-containe}(728) | |-{docker-containe}(757) | |-{docker-containe}(758) | `-{docker-containe}(766) |-{dockerd}(704) |-{dockerd}(705) |-{dockerd}(706) |-{dockerd}(713) |-{dockerd}(714) |-{dockerd}(715) |-{dockerd}(716) |-{dockerd}(734) `-{dockerd}(1047) $ DBPID=$(pgrep redis-server) $ echo Redis is $DBPID Redis is 1099 $ ls /proc 1 13 17 214 28 4 56 66 754 bus filesystems kpagecount partitions thread-self 10 130 170 215 29 473 57 67 8 cgroups fs kpageflags sched_debug timer_list 1083 131 18 22 3 475 58 674 844 cmdline interrupts loadavg schedstat timer_stats 1099 132 19 220 30 483 59 679 85 consoles iomem locks scsi tty 11 133 2 228 306 485 592 68 858 cpuinfo ioports mdstat self uptime 1172 134 20 23 309 5 6 698 86 crypto irq meminfo slabinfo version 12 135 200 235 31 52 60 7 87 devices kallsyms misc softirqs version_signature 124 14 203 24 32 53 61 707 9 diskstats kcore modules stat vmallocinfo 125 141 205 25 33 530 62 717 951 dma keys mounts swaps vmstat 126 15 21 26 34 54 63 72 957 driver key-users mtrr sys zoneinfo 127 16 210 264 35 540 64 725 acpi execdomains kmsg net sysrq-trigger 129 169 213 27 36 55 65 750 buddyinfo fb kpagecgroup pagetypeinfo sysvipc #每个进程都在不同的文件中定义了自己的配置和安全设置 $ ls /proc/$DBPID attr cmdline environ io mem ns pagemap schedstat stat timers autogroup comm exe limits mountinfo numa_maps personality sessionid statm uid_map auxv coredump_filter fd loginuid mounts oom_adj projid_map setgroups status wchan cgroup cpuset fdinfo map_files mountstats oom_score root smaps syscall clear_refs cwd gid_map maps net oom_score_adj sched stack task #例如,您可以查看和更新为该流程定义的环境变量 $ cat /proc/$DBPID/environ *:6379 $ docker exec -it db env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=e2edc005ea6e TERM=xterm REDIS_VERSION=6.2.5 REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-6.2.5.tar.gz REDIS_DOWNLOAD_SHA=4b9a75709a1b74b3785e20a6c158cab94cf52298aa381eea947a678a60d551ae HOME=/root
2. 命名空间
容器的基本组成部分之一是名称空间。名称空间的概念是限制进程可以看到和访问系统的某些部分,比如其他网络接口或进程。
当容器启动时,容器运行时(如Docker)将为进程创建新的命名空间。通过在它自己的Pid命名空间中运行进程,它将看起来像系统上的唯一进程。
可用的命名空间有:
Mount (mnt)
Process ID (pid)
Network (net)
Interprocess Communication (ipc)
UTS (hostnames)
User ID (user)
Control group (cgroup)
在不使用Docker等运行时的情况下,进程仍然可以在自己的命名空间内运行。一个可以提供帮助的工具是unshare。
$ unshare --help Usage: unshare [options] <program> [<argument>...] Run a program with some namespaces unshared from the parent. Options: -m, --mount[=<file>] unshare mounts namespace -u, --uts[=<file>] unshare UTS namespace (hostname etc) -i, --ipc[=<file>] unshare System V IPC namespace -n, --net[=<file>] unshare network namespace -p, --pid[=<file>] unshare pid namespace -U, --user[=<file>] unshare user namespace -f, --fork fork before launching <program> --mount-proc[=<dir>] mount proc filesystem first (implies --mount) -r, --map-root-user map current user to root (implies --user) --propagation slave|shared|private|unchanged modify mount propagation in mount namespace -s, --setgroups allow|deny control the setgroups syscall in user namespaces -h, --help display this help and exit -V, --version output version information and exit
使用unshare
,可以启动进程并让它创建一个新的名称空间,比如Pid。通过从主机取消Pid名称空间的共享,看起来bash提示符是唯一运行的进程:
$ sudo unshare --fork --pid --mount-proc bash $ ps PID TTY TIME CMD 1 pts/0 00:00:00 bash 9 pts/0 00:00:00 ps $ exit exit
名称空间是磁盘上的索引节点位置。这允许进程shared/reused
相同的名称空间,从而允许它们进行查看和交互。
#列出容器所有的名称空间 $ ls -lha /proc/$DBPID/ns/ total 0 dr-x--x--x 2 999 packer 0 Sep 27 08:50 . dr-xr-xr-x 9 999 packer 0 Sep 27 08:50 .. lrwxrwxrwx 1 999 packer 0 Sep 27 09:04 cgroup -> cgroup:[4026531835] lrwxrwxrwx 1 999 packer 0 Sep 27 08:52 ipc -> ipc:[4026532157] lrwxrwxrwx 1 999 packer 0 Sep 27 08:52 mnt -> mnt:[4026532155] lrwxrwxrwx 1 999 packer 0 Sep 27 08:50 net -> net:[4026532160] lrwxrwxrwx 1 999 packer 0 Sep 27 08:52 pid -> pid:[4026532158] lrwxrwxrwx 1 999 packer 0 Sep 27 08:52 user -> user:[4026531837] lrwxrwxrwx 1 999 packer 0 Sep 27 08:52 uts -> uts:[4026532156]
另一个工具nsenter
用于将进程附加到现有的命名空间。对调试有用。
$ nsenter --target $DBPID --mount --uts --ipc --net --pid ps aux PID USER TIME COMMAND 1 redis 0:02 redis-server *:6379 16 root 0:00 ps aux
在Docker中,可以使用语法container:<container-name>
共享这些名称空间。例如,下面的命令将nginx连接到DB命名空间。
$ docker run -d --name=web --net=container:db nginx:alpine WEBPID=$(pgrep nginx | tail -n1) $ echo nginx is $WEBPID $ cat /proc/$WEBPID/cgroup 11:hugetlb:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 10:perf_event:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 9:cpu,cpuacct:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 8:cpuset:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 7:freezer:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 6:memory:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 5:pids:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 4:devices:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 3:net_cls,net_prio:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 2:blkio:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 1:name=systemd:/docker/42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1
虽然网络已被共享,但它仍将作为名称空间列出。
$ ls -lha /proc/$WEBPID/ns/ total 0 dr-x--x--x 2 systemd-network systemd-journal 0 Sep 27 09:10 . dr-xr-xr-x 9 systemd-network systemd-journal 0 Sep 27 09:07 .. lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 cgroup -> cgroup:[4026531835] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 ipc -> ipc:[4026532225] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 mnt -> mnt:[4026532223] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 net -> net:[4026532160] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 pid -> pid:[4026532226] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 user -> user:[4026531837] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 uts -> uts:[4026532224]
但是,这两个进程的网络名称空间指向相同的位置。
$ ls -lha /proc/$WEBPID/ns/ | grep net dr-x--x--x 2 systemd-network systemd-journal 0 Sep 27 09:10 . dr-xr-xr-x 9 systemd-network systemd-journal 0 Sep 27 09:07 .. lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 cgroup -> cgroup:[4026531835] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 ipc -> ipc:[4026532225] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 mnt -> mnt:[4026532223] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 net -> net:[4026532160] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 pid -> pid:[4026532226] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 user -> user:[4026531837] lrwxrwxrwx 1 systemd-network systemd-journal 0 Sep 27 09:10 uts -> uts:[4026532224] $ ls -lha /proc/$DBPID/ns/ | grep net lrwxrwxrwx 1 999 packer 0 Sep 27 08:50 net -> net:[4026532160]
3. chroot
容器进程的一个重要部分是能够拥有独立于主机的不同文件。这就是我们如何基于不同的操作系统在我们的系统上运行不同的Docker映像。
Chroot允许进程在父操作系统的不同根目录下启动。这允许不同的文件出现在根目录中
4. cgroups
CGroups限制了进程可以消耗的资源数量。这些cgroup是在/proc目录中的特定文件中定义的值。
需要查看映射关系,使用命令:
$ cat /proc/$DBPID/cgroup 11:hugetlb:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 10:perf_event:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 9:cpu,cpuacct:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 8:cpuset:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 7:freezer:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 6:memory:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 5:pids:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 4:devices:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 3:net_cls,net_prio:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 2:blkio:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 1:name=systemd:/docker/e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0
这些被映射到磁盘上的其他cgroup目录:
$ ls /sys/fs/cgroup/ blkio cpuacct cpuset freezer memory net_cls,net_prio perf_event systemd cpu cpu,cpuacct devices hugetlb net_cls net_prio pids
4.1 进程的CPU统计信息
CPU统计数据和使用情况也存储在一个文件中!
$ cat /sys/fs/cgroup/cpu,cpuacct/docker/$DBID/cpuacct.stat user 139 system 144
这里还定义了CPU共享限制。
$ cat /sys/fs/cgroup/cpu,cpuacct/docker/$DBID/cpu.shares 1024
4.2 进程的内存配置
所有用于容器内存配置的Docker cgroups都存储在:
$ ls /sys/fs/cgroup/memory/docker/ 42e8d3ecb8137817669b58fd33c2b6e133bae8237513c7ed8af0a49a936248d1 memory.kmem.usage_in_bytes cgroup.clone_children memory.limit_in_bytes cgroup.event_control memory.max_usage_in_bytes cgroup.procs memory.move_charge_at_immigrate e2edc005ea6ef33fededdb8f7b58162665c81552a29a2ea777b8b9d05bd393d0 memory.numa_stat memory.failcnt memory.oom_control memory.force_empty memory.pressure_level memory.kmem.failcnt memory.soft_limit_in_bytes memory.kmem.limit_in_bytes memory.stat memory.kmem.max_usage_in_bytes memory.swappiness memory.kmem.slabinfo memory.usage_in_bytes memory.kmem.tcp.failcnt memory.use_hierarchy memory.kmem.tcp.limit_in_bytes notify_on_release memory.kmem.tcp.max_usage_in_bytes tasks memory.kmem.tcp.usage_in_bytes
4.3 如何配置cgroups?
Docker的属性之一是控制内存限制的能力。这是通过cgroup设置完成的。
默认情况下,容器对内存没有限制。我们可以通过docker stats
命令查看。
$ docker stats db --no-stream CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS e2edc005ea6e db 0.17% 6.754MiB / 992.1MiB 0.68% 1.3kB / 0B 0B / 0B 5
内存引号存储在一个名为memory.limit_in_bytes
通过写入文件,我们可以改变进程的限制
$ echo 8000000 > /sys/fs/cgroup/memory/docker/$DBID/memory.limit_in_bytes
如果将文件读回去,您将注意到它已被转换为7999488。
$ cat /sys/fs/cgroup/memory/docker/$DBID/memory.limit_in_bytes 7999488
当再次检查Docker Stats时,进程的内存限制现在是7.629M
$ docker stats db --no-stream CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS e2edc005ea6e db 0.19% 6.176MiB / 992.1MiB 0.62% 1.3kB / 0B 61.4kB / 0B 5