Como buildar imagens customizadas do control plane do Kubernetes (e testá-las com Kind)
A postagem original desse texto pode ser encontrada nesse link: How to build custom Kubernetes control plane images (and test them with Kind) escrito por Mike Dame, Software Engineer na Google e contribuidor de vários projetos no ecosistema Kubernetes. Resolvi traduzir pois é um passo inicial importante para fazer contribuições funcionais para o Kubernetes, e também pois já me perguntaram por material desse tipo em português.
Se você está trabalhando em mudanças para um componente Kubernetes core, como por exemplo, o kube-scheduler, controller-manager ou ainda api-server, você pode estar se perguntando como testar suas mudanças localmente, ter imagens para usar, e como deployar e ver funcionando. Esse blog vai explicar exatamente isso: Como buildar e testar mudanças em componentes do control plane do Kubernetes. Podem existir formas mais eficientes de fazer a mesma coisa, mas essa abordagem possibilita mais flexibilidade e controle de cada passo.
Geralmente meu time e eu estamos desenvolvendo schedulers customizados, e por conta disso, será o foco dos passos mostrados aqui (mas., como irá perceber, tudo aqui é reproduzível com outros componentes Kubernetes).
Buildando imagens customizadas de componentes
O primeiro passo (depois que tiver feito alguma alteração de código - tente adicionar algum log novo pra ver) é rodar o seguinte comando make quick-release-images:
~/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
Esse comando coloca as imagens no seguinte caminho: ./_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
Para carregar essas imagens no docker ou podman, basta rodar:
podman load -i _output/release-images/amd64/kube-scheduler.tar
ou
docker load -i _output/release-images/amd64/kube-scheduler.tar
E em seguida basta taggear e pushar a imagem para o registry de sua escolha:
docker tag k8s.gcr.io/kube-scheduler-amd64:v1.24-XXXXXXXXXXXXXXXX quay.io/rh_ee_lseveroa/kube-scheduler:blog-test
e o push:
docker push quay.io/rh_ee_lseveroa/kube-scheduler:blog-test
Deployando sua imagem customizada em um cluster Kind
Obs:
Na verdade o Kind disponibiliza maneiras para que você builde e carregue mudanças de código para um cluster. Veja Loading an Image Into Your Cluster e Building Images.
Então esse jeito pode ser um pouco overkill, mas te deixa fazer mudanças em um cluster já rodando, ou ainda em imagens que você ja buildou.
Comece com um cluster kind padrão:
$ kind create cluster
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.24.0) 🖼
✓ 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 🙂
Ache o container onde o cluster está rodando:
$ 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
Dê um cat no manifesto estático do componente que você quer rodar de maneira customizada:
$ 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: {}
Copie e modifique a parte onde fica a imagem para que use a imagem que você buildou antes:
[...]
- --port=0
image: quay.io/rh_ee_lseveroa/kube-scheduler:blog-test
imagePullPolicy: Always
[...]
Copie o arquivo diretamente para onde está o manifesto do pod estático (troque <cluster-container-ID> pelo id que pegou antes do docker ps):
$ docker cp kube-scheduler.yaml <cluster-container-ID>:/etc/kubernetes/manifests/kube-scheduler.yaml
Deployando novas mudanças depois do primeiro carregamento
Já que esses pods de componentes core são pods estáticos, deletar o pod após pushar imagens não vai ser suficiente para que uma nova imagem seja baixada e carregada (mesmo com imagePullPolicy: Always). Então para carregar novas imagens você vai precisar entrar no shell do docker rodando o cluster e parar o container do componente (assumindo docker no seu host, e cri-o no container do kind):
$ docker exec -it <container-ID> crictl ps
$ docker exec -it <container-ID> crictl stop cfcb6d257aa59
Em seguida você vai poder ver um novo pod começando com a nova imagem sendo baixada (como mencionado anteriormente, você também pode carregar imagens diretamente no registry do kind e carregar no pod diretamente).