# Getting started
In this tutorial, we will deploy and run an SD-Core 5G core network using Juju and Terraform.
As part of this tutorial, we will also deploy a gNB Simulator which is a 5G radio and a cellphone simulator.
The gNB Simulator serves only demonstration purposes and shouldn't be part of production deployments.
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
- 8GB of RAM
- 50GB of free disk space
## 1. Install Canonical K8s
From your terminal, install Canonical K8s and bootstrap it:
```console
sudo snap install k8s --classic --channel=1.33-classic/stable
cat << EOF | sudo k8s bootstrap --file -
containerd-base-dir: /opt/containerd
cluster-config:
network:
enabled: true
dns:
enabled: true
load-balancer:
enabled: true
local-storage:
enabled: true
annotations:
k8sd/v1alpha1/cilium/sctp/enabled: true
EOF
```
Add the Multus plugin.
```console
sudo k8s kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/master/deployments/multus-daemonset-thick.yml
```
We must give MetalLB an address range that has at least 3 IP addresses for Charmed Aether SD-Core.
```console
sudo k8s set load-balancer.cidrs="10.0.0.2-10.0.0.4"
```
## 2. Bootstrap a Juju controller
From your terminal, install Juju.
```console
sudo snap install juju --channel=3.6/stable
```
Save the K8s credentials to allow bootstrapping Juju controller.
```console
mkdir -p ~/.kube
sudo k8s config > ~/.kube/config
mkdir -p ~/.local/share/juju/
sudo k8s config > ~/.local/share/juju/credentials.yaml
```
Bootstrap a Juju controller
```console
juju bootstrap k8s
```
```{note}
There is a [bug](https://bugs.launchpad.net/juju/+bug/1988355) in Juju that occurs when
bootstrapping a controller on a new machine. If you encounter it, create the following
directory:
`mkdir -p ~/.local/share`
```
## 3. Install Terraform
From your terminal, install Terraform.
```console
sudo snap install terraform --classic
```
## 4. Deploy Charmed Aether SD-Core
On the host machine create a new directory called `terraform`:
```console
mkdir terraform
```
Inside newly created `terraform` directory create a `versions.tf` file:
```console
cd terraform
cat << EOF > versions.tf
terraform {
required_providers {
juju = {
source = "juju/juju"
version = ">= 0.12.0"
}
}
}
EOF
```
Create a Terraform module containing the SD-Core 5G core network:
```console
cat << EOF > core.tf
resource "juju_model" "sdcore" {
name = "sdcore"
}
module "sdcore" {
source = "git::https://github.com/canonical/terraform-juju-sdcore//modules/sdcore-k8s"
model = juju_model.sdcore.name
depends_on = [juju_model.sdcore]
traefik_config = {
routing_mode = "subdomain"
}
}
EOF
```
```{note}
You can get a ready example by cloning [this Git repository](https://github.com/canonical/charmed-aether-sd-core).
All necessary files are in the `examples/terraform/getting_started` directory.
```
Initialize Juju Terraform provider:
```console
terraform init
```
Deploy SD-Core by applying your Terraform configuration:
```console
terraform apply -auto-approve
```
The deployment process should take approximately 15-20 minutes.
Monitor the status of the deployment:
```console
juju switch sdcore
juju status --relations --watch 1s
```
The deployment is ready when all the charms are in the `active/idle` state.
It is normal for `grafana-agent` and `traefik` to be in `blocked` state.
Example:
```console
ubuntu@host:~/terraform $ juju status
Model Controller Cloud/Region Version SLA Timestamp
sdcore k8s k8s 3.6.6 unsupported 11:35:07+02:00
App Version Status Scale Charm Channel Rev Address Exposed Message
amf 1.6.4 active 1 sdcore-amf-k8s 1.6/edge 908 10.152.183.217 no
ausf 1.6.2 active 1 sdcore-ausf-k8s 1.6/edge 713 10.152.183.19 no
grafana-agent 0.40.4 blocked 1 grafana-agent-k8s latest/stable 111 10.152.183.102 no Missing ['grafana-cloud-config']|['logging-consumer'] for logging-provider; ['grafana-cloud-config']|['send-remote-wr...
mongodb active 1 mongodb-k8s 6/stable 61 10.152.183.18 no
nms 1.1.0 active 1 sdcore-nms-k8s 1.6/edge 849 10.152.183.42 no
nrf 1.6.2 active 1 sdcore-nrf-k8s 1.6/edge 790 10.152.183.234 no
nssf 1.6.1 active 1 sdcore-nssf-k8s 1.6/edge 669 10.152.183.40 no
pcf 1.6.1 active 1 sdcore-pcf-k8s 1.6/edge 710 10.152.183.129 no
self-signed-certificates active 1 self-signed-certificates 1/stable 263 10.152.183.71 no
smf 2.0.2 active 1 sdcore-smf-k8s 1.6/edge 801 10.152.183.81 no
traefik 2.11.0 blocked 1 traefik-k8s latest/stable 234 10.152.183.244 no "external_hostname" must be set while using routing mode "subdomain"
udm 1.6.1 active 1 sdcore-udm-k8s 1.6/edge 664 10.152.183.241 no
udr 1.6.2 active 1 sdcore-udr-k8s 1.6/edge 645 10.152.183.96 no
upf 2.0.1 active 1 sdcore-upf-k8s 1.6/edge 767 10.152.183.173 no
Unit Workload Agent Address Ports Message
amf/0* active idle 10.1.194.206
ausf/0* active idle 10.1.194.235
grafana-agent/0* blocked idle 10.1.194.208 Missing ['grafana-cloud-config']|['logging-consumer'] for logging-provider; ['grafana-cloud-config']|['send-remote-wr...
mongodb/0* active idle 10.1.194.237 Primary
nms/0* active idle 10.1.194.255
nrf/0* active idle 10.1.194.213
nssf/0* active idle 10.1.194.243
pcf/0* active idle 10.1.194.250
self-signed-certificates/0* active idle 10.1.194.239
smf/0* active idle 10.1.194.202
traefik/0* blocked idle 10.1.194.230 "external_hostname" must be set while using routing mode "subdomain"
udm/0* active idle 10.1.194.249
udr/0* active idle 10.1.194.245
upf/0* active idle 10.1.194.217
Offer Application Charm Rev Connected Endpoint Interface Role
amf amf sdcore-amf-k8s 908 0/0 fiveg-n2 fiveg_n2 provider
nms nms sdcore-nms-k8s 849 0/0 fiveg_core_gnb fiveg_core_gnb provider
upf upf sdcore-upf-k8s 767 0/0 fiveg_n3 fiveg_n3 provider
```
## 5. Configure the ingress
Get the external IP address of Traefik's `traefik-lb` LoadBalancer service:
```console
sudo k8s kubectl -n sdcore get svc | grep "traefik-lb"
```
The output should look similar to below:
```console
ubuntu@host:~/terraform $ sudo k8s kubectl -n sdcore get svc | grep "traefik-lb"
traefik-lb LoadBalancer 10.152.183.83 10.0.0.2 80:30462/TCP,443:30163/TCP 9m4s
```
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 `core.tf` file:
```
:caption: core.tf
(...)
module "sdcore" {
(...)
traefik_config = {
routing_mode = "subdomain"
external_hostname = "10.0.0.2.nip.io"
}
(...)
}
(...)
```
Apply new configuration:
```console
terraform apply -auto-approve
```
## 6. Deploy the gNodeB and a cellphone simulator
Inside the `terraform` directory create a new module:
```console
cat << EOF > ran.tf
resource "juju_model" "ran-simulator" {
name = "ran"
}
module "gnbsim" {
source = "git::https://github.com/canonical/sdcore-gnbsim-k8s-operator//terraform"
model = juju_model.ran-simulator.name
}
resource "juju_integration" "gnbsim-amf" {
model = juju_model.ran-simulator.name
application {
name = module.gnbsim.app_name
endpoint = module.gnbsim.requires.fiveg_n2
}
application {
offer_url = module.sdcore.amf_fiveg_n2_offer_url
}
}
resource "juju_integration" "gnbsim-nms" {
model = juju_model.ran-simulator.name
application {
name = module.gnbsim.app_name
endpoint = module.gnbsim.requires.fiveg_core_gnb
}
application {
offer_url = module.sdcore.nms_fiveg_core_gnb_offer_url
}
}
EOF
```
Initialize Juju Terraform provider:
```console
terraform init
```
Apply new configuration:
```console
terraform apply -auto-approve
```
Monitor the status of the deployment:
```console
juju switch ran
juju status --relations --watch 1s
```
The deployment is ready when the `gnbsim` application is in the `Waiting/Idle` state and the message is `Waiting for TAC and PLMNs configuration`.
Example:
```console
ubuntu@host:~/terraform $ juju status
Model Controller Cloud/Region Version SLA Timestamp
ran k8s k8s 3.6.6 unsupported 12:18:26+02:00
SAAS Status Store URL
amf active local admin/sdcore.amf
nms active local admin/sdcore.nms
App Version Status Scale Charm Channel Rev Address Exposed Message
gnbsim 1.4.5 waiting 1 sdcore-gnbsim-k8s 1.6/edge 638 10.152.183.85 no installing agent
Unit Workload Agent Address Ports Message
gnbsim/0* waiting idle 10.1.194.239 Waiting for TAC and PLMNs configuration
```
## 7. Configure the 5G core network through the Network Management System
Retrieve the NMS credentials (`username` and `password`):
```console
juju switch sdcore
juju show-secret NMS_LOGIN --reveal
```
The output looks like this:
```
cvn3usfmp25c7bgqqr60:
revision: 2
checksum: f2933262ee923c949cc0bd12b0456184bb85e5bf41075028893eea447ab40b68
owner: nms
label: NMS_LOGIN
created: 2025-04-03T07:57:40Z
updated: 2025-04-03T08:02:15Z
content:
password: pkxp9DYCcZG
token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3NDM2NzA5MzMsInVzZXJuYW1lIjoiY2hhcm0tYWRtaW4tVlNMTSIsInJvbGUiOjF9.Qwp0PIn9L07nTz0XooPvMb8v8-egYJT85MXjoOY9nYQ
username: charm-admin-VSLM
```
Retrieve the NMS address:
```console
juju run traefik/0 show-proxied-endpoints
```
The output should be `https://sdcore-nms.10.0.0.2.nip.io/`. Navigate to this address in your
browser and use the `username` and `password` to login.
### Assign Tracking Area Code (TAC) to the gNodeB
In the Network Management System (NMS) navigate to the `Inventory` tab. Click the `Edit` button next to the integrated gNB name and set `TAC` to `1`:
```{image} ../images/getting_started_gnb_tac.png
:alt: NMS Inventory
:align: center
```
Confirm new `TAC` value by clicking the `Submit` button.
### Create a Network Slice
Navigate to the `Network slices` tab and create a network slice with the following attributes:
- Name: `default`
- MCC: `001`
- MNC: `01`
- UPF: `upf-external.sdcore.svc.cluster.local:8805`
- gNodeB: `ran-gnbsim-gnbsim`
You should see the following network slice created:
```{image} ../images/getting_started_network_slice.png
:alt: NMS Network Slice
:align: center
```
### Create a Device Group
Navigate to the `Device groups` tab and create a device group with the following attributes:
- Name: `device-group`
- Network Slice: `default`
- Subscriber IP pool: `172.250.1.0/16`
- DNS: `8.8.8.8`
- MTU (bytes): `1456`
- Maximum bitrate (Mbps):
- Downstream: `200`
- Upstream: `20`
- QoS:
- 5QI: `1: GBR - Conversational Voice`
- ARP: `6`
You should see the following device group created:
```{image} ../images/getting_started_device_group.png
:alt: NMS Device Group
:align: center
```
### Create a Subscriber
Navigate to `Subscribers` tab and click the `Create` button. Fill in the following:
- Network Slice: `default`
- Device Group: `device-group`
Click the two `Generate` buttons to automatically fill in the values in the form. Note the IMSI, OPC, and Key; we are going to use them in the next step.
After clicking the `Submit` button you should see the subscriber created:
```{image} ../images/getting_started_subscriber.png
:alt: NMS Subscriber
:align: center
```
## 8. Run the 5G simulation
Switch to the `ran` model and set up the subscriber information using the values noted in the previous step:
```console
juju switch ran
juju config gnbsim imsi= usim-opc= usim-key=
```
Make sure that the `gnbsim` application is in `Active/Idle` state.
```console
juju status
```
The output should be similar to below:
Example:
```console
ubuntu@host:~/terraform $ juju status
Model Controller Cloud/Region Version SLA Timestamp
ran k8s k8s 3.6.0 unsupported 12:18:26+02:00
SAAS Status Store URL
amf active local admin/sdcore.amf
nms active local admin/sdcore.nms
App Version Status Scale Charm Channel Rev Address Exposed Message
gnbsim 1.4.5 active 1 sdcore-gnbsim-k8s 1.6/edge 638 10.152.183.85 no
Unit Workload Agent Address Ports Message
gnbsim/0* active idle 10.1.194.239
```
Run the simulation:
```console
juju run gnbsim/leader start-simulation
```
The simulation executed successfully if you see `success: "true"` as one of the output messages:
```console
ubuntu@host:~$ juju run gnbsim/leader start-simulation
Running operation 1 with 1 task
- task 2 on unit-gnbsim-0
Waiting for task 2...
info: 5/5 profiles passed
success: "true"
```
## 9. Destroy the environment
Destroy Terraform deployment:
```console
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 `core.tf`, `ran.tf`
and `versions.tf` files.
```
Destroy the Juju controller and all its models:
```console
juju kill-controller k8s
```