learn.lianglianglee.com/专栏/容器实战高手课/06 容器CPU(2):如何正确地拿到容器CPU的开销?.md.html
2022-05-11 18:52:13 +08:00

865 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>06 容器CPU2如何正确地拿到容器CPU的开销.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/容器实战高手课/00 开篇词 一个态度两个步骤,成为容器实战高手.md">00 开篇词 一个态度两个步骤,成为容器实战高手.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/01 认识容器:容器的基本操作和实现原理.md">01 认识容器:容器的基本操作和实现原理.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/02 理解进程1为什么我在容器中不能kill 1号进程.md">02 理解进程1为什么我在容器中不能kill 1号进程.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/03 理解进程2为什么我的容器里有这么多僵尸进程.md">03 理解进程2为什么我的容器里有这么多僵尸进程.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/04 理解进程3为什么我在容器中的进程被强制杀死了.md">04 理解进程3为什么我在容器中的进程被强制杀死了.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/05 容器CPU1怎么限制容器的CPU使用.md">05 容器CPU1怎么限制容器的CPU使用.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/容器实战高手课/06 容器CPU2如何正确地拿到容器CPU的开销.md">06 容器CPU2如何正确地拿到容器CPU的开销.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/07 Load Average加了CPU Cgroup限制为什么我的容器还是很慢.md">07 Load Average加了CPU Cgroup限制为什么我的容器还是很慢.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/08 容器内存:我的容器为什么被杀了?.md">08 容器内存:我的容器为什么被杀了?.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/09 Page Cache为什么我的容器内存使用量总是在临界点.md">09 Page Cache为什么我的容器内存使用量总是在临界点.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/10 Swap容器可以使用Swap空间吗.md">10 Swap容器可以使用Swap空间吗.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/11 容器文件系统:我在容器中读写文件怎么变慢了.md">11 容器文件系统:我在容器中读写文件怎么变慢了.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/12 容器文件Quota容器为什么把宿主机的磁盘写满了.md">12 容器文件Quota容器为什么把宿主机的磁盘写满了.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/13 容器磁盘限速:我的容器里磁盘读写为什么不稳定.md">13 容器磁盘限速:我的容器里磁盘读写为什么不稳定.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/14 容器中的内存与IO容器写文件的延时为什么波动很大.md">14 容器中的内存与IO容器写文件的延时为什么波动很大.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/15 容器网络我修改了procsysnet下的参数为什么在容器中不起效.md">15 容器网络我修改了procsysnet下的参数为什么在容器中不起效.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/16 容器网络配置1容器网络不通了要怎么调试.md">16 容器网络配置1容器网络不通了要怎么调试.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/17 容器网络配置2容器网络延时要比宿主机上的高吗.md">17 容器网络配置2容器网络延时要比宿主机上的高吗.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/18 容器网络配置3容器中的网络乱序包怎么这么高.md">18 容器网络配置3容器中的网络乱序包怎么这么高.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/19 容器安全1我的容器真的需要privileged权限吗.md">19 容器安全1我的容器真的需要privileged权限吗.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/20 容器安全2在容器中我不以root用户来运行程序可以吗.md">20 容器安全2在容器中我不以root用户来运行程序可以吗.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐01 案例分析怎么解决海量IPVS规则带来的网络延时抖动问题.md">加餐01 案例分析怎么解决海量IPVS规则带来的网络延时抖动问题.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐02 理解perf怎么用perf聚焦热点函数.md">加餐02 理解perf怎么用perf聚焦热点函数.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐03 理解ftrace1怎么应用ftrace查看长延时内核函数.md">加餐03 理解ftrace1怎么应用ftrace查看长延时内核函数.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐04 理解ftrace2怎么理解ftrace背后的技术tracepoint和kprobe.md">加餐04 理解ftrace2怎么理解ftrace背后的技术tracepoint和kprobe.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐05 eBPF怎么更加深入地查看内核中的函数.md">加餐05 eBPF怎么更加深入地查看内核中的函数.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐06 BCC入门eBPF的前端工具.md">加餐06 BCC入门eBPF的前端工具.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/加餐福利 课后思考题答案合集.md">加餐福利 课后思考题答案合集.md.html</a>
</li>
<li>
<a href="/专栏/容器实战高手课/结束语 跳出舒适区,突破思考的惰性.md">结束语 跳出舒适区,突破思考的惰性.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>06 容器CPU2如何正确地拿到容器CPU的开销</h1>
<p>你好,我是程远。今天我们聊一聊,如何正确地拿到容器 CPU 的开销。</p>
<p>为啥要解决这个问题呢,还是来源于实际工作中的需要。</p>
<p>无论是容器的所有者还是容器平台的管理者,我们想要精准地对运行着众多容器的云平台做监控,快速排查例如应用的处理能力下降,节点负载过高等问题,就绕不开容器 CPU 开销。因为 CPU 开销的异常,往往是程序异常最明显的一个指标。</p>
<p>在一台物理机器或者虚拟机里,如果你想得到这个节点的 CPU 使用率,最常用的命令就是 top 了吧top 一下子就能看到整个节点当前的 CPU 使用情况。</p>
<p>那么在容器里top 命令也可以做到这点吗?想要知道答案,我们还是得实际动手试一试。</p>
<h2>问题重现</h2>
<p>实际上,你在使用容器的时候,如果运行 top 命令来查看当前容器总共使用了多少 CPU你肯定马上就会失望了。</p>
<p>这是因为我们在容器中运行 top 命令,虽然可以看到容器中每个进程的 CPU 使用率,但是 top 中&quot;%Cpu(s)&quot;那一行中显示的数值,并不是这个容器的 CPU 整体使用率,而是容器宿主机的 CPU 使用率。</p>
<p>就像下面的这个例子,我们在一个 12 个 CPU 的宿主机上,启动一个容器,然后在容器里运行 top 命令。</p>
<p>这时我们可以看到,容器里有两个进程 threads-cpu总共消耗了 200% 的 CPU2 CPU Usage&quot;%Cpu(s)&quot;那一行的&quot;us cpu&quot;是 58.5%。对于 12CPU 的系统来说12 * 58.5%=7.02,也就是说这里显示总共消耗了 7 个 CPU远远大于容器中 2 个 CPU 的消耗。</p>
<p><img src="assets/byy517a2ee2e041c5ce0ae4a30753cfb.png" alt="img" /></p>
<p>运行top的示意图</p>
<p>这个例子说明top 这个工具虽然在物理机或者虚拟机上看得到系统 CPU 开销,但是如果是放在容器环境下,运行 top 就无法得到容器中总的 CPU 使用率。那么,我们还有什么其他的办法吗?</p>
<h2>进程 CPU 使用率和系统 CPU 使用率</h2>
<p>通过问题重现,我们发现 top 工具主要显示了宿主机系统整体的 CPU 使用率,以及单个进程的 CPU 使用率。既然没有现成的工具可以得到容器 CPU 开销,那我们需要自己开发一个工具来解决问题了。</p>
<p>其实我们自己推导,也没有那么难。我认为,最有效的思路还是从原理上去理解问题。</p>
<p>所以,在解决怎样得到单个容器整体的 CPU 使用率这个问题之前,我们先来学习一下,在 Linux 中到底是如何计算单个进程的 CPU 使用率,还有整个系统的 CPU 使用率的。</p>
<h3>进程 CPU 使用率</h3>
<p>Linux 中每个进程的 CPU 使用率,我们都可以用 top 命令查看。</p>
<p>对照我们前面的那张示意图,我们可以发现,每个进程在 top 命令输出中都有对应的一行,然后“%CPU”的那一列就是这个进程的实时 CPU 使用率了。</p>
<p>比如说100% 就表示这个进程在这个瞬时使用了 1 个 CPU200% 就是使用了 2 个 CPU。那么这个百分比的数值是怎么得到呢</p>
<p>最直接的方法,就是从源头开始寻找答案。因为是 top 命令的输出,我们可以去看一下 top 命令的源代码。在代码中你会看到对于每个进程top 都会从 proc 文件系统中每个进程对应的 stat 文件中读取 2 个数值。我们先来看这个文件,再来解读文件中具体的两个数值。</p>
<p>这个 stat 文件就是 /proc/[pid]/stat [pid] 就是替换成具体一个进程的 PID 值。比如 PID 值为 1 的进程,这个文件就是 /proc/1/stat ,那么这个 /proc/[pid]/stat 文件里有什么信息呢?</p>
<p>其实这个 stat 文件实时输出了进程的状态信息比如进程的运行态Running 还是 Sleeping、父进程 PID、进程优先级、进程使用的内存等等总共 50 多项。</p>
<p>完整的 stat 文件内容和格式在 proc 文件系统的 Linux programmers manual 里定义了。在这里我们只需要重点关注这两项数值stat 文件中的第 14 项 utime 和第 15 项 stime。</p>
<p><img src="assets/53c409d607942e8189f73636aafe3cb1.png" alt="img" /></p>
<p>那么这两项数值 utime 和 stime 是什么含义呢utime 是表示进程的用户态部分在 Linux 调度中获得 CPU 的 ticksstime 是表示进程的内核态部分在 Linux 调度中获得 CPU 的 ticks。</p>
<p>看到这个解释,你可能又冒出一个新问题,疑惑 ticks 是什么? 这个 ticks 就是 Linux 操作系统中的一个时间单位,你可以理解成类似秒,毫秒的概念。</p>
<p>在 Linux 中有个自己的时钟,它会周期性地产生中断。每次中断都会触发 Linux 内核去做一次进程调度,而这一次中断就是一个 tick。因为是周期性的中断比如 1 秒钟 100 次中断,那么一个 tick 作为一个时间单位看的话,也就是 1/100 秒。</p>
<p>我给你举个例子说明,假如进程的 utime 是 130ticks就相当于 130 * 1/100=1.3 秒,也就是进程从启动开始在用户态总共运行了 1.3 秒钟。</p>
<p>这里需要你注意utime 和 stime 都是一个累计值,也就是说从进程启动开始,这两个值就是一直在累积增长的。</p>
<p>那么我们怎么计算,才能知道某一进程在用户态和内核态中,分别获得了多少 CPU 的 ticks 呢?</p>
<p>首先,我们可以假设这个瞬时是 1 秒钟,这 1 秒是 T1 时刻到 T2 时刻之间的,那么这样我们就能获得 T1 时刻的 utime_1 和 stime_1同时获得 T2 时刻的 utime_2 和 stime_2。</p>
<p>在这 1 秒的瞬时,进程用户态获得的 CPU ticks 就是 (utime_2 utime_1), 进程内核态获得的 CPU ticks 就是 (stime_2 stime_1)。</p>
<p>那么我们可以推导出,进程 CPU 总的开销就是用户态加上内核态,也就是在 1 秒瞬时进程总的 CPU ticks 等于 (utime_2 utime_1) + (stime_2 stime_1)。</p>
<p>好了,现在我们得到了进程以 ticks 为单位的 CPU 开销,接下来还要做个转化。我们怎样才能把这个值转化成我们熟悉的百分比值呢?其实也不难,我们还是可以去 top 的源代码里得到这个百分比的计算公式。</p>
<p>简单总结一下,这个公式是这样的:</p>
<p>进程的 CPU 使用率 =((utime_2 utime_1) + (stime_2 stime_1)) * 100.0 / (HZ * et * 1 )</p>
<p>接下来,我再给你讲一下,这个公式里每一个部分的含义。</p>
<p>首先, ((utime_2 utime_1) + (stime_2 stime_1)) 是瞬时进程总的 CPU ticks。这个我们已经在前面解释过了。</p>
<p>其次,我们来看 100.0,这里乘以 100.0 的目的是产生百分比数值。</p>
<p>最后,我再讲一下 (HZ * et * 1)。这是被除数这里的三个参数,我给你详细解释一下。</p>
<p>第一个 HZ 是什么意思呢?前面我们介绍 ticks 里说了ticks 是按照固定频率发生的,在我们的 Linux 系统里 1 秒钟是 100 次,那么 HZ 就是 1 秒钟里 ticks 的次数,这里值是 100。</p>
<p>第二个参数 et 是我们刚才说的那个“瞬时”的时间,也就是得到 utime_1 和 utime_2 这两个值的时间间隔。</p>
<p>第三个“1”, 就更容易理解了,就是 1 个 CPU。那么这三个值相乘你是不是也知道了它的意思呢就是在这“瞬时”的时间et1 个 CPU 所包含的 ticks 数目。</p>
<p>解释了这些参数,我们可以把这个公式简化一下,就是下面这样:</p>
<p>进程的 CPU 使用率 =(进程的 ticks/ 单个 CPU 总 ticks*100.0</p>
<p>知道了这个公式,就需要上手来验证一下这个方法对不对,怎么验证呢?我们可以启动一个消耗 CPU 的小程序,然后读取一下进程对应的 /proc/[pid]/stat 中的 utime 和 stime然后用这个方法来计算一下进程使用率这个百分比值并且和 top 的输出对比一下,看看是否一致。</p>
<p>先启动一个消耗 200% 的小程序,它的 PID 是 10021CPU 使用率是 200%。</p>
<p><img src="assets/yy5f601a16cd9c5f8b01157daea0d4f9.png" alt="img" /></p>
<p>然后,我们查看这个进程对应的 stat 文件 /proc/10021/stat间隔 1 秒钟输出第二次,因为 stat 文件内容很多,我们知道 utime 和 stime 第 14 和 15 项,所以我们这里只截取了前 15 项的输出。这里可以看到utime_1 = 399stime_1=0utime_2=600stime_2=0。</p>
<p><img src="assets/29609d255bfd22eab6697f6ccd4923b0.png" alt="img" /></p>
<p>根据前面的公式,我们计算一下进程 threads-cpu 的 CPU 使用率。套用前面的公式,计算的过程是:</p>
<p>((600 399) + (0 0)) * 100.0 / (100 * 1 * 1) =201也就是 201%。你会发现这个值和我们运行 top 里的值是一样的。同时,我们也就验证了这个公式是没问题的。</p>
<h3>系统 CPU 使用率</h3>
<p>前面我们介绍了 Linux 中如何获取单个进程的 CPU 使用率,下面我们再来看看 Linux 里是怎么计算系统的整体 CPU 使用率的。</p>
<p>其实知道了如何计算单个进程的 CPU 使用率之后,要理解系统整体的 CPU 使用率计算方法就简单多了。</p>
<p>同样,我们要计算 CPU 使用率,首先需要拿到数据,数据源也同样可以从 proc 文件系统里得到,对于整个系统的 CPU 使用率,这个文件就是 /proc/stat。</p>
<p>在 /proc/stat 文件的 cpu 这行有 10 列数据,同样我们可以在 proc 文件系统的 Linux programmers manual 里,找到每一列数据的定义,而前 8 列数据正好对应 top 输出中&quot;%Cpu(s)&quot;那一行里的 8 项数据,也就是在上一讲中,我们介绍过的 user/system/nice/idle/iowait/irq/softirq/steal 这 8 项。</p>
<p><img src="assets/7da412b1b9e4f4b235e5dae6c997859d.png" alt="img" /></p>
<p>而在 /proc/stat 里的每一项的数值,就是系统自启动开始的 ticks。那么要计算出“瞬时”的 CPU 使用率,首先就要算出这个“瞬时”的 ticks比如 1 秒钟的“瞬时”,我们可以记录开始时刻 T1 的 ticks, 然后再记录 1 秒钟后 T2 时刻的 ticks再把这两者相减就可以得到这 1 秒钟的 ticks 了。</p>
<p><img src="assets/fa59040d49cb20d808619957ae1e2760.png" alt="img" /></p>
<p>这里我们可以得到,在这 1 秒钟里每个 CPU 使用率的 ticks</p>
<p><img src="assets/f550d24358e7cc909ff337f4840512bd.jpeg" alt="img" /></p>
<p>我们想要计算每一种 CPU 使用率的百分比,其实也很简单。我们只需要把所有在这 1 秒里的 ticks 相加得到一个总值,然后拿某一项的 ticks 值,除以这个总值。比如说计算 idle CPU 的使用率就是:</p>
<pre><code>1203 / 0 + 0 + 0 + 1203 + 0 + 0 + 0 + 0=100%
</code></pre>
<p>好了,我们现在来整体梳理一下,我们通过 Linux 里的工具,要怎样计算进程的 CPU 使用率和系统的 CPU 使用率。</p>
<p>对于单个进程的 CPU 使用率计算,我们需要读取对应进程的 /proc/[pid]/stat 文件,将进程瞬时用户态和内核态的 ticks 数相加,就能得到进程的总 ticks。</p>
<p>然后我们运用公式“(进程的 ticks / 单个 CPU 总 ticks) * 100.0”计算出进程 CPU 使用率的百分比值。</p>
<p>对于系统的 CPU 使用率,需要读取 /proc/stat 文件,得到瞬时各项 CPU 使用率的 ticks 值,相加得到一个总值,单项值除以总值就是各项 CPU 的使用率。</p>
<h2>解决问题</h2>
<p>前面我们学习了在 Linux 中top 工具是怎样计算每个进程的 CPU 使用率,以及系统总的 CPU 使用率。现在我们再来看最初的问题:为什么在容器中运行 top 命令不能得到容器中总的 CPU 使用率?</p>
<p>这就比较好解释了,对于系统总的 CPU 使用率,需要读取 /proc/stat 文件,但是这个文件中的各项 CPU ticks 是反映整个节点的,并且这个 /proc/stat 文件也不包含在任意一个 Namespace 里。</p>
<p>那么,对于 top 命令来说,它只能显示整个节点中各项 CPU 的使用率,不能显示单个容器的各项 CPU 的使用率。既然 top 命令不行,我们还有没有办法得到整个容器的 CPU 使用率呢?</p>
<p>我们之前已经学习过了 CPU Cgroup每个容器都会有一个 CPU Cgroup 的控制组。在这个控制组目录下面有很多参数文件,有的参数可以决定这个控制组里最大的 CPU 可使用率外,除了它们之外,目录下面还有一个可读项 cpuacct.stat。</p>
<p>这里包含了两个统计值,这两个值分别是这个控制组里所有进程的内核态 ticks 和用户态的 ticks那么我们就可以用前面讲过的公式也就是计算进程 CPU 使用率的公式,去计算整个容器的 CPU 使用率:</p>
<p>CPU 使用率 =((utime_2 utime_1) + (stime_2 stime_1)) * 100.0 / (HZ * et * 1 )</p>
<p>我们还是以问题重现中的例子说明,也就是最开始启动容器里的那两个容器 threads-cpu 进程。</p>
<p>就像下图显示的这样,整个容器的 CPU 使用率的百分比就是 ( (174021 - 173820) + (4 4)) * 100.0 / (100 * 1 * 1) = 201, 也就是 201%。所以,我们从每个容器的 CPU Cgroup 控制组里的 cpuacct.stat 的统计值中,可以比较快地得到整个容器的 CPU 使用率。</p>
<p><img src="assets/fdcfd5yy6e593cccea1bed7bfe4e5095.png" alt="img" /></p>
<h2>重点总结</h2>
<p>Linux 里获取 CPU 使用率的工具,比如 top都是通过读取 proc 文件系统下的 stat 文件来得到 CPU 使用了多少 ticks。而这里的 ticks是 Linux 操作系统里的一个时间单位,可以理解成类似秒,毫秒的概念。</p>
<p>对于每个进程来说,它的 stat 文件是 /proc/[pid]/stat里面包含了进程用户态和内核态的 ticks 数目;对于整个节点,它的 stat 文件是 /proc/stat里面包含了 user/system/nice/idle/iowait 等不同 CPU 开销类型的 ticks。</p>
<p>由于 /proc/stat 文件是整个节点全局的状态文件,不属于任何一个 Namespace因此在容器中无法通过读取 /proc/stat 文件来获取单个容器的 CPU 使用率。</p>
<p>所以要得到单个容器的 CPU 使用率,我们可以从 CPU Cgroup 每个控制组里的统计文件 cpuacct.stat 中获取。单个容器 CPU 使用率 =((utime_2 utime_1) + (stime_2 stime_1)) * 100.0 / (HZ * et * 1 )。</p>
<p>得到单个容器的 CPU 的使用率,那么当宿主机上负载变高的时候,就可以很快知道是哪个容器引起的问题。同时,用户在管理自己成百上千的容器的时候,也可以很快发现 CPU 使用率异常的容器,这样就能及早地介入去解决问题。</p>
<h2>思考题</h2>
<p>写一个小程序,在容器中执行,它可以显示当前容器中所有进程总的 CPU 使用率。</p>
<p>欢迎在留言区和我互动,一起探讨容器 CPU 的相关问题。如果这篇文章让你有所收获,也欢迎你分享给更多的朋友,一起学习进步</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/容器实战高手课/05 容器CPU1怎么限制容器的CPU使用.md">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/容器实战高手课/07 Load Average加了CPU Cgroup限制为什么我的容器还是很慢.md">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"7099778bac203cfa","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>