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 (IC/EC) en Google Cloud Platform mediante el uso de solo 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. El término "GitOps" fue creado por Weaveworks. 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 IC/EC que compila de forma automática una imagen de contenedor desde el código confirmado, almacena la imagen en Container Registry, actualiza un manifiesto de Kubernetes en un repositorio de Git y, luego, implementa la aplicación en Google Kubernetes Engine con ese manifiesto.

Arquitectura de la canalización de IC/EC

En este instructivo, se utilizan 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 app, la canalización de Cloud Build ejecuta las pruebas, y compila una imagen de contenedor y la envía a Container 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 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 modificarla 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 detección de fallos, múltiples nubes, etc.), es posible que su conjunto de características no sea necesario para una estrategia de IC/EC exitosa en organizaciones o proyectos más pequeños. En este instructivo, obtendrás información sobre cómo crear una canalización de IC/EC para aplicaciones alojadas en GKE con herramientas simples.

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.

Costos

Antes de comenzar

  1. Selecciona o crea un proyecto de GCP.

    IR A LA PÁGINA 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.

    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 \
        containeranalysis.googleapis.com
    
  6. En Cloud Shell, crea un clúster de GKE que usarás para implementar la aplicación de ejemplo de este instructivo.

    gcloud container clusters create hello-cloudbuild \
        --num-nodes 1 --zone us-central1-b
    
  7. 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. Para obtener más detalles, consulta Limpieza.

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/gke-gitops-tutorial-cloudbuild \
        hello-cloudbuild-app
    
  3. Configura Cloud Source Repositories como remoto.

    cd ~/hello-cloudbuild-app
    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 simple llamada "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 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, crea una imagen de contenedor con Cloud Build y guárdala en Container Registry.

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

    cd ~/hello-cloudbuild-app
    COMMIT_ID="$(git rev-parse --short=7 HEAD)"
    gcloud builds submit --tag="gcr.io/${PROJECT_ID}/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 Container Registry.

    IR A CONTAINER REGISTRY

    Imagen hello-cloudbuild en Container Registry

Crea una 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 Container 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.7-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'
  - 'gcr.io/$PROJECT_ID/hello-cloudbuild:$SHORT_SHA'
  - '.'

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

    IR A ACTIVADORES

  2. Haz clic en Crear activador.

  3. Selecciona “Cloud Source Repositories” como fuente y haz clic en Continuar.

  4. Selecciona el repositorio hello-cloudbuild-app y haz clic en Continuar.

  5. En la pantalla “Configuración de activadores”, ingresa los parámetros siguientes:

    • Nombre: hello-cloudbuild
    • Rama (regex): master
    • Configuración de compilación: cloudbuild.yaml
  6. Haz clic en Crear activador.

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

  7. 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 ~/hello-cloudbuild-app
    git push google master
    
  8. Abre la consola de Cloud Build.

    IR A CLOUD BUILD

    Deberías ver una compilación en ejecución o finalizada recientemente. Puedes hacer clic en la compilación para seguir la ejecución y examinar sus registros.

Crea una 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 propiedades siguientes:

  • 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 a Cloud Build acceso a GKE

Para implementar la aplicación en tu clúster de Kubernetes, Cloud Build necesita la función IAM del 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 Platform, abre la página Configuración (Settings) de Cloud Build:

    Abrir la página Configuración (Settings) de Cloud Build

    Verás la página Permisos de cuenta de servicio (Service account permissions):

    Captura de pantalla de la página de 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. Todavía estará vacía.

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

    cd ~/hello-cloudbuild-env
    cp ~/hello-cloudbuild-app/cloudbuild-delivery.yaml ~/hello-cloudbuild-env/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 los dos pasos siguientes:

    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_ZONE=us-central1-b'
      - '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
    

Crea 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 de Activadores de Cloud Build.

    IR A ACTIVADORES

  2. Haz clic en Agregar activador.

  3. Selecciona “Cloud Source Repositories” como fuente y haz clic en Continuar.

  4. Selecciona el repositorio hello-cloudbuild-env y haz clic en Continuar.

  5. En la pantalla “Configuración de activadores”, ingresa los parámetros siguientes:

    • Nombre: hello-cloudbuild-deploy
    • Rama (regex): candidate
    • Configuración de compilación: cloudbuild.yaml
  6. Haz clic en Crear activador.

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. Copia la versión extendida del archivo cloudbuild.yaml para el repositorio app.

    cd ~/hello-cloudbuild-app
    cp cloudbuild-trigger-cd.yaml cloudbuild.yaml
    

    cloudbuild-trigger-cd.yaml es una versión extendida del archivo cloudbuild.yaml. Este agrega los pasos que se detallan a continuación, que generan el manifiesto nuevo de Kubernetes y activan 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 gcr.io/${PROJECT_ID}/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 ~/hello-cloudbuild-app
    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

    Deberías ver una compilación en ejecución o finalizada recientemente 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

    Deberías ver una compilación en ejecución o finalizada recientemente 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

    Debes tener un solo servicio llamado hello-cloudbuild en la lista. Se creó con la implementación de entrega continua que se acaba de ejecutar.

  2. Haz clic en el extremo del servicio hello-cloudbuild. Deberías ver "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 ~/hello-cloudbuild-app
    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. Ahora deberías ver "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. Ahora deberías ver nuevamente "Hello World!".

Limpia

Sigue estos pasos para evitar que se apliquen cargos a tu cuenta de Google Cloud Platform por los recursos que usaste en este instructivo:

  1. En la GCP Console, dirígete a la página Proyectos.

    Ir a la página Proyectos

  2. En la lista de proyectos, selecciona el proyecto que deseas borrar y haz clic en Borrar.
  3. En el cuadro de diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Borra los recursos

Si deseas conservar el proyecto de GCP que utilizaste para este instructivo, borra los recursos individuales siguientes:

  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 de Activadores de Cloud Build.

      IR A ACTIVADORES

    2. Para cada activador, haz clic en los tres puntos verticales a la derecha del activador y, luego, haz clic en Borrar.

  4. Borra las imágenes en Container Registry.

    gcloud beta container images list-tags \
        gcr.io/${PROJECT_ID}/hello-cloudbuild \
        --format="value(tags)" | \
        xargs -I {} gcloud beta container images delete \
        --force-delete-tags --quiet \
        gcr.io/${PROJECT_ID}/hello-cloudbuild:{}
    
  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 \
        --zone us-central1-b
    

Qué sigue

¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Instructivos de Kubernetes Engine