<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Arthur Picerna</title>
    <description>The latest articles on Forem by Arthur Picerna (@ar2pi).</description>
    <link>https://forem.com/ar2pi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F169548%2F9a7449a2-55bf-4479-90ec-b8fed81fd62c.png</url>
      <title>Forem: Arthur Picerna</title>
      <link>https://forem.com/ar2pi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ar2pi"/>
    <language>en</language>
    <item>
      <title>Troubleshoot Container OOM Kills with eBPF</title>
      <dc:creator>Arthur Picerna</dc:creator>
      <pubDate>Mon, 16 Jun 2025 05:23:26 +0000</pubDate>
      <link>https://forem.com/ar2pi/troubleshoot-container-oom-kills-with-ebpf-4am</link>
      <guid>https://forem.com/ar2pi/troubleshoot-container-oom-kills-with-ebpf-4am</guid>
      <description>&lt;p&gt;So I had a little time on my hands a couple weeks ago and decided to explore how to use eBPF to monitor container OOM kills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;Out Of Memory (OOM) kills are an early sign of &lt;a href="https://sre.google/sre-book/monitoring-distributed-systems/#saturation" rel="noopener noreferrer"&gt;saturation&lt;/a&gt; in Linux systems. It happens when a machine runs out of available memory space and starts killing processes until it can breathe again.&lt;/p&gt;

&lt;p&gt;In the context of containers, with things such as Kubernetes or Docker, OOMs happen when a container's memory usage exceeds its memory limits. The offending container processes get selected and OOM killed. In essence, they receive a &lt;a href="https://www.gnu.org/software/libc/manual/html_node/Termination-Signals.html" rel="noopener noreferrer"&gt;&lt;code&gt;SIGKILL&lt;/code&gt; signal&lt;/a&gt; and unexpectedly terminate any ongoing task.&lt;/p&gt;

&lt;p&gt;OOM kills might lead to unexpected service degradation and prompt a mild "How did that happen?" (or "wtf just happened" depending on the situation) from whoever is investigating the crime scene. Trick being that all evidence is mostly lost to the void as the offending process is gone. So yea, back to the roots...&lt;/p&gt;

&lt;h2&gt;
  
  
  How do OOM kills happen in containers?
&lt;/h2&gt;

&lt;p&gt;Container resource limits are enforced through &lt;a href="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html" rel="noopener noreferrer"&gt;cgroups&lt;/a&gt;. Neither Kubernetes nor Docker have any control whatsoever over which process gets killed. It is the responsibility of the underlying Linux host kernel to enforce its resource boundaries.&lt;/p&gt;

&lt;p&gt;Journal kernel logs displaying a memory cgroup 'out of memory' message:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8o4gg4h3kgh54bac8hv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8o4gg4h3kgh54bac8hv.png" alt="kernel logs image" width="800" height="222"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Excessive container memory usage, hence OOM kills, can be attributed to either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Under-provisioned container memory&lt;/li&gt;
&lt;li&gt;Faulty container application behavior &lt;/li&gt;
&lt;li&gt;A bit of both 1. and 2.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Picture how an unoptimized process that needs to use a lot of memory for inbound requests might be prone to OOM kills. Now imagine what happens in peak traffic conditions if one container goes down and the same load gets redistributed to the remaining containers while it restarts? Highly likely to cause &lt;a href="https://en.wikipedia.org/wiki/Cascading_failure" rel="noopener noreferrer"&gt;cascading failures&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If the process chosen by the kernel is the container's main process, then the main process will exit which will stop the container and that would typically trigger a container restart. It takes some time to restart a container, time which is not spent running its workload.&lt;br&gt;
But the OOM killer could instead select a child process within the container's main process, then the container itself will outlive the OOM kill and new child processes might eventually respawn. One could argue that is even worse because the symptom is much less obvious.&lt;/p&gt;

&lt;p&gt;Unless you already know why OOMs happen and are fine with it, e.g. voluntarily relying on OOMs to restart containers; in most cases though you'd really want to be able to assert what is happening in your system at the time of the OOM kills.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ &lt;a href="https://github.com/google/cadvisor" rel="noopener noreferrer"&gt;cAdvisor&lt;/a&gt; metrics can be used to track containers memory usage and OOM kills to some extent. In this article we merely focus on exposing Linux kernel's internal memory management stats.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's where eBPF comes in.&lt;/p&gt;
&lt;h2&gt;
  
  
  eBPF enters the room
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://ebpf.io/what-is-ebpf/" rel="noopener noreferrer"&gt;extended Berkeley Packet Filter (eBPF)&lt;/a&gt; allows you to run sandboxed probe programs without changing the Linux kernel source code or loading kernel modules. Think of it as a way to hook to kernel function and execute custom code. This opens a plethora of possibilities, amongst which troubleshooting OOM kills.&lt;/p&gt;

&lt;p&gt;There are 2 main eBPF toolkits you can use: &lt;a href="https://github.com/iovisor/bcc" rel="noopener noreferrer"&gt;BCC&lt;/a&gt; and &lt;a href="https://github.com/bpftrace/bpftrace" rel="noopener noreferrer"&gt;bpftrace&lt;/a&gt;. Both of them provide an 'oomkill' probe example, respectively &lt;a href="https://github.com/iovisor/bcc/blob/master/tools/oomkill.py" rel="noopener noreferrer"&gt;tools/oomkill.py&lt;/a&gt; for BCC and &lt;a href="https://github.com/bpftrace/bpftrace/blob/master/tools/oomkill.bt" rel="noopener noreferrer"&gt;tools/oomkill.bt&lt;/a&gt; for bpftrace. As a side note both were written by &lt;a href="https://www.brendangregg.com/overview.html" rel="noopener noreferrer"&gt;Brendan Gregg&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I found bpftrace syntax more concise and easier to understand so I tried running that locally, here is an example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ &lt;span class="nb"&gt;sudo&lt;/span&gt; /usr/sbin/oomkill.bt
Attaching 2 probes...
Tracing oom_kill_process&lt;span class="o"&gt;()&lt;/span&gt;... Hit Ctrl-C to end.
19:44:13 Triggered by PID 1516777 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"python3"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;, OOM &lt;span class="nb"&gt;kill &lt;/span&gt;of PID 1516777 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"python3"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;, 32768 pages, loadavg: 0.72 0.64 0.71 3/2230 1517322
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above shows the process PID, total pages count and CPU loadavg, from the host perspective. It doesn't give much container context, let's see if we can change that. &lt;/p&gt;

&lt;p&gt;Here is the original bpftrace oomkill probe code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#ifndef BPFTRACE_HAVE_BTF
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;linux/oom.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#endif
&lt;/span&gt;
&lt;span class="n"&gt;BEGIN&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tracing oom_kill_process()... Hit Ctrl-C to end.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;kprobe&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;oom_kill_process&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;oom_control&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%H:%M:%S "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Triggered by PID %d (&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;), "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;comm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OOM kill of PID %d (&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;), %d pages, loadavg: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;comm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;totalpages&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/proc/loadavg"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's make sense of it, starting with this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="nl"&gt;kprobe:&lt;/span&gt;&lt;span class="n"&gt;oom_kill_process&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It tells that we define a kernel probe (&lt;a href="https://bpftrace.org/docs/0.23#probes-kprobe" rel="noopener noreferrer"&gt;kprobe&lt;/a&gt;) that will hook onto the &lt;code&gt;oom_kill_process()&lt;/code&gt; function. Makes sense.&lt;/p&gt;

&lt;p&gt;Now let's look at the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;oom_control&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It tells that we receive the function's first argument &lt;code&gt;arg0&lt;/code&gt; and type cast it into an &lt;code&gt;oom_control&lt;/code&gt; struct. But wait, how do we know that the &lt;code&gt;oom_kill_process()&lt;/code&gt; function receives an &lt;code&gt;oom_control&lt;/code&gt; struct as first argument? Well, let's take a look at the Linux kernel source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Short dive into the Linux kernel
&lt;/h2&gt;

&lt;p&gt;Go to the &lt;a href="https://github.com/torvalds/linux" rel="noopener noreferrer"&gt;torvalds/linux&lt;/a&gt; git repo and search for the &lt;code&gt;oom_kill_process()&lt;/code&gt; function declaration, like &lt;a href="https://github.com/search?q=repo%3Atorvalds%2Flinux+oom_kill_process&amp;amp;type=code" rel="noopener noreferrer"&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://github.com/torvalds/linux/blob/master/mm/oom_kill.c#L1017" rel="noopener noreferrer"&gt;mm/oom_kill.c&lt;/a&gt; you can see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;oom_kill_process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;oom_control&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not only have we confirmed where the &lt;code&gt;oom_control&lt;/code&gt; argument comes from but now we know that &lt;code&gt;oom_kill_process()&lt;/code&gt; receives a second string argument &lt;code&gt;message&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, what about what's inside an &lt;code&gt;oom_control&lt;/code&gt; struct? Might there be anything else useful in there? Let's have a look.&lt;/p&gt;

&lt;p&gt;You know the drill, search for &lt;code&gt;oom_control&lt;/code&gt; in the source code, like &lt;a href="https://github.com/search?q=repo%3Atorvalds%2Flinux%20oom_control&amp;amp;type=code" rel="noopener noreferrer"&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://github.com/torvalds/linux/blob/master/include/linux/oom.h#L24-L54" rel="noopener noreferrer"&gt;include/linux/oom.h&lt;/a&gt; you can see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/*
 * Details of the page allocation that triggered the oom killer that are used to
 * determine what should be killed.
 */&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;oom_control&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* Used to determine cpuset */&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;zonelist&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;zonelist&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* Used to determine mempolicy */&lt;/span&gt;
    &lt;span class="n"&gt;nodemask_t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;nodemask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* Memory cgroup in which oom is invoked, or NULL for global oom */&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;mem_cgroup&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;memcg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* Used to determine cpuset and node locality requirement */&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;gfp_t&lt;/span&gt; &lt;span class="n"&gt;gfp_mask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/*
     * order == -1 means the oom kill is required by sysrq, otherwise only
     * for display purposes.
     */&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* Used by oom implementation, do not set */&lt;/span&gt;
    &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;totalpages&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;task_struct&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;chosen_points&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cm"&gt;/* Used to print the constraint info. */&lt;/span&gt;
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;oom_constraint&lt;/span&gt; &lt;span class="n"&gt;constraint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's some interesting stuff in there. First of all, we now understand where the &lt;code&gt;$oc-&amp;gt;totalpages&lt;/code&gt; in the original oomkill probe's code comes from. And we can see a few other promising properties such as &lt;code&gt;memcg&lt;/code&gt;, &lt;code&gt;chosen&lt;/code&gt; and &lt;code&gt;chosen_points&lt;/code&gt;. Let's unpack what these are in more detail.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;memcg&lt;/code&gt; is a &lt;a href="https://github.com/torvalds/linux/blob/master/include/linux/memcontrol.h#L189" rel="noopener noreferrer"&gt;mem_cgroup&lt;/a&gt; object that holds the container's cgroup memory usage and limits.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;chosen&lt;/code&gt; is a &lt;a href="https://github.com/torvalds/linux/blob/master/include/linux/sched.h#L816" rel="noopener noreferrer"&gt;task_struct&lt;/a&gt; object that holds the chosen process properties amongst which you can find &lt;code&gt;mm&lt;/code&gt; and &lt;code&gt;nsproxy&lt;/code&gt; that respectively hold memory management and container's namespaces information.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;chosen_points&lt;/code&gt; comes from the &lt;a href="https://sourcegraph.com/search?q=repo:%5Egithub%5C.com/torvalds/linux%24+oom_badness&amp;amp;patternType=keyword&amp;amp;sm=0" rel="noopener noreferrer"&gt;oom_badness()&lt;/a&gt; score calculation that is used by the OOM killer to select which process to kill.&lt;/p&gt;

&lt;p&gt;The formula for the OOM badness score calculation ends up looking something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chosen_points = 
    mm_filepages + 
    mm_anonpages + 
    mm_shmempages + 
    mm_swapents + 
    mm_pgtables_bytes / PAGE_SIZE + 
    oom_score_adj * totalpages / 1000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;ℹ️ &lt;code&gt;PAGE_SIZE&lt;/code&gt; is a constant which may vary by system but is generally equivalent to 4096 bytes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the process resident set size (RSS) = &lt;code&gt;mm_filepages&lt;/code&gt; + &lt;code&gt;mm_anonpages&lt;/code&gt; + &lt;code&gt;mm_shmempages&lt;/code&gt; + &lt;code&gt;mm_swapents&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In essence, it's summing all of the process RSS memory footprint, counted in pages, plus some page tables space reserved by the kernel and an adjustable factor that can be set by the user per process. The main take away is that the process with highest RSS, hence a higher number of oom badness points, is the most likely to get OOM killed. And from the &lt;code&gt;$oc-&amp;gt;chosen-&amp;gt;active_mm-&amp;gt;rss_stat&lt;/code&gt; object you can access the various components of that process RSS. &lt;/p&gt;

&lt;p&gt;As the above are the main variables which influence the OOM killer's process selection let's see if we can include them in the output of our probe.&lt;/p&gt;

&lt;h2&gt;
  
  
  A revised oomkill probe, for containers
&lt;/h2&gt;

&lt;p&gt;Let's &lt;code&gt;mkdir container-oomkill-probe &amp;amp;&amp;amp; cd container-oomkill-probe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is the revised bpftrace script, let's name it &lt;code&gt;container_oomkill.bt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#!/usr/bin/env bpftrace
&lt;/span&gt;&lt;span class="cm"&gt;/*
 * container_oomkill    Trace OOM killer in containers.
 *        For Linux, uses bpftrace and eBPF.
 *
 * This traces the kernel out-of-memory killer by using kernel dynamic tracing of oom_kill_process().
 * Prints the process host pid, container id, cgroup path, command and a few other stats.
 * Note: There's no guarantee that the OOM killed process is within a "container", this script just assumes it is.
 *
 * Example of usage:
 *
 * # ./container_oomkill.bt
 * Tracing oom_kill_process()... Ctrl-C to end.
 *
 * Adapted from the original bpftrace's tools/oomkill.bt by Brendan Gregg:
 * -&amp;gt; https://github.com/bpftrace/bpftrace/blob/master/tools/oomkill.bt
 */&lt;/span&gt;

&lt;span class="cp"&gt;#ifndef BPFTRACE_HAVE_BTF
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;linux/oom.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#endif
&lt;/span&gt;
&lt;span class="n"&gt;BEGIN&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tracing oom_kill_process()... Hit Ctrl-C to end.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// fn: static void oom_kill_process(struct oom_control *oc, const char *message)&lt;/span&gt;
&lt;span class="c1"&gt;// https://github.com/torvalds/linux/blob/master/mm/oom_kill.c#L1017&lt;/span&gt;
&lt;span class="n"&gt;kprobe&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;oom_kill_process&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;oom_control&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;arg0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// print datetime with milliseconds precision&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%Y-%m-%d %H:%M:%S"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nsecs&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;",%03d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nsecs&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1000000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// print labels&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" probe=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;probe&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  message=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  host_pid=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; container_id=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; command=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nsproxy&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uts_ns&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nodename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;comm&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// oom_control stats&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  oc_totalpages=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; oc_chosen_points=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;totalpages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// = mem + swap&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen_points&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// = filepages + anonpages + swapents + shmempages + pgtables_bytes / PAGE_SIZE + oom_score_adj * totalpages / 1000&lt;/span&gt;

    &lt;span class="c1"&gt;// cgroup stats&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  memcg_memory_usage_pages=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; memcg_memory_max_pages=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; memcg_memory_low_pages=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;memcg&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// memory usage in pages&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;memcg&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;             &lt;span class="c1"&gt;// memory hard limit&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;memcg&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;            &lt;span class="c1"&gt;// memory request&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  memcg_swap_current_pages=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; memcg_swap_max_pages=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; memcg_swappiness=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;memcg&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;// swap usage in pages&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;memcg&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;               &lt;span class="c1"&gt;// swap hard limit&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;memcg&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;swappiness&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// stats used in OOM badness calculation&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  mm_rss_filepages=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; mm_rss_anonpages=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; mm_rss_swapents=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; mm_rss_shmempages=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rss_stat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rss_stat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rss_stat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;rss_stat&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// in case you get hit by&lt;/span&gt;
    &lt;span class="c1"&gt;// "ERROR: The array index operator [] can only be used on arrays and pointers, found record..."&lt;/span&gt;
    &lt;span class="c1"&gt;// "ERROR: Can not access field 'count' on expression of type 'none'..."&lt;/span&gt;
    &lt;span class="c1"&gt;// prior to linux 6.2 $oc-&amp;gt;chosen-&amp;gt;mm-&amp;gt;rss_stat is a mm_rss_stat struct&lt;/span&gt;
    &lt;span class="c1"&gt;// https://github.com/torvalds/linux/commit/f1a7941243c102a44e8847e3b94ff4ff3ec56f25#diff-dc57f7b72015cf5f95444ec4f8a60f85d773f40b96ac59bf55b281cd63c06142&lt;/span&gt;
    &lt;span class="c1"&gt;// you can use the version below instead&lt;/span&gt;
    &lt;span class="c1"&gt;//printf("  mm_rss_filepages=\"%d\" mm_rss_anonpages=\"%d\" mm_rss_swapents=\"%d\" mm_rss_shmempages=\"%d\"\n",&lt;/span&gt;
    &lt;span class="c1"&gt;//    $oc-&amp;gt;chosen-&amp;gt;mm-&amp;gt;rss_stat.count[0].counter,&lt;/span&gt;
    &lt;span class="c1"&gt;//    $oc-&amp;gt;chosen-&amp;gt;mm-&amp;gt;rss_stat.count[1].counter,&lt;/span&gt;
    &lt;span class="c1"&gt;//    $oc-&amp;gt;chosen-&amp;gt;mm-&amp;gt;rss_stat.count[2].counter,&lt;/span&gt;
    &lt;span class="c1"&gt;//    $oc-&amp;gt;chosen-&amp;gt;mm-&amp;gt;rss_stat.count[3].counter);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  mm_pgtables_bytes=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;mm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;pgtables_bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  proc_oom_score_adj=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;oom_score_adj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// score adj used in oom_badness calculation&lt;/span&gt;

    &lt;span class="c1"&gt;// minor and major page faults&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  proc_min_flt=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; proc_maj_flt=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%d&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;min_flt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="c1"&gt;// minor page faults&lt;/span&gt;
        &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;maj_flt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                  &lt;span class="c1"&gt;// major page faults&lt;/span&gt;

    &lt;span class="c1"&gt;// calculated stats&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  uptime_ms=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%lld&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nsecs&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;oc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;start_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few noteworthy bits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$oc-&amp;gt;chosen-&amp;gt;nsproxy-&amp;gt;uts_ns-&amp;gt;name.nodename&lt;/code&gt; gets the container id&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$oc-&amp;gt;memcg-&amp;gt;memory&lt;/code&gt; contains the cgroup memory usage and limits, &lt;code&gt;$oc-&amp;gt;memcg-&amp;gt;memory.max&lt;/code&gt; is what you would call in Kubernetes the "resource limit" while &lt;code&gt;$oc-&amp;gt;memcg-&amp;gt;memory.low&lt;/code&gt; would be the "resource request"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$oc-&amp;gt;memcg-&amp;gt;swap&lt;/code&gt; contains the cgroup swap usage and limits, which might be relevant as we saw that &lt;code&gt;swapents&lt;/code&gt; is one of the factors that influences OOM killer's selection&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$oc-&amp;gt;chosen-&amp;gt;mm-&amp;gt;rss_stat&lt;/code&gt; contains the process RSS stats list, ordered as defined in the enum &lt;a href="https://github.com/torvalds/linux/blob/master/include/linux/mm_types_task.h#L31" rel="noopener noreferrer"&gt;NR_MM_COUNTERS&lt;/a&gt;, respectively &lt;code&gt;MM_FILEPAGES&lt;/code&gt;, &lt;code&gt;MM_ANONPAGES&lt;/code&gt;, &lt;code&gt;MM_SWAPENTS&lt;/code&gt;, &lt;code&gt;MM_SHMEMPAGES&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$oc-&amp;gt;chosen-&amp;gt;signal-&amp;gt;oom_score_adj&lt;/code&gt; and &lt;code&gt;$oc-&amp;gt;chosen-&amp;gt;mm-&amp;gt;pgtables_bytes.counter&lt;/code&gt; are part of the variables we saw being used in the OOM badness calculation so we can display that as well&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let's write a Dockerfile to build an image with bpftrace installed and the updated &lt;code&gt;container_oomkill.bt&lt;/code&gt; so that the probe runs from within a container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; debian:stable-slim&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    linux-headers-generic &lt;span class="se"&gt;\
&lt;/span&gt;    bpftrace &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; *.bt /app/&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["bpftrace", "container_oomkill.bt"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; container-oomkill-probe &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host &lt;span class="nt"&gt;-v&lt;/span&gt; /sys:/sys:ro container-oomkill-probe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see somethings like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ docker run &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host &lt;span class="nt"&gt;-v&lt;/span&gt; /sys:/sys:ro container-oomkill-probe
Attaching 2 probes...
Tracing oom_kill_process&lt;span class="o"&gt;()&lt;/span&gt;... Hit Ctrl-C to end.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks fine and all, but how do we trigger the probe?&lt;/p&gt;

&lt;h2&gt;
  
  
  Test or it didn't happen
&lt;/h2&gt;

&lt;p&gt;Here is a simple python script that hogs memory by 4MiB increments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python3
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basicConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%(asctime)s %(levelname)s: %(message)s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;memory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Allocating &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; MiB...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save it in a new &lt;code&gt;stress-mem/&lt;/code&gt; folder as &lt;code&gt;stress-mem/main.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For testing purposes you can run the script in a 128MiB memory constrained Docker container with the container oomkill probe alongside it. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;probe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dockerfile&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/sys:/sys:ro&lt;/span&gt;  &lt;span class="c1"&gt;# required to access /sys/fs/cgroup/docker/CONTAINER_ID&lt;/span&gt;
    &lt;span class="na"&gt;privileged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# required to run bpftrace, as an alternative could try setting CAP_BPF, CAP_PERFMON&lt;/span&gt;
    &lt;span class="na"&gt;pid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;         &lt;span class="c1"&gt;# required to trace processes from host&lt;/span&gt;

  &lt;span class="na"&gt;stress-mem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python:3-slim&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./stress-mem:/app&lt;/span&gt;
    &lt;span class="na"&gt;working_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/app&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3 main.py&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on-failure&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;128MiB&lt;/span&gt;
        &lt;span class="na"&gt;reservations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;64MiB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sample output:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tucbh7aegpgn89qzcse.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tucbh7aegpgn89qzcse.png" alt="docker compose output showing an oom kill with swap enabled" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But wait, how come if we defined a 128MiB limit our &lt;code&gt;stress-mem&lt;/code&gt; container is able to allocate up to 252MiB?&lt;/p&gt;

&lt;p&gt;Surely our probe is completely wrong and we've wasted our valuable time on something useless. Unless... &lt;/p&gt;

&lt;p&gt;Notice the &lt;code&gt;mm_rss_swapents="32549"&lt;/code&gt;? Well, that is swap. And swap isn't disabled by default in Docker. Memory gets "swapped to disk" when memory pressure forces the kernel to do so. You can confirm that the system is struggling seeing the &lt;code&gt;proc_maj_flt&lt;/code&gt; count. Major page faults happen when the system reads from disk instead of RAM.&lt;/p&gt;

&lt;p&gt;Do we want swap? No we don't. Let's disable that. In our &lt;code&gt;docker-compose.yml&lt;/code&gt; we can disable swap by setting &lt;a href="https://docs.docker.com/reference/compose-file/services/#memswap_limit" rel="noopener noreferrer"&gt;memswap_limit&lt;/a&gt; equivalent to the memory limit, which is pretty confusing indeed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;stress-mem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
    &lt;span class="na"&gt;memswap_limit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;128MiB&lt;/span&gt;   &lt;span class="c1"&gt;# disable swap by setting memswap_limit = mem_limit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rerun the &lt;code&gt;docker compose up --build&lt;/code&gt; command and you should see:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwaqr01sdlf3x5solqaxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwaqr01sdlf3x5solqaxp.png" alt="docker compose output showing an oom kill with swap disabled" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well that's much better. No major page faults, no swap, the process got OOM killed at 124MiB which is the last increment before it could reach 128MiB. Perfect. Our probe works yay!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Taking a step back, here is what we did with our new container oomkill probe:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Show container contextualized information from the process memory cgroup and namespace&lt;/li&gt;
&lt;li&gt;Expose kernel's internal memory management stats providing additional insights on the reasons a process might have been OOM killed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's not much, but you are now fully equipped with the tools and knowledge to find out more, build your own eBPF probes, understand how the Linux kernel works.&lt;/p&gt;

&lt;h2&gt;
  
  
  References and further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.brendangregg.com/ebpf.html" rel="noopener noreferrer"&gt;Linux Extended BPF (eBPF) Tracing Tools by Brendan Gregg&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.kernel.org/admin-guide/mm/concepts.html" rel="noopener noreferrer"&gt;Memory Management in Linux - Concepts overview (kernel docs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tldp.org/LDP/tlk/mm/memory.html" rel="noopener noreferrer"&gt;Chapter 3 - Memory Management (LDP)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html" rel="noopener noreferrer"&gt;cgroups v2 (kernel docs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.man7.org/linux/man-pages/man7/namespaces.7.html" rel="noopener noreferrer"&gt;man namespaces&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  TL;DR:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/ar2pi/container-oomkill-probe" rel="noopener noreferrer"&gt;Here's the code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;glhf 😉&lt;/p&gt;

</description>
      <category>ebpf</category>
      <category>linux</category>
      <category>docker</category>
      <category>sre</category>
    </item>
    <item>
      <title>Publish your Markdown docs on GitHub Pages</title>
      <dc:creator>Arthur Picerna</dc:creator>
      <pubDate>Tue, 10 Aug 2021 19:12:18 +0000</pubDate>
      <link>https://forem.com/ar2pi/publish-your-markdown-docs-on-github-pages-6pe</link>
      <guid>https://forem.com/ar2pi/publish-your-markdown-docs-on-github-pages-6pe</guid>
      <description>&lt;p&gt;As a developer you've most likely switched machines, even OS several times. Maybe you've performed a specific set of operations that you'll have to repeat in the future? want to remember some command lines? share code snippets? capture online course notes? &lt;/p&gt;

&lt;p&gt;For all of the reasons above, and many more, having a personal documentation website is ideal. It lets you organize and synthesize all the information that is relevant to YOU in your own personal way, with the added benefit of being easily shareable. &lt;/p&gt;

&lt;h2&gt;
  
  
  What we'll do
&lt;/h2&gt;

&lt;p&gt;A project containing all your &lt;a href="https://github.github.com/gfm/#what-is-markdown-" rel="noopener noreferrer"&gt;Markdown&lt;/a&gt; files, hosted on &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;Github Pages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Requirements&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.python.org/download/releases/3.0/" rel="noopener noreferrer"&gt;Python 3&lt;/a&gt; (tested with version 3.9.5+)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt; (2.12+)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.gnu.org/software/make/" rel="noopener noreferrer"&gt;Make&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How we do it
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install &lt;code&gt;mkdocs&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We'll use &lt;a href="https://www.mkdocs.org/" rel="noopener noreferrer"&gt;&lt;code&gt;mkdocs&lt;/code&gt;&lt;/a&gt; as static site generator, so let's install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;mkdocs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create your docs source project
&lt;/h3&gt;

&lt;p&gt;Now create the basic project structure, in your projects folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mkdocs new GITHUB_USERNAME
&lt;span class="nb"&gt;cd &lt;/span&gt;GITHUB_USERNAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace GITHUB_USERNAME with your GitHub username or whatever you prefer to name your project. We'll be referencing it in the next steps and it will become clearer why you might want to choose that project name. &lt;/p&gt;

&lt;p&gt;Check the created files in that folder, you'll see something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./
-- docs/
-- mkdocs.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and try running &lt;code&gt;mkdocs serve&lt;/code&gt; then open &lt;a href="http://127.0.0.1:8000" rel="noopener noreferrer"&gt;http://127.0.0.1:8000&lt;/a&gt; in your browser. And voilà! You have a personal documentation local website. Any &lt;code&gt;.md&lt;/code&gt; file added in &lt;code&gt;docs/&lt;/code&gt; will appear as a new web page. Try to edit &lt;code&gt;docs/index.md&lt;/code&gt; for example.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mkdocs.yml&lt;/code&gt; contains your project configuration. You can customize your site name, navigation layout and so on. A simple example would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;site_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your site name&lt;/span&gt;
&lt;span class="na"&gt;site_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://GITHUB_USERNAME.github.io&lt;/span&gt;
&lt;span class="na"&gt;site_author&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;you&lt;/span&gt;
&lt;span class="na"&gt;site_description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;a short description of your website&lt;/span&gt;

&lt;span class="na"&gt;nav&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;About&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use the &lt;a href="https://www.mkdocs.org/user-guide/choosing-your-theme/#readthedocs" rel="noopener noreferrer"&gt;readthedocs&lt;/a&gt; provided theme by adding the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;readthedocs&lt;/span&gt;
  &lt;span class="na"&gt;highlightjs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;hljs_languages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For reference see &lt;a href="https://www.mkdocs.org/user-guide/configuration/" rel="noopener noreferrer"&gt;https://www.mkdocs.org/user-guide/configuration/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point you should have something like this locally:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1rdwqhzk6r4tefd36ki4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1rdwqhzk6r4tefd36ki4.png" alt="mkdocs generated website with readthedocs theme" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a local documentation project that would be enough. But we'll go ahead and make this deployable to your GitHub page.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create the GitHub repositories
&lt;/h3&gt;

&lt;p&gt;GitHub allows you to host a website in a specific repository matching GITHUB_USERNAME.github.io, with your actual GitHub username in place of GITHUB_USERNAME. &lt;/p&gt;

&lt;p&gt;Luckily &lt;code&gt;mkdocs&lt;/code&gt; makes it easy to push your project build files to your GitHub page through the &lt;a href="https://www.mkdocs.org/user-guide/deploying-your-docs/" rel="noopener noreferrer"&gt;&lt;code&gt;mkdocs gh-deploy&lt;/code&gt;&lt;/a&gt; command. &lt;/p&gt;

&lt;p&gt;The downside of &lt;code&gt;mkdocs gh-deploy&lt;/code&gt; is that it wipes out all the files in &lt;code&gt;GITHUB_USERNAME.github.io/&lt;/code&gt; to generate the website files (html, css, js).&lt;/p&gt;

&lt;p&gt;To overcome this constraint and simplify the deploy process you'll track your GitHub page repo, GITHUB_USERNAME.github.io, independently as a &lt;a href="https://git-scm.com/docs/git-submodule" rel="noopener noreferrer"&gt;git submodule&lt;/a&gt; of your docs source project. Which will allow to run &lt;code&gt;mkdocs gh-deploy&lt;/code&gt; from within the docs source project itself.&lt;/p&gt;

&lt;p&gt;Ultimately you'll need two distinct repositories: one for your GitHub page, another one for your source files. &lt;/p&gt;
&lt;h4&gt;
  
  
  Create the GitHub page repo
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://github.com/new" rel="noopener noreferrer"&gt;https://github.com/new&lt;/a&gt; and create your GitHub page repository&lt;/li&gt;
&lt;li&gt;Name it GITHUB_USERNAME.github.io (replace GITHUB_USERNAME with your actual GitHub username)
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm5ra3s973kly4u6uqaq8.png" alt="image" width="748" height="740"&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 &lt;strong&gt;Edit 16 Apr 2023&lt;/strong&gt; &lt;br&gt;
If you did not choose your GitHub username in lieu of GITHUB_USERNAME then you might have to do a couple of extra steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From the github.io repo, choose &lt;em&gt;Settings&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Under &lt;em&gt;Code and automation&lt;/em&gt;, click on &lt;em&gt;Pages&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;In &lt;em&gt;Source&lt;/em&gt; &amp;gt; &lt;em&gt;Deploy from a branch&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;In &lt;em&gt;Branch&lt;/em&gt; &amp;gt; choose &lt;code&gt;main&lt;/code&gt; and save&lt;/li&gt;
&lt;li&gt;Type in the URL of your website "my-new-docs-website.github.io" and save&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for the feedback &lt;a class="mentioned-user" href="https://dev.to/konsoul"&gt;@konsoul&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Create the source repo
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Go to &lt;a href="https://github.com/new" rel="noopener noreferrer"&gt;https://github.com/new&lt;/a&gt; and create your source repository&lt;/li&gt;
&lt;li&gt;Name it GITHUB_USERNAME (replace GITHUB_USERNAME with your actual GitHub username or anything else you chose to name the new &lt;code&gt;mkdocs&lt;/code&gt; project previously created)
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8b7js4mr84wv4lxbycly.png" alt="image" width="754" height="742"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 A repository named as your username is a special repository on GitHub. Its &lt;code&gt;README.md&lt;/code&gt; will appear on your public profile. So you can customize that as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this point you should have two GitHub repositories:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Git repo&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GITHUB_USERNAME.git (or anything else you chose to name it)&lt;/td&gt;
&lt;td&gt;Source repo with markdown files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GITHUB_USERNAME.github.io.git&lt;/td&gt;
&lt;td&gt;GitHub page repo with static web files&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Initialize the git repositories
&lt;/h3&gt;

&lt;p&gt;Back in the folder created previously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init

&lt;span class="nb"&gt;echo &lt;/span&gt;site/ &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .gitignore

git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"first commit"&lt;/span&gt;

git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
git remote add origin SOURCE_REPO &lt;span class="c"&gt;# replace SOURCE_REPO with your source repo HTTPS or SSH adddress&lt;/span&gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then initialize the GitHub page repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# still in your source folder&lt;/span&gt;

&lt;span class="nb"&gt;mkdir &lt;/span&gt;GITHUB_USERNAME.github.io
&lt;span class="nb"&gt;cd &lt;/span&gt;GITHUB_USERNAME.github.io

git init
git commit &lt;span class="nt"&gt;--allow-empty&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"first commit"&lt;/span&gt;

git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
git remote add origin GH_PAGE_REPO &lt;span class="c"&gt;# replace GH_PAGE_REPO with your source repo HTTPS or SSH adddress&lt;/span&gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main

&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now have two folders tracked in their respective GitHub repositories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GITHUB_USERNAME/                  Source repo
-- GITHUB_USERNAME.github.io/     GitHub page repo
-- docs/
-- .gitignore
-- mkdocs.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now delete &lt;code&gt;GITHUB_USERNAME.github.io/&lt;/code&gt; since there's no need for it to exist independently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rvf&lt;/span&gt; ./GITHUB_USERNAME.github.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your GitHub page repo as a submodule of your source repo instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git submodule add &lt;span class="nt"&gt;-b&lt;/span&gt; main GH_PAGE_REPO &lt;span class="c"&gt;# replace GH_PAGE_REPO with your source repo HTTPS or SSH adddress&lt;/span&gt;
git add GITHUB_USERNAME.github.io
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"add github page submodule"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy your docs to your GitHub page
&lt;/h3&gt;

&lt;p&gt;Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;GITHUB_USERNAME.github.io
mkdocs gh-deploy &lt;span class="nt"&gt;--config-file&lt;/span&gt; ../mkdocs.yml &lt;span class="nt"&gt;--remote-branch&lt;/span&gt; main
&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and open &lt;a href="https://GITHUB_USERNAME.github.io" rel="noopener noreferrer"&gt;https://GITHUB_USERNAME.github.io&lt;/a&gt; in your browser. And voilà! Your very own personal documentation on your GitHub page. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📝 You might have to wait a few seconds for your changes to be deployed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Add deploy command in Makefile
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;mkdocs gh-deploy&lt;/code&gt; command in itself isn't very user friendly nor easy to remember so we'll add a simple &lt;code&gt;Makefile&lt;/code&gt; to make (word choice on purpose) it easier to deploy changes.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;Makefile&lt;/code&gt; in your source folder with the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;SHELL&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; /bin/bash
&lt;span class="nv"&gt;GH_PAGE&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; GITHUB_USERNAME.github.io

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;deploy&lt;/span&gt;
&lt;span class="nl"&gt;deploy&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;GH_PAGE&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; mkdocs gh-deploy &lt;span class="nt"&gt;--config-file&lt;/span&gt; ../mkdocs.yml &lt;span class="nt"&gt;--remote-branch&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;📝 Ensure to paste &lt;code&gt;Makefile&lt;/code&gt; indentation as Tabs or else you might get an error in the lines of &lt;code&gt;[...] *** missing separator.  Stop.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now you can simply run &lt;code&gt;make deploy&lt;/code&gt; whenever you want to publish something.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep both repositories in sync
&lt;/h3&gt;

&lt;p&gt;You might also want to update latest commit version of your GitHub page submodule whenever a deploy is performed. Not technically required, but good for tracking purposes. Add the following in your &lt;code&gt;Makefile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;update-build-version&lt;/span&gt;
&lt;span class="nl"&gt;update-build-version&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    git submodule update &lt;span class="nt"&gt;--remote&lt;/span&gt; &lt;span class="nt"&gt;--merge&lt;/span&gt;
    git add &lt;span class="p"&gt;$(&lt;/span&gt;GH_PAGE&lt;span class="p"&gt;)&lt;/span&gt;
    git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"ci: update build version"&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;publish&lt;/span&gt;
&lt;span class="nl"&gt;publish&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;deploy update-build-version&lt;/span&gt;
    git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;make publish&lt;/code&gt; will handle the deploy and update your submodule version.&lt;/p&gt;

&lt;p&gt;A typical workflow would be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;make some changes to your markdown files or &lt;code&gt;mkdocs.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git commit -am "commit message"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git push&lt;/code&gt; to your source repo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;make publish&lt;/code&gt; to your GitHub page&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why so complicated ?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Can't I just run &lt;code&gt;mkdocs build&lt;/code&gt; and use &lt;code&gt;site/&lt;/code&gt; as my GitHub page?&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
You can! See the &lt;a href="https://docs.github.com/en/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site" rel="noopener noreferrer"&gt;GitHub docs&lt;/a&gt;. You will need to commit the build &lt;code&gt;site/&lt;/code&gt; artifacts, or you could even use a different branch for the GitHub page altogether. IMHO I'd much rather track the actual source code and deploy the minimal amount of files in the public project, and having two completely unrelated branches in the same repository is kind of an overly obscure solution. Hence the submodule approach. But that is my biased opinion, use whichever solution works best for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Just show me the code
&lt;/h2&gt;

&lt;p&gt;Here is a sample project containing the output of everything described in this post: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ar2pi/sample-mkdocs-gh-project" rel="noopener noreferrer"&gt;https://github.com/ar2pi/sample-mkdocs-gh-project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Similar articles
&lt;/h2&gt;

&lt;p&gt;Here are a few other articles I could find before writing this post:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/cosckoya/create-your-own-mkdocs-with-github-pages-a5c"&gt;Create your own mkdocs with Github pages (by 
cosckoya)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/michaelcurrin/github-pages-deploys-made-easy-343o"&gt;Custom GH Pages deploys made easy (by 
Michael)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/sandeepbalachandran/mkdocs-static-html-sites-and-documentation-preparation-tool-that-you-can-host-on-github-pages-262f"&gt;MkDocs : Static HTML sites and documentation preparation tool that you can host on GitHub pages (by Sandeep Balachandran)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You just created a brand new GitHub page hosting all your desired markdown generated documentation. Which can also contain your cv, portfolio, todo list, random thoughts, cat pictures, etc. you name it. Go nuts, the web is limitless.&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

</description>
      <category>github</category>
      <category>markdown</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
