运行时指标
Docker 统计信息
您可以使用该命令实时流式传输容器的
运行时指标。该命令支持 CPU、内存使用率、内存限制、
和网络 IO 指标。docker stats
以下是命令的输出示例docker stats
$ docker stats redis1 redis2
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
redis1 0.07% 796 KB / 64 MB 1.21% 788 B / 648 B 3.568 MB / 512 KB
redis2 0.07% 2.746 MB / 64 MB 4.29% 1.266 KB / 648 B 12.4 MB / 0 B
docker stats
参考
页面中提供了有关该命令的更多详细信息。docker stats
控制组
Linux 容器依赖于控制组,这些控制组不仅跟踪进程组,还公开有关以下内容的指标 CPU、内存和数据块 I/O 使用率。您可以访问这些指标和 同时获取网络使用情况指标。这与“纯”LXC 有关 容器,以及 Docker 容器。
控制组通过伪文件系统公开。在 Modern Distributions 中,您可以
应该在 下找到此文件系统。在该目录下,您
请参阅多个子目录,称为 、 、 等。
每个子目录实际上对应于不同的 cgroup 层次结构。/sys/fs/cgroup
devices
freezer
blkio
在较旧的系统上,控制组可能挂载在 上,而没有
不同的层次结构。在这种情况下,您不会看到子目录,
您会在该目录中看到一堆文件,可能还有一些目录
对应于现有容器。/cgroup
要找出控制组的挂载位置,您可以运行:
$ grep cgroup /proc/mounts
枚举 cgroups
cgroups 的文件布局在 v1 和 v2 之间有很大不同。
如果您的系统上存在 ,则您使用的是 v2,
否则,您使用的是 v1。
请参阅与您的 cgroup 版本对应的小节。/sys/fs/cgroup/cgroup.controllers
默认情况下,cgroup v2 用于以下分配:
- Fedora(自 31 年起)
- Debian GNU/Linux(自 11 起)
- Ubuntu(自 21.10 起)
cgroup v1
您可以查看不同的控制组子系统
系统已知、它们所属的层次结构以及它们包含的组数。/proc/cgroups
您还可以查看流程的控制组
属于。控制组显示为相对于 的根的路径
层次结构挂载点。 表示尚未将进程分配给
group,而表示该进程是
名为 的容器。/proc/<pid>/cgroup
/
/lxc/pumpkin
pumpkin
cgroup v2 版本
在 cgroup v2 主机上,的内容没有意义。
请参阅可用的控制器。/proc/cgroups
/sys/fs/cgroup/cgroup.controllers
更改 cgroup 版本
更改 cgroup 版本需要重启整个系统。
在基于 systemd 的系统上,可以通过添加到内核命令行来启用 cgroup v2。
要将 cgroup 版本恢复到 v1,您需要改为设置。systemd.unified_cgroup_hierarchy=1
systemd.unified_cgroup_hierarchy=0
如果您的系统(例如在 Fedora 上)有 command 可用,则可以按如下方式修改命令行:grubby
$ sudo grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1"
如果 command 不可用,请编辑 in 行并运行 。grubby
GRUB_CMDLINE_LINUX
/etc/default/grub
sudo update-grub
在 cgroup v2 上运行 Docker
Docker 从 Docker 20.10 开始支持 cgroup v2。 在 cgroup v2 上运行 Docker 还需要满足以下条件:
- containerd:v1.4 或更高版本
- runc:v1.0.0-rc91 或更高版本
- 内核:v4.15 及以上版本(推荐 v5.2 及以上版本)
请注意,cgroup v2 模式的行为与 cgroup v1 模式略有不同:
- 默认的 cgroup 驱动程序 () 在 v2 和 v1 上。
dockerd --exec-opt native.cgroupdriver
systemd
cgroupfs
- 默认的 cgroup 命名空间模式 () 在 v2 和 v1 上。
docker run --cgroupns
private
host
- flags 和 在 v2 上被丢弃。
docker run
--oom-kill-disable
--kernel-memory
查找给定容器的 cgroup
对于每个容器,在每个层次结构中创建一个 cgroup。上
使用旧版本的 LXC 用户空间工具的旧系统,名称为
cgroup 是容器的名称。使用较新的版本
的 LXC 工具中,cgroup 为lxc/<container_name>.
对于使用 cgroups 的 Docker 容器,容器名称为完整
容器的 ID 或长 ID。如果容器显示为 ae836c95b4c3
在 中,其长 ID 可能类似于 。您可以
用 or 查找它。docker ps
ae836c95b4c3c9e9179e0e91015512da89fdec91612f63cebae57df9a5444c79
docker inspect
docker ps --no-trunc
将所有内容放在一起以查看 Docker 的内存指标 容器,请查看以下路径:
/sys/fs/cgroup/memory/docker/<longid>/
在 cgroup v1 上,驱动程序cgroupfs
/sys/fs/cgroup/memory/system.slice/docker-<longid>.scope/
在 cgroup v1 上,驱动程序systemd
/sys/fs/cgroup/docker/<longid>/
在 cgroup v2 上,驱动程序cgroupfs
/sys/fs/cgroup/system.slice/docker-<longid>.scope/
在 cgroup v2 上,驱动程序systemd
来自 cgroups 的指标:内存、CPU、数据块 I/O
注意
本节尚未针对 cgroup v2 进行更新。 有关 cgroup v2 的更多信息,请参阅内核文档。
对于每个子系统(内存、CPU 和块 I/O),一个或 存在更多伪文件并包含统计信息。
内存指标:memory.stat
内存指标位于 cgroup 中。内存
Control Group 会增加一点开销,因为它执行非常精细的
主机上的内存使用情况的核算。因此,许多发行版
选择默认不启用它。通常,要启用它,您所拥有的
要做的是添加一些内核命令行参数: .memory
cgroup_enable=memory swapaccount=1
量度位于 pseudo-file 中。
这是它的样子:memory.stat
cache 11492564992
rss 1930993664
mapped_file 306728960
pgpgin 406632648
pgpgout 403355412
swap 0
pgfault 728281223
pgmajfault 1724
inactive_anon 46608384
active_anon 1884520448
inactive_file 7003344896
active_file 4489052160
unevictable 32768
hierarchical_memory_limit 9223372036854775807
hierarchical_memsw_limit 9223372036854775807
total_cache 11492564992
total_rss 1930993664
total_mapped_file 306728960
total_pgpgin 406632648
total_pgpgout 403355412
total_swap 0
total_pgfault 728281223
total_pgmajfault 1724
total_inactive_anon 46608384
total_active_anon 1884520448
total_inactive_file 7003344896
total_active_file 4489052160
total_unevictable 32768
前半部分(不带前缀)包含相关的统计数据
到 cgroup 中的进程,不包括子 cgroup。下半场
(带前缀)也包括 sub-cgroups。total_
total_
某些指标是 “仪表” ,或可以增加或减少的值。例如,是 cgroup 成员使用的交换空间量。
其他一些是 “counters”,或者只能上升的值,因为
它们表示特定事件的发生次数。例如,指示自创建 cgroup 以来的页面错误数。swap
pgfault
cache
- 此控制组的进程使用的内存量,可以是
与 Block 设备上的块精确关联。当您从 和 读取
写入磁盘上的文件,则此数量会增加。如果您使用
“常规”I/O(、、系统调用)以及映射文件
(带)。不过,它还考虑了挂载使用的内存
原因尚不清楚。
open
read
write
mmap
tmpfs
rss
- 与磁盘上的任何内容都不对应的内存量:stacks、 堆和匿名内存映射。
mapped_file
- 指示控制组中的进程映射的内存量。 它不会提供有关使用了多少内存的信息;它反而 告诉您它是如何使用的。
pgfault
,pgmajfault
- 指示 cgroup 的进程触发“页面
fault“和”major fault“分别是 Fault 的。当进程
访问其虚拟内存空间的一部分,该部分不存在或受保护。
如果进程有 bug 并尝试访问无效的
address (它被发送一个信号,通常用著名的消息杀死它)。后者可能在进程读取
从已换出的内存区,或对应于映射的
file:在这种情况下,内核从磁盘加载页面,并让 CPU
完成内存访问。当进程写入
Copy-on-Write 内存区:同样,内核会抢占进程,复制
Memory 页面,并在进程自己的
页面。当内核实际需要读取数据时,会发生 “重大” 故障
从磁盘。当它只是复制一个现有页面,或者分配一个空页面时,
这是一个常规的(或“轻微的”)错误。
SIGSEGV
Segmentation fault
swap
- 此 cgroup 中的进程当前使用的交换量。
active_anon
,inactive_anon
- 内核已识别的匿名内存量分别为 active 和 inactive。“匿名” 内存是未链接到磁盘页面的内存。换句话说,这相当于 rss
counter 的 Counter 进行验证。事实上,rss 计数器的定义是 + - (其中 tmpfs 是
此控制组挂载的文件系统占用的内存)。现在
“Active” 和 “Inactive” 有什么区别?页面最初是
“active”;内核会定期扫描内存和标签
一些页面显示为 “inactive”。每当再次访问它们时,它们都会
立即重新标记为 “active”。当内核内存几乎不足时,并且
是时候换出磁盘了,内核会交换 “inactive” 页面。
active_anon
inactive_anon
tmpfs
tmpfs
active_file
,inactive_file
- 缓存内存,具有 active 和 inactive ,类似于 anon 内存
以上。确切的公式是 = + + 。内核用于在
active 和 inactive sets 与用于匿名内存的 set 不同,
但一般原则是一样的。当内核需要回收内存时,
从这个池中回收一个干净的 (=未修改的) 页面更便宜,因为它
可以立即回收(虽然匿名页面和脏/修改页面
需要先写入磁盘)。
cache
active_file
inactive_file
tmpfs
unevictable
- 无法回收的内存量;一般来说,它占
已被 “锁定”的内存。它经常被加密货币使用
框架来确保密钥和其他敏感材料永远不会
换出到磁盘。
mlock
memory_limit
,memsw_limit
- 这些并不是真正的指标,而是提醒您对此施加的限制 cgroup 的第一个表示可以的最大物理内存量 由该控制组的进程使用;第二个表示 最大 RAM+swap 数量。
考虑页面缓存中的内存非常复杂。如果两个 不同控制组中的进程都读取同一个文件 (最终依赖于磁盘上的相同块),相应的 内存费用在控制组之间分配。这很好,但是 这也意味着,当 cgroup 终止时,它可能会增加 另一个 cgroup 的内存使用率,因为它们不会分摊成本 对于那些内存页。
CPU 指标:cpuacct.stat
现在我们已经介绍了内存指标,其他所有内容都已介绍
相比之下很简单。CPU 指标位于控制器中。cpuacct
对于每个容器,一个伪文件包含 CPU 使用率
由容器的进程积累,分解成 和 时间。区别在于:cpuacct.stat
user
system
user
time 是进程直接控制 CPU 的时间量, 执行流程代码。system
time 是内核代表 执行系统调用的时间 过程。
这些时间以 1/100 秒的刻度表示,也称为“用户
jiffies”。每秒有 “jiffies”,在 x86 系统上为 100。从历史上看,这正好映射到 scheduler 的数量
“ticks”,但更高的频率调度和无滴答内核使
勾号无关紧要。USER_HZ
USER_HZ
数据块 I/O 指标
块 I/O 计入控制器中。
不同的指标分散在不同的文件中。虽然您可以
在内核文档的 blkio-controller 文件中找到深入的详细信息,这里是大多数的简短列表
相关:blkio
blkio.sectors
- 包含进程读取和写入的 512 字节扇区数 cgroup 的成员,逐个设备。读取和写入合并到一个 计数器。
blkio.io_service_bytes
- 指示 cgroup 读取和写入的字节数。它有 4 个 counters 的 st,因为对于每个设备,它区分 同步与异步 I/O,以及读取与写入。
blkio.io_serviced
- 执行的 I/O 操作数,无论其大小如何。它还具有 每个设备 4 个计数器。
blkio.io_queued
- 指示当前为此 cgroup 排队的 I/O 操作数。在 换句话说,如果 cgroup 没有执行任何 I/O,则为 0。相反的是 不对。换句话说,如果没有 I/O 排队,这并不意味着 cgroup 处于空闲状态(I/O 方面)。它可以在 否则为静止设备,因此可以立即处理它们, 无需排队。此外,虽然弄清楚哪个 cgroup 是 对 I/O 子系统施加压力,请记住它是相对的 数量。即使进程组不执行更多的 I/O,其队列大小也可以 增加只是因为设备负载因其他设备而增加。
网络指标
网络指标不直接由控制组公开。有一个
对此的一个很好的解释:上下文中存在网络接口
的网络命名空间。内核可能会累积指标
关于一组进程发送和接收的数据包和字节数,但
这些指标不会很有用。您需要每个接口的指标
(因为本地接口上发生的流量并不真正计算在内)。但是,由于单个 cgroup 中的进程
可以属于多个网络命名空间,则这些指标会更难
解释:多个网络命名空间意味着多个接口,可能有多个接口,等等;所以这就是为什么没有简单的方法来收集网络
指标与控制组。lo
lo
eth0
相反,您可以从其他来源收集网络指标。
iptables
iptables 的 一个接口)可以做一些严肃的会计工作。
例如,您可以设置一个规则来考虑出站 HTTP Web 服务器上的流量:
$ iptables -I OUTPUT -p tcp --sport 80
没有 or 标志,
因此,规则只对匹配的数据包进行计数,并转到以下内容
统治。-j
-g
稍后,您可以使用以下命令检查计数器的值:
$ iptables -nxvL OUTPUT
从技术上讲,不是必需的,但它
阻止 iptables 进行 DNS 反向查找,这可能是
在这种情况下毫无用处。-n
计数器包括数据包和字节。如果要为
容器流量,您可以执行一个循环,为每个
链中的容器 IP 地址(每个方向一个)。这仅计量通过 NAT 的流量
层;您还需要添加通过 Userland 的流量
代理。for
iptables
FORWARD
然后,您需要定期检查这些计数器。如果你
碰巧使用 ,有一个很好的插件来自动化 iptables 计数器收集。collectd
接口级计数器
由于每个容器都有一个虚拟以太网接口,因此您可能需要检查
直接访问此接口的 TX 和 RX 计数器。每个容器都关联
添加到主机中的虚拟以太网接口,其名称类似于 .
不幸的是,弄清楚哪个接口对应于哪个容器,
难。vethKk8Zqi
但就目前而言,最好的方法是从 容器。为此,您可以从主机运行可执行文件 使用 IP-NETNS 的容器的网络命名空间中的环境 魔法。
该命令允许您执行任何
程序(存在于主机系统中)中的任何网络命名空间
对当前进程可见。这意味着您的房东可以
输入容器的网络命名空间,但输入容器
无法访问主机或其他对等容器。
不过,容器可以与它们的子容器进行交互。ip-netns exec
该命令的确切格式为:
$ ip netns exec <nsname> <command...>
例如:
$ ip netns exec mycontainer netstat -i
ip netns
通过以下方式查找容器
使用命名空间伪文件。每个进程都属于一个网络
namespace, 一个 PID namespace, 一个 namespace,
等,并且这些命名空间在 下具体化。例如,网络
PID 42 的命名空间由伪文件 实现。mycontainer
mnt
/proc/<pid>/ns/
/proc/42/ns/net
当您运行 时,它会
期望成为其中之一
那些伪文件。(接受符号链接。ip netns exec mycontainer ...
/var/run/netns/mycontainer
换句话说,要在 容器,我们需要:
- 找出我们想要调查的容器中任何进程的 PID;
- 创建一个从 到 的符号链接
/var/run/netns/<somename>
/proc/<thepid>/ns/net
- 执行
ip netns exec <somename> ....
查看枚举 Cgroups 了解如何查找
要测量其网络使用情况的容器内进程的 cgroup。
从那里,您可以检查名为 的伪文件,其中包含
cgroup(因此,在容器中)。选择任意一个 PID。tasks
将所有内容放在一起,如果容器的 “short ID” 保存在
环境变量 ,则可以执行此操作:$CID
$ TASKS=/sys/fs/cgroup/devices/docker/$CID*/tasks
$ PID=$(head -n 1 $TASKS)
$ mkdir -p /var/run/netns
$ ln -sf /proc/$PID/ns/net /var/run/netns/$CID
$ ip netns exec $CID netstat -i
高性能指标收集的提示
每次要更新指标时运行新进程 (相对)昂贵。如果您想在较高 分辨率和/或大量容器(想想 1000 个 containers 的 intent 上),您不希望每个 fork 新进程 时间。
下面介绍如何从单个进程收集指标。您需要
用 C 语言(或任何允许你编写
低级系统调用)。您需要使用一个特殊的系统调用 ,它允许当前进程进入任何
arbitrary 命名空间。但是,它需要一个打开的文件描述符,以
命名空间伪文件(请记住:这是 中的伪文件)。setns()
/proc/<pid>/ns/net
但是,有一个问题:您不得使此文件描述符保持打开状态。 如果这样做,当控制组的最后一个进程退出时, 命名空间未销毁,并且其网络资源(如 容器的虚拟接口)永远存在(或直到 关闭该文件描述符)。
正确的方法是跟踪每个 container,并每次重新打开 namespace 伪文件。
在容器退出时收集指标
有时,您并不关心实时指标收集,但是当 容器退出时,您想知道它有多少 CPU、内存等 使用。
Docker 使这变得困难,因为它依赖于 ,这仔细地
自行清理。定期收集指标通常更容易
intervals,这就是 LXC 插件的工作方式。lxc-start
collectd
但是,如果您仍然想在容器停止时收集统计数据, 方法如下:
对于每个容器,启动一个收集进程,并将其移动到 要通过将 PID 写入任务来监控的控制组 cgroup 的文件。收集过程应定期重新读取 tasks 文件来检查它是否是 Control Group 的最后一个进程。 (如果您还希望按照 上一节中,您还应将流程移动到相应的 network 命名空间。
当容器退出时,尝试
删除控制组。它失败了,因为控制组是
仍在使用;但这没关系。您的进程现在应该检测到它是
组中唯一剩下的。现在是收集的合适时机
您需要的所有指标!lxc-start
最后,您的进程应将自身移回根控制组
并删除容器控件组。要删除控制组,只需删除其目录。它与目录相反,因为它仍然包含文件;但
请记住,这是一个伪文件系统,因此通常的规则不适用。
清理完成后,收集过程可以安全退出。rmdir
rmdir