<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Demo on The Nested Lab</title>
    <link>https://thenestedlab.com/tags/demo/</link>
    <description>Recent content in Demo on The Nested Lab</description>
    <generator>Hugo</generator>
    <language>en-gb</language>
    <lastBuildDate>Wed, 16 Sep 2026 08:40:00 +0100</lastBuildDate>
    <atom:link href="https://thenestedlab.com/tags/demo/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Seven demo apps, one request: deploying a showcase stack via VCF Automation</title>
      <link>https://thenestedlab.com/posts/demo-apps-via-vcfa/</link>
      <pubDate>Wed, 16 Sep 2026 08:40:00 +0100</pubDate>
      <guid>https://thenestedlab.com/posts/demo-apps-via-vcfa/</guid>
      <description>KubeDoom, KubeInvaders, kube-ops-view, Pac-Man with persistent MongoDB, podinfo, Goldpinger and a Prometheus stack — deployed onto a VCFA-provisioned VKS cluster in a tenant VPC, each behind its own NSX VIP. The proper tenanted path (not the supervisor shortcut), what the platform does for free, and the traps.</description>
      <content:encoded><![CDATA[<p>Every platform needs a demo stack — something that looks alive on a
projector and quietly exercises every layer underneath. This is the lab&rsquo;s:
seven apps on a VKS cluster that VCF Automation provisioned, in a tenant
VPC, each app behind its own load-balancer VIP.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="n">kubedoom</span>       <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.20</span><span class="p">:</span><span class="mi">5900</span>   <span class="p">(</span><span class="n">VNC</span> <span class="err">—</span> <span class="n">yes</span><span class="p">,</span> <span class="n">it</span> <span class="n">kills</span> <span class="n">pods</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">kubeinvaders</span>   <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.21</span>
</span></span><span class="line"><span class="cl"><span class="n">kube</span><span class="o">-</span><span class="n">ops</span><span class="o">-</span><span class="n">view</span>  <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.22</span>
</span></span><span class="line"><span class="cl"><span class="n">pacman</span>         <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.23</span>        <span class="p">(</span><span class="o">+</span> <span class="n">MongoDB</span> <span class="n">on</span> <span class="n">a</span> <span class="n">PVC</span> <span class="err">—</span> <span class="n">persistent</span> <span class="n">high</span> <span class="n">scores</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">podinfo</span>        <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.24</span>
</span></span><span class="line"><span class="cl"><span class="n">goldpinger</span>     <span class="mf">192.168</span><span class="o">.</span><span class="mf">144.25</span>        <span class="p">(</span><span class="n">DaemonSet</span> <span class="n">incl</span><span class="o">.</span> <span class="n">control</span> <span class="n">plane</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">prometheus</span>     <span class="p">(</span><span class="ow">in</span><span class="o">-</span><span class="n">cluster</span><span class="p">:</span> <span class="n">KSM</span> <span class="o">+</span> <span class="n">node</span><span class="o">-</span><span class="n">exporter</span><span class="p">,</span> <span class="mi">11</span><span class="o">/</span><span class="mi">11</span> <span class="n">targets</span> <span class="n">up</span><span class="p">)</span>
</span></span></code></pre></div><p><img alt="Three of the seven live on their VIPs — KubeInvaders, kube-ops-view, Pac-Man — and the whole set as VCF Operations sees it" loading="lazy" src="/images/demo-apps-grid.jpg">
<em>Captured from the VIPs the platform handed out. KubeDoom is VNC-only and podinfo is an API, so they sit this one out; the Ops topology tile shows all seven by name.</em></p>
<h2 id="the-path-that-matters-tenanted-not-shortcut">The path that matters: tenanted, not shortcut</h2>
<p>The first version of this stack was deployed <em>against the supervisor</em> —
an admin kubeconfig, a vSphere namespace, <code>kubectl apply</code>. It worked and
it was wrong, for the reason the <a href="/posts/vks-kubectl-vs-vcfa-all-apps/">previous
post</a> spells out: nothing about it
was <em>provided</em> to anyone. So it was torn down (seven apps, cluster, VPC and
VIPs gone in about seven minutes) and rebuilt the proper way:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">dev-01 org → default-project → SupervisorNamespace (class large, region f06, VPC default-f06)
</span></span><span class="line"><span class="cl">          → VKS cluster vks-demo01 → seven apps
</span></span></code></pre></div><p>All created <strong>through VCF Automation</strong> — the CCI API for the namespace and
cluster, then the apps via the cluster&rsquo;s own kubeconfig. Two auth facts
worth writing down, because they cost a cycle each:</p>
<ul>
<li>A <strong>provider</strong> service account reaches the cloud API only. CCI
(<code>/cci/kubernetes/apis/...</code>) rejects provider tokens with 401 — it needs
an <strong>org-scoped</strong> service account.</li>
<li>Device-flow login uses the service account&rsquo;s own UUID as <code>client_id</code>
(not its software ID), against the tenant endpoint
<code>/oauth/tenant/&lt;org&gt;/device_authorization</code>.</li>
</ul>
<p><img alt="VCF Operations topology: the cluster with its apps named" loading="lazy" src="/images/ui/u9-ops-vks-topology.jpg"></p>
<h2 id="what-the-platform-does-for-free">What the platform does for free</h2>
<p>Each app is an ordinary Deployment + <code>Service</code> of type <code>LoadBalancer</code>. The
supervisor turns each service into an NSX VPC LB virtual server and hands
back a VIP from the org&rsquo;s external block. No ingress controller, no
MetalLB, no port-forwarding — seven services, seven VIPs, done. Pac-Man&rsquo;s
MongoDB asks for a PVC and gets a vSAN-backed volume through the CSI the
supervisor already installed. Goldpinger runs as a DaemonSet across every
node including the control plane and draws the node-to-node mesh live.</p>
<p>That&rsquo;s three platform services (LB, storage, networking) exercised by apps
that know nothing about VCF.</p>
<h2 id="the-traps">The traps</h2>
<p><strong>Supervisor refuses cluster-scoped RBAC — even to admin.</strong> Demo apps
that need ClusterRoles (kube-ops-view, Goldpinger, KubeDoom) <em>must</em> live
on a guest cluster. You cannot run them as vSphere Pods on the supervisor.</p>
<p><strong>PodSecurity <code>restricted</code> is the VKS 1.35 default.</strong> Half the demo set
runs as root. Symptom: Deployment shows <code>0 UP-TO-DATE</code>, ReplicaSet exists,
zero pods, events say <code>FailedCreate</code>. Fix: label the namespace
<code>pod-security.kubernetes.io/enforce=privileged</code> — and mention in the demo
that you did, because it&rsquo;s a teaching moment.</p>
<p><strong>node-exporter without <code>hostNetwork</code>.</strong> The VPC fabric blocks pod → node
IP scrapes, so the stock DaemonSet&rsquo;s <code>hostNetwork: true</code> doesn&rsquo;t help;
run it as a normal pod and let Prometheus scrape it in-cluster.</p>
<p><strong>Docker Hub is flaky from behind a proxy.</strong> TLS handshake timeouts put
pods into kubelet&rsquo;s image-pull backoff. Deleting the stuck pods bypasses
the backoff; a Harbor proxy-cache project fixes it properly.</p>
<p><strong>Pod CIDR shadowing.</strong> The stock <code>192.168.0.0/16</code> pod range hid the
org&rsquo;s <code>192.168.144.0/21</code> external block <em>from inside the cluster</em> — apps
couldn&rsquo;t reach their neighbours&rsquo; VIPs. Pod CIDR is now <code>172.16.0.0/16</code>.</p>
<h2 id="automation-notes">Automation notes</h2>
<p>The whole stack is a checkbox on the lab&rsquo;s catalog item that deploys a
supervisor: <code>installDemoApps</code> creates the cluster (newest compatible
Kubernetes release, newest built-in ClusterClass, auto-detected), applies
the seven apps, and reports their URLs in the deployment summary. Run
integrated on a fresh environment: 16.7 minutes to <code>CREATE_SUCCESSFUL</code>.
An immediate re-run: 3.3 minutes, all steps idempotent — which is the
number I actually care about, because it means a broken demo is a re-run,
not a rebuild.</p>
<h2 id="why-this-matters-outside-the-lab">Why this matters outside the lab</h2>
<p>A demo stack sounds like a toy. It&rsquo;s actually the fastest way to make a
platform <em>legible</em> to people who don&rsquo;t read YAML: a customer watches a
request become a cluster, watches seven services get their own addresses,
opens one and plays it. Everything underneath — self-service Kubernetes,
load balancing, persistent storage, isolation — is being exercised in a way
a non-technical stakeholder can see working. The same stack is what we
put in front of a new team on day one, and the same idempotent deploy is
what makes it safe to demonstrate live: if it breaks on stage, it re-runs
in three minutes.</p>
<h2 id="rules-learned">Rules learned</h2>
<ul>
<li>Demo apps that need cluster-scoped RBAC <strong>must</strong> run on a guest cluster;
the supervisor won&rsquo;t grant it, even to admin.</li>
<li>Build the stack through the <strong>tenanted path</strong> (org SA → CCI → namespace
→ cluster → apps). Same apps, but now they&rsquo;re provided, quota&rsquo;d and
visible in Ops.</li>
<li>VKS 1.35: <code>restricted</code> PodSecurity by default. Label the namespace and
say why.</li>
<li><code>LoadBalancer</code> per app = NSX VIP per app. No ingress needed for a demo.</li>
<li>Pick a pod CIDR that doesn&rsquo;t overlap the VPC external block.</li>
<li>Make the deploy idempotent; a demo that re-runs in 3 minutes is one you
can afford to break on stage.</li>
</ul>
<p><em>Previously: <a href="/posts/vks-kubectl-vs-vcfa-all-apps/">one VKS cluster, two ways</a>.
The Pac-Man instance here is the one from <a href="/posts/whats-a-vpc-with-pacman/">What&rsquo;s a VPC?</a>.</em></p>
<hr>
<p><em>Lab environment; opinions my own.</em></p>
]]></content:encoded>
    </item>
  </channel>
</rss>
