Recently, I’ve been enjoying tweaking my home lab environment, so I decided to put some effort into building a Kubernetes cluster. I thought “Building with kubeadm seems pretty tedious…”, but when I tried it, it went surprisingly smoothly, so I’m summarizing the record as a memorandum.
This time, I stood up Proxmox VE on a mini PC equipped with N150 and created VMs there to work on. Hardware-wise, the specs are quite modest, but it’s necessary and sufficient for verification purposes.
The content basically follows the procedures in the documentation, so there isn’t much deep technical diving. However, there were a few points where I thought “I’d get stuck if I forgot this” while actually running it, so it might be helpful if viewed from that perspective.
Execution Environment
I run it on VMs on Proxmox so that I can easily destroy and create environments.
- CPU: Intel N150 (Cores assigned to VM = 2)
- RAM: 8GB
- Disk: 256GB
- OS: ubuntu server 24.04.2
Procedure
I will perform the following tasks referring to the Official Documentation.
- Install container runtime
- Install kubeadm
- Launch cluster
- Install CNI
Install Container Runtime
Ref: https://kubernetes.io/docs/setup/production-environment/container-runtimes/
Enable IPv4 Forwarding
# As per documentationcat <<EOF | sudo tee /etc/modules-load.d/k8s.confoverlaybr_netfilterEOF
sudo modprobe overlaysudo modprobe br_netfilter
# Kernel parameters required for this configuration, values persist after rebootcat <<EOF | sudo tee /etc/sysctl.d/k8s.confnet.bridge.bridge-nf-call-iptables = 1net.bridge.bridge-nf-call-ip6tables = 1net.ipv4.ip_forward = 1EOF
# Apply kernel parameters without rebootsudo sysctl --systemInstall cri-o (Container Runtime)
Install referring to cri-o’s Official Documentation.
CRIO_VERSION=v1.32
# Install dependencies required for worksudo apt-get updatesudo apt-get install -y software-properties-common curl
# Add cri-o repositorycurl -fsSL https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/ /" | sudo tee /etc/apt/sources.list.d/cri-o.list
# Install cri-osudo apt-get updatesudo apt-get install -y cri-oOnce installed, start cri-o. If status is active, startup is successful.
systemctl start crio.servicesystemctl status crio.serviceInstall kubeadm
Ref: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm
Disable swap
If there is no swap, you can ignore this section.
# Check if swap existsfree -h
# Temporarily disable swap (returns on reboot)sudo swapoff -a
# To persist, comment out or delete swap line in /etc/fstabsudo sed -i '/ swap / s/^$.*$$/#\1/g' /etc/fstabInstall kubeadm
KUBERNETES_VERSION=v1.33
# Add Kubernetes repositorycurl -fsSL https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/$KUBERNETES_VERSION/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
# Install kubelet, kubeadm, kubectlsudo apt-get updatesudo apt-get install -y kubelet kubeadm kubectlsudo apt-mark hold kubelet kubeadm kubectlCreate Cluster
Ref: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm
Initialize kubeadm
Run the following command while praying no errors occur. If trying cilium’s kube-proxy replacement feature, skip kube-proxy setup here.
# Usually thissudo kubeadm init
# If replacing kube-proxy with cilium, thiskubeadm init --skip-phases=addon/kube-proxyIf displayed as follows, execute commands as instructed to complete initialization.
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
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.confRun the following command here. You can see control-plane is created, but it’s still in NotReady state. This is because CNI is not installed, so we will install it in the next section.
kubectl get nodes
# NAME STATUS ROLES AGE VERSION# test-01 NotReady control-plane 6m21s v1.33.0Install CNI
Install helm
Since cilium is introduced via helm, first install helm.
Note: helm is a package manager for kubernetes (it has other uses like managing deployment definitions, but omitting details).
Follow the procedure in Official Documentation as usual.
# Working dependenciessudo apt-get install apt-transport-https --yes
# Add helm repositorycurl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/nullecho "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
# Install helmsudo apt-get updatesudo apt-get install helmInstall Cilium
Install referring to this documentation. If trying kube-proxy replacement feature, use kubeProxyReplacement=true.
# Add cilium repositoryhelm repo add cilium https://helm.cilium.io/
# Install cilium (Normal)helm install cilium cilium/cilium --version 1.17.3 \ --namespace kube-system
# Install cilium (If replacing kube-proxy with cilium)API_SERVER_IP=<your_api_server_ip># Kubeadm default is 6443API_SERVER_PORT=<your_api_server_port>helm install cilium cilium/cilium --version 1.17.4 \ --namespace kube-system \ --set kubeProxyReplacement=true \ --set k8sServiceHost=${API_SERVER_IP} \ --set k8sServicePort=${API_SERVER_PORT}Install Cilium CLI
Install CLI as it’s mandatory for checking CNI status or customizing.
# Verify variablesCILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)CLI_ARCH=amd64if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
# Download packagecurl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
# Check hashsha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
# Unzip and place in /usr/local/binsudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/binrm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}You can check if it works by hitting the following command.
cilium statusConfirm node
Hit the following command again and confirm that kubelet is in a working state. If status becomes Ready, it is successful. If NotReady, probably missed something somewhere.
kubectl get node
# Success if status is ready# NAME STATUS ROLES AGE VERSION# test-01 Ready control-plane 28m v1.33.0Conclusion
So, I completed up to launching a Kubernetes cluster with a single-node configuration! Since I did everything including introducing Cilium via CLI base, I’m slightly hyped about the “I built it myself” feeling.
Next, I’d like to try GitOps-like operation using this environment, or expanding to multiple nodes. I’ll write again if interesting topics come up while playing around bit by bit.
Next Article ↓
>-









