$ docker stats db --no-stream
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
a125f186bfbe db 0.20% 8.277MiB / 992.1MiB 0.83% 1.3kB / 0B 0B / 0B 4
$ docker stats db --no-stream
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
a125f186bfbe db 0.19% 7.52MiB / 7.629MiB 98.57% 1.3kB / 0B 233kB / 0B 4
进程(process)
容器(container)只是应用了附加配置的普通Linux进程。启动以下的Redis容器,我们可以用来了解幕后的情况。
Docker容器启动一个名为redis-server的进程。我们可以查看所有正在运行的进程,包括Docker启动的进程。
Docker可以通过以下方式帮助我们识别有关进程的信息,包括进程ID(PID)、父进程ID(PPID)。
上面命令输出如下:
父进程ID是什么呢?使用
ps aux | grep <ppid>
找到父进程ID,有可能是容器(Containerd)。这里我输入
ps aux | grep 1094
,得到如下结果:pstree
命令将会列出所有子进程,使用pstree -c -p -A $(pgrep dockerd)
查看Docker进程树输出结果如下:
你亲眼所见了吧,从Linux角度来看,这些是标准进程,并且与我们系统上的其他进程具有相同的属性。
进程目录(Process Directory)
Linux只是一系列不可意思的文件和内容,这使得探索了解幕后发生的事情变得很有意思。
每个进程的配置在
/proc
目录中定义。如果知道进程ID,则可以配置标识(identify)目录。下面的命令将列出
/proc
的所有内容,并存储Redis PID已供后面使用。每个进程在不同文件中定义了自己的配置和安全设置
ls /proc/$DBPID
命令查看我们Redis Server的进程配置文件输出如下举个例子,你可以查看和更新该进程的环境变量
cat /proc/$DBPID/environ
, 输出如下docker exec -it db env
通过Docker容器查看环境变量,输出如下命名空间(Namespace)
容器的基本组成部分之一就是名称空间。名称空间的概念是为了限制哪些进程可以看到和访问系统的某些部分,例如其他网络接口或进程。
启动容器后,容器运行时(例如Docker)将创建新的名称空间以对进程进行沙箱处理。通过在它自己的PID 命名空间(Pid namespace)运行一个进程,它将看起来像是系统上唯一的进程。
可用的命名空间是:
Mount (mnt)
Process ID (pid)
Network (net)
Interprocess Communication (ipc)
UTS (hostnames)
User ID (user)
Control group (cgroup)
查看更多Linux Namespace信息
Unshare可以启动容器的进程
在不使用诸如Docker之类的运行时的情况下,进程仍可以在其自己的名称空间中运行。一种帮助工具是unshare。
使用unshare,可以启动进程并创建新的名称空间,例如Pid。通过从主机 unshare Pid命名空间,bash提示似乎是机器上唯一运行的进程。看起来好像bash是计算机上唯一运行的进程。
当我们共享一个命名空间时会发生什么?
在幕后,命名空间是磁盘上的inode位置。这允许进程共享/重用相同的名称空间,从而允许它们查看和交互。
列出所有命名空间
另一个工具·NSEnter·用于将进程附加到现有的命名空间。用于调试目的
使用Docker,可以使用以下语法共享这些命名空间
container:<container-name>
。例如,下面的命令会将nginx连接到数据库命名空间。网络共享后,它仍将作为命名空间列出
但是,两个进程的net命名空间指向相同的位置。
Chroot
容器进程的重要部分是拥有独立于主机的不同文件的能力。这就是我们可以基于系统上运行的不同操作系统拥有不同的Docker映像的方式。
Chroot允许进程从不同的根目录启动到父操作系统。这允许不同的文件出现在根目录中。
Cgroups (Control Groups)
CGroup限制了进程可以消耗的资源量。这些cgroup是在/ proc目录内的特定文件中定义的值。
运行一下命令查看这些映射
这些映射到磁盘上其他cgroup目录,位于:
进程的CPU统计信息是什么?
CPU统计信息和使用情况也存储在文件中!
CPU份额限制也在此处定义
容器内存配置的所有Docker cgroup都存储在以下位置:
每个目录都根据Docker分配的容器ID进行分组。
如何配置cgroup?
Docker的特性之一是能够控制内存限制。这是通过cgroup设置完成的,默认情况下,容器对内存没有限制。我们可以通过docker stats命令查看。
内存引用存储在一个名为
memory.limit_in_bytes
的文件中。通过写入文件,我们可以更改进程的限制。如果你再去读它,你会看到已经被转换为了7999488
再次检查Docker Stats时,该进程的内存限制现在为7.629M
Seccomp / AppArmor
Linux的所有操作都是通过syscall完成的。内核有330个系统调用,它们执行诸如读取文件,关闭句柄和检查访问权限之类的操作
AppArmor是一个应用程序定义的配置文件,它描述了进程可以访问系统的哪些部分。
可以通过以下方式查看分配给流程的当前AppArmor配置文件
Docker的默认AppArmor配置文件是
docker-default (enforce)
在Docker 1.13之前,它将AppArmor配置文件存储在/etc/apparmor.d/docker-default(在Docker启动时被覆盖,因此用户无法修改它)。在v1.13之后,Docker现在在tmpfs中生成docker-default,使用apparmor_parser将其加载到内核中,然后删除该文件。
这是它的模板连接
Seccomp提供了限制可以进行哪些系统调用,阻止安装内核模块或更改文件权限等方面的功能。
默认的Docker允许调用
当分配给一个进程时,这意味着该进程将限于系统调用的一部分。如果它尝试调用被阻止的系统调用,则会收到错误“不允许操作”。
SecComp的状态也在文件中定义
该flag的含义是:0:禁用 1:严格 2:过滤
Capabilities
Capabilities是有关进程或用户有权执行的操作的分组。这些功能可能涵盖多个系统调用或操作,例如更改系统时间或主机名
状态文件还包含功能标志。一个进程可以丢弃尽可能多的功能以确保其安全性。
这些标志被存储为位掩码,可以用capsh解码