Cloud Native 28 min read

How to Build a Kubernetes v1.22.1 Cluster with containerd Using kubeadm

This step‑by‑step guide shows how to prepare three CentOS 7 nodes, install containerd, configure system settings, deploy kubeadm/kubelet/kubectl, initialize a Kubernetes v1.22.1 control plane, add worker nodes, install the flannel CNI plugin, set up the Dashboard, and clean up the environment.

Cloud Native Technology Community
Cloud Native Technology Community
Cloud Native Technology Community
How to Build a Kubernetes v1.22.1 Cluster with containerd Using kubeadm

Environment preparation

Three CentOS 7.6 nodes (master, node1, node2) must have unique hostnames, firewalld and SELinux disabled, br_netfilter module loaded, and the following sysctl settings in /etc/sysctl.d/k8s.conf:

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
vm.swappiness = 0

Apply the settings with sysctl -p /etc/sysctl.d/k8s.conf. Install IPVS support:

cat > /etc/sysconfig/modules/ipvs.modules <<'EOF'
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF
chmod 755 /etc/sysconfig/modules/ipvs.modules
bash /etc/sysconfig/modules/ipvs.modules
lsmod | grep -e ip_vs -e nf_conntrack_ipv4

Install ipset and ipvsadm, synchronize time with chrony, and disable swap ( swapoff -a and comment out swap entries in /etc/fstab).

Install containerd

Download the bundled release that contains containerd, runc and CNI plugins, then extract it to the root filesystem:

wget https://github.com/containerd/containerd/releases/download/v1.5.5/cri-containerd-cni-1.5.5-linux-amd64.tar.gz
tar -C / -xzf cri-containerd-cni-1.5.5-linux-amd64.tar.gz

Generate a default configuration and enable the systemd cgroup driver:

mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml

Edit /etc/containerd/config.toml to set:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
  SystemdCgroup = true

[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
  endpoint = ["https://bqr1dr1n.mirror.aliyuncs.com"]

[plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
  endpoint = ["https://registry.aliyuncs.com/k8sxio"]

Start containerd with systemd:

systemctl daemon-reload
systemctl enable --now containerd

Install kubeadm, kubelet and kubectl

Add the Kubernetes yum repository (Google or Alibaba mirror) and install version 1.22.1:

cat > /etc/yum.repos.d/kubernetes.repo <<'EOF'
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF

yum makecache fast
yum install -y kubelet-1.22.1 kubeadm-1.22.1 kubectl-1.22.1 --disableexcludes=kubernetes
systemctl enable --now kubelet

Cluster initialization configuration

Create kubeadm.yaml with the required settings (image repository, IPVS proxy mode, pod subnet, etc.):

apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 192.168.31.30
  bindPort: 6443
nodeRegistration:
  criSocket: /run/containerd/containerd.sock
  name: master
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
imageRepository: registry.aliyuncs.com/k8sxio
kubernetesVersion: v1.22.1
networking:
  podSubnet: 10.244.0.0/16
  serviceSubnet: 10.96.0.0/12
  dnsDomain: cluster.local

Pre‑pull the required images (the coredns image may need manual handling):

kubeadm config images pull --config kubeadm.yaml
# If coredns is missing:
ctr -n k8s.io i pull docker.io/coredns/coredns:1.8.4
ctr -n k8s.io i tag docker.io/coredns/coredns:1.8.4 registry.aliyuncs.com/k8sxio/coredns:v1.8.4

Initialize the control plane:

kubeadm init --config kubeadm.yaml

Copy the admin kubeconfig for 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

Verify the master node:

kubectl get nodes

Add worker nodes

Run the kubeadm join command printed at the end of kubeadm init (or regenerate it with kubeadm token create --print-join-command) on each worker after copying the same kubeconfig file. Verify with kubectl get nodes on the master.

Install Flannel CNI plugin

Download the manifest, optionally set --iface=eth0 for multi‑NIC hosts, and apply:

wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# edit if needed: vi kube-flannel.yml (set --iface)
kubectl apply -f kube-flannel.yml

After a short wait, confirm that Flannel and CoreDNS pods are running in the 10.244.0.0/16 pod network.

Resolve containerd default CNI conflict

Containerd ships a default bridge CNI configuration ( 10-containerd-net.conflist) that conflicts with Flannel. Rename the file, delete the bridge interface, and restart services:

mv /etc/cni/net.d/10-containerd-net.conflist /etc/cni/net.d/10-containerd-net.conflist.bak
ifconfig cni0 down && ip link delete cni0
systemctl daemon-reload
systemctl restart containerd kubelet

Re‑create CoreDNS and Dashboard pods so they obtain IPs from the Flannel subnet.

Deploy Kubernetes Dashboard

Download the latest Dashboard manifest (v2.3.1), change the Service type to NodePort, and apply:

wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.3.1/aio/deploy/recommended.yaml
# edit: set spec.type: NodePort
kubectl apply -f recommended.yaml

Create an admin ServiceAccount and ClusterRoleBinding to obtain a login token:

cat > admin.yaml <<'EOF'
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: admin
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: admin
  namespace: kubernetes-dashboard
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin
  namespace: kubernetes-dashboard
EOF
kubectl apply -f admin.yaml
# Retrieve token:
kubectl -n kubernetes-dashboard get secret | grep admin-token
kubectl -n kubernetes-dashboard get secret admin-token-xxxx -o jsonpath={.data.token} | base64 -d

Access the Dashboard at https://<master‑IP>:<NodePort> (e.g., https://192.168.31.30:31050) and log in with the decoded token.

Final verification

Run the following to confirm the cluster state:

kubectl get nodes -o wide
kubectl get pods -n kube-system
kubectl get svc -n kubernetes-dashboard

All nodes should be Ready, the container runtime should be containerd://1.5.5, and pod IPs should belong to the 10.244.0.0/16 range.

Cluster cleanup

If a full reset is required:

kubeadm reset
ifconfig cni0 down && ip link delete cni0
ifconfig flannel.1 down && ip link delete flannel.1
rm -rf /var/lib/cni/
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

KubernetesDashboardcontainerdCentOSFlannelkubeadmIPVS
Cloud Native Technology Community
Written by

Cloud Native Technology Community

The Cloud Native Technology Community, part of the CNBPA Cloud Native Technology Practice Alliance, focuses on evangelizing cutting‑edge cloud‑native technologies and practical implementations. It shares in‑depth content, case studies, and event/meetup information on containers, Kubernetes, DevOps, Service Mesh, and other cloud‑native tech, along with updates from the CNBPA alliance.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.