ops-demo/docs/03-metallb-ingress.md
Paul Harkink 633f3f6e46 feat(ex03): MetalLB + Ingress-Nginx + podinfo ingress
- apps/networking/metallb.yaml: MetalLB Helm app (chart 0.14.9)
- apps/networking/metallb-config.yaml: IPAddressPool + L2Advertisement CRDs
- apps/networking/ingress-nginx.yaml: Ingress-Nginx Helm app (chart 4.12.0)
- manifests/networking/metallb/: values.yaml + metallb-config.yaml (pool 192.168.56.200-220)
- manifests/networking/ingress-nginx/values.yaml: LB IP 192.168.56.200, class nginx
- manifests/apps/podinfo/ingress.yaml: podinfo.192.168.56.200.nip.io
- docs/03-metallb-ingress.md: Exercise 03 participant guide
- manifests/argocd/values.yaml: ArgoCD ingress block commented (enabled in Ex03)
2026-02-28 15:28:39 +01:00

5.1 KiB

Exercise 03 — MetalLB + Ingress-Nginx (LAN exposure)

Time: ~45 min Goal: Expose podinfo and the ArgoCD UI on a real LAN IP — accessible from your laptop's browser without any port-forward.


What you'll learn

  • What MetalLB is and why you need it in a bare-metal / local Kubernetes cluster
  • How a LoadBalancer service gets a real IP via L2 ARP
  • How Ingress-Nginx routes HTTP traffic by hostname
  • nip.io — a public wildcard DNS service for local development

Background

In cloud Kubernetes (EKS, GKE, AKS), type: LoadBalancer automatically provisions a cloud load balancer with a public IP. On bare metal or local VMs, nothing does that — so pods stay unreachable.

MetalLB fills that gap: it watches for LoadBalancer services and assigns IPs from a pool you define. In L2 mode it uses ARP to answer "who has 192.168.56.200?" — so your laptop routes directly to the VM.

Ingress-Nginx is a single LoadBalancer service that MetalLB gives one IP. All your apps share that IP — Nginx routes to the right service based on the Host: header.

nip.io is a public DNS wildcard: anything.192.168.56.200.nip.io resolves to 192.168.56.200. No /etc/hosts editing needed.


Steps

1. Enable MetalLB

The ArgoCD Application manifests for MetalLB are already in this repo. The root App-of-Apps watches the apps/ directory, which includes apps/networking/. They are already being applied — MetalLB just needs a moment to become healthy.

Check MetalLB is running:

kubectl get pods -n metallb-system
# NAME                          READY   STATUS    RESTARTS   AGE
# controller-xxx                1/1     Running   0          Xm
# speaker-xxx                   1/1     Running   0          Xm

Check the IP pool is configured:

kubectl get ipaddresspool -n metallb-system
# NAME            AUTO ASSIGN   AVOID BUGGY IPS   ADDRESSES
# workshop-pool   true          false             ["192.168.56.200-192.168.56.220"]

2. Enable Ingress-Nginx

Similarly, apps/networking/ingress-nginx.yaml is already in the repo. Wait for it to become Synced in ArgoCD, then:

kubectl get svc -n ingress-nginx
# NAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)
# ingress-nginx-controller             LoadBalancer   10.43.x.x      192.168.56.200   80:xxx,443:xxx

The EXTERNAL-IP column shows 192.168.56.200. MetalLB assigned it.

From your laptop (not the VM), verify:

curl http://192.168.56.200
# 404 from Nginx — correct! No ingress rule yet, but Nginx is reachable.

3. Add a podinfo Ingress

The Ingress resource is already in manifests/apps/podinfo/ingress.yaml. ArgoCD will sync it automatically. After sync:

kubectl get ingress -n podinfo
# NAME      CLASS   HOSTS                              ADDRESS          PORTS
# podinfo   nginx   podinfo.192.168.56.200.nip.io      192.168.56.200   80

Open from your laptop browser: http://podinfo.192.168.56.200.nip.io

You should see the podinfo UI with version 6.6.2.


4. Enable the ArgoCD ingress

Now let's expose ArgoCD itself on a nice URL. Open manifests/argocd/values.yaml and find the commented-out ingress block near the server: section:

  # ── Exercise 03: uncomment this block after Ingress-Nginx is deployed ──────
  # ingress:
  #   enabled: true
  # ...

Uncomment the entire block (remove the # characters):

  ingress:
    enabled: true
    ingressClassName: nginx
    hostname: argocd.192.168.56.200.nip.io
    annotations:
      nginx.ingress.kubernetes.io/ssl-passthrough: "false"
      nginx.ingress.kubernetes.io/backend-protocol: "HTTP"

Commit and push:

git add manifests/argocd/values.yaml
git commit -m "feat(ex03): enable ArgoCD ingress"
git push

ArgoCD will detect the change, upgrade its own Helm release, and create the Ingress. Within a minute or two:

kubectl get ingress -n argocd
# NAME            CLASS   HOSTS                              ADDRESS
# argocd-server   nginx   argocd.192.168.56.200.nip.io      192.168.56.200

Open from your laptop: http://argocd.192.168.56.200.nip.io


Expected outcome

URL App
http://podinfo.192.168.56.200.nip.io podinfo v6.6.2
http://argocd.192.168.56.200.nip.io ArgoCD UI

Both accessible from your laptop without any port-forward.


Troubleshooting

Symptom Fix
EXTERNAL-IP is <pending> on ingress-nginx svc MetalLB not ready yet — check kubectl get pods -n metallb-system
Curl to 192.168.56.200 times out from laptop VirtualBox host-only adapter not configured; check VBoxManage list hostonlyifs
nip.io doesn't resolve Temporary DNS issue; try again or use /etc/hosts with 192.168.56.200 podinfo.local
ArgoCD ingress gives 502 Wait for ArgoCD to restart after values change; ArgoCD now runs in insecure (HTTP) mode

What's next

In Exercise 04 you'll build a Tekton pipeline that:

  1. Validates manifests
  2. Bumps the podinfo image tag from 6.6.2 to 6.7.0 in deployment.yaml
  3. Pushes the commit — and ArgoCD picks it up automatically