GitOps: Construcción y Despliegue de Aplicaciones en Kubernetes con GitLab CI/CD y ArgoCD.
Aprenderemos cómo construir el flujo de trabajo de GitOps de principio a fin utilizando GitLab CI y ArgoCD.
GitOps es una metodología para gestionar y operar infraestructuras y aplicaciones de software utilizando prácticas de desarrollo basadas en Git. Otro concepto que me gustó mucho es la premisa de utilizar Git como única fuente de verdad para la infraestructura como código declarativa.
En Internet hay bastante información sobre los conceptos y definiciones de GitOps, sin embargo, encontrar un ejemplo práctico de este flujo de trabajo puede ser bastante complicado y a la vez abstracto. Esta es la razón por la que estoy escribiendo este articulo, espero pueda ser de ayuda para la comunidad y que la información sea útil para implementar GitOps en tu organización.
Introducción.
Antes de comenzar con las configuraciones y adentrarnos en el laboratorio, me gustaría documentar los conceptos básicos de las soluciones que estaremos utilizando.
ArgoCD: Es una herramienta para gestionar implementaciones de aplicaciones en Kubernetes de manera declarativa y automatizada, integrándose con los flujos de trabajo de GitOps.
Gitlab CI: como su nombre indica, es la herramienta de Integración Continua y Entrega Continua de GitLab.
Para este laboratorio voy a utilizar minikube, que es una distribución reducida de kubernetes. En caso que lo necesites instalar te dejo el blog de Gerardo Ocampos donde detalla el paso a paso.
Instalación de Argo CD.
Una vez que tengamos lista la instalación de Minikube, necesitamos un controlador de Ingress para acceder a la interfaz gráfica de ArgoCD. Para habilitar esto en minikube debes ejecutar el siguiente comando:
minikube addons enable ingress
Ahora usaremos Helm para la instalación y configuración de ArgoCD:
kubectl create namespace argocd
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd -n argocd --values values.yml
values.yaml:
appVersion: "1.4.2"
version: 1.8.7
server:
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
hosts:
- argocd.lab.local
Con esto deberíamos tener instalado ArgoCD.
kubectl get pods -n argocd
NAME READY STATUS
argocd-application-controller-0 1/1 Running
argocd-applicationset-controller-dc8cfcbdc-mvrjf 1/1 Running
argocd-dex-server-76cc78cfcf-825ln 1/1 Running
argocd-notifications-controller-59c46695f5-jcn8n 1/1 Running
argocd-redis-5cf79bddc4-gjhv4 1/1 Running
argocd-redis-secret-init-r4nsm 0/1 Completed
argocd-repo-server-ccc55876c-fcznm 1/1 Running
argocd-server-74f66f98b9-kwqk5 1/1 Running
Para acceder necesitamos obtener la IP del Ingress configurado en el paso anterior y también vamos a añadir una entrada en el /etc/hosts
kubectl get ingress -n argocd
NAME CLASS HOSTS ADDRESS PORTS AGE
argocd-server <none> argocd.lab.com 192.168.59.103 80 16h
echo '192.168.59.103 argocd.lab.local' | sudo tee -a /etc/hosts
Con esto, ya podremos acceder a ArgoCD desde nuestro navegador. El usuario por defecto es “admin” y la contraseña podremos obtenerlo ejecutando el siguiente comando:
export ARGOCD_PASSWORD=`kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d`
echo $ARGOCD_PASSWORD
Gitlab.
Para este laboratorio voy a usar de ejemplo una aplicación web sencilla que básicamente muestra en pantalla un mensaje de bienvenida con una imagen de fondo y el nombre del pod. Esto se encuentra alojado en mi repositorio local de gitlab.
En el directorio “manifest” se encuentran los manifiestos para desplegar nuestra aplicación en kubernetes.
manifest/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- ingress.yaml
- service.yaml
commonLabels:
app: gitops-lab
namePrefix: gitops-lab-
manifest/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitops-lab-deployment
spec:
replicas: 2
selector:
matchLabels:
app: gitops-lab
template:
metadata:
labels:
app: gitops-lab
spec:
containers:
- image: iaguilera640/gitops-lab:1.0.0
name: gitops-lab
ports:
- containerPort: 8080
name: http
manifest/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gitops-lab-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: gitops-lab.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gitops-lab-service
port:
number: 8080
manifest/service.yaml
apiVersion: v1
kind: Service
metadata:
name: gitops-lab-service
spec:
selector:
app: gitops-lab
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
Configuración ArgoCD.
Ya tenemos nuestro proyecto creado en gitlab junto con todas las configuraciones necesarias. También tenemos instalado ArgoCD, lo que debemos de hacer ahora es preparar el manifiesto para desplegar nuestra aplicación.
deploy_argocdproj.yaml
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: gitops-labs # El nombre de nuestra aplicación en argocd.
namespace: argocd # Debe ser el mismo namespace donde instalamos argocd.
spec:
project: default # Nombre del proyecto en argocd.
source:
repoURL: http://gitops.example.com/gitops/gitops-kubernetes.git
targetRevision: HEAD # rama de git.
path: manifest/ # ruta donde se almacenan los manifiestos de Kubernetes dentro del repositorio git.
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
kubectl apply -f deploy.yml
Listo, una vez ejecutado la aplicación debe estar desplegada.
Para acceder a nuestra aplicación necesitamos añadir una entrada más en el /etc/hosts con la IP del Ingress apuntando al dns de nuestro servicio.
echo '192.168.59.103 gitops-lab.local' | sudo tee -a /etc/hosts
Perfecto! Ya solo queda un último paso que sería la configuración del Pipeline de Gitlab para poder crear una nueva versión de la imagen cuando modificamos nuestra aplicación.
.gitlab-ci.yaml
stages:
- build
- push
- update-version
variables:
DOCKER_IMAGE: iaguilera640/gitops-lab
APP_VERSION: 1.0.0
services:
- docker:dind
build_image:
stage: build
image: docker:latest
script:
- docker build -t $DOCKER_IMAGE:$APP_VERSION .
only:
changes:
- app.py
push_image:
stage: push
script:
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin
- docker push $DOCKER_IMAGE:$APP_VERSION
only:
changes:
- app.py
update_version:
stage: update-version
before_script:
- docker info
script:
- |
sed -i "s|image: iaguilera640/gitops-lab:.*|image: iaguilera640/gitops-lab:$APP_VERSION|g" manifest/deployment.yaml
- git remote set-url origin "https://${GITLAB_CICD_USER}:${GITLAB_CICD_PASSWORD}@gitops.example.com/gitops/gitops-lab-isma-manifest.git"
- git config --global user.email "${GITLAB_CICD_USER}@example.com"
- git config --global user.name "${GITLAB_CICD_USER}"
- git add manifest/deployment.yaml
- git commit -m "APP $APP_VERSION"
- git push origin main
only:
changes:
- app.py
Básicamente el pipeline realiza lo siguiente:
Cada vez que se realice una modificación de nuestra aplicación “app.py” se va a crear una nueva imagen con el tag que definiremos en la variable “APP_VERSION”, posterior a la construcción de la nueva imagen se realizará el push a mi repositorio de Docker Hub. Como último paso se actualizará la versión en nuestro manifiesto deployment.yaml para que ArgoCD lo despliegue cuando se realice el commit.
Veamos un ejemplo, voy a modificar el mensaje de bienvenida:
Posterior a este cambio procederemos a hacer el commit modificando la variable de la versión:
En el apartado de “Pipelines” podremos ver el estado de cada stage.
Podemos observar nuestros cambios aplicados en ArgoCD y en mi repositorio de Docker Hub.
Y listo! Hemos implementado nuestros cambios y con esto llegamos al final del laboratorio. Espero que este tutorial pueda ayudarte a comprender mejor el flujo de GitOps y seguir aprendiendo sobre las poderosas herramientas que estuvimos estudiando y aprendiendo.