0. Before you start
Complete this page before starting 01-bios-bkc.md. It describes the required setup for the rest of the guide and separates it from optional alternatives and later requirements.
Required before continuing
You need all of the following before starting Chapter 1:
- Two clean Ubuntu 22.04 or 24.04 hosts: one control-plane host and one Xeon worker.
- The same login name on both hosts.
- SSH key authentication from the control-plane host to itself and to every worker, with no password prompt.
sudoaccess for that login, orLAB_SSH_USER=rootfor an unattended install.- This repository checked out on the control-plane host.
config/nodes.envfilled in with the addresses and SSH login.
You do not need to install Kubernetes, kubectl, containerd, or Python manually
on the worker. Chapter 2 installs the node software over SSH. kubectl is also
installed by Chapter 2 on the control-plane host.
Optional or needed later
These items are not required to complete the initial setup:
make— optional convenience; everymaketarget has an equivalent script.- Helm — needed later in Chapter 6, not for the initial host setup.
- A proxy — only needed when the network requires one.
- Git — optional if you download the repository as a ZIP instead.
- A third machine for the repository — supported, but the repository must be on the
control-plane host from Chapter 3 onward, where the kubeconfig and
results/live. - Running host-local checks directly on the worker — supported as described below.
If you skipped this page, the first script you run says so:
scripts/check-bios.sh: line 19: LAB_SSH_USER: set LAB_SSH_USER in config/nodes.env
That means config/nodes.env has not been completed yet.
1. The two hosts
| Role | What it is | Required capabilities |
|---|---|---|
Control-plane host (control-plane) |
The Kubernetes control-plane node and where you work. It never runs the workload, so a modest machine is fine. | Ubuntu 22.04 or 24.04, sudo, this repo |
Worker (worker-1) |
The Xeon machine under test. Its specification is what the results describe. | Ubuntu 22.04 or 24.04, sudo, Intel RDT |
More workers only means more entries in step 4. Both hosts can be freshly installed; nothing else has to be prepared by hand.
You need one login that exists on both hosts, with the same name, able to become root. Every script uses exactly that login and never stores a password.
Choose one access mode:
- non-root user (Recommended): use a normal login with
sudo. SSH uses keys and must not prompt;sudomay prompt for the host's password during installation. - root user (Unattended option): set
LAB_SSH_USER=root. The scripts omitsudoand stop requesting a tty, so the installation does not prompt.
Passwordless SSH and passwordless sudo are different requirements:
| Access | Required? | Prompt allowed? |
|---|---|---|
| SSH key authentication | Yes | No |
sudo access |
Yes, unless using root | Yes for a normal login |
Passwordless sudo |
No | Only useful for unattended operation |
Steps 2, 3, and 4 happen in one place: on the control-plane host, as that login.
2. Required tools and repository location
Run the following on the control-plane host:
sudo apt-get update
sudo apt-get install -y git curl python3 python3-venv
The packages have these purposes:
gitandcurl— obtain the repository and download installation resources.python3-venv— letsscripts/setup.shbuild the runner's virtual environment.
make is optional. Install it only if you want to use the Make targets:
sudo apt-get install -y make
Helm is needed later by Chapter 6 for Prometheus and Grafana, so it can be installed now or immediately before that chapter:
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
sudo snap install helm --classic works too.
Put the repository in your home directory and stay in it. Unless a later chapter says otherwise, commands in this guide are run from the repository root:
git clone <this repository> ~/mxl-k8s-qos-lab
cd ~/mxl-k8s-qos-lab
The worker needs no packages installed by hand; the installers copy what they need there and run it over SSH.
Optional: use a downloaded ZIP
If Git is not available, a downloaded ZIP is equally fine:
sudo apt-get install -y unzip
unzip <branch>.zip && cd CRM-k8s-refrence-recipe-*
Optional: keep the repository on a third machine
This works for Chapters 1 and 2 because both installers reach every node over SSH,
including the control-plane host. From Chapter 3 onward, the repository must be on
the control-plane host so the kubeconfig, Python runner, and results/ are local.
You can copy it across with scripts/sync-controller.sh.
3. Required SSH access
Run these commands on the control-plane host as the same login configured in
LAB_SSH_USER — not as root, because the key must belong to the login used by the
scripts.
Almost every script reaches a host over SSH non-interactively (BatchMode), so SSH
password prompts must be gone before you start. Sudo passwords are different:
a normal login may be prompted by the host when installation reaches it, and the
password is never stored.
ssh-keygen -t ed25519 -C mxl-lab # only if you have no key yet
ssh-copy-id <login>@<control-plane IP> # yes, to this host itself
ssh-copy-id <login>@<worker IP> # once per worker
The first command is not a typo. It copies your key into your own
~/.ssh/authorized_keys. The installer treats the control-plane host like any
other node and reaches it the same way, so without this step Chapter 2 fails on the
first host.
Verify the required SSH path now. Each command must print ok, with no prompt:
for host in <control-plane IP> <worker IP>; do
ssh -o BatchMode=yes "<login>@$host" 'echo ok'
done
If either command asks for a password or reports Permission denied (publickey),
stop and fix SSH before continuing. Every later script uses the same connection
method.
There is also a second, different hop: control-plane host → worker at run time.
The RDT helper and host noisy neighbor use it. Chapter 2 configures that hop with
scripts/setup-controller-worker-ssh.sh; you do not prepare it by hand.
4. Required fill in config/nodes.env
The only file you must edit before Chapter 1 is config/nodes.env.
config/lab.env ships with working defaults and does not need to be touched
unless you are changing node names, roles, or topology.
Normal setup (default node names)
Open config/nodes.env in the repository on the control-plane host and fill in
these three values:
CONTROL_PLANE_HOST=<the control-plane IP or hostname>
WORKER_1_HOST=<the worker IP or hostname>
LAB_SSH_USER=<the login from step 1>
Address requirements
Use the address each host is reachable at from the other host. Give the control-plane host its real network address even though you are working on it.
localhostand127.0.0.1are wrong: these values are written into every node's/etc/hostsand the control-plane address becomes the API server's advertise address. A DNS name that resolves the same on both hosts is fine; a~/.ssh/configalias is not — the installer detects and rejects it.
How the variable names are derived
Each _HOST key is the Kubernetes node name, uppercased, with - replaced by
_, plus _HOST:
| Kubernetes node name | config/nodes.env key |
|---|---|
control-plane |
CONTROL_PLANE_HOST |
worker-1 |
WORKER_1_HOST |
lab-w1 (example) |
LAB_W1_HOST |
The shipped defaults in config/lab.env use control-plane and worker-1, so
the two keys above cover the default setup with no further changes.
Optional: using your own node names
Edit config/lab.env only if you want different Kubernetes node names or
roles. Change LAB_CONTROLLER, LAB_WORKERS, and LAB_DEFAULT_NODE to your
chosen names, then add the corresponding _HOST keys to config/nodes.env
using the derivation rule above.
Optional: keeping a host's existing hostname
Chapter 2 sets each host's hostname to its Kubernetes node name. If a host
already has a purpose and you need to keep its current hostname, set that
hostname as the node name in config/lab.env and use the matching _HOST key
in config/nodes.env.
5. Required readiness check
Do not start Chapter 1 until this check prints one ok line per host. Run it on
the control-plane host from the repository root:
source config/nodes.env
for var in $(grep -o '^[A-Z0-9_]*_HOST' config/nodes.env); do
ssh -o BatchMode=yes "$LAB_SSH_USER@${!var}" "echo $var ok"
done
Expected output is similar to:
CONTROL_PLANE_HOST ok
WORKER_1_HOST ok
Common failures:
LAB_SSH_USERis unset — fill inconfig/nodes.envor source it again.Permission denied (publickey)— SSH key setup is incomplete.Could not resolve hostname— check the address inconfig/nodes.env.- A sudo password prompt — expected for a normal sudo user; it is not an SSH failure.
You are ready to start 01-bios-bkc.md when every host responds without an SSH password prompt.
6. Optional: Proxy configuration (only when needed)
Skip this section if the hosts have direct internet access. If your network uses a
proxy, set these two values in config/lab.env:
LAB_HTTP_PROXY=http://<proxy>:<port>
LAB_HTTPS_PROXY=http://<proxy>:<port>
The cluster installer configures apt, containerd, kubeadm, and kubectl from
these values on every node. Also set http_proxy and https_proxy in your shell
and for apt on the control-plane host, because Git, curl, and Helm need them.
You do not need to calculate no_proxy for the cluster: each script excludes the
cluster's own addresses for its own process. scripts/preflight.sh reports if your
shell needs the same exclusions for kubectl commands you type by hand.
For more detail or troubleshooting, see 02-kubernetes-install.md § Behind a proxy.
7. Network access and later requirements
The installers download from the internet. Before Chapter 2, both hosts need
access to the Ubuntu archives, download.docker.com, pkgs.k8s.io, and
registry.k8s.io. Later chapters also use the following:
raw.githubusercontent.com— the Calico operator manifest.quay.io— the Tigera operator image.docker.io— Calico's own images. These are separate fromregistry.k8s.io; blocking either registry can leave nodes unable to becomeReady.- GitHub releases — nerdctl, Intel PerfSpect, and Intel PCM sources.
github.com/cbcrc— FFmpeg-MXL build guidance for Chapter 5.prometheus-community.github.ioandghcr.io— Helm charts and the stress-ng image for Chapter 6.
A fully air-gapped install is not supported.
The hosts must also permit Calico's VXLAN data plane between every pair of
Kubernetes node addresses: UDP port 4789 in both directions. The installer uses
each Node's Kubernetes InternalIP for the tunnel endpoint, including hosts with
multiple network interfaces. A blocked VXLAN path can leave every Node and Calico
Pod reporting Ready while cross-node Pod traffic times out.
Every host must synchronize time over NTP, normally UDP port 123. The node installer
enables chrony, and preflight rejects more than five seconds of
control-plane-to-worker skew. Measurement windows and Prometheus samples cannot be
compared reliably across unsynchronized hosts.
What is not in this repo
One 1080p60 source clip. Media is not shipped here; see 05-ffmpeg-mxl-container.md for what to supply and where it goes.
Where things live
| Path | What it is |
|---|---|
config/nodes.env |
Addresses and the SSH login — the file you just filled in |
config/lab.env |
Topology and every default, commented |
scripts/ |
One script per chapter of this guide, all idempotent |
docs/ |
This guide, in order |
results/ |
One directory per run, plus summary.html |
Next: 01-bios-bkc.md.