<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ inspektor gadget - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ inspektor gadget - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 27 Aug 2026 01:03:30 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/inspektor-gadget/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Debug Kubernetes Apps When Logs Fail You – An eBPF Tracing Handbook ]]>
                </title>
                <description>
                    <![CDATA[ Let’s say your Kubernetes pod crashes at 3am and the logs show nothing useful. By the time you SSH into the node, the container is gone, and you're left guessing what happened in those final moments. This is the reality of debugging modern applicatio... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-debug-kubernetes-apps-when-logs-fail-you-an-ebpf-tracing-handbook/</link>
                <guid isPermaLink="false">694190c566a5d5cb99995f9f</guid>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ eBPF ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ observability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Open Source ]]>
                    </category>
                
                    <category>
                        <![CDATA[ OpenTelemetry ]]>
                    </category>
                
                    <category>
                        <![CDATA[ inspektor gadget ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Opaluwa Emidowojo ]]>
                </dc:creator>
                <pubDate>Tue, 16 Dec 2025 17:03:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765899860869/3eadf316-8539-4624-afba-1d4190b6c62a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Let’s say your Kubernetes pod crashes at 3am and the logs show nothing useful. By the time you SSH into the node, the container is gone, and you're left guessing what happened in those final moments.</p>
<p>This is the reality of debugging modern applications. Traditional monitoring wasn't built for containers that live for seconds, services that shift across nodes, or network paths that change constantly.</p>
<p>eBPF changes this. It lets you see <em>inside</em> the kernel itself, watching every system call, every network packet, and every process execution – without modifying a single line of code.</p>
<p>In this tutorial, you will trace a real Kubernetes application using eBPF-powered tools. You’ll learn fundamentals that apply across the entire modern observability ecosystem, with gadgets from the Inspektor Gadget ecosystem.</p>
<p>By the end, you’ll be able to:</p>
<ul>
<li><p>Trace requests as they move through your Kubernetes pods</p>
</li>
<li><p>Observe behavior at the kernel and syscall level</p>
</li>
<li><p>Debug failures that logs and metrics simply can’t explain</p>
</li>
</ul>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p><strong>Knowledge requirements:</strong></p>
<ul>
<li><p>Basic Kubernetes concepts: pods, deployments, services, namespaces</p>
</li>
<li><p>Familiarity with kubectl: <code>get</code>, <code>describe</code>, <code>logs</code>, <code>exec</code></p>
</li>
<li><p>Container basics</p>
</li>
<li><p>Basic Linux concepts: processes, system calls</p>
</li>
</ul>
<p><strong>Technical requirements:</strong></p>
<ul>
<li><p>Kubernetes cluster (local or cloud-based)</p>
</li>
<li><p><code>kubectl</code> installed and configured</p>
</li>
<li><p>Cluster admin permissions</p>
</li>
<li><p>Linux kernel 5.10+ (most managed services have this)</p>
</li>
</ul>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-understanding-ebpf-observability">Understanding eBPF Observability</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-ebpf-tracing-works-without-getting-lost-in-the-kernel">How eBPF Tracing Works (Without Getting Lost in the Kernel)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-environment">How to Set Up Your Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-trace-your-first-request-hands-on-tutorial">How to Trace Your First Request: Hands-On Tutorial</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-interpret-traces">How to Interpret Traces</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-real-world-debugging-scenarios">Real-World Debugging Scenarios</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-advanced-tracing-insights">Advanced Tracing Insights</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-best-practices-and-production-considerations">Best Practices and Production Considerations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps-and-resources">Next Steps and Resources</a></p>
</li>
</ul>
<h2 id="heading-understanding-ebpf-observability">Understanding eBPF Observability</h2>
<p>eBPF (extended Berkeley Packet Filter) is a technology that allows you to run custom programs inside the Linux kernel without changing kernel code or loading kernel modules.</p>
<p>The Linux kernel is the control center of your operating system. Historically, if you wanted to observe low-level activity (like network packets, system calls, or file operations), you had to rely on kernel changes or kernel modules. Both approaches were fragile, difficult to maintain, and carried real stability and security risks.</p>
<p>eBPF shifts how we approach observability. It provides a safe, sandboxed environment where you can run observability programs directly in the kernel with built-in safety checks that prevent crashes or security vulnerabilities.</p>
<h3 id="heading-why-does-this-matter-for-observability">Why does this matter for observability?</h3>
<p>In traditional observability, you instrument your application code. You add logging statements, metrics libraries, and tracing SDKs. This works, but has significant limitations:</p>
<ul>
<li><p><strong>Code changes are required</strong>: You must modify and redeploy applications</p>
</li>
<li><p><strong>It’s language-specific</strong>: Different languages need different libraries</p>
</li>
<li><p><strong>There will likely be blind spots</strong>: You can only see what you explicitly instrument</p>
</li>
<li><p><strong>The overhead</strong>: Heavy instrumentation slows down applications</p>
</li>
<li><p><strong>Container challenges</strong>: By the time you add instrumentation and redeploy, the problem may have disappeared</p>
</li>
</ul>
<p>eBPF takes a different approach. Instead of instrumenting applications, you instrument the kernel. Since every application ultimately makes system calls to the kernel for network I/O, file operations, and process management, you can observe everything from one vantage point.</p>
<h3 id="heading-the-ebpf-advantage-for-kubernetes">The eBPF advantage for Kubernetes</h3>
<p>Kubernetes adds another layer of complexity. Your application might be spread across multiple containers, pods, and nodes. Traditional APM (Application Performance Monitoring) tools struggle here because containers come and go rapidly, network topology changes constantly, service meshes add routing complexity, and you often don't control application code (think third-party services or legacy applications you can't modify.)</p>
<p>eBPF doesn't care about any of this. It sees all activity at the kernel level, regardless of what language your app is written in, whether it's containerized, how many times the pod has been rescheduled, or whether you have access to modify the source code. This universal visibility is why the Cloud Native Computing Foundation (CNCF) and major cloud providers are betting heavily on eBPF for the future of observability.</p>
<h2 id="heading-how-ebpf-tracing-works-without-getting-lost-in-the-kernel">How eBPF Tracing Works (Without Getting Lost in the Kernel)</h2>
<p>When your application runs on Kubernetes, there's a clear separation between user space and kernel space. Your code runs in user space, where it's isolated, safe, and has limited access to system resources. To do anything useful – make network calls, read files, allocate memory – your application must ask the kernel for help. The kernel handles these requests via system calls, commonly called syscalls.</p>
<p>eBPF lets us hook into these syscalls without slowing the system down. It’s like having a CCTV camera at every doorway between user space and kernel space, watching who passes through, when, and what they’re carrying.</p>
<h3 id="heading-a-simple-example-http-request-tracing">A Simple Example: HTTP Request Tracing</h3>
<p>Your application initiates an HTTP GET request, which needs to go through the network stack. To establish a connection, your application first makes a <code>socket()</code> system call to create a network socket. Then it calls <code>connect()</code> to establish a connection to the remote server. Once connected, it uses <code>send()</code> to transmit the HTTP request. Network packets are sent across the wire, and eventually your application calls <code>recv()</code> to receive the response.</p>
<p>With eBPF tools like Inspektor Gadget's Traceloop, you can automatically hook into these syscalls. The eBPF program captures request metadata including source and destination IPs, ports, timing information, and payload sizes. You get a complete trace of the request without touching your application code.</p>
<h3 id="heading-the-ebpf-execution-flow">The eBPF Execution Flow</h3>
<p>Here's what happens under the hood when you run a trace. When you deploy Inspektor Gadget and run a gadget, several things happen behind the scenes. Once deployed, the eBPF program springs into action whenever a traced event occurs.</p>
<p>When your application makes a syscall, the eBPF hook triggers and quickly collects relevant data: timestamps, process IDs, container IDs, pod names, request details, and latency information. This data is sent to user space through eBPF maps, which are efficient data structures for kernel-to-userspace communication.</p>
<p>Inspektor Gadget adds Kubernetes context to raw kernel data. Instead of seeing only process IDs, you can see pod names, namespaces, labels, and other metadata. For example, you can tell that a request originated from the frontend pod in the production namespace and targeted the backend service.</p>
<p>The gadget then presents this information in a format that's immediately useful, whether you're using the CLI or integrating with other observability tools.</p>
<p>eBPF is fast because:</p>
<ul>
<li><p><strong>JIT compilation</strong>: Programs are turned into native machine code for maximum performance</p>
</li>
<li><p><strong>Event-driven</strong>: Only execute when relevant events occur, not continuously polling</p>
</li>
<li><p><strong>Kernel-resident</strong>: No expensive context switching between kernel and user space</p>
</li>
<li><p><strong>Highly optimized</strong>: Typically adds less than 5% overhead even under heavy load</p>
</li>
</ul>
<h3 id="heading-the-tool-inspektor-gadget-amp-traceloop">The Tool: Inspektor Gadget &amp; Traceloop</h3>
<p>For this tutorial, we're using Traceloop, an eBPF-based tool that traces request flows through applications by observing syscalls, network calls, and I/O operations at the kernel level.</p>
<p>Why are we using Traceloop for this tutorial?</p>
<ul>
<li><p>It’s quick to install and run (one command)</p>
</li>
<li><p>The output maps directly to the application’s behavior</p>
</li>
<li><p>It automatically adds Kubernetes context (pod names, namespaces)</p>
</li>
<li><p>You don’t need to make any application code changes</p>
</li>
</ul>
<p>What you'll learn applies beyond Traceloop. All eBPF tracing tools (Pixie, Cilium Hubble, Tetragon) work the same way under the hood. They attach to kernel hooks and collect event data. Once you understand the concepts here, you can use any eBPF observability tool effectively.</p>
<h2 id="heading-how-to-set-up-your-environment">How to Set Up Your Environment</h2>
<p>To get your environment ready for hands-on tracing, we'll verify that your cluster meets the requirements, install Inspektor Gadget, and deploy a sample application to trace.</p>
<h3 id="heading-verify-that-your-cluster-meets-the-requirements">Verify that Your Cluster Meets the Requirements</h3>
<p>Before installing anything, confirm that your Kubernetes cluster is ready for eBPF.</p>
<h4 id="heading-check-your-kubernetes-version">Check your Kubernetes version:</h4>
<pre><code class="lang-bash">kubectl version --short
</code></pre>
<p>You need Kubernetes 1.19 or later. Most modern clusters exceed this requirement, but it's worth verifying.</p>
<h4 id="heading-verify-kernel-version-on-your-nodes">Verify kernel version on your nodes:</h4>
<pre><code class="lang-bash">kubectl get nodes -o wide
</code></pre>
<p>Then check the kernel version on one of your nodes:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># If using a local cluster like minikube or kind</span>
uname -r

<span class="hljs-comment"># For cloud clusters, you might need to check node details</span>
kubectl debug node/&lt;node-name&gt; -it --image=ubuntu -- bash -c <span class="hljs-string">"uname -r"</span>
</code></pre>
<p>You need Linux kernel 5.10 or later for the best eBPF support. Kernel 4.18+ works but with some limitations. If you're using a managed Kubernetes service (GKE, EKS, AKS), you almost certainly have a compatible kernel.</p>
<h4 id="heading-confirm-that-you-have-cluster-admin-permissions">Confirm that you have cluster admin permissions:</h4>
<pre><code class="lang-bash">kubectl auth can-i create deployments --all-namespaces
</code></pre>
<p>This should return "yes". Inspektor Gadget needs elevated permissions to load eBPF programs into the kernel.</p>
<h3 id="heading-install-inspektor-gadget">Install Inspektor Gadget</h3>
<p>You can install Inspektor Gadget in several ways. We'll use the kubectl plugin method as it's the most straightforward for learning.</p>
<h4 id="heading-install-the-kubectl-gadget-plugin">Install the kubectl gadget plugin:</h4>
<pre><code class="lang-bash"><span class="hljs-comment"># Download and install kubectl-gadget</span>
kubectl krew install gadget

<span class="hljs-comment"># Verify installation</span>
kubectl gadget version
</code></pre>
<p>If you don't have krew (the kubectl plugin manager), you can install it first:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install krew</span>
(
  <span class="hljs-built_in">set</span> -x; <span class="hljs-built_in">cd</span> <span class="hljs-string">"<span class="hljs-subst">$(mktemp -d)</span>"</span> &amp;&amp;
  OS=<span class="hljs-string">"<span class="hljs-subst">$(uname | tr '[:upper:]' '[:lower:]')</span>"</span> &amp;&amp;
  ARCH=<span class="hljs-string">"<span class="hljs-subst">$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')</span>"</span> &amp;&amp;
  KREW=<span class="hljs-string">"krew-<span class="hljs-variable">${OS}</span>_<span class="hljs-variable">${ARCH}</span>"</span> &amp;&amp;
  curl -fsSLO <span class="hljs-string">"https://github.com/kubernetes-sigs/krew/releases/latest/download/<span class="hljs-variable">${KREW}</span>.tar.gz"</span> &amp;&amp;
  tar zxvf <span class="hljs-string">"<span class="hljs-variable">${KREW}</span>.tar.gz"</span> &amp;&amp;
  ./<span class="hljs-string">"<span class="hljs-variable">${KREW}</span>"</span> install krew
)

<span class="hljs-comment"># Add krew to your PATH</span>
<span class="hljs-built_in">export</span> PATH=<span class="hljs-string">"<span class="hljs-variable">${KREW_ROOT:-<span class="hljs-variable">$HOME</span>/.krew}</span>/bin:<span class="hljs-variable">$PATH</span>"</span>
</code></pre>
<h4 id="heading-deploy-inspektor-gadget-to-your-cluster">Deploy Inspektor Gadget to your cluster:</h4>
<pre><code class="lang-bash">kubectl gadget deploy
</code></pre>
<p>This creates a <code>gadget</code> namespace and deploys the Inspektor Gadget daemon as a DaemonSet, ensuring each node in your cluster can run eBPF programs.</p>
<h4 id="heading-verify-the-deployment">Verify the deployment:</h4>
<pre><code class="lang-bash">kubectl get pods -n gadget
</code></pre>
<p>You should see one <code>gadget-*</code> pod per node, all in the <code>Running</code> state. If a pod is stuck in <code>Pending</code> or <code>CrashLoopBackOff</code>, check that your kernel meets the version requirements.</p>
<h4 id="heading-deploying-a-sample-application">Deploying a sample application</h4>
<p>To learn tracing effectively, we need an application that does something interesting. We'll deploy a simple microservices application with multiple components so you can see traces flowing across service boundaries.</p>
<p>Start by creating a namespace for our demo app:</p>
<pre><code class="lang-bash">kubectl create namespace demo-app
</code></pre>
<p>Then deploy a simple web application with a backend:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">frontend</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">demo-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">frontend</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">frontend</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">frontend</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">gcr.io/google-samples/microservices-demo/frontend:v0.8.0</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8080</span>
        <span class="hljs-attr">env:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">PORT</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"8080"</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">PRODUCT_CATALOG_SERVICE_ADDR</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"productcatalog:3550"</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">frontend</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">demo-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">LoadBalancer</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">frontend</span>
  <span class="hljs-attr">ports:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
    <span class="hljs-attr">targetPort:</span> <span class="hljs-number">8080</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">productcatalog</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">demo-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">productcatalog</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">productcatalog</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">server</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">gcr.io/google-samples/microservices-demo/productcatalogservice:v0.8.0</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3550</span>
        <span class="hljs-attr">env:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">PORT</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"3550"</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">productcatalog</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">demo-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">productcatalog</span>
  <span class="hljs-attr">ports:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">port:</span> <span class="hljs-number">3550</span>
    <span class="hljs-attr">targetPort:</span> <span class="hljs-number">3550</span>
</code></pre>
<p>Apply the configuration:</p>
<pre><code class="lang-bash">kubectl apply -f demo-app.yaml
</code></pre>
<p>And wait for pods to be ready:</p>
<pre><code class="lang-bash">kubectl <span class="hljs-built_in">wait</span> --<span class="hljs-keyword">for</span>=condition=ready pod -l app=frontend -n demo-app --timeout=300s
kubectl <span class="hljs-built_in">wait</span> --<span class="hljs-keyword">for</span>=condition=ready pod -l app=productcatalog -n demo-app --timeout=300s
</code></pre>
<p>Then just verify that everything is running:</p>
<pre><code class="lang-bash">kubectl get pods -n demo-app
</code></pre>
<p>You should see both <code>frontend</code> and <code>productcatalog</code> pods in the <code>Running</code> state.</p>
<p>Now you’ll need to get the frontend URL:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># For local clusters (minikube, kind, Docker Desktop)</span>
kubectl port-forward -n demo-app service/frontend 8080:80

<span class="hljs-comment"># Then access http://localhost:8080 in your browser</span>

<span class="hljs-comment"># For cloud clusters</span>
kubectl get service frontend -n demo-app
<span class="hljs-comment"># Look for the EXTERNAL-IP</span>
</code></pre>
<p>Visit the application in your browser to confirm it's working. You should see a simple e-commerce storefront. This application makes HTTP requests from the frontend to the product catalog service, which is perfect for tracing.</p>
<h2 id="heading-how-to-trace-your-first-request-hands-on-tutorial">How to Trace Your First Request: Hands-On Tutorial</h2>
<p>Now that everything is set up, let's capture our first trace and see eBPF observability in action.</p>
<h3 id="heading-generate-the-traffic-to-trace">Generate the Traffic to Trace</h3>
<p>First, we need some application activity to observe. We will generate a few requests for our demo application.</p>
<p>In one terminal, start the Traceloop gadget:</p>
<pre><code class="lang-bash">kubectl gadget traceloop -n demo-app
</code></pre>
<p>This command starts tracing HTTP request handling in the <code>demo-app</code> namespace. Inspektor Gadget monitors the kernel to capture the function calls and system events that occur while processing each request.  </p>
<p>In another terminal, generate some traffic:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># If using port-forward</span>
curl http://localhost:8080

<span class="hljs-comment"># If you have an external IP</span>
curl http://&lt;EXTERNAL-IP&gt;

<span class="hljs-comment"># Generate multiple requests</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> {1..10}; <span class="hljs-keyword">do</span> curl http://localhost:8080; sleep 1; <span class="hljs-keyword">done</span>
```

<span class="hljs-comment">### Viewing Your First Trace</span>

Switch back to the terminal running the trace loop gadget. You should see output appearing as requests flow through your application. The output will look something like this:
```
NODE         NAMESPACE   POD              CONTAINER    PID    TYPE       COUNT  
minikube     demo-app    frontend-abc123  frontend     1234   loop       1      
minikube     demo-app    frontend-abc123  frontend     1234   loop       2
</code></pre>
<p>Each line shows a traced execution flow, with the count increasing as the same pattern is observed again.</p>
<p>We can make the output more interesting by filtering:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Stop the previous trace with Ctrl+C, then run:</span>
kubectl gadget traceloop -n demo-app --podname frontend
</code></pre>
<p>This narrows our observation to just the frontend pod, reducing noise and making patterns clearer.</p>
<h4 id="heading-understanding-what-youre-seeing">Understanding what you're seeing:</h4>
<p>Each column shows different information about your application:</p>
<ul>
<li><p><strong>NODE</strong>: Which Kubernetes node the traced event occurred on. In multi-node clusters, this helps you understand workload distribution and identify node-specific issues.</p>
</li>
<li><p><strong>NAMESPACE</strong>: The Kubernetes namespace. We filtered to <code>demo-app</code>, so you'll only see that namespace. In production, filtering by namespace is crucial for focusing on specific applications.</p>
</li>
<li><p><strong>POD</strong>: The specific pod where the event occurred. Each pod gets a unique name (like <code>frontend-abc123</code>), allowing you to distinguish between replicas of the same application.</p>
</li>
<li><p><strong>CONTAINER</strong>: Which container within the pod. Pods can have multiple containers (main application, sidecars, init containers), so this helps you pinpoint exactly where activity is happening.</p>
</li>
<li><p><strong>PID</strong>: The process ID inside the container. This is the actual Linux process that made the syscalls eBPF observed. Multiple PIDs might appear if your application uses multiple processes or threads.</p>
</li>
<li><p><strong>TYPE</strong>: The type of event traced. For Traceloop, this identifies kernel-level patterns detected during request processing.</p>
</li>
<li><p><strong>COUNT</strong>: How many times this pattern has been observed. A rapidly incrementing count indicates high request volume.</p>
</li>
</ul>
<h4 id="heading-what-this-tells-you-about-your-application">What this tells you about your application:</h4>
<p>Even from this simple output, you can derive insights. If you see events appearing for the <code>frontend</code> pod but not the <code>productcatalog</code> pod, it might indicate that requests aren't making it to the backend. This is a potential configuration issue. If the <code>COUNT</code> increases rapidly for one pod but not others, you know which replica is receiving traffic, useful for debugging load balancing issues.</p>
<p>The real power becomes clear when you correlate these kernel-level observations with what you know about your application. When you made 10 curl requests, you should see corresponding activity in the trace output. This direct relationship between application behavior and kernel observations is the foundation of eBPF observability.</p>
<h2 id="heading-how-to-interpret-traces">How to Interpret Traces</h2>
<p>Understanding raw trace output is valuable, but interpreting what it means for your application's health and performance is where the real skill lies.</p>
<h3 id="heading-trace-anatomy-spans-timing-and-request-flow">Trace Anatomy: Spans, Timing, and Request Flow</h3>
<p>A trace represents a single request's journey through your system. When you curl the frontend, that generates one trace. A span represents a single operation within that trace like "frontend handles request," "frontend calls product catalog," "product catalog queries data," and "frontend returns response." Each span has timing information: when it started, when it ended, and therefore how long it took.</p>
<p>In traditional distributed tracing with OpenTelemetry or Jaeger, you'd explicitly create these spans in your application code. With eBPF, the tool infers spans from syscall patterns. When eBPF sees your frontend process call <code>connect()</code> to the product catalog's IP, followed by <code>send()</code> and <code>recv()</code>, it understands that's a span representing an HTTP request to the backend service.</p>
<p>The request flow is the sequence of spans showing how your request moved through services. In our demo app,</p>
<ol>
<li><p>The user request arrives at the frontend,</p>
</li>
<li><p>the frontend connects to the product catalog,</p>
</li>
<li><p>the product catalog processes the request,</p>
</li>
<li><p>the product catalog returns the data, the frontend renders the page,</p>
</li>
<li><p>and finally, the response is sent to user.</p>
</li>
</ol>
<h3 id="heading-how-to-follow-requests-across-services">How to Follow Requests Across Services</h3>
<p>Let's trace a request across service boundaries to see this flow in action.</p>
<p>First, we’ll start a more detailed trace:</p>
<pre><code class="lang-bash">kubectl gadget trace_tcp -n demo-app
</code></pre>
<p>The trace_tcp gadget shows network connections, giving us visibility into service-to-service communication.</p>
<p>Next, generate a request:</p>
<pre><code class="lang-bash">curl http://localhost:8080
</code></pre>
<p>In the trace output, look for connection patterns:</p>
<p>You should see the frontend pod establishing a TCP connection to the product catalog service. The trace will show the source (frontend) and destination (product catalog) IPs and ports, along with timing information.</p>
<p>This is how eBPF lets you follow requests: by observing the network syscalls that implement service communication. You don't need a service mesh or instrumentation libraries, the kernel sees all network activity and eBPF captures it.</p>
<h4 id="heading-understanding-the-flow">Understanding the flow:</h4>
<ol>
<li><p>Your curl command triggers a TCP connection to the frontend pod's IP on port 8080</p>
</li>
<li><p>The frontend processes the request and opens a TCP connection to the product catalog's IP on port 3550</p>
</li>
<li><p>Data flows back and forth (you'll see send/receive events)</p>
</li>
<li><p>Connections close when requests complete</p>
</li>
</ol>
<p>Each step is visible to eBPF because each step requires syscalls that the kernel handles.</p>
<h3 id="heading-how-to-identify-bottlenecks-and-errors">How to Identify Bottlenecks and Errors</h3>
<p>We can also use tracing to identify performance issues.</p>
<p>First, let’s start by simulating a slow backend:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a deliberately slow endpoint by modifying our deployment</span>
kubectl scale deployment productcatalog -n demo-app --replicas=0

<span class="hljs-comment"># Wait a moment, then scale back up</span>
kubectl scale deployment productcatalog -n demo-app --replicas=1
</code></pre>
<p>While the product catalog is down, generate some requests:</p>
<pre><code class="lang-bash"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> {1..5}; <span class="hljs-keyword">do</span> curl http://localhost:8080; <span class="hljs-keyword">done</span>
</code></pre>
<p>You should see connection attempts from the frontend to the product catalog, but if the service is unavailable, you'll see different patterns, possibly connection timeouts or connection refused errors, depending on the exact timing.</p>
<p>What bottlenecks look like in traces:</p>
<ul>
<li><p><strong>Long spans</strong>: A span that takes significantly longer than others indicates a bottleneck. In trace loop output, you might see gaps between events or notice certain operations taking longer.</p>
</li>
<li><p><strong>Retries</strong>: Repeated connection attempts to the same destination suggest a failing or slow service.</p>
</li>
<li><p><strong>Error patterns</strong>: Connection failures, timeouts, or unusual syscall sequences indicate problems.</p>
</li>
</ul>
<p>The best skill to have is pattern recognition. A typical, healthy request flow has a rhythm, and events occur in predictable sequences with consistent timing. When something breaks, the rhythm changes. Requests take longer, errors appear, or expected events don't occur at all.</p>
<h2 id="heading-real-world-debugging-scenarios">Real-World Debugging Scenarios</h2>
<p>Now let's go through three realistic scenarios where eBPF helps:</p>
<h3 id="heading-scenario-1-finding-a-slow-endpoint">Scenario 1: Finding a Slow Endpoint</h3>
<p><strong>The problem:</strong> Users report that the product catalog page sometimes loads very slowly, but metrics show normal average latency.</p>
<p>Let’s use Traceloop to investigate:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Start tracing with timing information</span>
kubectl gadget traceloop -n demo-app --podname frontend
</code></pre>
<p>We’ll generate some mixed traffic:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Some requests to the homepage (fast)</span>
curl http://localhost:8080

<span class="hljs-comment"># Some requests to the product catalog (potentially slow)</span>
curl http://localhost:8080/products
</code></pre>
<p>In the trace output, compare the <code>COUNT</code> increments for different request patterns. If certain patterns show significantly more loop iterations or longer gaps between events, that indicates those requests are doing more work, possibly hitting a slow endpoint.</p>
<h4 id="heading-the-diagnosis">The diagnosis:</h4>
<p>You might notice that requests to <code>/products</code> cause the frontend to make multiple calls to the product catalog service (visible with <code>kubectl gadget trace_tcp</code>), while homepage requests don't. This explains why the product page is slow: it's making synchronous calls to a backend service, and if that service is slow or the network is congested, users feel the delay.</p>
<h4 id="heading-the-fix">The fix:</h4>
<p>You might implement caching, make the backend calls asynchronous, or optimize the product catalog service itself. The key is that eBPF helped you identify which specific code path was slow without adding instrumentation to your application.</p>
<h3 id="heading-scenario-2-tracking-down-failed-requests">Scenario 2: Tracking Down Failed Requests</h3>
<p><strong>The problem:</strong> Your monitoring shows a 5% error rate, but application logs don't show any errors. Where are the failures happening?</p>
<p>Now let’s use eBPF to investigate:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Trace network connections to see connection failures</span>
kubectl gadget trace_tcp -n demo-app
</code></pre>
<p>We’ll simulate intermittent failures:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a failing scenario by temporarily breaking service connectivity</span>
kubectl delete service productcatalog -n demo-app

<span class="hljs-comment"># Generate requests</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> {1..10}; <span class="hljs-keyword">do</span> curl http://localhost:8080; sleep 1; <span class="hljs-keyword">done</span>

<span class="hljs-comment"># Restore the service</span>
kubectl apply -f demo-app.yaml
</code></pre>
<p>In the TCP trace, you'll see connection attempts from the frontend to the product catalog that fail or time out. The trace will show the source, destination, and what happened (connection refused, timeout, and so on).</p>
<h4 id="heading-the-diagnosis-1">The diagnosis:</h4>
<p>The failures are happening at the network level, the frontend can't reach the product catalog. This might be due to network policy issues, service mesh misconfiguration, or DNS problems. Traditional application logs might not capture this because the application never receives a response to log, and the connection fails before the application layer even gets involved.</p>
<h4 id="heading-why-ebpf-finds-this-when-logs-dont">Why eBPF finds this when logs don't:</h4>
<p>Your application logs what it experiences. If a connection fails at the TCP level, your application might just see "connection refused" and retry without detailed logging.</p>
<p>eBPF sees the actual syscalls and network events, giving you visibility into what's happening beneath your application layer.</p>
<h3 id="heading-scenario-3-understanding-service-dependencies">Scenario 3: Understanding Service Dependencies</h3>
<p><strong>The problem:</strong> You're not sure which services depend on each other, and you want to understand the actual runtime dependencies before making changes.</p>
<p>We’ll use eBPF to map dependencies:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Trace all TCP connections to see who talks to whom</span>
kubectl gadget trace_tcp -n demo-app
</code></pre>
<p>And then generate normal traffic:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Make various requests to exercise different code paths</span>
curl http://localhost:8080
curl http://localhost:8080/products
curl http://localhost:8080/cart
</code></pre>
<p>The trace output shows source and destination for every connection. Build a mental (or actual) map of which pods connect to which services.</p>
<h4 id="heading-the-discovery">The discovery:</h4>
<p>You'll see that the frontend pod connects to the product catalog service, but you might also discover unexpected dependencies. Perhaps the frontend also makes calls to a Redis cache, an authentication service, or external APIs. These runtime dependencies might not be documented or might differ from what architectural diagrams show.</p>
<h4 id="heading-why-this-matters">Why this matters:</h4>
<p>Before deploying a change to the product catalog service, you now know exactly which services will be affected. Before implementing a network policy, you know which connections to allow. Before decomposing a monolith, you understand the actual communication patterns.</p>
<p>This is observability-driven architecture understanding: letting the system show you how it actually works, not how you think it works.</p>
<h2 id="heading-advanced-tracing-insights">Advanced Tracing Insights</h2>
<p>Once you're comfortable with basic request tracing, Inspektor Gadget offers deeper observability capabilities that reveal even more about your system's behavior.</p>
<h3 id="heading-syscall-level-observation">Syscall-Level Observation</h3>
<p>The traceloop and trace_tcp gadgets give you application-level insights, but sometimes you need to go deeper. The trace_exec gadget shows you every process execution in your containers.</p>
<p>First, let’s monitor process execution:</p>
<pre><code class="lang-bash">kubectl gadget trace_exec -n demo-app
</code></pre>
<p>And generate activity:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Exec into a pod and run commands</span>
kubectl <span class="hljs-built_in">exec</span> -it -n demo-app deployment/frontend -- /bin/sh
ls -la
ps aux
<span class="hljs-built_in">exit</span>
</code></pre>
<p>Every command you run inside the container appears in the trace: <code>/bin/sh</code>, <code>ls</code>, <code>ps</code>, and anything else. This helps you understand what's running in your containers, detect suspicious activity, or debug initialization issues.</p>
<p>In production scenarios, this helps you answer questions like: Is my application spawning unexpected subprocesses? Are there security issues like someone running <code>curl</code> to download malicious scripts? Is my <code>init</code> script actually running the commands I think it is?</p>
<h3 id="heading-network-tracing-insights">Network Tracing Insights</h3>
<p>Beyond TCP connections, you can trace DNS queries, which often reveal surprising things about your application's behavior.</p>
<p>Run <code>trace_dns</code>:</p>
<pre><code class="lang-bash">kubectl gadget trace_dns -n demo-app
</code></pre>
<p>Generate requests:</p>
<pre><code class="lang-bash">curl http://localhost:8080
</code></pre>
<p>You'll see every DNS query your application makes: resolving service names, checking for external APIs, perhaps even unexpected queries that indicate misconfiguration or dependencies you didn't know about.</p>
<p>Common insights from DNS tracing include discovering that your application is using external dependencies you didn't document, finding DNS resolution failures that cause intermittent errors, or identifying excessive DNS queries that could be cached.</p>
<h3 id="heading-combining-ebpf-data-with-logs-and-metrics">Combining eBPF Data with Logs and Metrics</h3>
<p>eBPF observability delivers the best results when combined with traditional observability signals. To combine them effectively:</p>
<ul>
<li><p>Use metrics for high-level health monitoring, alerting on anomalies, tracking trends over time, and dashboard visualization.</p>
</li>
<li><p>Use logs for application-specific context, business logic details, error messages with stack traces, and debugging application code.</p>
</li>
<li><p>Use eBPF traces for understanding request flows, identifying where time is spent, discovering runtime dependencies, and debugging issues that don't appear in logs.</p>
</li>
</ul>
<h4 id="heading-a-practical-workflow">A practical workflow:</h4>
<p>Your metrics alert you that latency increased. You check logs but don't see errors, requests are succeeding, just slowly. You use eBPF tracing to identify that requests are spending extra time in network I/O to a particular backend service. Now you check that service's metrics and logs, and discover it's under heavy load. The eBPF trace gave you the clue that logs and metrics alone couldn't provide.</p>
<p>This approach to observability, using the right tool for each question, is how experienced engineers debug complex systems efficiently.</p>
<h3 id="heading-what-ebpf-can-and-cant-see"><strong>What eBPF Can and Can't See</strong></h3>
<p>eBPF excels at:</p>
<ul>
<li><p>Network traffic (requests, responses, latency)</p>
</li>
<li><p>System calls (file I/O, process creation, memory allocation)</p>
</li>
<li><p>Kernel functions (scheduling, locking, resource usage)</p>
</li>
<li><p>Function calls in binaries (with uprobes)</p>
</li>
</ul>
<p>But keep in mind that eBPF has limitations:</p>
<ul>
<li><p>Cannot decrypt encrypted payloads (unless hooking SSL libraries before encryption)</p>
</li>
<li><p>Doesn't automatically understand application logic</p>
</li>
<li><p>Captures low-level events but may need context for high-level semantics</p>
</li>
</ul>
<p>That's why eBPF complements traditional observability rather than replacing it entirely. It gives you infrastructure-level visibility with no code changes and universal coverage. Traditional APM provides application-level context, business metrics, and custom instrumentation. Together, they give you complete observability across your entire stack.</p>
<h2 id="heading-best-practices-and-production-considerations">Best Practices and Production Considerations</h2>
<p>Before using eBPF tracing in production, there are important considerations around performance, security, and operational practices.</p>
<h3 id="heading-performance-impact">Performance Impact</h3>
<p>eBPF's reputation for low overhead is well-deserved, but "low" isn't "zero."</p>
<p>Most eBPF tracing tools add 2-5% CPU overhead and negligible memory overhead. The exact number depends on event frequency, tracing a service that handles 10,000 requests per second will have more overhead than one handling 10 per second.</p>
<p>Measuring the impact:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Before enabling tracing, check baseline resource usage</span>
kubectl top pods -n demo-app

<span class="hljs-comment"># Enable tracing</span>
kubectl gadget traceloop -n demo-app

<span class="hljs-comment"># Check resource usage again</span>
kubectl top pods -n demo-app
</code></pre>
<p>You should see a small increase in CPU usage in the pods where tracing is active. This is the cost of the eBPF programs running in the kernel and processing events.</p>
<h4 id="heading-production-best-practices">Production best practices:</h4>
<p>Use targeted tracing rather than tracing everything everywhere. Trace specific namespaces, pods, or individual containers when investigating issues. For high-volume services, reduce overhead by applying filters, aggregation, or sampling where supported by the tracing tool.</p>
<p>Stop tracing when you’re done investigating. Unlike metrics collection, which typically runs continuously, eBPF-based tracing is best used as an on-demand diagnostic tool to capture detailed insights during active debugging.</p>
<h4 id="heading-when-overhead-matters">When overhead matters:</h4>
<p>If you're running latency-sensitive applications (like high-frequency trading systems or real-time communications), even 2-5% overhead might be unacceptable. In these cases, use eBPF tracing in pre-production environments to identify issues, or enable it temporarily in production only when actively debugging.</p>
<h3 id="heading-security-considerations">Security Considerations</h3>
<p>eBPF is powerful, which means it requires elevated privileges. Understanding the security implications is crucial.</p>
<h4 id="heading-what-ebpf-can-access">What eBPF can access:</h4>
<p>eBPF programs can observe all syscalls, network traffic, and process execution in the kernel. This includes potentially sensitive data like connection details, file paths, and process arguments. While eBPF programs run in a sandbox and can't modify data or crash the kernel, they can read information that might be sensitive.</p>
<h4 id="heading-privilege-requirements">Privilege requirements:</h4>
<p>Loading eBPF programs requires <code>CAP_SYS_ADMIN</code> or <code>CAP_BPF</code> capabilities (on newer kernels). This is a privileged operation, only trusted users should have this access. The Inspektor Gadget DaemonSet runs with these privileges, so protect access to it accordingly.</p>
<h4 id="heading-best-practices">Best practices:</h4>
<p>Implement RBAC (Role-Based Access Control) to restrict who can run gadgets. Not every developer needs the ability to trace production systems.</p>
<p>Also, be mindful of what data you're collecting, if your traces might contain sensitive information (like authentication tokens in HTTP headers), restrict access to trace data.</p>
<p>Lastly, consider using admission controllers to prevent unauthorized eBPF program loading. Audit eBPF usage in production environments to track who ran which gadgets when.</p>
<h4 id="heading-network-policies">Network policies:</h4>
<p>Inspektor Gadget's DaemonSet needs to communicate with the API server and between its components. Ensure your network policies allow this communication while still maintaining appropriate segmentation.</p>
<h3 id="heading-when-to-use-ebpf-tracing-vs-traditional-apm">When to Use eBPF Tracing vs. Traditional APM</h3>
<p>eBPF tracing and traditional APM tools like New Relic, Datadog, or Dynatrace serve different purposes. Understanding when to use each helps you build an effective observability strategy.</p>
<p>Use eBPF tracing when:</p>
<ul>
<li><p>You can't modify application code (third-party applications, legacy systems, compiled binaries)</p>
</li>
<li><p>You need infrastructure-level visibility (network, syscalls, kernel behavior)</p>
</li>
<li><p>You're debugging issues that span service boundaries but don't show up in application logs</p>
</li>
<li><p>You want zero instrumentation overhead during normal operation (run tracing only when needed)</p>
</li>
<li><p>You need to understand what's actually happening versus what the application reports</p>
</li>
</ul>
<p>Use traditional APM when:</p>
<ul>
<li><p>You need business-context metrics (user IDs, transaction types, business-specific data)</p>
</li>
<li><p>You want automatic instrumentation with minimal setup for supported frameworks</p>
</li>
<li><p>You need long-term storage and analysis of all traces (eBPF tracing is often used for real-time investigation)</p>
</li>
<li><p>You want pre-built dashboards and alerting for common application patterns</p>
</li>
<li><p>You need application code-level visibility (stack traces, variable values, function calls)</p>
</li>
</ul>
<h3 id="heading-the-ideal-approach-use-both">The Ideal Approach: Use Both</h3>
<p>Many teams run traditional APM for continuous monitoring and use eBPF tracing for targeted investigation when APM data isn't sufficient. For example, your APM shows that a service is slow but doesn't explain why. You enable eBPF tracing on that service to understand what's happening at the kernel level, network delays, excessive syscalls, unexpected dependencies, and find the root cause.</p>
<p>This complementary approach gives you both the continuous visibility of APM and the deep diagnostic power of eBPF without the overhead of running both at maximum depth all the time.</p>
<h2 id="heading-next-steps-and-resources">Next Steps and Resources</h2>
<p>If you got this far, thanks for reading! Now that you have learned the fundamentals of eBPF observability, and hands-on tracing with Inspektor Gadget, you can continue your journey by:</p>
<h3 id="heading-exploring-other-ebpf-tools">Exploring Other eBPF Tools</h3>
<p>Now that you understand eBPF concepts through traceloop, exploring other tools will be much easier.</p>
<h4 id="heading-try-other-inspektor-gadget-gadgets">Try other Inspektor Gadget gadgets:</h4>
<pre><code class="lang-bash"><span class="hljs-comment"># See all available gadgets</span>
kubectl gadget --<span class="hljs-built_in">help</span>

<span class="hljs-comment"># Some useful ones to explore:</span>
kubectl gadget trace_open -n demo-app     <span class="hljs-comment"># File I/O tracing</span>
kubectl gadget trace_bind -n demo-app     <span class="hljs-comment"># Port binding events</span>
kubectl gadget profile cpu -n demo-app    <span class="hljs-comment"># CPU profiling</span>
kubectl gadget snapshot process -n demo-app  <span class="hljs-comment"># Process listing</span>
</code></pre>
<p>Each gadget teaches you something different about system behavior and gives you another diagnostic tool in your toolkit.</p>
<h3 id="heading-experiment-with-other-ebpf-platforms">Experiment with other eBPF platforms:</h3>
<p>If you're interested in broader observability platforms, try Pixie for its auto-instrumentation and rich UI. Install Cilium with Hubble if you're focused on network observability and want to understand service mesh behavior. Explore Tetragon if security observability interests you, seeing what processes are executing and what files they're accessing.</p>
<p>The concepts transfer directly: all these tools attach eBPF programs to kernel hooks, collect event data, and present it in different ways. Your understanding of syscalls, traces, and kernel-level observation applies universally.</p>
<h3 id="heading-connect-to-the-cncf-observability-ecosystem">Connect to the CNCF Observability Ecosystem</h3>
<p>eBPF observability tools don't exist in isolation. They're part of the broader Cloud Native Computing Foundation ecosystem.</p>
<h4 id="heading-opentelemetry-integration">OpenTelemetry integration:</h4>
<p>Many eBPF tools can export data in OpenTelemetry format, allowing you to combine kernel-level traces with application-level traces in a unified observability backend. This gives you the complete picture: eBPF shows you infrastructure behavior while OpenTelemetry shows you application context.</p>
<h4 id="heading-prometheus-and-grafana">Prometheus and Grafana:</h4>
<p>eBPF-derived metrics can be exposed as Prometheus metrics and visualized in Grafana alongside your application metrics. This unified dashboard approach helps you correlate infrastructure and application behavior.</p>
<h4 id="heading-service-mesh-integration">Service mesh integration:</h4>
<p>If you're using Istio, Linkerd, or other service meshes, eBPF tools like Cilium Hubble can provide deeper visibility into service-to-service communication than the mesh alone provides. The mesh handles traffic management while eBPF gives you kernel-level visibility.</p>
<h4 id="heading-jaeger-and-zipkin">Jaeger and Zipkin:</h4>
<p>For organizations using distributed tracing backends, eBPF traces can be exported to these systems, enriching your trace data with infrastructure-level spans that application instrumentation misses.</p>
<h3 id="heading-community-resources-and-learning-paths">Community Resources and Learning Paths</h3>
<p>The eBPF community is vibrant and welcoming. You can continue learning from the resources below.</p>
<p><strong>Official documentation and blog:</strong></p>
<ul>
<li><p><a target="_blank" href="http://eBPF.io">eBPF.io</a>: The central hub for eBPF documentation, tutorials, and project listings</p>
</li>
<li><p><a target="_blank" href="https://inspektor-gadget.io/docs/latest/">Inspektor Gadget docs</a>: Comprehensive guides for all gadgets and use cases</p>
</li>
<li><p><a target="_blank" href="https://docs.cilium.io/en/stable/index.html">Cilium documentation</a>: Deep dives into eBPF networking</p>
</li>
<li><p><a target="_blank" href="https://www.cncf.io/blog/2025/01/27/what-is-observability-2-0/">CNCF Blog — “What is Observability 2.0?</a>: A quick overview of how modern observability moves beyond traditional tools by unifying metrics, logs, and traces for real-time insight in cloud-native systems.</p>
</li>
</ul>
<p><strong>Learning resources:</strong></p>
<ul>
<li><p><a target="_blank" href="https://cilium.isovalent.com/hubfs/Learning-eBPF%20-%20Full%20book.pdf">Learning eBPF by Liz Rice</a>: Comprehensive book covering eBPF fundamentals</p>
</li>
<li><p><a target="_blank" href="https://ebpf.io/summit-2025/">eBPF Summit</a>: Annual conference with talks from eBPF creators and users</p>
</li>
<li><p><a target="_blank" href="https://www.cncf.io/online-programs/cncf-on-demand-webinar-how-to-start-building-a-self-service-infrastructure-platform-on-kubernetes/">CNCF webinars</a>: Regular sessions on observability topics</p>
</li>
<li><p><a target="_blank" href="https://www.kubernetes.dev/community/community-groups/">Kubernetes observability SIGs</a>: Community discussions and projects</p>
</li>
</ul>
<p>To make this tutorial easy to follow and experiment with, I have included all Kubernetes manifests, demo applications, and eBPF tracing commands in this <a target="_blank" href="https://github.com/Emidowojo/ebpf-k8s-tracing-tutorial">repository</a>. You can also connect with me on <a target="_blank" href="https://www.linkedin.com/in/emidowojo/">LinkedIn</a> if you’d like to stay in touch.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Debug Kubernetes Pods with Traceloop: A Complete Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ Debugging Kubernetes pods can feel like detective work. Your app crashes, and you're left wondering what happened in those critical moments leading up to failure. Traditional kubectl commands show you logs and statuses, but they can't tell you exactl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-debug-kubernetes-pods-with-traceloop-a-complete-beginners-guide/</link>
                <guid isPermaLink="false">68b1d0b4c2405fa2535ed0c8</guid>
                
                    <category>
                        <![CDATA[ Traceloop ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ debugging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ inspektor gadget ]]>
                    </category>
                
                    <category>
                        <![CDATA[ containers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ observability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SRE ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Opaluwa Emidowojo ]]>
                </dc:creator>
                <pubDate>Fri, 29 Aug 2025 16:09:24 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1756483063551/4179b718-7883-4a89-a9c2-1c678185469a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Debugging Kubernetes pods can feel like detective work. Your app crashes, and you're left wondering what happened in those critical moments leading up to failure. Traditional <code>kubectl</code> commands show you logs and statuses, but they can't tell you exactly what your application was doing at the system level when things went wrong.</p>
<p>What if you had a flight recorder for your applications, something that captures every system call in real-time, so you can "rewind" and see the exact sequence of events that led to a crash? That's what Traceloop does. It continuously traces system calls in your pods, giving you a detailed replay of what happened before, during, and after issues occur.</p>
<p>In this guide, you’ll learn how to use Traceloop's system call tracing to debug pod issues that would otherwise be nearly impossible to diagnose.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before we begin, here are some prerequisites – things you’ll need to know and have:</p>
<ul>
<li><p><strong>Basic Kubernetes concepts</strong>: Understanding of pods, deployments, services, and namespaces</p>
</li>
<li><p><strong>kubectl fundamentals</strong>: Comfortable with commands like <code>kubectl get</code>, <code>kubectl describe</code>, <code>kubectl logs</code>, and <code>kubectl exec</code></p>
</li>
<li><p><strong>Container basics</strong>: Understanding how containerized applications work</p>
</li>
<li><p><strong>Basic Linux concepts</strong>: Understanding of processes and system calls (helpful, but we'll explain as we go)</p>
</li>
</ul>
<p><strong>Technical Requirements</strong></p>
<ul>
<li><p><strong>Kubernetes cluster access</strong>: Local (minikube, kind, Docker Desktop) or cloud-based cluster</p>
</li>
<li><p><code>kubectl</code> installed and configured to connect to your cluster</p>
</li>
<li><p>Sufficient permissions (cluster admin or equivalent RBAC) to:</p>
<ul>
<li><p>Install and run eBPF-based tools (Traceloop uses eBPF)</p>
</li>
<li><p>Create/modify pods and deployments</p>
</li>
<li><p>Access pod logs and system-level data</p>
</li>
</ul>
</li>
<li><p><strong>Linux-based Kubernetes nodes</strong>: Most clusters already run on Linux.</p>
</li>
</ul>
<p><strong>System Requirements</strong></p>
<ul>
<li><p><strong>Extended Berkeley Packet Filter (eBPF) support</strong>: Used for tracing and monitoring at the kernel level. Kernel version 5.10+ recommended.</p>
</li>
<li><p><strong>Sufficient cluster resources</strong>: Traceloop runs alongside your applications</p>
</li>
</ul>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-traceloop">What is Traceloop?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-traceloop-works">How Traceloop Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-traceloop">How to Set Up Traceloop</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-your-first-trace-hands-on-tutorial">Your First Trace: Hands-On Tutorial</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-by-step-debugging-walkthrough">Step-by-Step Debugging Walkthrough</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-real-world-debugging-scenarios">Real-World Debugging Scenarios</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-best-practices">Best Practices</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-is-traceloop">What is Traceloop?</h2>
<p><a target="_blank" href="https://inspektor-gadget.io/docs/main/gadgets/traceloop/">Traceloop</a> is a system call tracing and observability tool that works across containerized environments, from Docker containers running locally to pods in production Kubernetes clusters. But before we discuss what that means, let's talk about why system calls matter for debugging.</p>
<p>Every time your application does anything (like opening a file, making a network request, allocating memory, or crashing), it has to interact with the operating system through system calls. These are the fundamental building blocks of how any program interacts with the world around it.</p>
<p>Here's where traditional debugging falls short: when your container crashes, the logs might tell you "segmentation fault" or "out of memory," but they don't tell you the sequence of events that led there. Did the application try to access a file that didn't exist? Was it making network calls that failed? Did it run out of file descriptors?</p>
<p>Traceloop captures this missing piece. It sits at the kernel level using eBPF technology, recording every system call your application makes in real-time. Think of it as installing a dashcam in your application. It's always recording with minimal resources, and when something goes wrong, you have the footage.</p>
<p>Strace is another popular debugging tool – but it requires you to know that there's a problem first. With Traceloop, we can conveniently run it continuously in the background with minimal overhead. If your container crashes at 3am, you can immediately "rewind the tape" and see exactly what system calls happened leading up to the crash.</p>
<p>This helps debug intermittent issues that happen randomly in production but never when you are watching. Because Traceloop is always recording, you finally have visibility into what your application was doing when these mysterious failures occur.</p>
<h2 id="heading-how-traceloop-works">How Traceloop Works</h2>
<p>Now that you understand what Traceloop does, let's look under the hood at how it captures and processes system calls in your containerized environments.</p>
<h3 id="heading-the-technical-foundation">The Technical Foundation</h3>
<p>Traceloop is built on eBPF, a technology that allows programs to run safely in the Linux kernel without changing kernel code. Think of eBPF as a way to install "hooks" directly into the kernel that can observe everything happening on your system with minimal performance impact.</p>
<p>Unlike traditional monitoring tools that work from userspace, eBPF programs run in kernel space, giving them access to system calls as they happen, without relying on the application logging appropriate error messages. This is why Traceloop can capture events that never make it to application logs, like failed system calls or crashes that happen before the application can write anything.</p>
<h3 id="heading-the-flight-recorder-architecture">The Flight Recorder Architecture</h3>
<p>Traceloop uses eBPF maps as an overwriteable ring buffer. Imagine a tape recorder that continuously records over itself. It's always capturing system calls, but it only keeps the most recent data in memory. When something goes wrong, the recording automatically preserves what happened leading up to the incident, just like an airplane's flight recorder after a crash.</p>
<p>This approach solves the production debugging problem: you don't need to predict when issues will happen or attach debuggers after the fact. The recording is always running, waiting for you to need it.</p>
<h3 id="heading-system-call-capture-flow">System Call Capture Flow</h3>
<p>Here's how Traceloop captures and processes system calls across your Kubernetes environment:</p>
<ol>
<li><p><strong>Application pods</strong> generate system calls through normal operation – opening files, making network connections, allocating memory.</p>
</li>
<li><p><strong>eBPF probes (also called hooks)</strong> intercept these system calls at the kernel level before they're processed.</p>
</li>
<li><p><strong>Traceloop recorder</strong> captures the events, buffers them, and adds container context using Inspektor Gadget enrichment (pod name, namespace, container ID).</p>
</li>
<li><p><strong>Output stream</strong> formats the data and makes it available for analysis in real-time or after an incident.</p>
</li>
<li><p><strong>Traceloop user</strong> views and analyzes the captured trace to diagnose the root cause of issues.</p>
</li>
</ol>
<p>Below is a visual representation of the flow. The key advantage is that Traceloop sees everything your application does, even actions that fail silently or happen too quickly for traditional logging to catch. This gives you complete visibility into your application's interaction with the operating system.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755043403339/c5047de7-afc4-48aa-a28e-ee3a1dfbe47f.jpeg" alt="Flow diagram showing how Traceloop works. Application Pods generate system calls, which undergo kernel-level interception via eBPF probes. The probes capture events and pass them to the Traceloop Recorder, which buffers and formats the data. The Output Stream then displays the results to the Traceloop User. The process highlights steps from generating syscalls to capturing, recording, formatting, and presenting the results." class="image--center mx-auto" width="2823" height="981" loading="lazy"></p>
<h3 id="heading-container-isolation-and-context">Container Isolation and Context</h3>
<p>One of Traceloop's strengths is understanding containerized environments. It doesn't just capture raw system calls – it adds context about which pod, container, and namespace generated each call. This means you can trace specific applications without getting overwhelmed by system calls from other containers running on the same node.</p>
<p>This container awareness makes Traceloop particularly powerful in Kubernetes environments where you might have dozens of pods running on a single node, but you only care about debugging one specific application.</p>
<h2 id="heading-how-to-set-up-traceloop">How to Set Up Traceloop</h2>
<p>Before we can start tracing system calls, we need to set up Traceloop in your Kubernetes environment. Traceloop is part of the <a target="_blank" href="https://inspektor-gadget.io/">Inspektor Gadget</a> ecosystem, which provides flexibility in how you use it.</p>
<h3 id="heading-installation-overview">Installation Overview</h3>
<p>This setup:</p>
<ul>
<li><p>Deploys Inspektor Gadget components to all worker nodes</p>
</li>
<li><p>Eliminates the download and initialization overhead on each use, as components are pre-loaded and ready </p>
</li>
<li><p>Eliminates the need to reinstall or reconfigure for each debugging session – just run your traces immediately</p>
</li>
<li><p>Requires cluster admin permissions</p>
</li>
<li><p>Works best for teams doing regular debugging</p>
</li>
</ul>
<h4 id="heading-installation-requirements">Installation Requirements</h4>
<p>First, ensure your cluster meets the requirements:</p>
<ul>
<li><p>Kubernetes cluster with Linux nodes</p>
</li>
<li><p>eBPF support</p>
</li>
<li><p>kubectl installed and configured</p>
</li>
<li><p>Cluster admin permissions</p>
</li>
</ul>
<h4 id="heading-install-kubectl-gadget">Install kubectl gadget</h4>
<p>The recommended way is using krew (kubectl plugin manager):</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install krew if you don't have it</span>
curl -fsSLO <span class="hljs-string">"https://github.com/kubernetes-sigs/krew/releases/latest/download/krew-linux_amd64.tar.gz"</span>
tar zxvf krew-linux_amd64.tar.gz
./krew-linux_amd64 install krew
<span class="hljs-built_in">export</span> PATH=<span class="hljs-string">"<span class="hljs-variable">${KREW_ROOT:-<span class="hljs-variable">$HOME</span>/.krew}</span>/bin:<span class="hljs-variable">$PATH</span>"</span>

<span class="hljs-comment"># Install kubectl gadget</span>
kubectl krew install gadget
</code></pre>
<p>Alternatively, you can install directly:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># For Linux/macOS</span>
curl -sL https://github.com/inspektor-gadget/inspektor-gadget/releases/latest/download/kubectl-gadget-linux-amd64.tar.gz | sudo tar -C /usr/<span class="hljs-built_in">local</span>/bin -xzf - kubectl-gadget

<span class="hljs-comment"># Verify installation</span>
kubectl gadget version
</code></pre>
<h4 id="heading-deploy-inspektor-gadget-to-your-cluster">Deploy Inspektor Gadget to Your Cluster</h4>
<p>Deploy the Inspektor Gadget components to your cluster:</p>
<pre><code class="lang-bash">kubectl gadget deploy
</code></pre>
<p>This installs the necessary DaemonSets and RBAC configurations that allow gadgets like Traceloop to run on your cluster nodes.</p>
<p>Alternatively, you can also deploy using <a target="_blank" href="https://inspektor-gadget.io/docs/v0.43.0/reference/install-kubernetes/#installation-with-the-helm-chart">Helm</a>.</p>
<h4 id="heading-verify-installation">Verify Installation</h4>
<p>Check that the gadget pods are running:</p>
<pre><code class="lang-bash">kubectl get pods -n gadget
</code></pre>
<p>You should see gadget pods running on each node in your cluster.</p>
<h2 id="heading-your-first-trace-hands-on-tutorial">Your First Trace: Hands-On Tutorial</h2>
<p>Now let's capture our first system call trace. We'll create a simple scenario and watch what happens at the system level.</p>
<h3 id="heading-setting-up-the-test-environment">Setting Up the Test Environment</h3>
<p>First, create a dedicated namespace for our tracing experiments:</p>
<pre><code class="lang-bash">kubectl create ns test-traceloop-ns
</code></pre>
<p><strong>Expected output:</strong></p>
<pre><code class="lang-bash">namespace/test-traceloop-ns created
</code></pre>
<p>Next, create a simple pod that we can interact with:</p>
<pre><code class="lang-bash">kubectl run -n test-traceloop-ns --image busybox test-traceloop-pod --<span class="hljs-built_in">command</span> -- sleep inf
</code></pre>
<p><strong>Expected output:</strong></p>
<pre><code class="lang-bash">pod/test-traceloop-pod created
</code></pre>
<p>This creates a BusyBox container that sleeps indefinitely, giving us a stable target for tracing.</p>
<h3 id="heading-starting-your-first-trace">Starting Your First Trace</h3>
<p>Next, start tracing system calls for our test pod:</p>
<pre><code class="lang-bash">kubectl gadget run traceloop:latest --namespace test-traceloop-ns
</code></pre>
<p>This command starts the flight recorder. You'll see column headers showing what information Traceloop captures:</p>
<pre><code class="lang-bash">K8S.NODE    K8S.NAMESPACE    K8S.PODNAME    K8S.CONTAINERNAME    CPU    PID    COMM    SYSCALL    PARAMETERS    RET
</code></pre>
<p>The trace is now running in the background, continuously recording system calls from our pod.</p>
<h3 id="heading-generating-system-calls">Generating System Calls</h3>
<p>With the trace running, let's generate some activity. In a new terminal window, run a command inside your test pod:</p>
<pre><code class="lang-bash">kubectl <span class="hljs-built_in">exec</span> -ti -n test-traceloop-ns test-traceloop-pod -- /bin/sh
</code></pre>
<p>Once inside the container, run some basic commands:</p>
<pre><code class="lang-bash">ls /
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Hello World"</span> &gt; /tmp/test.txt
cat /tmp/test.txt
</code></pre>
<h3 id="heading-collecting-the-trace">Collecting the Trace</h3>
<p>Back in your original terminal where Traceloop is running, press <strong>Ctrl+C</strong> to stop the recording and see the captured system calls.</p>
<p>You'll see output similar to this:</p>
<pre><code class="lang-bash">K8S.NODE            K8S.NAMESPACE        K8S.PODNAME          K8S.CONTAINERNAME    CPU  PID    COMM  SYSCALL      PARAMETERS                   RET
minikube-docker     test-traceloop-ns    test-traceloop-pod   test-traceloop-pod   2    95419  ls    openat       dfd=-100, filename=<span class="hljs-string">"/lib"</span>    3
minikube-docker     test-traceloop-ns    test-traceloop-pod   test-traceloop-pod   2    95419  ls    getdents64   fd=3, dirent=0x...          201
minikube-docker     test-traceloop-ns    test-traceloop-pod   test-traceloop-pod   2    95419  ls    write        fd=1, buf=<span class="hljs-string">"bin dev etc..."</span>   201
minikube-docker     test-traceloop-ns    test-traceloop-pod   test-traceloop-pod   2    95419  ls    exit_group   error_code=0                 0
</code></pre>
<h3 id="heading-understanding-your-first-trace">Understanding Your First Trace</h3>
<p>Let's break down what we're seeing:</p>
<ul>
<li><p><strong>K8S.PODNAME</strong>: Which pod generated these system calls</p>
</li>
<li><p><strong>PID</strong>: Process ID of the command that ran</p>
</li>
<li><p><strong>COMM</strong>: The command name (ls, echo, cat)</p>
</li>
<li><p><strong>SYSCALL</strong>: The actual system call made (openat, write, exit_group)</p>
</li>
<li><p><strong>PARAMETERS</strong>: Arguments passed to the system call</p>
</li>
<li><p><strong>RET</strong>: Return value (0 usually means success)</p>
</li>
</ul>
<p>This trace shows the <code>ls</code> command opening the <code>/lib</code> directory, reading directory entries, writing the output to stdout, and exiting successfully.</p>
<h3 id="heading-clean-up">Clean Up</h3>
<p>Remove the test resources:</p>
<pre><code class="lang-bash">kubectl delete pod test-traceloop-pod -n test-traceloop-ns
kubectl delete ns test-traceloop-ns
</code></pre>
<p>You can now see exactly what your applications are doing at the kernel level, something that traditional logs and kubectl commands can't show you.</p>
<p>Let's try this with an application that crashes.</p>
<h2 id="heading-step-by-step-debugging-walkthrough">Step-by-Step Debugging Walkthrough</h2>
<p>Now that you know how to capture traces, let's take a look at a real debugging scenario. We'll create an application that crashes and use Traceloop to uncover the root cause. Something that would be nearly impossible with traditional kubectl debugging.</p>
<h3 id="heading-the-scenario-a-mysterious-crash">The Scenario: A Mysterious Crash</h3>
<p>Let's create a Python application that has a subtle bug. It tries to write to a file it doesn't have permission to access, then crashes. This mimics real-world scenarios where applications fail due to permission issues, missing files, or resource constraints.</p>
<h3 id="heading-setting-up-the-problematic-application">Setting Up the Problematic Application</h3>
<p>First, we’ll create a new namespace for our debugging exercise:</p>
<pre><code class="lang-bash">kubectl create ns debug-traceloop-ns
</code></pre>
<p>Now, let's create a pod with an application that will crash:</p>
<pre><code class="lang-bash">kubectl run -n debug-traceloop-ns crash-app --image=python:3.9-slim --restart=Never -- python3 -c <span class="hljs-string">"
import time
import os
print('App starting...')
time.sleep(5)
print('Trying to write to restricted file...')
try:
    with open('/etc/passwd', 'w') as f:
        f.write('malicious content')
except Exception as e:
    print(f'Error: {e}')
    exit(1)
"</span>
</code></pre>
<p>This creates a pod that will:</p>
<ol>
<li><p>Start successfully</p>
</li>
<li><p>Try to write to <code>/etc/passwd</code> (a restricted system file)</p>
</li>
<li><p>Fail and crash with exit code 1</p>
</li>
</ol>
<h3 id="heading-starting-the-trace-before-the-crash">Starting the Trace Before the Crash</h3>
<p>Here's the key difference from traditional debugging. We start tracing before we know there's a problem. In a real scenario, you'd have Traceloop running continuously.</p>
<pre><code class="lang-bash">kubectl gadget run traceloop:latest --namespace debug-traceloop-ns
</code></pre>
<p>The trace starts recording immediately. You'll see the column headers, and the flight recorder is now capturing every system call.</p>
<h3 id="heading-observing-the-application-behavior">Observing the Application Behavior</h3>
<p>In another terminal, check the pod status:</p>
<pre><code class="lang-bash">kubectl get pods -n debug-traceloop-ns -w
</code></pre>
<p>You'll see the pod go through these states:</p>
<ul>
<li><code>Pending</code> → <code>Running</code> → <code>Error</code> → <code>CrashLoopBackOff</code></li>
</ul>
<p>Traditional debugging would show you:</p>
<pre><code class="lang-bash">kubectl logs -n debug-traceloop-ns crash-app
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">App starting...
Trying to write to restricted file...
Error: [Errno 13] Permission denied: <span class="hljs-string">'/etc/passwd'</span>
</code></pre>
<p>But this doesn't tell you exactly what the application tried to do at the system level.</p>
<h3 id="heading-collecting-and-analyzing-the-trace">Collecting and Analyzing the Trace</h3>
<p>Back in your Traceloop terminal, press <strong>Ctrl+C</strong> to stop the recording. You'll see system calls like this:</p>
<pre><code class="lang-bash">K8S.NODE        K8S.NAMESPACE      K8S.PODNAME  COMM    SYSCALL    PARAMETERS                           RET
minikube-docker debug-traceloop-ns crash-app    python3 openat     dfd=-100, filename=<span class="hljs-string">"/etc/passwd"</span>    -13
minikube-docker debug-traceloop-ns crash-app    python3 write      fd=3, buf=<span class="hljs-string">"App starting..."</span>         16
minikube-docker debug-traceloop-ns crash-app    python3 openat     dfd=-100, filename=<span class="hljs-string">"/etc/passwd"</span>    -13
minikube-docker debug-traceloop-ns crash-app    python3 exit_group error_code=1                        0
</code></pre>
<h3 id="heading-reading-the-system-call-story">Reading the System Call Story</h3>
<p>The trace reveals the exact sequence of events:</p>
<ol>
<li><p><code>openat filename="/etc/passwd" RET=-13</code>: The application tried to open <code>/etc/passwd</code> for writing</p>
<ul>
<li>Return code <code>-13</code> = <code>EACCES</code> (Permission denied)</li>
</ul>
</li>
<li><p><code>write buf="App starting..."</code>: Normal logging output (successful)</p>
</li>
<li><p><code>openat filename="/etc/passwd" RET=-13</code>: Second attempt to open the restricted file (still denied)</p>
</li>
<li><p><code>exit_group error_code=1</code>: Application exits with error code 1</p>
</li>
</ol>
<h3 id="heading-what-traceloop-revealed">What Traceloop Revealed</h3>
<p>Traditional debugging told us "Permission denied" but Traceloop shows us:</p>
<ul>
<li><p><strong>Exactly which file</strong> the application tried to access</p>
</li>
<li><p><strong>When</strong> the permission denial happened in the execution flow</p>
</li>
<li><p><strong>How many times</strong> it tried (twice in this case)</p>
</li>
<li><p><strong>The exact system call</strong> that failed (<code>openat</code>)</p>
</li>
</ul>
<h3 id="heading-real-world-applications">Real-World Applications</h3>
<p>This same approach works for debugging:</p>
<ul>
<li><p><strong>File not found errors</strong>: See exactly which files your app is looking for</p>
</li>
<li><p><strong>Network connection failures</strong>: Observe failed <code>connect()</code> system calls with specific addresses</p>
</li>
<li><p><strong>Memory issues</strong>: Watch <code>mmap()</code> and <code>brk()</code> calls that fail</p>
</li>
<li><p><strong>Container startup problems</strong>: See which system calls fail during initialization</p>
</li>
</ul>
<h3 id="heading-clean-up-1">Clean Up</h3>
<p>Remove the test resources:</p>
<pre><code class="lang-bash">kubectl delete pod crash-app -n debug-traceloop-ns
kubectl delete ns debug-traceloop-ns
</code></pre>
<h3 id="heading-key-takeaway">Key Takeaway</h3>
<p>Traditional Kubernetes debugging shows you what went wrong after it happened. Traceloop's continuous recording shows you exactly how it went wrong at the system level. This level of detail is invaluable for debugging complex production issues where the logs don't tell the full story.</p>
<h2 id="heading-real-world-debugging-scenarios">Real-World Debugging Scenarios</h2>
<p>Now that you understand the fundamentals, let's explore common production issues and how Traceloop helps diagnose them. These scenarios mirror real problems you'll encounter in Kubernetes environments.</p>
<h3 id="heading-scenario-1-container-startup-failures">Scenario 1: Container Startup Failures</h3>
<p><strong>The problem</strong>: Your pod gets stuck in <code>CrashLoopBackOff</code> with unhelpful logs.</p>
<p>Traditional <code>kubectl</code> commands show limited information:</p>
<pre><code class="lang-bash">kubectl describe pod failing-app
<span class="hljs-comment"># Events: Back-off restarting failed container</span>

kubectl logs failing-app
<span class="hljs-comment"># (Empty or minimal output)</span>
</code></pre>
<p>System calls show the application tried to:</p>
<ol>
<li><p>Access configuration files that don't exist</p>
</li>
<li><p>Connect to services that aren't available</p>
</li>
<li><p>Write to directories without proper permissions</p>
</li>
</ol>
<p>Key system calls to watch:</p>
<ol>
<li><p><code>openat</code> with <code>-2</code> return (file not found)</p>
</li>
<li><p><code>connect</code> with <code>-111</code> return (connection refused)</p>
</li>
<li><p><code>access</code> with <code>-13</code> return (permission denied)</p>
</li>
</ol>
<h3 id="heading-scenario-2-memory-and-resource-issues">Scenario 2: Memory and Resource Issues</h3>
<p><strong>The problem</strong>: Application performance degrades or gets OOMKilled.</p>
<p>What Traceloop shows:</p>
<ol>
<li><p><code>mmap</code> calls failing (memory allocation issues)</p>
</li>
<li><p><code>brk</code> system calls indicating heap growth</p>
</li>
<li><p>File descriptor exhaustion through failed <code>openat</code> calls</p>
</li>
<li><p>Excessive <code>write</code> calls indicating memory pressure</p>
</li>
</ol>
<p><strong>Example pattern</strong>:</p>
<pre><code class="lang-bash">SYSCALL    PARAMETERS           RET
mmap       length=1048576       -12  <span class="hljs-comment"># ENOMEM - out of memory</span>
brk        brk=0x55555557d000   0    <span class="hljs-comment"># Heap expansion</span>
openat     filename=<span class="hljs-string">"/tmp/..."</span>   -24  <span class="hljs-comment"># EMFILE - too many open files</span>
</code></pre>
<h3 id="heading-scenario-3-network-connectivity-problems">Scenario 3: Network Connectivity Problems</h3>
<p><strong>The problem</strong>: Service-to-service communication fails intermittently.</p>
<p>Traditional debugging limitations:</p>
<ol>
<li><p>Application logs show "connection timeout"</p>
</li>
<li><p>Network policies seem correct</p>
</li>
<li><p>DNS resolution appears to work</p>
</li>
</ol>
<p>What Traceloop reveals:</p>
<ol>
<li><p>Exact IP addresses and ports being attempted</p>
</li>
<li><p>DNS resolution patterns through <code>openat</code> on <code>/etc/resolv.conf</code></p>
</li>
<li><p>Failed <code>connect</code> calls with specific error codes</p>
</li>
<li><p>Socket creation and binding issues</p>
</li>
</ol>
<p><strong>Key indicators</strong>:</p>
<pre><code class="lang-bash">SYSCALL    PARAMETERS                    RET
socket     family=AF_INET, <span class="hljs-built_in">type</span>=SOCK     3
connect    fd=3, addr=10.96.0.1:443     -110  <span class="hljs-comment"># ETIMEDOUT</span>
close      fd=3                         0
</code></pre>
<h3 id="heading-scenario-4-configuration-and-secret-issues">Scenario 4: Configuration and Secret Issues</h3>
<p><strong>The problem</strong>: Application can't access mounted secrets or config maps.</p>
<p>What system calls reveal:</p>
<ol>
<li><p>File access patterns for mounted volumes</p>
</li>
<li><p>Permission checks on secret files</p>
</li>
<li><p>Configuration file parsing attempts</p>
</li>
</ol>
<p>Common patterns:</p>
<ol>
<li><p>Multiple <code>openat</code> attempts on different config file paths</p>
</li>
<li><p><code>access</code> calls checking file permissions before opening</p>
</li>
<li><p>Failed reads from mounted secret volumes</p>
</li>
</ol>
<h3 id="heading-scenario-5-performance-bottlenecks">Scenario 5: Performance Bottlenecks</h3>
<p><strong>The problem</strong>: Application response times are slow without obvious cause.</p>
<p>Traceloop analysis:</p>
<ol>
<li><p>Excessive <code>fsync</code> calls (disk I/O bottlenecks)</p>
</li>
<li><p>Many <code>futex</code> calls (lock contention)</p>
</li>
<li><p>Frequent <code>recvfrom</code> timeouts (network issues)</p>
</li>
<li><p>Repeated file system operations</p>
</li>
</ol>
<p><strong>Performance indicators</strong>:</p>
<pre><code class="lang-bash">SYSCALL     FREQUENCY    ISSUE
fsync       High         Disk I/O bottleneck
futex       Excessive    Lock contention
poll        Many         Waiting <span class="hljs-keyword">for</span> I/O
recvfrom    Timeouts     Network delays
</code></pre>
<h2 id="heading-best-practices"><strong>Best Practices</strong></h2>
<h3 id="heading-when-to-use-traceloop"><strong>When to Use Traceloop</strong></h3>
<p>Traceloop is most useful when you’re dealing with the kinds of problems that are notoriously difficult to pin down. If you’ve ever struggled with debugging intermittent crashes that don’t happen on demand, or run into confusing permission and access issues, this is where it works best.  </p>
<p>It also helps uncover performance bottlenecks at the system level and provides visibility into application behavior during tricky startup failures. Another common use case is diagnosing network connectivity problems between pods, where other tools usually can't help</p>
<p>Of course, not every problem requires system call tracing. For application-level issues, logs and APM tools are more effective. Cluster-level concerns are often better handled with <code>kubectl describe</code> or by looking at events, and if you’re primarily monitoring resources, standard metrics and dashboards show you what's happening.</p>
<h3 id="heading-performance-considerations"><strong>Performance Considerations</strong></h3>
<p>Like any tracing tool, Traceloop adds some overhead, but it keeps the overhead low. You can keep it efficient by narrowing the scope of your traces. For example, filtering by namespace with <code>--namespace specific-ns</code>, or targeting specific pods using <code>--podname target-pod</code>. In high-traffic environments, it’s best to run traces for shorter periods, and node-specific tracing can further isolate debugging when you don’t want to instrument the entire cluster.</p>
<p>In most cases, Traceloop uses very little CPU and memory, thanks to its eBPF-based approach. This makes it lighter than traditional tools like strace. The actual cost depends on the volume of system calls being recorded, so it’s a good practice to monitor resource usage in your own environment to confirm it’s operating within acceptable limits.</p>
<h3 id="heading-integration-with-your-workflow"><strong>Integration with Your Workflow</strong></h3>
<p>Traceloop works well in dev and production workflows. In development, it’s a powerful way to understand how your application interacts with the system. You can use it to confirm that your app handles edge cases correctly, or to validate permission and resource configurations before promoting workloads into production.</p>
<p>In production environments, you can deploy it in different ways. Depending on how much overhead you're okay with, some teams run it continuously on a small subset of nodes, while others use it only when traditional debugging methods don’t provide enough insight. Pairing Traceloop with your existing monitoring and logging stack can give you a much more complete picture of system behavior.</p>
<p>It also helps with teamwork. Sharing trace outputs makes it easier for teams to reason about complex issues together. The data it provides can guide improvements in error handling and logging, and documenting common system call patterns can help onboard new developers more quickly.</p>
<h3 id="heading-security-considerations"><strong>Security Considerations</strong></h3>
<p>Because Traceloop records low-level system activity, you need to be mindful of what it captures.</p>
<p><strong>What Traceloop Can See:</strong></p>
<ul>
<li><p>System call parameters (such as filenames and network addresses)</p>
</li>
<li><p>Process information and command arguments</p>
</li>
<li><p>File access patterns and permissions</p>
</li>
</ul>
<p><strong>Privacy Measures:</strong></p>
<ul>
<li><p>Limit trace duration to minimize data collection</p>
</li>
<li><p>Use namespace isolation to avoid capturing unrelated workloads</p>
</li>
<li><p>Apply data retention policies for trace outputs</p>
</li>
<li><p>Watch for sensitive information in file paths or system call parameters</p>
</li>
</ul>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Traceloop doesn’t just tell you something went wrong – it shows you how. By recording every system call in real time, it turns mysterious Kubernetes failures into solvable problems. Whether the issue happened seconds ago or in the middle of the night, the tool gives you the ability to rewind, inspect, and respond with confidence.</p>
<h3 id="heading-when-to-use-it">When to Use It</h3>
<p>Keep in mind that Traceloop complements your existing debugging toolkit rather than replacing it. Reach for it when logs don’t tell the whole story, when intermittent problems are hiding in the shadows, when <code>kubectl</code> commands leave you guessing, or when you need to see how your application is really interacting with the system.</p>
<p>Once you’re comfortable with Traceloop, you can add more tools. <a target="_blank" href="https://inspektor-gadget.io/">Inspektor Gadget</a> offers other tools for network, security, and performance debugging that pair well with Traceloop. Integrating it into your incident response workflow, sharing insights across your team, and even considering continuous tracing for critical workloads are good things to try next.</p>
<p>The next time you run into a stubborn Kubernetes pod failure, you won’t be stuck speculating. With Traceloop, you can “rewind the tape” and see exactly what happened. System call tracing may sound complex at first, but in practice, it’s one of the most powerful ways to truly understand how applications behave in containerized environments.</p>
<p><strong>PS:</strong> Have any questions about Traceloop or want to share your debugging challenges? The Inspektor Gadget team and community hang out in the <a target="_blank" href="https://kubernetes.slack.com/archives/CSYL75LF6">#inspektor-gadget</a> channel on Kubernetes Slack. It's a great place to get help from the engineers who built these tools, share experiences, and maybe even contribute to making the ecosystem even better.  </p>
<p>You can also connect with me on <a target="_blank" href="https://www.linkedin.com/in/emidowojo/">LinkedIn</a> if you’d like to stay in touch. If you made it to the end of this tutorial, thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
