MetalLB (again)

I won't document this separately for obvious reasons, but I followed the excellent "Getting Started" and "Ways of Structuring Your Repositories" documentation to get FluxCD set up with the gitops repository. Now I'm back to having some semblance of GitOps, although without any applications of note (though podinfo is really cool!).

So that brings us back to setting up MetalLB so that I can easily access Kubernetes services.

MetalLB was straightforward. Its namespace needs elevated privileges, but the Helm chart and repository definition were very straightforward:

---
apiVersion: v1
kind: Namespace
metadata:
  name: metallb-system
  labels:
    # Allow MetalLB speaker pods to use privileged capabilities
    pod-security.kubernetes.io/enforce: privileged
    pod-security.kubernetes.io/audit: privileged
    pod-security.kubernetes.io/warn: privileged
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: metallb
  namespace: flux-system
spec:
  interval: 30m
  targetNamespace: metallb-system
  chart:
    spec:
      chart: metallb
      version: "0.14.5"
      sourceRef:
        kind: HelmRepository
        name: metallb
        namespace: flux-system
  install:
    createNamespace: false
    crds: Create
    remediation:
      retries: 3
  upgrade:
    crds: CreateReplace
    remediation:
      retries: 3
  values:
    # Enable Prometheus metrics (ServiceMonitor disabled - requires Prometheus Operator)
    prometheus:
      serviceAccount: metallb-controller
      namespace: metallb-system
      serviceMonitor:
        enabled: false
    # Speaker configuration for L2 mode
    speaker:
      enabled: true
      tolerateMaster: true
      # Disable FRR for simple L2 mode
      frr:
        enabled: false
    # Controller configuration
    controller:
      enabled: true
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
  name: metallb
  namespace: flux-system
spec:
  interval: 24h
  url: https://metallb.github.io/metallb

I placed that information in infrastructure/metallb, and then the following MetalLB configuration resources in apps/metallb.yaml to deploy subsequently:

---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: primary
  namespace: metallb-system
spec:
  addresses:
    - "10.4.11.0-10.4.15.254"
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: primary
  namespace: metallb-system
spec:
  ipAddressPools:
    - primary

My podinfo configuration looked like this:

---
apiVersion: v1
kind: Namespace
metadata:
  name: podinfo
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: podinfo
  namespace: flux-system
spec:
  interval: 1m0s
  ref:
    branch: master
  url: https://github.com/stefanprodan/podinfo
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: podinfo
  namespace: flux-system
spec:
  interval: 30m0s
  path: ./kustomize
  prune: true
  retryInterval: 2m0s
  sourceRef:
    kind: GitRepository
    name: podinfo
  targetNamespace: podinfo
  timeout: 3m0s
  wait: true
  patches:
    - patch: |-
        apiVersion: autoscaling/v2
        kind: HorizontalPodAutoscaler
        metadata:
          name: podinfo
        spec:
          minReplicas: 3
      target:
        name: podinfo
        kind: HorizontalPodAutoscaler
    - patch: |-
        - op: add
          path: /metadata/annotations/metallb.io~1address-pool
          value: default
        - op: replace
          path: /spec/type
          value: LoadBalancer
        - op: add
          path: /spec/externalTrafficPolicy
          value: Local
        - op: replace
          path: /spec/ports
          value:
            - port: 80
              targetPort: 9898
              protocol: TCP
              name: http
            - port: 9999
              targetPort: 9999
              protocol: TCP
              name: grpc
      target:
        kind: Service
        name: podinfo

With this deployed, I was able to curl podinfo!

$ curl http://10.4.11.0/
{
  "hostname": "podinfo-6fd9b57958-7sr4v",
  "version": "6.9.2",
  "revision": "e86405a8674ecab990d0a389824c7ebbd82973b5",
  "color": "#34577c",
  "logo": "https://raw.githubusercontent.com/stefanprodan/podinfo/gh-pages/cuddle_clap.gif",
  "message": "greetings from podinfo v6.9.2",
  "goos": "linux",
  "goarch": "arm64",
  "runtime": "go1.25.1",
  "num_goroutine": "8",
  "num_cpu": "4"
}