Skip to main content

Install onto an Existing Kubernetes Cluster

The kubernetes platform installs the same chart every platform installs, with helm, against your current kube-context, and touches nothing on any host. Everything the k3s platform automates on the hosts (the node contract) is yours to implement first; this page is that checklist, then the install.

Before You Start​

  • helm and kubectl on the machine you run from, and a reachable kube-context. The installer refuses an OpenShift cluster and a cluster whose RAN is already managed by the k3s platform.
  • The Requirements: a schedulable node with an NVIDIA GPU for the CU planes and the core, sctp on every node, a default StorageClass, and the privileged namespaces allowed.
  • The release's installer package, which carries the files the steps below use: racora-installer-<tag>.tar.gz from the release, unpacked to installer/ (install-racora.sh, platforms/, node-provisioning/, cores/). Take them from the release you install, not from the repository's main branch.

1. Pod Security​

If your cluster enforces the baseline Pod Security level by default, exempt the namespaces centralized-unit, distributed-unit, 5g-core and racora-system before installing; under restricted, exempt every Racora namespace (Ports and Privileges says which workload needs what). The chart creates the namespaces without Pod Security labels; how you exempt them is your cluster's policy.

2. Radio Hosts​

Provision each host that drives a real radio. The provisioner is platform-independent:

curl -sfL https://get.racora.io | INSTALL_RACORA_MODE=provision-rf INSTALL_RACORA_VERSION=vX.Y.Z INSTALL_RACORA_PRO_TOKEN=<token> sh -

INSTALL_RACORA_VERSION pins the provisioner to the release you will install; without it the scripts come from the repository's main branch. It reboots the host through its phases and writes /etc/racora/node-capabilities. Your cluster tooling joins the host as a worker. Virtual cells (ruType: dummy) need no provisioned host.

3. Labels​

Apply the node-contract labels from each host's capabilities file, with the labeler from the installer package:

installer/platforms/kubernetes/label-node.sh <node> # reads /etc/racora/node-capabilities over ssh
installer/platforms/kubernetes/label-node.sh <node> <file> # or from a copy of the file

It sets racora.io/rf-ready, racora.io/gpu-ready and racora.io/fronthaul-ready with --overwrite, so re-running after re-provisioning updates them. The control node must carry racora.io/gpu-ready=true or CU-IP stays Pending.

4. SCTP on Every Node​

sudo modprobe sctp
echo sctp | sudo tee /etc/modules-load.d/racora-sctp.conf

F1-C, E1 and NGAP are SCTP over ClusterIP Services; kube-proxy needs the module.

5. The Control Node and Its GPU​

NVIDIA driver and container toolkit, the containerd nvidia runtime handler (nvidia-ctk runtime configure --runtime=containerd), and the label from step 3. The chart creates RuntimeClass/nvidia on this platform (racora-node.nvidia.runtimeClass.create: true); set it to false if the NVIDIA GPU Operator owns the RuntimeClass, and set racora-node.nodePlugins.nvidia.enabled to false if the Operator already runs a device plugin, so two plugins do not advertise nvidia.com/gpu on the node.

6. Placement and Your Values File​

The kubernetes overlay clears the control-plane pin (controlPlanePin: false on the CU planes, the core and the controllers) because control planes built by kubeadm or CAPI are usually tainted. Pin them yourself to the control node (the three charts take tolerations for a tainted node, and so does the NVIDIA device plugin that advertises the GPU there, racora-node.nodePlugins.nvidia.tolerations); CU-CP, CU-UP, CU-IP and an in-cluster core must share one node, and the controllers belong with them. Keep this, and everything else you override (a network identity other than the default) in one values file of your own, my-values.yaml:

racora-cu:
nodeSelector: { kubernetes.io/hostname: <gpu-node> }
tolerations: &control-plane # reused by the two anchors below; drop it, and the nvidia entry, when that node is not tainted
- { key: node-role.kubernetes.io/control-plane, operator: Exists, effect: NoSchedule }
racora-core-open5gs:
nodeSelector: { kubernetes.io/hostname: <gpu-node> }
tolerations: *control-plane
racora-controller:
nodeSelector: { kubernetes.io/hostname: <gpu-node> }
tolerations: *control-plane
racora-node:
nodePlugins:
nvidia:
tolerations: # replaces the default list, so keep its entry
- { key: nvidia.com/gpu, operator: Exists, effect: NoSchedule }
- { key: node-role.kubernetes.io/control-plane, operator: Exists, effect: NoSchedule }
# global:
# network: { plmn: "90170", tacs: [7], slices: [{ sst: 1 }] }
# core: { provider: external, amfAddr: amf.example.net } # external core: the file wins over the overlay on every run

The install and every upgrade start from the chart's defaults, so this file is passed on every run: RACORA_HELM_EXTRA_ARGS='-f my-values.yaml' to the installer, or -f my-values.yaml to helm. The registry is the one thing that does not belong in the file: the installer always passes --set global.systemDefaultRegistry=$RACORA_REGISTRY, and a --set wins over a file, so a mirror is RACORA_REGISTRY=<registry> for the installer or --set global.systemDefaultRegistry=<registry> for helm.

7. Storage and Grafana​

A default StorageClass for the Open5GS subscriber database. Grafana is a NodePort on 30300 readable by anyone (Read Logs and Traces has the login facts); beyond a lab keep it private, in my-values.yaml:

racora-monitoring:
grafana:
service: { type: ClusterIP }
anonymousAccess: false

8. The Core's Host Units (Open5GS)​

The Open5GS provider needs three host units on the node it runs on. On the k3s platform the installer renders them; here you do, from the installer package:

sudo mkdir -p /opt/racora/core-support
sudo cp installer/cores/open5gs/host/*.sh /opt/racora/core-support/
sudo chmod 755 /opt/racora/core-support/*.sh # the package carries them non-executable
sudo mkdir -p /etc/racora
echo 'RACORA_UE_CIDR=10.45.0.0/16' | sudo tee /etc/racora/core-support.env
for u in egress-nat fix-resolved-loopback nat-sanitize; do
sed 's/@CLUSTER_SERVICE@/kubelet.service/' "installer/cores/open5gs/host/${u}.service" | sudo tee "/etc/systemd/system/${u}.service" >/dev/null
done
sudo systemctl daemon-reload
sudo systemctl enable --now egress-nat.service fix-resolved-loopback.service nat-sanitize.service

The units are templates on @CLUSTER_SERVICE@, the cluster runtime's systemd unit; their ExecStart points at /opt/racora/core-support/ and egress-nat reads the UE pool from /etc/racora/core-support.env. What each unit does is on the Open5GS page. egress-nat.sh waits up to about a minute for a KUBE-POSTROUTING or FLANNEL-POSTRTG chain (the sign that kube-proxy or flannel has restored the nat table) and then adds its rule regardless; a CNI that rebuilds the nat table later can flush it, so check the rule after a CNI restart. An external core needs nothing here.

9. Install​

INSTALL_RACORA_PLATFORM=kubernetes INSTALL_RACORA_VERSION=vX.Y.Z RACORA_HELM_EXTRA_ARGS='-f my-values.yaml' sh installer/install-racora.sh

That is the installer of the release you unpacked, pinned to the same release, so the chart and the overlays match.

Add INSTALL_RACORA_CORE=external INSTALL_RACORA_AMF_ADDR=<amf> for your own core, and RACORA_REGISTRY=<registry> to pull every image from a mirror (Use Your Own Registry or Install Offline). The installer runs the one helm command of the kubernetes platform, which you can run yourself with the two overlays from the package and your values file; that page also says what the platform overlay decides.

10. Verify​

kubectl get pods -n racora-system # the two controllers and the device plugins
kubectl get deploy -n centralized-unit # cu-cp, cu-up, cuip at 0/0 (dormant)
kubectl get pods -n 5g-core # the core, on the control node

Then declare your first cell. Removing the RAN again is INSTALL_RACORA_MODE=uninstall on the same platform; the host and cluster scopes are refused because Racora did not install this cluster (Uninstall Racora).