If you are working on changes to a core Kubernetes component such as the scheduler, controller-manager, or apiserver, you may be confused about how to actually build and deploy your changes in a local environment to test them. This post will go over the method I use to build and test control plane component changes. There are probably more efficient ways to do it, but this works for me and gives me some flexibility over each step which I will explain.
I am usually developing custom schedulers, so that will be the focus for these steps (though as you will see, it pretty much applies to any kube control-plane pod). So this post may be particularly useful if you are also working on a custom scheduler, or plugins for the scheduler, or are simply trying to debug the default kube-scheduler.
Building control plane component images
The first step (after making any code changes) is to run make quick-release-images
from the root of the k8s.io/kubernetes repository.
This will build release images for all the core components:
~/go/src/k8s.io/kubernetes$ make quick-release-images +++ [1103 14:32:11] Verifying Prerequisites.... +++ [1103 14:32:11] Using Docker for MacOS +++ [1103 14:32:12] Building Docker image kube-build:build-841902342a-5-v1.15.2-1 +++ [1103 14:32:18] Syncing sources to container +++ [1103 14:32:21] Running build command... +++ [1103 14:33:25] Building go targets for linux/amd64: cmd/kube-apiserver cmd/kube-controller-manager cmd/kube-scheduler cmd/kube-proxy vendor/github.com/onsi/ginkgo/ginkgo test/e2e/e2e.test cluster/images/conformance/go-runner cmd/kubectl +++ [1103 14:34:31] Syncing out of container +++ [1103 14:34:40] Building images: linux-amd64 +++ [1103 14:34:40] Starting docker build for image: kube-apiserver-amd64 +++ [1103 14:34:40] Starting docker build for image: kube-controller-manager-amd64 +++ [1103 14:34:40] Starting docker build for image: kube-scheduler-amd64 +++ [1103 14:34:40] Starting docker build for image: kube-proxy-amd64 +++ [1103 14:34:40] Building conformance image for arch: amd64 +++ [1103 14:35:05] Deleting docker image k8s.gcr.io/kube-scheduler-amd64:v1.20.0-beta.1.5_c82d5ee0486191-dirty +++ [1103 14:35:08] Deleting docker image k8s.gcr.io/kube-proxy-amd64:v1.20.0-beta.1.5_c82d5ee0486191-dirty +++ [1103 14:35:15] Deleting docker image k8s.gcr.io/kube-controller-manager-amd64:v1.20.0-beta.1.5_c82d5ee0486191-dirty +++ [1103 14:35:15] Deleting docker image k8s.gcr.io/kube-apiserver-amd64:v1.20.0-beta.1.5_c82d5ee0486191-dirty +++ [1103 14:35:24] Deleting conformance image k8s.gcr.io/conformance-amd64:v1.20.0-beta.1.5_c82d5ee0486191-dirty +++ [1103 14:35:24] Docker builds done
And it places them as individual .tar files in ./_output/release-images/amd64
:
$ ls _output/release-images/amd64/ total 817M drwxr-xr-x 7 mdame staff 224 Nov 3 14:35 . drwxr-xr-x 3 mdame staff 96 Nov 3 14:34 .. -rw-r--r-- 1 mdame staff 288M Nov 3 14:35 conformance-amd64.tar -rw------- 2 mdame staff 159M Nov 3 14:35 kube-apiserver.tar -rw------- 2 mdame staff 150M Nov 3 14:35 kube-controller-manager.tar -rw------- 2 mdame staff 130M Nov 3 14:35 kube-proxy.tar -rw------- 2 mdame staff 63M Nov 3 14:35 kube-scheduler.tar
These can be loaded into docker, tagged, and pushed to your registry of choice:
$ docker load -i _output/release-images/amd64/kube-scheduler.tar 46d8985b3ff2: Loading layer [==================================================>] 60.37MB/60.37MB Loaded image: k8s.gcr.io/kube-scheduler-amd64:v1.20.0-beta.1.5_c82d5ee0486191-dirty $ docker tag k8s.gcr.io/kube-scheduler-amd64:v1.20.0-beta.1.5_c82d5ee0486191-dirty docker.io/mdame/kube-scheduler:test $ docker push docker.io/mdame/kube-scheduler:test The push refers to repository [docker.io/mdame/kube-scheduler] 46d8985b3ff2: Pushed c12e92a17b61: Layer already exists 79d541cda6cb: Layer already exists printconfig: digest: sha256:c093dad1f4a60ce1830d91ced3a350982b921183c1b4cd9bfaae6f70c67af7ee size: 949
Deploying your custom image into a Kind cluster
(Note: Kind actually provides ways for you to build and load code changes into a cluster, see https://kind.sigs.k8s.io/docs/user/quick-start/#loading-an-image-into-your-cluster and https://kind.sigs.k8s.io/docs/user/quick-start/#building-images. So this may be overkill, but allows you to make changes to an already-running cluster or with images you’ve already built).
Start up a stock Kind cluster and get the current static pod manifest for the kube-scheduler (use docker ps
to get the running cluster’s container, then the static pod manifests are located at /etc/kubernetes/manifests
):
$ kind create cluster Creating cluster "kind" ... β Ensuring node image (kindest/node:v1.19.1) πΌ β Preparing nodes π¦ β Writing configuration π β Starting control-plane πΉοΈ β Installing CNI π β Installing StorageClass πΎ Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community π $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b4f712eff358 kindest/node:v1.19.1 "/usr/local/bin/entrβ¦" 8 minutes ago Up 8 minutes 127.0.0.1:58206->6443/tcp kind-control-plane $ docker exec -it b4f712eff358 cat /etc/kubernetes/manifests/kube-scheduler.yaml apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: component: kube-scheduler tier: control-plane name: kube-scheduler namespace: kube-system spec: containers: - command: - kube-scheduler - --authentication-kubeconfig=/etc/kubernetes/scheduler.conf - --authorization-kubeconfig=/etc/kubernetes/scheduler.conf - --bind-address=127.0.0.1 - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true - --port=0 image: k8s.gcr.io/kube-scheduler:v1.19.1 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 8 httpGet: host: 127.0.0.1 path: /healthz port: 10259 scheme: HTTPS initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 15 name: kube-scheduler resources: requests: cpu: 100m startupProbe: failureThreshold: 24 httpGet: host: 127.0.0.1 path: /healthz port: 10259 scheme: HTTPS initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 15 volumeMounts: - mountPath: /etc/kubernetes/scheduler.conf name: kubeconfig readOnly: true hostNetwork: true priorityClassName: system-node-critical volumes: - hostPath: path: /etc/kubernetes/scheduler.conf type: FileOrCreate name: kubeconfig status: {}
Copy this and edit it to refer to my custom scheduler image (truncated here):
$ cat kube-scheduler.yaml apiVersion: v1 kind: Pod ... spec: containers: - command: - kube-scheduler - --authentication-kubeconfig=/etc/kubernetes/scheduler.conf - --authorization-kubeconfig=/etc/kubernetes/scheduler.conf - --bind-address=127.0.0.1 - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true - --port=0 image: docker.io/mdame/kube-scheduler:test imagePullPolicy: Always ...
Copy this manifest into the Kind cluster, overwriting the existing manifest:
$ docker cp kube-scheduler.yaml <cluster-container-ID>:/etc/kubernetes/manifests/kube-scheduler.yaml
And now the running pod should refer to your image:
$ kubectl get -o yaml pod/kube-scheduler-kind-control-plane -n kube-system apiVersion: v1 kind: Pod metadata: ... name: kube-scheduler-kind-control-plane namespace: kube-system ... spec: containers: - command: - kube-scheduler - --authentication-kubeconfig=/etc/kubernetes/scheduler.conf - --authorization-kubeconfig=/etc/kubernetes/scheduler.conf - --bind-address=127.0.0.1 - --kubeconfig=/etc/kubernetes/scheduler.conf - --leader-elect=true - --port=0 image: docker.io/mdame/kube-scheduler:test imagePullPolicy: Always
Deploying changes to the image
If you need to deploy changes to the running pod, repeat the make quick-release-images
and docker load/tag/push
steps from above. Your new image won’t be pulled just by deleting the pod even with imagePullPolicy: Always
set, because these are static pods mirroring a running container. So, to pull a new image you need to exec back into the cluster and stop the running container:
$ docker exec -it <container-ID> crictl ps CONTAINER IMAGE CREATED STATE NAME ATTEMPT POD ID cfcb6d257aa59 0c3dd0a038e11 47 minutes ago Running kube-scheduler 7 83163425eb615 ... ... ... $ docker exec -it <container-ID> crictl stop cfcb6d257aa59 cfcb6d257aa59
And you should see a new pod start with your image pulled (as mentioned above, you could also load the changes directly into Kind’s registry and pull from there).