docker的冷门高级玩法(1)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: docker的冷门高级玩法(1)

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


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
NoSQL Java Redis
Docker【Java 高级】4
Docker【Java 高级】4
84 0
|
6月前
|
Java Linux 网络架构
Docker【Java 高级】3
Docker【Java 高级】3
56 0
|
6月前
|
Java 应用服务中间件 Docker
Docker【Java 高级】2
Docker【Java 高级】2
547 0
|
6月前
|
数据可视化 Java 应用服务中间件
Docker【Java 高级】1
Docker【Java 高级】1
82 0
|
7月前
|
Ubuntu 网络协议 Linux
Docker高级网络实践
Docker中libnetwork提供的4种驱动,它们各有千秋,但实际上每一种方式都有一定的局限性。
129 0
|
7月前
|
存储 SpringCloudAlibaba 运维
docker高级篇:镜像原理和Dockerfile
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件
100 0
docker高级篇:镜像原理和Dockerfile
|
9月前
|
运维 Java Linux
Docker高级(完结)
Docker高级(完结)
|
存储 安全 Linux
docker的冷门高级玩法(2)
docker的冷门高级玩法(2)
|
消息中间件 Docker 容器
|
运维 前端开发 jenkins
Devops 开发运维高级篇之Jenkins+Docker+SpringCloud微服务持续集成——部署方案优化
之前我们做的方案部署都是只能选择一个微服务部署并只有一台生产服务器,每个微服务只有一个实例,容错率低 如何去解决?
Devops 开发运维高级篇之Jenkins+Docker+SpringCloud微服务持续集成——部署方案优化

相关产品

  • 云迁移中心