Pues ahí va la historia de este script:
Resulta que queremos mover una base de datos, la típica postgresql, de una maquina virtual a un pod de kubernetes. Hay muchos factores a tener en cuenta: Si se va a hacer subida de versión, como importar una copia de la base de datos, persistencia de discos… Aunque el que más me preocupaba era el disaster recovery.
Con una VM era sencillo. Script sacado de un blog de google y adaptado al entorno GCP, cambiando el almacenamiento local por un bucket de Storage. Pero claro, la mayoría de imágenes de postgresql vienen sobre alpine, que viene con las funcionalidades más que justas para que su consumo sea mínimo.
Se me ocurrieron un par de soluciones para abordar el problema:
- Instalar cron en el archivo Dockerfile de la imagen. Importar también los scripts actuales de la VM a dicha imágen. En definitiva, emular la VM en el entorno GKE.
- Levantar una imagen google/cloud-sdk, copiar un script .sh que trabajara con snapshots de Kubernetes y ejecutarlo desde un CronJob.
Al final, la que me pareció más adecuada fue la segunda. Y es que hace poco, intentando implementar ELK, descubrí «nuevos» tipos de carga de trabajo más allá de los deployments: Los CronJobs.
Un CronJob es un tipo de carga de trabajo que permite ejecutar trabajos en una programación basada en el tiempo. Estos trabajos automatizados se ejecutan como tareas Cron en un sistema Linux o UNIX.
Este tipo de carga de trabajo, junto a la potencia del SDK de Google, que permite trabajar con cuentas de servicio de permisos limitados, hacen posible que este script funcione. Eh aquí mis archivos «mágicos»:
Dockerfile
FROM google/cloud-sdk:latest
RUN mkdir /scripts
COPY ./[service-account-key].json /scripts
COPY ./snapshot_creation.sh /scripts
RUN chmod +x /scripts/snapshot_creation.sh
CMD tail -f /dev/null
Para poder utilizar GKE de forma desatendida, es necesario utilizar una cuenta de servicio. Os dejó aquí un enlace de como activar y empezar a usar una cuenta de servicio con GCP.
Script en Bash
#VAR definition
account_service_email="[account-service-email]"
account_service_json_dir="/scripts/.[service-account-key].json"
cluster_name="[cluster-name]"
cluster_zone="[cluster-zone]"
date=$(date +%Y%m%d-%H%M)
gcp_project_name="[gcp-project-name]"
pvc_name="pvc-[id]"
snapshot_name=$date-prod-psql-snapshot
snapshot_namespace="[kubernetes-namespace]"
#Create YAML
printf "apiVersion: snapshot.storage.k8s.io/v1beta1 \n" > snapshot.yaml
printf "kind: VolumeSnapshot \n" >> snapshot.yaml
printf "metadata: \n" >> snapshot.yaml
printf " name: $snapshot_name \n" >> snapshot.yaml
printf " namespace: $snapshot_namespace \n" >> snapshot.yaml
printf "spec: \n" >> snapshot.yaml
printf " source: \n" >> snapshot.yaml
printf " persistentVolumeClaimName: $pvc_name" >> snapshot.yaml
#Create snapshot
gcloud auth activate-service-account $account_service_email --key-file=$account_service_json_dir --project=$gcp_project_name
gcloud container clusters get-credentials $cluster_name --zone $cluster_zone --project $gcp_project_name
kubectl create -f snapshot.yaml --save-config
#Remove old versions
snapshot_count=`kubectl get volumesnapshot | grep prod-psql-snapshot | wc -l`
digest_array=(`kubectl get volumesnapshot | grep prod-psql-snapshot | awk '{print $1}'`)
if [ $snapshot_count -gt 14 ]
then
oldest_snapshot=`echo ${digest_array[0]}`
kubectl delete volumesnapshot $oldest_snapshot
fi
Lo que hago con este script es generar el YAML de un VolumeSnapshot, me autentico con la cuenta de servicio y creo ese snapshot. Para dejarlo todo limpio, cuento el número de copias al finalizar, y si pasan de 14, borro las últimas. Para más info acerca de como trabajar con los VolumeSnapshots en GCP, podéis consultarlo aquí.
CronJob
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: snapshot
spec:
schedule: "01 0 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: gcr.io/[project-name]/[image_name]
args:
- /bin/sh
- -c
- sh /scripts/snapshot_creation.sh
restartPolicy: Never
Y ya estaría! Problema resuelto!