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'}), 401app/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]), 200Dockerfile
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]), 200Dockerfile
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_dbKubernetes
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 decoratorapp/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
