<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Devops on IT Comparison</title><link>https://comparison.metacog.co.kr/tags/devops/</link><description>Recent content in Devops on IT Comparison</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Mon, 03 Aug 2026 06:33:25 +0900</lastBuildDate><atom:link href="https://comparison.metacog.co.kr/tags/devops/index.xml" rel="self" type="application/rss+xml"/><item><title>Auto Scaling vs Manual Scaling: Who Adjusts Capacity?</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-auto-scaling-vs-manual-scaling-who-adjusts-capacity/</link><pubDate>Mon, 03 Aug 2026 06:33:25 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-auto-scaling-vs-manual-scaling-who-adjusts-capacity/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Auto scaling and manual scaling both change how much compute capacity an application has, but they differ in who — or what — decides when that change happens. Auto scaling relies on an &lt;strong class="kw"&gt;automated feedback loop&lt;/strong&gt; that watches metrics and reacts on its own, while manual scaling depends on &lt;strong class="kw"&gt;human intervention&lt;/strong&gt; to notice load and issue the change. That difference in decision-maker drives everything else: reaction speed, cost efficiency, and how much ongoing attention the system needs.&lt;/p&gt;</description></item><item><title>Managed Database vs Self-Hosted Database: Who Runs the Stack</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-managed-database-vs-self-hosted-database-who-runs-the-stack/</link><pubDate>Mon, 03 Aug 2026 06:30:21 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-managed-database-vs-self-hosted-database-who-runs-the-stack/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;A managed database hands the operating system, patching, backups, and failover to a &lt;strong class="kw"&gt;cloud provider&lt;/strong&gt;, while a self-hosted database keeps the entire stack under your team&amp;rsquo;s &lt;strong class="kw"&gt;direct control&lt;/strong&gt;. The choice trades operational convenience against flexibility, cost structure, and how much low-level tuning you&amp;rsquo;re allowed to do.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;text x="160" y="30" text-anchor="middle" font-size="16" font-weight="bold" style="fill:var(--primary)"&gt;Managed Database&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" font-size="16" font-weight="bold" style="fill:var(--primary)"&gt;Self-Hosted Database&lt;/text&gt;&lt;rect x="60" y="55" width="200" height="45" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="160" y="82" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Application / Queries&lt;/text&gt;&lt;rect x="50" y="110" width="220" height="195" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="128" text-anchor="middle" font-size="12" font-weight="600" style="fill:var(--compare-a)"&gt;Provider Manages&lt;/text&gt;&lt;rect x="70" y="140" width="180" height="40" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="160" y="164" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;DB Engine&lt;/text&gt;&lt;rect x="70" y="195" width="180" height="40" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="160" y="219" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Operating System&lt;/text&gt;&lt;rect x="70" y="250" width="180" height="40" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="160" y="274" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Hardware / Storage&lt;/text&gt;&lt;text x="160" y="330" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Less control, less toil&lt;/text&gt;&lt;rect x="370" y="55" width="220" height="250" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="73" text-anchor="middle" font-size="12" font-weight="600" style="fill:var(--compare-b)"&gt;You Manage Everything&lt;/text&gt;&lt;rect x="390" y="85" width="180" height="40" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="480" y="109" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Application / Queries&lt;/text&gt;&lt;rect x="390" y="140" width="180" height="40" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="480" y="164" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;DB Engine&lt;/text&gt;&lt;rect x="390" y="195" width="180" height="40" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="480" y="219" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Operating System&lt;/text&gt;&lt;rect x="390" y="250" width="180" height="40" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="480" y="274" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Hardware / Storage&lt;/text&gt;&lt;text x="480" y="330" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;More control, more toil&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Managed Database&lt;/th&gt;
&lt;th&gt;Self-Hosted Database&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Provisioning &amp;amp; setup&lt;/td&gt;
&lt;td&gt;Spin up via console or API in minutes; provider installs and configures the engine&lt;/td&gt;
&lt;td&gt;Manually install and configure the OS, storage, and database software yourself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration &amp;amp; tuning access&lt;/td&gt;
&lt;td&gt;Limited to exposed parameters and flags; some engine internals and OS access are locked&lt;/td&gt;
&lt;td&gt;Full root or admin access to every config file, kernel setting, and storage layout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Resize compute or add read replicas with a click or API call; provider automates the process&lt;/td&gt;
&lt;td&gt;Provision new hardware and reconfigure sharding or replication topology yourself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backups &amp;amp; recovery&lt;/td&gt;
&lt;td&gt;Automated snapshots and point-in-time restore built into the service&lt;/td&gt;
&lt;td&gt;You script, schedule, and test your own backup and restore procedures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Patching &amp;amp; upgrades&lt;/td&gt;
&lt;td&gt;Provider applies OS and engine security patches on a maintenance schedule&lt;/td&gt;
&lt;td&gt;You plan, test, and execute every patch and major version upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High availability &amp;amp; failover&lt;/td&gt;
&lt;td&gt;Multi-AZ replication and automatic failover configured with a toggle&lt;/td&gt;
&lt;td&gt;You design, build, and test the replication and failover setup yourself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monitoring &amp;amp; support&lt;/td&gt;
&lt;td&gt;Built-in dashboards and alerts, plus vendor support tickets for engine-level issues&lt;/td&gt;
&lt;td&gt;You assemble your own monitoring stack; support is internal or community-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost model&lt;/td&gt;
&lt;td&gt;Higher per-hour price that bundles operational labor into the bill&lt;/td&gt;
&lt;td&gt;Lower raw infrastructure cost but a hidden cost in engineering time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Managed services abstract patching and OS maintenance behind a &lt;strong class="kw"&gt;provider&lt;/strong&gt; SLA.&lt;/li&gt;
&lt;li&gt;Self-hosted setups grant full &lt;strong class="kw"&gt;root access&lt;/strong&gt; to tune kernel, storage, and engine internals.&lt;/li&gt;
&lt;li&gt;Failover and multi-AZ replication are &lt;strong class="kw"&gt;automated&lt;/strong&gt; in managed offerings but hand-built elsewhere.&lt;/li&gt;
&lt;li&gt;Cost shifts from engineering hours to a recurring &lt;strong class="kw"&gt;subscription fee&lt;/strong&gt; with managed databases.&lt;/li&gt;
&lt;li&gt;Self-hosting permits any &lt;strong class="kw"&gt;custom extension&lt;/strong&gt; or fork that managed platforms often restrict.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Managed Database&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Docker Swarm vs Kubernetes: Container Orchestration Compared</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-docker-swarm-vs-kubernetes-container-orchestration-compared/</link><pubDate>Mon, 03 Aug 2026 05:29:45 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-docker-swarm-vs-kubernetes-container-orchestration-compared/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Docker Swarm and Kubernetes are both container orchestration platforms that manage deployment, scaling, and networking of containerized applications, but they differ sharply in operational complexity and feature depth. Swarm prioritizes &lt;strong class="kw"&gt;simplicity&lt;/strong&gt;, integrating directly into the Docker CLI for fast setup, while Kubernetes offers a far more &lt;strong class="kw"&gt;extensible&lt;/strong&gt;, battle-tested platform built for large-scale, production-grade workloads.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4 4"/&gt;&lt;text x="160" y="34" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Docker Swarm&lt;/text&gt;&lt;text x="480" y="34" text-anchor="middle" style="fill:var(--primary)" font-size="18" font-weight="bold"&gt;Kubernetes&lt;/text&gt;&lt;rect x="60" y="50" width="200" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="73" text-anchor="middle" style="fill:var(--content)" font-size="13" font-weight="bold"&gt;Swarm Manager&lt;/text&gt;&lt;text x="160" y="92" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Raft consensus store&lt;/text&gt;&lt;line x1="105" y1="105" x2="105" y2="150" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;line x1="215" y1="105" x2="230" y2="150" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="35" y="150" width="140" height="90" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="105" y="168" text-anchor="middle" style="fill:var(--content)" font-size="12" font-weight="bold"&gt;Worker Node&lt;/text&gt;&lt;rect x="50" y="180" width="50" height="24" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;text x="75" y="196" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Task&lt;/text&gt;&lt;rect x="110" y="180" width="50" height="24" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;text x="135" y="196" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Task&lt;/text&gt;&lt;rect x="50" y="210" width="110" height="24" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;text x="105" y="226" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Task&lt;/text&gt;&lt;rect x="185" y="150" width="90" height="90" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="230" y="168" text-anchor="middle" style="fill:var(--content)" font-size="12" font-weight="bold"&gt;Worker Node&lt;/text&gt;&lt;rect x="200" y="185" width="60" height="24" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;text x="230" y="201" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Task&lt;/text&gt;&lt;rect x="200" y="212" width="60" height="24" rx="3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1"/&gt;&lt;text x="230" y="228" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Task&lt;/text&gt;&lt;text x="160" y="320" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;2 node roles: manager + worker&lt;/text&gt;&lt;rect x="370" y="50" width="240" height="95" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="490" y="66" text-anchor="middle" style="fill:var(--content)" font-size="12" font-weight="bold"&gt;Control Plane&lt;/text&gt;&lt;rect x="380" y="74" width="105" height="26" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1"/&gt;&lt;text x="432" y="91" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;API Server&lt;/text&gt;&lt;rect x="495" y="74" width="105" height="26" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1"/&gt;&lt;text x="547" y="91" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;etcd&lt;/text&gt;&lt;rect x="380" y="105" width="105" height="26" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1"/&gt;&lt;text x="432" y="122" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Scheduler&lt;/text&gt;&lt;rect x="495" y="105" width="105" height="26" rx="3" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1"/&gt;&lt;text x="547" y="122" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Controller Mgr&lt;/text&gt;&lt;line x1="420" y1="145" x2="420" y2="170" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;line x1="560" y1="145" x2="560" y2="170" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="365" y="170" width="110" height="100" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="420" y="188" text-anchor="middle" style="fill:var(--content)" font-size="12" font-weight="bold"&gt;Worker Node&lt;/text&gt;&lt;text x="420" y="204" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;kubelet&lt;/text&gt;&lt;rect x="380" y="212" width="80" height="45" rx="4" style="fill:none;stroke:var(--compare-b)" stroke-width="1" stroke-dasharray="3 2"/&gt;&lt;text x="420" y="224" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;Pod&lt;/text&gt;&lt;rect x="386" y="230" width="30" height="18" rx="2" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1"/&gt;&lt;rect x="422" y="230" width="30" height="18" rx="2" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1"/&gt;&lt;rect x="505" y="170" width="110" height="100" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="560" y="188" text-anchor="middle" style="fill:var(--content)" font-size="12" font-weight="bold"&gt;Worker Node&lt;/text&gt;&lt;text x="560" y="204" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;kubelet&lt;/text&gt;&lt;rect x="520" y="212" width="80" height="45" rx="4" style="fill:none;stroke:var(--compare-b)" stroke-width="1" stroke-dasharray="3 2"/&gt;&lt;text x="560" y="224" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;Pod&lt;/text&gt;&lt;rect x="526" y="230" width="30" height="18" rx="2" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1"/&gt;&lt;rect x="562" y="230" width="30" height="18" rx="2" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1"/&gt;&lt;text x="490" y="300" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;Layered control plane + kubelet-managed pods&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Docker Swarm&lt;/th&gt;
&lt;th&gt;Kubernetes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup &amp;amp; installation&lt;/td&gt;
&lt;td&gt;Single command: docker swarm init/join&lt;/td&gt;
&lt;td&gt;Multi-step: kubeadm, managed service, or install tool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster architecture&lt;/td&gt;
&lt;td&gt;Manager nodes (Raft consensus) + worker nodes&lt;/td&gt;
&lt;td&gt;Control plane (API server, etcd, scheduler, controller manager) + worker nodes with kubelet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment unit&lt;/td&gt;
&lt;td&gt;Service made of identical Tasks, one container each&lt;/td&gt;
&lt;td&gt;Pod: one or more co-located, co-scheduled containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Networking&lt;/td&gt;
&lt;td&gt;Built-in overlay network, configured automatically&lt;/td&gt;
&lt;td&gt;Pluggable via CNI plugins (Calico, Cilium, Flannel)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service discovery &amp;amp; load balancing&lt;/td&gt;
&lt;td&gt;Built-in DNS plus routing mesh VIP&lt;/td&gt;
&lt;td&gt;kube-proxy with Service objects and Ingress controllers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling &amp;amp; scheduling&lt;/td&gt;
&lt;td&gt;Basic spread or binpack placement strategies&lt;/td&gt;
&lt;td&gt;Fine-grained scheduling with affinity rules, taints, and resource requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-healing &amp;amp; updates&lt;/td&gt;
&lt;td&gt;Restarts failed tasks, basic rolling updates&lt;/td&gt;
&lt;td&gt;Reconciliation loops, rolling updates, rollbacks, HPA/VPA autoscaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ecosystem &amp;amp; extensibility&lt;/td&gt;
&lt;td&gt;Minimal, small plugin ecosystem&lt;/td&gt;
&lt;td&gt;Vast ecosystem: CRDs, Operators, Helm, service meshes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Docker Swarm favors &lt;strong class="kw"&gt;simplicity&lt;/strong&gt;, built directly into the Docker CLI, while Kubernetes requires setting up a separate &lt;strong class="kw"&gt;control plane&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Swarm deploys single-container &lt;strong class="kw"&gt;Tasks&lt;/strong&gt;, whereas Kubernetes groups containers into &lt;strong class="kw"&gt;Pods&lt;/strong&gt; that share network and storage.&lt;/li&gt;
&lt;li&gt;Kubernetes offers far more granular &lt;strong class="kw"&gt;scheduling&lt;/strong&gt; controls than Swarm&amp;rsquo;s basic spread strategy.&lt;/li&gt;
&lt;li&gt;Kubernetes&amp;rsquo; &lt;strong class="kw"&gt;ecosystem&lt;/strong&gt; of CRDs, Operators, and Helm dwarfs Swarm&amp;rsquo;s, at the cost of a steeper learning curve.&lt;/li&gt;
&lt;li&gt;Swarm networking is &lt;strong class="kw"&gt;automatic&lt;/strong&gt; overlay by default, while Kubernetes relies on pluggable CNI plugins requiring explicit choice.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Docker Swarm&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Horizontal Pod Autoscaler vs Vertical Pod Autoscaler: Scaling Out vs Scaling Up</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-horizontal-pod-autoscaler-vs-vertical-pod-autoscaler-scaling/</link><pubDate>Mon, 03 Aug 2026 05:24:29 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-horizontal-pod-autoscaler-vs-vertical-pod-autoscaler-scaling/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Both controllers watch metrics and adjust Kubernetes workloads automatically, but they scale in different dimensions. The &lt;strong class="kw"&gt;Horizontal Pod Autoscaler&lt;/strong&gt; adds or removes pod replicas to handle load, while the &lt;strong class="kw"&gt;Vertical Pod Autoscaler&lt;/strong&gt; resizes the CPU and memory requests/limits of existing pods.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;line x1="320" y1="50" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 4"/&gt;&lt;text x="160" y="32" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Horizontal Pod Autoscaler&lt;/text&gt;&lt;text x="480" y="32" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Vertical Pod Autoscaler&lt;/text&gt;&lt;text x="160" y="58" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;before&lt;/text&gt;&lt;rect x="130" y="68" width="60" height="48" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="96" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;pod&lt;/text&gt;&lt;line x1="160" y1="122" x2="160" y2="148" style="stroke:var(--compare-a)" stroke-width="2" marker-end="url(#arrowA)"/&gt;&lt;text x="160" y="166" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;after (load increases)&lt;/text&gt;&lt;rect x="70" y="178" width="50" height="44" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="135" y="178" width="50" height="44" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="200" y="178" width="50" height="44" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="95" y="204" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;pod&lt;/text&gt;&lt;text x="160" y="204" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;pod&lt;/text&gt;&lt;text x="225" y="204" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;pod&lt;/text&gt;&lt;text x="160" y="246" text-anchor="middle" style="fill:var(--primary)" font-size="13" font-weight="bold"&gt;Scale OUT&lt;/text&gt;&lt;text x="160" y="264" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;more replicas, same pod size&lt;/text&gt;&lt;text x="480" y="58" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;before&lt;/text&gt;&lt;rect x="455" y="70" width="50" height="36" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="92" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;pod&lt;/text&gt;&lt;line x1="480" y1="122" x2="480" y2="150" style="stroke:var(--compare-b)" stroke-width="2" marker-end="url(#arrowB)"/&gt;&lt;text x="480" y="168" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;after (load increases)&lt;/text&gt;&lt;rect x="430" y="180" width="100" height="96" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="224" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;pod&lt;/text&gt;&lt;text x="480" y="242" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;CPU/mem ↑&lt;/text&gt;&lt;text x="480" y="300" text-anchor="middle" style="fill:var(--primary)" font-size="13" font-weight="bold"&gt;Scale UP&lt;/text&gt;&lt;text x="480" y="318" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;bigger pod, same replica count&lt;/text&gt;&lt;defs&gt;&lt;marker id="arrowA" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Horizontal Pod Autoscaler&lt;/th&gt;
&lt;th&gt;Vertical Pod Autoscaler&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it adjusts&lt;/td&gt;
&lt;td&gt;Number of pod replicas in a Deployment/ReplicaSet/StatefulSet&lt;/td&gt;
&lt;td&gt;CPU and memory requests/limits on the pod&amp;rsquo;s containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics source&lt;/td&gt;
&lt;td&gt;Metrics Server or custom/external metrics (CPU, memory, custom queries) via metrics.k8s.io API&lt;/td&gt;
&lt;td&gt;Historical and current usage sampled by the VPA recommender component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trigger condition&lt;/td&gt;
&lt;td&gt;Observed metric crosses a target threshold averaged across pods&lt;/td&gt;
&lt;td&gt;Recommender detects requests are consistently over- or under-provisioned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action taken&lt;/td&gt;
&lt;td&gt;Creates or deletes pod replicas to match target replica count&lt;/td&gt;
&lt;td&gt;Evicts and recreates pods with new resource requests (or just recommends, depending on updateMode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disruption to running pods&lt;/td&gt;
&lt;td&gt;None — existing pods are untouched, new ones are added or removed&lt;/td&gt;
&lt;td&gt;Pod restart required to apply new resource values, causing brief downtime unless using in-place resize&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best fit for workload type&lt;/td&gt;
&lt;td&gt;Stateless, horizontally scalable services behind a Service/load balancer&lt;/td&gt;
&lt;td&gt;Single-instance or hard-to-replicate workloads, or right-sizing before enabling HPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conflict risk&lt;/td&gt;
&lt;td&gt;Can fight with VPA if both manage CPU on the same workload&lt;/td&gt;
&lt;td&gt;Should not manage CPU/memory targeted by HPA on the same workload simultaneously&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration object&lt;/td&gt;
&lt;td&gt;HorizontalPodAutoscaler resource with min/max replicas and target metrics&lt;/td&gt;
&lt;td&gt;VerticalPodAutoscaler resource with updateMode (Off, Initial, Recreate, Auto)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;HPA changes &lt;strong class="kw"&gt;replica count&lt;/strong&gt;, VPA changes &lt;strong class="kw"&gt;resource requests&lt;/strong&gt; on existing pods.&lt;/li&gt;
&lt;li&gt;VPA updates typically require a &lt;strong class="kw"&gt;pod restart&lt;/strong&gt; to take effect, while HPA scaling adds/removes pods without disrupting the rest.&lt;/li&gt;
&lt;li&gt;Running both on the &lt;strong class="kw"&gt;same metric&lt;/strong&gt; (like CPU) causes conflicting decisions unless carefully scoped.&lt;/li&gt;
&lt;li&gt;VPA is often used in &lt;strong class="kw"&gt;recommendation-only mode&lt;/strong&gt; to right-size requests before HPA takes over scaling.&lt;/li&gt;
&lt;li&gt;HPA assumes the workload is &lt;strong class="kw"&gt;stateless and replicable&lt;/strong&gt;; VPA fits singleton or stateful workloads that can&amp;rsquo;t simply be duplicated.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Horizontal Pod Autoscaler&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Liveness Probe vs Readiness Probe: Kubernetes Health Checks Compared</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-liveness-probe-vs-readiness-probe-kubernetes-health-checks-c/</link><pubDate>Mon, 03 Aug 2026 05:22:16 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-liveness-probe-vs-readiness-probe-kubernetes-health-checks-c/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Kubernetes uses liveness and readiness probes to answer two different questions about a running container: is it alive, and is it ready to serve requests. A failed liveness probe triggers a container &lt;strong class="kw"&gt;restart&lt;/strong&gt;, while a failed readiness probe only affects &lt;strong class="kw"&gt;traffic routing&lt;/strong&gt; by pulling the pod out of Service endpoints without killing it.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;line x1="320" y1="10" x2="320" y2="350" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4 4"/&gt;&lt;text x="160" y="26" text-anchor="middle" style="fill:var(--primary)" font-size="15" font-weight="bold"&gt;Liveness Probe&lt;/text&gt;&lt;text x="480" y="26" text-anchor="middle" style="fill:var(--primary)" font-size="15" font-weight="bold"&gt;Readiness Probe&lt;/text&gt;&lt;rect x="90" y="42" width="140" height="34" rx="4" style="fill:none;stroke:var(--content)"/&gt;&lt;text x="160" y="63" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Container&lt;/text&gt;&lt;line x1="160" y1="76" x2="160" y2="96" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="60" y="96" width="200" height="38" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="119" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;kubelet: is it alive?&lt;/text&gt;&lt;line x1="160" y1="134" x2="160" y2="152" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="160,152 198,180 160,208 122,180" style="fill:none;stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="184" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Fails?&lt;/text&gt;&lt;line x1="160" y1="208" x2="160" y2="240" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="172" y="226" style="fill:var(--secondary)" font-size="10"&gt;yes&lt;/text&gt;&lt;rect x="60" y="240" width="200" height="38" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="263" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Kill &amp;amp; Restart Container&lt;/text&gt;&lt;path d="M60,259 C15,259 15,59 88,59" style="fill:none;stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="88,59 78,54 78,64" style="fill:var(--compare-a)"/&gt;&lt;line x1="122" y1="180" x2="90" y2="180" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="105" y="192" text-anchor="end" style="fill:var(--secondary)" font-size="10"&gt;no&lt;/text&gt;&lt;text x="30" y="184" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;stays&lt;/text&gt;&lt;text x="30" y="196" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;running&lt;/text&gt;&lt;text x="160" y="305" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;No effect on Service traffic&lt;/text&gt;&lt;rect x="410" y="42" width="140" height="34" rx="4" style="fill:none;stroke:var(--content)"/&gt;&lt;text x="480" y="63" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Container&lt;/text&gt;&lt;line x1="480" y1="76" x2="480" y2="96" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="380" y="96" width="200" height="38" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="119" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;kubelet: is it ready?&lt;/text&gt;&lt;line x1="480" y1="134" x2="480" y2="152" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="480,152 518,180 480,208 442,180" style="fill:none;stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="184" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Ready?&lt;/text&gt;&lt;line x1="480" y1="208" x2="480" y2="240" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="492" y="226" style="fill:var(--secondary)" font-size="10"&gt;yes&lt;/text&gt;&lt;rect x="380" y="240" width="200" height="38" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="263" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Added to Service Endpoints&lt;/text&gt;&lt;line x1="518" y1="180" x2="530" y2="180" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="535" y="175" style="fill:var(--secondary)" font-size="10"&gt;no&lt;/text&gt;&lt;rect x="530" y="186" width="80" height="46" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;&lt;text x="570" y="204" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;Removed&lt;/text&gt;&lt;text x="570" y="216" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;(not killed)&lt;/text&gt;&lt;line x1="480" y1="278" x2="480" y2="300" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="390" y="300" width="180" height="34" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5"/&gt;&lt;text x="480" y="321" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Service / Load Balancer&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Liveness Probe&lt;/th&gt;
&lt;th&gt;Readiness Probe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core question&lt;/td&gt;
&lt;td&gt;Is the process still functioning?&lt;/td&gt;
&lt;td&gt;Is the process ready to accept traffic?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action on failure&lt;/td&gt;
&lt;td&gt;kubelet kills and restarts the container&lt;/td&gt;
&lt;td&gt;Container is left running, no restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effect on Service endpoints&lt;/td&gt;
&lt;td&gt;None directly; pod may keep receiving traffic until restart completes&lt;/td&gt;
&lt;td&gt;Pod is removed from Service endpoints, stops receiving traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Effect on rolling deployments&lt;/td&gt;
&lt;td&gt;Not consulted for rollout progress&lt;/td&gt;
&lt;td&gt;Must pass before the pod counts as available and rollout proceeds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restart count impact&lt;/td&gt;
&lt;td&gt;Increments the container restart count on each failure&lt;/td&gt;
&lt;td&gt;Never causes a restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical checks used&lt;/td&gt;
&lt;td&gt;Lightweight self-check for hangs or deadlocks&lt;/td&gt;
&lt;td&gt;Checks dependency health: DB connections, cache warm-up, config load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Misconfiguration risk&lt;/td&gt;
&lt;td&gt;Too-aggressive thresholds cause restart loops (CrashLoopBackOff)&lt;/td&gt;
&lt;td&gt;Too-aggressive thresholds pull healthy pods out of rotation, cutting capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Liveness failure causes a container &lt;strong class="kw"&gt;restart&lt;/strong&gt;; readiness failure only removes the pod from &lt;strong class="kw"&gt;Service endpoints&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Liveness answers &amp;ldquo;is it alive,&amp;rdquo; readiness answers &amp;ldquo;is it &lt;strong class="kw"&gt;ready for traffic&lt;/strong&gt;.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Readiness gates &lt;strong class="kw"&gt;rolling deployments&lt;/strong&gt;; liveness has no say in rollout progress.&lt;/li&gt;
&lt;li&gt;Using a dependency check as a liveness probe risks a &lt;strong class="kw"&gt;restart loop&lt;/strong&gt; when the real problem is an external service, not the process.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Liveness Probe&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Helm vs Kustomize: Templating vs Overlay-Based Kubernetes Config</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-helm-vs-kustomize-templating-vs-overlay-based-kubernetes-con/</link><pubDate>Mon, 03 Aug 2026 05:20:13 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-helm-vs-kustomize-templating-vs-overlay-based-kubernetes-con/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Helm packages Kubernetes manifests as parameterized &lt;strong class="kw"&gt;charts&lt;/strong&gt; rendered through a Go templating engine, then tracks each install as a versioned release. Kustomize takes plain manifests and applies declarative &lt;strong class="kw"&gt;overlays&lt;/strong&gt; that patch a base configuration per environment, with no templating language or release state at all.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;text x="160" y="30" text-anchor="middle" font-size="18" style="fill:var(--primary)"&gt;Helm&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" font-size="18" style="fill:var(--primary)"&gt;Kustomize&lt;/text&gt;&lt;rect x="40" y="55" width="240" height="60" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="80" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Chart&lt;/text&gt;&lt;text x="160" y="100" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;templates/*.yaml + values.yaml&lt;/text&gt;&lt;line x1="160" y1="115" x2="160" y2="150" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;rect x="40" y="150" width="240" height="45" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="178" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;helm template / install&lt;/text&gt;&lt;line x1="160" y1="195" x2="160" y2="230" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;rect x="40" y="230" width="240" height="45" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="253" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Rendered manifests&lt;/text&gt;&lt;text x="160" y="268" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;tracked as a Release&lt;/text&gt;&lt;line x1="160" y1="275" x2="160" y2="305" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;rect x="40" y="305" width="240" height="40" rx="6" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 3"/&gt;&lt;text x="160" y="330" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Cluster&lt;/text&gt;&lt;rect x="400" y="55" width="110" height="55" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="455" y="78" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;base/&lt;/text&gt;&lt;text x="455" y="95" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;plain manifests&lt;/text&gt;&lt;rect x="520" y="55" width="110" height="55" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="575" y="78" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;overlays/prod&lt;/text&gt;&lt;text x="575" y="95" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;patches&lt;/text&gt;&lt;line x1="455" y1="115" x2="500" y2="150" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;line x1="575" y1="115" x2="510" y2="150" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;rect x="400" y="150" width="230" height="45" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="515" y="178" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;kustomize build&lt;/text&gt;&lt;line x1="515" y1="195" x2="515" y2="230" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;rect x="400" y="230" width="230" height="45" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="515" y="253" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Merged manifests&lt;/text&gt;&lt;text x="515" y="268" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;no state tracked&lt;/text&gt;&lt;line x1="515" y1="275" x2="515" y2="305" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;rect x="400" y="305" width="230" height="40" rx="6" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 3"/&gt;&lt;text x="515" y="330" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Cluster&lt;/text&gt;&lt;defs&gt;&lt;marker id="arrowA" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="4" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 Z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Helm&lt;/th&gt;
&lt;th&gt;Kustomize&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Configuration model&lt;/td&gt;
&lt;td&gt;Go template engine that generates YAML text before it&amp;rsquo;s parsed&lt;/td&gt;
&lt;td&gt;Native Kubernetes objects patched via strategic merge or JSON patch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input format&lt;/td&gt;
&lt;td&gt;Chart with templates/, values.yaml, and Chart.yaml metadata&lt;/td&gt;
&lt;td&gt;Plain, valid YAML manifests plus a kustomization.yaml&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parameterization&lt;/td&gt;
&lt;td&gt;Placeholder values injected as text, so output can become invalid YAML if misused&lt;/td&gt;
&lt;td&gt;Structured patches applied to already-valid objects, so output stays schema-correct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment customization&lt;/td&gt;
&lt;td&gt;Layered values files (values-prod.yaml) merged into one chart&lt;/td&gt;
&lt;td&gt;Overlay directories per environment referencing a shared base&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packaging &amp;amp; distribution&lt;/td&gt;
&lt;td&gt;Versioned, shareable chart archives published to chart repositories or OCI registries&lt;/td&gt;
&lt;td&gt;No packaging format; kustomization directories are just checked into git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependency management&lt;/td&gt;
&lt;td&gt;Subcharts declared in Chart.yaml and pulled via helm dependency update&lt;/td&gt;
&lt;td&gt;Bases and components composed by referencing other directories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment execution&lt;/td&gt;
&lt;td&gt;helm install/upgrade tracks a named release and its revision history&lt;/td&gt;
&lt;td&gt;kustomize build pipes to kubectl apply with no release object created&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback &amp;amp; drift&lt;/td&gt;
&lt;td&gt;helm rollback reverts to a stored prior release revision&lt;/td&gt;
&lt;td&gt;No built-in rollback; relies on git revert or kubectl&amp;rsquo;s own history&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Helm renders manifests through &lt;strong class="kw"&gt;text templating&lt;/strong&gt;, while Kustomize edits already-parsed objects via &lt;strong class="kw"&gt;structural patches&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Helm tracks installs as stateful &lt;strong class="kw"&gt;releases&lt;/strong&gt; with revision history; Kustomize has &lt;strong class="kw"&gt;no release state&lt;/strong&gt; at all&lt;/li&gt;
&lt;li&gt;Helm charts are &lt;strong class="kw"&gt;packaged and versioned&lt;/strong&gt; for reuse; Kustomize configs are just plain manifests in git&lt;/li&gt;
&lt;li&gt;Kustomize is built into &lt;strong class="kw"&gt;kubectl&lt;/strong&gt; directly, while Helm requires installing a separate CLI/tool&lt;/li&gt;
&lt;li&gt;Many teams combine both: a Helm chart as the base, customized per environment with Kustomize&amp;rsquo;s &lt;strong class="kw"&gt;overlay patches&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Helm&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Immutable vs Mutable Infrastructure: Replace vs Patch</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-immutable-vs-mutable-infrastructure-replace-vs-patch/</link><pubDate>Mon, 03 Aug 2026 05:18:27 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-immutable-vs-mutable-infrastructure-replace-vs-patch/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Immutable infrastructure treats servers as disposable artifacts — every change ships as a brand-new &lt;strong class="kw"&gt;image&lt;/strong&gt; that replaces the running instance rather than editing it. Mutable infrastructure instead &lt;strong class="kw"&gt;updates in place&lt;/strong&gt;, applying patches and config changes directly to long-lived servers. The choice determines how predictable, auditable, and drift-resistant your environments are.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;defs&gt;&lt;marker id="arrowA" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 z" style="fill:var(--compare-a)"/&gt;&lt;/marker&gt;&lt;marker id="arrowB" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 z" style="fill:var(--compare-b)"/&gt;&lt;/marker&gt;&lt;marker id="arrowN" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto"&gt;&lt;path d="M0,0 L8,4 L0,8 z" style="fill:var(--secondary)"/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;line x1="320" y1="20" x2="320" y2="335" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4 4"/&gt;&lt;text x="160" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="15" font-weight="bold"&gt;Immutable Infrastructure&lt;/text&gt;&lt;text x="480" y="30" text-anchor="middle" style="fill:var(--primary)" font-size="15" font-weight="bold"&gt;Mutable Infrastructure&lt;/text&gt;&lt;rect x="45" y="55" width="100" height="38" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="95" y="78" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Image v1&lt;/text&gt;&lt;line x1="95" y1="93" x2="95" y2="118" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;rect x="45" y="120" width="100" height="50" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="95" y="149" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Server v1&lt;/text&gt;&lt;line x1="95" y1="170" x2="95" y2="195" style="stroke:var(--secondary)" stroke-width="1.5" marker-end="url(#arrowN)"/&gt;&lt;rect x="45" y="197" width="100" height="34" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3 3"/&gt;&lt;text x="95" y="218" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;terminated&lt;/text&gt;&lt;rect x="175" y="55" width="100" height="38" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="225" y="78" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Image v2&lt;/text&gt;&lt;line x1="225" y1="93" x2="225" y2="118" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;rect x="175" y="120" width="100" height="50" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="225" y="149" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Server v2&lt;/text&gt;&lt;line x1="225" y1="170" x2="225" y2="195" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;&lt;rect x="175" y="197" width="100" height="34" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="225" y="218" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;live traffic&lt;/text&gt;&lt;text x="160" y="255" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;change = build new image,&lt;/text&gt;&lt;text x="160" y="270" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;deploy new instance, discard old&lt;/text&gt;&lt;rect x="410" y="130" width="140" height="80" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="160" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Server&lt;/text&gt;&lt;text x="480" y="180" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;v1 &amp;#8594; v1.1 &amp;#8594; v1.2&lt;/text&gt;&lt;path d="M 460 130 A 40 30 0 1 1 500 130" style="fill:none;stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;&lt;text x="480" y="95" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;patch in place&lt;/text&gt;&lt;text x="480" y="255" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;change = SSH in / run config&lt;/text&gt;&lt;text x="480" y="270" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;management, edit same instance&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Immutable Infrastructure&lt;/th&gt;
&lt;th&gt;Mutable Infrastructure&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initial provisioning&lt;/td&gt;
&lt;td&gt;Instance built once from a versioned image or template&lt;/td&gt;
&lt;td&gt;Instance provisioned once, then edited repeatedly over its life&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Applying a change&lt;/td&gt;
&lt;td&gt;Rebuild the image with the change baked in&lt;/td&gt;
&lt;td&gt;SSH in, or run a config-management tool, against the live server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment mechanism&lt;/td&gt;
&lt;td&gt;Orchestrator replaces old instances with new ones (rolling/blue-green)&lt;/td&gt;
&lt;td&gt;Update scripts or agents (Ansible, Chef, Puppet) mutate the running instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration drift&lt;/td&gt;
&lt;td&gt;Cannot occur — every instance matches its source image exactly&lt;/td&gt;
&lt;td&gt;Accumulates over time as ad hoc changes diverge from documented state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback&lt;/td&gt;
&lt;td&gt;Redeploy the prior image version, deterministic and fast&lt;/td&gt;
&lt;td&gt;Manually reverse changes on the server, often incomplete or unreliable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Emergency hotfixes&lt;/td&gt;
&lt;td&gt;Requires rebuilding and redeploying an image, slower to react&lt;/td&gt;
&lt;td&gt;Can be patched directly on the box in seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auditability &amp;amp; reproducibility&lt;/td&gt;
&lt;td&gt;Image is a versioned artifact; environment is fully reproducible&lt;/td&gt;
&lt;td&gt;True state only knowable by inspecting the live server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline &amp;amp; storage overhead&lt;/td&gt;
&lt;td&gt;Needs an image build pipeline and artifact/image registry&lt;/td&gt;
&lt;td&gt;Lower tooling overhead, no build pipeline required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Immutable instances are never touched after launch; mutable servers are &lt;strong class="kw"&gt;patched in place&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Immutable infrastructure eliminates &lt;strong class="kw"&gt;configuration drift&lt;/strong&gt; by construction.&lt;/li&gt;
&lt;li&gt;Rolling back immutable infra just means redeploying a prior &lt;strong class="kw"&gt;image version&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Mutable infra depends on ongoing &lt;strong class="kw"&gt;config management&lt;/strong&gt; tooling to keep state converged.&lt;/li&gt;
&lt;li&gt;Immutable workflows require an &lt;strong class="kw"&gt;image build pipeline&lt;/strong&gt; and registry that mutable setups skip.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Immutable Infrastructure&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>GitOps vs Traditional CI/CD: Push vs Pull Deployment</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-gitops-vs-traditional-ci-cd-push-vs-pull-deployment/</link><pubDate>Mon, 03 Aug 2026 05:17:05 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-gitops-vs-traditional-ci-cd-push-vs-pull-deployment/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Both aim to automate software delivery, but they differ in who initiates the deployment and where the source of truth lives. Traditional &lt;strong class="kw"&gt;CI/CD&lt;/strong&gt; pushes changes into infrastructure from an external pipeline, while &lt;strong class="kw"&gt;GitOps&lt;/strong&gt; has an in-cluster agent continuously pull and reconcile state against a Git repository. The distinction matters most for security posture, drift handling, and auditability in Kubernetes-native environments.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;
&lt;defs&gt;
&lt;marker id="arrowA" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"&gt;
&lt;path d="M0,0 L10,5 L0,10 z" style="fill:var(--compare-a)"/&gt;
&lt;/marker&gt;
&lt;marker id="arrowB" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"&gt;
&lt;path d="M0,0 L10,5 L0,10 z" style="fill:var(--compare-b)"/&gt;
&lt;/marker&gt;
&lt;/defs&gt;
&lt;line x1="320" y1="10" x2="320" y2="350" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;
&lt;text x="170" y="26" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Traditional CI/CD&lt;/text&gt;
&lt;text x="170" y="44" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;push-based&lt;/text&gt;
&lt;text x="490" y="26" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;GitOps&lt;/text&gt;
&lt;text x="490" y="44" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;pull-based&lt;/text&gt;
&lt;rect x="60" y="60" width="160" height="45" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="140" y="87" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Git Repo&lt;/text&gt;
&lt;line x1="140" y1="105" x2="140" y2="158" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;
&lt;text x="150" y="135" style="fill:var(--secondary)" font-size="10"&gt;merge trigger&lt;/text&gt;
&lt;rect x="60" y="160" width="160" height="45" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="140" y="187" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;CI/CD Pipeline&lt;/text&gt;
&lt;line x1="140" y1="205" x2="140" y2="258" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;
&lt;text x="150" y="235" style="fill:var(--secondary)" font-size="10"&gt;kubectl apply&lt;/text&gt;
&lt;text x="150" y="248" style="fill:var(--secondary)" font-size="10"&gt;(holds cluster creds)&lt;/text&gt;
&lt;rect x="60" y="260" width="160" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="140" y="291" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Production&lt;/text&gt;
&lt;text x="140" y="306" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Cluster&lt;/text&gt;
&lt;text x="140" y="335" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;external system pushes with cluster creds&lt;/text&gt;
&lt;rect x="420" y="60" width="160" height="45" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="500" y="87" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Git Repo&lt;/text&gt;
&lt;rect x="420" y="260" width="160" height="55" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="500" y="284" text-anchor="middle" style="fill:var(--content)" font-size="13"&gt;Production Cluster&lt;/text&gt;
&lt;text x="500" y="300" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;(GitOps agent)&lt;/text&gt;
&lt;path d="M 460,260 C 600,225 600,140 465,107" fill="none" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;
&lt;text x="605" y="185" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;pulls &amp;amp;&lt;/text&gt;
&lt;text x="605" y="198" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;diffs state&lt;/text&gt;
&lt;text x="500" y="335" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;agent auto-reconciles drift, no external creds&lt;/text&gt;
&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Traditional CI/CD&lt;/th&gt;
&lt;th&gt;GitOps&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Deployment trigger&lt;/td&gt;
&lt;td&gt;Pipeline job runs on merge/tag and executes a deploy step&lt;/td&gt;
&lt;td&gt;In-cluster agent continuously polls or watches the Git repo for changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source of truth&lt;/td&gt;
&lt;td&gt;Pipeline scripts and job history define what was deployed&lt;/td&gt;
&lt;td&gt;Git repository is the sole declarative source of desired state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cluster access model&lt;/td&gt;
&lt;td&gt;CI server holds cluster credentials and pushes from outside the network&lt;/td&gt;
&lt;td&gt;Agent runs inside the cluster; no external system needs cluster credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drift detection&lt;/td&gt;
&lt;td&gt;None built-in; manual kubectl edits go unnoticed until the next run&lt;/td&gt;
&lt;td&gt;Agent continuously compares live state to Git and flags or corrects drift&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback&lt;/td&gt;
&lt;td&gt;Re-run the pipeline against a previous artifact or commit&lt;/td&gt;
&lt;td&gt;git revert triggers an automatic re-sync to the prior state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audit trail&lt;/td&gt;
&lt;td&gt;Split across CI logs, deploy scripts, and any manual changes&lt;/td&gt;
&lt;td&gt;Single, complete history captured in Git commit log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-cluster scaling&lt;/td&gt;
&lt;td&gt;Pipeline needs explicit logic and credentials per target environment&lt;/td&gt;
&lt;td&gt;Each cluster runs its own agent watching the same or a branched repo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;CI/CD is &lt;strong class="kw"&gt;push-based&lt;/strong&gt; from an external system; GitOps is &lt;strong class="kw"&gt;pull-based&lt;/strong&gt; from inside the cluster&lt;/li&gt;
&lt;li&gt;GitOps treats the Git repo as the exclusive &lt;strong class="kw"&gt;source of truth&lt;/strong&gt;; CI/CD&amp;rsquo;s truth lives in pipeline state&lt;/li&gt;
&lt;li&gt;CI/CD requires the pipeline to hold &lt;strong class="kw"&gt;cluster credentials&lt;/strong&gt;; GitOps keeps them inside the cluster boundary&lt;/li&gt;
&lt;li&gt;GitOps performs automatic &lt;strong class="kw"&gt;drift correction&lt;/strong&gt;; CI/CD has no ongoing reconciliation&lt;/li&gt;
&lt;li&gt;Rollback in GitOps is a simple &lt;strong class="kw"&gt;git revert&lt;/strong&gt; instead of re-running a pipeline job&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Traditional CI/CD&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>CI vs CD: Continuous Integration vs Continuous Delivery/Deployment</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-ci-vs-cd-continuous-integration-vs-continuous-delivery-deplo/</link><pubDate>Mon, 03 Aug 2026 05:15:02 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-ci-vs-cd-continuous-integration-vs-continuous-delivery-deplo/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;CI (Continuous Integration) and CD (Continuous Delivery/Deployment) are two connected stages of the same pipeline, not synonyms. CI focuses on &lt;strong class="kw"&gt;automated testing&lt;/strong&gt; of every code change as it merges, while CD focuses on &lt;strong class="kw"&gt;automated deployment&lt;/strong&gt; of that validated code into staging or production. Teams that only automate the first half often assume they have full &amp;lsquo;CI/CD&amp;rsquo; when they&amp;rsquo;ve really just automated build-and-test.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg" font-family="sans-serif"&gt;&lt;line x1="25" y1="140" x2="315" y2="140" style="stroke:var(--compare-a)" stroke-width="2"/&gt;&lt;line x1="25" y1="140" x2="25" y2="150" style="stroke:var(--compare-a)" stroke-width="2"/&gt;&lt;line x1="315" y1="140" x2="315" y2="150" style="stroke:var(--compare-a)" stroke-width="2"/&gt;&lt;text x="170" y="122" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;CI&lt;/text&gt;&lt;line x1="325" y1="140" x2="615" y2="140" style="stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;line x1="325" y1="140" x2="325" y2="150" style="stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;line x1="615" y1="140" x2="615" y2="150" style="stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;text x="470" y="122" text-anchor="middle" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;CD&lt;/text&gt;&lt;rect x="25" y="150" width="90" height="60" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="70" y="185" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Commit&lt;/text&gt;&lt;rect x="125" y="150" width="90" height="60" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="170" y="185" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Build&lt;/text&gt;&lt;rect x="225" y="150" width="90" height="60" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="270" y="185" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Test&lt;/text&gt;&lt;rect x="325" y="150" width="90" height="60" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="370" y="185" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Package&lt;/text&gt;&lt;rect x="425" y="150" width="90" height="60" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="470" y="185" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Staging&lt;/text&gt;&lt;rect x="525" y="150" width="90" height="60" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="570" y="185" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;Production&lt;/text&gt;&lt;line x1="115" y1="180" x2="122" y2="180" style="stroke:var(--border)" stroke-width="2"/&gt;&lt;polygon points="125,180 120,176 120,184" style="fill:var(--border)"/&gt;&lt;line x1="215" y1="180" x2="222" y2="180" style="stroke:var(--border)" stroke-width="2"/&gt;&lt;polygon points="225,180 220,176 220,184" style="fill:var(--border)"/&gt;&lt;line x1="315" y1="180" x2="322" y2="180" style="stroke:var(--border)" stroke-width="2"/&gt;&lt;polygon points="325,180 320,176 320,184" style="fill:var(--border)"/&gt;&lt;line x1="415" y1="180" x2="422" y2="180" style="stroke:var(--border)" stroke-width="2"/&gt;&lt;polygon points="425,180 420,176 420,184" style="fill:var(--border)"/&gt;&lt;line x1="515" y1="180" x2="522" y2="180" style="stroke:var(--border)" stroke-width="2"/&gt;&lt;polygon points="525,180 520,176 520,184" style="fill:var(--border)"/&gt;&lt;text x="170" y="245" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;Runs automatically on every commit&lt;/text&gt;&lt;rect x="325" y="225" width="290" height="55" rx="4" fill="none" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 3"/&gt;&lt;text x="470" y="248" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;Delivery: manual approval before Production&lt;/text&gt;&lt;text x="470" y="266" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;Deployment: fully automatic, no gate&lt;/text&gt;&lt;text x="320" y="330" text-anchor="middle" font-size="13" style="fill:var(--content)"&gt;CI verifies the code — CD gets it running&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;CI (Continuous Integration)&lt;/th&gt;
&lt;th&gt;CD (Continuous Delivery/Deployment)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Trigger&lt;/td&gt;
&lt;td&gt;Code commit or push to a shared repository&lt;/td&gt;
&lt;td&gt;A successful CI run producing a new build artifact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core process&lt;/td&gt;
&lt;td&gt;Compile, run unit/integration tests, static analysis&lt;/td&gt;
&lt;td&gt;Package the artifact, provision environments, run deployment scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary output&lt;/td&gt;
&lt;td&gt;A verified, mergeable build artifact&lt;/td&gt;
&lt;td&gt;A running release in staging and/or production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Environment scope&lt;/td&gt;
&lt;td&gt;Build server or ephemeral test environment&lt;/td&gt;
&lt;td&gt;Staging and/or production environments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human gate&lt;/td&gt;
&lt;td&gt;None — fully automated on every commit&lt;/td&gt;
&lt;td&gt;Optional manual approval (Delivery) or none (Deployment)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release cadence&lt;/td&gt;
&lt;td&gt;N/A — runs per commit, not a release event&lt;/td&gt;
&lt;td&gt;Can range from multiple times a day to once per commit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure consequence&lt;/td&gt;
&lt;td&gt;Blocks the merge; breaks the build for the team&lt;/td&gt;
&lt;td&gt;Blocks or rolls back the release; production stays on the last good version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary goal&lt;/td&gt;
&lt;td&gt;Catch integration bugs early, keep the main branch releasable&lt;/td&gt;
&lt;td&gt;Get every releasable build to users quickly and safely&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;CI&amp;rsquo;s output is a verified &lt;strong class="kw"&gt;build artifact&lt;/strong&gt;, not a live release&lt;/li&gt;
&lt;li&gt;CD adds environment &lt;strong class="kw"&gt;provisioning&lt;/strong&gt; and deployment steps that CI never performs&lt;/li&gt;
&lt;li&gt;A CI failure blocks the &lt;strong class="kw"&gt;merge&lt;/strong&gt;; a CD failure blocks the rollout instead&lt;/li&gt;
&lt;li&gt;Continuous Delivery keeps a manual &lt;strong class="kw"&gt;approval gate&lt;/strong&gt; before production, while Continuous Deployment removes it&lt;/li&gt;
&lt;li&gt;CI runs on every single commit; CD can be throttled by environment or business &lt;strong class="kw"&gt;readiness&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;CI (Continuous Integration)&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Blue-Green Deployment vs Canary Deployment: Release Strategy Comparison</title><link>https://comparison.metacog.co.kr/posts/2026-08-03-blue-green-deployment-vs-canary-deployment-release-strategy/</link><pubDate>Mon, 03 Aug 2026 05:13:13 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-03-blue-green-deployment-vs-canary-deployment-release-strategy/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Blue-green and canary deployment are both techniques for releasing new code with minimal downtime, but they differ in how traffic moves to the new version. Blue-green performs an &lt;strong class="kw"&gt;instant cutover&lt;/strong&gt; between two full environments, while canary performs a &lt;strong class="kw"&gt;gradual rollout&lt;/strong&gt; to a small slice of live traffic before expanding.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;text x="160" y="28" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Blue-Green&lt;/text&gt;&lt;text x="480" y="28" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Canary&lt;/text&gt;&lt;line x1="320" y1="10" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4 4"/&gt;&lt;rect x="120" y="60" width="80" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="80" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Router&lt;/text&gt;&lt;rect x="40" y="150" width="100" height="90" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="2"/&gt;&lt;text x="90" y="190" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Blue&lt;/text&gt;&lt;text x="90" y="206" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;active&lt;/text&gt;&lt;rect x="180" y="150" width="100" height="90" rx="4" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="5 4"/&gt;&lt;text x="230" y="190" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Green&lt;/text&gt;&lt;text x="230" y="206" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;idle&lt;/text&gt;&lt;line x1="150" y1="92" x2="100" y2="150" style="stroke:var(--compare-a)" stroke-width="3"/&gt;&lt;text x="105" y="128" font-size="11" style="fill:var(--compare-a)"&gt;100%&lt;/text&gt;&lt;line x1="170" y1="92" x2="220" y2="150" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="3 3"/&gt;&lt;text x="195" y="128" font-size="11" style="fill:var(--secondary)"&gt;0%&lt;/text&gt;&lt;text x="160" y="270" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;instant switch on cutover&lt;/text&gt;&lt;rect x="440" y="60" width="80" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="80" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Router&lt;/text&gt;&lt;rect x="360" y="150" width="100" height="90" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;text x="410" y="190" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Stable v1&lt;/text&gt;&lt;text x="410" y="206" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;95%&lt;/text&gt;&lt;rect x="500" y="165" width="70" height="60" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="2"/&gt;&lt;text x="535" y="192" text-anchor="middle" font-size="11" style="fill:var(--content)"&gt;Canary v2&lt;/text&gt;&lt;text x="535" y="206" text-anchor="middle" font-size="10" style="fill:var(--secondary)"&gt;5%&lt;/text&gt;&lt;line x1="470" y1="92" x2="420" y2="150" style="stroke:var(--compare-b)" stroke-width="3"/&gt;&lt;line x1="495" y1="92" x2="525" y2="165" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="270" text-anchor="middle" font-size="12" style="fill:var(--secondary)"&gt;gradual shift by percentage&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Blue-Green Deployment&lt;/th&gt;
&lt;th&gt;Canary Deployment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Environment topology&lt;/td&gt;
&lt;td&gt;Two full, identical production environments (blue and green)&lt;/td&gt;
&lt;td&gt;Single environment with a small subset of new-version instances alongside the old&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic routing&lt;/td&gt;
&lt;td&gt;Router/load balancer switches all traffic at once between environments&lt;/td&gt;
&lt;td&gt;Load balancer incrementally shifts a percentage of traffic from old to new&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollout progression&lt;/td&gt;
&lt;td&gt;Binary cutover: 0% or 100% to the new environment&lt;/td&gt;
&lt;td&gt;Staged progression, e.g. 5% -&amp;gt; 25% -&amp;gt; 50% -&amp;gt; 100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Validation approach&lt;/td&gt;
&lt;td&gt;New version smoke-tested in green before it receives any live traffic&lt;/td&gt;
&lt;td&gt;New version validated using real live traffic on a limited subset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback speed&lt;/td&gt;
&lt;td&gt;Instant - flip the router back to the blue environment&lt;/td&gt;
&lt;td&gt;Fast but partial - reduce canary percentage to 0, though some users already saw it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure blast radius&lt;/td&gt;
&lt;td&gt;Zero pre-cutover, but 100% of users once switched&lt;/td&gt;
&lt;td&gt;Limited to whatever percentage of traffic is on the canary at the time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure cost&lt;/td&gt;
&lt;td&gt;Requires double full production capacity during the deploy window&lt;/td&gt;
&lt;td&gt;Requires only incremental capacity for the canary instances&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tooling requirement&lt;/td&gt;
&lt;td&gt;Needs environment provisioning and DB/schema compatibility between versions&lt;/td&gt;
&lt;td&gt;Needs real-time metrics and automated analysis to judge canary health&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Blue-green performs an instant &lt;strong class="kw"&gt;router cutover&lt;/strong&gt;; canary shifts traffic &lt;strong class="kw"&gt;incrementally&lt;/strong&gt; over stages.&lt;/li&gt;
&lt;li&gt;Blue-green requires &lt;strong class="kw"&gt;duplicate infrastructure&lt;/strong&gt;; canary needs only a small extra pool of instances.&lt;/li&gt;
&lt;li&gt;Canary limits failures to a &lt;strong class="kw"&gt;traffic percentage&lt;/strong&gt;, while blue-green exposes 100% of users the moment it cuts over.&lt;/li&gt;
&lt;li&gt;Canary depends on &lt;strong class="kw"&gt;live metrics&lt;/strong&gt; to progress safely; blue-green relies on pre-cutover testing in an isolated environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Blue-Green Deployment&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Move Fast and Break Things vs Stability and Strict Testing: Two Release Philosophies</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-move-fast-and-break-things-vs-stability-and-strict-testing-t/</link><pubDate>Sun, 02 Aug 2026 23:56:19 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-move-fast-and-break-things-vs-stability-and-strict-testing-t/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;These are two opposing engineering cultures for shipping software: one optimizes for &lt;strong class="kw"&gt;iteration speed&lt;/strong&gt;, accepting bugs as the cost of learning quickly, while the other optimizes for &lt;strong class="kw"&gt;reliability&lt;/strong&gt;, gating every release behind verification. The choice shapes how a team designs, tests, deploys, and responds to failure, and picking the wrong one for your context can be as costly as picking no strategy at all.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;
&lt;defs&gt;
&lt;marker id="arrowA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"&gt;
&lt;path d="M0,0 L10,5 L0,10 z" style="fill:var(--compare-a)"/&gt;
&lt;/marker&gt;
&lt;marker id="arrowB" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"&gt;
&lt;path d="M0,0 L10,5 L0,10 z" style="fill:var(--compare-b)"/&gt;
&lt;/marker&gt;
&lt;/defs&gt;
&lt;text x="20" y="28" style="fill:var(--primary)" font-size="14" font-weight="bold"&gt;Move Fast and Break Things&lt;/text&gt;
&lt;rect x="30" y="55" width="100" height="50" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="80" y="85" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Code&lt;/text&gt;
&lt;line x1="130" y1="80" x2="175" y2="80" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;
&lt;rect x="180" y="55" width="100" height="50" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="230" y="85" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Deploy&lt;/text&gt;
&lt;line x1="280" y1="80" x2="325" y2="80" style="stroke:var(--compare-a)" stroke-width="1.5" marker-end="url(#arrowA)"/&gt;
&lt;rect x="330" y="55" width="140" height="50" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;
&lt;text x="400" y="85" text-anchor="middle" style="fill:var(--content)" font-size="12"&gt;Bugs in Prod&lt;/text&gt;
&lt;path d="M 400,105 C 400,150 80,150 80,105" fill="none" style="stroke:var(--compare-a)" stroke-width="1.5" stroke-dasharray="3 3" marker-end="url(#arrowA)"/&gt;
&lt;text x="240" y="142" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;rapid fix-and-redeploy loop&lt;/text&gt;
&lt;line x1="20" y1="180" x2="620" y2="180" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4 4"/&gt;
&lt;text x="20" y="205" style="fill:var(--primary)" font-size="14" font-weight="bold"&gt;Stability and Strict Testing&lt;/text&gt;
&lt;rect x="20" y="230" width="95" height="45" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="67" y="257" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Code&lt;/text&gt;
&lt;line x1="115" y1="252" x2="125" y2="252" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;
&lt;rect x="125" y="230" width="95" height="45" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="172" y="250" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Unit&lt;/text&gt;
&lt;text x="172" y="262" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Tests&lt;/text&gt;
&lt;line x1="220" y1="252" x2="230" y2="252" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;
&lt;rect x="230" y="230" width="95" height="45" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="277" y="250" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Integration&lt;/text&gt;
&lt;text x="277" y="262" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Tests&lt;/text&gt;
&lt;line x1="325" y1="252" x2="335" y2="252" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;
&lt;rect x="335" y="230" width="95" height="45" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="382" y="257" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Staging&lt;/text&gt;
&lt;line x1="430" y1="252" x2="440" y2="252" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;
&lt;rect x="440" y="230" width="95" height="45" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="487" y="257" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Deploy&lt;/text&gt;
&lt;line x1="535" y1="252" x2="545" y2="252" style="stroke:var(--compare-b)" stroke-width="1.5" marker-end="url(#arrowB)"/&gt;
&lt;rect x="545" y="230" width="75" height="45" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;
&lt;text x="582" y="250" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Stable&lt;/text&gt;
&lt;text x="582" y="262" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Release&lt;/text&gt;
&lt;text x="320" y="300" text-anchor="middle" style="fill:var(--secondary)" font-size="10"&gt;slow, gated pipeline with verification at every stage&lt;/text&gt;
&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Move Fast and Break Things&lt;/th&gt;
&lt;th&gt;Stability and Strict Testing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core philosophy&lt;/td&gt;
&lt;td&gt;Ship early and let real usage drive iteration&lt;/td&gt;
&lt;td&gt;Verify correctness before anything reaches users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Development approach&lt;/td&gt;
&lt;td&gt;Minimal upfront design, rapid prototyping&lt;/td&gt;
&lt;td&gt;Thorough design review and spec before coding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing rigor&lt;/td&gt;
&lt;td&gt;Light smoke tests, manual QA optional&lt;/td&gt;
&lt;td&gt;Mandatory unit, integration, and e2e test suites&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release process&lt;/td&gt;
&lt;td&gt;Continuous deployment, frequent small pushes&lt;/td&gt;
&lt;td&gt;Staged rollouts with sign-off gates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure handling&lt;/td&gt;
&lt;td&gt;Bugs expected in prod, fixed via fast rollback&lt;/td&gt;
&lt;td&gt;Bugs prevented pre-release, incidents are exceptional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feedback loop&lt;/td&gt;
&lt;td&gt;Real users surface issues within hours&lt;/td&gt;
&lt;td&gt;Issues caught in staging before users ever see them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best-fit context&lt;/td&gt;
&lt;td&gt;Early-stage products, experimental features&lt;/td&gt;
&lt;td&gt;Regulated, critical, or high-traffic systems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Move Fast optimizes for &lt;strong class="kw"&gt;velocity&lt;/strong&gt;, while Stability optimizes for &lt;strong class="kw"&gt;reliability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Bugs are treated as acceptable &lt;strong class="kw"&gt;collateral&lt;/strong&gt; versus &lt;strong class="kw"&gt;preventable defects&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Testing gates are &lt;strong class="kw"&gt;optional&lt;/strong&gt; in one culture and &lt;strong class="kw"&gt;mandatory&lt;/strong&gt; in the other&lt;/li&gt;
&lt;li&gt;Release cadence contrasts &lt;strong class="kw"&gt;continuous deployment&lt;/strong&gt; with &lt;strong class="kw"&gt;staged rollouts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Recovery relies on &lt;strong class="kw"&gt;fast rollback&lt;/strong&gt; rather than upfront prevention&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Move Fast and Break Things&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Terraform vs Ansible: Provisioning vs Configuration Management</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-terraform-vs-ansible-provisioning-vs-configuration-managemen/</link><pubDate>Sun, 02 Aug 2026 11:25:27 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-terraform-vs-ansible-provisioning-vs-configuration-managemen/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Terraform and Ansible are both infrastructure-as-code tools but solve different problems: Terraform declaratively provisions and tracks cloud infrastructure using a state file, while Ansible procedurally configures and manages software on existing hosts with no persistent state. Many teams use them together — Terraform to stand up infrastructure, Ansible to configure it.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;line x1="320" y1="20" x2="320" y2="335" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 4"/&gt;&lt;text x="170" y="28" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Terraform&lt;/text&gt;&lt;text x="170" y="44" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;declarative&lt;/text&gt;&lt;rect x="70" y="52" width="200" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="170" y="72" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;main.tf (desired state)&lt;/text&gt;&lt;line x1="170" y1="84" x2="170" y2="98" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="70" y="98" width="200" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="170" y="118" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;state file (source of truth)&lt;/text&gt;&lt;line x1="170" y1="130" x2="170" y2="152" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="110" y="152" width="120" height="30" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="170" y="171" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;dependency graph&lt;/text&gt;&lt;line x1="150" y1="182" x2="65" y2="244" style="stroke:var(--compare-a)" stroke-width="1.2"/&gt;&lt;line x1="170" y1="182" x2="155" y2="244" style="stroke:var(--compare-a)" stroke-width="1.2"/&gt;&lt;line x1="190" y1="182" x2="245" y2="244" style="stroke:var(--compare-a)" stroke-width="1.2"/&gt;&lt;rect x="25" y="244" width="80" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="65" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;VM&lt;/text&gt;&lt;rect x="115" y="244" width="80" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="155" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Network&lt;/text&gt;&lt;rect x="205" y="244" width="80" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="245" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;DB&lt;/text&gt;&lt;text x="170" y="300" text-anchor="middle" style="fill:var(--secondary)" font-size="9.5"&gt;converges infra to match state&lt;/text&gt;&lt;text x="480" y="28" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Ansible&lt;/text&gt;&lt;text x="480" y="44" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;procedural&lt;/text&gt;&lt;rect x="380" y="52" width="200" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="72" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;playbook.yml&lt;/text&gt;&lt;line x1="480" y1="84" x2="480" y2="96" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="400" y="96" width="160" height="26" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="113" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;task 1: install pkg&lt;/text&gt;&lt;line x1="480" y1="122" x2="480" y2="132" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="400" y="132" width="160" height="26" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="149" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;task 2: configure&lt;/text&gt;&lt;line x1="480" y1="158" x2="480" y2="168" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="400" y="168" width="160" height="26" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="185" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;task 3: start service&lt;/text&gt;&lt;line x1="460" y1="194" x2="385" y2="244" style="stroke:var(--compare-b)" stroke-width="1.2" stroke-dasharray="3 3"/&gt;&lt;line x1="480" y1="194" x2="475" y2="244" style="stroke:var(--compare-b)" stroke-width="1.2" stroke-dasharray="3 3"/&gt;&lt;line x1="500" y1="194" x2="565" y2="244" style="stroke:var(--compare-b)" stroke-width="1.2" stroke-dasharray="3 3"/&gt;&lt;rect x="345" y="244" width="80" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="385" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Server A&lt;/text&gt;&lt;rect x="435" y="244" width="80" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="475" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Server B&lt;/text&gt;&lt;rect x="525" y="244" width="80" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="565" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Server C&lt;/text&gt;&lt;text x="480" y="300" text-anchor="middle" style="fill:var(--secondary)" font-size="9.5"&gt;sequential push over SSH, no state file&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Terraform&lt;/th&gt;
&lt;th&gt;Ansible&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary purpose&lt;/td&gt;
&lt;td&gt;Provision and tear down cloud/infra resources (VMs, networks, DBs)&lt;/td&gt;
&lt;td&gt;Configure software and manage state on existing hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration language&lt;/td&gt;
&lt;td&gt;HCL (HashiCorp Configuration Language), declarative&lt;/td&gt;
&lt;td&gt;YAML playbooks, procedural task lists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution model&lt;/td&gt;
&lt;td&gt;Builds a dependency graph and applies changes in parallel where possible&lt;/td&gt;
&lt;td&gt;Executes tasks sequentially, in order, per host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State management&lt;/td&gt;
&lt;td&gt;Maintains a state file mapping config to real resources&lt;/td&gt;
&lt;td&gt;Stateless — queries live system facts on each run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotency approach&lt;/td&gt;
&lt;td&gt;Diffs desired config against state file before acting&lt;/td&gt;
&lt;td&gt;Each module checks current condition before making a change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectivity/agent requirement&lt;/td&gt;
&lt;td&gt;Agentless — calls cloud/provider APIs directly&lt;/td&gt;
&lt;td&gt;Agentless — connects over SSH or WinRM to target hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure &amp;amp; recovery handling&lt;/td&gt;
&lt;td&gt;Partial applies are resolved by re-running against the state file&lt;/td&gt;
&lt;td&gt;Reruns the playbook from the start; tasks are re-checked, not resumed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Terraform tracks infrastructure in a persistent &lt;strong class="kw"&gt;state file&lt;/strong&gt;; Ansible has no state store and reads live system facts each run.&lt;/li&gt;
&lt;li&gt;Terraform resolves a &lt;strong class="kw"&gt;dependency graph&lt;/strong&gt; to apply changes in parallel; Ansible runs tasks &lt;strong class="kw"&gt;sequentially&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Terraform talks to infrastructure through provider &lt;strong class="kw"&gt;APIs&lt;/strong&gt;; Ansible connects to hosts via &lt;strong class="kw"&gt;SSH/WinRM&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Terraform is built for &lt;strong class="kw"&gt;provisioning&lt;/strong&gt; infra; Ansible is built for &lt;strong class="kw"&gt;configuration&lt;/strong&gt; of what already exists.&lt;/li&gt;
&lt;li&gt;They&amp;rsquo;re commonly paired: Terraform creates the servers, then Ansible &lt;strong class="kw"&gt;configures&lt;/strong&gt; them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Terraform&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Container vs VM: Virtualization Approaches Compared</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-container-vs-vm-virtualization-approaches-compared/</link><pubDate>Sun, 02 Aug 2026 09:04:53 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-container-vs-vm-virtualization-approaches-compared/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Containers and virtual machines both let you package and isolate workloads, but they virtualize at different layers of the stack: containers share the host OS kernel while VMs emulate entire hardware and run a full guest OS each. That difference drives everything else — startup speed, image size, isolation strength, and how many instances you can pack onto one host.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;line x1="320" y1="40" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4,4"/&gt;&lt;text x="320" y="30" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;VS&lt;/text&gt;&lt;text x="160" y="24" text-anchor="middle" style="fill:var(--primary)" font-size="15" font-weight="bold"&gt;Container&lt;/text&gt;&lt;text x="480" y="24" text-anchor="middle" style="fill:var(--primary)" font-size="15" font-weight="bold"&gt;VM&lt;/text&gt;&lt;rect x="30" y="45" width="80" height="190" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="70" y="145" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;App+Libs&lt;/text&gt;&lt;rect x="120" y="45" width="80" height="190" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="145" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;App+Libs&lt;/text&gt;&lt;rect x="210" y="45" width="80" height="190" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="250" y="145" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;App+Libs&lt;/text&gt;&lt;rect x="20" y="245" width="280" height="40" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="265" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Container Engine&lt;/text&gt;&lt;text x="160" y="279" text-anchor="middle" style="fill:var(--secondary)" font-size="8"&gt;(Docker / containerd)&lt;/text&gt;&lt;rect x="20" y="295" width="280" height="40" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3,3"/&gt;&lt;text x="160" y="319" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Host OS Kernel (shared)&lt;/text&gt;&lt;text x="160" y="352" text-anchor="middle" style="fill:var(--secondary)" font-size="9"&gt;~MBs · starts in ms&lt;/text&gt;&lt;rect x="350" y="45" width="80" height="100" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="390" y="98" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;App+Libs&lt;/text&gt;&lt;rect x="350" y="149" width="80" height="90" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5" stroke-dasharray="2,2"/&gt;&lt;text x="390" y="198" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;Guest OS&lt;/text&gt;&lt;rect x="440" y="45" width="80" height="100" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="98" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;App+Libs&lt;/text&gt;&lt;rect x="440" y="149" width="80" height="90" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5" stroke-dasharray="2,2"/&gt;&lt;text x="480" y="198" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;Guest OS&lt;/text&gt;&lt;rect x="530" y="45" width="80" height="100" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="570" y="98" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;App+Libs&lt;/text&gt;&lt;rect x="530" y="149" width="80" height="90" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5" stroke-dasharray="2,2"/&gt;&lt;text x="570" y="198" text-anchor="middle" style="fill:var(--content)" font-size="9"&gt;Guest OS&lt;/text&gt;&lt;rect x="340" y="245" width="280" height="40" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="265" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;Hypervisor&lt;/text&gt;&lt;text x="480" y="279" text-anchor="middle" style="fill:var(--secondary)" font-size="8"&gt;(ESXi / KVM / Hyper-V)&lt;/text&gt;&lt;rect x="340" y="295" width="280" height="40" style="fill:none;stroke:var(--border)" stroke-width="1.5" stroke-dasharray="3,3"/&gt;&lt;text x="480" y="319" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Physical Hardware&lt;/text&gt;&lt;text x="480" y="352" text-anchor="middle" style="fill:var(--secondary)" font-size="9"&gt;~GBs · starts in minutes&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Container&lt;/th&gt;
&lt;th&gt;VM&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Isolation boundary&lt;/td&gt;
&lt;td&gt;OS-level, enforced by kernel namespaces and cgroups&lt;/td&gt;
&lt;td&gt;Hardware-level, enforced by a hypervisor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guest OS&lt;/td&gt;
&lt;td&gt;None — shares the host kernel&lt;/td&gt;
&lt;td&gt;Full guest OS instance per VM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup time&lt;/td&gt;
&lt;td&gt;Milliseconds to a few seconds&lt;/td&gt;
&lt;td&gt;Tens of seconds to minutes (full OS boot)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image/footprint size&lt;/td&gt;
&lt;td&gt;Megabytes&lt;/td&gt;
&lt;td&gt;Gigabytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource overhead&lt;/td&gt;
&lt;td&gt;Low; near-native performance&lt;/td&gt;
&lt;td&gt;Higher; hypervisor plus guest OS overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portability&lt;/td&gt;
&lt;td&gt;Highly portable across any host with a compatible kernel and engine&lt;/td&gt;
&lt;td&gt;Portable via VM image formats but heavier to move and convert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security isolation strength&lt;/td&gt;
&lt;td&gt;Weaker — shared kernel widens attack surface&lt;/td&gt;
&lt;td&gt;Stronger — separate kernel per VM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical density per host&lt;/td&gt;
&lt;td&gt;Hundreds of instances&lt;/td&gt;
&lt;td&gt;Tens of instances&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Containers share the host &lt;strong class="kw"&gt;kernel&lt;/strong&gt; instead of running a separate OS like VMs.&lt;/li&gt;
&lt;li&gt;VM isolation is enforced by a &lt;strong class="kw"&gt;hypervisor&lt;/strong&gt;, giving stronger security boundaries than containers.&lt;/li&gt;
&lt;li&gt;Containers typically boot in &lt;strong class="kw"&gt;milliseconds&lt;/strong&gt;, while VMs take minutes to boot a full OS.&lt;/li&gt;
&lt;li&gt;Container images measure in &lt;strong class="kw"&gt;megabytes&lt;/strong&gt;; VM images measure in gigabytes.&lt;/li&gt;
&lt;li&gt;A single host can run far higher &lt;strong class="kw"&gt;density&lt;/strong&gt; of containers than VMs due to lower per-instance overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Container&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Declarative vs Imperative IaC: Describing the End State vs Scripting the Steps</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-declarative-vs-imperative-iac-describing-the-end-state-vs-sc/</link><pubDate>Sun, 02 Aug 2026 08:55:05 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-declarative-vs-imperative-iac-describing-the-end-state-vs-sc/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Declarative IaC (e.g. Terraform, CloudFormation) has you specify the desired end state of infrastructure and lets an engine figure out how to get there. Imperative IaC (e.g. shell scripts, Chef recipes, raw CLI calls) has you write the exact sequence of commands to execute. The distinction matters because it determines who — you or the tool — is responsible for ordering, idempotency, and reconciling drift.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;text x="160" y="28" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Declarative&lt;/text&gt;&lt;rect x="40" y="50" width="240" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="72" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Desired State&lt;/text&gt;&lt;text x="160" y="90" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;"3 servers, 1 LB"&lt;/text&gt;&lt;line x1="160" y1="105" x2="160" y2="130" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="160,140 155,130 165,130" style="fill:var(--compare-a)"/&gt;&lt;rect x="40" y="145" width="240" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="167" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Engine Computes Diff&lt;/text&gt;&lt;text x="160" y="185" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;plan + dependency graph&lt;/text&gt;&lt;line x1="160" y1="200" x2="160" y2="225" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="160,235 155,225 165,225" style="fill:var(--compare-a)"/&gt;&lt;rect x="40" y="240" width="240" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="262" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Infrastructure&lt;/text&gt;&lt;text x="160" y="280" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;converges to match state&lt;/text&gt;&lt;text x="160" y="320" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Engine decides how &amp;amp; in what order&lt;/text&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;&lt;text x="480" y="28" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Imperative&lt;/text&gt;&lt;rect x="360" y="45" width="240" height="40" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="70" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Step 1: Create VPC&lt;/text&gt;&lt;line x1="480" y1="85" x2="480" y2="100" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="480,110 475,100 485,100" style="fill:var(--compare-b)"/&gt;&lt;rect x="360" y="115" width="240" height="40" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="140" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Step 2: Launch Servers&lt;/text&gt;&lt;line x1="480" y1="155" x2="480" y2="170" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="480,180 475,170 485,170" style="fill:var(--compare-b)"/&gt;&lt;rect x="360" y="185" width="240" height="40" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="210" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Step 3: Attach LB&lt;/text&gt;&lt;line x1="480" y1="225" x2="480" y2="240" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="480,250 475,240 485,240" style="fill:var(--compare-b)"/&gt;&lt;rect x="360" y="255" width="240" height="40" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="280" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Infrastructure&lt;/text&gt;&lt;text x="480" y="320" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Author decides exact steps &amp;amp; order&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Declarative&lt;/th&gt;
&lt;th&gt;Imperative&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authoring model&lt;/td&gt;
&lt;td&gt;Write a &lt;strong class="kw"&gt;desired-state spec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Write an &lt;strong class="kw"&gt;ordered command list&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution engine&lt;/td&gt;
&lt;td&gt;Resolves a &lt;strong class="kw"&gt;dependency graph&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs a &lt;strong class="kw"&gt;sequential interpreter&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State tracking&lt;/td&gt;
&lt;td&gt;Maintains a &lt;strong class="kw"&gt;state file&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Stateless&lt;/strong&gt; execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Applying changes&lt;/td&gt;
&lt;td&gt;Single &lt;strong class="kw"&gt;apply&lt;/strong&gt; command&lt;/td&gt;
&lt;td&gt;Run the &lt;strong class="kw"&gt;script/playbook&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ordering &amp;amp; dependencies&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Auto-resolved&lt;/strong&gt; by engine&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Manually sequenced&lt;/strong&gt; by author&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotency&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Guaranteed&lt;/strong&gt; by design&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Developer-enforced&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drift detection&lt;/td&gt;
&lt;td&gt;Built-in &lt;strong class="kw"&gt;plan diff&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Not built-in&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure handling&lt;/td&gt;
&lt;td&gt;Partial apply, &lt;strong class="kw"&gt;replan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Manual rollback&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Declarative code answers &amp;lsquo;what&amp;rsquo;, imperative code answers &lt;strong class="kw"&gt;&amp;lsquo;how&amp;rsquo;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Declarative tools rely on a &lt;strong class="kw"&gt;state file&lt;/strong&gt; to know current vs. desired infrastructure; imperative scripts have no memory of prior runs.&lt;/li&gt;
&lt;li&gt;Idempotent re-runs are &lt;strong class="kw"&gt;automatic&lt;/strong&gt; in declarative tools but must be hand-coded (checks, conditionals) in imperative scripts.&lt;/li&gt;
&lt;li&gt;Declarative engines build a &lt;strong class="kw"&gt;dependency graph&lt;/strong&gt; to order operations; imperative code hardcodes that order line by line.&lt;/li&gt;
&lt;li&gt;Drift correction in declarative IaC is a matter of re-running &lt;strong class="kw"&gt;plan/apply&lt;/strong&gt;; imperative approaches require re-running or rewriting the exact script.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Declarative&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Continuous Delivery vs Continuous Deployment: Where the Pipeline Stops</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-continuous-delivery-vs-continuous-deployment-where-the-pipel/</link><pubDate>Sun, 02 Aug 2026 08:12:38 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-continuous-delivery-vs-continuous-deployment-where-the-pipel/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Both practices extend continuous integration by automatically building, testing, and preparing every code change for release. The distinction is the final step: Continuous Delivery leaves the production release as a manual, human-triggered decision, while Continuous Deployment releases every change that passes the pipeline straight to production with no human gate.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg" font-family="sans-serif"&gt;&lt;text x="20" y="50" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Continuous Delivery&lt;/text&gt;&lt;rect x="20" y="70" width="100" height="50" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="70" y="99" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Commit&lt;/text&gt;&lt;rect x="140" y="70" width="100" height="50" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="190" y="91" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Build &amp;amp;&lt;/text&gt;&lt;text x="190" y="105" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Test&lt;/text&gt;&lt;rect x="260" y="70" width="100" height="50" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="310" y="99" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Staging&lt;/text&gt;&lt;rect x="380" y="70" width="100" height="50" rx="6" stroke-dasharray="4,3" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="430" y="91" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Manual&lt;/text&gt;&lt;text x="430" y="105" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Gate&lt;/text&gt;&lt;rect x="500" y="70" width="100" height="50" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="550" y="99" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Production&lt;/text&gt;&lt;line x1="121" y1="95" x2="133" y2="95" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="133,90 133,100 140,95" style="fill:var(--compare-a)"/&gt;&lt;line x1="241" y1="95" x2="253" y2="95" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="253,90 253,100 260,95" style="fill:var(--compare-a)"/&gt;&lt;line x1="361" y1="95" x2="373" y2="95" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="373,90 373,100 380,95" style="fill:var(--compare-a)"/&gt;&lt;line x1="481" y1="95" x2="493" y2="95" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="493,90 493,100 500,95" style="fill:var(--compare-a)"/&gt;&lt;text x="430" y="140" font-size="11" text-anchor="middle" style="fill:var(--secondary)"&gt;Human approves release&lt;/text&gt;&lt;text x="20" y="210" font-size="18" font-weight="bold" style="fill:var(--primary)"&gt;Continuous Deployment&lt;/text&gt;&lt;rect x="20" y="230" width="100" height="50" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="70" y="259" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Commit&lt;/text&gt;&lt;rect x="140" y="230" width="100" height="50" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="190" y="251" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Build &amp;amp;&lt;/text&gt;&lt;text x="190" y="265" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Test&lt;/text&gt;&lt;rect x="260" y="230" width="100" height="50" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="310" y="259" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Staging&lt;/text&gt;&lt;rect x="380" y="230" width="100" height="50" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="430" y="251" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Auto&lt;/text&gt;&lt;text x="430" y="265" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Deploy&lt;/text&gt;&lt;rect x="500" y="230" width="100" height="50" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="550" y="259" font-size="12" text-anchor="middle" style="fill:var(--content)"&gt;Production&lt;/text&gt;&lt;line x1="121" y1="255" x2="133" y2="255" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="133,250 133,260 140,255" style="fill:var(--compare-b)"/&gt;&lt;line x1="241" y1="255" x2="253" y2="255" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="253,250 253,260 260,255" style="fill:var(--compare-b)"/&gt;&lt;line x1="361" y1="255" x2="373" y2="255" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="373,250 373,260 380,255" style="fill:var(--compare-b)"/&gt;&lt;line x1="481" y1="255" x2="493" y2="255" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="493,250 493,260 500,255" style="fill:var(--compare-b)"/&gt;&lt;text x="430" y="300" font-size="11" text-anchor="middle" style="fill:var(--secondary)"&gt;Pipeline releases automatically&lt;/text&gt;&lt;text x="320" y="340" font-size="11" text-anchor="middle" style="fill:var(--secondary)"&gt;Both automate build, test, and staging - only the final release step differs&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Continuous Delivery&lt;/th&gt;
&lt;th&gt;Continuous Deployment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Core definition&lt;/td&gt;
&lt;td&gt;Every change is automatically built, tested, and made release-ready&lt;/td&gt;
&lt;td&gt;Every change that passes the pipeline is automatically released to production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production release trigger&lt;/td&gt;
&lt;td&gt;Manual approval (button click, ticket, scheduled window)&lt;/td&gt;
&lt;td&gt;Fully automated, no human step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human involvement&lt;/td&gt;
&lt;td&gt;Required at the final gate&lt;/td&gt;
&lt;td&gt;None after code review/merge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Release frequency&lt;/td&gt;
&lt;td&gt;As often as the business decides to approve&lt;/td&gt;
&lt;td&gt;As often as commits pass the pipeline, often multiple times a day&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pipeline requirement&lt;/td&gt;
&lt;td&gt;Automated build, test, and staging deployment&lt;/td&gt;
&lt;td&gt;Same, plus very high test coverage and confidence since there&amp;rsquo;s no manual check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback strategy&lt;/td&gt;
&lt;td&gt;Can hold a release before it ships if issues are found&lt;/td&gt;
&lt;td&gt;Must rely on fast automated rollback/feature flags since bad code ships immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risk profile&lt;/td&gt;
&lt;td&gt;Lower immediate risk; human judgment as a final safeguard&lt;/td&gt;
&lt;td&gt;Higher immediate risk; depends entirely on pipeline quality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical adopters&lt;/td&gt;
&lt;td&gt;Regulated industries, teams needing release scheduling or compliance sign-off&lt;/td&gt;
&lt;td&gt;Mature engineering orgs with strong test automation, e.g. SaaS with frequent small releases&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Continuous Delivery guarantees releasability, not release — a human still decides when code ships&lt;/li&gt;
&lt;li&gt;Continuous Deployment removes the human gate entirely, so passing the pipeline is equivalent to shipping&lt;/li&gt;
&lt;li&gt;Continuous Deployment demands much stronger automated test coverage, since there&amp;rsquo;s no manual safety check before production&lt;/li&gt;
&lt;li&gt;Continuous Delivery supports scheduled or compliance-driven release windows; Continuous Deployment does not&lt;/li&gt;
&lt;li&gt;Both require the same underlying CI foundation — automated build, test, and staging deployment&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Continuous Delivery&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>