DEV Community

Celso Nery
Celso Nery

Posted on

Building an On-Premise Kubernetes Cluster — Part 3: Initializing the Master

🇧🇷 Leia a versão em português aqui

In previous parts of this series, we prepared the environment (Part 1) and installed containerd, kubelet, and kubeadm on all nodes (Part 2). Now it's time to finally initialize the cluster: we'll create the control-plane on the master server and get it ready to accept workers and a functional pod network.

Initializing the cluster with kubeadm

The simplest way to initialize the cluster is to run the command below on the master server:

# kubeadm init
Enter fullscreen mode Exit fullscreen mode

Or, specifying important parameters manually — such as the pod network CIDR, the IP address to be advertised by the API server, and the Kubernetes version:

# kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=10.0.10.100 --kubernetes-version="1.31.1"
Enter fullscreen mode Exit fullscreen mode

Using a configuration file (recommended)

More recent versions of kubelet no longer accept certain options via the command line (this behavior has been deprecated). Because of this, the current recommendation is to create a configuration file for kubeadm, instead of passing everything via flags.

Create the kubelet.yaml file:

apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: "1.31.1" # Replace with your desired version
controlPlaneEndpoint: "k8s-master"
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
Enter fullscreen mode Exit fullscreen mode

And run the initialization pointing to that file:

$ sudo kubeadm init --config kubelet.yaml
Enter fullscreen mode Exit fullscreen mode

What to expect from the output

If everything goes well, the command's output will look similar to this (summarized here for readability):

[init] Using Kubernetes version: v1.31.1
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'

[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Activating the kubelet service
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver-kubelet-client" certificate and key

...

[bootstraptoken] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.0.10.100:6443 --token d1dyaj.31zxywbg93s1ywjy --discovery-token-ca-cert-hash sha256:71a91721595fde66b6382908d801266602a14de8e16bdb7a3cede21509427009
Enter fullscreen mode Exit fullscreen mode

⚠️ Important: save the full kubeadm join command (with the token and certificate hash) shown at the end of the output. You'll need it to add the workers to the cluster.

Setting up kubectl access

To use kubectl as a regular user (without needing sudo every time), copy the administrator's configuration file:

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
Enter fullscreen mode Exit fullscreen mode

To check the current state of the cluster, run:

kubectl get pods --all-namespaces
Enter fullscreen mode Exit fullscreen mode

At this point, it's normal for some pods to show up as Pending — that's because we haven't installed a pod network (CNI) yet, which is exactly the next step.

Output example:
All namespaces in pending

Installing the pod network (CNI)

Kubernetes doesn't ship with a ready-made networking solution — you need to choose and install a network plugin (CNI), responsible for allowing communication between the cluster's pods. Some popular options are Calico, Canal, Flannel, and Weave.

Use only one of the options above — installing more than one CNI on the same cluster can cause network conflicts.

Example with Calico:

kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.2/manifests/calico.yaml
Enter fullscreen mode Exit fullscreen mode

Example with Flannel:

kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/v0.20.2/Documentation/kube-flannel.yml
Enter fullscreen mode Exit fullscreen mode

Example with Weave:

export kubever=$(kubectl version | base64 | tr -d '\n')
kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$kubever"

kubectl apply -f https://github.com/weaveworks/weave/releases/download/v2.8.1/weave-daemonset-k8s.yaml
Enter fullscreen mode Exit fullscreen mode

After applying the chosen manifest, run again:

kubectl get pods --all-namespaces
Enter fullscreen mode Exit fullscreen mode

And, within a few moments, the pods in the kube-system namespace — including CoreDNS — should switch to Running status. This confirms the pod network is working correctly.

The output must be the same as this.:
Output weave network

Next steps

With the control-plane initialized, kubectl configured, and the pod network installed, the cluster's master is now fully operational. Only one step remains: adding the worker servers to the cluster using the kubeadm join command we saved earlier.

In Part 4 of this series, we'll join the workers to the cluster and confirm all nodes are ready to receive workloads.

Continued in Part 4.

Top comments (0)