En aquest tema, implementarem un sistema de microserveis complet des de zero. Aquest exemple pràctic ens permetrà aplicar els conceptes apresos en els mòduls anteriors i veure com es poden integrar en una aplicació real.
Objectius
- Desenvolupar diversos microserveis que interactuïn entre ells.
- Utilitzar tecnologies i eines adequades per a la implementació.
- Desplegar els microserveis en un entorn de contenidors.
- Configurar la comunicació entre els microserveis.
- Implementar monitoratge i seguretat bàsica.
Descripció del Sistema
Crearem un sistema de comerç electrònic senzill amb els següents microserveis:
- Servei d'Usuari: Gestió d'usuaris (registre, autenticació).
- Servei de Productes: Gestió de productes (creació, actualització, eliminació).
- Servei de Comandes: Gestió de comandes (creació, actualització, estat).
Tecnologies Utilitzades
- Llenguatge de Programació: Python
- Framework: Flask per a APIs RESTful
- Base de Dades: PostgreSQL
- Contenidors: Docker
- Orquestració: Kubernetes
- Monitoratge: Prometheus i Grafana
- Autenticació: JWT (JSON Web Tokens)
Estructura del Projecte
ecommerce-system/ ├── user-service/ │ ├── app/ │ ├── Dockerfile │ ├── requirements.txt │ └── ... ├── product-service/ │ ├── app/ │ ├── Dockerfile │ ├── requirements.txt │ └── ... ├── order-service/ │ ├── app/ │ ├── Dockerfile │ ├── requirements.txt │ └── ... ├── docker-compose.yml └── k8s/ ├── user-service-deployment.yml ├── product-service-deployment.yml ├── order-service-deployment.yml └── ...
Desenvolupament dels Microserveis
Servei d'Usuari
Estructura del Servei d'Usuari
user-service/ ├── app/ │ ├── __init__.py │ ├── models.py │ ├── routes.py │ └── utils.py ├── Dockerfile ├── requirements.txt └── ...
Codi del Servei d'Usuari
app/__init__.py
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password@db:5432/user_db' db = SQLAlchemy(app) from app import routes
app/models.py
from app import db class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) password = db.Column(db.String(120), nullable=False)
app/routes.py
from flask import request, jsonify from app import app, db from app.models import User from app.utils import generate_token, verify_password @app.route('/register', methods=['POST']) def register(): data = request.get_json() new_user = User(username=data['username'], password=generate_password_hash(data['password'])) db.session.add(new_user) db.session.commit() return jsonify({'message': 'User registered successfully'}), 201 @app.route('/login', methods=['POST']) def login(): data = request.get_json() user = User.query.filter_by(username=data['username']).first() if user and verify_password(user.password, data['password']): token = generate_token(user.id) return jsonify({'token': token}), 200 return jsonify({'message': 'Invalid credentials'}), 401
app/utils.py
import jwt from werkzeug.security import generate_password_hash, check_password_hash SECRET_KEY = 'your_secret_key' def generate_token(user_id): return jwt.encode({'user_id': user_id}, SECRET_KEY, algorithm='HS256') def verify_password(stored_password, provided_password): return check_password_hash(stored_password, provided_password)
Dockerfile
FROM python:3.8-slim WORKDIR /app COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY . . CMD ["python", "app/__init__.py"]
requirements.txt
Servei de Productes
Estructura del Servei de Productes
product-service/ ├── app/ │ ├── __init__.py │ ├── models.py │ ├── routes.py │ └── utils.py ├── Dockerfile ├── requirements.txt └── ...
Codi del Servei de Productes
app/__init__.py
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password@db:5432/product_db' db = SQLAlchemy(app) from app import routes
app/models.py
from app import db class Product(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), unique=True, nullable=False) price = db.Column(db.Float, nullable=False)
app/routes.py
from flask import request, jsonify from app import app, db from app.models import Product @app.route('/products', methods=['POST']) def add_product(): data = request.get_json() new_product = Product(name=data['name'], price=data['price']) db.session.add(new_product) db.session.commit() return jsonify({'message': 'Product added successfully'}), 201 @app.route('/products', methods=['GET']) def get_products(): products = Product.query.all() return jsonify([{'id': p.id, 'name': p.name, 'price': p.price} for p in products]), 200
Dockerfile
FROM python:3.8-slim WORKDIR /app COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY . . CMD ["python", "app/__init__.py"]
requirements.txt
Servei de Comandes
Estructura del Servei de Comandes
order-service/ ├── app/ │ ├── __init__.py │ ├── models.py │ ├── routes.py │ └── utils.py ├── Dockerfile ├── requirements.txt └── ...
Codi del Servei de Comandes
app/__init__.py
from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://user:password@db:5432/order_db' db = SQLAlchemy(app) from app import routes
app/models.py
from app import db class Order(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, nullable=False) product_id = db.Column(db.Integer, nullable=False) quantity = db.Column(db.Integer, nullable=False) status = db.Column(db.String(50), nullable=False, default='Pending')
app/routes.py
from flask import request, jsonify from app import app, db from app.models import Order @app.route('/orders', methods=['POST']) def create_order(): data = request.get_json() new_order = Order(user_id=data['user_id'], product_id=data['product_id'], quantity=data['quantity']) db.session.add(new_order) db.session.commit() return jsonify({'message': 'Order created successfully'}), 201 @app.route('/orders', methods=['GET']) def get_orders(): orders = Order.query.all() return jsonify([{'id': o.id, 'user_id': o.user_id, 'product_id': o.product_id, 'quantity': o.quantity, 'status': o.status} for o in orders]), 200
Dockerfile
FROM python:3.8-slim WORKDIR /app COPY requirements.txt requirements.txt RUN pip install -r requirements.txt COPY . . CMD ["python", "app/__init__.py"]
requirements.txt
Desplegament amb Docker i Kubernetes
Docker Compose
docker-compose.yml
version: '3.8' services: user-service: build: ./user-service ports: - "5001:5000" environment: - SQLALCHEMY_DATABASE_URI=postgresql://user:password@db:5432/user_db product-service: build: ./product-service ports: - "5002:5000" environment: - SQLALCHEMY_DATABASE_URI=postgresql://user:password@db:5432/product_db order-service: build: ./order-service ports: - "5003:5000" environment: - SQLALCHEMY_DATABASE_URI=postgresql://user:password@db:5432/order_db db: image: postgres:13 environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: user_db
Kubernetes
k8s/user-service-deployment.yml
apiVersion: apps/v1 kind: Deployment metadata: name: user-service spec: replicas: 2 selector: matchLabels: app: user-service template: metadata: labels: app: user-service spec: containers: - name: user-service image: user-service:latest ports: - containerPort: 5000 env: - name: SQLALCHEMY_DATABASE_URI value: "postgresql://user:password@db:5432/user_db"
k8s/product-service-deployment.yml
apiVersion: apps/v1 kind: Deployment metadata: name: product-service spec: replicas: 2 selector: matchLabels: app: product-service template: metadata: labels: app: product-service spec: containers: - name: product-service image: product-service:latest ports: - containerPort: 5000 env: - name: SQLALCHEMY_DATABASE_URI value: "postgresql://user:password@db:5432/product_db"
k8s/order-service-deployment.yml
apiVersion: apps/v1 kind: Deployment metadata: name: order-service spec: replicas: 2 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: containers: - name: order-service image: order-service:latest ports: - containerPort: 5000 env: - name: SQLALCHEMY_DATABASE_URI value: "postgresql://user:password@db:5432/order_db"
Monitoratge i Seguretat
Monitoratge amb Prometheus i Grafana
docker-compose.yml
(afegint serveis de monitoratge)
services: ... prometheus: image: prom/prometheus ports: - "9090:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml grafana: image: grafana/grafana ports: - "3000:3000"
prometheus.yml
global: scrape_interval: 15s scrape_configs: - job_name: 'user-service' static_configs: - targets: ['user-service:5000'] - job_name: 'product-service' static_configs: - targets: ['product-service:5000'] - job_name: 'order-service' static_configs: - targets: ['order-service:5000']
Seguretat amb JWT
Ja hem implementat JWT en el servei d'usuari per a l'autenticació. Assegurem-nos que els altres serveis validin el token JWT en les seves peticions.
app/utils.py
(Servei de Productes i Comandes)
import jwt from flask import request, jsonify SECRET_KEY = 'your_secret_key' def token_required(f): def decorator(*args, **kwargs): token = request.headers.get('Authorization') if not token: return jsonify({'message': 'Token is missing!'}), 403 try: jwt.decode(token, SECRET_KEY, algorithms=['HS256']) except: return jsonify({'message': 'Token is invalid!'}), 403 return f(*args, **kwargs) return decorator
app/routes.py
(Servei de Productes i Comandes)
from app.utils import token_required @app.route('/products', methods=['POST']) @token_required def add_product(): ...
Conclusió
En aquest exemple pràctic, hem creat un sistema de comerç electrònic senzill utilitzant microserveis. Hem desenvolupat serveis per a la gestió d'usuaris, productes i comandes, i hem desplegat aquests serveis utilitzant Docker i Kubernetes. També hem implementat monitoratge amb Prometheus i Grafana, i hem assegurat la comunicació entre serveis amb JWT.
Aquest exemple ens proporciona una base sòlida per a la implementació de sistemes de microserveis més complexos i ens permet aplicar els conceptes apresos en els mòduls anteriors del curs.
Curs de Microserveis
Mòdul 1: Introducció als Microserveis
- Conceptes Bàsics de Microserveis
- Avantatges i Desavantatges dels Microserveis
- Comparació amb Arquitectura Monolítica
Mòdul 2: Disseny de Microserveis
- Principis de Disseny de Microserveis
- Descomposició d'Aplicacions Monolítiques
- Definició de Bounded Contexts