La construcción imágenes de contenedores es una fase bastante común en los ciclos de CI modernos, en los que estas imágenes son los principales artefactos que se utilizan para distribuir y ejecutar aplicaciones. Construir imágenes de la forma "tradicional", implica utilizar un demonio docker y el CLI de docker. Esta estrategia puede ser fácilmente implementada en entornos locales, donde todo lo que hace falta es un dockerfile, el código fuente y las dependencias a empaquetar.

Sin embargo, cuando hay que construir imágenes en un sistema de CI, a escala y de forma segura, no es tan fácil como hacerlo de forma local, especialmente cuando las herramientas o plataformas de CI corren en Kubernetes.
Como seguramente sabes, cada nodo de un clúster de Kubernetes ejecuta su propio container runtime, que recibe órdenes del Kubelet del nodo. Si un job de una plataforma de CI que se encarga de construir imágenes, corre dentro de un clúster de Kubernetes, necesitará acceder de alguna forma un demonio docker para construir la imagen.
Existen varias formas para hacer que esto funcione:
Exponer el demonio Docker 🔓
Es posible hacer que el demonio docker esté disponible para los contenedores que corren en el clúster exponiendo el socket de docker del nodo (/var/run/docker.sock) y montándolo como un volumen en el contenedor. Esta estrategia es bastante insegura, ya que cualquier contenedor del clúster puede acceder al demonio.
Docker in Docker 🐳
Cuando se utiliza docker-in-docker(dind), se usa una imagen que corre su propio demonio docker, y se expone para que sea utilizado. Esta estrategia es un poco más segura que el caso anterior, en el que se expone el demonio del nodo, pero aun así puede ser bastante peligrosa, ya que puede generar problemas con los Linux Security Modules (LSM) y los sistemas de ficheros de los contenedores. Si quieres leer un poco más acerca de este tema, te dejo aquí este artículo.
Dile 'no' a Docker 🤖
Quizá no sea obvio, pero la mejor manera de evitar problemas con el demonio docker es evitarlo directamente. Para ello, es posible utilizar constructores de imágenes que no necesitan un demonio (daemonless), como kaniko o buildah. Este tipo de constructores ejecuta todos los comandos del dockerfile en el espacio de usuario, sin necesidad de tener privilegios de root en el host.
En este artículo nos centraremos en kaniko, mostrando como integrarlo en diferentes plataformas de CI/CD. El código que se muestra en los diferentes ejemplos se encuentra en este repositorio.
Kaniko ⚙️📦
Kaniko fue creado por Google, como parte de las Google Containers Tools, un conjunto de herramientas para trabajar con contenedores y Kubernetes. Kaniko construye imágenes sin necesidad de un demonio docker, haciendo el proceso de construcción más seguro. Cada capa de la imagen se construye en el espacio de usuario así que no hay necesidad de disponer de privilegios de acceso para generar una imagen.
El ejecutor de kaniko (gcr.io/kaniko-project/executor) construye imágenes utilizando un dockerfile y posteriormente las almacena en un registro. Kaniko sigue estos pasos para hacerlo:
- En primer lugar, extrae el sistema de ficheros de la imagen base.
- Luego, ejecuta los comandos del dockerfile, haciendo una snapshot del sistema de ficheros resultante tras cada comando. Cada snapshot generará una nueva capa (layer).
- Tras cada comando, añade la nueva capa generada sobre la imagen base, y actualiza el metadata de la imagen.
El 'build context' 🧱
Al igual que docker, kaniko requiere un build context, que consiste en el directorio que contiene el dockerfile que kaniko utilizará y el resto de archivos que se utilizarán en el proceso de construcción. El context puede especificarse con la flag –context cuando se llama al ejecutor.
Los contexts tienen que almacenarse en lugares a los que kaniko pueda acceder. A día de hoy, se soportan las siguientes soluciones de almacenamiento:
- GCS Bucket
- S3 Bucket
- Azure Blob Storage
- Directorio local
- Fichero tar local
- Standard Input
- Repositorio git
Kaniko en Kubernetes ☸️⚙️
Kaniko puede ejecutarse en Kubernetes dentro de un pod, como puede verse en el siguiente ejemplo. En este caso, el build context es un repositorio de GitHub, y la imagen resultante se almacenará en un repositorio de DockerHub. Todos los componentes necesarios para desplegarlo, puedes encontrarlos en este repo. Si quieres probarlo, puedes hacer un fork del repo.
apiVersion: v1
kind: Pod
metadata:
name: kaniko
spec:
containers:
- name: kaniko
image: gcr.io/kaniko-project/executor:v1.7.0
args:
- "--dockerfile=./Dockerfile"
- "--context=git://github.com/mifonpe/kaniko-examples#refs/heads/main"
- "--destination=<your-repo>:<your-tag>"
env:
- name: GIT_TOKEN
valueFrom:
secretKeyRef:
name: git-token
key: GIT_TOKEN
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker/
restartPolicy: Never
volumes:
- name: docker-config
secret:
secretName: docker-regcred
items:
- key: .dockerconfigjson
path: config.json
Recuerda que para usar un repositorio privado necesitarás crear un token para que kaniko pueda tener acceso. En GitHub puedes hacerlo en settings > Developer Settings > Personal access Tokens. Crea un token con los siguientes permisos.

Una vez que el token esté listo, tendrás que crear un secreto en tu clúster utilizando el token.
kubectl create secret generic git-token --from-literal='GIT_TOKEN=<your-token>'
Pero esto es solo la mitad del trabajo, debemos proporcionarle al clúster credenciales para que pueda acceder al repositorio de imágenes. Para ello, deberás crear un secreto que contenga los valores del config.json , un fichero que almacena los credenciales y la configuración de los repositorios. Si compruebas el manifiesto del pod anterior, podrás ver como el secreto se monta como un volumen en la ruta /kaniko/.docker/config.json .
kubectl create secret docker-registry docker-regcred \
--docker-server=https://index.docker.io/v1/ \
--docker-username=<your-username> \
--docker-password=<your-password>
Por último, crea el pod.
kubectl apply -f kaniko-pod.yaml
Si compruebas los logs de este nuevo pod, podrás ver que kaniko ha construido la imagen y la ha subido al repositorio.

Ahora puedes probar tu nueva imagen. Ejecútala localmente y utiliza tu navegador para acceder al servidor web expuesto.
docker run -d -p 8082:80 <your-repo>/<your-tag>:v.0.0.1
Y voilà, tendrás un logo super chulo en tu navegador!

Kaniko en GitHub 🐙⚙️
Kaniko puede ejecutarse en GitHub actions. Para este caso, utilizaremos GitHub también como registro de imágenes. El código que verás a continuación muestra los steps de GitHub actions necesarios para construir una imagen y almacenarla en GitHub con kaniko. Este ejemplo utiliza el SHA del commit para etiquetar la imagen construida.
name: Commit
on: push
jobs:
docker:
runs-on: ubuntu-latest
name: Build docker image
steps:
- uses: actions/checkout@master
- name: Get short SHA
id: slug
run: echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)"
- name: GitHub Package Registry
uses: aevea/action-kaniko@master
with:
registry: docker.pkg.github.com
password: ${{ secrets.GITHUB_TOKEN }}
build_file: ./Dockerfile
image: nginx-kubesandclouds
tag: ${{ steps.slug.outputs.sha8 }}
cache: true
cache_registry: cache
Para poder subir imágenes al registry de GitHub, necesitarás un token con los siguientes permisos. Una vez que lo generes, tendrás que almacenar su valor como un secreto de GitHub, con el nombre GITHUB_TOKEN , para que pueda utilizarlo el pipeline de GitHub actions.
Crea el secreto y ya estará todo listo.
Si todo está bien configurado, podrás ver el pipeline de GitHub actions corriendo cuando hagas push en el repo. Una vez que termine de correr, la imagen estará disponible.

Ahora puedes comprobar tu imagen en los packages de tu repo!
Kaniko en GitLab CI 🦊⚙️
Kaniko también puede utilizarse en GitLab CI, eliminando la necesidad de utilizar servicios adicionales con docker-in-docker. Puedes utilizar las GitLab CI secret variables para almacenar las credenciales de tu repo y referenciarlas en tu script. El siguiente ejemplo almacena la imagen en el container registry del propio repo.
build:
stage: build
image:
name: gcr.io/kaniko-project/executor:v1.7.0
entrypoint: [""]
script:
- mkdir -p /kaniko/.docker
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context . --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
Al igual que para el caso de GitHub, necesitarás crear un Personal Access Token que te permita subir la imagen al container registry de GitLab. Habilita los permisos read_registry y write_registry para el token.
El siguiente paso es almacenar tu usuario de GitLab y el token que acabas de generar como variables secretas de GitLab CI. CI_REGISTRY_PASSWORD es una variable de entorno especial que está disponible dentro del pipeline y hace referencia a la dirección del registro de contenedores del repositorio.
Una vez que el pipeline se haya ejecutado, deberías poder ver tu imagen en el registro de contenedores.

Si quieres profundizar un poco más en cómo ejecutar kaniko dentro de GitLab CI, puedes consultar la documentación oficial.
Cacheo 📚⌛
Kaniko permite reducir el tiempo total para construir una imagen por medio del cacheo. Las capas que se generan con las directivas RUN y COPY definidas en el dockerfile pueden almacenarse también en repositorios. Así, cada vez que kaniko vaya a construir una capa específica, puede consultar si no ha cambiado y si se encuentra cacheada, y en caso afirmativo, la descarga.
El cacheo se activa utilizando el flag –cache=true , y el repositorio se especifica con –cache-repo . Si no se proporciona un repositorio para el cacheo, se utilizará el mismo repositorio que se emplea para almacenar las imágenes.
La siguiente imagen muestra un pipeline de GitLab CI, en el que se activó la flag cache para kaniko. Puede observarse como en primer lugar comprueba la capa cacheada previamente.

Como no se especificó un repositorio separado para el cacheo, utiliza el mismo repositorio que para la imagen, pero almacena las capas bajo la ruta /cache . Para este ejemplo específico, se utilizó AWS ECR como registro de imágenes.

Sigue aprendiendo👩💻👨💻
Si kaniko te ha parecido interesante, tómate tu tiempo para echarle un ojo a su repo en GitHub, ya que te ayudará a entender mejor todas las flags, funcionalidades y opciones para adaptarlo a tus entornos y necesidades. Puedes encontrar ejemplos de implementación aquí.
Otros Artículos
Karpenter vs Cluster Autoscaler ☸️ ⚒️
Getting the size of a Kubernetes cluster right is not an easy task, if the number of nodes provisioned is too high, resources might be…
Read MoreAutenticación a nivel de clúster para tus registries privados 🌐🔐
Using private container image registries is a common practice in the industry, as it ensures applications packaged within the images are just accessible for users…
Read MoreKaniko: construyendo imágenes sin Docker🐳🔫
Building container images is a pretty common stage within the modern CI flows, in which these images are the main artifact used to distribute and…
Read MoreKubernetes en 5 Minutos ⏱️☸️
English https://www.youtube.com/watch?v=N8LDO9pHY8I Spanish https://www.youtube.com/watch?v=TC6VkqQ835U Other Articles
Read MoreKsniff: capturando tráfico en Kubernetes 🕵️♂️📦
Troubleshooting containers in Kubernetes is a recurring topic that you may face regularly. Logging and monitoring tools come in really handy here as they help…
Read MoreHelmfile: superpoderes para Helm ☸️🔧
Helm is definitely one the most used tools in the Kubernetes world. It comes in pretty handy as it is a deployment tool, a package…
Read MoreTerraboard: Gestionando estados de Terrafom visualmente🌍🖥
Dealing with multiple terraform remote states can become a rather complex task. Besides, querying resources with terraform CLI isn’t very visual 😅. In this post…
Read Moreeksctl: el CLI para EKS ☸️ 🎮
Let’s be honest, managing Kubernetes clusters is not an easy task, even when it comes to managed Kubernetes services. Controlling the overall infrastructure, performing cluster…
Read MoreWerf: GitOps totalmente customizable 🛥️⚙️
This is the third post of a collection of GitOps tools articles. In this post, a continuous integration and continuous deployment tool for Kubernetes is…
Read More