The first control-plane node
This is the node that creates the cluster. Everything else joins what it starts. What a control-plane node is, and why this one is called the first, is covered in How a cluster fits together. Every command on this page runs on this one machine, as root.
Run check.sh on it first, and register the licence, before you get here.
Two files must be in place on this node before you run the command below:
| File | Why this node needs it |
|---|---|
/etc/bke/license |
mode 0600, from Register your cluster |
/etc/bke/config.yaml |
auth.enabled in it decides how the cluster is created |
install.sh --role master asks the API how to create this cluster before it
changes anything on the machine, and it stops if it cannot. It is not reading
config.yaml itself and it is not guessing — see Cluster login. A worker
needs neither file: it joins a cluster whose shape was decided here.
The command
curl -sL https://bke.maml.uk/install.sh | sh -s -- \
--role master \
--version 2.0.0 \
--hostname m1.prod.k8s.example.com \
--endpoint prod.bke.example.com:6443 \
--dns-domain cluster.local
--version is the BKE version, not a Kubernetes version. BKE names its own
versions, and each one decides which Kubernetes minor, which Calico, which Helm
and which container images it means. The release notes in the console list the
published versions and what each one pins.
The three fixed arguments
| Flag | Format | Fixed for the life of the cluster |
|---|---|---|
--hostname |
bare host or FQDN | yes — it is in the node’s certificates |
--endpoint |
host:port, no scheme |
yes — it is in every kubeconfig, and on your licence |
--dns-domain |
DNS suffix | yes — every service name resolves under it |
--endpoint takes a host and a port, not a URL. kubeadm wants
prod.bke.example.com:6443, and https://prod.bke.example.com:6443 will not
work.
Point the endpoint at whatever will still be correct when you have three control-plane nodes — a load balancer, a floating address, a DNS record you control. Pointing it at this machine’s own name works today and becomes the thing you cannot change when you add the second one. Reference architecture gives the load balancer configuration this address points at in production.
What the script does
In order:
- Asks the API how this cluster should be created, before anything on the machine changes. A node that cannot reach the API stops here, with nothing installed and nothing reconfigured.
- Sets the hostname.
- Installs base packages —
curl,jq,git,etcd-client,open-iscsi,wireguard, and others. - Enables the kernel modules the cluster needs at boot, including
dm_cryptfor encrypted Longhorn volumes. - Adds the Kubernetes and Docker apt repositories and installs
kubelet,kubeadm,kubectlandcontainerdat the versions BKE 2.0.0 pins. - Installs
k9s. - Configures UFW, disables swap and sets the kernel networking
parameters. It opens the SSH port your
sshd_configalready names, and does not change that file. - Runs
kubeadm initwith your three arguments,--pod-network-cidr 192.168.0.0/16and--upload-certs— or, whenauth.enabledistrue, with a configuration file carrying the same settings plus your identity provider. Same cluster either way; the file exists becausekubeadmhas no way to pass an API-server flag on the command line. - Writes the admin kubeconfig.
- Installs Calico via the Tigera operator, waits for it, and enables WireGuard encryption for pod traffic.
- Records the BKE version at
/etc/bke/version.
If you have not read the SSH warning on Before you begin, read it before step 6 happens to you.
Afterwards
kubectl get nodes
kubectl get pods -A
The node will be Ready once Calico is up. calicoctl node status reports the
BGP peerings; with a single node there is nothing to peer with yet, and that is
expected.
k9s is also installed — a terminal interface over the same API kubectl
talks to, useful for watching pods start and reading logs without composing
commands. Run k9s as root and it works immediately: provisioning copied the
administrator kubeconfig to ~/.kube/config, which is the file both kubectl
and k9s read. On nodes added later that copy is yours to make — Adding
nodes carries the command.
/etc/bke/version now records the BKE version this node is on. It is a record of
what happened, not a setting — nothing reads it back to decide anything.
What is not installed yet
No components. metrics-server, cert-manager, Longhorn, Kong, Kuma, Fluent Bit
and netdata are installed separately, once, for the whole cluster, after the
workers have joined. That is apply.sh, and it needs a config.yaml. It shows
you what it would install before it installs anything.
No authentication. The cluster is reachable with the kubeadm administrator
kubeconfig and nothing else. Cluster login is a separate, deliberate step.