Entrega continua tipo GitOps con Cloud Build


En esta página, se explica cómo crear una canalización de integración y entrega continuas (CI/CD) en Google Cloud con productos alojados y la metodología popular de GitOps.

Desde hace mucho tiempo, los ingenieros de Google almacenan los archivos de configuración y de implementación en nuestro repositorio principal de código fuente. Esta metodología se describe en el libro ingeniería de confiabilidad de sitios, capítulo 8 (Beyer et al., 2016), y Kelsey Hightower la demostró en su presentación de Google Cloud Next '17.

Una parte clave de GitOps es la idea de “entornos como código”, que describen tus implementaciones de forma declarativa con archivos (por ejemplo, los manifiestos de Kubernetes) almacenados en un repositorio de Git.

En este instructivo, crearás una canalización de CI/CD que compila una imagen de contenedor desde el código confirmado, la almacena en Artifact Registry, actualiza un manifiesto de Kubernetes en un repositorio de Git y, luego, implementa la aplicación en Google Kubernetes Engine (GKE) con ese manifiesto de forma automática.

Arquitectura de la canalización de IC/EC

En este instructivo, se usan los dos repositorios de Git siguientes:

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

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

Los repositorios app y env deben estar separados porque tienen diferentes ciclos de vida y usos. Los usuarios principales del repositorio app son personas reales, y este repositorio está destinado para una aplicación específica. Los usuarios principales del repositorio env son sistemas automatizados (como Cloud Build), y varias aplicaciones pueden compartir este repositorio. El repositorio env puede tener varias ramas y cada una se asigna a un entorno específico (en este instructivo, solo utilizas la rama de producción), y hace referencia a una imagen de contenedor específica, mientras que el repositorio app, no.

Cuando termines este instructivo, obtendrás un sistema en el que fácilmente podrás realizar las siguientes acciones:

  • Diferenciar las implementaciones fallidas y correctas mediante una observación del historial de Cloud Build.
  • Acceder al manifiesto utilizado en este momento mediante la observación de la rama production del repositorio env.
  • Revertir a cualquier versión anterior mediante una nueva ejecución de la compilación correspondiente de Cloud Build

Flujo de la canalización de IC/EC

Acerca de este instructivo

En este instructivo se utiliza Cloud Source Repositories para alojar los 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 a fin de usar una solicitud de extracción para este fin.

Si bien recomendamos Spinnaker a los equipos que quieren implementar patrones de implementación avanzada (azul/verde, análisis de versiones canary, múltiples nubes, etc.), es posible que su conjunto de atributos no sea necesario para una estrategia de CI/CD exitosa en organizaciones y proyectos más pequeños. En este instructivo, aprenderás cómo crear una canalización de CI/CD para aplicaciones alojadas en GKE con herramientas.

Para que este instructivo sea más simple, se utiliza solo el entorno de producción en el repositorio env, pero puedes ampliarlo para implementar varios entornos, en caso necesario.

Objetivos

  • Crear repositorios de Git en Cloud Source Repositories
  • Crear una imagen de contenedor con Cloud Build y almacenarla en Artifact Registry
  • Crear una canalización de CI
  • Crear una canalización de CD
  • Probar la canalización de CI/CD

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

  1. Selecciona o crea un proyecto de Google Cloud.

    Ir a Administrar recursos

  2. Habilita la facturación para tu proyecto.

    Habilitar facturación

  3. Abre Cloud Shell para ejecutar los comandos detallados en este instructivo. Cloud Shell es un entorno de shell interactivo para Google Cloud que te permite administrar proyectos y recursos desde el navegador web.

    Ir a Cloud Shell

  4. Si el comando gcloud config get-value project no muestra el ID del proyecto que acabas de seleccionar, configura Cloud Shell para que use tu proyecto.

    gcloud config set project [PROJECT_ID]
    
  5. En Cloud Shell, habilita las API requeridas.

    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 implementar la aplicación de ejemplo de este instructivo.

    Autopilot

    Crea un clúster de Autopilot con el nombrehello-cloudbuild:

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

    Estándar

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

    gcloud container clusters create hello-cloudbuild \
        --num-nodes 1 --region us-central1
    
  8. Si nunca usaste Git en Cloud Shell, configúralo con tu nombre y dirección de correo electrónico. Git usará esos datos para identificarte como el 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 finalices este instructivo, puedes borrar los recursos creados para evitar que se te siga facturando. Consulta Realiza una limpieza para obtener más información.

Crea repositorios de Git en Cloud Source Repositories

En esta sección, crearás dos repositorios de Git (app y env) utilizados en este instructivo y, luego, inicializarás app con algún 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 desde 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 clonaste 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)

Crea una imagen de contenedor con Cloud Build

El código que clonaste ya contiene el siguiente Dockerfile.

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

Con este Dockerfile, puede crear una imagen de contenedor con Cloud Build y guardarla 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 que generó la creación de la imagen de contenedor a tu terminal cuando ejecutas este comando.

  2. Cuando se complete la compilación, verifica que la imagen de contenedor nueva esté disponible en Artifact Registry.

    Ir a Artifact Registry

    Imagen hello-cloudbuild en Artifact Registry

Crea la canalización de integración continua

En esta sección, configurarás Cloud Build para ejecutar automáticamente una prueba de unidades pequeñas, compilar la imagen de contenedor y enviarla a Artifact Registry. Enviar una confirmación nueva a Cloud Source Repositories 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.12-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. Completa las siguientes opciones:

    • En el campo Nombre, escribe hello-cloudbuild.
    • En Evento, selecciona Enviar a una rama.
    • En Fuente, selecciona hello-cloudbuild-app como tu repositorio y ^master$ como la rama.
    • En Configuración de compilación, selecciona Cloud Build configuration file.
    • 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.

    Sugerencia: Si necesitas crear activadores de compilación para muchos proyectos, puedes usar la API de 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 IC 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

    Aparecerán las compilaciones ejecutadas y las que finalizaron recientemente. Puedes hacer clic en la compilación para seguir la ejecución y examinar sus registros.

Crea la canalización de entrega continua

Cloud Build también se usa para la canalización de entrega continua. La canalización se ejecuta cada vez que se envía una confirmación a la rama candidate del repositorio hello-cloudbuild-env. La canalización aplica la versión nueva del manifiesto al clúster de Kubernetes y, si lo hace 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.
  • En Cloud Build, tienes una vista de las implementaciones fallidas y correctas.
  • Puedes revertir cualquier implementación anterior si vuelves a ejecutar la compilación correspondiente en Cloud Build. Una reversión también actualiza la rama production para reflejar verdaderamente el historial de implementaciones.

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

Otorga acceso de GKE a Cloud Build

Para implementar la aplicación en el clúster de Kubernetes, Cloud Build necesita la función de administración de identidades y accesos de desarrollador de Kubernetes Engine.

Shell

En Cloud Shell, ejecuta el comando siguiente:

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

Console

  1. En la consola de Google Cloud, abre la página Configuración de Cloud Build:

    Abre Configuración de Cloud Build

    Aparecerá la página Permisos de la cuenta de servicio.

    Captura de pantalla de la página Permisos de la cuenta de servicio

  2. Configura el estado de la función Desarrollador de Kubernetes Engine como Habilitar.

Inicializa 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 ejecutará en Cloud Build. Este tiene dos pasos:

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

    2. Si lo hace correctamente, Cloud Build lo copia 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 la rama candidate y envía ambas ramas para que estén disponibles en Cloud Source Repositories.

    git checkout -b candidate
    git push origin production
    git push origin candidate
    
  4. Otorga la función de IAM del escritor del repositorio de código fuente 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
    

Cómo crear un activador para la canalización de entrega continua

En esta sección, configurarás Cloud Build para que se active con un envío 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. Completa las siguientes opciones:

    • En el campo Nombre, escribe hello-cloudbuild-deploy.
    • En Evento, selecciona Enviar a una rama.
    • En Fuente, selecciona hello-cloudbuild-env como tu repositorio y ^candidate$ como la rama.
    • En Configuración de compilación, selecciona 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.

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

En esta sección, agregarás algunos pasos a la canalización de integración continua que generará una versión nueva del manifiesto de Kubernetes y lo enviará al repositorio hello-cloudbuild-env para activar la canalización de entrega continua.

  1. Reemplaza el archivo cloudbuild.yaml por el ejemplo extendido en el archivo cloudbuild-trigger-cd.yaml.

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

    cloudbuild-trigger-cd.yaml es una versión extendida del archivo cloudbuild.yaml. Se agregan pasos para generar el nuevo manifiesto de Kubernetes y activar la canalización 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. Confirme las modificaciones y envíelas 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
    

    Esto 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 ejecutadas recientemente y finalizadas para el repositorio hello-cloudbuild-app. Puedes hacer clic en la compilación para seguir la ejecución y examinar sus registros. El último paso de esta canalización envía el manifiesto nuevo al repositorio hello-cloudbuild-env, que activa la canalización de entrega continua.

  4. Examina la compilación de entrega continua.

    Ir a Cloud Build

    Aparecerán tus compilaciones recientes y en ejecución para el repositorio hello-cloudbuild-env. Puedes hacer clic en la compilación para seguir la ejecución y examinar sus registros.

Prueba la canalización completa

La canalización de IC/EC ahora está configurada. En esta sección, la probarás de extremo a extremo.

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

    Ir a Servicios de Google Kubernetes Engine

    La lista contiene un solo servicio llamado hello-cloudbuild creado por la compilación de entrega continua que se compiló recientemente.

  2. Haz clic en el extremo del servicio hello-cloudbuild. Se muestra “Hello, World!”. Si no existe el extremo, o si ves un error del balanceador de cargas, es posible que debas esperar algunos minutos para que el balanceador de cargas se inicialice por completo. Haz clic en Actualizar para que la página se actualice, si es necesario.

  3. En Cloud Shell, reemplaza “Hello World” por “Hello Cloud Build” en la aplicación y en la prueba de unidades.

    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 envía el cambio a Cloud Source Repositories.

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

    Esto activa la canalización de IC/EC completa.

  5. Después de unos minutos, vuelve a cargar la aplicación en tu navegador. Se muestra “Hello Cloud Build!”.

Prueba la reversión

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

  1. Abre la consola Cloud Build para el repositorio hello-cloudbuild-env.

    Ir a Cloud Build

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

  3. Haz clic en Volver a compilar.

  4. Cuando se termine la compilación, vuelve a cargar la aplicación en tu navegador. Vuelve a aparecer “Hello World!”.

Limpia

Para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo, borra el proyecto que contiene los recursos o conserva el proyecto y borra los recursos individuales.

  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.

Borra los recursos

Si deseas conservar el proyecto de Google Cloud que usaste en este instructivo, borra los recursos individuales:

  1. Borra los repositorios de Git locales.

    cd ~
    rm -rf ~/hello-cloudbuild-app
    rm -rf ~/hello-cloudbuild-env
    
  2. Borra 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. Borra los activadores de Cloud Build.

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

      Ir a Activadores

    2. Para cada activador, haz clic en Más  y, luego, en Borrar.

  4. Borra el repositorio de Docker en Artifact Registry.

    gcloud artifacts repositories delete my-repository \
        --location=us-central1
    
  5. Quita el permiso otorgado a Cloud Build para conectarte 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. Borra el clúster de GKE.

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

Qué sigue