©
本文档使用 PHP中文网手册 发布
您可以使用该docker stats
命令实时流式传输容器的运行时指标。该命令支持CPU,内存使用情况,内存限制和网络IO度量标准。
以下是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统计参考页面有关于docker stats
命令的更多详细信息。
Linux Containers依赖于控制组,这些组不仅跟踪进程组,还公开有关CPU,内存和块I / O使用情况的度量标准。您也可以访问这些指标并获取网络使用指标。这与“纯”LXC容器以及Docker容器有关。
控制组通过伪文件系统公开。在最近的发行版中,你应该找到这个文件系统/sys/fs/cgroup
。在该目录下,您将看到多个子目录,称为设备,冰柜,blkio等; 每个子目录实际上对应于不同的cgroup层次结构。
在较早的系统上,控制组可能会安装在/cgroup
,而没有明确的层次结构。在这种情况下,您不会看到子目录,而是会在该目录中看到一堆文件,并且可能会有一些与现有容器相对应的目录。
若要确定将控件组安装在何处,可以运行:
$ grep cgroup /proc/mounts
您可以查看/proc/cgroups
系统已知的不同控制组子系统,它们所属的层次结构以及它们包含的组数。
你也可以看看/proc/<pid>/cgroup
哪些控制组属于一个进程。控制组将显示为相对于层次结构安装点根的路径; 例如,/
意思是“这个过程没有被分配到一个特定的组中”,而/lxc/pumpkin
意味着这个过程很可能是一个被命名为pumpkin
的容器的成员
对于每个容器,将在每个层次结构中创建一个cgroup。在较旧版本的LXC userland工具的旧系统上,cgroup的名称将是容器的名称。随着更新版本的LXC工具,cgroup将会lxc/<container_name>.
对于使用cgroup的Docker容器,容器名称将是容器的完整ID或长ID。如果容器显示为ae836c95b4c3 docker ps
,则其长ID可能类似于ae836c95b4c3c9e9179e0e91015512da89fdec91612f63cebae57df9a5444c79
。你可以用docker inspect
或查找docker ps --no-trunc
。
综合考虑Docker容器的内存指标,请看看/sys/fs/cgroup/memory/docker/<longid>/
。
对于每个子系统(内存,CPU和块I / O),您将找到一个或多个包含统计信息的伪文件。
memory.stat
内存指标可在“内存”cgroup中找到。请注意,内存控制组会增加一些开销,因为它会对主机上的内存使用情况进行非常细致的记帐。因此,许多发行版默认选择不启用它。一般来说,要启用它,你所要做的就是添加一些内核命令行参数:cgroup_enable=memory swapaccount=1
。
度量标准位于伪文件memory.stat
。以下是它的样子:
cache 11492564992rss 1930993664mapped_file 306728960pgpgin 406632648pgpgout 403355412swap 0pgfault 728281223pgmajfault 1724inactive_anon 46608384active_anon 1884520448inactive_file 7003344896active_file 4489052160unevictable 32768hierarchical_memory_limit 9223372036854775807hierarchical_memsw_limit 9223372036854775807total_cache 11492564992total_rss 1930993664total_mapped_file 306728960total_pgpgin 406632648total_pgpgout 403355412total_swap 0total_pgfault 728281223total_pgmajfault 1724total_inactive_anon 46608384total_active_anon 1884520448total_inactive_file 7003344896total_active_file 4489052160total_unevictable 32768
前半部分(不含total_
前缀)包含与cgroup内进程相关的统计信息,不包括子cgroup。下半部分(带total_
前缀)也包含子cgroups。
一些指标是“计量器”,即可以增加或减少的值(例如,交换cgroup成员使用的交换空间量)。其他一些是“计数器”,即只能上升的值,因为它们代表特定事件的发生(例如,pgfault,它表示自创建cgroup以来发生的页面错误的数量;该数字永远不会减少)。
Metric | 描述 |
---|---|
cache | 此控制组的进程使用的内存量可以与块设备上的块精确关联。当你读取和写入磁盘上的文件时,这个数量会增加。如果您使用“常规”I / O(打开,读取,写入系统调用)以及映射文件(使用mmap),情况就会如此。它也解释了tmpfs坐骑使用的内存,但原因尚不清楚。 |
RSS | 与磁盘上的任何内容不对应的内存量:堆栈,堆和匿名内存映射。 |
mapped_file | 指示控制组中的进程映射的内存量。它不会告诉你有多少内存被使用; 它会告诉你它是如何使用的。 |
pgfault,pgmajfault | 指示cgroup的进程分别触发“页面错误”和“严重错误”的次数。当进程访问不存在或受保护的部分虚拟内存空间时,会发生页面错误。如果进程有问题并尝试访问无效地址(它将被发送一个SIGSEGV信号,通常使用着名的分段错误消息将其杀死),前者可能发生。当进程从已被换出的内存区读取或者对应于映射文件时,后者可能发生:在这种情况下,内核将从磁盘加载页面,并让CPU完成内存访问。当进程写入写时复制内存区域时也会发生:同样,内核将抢占进程,复制内存页面,并在页面的进程自己的副本上恢复写入操作。内核实际上必须从磁盘读取数据时才会发生“重大”故障。当它只需复制现有页面或分配一个空白页面时,它就是一个常规(或“次要”)错误。| |
swap | 此cgroup中进程当前使用的交换量。 |
active_anon,inactive_anon | 内核已识别的匿名内存数量分别处于活动状态和非活动状态。“匿名”内存是未链接到磁盘页面的内存。换句话说,这就是上述rss计数器的等价物。实际上,rss计数器的定义是active_anon + inactive_anon - tmpfs(其中,tmpfs是由此控制组装载的tmpfs文件系统使用的内存量)。现在,“主动”和“非主动”之间有什么区别?页面最初是“活动的”; 并定期将内核扫描到内存中,并将某些页面标记为“不活动”。每当他们再次访问时,他们立即被重新标记为“活跃”。当内核几乎内存不足,并且需要时间换出磁盘时,内核将交换“非活动”页面。 |
active_file,inactive_file | 高速缓冲存储器,具有与上述匿名存储器相似的活动和非活动状态。确切的公式是cache = active_file + inactive_file + tmpfs。内核用于在活动和非活动集之间移动内存页的确切规则与用于匿名内存的确切规则不同,但一般原则相同。请注意,当内核需要回收内存时,从该池回收干净(=未修改)页面会更便宜,因为它可以立即回收(而匿名页面和脏/修改页面必须先写入磁盘) 。 |
unevictable | 无法回收的内存量; 一般来说,它会占用已被mlock“锁定”的内存。它通常被加密框架用来确保秘密密钥和其他敏感材料永远不会换出到磁盘。 |
memory_limit,memsw_limit | 这些并不是真正的指标,但是提醒了应用于此cgroup的限制。第一个表示该控制组的进程可以使用的最大物理内存量; 第二个表示RAM +交换的最大数量。 |
记录页面缓存中的内存非常复杂。如果不同控制组中的两个进程都读取同一文件(最终依靠磁盘上的相同块),则相应的内存费用将在控制组之间分配。这很好,但这也意味着当一个cgroup被终止时,它可能会增加另一个cgroup的内存使用率,因为它们不再为这些内存页面分摊成本。
cpuacct.stat
现在我们已经介绍了内存度量标准,其他一切都会看起来非常简单。CPU指标将在cpuacct
控制器中找到。
对于每个容器,你会发现一个伪文件cpuacct.stat
,载有集装箱的过程中积累的CPU使用率,之间分解user
和system
时间。如果您对区分不熟悉,user
是进程直接控制CPU的时间(即执行进程代码),并且system
是CPU代表这些进程执行系统调用的时间。
这些时间以1/100秒的刻度表示。实际上,它们是以“用户jiffies”表示的。每秒有USER_HZ
“jiffies”,在x86系统上USER_HZ
是100。这用于精确地映射每秒调度器“ticks”的数量; 但随着更高频率调度的出现以及无滴答内核的出现,内核滴答的数量不再相关。无论如何,它主要出于传统和兼容性的原因。
块I/O在blkio
控制器。不同的指标分散在不同的文件中。而您可以在BLKIO控制器在内核文档中,下面列出了一些最相关的文件:
Metric | 描述 |
---|---|
blkio.sectors | 包含由cgroup的进程成员逐个设备读取和写入的512字节扇区数。读取和写入被合并在一个计数器中。 |
blkio.io_service_bytes | 指示由cgroup读取和写入的字节数。它每个设备有4个计数器,因为对于每个设备,它区分同步I / O和异步I / O,以及读取与写入。 |
blkio.io_serviced | 不论其大小如何,执行的I / O操作的数量。它也有每个设备4个计数器。 |
blkio.io_queued | 指示当前为此cgroup排队的I / O操作的数量。换句话说,如果cgroup没有执行任何I / O操作,则这将为零。请注意,相反情况并非如此。换句话说,如果没有排队的I / O,这并不意味着cgroup是空闲的(I / O方式)。它可以在其他静态设备上进行纯同步读取,因此可以立即处理它们,而无需排队。此外,尽管找出哪个cgroup正在对I / O子系统施加压力是有帮助的,但请记住它是一个相对数量。即使进程组没有执行更多的I / O,其队列大小也会因为其他设备的负载增加而增加。 |
网络指标不直接由控制组公开。有一个很好的解释:网络接口存在于网络命名空间的上下文中。内核可能会累积有关一组进程发送和接收的数据包和字节的度量标准,但这些度量标准不会很有用。您需要每个接口的度量标准(因为在本地lo
接口上发生的流量并不真正计数)。但是由于单个cgroup中的进程可能属于多个网络名称空间,因此这些度量标准很难解释:多个网络名称空间意味着多个lo
接口,可能有多个eth0
接口等; 所以这就是为什么没有简单的方法来收集控制组的网络指标。
相反,我们可以从其他来源收集网络度量:
IPtables(或者说,iptables只是一个接口的netfilter框架)可以做一些严肃的会计。
例如,您可以设置一个规则来说明Web服务器上的出站HTTP流量:
$ iptables -I OUTPUT -p tcp --sport 80
没有-j
或-g
标志,所以规则将只计算匹配的数据包并转到以下规则。
稍后,您可以通过以下方法检查计数器的值:
$ iptables -nxvL OUTPUT
从技术上讲,-n
不是必需的,但它将阻止iptables执行DNS反向查找,在此场景中可能没有用处。
计数器包括数据包和字节。如果您想为此类容器流量设置度量标准,则可以执行一个for
循环以iptables
在FORWARD
链中为每个容器IP地址(每个方向一个)添加两条规则。这将只计量通过NAT层的流量; 您还必须添加通过用户级代理的流量。
然后,您需要定期检查这些计数器。如果你碰巧用collectd
,有一个不错的插件若要自动化iptables计数器集合,请执行以下操作。
因为每个容器都有一个虚拟以太网接口,所以您可能需要直接检查该接口的TX和RX计数器。您将注意到,每个容器都与主机中的虚拟以太网接口相关联,名称如下vethKk8Zqi
.不幸的是,找出哪个接口对应于哪个容器是困难的。
但现在,最好的方法是从容器中检查指标。为了达到这个目的,你可以使用ip-netns magic在容器的网络命名空间内的主机环境中运行一个可执行文件。
ip-netns exec
命令将允许您在当前进程可见的任何网络名称空间内执行任何程序(存在于主机系统中)。这意味着您的主机将能够输入您的容器的网络名称空间,但您的容器将无法访问主机,也不能访问它们的兄弟容器。尽管如此,容器将能够“看到”并影响其子容器。
命令的确切格式是:
$ ip netns exec <nsname> <command...>
例如:
$ ip netns exec mycontainer netstat -i
ip netns
通过使用命名空间伪文件找到“mycontainer”容器。每个进程属于一个网络名称空间,一个PID名称空间,一个mnt
名称空间等,并且这些名称空间在/proc/<pid>/ns/
下具体化。例如,PID 42的网络名称空间由伪文件/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。从那里,您可以检查名为的伪文件tasks
,其中包含控件组(即容器中)的PID。选择他们中的任何一个。
将所有内容组合在一起,如果容器的“短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个),则不需要每次都分配一个新进程。
以下是如何从单个流程收集指标。您必须使用C编写公制收集器(或任何允许执行低级别系统调用的语言)。你需要使用一个特殊的系统调用,setns()
它允许当前进程输入任意的命名空间。然而,它需要一个打开的文件描述符到命名空间伪文件(记住:这是伪文件/proc/<pid>/ns/net
)。
然而,有一个问题:你不能保持这个文件描述符打开。如果这样做,当控制组的最后一个进程退出时,命名空间不会被销毁,并且其网络资源(如容器的虚拟接口)将一直保留(或直到关闭该文件描述符)。
正确的方法是跟踪每个容器的第一个PID,每次重新打开名称空间伪文件。
有时,您并不关心实时度量集合,但是当容器退出时,您想知道它使用了多少CPU、内存等等。
Docker使它变得困难,因为它依赖于lxc-start
它,它会在它自己之后仔细清理,但它仍然是可能的。定期收集指标通常更容易(例如,每隔一分钟,使用collectd LXC插件)并依靠它来代替。
但是,如果您仍然希望在容器停止时收集统计数据,下面为方法:
对于每个容器,启动一个收集过程,并通过将其PID写入cgroup的任务文件,将其移至要监控的控制组。收集过程应定期重新读取任务文件以检查它是否是控制组的最后一个过程。(如果您还想按前一节中的说明收集网络统计信息,则还应该将过程移至适当的网络名称空间。)
当容器退出时,lxc-start
会尝试删除控制组。它会失败,因为控制组仍在使用中; 但没关系。现在您的过程应该检测到它是该组中剩下的唯一一个。现在是收集您需要的所有指标的适当时机!
最后,你的过程应该移回到根控制组,并删除容器控制组。删除一个控制组,只是rmdir
是目录。由于仍然包含文件,因此它对目录不直观; 但请记住这是一个伪文件系统,所以通常的规则不适用。清理完成后,收集过程可以安全地退出。