En el desarrollo de software moderno, los microservicios han surgido como una arquitectura fundamental, que permite la escalabilidad, la flexibilidad y la gestión eficiente de sistemas complejos.

Los microservicios son aplicaciones pequeñas e independientes que realizan tareas específicas, permitiendo un despliegue y escalado flexibles. Este enfoque modular del diseño de software flexibiliza el acoplamiento entre componentes, mejorando la flexibilidad y la capacidad de gestión a lo largo del desarrollo.

El artículo proporciona una visión general de los microservicios, su funcionalidad y su creación utilizando Python. También muestra cómo desplegar tus microservicios en Kinsta utilizando un Dockerfile.

¿Qué Son los Microservicios?

Los microservicios son servicios independientes y autónomos dentro de una aplicación, cada uno de los cuales aborda necesidades empresariales específicas. Se comunican a través de API ligeras o brokers de mensajes, formando un sistema integral.

A diferencia de los sistemas monolíticos que escalan totalmente en función de la demanda, los microservicios permiten escalar componentes individuales de alto tráfico. Esta arquitectura facilita la gestión de fallos y las actualizaciones de funciones, contrarrestando las limitaciones monolíticas.

El uso de microservicios tiene varias ventajas, como:

  • Flexibilidad y escalabilidad — Desvincular servicios individuales te permite aumentar el número de nodos que ejecutan una instancia de un servicio concreto que experimenta un tráfico elevado.
  • Modularidad del código — Cada servicio puede utilizar un stack tecnológico distinto, lo que significa que puedes elegir las mejores herramientas de desarrollo para cada uno.

Sin embargo, algunas dificultades acompañan a las arquitecturas de microservicios:

  • Supervisión de múltiples servicios — Supervisar los servicios individuales de un sistema se convierte en un reto a medida que las instancias de un servicio concreto se despliegan y distribuyen por varios nodos. Esta dificultad es especialmente evidente durante los fallos de red u otros problemas del sistema.
  • Coste —Desarrollar aplicaciones de microservicios puede ser significativamente más caro que construir sistemas monolíticos, debido a los costes asociados a la gestión de múltiples servicios. Cada servicio requiere su propia infraestructura y recursos, lo que puede resultar costoso, sobre todo cuando se amplía el sistema.

Cómo Diseñar un Microservicio con Python

Ahora que conoces las ventajas de utilizar una arquitectura de microservicios, es hora de construir una con Python.

Para este ejemplo, supongamos que quieres construir una aplicación web de comercio electrónico. El sitio web tiene varios componentes, como el catálogo de productos, una lista de pedidos y un sistema de procesamiento de pagos y registros, cada uno de los cuales necesitas implementar como un servicio independiente. Además, necesitas establecer un método de comunicación de servicio a servicio para transferir datos entre estos servicios, como HTTP, de forma eficiente.

Construyamos un microservicio utilizando Python para gestionar un catálogo de productos. El microservicio obtendrá los datos de los productos de una fuente especificada y los devolverá en formato JSON.

Requisitos Previos

Para seguir este tutorial, asegúrate de que tienes

1. Crea Tu Proyecto

  1. Para empezar, crea una carpeta para tu proyecto llamada flask-microservice y el directorio actual en el directorio del proyecto.
  2. A continuación, ejecuta python3 --version para confirmar que Python está instalado correctamente en tu ordenador.
  3. Instala virtualenv para crear un entorno de desarrollo aislado para el microservicio Flask ejecutando el siguiente comando:
    pip3 install virtualenv
  4. Crea un entorno virtual ejecutando lo siguiente:
    virtualenv venv
  5. Por último, activa el entorno virtual utilizando uno de los siguientes comandos en función del sistema operativo de tu ordenador:
    # Windows: 
    .\venv\Scripts\activate
    # Unix or macOS:
    source venv/bin/activate

2. Configurar un Servidor Flask

En el directorio root, crea un archivo requirements.txt y añade estas dependencias.

flask
requests

Ejecuta el programa pip3 command on your terminal to install the dependencies.

pip install -r requirements.txt

A continuación, crea una nueva carpeta en el directorio raíz y llámala services. Dentro de esta carpeta, crea un nuevo archivo, products.py, y añade el código que aparece a continuación para configurar un servidor Flask.

import requests
import os
from flask import Flask, jsonify
app = Flask(__name__)
port = int(os.environ.get('PORT', 5000))

@app.route("/")
def home():
    return "Hello, this is a Flask Microservice"
if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=port)

En el código anterior, se configura un servidor Flask básico. Inicializa una aplicación Flask, define una única ruta para la URL raíz ("/"), y cuando se accede a ella, muestra el mensaje "Hello, this is a Flask Microservice". El servidor se ejecuta en un puerto especificado, obtenido a partir de una variable de entorno o por defecto en el puerto 5000, y se inicia en modo depuración, lo que lo prepara para gestionar las peticiones entrantes.

3. Define los Endpoints de la API

Con el servidor configurado, crea un endpoint de API para un microservicio que obtenga datos de productos de una API disponible públicamente. Añade este código al archivo products.py:

BASE_URL = "https://dummyjson.com"
@app.route('/products', methods=['GET'])
def get_products():
    response = requests.get(f"{BASE_URL}/products")
    if response.status_code != 200:
        return jsonify({'error': response.json()['message']}), response.status_code
    products = []
    for product in response.json()['products']:
        product_data = {
            'id': product['id'],
            'title': product['title'],
            'brand': product['brand'],
            'price': product['price'],
            'description': product['description']
        }
        products.append(product_data)
    return jsonify({'data': products}), 200 if products else 204

El código anterior crea un endpoint /products en el servidor Flask. Cuando se accede a él mediante una solicitud GET, obtiene datos de productos de una API ficticia. Si tiene éxito, procesa los datos recuperados, extrae los detalles del producto y devuelve la información en formato JSON. En caso de error o de que no haya datos disponibles, responde con un mensaje de error y un código de estado adecuados.

4. Prueba el Microservicio

Llegados a este punto, has configurado con éxito un microservicio sencillo. Para iniciar el servicio, pon en marcha el servidor de desarrollo, que comenzará a ejecutarse en http://localhost:5000.

flask --app services/products run

A continuación, puedes realizar una solicitud GET al endpoint /products utilizando el cliente Postman, deberías ver una respuesta similar a la de la captura de pantalla siguiente.

Solicitud GET correcta al endpoint del producto API ficticio en Postman
Probando la petición HTTP API GET en Postman.

Cómo Implementar la Autenticación y la Autorización en un Microservicio Python

Al construir microservicios, es importante implementar medidas de seguridad robustas, como la autenticación y la autorización. Asegurar tu microservicio garantiza que sólo los usuarios autorizados puedan acceder al servicio y utilizarlo, protegiendo los datos sensibles y evitando ataques maliciosos.

Un método eficaz para implementar la autenticación y autorización seguras en microservicios son los Tokens Web JSON (JWTs).

JWT es un estándar abierto ampliamente utilizado que proporciona una forma segura y eficaz de transmitir información de autenticación entre clientes y servidores. Son tokens compactos, encriptados y firmados digitalmente que pasas junto a las peticiones HTTP. Cuando incluyes un JWT con cada solicitud, el servidor puede verificar rápidamente la identidad y los permisos de un usuario.

Para implementar la autenticación JWT en el microservicio, haz lo siguiente:

  1. Añade el paquete pyjwt de Python a tu archivo requirements.txt y reinstala las dependencias utilizando pip install -r requirements.txt.
  2. Como el servicio no tiene una base de datos dedicada, crea un archivo users.json en el directorio root de tu proyecto para almacenar una lista de usuarios autorizados. Pega el código siguiente en el archivo:
    [
        {   
            "id": 1,
            "username": "admin",
            "password": "admin"
        
        }
    ]

  3. A continuación, en tu archivo services/products.py, sustituye las sentencias import por lo siguiente:
    import requests 
    from flask import Flask, jsonify, request, make_response
    import jwt
    from functools import wraps
    import json
    import os
    from jwt.exceptions import DecodeError

    Estás importando estos módulos para gestionar solicitudes HTTP, crear una aplicación Flask, gestionar datos JSON, implementar la autenticación basada en JWT y gestionar excepciones, lo que permite una amplia gama de funciones dentro del servidor Flask.

  4. Añade el siguiente código debajo de la creación de la instancia de la app Flask para generar una clave secreta que se utilizará para firmar los tokens JWT.
    app.config['SECRET_KEY'] = os.urandom(24)
  5. Para verificar los JWT, crea una función decoradora y añade el siguiente código por encima de las rutas API en el código de tu servidor Flask. Esta función decoradora autenticará y validará a los usuarios antes de que accedan a las rutas protegidas.
    def token_required(f):
        @wraps(f)
        def decorated(*args, **kwargs):
            token = request.cookies.get('token')
            if not token:
                return jsonify({'error': 'Authorization token is missing'}), 401
            try:
                data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
                current_user_id = data['user_id']
            except DecodeError:
                return jsonify({'error': 'Authorization token is invalid'}), 401
            return f(current_user_id, *args, **kwargs)
        return decorated

    Esta función decoradora comprueba las peticiones HTTP entrantes en busca de un token de autorización JWT, que debe estar en las cabeceras de la petición o en las cookies. Si falta el token o no es válido, el decorador envía un mensaje unauthorized status code como respuesta.

    Por el contrario, si hay un token válido, el decorator extrae el ID de usuario tras descodificarlo. Este proceso salvaguarda los endpoints protegidos de la API concediendo acceso sólo a los usuarios autorizados.

  6. Define un endpoint de la API para la autenticación de usuarios utilizando el siguiente código.
    with open('users.json', 'r') as f:
        users = json.load(f)
    @app.route('/auth', methods=['POST'])
    def authenticate_user():
        if request.headers['Content-Type'] != 'application/json':
            return jsonify({'error': 'Unsupported Media Type'}), 415
        username = request.json.get('username')
        password = request.json.get('password')
        for user in users:
            if user['username'] == username and user['password'] == password:
                token = jwt.encode({'user_id': user['id']}, app.config['SECRET_KEY'],algorithm="HS256")
                response = make_response(jsonify({'message': 'Authentication successful'}))
                response.set_cookie('token', token)
                return response, 200
        return jsonify({'error': 'Invalid username or password'}), 401

    Para autenticar y autorizar a los usuarios, el endpoint de la API /auth comprueba las credenciales del payload JSON de la solicitud POST con la lista de usuarios permitidos. Si las credenciales son válidas, genera un token JWT utilizando el ID del usuario y la clave secreta de la aplicación, y establece el token como una cookie en la respuesta. Ahora los usuarios pueden utilizar este token para realizar posteriores solicitudes a la API.

    Tras crear el endpoint /auth, utiliza Postman para enviar una solicitud HTTP POST a http://localhost:5000/auth. En el cuerpo de la solicitud, incluye las credenciales del usuario administrador simulado que has creado.

    Solicitud Postman mostrando el cuerpo de la solicitud.
    Solicitud Postman mostrando el cuerpo de la solicitud.

    Si la solicitud tiene éxito, la API generará un token JWT, lo almacenará en las cookies de Postman y enviará una respuesta de éxito autenticada.

  7. Por último, actualiza el endpoint de la API GET para comprobar y verificar el token JWT utilizando el código siguiente:
    @app.route('/products', methods=['GET'])
    @token_required
    def get_products(current_user_id):
        headers = {'Authorization': f'Bearer {request.cookies.get("token")}'}    
        response = requests.get(f"{BASE_URL}/products", headers=headers)
        if response.status_code != 200:
            return jsonify({'error': response.json()['message']}), response.status_code
        products = []
        for product in response.json()['products']:
            product_data = {
                'id': product['id'],
                'title': product['title'],
                'brand': product['brand'],
                'price': product['price'],
                'description': product['description']
            }
            products.append(product_data)
        return jsonify({'data': products}), 200 if products else 204

Cómo Contenerizar Microservicios Python con Docker

Docker es una plataforma que empaqueta aplicaciones y sus dependencias en un entorno de desarrollo aislado. Empaquetar microservicios en contenedores agiliza sus procesos de despliegue y gestión en servidores, ya que cada servicio se inicia y ejecuta de forma independiente en su contenedor.

Para empaquetar el microservicio en contenedores, debes crear una imagen Docker a partir de un Dockerfile que especifique las dependencias necesarias para ejecutar la aplicación en un contenedor. Crea un Dockerfile en el directorio root de tu proyecto y añade estas instrucciones:

FROM python:3.9-alpine
WORKDIR /app
COPY requirements.txt ./
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "./services/products.py"]

Antes de crear la imagen, revisa estos comandos:

  • FROM — Indica a Docker qué imagen base debe utilizar. Una imagen base es una instancia preconstruida que contiene el software y las dependencias para ejecutar la aplicación Flask en un contenedor.
  • WORKDIR — Establece el directorio especificado dentro del contenedor como directorio de trabajo.
  • COPY requirements.txt ./ — Copia las dependencias del archivo requirements.txt en el archivo requirements.txt del contenedor.
  • RUN — Ejecuta el comando especificado para instalar las dependencias que requiere la imagen.
  • COPY . . — Copia todos los archivos del directorio root del proyecto al directorio de trabajo dentro del contenedor.
  • EXPOSE — Especifica el puerto en el que el contenedor escuchará las peticiones. Sin embargo, Docker no publica el puerto en el host.
  • CMD — Especifica el comando por defecto que se ejecutará cuando se inicie el contenedor.

A continuación, añade un archivo .dockerignore en el directorio root de tu proyecto para especificar los archivos que la imagen Docker debe excluir. Limitar el contenido de la imagen reducirá su tamaño final y el tiempo de construcción asociado.

/venv
/services/__pycache__/
.gitignore

Ahora, ejecuta el siguiente comando para construir la imagen Docker:

docker build -t flask-microservice .

Por último, una vez construida la imagen, puedes ejecutar el microservicio en un contenedor Docker utilizando el siguiente comando:

docker run -p 5000:5000 flask-microservice

Este comando iniciará un contenedor Docker que ejecuta el microservicio y expondrá el puerto 5000 del contenedor al puerto 5000 del host, permitiéndote hacer peticiones HTTP desde tu navegador web o Postman utilizando la URL http://localhost:5000.

Despliega Microservicios Python con Kinsta

Kinsta ofrece soluciones de alojamiento administrado para aplicaciones web y bases de datos — puedes desplegar y gestionar sin problemas tus microservicios Python y API backend en un entorno de producción.

Sigue estos pasos para configurar tu microservicio Flask para su despliegue con MyKinsta:

  1. En primer lugar, crea un nuevo Procfile en el directorio raíz y añade el código que aparece a continuación. Especifica el comando para ejecutar el microservicio Flask en el servidor HTTP Gunicorn WSGI de Kinsta para aplicaciones Python.
    web: gunicorn services.wsgi
  2. En tu archivo requirements.txt, añade la dependencia de Gunicorn:
    gunicorn==20.1.*
  3. A continuación, crea un nuevo archivo services/wsgi.py y añade el código que aparece a continuación.
    from services.products import app as application
    if __name__ == "__main__":
                    application.run()
  4. Crea un archivo .gitignore en la carpeta raíz del proyecto y añade lo siguiente:
    services/__pycache__
    venv
  5. Por último, crea un nuevo repositorio en GitHub y envía los archivos de tu proyecto.

Una vez que tu repositorio esté listo, sigue estos pasos para desplegar el microservicio Flask en Kinsta:

  1. Inicia sesión o crea una cuenta para ver tu panel MyKinsta.
  2. Autoriza a Kinsta con tu proveedor de Git (Bitbucket, GitHub o GitLab).
  3. Haz clic en Aplicaciones en la barra lateral izquierda, y luego en Añadir aplicación.
  4. En el panel de control, haz clic en Añadir servicio y selecciona Aplicación.
  5. Selecciona el repositorio y la rama desde la que deseas desplegar.
  6. Asigna un nombre único a tu aplicación y elige la ubicación del centro de datos.
  7. Para configurar el entorno de Construcción, selecciona la opción de utilizar un Dockerfile para construir la imagen del contenedor.
  8. Proporciona la ruta a tu Dockerfile y el contexto.
  9. Por último, revisa el resto de la información y haz clic en Crear aplicación.

Prueba el Microservicio

Una vez que el proceso de despliegue se haya realizado correctamente, haz clic en la URL proporcionada para probar el microservicio realizando peticiones HTTP en Postman. Haz una petición GET al endpoint raíz.

Solicitud GET correcta a la URL del microservicio desplegado para la ruta Raíz
Haz una petición HTTP API GET al endpoint product del microservicio.

Para autenticarte y generar un token JWT, envía una solicitud POST al endpoint de la API /auth, pasando las credenciales de administrador en el cuerpo de la solicitud.

Una petición POST a un Microservicio Auth Endpoint para autenticación en Postman
Solicitud HTTP API POST al endpoint auth del microservicio.

Por último, tras autenticarte correctamente, realiza una solicitud GET al endpoint /products para obtener datos.

Petición GET de la API HTTP al endpoint de productos en Postman realizada con éxito
Solicitud de la API HTTP GET a un endpoint de microservicio products.

Resumen

A medida que las aplicaciones crecen en tamaño y complejidad, es crucial adoptar patrones de arquitectura que permitan a los sistemas de software escalar sin forzar los recursos disponibles.

La arquitectura de microservicios ofrece escalabilidad, flexibilidad de desarrollo y facilidad de mantenimiento, facilitándote la gestión de aplicaciones complejas.

Kinsta simplifica el proceso de alojar tus microservicios. Te permite utilizar sin esfuerzo tu base de datos preferida y alojar cómodamente tanto tu aplicación como tu base de datos a través de un panel de control unificado.

Jeremy Holcombe Kinsta

Editor de Contenidos y Marketing en Kinsta, Desarrollador Web de WordPress y Redactor de Contenidos. Aparte de todo lo relacionado con WordPress, me gusta la playa, el golf y el cine. También tengo problemas con la gente alta ;).