En estos días de fiesta, sin prisa pero sin pausa, me he montado una pequeña demo para contarte lo que es Kustomize, una herramienta que forma parte de kubectl desde la versión 1.14, que nos permite personalizar nuestros despliegues, pensando en diferentes entornos/aplicaciones, sin la necesidad de utilizar plantillas. Me han dicho que «se me da muy mal no hacer nada», así que en este artículo te cuento cómo usarlo 😃
Aplicación de ejemplo: un WordPress
Vamos a imaginar que tenemos el siguiente escenario, donde queremos desplegar un WordPress, el cual consta de un MySQL:
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: mysql
spec:
containers:
- image: mysql:5.6
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: wp_password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-storage
emptyDir: {}
y su servicio:
apiVersion: v1
kind: Service
metadata:
name: wordpress-mysql
labels:
app: wordpress
spec:
ports:
- port: 3306
selector:
app: wordpress
tier: mysql
clusterIP: None
y un WordPress:
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
labels:
app: wordpress
spec:
selector:
matchLabels:
app: wordpress
tier: frontend
replicas: 2
strategy:
type: Recreate
template:
metadata:
labels:
app: wordpress
tier: frontend
spec:
containers:
- image: wordpress:4.8-apache
name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
value: wp_password
ports:
- containerPort: 80
name: wordpress
volumeMounts:
- name: wordpress-storage
mountPath: /var/www/html
volumes:
- name: wordpress-storage
emptyDir: {}
y su servicio:
apiVersion: v1
kind: Service
metadata:
name: wordpress
labels:
app: wordpress
spec:
ports:
- port: 80
selector:
app: wordpress
tier: frontend
type: LoadBalancer
Ahora bien, lo que me gustaría hacer es que, sin tocar estos archivos pudiera personalizarlos, para adecuarlos al entorno donde los quiera desplegar, ya sea desarrollo, qa, producción, etcétera. De esta forma siempre tendría unos manifiestos base y otros que complementan o reemplazan de estos lo que consideremos, y justamente para eso es para lo que queremos Kustomize.
Estructura del repositorio
Para que quede lo más ordenado posible, la estructura que va a tener mi repositorio es la siguiente:
Como ves, tenemos una carpeta base donde están los archivos que te acabo de mostrar, y dos carpetas, dev y prod, con las diferencias sobre la base para estos entornos.
En el directorio base, además de los despliegues y los servicios mostrados, hay un archivo adicional dentro de este llamado kustomization.yaml el cual únicamente contiene un listado de todos los recursos que debería de tener en cuenta esta herramienta:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- mysql-deployment.yaml
- mysql-service.yaml
- wordpress-deployment.yaml
- wordpress-service.yaml
La forma en la que organices tu proyecto es indiferente, ya que haces mención siempre a los archivos que están involucrados, pero a nivel visual es más sencillo tener algo de este estilo.
Personalización para dev
Ahora vamos a fijarnos en la carpeta dev donde vamos a tener, de nuevo, un archivo kustomization.yaml que en este caso tendrá lo siguiente:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev
commonLabels:
env: dev
namePrefix: dev-
bases:
- ../base
secretGenerator:
- name: dbpassword
literals:
- "password=Passw0rd"
resources:
- namespace.yaml
patchesStrategicMerge:
- mysql-deployment-patch.yaml
- wordpress-deployment-patch.yaml
Este se encarga de recoger todos los cambios para el entorno, que para este caso son los siguientes:
- Gracias a la propiedad namespace estoy diciendo que, sin tocar ningún archivo base, todos los elementos que se vayan a generar estén dentro de dev. Como este es posible que no exista, en el apartado resources he añadido un YAML que lo genera.
- También he utilizado commonLabels para añadir etiquetas comunes a todos los objetos del entorno.
- namePrefix me permite utilizar un prefijo para todo lo que cree en este entorno (también existe nameSufix)
- bases: se utiliza para indicar dónde están los manifiestos de partida, que son los que vamos a alterar. También se pueden utilizar URLs de repositorios git.
- secretGenerator: me permite generar los secretos que necesito para este entorno. Aquí estoy utilizando literals pero puedes también indicar un archivo con texto plano o .env, como puedes ver en el kustomization de prod.
- Por último, el apartado patchesStrategicMerge son el nombre de los archivos que contienen mis cambios respecto a los objetos base.
Para que este ejemplo no se haga eterno, los cambios en mysql-deployment-patch.yaml y wordpress-deployment-patch.yaml son los mismos, y es el cambio del valor de la contraseña de la base de datos por la que he generado aquí:
apiVersion: apps/v1
kind: Deployment
metadata:
name: wordpress
spec:
template:
spec:
containers:
- name: wordpress
env:
- name: WORDPRESS_DB_HOST
value: dev-wordpress-mysql
- name: WORDPRESS_DB_PASSWORD
value: ""
valueFrom:
secretKeyRef:
name: dbpassword
key: password
Como ves, la forma de definir los cambios es usando simplemente la misma estructura que ya conocemos, en este caso de un Deployment, pero sin que tengas que incluir aquellas propiedades que no quieres que cambien.
Para ver el resultado sin tener ejecutarlo sobre tu clúster puedes utilizar el siguiente comando:
kustomize build dev
Por ejemplo, en el despliegue del MySQL puedes ver todos estos cambios:
Si estás conforme con lo que has visto, para aplicarlos, en lugar de usar kubectl apply -f, utilizamos el siguiente comando:
kubectl apply -k dev
Básicamente una k en lugar de una f 😊 Si ahora te fijas en el namespace dev verás que todo lo que has personalizado, incluido el propio namespace, está en su sitio:
El código del ejemplo lo tienes en mi GitHub.
¡Saludos!

Bootcamp DevOps
Si tienes ganas de meterte en el área de DevOps, formo parte del
equipo de docentes del Bootcamp DevOps Lemoncode, ¿Te animas a
aprender con nosotros?