- Published on
Understanding Kubernetes internals by building your own cluster
Introduction
This is the blog post version of the talk I gave at Kubernetes Community Day Kerala. I will add the video to the talk here once its up.
Learning how Kubernetes works internally can be a highly rewarding exercise. It will make you more confident since the different components of Kubernetes are no longer a black box. Building and running Kubernetes locally is also the first step if you want to contribute to the Kubernetes code base. In this post, we go over how to setup a very minimal Kubernetes cluster in an AWS EC2 VM.
Initial setup
Make sure you have essential toold like curl
, tmux
and anything else you might wanna use already installed.
Download and install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
Download the binaries for the Kubernetes components, kube-apiserver
, kube-controller-manager
, kube-scheduler
and kubelet
.
# Download the kube-apiserver binary
curl -LO "https://dl.k8s.io/v1.29.1/bin/linux/amd64/kube-apiserver"
# Download the kube-controller-manager binary
curl -LO "https://dl.k8s.io/v1.29.1/bin/linux/amd64/kube-controller-manager"
# Download the kube-scheduler binary
curl -LO "https://dl.k8s.io/v1.29.1/bin/linux/amd64/kube-scheduler"
# Download the kubelet binary
curl -LO "https://dl.k8s.io/v1.29.1/bin/linux/amd64/kubelet"
Download and install etcd
ETCD_VER=v3.4.29
GITHUB_URL=https://github.com/etcd-io/etcd/releases/download
DOWNLOAD_URL=${GITHUB_URL}
mkdir etcd
curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf etcd-${ETCD_VER}-linux-amd64.tar.gz -C etcd --strip-components=1
rm etcd-v3.4.29-linux-amd64.tar.gz
Next we will setup the certificates needed to run the kube-apiserver
locally. Using these certificates, we're also gonna setup the kubeconfig
file which we'll use to interact with the cluster. I referred to this article written by Masato Naka for preparing the certificates.
mkdir certs
cd certs
cat <<EOF >> csr.conf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
req_extensions = req_ext
distinguished_name = dn
[ dn ]
C = IN
ST = Kerala
L = Kochi
O = BB
OU = BB
CN = 127.0.0.1
[ req_ext ]
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster
DNS.5 = kubernetes.default.svc.cluster.local
IP.1 = 127.0.0.1
IP.2 = 127.0.0.1
[ v3_ext ]
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment
extendedKeyUsage=serverAuth,clientAuth
subjectAltName=@alt_names
EOF
# Create the service accounts
openssl genrsa -out service-account-key.pem 4096
openssl req -new -x509 -days 365 -key service-account-key.pem -subj "/CN=test" -sha256 -out service-account.pem
# Certificates for kube-apiserver
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=test" -days 10000 -out ca.crt
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -config csr.conf
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 10000 \
-extensions v3_ext -extfile csr.conf
# Generating kubeconfig
kubectl config set-cluster local-apiserver \
--certificate-authority=certs/ca.crt \
--embed-certs=true \
--server=https://127.0.0.1:6443 \
--kubeconfig=kubeconfig
kubectl config set-credentials admin \
--client-certificate=certs/server.crt \
--client-key=certs/server.key \
--embed-certs=true \
--kubeconfig=kubeconfig
kubectl config set-context default \
--cluster=local-apiserver \
--user=admin \
--kubeconfig=kubeconfig
kubectl config use-context default --kubeconfig=kubeconfig
mkdir /root/.kube
mv kubeconfig /root/.kube/config
Getting the cluster up and running
Start the etcd server with the following command
./etcd/etcd
Start the kube-apiserver with the following command. Notice that we're passing in the etcd server url as well as the certificates we generated.
./kube-apiserver --etcd-servers http://localhost:2379 \
--service-account-key-file=certs/service-account-key.pem \
--service-account-signing-key-file=certs/service-account-key.pem \
--service-account-issuer=api \
--tls-cert-file=certs/server.crt \
--tls-private-key-file=certs/server.key \
--client-ca-file=certs/ca.crt
At this point if you run kubectl create deployment nginx --image=nginx
, the Deployment will get created. The ReplicaSet won't be created at this point however. For this we need to start the kube-controller-manager
Start the kube-controller-manager with the following
./kube-controller-manager --kubeconfig=/root/.kube/config
Now if you do kubectl get all
, you can see that the ReplicaSet is created and the pods of your deployment are stuck in Pending
state. This is because we don't have a kube-scheduler running that would schedule the pods to a node. We don't have a node either for that matter. Let's see how we can set these up.
Start the kube-scheduler like so
./kube-scheduler --kubeconfig=/root/.kube/config
At this point, if you describe your pods, you can see that it says FailedScheduling
. This is because we don't have a proper node. You can run kubectl get nodes
and see that the node, aka the machine you're running all of this on will be listed, but the status will be NotReady
. To fix this we can start a container runtime and start the kubelet process.
Run containerd
containerd
Now, start the kubelet
./kubelet --kubeconfig=kubeconfig
Now you can run kubectl get nodes
again and you can see that the Node is shown as Ready
. At this point your Pod would also be running if everything goes well.
Common errors you might face
Debugging issues with CNI
You will probably face issues with CNI when setting up containerd and kubelet for the first time. If CNI is not configured properly, you might get a bunch of errors from containerd and kubelet. The Pods will be scheduled and will go into ContainerCreating
state, but the container won't actually start because of the absence of CNI. I'm not entirely sure how CNI works. I plan to learn more about this and I'll write about what I learn here.
I installed flannel CNI
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Installing CNI packages
If you face any errors with CNI packages while running containerd, first confirm if the packages are present in the /opt/cni/bin
directory. If not, you can download and install them like so:
sudo mkdir -p /opt/cni/bin
curl -LO https://github.com/containernetworking/plugins/releases/download/v0.8.3/cni-plugins-linux-amd64-v0.8.3.tgz
sudo tar -xvf cni-plugins-linux-amd64-v0.8.3.tgz -C /opt/cni/bin/
Installing iptables
If your containerd logs says something along the lines of the following, you probably don't have the iptables
package installed in your machine. Try installing iptables and restarting containerd
and kubelet
.
Failed to destroy network for sandbox. Neither iptables nor ip6tables usable
This blog post is still evolving! I'm still learning about things like CNI etc. and I'll be continuing to update this blog post as I do. If you want to get updates on when the post is updated, you can follow me on X at @sreeramvnkitesh.