Entrega continua tipo GitOps con Cloud Build


En esta página se explica cómo crear un flujo de procesamiento de integración y entrega continuas (CI/CD) en Google Cloud usando únicamente productos alojados y la popular metodología de GitOps.

Los ingenieros de Google llevan mucho tiempo almacenando archivos de configuración y de implementación en nuestro repositorio de código fuente principal. Esta metodología se describe en el capítulo 8 del libro Site Reliability Engineering (Beyer et al., 2016) y Kelsey Hightower lo demostró en su ponencia inaugural de Google Cloud Next '17.

Una parte fundamental de GitOps es la idea de "entornos como código": describir los despliegues de manera declarativa usando archivos (por ejemplo, archivos de manifiesto de Kubernetes) almacenados en un repositorio de Git.

En este tutorial, crearás un flujo de procesamiento de CI/CD que compila automáticamente una imagen de contenedor a partir del código confirmado, almacena la imagen en Artifact Registry, actualiza un manifiesto de Kubernetes en un repositorio de Git y despliega la aplicación en Google Kubernetes Engine (GKE) mediante ese manifiesto.

Arquitectura del flujo de procesamiento de CI/CD

En este tutorial se usan dos repositorios de Git:

  • Repositorio de la aplicación: contiene el código fuente de la aplicación.
  • Repositorio env: contiene los manifiestos de la implementación de Kubernetes.

Cuando envías un cambio al repositorio app, la canalización de Cloud Build ejecuta pruebas, crea una imagen de contenedor y la envía a Artifact Registry. Después de enviar la imagen, Cloud Build actualiza el manifiesto de Deployment y lo envía al repositorio env. De esta forma, se activa otra canalización de Cloud Build que aplica el manifiesto al clúster de GKE y, si se completa correctamente, almacena el manifiesto en otra rama del repositorio env.

Mantenemos los repositorios app y env separados porque tienen ciclos de vida y usos diferentes. Los principales usuarios del repositorio de la aplicación son personas reales y este repositorio está dedicado a una aplicación específica. Los principales usuarios del repositorio env son sistemas automatizados (como Cloud Build) y es posible que varias aplicaciones compartan este repositorio. El repositorio env puede tener varias ramas, cada una de las cuales se asigna a un entorno específico (en este tutorial solo se usa el entorno de producción) y hace referencia a una imagen de contenedor específica, mientras que el repositorio app no.

Cuando termines este tutorial, tendrás un sistema con el que podrás hacer lo siguiente fácilmente:

  • Para distinguir entre las implementaciones fallidas y las correctas, consulta el historial de Cloud Build.
  • Accede al manifiesto que se está usando actualmente consultando la rama production del repositorio env.
  • Vuelve a cualquier versión anterior volviendo a ejecutar la compilación de Cloud Build correspondiente.

Flujo del flujo de procesamiento de CI/CD

Acerca de este tutorial

En este tutorial se usa Cloud Source Repositories para alojar repositorios de Git, pero puedes obtener los mismos resultados con otros productos de terceros, como GitHub, Bitbucket o GitLab.

Esta canalización no implementa un mecanismo de validación antes de la implementación. Si usas GitHub, Bitbucket o GitLab, puedes modificar la canalización para usar una solicitud de extracción con este fin.

Aunque recomendamos Spinnaker a los equipos que quieran implementar patrones de implementación avanzados (azul/verde, análisis canary, multicloud, etc.), puede que no necesiten su conjunto de funciones para llevar a cabo una estrategia de CI/CD eficaz en organizaciones y proyectos más pequeños. En este tutorial, aprenderás a crear un flujo de procesamiento de CI/CD adecuado para aplicaciones alojadas en GKE con herramientas.

Para simplificar las cosas, en este tutorial se usa un único entorno (producción) en el repositorio env, pero puedes ampliarlo para desplegarlo en varios entornos si es necesario.

Objetivos

  • Crea repositorios de Git en Cloud Source Repositories.
  • Crea una imagen de contenedor con Cloud Build y almacénala en Artifact Registry.
  • Crea un flujo de procesamiento de CI.
  • Crea un flujo de procesamiento de CD.
  • Prueba el flujo de procesamiento de CI/CD.

Costes

En este documento, se utilizan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costes basada en el uso previsto, utiliza la calculadora de precios.

Los usuarios nuevos Google Cloud pueden disfrutar de una prueba gratuita.

Cuando termines las tareas que se describen en este documento, puedes evitar que se te siga facturando eliminando los recursos que has creado. Para obtener más información, consulta la sección Limpiar.

Antes de empezar

  1. Selecciona o crea un Google Cloud proyecto.

    Ir a Gestionar recursos

  2. Habilita la facturación de tu proyecto.

    Habilitar facturación

  3. Abre Cloud Shell para ejecutar los comandos que se indican en este tutorial. Cloud Shell es un entorno de shell interactivo para Google Cloud que te permite gestionar tus proyectos y recursos desde el navegador web.

    Ir a Cloud Shell

  4. Si el comando gcloud config get-value project no devuelve el ID del proyecto que has seleccionado, configura Cloud Shell para que use tu proyecto.

    gcloud config set project [PROJECT_ID]
    
  5. En Cloud Shell, habilita las APIs necesarias.

    gcloud services enable container.googleapis.com \
        cloudbuild.googleapis.com \
        sourcerepo.googleapis.com \
        artifactregistry.googleapis.com
    
  6. Crea un repositorio de Docker de Artifact Registry llamado my-repository en la región us-central1 para almacenar tus imágenes de contenedor.

    gcloud artifacts repositories create my-repository \
      --repository-format=docker \
      --location=us-central1
    
  7. En Cloud Shell, crea un clúster de GKE que usarás para desplegar la aplicación de ejemplo de este tutorial.

    Autopilot

    Crea un clúster de Autopilot llamado hello-cloudbuild:

    gcloud container clusters create-auto hello-cloudbuild \
        --location us-central1
    

    Estándar

    Crea un clúster Estándar de un solo nodo llamado hello-cloudbuild:

    gcloud container clusters create hello-cloudbuild \
        --num-nodes 1 --location us-central1
    
  8. Si nunca has usado Git en Cloud Shell, configúralo con tu nombre y tu dirección de correo electrónico. Git los usará para identificarte como autor de las confirmaciones que crearás en Cloud Shell.

    git config --global user.email "YOUR_EMAIL_ADDRESS"
    git config --global user.name "YOUR_NAME"
    

Cuando termines este tutorial, puedes evitar que se te siga facturando eliminando los recursos que has creado. Consulta la sección Limpieza para obtener más información.

Crear los repositorios de Git en Cloud Source Repositories

En esta sección, crearás los dos repositorios de Git (app y env) que se usan en este tutorial e inicializarás el repositorio app con código de ejemplo.

  1. En Cloud Shell, crea los dos repositorios de Git.

    gcloud source repos create hello-cloudbuild-app
    gcloud source repos create hello-cloudbuild-env
    
  2. Clona el código de ejemplo de GitHub.

    cd ~
    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    
  3. Configura Cloud Source Repositories como remoto.

    PROJECT_ID=$(gcloud config get-value project)
    git remote add google \
        "https://source.developers.google.com/p/${PROJECT_ID}/r/hello-cloudbuild-app"
    

El código que has clonado contiene una aplicación "Hello World".

from flask import Flask
app = Flask('hello-cloudbuild')

@app.route('/')
def hello():
  return "Hello World!\n"

if __name__ == '__main__':
  app.run(host = '0.0.0.0', port = 8080)

Crear una imagen de contenedor con Cloud Build

El código que has clonado contiene el siguiente Dockerfile.

FROM python:3.13-slim
RUN pip install flask
WORKDIR /app
COPY app.py /app/app.py
ENTRYPOINT ["python"]
CMD ["/app/app.py"]

Con este Dockerfile, puedes crear una imagen de contenedor con Cloud Build y almacenarla en Artifact Registry.

  1. En Cloud Shell, crea una compilación de Cloud Build basada en la última confirmación con el siguiente comando.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    COMMIT_ID="$(git rev-parse --short=7 HEAD)"
    gcloud builds submit --tag="us-central1-docker.pkg.dev/${PROJECT_ID}/my-repository/hello-cloudbuild:${COMMIT_ID}" .
    

    Cloud Build transmite los registros generados por la creación de la imagen de contenedor a tu terminal cuando ejecutas este comando.

  2. Una vez que se haya completado la compilación, comprueba que la nueva imagen de contenedor esté disponible en Artifact Registry.

    Ir a Artifact Registry

    Imagen hello-cloudbuild en Artifact Registry

Crear el flujo de procesamiento de integración continua

En esta sección, configurarás Cloud Build para que ejecute automáticamente una pequeña prueba unitaria, compile la imagen de contenedor y, a continuación, la envíe a Artifact Registry. Al enviar una nueva confirmación a Cloud Source Repositories, se activa automáticamente esta canalización. El archivo cloudbuild.yaml incluido en el código es la configuración de la canalización.

steps:
# This step runs the unit tests on the app
- name: 'python:3.13-slim'
  id: Test
  entrypoint: /bin/sh
  args:
  - -c
  - 'pip install flask && python test_app.py -v'

# This step builds the container image.
- name: 'gcr.io/cloud-builders/docker'
  id: Build
  args:
  - 'build'
  - '-t'
  - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA'
  - '.'

# This step pushes the image to Artifact Registry
# The PROJECT_ID and SHORT_SHA variables are automatically
# replaced by Cloud Build.
- name: 'gcr.io/cloud-builders/docker'
  id: Push
  args:
  - 'push'
  - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA'
  1. Abre la página Activadores de Cloud Build.

    Ir a Activadores

  2. Haz clic en Crear activador.

  3. Rellena las siguientes opciones:

    • En el campo Nombre, escribe hello-cloudbuild.
    • En Evento, selecciona Enviar a una rama.
    • En Source (Origen), selecciona hello-cloudbuild-app como Repository (Repositorio) y ^master$ como Branch (Rama).
    • En Configuración de compilación, selecciona Archivo de configuración de Cloud Build.
    • En el campo Ubicación del archivo de configuración de Cloud Build, escribe cloudbuild.yaml después de /.
  4. Haz clic en Crear para guardar el activador de compilación.

    Nota: Si necesitas crear activadores de compilación para muchos proyectos, puedes usar la API Build Triggers.

  5. En Cloud Shell, envía el código de la aplicación a Cloud Source Repositories para activar la canalización de integración continua en Cloud Build.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git push google master
    
  6. Abre la consola de Cloud Build.

    Ir a Cloud Build

    Se mostrarán las compilaciones que hayas ejecutado y completado recientemente. Puedes hacer clic en una compilación para seguir su ejecución y examinar sus registros.

Crear el flujo de procesamiento de entrega continua

Cloud Build también se usa en el flujo de procesamiento de entrega continua. La pipeline se ejecuta cada vez que se inserta una confirmación en la rama candidate del repositorio hello-cloudbuild-env. La canalización aplica la nueva versión del manifiesto al clúster de Kubernetes y, si se realiza correctamente, copia el manifiesto en la rama production. Este proceso tiene las siguientes propiedades:

  • La rama candidate es un historial de los intentos de implementación.
  • La rama production es un historial de las implementaciones correctas.
  • Tiene una vista de las implementaciones correctas y fallidas en Cloud Build.
  • Puedes volver a cualquier implementación anterior volviendo a ejecutar la compilación correspondiente en Cloud Build. Una reversión también actualiza la rama producción para reflejar con precisión el historial de los despliegues.

Modificarás la canalización de integración continua para actualizar la rama candidate del repositorio hello-cloudbuild-env, lo que activará la canalización de entrega continua.

Conceder acceso de Cloud Build a GKE

Para desplegar la aplicación en tu clúster de Kubernetes, Cloud Build necesita el rol de gestión de identidades y accesos Desarrollador de Kubernetes Engine.

Shell

En Cloud Shell, ejecuta el siguiente comando:

PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} --format='get(projectNumber)')"
gcloud projects add-iam-policy-binding ${PROJECT_NUMBER} \
    --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
    --role=roles/container.developer

Consola

  1. En la Google Cloud consola, ve a la página Permisos de GKE:

    Ve a Permisos.

  2. Define el estado del rol Desarrollador de Kubernetes Engine como Habilitar.

Inicializar el repositorio hello-cloudbuild-env

Debes inicializar el repositorio hello-cloudbuild-env con dos ramas (production y candidate) y un archivo de configuración de Cloud Build que describa el proceso de implementación.

  1. En Cloud Shell, clona el repositorio hello-cloudbuild-env y crea la rama production.

    cd ~
    gcloud source repos clone hello-cloudbuild-env
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git checkout -b production
    
  2. Copia el archivo cloudbuild-delivery.yaml disponible en el repositorio hello-cloudbuild-app y confirma el cambio.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    cp ~/hello-cloudbuild-app/cloudbuild-delivery.yaml ~/kubernetes-engine-samples/management/gitops-style-delivery/cloudbuild.yaml
    git add .
    git commit -m "Create cloudbuild.yaml for deployment"
    

    El archivo cloudbuild-delivery.yaml describe el proceso de implementación que se va a ejecutar en Cloud Build. Tiene dos pasos:

    1. Cloud Build aplica el manifiesto en el clúster de GKE.

    2. Si se realiza correctamente, Cloud Build copia el manifiesto en la rama production.

    steps:
    # This step deploys the new version of our container image
    # in the hello-cloudbuild Kubernetes Engine cluster.
    - name: 'gcr.io/cloud-builders/kubectl'
      id: Deploy
      args:
      - 'apply'
      - '-f'
      - 'kubernetes.yaml'
      env:
      - 'CLOUDSDK_COMPUTE_REGION=us-central1'
      - 'CLOUDSDK_CONTAINER_CLUSTER=hello-cloudbuild'
    
    # This step copies the applied manifest to the production branch
    # The COMMIT_SHA variable is automatically
    # replaced by Cloud Build.
    - name: 'gcr.io/cloud-builders/git'
      id: Copy to production branch
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        set -x && \
        # Configure Git to create commits with Cloud Build's service account
        git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)') && \
        # Switch to the production branch and copy the kubernetes.yaml file from the candidate branch
        git fetch origin production && git checkout production && \
        git checkout $COMMIT_SHA kubernetes.yaml && \
        # Commit the kubernetes.yaml file with a descriptive commit message
        git commit -m "Manifest from commit $COMMIT_SHA
        $(git log --format=%B -n 1 $COMMIT_SHA)" && \
        # Push the changes back to Cloud Source Repository
        git push origin production
  3. Crea una rama candidata e inserta ambas ramas para que estén disponibles en Cloud Source Repositories.

    git checkout -b candidate
    git push origin production
    git push origin candidate
    
  4. Concede el rol de IAM Escritor de repositorio de origen a la cuenta de servicio de Cloud Build para el repositorio hello-cloudbuild-env.

    PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \
        --format='get(projectNumber)')"
    cat >/tmp/hello-cloudbuild-env-policy.yaml <<EOF
    bindings:
    - members:
      - serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com
      role: roles/source.writer
    EOF
    gcloud source repos set-iam-policy \
        hello-cloudbuild-env /tmp/hello-cloudbuild-env-policy.yaml
    

Crear el activador del flujo de procesamiento de entrega continua

En esta sección, configurarás Cloud Build para que se active cuando se envíe contenido a la rama candidate del repositorio hello-cloudbuild-env.

  1. Abre la página Activadores de Cloud Build.

    Ir a Activadores

  2. Haz clic en Crear activador.

  3. Rellena las siguientes opciones:

    • En el campo Nombre, escribe hello-cloudbuild-deploy.
    • En Evento, selecciona Enviar a una rama.
    • En Source (Origen), selecciona hello-cloudbuild-env como Repository (Repositorio) y ^candidate$ como Branch (Rama).
    • En Configuration (Configuración), selecciona Cloud Build configuration file (yaml or json) (Archivo de configuración de Cloud Build [yaml o json]).
    • En el campo Ubicación del archivo de configuración de Cloud Build, escribe cloudbuild.yaml después de /.
  4. Haz clic en Crear.

Modificar la canalización de integración continua para activar la canalización de entrega continua

En esta sección, se añaden algunos pasos al flujo de procesamiento de integración continua que genera una nueva versión del manifiesto de Kubernetes y la envía al repositorio hello-cloudbuild-env para activar el flujo de procesamiento de entrega continua.

  1. Sustituye el archivo cloudbuild.yaml por el ejemplo ampliado del archivo cloudbuild-trigger-cd.yaml.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    cp cloudbuild-trigger-cd.yaml cloudbuild.yaml
    

    El archivo cloudbuild-trigger-cd.yaml es una versión ampliada del archivo cloudbuild.yaml. Añade pasos para generar el nuevo manifiesto de Kubernetes y activar el flujo de procesamiento de entrega continua.

    # This step clones the hello-cloudbuild-env repository
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Clone env repository
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        gcloud source repos clone hello-cloudbuild-env && \
        cd hello-cloudbuild-env && \
        git checkout candidate && \
        git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)')
    
    # This step generates the new manifest
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Generate manifest
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
         sed "s/GOOGLE_CLOUD_PROJECT/${PROJECT_ID}/g" kubernetes.yaml.tpl | \
         sed "s/COMMIT_SHA/${SHORT_SHA}/g" > hello-cloudbuild-env/kubernetes.yaml
    
    # This step pushes the manifest back to hello-cloudbuild-env
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Push manifest
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        set -x && \
        cd hello-cloudbuild-env && \
        git add kubernetes.yaml && \
        git commit -m "Deploying image us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:${SHORT_SHA}
        Built from commit ${COMMIT_SHA} of repository hello-cloudbuild-app
        Author: $(git log --format='%an <%ae>' -n 1 HEAD)" && \
        git push origin candidate
    
  2. Confirma las modificaciones y envíalas a Cloud Source Repositories.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git add cloudbuild.yaml
    git commit -m "Trigger CD pipeline"
    git push google master
    

    De esta forma, se activa la canalización de integración continua en Cloud Build.

  3. Examina la compilación de integración continua.

    Ir a Cloud Build

    Aparecerán las compilaciones que hayas ejecutado y completado recientemente del repositorio hello-cloudbuild-app. Puedes hacer clic en una compilación para seguir su ejecución y examinar sus registros. El último paso de esta canalización envía el nuevo manifiesto al repositorio hello-cloudbuild-env, lo que activa la canalización de entrega continua.

  4. Examina la compilación de entrega continua.

    Ir a Cloud Build

    Aparecerán las compilaciones que has ejecutado y completado recientemente del repositorio hello-cloudbuild-env. Puedes hacer clic en una compilación para seguir su ejecución y examinar sus registros.

Probar todo el flujo de procesamiento

El flujo de procesamiento de CI/CD completo ya está configurado. En esta sección, la probarás de principio a fin.

  1. Ve a la página Servicios de GKE.

    Ir a Servicios de Google Kubernetes Engine

    La lista contiene un único servicio llamado hello-cloudbuild creado por la compilación de entrega continua que se ha completado recientemente.

  2. Haz clic en el endpoint del servicio hello-cloudbuild. Aparecerá el mensaje "Hello World!". Si no hay ningún endpoint o aparece un error del balanceador de carga, es posible que tengas que esperar unos minutos para que el balanceador de carga se inicialice por completo. Si es necesario, haga clic en Actualizar para actualizar la página.

  3. En Cloud Shell, sustituye "Hello World" por "Hello Cloud Build" tanto en la aplicación como en la prueba unitaria.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    sed -i 's/Hello World/Hello Cloud Build/g' app.py
    sed -i 's/Hello World/Hello Cloud Build/g' test_app.py
    
  4. Confirma y sube el cambio a Cloud Source Repositories.

    git add app.py test_app.py
    git commit -m "Hello Cloud Build"
    git push google master
    

    De esta forma, se activará todo el flujo de procesamiento de CI/CD.

  5. Al cabo de unos minutos, vuelve a cargar la aplicación en el navegador. Aparecerá el mensaje "Hello Cloud Build!".

Probar la restauración

En esta sección, volverás a la versión de la aplicación que decía "Hello World!".

  1. Abre la consola de Cloud Build del repositorio hello-cloudbuild-env.

    Ir a Cloud Build

  2. Haz clic en la segunda compilación más reciente disponible.

  3. Haz clic en Recompilar.

  4. Cuando termine la compilación, vuelve a cargar la aplicación en el navegador. Vuelve a aparecer "Hello World!".

Limpieza

Para evitar que los recursos utilizados en este tutorial se cobren en tu cuenta de Google Cloud, elimina el proyecto que contiene los recursos o conserva el proyecto y elimina los recursos.

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Eliminar los recursos

Si quieres conservar el Google Cloud proyecto que has usado en este tutorial, elimina los recursos:

  1. Elimina los repositorios de Git locales.

    cd ~
    rm -rf ~/hello-cloudbuild-app
    rm -rf ~/hello-cloudbuild-env
    
  2. Elimina los repositorios de Git en Cloud Source Repositories.

    gcloud source repos delete hello-cloudbuild-app --quiet
    gcloud source repos delete hello-cloudbuild-env --quiet
    
  3. Elimina los activadores de Cloud Build.

    1. Abre la página Activadores de Cloud Build.

      Ir a Activadores

    2. En cada activador, haz clic en Más y, a continuación, en Eliminar.

  4. Elimina el repositorio de Docker en Artifact Registry.

    gcloud artifacts repositories delete my-repository \
        --location=us-central1
    
  5. Quita el permiso concedido a Cloud Build para conectarse a GKE.

    PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \
        --format='get(projectNumber)')"
    gcloud projects remove-iam-policy-binding ${PROJECT_NUMBER} \
        --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
        --role=roles/container.developer
    
  6. Elimina el clúster de GKE.

    gcloud container clusters delete hello-cloudbuild \
       --location us-central1
    

Siguientes pasos