Por Cristhian Cueva
Python y AWS: Configuración de integración continua
Hoy en día, Python es el lenguaje más usado en el mundo de la programación, y a su vez, existe una gran cantidad de herramientas que podemos usar como desarrolladores. Ejemplo de ellas son Docker, Django, pip, Cloud y AWS que llevan a un nuevo nivel conceptos como Continuous Delivery, desarrollo y procesos de integración.
Este es un tutorial paso a paso en el que se presentan todas las tecnologías y lineamientos para aprender cómo construir un pequeño microservicio y prepararlo para "continuous integration" en AWS.
Con la evolución de la arquitectura de microservicios y las nuevas metodologías AgileOps, DevOps y Continuous Integration / Delivery CI/CD, la productividad ha llegado a un punto donde desarrollar software se hace cada vez más fácil.
Automatizar un pipeline de desarrollo con las herramientas correctas, hace el desarrollo y la entrega de software más rápida y sencilla.
En este artículo abordaremos el proceso desde la perspectiva de un desarrollador Python y prácticas DevOps para optimizar el proceso de entrega en el ciclo de desarrollo.
Empecemos definiendo algunos conceptos básicos que son frecuentemente utilizados pero poco entendidos… O si lo prefieres ve directo al tutorial.
Conceptos básicos
Docker
"Setup once, run anywhere"
Configurar una vez, ejecutar en cualquier lugar, es el concepto bajo el cual la comunidad ha ido adoptando esta nueva tecnología, Docker es una herramienta que nos permite correr software en casi cualquier lugar que se desee.
En el mundo del desarrollo, lanzar una aplicación suena sencillo… hasta que encontramos múltiples versiones del lenguaje Python o múltiples paquetes que usamos sin control de versiones. Es ahí donde Docker viene al rescate: Docker simplifica el proceso de desarrollo, pruebas unitarias y la entrega del software.
pip
Nos permite instalar paquetes que usaremos posteriormente.
Continuous Integration con Github CI
DevOps permite integrar el desarrollo de software con el componente de administración de sistemas; con esto en mente, el proceso resulta sencillo y eleva, tanto nuestra productividad, como la eficiencia para el usuario final.
Continuous Integration (CI), es una de las piedras angulares del proceso DevOps. La idea detrás de esto es que cuando un desarrollador agrega algo de código fuente, se entra en un proceso de CI/CD para ejecutar pruebas automatizadas y sea entregado a producción.
Existen algunas herramientas que podemos usar para implementar el proceso de CI. En nuestro caso, vamos a usar Gitlab CI, ya que nos permite integrar fácilmente nuestro flujo de trabajo en git.
Amazon Web Services (AWS)
La principal ventaja de migrar a la nube, es que nos quita un gran peso de encima, ya que al ser flexible en la asignación de recursos (CPU, memoria…), podemos escalar nuestra aplicación, desde una simple máquina, hasta un conjunto en red.
En nuestro tutorial utilizaremos los siguientes servicios de AWS:
- Amazon ECR (equivalente de un Docker Container Registry)
- Amazon ECS (permite desplegar soluciones de Docker)
Tutorial paso a paso
La meta es prepararnos para el despliegue de un microservicio con programación en Python. Los pasos a realizar son:
- Tener el microservicio listo, lo que implica “dockerizar” nuestra aplicación
- Tener la cuenta en Gitlab lista y conocer el proceso en Gitlab para integrarlo al CI/CD
- Integrar Gitlab CI para empaquetar la aplicación y desplegar en AWS
- Subir a ECR y desplegar el servicio
Prerrequisitos
Primero necesitamos crear una cuenta AWS, con la cual obtendremos todos los beneficios de la capa gratuita.
Como mencionamos, en este tutorial usaremos Amazon ECR y Amazon ECS, para lo cual necesitamos access keys para conectar los servicios.
- Después de tener la cuenta de AWS, debemos ir a la parte de Usuarios en la región us-east-1, y creamos un nuevo usuario que pueda ingresar con claves de API.
- Le asociamos las políticas:
- [Política administrada por AWS]
- AmazonEC2ContainerRegistryFullAccess
- AmazonECS_FullAccess
- [Política administrada por AWS]
- Al crear el usuario nos dará la opción de guardar las claves creadas (ID de clave de acceso y Clave de acceso secreta), las cuales usaremos después.
Siguiente paso: Como mencionamos, también necesitamos Gitlab para hospedar nuestro código y desde allí disparar los deployments, para lo cual debemos crear una cuenta en Gitlab.
Desarrollo de aplicación
Nuestra aplicación es simple, es una aplicación en FastAPI. Aquí la estructura del proyecto:
En vez de centrarnos en los detalles de cómo funciona la aplicación, nos enfocaremos desde la perspectiva de un developer, en la parte más centrada en el setup y las herramientas necesarias para la integración.
Nota: todos los ejemplos de código, están disponibles en el repositorio en Gitlab.
Hemos escogido pip y Docker como herramienta de compilación.
Nuestro archivo Dockerfile contiene una lista de comandos Docker que nos servirán para preparar nuestra imagen.
FROM python:3.9
ARG DEBIAN_FRONTEND=noninteractive
ENV LANG C.UTF-8
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["uvicorn", "main:app", "--reload", "--host=0.0.0.0", "--port=8000"]
Nota: un prerrequisito para correr aplicaciones es tener instalado Python.
- Docker nos brinda una lista de imágenes con Python instalado, escogeremos la versión de python 3.9.
- El comando FROM nos indica la imagen base en la que nos basaremos, agregamos las dependencias y las instalamos.
- Lo siguiente, es definir qué Docker correrá en el puerto 8000.
Hemos agregado docker-compose para manejar la parte del puerto, y lo publicaremos luego en un repositorio Amazon.
Con eso hemos terminado la parte de "desarrollo de la aplicación", el servicio creado aquí lo usaremos al correr el contenedor Docker.
Siguiente paso: Empecemos con la instalación y configuración de las herramientas necesarias para configurar la entrega continua.
Aplicación
Para nuestro caso, hemos escogido Gitlab CI, lo que nos permite hacer el despliegue usando boto3.
- En nuestro caso, necesitamos usar Docker.
sudo apt-get update
sudo apt-get install \
- ca-certificates \
- curl \
- gnupg \
- lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullsudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
- Lo siguiente es instalar AWS CLI. Lo que nos permite ejecutar comandos AWS y subir la imagen a ECR.
pip install awscli
- En Gitlab vamos a necesitar variables de entorno, las que generamos anteriormente para el usuario.
- Además de ello, debemos agregar un archivo gitlab-ci.yml
image: docker:stable
services:
- - docker:stable-dind
stages:
- - test
- - deploy
test:
- stage: test
- only:
- - main
- script:
- - apk add --no-cache py-pip
- - pip install docker-compose
- - docker-compose run app-test
deploy:
- stage: deploy
- only:
- - main
- variables:
- ECR: 263424986970.dkr.ecr.us-east-1.amazonaws.com/app
- STAGE: main
- script:
- - apk add --update python3 python3-dev py3-pip docker jq
- - pip install awscli --upgrade
- - aws ecr get-login --no-include-email | sh
- - docker build -f Dockerfile -t $ECR:$STAGE .
- - docker push $ECR:$STAGE
- - SERVICE_TASK_VERSION=$( aws ecs register-task-definition --family $STAGE-service --network-mode "awsvpc" --requires-compatibilities FARGATE --cpu 512 --memory 1024 --container-definitions "[{ \"image\": \"$ECR:$STAGE\", \"cpu\": 512, \"memory\": 1024, \"name\": \"main-service\", \"command\": [ \"/bin/bash\", \"-c\", \"uvicorn main:app --reload --host=0.0.0.0 --port=8000\" ], \"portMappings\": [{ \"protocol\": \"tcp\", \"containerPort\": 8000, \"hostPort\": 8000 }], \"environment\": [{ \"name\": \"v\", \"value\": \"$CI_COMMIT_SHORT_SHA\" }] }]" | jq --raw-output '.taskDefinition.revision' )
- - aws ecs update-service --cluster main --service main-service --task-definition main-service:$SERVICE_TASK_VERSION --desired-count 1
- - SERVICE_TASK_VERSION=$( aws ecs register-task-definition --family $STAGE-service --network-mode "awsvpc" --requires-compatibilities FARGATE --cpu 512 --memory 1024 --container-definitions "[{ \"image\": \"$ECR:$STAGE\", \"cpu\": 512, \"memory\": 1024, \"name\": \"main-service\", \"command\": [ \"/bin/bash\", \"-c\", \"uvicorn main:app --reload --host=0.0.0.0 --port=8000\" ], \"portMappings\": [{ \"protocol\": \"tcp\", \"containerPort\": 8000, \"hostPort\": 8000 }], \"environment\": [{ \"name\": \"v\", \"value\": \"$CI_COMMIT_SHORT_SHA\" }] }]" | jq --raw-output '.taskDefinition.revision' )
Guía de pasos en AWS
1. En AWS necesitamos crear un repositorio ECR que nos permitirá subir las imágenes Docker.
2. Además de ello necesitamos crear el servicio en ECS, para lo cual debemos crear el Clúster.
3. Necesitamos crear una definición de tarea en Fargate, también podemos configurar la memoria (0,5 GB) y la CPU (0,25 vCPU).
4. Necesitamos crear un nuevo servicio, en Fargate, necesitamos configurar ECR en Container Registry.
5. Necesitamos crear un balanceador de carga (orientado a Internet).
6. Después de crear el ALB podemos obtener en la descripción la URL que podemos usar.
7. Necesitamos crear un nuevo servicio en ECS.
8. Después de eso, lo deberíamos tener corriendo.
9. Necesitamos crear un Load Balancer y un Target Group (asociado a IP addresses).
10. Finalmente, con esto hemos terminado de construir nuestro pipeline. Después de subir nuevo código a gitlab, este será desplegado de modo que nos permita tener un pipeline funcional.
Siguientes pasos
Siempre hay más cosas para hacer. Pero una gran parte del proceso ha sido cubierta en este tutorial, considero esto sólo como un punto de partida para todo lo que queremos aprender.
Conclusión
Hemos intentado crear un proceso de desarrollo de software limpio y simple. Utilizando las herramientas disponibles, hemos creado una infraestructura que ayuda a maximizar nuestra productividad. No tenemos que preocuparnos mucho de la configuración ya que es un servicio web simple que sirve como un API REST.
Este tutorial es solo una pequeña muestra inicial de los laboratorios, prácticas y proyectos que podrás encontrar en el programa de Cloud SRE Engineer en AWS, donde aprenderás a construir canalizaciones completas y a automatizar todo el proceso de desarrollo.
Si tienes dudas consulta a un Cloud Advisor para obtener más información sobre nuestro Bootcamp o conoce todos los programas educativos AWS que tenemos para ti.