Getting started

In this tutorial, we will deploy and run Charmed OAI RAN (Radio Access Network) using Juju and Terraform. The Charmed OAI RAN consists of two Juju charms representing the CU (Central Unit) and the DU (Distributed Unit).

As part of this tutorial, we will also deploy additional components:

  • Charmed Aether SD-Core: A 5G core network which will manage our RAN network

  • SD-Core Router: a software router facilitating communication between the 5G Core and the RAN

  • User Equipment (UE) simulator: A simulated cellphone which will allow us to validate the correctness of the entire deployment

To complete this tutorial, you will need a machine which meets the following requirements:

  • A recent x86_64 CPU (Intel 4ᵗʰ generation or newer, or AMD Ryzen or newer)

  • At least 4 cores (8 recommended)

  • At least 8 GB of RAM (16 GB recommended)

  • 50GB of free disk space

1. Deploy Charmed Aether SD-Core

Install MicroK8s

From your terminal, install MicroK8s:

sudo snap install microk8s --channel=1.29/stable --classic

Add your user to the microk8s group:

sudo usermod -a -G microk8s $USER
newgrp microk8s

Add the community repository MicroK8s addon:

microk8s addons repo add community https://github.com/canonical/microk8s-community-addons --reference feat/strict-fix-multus

Enable the following MicroK8s addons. We must give MetalLB an address range that has at least 3 IP addresses for Charmed Aether SD-Core.

microk8s enable hostpath-storage
microk8s enable multus
microk8s enable metallb:10.0.0.2-10.0.0.4

Export MicroK8s cluster’s configuration:

microk8s config > ~/.kube/config

Bootstrap a Juju controller

From your terminal, install Juju:

sudo snap install juju --channel=3.4/stable

Add MicroK8s cluster to Juju:

juju add-k8s microk8s-classic

Bootstrap a Juju controller:

juju bootstrap microk8s-classic

Install Terraform

From your terminal, install Terraform:

sudo snap install terraform --classic

Create Terraform module

On the host machine create a new directory called terraform:

mkdir terraform

Inside newly created terraform directory create a terraform.tf file:

cd terraform
cat << EOF > versions.tf
terraform {
  required_providers {
    juju = {
      source  = "juju/juju"
      version = ">= 0.12.0"
    }
  }
}
EOF

Create a Terraform module containing the Charmed Aether SD-Core and a router:

cat << EOF > main.tf
resource "juju_model" "sdcore" {
  name = "sdcore"
}

module "sdcore-router" {
  source = "git::https://github.com/canonical/sdcore-router-k8s-operator//terraform"

  model      = juju_model.sdcore.name
  depends_on = [juju_model.sdcore]
}

module "sdcore" {
  source = "git::https://github.com/canonical/terraform-juju-sdcore//modules/sdcore-k8s"

  model        = juju_model.sdcore.name
  depends_on = [module.sdcore-router]

  traefik_config = {
    routing_mode = "subdomain"
  }
}

EOF

Note

You can get a ready example by cloning this Git repository. All necessary files are in the examples/terraform/getting_started directory.

Deploy 5G Core network

Initialize Juju Terraform provider:

terraform init

Deploy SD-Core by applying your Terraform configuration:

terraform apply -auto-approve

The deployment process should take approximately 10-15 minutes.

Monitor the status of the deployment:

juju switch sdcore
watch -n 1 -c juju status --color --relations

The deployment is ready when all the charms are in the active/idle state.
It is normal for grafana-agent to remain in waiting state.
It is also expected that traefik goes to the error state (related Traefik bug).

Example:

ubuntu@host:~$ juju status
Model      Controller                  Cloud/Region                Version  SLA          Timestamp
sdcore     microk8s-classic-localhost  microk8s-classic/localhost  3.4.5    unsupported  08:08:50Z

App                       Version  Status   Scale  Charm                     Channel        Rev  Address         Exposed  Message
amf                       1.4.4    active       1  sdcore-amf-k8s            1.5/edge       707  10.152.183.176  no       
ausf                      1.4.2    active       1  sdcore-ausf-k8s           1.5/edge       520  10.152.183.65   no       
grafana-agent             0.32.1   waiting      1  grafana-agent-k8s         latest/stable   45  10.152.183.221  no       installing agent
mongodb                            active       1  mongodb-k8s               6/beta          38  10.152.183.92   no       Primary
nms                       1.0.0    active       1  sdcore-nms-k8s            1.5/edge       580  10.152.183.141  no       
nrf                       1.4.1    active       1  sdcore-nrf-k8s            1.5/edge       580  10.152.183.130  no       
nssf                      1.4.1    active       1  sdcore-nssf-k8s           1.5/edge       462  10.152.183.62   no       
pcf                       1.4.3    active       1  sdcore-pcf-k8s            1.5/edge       512  10.152.183.144  no       
router                             active       1  sdcore-router-k8s         1.5/edge       341  10.152.183.218  no       
self-signed-certificates           active       1  self-signed-certificates  latest/stable  155  10.152.183.33   no       
smf                       1.5.2    active       1  sdcore-smf-k8s            1.5/edge       590  10.152.183.64   no       
traefik                   v2.11.0  waiting      1  traefik-k8s               latest/stable  194  10.152.183.198  no       installing agent
udm                       1.4.3    active       1  sdcore-udm-k8s            1.5/edge       489  10.152.183.31   no       
udr                       1.4.1    active       1  sdcore-udr-k8s            1.5/edge       486  10.152.183.82   no       
upf                       1.4.0    active       1  sdcore-upf-k8s            1.5/edge       591  10.152.183.164  no       

Unit                         Workload  Agent  Address      Ports  Message
amf/0*                       active    idle   10.1.10.181         
ausf/0*                      active    idle   10.1.10.186         
grafana-agent/0*             blocked   idle   10.1.10.133         grafana-cloud-config: off, logging-consumer: off
mongodb/0*                   active    idle   10.1.10.155         Primary
nms/0*                       active    idle   10.1.10.174         
nrf/0*                       active    idle   10.1.10.151         
nssf/0*                      active    idle   10.1.10.136         
pcf/0*                       active    idle   10.1.10.146         
router/0*                    active    idle   10.1.10.145         
self-signed-certificates/0*  active    idle   10.1.10.141         
smf/0*                       active    idle   10.1.10.154         
traefik/0*                   error     idle   10.1.10.160         hook failed: "ingress-relation-changed"
udm/0*                       active    idle   10.1.10.187         
udr/0*                       active    idle   10.1.10.176         
upf/0*                       active    idle   10.1.10.169

Configure the ingress

Get the external IP address of Traefik’s traefik-lb LoadBalancer service:

microk8s.kubectl -n sdcore get svc | grep "traefik-lb"

The output should look similar to below:

ubuntu@host:~/terraform$ microk8s.kubectl -n sdcore get svc | grep "traefik-lb"
traefik-lb                           LoadBalancer   10.152.183.142   10.0.0.2      80:32435/TCP,443:32483/TCP    11m

In this tutorial, the IP is 10.0.0.2. Please note it, as we will need it in the next step.

Configure Traefik to use an external hostname. To do that, edit traefik_config in the main.tf file:

:caption: main.tf
(...)
module "sdcore" {
  (...)
  traefik_config = {
    routing_mode      = "subdomain"
    external_hostname = "10.0.0.2.nip.io"
  }
  (...)
}
(...)

Apply new configuration:

terraform apply -auto-approve

Resolve Traefik error in Juju:

juju resolve traefik/0

2. Deploy Charmed OAI RAN CU

Create a Terraform module for the Radio Access Network and add Charmed OAI RAN CU to it:

cat << EOF > ran.tf
resource "juju_model" "oai-ran" {
  name = "ran"
}

module "cu" {
  source = "git::https://github.com/canonical/oai-ran-cu-k8s-operator//terraform"

  model_name = juju_model.oai-ran.name
  config     = {
    "n3-interface-name": "ran"
  }
}

resource "juju_offer" "cu-fiveg-gnb-identity" {
  model            = juju_model.oai-ran.name
  application_name = module.cu.app_name
  endpoint         = module.cu.fiveg_gnb_identity_endpoint
}

resource "juju_integration" "cu-amf" {
  model = juju_model.oai-ran.name
  application {
    name     = module.cu.app_name
    endpoint = module.cu.fiveg_n2_endpoint
  }
  application {
    offer_url = module.sdcore.amf_fiveg_n2_offer_url
  }
}

resource "juju_integration" "cu-nms" {
  model = juju_model.sdcore.name
  application {
    name     = module.sdcore.nms_app_name
    endpoint = module.sdcore.fiveg_gnb_identity_endpoint
  }
  application {
    offer_url = juju_offer.cu-fiveg-gnb-identity.url
  }
}

EOF

Update Juju Terraform provider:

terraform init

Deploy CU:

terraform apply -auto-approve

Monitor the status of the deployment:

juju switch ran
juju status --watch 1s --relations

The deployment is ready when the cu application is in the active/idle state.

3. Configure the 5G core network through the Network Management System

Retrieve the NMS address:

juju switch sdcore
juju run traefik/0 show-proxied-endpoints

The output should be http://sdcore-nms.10.0.0.2.nip.io/.
Navigate to this address in your browser.

In the Network Management System (NMS), create a network slice with the following attributes:

  • Name: Tutorial

  • MCC: 001

  • MNC: 01

  • UPF: upf-external.private5g.svc.cluster.local:8805

  • gNodeB: private5g-cu-cu (tac:1)

You should see the following network slice created:

NMS Network Slice

Navigate to Subscribers and create a new subscriber with the following attributes:

  • IMSI: 001010100007487

  • OPC: 981d464c7c52eb6e5036234984ad0bcf

  • Key: 5122250214c33e723a5dd523fc145fc0

  • Sequence Number: 16f3b3f70fc2

  • Network Slice: Tutorial

  • Device Group: Tutorial-default

You should see the following subscriber created:

NMS Subscriber

Note

Due to current limitations in the network configuration procedure, it is required to restart the CU Pod after configuring the network. This limitation will be addressed in the future. To restart the CU Pod execute: microk8s.kubectl -n ran delete pod cu-0

4. Deploy Charmed OAI RAN DU

Add Charmed OAI RAN DU Terraform module to ran.tf:

cat << EOF >> ran.tf
module "du" {
  source = "git::https://github.com/canonical/oai-ran-du-k8s-operator//terraform"

  model_name = juju_model.oai-ran.name
  config     = {
    "simulation-mode": true
  }
}

resource "juju_integration" "du-cu" {
  model = juju_model.oai-ran.name
  application {
    name     = module.du.app_name
    endpoint = module.du.fiveg_f1_endpoint
  }
  application {
    name     = module.cu.app_name
    endpoint = module.cu.fiveg_f1_endpoint
  }
}

EOF

Update Juju Terraform provider:

terraform init

Deploy DU:

terraform apply -auto-approve

Monitor the status of the deployment:

juju switch ran
juju status --watch 1s --relations

The deployment is ready when the du application is in the active/idle state.

5. Deploy Charmed OAI RAN UE Simulator

Add Charmed OAI RAN UE Terraform module to ran.tf:

cat << EOF >> ran.tf
module "ue" {
  source = "git::https://github.com/canonical/oai-ran-ue-k8s-operator//terraform"

  model_name = juju_model.oai-ran.name
}

resource "juju_integration" "ue-du" {
  model = juju_model.oai-ran.name
  application {
    name     = module.ue.app_name
    endpoint = module.ue.fiveg_rfsim_endpoint
  }
  application {
    name     = module.du.app_name
    endpoint = module.du.fiveg_rfsim_endpoint
  }
}

EOF

Update Juju Terraform provider:

terraform init

Deploy the UE simulator:

terraform apply -auto-approve

Monitor the status of the deployment:

juju status --watch 1s --relations

The deployment is ready when the ue application is in the active/idle state.

6. Run 5G network traffic simulation

Run the simulation:

juju run ue/leader start-simulation

The simulation executed successfully if you see success: "true" as one of the output messages:

ubuntu@host:~$ juju run ue/leader start-simulation
Running operation 1 with 1 task
  - task 2 on unit-ue-0

Waiting for task 2...
result: |
  PING 8.8.8.8 (8.8.8.8) from 172.250.0.2 oaitun_ue1: 56(84) bytes of data.
  64 bytes from 8.8.8.8: icmp_seq=1 ttl=116 time=13.2 ms
  64 bytes from 8.8.8.8: icmp_seq=2 ttl=116 time=15.3 ms
  64 bytes from 8.8.8.8: icmp_seq=3 ttl=116 time=13.8 ms
  64 bytes from 8.8.8.8: icmp_seq=4 ttl=116 time=12.6 ms
  64 bytes from 8.8.8.8: icmp_seq=5 ttl=116 time=14.1 ms
  64 bytes from 8.8.8.8: icmp_seq=6 ttl=116 time=14.8 ms
  64 bytes from 8.8.8.8: icmp_seq=7 ttl=116 time=14.6 ms
  64 bytes from 8.8.8.8: icmp_seq=8 ttl=116 time=14.6 ms
  64 bytes from 8.8.8.8: icmp_seq=9 ttl=116 time=14.6 ms
  64 bytes from 8.8.8.8: icmp_seq=10 ttl=116 time=14.5 ms

  --- 8.8.8.8 ping statistics ---
  10 packets transmitted, 10 received, 0% packet loss, time 9010ms
  rtt min/avg/max/mdev = 12.561/14.217/15.294/0.772 ms
success: "true"

7. Destroy the environment

Destroy Terraform deployment:

terraform destroy -auto-approve

Note

Terraform does not remove anything from the working directory. If needed, please clean up the terraform directory manually by removing everything except for the main.tf and terraform.tf files.

Destroy the Juju controller and all its models:

juju kill-controller microk8s-classic-localhost