<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Carlos Sanchez&#039;s Weblog</title>
	<atom:link href="https://blog.csanchez.org/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.csanchez.org</link>
	<description>software at the end of the universe</description>
	<lastBuildDate>Tue, 23 Jun 2026 16:06:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<site xmlns="com-wordpress:feed-additions:1">1255600</site><cloud domain='blog.csanchez.org' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>https://secure.gravatar.com/blavatar/798cf32363ef39a4604507a37886c42733f7722f8c1d2084c9f7a5a8481054d9?s=96&#038;d=https%3A%2F%2Fs0.wp.com%2Fi%2Fwebclip.png</url>
		<title>Carlos Sanchez&#039;s Weblog</title>
		<link>https://blog.csanchez.org</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="https://blog.csanchez.org/osd.xml" title="Carlos Sanchez&#039;s Weblog" />
	<atom:link rel='hub' href='https://blog.csanchez.org/?pushpress=hub'/>
	<item>
		<title>Testing Kubernetes sandboxing technologies</title>
		<link>https://blog.csanchez.org/2026/06/23/testing-kubernetes-sandboxing-technologies/</link>
					<comments>https://blog.csanchez.org/2026/06/23/testing-kubernetes-sandboxing-technologies/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Tue, 23 Jun 2026 15:05:00 +0000</pubDate>
				<category><![CDATA[ai]]></category>
		<category><![CDATA[kubernetes]]></category>
		<category><![CDATA[agent]]></category>
		<category><![CDATA[sandbox]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2186</guid>

					<description><![CDATA[There are several flavours of &#8220;give a workload its own isolated runtime on Kubernetes&#8221; floating around right now. We wanted a head-to-head comparison — not a slide-deck comparison, but one with real numbers against a non-trivial workload that exercises identity &#8230; <a href="https://blog.csanchez.org/2026/06/23/testing-kubernetes-sandboxing-technologies/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">There are several flavours of &#8220;give a workload its own isolated runtime on Kubernetes&#8221; floating around right now. We wanted a head-to-head comparison — not a slide-deck comparison, but one with real numbers against a non-trivial workload that exercises identity routing, port forwarding, WebSocket upgrades, and a heavy long-lived process. Playwright driving Chromium is exactly that workload. This post walks through how we tested four sandboxing technologies —&nbsp;<code>agent-sandbox</code>,&nbsp;<code>OpenShell</code>,&nbsp;<code>substrate</code>, and&nbsp;<code>KarsSandbox</code>&nbsp;— against the same Playwright harness, what we found, and what each option is actually good at.</p>



<p class="wp-block-paragraph">The harness and bench live at&nbsp;<a href="https://github.com/carlossg/playwright-k8s-sandbox">github.com/carlossg/playwright-k8s-sandbox</a>. The deep-dive architecture doc with full sequence diagrams is at&nbsp;<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/ARCHITECTURE.md"><code>docs/ARCHITECTURE.md</code></a>.</p>



<h2 class="wp-block-heading">Why Playwright as the test workload<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/blog/playwright-k8s-sandbox-proxy.md#why-playwright-as-the-test-workload"></a></h2>



<p class="wp-block-paragraph">Most sandboxing demos use&nbsp;<code>traefik/whoami</code>&nbsp;or&nbsp;<code>nginx</code>. Both are useful for a smoke test and useless for telling sandboxing options apart, because they don&#8217;t stress anything. Real agent workloads do. Playwright gives us, in one process tree:</p>



<ul class="wp-block-list">
<li>A&nbsp;<strong>long-lived stateful process</strong>&nbsp;(Chromium with ~6 child processes, hundreds of MB of memory).</li>



<li>A&nbsp;<strong>native WebSocket protocol</strong>&nbsp;(<code>chromium.connect(wsEndpoint)</code>), which requires HTTP upgrade handling end-to-end through the data plane.</li>



<li>A&nbsp;<strong>non-trivial cold-start cost</strong>&nbsp;that&#8217;s measurable and stable (Node + Chromium boot is ~3 seconds).</li>



<li>A&nbsp;<strong>real possibility of checkpoint/restore value</strong>&nbsp;— Chromium with a warm page tree is genuinely expensive to recreate, so the snapshot story is interesting to test rather than theoretical.</li>



<li>A&nbsp;<strong>clear correctness oracle</strong>&nbsp;—&nbsp;<code>page.goto(url)</code>&nbsp;either fetches the page or it doesn&#8217;t.</li>
</ul>



<p class="wp-block-paragraph">So each &#8220;test&#8221; in our harness is: instantiate a sandbox per tenant, get a Playwright client to connect over WebSocket, open a page, fetch a URL, measure each phase.</p>



<h2 class="wp-block-heading">The four sandboxing technologies under test</h2>



<p class="wp-block-paragraph"><a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/blog/playwright-k8s-sandbox-proxy.md#the-four-sandboxing-technologies-under-test"></a></p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th></th><th>Sandbox unit</th><th>Isolation</th><th>Persistence model</th></tr></thead><tbody><tr><td><a href="https://github.com/kubernetes-sigs/agent-sandbox"><strong>agent-sandbox</strong></a></td><td>Pod from a&nbsp;<code>SandboxWarmPool</code>, bound by a&nbsp;<code>SandboxClaim</code>&nbsp;CRD</td><td>Pluggable per&nbsp;<code>RuntimeClass</code>&nbsp;— runc by default, gVisor or Kata if you point the&nbsp;<code>SandboxTemplate</code>&nbsp;at the corresponding&nbsp;<code>RuntimeClass</code></td><td>Stateless. Claim is the pod&#8217;s lifecycle.</td></tr><tr><td><a href="https://github.com/nvidia/openshell"><strong>OpenShell</strong></a></td><td>Same machinery as agent-sandbox; with added process level isolation</td><td>Stateless.</td><td></td></tr><tr><td><a href="https://github.com/agent-substrate/substrate"><strong>substrate</strong></a></td><td>gVisor sandbox on a worker pod, managed as an &#8220;Actor&#8221;</td><td>gVisor (runsc, systrap platform); built in, not pluggable</td><td>Designed for full sandbox checkpoint/restore to S3.</td></tr><tr><td><a href="https://github.com/Azure/kars"><strong>KarsSandbox</strong></a></td><td>Namespaced pod per&nbsp;<code>KarsSandbox</code>&nbsp;CR (KARS controller)</td><td>Namespace-level isolation + optional Azure runtime sandboxing</td><td>Stateless. CR deletion destroys both namespace and pod.</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">The first two are mechanically identical — same CRDs, same controller — and both can run with runc, gVisor (runsc), or Kata Containers by pointing the&nbsp;<code>SandboxTemplate</code>&nbsp;at the appropriate&nbsp;<code>RuntimeClass</code>. We ran them with the cluster default (runc) for the bench.</p>



<p class="wp-block-paragraph">KarsSandbox takes a different approach: each sandbox gets its own dedicated namespace (not just a pod), providing stronger isolation boundaries and compatibility with Azure-specific runtime features like InferencePolicy for AI/GPU workloads. Unlike agent-sandbox&#8217;s warmpool model, KARS provisions sandboxes on-demand.</p>



<p class="wp-block-paragraph">The interesting comparison isn&#8217;t really &#8220;container vs gVisor&#8221; — multiple models can do gVisor — it&#8217;s&nbsp;<em>warmpool of pre-bound pods</em>&nbsp;vs&nbsp;<em>on-demand namespace provisioning</em>&nbsp;vs&nbsp;<em>substrate&#8217;s actor lifecycle with snapshot/restore</em>.</p>



<h2 class="wp-block-heading">The test harness<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/blog/playwright-k8s-sandbox-proxy.md#the-test-harness"></a></h2>



<p class="wp-block-paragraph">To make the comparison a bit similar we built a small proxy that abstracts the four backends behind one interface. Each backend implements&nbsp;<code>Ensure(id) → Endpoint</code>&nbsp;+&nbsp;<code>Delete(id)</code>; the proxy handles caller identification, session caching, idle reaping, and WebSocket upgrade forwarding identically across all four. That way, when we compare bench numbers, we&#8217;re comparing the&nbsp;<em>sandboxing technology</em>, not four different ad-hoc client implementations.</p>


<div class="wp-block-code">
	<div class="cm-editor">
		<div class="cm-scroller">
			
<pre>
<code><div class="cm-line">┌─ test client pod ┐                ┌─ proxy ─────────┐                ┌─ backend ─────────┐</div><div class="cm-line">│  labels:         │                │ identify        │                │  one of:          │</div><div class="cm-line">│   playwright-id  ├──HTTP / WS────&#x25b6;│ session.Manager ├──Ensure(id)───&#x25b6;│  - SandboxClaim   │</div><div class="cm-line">│   = bench-X      │                │  (singleflight) │                │  - SandboxClaim   │</div><div class="cm-line">└──────────────────┘                │ reverse proxy   │                │  - Actor (gRPC)   │</div><div class="cm-line">                                    │ idle reaper     │                │  - KarsSandbox CR │</div><div class="cm-line">                                    └────────┬────────┘                └─────────┬─────────┘</div><div class="cm-line">                                             │                                   │</div><div class="cm-line">                                             │                         ┌─────────▼─────────┐</div><div class="cm-line">                                             └──HTTP / WS upgrade─────&#x25b6;│ Chromium sandbox  │</div><div class="cm-line">                                                                       └───────────────────┘</div><div class="cm-line"></div><div class="cm-line"></div></code></pre>
		</div>
	</div>
</div>


<p class="wp-block-paragraph">The proxy identifies callers by&nbsp;<strong>pod label</strong>: the test client sets&nbsp;<code>metadata.labels.playwright-id</code>&nbsp;on its Deployment, the proxy looks up the caller&#8217;s pod IP via a client-go informer and resolves it to that id. No agent-side SDK, no token plumbing — just one label. Each unique id gets its own sandbox.</p>



<p class="wp-block-paragraph">Three scenarios per backend:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Scenario</th><th>Setup</th><th>Measures</th></tr></thead><tbody><tr><td><strong>cold</strong></td><td>Delete any prior sandbox, then connect for the first time.</td><td>Full provisioning cost: CreateClaim/CreateActor + Resume + WS upgrade + handshake.</td></tr><tr><td><strong>warm</strong></td><td>Connect again with the same id, sandbox still alive.</td><td>Steady-state cost: proxy hop + WS upgrade only.</td></tr><tr><td><strong>restore</strong></td><td>Out-of-band suspend (substrate) or wipe (sandboxclaim), then a fresh request.</td><td>The persistence story: does the sandbox come back faster than a cold start?</td></tr></tbody></table></figure>



<h2 class="wp-block-heading">What the agent-sandbox / OpenShell flow looks like<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/blog/playwright-k8s-sandbox-proxy.md#what-the-agent-sandbox--openshell-flow-looks-like"></a></h2>



<p class="wp-block-paragraph">Both back ends share the same CRD lifecycle: the proxy creates a&nbsp;<code>SandboxClaim</code>, the agent-sandbox controller picks a warm pod from the pool, binds it to the claim, and the proxy gets back an endpoint.</p>



<figure class="wp-block-embed is-type-rich is-provider-embed wp-block-embed-embed"><div class="wp-block-embed__wrapper">
<a href="https://viewscreen.githubusercontent.com/markdown/mermaid?docs_host=https%3A%2F%2Fdocs.github.com&#038;color_mode=light#b580a2bf-2583-4a74-a63f-7f41726379e1" rel="nofollow">https://viewscreen.githubusercontent.com/markdown/mermaid?docs_host=https%3A%2F%2Fdocs.github.com&#038;color_mode=light#b580a2bf-2583-4a74-a63f-7f41726379e1</a>
</div></figure>



<p class="wp-block-paragraph">There is no checkpoint/restore in this model. A claim&#8217;s life&nbsp;<em>is</em>&nbsp;the sandbox&#8217;s life; deleting the claim destroys the pod, and the next call for the same id gets a fresh warm pod from the pool. The &#8220;restore&#8221; scenario therefore re-creates the claim and behaves identically to cold. The interesting question this design answers well is:&nbsp;<em>how cheap can a cold-start be when you have warm capacity pre-allocated?</em>&nbsp;Answer below.</p>



<p class="wp-block-paragraph">OpenShell&#8217;s flow is the same shape; the only difference is the added process isolation and OpenShell features.</p>



<h2 class="wp-block-heading">What substrate&#8217;s flow looks like<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/blog/playwright-k8s-sandbox-proxy.md#what-substrates-flow-looks-like"></a></h2>



<p class="wp-block-paragraph">Substrate is a different beast. Each tenant gets an &#8220;Actor&#8221; living inside a gVisor sandbox on a worker pod. The data plane is&nbsp;<code>atenet-router</code>&nbsp;(Envoy with an ext_proc filter) which dispatches to the right worker pod by&nbsp;<code>Host: &lt;actor-id&gt;.actors.resources.substrate.ate.dev</code>. Actor lifecycle (Create, Resume, Suspend, Delete) is a gRPC API on&nbsp;<code>ate-api-server</code>.</p>



<figure class="wp-block-embed is-type-rich is-provider-embed wp-block-embed-embed"><div class="wp-block-embed__wrapper">
<a href="https://viewscreen.githubusercontent.com/markdown/mermaid?docs_host=https%3A%2F%2Fdocs.github.com&#038;color_mode=light#adea08fd-13b7-4d8c-828f-6540909bfc0a" rel="nofollow">https://viewscreen.githubusercontent.com/markdown/mermaid?docs_host=https%3A%2F%2Fdocs.github.com&#038;color_mode=light#adea08fd-13b7-4d8c-828f-6540909bfc0a</a>
</div></figure>



<p class="wp-block-paragraph">In principle, substrate gives you&nbsp;<em>persistent</em>&nbsp;sandboxes — suspend an actor mid-session, restore it later, and Chromium picks up where it left off with all its in-memory state intact. That&#8217;s the headline feature you don&#8217;t get from container-with-warmpool or namespace-scoped sandboxes. Whether it actually works is the interesting test result.</p>



<h2 class="wp-block-heading">What KarsSandbox&#8217;s flow looks like<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/blog/playwright-k8s-sandbox-proxy.md#what-karssandboxs-flow-looks-like"></a></h2>



<p class="wp-block-paragraph">KarsSandbox uses Azure&#8217;s KARS (Kubernetes Azure Runtime Sandboxes) controller to provision a dedicated namespace per tenant. Each&nbsp;<code>KarsSandbox</code>&nbsp;CR (kars.azure.com/v1alpha1, runtime: BYO) triggers the controller to create both a namespace and the sandbox pod within it. The proxy polls&nbsp;<code>status.phase=Running</code>&nbsp;then locates the pod IP via the CoreV1 API.</p>



<figure class="wp-block-embed is-type-rich is-provider-embed wp-block-embed-embed"><div class="wp-block-embed__wrapper">
<a href="https://viewscreen.githubusercontent.com/markdown/mermaid?docs_host=https%3A%2F%2Fdocs.github.com&#038;color_mode=light#9d2da80b-ec46-4bc1-a2bc-e4b2bdc22d1c" rel="nofollow">https://viewscreen.githubusercontent.com/markdown/mermaid?docs_host=https%3A%2F%2Fdocs.github.com&#038;color_mode=light#9d2da80b-ec46-4bc1-a2bc-e4b2bdc22d1c</a>
</div></figure>



<p class="wp-block-paragraph">Unlike agent-sandbox&#8217;s warmpool or substrate&#8217;s actor pool, KARS provisions resources on-demand. The tradeoff is no pre-warmed capacity, but you get namespace-level isolation that plays well with Azure-specific features like InferencePolicy for GPU scheduling.</p>



<p class="wp-block-paragraph"><strong>Configuration:</strong></p>



<pre class="wp-block-preformatted">BACKEND=karssandbox
KARS_SANDBOX_IMAGE=&lt;your-playwright-image&gt;     # Required: sandbox container image
KARS_INFERENCE_REF=&lt;inference-policy-name&gt;     # Optional: for AI/GPU workloads</pre>



<p class="wp-block-paragraph"><strong>State across runs:</strong>&nbsp;None, like agent-sandbox. A KarsSandbox CR creates a dedicated namespace and pod; when the CR is deleted (idle reap or explicit Delete), both the namespace and pod are destroyed by the KARS controller. The next caller for the same id gets a brand-new isolated sandbox. Reuse only happens while the sandbox is alive.</p>



<p class="wp-block-paragraph"><strong>RBAC requirements:</strong>&nbsp;The proxy needs additional permissions beyond the base ClusterRole:</p>



<pre class="wp-block-preformatted">- apiGroups: ["kars.azure.com"]
  resources: ["karssandboxes"]
  verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: ["kars.azure.com"]
  resources: ["karssandboxes/status"]
  verbs: ["get"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]  # To locate pod IP after KarsSandbox is Running</pre>



<p class="wp-block-paragraph">See&nbsp;<code>deploy/examples/kars/</code>&nbsp;for complete deployment manifests including proxy configuration, RBAC patches, and InferencePolicy examples.</p>



<h2 class="wp-block-heading">The numbers<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/blog/playwright-k8s-sandbox-proxy.md#the-numbers"></a></h2>



<p class="wp-block-paragraph">Run on Colima 16 GiB / 6 CPU, kind 1.33, arm64. All scenarios pass (KARS results pending).</p>


<div class="wp-block-code">
	<div class="cm-editor">
		<div class="cm-scroller">
			
<pre>
<code><div class="cm-line">| backend       | scenario | result | connect_ms | newPage_ms | goto_ms | total_ms |</div><div class="cm-line">|---------------|----------|--------|-----------:|-----------:|--------:|---------:|</div><div class="cm-line">| agent-sandbox | cold     | PASS   |        579 |        192 |      34 |      805 |</div><div class="cm-line">| agent-sandbox | warm     | PASS   |         23 |         23 |      13 |       59 |</div><div class="cm-line">| agent-sandbox | restore  | PASS   |        544 |         37 |      14 |      595 |</div><div class="cm-line">| openshell     | cold     | PASS   |        556 |         42 |      19 |      617 |</div><div class="cm-line">| openshell     | warm     | PASS   |         23 |         32 |      13 |       68 |</div><div class="cm-line">| openshell     | restore  | PASS   |        549 |         47 |      16 |      612 |</div><div class="cm-line">| substrate     | cold     | PASS   |       3610 |         72 |      33 |     3715 |</div><div class="cm-line">| substrate     | warm     | PASS   |         29 |         48 |      27 |      104 |</div><div class="cm-line">| substrate     | restore  | PASS   |        133 |         50 |      15 |      198 |</div><div class="cm-line"></div><div class="cm-line"></div></code></pre>
		</div>
	</div>
</div>


<p class="wp-block-paragraph">What this comparison says:</p>



<ul class="wp-block-list">
<li><strong>Container-with-warmpool wins on cold-start by ~6×.</strong>&nbsp;agent-sandbox and OpenShell come in ~580–600ms cold; substrate is 3.6s. The warmpool model amortizes container start-up at provisioning time; substrate has pre-warmed&nbsp;<em>worker pods</em>, but the user workload (Node + Chromium) still boots cold inside the gVisor sandbox on every cold-start.</li>



<li><strong>All three are essentially free at warm.</strong>&nbsp;60–100ms total at warm — the proxy hop and WS handshake dominate. The choice of backend doesn&#8217;t matter once the sandbox is up.</li>



<li><strong>Substrate&#8217;s &#8220;restore&#8221; is fast (198ms) for the wrong reason.</strong>&nbsp;The bench&#8217;s suspend leaves the worker pod hot — the OCI bundle is already extracted on disk — so the boot-from-spec restore reuses cached state. With working snapshot restore, this would be the most interesting cell in the table; today, it doesn&#8217;t prove much.</li>



<li><strong>The 3.6s substrate cold-start isn&#8217;t just gVisor.</strong>&nbsp;A meaningful chunk of it is Node + Chromium boot itself, plus the actor lifecycle workflow (CreateActor → AssignWorker → AteletRestore → URPC into the sentry). Running agent-sandbox under gVisor via&nbsp;<code>RuntimeClass</code>&nbsp;would add some runsc-specific overhead to its ~580ms cold, but not the full 3s gap — the rest is substrate&#8217;s per-tenant actor setup vs agent-sandbox&#8217;s &#8220;the warm pod already exists, just bind it&#8221; model.</li>
</ul>



<h2 class="wp-block-heading">When to use which<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/blog/playwright-k8s-sandbox-proxy.md#when-to-use-which"></a></h2>



<p class="wp-block-paragraph">Based on what testing actually surfaced:</p>



<p class="wp-block-paragraph"><strong>agent-sandbox is the safe default</strong>&nbsp;for browser-style workloads. Sub-second cold-start, trivial to operate (one CRD, one controller, one warmpool per template), and the model is easy to reason about — claim&#8217;s life is the pod&#8217;s life. If you need gVisor or Kata isolation, swap the&nbsp;<code>RuntimeClass</code>&nbsp;on the&nbsp;<code>SandboxTemplate</code>; you keep the same controller and the same warmpool semantics. The OpenShell flavor demonstrates how easy it is to fork the image story without touching the controller.</p>



<p class="wp-block-paragraph"><strong>OpenShell adds little value</strong>&nbsp;with agent-sandbox gVisor isolation. Adds process isolation when using default&nbsp;<code>RuntimeClass</code>.</p>



<p class="wp-block-paragraph"><strong>substrate is the right answer when you need per-tenant snapshot/ restore</strong>&nbsp;— suspend an actor mid-session, ship the checkpoint elsewhere, restore later with browser state intact. That&#8217;s the capability nothing else in this comparison offers. gVisor isolation alone is&nbsp;<em>not</em>&nbsp;the differentiator (agent-sandbox can do that too via RuntimeClass); the actor lifecycle + S3-backed snapshots is. Today the snapshot path needs work in our environment, so we&#8217;re paying substrate&#8217;s per-tenant boot cost without yet getting the persistence benefit; once snapshot restore is reliable end-to-end, the substrate story becomes very compelling.</p>



<p class="wp-block-paragraph"><strong>KarsSandbox is the choice for Azure/AKS environments</strong>&nbsp;where you need stronger isolation boundaries than pod-level (each tenant gets its own namespace) or integration with Azure-specific features like InferencePolicy for AI/GPU workloads. The on-demand provisioning model means no warmpool capacity planning, but cold-starts will be slower than agent-sandbox since KARS must create both namespace and pod from scratch. Best fit for multi-tenant scenarios on AKS where namespace-level RBAC and resource quotas matter, or when targeting Azure&#8217;s runtime sandbox extensions.</p>



<h2 class="wp-block-heading">Try it yourself<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/blog/playwright-k8s-sandbox-proxy.md#try-it-yourself"></a></h2>



<pre class="wp-block-preformatted">git clone https://github.com/carlossg/playwright-k8s-sandbox
cd playwright-k8s-sandbox
./test/harness.sh up         # spin up the agent-sandbox kind cluster
./test/harness.sh up-kars    # spin up the KARS kind cluster
./test/bench.sh all          # run cold/warm/restore against all backends
./test/bench.sh kars         # run KARS-specific benchmarks</pre>



<p class="wp-block-paragraph">For substrate you&#8217;ll also need its own kind cluster and the ate.dev control plane installed (<code>hack/install-ate-kind.sh</code>&nbsp;in the substrate repo). The full architecture deep-dive — including the sequence diagrams, identity model, idle-reap policy, and the bench methodology — is at&nbsp;<a href="https://github.com/carlossg/playwright-k8s-sandbox/blob/main/docs/ARCHITECTURE.md"><code>docs/ARCHITECTURE.md</code></a>.</p>



<p class="wp-block-paragraph">KARS test harness commands:</p>



<pre class="wp-block-preformatted">./test/harness.sh up-kars      # Create KARS cluster with controller
./test/harness.sh test kars    # Run integration tests
./test/bench.sh kars           # Run cold/warm/restore benchmarks
./test/harness.sh down-kars    # Cleanup</pre>



<p class="wp-block-paragraph">If you&#8217;re picking a Kubernetes sandboxing technology for a new workload, the meta-takeaway is: build a small harness around your actual workload (whatever it is), put it through cold/warm/restore on the candidates, and let the numbers + the debugging stories decide. The harness in this repo is built around Playwright; the same shape works for anything with a WebSocket or HTTP frontend.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2026/06/23/testing-kubernetes-sandboxing-technologies/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2186</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2015/11/kubernetes.jpg" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2015/11/kubernetes.jpg" medium="image">
			<media:title type="html">kubernetes</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>
	</item>
		<item>
		<title>Building a TripIt Visualizer in a few hours with Google Antigravity</title>
		<link>https://blog.csanchez.org/2026/01/20/building-a-tripit-visualizer-in-a-few-hours-with-google-antigravity/</link>
					<comments>https://blog.csanchez.org/2026/01/20/building-a-tripit-visualizer-in-a-few-hours-with-google-antigravity/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Tue, 20 Jan 2026 16:09:04 +0000</pubDate>
				<category><![CDATA[ai]]></category>
		<category><![CDATA[antigravity]]></category>
		<category><![CDATA[artificial-intelligence]]></category>
		<category><![CDATA[gemini]]></category>
		<category><![CDATA[llm]]></category>
		<category><![CDATA[technology]]></category>
		<category><![CDATA[travel]]></category>
		<category><![CDATA[tripit]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2169</guid>

					<description><![CDATA[We travel to see the world, but we track our travels to remember it. For years, I relied on TripIt to keep my travel history organized. But the API is no longer available and in their UI is hard to &#8230; <a href="https://blog.csanchez.org/2026/01/20/building-a-tripit-visualizer-in-a-few-hours-with-google-antigravity/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">We travel to see the world, but we track our travels to remember it. For years, I relied on <strong>TripIt</strong> to keep my travel history organized. But the API is no longer available and in their UI is hard to find older trips and get cool stats and visualizations.</p>



<p class="wp-block-paragraph">Instead of losing that history, I spent a recent holiday afternoon building a custom solution: a <strong>TripIt Data Visualization site</strong>.</p>



<p class="wp-block-paragraph">The initial motivation was simple: <strong>data ownership.</strong> I knew I could still get a JSON export of my data (thanks to GDPR), but a JSON file isn&#8217;t exactly &#8220;visual.&#8221;</p>



<h1 class="wp-block-heading"><img src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f30d.png" alt="🌍" class="wp-smiley" style="height: 1em; max-height: 1em;" /> See it in Action (Try the Sample Data)</h1>



<p class="wp-block-paragraph">You don’t need your own data export to see how it works. I’ve included a &#8220;Sample Data&#8221; mode so anyone can explore the dashboard immediately.</p>



<p class="wp-block-paragraph"><strong><img src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f449.png" alt="👉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Check out the demo here: <a target="_blank" rel="noreferrer noopener" href="https://tripit.csanchez.org/">tripit.csanchez.org</a></strong></p>



<figure class="wp-block-image size-large"><a href="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png"><img width="1024" height="842" data-attachment-id="2172" data-permalink="https://blog.csanchez.org/2026/01/20/building-a-tripit-visualizer-in-a-few-hours-with-google-antigravity/screenshot-2026-01-20-at-11-10-21/" data-orig-file="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png" data-orig-size="2482,2042" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Screenshot 2026-01-20 at 11.10.21" data-image-description="" data-image-caption="" data-large-file="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png?w=584" src="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png?w=1024" alt="A web dashboard titled &quot;Viewing Sample Data&quot; showing four key statistics cards: 23 total trips, 34 flights (123,420 miles), 23 unique countries visited, and 206 days traveling. Below the stats is a dark world map with visited countries like the USA, Brazil, Australia, and parts of Europe highlighted in bright blue." class="wp-image-2172" srcset="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png?w=1024 1024w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png?w=2048 2048w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png?w=150 150w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png?w=300 300w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png?w=768 768w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png?w=1440 1440w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<figure class="wp-block-image size-large"><a href="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png"><img width="1024" height="557" data-attachment-id="2173" data-permalink="https://blog.csanchez.org/2026/01/20/building-a-tripit-visualizer-in-a-few-hours-with-google-antigravity/screenshot-2026-01-20-at-11-02-33/" data-orig-file="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png" data-orig-size="2476,1348" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Screenshot 2026-01-20 at 11.02.33" data-image-description="" data-image-caption="" data-large-file="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png?w=584" src="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png?w=1024" alt="A dark-themed interactive world map displaying &quot;Flight Paths.&quot; Numerous purple arced lines connect cities across the globe, showing a dense network of flight routes primarily originating from Europe and connecting to North America, South America, Africa, and Asia." class="wp-image-2173" srcset="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png?w=1024 1024w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png?w=2048 2048w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png?w=150 150w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png?w=300 300w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png?w=768 768w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png?w=1440 1440w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<figure class="wp-block-image size-large"><a href="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png"><img width="1024" height="424" data-attachment-id="2175" data-permalink="https://blog.csanchez.org/2026/01/20/building-a-tripit-visualizer-in-a-few-hours-with-google-antigravity/screenshot-2026-01-20-at-11-03-18/" data-orig-file="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png" data-orig-size="2472,1024" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Screenshot 2026-01-20 at 11.03.18" data-image-description="" data-image-caption="" data-large-file="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png?w=584" src="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png?w=1024" alt="A &quot;Yearly Statistics&quot; bar chart tracking travel data from 2007 to 2026. The chart uses multi-colored bars to compare Days, Flights, Miles, and Trips per year, showing a significant peak in travel around 2019 and a visible dip during 2020-2021." class="wp-image-2175" srcset="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png?w=1024 1024w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png?w=2048 2048w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png?w=150 150w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png?w=300 300w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png?w=768 768w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png?w=1440 1440w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<figure class="wp-block-image size-large"><a href="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png"><img loading="lazy" width="1024" height="500" data-attachment-id="2176" data-permalink="https://blog.csanchez.org/2026/01/20/building-a-tripit-visualizer-in-a-few-hours-with-google-antigravity/screenshot-2026-01-20-at-11-03-51/" data-orig-file="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png" data-orig-size="2508,1226" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Screenshot 2026-01-20 at 11.03.51" data-image-description="" data-image-caption="" data-large-file="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png?w=584" src="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png?w=1024" alt="A clean user interface showing a list of &quot;Past Trips&quot; with cards for Auckland Nature 2025, Sydney NYE 2025, Oslo Summer 2024, and Summer in Europe 2024. Each card displays the destination, date range, duration in days, and number of flights." class="wp-image-2176" srcset="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png?w=1024 1024w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png?w=2048 2048w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png?w=150 150w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png?w=300 300w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png?w=768 768w, https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png?w=1440 1440w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></figure>



<hr class="wp-block-separator has-alpha-channel-opacity" />



<h1 class="wp-block-heading">The Goal: Beyond the Itinerary</h1>



<p class="wp-block-paragraph">I wanted more than just a list of past trips. I wanted a comprehensive dashboard that felt like a mix of the best travel apps out there:</p>



<ul class="wp-block-list">
<li><strong>Countries Visited:</strong> A high-level view of global coverage.</li>



<li><strong>Deep Statistics:</strong> Yearly and monthly breakdowns of flights, miles, and airline preferences.</li>
</ul>



<ul class="wp-block-list">
<li><strong>Interactive Flight Paths:</strong> View every take-off and landing as a beautiful arc on a global map.</li>



<li><strong>Country Tracking:</strong> Automatically highlights every country you&#8217;ve visited based on your trip history.</li>



<li><strong>Deep-Dive Statistics:</strong> Automated breakdowns of your travels by year, month, and even specific airlines.</li>



<li><strong>Privacy-First Architecture:</strong> Your data never leaves your browser. There is no backend server storing your history; it’s all processed locally using your tripit export.</li>



<li><strong>Zero Friction:</strong> Don&#8217;t have your data yet? You can explore every feature using the Gemini generated <strong>Sample Data</strong> .</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" />



<h1 class="wp-block-heading">The Secret Sauce: Building with Google Antigravity &amp; Gemini</h1>



<p class="wp-block-paragraph">The most remarkable part of this project wasn&#8217;t the code itself, but <strong>how fast it came together.</strong> The entire project took just a few hours, and complex visualizations were trivial to add. Which also hooks you into adding more and more features as it is too easy!</p>



<p class="wp-block-paragraph">I used the <strong>Antigravity</strong> browser, which allowed for a feedback loop with <strong>Gemini</strong>. Instead of manually debugging CSS or layout issues, I could:</p>



<ol start="1" class="wp-block-list">
<li>Ask Gemini for a design change or a new feature.</li>



<li>The AI would &#8220;see&#8221; the current state of the app via screenshots.</li>



<li>It would provide the fix or the code block instantly based on the visual context.</li>
</ol>



<p class="wp-block-paragraph">This &#8220;visual-first&#8221; development meant I could spend more time on the logic of the data and less time wrestling with the UI.</p>



<h1 class="wp-block-heading">Privacy First: Your Data Stays Yours</h1>



<p class="wp-block-paragraph">I wanted to ensure this tool was 100% private.</p>



<ul class="wp-block-list">
<li>Since there is no API, you need to request a JSON export from TripIt (thank you, GDPR!).</li>



<li>Your data never leaves your browser. It’s processed locally, visualized, and saved for the next time you open the page.</li>



<li></li>
</ul>



<p class="wp-block-paragraph"><img src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f4c1.png" alt="📁" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <strong>GitHub:</strong> <a target="_blank" rel="noreferrer noopener" href="https://github.com/carlossg/tripit-view">carlossg/tripit-view</a></p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2026/01/20/building-a-tripit-visualizer-in-a-few-hours-with-google-antigravity/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2169</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2026/01/antigravity-logo.png" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2026/01/antigravity-logo.png" medium="image">
			<media:title type="html">antigravity-logo</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>

		<media:content url="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.10.21.png?w=1024" medium="image">
			<media:title type="html">A web dashboard titled &#034;Viewing Sample Data&#034; showing four key statistics cards: 23 total trips, 34 flights (123,420 miles), 23 unique countries visited, and 206 days traveling. Below the stats is a dark world map with visited countries like the USA, Brazil, Australia, and parts of Europe highlighted in bright blue.</media:title>
		</media:content>

		<media:content url="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.02.33.png?w=1024" medium="image">
			<media:title type="html">A dark-themed interactive world map displaying &#034;Flight Paths.&#034; Numerous purple arced lines connect cities across the globe, showing a dense network of flight routes primarily originating from Europe and connecting to North America, South America, Africa, and Asia.</media:title>
		</media:content>

		<media:content url="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.18.png?w=1024" medium="image">
			<media:title type="html">A &#034;Yearly Statistics&#034; bar chart tracking travel data from 2007 to 2026. The chart uses multi-colored bars to compare Days, Flights, Miles, and Trips per year, showing a significant peak in travel around 2019 and a visible dip during 2020-2021.</media:title>
		</media:content>

		<media:content url="https://blog.csanchez.org/wp-content/uploads/2026/01/screenshot-2026-01-20-at-11.03.51.png?w=1024" medium="image">
			<media:title type="html">A clean user interface showing a list of &#034;Past Trips&#034; with cards for Auckland Nature 2025, Sydney NYE 2025, Oslo Summer 2024, and Summer in Europe 2024. Each card displays the destination, date range, duration in days, and number of flights.</media:title>
		</media:content>
	</item>
		<item>
		<title>Managing the Machine: A Practical Look at Google Antigravity</title>
		<link>https://blog.csanchez.org/2026/01/15/managing-the-machine-a-practical-look-at-google-antigravity/</link>
					<comments>https://blog.csanchez.org/2026/01/15/managing-the-machine-a-practical-look-at-google-antigravity/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Thu, 15 Jan 2026 12:52:19 +0000</pubDate>
				<category><![CDATA[ai]]></category>
		<category><![CDATA[antigravity]]></category>
		<category><![CDATA[gemini]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2162</guid>

					<description><![CDATA[By now, most of us have experimented with AI coding. But while tools like Cursor have mastered the &#8220;co-pilot&#8221; experience—keeping you in the flow of writing code—Google Antigravity (released late 2025) is trying something different. It is an agentic IDE &#8230; <a href="https://blog.csanchez.org/2026/01/15/managing-the-machine-a-practical-look-at-google-antigravity/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">By now, most of us have experimented with AI coding. But while tools like Cursor have mastered the &#8220;co-pilot&#8221; experience—keeping you in the flow of writing code—Google Antigravity (released late 2025) is trying something different. It is an <strong>agentic IDE</strong> where your primary job is reviewing &#8220;Artifacts&#8221; rather than syntax.</p>



<p class="wp-block-paragraph">One of the killer features is the <strong>Browser Integration</strong>.</p>



<h3 class="wp-block-heading">The Browser as a &#8220;Feedback Loop&#8221;</h3>



<p class="wp-block-paragraph">In Antigravity, the built-in browser isn&#8217;t just for documentation; it&#8217;s a sandbox for the AI agents. When an agent implements a UI change or a new route, it doesn&#8217;t just hope the code works. It can:</p>



<ul class="wp-block-list">
<li><strong>Self-Verify:</strong> Open a headless window to check if a component actually renders.</li>



<li><strong>Visual Debugging:</strong> Use DOM inspection to fix styling bugs that text-based models usually struggle with.</li>



<li><strong>Artifact Generation:</strong> It produces browser recordings and screenshots so you can verify the &#8220;Mission&#8221; was successful without having to manually run the build yourself.</li>
</ul>



<p class="wp-block-paragraph">It’s less about &#8220;the AI browsing the web&#8221; and more about the AI having a way to prove its work to you before you merge.</p>



<hr class="wp-block-separator has-alpha-channel-opacity" />



<h3 class="wp-block-heading">The Reality of the Free Tier: Knowing Your Quotas</h3>



<p class="wp-block-paragraph">The biggest change in the 2026 version of Antigravity is the move to <strong>Weekly Quotas</strong>. Instead of a daily reset that might cut you off mid-task, you now have a larger &#8220;bucket&#8221; of usage that refreshes weekly.</p>



<p class="wp-block-paragraph">Here is how those limits are structured for Free Tier users:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><td><strong>Category</strong></td><td><strong>Quota Type</strong></td><td><strong>Key Detail</strong></td></tr></thead><tbody><tr><td><strong>Gemini Models</strong></td><td><strong>Dedicated per Model</strong></td><td>Gemini 3 Pro and Gemini 3 Flash each have their own separate weekly allowance. If you run out of Pro, you can still use Flash for lighter tasks.</td></tr><tr><td><strong>Claude Models</strong></td><td><strong>Shared Pool</strong></td><td>All non-Google models (Claude 3.5/4.5 Sonnet and Opus) share a <strong>single combined bucket</strong>. High-intensity work with Opus will drain the same quota you use for Sonnet.</td></tr><tr><td><strong>Completions</strong></td><td><strong>Unlimited</strong></td><td>Standard inline tab-completions do not count toward your weekly agent/chat limits.</td></tr></tbody></table></figure>



<h3 class="wp-block-heading">The Strategy: Staggering Your Usage</h3>



<p class="wp-block-paragraph">Because of this &#8220;separate vs. shared&#8221; structure, the smartest way to use Antigravity is to <strong>stagger your models</strong>:</p>



<ol start="1" class="wp-block-list">
<li>Use <strong>Gemini 3 Pro</strong> for your initial architectural planning and browser-heavy UI tasks.</li>



<li>Switch to the <strong>Claude pool</strong> only when you need a &#8220;second opinion&#8221; on complex logic or specific refactoring patterns where Anthropic&#8217;s reasoning shines.</li>



<li>Save <strong>Gemini 3 Flash</strong> for documentation questions and basic unit test generation to preserve your &#8220;Heavy Reasoning&#8221; buckets.</li>
</ol>



<h3 class="wp-block-heading">Final Verdict</h3>



<p class="wp-block-paragraph">Antigravity isn&#8217;t a &#8220;Cursor killer&#8221;—it’s a different workflow. While Cursor is for when you want to be the pilot, Antigravity is for when you want to be the air traffic controller. Just keep a close eye on your <strong>Claude shared pool</strong>; if you&#8217;re not careful, a single complex feature build on Monday can leave you without Claude access for the rest of the week.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2026/01/15/managing-the-machine-a-practical-look-at-google-antigravity/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2162</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2026/01/antigravity-logo.png" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2026/01/antigravity-logo.png" medium="image">
			<media:title type="html">antigravity-logo</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>
	</item>
		<item>
		<title>Self-Healing Rollouts: Automating Production Fixes with Agentic AI and Argo Rollouts</title>
		<link>https://blog.csanchez.org/2025/10/15/self-healing-rollouts-automating-production-fixes-with-agentic-ai-and-argo-rollouts/</link>
					<comments>https://blog.csanchez.org/2025/10/15/self-healing-rollouts-automating-production-fixes-with-agentic-ai-and-argo-rollouts/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Wed, 15 Oct 2025 10:56:54 +0000</pubDate>
				<category><![CDATA[ai]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[artificial-intelligence]]></category>
		<category><![CDATA[kubernetes]]></category>
		<category><![CDATA[technology]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2147</guid>

					<description><![CDATA[Rolling out changes to all users at once in production is risky—we&#8217;ve all learned this lesson at some point. But what if we could combine progressive delivery techniques with AI agents to automatically detect, analyze, and fix deployment issues? In &#8230; <a href="https://blog.csanchez.org/2025/10/15/self-healing-rollouts-automating-production-fixes-with-agentic-ai-and-argo-rollouts/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" class="youtube-player" width="584" height="329" src="https://www.youtube.com/embed/ABjIUw7ojiU?version=3&#038;rel=1&#038;showsearch=0&#038;showinfo=1&#038;iv_load_policy=1&#038;fs=1&#038;hl=en&#038;autohide=2&#038;wmode=transparent&#038;listType=playlist&#038;list=PLHsuXkXI4xdjGlGkCBdxIAmkzfWXqsUrO" allowfullscreen="true" style="border:0;" sandbox="allow-scripts allow-same-origin allow-popups allow-presentation allow-popups-to-escape-sandbox"></iframe>
</div></figure>



<p class="wp-block-paragraph">Rolling out changes to all users at once in production is risky—we&#8217;ve all learned this lesson at some point. But what if we could combine progressive delivery techniques with AI agents to automatically detect, analyze, and fix deployment issues? In this article, I&#8217;ll show you how to implement self-healing rollouts using&nbsp;<a href="https://argoproj.github.io/rollouts/">Argo Rollouts</a>&nbsp;and agentic AI to create a fully automated feedback loop that can fix production issues while you grab a coffee.</p>



<h2 class="wp-block-heading" id="the-case-for-progressive-delivery">The Case for Progressive Delivery</h2>



<p class="wp-block-paragraph"><a href="https://redmonk.com/jgovernor/2018/08/06/towards-progressive-delivery/">Progressive Delivery</a>&nbsp;is a term that encompasses deployment strategies designed to avoid the pitfalls of all-or-nothing deployments. The concept gained significant attention after the&nbsp;<a href="https://www.crowdstrike.com/falcon-content-update-remediation-and-guidance-hub/">CrowdStrike incident</a>, where a faulty update took down a substantial portion of the internet. Their post-mortem revealed a crucial lesson: they should have deployed to progressive &#8220;rings&#8221; or &#8220;waves&#8221; of customers, with time between deployments to gather metrics and telemetry.</p>



<p class="wp-block-paragraph">The key principles of progressive delivery are:</p>



<ul class="wp-block-list">
<li><strong>Avoiding downtime</strong>: Deploy changes gradually with quick rollback capabilities</li>



<li><strong>Limiting the blast radius</strong>: Only a small percentage of users are affected if something goes wrong</li>



<li><strong>Shorter time to production</strong>: Safety nets enable faster, more confident deployments</li>
</ul>



<p class="wp-block-paragraph">As I like to say: &#8220;If you haven&#8217;t automatically destroyed something by mistake, you&#8217;re not automating enough.&#8221;</p>



<h2 class="wp-block-heading" id="progressive-delivery-techniques">Progressive Delivery Techniques</h2>



<h3 class="wp-block-heading" id="rolling-updates">Rolling Updates</h3>



<p class="wp-block-paragraph"><a href="https://kubernetes.io/">Kubernetes</a>&nbsp;provides rolling updates by default. As new pods come up, old pods are gradually deleted, automatically shifting traffic to the new version. If issues arise, you can roll back quickly, affecting only the percentage of traffic that hit the new pods during the update window.</p>



<h3 class="wp-block-heading" id="blue-green-deployment">Blue-Green Deployment</h3>



<p class="wp-block-paragraph">This technique involves deploying a complete copy of your application (the &#8220;blue&#8221; version) alongside the existing production version (the &#8220;green&#8221; version). After testing, you switch all traffic to the new version. While this provides quick rollbacks, it requires twice the resources and switches all traffic at once, potentially affecting all users before you can react.</p>



<h3 class="wp-block-heading" id="canary-deployment">Canary Deployment</h3>



<p class="wp-block-paragraph">Canary deployments offer more granular control. You deploy a new version alongside the stable version and gradually increase the percentage of traffic going to the new version—perhaps starting with 5%, then 10%, and so on. You can route traffic based on various parameters: internal employees, IP ranges, or random percentages. This approach allows you to detect issues early while minimizing user impact.</p>



<h3 class="wp-block-heading" id="feature-flags">Feature Flags</h3>



<p class="wp-block-paragraph"><a href="https://martinfowler.com/articles/feature-toggles.html">Feature flags</a>&nbsp;provide even more granular control at the application level. You can deploy code with new features disabled by default, then enable them selectively for specific user groups. This decouples deployment from feature activation, allowing you to:</p>



<ul class="wp-block-list">
<li>Ship faster without immediate risk</li>



<li>Enable features for specific customers or user segments</li>



<li>Quickly disable problematic features without redeployment</li>
</ul>



<p class="wp-block-paragraph">You can implement feature flags using dedicated services like&nbsp;<a href="https://openfeature.dev/">OpenFeature</a>&nbsp;or simpler approaches like environment variables.</p>



<h2 class="wp-block-heading" id="progressive-delivery-in-kubernetes">Progressive Delivery in Kubernetes</h2>



<p class="wp-block-paragraph">Kubernetes provides two main architectures for traffic routing:</p>



<h3 class="wp-block-heading" id="service-architecture">Service Architecture</h3>



<p class="wp-block-paragraph">The traditional approach uses load balancers directing traffic to services, which then route to pods based on labels. This works well for basic scenarios but lacks flexibility for advanced routing.</p>



<h3 class="wp-block-heading" id="ingress-architecture">Ingress Architecture</h3>



<p class="wp-block-paragraph">The Ingress layer provides more sophisticated traffic management. You can route traffic based on domains, paths, headers, and other criteria, enabling fine-grained control essential for canary deployments. Popular ingress controllers include:</p>



<ul class="wp-block-list">
<li>Cloud provider options (AWS, GCE)</li>



<li><a href="https://kubernetes.github.io/ingress-nginx/">NGINX</a></li>



<li><a href="https://www.getambassador.io/">Ambassador</a>&nbsp;(based on&nbsp;<a href="https://www.envoyproxy.io/">Envoy</a>)</li>



<li><a href="https://istio.io/">Istio</a>&nbsp;Ingress</li>



<li><a href="https://traefik.io/">Traefik</a></li>



<li><a href="https://www.haproxy.org/">HAProxy</a></li>
</ul>



<h2 class="wp-block-heading" id="enter-argo-rollouts">Enter Argo Rollouts</h2>



<p class="wp-block-paragraph">Argo Rollouts is a Kubernetes controller that provides advanced deployment capabilities including blue-green deployments, canary releases, analysis, and experimentation. It&#8217;s a powerful tool for implementing progressive delivery in Kubernetes environments.</p>



<h3 class="wp-block-heading" id="how-argo-rollouts-works">How Argo Rollouts Works</h3>



<p class="wp-block-paragraph">The architecture includes:</p>



<ol class="wp-block-list">
<li><strong>Rollout Controller</strong>: Manages the deployment process</li>



<li><strong>Rollout Object</strong>: Defines the deployment strategy and analysis configuration</li>



<li><strong>Analysis Templates</strong>: Specify metrics and success criteria</li>



<li><strong>Replica Sets</strong>: Manages stable and canary versions with automatic traffic shifting</li>
</ol>



<p class="wp-block-paragraph">When you update a Rollout, it creates separate replica sets for stable and canary versions, gradually increasing canary pods while decreasing stable pods based on your defined rules. If you&#8217;re using a service mesh or advanced ingress, you can implement fine-grained routing—sending specific headers, paths, or user segments to the canary version.</p>



<h3 class="wp-block-heading" id="analysis-options">Analysis Options</h3>



<p class="wp-block-paragraph">Argo Rollouts supports various analysis methods:</p>



<ul class="wp-block-list">
<li><strong><a href="https://prometheus.io/">Prometheus</a></strong>: Query metrics to determine rollout health</li>



<li><strong><a href="https://www.datadoghq.com/">Datadog</a></strong>: Integration with Datadog monitoring</li>



<li><strong><a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/">Kubernetes Jobs</a></strong>: Run custom analysis logic—check databases, call APIs, or perform any custom validation</li>
</ul>



<p class="wp-block-paragraph">The experimentation feature is particularly interesting. We considered using it to test Java upgrades: deploy a new Java version, run it for a few hours gathering metrics on response times and latency, then decide whether to proceed with the full rollout—all before affecting real users.</p>



<h2 class="wp-block-heading" id="adding-ai-to-the-mix">Adding AI to the Mix</h2>



<p class="wp-block-paragraph">Now, here&#8217;s where it gets interesting: what if we use AI to analyze logs and automatically make rollout decisions?</p>



<h3 class="wp-block-heading" id="the-ai-powered-analysis-plugin">The AI-Powered Analysis Plugin</h3>



<p class="wp-block-paragraph">I developed a plugin for Argo Rollouts that uses Large Language Models (specifically&nbsp;<a href="https://ai.google.dev/gemini-api">Google&#8217;s Gemini</a>) to analyze deployment logs and make intelligent decisions about whether to promote or rollback a deployment. The workflow is:</p>



<ol class="wp-block-list">
<li><strong>Log Collection</strong>: Gather logs from stable and canary versions</li>



<li><strong>AI Analysis</strong>: Send logs to an LLM with a structured prompt</li>



<li><strong>Decision Making</strong>: The AI responds with a promote/rollback recommendation and confidence level</li>



<li><strong>Automated Action</strong>: Argo Rollouts automatically promotes or rolls back based on the AI&#8217;s decision</li>
</ol>



<p class="wp-block-paragraph">The prompt asks the LLM to:</p>



<ul class="wp-block-list">
<li>Analyze canary behavior compared to the stable version</li>



<li>Respond in JSON format with a boolean promotion decision</li>



<li>Provide a confidence level (0-100%)</li>
</ul>



<p class="wp-block-paragraph">For example, if the confidence threshold is set to 50%, any recommendation with confidence above 50% is executed automatically.</p>



<h2 class="wp-block-heading" id="the-complete-self-healing-loop">The Complete Self-Healing Loop</h2>



<p class="wp-block-paragraph">But we can go further. When a rollout fails and rolls back, the plugin automatically:</p>



<ol class="wp-block-list">
<li><strong>Creates a&nbsp;<a href="https://github.com/">GitHub</a>&nbsp;Issue</strong>: The LLM generates an appropriate title and detailed description of the problem, including log analysis and recommended fixes</li>



<li><strong>Assigns a Coding Agent</strong>: Labels the issue to trigger agents like&nbsp;<a href="https://jules.google/">Jules</a>,&nbsp;<a href="https://github.com/features/copilot">GitHub Copilot</a>, or similar tools</li>



<li><strong>Automatic Fix</strong>: The coding agent analyzes the issue, creates a fix, and submits a pull request</li>



<li><strong>Continuous Loop</strong>: Once merged, the new version goes through the same rollout process</li>
</ol>



<h3 class="wp-block-heading" id="live-demo-results">Live Demo Results</h3>



<p class="wp-block-paragraph">In my live demonstration, I showed this complete workflow in action:</p>



<p class="wp-block-paragraph"><strong>Successful Deployment</strong>: When deploying a working version (changing from &#8220;blue&#8221; to &#8220;green&#8221;), the rollout progressed smoothly through the defined steps (20%, 40%, 60%, 80%, 100%) at 10-second intervals. The AI analyzed the logs and determined: &#8220;The stable version consistently returns 100 blue, the canary version returns 100 green, both versions return 200 status codes. Based on the logs, the canary version seems stable.&#8221;</p>



<p class="wp-block-paragraph"><strong>Failed Deployment</strong>: When deploying a broken version that returned random colors and threw panic errors, the system:</p>



<ul class="wp-block-list">
<li>Detected the issue during the canary phase</li>



<li>Automatically rolled back to the stable version</li>



<li>The AI analysis identified: &#8220;The canary version returns a mix of colors (purple, blue, green, orange, yellow) along with several panic errors due to runtime error index out of range with length zero&#8221;</li>



<li>Provided a confidence level of 95% that the deployment should not be promoted</li>



<li>Automatically created a GitHub issue with detailed analysis</li>



<li>Assigned the issue to Jules (coding agent)</li>



<li>Within 3-5 minutes, received a pull request with a fix</li>
</ul>



<p class="wp-block-paragraph">The coding agents (I demonstrated both Jules and GitHub Copilot) analyzed the code, identified the problem in the&nbsp;<code>getColor()</code>&nbsp;function, fixed the bug, added tests, and created well-documented pull requests with proper commit messages.</p>



<h2 class="wp-block-heading" id="technical-implementation">Technical Implementation</h2>



<h3 class="wp-block-heading" id="the-rollout-configuration">The Rollout Configuration</h3>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: canary-demo
spec:
  strategy:
    canary:
      analysis:
        templates:
          - templateName: canary-analysis-ai

</pre></div>


<h3 class="wp-block-heading" id="the-analysis-template">The Analysis Template</h3>



<p class="wp-block-paragraph">The template configures the AI plugin to check every 10 seconds and require a confidence level above 50% for promotion:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: canary-analysis-ai
spec:
  metrics:
    - name: success-rate
      interval: 10s
      successCondition: result &gt; 0.50
      provider:
        plugin:
          argoproj-labs/metric-ai:
            model: gemini-2.0-flash
            githubUrl: https://github.com/carlossg/rollouts-demo
            extraPrompt: |
              Ignore color changes.
</pre></div>


<h3 class="wp-block-heading" id="agent-to-agent-communication">Agent-to-Agent Communication</h3>



<p class="wp-block-paragraph">The plugin supports two modes:</p>



<ol class="wp-block-list">
<li><strong>Inline Mode</strong>: The plugin directly calls the LLM, makes decisions, and creates GitHub issues</li>



<li><strong>Agent Mode</strong>: Uses agent-to-agent (A2A) communication to call specialized agents with domain-specific knowledge and tools</li>
</ol>



<p class="wp-block-paragraph">The native mode is particularly powerful because you can build agents that understand your specific problem space, with access to internal databases, monitoring tools, or other specialized resources.</p>



<h2 class="wp-block-heading" id="the-future-of-self-healing-systems">The Future of Self-Healing Systems</h2>



<p class="wp-block-paragraph">This approach demonstrates the practical application of AI agents in production environments. The key insight is creating a continuous feedback loop:</p>



<ol class="wp-block-list">
<li>Deploy changes progressively</li>



<li>Automatically detect issues</li>



<li>Roll back when necessary</li>



<li>Generate detailed issue reports</li>



<li>Let AI agents propose fixes</li>



<li>Review and merge fixes</li>



<li>Repeat</li>
</ol>



<p class="wp-block-paragraph">The beauty of this system is that it works continuously. You can have multiple issues being addressed simultaneously by different agents, working 24/7 to keep your systems healthy. As humans, we just need to review and ensure the proposed fixes align with our intentions.</p>



<h2 class="wp-block-heading" id="practical-considerations">Practical Considerations</h2>



<p class="wp-block-paragraph">While this technology is impressive, it&#8217;s important to note:</p>



<ul class="wp-block-list">
<li><strong>AI isn&#8217;t perfect</strong>: The agents don&#8217;t always get it right on the first try (as demonstrated when the AI ignored my instruction about color variations)</li>



<li><strong>Human oversight is still crucial</strong>: Review pull requests before merging</li>



<li><strong>Start simple</strong>: Begin with basic metrics before adding AI analysis</li>



<li><strong>Tune your confidence thresholds</strong>: Adjust based on your risk tolerance</li>



<li><strong>Monitor the monitors</strong>: Ensure your analysis systems are reliable</li>
</ul>



<h2 class="wp-block-heading" id="getting-started">Getting Started</h2>



<p class="wp-block-paragraph">If you want to implement similar systems:</p>



<ol class="wp-block-list">
<li><strong>Start with Argo Rollouts</strong>: Learn basic canary deployments without AI</li>



<li><strong>Implement analysis</strong>: Use Prometheus or custom jobs for analysis</li>



<li><strong>Add AI gradually</strong>: Experiment with AI analysis for non-critical deployments</li>



<li><strong>Build the feedback loop</strong>: Integrate issue creation and coding agents</li>



<li><strong>Iterate and improve</strong>: Refine your prompts and confidence thresholds</li>
</ol>



<h2 class="wp-block-heading" id="conclusion">Conclusion</h2>



<p class="wp-block-paragraph">Progressive delivery isn&#8217;t new, but combining it with agentic AI creates powerful new possibilities for self-healing systems. While we&#8217;re not at full autonomous production management yet, we&#8217;re getting closer. The technology exists today to automatically detect, analyze, and fix many production issues without human intervention.</p>



<p class="wp-block-paragraph">As I showed in the demo, you can literally watch the system detect a problem, roll back automatically, create an issue, and have a fix ready for review—all while you&#8217;re having coffee. That&#8217;s the future I want to work toward: systems that heal themselves and learn from their mistakes.</p>



<h2 class="wp-block-heading" id="resources">Resources</h2>



<ul class="wp-block-list">
<li><a href="https://argoproj.github.io/rollouts/">Argo Rollouts Documentation</a></li>



<li><a href="https://github.com/argoproj-labs/rollouts-plugin-metric-ai/">AI Metric Plugin for Argo Rollouts</a></li>



<li><a href="https://github.com/carlossg/rollouts-demo">Demo Repository</a></li>
</ul>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2025/10/15/self-healing-rollouts-automating-production-fixes-with-agentic-ai-and-argo-rollouts/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2147</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2025/10/argo-icon-color-square.png" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2025/10/argo-icon-color-square.png" medium="image">
			<media:title type="html">argo-icon-color-square</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>
	</item>
		<item>
		<title>Monoliths vs micro-services, here we go again</title>
		<link>https://blog.csanchez.org/2023/05/11/monoliths-vs-micro-services-here-we-go-again/</link>
					<comments>https://blog.csanchez.org/2023/05/11/monoliths-vs-micro-services-here-we-go-again/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Thu, 11 May 2023 14:21:41 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[devops]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2139</guid>

					<description><![CDATA[The micro-services vs monoliths battle is heating up. The latest munition is Amazon Prime Video article Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%. At Adobe Experience Manager Cloud Service we are running the whole range &#8230; <a href="https://blog.csanchez.org/2023/05/11/monoliths-vs-micro-services-here-we-go-again/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">The micro-services vs monoliths battle is heating up. The latest munition is Amazon Prime Video article <a href="https://www.primevideotech.com/video-streaming/scaling-up-the-prime-video-audio-video-monitoring-service-and-reducing-costs-by-90">Scaling up the Prime Video audio/video monitoring service and reducing costs by 90%</a>.</p>



<p class="wp-block-paragraph">At Adobe Experience Manager Cloud Service we are running the whole range from tiny micro-services to big Java monoliths. So I&#8217;ll try to give you my personal balanced view on the topic.</p>



<p class="wp-block-paragraph">Any reasonably sized product with a bit of history is going to have a mix of micro-services and monoliths. Micro-services are not about the code, but the organization. This is the most valuable selling point. You cannot have velocity when multiple teams and lots of people making decisions and synchronizing multiple codebases. So to move fast you need some micro-services (for some definition of &#8220;micro&#8221;).</p>



<p class="wp-block-paragraph">On one hand we have monoliths that are easier to understand or follow as everything is in the same place, contributed to by multiple teams. They require synchronization and locking around code, releases, tests, etc as multiple teams need to be involved. As time passes these monoliths can grow increasing the synchronization issues. But they are fast and efficient as all the calls between modules happen in-process and the overhead is minimal as much functionality is put together.</p>



<p class="wp-block-paragraph">On the other hand we have micro-services that are harder to grasp as there are calls between multiple of them that are typically spread out across multiple git repos. The spreading of compute causes more inefficiencies, network latencies, more overhead as common functionality is duplicated in each micro-service, etc. But the responsibility is clearly delimited through APIs and interfaces that makes it easier to understand who is responsible and identify where problems are.</p>



<p class="wp-block-paragraph">There is a lot of talk about teams owning one service, but I don&#8217;t think this is realistic. As time goes by services are developed and then move into more of a maintenance role that requires less engineering time and the team moves own to create other services that provide value. So any team will own multiple services, as (if) functionality grows.</p>



<p class="wp-block-paragraph">For our teams splitting the monolith brings several benefits that steam from two: full ownership and faster iterations</p>



<ul class="wp-block-list">
<li>the service is owned by a team</li>



<li>independent testing and release cycles mean faster time to market</li>



<li>pick the right tool/language for the job (or the prefered one by the team, not necessarily the best <img src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /> )</li>
</ul>



<p class="wp-block-paragraph">Some problems I have seen:</p>



<ul class="wp-block-list">
<li>knowledge is limited to the owner team, there is no motivation for other teams to understand a service</li>



<li>duplication of efforts, multiple teams doing the same things (release management, logging, monitoring, etc). This is where Platform teams and Developer eXperience are supposed to jump in to make things easier</li>



<li>duplication of infrastructure and tooling around it. For example each micro-service should have their own image registry, databases, etc</li>



<li>interactions between services are in a bit of a limbo</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2023/05/11/monoliths-vs-micro-services-here-we-go-again/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2139</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2023/05/cog-icon-vector-11187838.jpg" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2023/05/cog-icon-vector-11187838.jpg" medium="image">
			<media:title type="html">cog-icon-vector-11187838</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>
	</item>
		<item>
		<title>Serverless Jenkins Pipelines with Google Cloud Run</title>
		<link>https://blog.csanchez.org/2021/06/15/serverless-jenkins-pipelines-with-google-cloud-run/</link>
					<comments>https://blog.csanchez.org/2021/06/15/serverless-jenkins-pipelines-with-google-cloud-run/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Tue, 15 Jun 2021 17:22:23 +0000</pubDate>
				<category><![CDATA[cloud]]></category>
		<category><![CDATA[jenkins]]></category>
		<category><![CDATA[gcp]]></category>
		<category><![CDATA[gcr]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[googlecloudrun]]></category>
		<category><![CDATA[serverless]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2124</guid>

					<description><![CDATA[Jenkinsfile-Runner-Google-Cloud-Run project is a Google Cloud Run (a container native, serverless platform) Docker image to run Jenkins pipelines. It will process a GitHub webhook, git clone the repository and execute the Jenkinsfile in that git repository. It allows high scalability and pay per use with zero cost if not used. <a href="https://blog.csanchez.org/2021/06/15/serverless-jenkins-pipelines-with-google-cloud-run/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<div class="wp-block-image"><figure class="alignright is-resized"><img loading="lazy" src="https://blog.csanchez.org/wp-content/uploads/2021/05/jenkins-google-cloud-run.png" alt="jenkins-google-cloud-run" class="wp-image-2126" width="187" height="258" /></figure></div>



<p class="wp-block-paragraph"><a href="https://github.com/carlossg/jenkinsfile-runner-google-cloud-run">Jenkinsfile-Runner-Google-Cloud-Run</a>&nbsp;project is a <a href="https://cloud.google.com/run">Google Cloud Run</a> (a container native, serverless platform) Docker image to run Jenkins pipelines. It will process a GitHub webhook, git clone the repository and execute the Jenkinsfile in that git repository. It allows high scalability and pay per use with zero cost if not used.</p>



<p class="wp-block-paragraph">This image allows <code>Jenkinsfile</code>&nbsp;execution without needing a persistent Jenkins master running in the same way as&nbsp;<a href="https://medium.com/@jdrawlings/serverless-jenkins-with-jenkins-x-9134cbfe6870" rel="nofollow">Jenkins X Serverless</a>, but using the Google Cloud Run platform&nbsp;instead of Kubernetes.</p>



<h1 class="code-line wp-block-heading" id="google-cloud-run-vs-project-fn-vs-aws-lambda">Google Cloud Run vs Project Fn vs AWS Lambda</h1>



<p class="code-line wp-block-paragraph">I wrote three flavors of Jenkinsfile Runner</p>



<ul class="wp-block-list"><li class="code-line"><a title="https://github.com/carlossg/jenkinsfile-runner-google-cloud-run" href="https://github.com/carlossg/jenkinsfile-runner-google-cloud-run">Google Cloud Run</a></li><li class="code-line"><a title="https://github.com/carlossg/jenkinsfile-runner-lambda" href="https://github.com/carlossg/jenkinsfile-runner-lambda">AWS lambda</a></li><li class="code-line"><a title="https://github.com/carlossg/jenkinsfile-runner-fn" href="https://github.com/carlossg/jenkinsfile-runner-fn">Project Fn</a></li></ul>



<p class="code-line wp-block-paragraph">The image is similar to the other ones. The main difference between Lambda and Google Cloud Run is in the packaging, as Lambda layers are limited in size and are expanded in <code>/opt</code> while Google Cloud Run allows any custom Dockerfile where you can install whatever you want in a much easier way.</p>



<p class="wp-block-paragraph">This image is extending the Jenkinsfile Runner image instead of doing a Maven build with it as a dependency as it simplifies classpath magement.</p>



<h1 class="code-line wp-block-heading" id="limitations">Limitations</h1>



<p class="code-line wp-block-paragraph">Max build duration is 15 minutes but we can use a timeout value up tos 60 minutes by using&nbsp;<code>gcloud beta</code>.</p>



<p class="code-line wp-block-paragraph">Current implementation limitations:</p>



<ul class="wp-block-list"><li class="code-line"><code>checkout scm</code>&nbsp;does not work, change it to&nbsp;<code>sh 'git clone https://github.com/carlossg/jenkinsfile-runner-example.git'</code></li></ul>



<h1 class="code-line wp-block-heading" id="example">Example</h1>



<p class="wp-block-paragraph">See the&nbsp;<a href="https://github.com/carlossg/jenkinsfile-runner-example">jenkinsfile-runner-example</a>&nbsp;project for an example.</p>



<p class="wp-block-paragraph">When the PRs are built Jenkins writes a comment back to the PR to show status, as defined in the Jenkinsfile, and totally customizable.</p>



<figure class="wp-block-image size-large"><img src="https://raw.githubusercontent.com/carlossg/jenkinsfile-runner-google-cloud-run/5c7f959804db5cef910603846d1c2316a48d8694/images/pr-success.png" alt="" /></figure>



<figure class="wp-block-image size-large"><img src="https://raw.githubusercontent.com/carlossg/jenkinsfile-runner-google-cloud-run/5c7f959804db5cef910603846d1c2316a48d8694/images/pr-failure.png" alt="" /></figure>



<p class="wp-block-paragraph">Check the PRs at&nbsp;<a href="https://github.com/carlossg/jenkinsfile-runner-example/pulls">carlossg/jenkinsfile-runner-example</a></p>



<h1 class="code-line wp-block-heading" id="extending">Extending</h1>



<p class="code-line wp-block-paragraph">You can add your plugins to&nbsp;<code>plugins.txt</code>. You could also add the Configuration as Code plugin for configuration, example at&nbsp;<code>jenkins.yaml</code>.</p>



<p class="code-line wp-block-paragraph">Other tools can be added to the&nbsp;<code>Dockerfile</code>.</p>



<h1 class="code-line wp-block-heading" id="installation">Installation</h1>



<p class="code-line wp-block-paragraph">GitHub webhooks execution will time out if the call takes too long, so we also create a nodejs Google function (<code>index.js</code>) that forwards the request to Google Cloud Run and returns the response to GitHub while the build runs.</p>



<h2 class="code-line wp-block-heading" id="building">Building</h2>



<p class="code-line wp-block-paragraph">Build the package</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
mvn verify 
docker build -t jenkinsfile-runner-google-cloud-run .
</pre></div>


<h2 class="code-line wp-block-heading" id="publishing">Publishing</h2>



<p class="code-line wp-block-paragraph">Both the function and the Google Cloud Run need to be deployed.</p>



<p class="code-line wp-block-paragraph">Set&nbsp;<code>GITHUB_TOKEN_JENKINSFILE_RUNNER</code>&nbsp;to a token that allows posting PR comments. A more secure way would be to use Google Cloud Secret Manager.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
export GITHUB_TOKEN_JENKINSFILE_RUNNER=... 
PROJECT_ID=$(gcloud config get-value project 2&gt; /dev/null) 
make deploy
</pre></div>


<p class="code-line wp-block-paragraph">Note the function url and use it to create a GitHub webhook of type&nbsp;<code>json</code>.</p>



<h2 class="code-line wp-block-heading" id="execution">Execution</h2>



<p class="code-line wp-block-paragraph">To test the Google Cloud Run execution</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
URL=$(gcloud run services describe jenkinsfile-runner \ 
  --platform managed \ 
  --region us-east1 \ 
  --format 'value(status.address.url)') 

curl -v -H "Content-Type: application/json" ${URL}/handle \
  -d @src/test/resources/github.json
</pre></div>


<h2 class="code-line wp-block-heading" id="logging">Logging</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
gcloud logging read \
  "resource.type=cloud_run_revision AND resource.labels.service_name=jenkinsfile-runner" \ 
  --format "value(textPayload)" --limit 100
</pre></div>


<p class="code-line wp-block-paragraph">or</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
gcloud alpha logging tail \
  "resource.type=cloud_run_revision AND resource.labels.service_name=jenkinsfile-runner" \ 
  --format "value(textPayload)"
</pre></div>


<h2 class="code-line wp-block-heading" id="github-events">GitHub events</h2>



<p class="code-line wp-block-paragraph">Add a GitHub&nbsp;<code>json</code>&nbsp;webhook to your git repo pointing to the Google Cloud Function url than you can get with</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
gcloud functions describe jenkinsfile-runner-function \
  --format 'value(httpsTrigger.url)'
</pre></div>


<h1 class="code-line wp-block-heading" id="testing">Testing</h1>



<p class="code-line wp-block-paragraph">The image can be run locally</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
docker run -ti --rm -p 8080:8080 \
  -e GITHUB_TOKEN=${GITHUB_TOKEN_JENKINSFILE_RUNNER} \
  jenkinsfile-runner-google-cloud-run
curl -v -H "Content-Type: application/json" \
  -X POST http://localhost:8080/handle \
  -d @src/test/resources/github.json
</pre></div>


<p class="wp-block-paragraph">More information in the <a href="https://github.com/carlossg/jenkinsfile-runner-google-cloud-run">Jenkinsfile-Runner-Google-Cloud-Run GitHub page</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2021/06/15/serverless-jenkins-pipelines-with-google-cloud-run/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2124</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2021/06/jenkins-google-cloud-run.png" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2021/06/jenkins-google-cloud-run.png" medium="image">
			<media:title type="html">jenkins-google-cloud-run</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>

		<media:content url="https://blog.csanchez.org/wp-content/uploads/2021/05/jenkins-google-cloud-run.png" medium="image">
			<media:title type="html">jenkins-google-cloud-run</media:title>
		</media:content>

		<media:content url="https://raw.githubusercontent.com/carlossg/jenkinsfile-runner-google-cloud-run/5c7f959804db5cef910603846d1c2316a48d8694/images/pr-success.png" medium="image" />

		<media:content url="https://raw.githubusercontent.com/carlossg/jenkinsfile-runner-google-cloud-run/5c7f959804db5cef910603846d1c2316a48d8694/images/pr-failure.png" medium="image" />
	</item>
		<item>
		<title>Building Docker Images with Kaniko Pushing to Amazon Elastic Container Registry (ECR)</title>
		<link>https://blog.csanchez.org/2020/10/07/building-docker-images-with-kaniko-pushing-to-amazon-elastic-container-registry-ecr/</link>
					<comments>https://blog.csanchez.org/2020/10/07/building-docker-images-with-kaniko-pushing-to-amazon-elastic-container-registry-ecr/#comments</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Wed, 07 Oct 2020 15:00:00 +0000</pubDate>
				<category><![CDATA[kubernetes]]></category>
		<category><![CDATA[aws]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[ecr]]></category>
		<category><![CDATA[kaniko]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2097</guid>

					<description><![CDATA[To deploy to Amazon Elastic Container Registry (ECR) we can create a secret with AWS credentials or we can run with more secure IAM node instance roles. When running on EKS we would have an EKS worker node IAM role &#8230; <a href="https://blog.csanchez.org/2020/10/07/building-docker-images-with-kaniko-pushing-to-amazon-elastic-container-registry-ecr/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<ul class="wp-block-list"><li><a href="https://blog.csanchez.org/2020/09/02/building-docker-images-with-kaniko/">Building Docker Images with Kaniko</a></li><li><a href="https://blog.csanchez.org/2020/09/15/building-docker-images-with-kaniko-pushing-to-docker-registries/">Building Docker Images with Kaniko Pushing to Docker Registries</a></li><li><a href="https://blog.csanchez.org/2020/09/22/building-docker-images-with-kaniko-pushing-to-google-container-registry-gcr/">Building Docker Images with Kaniko Pushing to Google Container Registry (GCR)</a></li><li><a href="https://blog.csanchez.org/2020/09/30/building-docker-images-with-kaniko-pushing-to-azure-container-registry-acr/">Building Docker Images with Kaniko Pushing to Azure Container Registry (ACR)</a></li><li><a href="https://blog.csanchez.org/2020/10/07/building-docker-images-with-kaniko-pushing-to-amazon-elastic-container-registry-ecr/">Building Docker Images with Kaniko Pushing to Amazon Elastic Container Registry (ECR)</a></li></ul>



<p class="wp-block-paragraph">To deploy to Amazon Elastic Container Registry (ECR) we can create a secret with AWS credentials or we can run with more secure IAM node instance roles.</p>



<p class="wp-block-paragraph">When running on EKS we would have an EKS worker node IAM role (<code>NodeInstanceRole</code>), we need to add the IAM permissions to be able to pull and push from ECR. These permissions are grouped in the&nbsp;<code>arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser</code>&nbsp;policy, that can be attached to the node instance role.</p>



<p class="wp-block-paragraph">When using instance roles we no longer need a secret, but we still need to configure kaniko to authenticate to AWS, by using a&nbsp;<code>config.json</code>&nbsp;containing just&nbsp;<code>{ "credsStore": "ecr-login" }</code>, mounted in&nbsp;<code>/kaniko/.docker/</code>.</p>



<p class="wp-block-paragraph">We also need to create the ECR repository beforehand, and, if using caching, another one for the cache.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
REPOSITORY=kanikorepo
REGION=us-east-1
# create the repository to push to
aws ecr create-repository --repository-name ${REPOSITORY}/kaniko-demo --region ${REGION}
# when using cache we need another repository for it
aws ecr create-repository --repository-name ${REPOSITORY}/kaniko-demo/cache --region ${REGION}

cat &lt;&lt; EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: kaniko-eks
spec:
  restartPolicy: Never
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:v1.0.0
    imagePullPolicy: Always
    args: &#91;&quot;--dockerfile=Dockerfile&quot;,
            &quot;--context=git://github.com/carlossg/kaniko-demo.git&quot;,
            &quot;--destination=${ACCOUNT}.dkr.ecr.${REGION}.amazonaws.com/${REPOSITORY}/kaniko-demo:latest&quot;,
            &quot;--cache=true&quot;]
    volumeMounts:
      - name: docker-config
        mountPath: /kaniko/.docker/
    resources:
      limits:
        cpu: 1
        memory: 1Gi
  volumes:
    - name: docker-config
      configMap:
        name: docker-config
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: docker-config
data:
  config.json: |-
    { &quot;credsStore&quot;: &quot;ecr-login&quot; }
EOF
</pre></div>]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2020/10/07/building-docker-images-with-kaniko-pushing-to-amazon-elastic-container-registry-ecr/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2097</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2018/06/kaniko-logo.png" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2018/06/kaniko-logo.png" medium="image">
			<media:title type="html">Kaniko-Logo</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>
	</item>
		<item>
		<title>Building Docker Images with Kaniko Pushing to Azure Container Registry (ACR)</title>
		<link>https://blog.csanchez.org/2020/09/30/building-docker-images-with-kaniko-pushing-to-azure-container-registry-acr/</link>
					<comments>https://blog.csanchez.org/2020/09/30/building-docker-images-with-kaniko-pushing-to-azure-container-registry-acr/#respond</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Wed, 30 Sep 2020 15:00:00 +0000</pubDate>
				<category><![CDATA[kubernetes]]></category>
		<category><![CDATA[acr]]></category>
		<category><![CDATA[azure]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[kaniko]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2101</guid>

					<description><![CDATA[To push to Azure Container Registry (ACR) we can create an admin password for the ACR registry and use the standard Docker registry method or we can use a token. We use that token to craft both the standard Docker &#8230; <a href="https://blog.csanchez.org/2020/09/30/building-docker-images-with-kaniko-pushing-to-azure-container-registry-acr/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<ul class="wp-block-list"><li><a href="https://blog.csanchez.org/2020/09/02/building-docker-images-with-kaniko/">Building Docker Images with Kaniko</a></li><li><a href="https://blog.csanchez.org/2020/09/15/building-docker-images-with-kaniko-pushing-to-docker-registries/">Building Docker Images with Kaniko Pushing to Docker Registries</a></li><li><a href="https://blog.csanchez.org/2020/09/22/building-docker-images-with-kaniko-pushing-to-google-container-registry-gcr/">Building Docker Images with Kaniko Pushing to Google Container Registry (GCR)</a></li><li><a href="https://blog.csanchez.org/2020/09/30/building-docker-images-with-kaniko-pushing-to-azure-container-registry-acr/">Building Docker Images with Kaniko Pushing to Azure Container Registry (ACR)</a></li><li><a href="https://blog.csanchez.org/2020/10/07/building-docker-images-with-kaniko-pushing-to-amazon-elastic-container-registry-ecr/">Building Docker Images with Kaniko Pushing to Amazon Elastic Container Registry (ECR)</a></li></ul>



<p class="wp-block-paragraph">To push to Azure Container Registry (ACR) we can create an admin password for the ACR registry and use the standard Docker registry method or we can use a token. We use that token to craft both the standard Docker config file at&nbsp;<code>/kaniko/.docker/config.json</code>&nbsp;plus the ACR specific file used by the Docker ACR credential helper in&nbsp;<code>/kaniko/.docker/acr/config.json</code>. ACR does support caching and so it will push the intermediate layers to&nbsp;<code>${REGISTRY_NAME}.azurecr.io/kaniko-demo/cache:_some_large_uuid_</code>&nbsp;to be reused in subsequent builds.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; gutter: false; title: ; notranslate">
RESOURCE_GROUP=kaniko-demo
REGISTRY_NAME=kaniko-demo
LOCATION=eastus
az login
# Create the resource group
az group create --name $RESOURCE_GROUP -l $LOCATION
# Create the ACR registry
az acr create --resource-group $RESOURCE_GROUP --name $REGISTRY_NAME --sku Basic
# If we want to enable password based authentication
# az acr update -n $REGISTRY_NAME --admin-enabled true

# Get the token
token=$(az acr login --name $REGISTRY_NAME --expose-token | jq -r '.accessToken')

</pre></div>


<p class="wp-block-paragraph">And to build the image with kaniko</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; gutter: false; title: ; notranslate">
git clone https://github.com/carlossg/kaniko-demo.git
cd kaniko-demo

cat &lt;&lt; EOF &gt; config.json
{
  &quot;auths&quot;: {
		&quot;${REGISTRY_NAME}.azurecr.io&quot;: {}
	},
	&quot;credsStore&quot;: &quot;acr&quot;
}
EOF
cat &lt;&lt; EOF &gt; config-acr.json
{
	&quot;auths&quot;: {
		&quot;${REGISTRY_NAME}.azurecr.io&quot;: {
			&quot;identitytoken&quot;: &quot;${token}&quot;
		}
	}
}
EOF
docker run \
    -v `pwd`/config.json:/kaniko/.docker/config.json:ro \
    -v `pwd`/config-acr.json:/kaniko/.docker/acr/config.json:ro \
    -v `pwd`:/workspace \
    gcr.io/kaniko-project/executor:v1.0.0 \
    --destination $REGISTRY_NAME.azurecr.io/kaniko-demo:kaniko-docker \
    --cache

</pre></div>


<h2 class="wp-block-heading" id="in-kubernetes-2">In Kubernetes</h2>



<p class="wp-block-paragraph">If you want to create a new Kubernetes cluster</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; gutter: false; title: ; notranslate">
az aks create --resource-group $RESOURCE_GROUP \
    --name AKSKanikoCluster \
    --generate-ssh-keys \
    --node-count 2
az aks get-credentials --resource-group $RESOURCE_GROUP --name AKSKanikoCluster --admin

</pre></div>


<p class="wp-block-paragraph">In Kubernetes we need to mount the docker config file and the ACR config file with the token.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; gutter: false; title: ; notranslate">
token=$(az acr login --name $REGISTRY_NAME --expose-token | jq -r &#039;.accessToken&#039;)
cat &lt;&lt; EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: kaniko-aks
spec:
  restartPolicy: Never
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:v1.0.0
    imagePullPolicy: Always
    args: &#91;&quot;--dockerfile=Dockerfile&quot;,
            &quot;--context=git://github.com/carlossg/kaniko-demo.git&quot;,
            &quot;--destination=${REGISTRY_NAME}.azurecr.io/kaniko-demo:latest&quot;,
            &quot;--cache=true&quot;]
    volumeMounts:
    - name: docker-config
      mountPath: /kaniko/.docker/
    - name: docker-acr-config
      mountPath: /kaniko/.docker/acr/
    resources:
      limits:
        cpu: 1
        memory: 1Gi
  volumes:
  - name: docker-config
    configMap:
      name: docker-config
  - name: docker-acr-config
    secret:
      name: kaniko-secret
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: docker-config
data:
  config.json: |-
    {
      &quot;auths&quot;: {
    		&quot;${REGISTRY_NAME}.azurecr.io&quot;: {}
    	},
    	&quot;credsStore&quot;: &quot;acr&quot;
    }
---
apiVersion: v1
kind: Secret
metadata:
  name: kaniko-secret
stringData:
  config.json: |-
    {
    	&quot;auths&quot;: {
    		&quot;${REGISTRY_NAME}.azurecr.io&quot;: {
    			&quot;identitytoken&quot;: &quot;${token}&quot;
    		}
    	}
    }
EOF
</pre></div>]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2020/09/30/building-docker-images-with-kaniko-pushing-to-azure-container-registry-acr/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2101</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2018/06/kaniko-logo.png" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2018/06/kaniko-logo.png" medium="image">
			<media:title type="html">Kaniko-Logo</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>
	</item>
		<item>
		<title>Building Docker Images with Kaniko Pushing to Google Container Registry (GCR)</title>
		<link>https://blog.csanchez.org/2020/09/22/building-docker-images-with-kaniko-pushing-to-google-container-registry-gcr/</link>
					<comments>https://blog.csanchez.org/2020/09/22/building-docker-images-with-kaniko-pushing-to-google-container-registry-gcr/#comments</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Tue, 22 Sep 2020 16:00:00 +0000</pubDate>
				<category><![CDATA[kubernetes]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[gcp]]></category>
		<category><![CDATA[gcr]]></category>
		<category><![CDATA[google cloud]]></category>
		<category><![CDATA[kaniko]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2093</guid>

					<description><![CDATA[To push to Google Container Registry (GCR) we need to login to Google Cloud and mount our local $HOME/.config/gcloud containing our credentials into the kaniko container so it can push to GCR. GCR does support caching and so it will push the &#8230; <a href="https://blog.csanchez.org/2020/09/22/building-docker-images-with-kaniko-pushing-to-google-container-registry-gcr/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<ul class="wp-block-list"><li><a href="https://blog.csanchez.org/2020/09/02/building-docker-images-with-kaniko/">Building Docker Images with Kaniko</a></li><li><a href="https://blog.csanchez.org/2020/09/15/building-docker-images-with-kaniko-pushing-to-docker-registries/">Building Docker Images with Kaniko Pushing to Docker Registries</a></li><li><a href="https://blog.csanchez.org/2020/09/22/building-docker-images-with-kaniko-pushing-to-google-container-registry-gcr/">Building Docker Images with Kaniko Pushing to Google Container Registry (GCR)</a></li><li><a href="https://blog.csanchez.org/2020/09/30/building-docker-images-with-kaniko-pushing-to-azure-container-registry-acr/">Building Docker Images with Kaniko Pushing to Azure Container Registry (ACR)</a></li><li><a href="https://blog.csanchez.org/2020/10/07/building-docker-images-with-kaniko-pushing-to-amazon-elastic-container-registry-ecr/">Building Docker Images with Kaniko Pushing to Amazon Elastic Container Registry (ECR)</a></li></ul>



<p class="wp-block-paragraph">To push to Google Container Registry (GCR) we need to login to Google Cloud and mount our local <code>$HOME/.config/gcloud</code> containing our credentials into the kaniko container so it can push to GCR. GCR does support caching and so it will push the intermediate layers to <code>gcr.io/$PROJECT/kaniko-demo/cache:_some_large_uuid_</code> to be reused in subsequent builds.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; gutter: false; title: ; notranslate">
git clone https://github.com/carlossg/kaniko-demo.git
cd kaniko-demo

gcloud auth application-default login # get the Google Cloud credentials
PROJECT=$(gcloud config get-value project 2&gt; /dev/null) # Your Google Cloud project id
docker run \
    -v $HOME/.config/gcloud:/root/.config/gcloud:ro \
    -v `pwd`:/workspace \
    gcr.io/kaniko-project/executor:v1.0.0 \
    --destination gcr.io/$PROJECT/kaniko-demo:kaniko-docker \
    --cache

</pre></div>


<p class="wp-block-paragraph">kaniko can cache layers created by RUN commands in a remote repository. Before executing a command, kaniko checks the cache for the layer. If it exists, kaniko will pull and extract the cached layer instead of executing the command. If not, kaniko will execute the command and then push the newly created layer to the cache.</p>



<p class="wp-block-paragraph">We can see in the output how kaniko uploads the intermediate layers to the cache.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
INFO&#91;0001] Resolved base name golang to build-env
INFO&#91;0001] Retrieving image manifest golang
INFO&#91;0001] Retrieving image golang
INFO&#91;0004] Retrieving image manifest golang
INFO&#91;0004] Retrieving image golang
INFO&#91;0006] No base image, nothing to extract
INFO&#91;0006] Built cross stage deps: map&#91;0:&#91;/src/bin/kaniko-demo]]
INFO&#91;0006] Retrieving image manifest golang
INFO&#91;0006] Retrieving image golang
INFO&#91;0008] Retrieving image manifest golang
INFO&#91;0008] Retrieving image golang
INFO&#91;0010] Executing 0 build triggers
INFO&#91;0010] Using files from context: &#91;/workspace]
INFO&#91;0011] Checking for cached layer gcr.io/api-project-642841493686/kaniko-demo/cache:0ab16b2e8a90e3820282b9f1ef6faf5b9a083e1fbfe8a445c36abcca00236b4f...
INFO&#91;0011] No cached layer found for cmd RUN cd /src &amp;&amp; make
INFO&#91;0011] Unpacking rootfs as cmd ADD . /src requires it.
INFO&#91;0051] Using files from context: &#91;/workspace]
INFO&#91;0051] ADD . /src
INFO&#91;0051] Taking snapshot of files...
INFO&#91;0051] RUN cd /src &amp;&amp; make
INFO&#91;0051] Taking snapshot of full filesystem...
INFO&#91;0061] cmd: /bin/sh
INFO&#91;0061] args: &#91;-c cd /src &amp;&amp; make]
INFO&#91;0061] Running: &#91;/bin/sh -c cd /src &amp;&amp; make]
CGO_ENABLED=0 go build -ldflags '' -o bin/kaniko-demo main.go
INFO&#91;0065] Taking snapshot of full filesystem...
INFO&#91;0070] Pushing layer gcr.io/api-project-642841493686/kaniko-demo/cache:0ab16b2e8a90e3820282b9f1ef6faf5b9a083e1fbfe8a445c36abcca00236b4f to cache now
INFO&#91;0144] Saving file src/bin/kaniko-demo for later use
INFO&#91;0144] Deleting filesystem...
INFO&#91;0145] No base image, nothing to extract
INFO&#91;0145] Executing 0 build triggers
INFO&#91;0145] cmd: EXPOSE
INFO&#91;0145] Adding exposed port: 8080/tcp
INFO&#91;0145] Checking for cached layer gcr.io/api-project-642841493686/kaniko-demo/cache:6ec16d3475b976bd7cbd41b74000c5d2543bdc2a35a635907415a0995784676d...
INFO&#91;0146] No cached layer found for cmd COPY --from=build-env /src/bin/kaniko-demo /
INFO&#91;0146] Unpacking rootfs as cmd COPY --from=build-env /src/bin/kaniko-demo / requires it.
INFO&#91;0146] EXPOSE 8080
INFO&#91;0146] cmd: EXPOSE
INFO&#91;0146] Adding exposed port: 8080/tcp
INFO&#91;0146] No files changed in this command, skipping snapshotting.
INFO&#91;0146] ENTRYPOINT &#91;"/kaniko-demo"]
INFO&#91;0146] No files changed in this command, skipping snapshotting.
INFO&#91;0146] COPY --from=build-env /src/bin/kaniko-demo /
INFO&#91;0146] Taking snapshot of files...
INFO&#91;0146] Pushing layer gcr.io/api-project-642841493686/kaniko-demo/cache:6ec16d3475b976bd7cbd41b74000c5d2543bdc2a35a635907415a0995784676d to cache now

</pre></div>


<p class="wp-block-paragraph">If we run kaniko twice we can see how the cached layers are pulled instead of rebuilt.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
INFO&#91;0001] Resolved base name golang to build-env
INFO&#91;0001] Retrieving image manifest golang
INFO&#91;0001] Retrieving image golang
INFO&#91;0004] Retrieving image manifest golang
INFO&#91;0004] Retrieving image golang
INFO&#91;0006] No base image, nothing to extract
INFO&#91;0006] Built cross stage deps: map&#91;0:&#91;/src/bin/kaniko-demo]]
INFO&#91;0006] Retrieving image manifest golang
INFO&#91;0006] Retrieving image golang
INFO&#91;0008] Retrieving image manifest golang
INFO&#91;0008] Retrieving image golang
INFO&#91;0010] Executing 0 build triggers
INFO&#91;0010] Using files from context: &#91;/workspace]
INFO&#91;0010] Checking for cached layer gcr.io/api-project-642841493686/kaniko-demo/cache:0ab16b2e8a90e3820282b9f1ef6faf5b9a083e1fbfe8a445c36abcca00236b4f...
INFO&#91;0012] Using caching version of cmd: RUN cd /src &amp;&amp; make
INFO&#91;0012] Unpacking rootfs as cmd ADD . /src requires it.
INFO&#91;0049] Using files from context: &#91;/workspace]
INFO&#91;0049] ADD . /src
INFO&#91;0049] Taking snapshot of files...
INFO&#91;0049] RUN cd /src &amp;&amp; make
INFO&#91;0049] Found cached layer, extracting to filesystem
INFO&#91;0051] Saving file src/bin/kaniko-demo for later use
INFO&#91;0051] Deleting filesystem...
INFO&#91;0052] No base image, nothing to extract
INFO&#91;0052] Executing 0 build triggers
INFO&#91;0052] cmd: EXPOSE
INFO&#91;0052] Adding exposed port: 8080/tcp
INFO&#91;0052] Checking for cached layer gcr.io/api-project-642841493686/kaniko-demo/cache:6ec16d3475b976bd7cbd41b74000c5d2543bdc2a35a635907415a0995784676d...
INFO&#91;0054] Using caching version of cmd: COPY --from=build-env /src/bin/kaniko-demo /
INFO&#91;0054] Skipping unpacking as no commands require it.
INFO&#91;0054] EXPOSE 8080
INFO&#91;0054] cmd: EXPOSE
INFO&#91;0054] Adding exposed port: 8080/tcp
INFO&#91;0054] No files changed in this command, skipping snapshotting.
INFO&#91;0054] ENTRYPOINT &#91;"/kaniko-demo"]
INFO&#91;0054] No files changed in this command, skipping snapshotting.
INFO&#91;0054] COPY --from=build-env /src/bin/kaniko-demo /
INFO&#91;0054] Found cached layer, extracting to filesystem

</pre></div>


<h2 class="wp-block-heading" id="in-kubernetes-1">In Kubernetes</h2>



<p class="wp-block-paragraph">To deploy to GCR we can use a service account and mount it as a Kubernetes secret, but when running on Google Kubernetes Engine (GKE) it is more convenient and safe to use the node pool service account.</p>



<p class="wp-block-paragraph">When creating the GKE node pool the default configuration only includes read-only access to Storage API, and we need&nbsp;<strong>full</strong>&nbsp;access in order to push to GCR. This is something that we need to change under Add a new node pool &#8211; Security &#8211; Access scopes &#8211; Set access for each API &#8211; Storage &#8211; Full. Note that the scopes cannot be changed once the node pool has been created.</p>



<p class="wp-block-paragraph">If the nodes have the correct service account with full storage access scope then we do not need to do anything extra on our kaniko pod, as it will be able to push to GCR just fine.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; gutter: false; title: ; notranslate">
PROJECT=$(gcloud config get-value project 2&gt; /dev/null)

cat &lt;&lt; EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: kaniko-gcr
spec:
  restartPolicy: Never
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:v1.0.0
    imagePullPolicy: Always
    args: &#91;&quot;--dockerfile=Dockerfile&quot;,
            &quot;--context=git://github.com/carlossg/kaniko-demo.git&quot;,
            &quot;--destination=gcr.io/${PROJECT}/kaniko-demo:latest&quot;,
            &quot;--cache=true&quot;]
    resources:
      limits:
        cpu: 1
        memory: 1Gi
EOF
</pre></div>]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2020/09/22/building-docker-images-with-kaniko-pushing-to-google-container-registry-gcr/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2093</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2018/06/kaniko-logo.png" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2018/06/kaniko-logo.png" medium="image">
			<media:title type="html">Kaniko-Logo</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>
	</item>
		<item>
		<title>Building Docker Images with Kaniko Pushing to Docker Registries</title>
		<link>https://blog.csanchez.org/2020/09/15/building-docker-images-with-kaniko-pushing-to-docker-registries/</link>
					<comments>https://blog.csanchez.org/2020/09/15/building-docker-images-with-kaniko-pushing-to-docker-registries/#comments</comments>
		
		<dc:creator><![CDATA[Carlos Sanchez]]></dc:creator>
		<pubDate>Tue, 15 Sep 2020 16:00:00 +0000</pubDate>
				<category><![CDATA[kubernetes]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[kaniko]]></category>
		<guid isPermaLink="false">http://blog.csanchez.org/?p=2084</guid>

					<description><![CDATA[We can build a Docker image with kaniko and push it to Docker Hub or any other standard Docker registry. Running kaniko from a Docker daemon does not provide much advantage over just running a docker build, but it is &#8230; <a href="https://blog.csanchez.org/2020/09/15/building-docker-images-with-kaniko-pushing-to-docker-registries/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
										<content:encoded><![CDATA[
<ul class="wp-block-list"><li><a href="https://blog.csanchez.org/2020/09/02/building-docker-images-with-kaniko/">Building Docker Images with Kaniko</a></li><li><a href="https://blog.csanchez.org/2020/09/15/building-docker-images-with-kaniko-pushing-to-docker-registries/">Building Docker Images with Kaniko Pushing to Docker Registries</a></li><li><a href="https://blog.csanchez.org/2020/09/22/building-docker-images-with-kaniko-pushing-to-google-container-registry-gcr/">Building Docker Images with Kaniko Pushing to Google Container Registry (GCR)</a></li><li><a href="https://blog.csanchez.org/2020/09/30/building-docker-images-with-kaniko-pushing-to-azure-container-registry-acr/">Building Docker Images with Kaniko Pushing to Azure Container Registry (ACR)</a></li><li><a href="https://blog.csanchez.org/2020/10/07/building-docker-images-with-kaniko-pushing-to-amazon-elastic-container-registry-ecr/">Building Docker Images with Kaniko Pushing to Amazon Elastic Container Registry (ECR)</a></li></ul>



<p class="wp-block-paragraph">We can build a Docker image with kaniko and push it to Docker Hub or any other standard Docker registry.</p>



<p class="wp-block-paragraph">Running kaniko from a Docker daemon does not provide much advantage over just running a docker build, but it is useful for testing or validation. It also helps understand how kaniko works and how it supports the different registries and authentication mechanisms.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; gutter: false; title: ; notranslate">
git clone https://github.com/carlossg/kaniko-demo.git
cd kaniko-demo
# if you just want to test the build, no pushing
docker run \
    -v `pwd`:/workspace gcr.io/kaniko-project/executor:v1.0.0 \
    --no-push

</pre></div>


<p class="wp-block-paragraph">Building by itself is not very useful, so we want to push to a remote Docker registry.</p>



<p class="wp-block-paragraph">To push to DockerHub or any other username and password Docker registries we need to mount the Docker&nbsp;<code>config.json</code>&nbsp;file that contains the credentials. Caching will not work for DockerHub as it does not support repositories with more than 2 path sections (<code>acme/myimage/cache</code>), but it will work in Artifactory and maybe other registry implementations.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; gutter: false; title: ; notranslate">
DOCKER_USERNAME=&#91;...]
DOCKER_PASSWORD=&#91;...]
AUTH=$(echo -n &quot;${DOCKER_USERNAME}:${DOCKER_PASSWORD}&quot; | base64)
cat &lt;&lt; EOF &gt; config.json
{
    &quot;auths&quot;: {
        &quot;https://index.docker.io/v1/&quot;: {
            &quot;auth&quot;: &quot;${AUTH}&quot;
        }
    }
}
EOF
docker run \
    -v `pwd`/config.json:/kaniko/.docker/config.json:ro \
    -v `pwd`:/workspace \
    gcr.io/kaniko-project/executor:v1.0.0 \
    --destination $DOCKER_USERNAME/kaniko-demo:kaniko-docker

</pre></div>


<h2 class="wp-block-heading" id="in-kubernetes">In Kubernetes</h2>



<p class="wp-block-paragraph">In Kubernetes we can manually create a pod that will do our Docker image build. We need to provide the build context, containing the same files that we would put in the directory used when building a Docker image with a Docker daemon. It should contain the&nbsp;<code>Dockerfile</code>&nbsp;and any other files used to build the image, ie. referenced in&nbsp;<code>COPY</code>&nbsp;commands.</p>



<p class="wp-block-paragraph">As build context we can use multiple sources</p>



<ul class="wp-block-list"><li>GCS Bucket (as a&nbsp;<code>tar.gz</code>&nbsp;file)<ul><li><code>gs://kaniko-bucket/path/to/context.tar.gz</code></li></ul></li><li>S3 Bucket (as a&nbsp;<code>tar.gz</code>&nbsp;file) `<ul><li><code>s3://kaniko-bucket/path/to/context.tar.gz</code></li></ul></li><li>Azure Blob Storage (as a&nbsp;<code>tar.gz</code>&nbsp;file)<ul><li><code>https://myaccount.blob.core.windows.net/container/path/to/context.tar.gz</code></li></ul></li><li>Local Directory, mounted in the&nbsp;<code>/workspace</code>&nbsp;dir as shown above<ul><li><code>dir:///workspace</code></li></ul></li><li>Git Repository<ul><li><code>git://github.com/acme/myproject.git#refs/heads/mybranch</code></li></ul></li></ul>



<p class="wp-block-paragraph">Depending on where we want to push to, we will also need to create the corresponding secrets and config maps.</p>



<p class="wp-block-paragraph">We are going to show examples building from a git repository as it will be the most typical use case.</p>



<h2 class="wp-block-heading" id="deploying-to-docker-hub-or-a-docker-registry">Deploying to Docker Hub or a Docker registry</h2>



<p class="wp-block-paragraph">We will need the Docker registry credentials in a&nbsp;<code>config.json</code>&nbsp;file, the same way that we need them to pull images from a private registry in Kubernetes.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; gutter: false; title: ; notranslate">
DOCKER_USERNAME=&#91;...]
DOCKER_PASSWORD=&#91;...]
DOCKER_SERVER=https://index.docker.io/v1/
kubectl create secret docker-registry regcred \
    --docker-server=${DOCKER_SERVER} \
    --docker-username=${DOCKER_USERNAME} \
    --docker-password=${DOCKER_PASSWORD}

cat &lt;&lt; EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: kaniko-docker
spec:
  restartPolicy: Never
  containers:
  - name: kaniko
    image: gcr.io/kaniko-project/executor:v1.0.0
    imagePullPolicy: Always
    args: &#91;&quot;--dockerfile=Dockerfile&quot;,
            &quot;--context=git://github.com/carlossg/kaniko-demo.git&quot;,
            &quot;--destination=${DOCKER_USERNAME}/kaniko-demo&quot;]
    volumeMounts:
      - name: docker-config
        mountPath: /kaniko/.docker
    resources:
      limits:
        cpu: 1
        memory: 1Gi
  volumes:
  - name: docker-config
    projected:
      sources:
      - secret:
          name: regcred
          items:
            - key: .dockerconfigjson
              path: config.json
EOF
</pre></div>]]></content:encoded>
					
					<wfw:commentRss>https://blog.csanchez.org/2020/09/15/building-docker-images-with-kaniko-pushing-to-docker-registries/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">2084</post-id>
		<media:thumbnail url="https://blog.csanchez.org/wp-content/uploads/2018/06/kaniko-logo.png" />
		<media:content url="https://blog.csanchez.org/wp-content/uploads/2018/06/kaniko-logo.png" medium="image">
			<media:title type="html">Kaniko-Logo</media:title>
		</media:content>

		<media:content url="https://2.gravatar.com/avatar/82f433a63821f1589c90df333938ad7c7780369b1c16201b96d8000643b5ccd7?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">carlossanchez</media:title>
		</media:content>
	</item>
	</channel>
</rss>
