<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title> Chever John 's Blog</title>
        <link>https://blog.cheverjohn.me</link>
        <description> Chever John 's blog and personal website. Software engineer, Photographer</description>
        <lastBuildDate>Fri, 30 Jan 2026 07:47:53 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>Feed for Node.js</generator>
        <language>en</language>
        <ttl>60</ttl>
        <image>
            <title> Chever John 's Blog</title>
            <url>https://blog.cheverjohn.me/assets/avatar.jpg</url>
            <link>https://blog.cheverjohn.me</link>
        </image>
        <copyright>All rights reserved 2026, Chenwei Jiang</copyright>
        <item>
            <title><![CDATA[TLS Basics and a Real Traefik “Not secure” Incident]]></title>
            <link>https://blog.cheverjohn.me/traefik-certificate-troubleshooting</link>
            <guid>https://blog.cheverjohn.me/traefik-certificate-troubleshooting</guid>
            <pubDate>Wed, 21 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[TL;DR - Symptom: https://traefik.example.local/dashboard/ shows Not secure in Chrome/Safari; Firefox works fine. - Root cause: macOS Trust validator rejects wildcard cert .example.local for subdomain traefik.example.local (OpenSSL accepts it, Apple Trust does not). - Fix: Issue explicit hostname cer...]]></description>
            <content:encoded><![CDATA[<article><div><h2 id="tldr">TL;DR</h2><ul><li><strong>Symptom</strong>: <code>https://traefik.example.local/dashboard/</code> shows <strong>Not secure</strong> in Chrome/Safari; Firefox works fine.</li><li><strong>Root cause</strong>: macOS Trust validator rejects wildcard cert <code>*.example.local</code> for subdomain <code>traefik.example.local</code> (OpenSSL accepts it, Apple Trust does not).</li><li><strong>Fix</strong>: Issue explicit hostname certificate for <code>traefik.example.local</code> instead of betting on wildcard.</li><li><strong>In one sentence</strong>: Validator rules determine everything, not &quot;the cert is broken.&quot;</li></ul><hr/><p>This was not &quot;the certificate is broken.&quot; It was &quot;the certificate is validated by a different engine.&quot;
OpenSSL said OK. Apple Trust said no. Chrome/Safari follow Apple Trust.</p><p><strong>This post has two parts: TLS basics + the real troubleshooting chain.</strong></p><h2 id="tls-basics-only-the-essentials">TLS basics (only the essentials)</h2><p>Let’s keep the core pieces:</p><ul><li><strong>Certificate chain</strong>: leaf → intermediate (optional) → root CA. Browsers trust roots, not leaves.</li><li><strong>SAN (Subject Alternative Name)</strong>: hostname matching is done here; CN is legacy.</li><li><strong>Trust store</strong>: macOS uses System Keychain; Firefox has its own store; Chrome/Safari use system trust.</li><li><strong>Validation</strong>: the same cert can be accepted by one validator and rejected by another.</li></ul><p>One sentence: <strong>“Valid” does not mean “accepted by your current validator.”</strong></p><h2 id="problem-symptoms-and-impact-scope">Problem Symptoms and Impact Scope</h2><p>Accessing <code>https://traefik.example.local/dashboard/#/</code> showed <strong>Not secure</strong> in Chrome/Safari.</p><p><strong>Impact scope:</strong></p><ul><li>❌ Chrome (relies on macOS System Keychain)</li><li>❌ Safari (relies on macOS System Keychain)</li><li>✅ Firefox (uses its own cert store, unaffected)</li></ul><p><strong>Reproduction prerequisites:</strong></p><ul><li>macOS version: Sonoma 14.x+ (other versions may behave differently)</li><li>CA already installed in macOS Keychain and set to &quot;Always Trust&quot;</li><li>Certificate issued by mkcert (version 1.4.4)</li><li>Service running on <code>10.0.0.100</code></li></ul><p><strong>I needed three answers:</strong></p><ol start="1"><li>Does DNS point to the right server?</li><li>Is the cert chain complete and SAN correct?</li><li>Does the system trust engine accept the hostname?</li></ol><h2 id="hypothesis-checklist-in-elimination-order">Hypothesis Checklist (in elimination order)</h2><p>Before troubleshooting, list possible root causes:</p><ul><li><p><strong>H1: CA not properly installed in system trust store</strong></p><ul><li>Validation: Check macOS Keychain, confirm CA cert exists and is &quot;Always Trust&quot;</li><li>Expected: If CA missing, all browsers would fail (but Firefox works, so low probability)</li></ul></li><li><p><strong>H2: Incomplete certificate chain (missing intermediate cert)</strong></p><ul><li>Validation: <code>openssl s_client -showcerts</code> to check chain depth</li><li>Expected: Self-signed CA usually has only two levels (leaf + root), mkcert default is complete</li></ul></li><li><p><strong>H3: SAN does not include target domain</strong></p><ul><li>Validation: <code>openssl x509 -text</code> to check Subject Alternative Name field</li><li>Expected: If SAN missing or mismatched, OpenSSL would also fail (but curl passed, so low probability)</li></ul></li><li><p><strong>H4: Validator rule differences (OpenSSL vs Apple Trust)</strong></p><ul><li>Validation: Use <code>security verify-cert</code> to simulate macOS system validation</li><li>Expected: <strong>This is the most likely root cause</strong> — different validators have different hostname matching rules for wildcards</li></ul></li></ul><p>Validate each hypothesis in order.</p><h2 id="validation-flow-visualization">Validation Flow Visualization</h2><p>This diagram shows the 4-layer validation progression (no execution, conceptual only):</p><pre><code class="lang-bash">#!/usr/bin/env bash
# Validation flow: 4-layer check to identify which layer rejects

cat &lt;&lt;&#x27;EOF&#x27;

┌─────────────────────────────────────────────────────────┐
│  Layer 1: DNS Resolution                                │
│  dig traefik.example.local +short                       │
│  ✅ Result: 10.0.0.100                                   │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  Layer 2: Certificate Content (SAN)                     │
│  openssl s_client + grep &quot;Subject Alternative Name&quot;     │
│  ✅ Result: DNS:*.example.local, DNS:example.local      │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  Layer 3: OpenSSL Validator (Linux/curl)                │
│  curl -Iv https://traefik.example.local/                │
│  ✅ Result: SSL certificate verify ok                   │
└─────────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────────┐
│  Layer 4: Apple Trust Validator (macOS)                 │
│  security verify-cert -n traefik.example.local          │
│  ❌ Result: Host name mismatch                          │
└─────────────────────────────────────────────────────────┘

Key finding: Layer 3 ✅ but Layer 4 ❌
→ Different validators have different hostname matching rules for wildcards
→ Chrome/Safari rely on Apple Trust (Layer 4)

EOF
</code></pre>
<h2 id="the-troubleshooting-chain-in-order">The troubleshooting chain (in order)</h2><h3 id="1-dns-check">1) DNS check</h3><pre><code class="lang-bash">dig traefik.example.local +short
</code></pre>
<p><strong>Expected output:</strong></p><pre><code class="lang-text">10.0.0.100
</code></pre>
<p><strong>Observation</strong>: DNS points correctly, network layer is fine.</p><p><strong>Conclusion</strong>: Network connectivity ruled out, proceed to certificate layer.</p><h3 id="2-inspect-the-cert-chain-and-san">2) Inspect the cert chain and SAN</h3><pre><code class="lang-bash">openssl s_client -connect traefik.example.local:443 -servername traefik.example.local -showcerts 2&gt;/dev/null | openssl x509 -text -noout | grep -A2 &quot;Subject Alternative Name&quot;
</code></pre>
<p><strong>Expected output (key part):</strong></p><pre><code class="lang-text">X509v3 Subject Alternative Name:
    DNS:*.example.local, DNS:example.local
</code></pre>
<p><strong>Observation</strong>: SAN contains wildcard <code>*.example.local</code>, which should theoretically match <code>traefik.example.local</code> per RFC 6125.</p><p><strong>Conclusion</strong>: Certificate fields are correct, <strong>H3 (SAN missing) eliminated</strong>.</p><h3 id="3-openssl-validation-passes">3) OpenSSL validation (passes)</h3><pre><code class="lang-bash">curl -Iv https://traefik.example.local/ 2&gt;&amp;1 | grep -E &quot;SSL certificate verify|subject:&quot;
</code></pre>
<p><strong>Expected output:</strong></p><pre><code class="lang-text">* SSL certificate verify ok.
* subject: CN=*.example.local
</code></pre>
<p><strong>Observation</strong>: OpenSSL validator considers cert valid, hostname matching passed.</p><p><strong>Conclusion</strong>: <strong>OpenSSL finds no issues</strong>, <strong>H2 (incomplete chain) eliminated</strong>.</p><h3 id="4-macos-system-validation-fails">4) macOS system validation (fails)</h3><pre><code class="lang-bash"># Export cert to temp file first (if not already done)
echo | openssl s_client -connect traefik.example.local:443 -servername traefik.example.local 2&gt;/dev/null | \
  openssl x509 &gt; /tmp/traefik-leaf.pem

# macOS system-level validation
security verify-cert -c /tmp/traefik-leaf.pem -p ssl -n traefik.example.local -L -v
</code></pre>
<p><strong>Expected output (key error):</strong></p><pre><code class="lang-text">...certificate verification failed
...Host name mismatch
</code></pre>
<p><strong>Observation</strong>: Apple Trust explicitly rejects this wildcard match for <code>traefik.example.local</code>.</p><p><strong>Conclusion</strong>: <strong>Apple Trust considers this SAN unable to match the target domain</strong>. This is the pivotal point: OpenSSL OK, Apple Trust not OK. Chrome/Safari rely on Apple Trust. <strong>H4 (validator rule differences) confirmed</strong>.</p><h2 id="root-cause">Root cause</h2><p>Not a missing CA, not a broken chain. The real cause:</p><p><strong>macOS hostname validation did not accept the wildcard cert for <code>traefik.example.local</code>.</strong></p><p>This is not theory; it is the system validator&#x27;s explicit result.</p><h2 id="minimal-reliable-fix">Minimal, reliable fix</h2><p>Stop relying on the wildcard. Issue an explicit hostname certificate.</p><pre><code class="lang-bash">mkcert -cert-file traefik.example.local.pem -key-file traefik.example.local-key.pem &quot;traefik.example.local&quot;
</code></pre>
<p>If you want to keep wildcard + apex too:</p><pre><code class="lang-bash">mkcert -cert-file traefik.example.local.pem -key-file traefik.example.local-key.pem \
  &quot;traefik.example.local&quot; &quot;*.example.local&quot; &quot;example.local&quot;
</code></pre>
<p>Then replace the Traefik TLS cert and restart the service.</p><h2 id="a-reusable-checklist">A reusable checklist</h2><ul><li>DNS resolution is correct (<code>dig</code>)</li><li>SAN includes the target hostname (<code>openssl x509 -text</code>)</li><li>OpenSSL validation (<code>curl -Iv</code>)</li><li>macOS validation (<code>security verify-cert</code>)</li><li>Browser trust store alignment</li></ul><h2 id="retrospective-and-prevention">Retrospective and Prevention</h2><h3 id="one-sentence-lesson">One-Sentence Lesson</h3><p><strong>The most valuable evidence:</strong>
The <code>Host name mismatch</code> output from <code>security verify-cert</code> — it directly proved the issue was not &quot;CA trust&quot; but &quot;hostname validation rules.&quot;</p><p><strong>If you remember one thing:</strong>
Don&#x27;t judge cert correctness by &quot;it opens on my machine.&quot; Different validators (OpenSSL / Apple Trust / Firefox NSS) can reach different conclusions on the same cert.</p><hr/><h3 id="prevention-checklist-for-next-time-you-issue-certs">Prevention Checklist (for next time you issue certs)</h3><p><strong>For internal self-signed CA (mkcert/self-built CA):</strong></p><ul><li><input readonly="" type="checkbox"/> Prefer explicit hostnames over betting on wildcard cross-platform compatibility</li><li><input readonly="" type="checkbox"/> If you must use wildcards, pre-validate with <code>security verify-cert</code> on macOS</li><li><input readonly="" type="checkbox"/> Keep issuance command records (with complete SAN list) for easy re-issuance</li></ul><p><strong>For public certs (Let&#x27;s Encrypt, etc.):</strong></p><ul><li><input readonly="" type="checkbox"/> Request both <code>example.com</code> and <code>*.example.com</code> if you need to cover apex and subdomains</li><li><input readonly="" type="checkbox"/> Validate on at least 3 platforms: Linux (OpenSSL) / macOS (Apple Trust) / Windows (Schannel)</li></ul><p><strong>Validation template (reusable script):</strong></p><pre><code class="lang-bash">#!/usr/bin/env bash
# Quick validation of cert behavior across validators

DOMAIN=&quot;traefik.example.local&quot;

echo &quot;==&gt; OpenSSL validation&quot;
curl -Iv https://${DOMAIN}/ 2&gt;&amp;1 | grep -E &quot;SSL certificate verify|subject:&quot;

echo &quot;==&gt; macOS validation (must run on macOS)&quot;
# Export cert first
echo | openssl s_client -connect ${DOMAIN}:443 -servername ${DOMAIN} 2&gt;/dev/null | \
  openssl x509 &gt; /tmp/cert-to-verify.pem
# System validation
security verify-cert -c /tmp/cert-to-verify.pem -p ssl -n ${DOMAIN} -L -v

echo &quot;==&gt; Check SAN&quot;
openssl s_client -connect ${DOMAIN}:443 -servername ${DOMAIN} &lt;/dev/null 2&gt;/dev/null | \
  openssl x509 -text -noout | grep -A2 &quot;Subject Alternative Name&quot;
</code></pre>
<hr/><h3 id="final-conclusion">Final Conclusion</h3><p>&quot;I already installed the CA&quot; is not the end of the story.
What matters is <strong>which validator you are actually using</strong>.</p><p>This time the fix is simple and clean:
<strong>issue an explicit cert for the host and the warning disappears.</strong></p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>tls</category>
            <category>https</category>
            <category>traefik</category>
            <category>ops</category>
        </item>
        <item>
            <title><![CDATA[Self-Hosting Deployment Notes: Traefik + Docker Compose + Domain-Only Access + Shared Middleware]]></title>
            <link>https://blog.cheverjohn.me/self-hosting-deployment-notes</link>
            <guid>https://blog.cheverjohn.me/self-hosting-deployment-notes</guid>
            <pubDate>Thu, 08 Jan 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[This post summarizes what worked (and what hurt) when I deployed multiple self-hosted services in this repo (e.g. dockhand, vaultwarden, Checkmate, Miniflux). It focuses on repeatable defaults, pitfalls, and a practical debugging workflow. Goals and non-negotiables - Domain-only access: Do not expos...]]></description>
            <content:encoded><![CDATA[<article><div><p>This post summarizes what worked (and what hurt) when I deployed multiple self-hosted services in this repo (e.g. <code>dockhand</code>, <code>vaultwarden</code>, <code>Checkmate</code>, <code>Miniflux</code>). It focuses on repeatable defaults, pitfalls, and a practical debugging workflow.</p><h2 id="goals-and-non-negotiables">Goals and non-negotiables</h2><ul><li><strong>Domain-only access</strong>: Do not expose host ports for business services. Avoid <code>IP:port</code> bypassing auth or leaking admin panels.</li><li><strong>Single entrypoint</strong>: Only open <code>80/443</code> externally. Let <strong>Traefik</strong> handle reverse proxying, TLS, and security headers.</li><li><strong>Shared middleware</strong>: Run shared MongoDB / Redis / PostgreSQL once and reuse them across apps to reduce waste.</li><li><strong>Never break userspace</strong>: Any change must not break existing online services (routing/port/network/auth conflicts).</li></ul><h2 id="repo-layout-and-my-deployment-defaults">Repo layout and my deployment defaults</h2><ul><li><strong>One app per directory</strong>: e.g. <code>dockhand/</code>, <code>vaultwarden/</code>, <code>Checkmate/</code>, <code>v2/</code> (Miniflux).</li><li><strong>Each app has a runnable <code>docker-compose.yml</code></strong>: avoid scattered upstream examples under <code>contrib/</code>.</li><li><strong>Shared middleware is a separate project</strong>: <code>middlewares/</code> runs <code>postgres</code> / <code>mongodb</code> / <code>redis</code>.
<ul><li>Shared middleware joins internal networks only and exposes <strong>no public ports</strong>.</li><li>Apps join shared networks via <code>external</code> networks.</li></ul></li><li><strong>One Traefik network</strong>: all services that Traefik should reach join an external network like <code>traefik_proxy</code>.</li><li><strong>Prefer Docker labels</strong> (docker provider): use file provider only for a few non-container targets or special cases.</li></ul><h2 id="standard-deployment-playbook-the-core-tricks">Standard deployment playbook (the core tricks)</h2><h3 id="1-do-not-publish-host-ports">1) Do not publish host ports</h3><p>For public-facing services:</p><ul><li>Use <code>expose:</code> (container-network visible only) instead of <code>ports:</code> (host visible).</li><li>Route traffic via Traefik using labels.</li></ul><p>This immediately reduces:</p><ul><li><strong>Port conflicts</strong> (e.g. host <code>3000</code> already in use).</li><li><strong><code>IP:port</code> bypass</strong> risks.</li></ul><h3 id="2-traefik-labels-domain-routing--tls--security-headers">2) Traefik labels (domain routing + TLS + security headers)</h3><p>Typical label set (example):</p><ul><li><code>traefik.enable=true</code></li><li><code>traefik.http.routers.&lt;name&gt;.rule=Host(`example.com`)</code></li><li><code>traefik.http.routers.&lt;name&gt;.entrypoints=websecure</code></li><li><code>traefik.http.routers.&lt;name&gt;.tls.certresolver=letsencrypt</code></li><li><code>traefik.http.routers.&lt;name&gt;.middlewares=security-headers@file</code></li><li><code>traefik.http.services.&lt;name&gt;.loadbalancer.server.port=&lt;container_port&gt;</code></li></ul><h3 id="3-multi-network-containers-must-pin-the-traefik-network">3) Multi-network containers must pin the Traefik network</h3><p>When a container joins both:</p><ul><li><code>traefik_proxy</code> (for Traefik)</li><li><code>infra_backend</code> (for databases/caches)</li></ul><p>Traefik may pick the wrong container IP (from a network Traefik cannot reach), resulting in <strong>504 Gateway Timeout</strong>.</p><p>Fix: add this label to the service:</p><ul><li><code>traefik.docker.network=traefik_proxy</code></li></ul><p>This was the typical cause for Checkmate: adding it removed the 504 immediately.</p><h2 id="shared-middleware-middlewares">Shared middleware (<code>middlewares/</code>)</h2><h3 id="design-principles">Design principles</h3><ul><li><strong>High cohesion, low coupling</strong>: middleware provides network services only.</li><li><strong>Least privilege</strong>: no public ports by default; internal networks only.</li><li><strong>Centralized persistence</strong>: volumes managed in <code>middlewares/</code> for easier backup/migration.</li></ul><h3 id="practical-notes">Practical notes</h3><ul><li>Use a stable shared network name (e.g. <code>infra_backend</code>), and let apps join with <code>external: true</code>.</li><li>Use service names in connection strings (e.g. <code>postgres</code> / <code>mongodb</code>).</li><li>Prefer separate DB/schema/user per app to avoid data pollution.</li></ul><h2 id="app-specific-pitfalls-i-hit-this-time">App-specific pitfalls I hit this time</h2><h3 id="dockhand-high-risk-admin-surface">Dockhand (high-risk admin surface)</h3><ul><li><strong>Do not publish host ports</strong>, domain + Traefik only.</li><li><strong>Always add auth</strong>: protect it with Traefik <code>basicAuth</code>.</li><li>Keep it simple; complexity is how incidents are born.</li></ul><h3 id="vaultwarden-bitwarden-compatible">Vaultwarden (Bitwarden-compatible)</h3><p>This is where it is easy to “be clever” and break clients:</p><ul><li><strong>Do not put BasicAuth on the entire site</strong>: official clients and browser extensions will fail.</li><li>Correct approach: protect <code>/admin</code> only.
<ul><li>Client paths like <code>/</code>, <code>/api</code>, <code>/identity</code> must not be blocked by BasicAuth.</li></ul></li><li>If “the UI loads but login fails”, check:
<ul><li>whether Cloudflare is challenging API requests (see below)</li><li>whether <code>/api</code> is treated as ServerConfig (may require rewriting to <code>/api/config</code> in Traefik)</li></ul></li></ul><h3 id="checkmate-multi-network--mongodb">Checkmate (multi-network + MongoDB)</h3><ul><li>Sharing MongoDB + multiple networks often triggers the Traefik wrong-network issue:
<ul><li>symptoms: 504</li><li>fix: <code>traefik.docker.network=traefik_proxy</code></li></ul></li></ul><h3 id="miniflux-rss-reader">Miniflux (rss reader)</h3><ul><li>Miniflux requires <strong>PostgreSQL</strong>. No MongoDB/Redis is needed (unless you add RSSHub/Browserless, etc.).</li><li><code>HEAD /</code> returning <code>405</code> can be normal as long as <code>GET</code> works; do not misdiagnose it as downtime.</li></ul><h2 id="cloudflare-pitfall-clientsextensions-cannot-log-in">Cloudflare pitfall: clients/extensions cannot log in</h2><p>I hit the same issue twice:</p><ul><li>Vaultwarden browser extension: Cloudflare challenge → 403</li><li>Reeder (Google Reader API): Cloudflare challenge → 403</li></ul><p>Common signals:</p><ul><li>response headers contain <code>server: cloudflare</code> + <code>cf-mitigated: challenge</code></li><li>requests never reach Traefik/app logs</li></ul><p>Mitigation options (recommended order):</p><ul><li><strong>A: set the subdomain to DNS-only (grey cloud)</strong>: the most robust, no client-side hacks needed.</li><li><strong>B: keep proxy (orange cloud), but skip challenge for this host/path</strong>:
<ul><li>at minimum allow <code>/api</code>, <code>/identity</code>, <code>/reader/api/0</code>, <code>/accounts/ClientLogin</code>, etc.</li></ul></li></ul><p>Key takeaway:</p><ul><li><strong>“It works in a browser” does not mean “it works in a client”</strong>. Browsers can pass challenges; clients usually cannot.</li></ul><h2 id="debug-playbook-fast-to-slow">Debug playbook (fast to slow)</h2><h3 id="1-decide-whether-the-request-reaches-your-server">1) Decide whether the request reaches your server</h3><ul><li>Cloudflare 403 challenge: request never reaches your server. Fix Cloudflare/WAF first.</li><li>Traefik 404/504: request reaches Traefik, but routing/network/backend is wrong.</li><li>Backend 4xx/5xx: request reaches the app. Inspect app logs/config.</li></ul><h3 id="2-traefik-side-debugging">2) Traefik-side debugging</h3><ul><li>Check access logs: which router/service handled the request and what status code it returned.</li><li>Multi-network containers: suspect missing <code>traefik.docker.network</code> first.</li><li>Same host defined twice: suspect file-provider vs docker-provider conflicts (delete legacy file routes).</li></ul><h3 id="3-app-side-debugging">3) App-side debugging</h3><ul><li><code>docker logs &lt;container&gt;</code>: verify startup, listening port, DB connectivity.</li><li>Verify you did not accidentally use <code>ports:</code> and expose/conflict host ports.</li></ul><h2 id="final-security-checklist-before-going-public">Final security checklist (before going public)</h2><ul><li><strong>No business ports published on the host</strong> (only keep <code>80/443</code>)</li><li><strong>Admin surfaces must be authenticated</strong> (dockhand, vaultwarden <code>/admin</code>, etc.)</li><li><strong>Databases/caches are never exposed to the public internet</strong></li><li><strong>No Traefik routing conflicts</strong> (avoid defining the same host in both file-provider and docker-provider)</li><li><strong>Cloudflare must not challenge API paths</strong> (clients/extensions/third-party integrations will break)</li></ul></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>homelab</category>
            <category>self-hosting</category>
            <category>deployment</category>
            <category>devops</category>
            <category>security</category>
            <category>observability</category>
        </item>
        <item>
            <title><![CDATA[Daily Debugging: Minisforum N100 Random Disk Drop / System Freeze (DNS, 1+ Day Uptime)]]></title>
            <link>https://blog.cheverjohn.me/minisforum-n100-dns-random-freeze</link>
            <guid>https://blog.cheverjohn.me/minisforum-n100-dns-random-freeze</guid>
            <pubDate>Tue, 16 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[This is a troubleshooting log template. Facts first, evidence second, conclusions last. TL;DR - Symptoms: The DNS service becomes unresponsive; the whole machine stutters/freezes; sometimes it looks like storage I/O issues or a “disk drop”. - Pattern: Random, usually after 1+ day of continuous runni...]]></description>
            <content:encoded><![CDATA[<article><div><blockquote><p>This is a troubleshooting log template. Facts first, evidence second, conclusions last.</p></blockquote>
<h2 id="tldr">TL;DR</h2><ul><li><strong>Symptoms</strong>: The DNS service becomes unresponsive; the whole machine stutters/freezes; sometimes it looks like storage I/O issues or a “disk drop”.</li><li><strong>Pattern</strong>: Random, usually after <strong>1+ day</strong> of continuous running.</li><li><strong>Recovery</strong>: <strong>Power off and let it cool down</strong> for a while, then it works again.</li><li><strong>Current conclusion (probabilistic)</strong>: ____ (e.g., thermals / power / storage-controller reset).</li></ul><h2 id="impact">Impact</h2><ul><li><strong>Direct impact</strong>: ____ (e.g., LAN-wide DNS timeouts).</li><li><strong>Indirect impact</strong>: ____ (services that depend on name resolution).</li><li><strong>Time window</strong>: ____ (start time / duration / auto-recovery?).</li></ul><h2 id="environment-reproducible">Environment (reproducible)</h2><h3 id="hardware">Hardware</h3><ul><li><strong>CPU</strong>: Intel® N100 (4C/4T, 6MB cache, up to 3.4GHz)</li><li><strong>GPU</strong>: Intel® UHD Graphics</li><li><strong>RAM</strong>: 8GB LPDDR5 (single channel, onboard, 4800MHz)</li><li><strong>Storage</strong>: UFS 2.1 256G</li><li><strong>Wireless</strong>: Intel AX200/201 (Wi‑Fi 6 / Bluetooth 5.2)</li><li><strong>Ethernet</strong>: 2.5G RJ45 ×1 (PoE IEEE 802.3at)</li><li><strong>Ports</strong>: USB 3.2 Gen2 Type‑A ×2; USB 3.2 Gen2 Type‑C ×1 (Alt DP/PD); HDMI ×1</li><li><strong>Power</strong>: 65W USB‑C Power Delivery adapter</li></ul><h3 id="software">Software</h3><ul><li><strong>Actual OS</strong>: Linux (current kernel: <code>6.8.12-5-pve</code>; factory OS: Windows 11 Home)</li><li><strong>Kernel (if Linux)</strong>: <code>6.8.12-5-pve</code></li><li><strong>Bootloader (GRUB)</strong>: <code>grub2 2.06-13+pmx2</code></li><li><strong>DNS stack</strong>: ____ (dnsmasq / unbound / AdGuard Home / Pi‑hole / other)</li><li><strong>Deployment</strong>: ____ (systemd / Docker / other)</li><li><strong>Other services</strong>: ____</li><li><strong>Logging policy</strong>: ____ (write frequency / rotation / same disk?).</li></ul><h2 id="problem-statement-facts-only">Problem statement (facts only)</h2><ul><li><strong>First seen</strong>: ____</li><li><strong>Frequency</strong>: about every ____ hours/days (random).</li><li><strong>Observable symptoms during failure</strong>:
<ul><li><strong>DNS</strong>: ____ (timeouts / refusal / high latency)</li><li><strong>System</strong>: ____ (SSH/RDP reachable? CPU/memory spike?)</li><li><strong>Storage</strong>: ____ (mount disappears / read-only remount / I/O error / device reset)</li><li><strong>Network</strong>: ____ (packet loss? NIC reset?)</li></ul></li><li><strong>Recovery</strong>: ____ (power off for ____ minutes; does reboot also work: ____)</li><li><strong>Load correlation</strong>: ____ (QPS / logging / other tasks).</li></ul><h2 id="immediate-mitigation-timeline">Immediate mitigation (timeline)</h2><ul><li><strong>T+0</strong>: ____ (e.g., switch upstream DNS)</li><li><strong>T+5m</strong>: ____ (stop DNS or high-IO workloads)</li><li><strong>T+10m</strong>: ____ (collect logs and metrics snapshots)</li><li><strong>Result</strong>: ____</li></ul><h2 id="evidence-model-align-everything-by-timestamp">Evidence model (align everything by timestamp)</h2><ul><li><strong>One timeline</strong>: failure start time, temperature peak, I/O errors, service timeouts.</li><li><strong>One core question</strong>: is DNS dead, or is the storage/system dead and DNS is just the first visible symptom?</li></ul><h2 id="hypotheses-prioritized">Hypotheses (prioritized)</h2><ul><li><strong>H1: Thermals / overheating → throttling or protection → instability</strong>
<ul><li>Evidence: ____</li></ul></li><li><strong>H2: Power instability (PD/PoE/adapter) → controller reset</strong>
<ul><li>Evidence: ____</li></ul></li><li><strong>H3: Storage media/controller issue → I/O stall or device drop</strong>
<ul><li>Evidence: ____</li></ul></li><li><strong>H4: Software resource exhaustion (FD/memory/log amplification/IO saturation)</strong>
<ul><li>Evidence: ____</li></ul></li><li><strong>H5: Kernel/driver bug (power management, device PM policy)</strong>
<ul><li>Evidence: ____</li></ul></li></ul><h2 id="data-to-capture-during-failure">Data to capture (during failure)</h2><ul><li><strong>Temps/fans</strong>: ____ (<code>sensors</code>, <code>/sys/class/thermal/</code>, Windows tools)</li><li><strong>System logs</strong>: ____ (<code>journalctl</code>, Windows Event Viewer)</li><li><strong>Storage logs</strong>: ____ (I/O error, timeout, reset, read-only remount)</li><li><strong>Resource metrics</strong>: ____ (CPU/load/iowait/memory/disk throughput)</li><li><strong>DNS metrics</strong>: ____ (QPS, cache hit ratio, upstream latency, error rate)</li><li><strong>Client checks</strong>: ____ (<code>dig/nslookup</code> outputs)</li></ul><h2 id="key-evidence-35-snippets">Key evidence (3–5 snippets)</h2><pre><code class="lang-text">[time] ____
</code></pre>
<pre><code class="lang-text">[time] ____
</code></pre>
<pre><code class="lang-text">[time] ____
</code></pre>
<h2 id="investigation-attempt--observation--conclusion">Investigation (Attempt → Observation → Conclusion)</h2><h3 id="attempt-a-">Attempt A: ____</h3><ul><li>Change: ____</li><li>Observation: ____</li><li>Conclusion: ____</li></ul><h3 id="attempt-b-">Attempt B: ____</h3><ul><li>Change: ____</li><li>Observation: ____</li><li>Conclusion: ____</li></ul><h3 id="attempt-c-">Attempt C: ____</h3><ul><li>Change: ____</li><li>Observation: ____</li><li>Conclusion: ____</li></ul><h2 id="final-conclusion-probability-not-absolute-claims">Final conclusion (probability, not absolute claims)</h2><ul><li><strong>Most likely root cause</strong>: ____</li><li><strong>Secondary factors</strong>: ____</li><li><strong>Ruled out</strong>: ____ (with evidence)</li></ul><h2 id="fix-plan-short-term-vs-long-term">Fix plan (short-term vs long-term)</h2><h3 id="short-term-mitigation">Short-term mitigation</h3><ul><li>____ (e.g., improve cooling)</li><li>____ (e.g., cap power / disable aggressive power saving)</li><li>____ (e.g., reduce disk writes / configure log rotation)</li></ul><h3 id="long-term-fix">Long-term fix</h3><ul><li>____ (e.g., replace power adapter / replace storage / BIOS update)</li><li>____ (e.g., airflow redesign / thermal pads)</li><li>____ (e.g., kernel/OS change)</li></ul><h3 id="validation">Validation</h3><ul><li>Uptime: ____ days without recurrence</li><li>Temperature: CPU ≤ ____°C; storage ≤ ____°C</li><li>Logs: no I/O errors / resets / read-only remount</li></ul><h2 id="retrospective">Retrospective</h2><ul><li>Biggest learning: ____</li><li>What I will do earlier next time: ____ (monitoring, alerting, auto log capture)</li><li>Open questions: ____</li></ul><h2 id="attempt-plan-monitor-1921682218-minisforum-via-a-monitoring-lxc-1921682219-on-pve-1921682212">Attempt plan: Monitor 192.168.22.18 (Minisforum) via a monitoring LXC (192.168.22.19) on PVE 192.168.22.12</h2><blockquote><p>Goal: turn “it feels like overheating / disk drop / DNS died” into an evidence-aligned timeline: <strong>temps + kernel/storage errors + real DNS query success/latency + host reachability</strong>.</p><p>Note: <strong>never put passwords into configs or the repo</strong>. Any credentials should live in root-only local files on the monitoring host.</p></blockquote>
<h3 id="extra-pitfall-port-22-is-reachable-but-ssh-still-hangs--drops-more-common-across-subnets">Extra pitfall: port <code>22</code> is reachable, but <code>ssh</code> still hangs / drops (more common across subnets)</h3><p>This one is misleading: <code>nc</code> says <code>22/tcp open</code>, so people assume “network is fine”. But <code>ssh</code> can still stall during handshake and eventually time out (or die with <code>Broken pipe</code>).</p><h4 id="topology-what-happened-in-this-incident">Topology (what happened in this incident)</h4><ul><li><strong>Client subnet</strong>: <code>192.168.11.0/24</code></li><li><strong>Target host</strong>: <code>192.168.22.12</code> (PVE)</li><li><strong>Same-subnet control point</strong>: <code>192.168.22.18</code> (SSH from here to <code>.12</code> is stable)</li><li><strong>Correlated signal</strong>: DNS server (e.g. <code>192.168.22.53:53</code>) experienced timeouts — reverse lookups can amplify SSH handshake latency.</li></ul><h4 id="symptoms-typical">Symptoms (typical)</h4><ul><li><code>nc -vz -G 2 192.168.22.12 22</code> reports <strong>succeeded</strong> (TCP handshake is OK)</li><li><code>ssh root@192.168.22.12</code> hangs and later shows:
<ul><li><code>ssh_dispatch_run_fatal: Connection to 192.168.22.12 port 22: Operation timed out</code></li><li>or <code>Read from remote host 192.168.22.12: Operation timed out</code> / <code>client_loop: send disconnect: Broken pipe</code></li></ul></li></ul><h4 id="quick-triage-split-port-is-open-into-smaller-truths">Quick triage (split “port is open” into smaller truths)</h4><ol start="1"><li><strong>Check whether the SSH banner is returned immediately</strong> (protocol only, no auth):</li></ol><pre><code class="lang-bash">nc -v 192.168.22.12 22
</code></pre>
<p>You should quickly see <code>SSH-2.0-OpenSSH_...</code>. If the TCP connect succeeds but the banner is <strong>slow/missing</strong>, the issue is likely in <strong>sshd/system path</strong> (DNS reverse lookup stalls, high load, policy/ACL causing unstable data path), not “port blocked”.</p><ol start="2"><li><strong>Capture where SSH stalls</strong> (useful evidence):</li></ol><pre><code class="lang-bash">ssh -vvv -o ConnectTimeout=5 -o ServerAliveInterval=5 -o ServerAliveCountMax=1 root@192.168.22.12
</code></pre>
<h4 id="minimal-fix-eliminate-dnsgssapi-special-cases-first">Minimal fix (eliminate DNS/GSSAPI special cases first)</h4><p>If you can SSH from a same-subnet host (e.g. <code>.22.18</code>) into <code>.22.12</code>, prefer the <strong>server-side</strong> fix:</p><ol start="1"><li>On <code>.22.12</code>, edit <code>/etc/ssh/sshd_config</code> and make sure you have:</li></ol><ul><li><code>UseDNS no</code></li><li><code>GSSAPIAuthentication no</code></li></ul><ol start="2"><li>Validate and restart:</li></ol><pre><code class="lang-bash">sshd -t &amp;&amp; systemctl restart ssh
systemctl status ssh --no-pager
</code></pre>
<blockquote><p>Practical rationale: when DNS/reverse lookup is flaky, <code>UseDNS yes</code> can block parts of the handshake/audit path. Disabling it removes an entire class of “it depends on DNS today” failures.</p></blockquote>
<h4 id="if-it-is-still-flaky-continue-with-the-cross-subnet-triad">If it is still flaky: continue with the “cross-subnet triad”</h4><ul><li><strong>Return path routing</strong> (the target must know how to route back to <code>192.168.11.0/24</code>):</li></ul><pre><code class="lang-bash">ip route get 192.168.11.12
</code></pre>
<ul><li><strong>Firewall/ACL/state tracking</strong>: allow not only <code>22/tcp</code> inbound but also the correct return direction and conntrack behavior (especially with gateways/NAT/policy routing).</li><li><strong>MTU/fragmentation</strong>: tunnels/PPPoE/VLAN paths can yield “SYN works but data is unstable”; validate with DF-sized pings on Linux and temporarily lower MSS/MTU to confirm.</li></ul><h3 id="architecture-minimal-but-sufficient">Architecture (minimal, but sufficient)</h3><ul><li><strong>192.168.22.19 (monitoring LXC on PVE 192.168.22.12)</strong>: Prometheus + Grafana + blackbox_exporter (optional Pushgateway)</li><li><strong>192.168.22.18 (target host)</strong>: node<em>exporter (host metrics) + (optional) smartctl</em>exporter + custom textfile metrics (kernel/I/O error counter)</li></ul><p>Data model:</p><ul><li><strong>Time series (Prometheus)</strong>: scrape every 15s, keep at least 48h around failures.</li><li><strong>Active probing (blackbox_exporter)</strong>: probe <code>.18/.54</code> from <code>.19</code> to separate “host down” from “service down”.</li><li><strong>Alerting</strong>: pin the incident timestamp immediately.</li></ul><hr/><h3 id="attempt-d-bring-up-the-monitoring-stack-prometheus--grafana--blackboxexporter">Attempt D: Bring up the monitoring stack (Prometheus + Grafana + blackbox_exporter)</h3><h4 id="d0-pre-checks-network--ports">D0. Pre-checks (network &amp; ports)</h4><p>From <code>192.168.22.19</code>, verify reachability to <code>192.168.22.18</code>:</p><pre><code class="lang-bash">ping 192.168.22.18
Test-NetConnection 192.168.22.18 -Port 22
</code></pre>
<p>Ports used (defaults):</p><ul><li><code>.18:9100</code> node_exporter</li><li><code>.18:9633</code> smartctl_exporter (optional)</li><li><code>.19:9090</code> Prometheus</li><li><code>.19:3000</code> Grafana</li><li><code>.19:9115</code> blackbox_exporter</li><li><code>.19:9091</code> Pushgateway (optional)</li></ul><h4 id="d1-deploy-nodeexporter-on-1921682218-systemd-stable">D1. Deploy node_exporter on 192.168.22.18 (systemd, stable)</h4><p>Run on <code>.18</code>:</p><pre><code class="lang-bash">export VER=&quot;1.7.0&quot;
curl -fsSL -o /tmp/node_exporter.tar.gz &quot;https://github.com/prometheus/node_exporter/releases/download/v${VER}/node_exporter-${VER}.linux-amd64.tar.gz&quot;
tar -C /tmp -xzf /tmp/node_exporter.tar.gz
install -m 0755 /tmp/node_exporter-${VER}.linux-amd64/node_exporter /usr/local/bin/node_exporter

useradd --system --no-create-home --shell /usr/sbin/nologin nodeexp || true
mkdir -p /var/lib/node_exporter/textfile_collector
chown -R nodeexp:nodeexp /var/lib/node_exporter

cat &gt;/etc/systemd/system/node_exporter.service &lt;&lt;&#x27;EOF&#x27;
[Unit]
Description=Prometheus Node Exporter
After=network-online.target
Wants=network-online.target

[Service]
User=nodeexp
Group=nodeexp
ExecStart=/usr/local/bin/node_exporter \
  --web.listen-address=:9100 \
  --collector.textfile.directory=/var/lib/node_exporter/textfile_collector
Restart=on-failure
RestartSec=2

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now node_exporter
systemctl status node_exporter --no-pager
</code></pre>
<p>Verify (from <code>.12</code> or any LAN host):</p><pre><code class="lang-bash">curl -fsS http://192.168.22.18:9100/metrics | head
</code></pre>
<h4 id="d2-recommended-add-a-kernelstorage-error-counter-as-a-custom-metric">D2. (Recommended) Add a kernel/storage error counter as a custom metric</h4><p>The keywords that matter:
<code>I/O error|timeout|reset|remount read-only|EXT4-fs error|BTRFS error|blk_update_request</code></p><p>Use node_exporter <strong>textfile collector</strong> and a timer:</p><pre><code class="lang-bash">cat &gt;/usr/local/bin/collect_kernel_error_metrics.sh &lt;&lt;&#x27;EOF&#x27;
#!/usr/bin/env bash
set -euo pipefail

OUT=&quot;/var/lib/node_exporter/textfile_collector/kernel_errors.prom&quot;
TMP=&quot;$(mktemp)&quot;

COUNT=&quot;$(journalctl -k --since &quot;10 min ago&quot; --no-pager 2&gt;/dev/null | \
  grep -Eic &#x27;I/O error|timeout|reset|remount read-only|EXT4-fs error|BTRFS error|blk_update_request|nvme|ufs&#x27; || true)&quot;

NOW=&quot;$(date +%s)&quot;

{
  echo &quot;# HELP kernel_error_events_10m Number of kernel error-like events in last 10 minutes&quot;
  echo &quot;# TYPE kernel_error_events_10m gauge&quot;
  echo &quot;kernel_error_events_10m ${COUNT}&quot;
  echo &quot;# HELP kernel_error_scrape_time_seconds Last collect time&quot;
  echo &quot;# TYPE kernel_error_scrape_time_seconds gauge&quot;
  echo &quot;kernel_error_scrape_time_seconds ${NOW}&quot;
} &gt;&quot;${TMP}&quot;

mv &quot;${TMP}&quot; &quot;${OUT}&quot;
EOF

chmod +x /usr/local/bin/collect_kernel_error_metrics.sh

cat &gt;/etc/systemd/system/kernel-error-metrics.service &lt;&lt;&#x27;EOF&#x27;
[Unit]
Description=Collect kernel error metrics for node_exporter textfile collector

[Service]
Type=oneshot
ExecStart=/usr/local/bin/collect_kernel_error_metrics.sh
EOF

cat &gt;/etc/systemd/system/kernel-error-metrics.timer &lt;&lt;&#x27;EOF&#x27;
[Unit]
Description=Run kernel error metrics collector every 60s

[Timer]
OnBootSec=30s
OnUnitActiveSec=60s
AccuracySec=1s

[Install]
WantedBy=timers.target
EOF

systemctl daemon-reload
systemctl enable --now kernel-error-metrics.timer
</code></pre>
<p>Verify:</p><pre><code class="lang-bash">curl -fsS http://192.168.22.18:9100/metrics | grep -E &#x27;^kernel_error_events_10m|^kernel_error_scrape_time_seconds&#x27; || true
</code></pre>
<h4 id="d3-optional-smartctlexporter-for-disk-health">D3. (Optional) smartctl_exporter for disk health</h4><p>If the storage device does not expose SMART/health, this may not work. Still worth trying.</p><pre><code class="lang-bash">apt-get update
apt-get install -y smartmontools

export VER=&quot;0.12.0&quot;
curl -fsSL -o /tmp/smartctl_exporter.tar.gz &quot;https://github.com/prometheus-community/smartctl_exporter/releases/download/v${VER}/smartctl_exporter-${VER}.linux-amd64.tar.gz&quot;
tar -C /tmp -xzf /tmp/smartctl_exporter.tar.gz
install -m 0755 /tmp/smartctl_exporter-${VER}.linux-amd64/smartctl_exporter /usr/local/bin/smartctl_exporter

useradd --system --no-create-home --shell /usr/sbin/nologin smartctl-exp || true

cat &gt;/etc/systemd/system/smartctl_exporter.service &lt;&lt;&#x27;EOF&#x27;
[Unit]
Description=Prometheus Smartctl Exporter
After=network-online.target
Wants=network-online.target

[Service]
User=smartctl-exp
Group=smartctl-exp
ExecStart=/usr/local/bin/smartctl_exporter --web.listen-address=:9633
Restart=on-failure
RestartSec=2

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now smartctl_exporter
</code></pre>
<h4 id="d4-deploy-prometheus--grafana--blackboxexporter-on-1921682212-pve-via-lxc-recommended">D4. Deploy Prometheus + Grafana + blackbox_exporter on 192.168.22.12 (PVE) via LXC (recommended)</h4><p>Since <code>192.168.22.12</code> is also a PVE host, avoid piling Docker onto the host OS. The simplest operationally is: <strong>one Debian LXC container running the monitoring stack with systemd</strong>.</p><h5 id="d41-create-a-debian-lxc-container-on-pve-12">D4.1 Create a Debian LXC container on PVE (.12)</h5><p>You can do it in the PVE Web UI, or via CLI like this (replace <code>&lt;CTID&gt;</code>, <code>&lt;GW&gt;</code>; LXC IP is pinned to <code>192.168.22.19</code>):</p><pre><code class="lang-bash"># Run on the PVE host 192.168.22.12
pct create &lt;CTID&gt; local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
  --hostname monitoring \
  --cores 2 --memory 2048 --swap 512 \
  --rootfs local-lvm:8 \
  --net0 name=eth0,bridge=vmbr0,ip=192.168.22.19/24,gw=&lt;GW&gt; \
  --unprivileged 1

pct set &lt;CTID&gt; -features keyctl=1,nesting=1
pct start &lt;CTID&gt;
</code></pre>
<h5 id="d42-install-prometheus-in-the-container-systemd">D4.2 Install Prometheus in the container (systemd)</h5><p>Enter the container:</p><pre><code class="lang-bash">pct exec &lt;CTID&gt; -- bash
</code></pre>
<p>Inside the container:</p><pre><code class="lang-bash">apt-get update
apt-get install -y ca-certificates curl tar

useradd --system --no-create-home --shell /usr/sbin/nologin prometheus || true
mkdir -p /etc/prometheus /var/lib/prometheus
chown -R prometheus:prometheus /etc/prometheus /var/lib/prometheus

export VER=&quot;2.54.1&quot;
curl -fsSL -o /tmp/prometheus.tar.gz &quot;https://github.com/prometheus/prometheus/releases/download/v${VER}/prometheus-${VER}.linux-amd64.tar.gz&quot;
tar -C /tmp -xzf /tmp/prometheus.tar.gz
install -m 0755 /tmp/prometheus-${VER}.linux-amd64/prometheus /usr/local/bin/prometheus
install -m 0755 /tmp/prometheus-${VER}.linux-amd64/promtool /usr/local/bin/promtool
cp -r /tmp/prometheus-${VER}.linux-amd64/consoles /etc/prometheus/
cp -r /tmp/prometheus-${VER}.linux-amd64/console_libraries /etc/prometheus/
chown -R prometheus:prometheus /etc/prometheus
</code></pre>
<p>Create <code>/etc/prometheus/prometheus.yml</code> (scrape <code>.18</code> + active probes; DNS is the AdGuardDNS LXC at <code>.54</code>):</p><pre><code class="lang-yaml">global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: &quot;minisforum-node&quot;
    static_configs:
      - targets: [&quot;192.168.22.18:9100&quot;]

  - job_name: &quot;minisforum-smartctl&quot;
    static_configs:
      - targets: [&quot;192.168.22.18:9633&quot;]

  - job_name: &quot;blackbox-icmp&quot;
    metrics_path: /probe
    params:
      module: [icmp]
    static_configs:
      - targets:
          - 192.168.22.18
          - 192.168.22.54
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 127.0.0.1:9115

  - job_name: &quot;blackbox-tcp&quot;
    metrics_path: /probe
    params:
      module: [tcp_connect]
    static_configs:
      - targets:
          - 192.168.22.18:22
          - 192.168.22.18:9100
          - 192.168.22.54:53
          - 192.168.22.54:3000
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 127.0.0.1:9115
</code></pre>
<p>Create the systemd service:</p><pre><code class="lang-bash">cat &gt;/etc/systemd/system/prometheus.service &lt;&lt;&#x27;EOF&#x27;
[Unit]
Description=Prometheus
After=network-online.target
Wants=network-online.target

[Service]
User=prometheus
Group=prometheus
ExecStart=/usr/local/bin/prometheus \
  --config.file=/etc/prometheus/prometheus.yml \
  --storage.tsdb.path=/var/lib/prometheus \
  --storage.tsdb.retention.time=30d
Restart=on-failure
RestartSec=2

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now prometheus
</code></pre>
<h5 id="d43-install-blackboxexporter-in-the-container-systemd">D4.3 Install blackbox_exporter in the container (systemd)</h5><pre><code class="lang-bash">useradd --system --no-create-home --shell /usr/sbin/nologin blackbox || true
mkdir -p /etc/blackbox_exporter
chown -R blackbox:blackbox /etc/blackbox_exporter

export VER=&quot;0.25.0&quot;
curl -fsSL -o /tmp/blackbox.tar.gz &quot;https://github.com/prometheus/blackbox_exporter/releases/download/v${VER}/blackbox_exporter-${VER}.linux-amd64.tar.gz&quot;
tar -C /tmp -xzf /tmp/blackbox.tar.gz
install -m 0755 /tmp/blackbox_exporter-${VER}.linux-amd64/blackbox_exporter /usr/local/bin/blackbox_exporter

cat &gt;/etc/blackbox_exporter/blackbox.yml &lt;&lt;&#x27;EOF&#x27;
modules:
  icmp:
    prober: icmp
    timeout: 5s

  tcp_connect:
    prober: tcp
    timeout: 5s
EOF

chown -R blackbox:blackbox /etc/blackbox_exporter

cat &gt;/etc/systemd/system/blackbox_exporter.service &lt;&lt;&#x27;EOF&#x27;
[Unit]
Description=Prometheus Blackbox Exporter
After=network-online.target
Wants=network-online.target

[Service]
User=blackbox
Group=blackbox
ExecStart=/usr/local/bin/blackbox_exporter \
  --config.file=/etc/blackbox_exporter/blackbox.yml \
  --web.listen-address=:9115
Restart=on-failure
RestartSec=2

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now blackbox_exporter
</code></pre>
<h5 id="d44-install-grafana-in-the-container-systemd">D4.4 Install Grafana in the container (systemd)</h5><p>Do not commit any Grafana admin password. Install it, then set the password manually.</p><pre><code class="lang-bash">apt-get install -y apt-transport-https software-properties-common wget gpg
mkdir -p /etc/apt/keyrings
wget -qO- https://apt.grafana.com/gpg.key | gpg --dearmor &gt;/etc/apt/keyrings/grafana.gpg
echo &quot;deb [signed-by=/etc/apt/keyrings/grafana.gpg] https://apt.grafana.com stable main&quot; &gt;/etc/apt/sources.list.d/grafana.list
apt-get update
apt-get install -y grafana
systemctl enable --now grafana-server
</code></pre>
<p>Reset admin password (run in the container):</p><pre><code class="lang-bash">grafana-cli admin reset-admin-password
</code></pre>
<hr/><h3 id="attempt-e-deploy-adguarddns-on-1921682218-via-lxc-fixed-ip-1921682254--real-dns-query-probing">Attempt E: Deploy AdGuardDNS on 192.168.22.18 via LXC (fixed IP: 192.168.22.54) + real DNS query probing</h3><p>Goal: isolate DNS service from the host and <strong>pin the DNS IP to <code>192.168.22.54</code></strong> so monitoring targets never drift.</p><h4 id="e0-create-the-adguarddns-lxc-container-on-pve-18-with-ip-1921682254">E0. Create the AdGuardDNS LXC container on PVE (.18) with IP 192.168.22.54</h4><p>Example (replace <code>&lt;CTID&gt;</code>, <code>&lt;GW&gt;</code>):</p><pre><code class="lang-bash"># Run on the PVE host 192.168.22.18
pct create &lt;CTID&gt; local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst \
  --hostname adguarddns \
  --cores 1 --memory 1024 --swap 256 \
  --rootfs local-lvm:4 \
  --net0 name=eth0,bridge=vmbr0,ip=192.168.22.54/24,gw=&lt;GW&gt; \
  --unprivileged 1

pct set &lt;CTID&gt; -features keyctl=1,nesting=1
pct start &lt;CTID&gt;
</code></pre>
<h4 id="e1-install-adguard-home-inside-the-container-as-adguarddns">E1. Install AdGuard Home inside the container (as AdGuardDNS)</h4><p>Enter the container:</p><pre><code class="lang-bash">pct exec &lt;CTID&gt; -- bash
</code></pre>
<p>Inside the container:</p><pre><code class="lang-bash">apt-get update
apt-get install -y ca-certificates curl tar

export VER=&quot;0.107.57&quot;
curl -fsSL -o /tmp/adguardhome.tar.gz &quot;https://github.com/AdguardTeam/AdGuardHome/releases/download/v${VER}/AdGuardHome_linux_amd64.tar.gz&quot;
tar -C /opt -xzf /tmp/adguardhome.tar.gz

/opt/AdGuardHome/AdGuardHome -s install
systemctl status AdGuardHome --no-pager
</code></pre>
<p>First-time setup:</p><ul><li>Admin UI: <code>http://192.168.22.54:3000</code></li><li>DNS: <code>192.168.22.54:53</code> (UDP/TCP)</li></ul><h4 id="e1-real-dns-query-probe-using-dig--pushgateway-credential-free">E1. Real DNS query probe using <code>dig</code> + Pushgateway (credential-free)</h4><p>Use <code>.12</code> to run <code>dig</code> periodically and push two metrics: success and latency.</p><ol start="1"><li>Optionally install Pushgateway on the monitoring side (the <code>.12</code> monitoring LXC) via systemd:</li></ol><pre><code class="lang-bash">apt-get update
apt-get install -y ca-certificates curl tar

useradd --system --no-create-home --shell /usr/sbin/nologin pushgateway || true
mkdir -p /etc/pushgateway /var/lib/pushgateway
chown -R pushgateway:pushgateway /etc/pushgateway /var/lib/pushgateway

export VER=&quot;1.10.0&quot;
curl -fsSL -o /tmp/pushgateway.tar.gz &quot;https://github.com/prometheus/pushgateway/releases/download/v${VER}/pushgateway-${VER}.linux-amd64.tar.gz&quot;
tar -C /tmp -xzf /tmp/pushgateway.tar.gz
install -m 0755 /tmp/pushgateway-${VER}.linux-amd64/pushgateway /usr/local/bin/pushgateway

cat &gt;/etc/systemd/system/pushgateway.service &lt;&lt;&#x27;EOF&#x27;
[Unit]
Description=Prometheus Pushgateway
After=network-online.target
Wants=network-online.target

[Service]
User=pushgateway
Group=pushgateway
ExecStart=/usr/local/bin/pushgateway --web.listen-address=:9091
Restart=on-failure
RestartSec=2

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now pushgateway
systemctl status pushgateway --no-pager
</code></pre>
<ol start="2"><li>Add a Prometheus scrape job (use <code>127.0.0.1</code> since Pushgateway runs on the same container):</li></ol><pre><code class="lang-yaml">- job_name: &quot;pushgateway&quot;
  honor_labels: true
  static_configs:
    - targets: [&quot;127.0.0.1:9091&quot;]
</code></pre>
<ol start="3"><li>Create a probe script (DNS IP is pinned to <code>192.168.22.54</code>):</li></ol><pre><code class="lang-bash">cat &gt;/opt/monitoring/dns_probe_push.sh &lt;&lt;&#x27;EOF&#x27;
#!/usr/bin/env bash
set -euo pipefail

DNS_IP=&quot;192.168.22.54&quot;
NAME=&quot;www.baidu.com&quot;
PUSH_URL=&quot;http://127.0.0.1:9091/metrics/job/dns_probe/instance/${DNS_IP}&quot;

OUT=&quot;$(dig +tries=1 +time=2 +stats @&quot;${DNS_IP}&quot; &quot;${NAME}&quot; A 2&gt;/dev/null || true)&quot;

if echo &quot;${OUT}&quot; | grep -qE &#x27;^;; ANSWER SECTION:&#x27;; then
  SUCCESS=1
else
  SUCCESS=0
fi

LAT_MS=&quot;$(echo &quot;${OUT}&quot; | awk -F&#x27;: &#x27; &#x27;/^;; Query time:/{print $2}&#x27; | awk &#x27;{print $1}&#x27; || true)&quot;
LAT_MS=&quot;${LAT_MS:-0}&quot;

cat &lt;&lt;METRICS | curl -fsS --data-binary @- &quot;${PUSH_URL}&quot; &gt;/dev/null
# TYPE dns_probe_success gauge
dns_probe_success ${SUCCESS}
# TYPE dns_probe_latency_ms gauge
dns_probe_latency_ms ${LAT_MS}
METRICS
EOF

chmod +x /opt/monitoring/dns_probe_push.sh
</code></pre>
<ol start="4"><li>Schedule it (during investigation, 15s is useful):</li></ol><pre><code class="lang-bash">crontab -e
*/1 * * * * /opt/monitoring/dns_probe_push.sh
*/1 * * * * sleep 15; /opt/monitoring/dns_probe_push.sh
*/1 * * * * sleep 30; /opt/monitoring/dns_probe_push.sh
*/1 * * * * sleep 45; /opt/monitoring/dns_probe_push.sh
</code></pre>
<h4 id="e2-optional-adguard-home-internal-stats-requires-admin-api-creds-never-commit-them">E2. (Optional) AdGuard Home internal stats (requires admin API creds; never commit them)</h4><p>If you need AdGuard-specific stats (blocked/allowed/qps/upstream latency), run an exporter on <code>.12</code> that talks to AdGuard’s admin API.
Store credentials in a root-only local file (e.g. <code>/opt/monitoring/secrets/adguard.env</code>, <code>chmod 600</code>) and keep it out of Git.</p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>homelab</category>
            <category>dns</category>
            <category>troubleshooting</category>
            <category>linux</category>
            <category>hardware</category>
        </item>
        <item>
            <title><![CDATA[Daily Debugging: PVE Cross-VLAN Ping Fails (Wrong Default Gateway + OpenWrt LAN NAT)]]></title>
            <link>https://blog.cheverjohn.me/pve-openwrt-icmp-debug</link>
            <guid>https://blog.cheverjohn.me/pve-openwrt-icmp-debug</guid>
            <pubDate>Fri, 05 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[This is a practical troubleshooting post. Facts first, evidence second, conclusions last. TL;DR - Symptom: Cross-VLAN ping times out. The target host shows echo reply sent, yet the client never receives it. - Root cause 1 (decisive): PVE intentionally kept OpenWrt (192.168.22.13) as the default gate...]]></description>
            <content:encoded><![CDATA[<article><div><blockquote><p>This is a practical troubleshooting post. Facts first, evidence second, conclusions last.</p></blockquote>
<h2 id="tldr">TL;DR</h2><ul><li><strong>Symptom</strong>: Cross-VLAN <code>ping</code> times out. The target host shows <code>echo reply</code> sent, yet the client never receives it.</li><li><strong>Root cause #1 (decisive)</strong>: PVE <strong>intentionally</strong> kept OpenWrt (<code>192.168.22.13</code>) as the default gateway for QoS/traffic handling, but had <strong>no explicit routes for other internal VLAN subnets</strong>, so return traffic to internal networks followed the default route into OpenWrt and never traversed the primary L3 gateway (classic asymmetric path).</li><li><strong>Root cause #2 (amplifier)</strong>: OpenWrt had <code>masq=1</code> on the <strong>LAN zone</strong>, applying <strong>SNAT</strong> to internal east-west traffic and making paths/sessions unpredictable.</li><li><strong>Fix</strong>:
<ul><li><strong>Keep</strong> PVE default gateway = OpenWrt (<code>192.168.22.13</code>) for QoS, but add explicit routes for internal subnets (e.g., <code>192.168.11.0/24</code>, <code>192.168.183.0/24</code>) via the primary gateway (<code>192.168.22.1</code>)</li><li>Disable masquerade on OpenWrt <code>lan</code> zone</li></ul></li><li><strong>Prevention</strong>: Route internally, NAT only on real WAN egress; if a side gateway must be the default route (QoS/proxy), explicitly split internal routing vs Internet default; use <code>tcpdump</code> + <code>ip route get</code> as a standard SOP.</li></ul><hr/><h2 id="background-topology-and-roles">Background (Topology and Roles)</h2><p>Multi-VLAN home network:</p><ul><li><strong>Cloud Gateway Fiber (primary L3 gateway)</strong>
<ul><li>Inter-VLAN routing</li><li>Example subnets:
<ul><li>VLAN11: <code>192.168.11.0/24</code> (GW <code>192.168.11.1</code>)</li><li>VLAN22 (PVE/Lab): <code>192.168.22.0/24</code> (GW <code>192.168.22.1</code>)</li><li>VLAN183: <code>192.168.183.0/24</code> (GW <code>192.168.183.1</code>)</li></ul></li></ul></li><li><strong>OpenWrt (secondary / side gateway)</strong>
<ul><li><code>192.168.22.13/24</code> on <code>br-lan</code></li><li><code>fw3 + iptables</code>, plus proxy tooling (OpenClash/Passwall)</li></ul></li><li><strong>PVE node</strong>: <code>192.168.22.12/24</code> on <code>vmbr0</code></li><li><strong>Test hosts</strong>:
<ul><li><code>192.168.183.235</code> (VLAN183)</li><li><code>192.168.11.29</code> (VLAN11)</li></ul></li></ul><p>The intended design is simple:</p><ul><li>Inter-VLAN traffic should be routed by the primary gateway.</li><li>NAT should only happen on a true WAN egress, not on internal VLAN-to-VLAN paths.</li></ul><hr/><h2 id="ascii-topology-printable-via-shell">ASCII topology (printable via shell)</h2><p>This script does not change any config. It prints the topology and the two key paths (bad vs fixed) so readers can visualize the data flow:</p><pre><code class="lang-bash">#!/usr/bin/env bash
set -euo pipefail

# Print the topology and the two paths (bad vs fixed).
cat &lt;&lt;&#x27;EOF&#x27;

                    +------------------------------+
                    |   Cloud Gateway Fiber (L3)   |
                    |   VLAN11: 192.168.11.1       |
                    |   VLAN22: 192.168.22.1       |
                    |   VLAN183: 192.168.183.1     |
                    +---------------+--------------+
                                    |
                                  VLAN22
                                    |
                   +----------------+----------------+
                   |                                 |
        +----------v-----------+          +----------v-----------+
        |  OpenWrt (side GW)   |          |        PVE           |
        |  192.168.22.13       |          |  192.168.22.12       |
        |  QoS / traffic mgmt  |          |  vmbr0               |
        +----------------------+          +----------------------+

Clients:
  VLAN183 host: 192.168.183.235
  VLAN11  host: 192.168.11.29

Problem #1 (before):
  192.168.183.235 -&gt; Fiber -&gt; PVE
  PVE reply -&gt; (default gw) OpenWrt -&gt; [NAT/proxy/unknown] -&gt; ???   (Fiber never sees the reply)

Fix idea:
  Keep PVE default gw = OpenWrt (for QoS),
  but route internal subnets via Fiber.

Problem #2 (amplifier):
  OpenWrt LAN masquerade (SNAT) rewrites east-west traffic,
  making internal routing/session behavior unpredictable.

EOF
</code></pre>
<h2 id="incidents-and-impact">Incidents and Impact</h2><h3 id="incident-1-vlan183-could-not-ping-pve">Incident #1: VLAN183 could not ping PVE</h3><ul><li>Client <code>192.168.183.235</code> → <code>ping 192.168.22.12</code> timed out</li><li>On PVE, captures showed:
<ul><li><code>ICMP echo request</code> arriving</li><li><code>ICMP echo reply</code> being generated</li></ul></li><li>On the primary gateway (Fiber), captures on <code>br22</code>/<code>br0</code> showed requests only, no replies</li></ul><p><strong>Meaning</strong>: the reply never traversed the primary gateway.</p><h3 id="incident-2-after-fixing-1-vlan11-still-could-not-ping-pve">Incident #2: After fixing #1, VLAN11 still could not ping PVE</h3><ul><li><code>192.168.11.29</code> could not ping <code>192.168.22.12</code></li><li>But it could ping OpenWrt <code>192.168.22.13</code></li></ul><p>This pointed to OpenWrt NAT/firewall behavior affecting internal reachability.</p><hr/><h2 id="investigation-evidence-driven">Investigation (Evidence-driven)</h2><h3 id="1-confirm-the-primary-gateway-is-fine">1) Confirm the primary gateway is fine</h3><p>On Fiber:</p><ul><li>It could ping both <code>192.168.183.235</code> and <code>192.168.22.12</code>.</li><li><code>ip route</code> showed both subnets as directly connected.</li></ul><p>Conclusion: core L3 routing on Fiber was not broken.</p><h3 id="2-hop-by-hop-capture-find-where-the-reply-disappears">2) Hop-by-hop capture: find where the reply disappears</h3><ul><li>On PVE: request/reply both exist → PVE is replying.</li><li>On Fiber (<code>br22</code>/<code>br0</code>): request only → reply never reached Fiber.</li></ul><p>Conclusion: the return path is not going through the primary gateway.</p><h3 id="3-let-ip-route-get-tell-the-truth">3) Let <code>ip route get</code> tell the truth</h3><p>On PVE:</p><ul><li><code>ip route</code> showed: <code>default via 192.168.22.13 dev vmbr0</code></li><li><code>ip route get 192.168.183.235</code> showed: <code>via 192.168.22.13</code></li></ul><p><strong>Root cause #1</strong>: PVE had <strong>no explicit route</strong> to internal VLAN subnets, so return traffic to <code>192.168.183.0/24</code> naturally followed the default route into OpenWrt. Once that happens, any NAT/proxy/misroute on OpenWrt can prevent the primary gateway from ever seeing the reply.</p><p>That creates asymmetric routing:</p><ul><li>Forward: <code>183 → Fiber → 22 → PVE</code> (OK)</li><li>Return: <code>PVE → OpenWrt → (NAT/proxy/unknown)</code> (uncontrolled)</li></ul><h3 id="4-validate-openwrt-firewallnat-configuration">4) Validate OpenWrt firewall/NAT configuration</h3><p>After confirming OpenWrt uses <code>fw3 + iptables</code> (no nftables), the key finding was:</p><ul><li><code>lan</code> zone had <code>masq=&#x27;1&#x27;</code></li></ul><p><strong>Root cause #2</strong>: OpenWrt applied SNAT to internal traffic, rewriting sources to <code>192.168.22.13</code> and making internal routing/session behavior unstable, especially with proxy redirection.</p><hr/><h2 id="fix-minimal-changes">Fix (Minimal changes)</h2><h3 id="fix-1-keep-default-gateway--openwrt-but-route-internal-subnets-via-fiber">Fix #1: Keep default gateway = OpenWrt, but route internal subnets via Fiber</h3><p>Constraint: PVE default route must remain <code>192.168.22.13</code> (OpenWrt) for QoS/traffic handling. In that case, do not touch the default route. Instead, explicitly route internal VLAN subnets via the primary L3 gateway (<code>192.168.22.1</code>).</p><p>Temporary test (examples for VLAN11/VLAN183; add others as needed):</p><pre><code class="lang-bash"># Keep default via OpenWrt for QoS / traffic management
# default via 192.168.22.13

# Route internal VLANs via the primary L3 gateway (Fiber)
ip route replace 192.168.183.0/24 via 192.168.22.1 dev vmbr0
ip route replace 192.168.11.0/24  via 192.168.22.1 dev vmbr0
</code></pre>
<p>Verification tips:</p><ul><li><code>ip route get 192.168.183.235</code> should show <code>via 192.168.22.1</code></li><li>ICMP replies should be visible on Fiber interfaces (e.g., <code>br22/br0</code>)</li></ul><p>Persist it in Proxmox networking (commonly <code>/etc/network/interfaces</code> for <code>vmbr0</code>) with <code>post-up</code>:</p><pre><code class="lang-bash">post-up ip route replace 192.168.183.0/24 via 192.168.22.1 dev vmbr0
post-up ip route replace 192.168.11.0/24  via 192.168.22.1 dev vmbr0
post-down ip route del 192.168.183.0/24 via 192.168.22.1 dev vmbr0 || true
post-down ip route del 192.168.11.0/24  via 192.168.22.1 dev vmbr0 || true
</code></pre>
<p>Verification:</p><ul><li>Fiber sees ICMP replies on <code>br22/br0</code>.</li><li><code>192.168.183.235</code> can ping <code>192.168.22.12</code> again.</li></ul><h3 id="fix-2-disable-masquerade-on-openwrt-lan-zone">Fix #2: Disable masquerade on OpenWrt LAN zone</h3><pre><code class="lang-bash">uci set firewall.@zone[0].masq=&#x27;0&#x27;
uci commit firewall
/etc/init.d/firewall restart
</code></pre>
<p>Verification:</p><ul><li><code>192.168.11.29</code> can ping <code>192.168.22.12</code>.</li><li>Internal traffic keeps real source IPs, making debugging and ACLs sane.</li></ul><hr/><h2 id="prevention">Prevention</h2><ul><li><strong>Route internally; NAT only on real WAN</strong>
<ul><li><code>lan</code>/internal zones: <code>masq=0</code></li><li><code>wan</code> egress: <code>masq=1</code></li></ul></li><li><strong>If a side gateway must be the default route (QoS/proxy), split routing explicitly</strong>
<ul><li>Default route can stay on OpenWrt, but internal subnets must have explicit routes via the primary L3 gateway (or use policy routing by destination).</li><li>Keep east-west traffic on predictable L3 routing. Do not let it fall into NAT/proxy black boxes.</li></ul></li><li><strong>Standard SOP (Standard Operating Procedure)</strong>
<ul><li>Meaning: for “cross-VLAN ping fails”, do not guess. Follow the same evidence-driven checklist every time.</li><li><strong>Step 1: packet captures answer 3 questions</strong>: did the request arrive, did the target reply, and at which hop did the reply disappear?
<ul><li>On PVE: <code>tcpdump -ni vmbr0 icmp</code></li><li>On the primary gateway (both VLAN interfaces): <code>tcpdump -ni br22 icmp</code> and <code>tcpdump -ni br0 icmp</code></li></ul></li><li><strong>Step 2: verify routing decisions (no imagination)</strong>
<ul><li>On PVE: <code>ip route</code> + <code>ip route get 192.168.183.235</code></li><li>On OpenWrt/Fiber: <code>ip route get 192.168.183.235</code> (confirm the actual next hop)</li></ul></li><li><strong>Step 3: simplify the data path first</strong>
<ul><li>Restore pure routing with real source IPs and predictable return paths before layering QoS/proxy/policy routing.</li></ul></li></ul></li><li><strong>Make proxy behavior explicit</strong>
<ul><li>Exclude internal subnets from transparent proxy/tunnel redirection.</li></ul></li></ul><hr/><h2 id="one-line-lesson">One-line lesson</h2><p>If ICMP looks like “ghost packets”, you most likely built a black box with the wrong default gateway and the wrong NAT — not a broken protocol stack.</p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>homelab</category>
            <category>networking</category>
            <category>openwrt</category>
            <category>proxmox</category>
            <category>troubleshooting</category>
            <category>vlan</category>
        </item>
        <item>
            <title><![CDATA[How to Properly Use Claude as an Individual]]></title>
            <link>https://blog.cheverjohn.me/how-to-properly-use-claude-as-an-individual</link>
            <guid>https://blog.cheverjohn.me/how-to-properly-use-claude-as-an-individual</guid>
            <pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[As AI assistants become increasingly integrated into our daily workflows, understanding how to properly leverage tools like Claude can significantly enhance your productivity and decision-making. This comprehensive guide will help you maximize the value you get from Claude while maintaining good pra...]]></description>
            <content:encoded><![CDATA[<article><div><p>As AI assistants become increasingly integrated into our daily workflows, understanding how to properly leverage tools like Claude can significantly enhance your productivity and decision-making. This comprehensive guide will help you maximize the value you get from Claude while maintaining good practices and realistic expectations.</p><h2 id="understanding-claudes-core-strengths">Understanding Claude&#x27;s Core Strengths</h2><p>Before diving into specific use cases, it&#x27;s crucial to understand what Claude excels at:</p><h3 id="1-text-analysis-and-processing">1. Text Analysis and Processing</h3><p>Claude can quickly analyze, summarize, and extract insights from large volumes of text. Whether you&#x27;re dealing with research papers, meeting notes, or technical documentation, Claude can help you identify key points and patterns.</p><h3 id="2-creative-and-technical-writing">2. Creative and Technical Writing</h3><p>From drafting emails to writing code, Claude can assist with various forms of content creation while maintaining your personal voice and style preferences.</p><h3 id="3-problem-solving-and-research">3. Problem-Solving and Research</h3><p>Claude can help break down complex problems into manageable components and provide structured approaches to finding solutions.</p><h3 id="4-learning-and-explanation">4. Learning and Explanation</h3><p>Need something explained? Claude can adapt explanations to your level of expertise and learning style.</p><h2 id="essential-best-practices">Essential Best Practices</h2><h3 id="start-with-clear-specific-prompts">Start with Clear, Specific Prompts</h3><p><strong>Good:</strong> &quot;Help me write a professional email declining a meeting request for Thursday afternoon, suggesting alternative times next week, and maintaining a collaborative tone.&quot;</p><p><strong>Poor:</strong> &quot;Write an email for me.&quot;</p><p>The more context and specificity you provide, the better Claude can tailor its response to your needs.</p><h3 id="iterate-and-refine">Iterate and Refine</h3><p>Don&#x27;t expect perfect results on the first try. Use follow-up prompts to:</p><ul><li>Ask for clarification or expansion on specific points</li><li>Request different approaches or styles</li><li>Provide feedback on what works and what doesn&#x27;t</li></ul><h3 id="verify-important-information">Verify Important Information</h3><p>While Claude is knowledgeable, always verify critical facts, especially for:</p><ul><li>Medical or legal advice</li><li>Financial decisions</li><li>Current events or rapidly changing information</li><li>Technical specifications or requirements</li></ul><h2 id="practical-use-cases-for-individuals">Practical Use Cases for Individuals</h2><h3 id="professional-development">Professional Development</h3><p><strong>Email Communication:</strong></p><ul><li>Draft professional emails with appropriate tone</li><li>Summarize long email threads</li><li>Translate complex technical concepts for non-technical audiences</li></ul><p><strong>Document Creation:</strong></p><ul><li>Create structured reports and presentations</li><li>Write job application materials</li><li>Develop project plans and timelines</li></ul><p><strong>Learning and Skill Development:</strong></p><ul><li>Get explanations of complex topics</li><li>Practice interview questions</li><li>Learn new programming languages or frameworks</li></ul><h3 id="personal-productivity">Personal Productivity</h3><p><strong>Research and Planning:</strong></p><ul><li>Compare options for major purchases</li><li>Plan travel itineraries</li><li>Research topics of personal interest</li></ul><p><strong>Creative Projects:</strong></p><ul><li>Brainstorm ideas for hobbies or side projects</li><li>Get feedback on creative writing</li><li>Plan events or celebrations</li></ul><p><strong>Daily Life Management:</strong></p><ul><li>Create meal plans and shopping lists</li><li>Organize and prioritize tasks</li><li>Draft important personal correspondence</li></ul><h2 id="advanced-techniques">Advanced Techniques</h2><h3 id="context-management">Context Management</h3><p>Provide relevant background information at the start of conversations:</p><ul><li>Your role and expertise level</li><li>The specific context or constraints you&#x27;re working within</li><li>Your goals and success criteria</li></ul><h3 id="chain-of-thought-prompting">Chain of Thought Prompting</h3><p>For complex problems, ask Claude to &quot;think step by step&quot; or &quot;explain your reasoning.&quot; This often leads to more thorough and accurate responses.</p><h3 id="role-playing-scenarios">Role-Playing Scenarios</h3><p>Ask Claude to take on specific roles or perspectives:</p><ul><li>&quot;Act as a senior software engineer reviewing my code&quot;</li><li>&quot;Respond as someone unfamiliar with this topic would&quot;</li><li>&quot;Take the perspective of a potential customer&quot;</li></ul><h3 id="template-creation">Template Creation</h3><p>Develop reusable prompts for recurring tasks:</p><ul><li>Meeting summary templates</li><li>Email response frameworks</li><li>Problem-solving methodologies</li></ul><h2 id="what-claude-cannot-do">What Claude Cannot Do</h2><p>Understanding limitations is crucial for effective use:</p><h3 id="real-time-information">Real-Time Information</h3><p>Claude&#x27;s training has a knowledge cutoff and cannot access current web information or real-time data.</p><h3 id="personal-data-access">Personal Data Access</h3><p>Claude cannot access your personal files, emails, or private information unless you explicitly share it in the conversation.</p><h3 id="execute-actions">Execute Actions</h3><p>Claude cannot directly perform actions in external systems, send emails, or make purchases on your behalf.</p><h3 id="replace-human-judgment">Replace Human Judgment</h3><p>While Claude can provide insights and suggestions, important decisions should always involve human judgment and expertise.</p><h2 id="maintaining-privacy-and-security">Maintaining Privacy and Security</h2><h3 id="information-sharing">Information Sharing</h3><ul><li>Avoid sharing sensitive personal information (SSNs, passwords, financial details)</li><li>Be cautious with proprietary or confidential business information</li><li>Consider anonymizing data when seeking help with specific scenarios</li></ul><h3 id="data-retention">Data Retention</h3><ul><li>Understand the platform&#x27;s data retention policies</li><li>Don&#x27;t rely on Claude to remember information between separate conversations</li><li>Keep your own records of important insights or decisions</li></ul><h2 id="building-an-effective-workflow">Building an Effective Workflow</h2><h3 id="1-define-your-use-cases">1. Define Your Use Cases</h3><p>Identify specific, recurring tasks where Claude can add value to your routine.</p><h3 id="2-develop-standard-prompts">2. Develop Standard Prompts</h3><p>Create and refine prompt templates for your most common use cases.</p><h3 id="3-set-realistic-expectations">3. Set Realistic Expectations</h3><p>Understand that Claude is a tool to augment your capabilities, not replace your thinking and judgment.</p><h3 id="4-measure-impact">4. Measure Impact</h3><p>Track how Claude usage affects your productivity and the quality of your work.</p><h3 id="5-stay-updated">5. Stay Updated</h3><p>AI capabilities evolve rapidly. Stay informed about new features and best practices.</p><h2 id="common-pitfalls-to-avoid">Common Pitfalls to Avoid</h2><h3 id="over-reliance">Over-Reliance</h3><p>Don&#x27;t become dependent on Claude for basic thinking or decision-making. Use it to enhance your capabilities, not replace them.</p><h3 id="prompt-laziness">Prompt Laziness</h3><p>Avoid vague or minimal prompts. Investing time in clear communication yields better results.</p><h3 id="ignoring-context">Ignoring Context</h3><p>Remember to provide sufficient context for complex or specialized topics.</p><h3 id="assuming-perfection">Assuming Perfection</h3><p>Always review and validate Claude&#x27;s outputs, especially for important tasks.</p><h2 id="conclusion">Conclusion</h2><p>Claude can be a powerful ally in both professional and personal contexts when used thoughtfully and strategically. The key to success lies in understanding its strengths and limitations, developing clear communication patterns, and maintaining appropriate skepticism and verification practices.</p><p>Start with simple use cases, build your skills gradually, and always remember that Claude is most effective when it augments your human intelligence rather than attempting to replace it. With practice and the right approach, you&#x27;ll find Claude becomes an invaluable tool in your personal and professional toolkit.</p><p>Remember: the goal isn&#x27;t to use Claude for everything, but to use it for the right things in the right way.</p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>AI</category>
            <category>Claude</category>
            <category>productivity</category>
            <category>guide</category>
        </item>
        <item>
            <title><![CDATA[AI Is Coming, Can You Really Code?]]></title>
            <link>https://blog.cheverjohn.me/can-you-really-code</link>
            <guid>https://blog.cheverjohn.me/can-you-really-code</guid>
            <pubDate>Fri, 15 Aug 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[AI has been moving at a breakneck pace. Many people are getting hooked on barking orders at AI and then discovering piles of error‑ridden code it produced (and in truth, you may not fully understand the code the AI wrote either). If you do understand AI, sure—you know how to use prompt engineering....]]></description>
            <content:encoded><![CDATA[<article><div><p>AI has been moving at a breakneck pace. Many people are getting hooked on barking orders at AI and then discovering piles of error‑ridden code it produced (and in truth, you may not fully understand the code the AI wrote either).</p><p>If you do understand AI, sure—you know how to use prompt engineering. Still, I want to reinforce the fundamentals of software engineering.</p><p>Object‑oriented programming, design principles, design patterns, coding conventions, and refactoring.</p><h2 id="programming-paradigmsstyles">Programming paradigms/styles</h2><p>The mainstream programming paradigms/styles are:</p><ul><li>Procedural programming</li><li>Object‑oriented programming (OOP)</li><li>Functional programming</li></ul><p>Among them, OOP remains the most widely adopted paradigm. Most popular programming languages today are object‑oriented or provide strong OO support.</p><h2 id="design-principles">Design principles</h2><p>Definition: distilled practical experience for code design.</p><p>These principles often sound abstract and are described vaguely.</p><p>The hard part: understand the original intent, the specific problems they solve, and the scenarios where they apply.</p><p>Common principles include:</p><ul><li><p>SOLID principles</p><blockquote><p>You might wonder why SOLID uniquely comes with five additional “sub‑principles.”</p><p>I had the same question, so I looked it up.</p><p>SOLID is called the cornerstone of object‑oriented design because it offers a concrete, actionable toolkit—rather than vague ideas—for solving real problems in software development.</p><p>Each sub‑principle targets a different layer of design pitfalls. It stands out precisely because it is not a single fuzzy rule, but a collection of five specific, operable design principles.</p></blockquote>
<ul><li>Single Responsibility Principle (SRP)</li><li>Open/Closed Principle (OCP)</li><li>Liskov Substitution Principle (LSP)</li><li>Interface Segregation Principle (ISP)</li><li>Dependency Inversion Principle (DIP)</li></ul></li><li><p>KISS principle (Keep It Simple, Stupid)</p></li><li><p>DRY principle (Don’t Repeat Yourself)</p></li><li><p>YAGNI principle (You Aren’t Gonna Need It)</p></li><li><p>Law of Demeter (LOD)</p></li></ul><h2 id="design-patterns">Design patterns</h2><p>Definition: recurring solutions or design approaches distilled from frequently encountered problems in software development.</p><p>Most design patterns are fundamentally about extensibility.</p><p>The hard part: knowing which problem each pattern addresses, recognizing typical application scenarios, and—importantly—avoiding overuse.</p><p>What’s included: there are 23 classic design patterns.</p><p>As languages evolve:</p><ul><li>Some patterns (e.g., Singleton) have aged poorly and even become anti‑patterns.</li><li>Some are now built into languages (e.g., Iterator).</li><li>New patterns keep emerging (e.g., Monostate).</li></ul><p>The 23 classic patterns are grouped into three categories: creational, structural, and behavioral.</p><h3 id="creational">Creational</h3><p>Common: Singleton, Factory (Factory Method and Abstract Factory), Builder.</p><p>Less common: Prototype.</p><h3 id="structural">Structural</h3><p>Common: Proxy, Bridge, Decorator, Adapter.</p><p>Less common: Facade, Composite, Flyweight.</p><h3 id="behavioral">Behavioral</h3><p>Common: Observer, Template Method, Strategy, Chain of Responsibility, Iterator, State.</p><p>Less common: Visitor, Memento, Command, Interpreter, Mediator.</p><h2 id="coding-conventions">Coding conventions</h2><p>Definition: primarily concerned with readability. Compared with design principles and design patterns, conventions are more concrete and closer to code‑level details. Even if you’re unfamiliar with design principles or patterns, at minimum you should master basic coding conventions.</p><p>For example: how to name variables, classes, and functions; how to write comments; keep functions short; avoid too many parameters; and so on.</p><p>There are plenty of classic books to learn from—“Refactoring,” “Code Complete,” and “Clean Code,” to name a few.</p><p>Each convention is straightforward and specific. Just remember them and follow them. Unlike design principles, they require less personal interpretation.</p><h2 id="refactoring">Refactoring</h2><p>As long as a project is alive and people keep working on it, the software will evolve. New features will inevitably push older code to be refactored. Refactoring is the practical way to keep code quality from decaying to an unrecoverable state.</p><p>The toolkit for refactoring includes everything mentioned earlier: paradigms, design principles, design patterns, and coding conventions.</p><p>While design patterns can improve extensibility, overuse or misuse increases complexity and harms readability.</p><p>In early development, unless absolutely necessary, don’t over‑design or apply complex patterns.</p><p>Instead, when code shows concrete issues, refactor to address those issues using the appropriate principles and patterns.</p><p>This effectively prevents premature over‑engineering.</p><h3 id="mustknow-points">Must‑know points</h3><p>As follows:</p><ul><li>The purpose (why), target (what), timing (when), and methods (how) of refactoring</li><li>Techniques for safe refactoring: unit tests and code testability</li><li>Two scales of refactoring: large‑scale/high‑level and small‑scale/low‑level</li></ul><h2 id="how-the-five-pieces-fit-together">How the five pieces fit together</h2><p>The relationship among OOP, design principles, design patterns, coding conventions, and refactoring is as follows:</p><ul><li>OOP, with its rich features (encapsulation, abstraction, inheritance, polymorphism), enables sophisticated design ideas and is the foundation for implementing many principles and patterns.</li><li>Design principles guide design decisions and help determine whether a particular pattern should be applied in a given scenario. For example, the Open/Closed Principle underpins patterns such as Strategy and Template Method.</li><li>Design patterns are recurring solutions to common design problems in software development. Their main goal is to improve extensibility. In terms of abstraction, principles are more abstract; patterns are more concrete and executable.</li><li>Coding conventions primarily address readability. Compared with principles and patterns, conventions are more specific, detail‑oriented, and directly actionable. Continuous small‑scale refactoring relies heavily on conventions as its theoretical backbone.</li></ul><p>Ultimately, this article is about one thing: writing high‑quality code. Once you trace it back to that, how to do many things—and how to implement them in code—becomes clear.</p><p><img alt="code-knowledge-graph_en.png" src="code-knowledge-graph_en.png"/></p><pre><code class="lang-mermaid">mindmap
  root((Writing High-Quality Code))
    Object-Oriented
      Encapsulation, Abstraction, Inheritance, Polymorphism
      OOP vs Procedural Programming
      OO Analysis, Design, Programming
      Interface vs Abstract Class
      Program to an Interface, Not an Implementation
      Favor Composition Over Inheritance
      Anemic vs Rich Domain Model
    Design Principles
      SOLID Principles
        SRP Single Responsibility
        OCP Open/Closed
        LSP Liskov Substitution
        ISP Interface Segregation
        DIP Dependency Inversion
      Others
        DRY Principle
        KISS Principle
        YAGNI Principle
        Law of Demeter (LOD)
    Coding Conventions
      20 Quick Rules to Improve Code Quality
    Refactoring
      Purpose, Target, Timing, Methods
      Unit Tests and Testability
      Large-Scale (High-Level)
      Small-Scale (Low-Level)
    Design Patterns
      Creational
        Common
          Singleton
          Factory (Factory Method &amp; Abstract Factory)
          Builder
        Less Common
          Prototype
      Structural
        Common
          Proxy
          Bridge
          Decorator
          Adapter
        Less Common
          Facade
          Composite
          Flyweight
      Behavioral
        Common
          Observer
          Template Method
          Strategy
          Chain of Responsibility
          Iterator
          State
        Less Common
          Visitor
          Memento
          Command
          Interpreter
          Mediator
</code></pre>
<pre><code class="lang-mermaid">mindmap
  root((Writing High-Quality Code))
    Object-Oriented
      Encapsulation, Abstraction, Inheritance, Polymorphism
      OOP vs Procedural Programming
      OO Analysis, Design, Programming
      Interface vs Abstract Class
      Program to an Interface, Not an Implementation
      Favor Composition Over Inheritance
      Anemic vs Rich Domain Model
    Design Principles
      SOLID Principles
        SRP Single Responsibility
        OCP Open/Closed
        LSP Liskov Substitution
        ISP Interface Segregation
        DIP Dependency Inversion
      Others
        DRY Principle
        KISS Principle
        YAGNI Principle
        Law of Demeter (LOD)
    Coding Conventions
      20 Quick Rules to Improve Code Quality
    Refactoring
      Purpose, Target, Timing, Methods
      Unit Tests and Testability
      Large-Scale (High-Level)
      Small-Scale (Low-Level)
    Design Patterns
      Creational
        Common
          Singleton
          Factory (Factory Method &amp; Abstract Factory)
          Builder
        Less Common
          Prototype
      Structural
        Common
          Proxy
          Bridge
          Decorator
          Adapter
        Less Common
          Facade
          Composite
          Flyweight
      Behavioral
        Common
          Observer
          Template Method
          Strategy
          Chain of Responsibility
          Iterator
          State
        Less Common
          Visitor
          Memento
          Command
          Interpreter
          Mediator
</code></pre>
<p>PS: A bunch of keywords for further exploration:</p><p>System architecture principles
High cohesion and low coupling
Law of Demeter (principle of least knowledge)
Composition over inheritance
Separation of concerns
Design by contract
Cloud‑native design principles
Resilience design principles
Observability principles
Immutable infrastructure
Service autonomy
Declarative configuration
Security by design
Defense in depth
Principle of least privilege
Secure by default
Separation of duties
Fail‑safe</p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>code</category>
        </item>
        <item>
            <title><![CDATA[Performance Optimization of Legacy Systems]]></title>
            <link>https://blog.cheverjohn.me/performance-optimization-of-old-systems</link>
            <guid>https://blog.cheverjohn.me/performance-optimization-of-old-systems</guid>
            <pubDate>Mon, 30 Jun 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[When legacy systems are initially created and coded, the final system performance and maintainability depend entirely on the developer's expertise. When you inherit a system with the following issues, this article might help you: 1. No monitoring 2. No alerting 3. No business success rate metrics 4....]]></description>
            <content:encoded><![CDATA[<article><div>
<p>When legacy systems are initially created and coded, the final system performance and maintainability depend entirely on the developer&#x27;s expertise.</p><p>When you inherit a system with the following issues, this article might help you:</p><ol start="1"><li>No monitoring</li><li>No alerting</li><li>No business success rate metrics</li><li>Frequent optimistic locking failures</li><li>Black box system response time (yes, your business users only vaguely tell you the system is slow, but don&#x27;t know specifically where it&#x27;s slow)</li><li>Complex log troubleshooting</li></ol><p>Yes, these are the headache-inducing problems I&#x27;ve encountered over the past month.</p><p>Now that the month is up, I&#x27;m starting to take action and perform a series of performance optimizations on this system.</p><p>First, you need to understand the overall situation of the system. For optimization, you should at least know what you&#x27;re optimizing. Business users should also know where exactly their system is slow.</p><p>So what you need to do is <strong>establish system baselines</strong>.</p><h2 id="establishing-system-baselines">Establishing System Baselines</h2><p>System baselines are quantitative descriptions of key performance indicators when the system is running normally.</p><p>They are the starting point for system performance optimization and the standard reference for measuring optimization effectiveness. Simply put, system baselines are your system&#x27;s &quot;health checkup report,&quot; recording system performance under various conditions. (<a href="https://zuplo.com/blog/2025/02/28/improving-api-performance-in-legacy-systems">1</a>, <a href="https://dzone.com/articles/optimizing-legacy-systems-through-scalable-architectures">2</a>)</p><h3 id="system-baselines-include-the-following">System Baselines Include the Following</h3><p>A complete system baseline should include the following aspects:</p><h4 id="performance-metrics">Performance Metrics</h4><ul><li>Response time (average, P95, P99)</li><li>Throughput (QPS/TPS)</li><li>Latency</li><li>Concurrent processing capability</li></ul><h4 id="resource-usage"><strong>Resource Usage</strong></h4><ul><li><strong>CPU utilization</strong></li><li><strong>Memory usage and allocation</strong></li><li><strong>Disk I/O</strong></li><li><strong>Network I/O</strong></li><li><strong>Connection pool usage status</strong></li></ul><h4 id="business-metrics">Business Metrics</h4><ul><li>Business success rate</li><li>Error rate</li><li>Key business process completion time</li></ul><h4 id="system-stability-metrics">System Stability Metrics</h4><ul><li>Mean Time Between Failures (MTBF)</li><li>Mean Time To Recovery (MTTR)</li><li>Lock contention situations (such as the optimistic lock failure rate you mentioned)</li></ul></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>Performance Optimization</category>
            <category>System Design</category>
        </item>
        <item>
            <title><![CDATA[Google MapReduce: Simplified Data Processing on Large Clusters]]></title>
            <link>https://blog.cheverjohn.me/google-map-reduce</link>
            <guid>https://blog.cheverjohn.me/google-map-reduce</guid>
            <pubDate>Mon, 05 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Taking this opportunity during the May Day holiday to thoroughly analyze one of the most famous papers: "MapReduce: Simplified Data Processing on Large Clusters." Of course, this article is deeply influenced by the article from "Muniao's Notes," so my article is purely a combination of two articles...]]></description>
            <content:encoded><![CDATA[<article><div><p>Taking this opportunity during the May Day holiday to thoroughly analyze one of the most famous papers: &quot;MapReduce: Simplified Data Processing on Large Clusters.&quot;</p><p>Of course, this article is deeply influenced by the <a href="https://www.qtmuniao.com/2019/04/30/map-reduce/">article</a> from &quot;Muniao&#x27;s Notes,&quot; so my article is purely a combination of two articles plus my own thoughts.</p><p><img alt="MR Execution Flow" src="The-Whole-Arch.png"/></p><p>I&#x27;ve always heard senior colleagues talk about how powerful MapReduce distributed computing is, so today&#x27;s the opportunity to dive in!</p><h2 id="introduction">Introduction</h2><p>MapReduce in the paper is actually a concept. But in another context, it can also be a programming model, as well as a distributed system implementation supporting that model. I found an article that explains this concept better:</p><blockquote><p>MapReduce is a concept proposed in Google&#x27;s 2004 paper (Google internally wrote the first version starting in 2003).</p><p>In Google&#x27;s context, MapReduce is both a programming model and a distributed system implementation supporting that model. Its introduction enables developers without distributed systems background to leverage large-scale clusters for high-throughput processing of massive data with relative ease.</p></blockquote>
<p>This article also has a sentence explaining the problem-solving approach for applying this technology: <strong>Finding pain points in requirements (such as how to maintain, update, and rank massive indexes), performing high-level abstractions of key processing flows (sharding Map, on-demand Reduce), for efficient system implementation (so-called tailored solutions).</strong></p><p><em>Among these, finding an appropriate computational abstraction is the most difficult part, requiring both intuitive understanding of requirements and extremely high computer science literacy.</em></p><p>The quote above is from the referenced article from &quot;Muniao&#x27;s Notes.&quot;</p><p>Returning to the paper, we can see that on the first page, Google&#x27;s experts clearly explained what this is.</p><blockquote><p>As a reaction to this complexity, we designed a new abstraction that allows us to express the simple computations we were trying to perform but hides the messy details of parallelization, fault-tolerance, data distribution and load balancing in a library.</p><p>This means we abstracted something to express a computational method that can hide many conceptual details (parallelization, fault tolerance, data distribution, and load balancing).</p><p>This abstraction is inspired by the map and reduce primitives present in Lisp and many other functional languages.</p><p>We realized that most of our computations involved applying a map operation to each logical &quot;record&quot; in our input in order to compute a set of intermediate key/value pairs, and then applying a reduce operation to all the values that shared the same key, in order to combine the derived data appropriately.</p><p>Most of our computations involve applying a map operation to each logical record in the input to compute a set of intermediate key/value pairs, then applying a reduce operation to all values sharing the same key to appropriately combine the derived data.</p><p>Our use of a functional model with user-specified map and reduce operations allows us to parallelize large computations easily and to use re-execution as the primary mechanism for fault tolerance.</p><p>We use a functional model with user-specified map and reduce operations, enabling easy parallelization of large computations.</p></blockquote>
<p>I find the last sentence very interesting:</p><blockquote><p>use re-execution as the primary mechanism for fault tolerance.</p></blockquote>
<p>Using &quot;re-execution&quot; as the primary mechanism for fault tolerance.</p><p>The paper&#x27;s Abstract outlines the content:</p><ol start="1"><li>Section 1 is the Introduction above</li><li>Section 2 describes the basic programming model and gives several examples, <a href="#programming-model">Programming Model</a></li><li>Section 3 describes an implementation of the MapReduce interface tailored towards our cluster-based computing environment, <a href="#implementation">Implementation</a><ul><li>Implementation of MapReduce interface customized for cluster computing environments</li></ul></li><li>Section 4 describes several refinements of the programming model that we have found useful - Several programming model improvements</li><li>Section 5 has performance measurements of our implementation for a variety of tasks - Performance measurements of implementing various tasks</li><li>Section 6 explores the use of MapReduce within Google including our experiences in using it as the basis for a rewrite of our production indexing system - MapReduce applications within Google</li><li>Section 7 discusses related and future work</li></ol><h2 id="programming-model">Programming Model</h2><p>The Map key is a normal key, and the value can be thought of as a string array.</p><p>MapReduce, simply put, consists of two functions: the map function and the reduce function.</p><p>The Map function receives an input pair and generates a set of intermediate key/value pairs, then the MapReduce library combines all intermediate values associated with the same key.</p><h3 id="example">Example</h3><p>Below is pseudo-code, grandfather-level, from the original paper:</p><pre><code class="lang-java">map(String key, String value):
    // key: document name
    // value: document contents
    for each word w in value:
      EmitIntermediate(w, &quot;1&quot;);

reduce(String key, Iterator values):
    // key: a word
    // values: a list of counts
    int result = 0;
    for each v in values:
      result += ParseInt(v);
    Emit(AsString(result));
</code></pre>
<p>This is a classic MapReduce word count implementation, one of the most common examples in the MapReduce programming model.</p><ul><li>key: document name</li><li>value: complete document content (text string)</li></ul><p>EmitIntermediate represents the function provided by the MapReduce framework for outputting intermediate key/value pairs. Each call to this function produces a key-value pair: (word, &quot;1&quot;), indicating that the word appeared once.</p><p>For example, processing document content &quot;hello world hello&quot;:</p><ul><li>First word &quot;hello&quot; → EmitIntermediate(&quot;hello&quot;, &quot;1&quot;)</li><li>Second word &quot;world&quot; → EmitIntermediate(&quot;world&quot;, &quot;1&quot;)</li><li>Third word &quot;hello&quot; → EmitIntermediate(&quot;hello&quot;, &quot;1&quot;)</li></ul><p>Map phase intermediate results:</p><pre><code class="lang-shell">(&quot;hello&quot;, &quot;1&quot;)
(&quot;world&quot;, &quot;1&quot;)
(&quot;hello&quot;, &quot;1&quot;)
</code></pre>
<h3 id="shuffle-stage-automatically-completed-by-framework">Shuffle Stage (Automatically Completed by Framework)</h3><p>Between Map and Reduce, the MapReduce framework automatically performs the Shuffle operation:</p><ol start="1"><li>Collect all mapper outputs</li><li>Sort by key (word)</li><li>Group all values with the same key together</li></ol><p>So the above example after Shuffle:</p><pre><code class="lang-shell">(&quot;hello&quot;, [&quot;1&quot;, &quot;1&quot;])
(&quot;world&quot;, [&quot;1&quot;])
</code></pre>
<h3 id="reduce-function">Reduce Function</h3><p>After Shuffle comes the Reduce function&#x27;s work. The pseudo-code above essentially performs accumulation, which needs no further explanation.</p><h3 id="complete-execution-flow">Complete Execution Flow</h3><p>Here&#x27;s a larger example showing the entire MapReduce execution flow:</p><p><strong>Assume three documents</strong>:</p><ul><li>document1.txt: &quot;hello world&quot;</li><li>document2.txt: &quot;hello mapreduce&quot;</li><li>document3.txt: &quot;mapreduce world example&quot;</li></ul><p><strong>Map Phase (Parallel Execution)</strong></p><p>Mapper 1 processes document1.txt:</p><pre><code class="lang-shell">EmitIntermediate(&quot;hello&quot;, &quot;1&quot;)
EmitIntermediate(&quot;world&quot;, &quot;1&quot;)
</code></pre>
<p>Mapper 2 processes document2.txt:</p><pre><code class="lang-shell">EmitIntermediate(&quot;hello&quot;, &quot;1&quot;)
EmitIntermediate(&quot;mapreduce&quot;, &quot;1&quot;)
</code></pre>
<p>Mapper 3 processes document3.txt:</p><pre><code class="lang-shell">EmitIntermediate(&quot;mapreduce&quot;, &quot;1&quot;)
EmitIntermediate(&quot;world&quot;, &quot;1&quot;)
EmitIntermediate(&quot;example&quot;, &quot;1&quot;)
</code></pre>
<p><strong>Shuffle Phase (Automatically Completed by Framework)</strong>:</p><pre><code class="lang-shell">(&quot;hello&quot;, [&quot;1&quot;, &quot;1&quot;])
(&quot;world&quot;, [&quot;1&quot;, &quot;1&quot;])
(&quot;mapreduce&quot;, [&quot;1&quot;, &quot;1&quot;])
(&quot;example&quot;, [&quot;1&quot;])
</code></pre>
<p><strong>Reduce Phase (Parallel Execution)</strong>:</p><p>Reducer processes &quot;hello&quot;:</p><pre><code class="lang-shell">result = 0
result += 1 = 1
result += 1 = 2
Emit(&quot;2&quot;)  # Output (&quot;hello&quot;, &quot;2&quot;)
</code></pre>
<p>Similarly for other words...</p><p><strong>Final Results</strong></p><pre><code class="lang-shell">(&quot;hello&quot;, &quot;2&quot;)
(&quot;world&quot;, &quot;2&quot;)
(&quot;mapreduce&quot;, &quot;2&quot;)
(&quot;example&quot;, &quot;1&quot;)
</code></pre>
<h4 id="mapreduce-frameworks-role">MapReduce Framework&#x27;s Role</h4><p>In this process, the MapReduce framework is responsible for:</p><ol start="1"><li>Splitting input data into multiple shards, assigning them to different Mappers</li><li>Executing multiple Map tasks in parallel</li><li>Performing Shuffle operations, reorganizing and sorting intermediate results</li><li>Executing multiple Reduce tasks in parallel</li><li>Collecting and integrating Reduce outputs</li><li>Handling task failures and retries</li><li>Optimizing data locality, processing data on nodes where it resides when possible</li></ol><p>This pattern allows developers to focus on business logic (Map and Reduce functions) without worrying about complex issues like parallelization, distributed computing, and fault tolerance.</p><h3 id="more-examples">More Examples</h3><p>Here are more examples:</p><p><strong>Distributed Grep</strong></p><p><strong>Working Principle</strong></p><p>In this example:</p><ul><li>Map function checks each line of input text, emitting the line if it matches a specified pattern</li><li>Reduce function is a simple identity function that directly copies intermediate results to output</li></ul><p><strong>Application Value</strong></p><p>This pattern is very suitable for quickly finding text lines with specific patterns in large-scale distributed file systems. It fully utilizes MapReduce&#x27;s parallel processing capabilities and is extremely efficient when searching for specific error information in TB or even PB-level log files.</p><p><strong>Count of URL Access Frequency</strong></p><p><strong>Working Principle</strong></p><ul><li><strong>Map function</strong>: Processes web request logs, emitting <code>&lt;URL, 1&gt;</code> key-value pairs for each URL</li><li><strong>Reduce function</strong>: Sums all counts for the same URL, outputting <code>&lt;URL, total count&gt;</code></li></ul><p><strong>Application Value</strong></p><p>This is a fundamental operation in web analytics, crucial for understanding website traffic distribution, identifying popular content, and detecting abnormal access patterns. In large websites, daily log data can reach several TB, and MapReduce can effectively handle this scale of data.</p><p><strong>Reverse Web-Link Graph</strong></p><p><strong>Working Principle</strong></p><ul><li><strong>Map function</strong>: Analyzes web content, outputting <code>&lt;target URL, source URL&gt;</code> for each discovered link</li><li><strong>Reduce function</strong>: Collects all source URLs for a target URL, outputting <code>&lt;target URL, source URL list&gt;</code></li></ul><p><strong>Application Value</strong></p><p>Reverse link graphs are one of the core data structures for modern search engines, used in the following scenarios:</p><ul><li>Foundation data for PageRank and other web importance algorithms</li><li>Analyzing citation relationships between websites</li><li>Discovering influential content creators</li><li>Providing backlink analysis tools for webmasters</li></ul><p>Building a complete web reverse link graph is a computation-intensive task, and the MapReduce model is very suitable for this naturally parallelizable problem.</p><p><strong>Term-Vector per Host</strong></p><p><strong>Working Principle</strong></p><ul><li><strong>Map function</strong>: Analyzes document content, extracts hostname from URL, outputs <code>&lt;hostname, document term vector&gt;</code></li><li><strong>Reduce function</strong>: Merges all term vectors for the same host, filters low-frequency terms, outputs <code>&lt;hostname, aggregated term vector&gt;</code></li></ul><p><strong>Application Value</strong></p><p>This analysis is very valuable for understanding website content characteristics:</p><ul><li>Can be used for website topic classification</li><li>Helps with search engine optimization</li><li>Content similarity comparison</li><li>Competitor website content analysis</li><li>Foundation data for content recommendation systems</li></ul><p><strong>Inverted Index</strong></p><p><strong>Working Principle</strong></p><ul><li><strong>Map function</strong>: Parses each document, outputs <code>&lt;word, document ID&gt;</code> key-value pairs</li><li><strong>Reduce function</strong>: Receives all document IDs for a given word, sorts them and outputs <code>&lt;word, document ID list&gt;</code> key-value pairs</li></ul><p><strong>Application Value</strong></p><p>Inverted indexes are the fundamental data structure for modern search engines, used for:</p><ol start="1"><li><strong>Full-text search</strong>: Quickly find all documents containing query terms</li><li><strong>Phrase search</strong>: Achieve precise phrase search through positional information</li><li><strong>TF-IDF calculation</strong>: Provide term frequency statistics for information retrieval systems</li><li><strong>Keyword highlighting</strong>: Help frontend display matched text fragments</li><li><strong>Relevance ranking</strong>: Provide foundation data for search result ranking</li></ol><p>MapReduce is particularly suitable for building inverted indexes because it can efficiently process large numbers of documents in parallel and naturally implement index merging in the Reduce phase.</p><p><strong>Distributed Sort</strong></p><p><strong>Working Principle</strong></p><ul><li><strong>Map function</strong>: Extracts the key of each record, outputs <code>&lt;key, record&gt;</code> key-value pairs</li><li><strong>Reduce function</strong>: Directly outputs all received key-value pairs without modification</li></ul><p>This seemingly simple example actually cleverly utilizes two core features of the MapReduce framework:</p><ol start="1"><li><strong>Partitioning mechanism</strong>: Ensures records with keys in the same range are sent to the same Reducer</li><li><strong>Sorting property</strong>: Ensures keys received by Reducers are in order</li></ol><p><strong>MapReduce Framework&#x27;s Special Contribution</strong>:</p><p>In distributed sorting, the MapReduce framework does most of the important work:</p><ol start="1"><li><p><strong>Custom Partitioner</strong>:</p><pre><code class="lang-go">// Example: Range partitioner
func RangePartitioner(key string, numReducers int) int {
    // Determine which reducer to send to based on key range
    // This ensures global sorting
    if key &lt; &quot;D&quot; {
        return 0
    } else if key &lt; &quot;N&quot; {
        return 1
    } else {
        return 2
    }
}
</code></pre>
</li><li><p><strong>Sort Comparator</strong>:</p><pre><code class="lang-go">// Define natural sorting order for keys
func KeyComparator(key1, key2 string) int {
    return strings.Compare(key1, key2)
}
</code></pre>
</li></ol><p><strong>Application Value</strong></p><p>Distributed sorting is a fundamental operation for many big data processing workflows:</p><ol start="1"><li><strong>Data preprocessing</strong>: Preparing large datasets for further analysis</li><li><strong>Log analysis</strong>: Sorting massive log records by timestamp</li><li><strong>Building indexes</strong>: Creating sorted indexes for databases or search engines</li><li><strong>Merging sorted data</strong>: Combining multiple sorted datasets into one</li><li><strong>TopN queries</strong>: Quickly finding top N records for a given metric</li></ol><p><strong>Overall Examples Analysis and Comparison</strong></p><p>The examples demonstrate the versatility and adaptability of the MapReduce model:</p><ol start="1"><li><strong>Distributed Grep</strong>: Simplest application, basically only uses Map functionality, suitable for simple filtering operations</li><li><strong>URL Access Frequency Count</strong>: Classic word count variant, demonstrates MapReduce advantages in statistical aggregation</li><li><strong>Reverse Web-Link Graph</strong>: Shows how to use MapReduce to build complex relationship graphs and index structures</li><li><strong>Term-Vector per Host</strong>: Combines text analysis and aggregation functions, suitable for advanced content analysis</li></ol><h2 id="implementation">Implementation</h2><p>Multiple different implementations of the MapReduce interface are possible.</p><p><strong>The right choice depends on the specific environment (meaning specific problems require specific analysis).</strong></p><p>For example, one implementation might be suitable for small shared-memory machines, another for large NUMA multiprocessors, and yet another for even larger networked machine clusters.</p><p>Below is the computing environment widely used at Google:</p><blockquote><p>This section describes an implementation targeted to the computing environment in wide use at Google: large clusters of commodity PCs connected together with switched Ethernet.</p><p>In our environment:</p><p>(1) Machines are typically dual-processor x86 processors running Linux, with 2-4 GB of memory per machine.</p><p>(2) Commodity networking hardware is used – typically either 100 megabits/second or 1 gigabit/second at the machine level, but averaging considerably less in overall bisection bandwidth.</p><p>(3) A cluster consists of hundreds or thousands of machines, and therefore machine failures are common.</p><p>(4) Storage is provided by inexpensive IDE disks attached directly to individual machines. A distributed file system developed in-house is used to manage the data stored on these disks. The file system uses replication to provide availability and reliability on top of unreliable hardware.</p><p>(5) Users submit jobs to a scheduling system. Each job consists of a set of tasks, and is mapped by the scheduler to a set of available machines within a cluster.</p></blockquote>
<p>(1) Machines typically feature dual-processor x86 architecture, running Linux systems, with 2-4 GB of memory per machine.</p><p>(2) Commodity networking hardware is used — typically 100 megabits/second or 1 gigabit/second at the machine level, but the average overall bisection bandwidth is significantly lower.</p><p>(3) A cluster consists of hundreds or thousands of machines, so machine failures are common.</p><p>(4) Storage is provided by inexpensive IDE disks directly connected to individual machines. An internally developed distributed file system is used to manage data stored on these disks. The file system provides availability and reliability on top of unreliable hardware through data replication.</p><p>(5) Users submit jobs to a scheduling system. Each job consists of a set of tasks and is mapped by the scheduler to a set of available machines within the cluster.</p><h3 id="execution-process">Execution Process</h3><blockquote><p>The Map invocations are distributed across multiple machines by automatically partitioning the input data into a set of M splits. The input splits can be processed in parallel by different machines. Reduce invocations are distributed by partitioning the intermediate key space into R pieces using a partitioning function (e.g., hash(key) mod R). The number of partitions (R) and the partitioning function are specified by the user.</p><p>Following figure shows the overall flow of a MapReduce operation in our implementation. When the user program calls the MapReduce function, the following sequence of actions occurs (the numbered labels in Figure correspond to the numbers in the list below):</p></blockquote>
<p><img alt="MR Execution Flow" src="The-Whole-Arch.png"/></p><ol start="1"><li><p>The MapReduce Library in the user program first splits the files into M pieces, each piece typically 16-64 MB in size;</p></li><li><p>The program copy on the Master is special, while other workers are assigned tasks by the master. There are typically M map tasks and R reduce tasks to assign.</p><blockquote><p>There are M map tasks and R reduce tasks to assign. The master picks idle workers and assigns each one a map task or a reduce task.</p></blockquote>
<p>Here, the word &quot;idle&quot; refers to idle workers - in Google&#x27;s MapReduce architecture, the entire computation task is executed distributedly, including:</p><ol start="1"><li><strong>Master (master node)</strong>: A special program copy responsible for task scheduling and coordinating the entire computation process</li><li><strong>Workers (worker nodes)</strong>: Other program copies responsible for executing actual computation tasks</li><li><strong>Idle workers</strong>: Worker nodes currently not executing any tasks and in a waiting state</li></ol><p>I hope this clarifies what idle workers are.</p><p>So generally speaking, the entire MapReduce workflow related to idle worker nodes:</p><ol start="1"><li>When computation tasks begin, the system starts multiple program copies, with one as the master node and others as worker nodes</li><li>The master node maintains the state of the entire cluster, including whether each worker node is idle</li><li>When the master node detects that a worker node is &quot;idle&quot; (not executing tasks), it selects one from the pending M Map tasks or R Reduce tasks to assign to that worker node</li></ol></li><li><p>A worker assigned a map task reads the contents of the corresponding input split, parses key/value pairs from the input data, and passes each pair to the user-defined map function.</p><p>These intermediate key/value pairs produced by map are buffered in memory.</p><blockquote><p>A worker who is assigned a map task reads the contents of the corresponding input split. It parses key/value pairs out of the input data and passes each pair to the user-defined Map function. The intermediate key/value pairs produced by the Map function are buffered in memory.</p></blockquote>
<p>In fact, Hadoop does exactly this.</p></li><li><p>Buffered intermediate result pairs are periodically written to local disk, then partitioned into R regions by the partitioning function.</p><p>These buffered pairs on local disk are passed back to the master, so the master can inform reduce workers of these pair locations.</p><blockquote><p>Periodically, the buffered pairs are written to local disk, partitioned into R regions by the partitioning function.</p><p>The locations of these buffered pairs on the local disk are passed back to the master, who is responsible for forwarding these locations to the reduce workers.</p></blockquote>
</li><li><p>When a reduce worker receives the buffered pair locations mentioned above, it uses RPC to read the corresponding partition data.</p><p>When a reduce worker has read all the data, it sorts by key so that all data with the same key is sorted together.</p><blockquote><p>When a reduce worker is notified by the master about these locations, it uses remote procedure calls to read the buffered data from the local disks of the map workers.</p><p>When a reduce worker has read all intermediate data, it sorts it by the intermediate keys so that all occurrences of the same key are grouped together.</p><p>The sorting is needed because typically many different keys map to the same reduce task.</p><p>If the amount of intermediate data is too large to fit in memory, an external sort is used.</p></blockquote>
<p>If the intermediate data is too large, external sorting programs are needed. This is where performance optimization points come in.</p></li></ol><p><strong>Steps 4 and 5 together are called shuffle</strong></p><ol start="6"><li><p>The Reduce Worker then iterates through this sorted intermediate data and passes this data along with its key-related data to the user&#x27;s reduce function.</p><p>The Reduce function&#x27;s output is appended to the final output file.</p><blockquote><p>The reduce worker iterates over the sorted intermediate data and for each unique intermediate key encountered, it passes the key and the corresponding set of intermediate values to the user&#x27;s Reduce function.</p><p>The output of the Reduce function is appended to a final output file for this reduce partition.</p></blockquote>
</li><li><p>When all map tasks and reduce tasks are completed, the master wakes up the user program.</p><p>At this point, the user program receives a final computation result (MapReduce call).</p><blockquote><p>When all map tasks and reduce tasks have been completed, the master wakes up the user program. At this point, the MapReduce call in the user program returns back to the user code.</p></blockquote>
</li></ol><h3 id="master-data-structures">Master Data Structures</h3><p>Understanding the key data structures maintained by the Master node in the MapReduce framework and its core responsibilities in task coordination.</p><p>The Master node is actually the &quot;brain&quot; of the entire MapReduce execution process, maintaining the following important data:</p><ol start="1"><li><p><strong>Task State Records</strong>: For each map and reduce task, the master node records its current state:</p><ul><li>idle: pending tasks</li><li>in-progress: tasks assigned to workers but not yet completed</li><li>completed: finished tasks</li></ul></li><li><p><strong>Worker Machine Identity</strong>: For non-idle state tasks, the master node records the identity of the worker machine executing that task, used for tracking task execution and handling failures</p></li><li><p><strong>Intermediate File Metadata</strong>: For completed map tasks, the master stores location and size information of intermediate result files produced by that task</p></li></ol><p><strong>Master as Information Transfer Channel</strong></p><p>The Master node plays the role of an information transfer channel for intermediate result location information. When a map task completes, it informs the master node of which intermediate files were produced, along with their location and size information.</p><p>Additionally, Google&#x27;s MapReduce implementation has job-level encapsulation, where each job contains a series of tasks, namely Map Tasks and Reduce Tasks.</p><p>To maintain metadata for a running job, it&#x27;s necessary to save the state of all executing tasks and their machine IDs.</p><p>This information is crucial for reduce tasks, as reduce tasks need to know where to obtain the data they need to process.</p><p>Moreover, the Master also serves as an information channel from Map Task output to Reduce Task. The master node incrementally pushes this information to worker nodes executing reduce tasks.</p><p>When each Map Task ends, it notifies the Master of the location information of its output intermediate results, the Master then forwards this to the corresponding Reduce Task, and the Reduce Task fetches the corresponding size of data from the corresponding location.</p><p><strong>Note: Since Map Task completion times are not uniform, this notification → forwarding → fetching process is incremental. It&#x27;s reasonable to infer that the sorting of intermediate data on the reduce side should be a continuous merge process, unlikely to be global sorting after all data is in place.</strong> — from Muniao</p><blockquote><p>The master keeps several data structures. For each map task and reduce task, it stores the state (idle, in-progress, or completed), and the identity of the worker machine (for non-idle tasks).</p><p>The master is the conduit through which the location of intermediate file regions is propagated from map tasks to reduce tasks. Therefore, for each completed map task, the master stores the locations and sizes of the R intermediate file regions produced by the map task. Updates to this location and size information are received as map tasks are completed. The information is pushed incrementally to workers that have in-progress reduce tasks.</p></blockquote>
<h3 id="fault-tolerance">Fault Tolerance</h3><p>Distributed systems must gracefully handle various errors on distributed machines when processing large amounts of data. The paper roughly divides failures into three types:</p><ol start="1"><li><a href="#worker-failure">Worker Failure</a></li><li><a href="#master-failure">Master Failure</a></li><li><a href="#semantics-in-the-presence-of-failures">Semantics in the Presence of Failures</a></li></ol><h4 id="worker-failure">Worker Failure</h4><p>The master periodically pings each worker node, and if there&#x27;s no response, the master marks it as a failed node.</p><p>At this time, both completed and uncompleted map tasks are marked back to the initial idle state, then wait to be scheduled to other normal workers.</p><p>These map tasks are then re-executed and re-stored to local disk, and the master continues to report this information to Reduce workers.</p><p>If a Reducer worker has already processed one of the map tasks, it doesn&#x27;t need to fetch processing data again from the master&#x27;s provided information; if not processed, it continues to fetch.</p><p>On the Reducer worker side, when it discovers that the map worker processing a certain segment of map tasks has failed, for example, this Reduce program is R5, processing map tasks (41-51) on Worker-37, when R5 processes M47 and discovers a failure, its countermeasures are as follows:</p><ul><li><strong>Transmission Interruption Handling</strong>: If R5&#x27;s connection is interrupted while pulling data from Worker-37, it triggers exception handling</li></ul><pre><code class="lang-java">// Simplified pseudo-code
try {
    fetchMapOutput(worker37, mapTaskId47);
} catch (FetchFailureException e) {
    // Wait for Master notification of new data location
    waitForNotification(mapTaskId47);
    // Retry with new data location
    fetchMapOutput(worker51, mapTaskId47);
}
</code></pre>
<ul><li><strong>Data Consistency</strong>: R5 discards incomplete data partially pulled from Worker-37</li></ul><pre><code class="lang-java">if (partialData &amp;&amp; dataSource != currentSourceForTask) {
    discardPartialData();
    fetchFromNewSource();
}
</code></pre>
<ul><li><strong>Notification Mechanism</strong>: Master notifies Reduce tasks through the following ways</li></ul><pre><code class="lang-shell">1. Heartbeat responses include updated Map output location information
2. RPC calls notify state changes
3. Reduce tasks periodically poll Master for latest mapping information
</code></pre>
<h4 id="master-failure">Master Failure</h4><p>Google&#x27;s paper handles Master failure relatively simply: saving state through checkpoint mechanisms, but terminating the entire job when Master actually fails.</p><p>This design is based on two considerations:</p><ol start="1"><li><strong>Single Point Characteristic</strong>: There&#x27;s typically only one Master node in the system, with relatively low failure probability</li><li><strong>Simplified Design</strong>: Simple failure handling mechanisms reduce system complexity</li></ol><p>However, in critical production environments, this simple handling approach obviously cannot meet high availability requirements.</p><p>With the development of distributed systems, more robust Master failure handling mechanisms should be seriously considered.</p><h4 id="semantics-in-the-presence-of-failures">Semantics in the Presence of Failures</h4><p>MapReduce provides a key promise: <strong>In deterministic operations, the results of distributed parallel execution are completely consistent with sequential execution</strong>.</p><p>This feature greatly simplifies the complexity of distributed programs.</p><p>How is this &quot;computational result consistency&quot; achieved?</p><p><strong>Key Mechanism: Atomic Commits</strong></p><p>MapReduce achieves result consistency through carefully designed atomic commit mechanisms:</p><ol start="1"><li>Temporary file strategy</li><li>Task completion flow</li><li>Redundant execution handling</li></ol><p><strong>Core Role of Atomic Operations</strong></p><p>Atomic operations are the foundation of the entire fault tolerance mechanism, mainly reflected in two levels:</p><ol start="1"><li>Master data structure updates</li><li>File system atomic renaming - for example, atomic renaming operations when Reduce tasks complete; only one execution instance can successfully rename, meaning if there&#x27;s a successfully named file, it indicates one instance has been successfully executed</li></ol><h3 id="locality">Locality</h3><p>A commonly used principle in computer science is called <strong>locality of reference</strong> (specifically spatial locality), which states that when a program executes sequentially, after accessing a block of data, it&#x27;s highly likely to access data physically adjacent to that data next. This simple assertion is the foundation for all cache effectiveness, leading to the formation of a storage hierarchy system from slow to fast, cheap to expensive, large to small (hard disk → memory → cache → registers).</p><p>In distributed environments, this hierarchy system requires at least one more layer — network I/O. This is also the first sentence in the paper: &quot;Network bandwidth is a relatively scarce resource in our computing environment.&quot;</p><p>In MapReduce systems, we also fully utilize input data locality. However, this time, instead of loading data over, we <strong>schedule</strong> the program to go there (<strong>Moving Computation is Cheaper than Moving Data</strong>). If input exists on GFS, it manifests as a series of logical blocks, each of which may have several (typically three) physical replicas. For each logical block of input, we can run Map Tasks on machines where one of its physical replicas resides (if it fails, try another replica), thereby minimizing network data transmission and reducing latency while saving bandwidth.</p><blockquote><p>Network bandwidth is a relatively scarce resource in our computing environment. We conserve network bandwidth by taking advantage of the fact that the input data (managed by GFS) is stored on the local disks of the machines that make up our cluster. GFS divides each file into 64 MB blocks, and stores several copies of each block (typically 3 copies) on different machines. The MapReduce master takes the location information of the input files into account and attempts to schedule a map task on a machine that contains a replica of the corresponding input data. Failing that, it attempts to schedule a map task near a replica of that task&#x27;s input data (e.g., on a worker machine that is on the same network switch as the machine containing the data). When running large MapReduce operations on a significant fraction of the workers in a cluster, most input data is read locally and consumes no network bandwidth.</p></blockquote>
<h3 id="task-granularity">Task Granularity</h3><p>Deep analysis of the core design principles, influencing factors, and best practices of Task Granularity in the MapReduce framework.</p><p>The paper has an important viewpoint: &quot;<strong>M and R should be much larger than the number of worker machines</strong>.&quot;</p><h4 id="m-and-r-should-be-much-larger-than-worker-machine-count">M and R Should Be Much Larger Than Worker Machine Count</h4><ol start="1"><li><p><strong>Dynamic Load Balancing</strong></p><pre><code class="lang-shell">// Simplified load balancing scenario
100 Map tasks, 10 machines
- Machines 1-9: Each processes 10 tasks, uniform load
- Machine 10: Slower hardware, only completes 5 tasks
- Task allocator automatically redistributes remaining 5 tasks to machines that completed tasks
</code></pre>
<p>When each machine processes multiple small tasks rather than single large tasks, fast machines can process more tasks while slow machines process fewer, naturally forming performance-based work distribution.</p></li><li><p><strong>Accelerated Failure Recovery</strong></p><pre><code>Assumption:
- 2000 machines, each executing about 100 Map tasks
- Single Worker-37 fails, has completed 92 Map tasks

Impact:
- Traditional design (1 large task per machine): Lose entire Worker-37 computation results
- Fine-grained design: Only need to re-execute 92 small tasks, distributed to other 1999 machines
- Recovery speed: About 1/20 of traditional method (average less than 1 additional task per machine)
</code></pre>
<p>When a worker fails, its completed multiple small tasks can be quickly redistributed to other machines in the cluster for re-execution, significantly accelerating recovery speed.</p></li></ol><h3 id="backup-tasks">Backup Tasks</h3><p>This mechanism was designed by Google to solve the &quot;straggler&quot; problem.</p><blockquote><p>One of the common causes that lengthens the total time taken for a MapReduce operation is a &quot;straggler&quot;: a machine that takes an unusually long time to complete one of the last few map or reduce tasks in the computation.</p></blockquote>
<p>An important optimization introduced to solve slow task problems.</p><h4 id="straggler-problem-key-challenge-in-distributed-systems">Straggler Problem: Key Challenge in Distributed Systems</h4><p>&quot;Stragglers&quot; refer to machines that complete tasks abnormally slowly, severely slowing down the completion time of entire MapReduce jobs. This problem is particularly prominent in large-scale distributed environments.</p><h5 id="main-causes">Main Causes</h5><ol start="1"><li><p><strong>Hardware Issues</strong>:</p><pre><code>- Disk errors: Correctable errors reduce read speed from 30MB/s to 1MB/s
- Network problems: Network card failures cause bandwidth reduction
- CPU or memory failures: Significantly reduced processing capability
</code></pre>
</li><li><p><strong>Resource Competition</strong>:</p><pre><code>- Multi-task scheduling conflicts: Other jobs occupy CPU, memory resources
- I/O contention: Multiple processes compete for disk or network I/O
- Memory pressure: Insufficient memory causes frequent page swapping
</code></pre>
</li><li><p><strong>Software Issues</strong>:</p><pre><code>- Configuration errors: Such as Google&#x27;s processor cache disabled bug (100x performance degradation)
- GC pauses: Long pauses caused by garbage collection
- System updates: Background services or updates consuming resources
</code></pre>
</li></ol><h5 id="impact-of-stragglers">Impact of Stragglers</h5><p>In MapReduce jobs, job completion time is limited by the last completing task. When 99% of tasks complete quickly but a few tasks are abnormally slow, the entire job&#x27;s completion time will be dominated by these slow tasks.</p><p>Google designed the backup task mechanism as a simple yet effective strategy, using moderate resource redundancy to reduce overall execution time.</p><h2 id="refinements">Refinements</h2><p>The paper provides several extensions beyond the basic Mapper and Reducer primitives that have become recognized as standard components: Partitioner, Combiner, and Reader/Writer.</p><h3 id="partitioning-function">Partitioning Function</h3><p>The Partitioning Function in MapReduce is an important extension mechanism connecting the Map and Reduce phases.</p><h4 id="core-concept">Core Concept</h4><p>The partitioning function is a key component in MapReduce that connects the Map and Reduce phases, determining which intermediate key-value pairs are sent to which Reduce task.</p><pre><code class="lang-go">// Basic definition of partitioning function
type PartitionFunc func(key interface{}, numPartitions int) int
</code></pre>
<p>Core functions:</p><ul><li>Determines which Reduce task processes Map output intermediate key-value pairs, ultimately affecting output file organization</li><li>Controls the number and content organization of final output files</li><li>Affects data distribution and load balancing across the cluster</li></ul><h4 id="default-hash-partitioning-mechanism">Default Hash Partitioning Mechanism</h4><p>MapReduce provides a simple and efficient default partitioning strategy:</p><pre><code class="lang-java">// Default hash partitioning implementation
public int getPartition(K key, V value, int numReduceTasks) {
    return (key.hashCode() &amp; Integer.MAX_VALUE) % numReduceTasks;
}
</code></pre>
<p>Characteristics:</p><ul><li><strong>Simple and efficient</strong>: Low computational overhead, suitable for most scenarios</li><li><strong>Relatively balanced</strong>: Uses hash function to evenly distribute keys across partitions</li><li><strong>Deterministic</strong>: Same keys always map to the same partition, ensuring correct aggregation</li></ul><p>Data flow diagram:</p><pre><code class="lang-shell">    Map Output          Partition Function       Reduce Tasks
 [K1:V1, K2:V2]       hash(K) % R        -&gt;    Reduce-0
 [K3:V3, K4:V4]                          -&gt;    Reduce-1
 [K5:V5, K6:V6]                          -&gt;    Reduce-2
       ...                                       ...
</code></pre>
<h4 id="custom-partitioning-function-use-cases">Custom Partitioning Function Use Cases</h4><p>The paper points out that certain scenarios require specific partitioning logic, such as when processing URL data where you want all URLs from the same host to go into the same output file.</p><h3 id="combiner-function">Combiner Function</h3><h4 id="core-concept-and-working-principle">Core Concept and Working Principle</h4><p>Combiner is a key optimization component in the MapReduce framework that <strong>performs partial data aggregation on the Map side</strong>, reducing network transmission volume.</p><pre><code>Workflow:
Map output → Combiner local aggregation → Network transmission → Reducer final aggregation
</code></pre>
<p>Applicable conditions:</p><ul><li>Map output intermediate keys have <strong>lots of duplicates</strong></li><li>Reduce function has <strong>commutativity and associativity</strong> (such as sum, max)</li></ul><h4 id="performance-advantage-example">Performance Advantage Example</h4><p>Using word count as an example:</p><pre><code>Without Combiner:
Map1 output: &lt;&quot;hello&quot;,1&gt;, &lt;&quot;world&quot;,1&gt;, &lt;&quot;hello&quot;,1&gt;, &lt;&quot;hello&quot;,1&gt; // 4 records transmitted
Map2 output: &lt;&quot;hello&quot;,1&gt;, &lt;&quot;hadoop&quot;,1&gt;, &lt;&quot;hello&quot;,1&gt; // 3 records transmitted
Total network transmission: 7 records

With Combiner:
Map1 output: &lt;&quot;hello&quot;,3&gt;, &lt;&quot;world&quot;,1&gt; // 2 records transmitted
Map2 output: &lt;&quot;hello&quot;,2&gt;, &lt;&quot;hadoop&quot;,1&gt; // 2 records transmitted
Total network transmission: 4 records (43% reduction)
</code></pre>
<p>For data following Zipf distribution (like word frequency), Combiner can significantly reduce network transmission and improve performance.</p><h4 id="difference-from-reduce">Difference from Reduce</h4><ul><li>Reducer output writes to final result files</li><li>Combiner output writes to intermediate files, then transmitted to Reducer</li></ul><p>Combiner is an important optimization in the MapReduce framework for improving data processing efficiency through &quot;pre-aggregation,&quot; significantly reducing data transmission and processing time, especially effective for aggregation operations.</p><h3 id="input-and-output-types">Input and Output Types</h3><p>Support for different input data formats:</p><pre><code>1. TextInputFormat (default)
   - Each line as one record
   - Key: line offset (LongWritable)
   - Value: line content (Text)
   - Smart splitting: ensures splitting at line boundaries

2. KeyValueTextInputFormat
   - Split each line into key-value by delimiter (default Tab)
   - Suitable for: simple structured text data

3. SequenceFileInputFormat
   - Read binary sequence files (key-value pairs)
   - Support compression, efficient random access
   - Commonly used for passing data between MapReduce jobs

4. DBInputFormat
   - Read records from relational databases
   - Support SQL queries as data source
</code></pre>
<p>This demonstrates the <strong>flexibility and extensibility</strong> of the framework.</p><p>MapReduce&#x27;s input/output interface design enables it to handle diverse data sources:</p><pre><code>- HDFS files
- Local file systems
- S3, Azure Blob and other cloud storage
- HBase, MongoDB and other NoSQL databases
- Kafka streaming data
</code></pre>
<p>By implementing appropriate InputFormat/OutputFormat, developers can integrate MapReduce with almost any data source/target, demonstrating the framework&#x27;s powerful extensibility and making it suitable for various big data processing scenarios.</p><h2 id="performance">Performance</h2><p>The paper mentions two performance tests representing MapReduce framework&#x27;s capability to handle two typical big data processing scenarios:</p><p><strong>Test Scenario Analysis</strong></p><ol start="1"><li><p><strong>Pattern Search Test</strong>: Search for specific patterns in about 1TB of data</p><ul><li>Represents the &quot;extract small amounts of valuable information from large datasets&quot; computation pattern</li><li>Typical applications include log analysis, anomaly detection, specific record finding, etc.</li></ul></li><li><p><strong>Large Data Sorting Test</strong>: Sort about 1TB of data</p><ul><li>Represents the &quot;transform data from one representation to another&quot; computation pattern</li><li>Typical applications include ETL processes, data preprocessing, data reorganization, etc.</li></ul></li></ol><p>Below, the entire MapReduce distributed framework will be analyzed from five angles:</p><ol start="1"><li>Cluster Configuration</li><li>Grep</li><li>Sort</li><li>Effect of Backup Tasks</li><li>Machine Failures</li></ol><h3 id="cluster-configuration-analysis">Cluster Configuration Analysis</h3><p>First, a cluster configuration is given:</p><pre><code class="lang-go">// Cluster configuration overview
type ClusterConfig struct {
    Nodes           int     // About 1800 machines
    CpuPerNode      int     // 2 × 2GHz Intel Xeon per node (with hyperthreading)
    MemoryPerNode   string  // 4GB per node (actual usable 2.5-3GB)
    DisksPerNode    int     // 2 × 160GB IDE disks per node
    NetworkBandwidth string  // Gigabit Ethernet
    NetworkTopology string   // Two-level tree switching network
    RootBandwidth   string   // 100-200Gbps aggregate bandwidth
    Latency         string   // &lt;1ms latency between nodes
}
</code></pre>
<p><strong>Analysis</strong>:</p><ul><li>This is a balanced cluster design for both computation and I/O, particularly suitable for MapReduce&#x27;s divide-and-conquer model</li><li>Storage-wise, each node has over 300GB total storage space, providing sufficient local storage for TB-level data processing</li><li>Network-wise, tree topology is simple but may form bottlenecks during shuffle phase</li><li>Low inter-node latency (&lt;1ms) is extremely beneficial for data transmission in the reduce phase</li><li>Cluster scale (1800 nodes) enables effective parallelization when processing TB-level data</li></ul><h3 id="grep">Grep</h3><p><img alt="Figure2 Data Transfer Rate Over Time" src="Figure2-Data-transfer-rate-over-time.png"/></p><p>This is a typical &quot;extract small amounts of information from massive data&quot; scenario:</p><pre><code class="lang-go">// Grep task configuration
type GrepJobConfig struct {
    InputSize       string  // About 1TB (10^10 100-byte records)
    Pattern         string  // Three-character pattern (matches 92,337 records)
    InputSplits     int     // M=15000 (about 64MB per block)
    ReduceTasks     int     // R=1 (single output file)
    PeakScanRate    string  // &gt;30GB/s (with 1764 workers)
    TotalTime       int     // About 150 seconds (including 60 seconds startup overhead)
}
</code></pre>
<p><strong>Performance Analysis</strong>:</p><ol start="1"><li><strong>Scalability Performance</strong>: From Figure 2, as worker count increases, scan rate linearly improves to 30GB/s, showing excellent horizontal scaling capability for map-intensive tasks</li><li><strong>I/O Bound Characteristics</strong>: Grep is essentially I/O intensive work; the test achieving 30GB/s throughput approaches the theoretical upper limit of total disk I/O for 1764 nodes</li><li><strong>Optimization Opportunities</strong>: About 60 seconds startup overhead (40% of total time) shows an optimization point - GFS metadata operations and task distribution can be further optimized</li><li><strong>R=1 Design</strong>: Single reduce design suits this &quot;filtering&quot; scenario, but also means final result collection could become a bottleneck (not manifested in this example due to small data volume)</li></ol><h3 id="sort">Sort</h3><p><img alt="Figure3 Data Transfer Rates Over Time for Different Executions" src="Figure3-Data-transfer-rates-over-time-for-diff-exec-of-sort-program.png"/></p><p>Comprehensive test of the entire MapReduce framework capability:</p><pre><code class="lang-golang">// Sort task characteristic analysis
type SortJobAnalysis struct {
    InputSize       string  // About 1TB (10^10 100-byte records)
    InputRate       string  // Peak 13GB/s (lower than Grep due to writing intermediate data)
    ShufflePattern  string  // Two-phase pattern, related to reduce task batching
    OutputRate      string  // 2-4GB/s (dual replica writes, actual physical writes 4-8GB/s)
    MapTasks        int     // M=15000 (about 64MB per block)
    ReduceTasks     int     // R=4000 (partitioning strategy leverages key distribution knowledge)
    TotalTime       int     // 891 seconds (close to TeraSort benchmark 1057 seconds)
}
</code></pre>
<p><strong>Technical Analysis</strong>:</p><ol start="1"><li><strong>Data Pipeline</strong>: Test clearly shows MapReduce three-phase pipeline - map phase (0-200 seconds), shuffle phase (200-600 seconds), and reduce phase (600-850 seconds)</li><li><strong>Resource Bottleneck Shifting</strong>:
<ul><li>0-200 seconds: Bottleneck in disk I/O and CPU (parsing data)</li><li>200-600 seconds: Bottleneck shifts to network bandwidth (shuffle)</li><li>600-850 seconds: Bottleneck in sorting computation and output disk I/O</li></ul></li><li><strong>Locality Optimization Effect</strong>: Input rate (13GB/s) higher than shuffle rate mainly due to data locality optimization, with most reads going through local disk rather than network</li><li><strong>Replication Overhead</strong>: Output rate (2-4GB/s) relatively low mainly due to GFS dual replica strategy, actual physical writes are twice this rate</li></ol><h4 id="effect-of-backup-tasks">Effect of Backup Tasks</h4><p>Figure 3 also shows:</p><pre><code class="lang-go">// Backup task impact analysis
type BackupTaskAnalysis struct {
    WithBackup      int     // Normal execution, total time 891 seconds
    WithoutBackup   int     // 1283 seconds, 44% increase
    StragglerDelay  int     // Last 5 reduce tasks took additional 300 seconds
    EfficiencyGain  string  // Backup task mechanism improves performance by 44%
}
</code></pre>
<p><strong>Professional Interpretation</strong>:</p><ol start="1"><li><strong>Severity of Straggler Problem</strong>: Data clearly demonstrates the severity of the &quot;straggler problem&quot; in distributed systems - just 5 slow tasks increased total time by 44%</li><li><strong>Root Cause Analysis</strong>: Stragglers typically stem from:
<ul><li>Hardware anomalies (like disk performance degradation, memory errors)</li><li>Resource competition (like interference from other processes)</li><li>Data skew (some reduce tasks process significantly more data than others)</li></ul></li><li><strong>Cloud-Native Environment Significance</strong>: In shared resource cloud environments, straggler problems are more prevalent; backup task mechanisms are key to ensuring performance predictability</li></ol><h4 id="machine-failures">Machine Failures</h4><p>Figure 3 also shows:</p><pre><code class="lang-go">// Fault tolerance capability analysis
type FaultToleranceAnalysis struct {
    NodesKilled     int     // 200 nodes (about 11.5% of nodes)
    RecoveryPattern string  // Brief negative input rate, then quick recovery
    TotalTime       int     // 933 seconds, only 5% increase
    KeyMechanism    string  // Automatically detect failures and re-execute tasks
}
</code></pre>
<p><strong>Analysis</strong>:</p><ol start="1"><li><strong>Failure Impact Visualization</strong>: Negative input rate in the graph intuitively shows how node failures cause loss of completed work and re-execution requirements</li><li><strong>Quick Recovery Principle</strong>:
<ul><li>Task state tracking: master node continuously tracks each task&#x27;s state</li><li>Heartbeat detection: detect worker failures through periodic heartbeats</li><li>Task rescheduling: reassign failed node tasks to healthy nodes</li><li>Redundant execution: key is MapReduce design allowing any node to handle any task</li></ul></li><li><strong>Comparison with Traditional Systems</strong>: Traditional MPP databases typically fail completely or have 50%+ performance degradation with 11% node failure; MapReduce&#x27;s 5% performance loss highlights its excellent fault tolerance capability</li></ol><h2 id="experience">Experience</h2><p>This content is excerpted from Jeff Dean and Sanjay Ghemawat&#x27;s MapReduce paper, detailing MapReduce&#x27;s early development history and application within Google.</p><p><img alt="Table1 MapReduce Jobs Run in August 2004" src="Table1-MapReduce-jobs-run-in-august-2004.png"/></p><h3 id="technical-development-history">Technical Development History</h3><p>The first version of the MapReduce library was developed in February 2003, with major enhancements in August of the same year, including:</p><ul><li>Locality optimization</li><li>Dynamic load balancing of task execution across worker nodes</li><li>Other performance optimizations</li></ul><h3 id="wide-application-scope">Wide Application Scope</h3><p>MapReduce gained widespread application within Google, covering multiple domains:</p><ol start="1"><li>Large-scale machine learning problems</li><li>Clustering problems for Google News and Froogle (early Google Shopping) products</li><li>Popular query report data extraction (such as Google Zeitgeist)</li><li>Attribute extraction from large-scale web corpora (such as geographical locations for localized search)</li><li>Large-scale graph computations</li></ol><h3 id="explosive-growth">Explosive Growth</h3><p>From the graph, MapReduce usage within Google showed exponential growth:</p><ul><li>Early 2003: Near 0 instances</li><li>End of September 2004: Nearly 900 instances</li></ul><p>This rapid growth indicates MapReduce gained extremely high recognition and application value within Google.</p><h3 id="success-factor-analysis">Success Factor Analysis</h3><p>Key factors for MapReduce success:</p><ol start="1"><li><strong>Simplified Distributed Computing</strong>: Enables developers to write simple programs that run efficiently on thousands of machines</li><li><strong>Accelerated Development Cycles</strong>: Significantly shortened development and prototyping cycles</li><li><strong>Lowered Technical Barriers</strong>: Allows programmers without distributed/parallel systems experience to easily leverage large-scale computing resources</li></ol><h3 id="scale-and-efficiency-analysis-august-2004-data">Scale and Efficiency Analysis (August 2004 Data)</h3><p>From table data, the following insights emerge:</p><ol start="1"><li><strong>Wide Usage</strong>: Executed 29,423 MapReduce jobs in a single month</li><li><strong>High Processing Efficiency</strong>: Average job completion time was 634 seconds (about 10.5 minutes)</li><li><strong>Large Computation Scale</strong>:
<ul><li>Used equivalent of 79,186 machine-days of computation time</li><li>Processed 3,288 TB of input data</li><li>Generated 758 TB of intermediate data</li><li>Output 193 TB of results</li></ul></li><li><strong>Task Distribution Characteristics</strong>:
<ul><li>Each job used average of 157 worker machines</li><li>Average 1.2 worker node failures per job (indicating good fault tolerance)</li><li>Average 3,351 map tasks and 55 reduce tasks per job</li></ul></li><li><strong>Code Reusability</strong>:
<ul><li>395 unique map implementations</li><li>269 unique reduce implementations</li><li>426 unique map/reduce combinations</li></ul></li></ol><h2 id="conclusions">Conclusions</h2><p>The paper&#x27;s conclusions explain why this paper is so famous:</p><h3 id="three-key-success-factors-of-mapreduce">Three Key Success Factors of MapReduce</h3><h4 id="1-simple-and-easy-to-use-programming-model">1. Simple and Easy-to-Use Programming Model</h4><p>MapReduce&#x27;s primary success factor is its concise programming interface:</p><pre><code class="lang-go">// Users only need to define these two functions, without worrying about distributed system complexity
func Map(key, value string) []KeyValue { /* User-defined mapping logic */ }
func Reduce(key string, values []string) string { /* User-defined reduction logic */ }
</code></pre>
<p>This design is extremely developer-friendly because it:</p><ul><li>Hides the complex details of parallelization</li><li>Automatically handles fault tolerance mechanisms</li><li>Built-in locality optimization</li><li>Provides transparent load balancing</li></ul><p>This enables even programmers without distributed systems experience to easily write efficient distributed programs.</p><h4 id="2-powerful-expressiveness">2. Powerful Expressiveness</h4><p>The MapReduce model can easily express various types of computational problems, widely applied within Google for:</p><ul><li>Web search service data generation</li><li>Large-scale sorting</li><li>Data mining</li><li>Machine learning</li><li>Many other systems</li></ul><p>This versatility makes MapReduce a fundamental computing framework within Google.</p><h4 id="3-excellent-scalability">3. Excellent Scalability</h4><p>MapReduce implementation can scale to large clusters containing thousands of machines:</p><pre><code class="lang-go">// Pseudo-code: MapReduce scheduling process
func Schedule(input []string, mappers int, reducers int) Result {
    // Automatically handles:
    // 1. Task allocation and parallelization
    // 2. Machine failure detection and recovery
    // 3. Data locality optimization
    // 4. Intermediate result management
}
</code></pre>
<p>This enables efficient handling of large-scale computational problems encountered by Google, laying the foundation for big data processing.</p><h3 id="three-key-insights-from-research-team">Three Key Insights from Research Team</h3><h4 id="1-value-of-restricted-programming-models">1. Value of Restricted Programming Models</h4><p>Research shows that by consciously restricting the programming model, enormous system advantages can be gained:</p><ul><li>Easy parallelization and distributed computing</li><li>Natural implementation of fault tolerance mechanisms</li><li>Reduced development and maintenance costs</li></ul><p>This &quot;less is more&quot; philosophy contrasts sharply with other systems attempting to provide completely general parallel programming environments.</p><h4 id="2-network-bandwidth-is-a-scarce-resource">2. Network Bandwidth is a Scarce Resource</h4><p>The research team found network bandwidth is a precious resource in distributed systems, so many optimizations target reducing network transmission:</p><ul><li><strong>Locality optimization</strong>: Prioritize reading data from local disks, reducing cross-network data transmission</li><li><strong>Local intermediate data storage</strong>: Write intermediate results to local disks rather than distributed storage, saving network bandwidth</li></ul><p>These designs are particularly important in large-scale clusters, where data transmission can become system bottlenecks.</p><h4 id="3-importance-of-redundant-execution">3. Importance of Redundant Execution</h4><p>Redundant execution is a key innovation in MapReduce, used for:</p><ul><li>Reducing impact of slow machines (stragglers)</li><li>Gracefully handling machine failures</li><li>Preventing data loss</li></ul><pre><code class="lang-go">// Pseudo-code: Redundant task scheduling in MapReduce
func scheduleBackupTasks(slowTasks []Task) {
    for _, task := range slowTasks {
        if time.Now() - task.StartTime &gt; slowThreshold {
            // Launch backup copy of same task on another machine
            launchDuplicateTask(task)
        }
    }
}
</code></pre>
<p>This mechanism significantly improves reliability and performance consistency of large distributed systems.</p><h3 id="technical-legacy-of-mapreduce">Technical Legacy of MapReduce</h3><p>The paper&#x27;s conclusions reveal that MapReduce is not just a technical innovation, but a new paradigm for large-scale data processing:</p><ol start="1"><li><strong>Architectural Influence</strong>: MapReduce design philosophy influenced later big data processing frameworks like Hadoop, Spark</li><li><strong>Programming Model Innovation</strong>: Proved that simplified programming models can solve complex distributed computing problems</li><li><strong>Engineering Practice Revolution</strong>: Changed methods for building large-scale data processing systems, from expert systems to general frameworks</li><li><strong>Commercial Value Creation</strong>: Laid foundation for later big data ecosystems, creating enormous commercial value</li></ol><p>In summary, MapReduce successfully solved core challenges of large-scale distributed data processing through simple yet powerful abstractions, making massive data processing accessible. This is why it achieved tremendous success both within Google and across the industry.</p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>Distributed System</category>
            <category>MapReduce</category>
        </item>
        <item>
            <title><![CDATA[Gateway Architecture Design for High-Concurrency API Management Scenarios]]></title>
            <link>https://blog.cheverjohn.me/gateway-high-concurrency-api-management</link>
            <guid>https://blog.cheverjohn.me/gateway-high-concurrency-api-management</guid>
            <pubDate>Thu, 27 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Overall Architectural Design Principles In high-concurrency API management scenarios, gateway architecture must adhere to the following core design principles: 1. Layered Architecture Design Adopt a strictly layered architecture with separation of concerns at each layer, making the system more scala...]]></description>
            <content:encoded><![CDATA[<article><div><h2 id="overall-architectural-design-principles">Overall Architectural Design Principles</h2><p>In high-concurrency API management scenarios, gateway architecture must adhere to the following core design principles:</p><ol start="1"><li><p><strong>Layered Architecture Design</strong></p><pre><code class="lang-shell">Traffic Ingress Layer → Processing Layer → Routing Layer → Backend Service Layer
</code></pre>
<p>Adopt a strictly layered architecture with separation of concerns at each layer, making the system more scalable horizontally and optimizable vertically.</p></li><li><p><strong>Stateless Design</strong></p><p>Design stateless gateway nodes to ensure any gateway instance can handle any request - this is fundamental to supporting high concurrency. Session state and user information should be stored in distributed caches or dedicated state storage systems.</p></li></ol><h2 id="high-performance-technology-stack-selection">High-Performance Technology Stack Selection</h2><ol start="1"><li><p><strong>Core Technology Selection</strong></p><ol start="1"><li><strong>Data Plane</strong>: Based on high-performance proxies like Envoy, NGINX, or custom components built with Go/Rust</li><li><strong>Control Plane</strong>: Employ efficient configuration management and service discovery mechanisms</li></ol></li><li><p><strong>Asynchronous I/O Model</strong></p><ol start="1"><li>Adopt non-blocking I/O models (such as Go&#x27;s goroutine+channel, Rust&#x27;s tokio, Node.js event loop)</li><li>Avoid traditional thread pool models to reduce context switching overhead</li></ol></li></ol><h2 id="multi-level-caching-architecture">Multi-Level Caching Architecture</h2><ol start="1"><li><p><strong>Global Distributed Cache Layer</strong></p><pre><code class="lang-shell">Client → CDN → Edge Cache → API Gateway Local Cache → Service Cache
</code></pre>
</li><li><p><strong>Multi-Dimensional Caching Strategy</strong></p><ol start="1"><li><strong>Routing Information Cache</strong>: Local high-speed cache with periodic updates</li><li><strong>Authentication Information Cache</strong>: Distributed token validation result caching</li><li><strong>Response Data Cache</strong>: Intelligent caching strategy based on content characteristics</li></ol></li></ol><h2 id="dynamic-scaling-design">Dynamic Scaling Design</h2><ol start="1"><li><p><strong>Flexible Deployment Architecture</strong></p><pre><code class="lang-shell">Multi-Region → Multi-AZ → Multi-Cluster → Multi-Instance
</code></pre>
</li><li><p><strong>Elastic Scaling Strategy</strong></p><ol start="1"><li><strong>Predictive Scaling</strong>: Predict scaling needs based on historical traffic patterns</li><li><strong>Reactive Scaling</strong>: Trigger scaling based on real-time metrics (CPU, memory, request queue depth)</li><li><strong>Graceful Scale-Down</strong>: Ensure graceful connection closure and request completion processing</li></ol></li></ol><h2 id="efficient-traffic-control-mechanisms">Efficient Traffic Control Mechanisms</h2><ol start="1"><li><p><strong>Multi-Level Rate Limiting Design</strong></p><pre><code class="lang-mermaid">flowchart LR
    Client --&gt; GlobalLimit[&quot;Global Rate Limiting Layer&quot;]
    GlobalLimit --&gt; ServiceLimit[&quot;Service-Level Rate Limiting&quot;]
    ServiceLimit --&gt; APILimit[&quot;API-Level Rate Limiting&quot;]
    APILimit --&gt; UserLimit[&quot;User-Level Rate Limiting&quot;]
    UserLimit --&gt; Backend[&quot;Backend Services&quot;]
</code></pre>
</li><li><p><strong>Adaptive Flow Control Algorithms</strong></p><ol start="1"><li><strong>Token Bucket + Leaky Bucket Combination</strong>: Balance burst traffic and steady traffic</li><li><strong>Priority-Based Differential Processing</strong>: Prioritize core API protection</li><li><strong>Adaptive Rate Limiting</strong>: Dynamically adjust rate limiting thresholds based on backend service health</li></ol></li></ol><h2 id="gateway-cluster-high-availability-design">Gateway Cluster High Availability Design</h2><ol start="1"><li><p><strong>Multi-Region Deployment Architecture</strong></p><ol start="1"><li><strong>Geographic-Level Redundancy</strong>: Cross-region deployment ensures regional-level fault isolation</li><li><strong>Proximity Access</strong>: Intelligent DNS or global load balancing for traffic proximity routing</li></ol></li><li><p><strong>Fault Isolation Strategy</strong></p><pre><code class="lang-shell">Client Grouping → Gateway Instance Grouping → Backend Service Grouping
</code></pre>
<ol start="1"><li><strong>Bulkhead Pattern</strong>: Isolate client requests to different gateway instance groups</li><li><strong>Circuit Breaker Mechanism</strong>: Intelligent circuit breaker design based on multi-dimensional metrics like error rates and latency</li><li><strong>Degradation Strategy</strong>: Define clear service degradation paths and fallback mechanisms</li></ol></li></ol><h2 id="request-processing-optimization">Request Processing Optimization</h2><ol start="1"><li><p><strong>Request Processing Pipeline</strong></p><pre><code class="lang-shell">Request Reception → Authentication &amp; Authorization → Request Transformation → 
Routing Decision → Load Balancing → Backend Invocation → Response Processing
</code></pre>
</li><li><p><strong>Performance Optimization Techniques</strong></p><ol start="1"><li><strong>Batch Processing</strong>: Merge fragmented requests to reduce network round trips</li><li><strong>Request Collapsing</strong>: Merge concurrent requests for the same resource</li><li><strong>Parallel Processing</strong>: Parallelize cross-service request processing</li><li><strong>Streaming Response Processing</strong>: Stream transmission for large responses</li><li><strong>Zero-Copy Technology</strong>: Reduce data copying stages</li></ol></li></ol><h2 id="efficient-communication-protocols">Efficient Communication Protocols</h2><ol start="1"><li><p><strong>Protocol Support and Optimization</strong></p><ol start="1"><li><strong>HTTP/2 Multiplexing</strong>: Reduce connection establishment overhead</li><li><strong>gRPC Support</strong>: Efficient binary transmission and stream processing</li><li><strong>WebSocket Optimization</strong>: Long connection management and heartbeat mechanisms</li></ol></li><li><p><strong>Connection Pool Management</strong></p><ol start="1"><li>Dynamically adjustable backend connection pools</li><li>Long connection reuse and keep-alive strategies</li><li>Connection warm-up mechanisms to avoid cold start latency</li></ol></li></ol><h2 id="end-to-end-observability">End-to-End Observability</h2><ol start="1"><li><p><strong>Multi-Dimensional Monitoring System</strong></p><pre><code class="lang-shell">Infrastructure Metrics → Gateway Performance Metrics → API Call Metrics → Business Metrics
</code></pre>
</li><li><p><strong>Real-Time Monitoring and Alerting</strong></p><ol start="1"><li><strong>Health Checks</strong>: Combination of active and passive health detection</li><li><strong>Performance Analysis</strong>: Key metrics like request latency distribution and queue depth</li><li><strong>Anomaly Detection</strong>: Machine learning-based abnormal behavior identification</li></ol></li></ol><h2 id="dynamic-configuration-update-mechanism">Dynamic Configuration Update Mechanism</h2><ol start="1"><li><p><strong>Dynamic Configuration Architecture</strong></p><ol start="1"><li>Distributed configuration center + local cache</li><li>Configuration change event notification mechanism</li><li>Incremental configuration updates to reduce resource consumption</li></ol></li><li><p><strong>Canary Release Capabilities</strong></p><ol start="1"><li>Canary deployment for configuration changes</li><li>Smooth traffic migration switching</li><li>Emergency rollback mechanisms</li></ol></li></ol></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>Gateway</category>
            <category>Architecture</category>
        </item>
        <item>
            <title><![CDATA[Challenges in Microservices Architecture: The Redemption Path of API Gateway]]></title>
            <link>https://blog.cheverjohn.me/gateway-api-management-in-multi-scenories</link>
            <guid>https://blog.cheverjohn.me/gateway-api-management-in-multi-scenories</guid>
            <pubDate>Fri, 27 Dec 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[While browsing through technical articles, I came across this insightful piece on microservices architecture, which resonated with our company's actual business scenarios. Challenges at Scale The article discusses how client applications access microservices in a distributed architecture. The main c...]]></description>
            <content:encoded><![CDATA[<article><div><p>While browsing through technical articles, I came across this insightful <a href="https://microservices.io/patterns/apigateway.html">piece</a> on microservices architecture, which resonated with our company&#x27;s actual business scenarios.</p><h2 id="challenges-at-scale">Challenges at Scale</h2><p>The article discusses how client applications access microservices in a distributed architecture. The main challenges include:</p><ol start="1"><li><strong>API Granularity Mismatch</strong>: Microservices typically expose fine-grained APIs, while clients need aggregated data, forcing multiple service interactions</li><li><strong>Client Requirement Diversity</strong>: Different clients (desktop browsers, mobile apps, third-party applications) require different data structures and payloads</li><li><strong>Network Performance Variations</strong>: Mobile networks are typically slower with higher latency compared to internal networks, impacting user experience</li><li><strong>Dynamic Service Instance Changes</strong>: Service instances and their locations (host + port) change dynamically</li><li><strong>Protocol Heterogeneity</strong>: Backend services may use different protocols, some of which are not web-friendly</li></ol><p>I&#x27;ll skip the detailed explanations here - feel free to reach out for deeper technical discussions.</p><h2 id="solution-architecture">Solution Architecture</h2><p>The author proposes implementing the API Gateway pattern as the solution:</p><ol start="1"><li><strong>Unified Entry Point</strong>: Design an API gateway as the single entry point for all clients</li><li><strong>Request Processing Strategies</strong>:
<ul><li>Simple proxy/routing to corresponding services</li><li>Fan-out requests to multiple services with result aggregation</li></ul></li><li><strong>Client-Specific APIs</strong>: Provide customized APIs for different clients rather than one-size-fits-all solutions</li><li><strong>Cross-Cutting Concerns</strong>: Implement authentication, authorization, SSL termination, caching, and other shared functionalities</li></ol><p><strong>The article also introduces a pattern variant: Backends for Frontends (BFF), which involves designing dedicated API gateways for each client type (web applications, mobile apps, third-party applications).</strong></p><h2 id="personal-analysis">Personal Analysis</h2><h3 id="advantages"><strong>Advantages</strong></h3><p><strong>Centralized Management</strong>: All API traffic flows through a single entry point, enabling unified monitoring, tracing, and governance</p><pre><code class="lang-shell">A financial institution reduced security incident response time from hours to minutes 
after implementation, as all suspicious traffic could be quickly identified at the gateway layer.
</code></pre>
<p><strong>Unified Cross-Cutting Concerns</strong>: Authentication, authorization, rate limiting, and other functionalities need to be implemented only once at the gateway layer</p><p><strong>Protocol Translation &amp; Aggregation</strong>: Convert internal protocols to unified external interfaces, reducing client request counts. An e-commerce platform reduced mobile app loading time by 68% by aggregating 7 independent calls into a single API</p><p><strong>Decoupling Implementation</strong>: Clients are decoupled from internal microservice structure, allowing services to evolve independently without affecting clients</p><p><strong>Traffic Control</strong>: Provides intelligent routing, load balancing, and circuit breaker capabilities, enhancing system resilience</p><p>These benefits are well-established in the industry.</p><h3 id="disadvantages"><strong>Disadvantages</strong></h3><p><strong>Potential Single Point of Failure</strong>: All traffic depends on a single component, which could become a system bottleneck if not properly designed - though this depends on the distributed architecture team&#x27;s capabilities</p><p><strong>Increased Latency</strong>: Additional network hops may increase response times (typically 5-10ms)</p><p><strong>Complexity Escalation</strong>: As scale increases, the gateway may become bloated and difficult to maintain</p><blockquote><p>A large enterprise&#x27;s API gateway accumulated over 200 custom processing rules, 
eventually reaching a point where no one dared to modify the codebase.</p></blockquote>
<p><strong>Team Collaboration Challenges</strong>: Gateway changes require cross-team coordination, potentially creating development bottlenecks</p><h3 id="applicable-scenarios">Applicable Scenarios</h3><ol start="1"><li><strong>Small to Medium Microservices Architecture</strong>: Moderate service count (10-50 services) with limited client types</li><li><strong>High-Security Systems</strong>: Financial, healthcare, and other domains requiring unified security policies</li><li><strong>Mixed Protocol Environments</strong>: Internal services using different protocols (REST, gRPC, AMQP, etc.)</li><li><strong>Centralized API Governance</strong>: Enterprise applications requiring centralized API governance strategies</li></ol><h2 id="core-functionality--implementation-effects">Core Functionality &amp; Implementation Effects</h2><p>The author outlines three core functions provided by API gateways:</p><ol start="1"><li><strong>Reverse Proxy/Gateway Routing</strong>: Using Layer 7 routing to redirect HTTP requests</li><li><strong>Request Aggregation</strong>: Aggregating multiple internal microservice requests into single client requests</li><li><strong>Cross-Cutting Concerns &amp; Gateway Offloading</strong>: Centralized implementation of common functionalities</li></ol><h3 id="benefits">Benefits</h3><ol start="1"><li><strong>Client-Microservice Decoupling</strong>: Isolates clients from microservice architecture details</li><li><strong>Optimal API Provision</strong>: Provides customized APIs for various client types</li><li><strong>Reduced Request Count</strong>: Single requests retrieve multi-service data, reducing network overhead</li><li><strong>Simplified Client Logic</strong>: Complex call logic migrates from client to API gateway</li><li><strong>Protocol Translation</strong>: Converts public web-friendly API protocols to internal protocols</li></ol><h3 id="drawbacks">Drawbacks</h3><ol start="1"><li><strong>Increased Complexity</strong>: Introduces a new component requiring development, deployment, and management</li><li><strong>Increased Response Time</strong>: Additional network hops may increase latency (minimal impact for most applications)</li><li><strong>Single Point of Failure Risk</strong>: A single API gateway may become a bottleneck or single point of failure</li></ol><p>These align with my initial assessment.</p><h2 id="production-deployment-strategies">Production Deployment Strategies</h2><pre><code class="lang-shell">┌───────────┐  ┌───────────┐  ┌─────────────┐
│  Web BFF  │  │ Mobile BFF│  │ Third-party │
│           │  │           │  │   BFF       │
└─────┬─────┘  └─────┬─────┘  └─────┬───────┘
      │              │              │
      ▼              ▼              ▼
┌─────────────────────────────────────┐
│        Core API Gateway             │
│ (Auth, Monitoring, Rate Limiting,   │
│      Basic Routing)                 │
└─────────────────────────────────────┘
      │              │              │
      ▼              ▼              ▼
┌─────────┐     ┌───────────┐     ┌───────────┐
│Service  │     │Service    │     │Service    │
│Cluster 1│     │Cluster 2  │     │Cluster 3  │
└─────────┘     └───────────┘     └───────────┘
</code></pre>
<h3 id="scale-considerations">Scale Considerations</h3><ol start="1"><li><strong>Small Applications (5-15 microservices)</strong>:
<ul><li>Recommendation: Single API gateway</li><li>Rationale: Simple and efficient, avoiding over-engineering</li></ul></li><li><strong>Medium Applications (15-50 microservices)</strong>:
<ul><li>Recommendation: Basic API gateway + key BFFs</li><li>Rationale: Balances complexity and client optimization needs</li></ul></li><li><strong>Large Applications (50+ microservices)</strong>:
<ul><li>Recommendation: Complete three-tier architecture (Edge Gateway + BFF Layer + Internal Gateway)</li><li>Rationale: Supports organizational scaling and complex business domains</li></ul></li></ol><h3 id="decision-factors">Decision Factors</h3><p>When selecting API gateway strategies, consider:</p><ol start="1"><li><strong>Organizational Structure</strong>: Conway&#x27;s Law suggests system design often reflects organizational structure</li><li><strong>Scaling Expectations</strong>: Consider business growth and diversification over 3-5 years</li><li><strong>Operational Capabilities</strong>: Assess team capacity for managing multiple gateways</li><li><strong>Consistency Requirements</strong>: Business requirements for API behavior consistency</li><li><strong>Performance Budget</strong>: Evaluate latency impact of additional network hops</li></ol><p><strong>Regardless of the chosen pattern, maintain lightweight gateway layers, avoid excessive business logic drift leading to &quot;smart pipes&quot; anti-patterns, and establish robust monitoring systems to ensure gateway layer performance and stability.</strong></p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>Gateway</category>
            <category>Architecture</category>
        </item>
        <item>
            <title><![CDATA[Gateway Performance Bottleneck Analysis and Optimization Techniques (Private Collection)]]></title>
            <link>https://blog.cheverjohn.me/performance-bottleneck-analysis-and-optimization-tech</link>
            <guid>https://blog.cheverjohn.me/performance-bottleneck-analysis-and-optimization-tech</guid>
            <pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[2025-03-07 Update: Recently, Xiaohongshu's gateway practices follow many of the principles below. —— 《Xiaohongshu Launches Self-Developed Rust High-Performance Layer 7 Gateway ROFF》 This article is divided into two parts: 1. Performance Bottleneck Analysis Methods 2. Hands-on Analysis of APISIX, Kon...]]></description>
            <content:encoded><![CDATA[<article><div><p>2025-03-07 Update: Recently, Xiaohongshu&#x27;s gateway practices follow many of the principles below. —— <a href="https://mp.weixin.qq.com/s/wnkYr4qKIFmh9E9H_XcwTA">《Xiaohongshu Launches Self-Developed Rust High-Performance Layer 7 Gateway ROFF》</a></p><p>This article is divided into two parts:</p><ol start="1"><li><a href="#performance-bottleneck-analysis-methods">Performance Bottleneck Analysis Methods</a></li><li><a href="#hands-on-analysis">Hands-on Analysis of APISIX, Kong, and Nginx</a></li></ol><h2 id="performance-bottleneck-analysis-methods">Performance Bottleneck Analysis Methods</h2><h3 id="performance-bottleneck-analysis-methods-the-real-deal">Performance Bottleneck Analysis Methods (The Real Deal)</h3><p>This section covers the actual methods.</p><h4 id="monitoring-metrics-analysis"><strong>Monitoring Metrics Analysis</strong></h4><ul><li>Latency Metrics: Monitor request latency and integration latency</li><li>Error Rate Metrics: Monitor 4XX and 5XX error rates to identify system failure points</li><li>Throughput Metrics: Request count and requests per second (RPS)</li><li>Cache Hit Metrics: Monitor CacheHitCount and CacheMissCount ratios</li></ul><h4 id="system-resource-monitoring"><strong>System Resource Monitoring</strong></h4><ul><li>CPU utilization analysis</li><li>Memory consumption analysis</li><li>Network I/O monitoring</li><li>Disk I/O monitoring (especially for logs and cache)</li></ul><h4 id="request-flow-analysis"><strong>Request Flow Analysis</strong></h4><ul><li>Request processing chain tracing</li><li>Hot path identification</li><li>Serial processing bottleneck discovery</li></ul><h3 id="infrastructure-level-optimization">Infrastructure-Level Optimization</h3><h4 id="hardware-resource-optimization">Hardware Resource Optimization</h4><ul><li>Increase CPU cores and memory capacity</li><li>Use SSD storage to improve I/O speed</li><li>Deploy high-performance network cards to reduce network latency</li><li>Properly evaluate and adjust container resource limits (in Kubernetes environments)</li></ul><h4 id="network-optimization">Network Optimization</h4><ul><li>Optimize network topology structure</li><li>Reduce network hops</li><li>Use CDN to accelerate static content</li><li>Implement DNS optimization</li><li>Deploy gateways and backend services in the same network zone</li></ul><h4 id="operating-system-tuning">Operating System Tuning</h4><ul><li>Adjust TCP/IP stack parameters (such as increasing connection queues)</li><li>Optimize file descriptor limits</li><li>Adjust kernel parameters (such as somaxconn, tcp<em>fin</em>timeout)</li><li>Optimize client thread count in Linux environments</li></ul><h3 id="gateway-configuration-level-optimization">Gateway Configuration-Level Optimization</h3><h4 id="connection-pool-optimization">Connection Pool Optimization</h4><ul><li>Configure appropriate database connection pool sizes (recommended to match expected client count) —— <a href="https://docs.oracle.com/cd/E55956_01/doc.11123/administrator_guide/content/admin_performance.html">Oracle Database Administrator Essentials</a></li><li>Adjust backend service connection pool parameters</li><li>Set connection timeout and retry strategies</li><li>Implement connection keep-alive mechanisms</li></ul><h4 id="http-optimization">HTTP Optimization</h4><ul><li>Enable HTTP keep-alive to reuse TCP connections —— <a href="https://docs.oracle.com/cd/E55956_01/doc.11123/administrator_guide/content/admin_performance.html">Oracle Performance Practices</a></li><li>Configure appropriate chunked encoding strategies</li><li>Adjust HTTP header size limits</li><li>Set reasonable request/response timeout values</li></ul><h4 id="caching-strategy-optimization">Caching Strategy Optimization</h4><ul><li>Implement response caching to reduce backend calls —— <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/rest-api-optimize.html">Amazon API Optimization Practices</a></li><li>Configure cache key strategies and TTL (Time To Live)</li><li>Implement multi-level caching strategies</li><li>Optimize cache validation and invalidation strategies</li></ul><h3 id="request-processing-optimization">Request Processing Optimization</h3><h4 id="concurrent-processing-optimization">Concurrent Processing Optimization</h4><ul><li>Increase worker thread count</li><li>Implement asynchronous processing patterns</li><li>Optimize thread pool configuration</li><li>Use event-driven architecture for high-concurrency request handling</li></ul><h4 id="load-balancing-strategies">Load Balancing Strategies</h4><ul><li>Implement intelligent load balancing algorithms (weighted round-robin, least connections, etc.)</li><li>Configure dynamic load balancing strategies</li><li>Implement service health checks and automatic failover</li><li>Adjust weights based on backend service capacity</li></ul><h4 id="routing-optimization">Routing Optimization</h4><ul><li>Optimize routing lookup algorithms</li><li>Implement routing caching</li><li>Configure routing warm-up strategies</li><li>Implement intelligent routing based on traffic characteristics</li></ul><h3 id="data-processing-optimization">Data Processing Optimization</h3><h4 id="message-processing-optimization">Message Processing Optimization</h4><ul><li>Configure &quot;spill to disk&quot; strategies for large messages (e.g., set &gt;4MB messages to write to disk) <a href="https://docs.oracle.com/cd/E55956_01/doc.11123/administrator_guide/content/admin_performance.html">3</a></li><li>Implement request/response compression <a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/rest-api-optimize.html">1</a></li><li>Optimize serialization/deserialization processes</li><li>Reduce unnecessary data transformations</li></ul><h4 id="protocol-optimization">Protocol Optimization</h4><ul><li>Use HTTP/2 to reduce latency and improve parallel processing capability</li><li>Use WebSocket and other long-connection protocols in appropriate scenarios</li><li>Consider using gRPC to improve microservice communication efficiency</li><li>Implement protocol upgrade strategies</li></ul><h3 id="monitoring-and-logging-optimization">Monitoring and Logging Optimization</h3><h4 id="log-optimization">Log Optimization</h4><ul><li>Reduce unnecessary trace information (set production environment to ERROR or FATAL level) <a href="https://docs.oracle.com/cd/E55956_01/doc.11123/administrator_guide/content/admin_performance.html">3</a></li><li>Disable or reduce access log recording <a href="https://docs.oracle.com/cd/E55956_01/doc.11123/administrator_guide/content/admin_performance.html">3</a></li><li>Disable transaction logs to reduce disk I/O burden <a href="https://docs.oracle.com/cd/E55956_01/doc.11123/administrator_guide/content/admin_performance.html">3</a></li><li>Implement asynchronous log writing strategies</li></ul><h4 id="monitoring-optimization">Monitoring Optimization</h4><ul><li>Disable real-time monitoring to reduce overhead <a href="https://docs.oracle.com/cd/E55956_01/doc.11123/administrator_guide/content/admin_performance.html">3</a></li><li>Disable or reduce traffic monitoring <a href="https://docs.oracle.com/cd/E55956_01/doc.11123/administrator_guide/content/admin_performance.html">3</a></li><li>Configure reasonable monitoring sampling rates</li><li>Implement intelligent alerting thresholds to avoid excessive monitoring system load</li></ul><h3 id="security-performance-optimization">Security Performance Optimization</h3><h4 id="authentication-optimization">Authentication Optimization</h4><ul><li>Cache authentication results</li><li>Use lightweight token validation</li><li>Implement tiered authentication strategies</li><li>Optimize JWT processing workflows</li></ul><h4 id="ssltls-optimization">SSL/TLS Optimization</h4><ul><li>Use session reuse to reduce handshake overhead</li><li>Configure OCSP stapling</li><li>Implement TLS connection pools</li><li>Select efficient cipher suites</li></ul><h3 id="advanced-optimization-strategies">Advanced Optimization Strategies</h3><h4 id="circuit-breaking-and-rate-limiting">Circuit Breaking and Rate Limiting</h4><ul><li>Implement request rate limiting to protect backend systems</li><li>Configure circuit breakers to prevent system overload</li><li>Implement backoff algorithms for retry handling</li><li>Traffic shaping to optimize request distribution</li></ul><h4 id="service-mesh-integration">Service Mesh Integration</h4><ul><li>Integrate with service meshes like Istio/Envoy</li><li>Delegate some gateway functions to sidecar proxies</li><li>Implement collaborative strategies between gateways and service meshes</li><li>Leverage traffic management capabilities provided by service meshes</li></ul><h4 id="architectural-optimization">Architectural Optimization</h4><ul><li>Consider multi-tier gateway architecture (edge gateway + internal gateway)</li><li>Implement edge computing to reduce latency</li><li>Domain-driven API gateway design</li><li>Gateway sharding based on traffic characteristics</li></ul><p>By systematically analyzing performance bottlenecks in the above aspects and applying corresponding optimization techniques, gateway performance, throughput, and reliability can be significantly improved. The key is to select the most suitable combination of optimization strategies based on actual system characteristics and business requirements, and maintain high system performance through continuous monitoring and tuning.</p><h2 id="hands-on-analysis">Hands-on Analysis</h2><h3 id="nginx-gateway">Nginx Gateway</h3><h4 id="main-performance-bottlenecks">Main Performance Bottlenecks</h4><ol start="1"><li><p><strong>Connection Handling Capacity Bottlenecks</strong></p><ul><li>Default worker process configuration may not match server CPU cores</li><li>Connection pool size limits cause connection rejection under high concurrency</li><li>File descriptor limits cause &quot;too many open files&quot; errors</li></ul></li><li><p><strong>Configuration Complexity Bottlenecks</strong></p><ul><li>Static configuration files require manual modification and reloading</li><li>Large-scale routing rules make configuration maintenance difficult</li><li>Configuration changes require reloading, potentially causing request interruptions</li></ul></li><li><p><strong>SSL Processing Bottlenecks</strong></p><ul><li>High SSL handshake overhead, CPU usage spikes under high concurrency</li><li>Inefficient key exchange algorithms</li><li>Improper session cache configuration causes repeated handshakes</li></ul></li></ol><h4 id="optimization-methods">Optimization Methods</h4><ol start="1"><li><p><strong>Worker Process and Connection Optimization</strong></p><ul><li>Set <code>worker_processes</code> to match CPU core count</li><li>Increase <code>worker_connections</code> value (typically set to 4096 or higher)</li><li>Use <code>worker_cpu_affinity</code> to bind worker processes to specific CPU cores</li><li>Adjust system file descriptor limits (ulimit -n)</li></ul></li><li><p><strong>Event Processing Optimization</strong></p><ul><li>Enable <code>multi_accept</code> and <code>accept_mutex</code></li><li>Use <code>epoll</code> event handling model (on Linux systems)</li><li>Adjust <code>worker_aio_requests</code> to improve async I/O performance</li></ul></li><li><p><strong>HTTP Optimization</strong></p><ul><li>Configure <code>keepalive_timeout</code> and <code>keepalive_requests</code> parameters</li><li>Enable <code>sendfile</code>, <code>tcp_nopush</code>, and <code>tcp_nodelay</code> options</li><li>Implement <code>gzip</code> compression to reduce transmitted data</li><li>Set <code>client_body_buffer_size</code> and <code>client_max_body_size</code> to limit request size</li></ul></li><li><p><strong>SSL Performance Optimization</strong></p><ul><li>Enable <code>ssl_session_cache shared</code> to improve session reuse rate</li><li>Configure OCSP stapling to reduce handshake latency</li><li>Use ECC certificates to reduce computational overhead</li><li>Prioritize high-performance cipher suites (like AES-GCM)</li></ul></li></ol><h3 id="kong-gateway">Kong Gateway</h3><p>A reliable and well-established gateway.</p><h4 id="main-performance-bottlenecks">Main Performance Bottlenecks</h4><ol start="1"><li><p><strong>Database Dependency Bottlenecks</strong></p><ul><li>PostgreSQL/Cassandra database queries become performance bottlenecks</li><li>Database pressure increases during configuration changes</li><li>Database consistency challenges in distributed deployments</li></ul></li><li><p><strong>Lua Script Processing Bottlenecks</strong></p><ul><li>Overly long plugin execution chains increase request latency <a href="https://www.f5.com/company/blog/nginx/nginx-controller-api-management-module-vs-kong-performance-comparison">1</a></li><li>Improper Lua VM memory usage causes performance degradation</li><li>JIT compilation limitations affect dynamic script performance</li></ul></li><li><p><strong>JWT Validation Bottlenecks</strong></p><ul><li>High JWT validation processing overhead, noticeable in high-percentile latency <a href="https://www.f5.com/company/blog/nginx/benchmarking-api-management-solutions-nginx-kong-amazon-real-time-apis">2</a></li><li>Kong&#x27;s 99.99 percentile latency can reach 3 times that of NGINX <a href="https://www.f5.com/company/blog/nginx/benchmarking-api-management-solutions-nginx-kong-amazon-real-time-apis">2</a></li></ul></li></ol><h4 id="optimization-methods">Optimization Methods</h4><ol start="1"><li><p><strong>Database Optimization</strong></p><ul><li>Use DB-less mode to reduce database dependencies</li><li>Increase database connection pool size (pg_pool parameter in kong.conf)</li><li>Implement database read-write separation (master-slave architecture)</li><li>Consider using declarative configuration instead of database storage</li></ul></li><li><p><strong>Plugin Chain Optimization</strong></p><ul><li>Enable only necessary plugins to reduce processing chain length</li><li>Adjust plugin execution order (lightweight, high-frequency plugins first)</li><li>Configure independent caches for plugins (like Redis cache for rate-limiting plugin)</li><li>Monitor and optimize long-running plugins</li></ul></li><li><p><strong>Cache Optimization</strong></p><ul><li>Configure <code>lua_shared_dict</code> cache size</li><li>Adjust plugin-level cache TTL</li><li>Use external Redis cache to improve hit rates</li><li>Configure entity cache to reduce database queries</li></ul></li><li><p><strong>Connection Pool Optimization</strong></p><ul><li>Adjust upstream_keepalive parameter (typically 100-200)</li><li>Increase nginx<em>upstream</em>keepalive_timeout value</li><li>Set reasonable nginx<em>upstream</em>keepalive_requests value</li><li>Increase nginx<em>http</em>client<em>body</em>buffer_size for large request bodies</li></ul></li></ol><h3 id="apisix-gateway">APISIX Gateway</h3><h4 id="main-performance-bottlenecks">Main Performance Bottlenecks</h4><ol start="1"><li><p><strong>etcd Dependency Bottlenecks</strong></p><ul><li>etcd cluster stability affects gateway configuration propagation</li><li>Frequent configuration changes increase etcd pressure</li><li>etcd read-write latency affects dynamic routing updates</li></ul></li><li><p><strong>Route Matching Bottlenecks</strong></p><ul><li>Large numbers of fine-grained routes increase matching latency</li><li>Complex regex routes reduce matching efficiency</li><li>Untimely route cache updates cause routing errors</li></ul></li></ol><h4 id="optimization-methods">Optimization Methods</h4><ol start="1"><li><p><strong>etcd Optimization</strong></p><ul><li>Build highly available etcd clusters</li><li>Optimize etcd configuration (like setting reasonable heartbeat intervals)</li><li>Implement etcd sharding to reduce single-node pressure</li><li>Increase config_center.timeout parameter value (default 30 seconds)</li></ul></li><li><p><strong>Route Optimization</strong></p><ul><li>Use prefix matching instead of full regex</li><li>Increase route cache TTL</li><li>Reduce route rule complexity, split overly complex rules</li><li>Use domain or hostname pre-filtering</li></ul></li></ol><h3 id="envoy-gateway">Envoy Gateway</h3><h4 id="main-performance-bottlenecks">Main Performance Bottlenecks</h4><ol start="1"><li><p><strong>xDS Configuration Update Bottlenecks</strong></p><ul><li>Dynamic configuration updates cause resource reallocation overhead</li><li>Control Plane communication latency affects configuration delivery</li><li>Large numbers of listener and cluster configurations cause high memory usage</li></ul></li><li><p><strong>Filter Chain Processing Bottlenecks</strong></p><ul><li>Long HTTP filter chains increase processing latency</li><li>Complex filter logic causes high CPU usage</li><li>Lua filters are less efficient than native filters</li></ul></li></ol><h4 id="optimization-methods">Optimization Methods</h4><ol start="1"><li><p><strong>xDS Configuration Optimization</strong></p><ul><li>Implement incremental xDS to reduce configuration update overhead</li><li>Optimize Control Plane communication (use gRPC streams instead of polling)</li><li>Set reasonable configuration cache TTL</li><li>Use Aggregated Discovery Service (ADS) to ensure configuration consistency</li></ul></li><li><p><strong>Filter Optimization</strong></p><ul><li>Reduce filter chain length, keep only necessary filters</li><li>Prioritize native C++ filters over Lua or WASM</li><li>Adjust filter execution order (high-frequency filters first)</li><li>Enable statistical monitoring for critical filters</li></ul></li></ol><h3 id="performance-comparison-and-selection-recommendations-for-different-gateways">Performance Comparison and Selection Recommendations for Different Gateways</h3><h4 id="performance-comparison">Performance Comparison</h4><ul><li>In standard API call tests, NGINX API Management Module performance can be 2x better than Kong <a href="https://www.f5.com/company/blog/nginx/nginx-controller-api-management-module-vs-kong-performance-comparison">Data Support</a></li><li>In terms of latency, NGINX adds 20-30% lower latency than Kong <a href="https://www.f5.com/company/blog/nginx/nginx-controller-api-management-module-vs-kong-performance-comparison">Data Support</a></li><li>In CPU efficiency, NGINX is about 40% more efficient than Kong <a href="https://www.f5.com/company/blog/nginx/nginx-controller-api-management-module-vs-kong-performance-comparison">Data Support</a></li><li>In JWT validation scenarios, NGINX can handle 2x more API calls than Kong <a href="https://www.f5.com/company/blog/nginx/benchmarking-api-management-solutions-nginx-kong-amazon-real-time-apis">Data Support</a></li></ul><h4 id="selection-recommendations">Selection Recommendations</h4><ol start="1"><li><p><strong>NGINX Suitable Scenarios</strong>:</p><ul><li>Stable API gateway needs with static routing configuration</li><li>Scenarios prioritizing performance and low latency</li><li>Lightweight gateway needs in resource-constrained environments</li><li>Primarily providing reverse proxy and load balancing functions</li></ul></li><li><p><strong>Kong Suitable Scenarios</strong>:</p><ul><li>Need rich API management features (authentication, rate limiting, transformation, etc.)</li><li>Teams pursuing development convenience (RESTful API configuration)</li><li>Microservice architectures with dynamic routing needs</li><li>Can accept some performance loss for feature richness</li></ul></li><li><p><strong>APISIX Suitable Scenarios</strong>:</p><ul><li>Teams pursuing balance between dynamic routing and high performance</li><li>Cloud-native architectures with service discovery integration needs</li><li>Scenarios requiring fine-grained traffic control</li></ul></li><li><p><strong>Envoy Suitable Scenarios</strong>:</p><ul><li>Kubernetes/Istio service mesh infrastructure</li><li>Modern cloud architectures requiring advanced observability</li><li>DevOps teams pursuing programmability and extensibility</li></ul></li></ol><p>By understanding the performance bottleneck characteristics and optimization methods of different gateways, you can choose the most suitable gateway type based on your business characteristics and technology stack, and implement targeted performance optimization to achieve the best balance of gateway performance and functionality.</p><h2 id="summary">Summary</h2><p>Gateway performance optimization is systematic work that requires analysis and optimization from multiple levels. Through the analysis methods, optimization techniques, and code examples provided above, you can implement targeted optimization for different performance bottlenecks. The key points are:</p><ol start="1"><li>Establish comprehensive performance monitoring systems to detect performance bottlenecks promptly <a href="https://learn.microsoft.com/en-us/data-integration/gateway/service-gateway-performance">1</a></li><li>Adopt multi-level caching strategies to reduce redundant calculations and network requests</li><li>Optimize connection pool management to improve connection reuse efficiency</li><li>Implement efficient load balancing algorithms for intelligent request distribution</li><li>Use regular benchmark testing to continuously evaluate and optimize performance</li></ol><p>For gateways in cloud-native environments, you can also consider leveraging Kubernetes&#x27; auto-scaling capabilities to dynamically adjust gateway instance counts in response to traffic changes.</p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>Gateway</category>
            <category>Architecture</category>
        </item>
        <item>
            <title><![CDATA[How the Kernel Receives Network Packets]]></title>
            <link>https://blog.cheverjohn.me/network-stack</link>
            <guid>https://blog.cheverjohn.me/network-stack</guid>
            <pubDate>Tue, 27 Feb 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Overall Architecture Overview Thanks to Zhang Yanfei for the diagram. After you basically understand what network card drivers, hardware interrupts, software interrupts, and ksoftirqd threads are, you can provide a kernel packet reception path diagram as shown above. The general process is as follow...]]></description>
            <content:encoded><![CDATA[<article><div><h2 id="overall-architecture-overview">Overall Architecture Overview</h2><p><img alt="image-20250310194742752" src="theWholeLand.png"/></p><blockquote><p>Thanks to Zhang Yanfei for the diagram.</p></blockquote>
<p>After you basically understand what network card drivers, hardware interrupts, software interrupts, and ksoftirqd threads are, you can provide a kernel packet reception path diagram as shown above.</p><p>The general process is as follows:</p><ol start="1"><li>When the network card receives data, it writes the received frames to memory using DMA, then sends an interrupt to the CPU to notify that data has arrived.</li><li>When the CPU receives the interrupt request, it calls the interrupt handler registered by the network device driver.</li><li>The network card&#x27;s interrupt handler doesn&#x27;t do much work - it issues a software interrupt request and quickly releases CPU resources.</li><li>The ksoftirqd kernel thread detects the software interrupt request, calls poll to start polling for packet reception, and forwards received packets to various protocol stack layers for processing. For TCP packets, they are placed in the user socket&#x27;s receive queue.</li></ol><h2 id="foundation-work-before-everything-else">Foundation Work Before Everything Else</h2><p>Before Linux drivers, kernel protocol stacks, and other modules can receive network card data packets, extensive preparation work must be done:</p><ol start="1"><li>Pre-create ksoftirqd kernel threads;</li><li>Register processing functions for various protocols;</li><li>Pre-initialize the network device subsystem;</li><li>Start up the network card.</li></ol><h3 id="initialization-work">Initialization Work</h3><h4 id="creating-ksoftirqd-kernel-threads">Creating ksoftirqd Kernel Threads</h4><p>All Linux software interrupts are handled in dedicated kernel threads (ksoftirqd), so it&#x27;s essential to understand how these threads are initialized.</p><p>First, there isn&#x27;t just one thread, but N threads, where N equals the number of cores on your machine.</p><p>During system initialization, the <code>smpboot_register_percpu_thread</code> function is called in <code>kernel/smpboot.c</code>, which further executes <code>spawn_ksoftirqd</code> (located in <code>kernel/softirq.c</code>) to create softirqd threads, as shown below:</p><p>Related code:</p><pre><code class="lang-c">static struct smp_hotplug_thread softirq_threads = {
    .store            = &amp;ksoftirqd,
    .thread_should_run    = ksoftirqd_should_run,
    .thread_fn        = run_ksoftirqd,
    .thread_comm        = &quot;ksoftirqd/%u&quot;,
};

static __init int spawn_ksoftirqd(void)
{
    cpuhp_setup_state_nocalls(CPUHP_SOFTIRQ_DEAD, &quot;softirq:dead&quot;, NULL,
                  takeover_tasklets);
    BUG_ON(smpboot_register_percpu_thread(&amp;softirq_threads));

    return 0;
}
early_initcall(spawn_ksoftirqd);
</code></pre>
<p>After ksoftirqd is created, it enters its thread loop functions ksoftirqd<em>should</em>run and run_ksoftirqd, then checks if there are any software interrupts to process.</p><p>Software interrupts include not only network software interrupts but other types as well. The Linux kernel defines all software interrupt types in interrupt.h:</p><pre><code class="lang-c">// file: include/linux/interrupt.h
enum
{
    HI_SOFTIRQ=0,
    TIMER_SOFTIRQ,
    NET_TX_SOFTIRQ,
    NET_RX_SOFTIRQ,
    BLOCK_SOFTIRQ,
    IRQ_POLL_SOFTIRQ,
    TASKLET_SOFTIRQ,
    SCHED_SOFTIRQ,
    HRTIMER_SOFTIRQ,
    RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

    NR_SOFTIRQS
};
</code></pre>
<h4 id="network-subsystem-initialization">Network Subsystem Initialization</h4><p>During network subsystem initialization, softnet<em>data is initialized for each CPU, and processing functions are registered for RX</em>SOFTIRQ and TX_SOFTIRQ, as shown in Figure 2.4.</p><p>The Linux kernel initializes various subsystems by calling subsys_initcall.</p><p><strong>Key Point!!! The network subsystem initialization mentioned here executes the net<em>dev</em>init function.</strong></p><p>This is the net<em>dev</em>init function in subsys<em>initcall(net</em>dev_init). Code as follows:</p><pre><code class="lang-c">static int __init net_dev_init(void)
{
    ......

    /*
     *    Initialise the packet receive queues.
     */

  /*
   * Allocate a softnet_data structure for each CPU. The poll_list in this structure
   * is used to wait for driver programs to register their poll functions, which can
   * be seen later when network card drivers initialize.
   */
        for_each_possible_cpu(i) {
        struct softnet_data *sd = &amp;per_cpu(softnet_data, i);

        memset(sd, 0, sizeof(*sd));
        skb_queue_head_init(&amp;sd-&gt;input_pkt_queue);
        skb_queue_head_init(&amp;sd-&gt;process_queue);
        sd-&gt;completion_queue = NULL;
        INIT_LIST_HEAD(&amp;sd-&gt;poll_list);
    ......
  }
  ......
    /*
     * open_softirq registers a processing function for each type of software interrupt.
     * NET_TX_SOFTIRQ processing function is net_tx_action;
     * NET_RX_SOFTIRQ processing function is net_rx_action;
     */
  open_softirq(NET_TX_SOFTIRQ, net_tx_action);
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);
}

subsys_initcall(net_dev_init);
</code></pre>
<p>Following open<em>softirq further reveals that this registration method is recorded in the softirq</em>vec variable. Later, when the softirqd thread receives software interrupts, it will use this variable to find the corresponding processing function for each type of software interrupt.</p><pre><code class="lang-c">void open_softirq(int nr, void (*action)(struct softirq_action *))
{
    softirq_vec[nr].action = action;
}
</code></pre>
<h4 id="protocol-stack-registration">Protocol Stack Registration</h4><h4 id="network-card-driver-initialization">Network Card Driver Initialization</h4><p>Each driver uses <code>module_init</code> to register an initialization function with the kernel. When the driver is loaded, the kernel calls this function.</p><p>After completion, the Linux kernel knows the driver&#x27;s relevant information, such as the igb network card driver&#x27;s igb<em>driver</em>name and igb_probe function address.</p><p>When a network card device is identified, the kernel calls its driver&#x27;s probe method. (Continuing with the igb network card driver example), the igb<em>driver&#x27;s probe method is igb</em>probe.</p><p>The purpose of the igb_probe method is to get the device into a ready state as quickly as possible.</p><p>Additionally, there&#x27;s a critical step: registering the poll function required by the NAPI mechanism, which for the igb network card driver is igb_poll.</p><h3 id="after-initialization-completion">After Initialization Completion</h3><h4 id="starting-the-network-card">Starting the Network Card</h4><p>After all the above initialization is complete, the network card can be started. The general startup sequence is similar, as shown below:</p><p>igb_open code:</p><pre><code class="lang-c">static int __igb_open(struct net_device *netdev, bool resuming)
{
  // Allocate transmit descriptor arrays
  err = igb_setup_all_tx_resources(adapter);
  // Allocate receive descriptor arrays
    err = igb_setup_all_rx_resources(adapter);

  // Register interrupt handler
  err = igb_request_irq(adapter);
    if (err)
        goto err_req_irq;

  // Enable NAPI
      for (i = 0; i &lt; adapter-&gt;num_q_vectors; i++)
        napi_enable(&amp;(adapter-&gt;q_vector[i]-&gt;napi));
    ......
}
</code></pre>
<p>The igb<em>open function calls igb</em>setup<em>all</em>tx<em>resources and igb</em>setup<em>all</em>rx<em>resources. In the igb</em>setup<em>all</em>rx_resources step, RingBuffer is allocated and the mapping relationship between memory and Rx queues is established.</p><pre><code class="lang-c">static int igb_setup_all_rx_resources(struct igb_adapter *adapter)
{
    ......

    for (i = 0; i &lt; adapter-&gt;num_rx_queues; i++) {
        err = igb_setup_rx_resources(adapter-&gt;rx_ring[i]);
        ...
    }

    return err;
}
</code></pre>
<p>Using a for loop with the igb<em>setup</em>rx<em>resources function, several queues are created. The igb</em>setup<em>rx</em>resources function:</p><pre><code class="lang-c">int igb_setup_rx_resources(struct igb_ring *rx_ring)
{
    struct device *dev = rx_ring-&gt;dev;
    int size;

    // 1. Allocate igb_rx_buffer array memory
    size = sizeof(struct igb_rx_buffer) * rx_ring-&gt;count;

    rx_ring-&gt;rx_buffer_info = vzalloc(size);
    if (!rx_ring-&gt;rx_buffer_info)
        goto err;

    /* Round up to nearest 4K */
    // 2. Allocate e1000_adv_rx_desc DMA array memory
    rx_ring-&gt;size = rx_ring-&gt;count * sizeof(union e1000_adv_rx_desc);
    rx_ring-&gt;size = ALIGN(rx_ring-&gt;size, 4096);

    rx_ring-&gt;desc = dma_alloc_coherent(dev, rx_ring-&gt;size,
                       &amp;rx_ring-&gt;dma, GFP_KERNEL);
    if (!rx_ring-&gt;desc)
        goto err;

    // 3. Initialize queue members
    rx_ring-&gt;next_to_alloc = 0;
    rx_ring-&gt;next_to_clean = 0;
    rx_ring-&gt;next_to_use = 0;

    return 0;

err:
    vfree(rx_ring-&gt;rx_buffer_info);
    rx_ring-&gt;rx_buffer_info = NULL;
    dev_err(dev, &quot;Unable to allocate memory for the Rx descriptor ring\n&quot;);
    return -ENOMEM;
}
</code></pre>
<p>From the above source code, you can see that internally, a RingBuffer doesn&#x27;t have just one circular queue array, but two:</p><ol start="1"><li>igb<em>rx</em>buffer array: This array is used by the kernel, allocated via vzalloc;</li><li>e1000<em>adv</em>rx<em>desc array: This array is used by network card hardware, allocated via dma</em>alloc_coherent.</li></ol><p>Then there&#x27;s the final step of interrupt function registration, which can be seen in igb<em>request</em>irq.</p><p>OK, that&#x27;s all the preparation work! Next comes receiving data packets.</p><h2 id="starting-to-receive-data-packets">Starting to Receive Data Packets</h2><p>This section includes hardware interrupt processing.</p><h3 id="hardware-interrupt-processing">Hardware Interrupt Processing</h3><p>First, when data frames arrive at the network card from the network cable, the first stop is the network card&#x27;s receive queue. The network card searches for available memory locations in its allocated RingBuffer, and when found, the DMA engine will DMA the data to the memory previously associated with the network card. At this point, the CPU is unaware.</p><p>When the DMA operation completes, the network card sends a hardware interrupt to the CPU, notifying it that data has arrived. The hardware interrupt processing is as follows:</p><p>In the &quot;Starting the Network Card&quot; section mentioned earlier, the network card&#x27;s hardware interrupt registered processing function is igb<em>msix</em>ring.</p><pre><code class="lang-c">// file: drivers/net/ethernet/intel/igb/igb_main.c
static irqreturn_t igb_msix_ring(int irq, void *data)
{
    struct igb_q_vector *q_vector = data;

    /* Write the ITR value calculated from the previous interrupt. */
    igb_write_itr(q_vector);

    napi_schedule(&amp;q_vector-&gt;napi);

    return IRQ_HANDLED;
}
</code></pre>
<p>The igb<em>write</em>itr only records hardware interrupt frequency. Following the napi<em>schedule call, you&#x27;ll discover that Linux only completes simple necessary work in hardware interrupts, leaving most processing to software interrupts. From the above code, you can see that the hardware interrupt processing is really very short - it just records a register, modifies the CPU&#x27;s poll</em>list, and then issues a software interrupt. That&#x27;s it, the hardware interrupt work is complete.</p><h3 id="ksoftirqd-kernel-thread-processing-software-interrupts">ksoftirqd Kernel Thread Processing Software Interrupts</h3><p>Network packet reception processing mainly occurs in the ksoftirqd kernel thread, where all software interrupts are processed, as shown below:</p><h3 id="network-protocol-stack-processing">Network Protocol Stack Processing</h3><p>The netif<em>receive</em>skb function processes packets according to their protocol. For UDP packets, packets are sent sequentially to protocol processing functions like ip<em>rcv, udp</em>rcv, etc., as shown below:</p><h3 id="ip-layer-processing">IP Layer Processing</h3><p>Linux IP layer operations are in the code file <code>net/ipv4/ip_input.c</code>.</p><h2 id="summary">Summary</h2><p>The network module is the most complex module in the Linux kernel. The entire process involves interactions between many kernel components, such as network card drivers, protocol stacks, kernel ksoftirqd threads, etc. It looks complex, but the overall picture is actually quite clear. Simple summary as follows.</p><p>After a user executes a recvfrom call, the user process enters kernel mode through the system call. If the receive queue has no data, the process enters sleep state and is suspended by the operating system. This part is relatively simple. Next comes the work between various Linux kernel components.</p><p>First, before starting packet reception, Linux must do extensive preparation work:</p><ul><li>Create ksoftirqd kernel threads, set up their thread functions, and rely on them to handle software interrupts later;</li><li>Protocol stack registration: Linux implements many protocols like ARP, ICMP, IP, UDP, and TCP. Each protocol registers its processing function, making it convenient to quickly find the corresponding processing function when packets arrive;</li><li>Network card driver initialization: Each driver has an initialization function that the kernel will initialize. During this initialization process, prepare DMA and tell the kernel the NAPI poll function address;</li><li>Start the network card: Allocate RX and TX queues, register interrupt corresponding processing functions.</li></ul><p>After preparation work is complete, data arrives. The first to greet it is the network card:</p><ul><li>The network card DMAs data frames to memory&#x27;s RingBuffer, then sends an interrupt notification to the CPU;</li><li>CPU responds to the interrupt request, calls the interrupt processing function registered when the network card started;</li><li>The interrupt processing function only issues a software interrupt request and does nothing else;</li><li>Kernel thread ksoftirqd discovers a software interrupt request has arrived, first disables hardware interrupts;</li><li>ksoftirqd thread starts calling the driver&#x27;s poll function to receive packets;</li><li>The poll function sends received packets to the protocol stack&#x27;s registered ip_rcv function;</li><li>The ip<em>rcv function sends packets to the udp</em>rcv function (for TCP packets, sent to tcp<em>rcv</em>v4).</li></ul><h2 id="some-conclusions">Some Conclusions</h2><h3 id="question-1-what-exactly-is-ringbuffer-and-why-does-ringbuffer-drop-packets">Question 1: What exactly is RingBuffer, and why does RingBuffer drop packets?</h3><p>RingBuffer is a special area in memory, a circular queue array. In fact, this data structure includes igb<em>rx</em>buffer circular queue arrays, e1000<em>adv</em>rx_desc circular queue arrays, and numerous skbs.</p><p>If RingBuffer represents pointer arrays, they are pre-allocated. If they are skbs, they are dynamically allocated during the packet reception process.</p><h3 id="question-2-what-are-software-interrupts-and-hardware-interrupts-respectively">Question 2: What are software interrupts and hardware interrupts respectively?</h3><p>Key flow of data packet reception in Linux network stack:</p><ol start="1"><li><strong>Hardware Stage</strong>: Network card places received data packets into RingBuffer</li><li><strong>Hardware Interrupt Trigger</strong>: Network card generates hardware interrupt to notify CPU</li><li><strong>Hardware Interrupt Processing</strong>: Add network card device to <code>poll_list</code> doubly linked list in <code>softnet_data</code> structure</li><li><strong>Software Interrupt Trigger</strong>: Trigger <code>NET_RX_SOFTIRQ</code> software interrupt</li><li><strong>Software Interrupt Processing</strong>: Traverse <code>poll_list</code>, execute network card driver&#x27;s <code>poll</code> function to collect network packets</li><li><strong>Protocol Stack Processing</strong>: Forward data packets to protocol processing functions like <code>ip_rcv</code>, <code>udp_rcv</code>, <code>tcp_rcv_v4</code></li></ol><p>This describes the Linux NAPI (New API) mechanism, an efficient method for handling network data packets.</p><h4 id="actual-application-of-ringbuffer-in-network-stack">Actual Application of RingBuffer in Network Stack</h4><h5 id="network-card-rxtx-ring-buffers">Network Card RX/TX Ring Buffers</h5><pre><code class="lang-c">/* Simplified network card RX Ring structure */
struct e1000_rx_desc {
    __le64 buffer_addr;    /* Data buffer address */
    __le16 length;         /* Packet length */
    __le16 checksum;       /* Checksum */
    __u8  status;          /* Descriptor status */
    __u8  errors;          /* Error code */
    __le16 special;
};
</code></pre>
<p>In practice, an Intel network card&#x27;s RX Ring might contain 256 such descriptors, forming a circular structure.</p><h5 id="actual-example-of-hardware-and-software-interrupt-cooperation">Actual Example of Hardware and Software Interrupt Cooperation</h5><p>In Intel 82599 network card driver:</p><pre><code class="lang-c">/* Hardware interrupt handler */
static irqreturn_t ixgbe_msix_lsc(int irq, void *data)
{
    struct net_device *netdev = data;

    /* Disable network card interrupt */
    ixgbe_disable_interrupt();

    /* Add device to poll_list */
    napi_schedule(&amp;adapter-&gt;q_vector[vector]-&gt;napi);

    return IRQ_HANDLED;
}

/* NAPI poll function */
static int ixgbe_poll(struct napi_struct *napi, int budget)
{
    struct ixgbe_q_vector *q_vector = container_of(napi, struct ixgbe_q_vector, napi);
    struct ixgbe_adapter *adapter = q_vector-&gt;adapter;
    int work_done = 0;

    /* Batch receive packets from RingBuffer, process at most budget packets */
    work_done = ixgbe_clean_rx_irq(q_vector, budget);

    /* If work is incomplete, stay in poll_list */
    if (work_done &lt; budget) {
        napi_complete(napi);
        ixgbe_enable_interrupt();
    }

    return work_done;
}
</code></pre></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>Network</category>
            <category>Tech</category>
        </item>
        <item>
            <title><![CDATA[Shell Script for Pushing Large Git Repositories]]></title>
            <link>https://blog.cheverjohn.me/shell-to-push-large-git-repo</link>
            <guid>https://blog.cheverjohn.me/shell-to-push-large-git-repo</guid>
            <pubDate>Wed, 27 Dec 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Check Out My Results My requirement: I want to read Linux source code, manage my Linux source code reading with git, and save my source code reading with my self-built GitLab. Then I discovered that simply using git push for a 2GB Linux source code file would cause problems. So I wrote a script to s...]]></description>
            <content:encoded><![CDATA[<article><div><h2 id="check-out-my-results">Check Out My Results</h2><p><img alt="image-20250310233801870" src="beforeShow.png"/></p><p>My requirement: I want to read Linux source code, manage my Linux source code reading with git, and save my source code reading with my self-built GitLab. Then I discovered that simply using <code>git push</code> for a 2GB Linux source code file would cause problems.</p><p>So I wrote a script to solve this problem.</p><p>The script content is as follows:</p><pre><code class="lang-shell">#!/bin/bash

# Configuration parameters (modify as needed)
REMOTE_NAME=&quot;origin&quot;              # Remote repository name
BRANCH_NAME=&quot;main&quot;                # Target branch name
REMOTE_URL=&quot;git@gitlab.cheverjohn.me:CheverJohn/linux.git&quot;  # Remote repository URL
BATCH_SIZE=500                    # Number of files per batch
COMMIT_MESSAGE=&quot;Batch file upload&quot;      # Commit message

# Color configuration
RED=&#x27;\033[0;31m&#x27;
GREEN=&#x27;\033[0;32m&#x27;
YELLOW=&#x27;\033[0;33m&#x27;
BLUE=&#x27;\033[0;34m&#x27;
NC=&#x27;\033[0m&#x27; # No color

# Check if we&#x27;re inside a git repository
if ! git rev-parse --is-inside-work-tree &gt; /dev/null 2&gt;&amp;1; then
    echo -e &quot;${YELLOW}Current directory is not a git repository, initializing...${NC}&quot;
    git init
    echo -e &quot;${GREEN}Git repository initialized${NC}&quot;
else
    echo -e &quot;${GREEN}Git repository already exists${NC}&quot;
fi

# Check if remote repository is configured
if ! git remote | grep -q &quot;$REMOTE_NAME&quot;; then
    echo -e &quot;${YELLOW}Adding remote repository $REMOTE_NAME: $REMOTE_URL${NC}&quot;
    git remote add $REMOTE_NAME $REMOTE_URL
    echo -e &quot;${GREEN}Remote repository added${NC}&quot;
else
    CURRENT_URL=$(git remote get-url $REMOTE_NAME 2&gt;/dev/null || echo &quot;&quot;)
    if [ &quot;$CURRENT_URL&quot; != &quot;$REMOTE_URL&quot; ]; then
        echo -e &quot;${YELLOW}Updating remote repository URL: $REMOTE_URL${NC}&quot;
        git remote set-url $REMOTE_NAME $REMOTE_URL
        echo -e &quot;${GREEN}Remote repository URL updated${NC}&quot;
    else
        echo -e &quot;${GREEN}Remote repository correctly configured${NC}&quot;
    fi
fi

# Ensure main branch exists
if ! git show-ref --quiet refs/heads/$BRANCH_NAME; then
    echo -e &quot;${YELLOW}Creating branch $BRANCH_NAME...${NC}&quot;
    git checkout -b $BRANCH_NAME
    echo -e &quot;${GREEN}Branch $BRANCH_NAME created${NC}&quot;
else
    echo -e &quot;${GREEN}Branch $BRANCH_NAME already exists${NC}&quot;
    git checkout $BRANCH_NAME
fi

# Get all untracked and modified files
echo -e &quot;${BLUE}Getting list of files to upload...${NC}&quot;
FILES=($(git ls-files --others --exclude-standard) $(git diff --name-only))
TOTAL_FILES=${#FILES[@]}

if [ $TOTAL_FILES -eq 0 ]; then
    echo -e &quot;${YELLOW}No files found to add, trying to add all files...${NC}&quot;
    git add -A
    FILES=($(git diff --name-only --cached))
    TOTAL_FILES=${#FILES[@]}

    if [ $TOTAL_FILES -eq 0 ]; then
        echo -e &quot;${RED}Error: No files found to push${NC}&quot;
        exit 1
    fi
else
    # Add all files to staging area
    echo -e &quot;${BLUE}Adding all files to staging area...${NC}&quot;
    git add -A
fi

echo -e &quot;${GREEN}Found $TOTAL_FILES files to upload${NC}&quot;

# Calculate number of batches
BATCH_COUNT=$(( ($TOTAL_FILES + $BATCH_SIZE - 1) / $BATCH_SIZE ))
echo -e &quot;${GREEN}Will upload in $BATCH_COUNT batches${NC}&quot;

# First commit all files
echo -e &quot;${BLUE}Committing all files...${NC}&quot;
git commit -m &quot;$COMMIT_MESSAGE&quot;

# Push in batches
echo -e &quot;${BLUE}Starting batch push...${NC}&quot;
git push -u $REMOTE_NAME $BRANCH_NAME

if [ $? -eq 0 ]; then
    echo -e &quot;${GREEN}All files successfully pushed to remote repository${NC}&quot;
else
    echo -e &quot;${YELLOW}Regular push failed, trying batch push method...${NC}&quot;
    # Using git batch push method (referenced from search results examples)
    # Based on git_batch_push.sh concept, but simplified implementation

    # Use git rev-list to get all commits
    COMMITS=($(git rev-list --reverse HEAD))
    TOTAL_COMMITS=${#COMMITS[@]}

    if [ $TOTAL_COMMITS -eq 0 ]; then
        echo -e &quot;${RED}Error: No commits found${NC}&quot;
        exit 1
    fi

    echo -e &quot;${GREEN}Found $TOTAL_COMMITS commits, will push in batches${NC}&quot;

    # Calculate number of batches (500 commits per batch)
    COMMIT_BATCH_SIZE=500
    COMMIT_BATCH_COUNT=$(( ($TOTAL_COMMITS + $COMMIT_BATCH_SIZE - 1) / $COMMIT_BATCH_SIZE ))

    echo -e &quot;${GREEN}Will push commits in $COMMIT_BATCH_COUNT batches${NC}&quot;

    # Push commits in batches
    for ((i=0; i&lt;$COMMIT_BATCH_COUNT; i++)); do
        START=$(($i * $COMMIT_BATCH_SIZE))
        END=$((($i + 1) * $COMMIT_BATCH_SIZE))

        if [ $END -gt $TOTAL_COMMITS ]; then
            END=$TOTAL_COMMITS
        fi

        BATCH_END_INDEX=$((END - 1))
        TARGET_COMMIT=${COMMITS[$BATCH_END_INDEX]}

        echo -e &quot;${BLUE}Pushing batch $((i+1))/${COMMIT_BATCH_COUNT} (commits $((START+1))-$END)...${NC}&quot;

        if git push $REMOTE_NAME $TARGET_COMMIT:refs/heads/$BRANCH_NAME; then
            echo -e &quot;${GREEN}Batch $((i+1)) successfully pushed${NC}&quot;
        else
            echo -e &quot;${RED}Batch $((i+1)) push failed${NC}&quot;
            echo -e &quot;${YELLOW}Trying alternative push method...${NC}&quot;

            # If above method fails, try another batch push method
            if [ $i -eq 0 ]; then
                # First batch, create new branch
                git push $REMOTE_NAME $BRANCH_NAME
            else
                # Get the end commit of previous batch
                PREV_END=$(($START - 1))
                PREV_COMMIT=${COMMITS[$PREV_END]}
                CURR_COMMIT=${COMMITS[$BATCH_END_INDEX]}

                echo -e &quot;${BLUE}Using range push $PREV_COMMIT..$CURR_COMMIT${NC}&quot;
                git push $REMOTE_NAME $PREV_COMMIT:refs/heads/$BRANCH_NAME $CURR_COMMIT:refs/heads/$BRANCH_NAME
            fi
        fi

        echo
    done
fi

echo -e &quot;${GREEN}Batch upload process completed!${NC}&quot;
</code></pre>
<p>The usage method is simple.</p><pre><code class="lang-shell">chmod +x git_batch_push.sh
</code></pre>
<p>Then run the script in the current folder.</p><h2 id="final-results">Final Results</h2><p><img alt="image-20250310234253015" src="afterShow.png"/></p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>Shell</category>
            <category>Chore</category>
        </item>
        <item>
            <title><![CDATA[Applied Cryptography: GPG File Encryption (Practical Guide)]]></title>
            <link>https://blog.cheverjohn.me/cryptography-gpg</link>
            <guid>https://blog.cheverjohn.me/cryptography-gpg</guid>
            <pubDate>Tue, 26 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[This article focuses on practical implementation with a brief introduction to the underlying principles. Throughout this article, I'll first cover the basic principles, then walk through three main sections: how to create GPG keys, how to manage your keys (key management commands), and hands-on prac...]]></description>
            <content:encoded><![CDATA[<article><div><p>This article focuses on practical implementation with a brief introduction to the underlying principles.</p><p>Throughout this article, I&#x27;ll first cover the <strong>basic principles</strong>, then walk through three main sections: <strong>how to create GPG keys</strong>, <strong>how to manage your keys (key management commands)</strong>, and <strong>hands-on practice: using self-created GPG keys to decrypt transmitted encrypted files</strong>.</p><h2 id="principle-overview">Principle Overview</h2><p>The principle behind using GPG for encrypting and decrypting documents is straightforward, as described in the documentation:</p><blockquote><p>The procedure for encrypting and decrypting documents is straightforward with this mental model. If you want to encrypt a message to Alice, you encrypt it using Alice&#x27;s public key, and she decrypts it with her private key. If Alice wants to send you a message, she encrypts it using your public key, and you decrypt it with your key.</p></blockquote>
<p>The entire process is: I encrypt the file I want to send you with my private key. Then you use my public key to decrypt the file I want to send you, which has been encrypted by me.</p><p>This is the basic process.</p><p>This article provides a quick overview of how GPG encrypts document content.</p><p>This article is divided into two parts, starting with creating a usable GPG key.</p><h2 id="how-to-create-gpg-keys">How to Create GPG Keys</h2><pre><code class="lang-shell">[Se] gpg --list-keys
gpg: checking the trustdb
gpg: no ultimately trusted keys found
</code></pre>
<p>First, let&#x27;s list whether my machine currently has any GPG keys. As we can see, there are none. This mainly refers to whether there are GPG public keys. Let&#x27;s also check for private keys.</p><pre><code class="lang-shell">[Se] gpg --list-secret-keys
[Se]
</code></pre>
<p>Nothing there either.</p><h3 id="one-command-to-rule-them-all">One Command to Rule Them All</h3><p>Let&#x27;s start creating.</p><p>The detailed steps begin with <code>gpg --full-generate-key</code>, as follows:</p><pre><code class="lang-shell">[Se] gpg --full-generate-key
gpg (GnuPG) 2.4.0; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (14) Existing key from card
Your selection? 1
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      &lt;n&gt;  = key expires in n days
      &lt;n&gt;w = key expires in n weeks
      &lt;n&gt;m = key expires in n months
      &lt;n&gt;y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y

GnuPG needs to construct a user ID to identify your key.

Real name: Chenwei Jiang
Email address: cheverjonathan@gmail.com
Comment: Used for learning
You selected this USER-ID:
    &quot;Chenwei Jiang (Used for learning) &lt;cheverjonathan@gmail.com&gt;&quot;

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

gpg: revocation certificate stored as &#x27;/home/cheverjohn/.gnupg/openpgp-revocs.d/D1DED33E8FE9CA4B315A488968CC99424BF9EF81.rev&#x27;
public and secret key created and signed.

pub   rsa4096 2023-09-19 [SC]
      D1DED33E8FE9CA4B315A488968CC99424BF9EF81
uid                      Chenwei Jiang (Used for learning) &lt;cheverjonathan@gmail.com&gt;
sub   rsa4096 2023-09-19 [E]
</code></pre>
<h3 id="detailed-stage-by-stage-command-explanation">Detailed Stage-by-Stage Command Explanation</h3><p>Let me break down what happens at each stage.</p><h4 id="stage-1-select-encryption-and-signing-algorithm">Stage 1: Select Encryption and Signing Algorithm</h4><p>In the first stage, you need to select the encryption algorithm. I chose option 1, which means both encryption and signing use the RSA algorithm.</p><pre><code class="lang-shell">[Se] gpg --full-generate-key
gpg (GnuPG) 2.4.0; Copyright (C) 2021 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (14) Existing key from card
Your selection? 1
</code></pre>
<p>Oh, and there&#x27;s also a copyright notice.</p><h4 id="stage-2-select-key-length">Stage 2: Select Key Length</h4><p>Longer keys are more secure. I chose 4096 here.</p><pre><code class="lang-shell">RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
Requested keysize is 4096 bits
</code></pre>
<h4 id="stage-3-set-key-validity-period">Stage 3: Set Key Validity Period</h4><p>Set the validity period.</p><pre><code class="lang-shell">Please specify how long the key should be valid.
         0 = key does not expire
      &lt;n&gt;  = key expires in n days
      &lt;n&gt;w = key expires in n weeks
      &lt;n&gt;m = key expires in n months
      &lt;n&gt;y = key expires in n years
Key is valid for? (0) 0
Key does not expire at all
Is this correct? (y/N) y
</code></pre>
<p>For this demonstration, I configured <code>Key does not expire at all</code>.</p><h4 id="stage-4-personal-information">Stage 4: Personal Information</h4><p>This section will ultimately be used to generate your user ID.</p><pre><code class="lang-shell">GnuPG needs to construct a user ID to identify your key.

Real name: Chenwei Jiang
Email address: cheverjonathan@gmail.com
Comment: Used for learning
You selected this USER-ID:
    &quot;Chenwei Jiang (Used for learning) &lt;cheverjonathan@gmail.com&gt;&quot;

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
</code></pre>
<p>Here I set my <code>Real name</code> to &quot;Chenwei Jiang&quot;, my <code>Email address</code> to &quot;cheverjonathan@gmail.com&quot;, and added a <code>comment</code>. These are some basic information I commonly use, primarily for demonstration purposes.</p><p>The result is a USER-ID: &quot;Chenwei Jiang (Used for learning) <a href="mailto:cheverjonathan@gmail.com">cheverjonathan@gmail.com</a>&quot;.</p><h2 id="how-to-manage-your-keys-key-management-commands">How to Manage Your Keys (Key Management Commands)</h2><p>This section covers how to manage multiple keys on a host machine.</p><h3 id="listing-keys">Listing Keys</h3><p>There are two types of key listings: public keys and private keys.</p><pre><code class="lang-shell">[Se] gpg --list-keys
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
/home/cheverjohn/.gnupg/pubring.kbx
-----------------------------------
pub   rsa4096 2023-09-19 [SC]
      D1DED33E8FE9CA4B315A488968CC99424BF9EF81
uid           [ultimate] Chenwei Jiang (Used for learning) &lt;cheverjonathan@gmail.com&gt;
sub   rsa4096 2023-09-19 [E]
</code></pre>
<p>The above command shows the details.</p><p>The key section:</p><pre><code class="lang-shell">/home/cheverjohn/.gnupg/pubring.kbx
-----------------------------------
pub   rsa4096 2023-09-19 [SC]
      D1DED33E8FE9CA4B315A488968CC99424BF9EF81
uid           [ultimate] Chenwei Jiang (Used for learning) &lt;cheverjonathan@gmail.com&gt;
sub   rsa4096 2023-09-19 [E]
</code></pre>
<p>The first line shows the public key filename.</p><table><thead><tr><th> Value </th><th> Explanation </th></tr></thead><tbody><tr><td> /home/cheverjohn/.gnupg/pubring.kbx </td><td> First line shows the public key filename (pubring.kbx) </td></tr><tr><td> pub rsa4096 2023-09-19 [SC]<br/>D1DED33E8FE9CA4B315A488968CC99424BF9EF81 </td><td> Second line shows public key characteristics (4096 bits, hash string, and creation time) </td></tr><tr><td> uid [ultimate] Chenwei Jiang (Used for learning) <a href="mailto:cheverjonathan@gmail.com">cheverjonathan@gmail.com</a> </td><td> Third line shows the &quot;user ID&quot; </td></tr><tr><td> sub rsa4096 2023-09-19 [E] </td><td> Fourth line shows private key characteristics </td></tr></tbody></table><h3 id="exporting-keys">Exporting Keys</h3><p>The public key file is located at <code>~/.gnupg/pubring.kbx</code> and stored in binary format. Using the armor parameter converts it to ASCII display.</p><p>Using commands to export as <code>public-key.txt</code> and <code>private-key.txt</code>:</p><pre><code class="lang-shell">[gpg] pwd
/home/cheverjohn/Se/gpg
[gpg] ls
[gpg] ls -la
total 0
drwxr-xr-x. 1 cheverjohn cheverjohn  0 Sep 19 23:36 .
drwxr-xr-x. 1 cheverjohn cheverjohn 34 Sep 19 23:36 ..
[gpg] gpg --armor --output public-key.txt --export &#x27;Chenwei Jiang (used for learning) &lt;cheverjonathan@gmail.com&gt;&#x27;
[gpg] ls
public-key.txt
[gpg] cat public-key.txt
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBGUJt+QBEADAzoLPAdK8GfJ/5Ouxh2rOrMsClMmoOznMm2GOBcqSaQsdmP4G
................................................................
oM3YFFujtMxK/cQ/KkbmwAtlMkWx5x8RT/dJ
=NMtR
-----END PGP PUBLIC KEY BLOCK-----
[gpg]
</code></pre>
<p>As shown above, this demonstrates the steps to export <code>public-key.txt</code>. The detailed command is:</p><pre><code class="lang-shell">gpg --armor --output public-key.txt --export &#x27;Chenwei Jiang (used for learning) &lt;cheverjonathan@gmail.com&gt;&#x27;
</code></pre>
<p>Below are the steps to export <code>private-key.txt</code>:</p><pre><code class="lang-shell">[gpg] ls
public-key.txt
[gpg] gpg --armor --output private-key.txt --export-secret-keys
[gpg] ls
private-key.txt  public-key.txt
[gpg]
</code></pre>
<p>If you previously set a password, this operation requires the password. The command requiring password is:</p><pre><code class="lang-shell">gpg --armor --output private-key.txt --export-secret-keys
</code></pre>
<h3 id="uploading-public-keys">Uploading Public Keys</h3><p>Public key servers are network servers specifically for storing user public keys. The <code>--send-keys</code> subcommand can achieve this.</p><p>Public key servers have no verification mechanism - anyone can upload public keys in your name, so there&#x27;s no guarantee of reliability for public keys on servers.</p><p>Generally, we publish a public key fingerprint on our own website for others to verify that the downloaded public key is authentic. The <code>--fingerprint</code> subcommand can generate a public key fingerprint.</p><h2 id="hands-on-practice--practice-makes-perfect">Hands-On Practice / Practice Makes Perfect</h2><p>In this section, I&#x27;ll demonstrate exporting public keys, encrypting with private keys, and decrypting with public keys on another device.</p><p>I&#x27;ll show how to encrypt a file containing &quot;hello world&quot; and then decrypt it remotely.</p><p>The steps are divided into encryption and decryption.</p><p>Detailed steps:</p><ol start="1"><li><p>Create file with the following commands:</p><pre><code class="lang-shell">[gpg] touch demo.txt
echo &quot;hello world&quot; &gt; demo.txt
[gpg] ls
demo.txt  private-key.txt  public-key.txt
[gpg] cat demo.txt
hello world
[gpg]
</code></pre>
<p>The file creation commands are:</p><pre><code class="lang-shell">touch demo.txt
echo &quot;hello world&quot; &gt; demo.txt
</code></pre>
<p>Now we have a demo.txt file containing &quot;hello world&quot;.</p></li><li><p>Encrypt the file:</p><pre><code class="lang-shell">[gpg] ls
demo.txt  private-key.txt  public-key.txt
[gpg] gpg --recipient &#x27;cheverjonathan@gmail.com&#x27; --output demo.gpg --encrypt demo.txt
[gpg] ls
demo.gpg  demo.txt  private-key.txt  public-key.txt
[gpg] cat demo.gpg
�
..............
�&amp;5p�.�)I�槹iQ%
[gpg]
</code></pre>
<p>Encryption command:</p><pre><code class="lang-shell">gpg --recipient &#x27;cheverjonathan@gmail.com&#x27; --output demo.gpg --encrypt demo.txt
</code></pre>
<p>You can see that demo.gpg is the encrypted file.</p></li><li><p>Decrypt the file:</p><pre><code class="lang-shell">[gpg] gpg --output demo --decrypt demo.gpg
gpg: encrypted with rsa4096 key, ID 13C117D5FEC4F051, created 2023-09-19
      &quot;Chenwei Jiang (Used for learning) &lt;cheverjonathan@gmail.com&gt;&quot;
[gpg] ls
demo  demo.gpg  demo.txt  private-key.txt  public-key.txt
[gpg] cat demo
hello world
[gpg]
</code></pre>
<p>Decryption command:</p><pre><code class="lang-shell">gpg --output demo --decrypt demo.gpg
</code></pre>
<p>After entering this command, you need to input the password set earlier, then you can get the file.</p><p>The above demonstrates the process of encrypting files using local keys and decrypting them locally.</p><p>Next, let&#x27;s begin...</p><h3 id="obtaining-files-on-other-computers-encrypting-them-then-having-the-original-host-decrypt-using-private-keys">Obtaining Files on Other Computers, Encrypting Them, Then Having the Original Host Decrypt Using Private Keys</h3><p>First, we transfer public-key.txt to another host. You can use GitHub&#x27;s functionality to upload/download files. For convenience, I&#x27;ll upload the following file tree from the above process:</p><pre><code class="lang-Shell">cheverjohn@Dell-G33579 git:(doc/test-gpg*)% tree ~/workspace/Opensource/github.com/Chever-John/cheverjohn.me/docs/wait-for-publish/gpg
.
├── demo
├── demo.gpg
├── demo.txt
├── private-key.txt
└── public-key.txt
</code></pre>
<p>After removing private-key.txt, I&#x27;ll upload the files to GitHub.</p><p>You can see I&#x27;ve downloaded the public-key.txt file on another host, as shown in the file tree:</p><pre><code class="lang-shell">cheverjohn:wait-for-publish/ git:(doc/test-gpg*)$ tree gpg                                                                   [0:54:06]
gpg
├── demo
├── demo.gpg
├── demo.txt
└── public-key.txt

1 directory, 4 files
</code></pre>
<p>Next, I need to use this public-key.txt to encrypt a file, then transfer this file back to the host with the private key for decryption.</p><p>First, we need to import this public-key.txt locally:</p><pre><code class="lang-shell">gpg --import public-key.txt 
</code></pre>
<p>Command execution result:</p><pre><code class="lang-shell">cheverjohn:gpg/ git:(doc/test-gpg*)$ gpg --import public-key.txt                                                             [1:20:11]
gpg: key 3BE465D20064251C: public key &quot;Chenwei Jiang (Learning second) &lt;cheverjonathan@gmail.com&gt;&quot; imported
gpg: Total number processed: 1
gpg:               imported: 1
</code></pre>
<p>Now we need to use this public key to encrypt text.</p><p>First, let&#x27;s create a file hello-no-encrypt.txt:</p><pre><code class="lang-shell">cheverjohn:gpg/ git:(doc/test-gpg*)$ touch hello-no-encrypt.txt                                                              [1:16:07]
cheverjohn:gpg/ git:(doc/test-gpg*)$ nvim hello-no-encrypt.txt                                                               [1:16:15]
cheverjohn:gpg/ git:(doc/test-gpg*)$ cat hello-no-encrypt.txt                                                                [1:16:24]
Hello Sasa!
cheverjohn:gpg/ git:(doc/test-gpg*)$ tree                                                                                    [1:16:28]
.
├── demo
├── demo.gpg
├── demo.txt
├── hello-no-encrypt.txt
└── public-key.txt

1 directory, 5 files
</code></pre>
<p>We need to encrypt this file and then send it to the host with the private key for decryption.</p><p>The entire encryption process:</p><pre><code class="lang-shell">cheverjohn:gpg/ git:(doc/test-gpg*)$ gpg --output hello-encrypted.gpg --encrypt --recipient &#x27;Chenwei Jiang (Learning second) &lt;cheverjonathan@gmail.com&gt;&#x27; hello-no-encrypt.txt
gpg: 617C5542800731C1: There is no assurance this key belongs to the named user

sub  rsa4096/617C5542800731C1 2023-09-21 Chenwei Jiang (Learning second) &lt;cheverjonathan@gmail.com&gt;
 Primary key fingerprint: 5588 D37D AF51 50FD 9186  47E7 3BE4 65D2 0064 251C
      Subkey fingerprint: A038 0DA1 D481 62C7 517C  D6C6 617C 5542 8007 31C1

It is NOT certain that the key belongs to the person named
in the user ID.  If you *really* know what you are doing,
you may answer the next question with yes.

Use this key anyway? (y/N) y
</code></pre>
<p>Encryption command:</p><pre><code class="lang-shell">gpg --output hello-encrypted.gpg --encrypt --recipient &#x27;Chenwei Jiang (Learning second) &lt;cheverjonathan@gmail.com&gt;&#x27; hello-no-encrypt.txt
</code></pre>
<p>Then we need to upload the encrypted file hello-encrypted.gpg to the machine with the private key.</p><p>Again, I&#x27;ll use GitHub as the file transfer tool.</p><p>You can see I&#x27;ve obtained the file on the host machine (the machine with the keys) and started decryption:</p><pre><code class="lang-shell">[gpg] tree                                                                                                         git:(doc/test-gpg)
.
├── demo
├── demo.gpg
├── demo.txt
├── hello-encrypted.gpg
├── hello-no-encrypt.txt
└── public-key.txt

1 directory, 6 files
</code></pre>
<p>Next, we need to decrypt hello-encrypted.gpg:</p><pre><code class="lang-shell">[gpg] gpg --output hello-decrypted.txt --decrypt hello-encrypted.gpg                                               git:(doc/test-gpg)
gpg: encrypted with rsa4096 key, ID 617C5542800731C1, created 2023-09-21
      &quot;Chenwei Jiang (Learning second) &lt;cheverjonathan@gmail.com&gt;&quot;
[gpg] ls                                                                                                        git:(doc/test-gpg*)  ✱
demo  demo.gpg  demo.txt  hello-decrypted.txt  hello-encrypted.gpg  hello-no-encrypt.txt  public-key.txt
[gpg] cat hello-decrypted.txt                                                                                   git:(doc/test-gpg*)  ✱
Hello Sasa!
</code></pre>
<p>Decryption command:</p><pre><code class="lang-shell">gpg --output hello-decrypted.txt --decrypt hello-encrypted.gpg
</code></pre>
</li></ol><h2 id="related-articles">Related Articles</h2><ul><li><a href="https://www.gnupg.org/gph/en/manual/x110.html">gnupg.org Official Manual</a></li></ul></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>Cryptography</category>
            <category>GPG</category>
        </item>
        <item>
            <title><![CDATA[Hello, I am Chever John]]></title>
            <link>https://blog.cheverjohn.me/hello-world</link>
            <guid>https://blog.cheverjohn.me/hello-world</guid>
            <pubDate>Tue, 27 Dec 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Hello! I am Chever John, a software engineer.]]></description>
            <content:encoded><![CDATA[<article><div><p>Hello!</p><p>I am Chever John, a software engineer.</p></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>hello</category>
            <category>world</category>
        </item>
        <item>
            <title><![CDATA[How to run go plugin runner with Apisix-Ingress-controller]]></title>
            <link>https://blog.cheverjohn.me/how-to-run-go-plugin-runner-in-apisix-ingress</link>
            <guid>https://blog.cheverjohn.me/how-to-run-go-plugin-runner-in-apisix-ingress</guid>
            <pubDate>Fri, 29 Apr 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Just as title says. Background Description While wandering around the community, I found a user confused about "how to use multilingual plugins in APISIX Ingress environment". I happen to be a user of go-plugin-runner and have a little knowledge of the APISIX Ingress project, so this document was bo...]]></description>
            <content:encoded><![CDATA[<article><div><p>Just as title says.</p><h2 id="background-description">Background Description</h2><p>While wandering around the community, I found a user confused about &quot;how to use multilingual plugins in APISIX Ingress environment&quot;. I happen to be a user of go-plugin-runner and have a little knowledge of the APISIX Ingress project, so this document was born.</p>
<h2 id="proposal-description">Proposal Description</h2><p>Based on version 0.3 of the go-plugin-runner plugin and version 1.4.0 of APISIX Ingress, this article goes through building the cluster, building the image, customizing the helm chart package, and finally, deploying the resources. It is guaranteed that the final result can be derived in full based on this documentation.</p><pre><code class="lang-bash">go-plugin-runner: 0.3
APISIX Ingress: 1.4.0

kind: kind v0.12.0 go1.17.8 linux/amd64
kubectl version: Client Version: v1.23.5/Server Version: v1.23.4
golang: go1.18 linux/amd64
</code></pre>
<h2 id="begin">Begin</h2><h3 id="build-a-cluster-environment">Build a cluster environment</h3><p>Select <code>kind</code> to build a local cluster environment. The command is as follows:</p><pre><code class="lang-bash">cat &lt;&lt;EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  kubeadmConfigPatches:
  - |
    kind: InitConfiguration
    nodeRegistration:
      kubeletExtraArgs:
        node-labels: &quot;ingress-ready=true&quot;
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
EOF
</code></pre>
<h3 id="build-the-go-plugin-runner-executable">Build the go-plugin-runner executable</h3><p>If you have finished writing the plugin, you can start compiling the executable to run with APISIX.</p><p>This article recommends two packaging build options.</p><ol start="1"><li>put the packaging process into the Dockerfile and finish the compilation process when you build the docker image later.</li><li>You can also follow the scheme used in this document, building the executable first and then copying the packaged executable to the image.</li></ol><p>How you choose the option should depend on your local hardware considerations. The reason for selecting the second option here is that I want to rely on my powerful local hardware to increase the building speed and speed up the process.</p><h3 id="go-to-the-go-plugin-runner-directory">Go to the go-plugin-runner directory</h3><p>Choose a folder address <code>/home/chever/api7/cloud_native/tasks/plugin-runner</code> and place our <code>apisix-go-plugin-runner</code> project in this folder.</p><p>After successful placement, the file tree is shown below:</p><pre><code class="lang-bash">chever@cloud-native-01:~/api7/cloud_native/tasks/plugin-runner$ tree -L 1
.
└── apisix-go-plugin-runner

1 directory, 0 files
</code></pre>
<p>Then you need to go to the <code>apisix-go-plugin-runner/cmd/go-runner/plugins</code> directory and write the plugins you need in that directory. This article will use the default plugin <code>say</code> for demonstration purposes.</p><pre><code class="lang-bash">chever@cloud-native-01:~/api7/cloud_native/tasks/plugin-runner/apisix-go-plugin-runner$ tree cmd
cmd
└── go-runner
    ├── main.go
    ├── main_test.go
    ├── plugins
    │   ├── fault_injection.go
    │   ├── fault_injection_test.go
    │   ├── limit_req.go
    │   ├── limit_req_test.go
    │   ├── say.go
    │   └── say_test.go
    └── version.go
    
2 directories, 10 files
</code></pre>
<p>After writing the plugins, start compiling the executable formally, and note here that you should build static executables, not dynamic ones.</p><p>The package compile command is as follows.</p><pre><code class="lang-bash">CGO_ENABLED=0 go build -a -ldflags &#x27;-extldflags &quot;-static&quot;&#x27; .
</code></pre>
<p>This successfully packages a statically compiled <code>go-runner</code> executable.</p><p>In the <code>apisix-go-plugin-runner/cmd/go-runner/</code> directory, you can see that the current file tree looks like this:</p><pre><code class="lang-bash">chever@cloud-native-01:~/api7/cloud_native/tasks/plugin-runner/apisix-go-plugin-runner/cmd/go-runner$ tree -L 1
.
├── go-runner
├── main.go
├── main_test.go
├── plugins
└── version.go

1 directory, 4 files
</code></pre>
<p>Please remember the path <code>apisix-go-plugin-runner/cmd/go-runner/go-runner</code>, we will use it later.</p><h3 id="build-docker-image">Build Docker Image</h3><p>The image is built here in preparation for installing APISIX later using <code>helm</code>.</p><h4 id="write-dockerfile">Write Dockerfile</h4><p>Return to the path <code>/home/chever/api7/cloud_native/tasks/plugin-runner</code> and create a Dockerfile in that directory, a demonstration of which is given here.</p><pre><code class="lang-dockerfile">ARG ENABLE_PROXY=false

# Build Apache APISIX
FROM api7/apisix-base:1.19.9.1.5

ADD ./apisix-go-plugin-runner /usr/local/apisix-go-plugin-runner

ARG APISIX_VERSION=2.13.1
LABEL apisix_version=&quot;${APISIX_VERSION}&quot;

ARG ENABLE_PROXY
RUN set -x \
    &amp;&amp; (test &quot;${ENABLE_PROXY}&quot; != &quot;true&quot; || /bin/sed -i &#x27;s,http://dl-cdn.alpinelinux.org,https://mirrors.aliyun.com,g&#x27; /etc/apk/repositories) \
    &amp;&amp; apk add --no-cache --virtual .builddeps \
        build-base \
        automake \
        autoconf \
        make \
        libtool \
        pkgconfig \
        cmake \
        unzip \
        curl \
        openssl \
        git \
        openldap-dev \
    &amp;&amp; luarocks install https://github.com/apache/apisix/raw/master/rockspec/apisix-${APISIX_VERSION}-0.rockspec --tree=/usr/local/apisix/deps PCRE_DIR=/usr/local/openresty/pcre \
    &amp;&amp; cp -v /usr/local/apisix/deps/lib/luarocks/rocks-5.1/apisix/${APISIX_VERSION}-0/bin/apisix /usr/bin/ \
    &amp;&amp; (function ver_lt { [ &quot;$1&quot; = &quot;$2&quot; ] &amp;&amp; return 1 || [ &quot;$1&quot; = &quot;`echo -e &quot;$1\n$2&quot; | sort -V | head -n1`&quot; ]; };  if [ &quot;$APISIX_VERSION&quot; = &quot;master&quot; ] || ver_lt 2.2.0 $APISIX_VERSION; then echo &#x27;use shell &#x27;;else bin=&#x27;#! /usr/local/openresty/luajit/bin/luajit\npackage.path = &quot;/usr/local/apisix/?.lua;&quot; .. package.path&#x27;; sed -i &quot;1s@.*@$bin@&quot; /usr/bin/apisix ; fi;) \
    &amp;&amp; mv /usr/local/apisix/deps/share/lua/5.1/apisix /usr/local/apisix \
    &amp;&amp; apk del .builddeps \
    &amp;&amp; apk add --no-cache \
        bash \
        curl \
        libstdc++ \
        openldap \
        tzdata \
    # forward request and error logs to docker log collector
    &amp;&amp; ln -sf /dev/stdout /usr/local/apisix/logs/access.log \
    &amp;&amp; ln -sf /dev/stderr /usr/local/apisix/logs/error.log

WORKDIR /usr/local/apisix

ENV PATH=$PATH:/usr/local/openresty/luajit/bin:/usr/local/openresty/nginx/sbin:/usr/local/openresty/bin

EXPOSE 9080 9443

CMD [&quot;sh&quot;, &quot;-c&quot;, &quot;/usr/bin/apisix init &amp;&amp; /usr/bin/apisix init_etcd &amp;&amp; /usr/local/openresty/bin/openresty -p /usr/local/apisix -g &#x27;daemon off;&#x27;&quot;]

STOPSIGNAL SIGQUIT
</code></pre>
<p>这份 Dockerfile 配置文档，来源于这个<a href="https://github.com/apache/apisix-docker/blob/master/alpine/Dockerfile">链接</a>。我做的唯一修改如下：</p><pre><code class="lang-bash">ARG ENABLE_PROXY=false

# Build Apache APISIX
FROM api7/apisix-base:1.19.9.1.5

ADD ./apisix-go-plugin-runner /usr/local/apisix-go-plugin-runner

ARG APISIX_VERSION=2.13.1
LABEL apisix_version=&quot;${APISIX_VERSION}&quot;

ARG ENABLE_PROXY

</code></pre>
<p>Package all the <code>/apisix-go-plugin-runner</code> files in the <code>/home/chever/api7/cloud_native/tasks/plugin-runner</code> directory into a Docker image. Note down the location of the executable <code>apisix-go-plugin-runner/cmd/go-runner/go-runner</code> and the location of the <code>/usr/local/apisix-go-plugin-runner</code> directory in the Dockerfile above to get the final location of the executable in the Docker image is located as follows.</p><pre><code class="lang-bash">/usr/local/apisix-go-plugin-runner/cmd/go-runner/go-runner
</code></pre>
<p>Please make a note of this address. We will use it in the rest of the configuration.</p><h4 id="begin-to-build-docker-image">Begin to build Docker Image</h4><p>Start building a Docker image based on the Dockerfile. The command is executed in the <code>/home/chever/api7/cloud_native/tasks/plugin-runner</code> directory. The command is as follows:</p><pre><code class="lang-bash">docker build -t apisix/forrunner:0.1 .
</code></pre>
<p>Command Explanation: Build an image with the name <code>apisix/forrunner</code> and mark it as version 0.1.</p><h4 id="load-the-image-to-the-cluster-environment">Load the image to the cluster environment</h4><pre><code class="lang-bash">kind  load docker-image apisix/forrunner:0.1 
</code></pre>
<p>Load the image into the kind cluster environment to pull the custom local image for installation during the helm installation.</p><h3 id="install-apisix-ingress">Install APISIX Ingress</h3><h4 id="customize-the-helm-chart">Customize the helm chart</h4><p>This section focuses on modifying the <code>values.yaml</code> file in the official helm package so that it can install locally packaged images and run the <code>go-plugin-runner</code> executable properly.</p><h5 id="fetch-official-helm-chart">Fetch Official helm chart</h5><p>First, fetch the latest apisix helm chart package with the following command:</p><pre><code class="lang-bash">helm fetch apisix/apisix
</code></pre>
<p>The file tree is as follows:</p><pre><code class="lang-bash">chever@cloud-native-01:~/api7/cloud_native/tasks/plugin-runner$ tree -L 1
.
├── apisix-0.9.1.tgz
└── apisix-go-plugin-runner

1 directory, 1 file
</code></pre>
<h5 id="unzip">Unzip</h5><p>Unzip the <code>apisix-0.9.1.tgz</code> file and prepare to rewrite the configuration. The unzip command is as follows.</p><pre><code class="lang-bash">tar zxvf apisix-0.9.1.tgz
</code></pre>
<p>The file tree is as follows:</p><pre><code class="lang-bash">chever@cloud-native-01:~/api7/cloud_native/tasks/plugin-runner$ tree -L 1
.
├── apisix
├── apisix-0.9.1.tgz
└── apisix-go-plugin-runner

2 directories, 1 file
</code></pre>
<h5 id="change-valuesyaml">Change <code>values.yaml</code></h5><p>Go to the <code>apisix</code> folder and modify the <code>values.yaml</code> file. The two changes are as follows:</p><pre><code class="lang-yaml">image:
  repository: apisix/forrunner
  pullPolicy: IfNotPresent
  # Overrides the image tag whose default is the chart appVersion.
  tag: 0.1
</code></pre>
<p>The first change sets the image for the helm installation to be a locally packaged image of your own.</p><pre><code class="lang-yaml">extPlugin:
  enabled: true
  cmd: [&quot;/usr/local/apisix-go-plugin-runner/cmd/go-runner/go-runner&quot;, &quot;run&quot;]
</code></pre>
<p>The second change sets the position of go-runner in the container after running the container.</p><h5 id="compress-the-modified-helm-chart">Compress the modified helm chart</h5><p>Once configured, compress the <code>apisix</code> file. The compression command is as follows:</p><pre><code class="lang-bash">tar zcvf apisix.tgz apisix/
</code></pre>
<p>The compressed file is obtained, at which point the file tree is as follows:</p><pre><code class="lang-bash">chever@cloud-native-01:~/api7/cloud_native/tasks/plugin-runner$ tree -L 1
.
├── apisix
├── apisix-0.9.1.tgz
├── apisix-go-plugin-runner
└── apisix.tgz

2 directories, 2 files
</code></pre>
<h4 id="execute-the-helm-install-command">Execute the helm install command</h4><h5 id="create-namespace">Create namespace</h5><p>Before installation, create namespaces with the following command:</p><pre><code class="lang-bash">kubectl create ns ingress-apisix
</code></pre>
<p>Then install APISIX using helm with the following command:</p><pre><code class="lang-bash">helm install apisix ./apisix.tgz --set gateway.type=NodePort --set ingress-controller.enabled=true --namespace ingress-apisix --set ingress-controller.config.apisix.serviceNamespace=ingress-apisix
</code></pre>
<h3 id="create-httpbin-service-and-apisixroute-resources">Create httpbin service and ApisixRoute resources</h3><p>Create an httpbin backend resource to run with the deployed ApisixRoute resource to test that the functionality is working correctly.</p><h4 id="create-httpbin-service">Create httpbin service</h4><p>Create an httpbin service with the following command:</p><pre><code class="lang-bash">kubectl run httpbin --image kennethreitz/httpbin --port 80
</code></pre>
<p>Expose the port with the following command:</p><pre><code class="lang-bash">kubectl expose pod httpbin --port 80
</code></pre>
<h4 id="create-apisixroute-resource">Create ApisixRoute Resource</h4><p>Create the <code>go-plugin-runner-route.yaml</code> file to enable the ApisixRoute resource, with the following configuration file:</p><pre><code class="lang-yaml">apiVersion: apisix.apache.org/v2beta3
kind: ApisixRoute
metadata:
  name: plugin-runner-demo
spec:
  http:
  - name: rule1
    match:
      hosts:
      - local.httpbin.org
      paths:
      - /get
    backends:
    - serviceName: httpbin
      servicePort: 80
    plugins:
    - name: ext-plugin-pre-req
      enable: true
      config:
        conf:
        - name: &quot;say&quot;
          value: &quot;{\&quot;body\&quot;: \&quot;hello\&quot;}&quot;
</code></pre>
<p>The create resource command is as follows:</p><pre><code class="lang-bash">kubectl apply -f go-plugin-runner-route.yaml
</code></pre>
<h3 id="test">Test</h3><p>The command is as follows to test if the plugin written in Golang is working correctly:</p><pre><code class="lang-bash">kubectl exec -it -n ${namespace of Apache APISIX} ${Pod name of Apache APISIX} -- curl http://127.0.0.1:9080/get -H &#x27;Host: local.httpbin.org&#x27;
</code></pre>
<p>Here I derived from the <code>kubectl get pods --all-namespaces</code> command that the <code>${namespace of Apache APISIX}</code> and <code>${Pod name of Apache APISIX}</code> parameters here are <code>ingress-apisix</code> and <code>apisix- 55d476c64-s5lzw</code>, execute the command as follows:</p><pre><code class="lang-bash">kubectl exec -it -n ingress-apisix apisix-55d476c64-s5lzw -- curl http://127.0.0.1:9080/get -H &#x27;Host: local.httpbin.org&#x27;
</code></pre>
<p>The expected response obtained is:</p><pre><code class="lang-bash">chever@cloud-native-01:~/api7/cloud_native/tasks/plugin-runner$ kubectl exec -it -n ingress-apisix apisix-55d476c64-s5lzw -- curl http://127.0.0.1:9080/get -H &#x27;Host: local.httpbin.org&#x27;
Defaulted container &quot;apisix&quot; out of: apisix, wait-etcd (init)
hello
</code></pre></div></article>]]></content:encoded>
            <author>cheverjonathan@gmail.com (Chenwei Jiang)</author>
            <category>go</category>
        </item>
    </channel>
</rss>