# Un pipeline simple, pero efectivo. 😉

## Introducción

Hace tiempo que no actualizo los proyectos DevOps del Blog, así que vamos a crear algo sencillo pero eficaz para aquellos que están haciendo sus primeras armas en este mundo.

Les comparto este [proyecto](https://github.com/safernandez666/cicd_app) para que puedan desplegarlo fácilmente en sus entornos. Un pipeline completo. Explicaremos cómo crear una imagen Docker y subirla a Docker Hub, utilizar un Helm Chart y modificarlo, automatizar estos procesos con GitHub Actions, para finalmente desplegarlo en nuestro Kubernetes via ArgoCD.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723645549066/f2f3088e-72f9-4a3d-9093-b9d81bfac83c.png align="center")

Aca el contenido del proyecto, pero tranquilos que vamos a ir paso a paso.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723645952934/1b1ede28-9074-4662-a2fe-afc6bacccc20.png align="center")

## Creación de Imagen Docker

Vamos a crear local nuestra imagen docker, solo para probar que el Dockerfile está correcto y luego correrla en el puerto 8080.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723646550910/cde6fc9b-7a50-4f2a-9af7-6e8dcb5301c0.png align="center")

¡Todo perfecto!

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723646582027/84418fc0-1035-44cd-867c-761ff4f64051.png align="center")

Repasamos los comandos.

```bash
# Construccion de Imagen, debemos estar en la carpeta app donde encontramos el Dockerfile
docker build . -t app
# Listar Imagenes
docker images
# Levantar el Contenedor 
docker run -it --rm -d -p 8080:80 app
# Status del Contenedor
docker ps
```

El primer paso está correcto. ¡Seguimos!

## Despliegue en Kubernetes

Vamos a revisar los manifiestos y desplegar. En este caso ya tenemos la imagen en **DockerHub**, para esta prueba, algo que luego hara **GitHub Actions** no solo sera el build, si no también el push al registro.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723647515892/07ceab3d-6cbf-4a6e-a9d3-8f179388fcc2.png align="center")

Vamos a repasar los comandos.

```bash
# Creo el Namespace app
kubectl create namespace app
# Aplico los manifiestos
kubectl apply -f .
# Revisamos el Ingress
kubectl get ingress -n app
# Listamos los artefactos del Namespace app
kubectl get all -n app
```

¿Le pegamos una mirada a cada manifiesto, dentro de k8s/manifest?

Primero el `deployment.yaml` que referencia a la imagen subida a **DockerHub**.

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: app
  labels:
    app: app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
      - name: app
        image: safernandez666/app # Cambiar por tu imagen en DockerHub
        ports:
        - containerPort: 80
```

Segundo el `service.yaml`.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: app
  namespace: app
  labels:
    app: app
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
  selector:
    app: app
  type: ClusterIP
```

Y por último el `ingress.yaml`, que referencia la FQDN que queremos resolver.

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app
  namespace: app
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: app.esprueba.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: app
            port:
              number: 80
```

**⚠️ Es necesario que tengamos desplegado ingress en nuestro cluster.**

La resolucion, para esta prueba, del FQDN la hice modificando mis registro local apuntando a mi cluster de **Kubernetes**. Dependiendo de la plataforma se hace de diferente manera, si estás tocando Clusters seguramente sepas hacerlo. Si no sabes me avisas 📞 y te guio.

## CI con GitHub Actions

Vamos al corazón de esta automatización. El manifiesto de **GitHub Actions**. Antes que nada vamos a sumar los secretos de **DockerHub** en las variables. Para ello en tu repositorio, debes hacer lo siguiente.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723648360461/f4870a59-ee1c-4284-8f21-2429297b5ad2.png align="center")

Vamos a cargar las variables de inicio para el login en **DockerHub**, una vez cargados los secretos, vamos modificar los permisos de **GITHUB\_TOKEN** para poder escribir, desde la **GitHub Actions** en el YAML del Helm Chart.

![fix GitHub bot permission](https://www.raulmelo.me/_vercel/image?url=https%3A%2F%2Fcdn.sanity.io%2Fimages%2Fgc3hakk3%2Fproduction%2F8b5476684f1dfe262c1d8c0abe8b9fca7124311a-1220x1381.png%3Fw%3D1220%26h%3D1381%26auto%3Dformat&w=1280&q=100 align="left")

Ahora si revisamos el Workflow que se puede generar desde acá.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723648458297/d42377c7-3d31-4370-8426-010eca5120fa.png align="center")

```yaml
name: Build and Push Docker Image to Docker Hub

on: 
  push:
    branches:
      - master
    paths-ignore: # Si realizamos cambios, aqui, no se dispara el pipeline.
      - 'k8s/**'
      - 'README.md'

jobs:
  build-and-update: # Construccion de la Imagen & Push en DockerHub
    name: Build Image and Update Helm Chart
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repository
        uses: actions/checkout@v4

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
        # Agregamos el TAG para subir no solo latest, si no tambien la version.
      - name: Extract version number from GIT SHA
        id: extract_sha
        run: echo "BUILD_TAG=${GITHUB_SHA::7}" >> $GITHUB_ENV

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: app/
          push: true
          tags: safernandez666/app:latest, safernandez666/app:${{ env.BUILD_TAG }}

      - name: Update tag in Helm Chart # Cambiamos por el numero de version el values.yaml
        run: |
          sed -i 's/tag: .*/tag: "${{ env.BUILD_TAG }}"/' app-chart/values.yaml

      - name: Commit and push changes
        run: |
          git config --global user.email "santiagoagustinfernandez@gmail.com"
          git config --global user.name "Santiago Fernandez"
          git add app-chart/values.yaml
          git commit -m "Update tag to '${{ env.BUILD_TAG }}' after building image"
          git push
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Usando el token para autenticar el push
```

Perfecto, vamos a realizar un cambio para ver como interactua. Modificamos el archivo `values.yaml` para actualizar la etiqueta con el número de versión después de construir la imagen. Ahora ponemos latest solo para ver el cambio 😎.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723649194084/3bfb23b4-8baa-4657-a76e-da20ff6a7f6d.png align="center")

¡Hacemos el commit!

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723649260464/ed80b629-21f4-4c17-ab03-edf29b29c06c.png align="center")

¡Excelente!

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723649349098/84ede56f-10c4-442a-809c-5e6cf5f3fd44.png align="center")

Se está realizando el Build y Subiendo a **DockerHub**. ¡Miremos le Job a detalle! Se modifico el archivo `app-chart/values.yaml` con el `tag:4044587`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723649515619/9b75fd6a-9ba9-4899-9b13-d078bca8966a.png align="center")

Revisemos el archivo del Helm Chart y el registro.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723649693419/07a538f7-3621-46b9-afd1-b8ff0e28660b.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723649733142/34c25dcf-aeda-402c-8a03-1faa2d9c168e.png align="center")

Todo está funcionando como queremos. Ahora solo nos queda instalar **ArgoCD** para desplegar la versión que deseamos.

## CD con ArgoCD

¿Que es ArgoCD? No es ni más ni menos que una herramienta de despliegue continuo que nos permite automatizar el despliegue y la gestión de sus aplicaciones utilizando Git como una única fuente de verdad

### Instalación de ArgoCD

Creamos el Namespace y descargamos los manifiestos.

```yaml
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
```

Ahora nos queda, para facilitar el ingreso, cambiar el servicio por LoadBalancer.

```yaml
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
kubectl get svc argocd-server -n argocd
```

Obtenemos la dirección IP y por último necesitar el password para el usuario *admin*, con este comando podemos conocerlo.

```yaml

kubectl get svc argocd-server -n argocd
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723659631437/e1084049-6725-4709-b226-89dba7f77629.png align="center")

## Configuración de ArgoCD

Conectamos nuestro repositorio de **GitHub**.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723659835945/fb3cd0dc-ce28-46fa-bf45-ff7f443b4ce3.png align="center")

Creamos un proyecto donde configuramos la fuente y con qué Cluster vamos a trabajar.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723659738204/18f1c2d3-d37b-44e5-8857-769d484fcab7.png align="center")

Ahora el paso final, crear la aplicación con algunas configuraciones básicas.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723659976399/ba31b98b-3706-4059-ab17-d3e64b963c25.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723660292518/234abd00-767b-4615-b853-d2ebf814304b.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723660485571/609c6442-8ce8-44d1-97e8-266d93ad4887.png align="center")

¡Uala! Ya tenemos el aplicativo sincronizado.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723660551733/59d1878d-f67f-467d-b9af-306445279405.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1723660570072/cc38daab-27a1-4db4-b124-fd7d4a5e288f.png align="center")

Cada vez que **ArgoCD** detecte un cambio en el repositorio hará el despliegue del Helm Chart, con la última versión de build que generamos.

Te dejo un video, para que lo veas en acción.

%[https://youtu.be/D4A3TzFBQPg] 

Espero que te sirva para comenzar en el mundo DevOps.

Saludos.
