Administra la infraestructura como código con Terraform, Cloud Build y GitOps

En este instructivo, se explica cómo administrar la infraestructura como código con Terraform y Cloud Build mediante la popular metodología GitOps. Weaveworks acuñó el término GitOps y su concepto clave es usar un repositorio de Git para almacenar el estado del entorno que deseas. Terraform es una herramienta de código abierto de HashiCorp que te permite crear, cambiar y mejorar de forma predecible tu infraestructura de nube mediante código. En este instructivo, usarás Cloud Build, un servicio de integración continua de Google Cloud, para aplicar de forma automática los manifiestos de Terraform a un entorno.

Este instructivo es para desarrolladores y operadores que buscan una estrategia precisa a fin de realizar cambios de forma predecible en la infraestructura. En el artículo, se supone que estás familiarizado con Google Cloud, Linux y GitHub.

Los informes State of DevOps identificaron las capacidades que impulsan el rendimiento de la entrega de software. Este instructivo te ayudará con las siguientes funciones:

Arquitectura

Para demostrar cómo este instructivo aplica las prácticas de GitOps a fin de administrar las ejecuciones de Terraform, considera el siguiente diagrama de arquitectura. Ten en cuenta que se usan ramas de GitHub (dev y prod) para representar entornos reales. Estos entornos se definen mediante redes de nube privada virtual (VPC), dev y prod, respectivamente, en un proyecto de Google Cloud.

Infraestructura con entornos dev y prod.

El proceso se inicia cuando envías código de Terraform a la rama dev o prod. En esta situación, Cloud Build se activa y aplica los manifiestos de Terraform para lograr el estado que deseas en el entorno respectivo. Por otro lado, cuando envías código de Terraform a cualquier otra rama (por ejemplo, a una rama de características), Cloud Build se activa para ejecutar terraform plan,, pero no se aplica nada a ningún entorno.

Lo ideal es que los operadores o desarrolladores deban hacer propuestas de infraestructura a ramas no protegidas y, luego, enviarlas a través de solicitudes de extracción. La app de GitHub de Cloud Build, que se detalla más adelante en este instructivo, activa de forma automática los trabajos de compilación y vincula los informes terraform plan a estas solicitudes de extracción. De esta manera, puedes analizar y revisar los posibles cambios con los colaboradores y agregar confirmaciones de seguimiento antes de que los cambios se combinen en la rama base.

Si no surgen problemas, primero se deben combinar los cambios en la rama dev. Esta combinación activa una implementación de infraestructura para el entorno dev, lo que te permite probar este entorno. Después de realizar las pruebas y asegurarte de que la implementación es correcta, debes combinar la rama dev con la rama prod para activar la instalación de la infraestructura en el entorno de producción.

Objetivos

  • Configurar tu repositorio de GitHub.
  • Configurar Terraform para almacenar el estado en un depósito de Cloud Storage.
  • Otorgar permisos a tu cuenta de servicio de Cloud Build.
  • Conectar Cloud Build a tu repositorio de GitHub.
  • Cambiar la configuración de tu entorno en una rama de características.
  • Promover los cambios en el entorno de desarrollo.
  • Promover los cambios en el entorno de producción.

Costos

En este instructivo, se usan 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 sean aptos para obtener una prueba gratuita.

Antes de comenzar

  1. Accede a tu Cuenta de Google.

    Si todavía no tienes una cuenta, regístrate para obtener una nueva.

  2. En la página de selección de proyectos de Cloud Console, selecciona o crea un proyecto de Cloud.

    Ir a la página Selector de proyectos

  3. Comprueba que la facturación esté habilitada en tu proyecto.

    Descubre cómo puedes habilitar la facturación

  4. En Cloud Console, activa Cloud Shell.

    Activar Cloud Shell

    En la parte inferior de Cloud Console, se inicia una sesión de Cloud Shell en la que se muestra una ventana de línea de comandos. Cloud Shell es un entorno de shell que tiene el SDK de Cloud preinstalado, incluida la herramienta de línea de comandos de gcloud, y valores ya establecidos para el proyecto actual. La inicialización de la sesión puede tomar unos minutos.

  5. En Cloud Shell, obtén el ID del proyecto que acabas de seleccionar:
    gcloud config get-value project
    Si este comando no muestra el ID del proyecto, configura Cloud Shell para que use tu proyecto. Reemplaza PROJECT_ID por el ID del proyecto.
    gcloud config set project PROJECT_ID
  6. Habilita las API necesarias:
    gcloud services enable cloudbuild.googleapis.com compute.googleapis.com
    Este paso puede tardar unos minutos en finalizar.
  7. Si nunca usaste Git en Cloud Shell, configúralo con tu nombre y dirección de correo electrónico:
    git config --global user.email "your-email-address"
    git config --global user.name "your-name"
    
    Git usa esta información para identificarte como el autor de las confirmaciones que creas en Cloud Shell.

Cuando finalices este instructivo, podrás borrar los recursos creados para evitar que se te siga facturando. Para obtener más información, consulta cómo hacer una limpieza.

Configura tu repositorio de GitHub

En este instructivo, usarás un único repositorio de Git para definir tu infraestructura de nube. Debes tener diferentes ramas que correspondan a diferentes entornos para organizar esta infraestructura:

  • La rama dev contiene los cambios más recientes que se aplican al entorno de desarrollo.
  • La rama prod contiene los cambios más recientes que se aplican al entorno de producción.

Con esta infraestructura, siempre se puede hacer referencia al repositorio a fin de saber qué configuración se espera en cada entorno y combinar cambios nuevos con el entorno dev para proponerlos. Luego, debes combinar la rama dev con la rama posterior prod para promover los cambios.

Para comenzar, debes bifurcar el repositorio solutions-terraform-cloudbuild-gitops.

  1. En GitHub, dirígete a https://github.com/GoogleCloudPlatform/solutions-terraform-cloudbuild-gitops.git.
  2. En la esquina superior derecha de la página, haz clic en Bifurcar (Fork).

    Bifurca un repositorio.

    Ahora tienes una copia del repositorio solutions-terraform-cloudbuild-gitops con archivos de origen.

  3. En Cloud Shell, reemplaza your-github-username por tu nombre de usuario de GitHub para clonar este repositorio bifurcado:

    cd ~
    git clone https://github.com/your-github-username/solutions-terraform-cloudbuild-gitops.git
    cd ~/solutions-terraform-cloudbuild-gitops
    

El código de este repositorio está estructurado de la siguiente manera:

  • La carpeta environments/ contiene subcarpetas que representan entornos, como devprod, que proporcionan una separación lógica entre las cargas de trabajo en diferentes etapas de madurez, desarrollo y producción, respectivamente. Aunque se recomienda que estos entornos sean lo más similares posible, cada subcarpeta tiene su propia configuración de Terraform para garantizar que puedan tener una configuración única según sea necesario.

  • La carpeta modules/ contiene módulos de Terraform intercalados. Estos módulos representan agrupaciones lógicas de recursos relacionados y se usan para compartir código entre diferentes entornos.

  • El archivo cloudbuild.yaml es un archivo de configuración de compilación que contiene instrucciones para Cloud Build, por ejemplo, cómo realizar tareas en función de un conjunto de pasos. Este archivo especifica una ejecución condicional según la rama de la cual Cloud Build recupera el código, por ejemplo:

    • En las ramas devprod, se ejecutan los siguientes pasos:

      1. terraform init
      2. terraform plan
      3. terraform apply
    • Para cualquier otra rama, se ejecutan los siguientes pasos:

      1. terraform init para todas las subcarpetas environments
      2. terraform plan para todas las subcarpetas environments

terraform init y terraform plan se ejecutan en todas las subcarpetas environments a fin de garantizar que los cambios propuestos se mantengan para cada entorno. De esta manera, antes de combinar la solicitud de extracción, puedes revisar los planes para asegurarte de que no se otorgue acceso a una entidad no autorizada, por ejemplo.

Configura Terraform para almacenar el estado en un depósito de Cloud Storage

De forma predeterminada, Terraform almacena el estado de manera local en un archivo llamado terraform.tfstate. Esta configuración predeterminada puede dificultar el uso de Terraform para equipos, en especial cuando muchos usuarios ejecutan Terraform al mismo tiempo y cada máquina tiene su propia comprensión de la infraestructura actual.

Para ayudarte a evitar esos problemas, en esta sección, se configura un estado remoto que apunta a un depósito de Cloud Storage. El estado remoto es una característica de los backends y, en este instructivo, se configura en los archivos backend.tf, por ejemplo:

# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

terraform {
  backend "gcs" {
    bucket = "PROJECT_ID-tfstate"
    prefix = "env/dev"
  }
}

En los siguientes pasos, creas un depósito de Cloud Storage y cambias algunos archivos para que apunten a tu depósito nuevo y a tu proyecto de Google Cloud.

  1. En Cloud Shell, crea el depósito de Cloud Storage:

    PROJECT_ID=$(gcloud config get-value project)
    gsutil mb gs://${PROJECT_ID}-tfstate
    
  2. Habilita el control de versiones de objetos para mantener el historial de tus implementaciones:

    gsutil versioning set on gs://${PROJECT_ID}-tfstate
    

    Habilitar el control de versiones de objetos aumenta los costos de almacenamiento, lo cual se puede mitigar si configuras la Administración del ciclo de vida de los objetos para que borre versiones de estado antiguas.

  3. Reemplaza el marcador de posición PROJECT_ID por el ID del proyecto en los archivos terraform.tfvars y backend.tf:

    cd ~/solutions-terraform-cloudbuild-gitops
    sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/terraform.tfvars
    sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/backend.tf
    
  4. Verifica si todos los archivos se actualizaron:

    git status
    

    El resultado obtenido se verá así:

    On branch dev
    Your branch is up-to-date with 'origin/dev'.
    Changes not staged for commit:
     (use "git add <file>..." to update what will be committed)
     (use "git checkout -- <file>..." to discard changes in working directory)
           modified:   environments/dev/backend.tf
           modified:   environments/dev/terraform.tfvars
           modified:   environments/prod/backend.tf
           modified:   environments/prod/terraform.tfvars
    no changes added to commit (use "git add" and/or "git commit -a")
    
  5. Confirma y envía los cambios:

    git add --all
    git commit -m "Update project IDs and buckets"
    git push origin dev
    

    Según tu configuración de GitHub, tendrás que autenticarte para enviar los cambios anteriores.

Otorga permisos a tu cuenta de servicio de Cloud Build

Para permitir que la cuenta de servicio de Cloud Build ejecute secuencias de comandos de Terraform con el objetivo de administrar recursos de Google Cloud, debes otorgarle el acceso adecuado a tu proyecto. Para hacerlo más simple, en este instructivo, se otorga acceso al editor de proyectos. Sin embargo, cuando la función de editor de proyectos tiene un permiso de amplio rango, en entornos de producción debes seguir las prácticas recomendadas de seguridad de TI de tu empresa, para lo cual, por lo general, debes proporcionar el acceso de mínimo privilegio.

  1. En Cloud Shell, recupera el correo electrónico de la cuenta de servicio de Cloud Build de tu proyecto:

    CLOUDBUILD_SA="$(gcloud projects describe $PROJECT_ID \
        --format 'value(projectNumber)')@cloudbuild.gserviceaccount.com"
    
  2. Otorga el acceso requerido a tu cuenta de servicio de Cloud Build:

    gcloud projects add-iam-policy-binding $PROJECT_ID \
        --member serviceAccount:$CLOUDBUILD_SA --role roles/editor
    

Conecta Cloud Build directamente a un repositorio de GitHub

En esta sección, se muestra cómo instalar la app de GitHub de Cloud Build. Esta instalación te permite conectar el repositorio de GitHub con tu proyecto de Google Cloud para que Cloud Build pueda aplicar de forma automática los manifiestos de Terraform cada vez que crees una rama nueva o envíes código a GitHub.

En los siguientes pasos, se proporcionan instrucciones a fin de instalar la app solo para el repositorio solutions-terraform-cloudbuild-gitops, pero puedes elegir instalarla en más repositorios o en todos.

  1. Ve a la página GitHub Marketplace de la app de Cloud Build:

    Abrir la página de la app de Cloud Build

  2. Si es la primera vez que configuras una app en GitHub, haz clic en Configurar con Google Cloud Build. De lo contrario, haz clic en Edita tu plan, selecciona los datos de facturación y, en la página Edita tu plan, haz clic en otorgar acceso a esta app.

  3. En la página Instalar Google Cloud Build, selecciona Solo repositorios seleccionados y, luego, ingresa your-user/solutions-terraform-cloudbuild-gitops para conectarte al repositorio bifurcado.

  4. Haz clic en Instalar.

  5. Accede a Google Cloud.

    Se muestra la página Autorización. Se te solicitará que autorices la app de GitHub de Cloud Build para que se conecte a Google Cloud.

    Accede a Google Cloud

  6. Haz clic en Authorize Google Cloud Build by GoogleCloudBuild (Autorizar Google Cloud Build a través de GoogleCloudBuild).

    Se te redireccionará a Cloud Console.

  7. Selecciona el proyecto de Google Cloud en el que estás trabajando.

  8. Si aceptas los Términos y Condiciones, selecciona la casilla de verificación y, luego, haz clic en Siguiente.

  9. En el paso Selección de repositorio, selecciona your-user/solutions-terraform-cloudbuild-gitops para conectarte al proyecto de Google Cloud y, luego, haz clic en Conectar.

  10. Haz clic en Listo y, luego, en Conectar.

La app de GitHub de Cloud Build ya está configurada y el repositorio de GitHub está vinculado al proyecto de Google Cloud. Desde ahora, los cambios en el repositorio de GitHub activan ejecuciones de Cloud Build, que informan los resultados a GitHub mediante las Verificaciones de GitHub.

Cambia la configuración de tu entorno en una nueva rama de características

En este punto, ya tienes la mayor parte de tu entorno configurado. Por lo tanto, es hora de hacer algunos cambios en el código de tu entorno de desarrollo.

  1. En GitHub, dirígete a la página principal del repositorio bifurcado.
  2. Asegúrate de estar en la rama dev.

    Asegúrate de estar en la rama dev.

  3. Para abrir el archivo y editarlo, ve al archivo modules/firewall/main.tf y haz clic en el ícono de lápiz.

  4. En la línea 15, corrige el error tipográfico "http-server**2**" en el campo target_tags.

    El valor debe ser "http-server".

  5. Agrega un mensaje de confirmación en la parte inferior de la página, como “Corrección del objetivo de firewall HTTP”, y selecciona Crear una rama nueva para esta confirmación.

  6. Haz clic en Proponer cambio de archivo.

  7. En la página siguiente, haz clic en Crear solicitud de extracción para abrir una nueva solicitud de extracción con tu cambio.

    Una vez que tu solicitud de extracción esté abierta, se inicia de forma automática un trabajo de Cloud Build.

  8. Haz clic en Mostrar todas las verificaciones y espera a que la marca se vuelva verde.

    Muestra todas las verificaciones en una solicitud de extracción.

  9. Haz clic en Detalles (Details) para ver más información, incluido el resultado del terraform plan en el vínculo Ver más detalles de Google Cloud Build.

Ten en cuenta que el trabajo de Cloud Build ejecutó la canalización definida en el archivo cloudbuild.yaml. Como se detalló antes, esta canalización tiene comportamientos diferentes según la rama que se recupera. La compilación verifica si la variable $BRANCH_NAME coincide con alguna carpeta de entorno. Si es así, Cloud Build ejecuta terraform plan para ese entorno. De lo contrario, Cloud Build ejecuta terraform plan para todos los entornos a fin de garantizar que el cambio propuesto sea válido en todos ellos. Si alguno de estos planes no se ejecuta, la compilación también falla.

- id: 'tf plan'
  name: 'hashicorp/terraform:0.11.14'
  entrypoint: 'sh'
  args:
  - '-c'
  - |
      if [ -d "environments/$BRANCH_NAME/" ]; then
        cd environments/$BRANCH_NAME
        terraform plan
      else
        for dir in environments/*/
        do
          cd ${dir}
          env=${dir%*/}
          env=${env#*/}
          echo ""
          echo "*************** TERRAFOM PLAN ******************"
          echo "******* At environment: ${env} ********"
          echo "*************************************************"
          terraform plan || exit 1
          cd ../../
        done
      fi 

De manera similar, el comando terraform apply se ejecuta para las ramas de entorno, pero se ignora por completo en cualquier otro caso. En esta sección, enviaste un cambio de código a una rama nueva, por lo que no se aplicaron implementaciones de infraestructura en el proyecto de Google Cloud.

- id: 'tf apply'
  name: 'hashicorp/terraform:0.11.14'
  entrypoint: 'sh'
  args:
  - '-c'
  - |
      if [ -d "environments/$BRANCH_NAME/" ]; then
        cd environments/$BRANCH_NAME
        terraform apply -auto-approve
      else
        echo "***************************** SKIPPING APPLYING *******************************"
        echo "Branch '$BRANCH_NAME' does not represent an oficial environment."
        echo "*******************************************************************************"
      fi

Fuerza la ejecución exitosa de Cloud Build antes de combinar las ramas

Para garantizar que las combinaciones solo se puedan aplicar cuando las ejecuciones respectivas de Cloud Build sean exitosas, continúa con los siguientes pasos:

  1. En GitHub, dirígete a la página principal de tu repositorio bifurcado.
  2. En el nombre de tu repositorio, haz clic en Configuración.
  3. En el menú de la izquierda, haz clic en Ramas.
  4. En Reglas de protección de ramas, haz clic en Agregar regla.
  5. En Patrón de nombre de rama, selecciona dev.
  6. En Configuración de regla, selecciona Requerir que se aprueben las verificaciones de estado antes de la combinación y, luego, en Verificaciones de estado encontradas en la última semana en este repositorio, haz clic en Compilar.
  7. Haz clic en Crear.
  8. Repite los pasos 5 a 8, pero establece el Patrón de nombre de rama en prod.

Esta configuración es importante para proteger las ramas devprod. Es decir, las confirmaciones primero deben enviarse a otra rama y solo entonces pueden combinarse con la rama protegida. En este instructivo, la protección requiere que la ejecución de Cloud Build sea exitosa para que se permita la combinación.

Promueve los cambios en el entorno de desarrollo

Tienes una solicitud de extracción en espera para combinarse. Es momento de aplicar el estado que deseas en el entorno dev.

  1. En GitHub, dirígete a la página principal del repositorio bifurcado.
  2. Bajo el nombre del repositorio, haz clic en Solicitudes de extracción.
  3. Haz clic en la solicitud de extracción que acabas de crear.
  4. Haz clic en Merge pull request (Combinar solicitud de extracción) y, luego, en Confirmar combinación.

    Confirma la combinación.

  5. Verifica que se haya activado una Cloud Build nueva:

    Ir a la página Cloud Build

  6. Abre la compilación y verifica los registros.

    Cuando finalice la compilación, verás algo como lo siguiente:

    Step #3 - "tf apply": external_ip = external-ip-value
    Step #3 - "tf apply": firewall_rule = dev-allow-http
    Step #3 - "tf apply": instance_name = dev-apache2-instance
    Step #3 - "tf apply": network = dev
    Step #3 - "tf apply": subnet = dev-subnet-01
    
  7. Copia external-ip-value y abre la dirección en un navegador web.

    Este aprovisionamiento puede tardar unos segundos en iniciar la VM y propagar la regla de firewall, pero, cuando termine, verás Entorno: dev en el navegador web.

Promueve los cambios en el entorno de producción

Ahora que probaste por completo tu entorno de desarrollo, puedes promover tu código de infraestructura en la producción.

  1. En GitHub, dirígete a la página principal de tu repositorio bifurcado.
  2. Haz clic en Nueva solicitud de extracción.
  3. En el repositorio base, selecciona el repositorio recién bifurcado.
  4. En base, selecciona prod y, en comparar, selecciona dev.
  5. Haz clic en Crear solicitud de extracción.
  6. En título, ingresa un título como Promoting networking changes y, luego, haz clic en Crear solicitud de extracción.
  7. Revisa los cambios propuestos, incluidos los detalles del terraform plan de Cloud Build y, luego, haz clic en Combinar solicitud de extracción.
  8. Haz clic en Confirmar combinación.
  9. En Cloud Console, abre la página Historial de compilación para ver cómo se aplican los cambios al entorno de producción:

    Ir a la página Cloud Build

  10. Espera a que termine la compilación y, luego, verifica los registros.

    Al final de los registros, verás algo como esto:

    Step #3 - "tf apply": external_ip = external-ip-value
    Step #3 - "tf apply": firewall_rule = prod-allow-http
    Step #3 - "tf apply": instance_name = prod-apache2-instance
    Step #3 - "tf apply": network = prod
    Step #3 - "tf apply": subnet = prod-subnet-01
    
  11. Copia external-ip-value y abre la dirección en un navegador web.

    Este aprovisionamiento puede tardar unos segundos en iniciar la VM y propagar la regla de firewall, pero, cuando termine, verás Entorno: prod en el navegador web.

Configuraste con éxito una canalización de infraestructura como código sin servidores en Cloud Build. En el futuro, podrías probar lo siguiente:

  • Agregar implementaciones para casos prácticos independientes
  • Crear entornos adicionales para reflejar tus necesidades
  • Usar un proyecto por entorno en lugar de una VPC por entorno

Realiza una limpieza

Una vez que completaste el instructivo, borra los recursos que creaste en Google Cloud a fin de que no se te cobre por ellos en el futuro.

Borra el proyecto

  1. En Cloud Console, ve a la página Administrar recursos.

    Ir a la página Administrar recursos

  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 haz clic en Cerrar para borrar el proyecto.

Próximos pasos