Initial commit: GitOps Starter Pack
A teaching-first, single-cluster GitOps setup: Talos Linux + Argo CD with an app-of-apps layout, MetalLB pre-installed for real LoadBalancer IPs on bare metal, a demo app, and a progressive docs/ walkthrough with a CLAUDE.md tutor persona and guided bootstrap checklist. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
# 05 — MetalLB: getting real IP addresses on bare metal
|
||||
|
||||
The app-of-apps you just applied ([`docs/04-app-of-apps.md`](04-app-of-apps.md))
|
||||
already brought up `metallb` alongside `demo`. This doc explains what it
|
||||
does, walks you through the one piece only you can decide (which IP
|
||||
addresses to actually use), and finishes by giving Argo CD's own UI a real
|
||||
address too.
|
||||
|
||||
## Why this is needed at all
|
||||
|
||||
On a cloud provider, a Kubernetes Service of `type: LoadBalancer` triggers
|
||||
the cloud to provision an actual load balancer and hand your Service a
|
||||
real, routable IP automatically. Bare metal — your Talos node, sitting on
|
||||
your own network — has no such thing behind it. Without help, a
|
||||
`LoadBalancer` Service on bare metal just sits with its address stuck at
|
||||
`<pending>` forever.
|
||||
|
||||
**MetalLB** is that help: software that runs inside your cluster,
|
||||
watches for `LoadBalancer` Services, and hands them a real address from a
|
||||
range *you* configure — then makes that address actually reachable on your
|
||||
network. It's the standard, widely-used answer to this exact gap, which is
|
||||
why it's installed as a pre-installed app in this repo rather than left as
|
||||
a "next step."
|
||||
|
||||
`apps/demo/service.yaml` is already `type: LoadBalancer` — that's what you
|
||||
saw sitting at `<pending>` if you looked closely in the last doc. This doc
|
||||
is what turns that into a real, reachable address.
|
||||
|
||||
## What's already running
|
||||
|
||||
**`apps/metallb.yaml`** → one Application, pointing at `apps/metallb`, a
|
||||
Helm chart that does two things at once (see
|
||||
[`apps/metallb/Chart.yaml`](../apps/metallb/Chart.yaml)):
|
||||
|
||||
- Pulls in the official upstream MetalLB chart as a dependency — this is
|
||||
also the real, working example of a Helm-based app mentioned in
|
||||
[`docs/07-adding-new-apps.md`](07-adding-new-apps.md).
|
||||
- Carries two templates of its own
|
||||
([`apps/metallb/templates/`](../apps/metallb/templates)): an
|
||||
`IPAddressPool` and an `L2Advertisement` — MetalLB's own custom
|
||||
resources, which tell it which addresses it's allowed to use and how to
|
||||
advertise them. Right now these still contain **placeholder** IP
|
||||
addresses that won't work on your network — that's what you're about to
|
||||
fix.
|
||||
|
||||
Both the upstream chart and our two extra resources sync together as one
|
||||
Application. Look at the `sync-wave` annotation on
|
||||
[`apps/metallb/templates/ipaddresspool.yaml`](../apps/metallb/templates/ipaddresspool.yaml) —
|
||||
it's what keeps this safe despite being one sync: MetalLB's own resources
|
||||
(including its CRDs) apply first, and Argo CD waits for them to be healthy
|
||||
before applying our two resources, which depend on those CRDs existing.
|
||||
|
||||
## Answer three questions before editing anything
|
||||
|
||||
> **Ask Claude:** literally ask — "help me pick MetalLB's IP range and the
|
||||
> addresses for Argo CD and the demo app." This is the one part of this
|
||||
> whole repo that depends entirely on *your* network, which nothing in
|
||||
> this repo can know in advance. Have your router's admin page or DHCP
|
||||
> settings handy; Claude will ask what your LAN's subnet is and what range
|
||||
> your DHCP server already hands out, so the pool it suggests doesn't
|
||||
> collide with anything.
|
||||
|
||||
You need three decisions:
|
||||
|
||||
1. **The pool range** — a handful of addresses (5–10 is plenty for this
|
||||
repo) on your LAN's subnet that your DHCP server will never hand to
|
||||
anything else. Commonly a slice just above or below your DHCP range,
|
||||
e.g. if your router hands out `.100`–`.199`, something like
|
||||
`.240`–`.250` is a safe, unused slice.
|
||||
2. **Argo CD's UI address** — one address from that pool.
|
||||
3. **The demo app's address** — a different address from that pool.
|
||||
|
||||
## Edit the placeholders
|
||||
|
||||
1. [`apps/metallb/templates/ipaddresspool.yaml`](../apps/metallb/templates/ipaddresspool.yaml)
|
||||
— replace the `addresses` range with your chosen pool.
|
||||
2. [`apps/demo/service.yaml`](../apps/demo/service.yaml) — replace the
|
||||
`metallb.universe.tf/loadBalancerIPs` annotation with the demo app's
|
||||
chosen address (must be inside the pool from step 1).
|
||||
3. [`docs/network.md`](network.md) — record the pool range and the demo
|
||||
app's address in the tables there (Argo CD's address goes in once you've
|
||||
picked it below).
|
||||
4. Commit and push all three:
|
||||
```sh
|
||||
git add apps/metallb/templates/ipaddresspool.yaml apps/demo/service.yaml docs/network.md
|
||||
git commit -m "Configure MetalLB's address pool for my network"
|
||||
git push
|
||||
```
|
||||
5. Force a sync rather than waiting for the next poll:
|
||||
```sh
|
||||
argocd app sync metallb demo
|
||||
```
|
||||
|
||||
## Verify
|
||||
|
||||
```sh
|
||||
kubectl -n demo get svc demo
|
||||
```
|
||||
`EXTERNAL-IP` should now show the address you chose instead of `<pending>`.
|
||||
Visit `http://<that-address>` in a browser — same "It works!" page as
|
||||
before, now on its own real IP instead of a NodePort.
|
||||
|
||||
```sh
|
||||
kubectl -n metallb-system get ipaddresspools,l2advertisements
|
||||
```
|
||||
Confirms your pool and advertisement were applied as expected.
|
||||
|
||||
## Give Argo CD's UI a real address too
|
||||
|
||||
So far you've reached the Argo CD UI through `kubectl port-forward`
|
||||
(see [`docs/03-installing-argocd.md`](03-installing-argocd.md)). Now that
|
||||
MetalLB is running, you can give it a permanent address instead. Argo CD's
|
||||
own install isn't managed through this repo's GitOps loop (it's the
|
||||
imperative bootstrap step — see `docs/03`), so this one change is applied
|
||||
directly rather than through Git:
|
||||
|
||||
```sh
|
||||
kubectl -n argocd patch svc argocd-server --type merge -p \
|
||||
'{"spec":{"type":"LoadBalancer"},"metadata":{"annotations":{"metallb.universe.tf/loadBalancerIPs":"<ARGOCD_IP>"}}}'
|
||||
```
|
||||
|
||||
Replace `<ARGOCD_IP>` with the address you chose in step 2 above. Then:
|
||||
|
||||
```sh
|
||||
kubectl -n argocd get svc argocd-server
|
||||
```
|
||||
|
||||
Once `EXTERNAL-IP` shows your chosen address, visit
|
||||
`https://<that-address>` directly — no port-forward needed from now on.
|
||||
(You'll still hit the same self-signed certificate warning as before; see
|
||||
[`docs/03-installing-argocd.md`](03-installing-argocd.md) for why that's
|
||||
expected here.)
|
||||
|
||||
Update [`docs/network.md`](network.md) one more time with this address —
|
||||
commit and push it. This one's a genuine exception to "everything's driven
|
||||
by Git": the patch command above isn't a file in this repo, so this file is
|
||||
the only place this address gets recorded at all.
|
||||
|
||||
> **Ask Claude:** "why did the demo app get its LoadBalancer IP through
|
||||
> Git, but Argo CD's didn't?" — a good check that the boundary between
|
||||
> "managed by this repo" and "the imperative bootstrap that starts it"
|
||||
> has actually landed.
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Chosen a pool range, an Argo CD UI address, and a demo app address,
|
||||
with Claude's help, that fit your actual network
|
||||
- [ ] `apps/metallb/templates/ipaddresspool.yaml` edited to your real range
|
||||
- [ ] `apps/demo/service.yaml` edited to your real demo address
|
||||
- [ ] `docs/network.md` updated with the pool range and both addresses
|
||||
- [ ] Committed, pushed, synced
|
||||
- [ ] `kubectl -n demo get svc demo` shows a real `EXTERNAL-IP`, page loads in a browser
|
||||
- [ ] `argocd-server` patched to `LoadBalancer`, reachable at its own address without port-forwarding
|
||||
|
||||
Continue to [`docs/06-your-first-change.md`](06-your-first-change.md).
|
||||
Reference in New Issue
Block a user