Configurando GitHub Actions – Bot con AWS Lambda: P6

← Back to blog

Esta serie de posts consta de 8 entregas, siendo esta la sexta en donde vamos a usar GitHub actions para automatizar el provisionamiento de recursos con Terraform y la publicación de nuevas versiones de nuestra lambda. Los otros posts en la serie abordan a su vez un aspecto muy específico del problema, puedes encontrarlos aquí:

  • Configurando Twitter y AWS - Parte 1
  • Programando la lambda con Python - Parte 2
  • Mejorando el mapa con GeoPandas - Parte 3
  • Creando la lambda en un contenedor - Parte 4
  • Infraestructura con Terraform - Parte 5
  • Automatización con GitHub Actions - Parte 6
  • Agregando pruebas con Pytest - Parte 7
  • Optimizando Docker - Parte 8

Viene la parte de la automatización en GitHub – a través de un pipeline de CI/CD, lo primero que vamos a hacer es crear un archivo llamado aws.yml en la carpeta .github/workflows, como la extensión lo sugiere es un archivo que sigue el formato YAML.

Lo primero que vamos a especificar es el nombre del pipeline y las condiciones bajo las que se debe ejecutar:

name: Build and deploy lambda-cycles image

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
# Job definitions

Por el momento quiero que el pipeline se ejecute cada vez que alguien pone algo en main y cada vez que alguien abre una pull request hacia main.

Lo siguiente es definir los jobs que forman parte del flujo de trabajo – en este caso tendremos dos: uno para preparar nuestra aplicación y la otra para publicarla.

Preparar la imagen – build

Para definir un trabajo debemos especificar los pasos (steps) que lo forman, individualmente puedes especificar un nombre más amigable y el tipo de runner en el que se ejecuta. Nosotros no usaremos nada complicado, así que ubuntu-latest nos funciona bien.

  build:
    name: Build
    runs-on: ubuntu-latest
    steps:

El siguiente paso es especificar los pasos (o steps, valga la redundancia) que forman parte de este job.

Pasos

Necesitamos obtener una copia de nuestro código recién pusheado a main, usamos la action checkout:

    - name: Checkout
      uses: actions/checkout@v2

Como vamos a interactuar con AWS necesitamos configurar las credenciales en el runner, Amazon mantiene una action para esto, lo que debemos especificar son nuestras credenciales (que previamente establecimos como secretos en nuestro repo).

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: eu-west-1

Este par de pasos es solo para mi implementación, y es que estoy usando pipenv, así que toca instalar Python, luego pipenv e instalamos las dependencias:

    - name: Set up Python 3.8
      uses: actions/setup-python@v1
      with:
        python-version: 3.8

    - name: Install pipenv
      run: |
        pip install pipenv
        pipenv install

Los siguientes tres pasos tienen que ver con la creación de la imagen que usaremos para crear la instancia de nuestra lambda.

El primer paso llama a make container, la utilidad que añadí en en posts pasados para construir una imagen con la etiqueta lambda-cycles. El segundo paso exporta esta imagen a un archivo comprimido. El tercer paso almacena la imagen de Docker recién exportada como un artefacto, y es que la usaremos en el siguiente job, el del despliegue.

    - name: Build lambda-cycles image
      run: make container

    - name: Pack docker image
      run: docker save lambda-cycles > ./lambda-cycles.tar

    - name: Temporarily save Docker image and dependencies
      uses: actions/upload-artifact@v2
      with:
        name: lambda-cycles-build
        path: |
          ./shapefiles/
          ./requirements.txt
          ./lambda-cycles.tar
        retention-days: 1

Debemos configurar, inicializar y por último planear la creación de la infraestructura en terraform, para lo primero, Hashicorp también nos ofrece una acción pre-definida, para las siguientes dos tareas con que ejecutemos la herramienta de consola terraform basta:

    - name: Set up terraform
      uses: hashicorp/setup-terraform@v1

    - name: Terraform init
      run: terraform -chdir=terraform init

    - name: Terraform plan
      run: terraform -chdir=terraform plan

Crear infraestructura en AWS

Una vez que GitHub Actions terminó el trabajo build, podemos seguir con el de deploy. Para definir este trabajo (además de la información de nombre y runner) le indico que depende del trabajo build y muy importante, que solo debe ejecutarse cuando la rama que va a ejecutar este trabajo es la rama main, ojo a la instrucción if.

	deploy:
    name: Deploy
    runs-on: ubuntu-latest
    needs: build
    if: github.ref == 'refs/heads/main'

    steps:

Pasos

Lo de siempre, obtenemos una copia del código con actions/checkout@v2:

    - name: Checkout
      uses: actions/checkout@v2

Volvemos a configurar nuestras credenciales, recuerda que cada trabajo se ejecuta en un runner diferente:

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: eu-west-1

¿Recuerdas que en el job anterior creamos un artefacto llamado lambda-cycles-build que contenía una imagen de Docker y unas dependencias más? – pues ahora vamos a descargarlo, y después de eso usaremos docker load para importar la imagen y que esté disponible para ser usada por docker.

    - name: Retrieve saved Docker image
      uses: actions/download-artifact@v2
      with:
        name: lambda-cycles-build
        path: ./

    - name: Docker load
      run: docker load < ./lambda-cycles.tar

Nuevamente configuramos terraform, lo inicializamos y por último aplicamos los cambios, fíjate que estamos usando la opción -auto-approve para que los cambios sean aprobados automáticamente sin necesidad de interacción humana.

    - name: Set up terraform
      uses: hashicorp/setup-terraform@v1

    - name: Terraform init
      run: terraform -chdir=terraform init

    - name: Terraform apply
      run: terraform -chdir=terraform apply -auto-approve

Así es como se ve el repositorio al terminar este post.

Recuerda que me puedes encontrar en Twitter en @feregri_no para preguntarme sobre este post – si es que algo no queda tan claro o encontraste un typo. El código final de esta serie está en GitHub y la cuenta que tuitea el estado de la red de bicicletas es @CyclesLondon