468 lines
27 KiB
HTML
468 lines
27 KiB
HTML
<title>planpalto-helm — render pipeline</title>
|
||
<style>
|
||
:root {
|
||
--paper: #E7EAEE;
|
||
--paper-2: #DDE1E7;
|
||
--rule: #A4ADB8;
|
||
--ink: #242A31;
|
||
--ink-dim: #5C6672;
|
||
--accent: #1F5FA8;
|
||
--accent-weak: rgba(31, 95, 168, 0.14);
|
||
--grid: rgba(164, 173, 184, 0.14);
|
||
|
||
--font-display: "IBM Plex Mono", ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", Consolas, monospace;
|
||
--font-mono: ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", Consolas, monospace;
|
||
--font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||
}
|
||
@media (prefers-color-scheme: dark) {
|
||
:root {
|
||
--paper: #161A1F; --paper-2: #1D232A; --rule: #39424D;
|
||
--ink: #D6DBE1; --ink-dim: #828C98; --accent: #4C93E0;
|
||
--accent-weak: rgba(76, 147, 224, 0.18);
|
||
--grid: rgba(57, 66, 77, 0.22);
|
||
}
|
||
}
|
||
:root[data-theme="dark"] {
|
||
--paper: #161A1F; --paper-2: #1D232A; --rule: #39424D;
|
||
--ink: #D6DBE1; --ink-dim: #828C98; --accent: #4C93E0;
|
||
--accent-weak: rgba(76, 147, 224, 0.18);
|
||
--grid: rgba(57, 66, 77, 0.22);
|
||
}
|
||
:root[data-theme="light"] {
|
||
--paper: #E7EAEE; --paper-2: #DDE1E7; --rule: #A4ADB8;
|
||
--ink: #242A31; --ink-dim: #5C6672; --accent: #1F5FA8;
|
||
--accent-weak: rgba(31, 95, 168, 0.14);
|
||
--grid: rgba(164, 173, 184, 0.14);
|
||
}
|
||
|
||
* { box-sizing: border-box; border-radius: 0; }
|
||
body {
|
||
margin: 0; padding: 20px 12px;
|
||
background: var(--paper); color: var(--ink);
|
||
font-family: var(--font-body); font-size: 14px; line-height: 1.5;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
/* Double-rule drafting frame */
|
||
.frame-outer { max-width: 1100px; margin: 0 auto; border: 2px solid var(--ink); padding: 6px; }
|
||
.frame-inner {
|
||
border: 1px solid var(--ink); padding: 24px;
|
||
background-color: var(--paper);
|
||
background-image:
|
||
repeating-linear-gradient(to right, var(--grid) 0 1px, transparent 1px 24px),
|
||
repeating-linear-gradient(to bottom, var(--grid) 0 1px, transparent 1px 24px);
|
||
}
|
||
|
||
.mono { font-family: var(--font-mono); }
|
||
.disp { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 500; }
|
||
.dim { color: var(--ink-dim); }
|
||
.acc { color: var(--accent); }
|
||
.ms { font-family: var(--font-mono); font-size: 11px; line-height: 1.35; }
|
||
|
||
.sep { border: 0; border-top: 1px solid var(--rule); margin: 20px 0; }
|
||
|
||
/* ── (a) title / context strip ── */
|
||
.strip { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; flex-wrap: wrap; }
|
||
.strip h1 { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 500; font-size: 15px; margin: 0 0 4px; text-wrap: balance; }
|
||
.strip .sub { font-size: 14px; color: var(--ink-dim); max-width: 60ch; margin: 0; }
|
||
.legend { display: flex; gap: 18px; flex-wrap: wrap; align-items: center; }
|
||
.legend .item { display: flex; align-items: center; gap: 6px; }
|
||
.sw-acc { width: 22px; height: 0; border-top: 2px solid var(--accent); }
|
||
.sw-ink { width: 22px; height: 0; border-top: 1px solid var(--ink); }
|
||
.stamp { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border: 1px solid var(--ink); font-family: var(--font-display); font-size: 11px; flex: none; }
|
||
|
||
.secthead { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 500; font-size: 12px; margin: 0 0 12px; }
|
||
.secthead .st-sub { font-family: var(--font-body); text-transform: none; letter-spacing: 0; color: var(--ink-dim); font-size: 12px; margin-left: 10px; }
|
||
|
||
/* ── (b→h) pipeline spine ── */
|
||
.spine { display: grid; grid-template-columns: repeat(6, 1fr); gap: 0; position: relative; }
|
||
.stage { padding: 0 12px 0 0; position: relative; }
|
||
.stage:not(:last-child)::after {
|
||
/* dimension arrow between stages */
|
||
content: ""; position: absolute; top: 9px; right: 4px; width: 20px; height: 0;
|
||
border-top: 1.5px solid var(--accent);
|
||
}
|
||
.stage:not(:last-child) .flow-note {
|
||
position: absolute; top: -6px; right: 2px; color: var(--accent); font-size: 10px;
|
||
}
|
||
.stage-hd { display: flex; gap: 8px; align-items: flex-start; }
|
||
.stage-stamp { width: 20px; height: 20px; border: 1px solid var(--ink); display: flex; align-items: center; justify-content: center; font-family: var(--font-display); font-size: 11px; flex: none; }
|
||
.stage-name { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; font-size: 11px; }
|
||
.stage-desc { font-size: 11px; color: var(--ink-dim); margin-top: 6px; padding-left: 0; line-height: 1.4; }
|
||
.stage-desc code { font-family: var(--font-mono); color: var(--ink); }
|
||
|
||
/* recessed panels / wells */
|
||
.well { background: var(--paper-2); border: 1px solid var(--rule); border-top: 1px solid var(--ink); padding: 14px; }
|
||
.well-hd { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.08em; font-size: 11px; margin: 0 0 10px; }
|
||
|
||
.subgrid { display: grid; grid-template-columns: 1.1fr 1fr; gap: 16px; margin-top: 14px; }
|
||
|
||
.filetree { font-family: var(--font-mono); font-size: 12px; line-height: 1.55; white-space: pre; }
|
||
.filetree .an { color: var(--ink-dim); }
|
||
|
||
/* command callout */
|
||
.cmd-wrap { margin-top: 14px; }
|
||
.cmd-lead { color: var(--accent); font-size: 10px; font-family: var(--font-mono); margin-bottom: 4px; }
|
||
.cmd-box { position: relative; border: 1px solid var(--ink); background: var(--paper); padding: 10px 12px; font-family: var(--font-mono); font-size: 13px; overflow-x: auto; }
|
||
.cmd-box::before { content: ""; position: absolute; left: -8px; top: 50%; width: 8px; height: 0; border-top: 1px solid var(--accent); }
|
||
.cmd-note { font-family: var(--font-mono); font-size: 11px; color: var(--ink-dim); margin-top: 6px; }
|
||
|
||
/* render well 3 columns */
|
||
.render3 { display: grid; grid-template-columns: 1fr 1.15fr 1fr; gap: 0; }
|
||
.render3 .rcol { padding: 0 16px; border-left: 1px solid var(--rule); position: relative; }
|
||
.render3 .rcol:first-child { border-left: 0; padding-left: 0; }
|
||
.render3 .rcol:last-child { padding-right: 0; }
|
||
.render3 .rcol::after { content: attr(data-cross); position: absolute; top: 24px; right: -6px; z-index: 2; color: var(--accent); font-size: 9px; font-family: var(--font-mono); background: var(--paper-2); padding: 0 2px; }
|
||
.render3 .rcol:last-child::after { content: none; }
|
||
.rcol .rhd { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; font-size: 11px; margin: 0 0 8px; }
|
||
.rcol pre { font-family: var(--font-mono); font-size: 11.5px; line-height: 1.5; margin: 0; white-space: pre-wrap; word-break: break-word; }
|
||
.mech { list-style: none; margin: 0; padding: 0; }
|
||
.mech li { font-family: var(--font-mono); font-size: 11.5px; line-height: 1.5; margin-bottom: 6px; }
|
||
.mech .tok { color: var(--ink); }
|
||
.mech .gl { color: var(--ink-dim); }
|
||
|
||
.twin { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 14px; }
|
||
.chain { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: 11.5px; }
|
||
.chain .bx { border: 1px solid var(--rule); padding: 3px 7px; }
|
||
.chain .ar { color: var(--accent); }
|
||
.addon-row { display: grid; grid-template-columns: auto auto 1fr; gap: 8px; align-items: baseline; font-family: var(--font-mono); font-size: 11.5px; padding: 5px 0; border-top: 1px solid var(--rule); }
|
||
.addon-row:first-of-type { border-top: 0; }
|
||
.addon-row .ar { color: var(--accent); }
|
||
.addon-row .obj { color: var(--ink); }
|
||
.addon-row .gl { color: var(--ink-dim); }
|
||
|
||
/* ── (e) BOM table ── */
|
||
.bom { width: 100%; border-collapse: collapse; font-family: var(--font-mono); font-size: 13px; }
|
||
.bom th, .bom td { text-align: left; padding: 7px 10px; border: 1px solid var(--rule); vertical-align: top; }
|
||
.bom thead th { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; font-size: 11px; color: var(--ink-dim); background: var(--paper-2); }
|
||
.bom tbody tr:nth-child(even) td { background: var(--paper-2); }
|
||
.bom .note { color: var(--accent); font-size: 11px; }
|
||
.bom .note-dim { color: var(--ink-dim); font-size: 11px; }
|
||
.bom .sum td { background: var(--accent-weak); border-top: 1px solid var(--accent); border-bottom: 1px solid var(--accent); color: var(--accent); font-size: 12.5px; }
|
||
.bom-wrap { overflow-x: auto; }
|
||
|
||
/* ── lower band ── */
|
||
.lower { display: grid; grid-template-columns: 62fr 38fr; gap: 24px; align-items: start; }
|
||
.detail { border: 1px solid var(--ink); padding: 16px 16px 14px; position: relative; }
|
||
.detail-tab { position: absolute; top: -1px; left: -1px; border: 1px solid var(--ink); background: var(--paper); font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.08em; font-size: 11px; padding: 3px 8px; }
|
||
.detail-cap { font-size: 11.5px; color: var(--ink-dim); margin: 22px 0 16px; line-height: 1.45; }
|
||
.detail-cap code { font-family: var(--font-mono); color: var(--ink); }
|
||
|
||
/* DETAIL A fan */
|
||
.fan { position: relative; }
|
||
.fan svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
|
||
.fan-grid { display: grid; grid-template-columns: minmax(150px,1.1fr) 78px minmax(210px,1.5fr); gap: 0; align-items: stretch; }
|
||
.lanes { display: flex; flex-direction: column; gap: 8px; }
|
||
.srcbox { border: 1px solid var(--rule); padding: 6px 8px; min-height: 44px; display: flex; flex-direction: column; justify-content: center; }
|
||
.srcbox .nm { font-family: var(--font-mono); font-size: 12.5px; }
|
||
.srcbox .tags { margin-top: 3px; display: flex; gap: 8px; flex-wrap: wrap; }
|
||
.srcbox .tag { font-family: var(--font-mono); font-size: 10.5px; color: var(--accent); }
|
||
.bracket-col { display: flex; align-items: center; justify-content: center; position: relative; }
|
||
.bracket-lbl { font-family: var(--font-mono); font-size: 10px; color: var(--accent); writing-mode: vertical-rl; text-orientation: mixed; letter-spacing: 0.04em; }
|
||
.outlanes { display: flex; flex-direction: column; gap: 8px; }
|
||
.oline { min-height: 44px; display: flex; align-items: center; gap: 5px; flex-wrap: nowrap; }
|
||
.objbox { border: 1px solid var(--rule); padding: 4px 7px; font-family: var(--font-mono); font-size: 11.5px; white-space: nowrap; }
|
||
.objbox.dep { border-color: var(--ink); }
|
||
.gate { font-family: var(--font-mono); font-size: 10px; color: var(--ink-dim); white-space: nowrap; }
|
||
.gate::before { content: "├ "; color: var(--accent); }
|
||
.fan-recon { font-family: var(--font-mono); font-size: 11px; color: var(--ink-dim); margin-top: 14px; line-height: 1.45; }
|
||
.fan-recon code { color: var(--ink); }
|
||
.fan-x5 { position: absolute; font-family: var(--font-mono); font-size: 10px; color: var(--accent); }
|
||
|
||
/* DETAIL B multi-release */
|
||
.rel-cols { display: flex; gap: 48px; justify-content: flex-start; }
|
||
.rel-col { flex: 1; min-width: 0; }
|
||
.rel-title { font-family: var(--font-mono); font-size: 12px; color: var(--accent); text-align: center; margin-bottom: 6px; }
|
||
.rel-stack { border: 1px solid var(--rule); }
|
||
.rel-cell { border-top: 1px solid var(--rule); padding: 7px 9px; font-family: var(--font-mono); font-size: 11.5px; line-height: 1.4; }
|
||
.rel-cell:first-child { border-top: 0; }
|
||
.rel-cell .v { color: var(--accent); }
|
||
.rel-note { font-family: var(--font-mono); font-size: 10.5px; color: var(--ink-dim); margin-top: 14px; line-height: 1.55; white-space: pre-wrap; }
|
||
.rel-note .v { color: var(--accent); }
|
||
|
||
/* ── title block ── */
|
||
.tblock-wrap { display: flex; justify-content: flex-end; margin-top: 20px; }
|
||
.tblock { width: 340px; border: 1px solid var(--ink); }
|
||
.tblock .tb-row { display: grid; grid-template-columns: 128px 1fr; border-top: 1px solid var(--rule); }
|
||
.tblock .tb-row:first-child { border-top: 0; }
|
||
.tblock .tb-k { font-family: var(--font-display); text-transform: uppercase; letter-spacing: 0.06em; font-size: 10px; color: var(--ink-dim); padding: 5px 8px; border-right: 1px solid var(--rule); }
|
||
.tblock .tb-v { font-family: var(--font-mono); font-size: 11px; padding: 5px 8px; }
|
||
.tblock .tb-v.v-acc { color: var(--accent); }
|
||
|
||
@media (max-width: 959px) {
|
||
.spine { grid-template-columns: repeat(3, 1fr); row-gap: 20px; }
|
||
.stage:nth-child(3)::after { content: none; }
|
||
.render3 { grid-template-columns: 1fr; }
|
||
.render3 .rcol { border-left: 0; padding: 12px 0 0; border-top: 1px solid var(--rule); }
|
||
.render3 .rcol:first-child { border-top: 0; padding-top: 0; }
|
||
.render3 .rcol::after { content: none; }
|
||
}
|
||
@media (max-width: 719px) {
|
||
.strip { flex-direction: column; }
|
||
.spine { grid-template-columns: 1fr; }
|
||
.stage::after { content: none !important; }
|
||
.stage { border-top: 1px solid var(--rule); padding-top: 12px; }
|
||
.stage:first-child { border-top: 0; padding-top: 0; }
|
||
.subgrid, .twin, .lower { grid-template-columns: 1fr; }
|
||
.fan-grid { grid-template-columns: 1fr; gap: 10px; }
|
||
.fan svg { display: none; }
|
||
.bracket-col { justify-content: flex-start; }
|
||
.bracket-lbl { writing-mode: horizontal-tb; }
|
||
.tblock { width: 100%; }
|
||
}
|
||
@media (prefers-reduced-motion: no-preference) {
|
||
.draw { stroke-dasharray: var(--len); stroke-dashoffset: var(--len); animation: draw 0.9s ease-out forwards; }
|
||
.fan .draw { animation-duration: 0.7s; }
|
||
@keyframes draw { to { stroke-dashoffset: 0; } }
|
||
}
|
||
</style>
|
||
|
||
<div class="frame-outer"><div class="frame-inner">
|
||
|
||
<!-- (a) title / context strip -->
|
||
<div class="strip">
|
||
<div>
|
||
<h1>planpalto-helm — render pipeline</h1>
|
||
<p class="sub">How <span class="mono">helm install</span> turns the chart on disk into running objects on the fakhri-k8 EKS cluster.</p>
|
||
</div>
|
||
<div class="legend">
|
||
<div class="item"><span class="sw-acc"></span><span class="ms dim">live / rendered value</span></div>
|
||
<div class="item"><span class="sw-ink"></span><span class="ms dim">static linework</span></div>
|
||
<div class="item"><span class="stamp">N</span><span class="ms dim">pipeline stage</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="sep">
|
||
|
||
<!-- (b→h) pipeline spine -->
|
||
<p class="secthead">Render pipeline — 6 stages</p>
|
||
<div class="spine">
|
||
<div class="stage">
|
||
<div class="stage-hd"><span class="stage-stamp">1</span><span class="stage-name">Inputs</span></div>
|
||
<p class="stage-desc"><code>Chart.yaml</code> · <code>values.yaml</code> · <code>templates/*</code> · <code>_helpers.tpl</code> · optional <code>-f envs/a.yaml</code></p>
|
||
<span class="flow-note">paths</span>
|
||
</div>
|
||
<div class="stage">
|
||
<div class="stage-hd"><span class="stage-stamp">2</span><span class="stage-name">Command</span></div>
|
||
<p class="stage-desc">You run one <code>helm</code> command. Helm reads the chart and any override file.</p>
|
||
<span class="flow-note">flags+values</span>
|
||
</div>
|
||
<div class="stage">
|
||
<div class="stage-hd"><span class="stage-stamp">3</span><span class="stage-name">Render</span></div>
|
||
<p class="stage-desc">Go templating merges values + <code>.Release.*</code>, walks <code>templates/</code>, emits one multi-doc YAML.</p>
|
||
<span class="flow-note">YAML</span>
|
||
</div>
|
||
<div class="stage">
|
||
<div class="stage-hd"><span class="stage-stamp">4</span><span class="stage-name">Submit</span></div>
|
||
<p class="stage-desc">Manifest sent to the k8s API server. Helm records the revision in a Secret.</p>
|
||
<span class="flow-note">objects</span>
|
||
</div>
|
||
<div class="stage">
|
||
<div class="stage-hd"><span class="stage-stamp">5</span><span class="stage-name">Reconcile</span></div>
|
||
<p class="stage-desc">Controllers act: Deployment → ReplicaSet → Pod. Scheduler places Pods on EKS nodes.</p>
|
||
<span class="flow-note">pods</span>
|
||
</div>
|
||
<div class="stage">
|
||
<div class="stage-hd"><span class="stage-stamp">6</span><span class="stage-name">Add-ons</span></div>
|
||
<p class="stage-desc">ESO, AWS Load Balancer Controller, and metrics-server finish the job.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- (b) inputs + (c) command -->
|
||
<div class="subgrid">
|
||
<div class="well">
|
||
<p class="well-hd">1 · Inputs on disk</p>
|
||
<div class="filetree">planpalto-helm/
|
||
├─ Chart.yaml
|
||
├─ values.yaml <span class="an">apps: map (5 entries) + deps + global</span>
|
||
├─ templates/
|
||
│ ├─ namespace.yaml
|
||
│ ├─ apps.yaml <span class="acc">{{ range .Values.apps }}</span>
|
||
│ ├─ deps.yaml <span class="an">redis, nats</span>
|
||
│ ├─ external-secrets.yaml
|
||
│ ├─ ingress.yaml
|
||
│ └─ seed-job.yaml
|
||
└─ _helpers.tpl <span class="an">planpal.namespace / .storeName / .image</span>
|
||
+ override: <span class="acc">-f envs/a.yaml</span></div>
|
||
</div>
|
||
<div>
|
||
<div class="cmd-wrap">
|
||
<p class="cmd-lead">2 · command ↓</p>
|
||
<div class="cmd-box"><span>helm upgrade --install </span><span class="acc">planpal</span><span> ./planpalto-helm -f </span><span class="acc">envs/a.yaml</span></div>
|
||
<p class="cmd-note">upgrade --install = create if absent, else new revision.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- (d) render well -->
|
||
<div class="well" style="margin-top:16px">
|
||
<p class="well-hd">3 · Render — Go template engine (client-side, in Helm)</p>
|
||
<div class="render3">
|
||
<div class="rcol" data-cross="merge">
|
||
<p class="rhd">Merged values</p>
|
||
<pre>values.yaml
|
||
+ <span class="acc">-f envs/a.yaml</span> (override)
|
||
+ <span class="acc">.Release.Name planpal</span>
|
||
+ <span class="acc">.Release.Namespace</span></pre>
|
||
</div>
|
||
<div class="rcol" data-cross="walk templates/">
|
||
<p class="rhd">Go template engine</p>
|
||
<ul class="mech">
|
||
<li><span class="tok">{{ .Values.x }}</span> <span class="gl">substitute</span></li>
|
||
<li><span class="tok">{{ if }} / {{ with }}</span> <span class="gl">conditional / scope</span></li>
|
||
<li><span class="tok">{{ range }}</span> <span class="gl">loop over apps map</span></li>
|
||
<li><span class="tok">{{ include "planpal.namespace" . }}</span> <span class="gl">named template from _helpers.tpl</span></li>
|
||
<li><span class="tok">| default | toJson | toString</span> <span class="gl">pipes</span></li>
|
||
</ul>
|
||
</div>
|
||
<div class="rcol">
|
||
<p class="rhd">Multi-doc YAML</p>
|
||
<pre>---
|
||
kind: Namespace
|
||
...
|
||
---
|
||
kind: Deployment
|
||
metadata:
|
||
name: <span class="acc">planpal-backend</span>
|
||
...
|
||
--- <span class="dim">(× many docs)</span></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- (g) reconcile + (h) add-ons -->
|
||
<div class="twin">
|
||
<div class="well">
|
||
<p class="well-hd">5 · Reconcile</p>
|
||
<div class="chain">
|
||
<span class="bx">Deployment</span><span class="ar">→</span>
|
||
<span class="bx">ReplicaSet</span><span class="ar">→</span>
|
||
<span class="bx">Pod</span><span class="ar">→</span>
|
||
<span class="bx">scheduler</span><span class="ar">→</span>
|
||
<span class="bx">EKS node</span>
|
||
</div>
|
||
<p class="cmd-note">Controllers reconcile the declared state. HPA scales replicas via metrics-server.</p>
|
||
</div>
|
||
<div class="well">
|
||
<p class="well-hd">6 · Cluster add-ons</p>
|
||
<div class="addon-row"><span class="obj">ExternalSecret</span><span class="ar">▶ ESO</span><span class="gl">reads AWS Secrets Manager → real k8s Secret</span></div>
|
||
<div class="addon-row"><span class="obj">Ingress</span><span class="ar">▶ ALB ctrl</span><span class="gl">provisions an ALB</span></div>
|
||
<div class="addon-row"><span class="obj">HPA</span><span class="ar">▶ metrics-server</span><span class="gl">feeds CPU/mem → scale</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="sep">
|
||
|
||
<!-- (e) BOM table -->
|
||
<p class="secthead">Template → object map<span class="st-sub">bill of materials · rendered from the chart, exact counts</span></p>
|
||
<div class="bom-wrap">
|
||
<table class="bom">
|
||
<thead><tr><th style="width:32%">Source file</th><th>Rendered objects</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>templates/namespace.yaml</td><td>1 Namespace · 1 ServiceAccount <span class="dim">planpal-backend</span></td></tr>
|
||
<tr><td>templates/apps.yaml</td><td>5 Deployment · 2 Service · 5 HPA · 2 PDB <span class="note">← one {{ range }} loop, see DETAIL A</span></td></tr>
|
||
<tr><td>templates/deps.yaml</td><td>2 Deployment <span class="dim">redis, nats</span> · 2 Service <span class="note-dim">(these 2 also count toward the 7 Deployment total)</span></td></tr>
|
||
<tr><td>templates/external-secrets.yaml</td><td>1 ClusterSecretStore · 4 ExternalSecret</td></tr>
|
||
<tr><td>templates/ingress.yaml</td><td>1 Ingress <span class="dim">(ALB)</span></td></tr>
|
||
<tr><td>templates/seed-job.yaml</td><td>1 Job <span class="dim">seed-admin (runs once)</span></td></tr>
|
||
<tr class="sum"><td>Σ total</td><td>7 Deployment · 4 Service · 5 HPA · 2 PDB · 1 Ingress · 1 Job · 1 Namespace · 1 ServiceAccount · 1 ClusterSecretStore · 4 ExternalSecret</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<hr class="sep">
|
||
|
||
<!-- lower band: DETAIL A | DETAIL B -->
|
||
<div class="lower">
|
||
|
||
<!-- (f) DETAIL A -->
|
||
<div class="detail">
|
||
<span class="detail-tab">Detail A</span>
|
||
<p class="detail-cap">One <code>{{ range .Values.apps }}</code> over a 5-entry map builds 5 Deployments. Conditionals add the extras: a Service <code>if service:</code>, an HPA <code>if hpa:</code>, a PDB <code>if pdb:</code>.</p>
|
||
<div class="fan">
|
||
<svg viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
|
||
<defs>
|
||
<marker id="ah" markerWidth="6" markerHeight="6" refX="5" refY="3" orient="auto">
|
||
<path d="M0,0 L6,3 L0,6 Z" fill="var(--accent)"/>
|
||
</marker>
|
||
</defs>
|
||
<!-- fan: single origin at bracket right-mid (x~40.5), 5 lines to lane mids -->
|
||
<line class="draw" style="--len:60" x1="40.5" y1="50" x2="52" y2="10" stroke="var(--accent)" stroke-width="0.5" marker-end="url(#ah)"/>
|
||
<line class="draw" style="--len:55" x1="40.5" y1="50" x2="52" y2="30" stroke="var(--accent)" stroke-width="0.5" marker-end="url(#ah)"/>
|
||
<line class="draw" style="--len:50" x1="40.5" y1="50" x2="52" y2="50" stroke="var(--accent)" stroke-width="0.5" marker-end="url(#ah)"/>
|
||
<line class="draw" style="--len:55" x1="40.5" y1="50" x2="52" y2="70" stroke="var(--accent)" stroke-width="0.5" marker-end="url(#ah)"/>
|
||
<line class="draw" style="--len:60" x1="40.5" y1="50" x2="52" y2="90" stroke="var(--accent)" stroke-width="0.5" marker-end="url(#ah)"/>
|
||
</svg>
|
||
<div class="fan-grid">
|
||
<!-- Zone L: apps map -->
|
||
<div class="lanes">
|
||
<div class="srcbox"><span class="nm">backend</span><span class="tags"><span class="tag">service:</span><span class="tag">hpa:</span><span class="tag">pdb:</span></span></div>
|
||
<div class="srcbox"><span class="nm">frontend</span><span class="tags"><span class="tag">service:</span><span class="tag">hpa:</span><span class="tag">pdb:</span></span></div>
|
||
<div class="srcbox"><span class="nm">schedule-worker</span><span class="tags"><span class="tag">hpa:</span></span></div>
|
||
<div class="srcbox"><span class="nm">notification-worker</span><span class="tags"><span class="tag">hpa:</span></span></div>
|
||
<div class="srcbox"><span class="nm">ai-worker</span><span class="tags"><span class="tag">hpa:</span></span></div>
|
||
</div>
|
||
<!-- Zone C: range bracket -->
|
||
<div class="bracket-col">
|
||
<span class="bracket-lbl">{{ range .Values.apps }} ×5</span>
|
||
</div>
|
||
<!-- Zone R: emitted objects -->
|
||
<div class="outlanes">
|
||
<div class="oline"><span class="objbox dep">Deployment</span><span class="gate">if service:</span><span class="objbox">Service</span><span class="gate">if hpa:</span><span class="objbox">HPA</span><span class="gate">if pdb:</span><span class="objbox">PDB</span></div>
|
||
<div class="oline"><span class="objbox dep">Deployment</span><span class="gate">if service:</span><span class="objbox">Service</span><span class="gate">if hpa:</span><span class="objbox">HPA</span><span class="gate">if pdb:</span><span class="objbox">PDB</span></div>
|
||
<div class="oline"><span class="objbox dep">Deployment</span><span class="gate">if hpa:</span><span class="objbox">HPA</span></div>
|
||
<div class="oline"><span class="objbox dep">Deployment</span><span class="gate">if hpa:</span><span class="objbox">HPA</span></div>
|
||
<div class="oline"><span class="objbox dep">Deployment</span><span class="gate">if hpa:</span><span class="objbox">HPA</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="fan-recon"><code>apps.yaml → 5 Deployment · 2 Service · 5 HPA · 2 PDB.</code> The redis + nats Deployments come from <code>deps.yaml</code>, not this loop — they are the other 2 in the total of 7.</p>
|
||
</div>
|
||
|
||
<!-- (i) DETAIL B -->
|
||
<div class="detail">
|
||
<span class="detail-tab">Detail B</span>
|
||
<p class="detail-cap">Each <code>helm install</code> lands in its own namespace + ALB + ClusterSecretStore, all defaulting to <code>.Release.Name</code> via <code>_helpers.tpl</code>.</p>
|
||
<div class="rel-cols">
|
||
<div class="rel-col">
|
||
<p class="rel-title">planpal-a</p>
|
||
<div class="rel-stack">
|
||
<div class="rel-cell">Namespace<br><span class="v">planpal-a</span></div>
|
||
<div class="rel-cell">Ingress → ALB<br><span class="dim">(own LB)</span></div>
|
||
<div class="rel-cell">ClusterSecretStore<br><span class="v">planpal-a-aws-planpal</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="rel-col">
|
||
<p class="rel-title">planpal-b</p>
|
||
<div class="rel-stack">
|
||
<div class="rel-cell">Namespace<br><span class="v">planpal-b</span></div>
|
||
<div class="rel-cell">Ingress → ALB<br><span class="dim">(own LB)</span></div>
|
||
<div class="rel-cell">ClusterSecretStore<br><span class="v">planpal-b-aws-planpal</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="rel-note">planpal.namespace = default .Release.Name → <span class="v">planpal-a</span> / <span class="v">planpal-b</span>
|
||
planpal.storeName = per-release CSS
|
||
|
||
The empty gutter is the isolation: no
|
||
connector crosses it. Two releases share
|
||
nothing on the cluster.</p>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- title block -->
|
||
<div class="tblock-wrap">
|
||
<div class="tblock">
|
||
<div class="tb-row"><div class="tb-k">Chart</div><div class="tb-v">planpalto-helm</div></div>
|
||
<div class="tb-row"><div class="tb-k">Release</div><div class="tb-v v-acc">planpal</div></div>
|
||
<div class="tb-row"><div class="tb-k">Cluster</div><div class="tb-v">fakhri-k8 (EKS)</div></div>
|
||
<div class="tb-row"><div class="tb-k">Release state</div><div class="tb-v">Secret sh.helm.release.v1</div></div>
|
||
<div class="tb-row"><div class="tb-k">Scale</div><div class="tb-v">NTS</div></div>
|
||
<div class="tb-row"><div class="tb-k">Sheet</div><div class="tb-v">1 of 1</div></div>
|
||
<div class="tb-row"><div class="tb-k">Date</div><div class="tb-v">2026-09-01</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
</div></div>
|