Python Cloud Function
Propósito y Alcance
Este proyecto es un template básico diseñado para facilitar el desarrollo y despliegue de funciones de Google Cloud Functions Generation 2 utilizando Python. El objetivo principal es proporcionar una estructura de proyecto que permita a los desarrolladores comenzar rápidamente y que sea adaptable a diversas necesidades de desarrollo en la nube.
Problema que resuelve: Simplifica la configuración inicial y la estructura de un proyecto para Google Cloud Functions Gen 2, promoviendo buenas prácticas de organización y despliegue.
Alcance:
- Proporciona una estructura de directorios base.
- Incluye un ejemplo de función HTTP simple (
main.py). - Define la gestión de dependencias (
requirements.txt). - Configuración para despliegue en múltiples entornos (dev, intg, prod) a través de archivos YAML.
- Integración con pipelines de CI/CD (GitLab CI y Jenkins).
- Configuración básica para la Cloud Function (runtime, memoria, timeout, etc.).
Este template está pensado para una función sencilla, pero puede ser adaptado para funciones más complejas o integrado con otros servicios de Google Cloud, como Pub/Sub, Firestore, etc.
Arquitectura
El proyecto está diseñado para desplegarse como una Google Cloud Function de Segunda Generación (Gen 2). Este tipo de función se ejecuta en un entorno containerizado sobre Cloud Run, lo que ofrece mayor flexibilidad y capacidades que la primera generación.
La arquitectura del proyecto se organiza de la siguiente manera:
.
├── deploy
│ ├── env
│ │ ├── dev.yaml # Variables de entorno para desarrollo
│ │ ├── intg.yaml # Variables de entorno para integración
│ │ └── prod.yaml # Variables de entorno para producción
│ └── function # Folder de configuración Cloud Function
│ ├── environment.yaml.erb # Configuración de entorno para la función (template)
│ └── runtime-config.yaml.erb # Configuración de tiempo de ejecución (template)
├── src # Folder principal del código
│ ├── main.py # Código principal de la función
│ └── requirements.txt # Dependencias de Python
├── .gitlab-ci.yml # Configuración de pipeline para GitLab CI
├── Jenkinsfile # Pipelines de integración Jenkins
└── README.md # Documentación del proyecto
El punto de entrada de la función se define en src/main.py. Las configuraciones específicas del despliegue y del
entorno de ejecución de la Cloud Function se gestionan mediante los archivos .yaml y .yaml.erb en el directorio
deploy/.
Tecnologías y Dependencias
- Lenguaje: Python 3.12 (definido en
deploy/function/runtime-config.yaml.erb) - Plataforma Cloud: Google Cloud Functions Gen 2
- Framework de Funciones:
functions-framework==3.8.1 - Librerías Principales:
MarkupSafe~=2.1.5(para escapado seguro de HTML)- (Otras dependencias se añaden en
src/requirements.txtsegún sea necesario)
- CI/CD:
- GitLab CI (configurado en
.gitlab-ci.ymlusando$LATAM_PIPELINE_CLOUD_FUNCTION_DEPLOY) - Jenkins (referenciado por
Jenkinsfile)
- GitLab CI (configurado en
- Gestión de Dependencias: Pip (a través de
requirements.txt)
Configuración Local
Prerrequisitos
- Python 3.12
- Pip (generalmente viene con Python)
- Google Cloud SDK (CLI
gcloud) instalado y configurado. - (Opcional) Un entorno virtual para Python (e.g.,
venv,conda).
Pasos de Configuración
-
Clonar el repositorio: Sigue los siguientes pasos
-
(Opcional pero recomendado) Crear y activar un entorno virtual:
python -m venv venvsource venv/bin/activate # En Linux/macOS# venv\Scriptsctivate # En Windows -
Instalar dependencias:
pip install -r src/requirements.txt -
Configurar
src/main.py: Modifica la funciónentry_point(o la función que definas como punto de entrada) ensrc/main.pysegún las necesidades de tu lógica de negocio.# src/main.pyimport functions_frameworkfrom markupsafe import escape@functions_framework.httpdef entry_point(request):"""HTTP Cloud Function.Args:request (flask.Request): The request object.Returns:The response text, or any set of values that can be turned into aResponse object using `make_response`."""request_json = request.get_json(silent=True)request_args = request.argsif request_json and "name" in request_json:name = request_json["name"]elif request_args and "name" in request_args:name = request_args["name"]else:name = "World"return f"Hello {escape(name)}!" -
Ejecutar localmente (usando Functions Framework): Para probar la función localmente, puedes usar el Functions Framework:
functions-framework --target=entry_point --source=src/main.py --port=8080 --debugLuego, puedes acceder a
http://localhost:8080en tu navegador o concurl.
Despliegue
El despliegue de la Cloud Function se gestiona a través de pipelines de CI/CD y scripts de gcloud.
Configuración de Despliegue
Los archivos clave para la configuración del despliegue son:
-
deploy/function/runtime-config.yaml.erb: Define las propiedades de ejecución de la Cloud Function.runtime: python312cpu: 1memory: 512MBtimeout: 3600sregion: "<%= region %>" # Se interpola desde archivos de entornoentry-point: entry_pointgen2: ~source: srcmax-instances: 2trigger-http:update-labels: "cmdb-<%= cmdb_it_element %>=<%= cmdb_it_element %>"service-account: "<%= service_account %>" -
deploy/function/environment.yaml.erb: Define variables de entorno que se pasarán a la función.BRANCH: "<%= branch_name %>"IS_PROD: "<%= is_prod_deploy %>"GIT_COMMIT: "<%= git_commit %>"BUILD_ID: "<%= build_id %>"TEST_ENV: "<%= test_env %>" # Ejemplo, se interpola desde archivos de entorno -
deploy/env/*.yaml: Contienen variables específicas para cada entorno (dev, intg, prod). Ejemplodev.yaml:test_env: "test in dev"region: ${{ REGION }} # Estas pueden ser variables de CI/CDservice_account: ${{ SERVICE_ACCOUNT }}project_id: ${{ PROJECT_ID }}cmdb_it_element: ${{ IT_ELEMENT }}
Pipelines de CI/CD
-
GitLab CI: El archivo
.gitlab-ci.ymlutiliza un componente de pipeline predefinido para el despliegue:include:- component: $LATAM_PIPELINE_CLOUD_FUNCTION_DEPLOYEste pipeline se encarga de los pasos de build, test, security, release, deploy, etc.
-
Jenkins: Un
Jenkinsfileestá incluido en la raíz del proyecto, sugiriendo la posibilidad de integración con Jenkins para los pipelines.
Comandos gcloud (Ejemplo Manual)
Aunque el despliegue suele ser automatizado, un comando gcloud manual para desplegar una función Gen 2 podría
parecerse a (adaptado de la configuración):
gcloud functions deploy YOUR_FUNCTION_NAME \
--gen2 \
--runtime=python312 \
--region=YOUR_REGION \
--source=./src \
--entry-point=entry_point \
--trigger-http \
--allow-unauthenticated \ # O configurar autenticación según sea necesario
--service-account=YOUR_SERVICE_ACCOUNT_EMAIL \
--memory=512MB \
--cpu=1 \
--timeout=3600s \
--max-instances=2 \
--set-env-vars=VAR1=value1,VAR2=value2 \ # Variables del environment.yaml.erb
--update-labels=cmdb-YOUR_CMDB_ELEMENT=YOUR_CMDB_ELEMENT
Las variables (YOUR_FUNCTION_NAME, YOUR_REGION, etc.) y los archivos de configuración (--env-vars-file) serían
manejados por el pipeline de CI/CD a partir de los archivos deploy/env/*.yaml y deploy/function/*.yaml.erb.
Endpoints de la API
La función de ejemplo en src/main.py expone un endpoint HTTP.
| Método | Endpoint | Descripción | Parámetros de Solicitud (JSON o Query) | Ejemplo de Solicitud (Query) | Ejemplo de Respuesta (Éxito) | Autenticación |
|---|---|---|---|---|---|---|
| GET/POST | / (relativo a la URL de la función) | Saluda al nombre proporcionado o a "World" si no se proporciona nombre. El nombre de la función forma parte de la URL base. | name (string, opcional) | curl "FUNCTION_URL?name=Usuario" | Hello Usuario! | --trigger-http (por defecto permite no autenticado si se especifica, o requiere IAM) |
Ejemplo de Solicitud (JSON con curl):
curl -X POST \
FUNCTION_URL \
-H "Content-Type: application/json" \
-d '{"name": "Mundo"}'
Respuesta:
Hello Mundo!
Nota: La URL base de la función (FUNCTION_URL) es proporcionada por Google Cloud después del despliegue.
Componentes Específicos
Este template base no incluye integraciones directas con componentes como Pub/Sub, Storage, Redis, Kafka, etc. Sin embargo, está diseñado para ser extendido fácilmente.
Para integrar con otros servicios de Google Cloud o servicios externos:
-
Añadir Dependencias: Agrega las librerías cliente necesarias al archivo
src/requirements.txt. Por ejemplo:- Para Google Cloud Storage:
google-cloud-storage - Para Google Cloud Pub/Sub:
google-cloud-pubsub
- Para Google Cloud Storage:
-
Modificar
src/main.py: Implementa la lógica para interactuar con el servicio deseado dentro de tu función.# Ejemplo conceptual para interactuar con Storagefrom google.cloud import storagedef interact_with_storage():storage_client = storage.Client()bucket = storage_client.bucket("my-bucket-name")blob = bucket.blob("my-file.txt")# ... realizar operaciones con el blob ... -
Configuración de Entorno: Si es necesario, añade variables de entorno (nombres de buckets, topics, etc.) en los archivos
deploy/function/environment.yaml.erbydeploy/env/*.yaml. -
Permisos: Asegúrate de que la cuenta de servicio (
service-accountespecificada enruntime-config.yaml.erb) asociada a la Cloud Function tenga los permisos IAM necesarios para interactuar con los otros servicios.
Pruebas
Este template no incluye un framework de pruebas configurado por defecto (como pytest o unittest). Se recomienda
añadir pruebas unitarias y de integración según las necesidades del proyecto.
Pasos Sugeridos para Añadir Pruebas:
-
Elegir un Framework de Pruebas: Por ejemplo,
pytest.- Añadir
pytestasrc/requirements.txt(o a unrequirements-dev.txt).
- Añadir
-
Escribir Pruebas: Crear un directorio
tests/en la raíz del proyecto o dentro desrc/y escribir los archivos de prueba.# ejemplo tests/test_main.py# from src.main import entry_point # Ajustar import según estructura# import flask# def test_entry_point_no_name():# req = flask.Request.from_values() # Simulación básica# # Mockear get_json y args si es necesario para una prueba más robusta# # O usar el cliente de prueba de Flask con Functions Framework# response = entry_point(req)# assert response == "Hello World!"# def test_entry_point_with_name_query():# req = flask.Request.from_values(args={'name': 'TestUser'})# response = entry_point(req)# assert response == "Hello TestUser!" -
Ejecutar Pruebas:
python -m pytest -
Integración con CI/CD: Añadir un paso en el pipeline de CI/CD (
.gitlab-ci.ymloJenkinsfile) para ejecutar las pruebas automáticamente.
Consideraciones de Seguridad
- Permisos de Cuenta de Servicio: La cuenta de servicio especificada en
deploy/function/runtime-config.yaml.erb(service_account) debe seguir el principio de moindre privilège (menor privilegio). Otorgar solo los roles y permisos IAM estrictamente necesarios para que la función opere. - Acceso a la Función:
- Para funciones HTTP, considera si deben ser de acceso público (
--allow-unauthenticated) o si deben requerir autenticación IAM. - Para funciones activadas por eventos (e.g., Pub/Sub, Storage), la autenticación es manejada por el servicio que invoca.
- Para funciones HTTP, considera si deben ser de acceso público (
- Manejo de Secretos: Evita hardcodear secretos (API keys, contraseñas) en el código. Utiliza variables de entorno
(configuradas a través de
environment.yaml.erby los archivosenv/*.yaml, cuyos valores pueden provenir de un gestor de secretos en el pipeline de CI/CD) o Google Secret Manager. - Dependencias: Mantén las dependencias en
src/requirements.txtactualizadas y revisa regularmente por vulnerabilidades conocidas. - Validación de Entrada: Para funciones HTTP, valida y sanea todas las entradas del usuario (e.g.,
request.args,request.get_json()) para prevenir vulnerabilidades como XSS (aunqueMarkupSafeayuda con el escapado en la salida en el ejemplo). - Registro y Monitoreo: Configura un logging adecuado para auditoría y depuración. Utiliza Cloud Logging y Cloud Monitoring.