Kubernetes Operator
Kubernetes Operator
nexus-gslb-operator is a separate
controller-runtime-based controller-manager (repo:
nexus-gslb-operator, not part of this repo's build) that
watches Service and Ingress objects in a
Kubernetes cluster and keeps a matching Nexus GSLB pool/service/members
in sync automatically. There are no CRDs — everything is driven by
annotations, the same model as external-dns.
Annotate a Service or Ingress with
nexus.gslb/hostname and the operator:
- Find-or-creates a Nexus Pool
(
nexus.gslb/pool, default<namespace>-<name>). Pools are shared across objects/clusters that use the same pool name — aLoadBalancerService in one cluster and another in a second cluster can both feed members into one pool for true multi-cluster failover. - Find-or-creates a Nexus Service (DNS record) per listed hostname, bound to that pool.
- Resolves the object's externally-reachable address(es) — LB
IP/hostname for
type: LoadBalancerServices and Ingress frontends, or NodePort + node IPs fortype: NodePortServices withReadybacking pods — and keeps the pool's members in sync. - On deletion, removes the members and DNS records it created. The pool itself is never auto-deleted, since other objects/clusters may still reference it.
Full annotation reference (nexus.gslb/algorithm,
filter-chain, ttl, min-healthy,
fallback-cname, weight, priority,
region, target-port,
endpoint-mode, node-address-type, …) lives in
the operator repo's README.
Install
helm upgrade --install nexus-gslb-operator ./charts/nexus-gslb-operator \
--namespace nexus-system --create-namespace \
--set nexus.apiUrl=http://gslbd.nexus-system.svc.cluster.local:8080 \
--set nexus.apiKey=<tenant-or-system-api-key>The API key is a normal Nexus tenant API key or the system
break-glass key (/etc/gslb/env → GSLB_API_KEY
on each node) — same auth path as gslbctl/Terraform, sent
as a Bearer token.
RBAC
The operator needs cluster-wide read on
Service/Node/EndpointSlice/
Ingress (to resolve member addresses) and write access to
the specific objects it manages (to set finalizers/annotations). The
Helm chart generates this; the equivalent raw manifest:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nexus-gslb-operator
rules:
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["services/finalizers"]
verbs: ["update"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "patch"]
- apiGroups: ["discovery.k8s.io"]
resources: ["endpointslices"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses/finalizers"]
verbs: ["update"]Set rbac.clusterScoped: false (Helm) for a namespaced
Role/RoleBinding instead, if you restrict
watchNamespace and only ever use
LoadBalancer-backed Services/Ingresses (NodePort resolution
needs cluster-wide Node visibility).
Runs under OpenShift's default
restricted/restricted-v2 SCC — the pod spec
only requests runAsNonRoot: true, no fixed UID.
Known limitations
ClusterIPServices don't get members (not externally routable); the Nexus Service/Pool are still created so DNS resolves via the pool's fallback CNAME once members do appear.- Node IP changes for NodePort members are only picked up via EndpointSlice events plus the manager's cache resync, not a direct Node watch.
- Pools are never auto-deleted by the operator; prune unused ones via
gslbctlor the API.
Surface parity
API ✅ (existing pools/members/services endpoints) WebUI N/A (k8s-native, annotation-driven) TUI N/A Terraform N/A (separate operator/Helm install path, not a Terraform-managed resource) Kubernetes ✅ (this page) Docs ✅