基于Python的云原生TodoList Demo 项目,验证云原生核心特性
以下是一个基于 Python 的云原生 TodoList Demo 项目,涵盖 容器化、Kubernetes 编排、CI/CD、可观测性、弹性扩缩容 等核心云原生特性,代码简洁且附详细操作指南,适合入门学习。
项目概览
- 目标:实现一个支持增删改查(CRUD)的 TodoList 后端服务,通过云原生技术栈部署,展示完整的云原生实践流程。
- 技术栈:
- 后端:Python + FastAPI(轻量高性能框架)
- 数据库:PostgreSQL(云原生持久化存储)
- 容器化:Docker
- 编排:Kubernetes(K8s)
- CI/CD:GitHub Actions
- 可观测性:Prometheus(监控) + Grafana(可视化) + Loki(日志)
一、项目结构
todolist-cloudnative/
├── app/ # 核心业务代码
│ ├── main.py # FastAPI 入口
│ ├── models.py # SQLAlchemy 模型(数据库表结构)
│ ├── database.py # 数据库连接工具
│ └── requirements.txt # Python 依赖
├── docker/ # Docker 相关配置
│ └── Dockerfile # 容器化构建脚本
├── k8s/ # K8s 部署配置
│ ├── todo-deployment.yaml # 应用 Deployment
│ ├── todo-service.yaml # 应用 Service
│ ├── postgres-statefulset.yaml # PostgreSQL StatefulSet
│ ├── postgres-service.yaml # PostgreSQL Service
│ ├── configmap.yaml # 配置(非敏感)
│ └── secret.yaml # 敏感配置(数据库密码)
├── prometheus/ # Prometheus 监控配置
│ └── todolist.yml # 抓取规则
├── .github/ # GitHub Actions 工作流
│ └── workflows/
│ └── deploy.yml # CI/CD 自动化流程
└── README.md # 操作指南
二、核心功能实现(Python 后端)
1. 安装依赖(app/requirements.txt
)
fastapi==0.104.1
uvicorn==0.23.2
sqlalchemy==2.0.23
psycopg2-binary==2.9.7 # PostgreSQL 驱动
python-multipart==0.0.6 # 文件上传(可选)
prometheus-fastapi-instrumentator==0.3.1 # Prometheus 指标暴露
2. 数据库模型(app/models.py
)
from sqlalchemy import Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_baseBase = declarative_base()class TodoItem(Base):__tablename__ = "todo_items"id = Column(Integer, primary_key=True, index=True)title = Column(String(255), index=True, nullable=False)description = Column(String(500))completed = Column(Boolean, default=False)
3. 数据库连接(app/database.py
)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
import os# 从环境变量获取数据库连接信息(K8s ConfigMap/Secret 注入)
DATABASE_URL = f"postgresql://{os.getenv('POSTGRES_USER')}:{os.getenv('POSTGRES_PASSWORD')}@{os.getenv('POSTGRES_HOST')}:{os.getenv('POSTGRES_PORT')}/{os.getenv('POSTGRES_DB')}"engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
4. FastAPI 入口(app/main.py
)
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from . import models, schemas, database
from prometheus_fastapi_instrumentator import Instrumentator# 初始化数据库表(仅首次运行时创建)
models.Base.metadata.create_all(bind=database.engine)app = FastAPI(title="TodoList 云原生服务")# 注入数据库会话
def get_db():db = database.SessionLocal()try:yield dbfinally:db.close()# 自动暴露 Prometheus 指标(请求计数、延迟)
Instrumentator().instrument(app).expose(app)# Todo 业务接口
@app.post("/todos/", response_model=schemas.TodoItem)
def create_todo(todo: schemas.TodoCreate, db: Session = Depends(get_db)):db_todo = models.TodoItem(**todo.dict())db.add(db_todo)db.commit()db.refresh(db_todo)return db_todo@app.get("/todos/{todo_id}", response_model=schemas.TodoItem)
def read_todo(todo_id: int, db: Session = Depends(get_db)):db_todo = db.query(models.TodoItem).filter(models.TodoItem.id == todo_id).first()if not db_todo:raise HTTPException(status_code=404, detail="Todo 不存在")return db_todo# 其他接口(更新、删除)类似,此处省略...
5. Pydantic 模型(app/schemas.py
)
from pydantic import BaseModelclass TodoCreate(BaseModel):title: strdescription: str = Noneclass TodoItem(TodoCreate):id: intcompleted: bool
三、容器化(Docker)
docker/Dockerfile
# 使用 Python 官方轻量镜像
FROM python:3.11-slim-bookworm# 设置工作目录
WORKDIR /app# 安装系统依赖(PostgreSQL 驱动需要)
RUN apt-get update && apt-get install -y --no-install-recommends gcc python3-dev \&& rm -rf /var/lib/apt/lists/*# 复制依赖文件并安装
COPY app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt# 复制项目代码
COPY app/ ./app/# 暴露 FastAPI 端口(8000)
EXPOSE 8000# 启动命令(UVicorn 自动重载仅在开发时启用)
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
四、Kubernetes 编排(云原生核心)
1. 配置管理(k8s/configmap.yaml
和 k8s/secret.yaml
)
非敏感配置(ConfigMap):
apiVersion: v1
kind: ConfigMap
metadata:name: todo-config
data:POSTGRES_DB: "tododb"POSTGRES_HOST: "postgres-service" # K8s Service 名称(服务发现)
敏感配置(Secret,需手动创建或通过 CI 注入):
# 生成 base64 编码的密码(实际生产建议用外部密钥管理系统)
echo -n "mypassword" | base64
apiVersion: v1
kind: Secret
metadata:name: todo-secret
type: Opaque
data:POSTGRES_USER: "admin" # base64 编码值POSTGRES_PASSWORD: "bXlwYXNzd29yZA==" # "mypassword" 的 base64POSTGRES_PORT: "5432"
2. PostgreSQL 部署(k8s/postgres-statefulset.yaml
)
apiVersion: apps/v1
kind: StatefulSet
metadata:name: postgres
spec:serviceName: postgres-servicereplicas: 1 # 生产环境建议 3 副本 + 主从复制selector:matchLabels:app: postgrestemplate:metadata:labels:app: postgresspec:containers:- name: postgresimage: postgres:15-alpineenv:- name: POSTGRES_USERvalueFrom:secretKeyRef:name: todo-secretkey: POSTGRES_USER- name: POSTGRES_PASSWORDvalueFrom:secretKeyRef:name: todo-secretkey: POSTGRES_PASSWORD- name: POSTGRES_DBvalueFrom:configMapKeyRef:name: todo-configkey: POSTGRES_DBports:- containerPort: 5432volumeMounts:- name: postgres-datamountPath: /var/lib/postgresql/datavolumeClaimTemplates: # 持久化存储(云原生存储卷)- metadata:name: postgres-dataspec:accessModes: [ "ReadWriteOnce" ]resources:requests:storage: 1Gi
3. Todo 服务部署(k8s/todo-deployment.yaml
)
apiVersion: apps/v1
kind: Deployment
metadata:name: todo-deployment
spec:replicas: 2 # 初始副本数(弹性扩缩容基础)selector:matchLabels:app: todotemplate:metadata:labels:app: todospec:containers:- name: todoimage: your-docker-username/todolist:v1 # 替换为实际镜像地址ports:- containerPort: 8000env:- name: POSTGRES_USERvalueFrom:secretKeyRef:name: todo-secretkey: POSTGRES_USER- name: POSTGRES_PASSWORDvalueFrom:secretKeyRef:name: todo-secretkey: POSTGRES_PASSWORD- name: POSTGRES_HOSTvalueFrom:configMapKeyRef:name: todo-configkey: POSTGRES_HOST- name: POSTGRES_PORTvalueFrom:secretKeyRef:name: todo-secretkey: POSTGRES_PORT- name: POSTGRES_DBvalueFrom:configMapKeyRef:name: todo-configkey: POSTGRES_DBlivenessProbe: # 存活探针(自动重启异常实例)httpGet:path: /healthport: 8000initialDelaySeconds: 30periodSeconds: 10readinessProbe: # 就绪探针(流量路由前检查)httpGet:path: /healthport: 8000initialDelaySeconds: 5periodSeconds: 5
4. 服务暴露(k8s/todo-service.yaml
和 k8s/todo-ingress.yaml
)
ClusterIP Service(内部访问):
apiVersion: v1
kind: Service
metadata:name: todo-service
spec:type: ClusterIPselector:app: todoports:- protocol: TCPport: 80targetPort: 8000
Ingress(外部访问,需安装 NGINX Ingress Controller):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: todo-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target: /
spec:rules:- http:paths:- path: /todospathType: Prefixbackend:service:name: todo-serviceport:number: 80
五、CI/CD 自动化(GitHub Actions)
.github/workflows/deploy.yml
name: Deploy to Kuberneteson:push:branches: [ "main" ]jobs:build-and-push:runs-on: ubuntu-lateststeps:- name: Checkout codeuses: actions/checkout@v4- name: Set up Docker Buildxuses: docker/setup-buildx-action@v3- name: Login to Docker Hubuses: docker/login-action@v3with:username: ${{ secrets.DOCKERHUB_USERNAME }}password: ${{ secrets.DOCKERHUB_TOKEN }}- name: Build and push Docker imageuses: docker/build-push-action@v5with:context: .file: docker/Dockerfilepush: truetags: your-docker-username/todolist:latest,your-docker-username/todolist:${{ github.sha }}deploy-to-k8s:needs: build-and-pushruns-on: ubuntu-lateststeps:- name: Checkout codeuses: actions/checkout@v4- name: Set up kubectluses: azure/setup-kubectl@v3with:version: 'v1.28.0'- name: Deploy to Kubernetes clusterrun: |# 替换镜像标签为最新提交 SHAsed -i "s|your-docker-username/todolist:v1|your-docker-username/todolist:${{ github.sha }}|g" k8s/todo-deployment.yaml# 应用 K8s 配置kubectl apply -f k8s/env:KUBECONFIG: ${{ secrets.KUBECONFIG }} # 从 GitHub Secrets 读取集群配置
六、可观测性配置
1. Prometheus 监控(prometheus/todolist.yml
)
scrape_configs:- job_name: "todo_service"scrape_interval: 15sstatic_configs:- targets: ["todo-service:80"] # K8s Service 域名(集群内部可解析)
2. Grafana 仪表盘(示例查询)
- 请求速率:
rate(http_requests_total{job="todo_service"}[5m])
- 平均延迟:
rate(http_request_duration_seconds_sum{job="todo_service"}[5m]) / rate(http_request_duration_seconds_count{job="todo_service"}[5m])
3. Loki 日志收集
- 通过 Fluentd 或 Vector 将 Pod 日志(
stdout/stderr
)转发到 Loki。 - Grafana 中查询日志:
{job="todo_service"} | json
七、云原生特性验证
1. 容器化
- 本地构建镜像:
docker build -t todolist:v1 -f docker/Dockerfile .
- 运行测试:
docker run -p 8000:8000 todolist:v1
,访问http://localhost:8000/docs
验证接口。
2. Kubernetes 部署
- 本地搭建 K8s 集群(Minikube 或 Kind):
minikube start
- 应用配置:
kubectl apply -f k8s/
- 查看状态:
kubectl get pods,svc,ingress
。
3. 弹性扩缩容
- 手动扩缩容:
kubectl scale deployment/todo-deployment --replicas=3
- 自动扩缩容(HPA):
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata:name: todo-hpa spec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: todo-deploymentminReplicas: 2maxReplicas: 10metrics:- type: Resourceresource:name: cputarget:type: UtilizationaverageUtilization: 70 # CPU 使用率超 70% 自动扩容
4. 故障自愈
- 手动删除 Pod:
kubectl delete pod <pod-name>
,观察 K8s 自动重建新 Pod(状态变为Running
)。
5. 持续交付
- 推送代码到 GitHub
main
分支,触发 GitHub Actions 自动构建、测试、部署。
八、总结
通过这个 TodoList Demo,你可以完整体验云原生应用的核心流程:
- 容器化:用 Docker 封装应用,确保环境一致性。
- Kubernetes 编排:通过 Deployment、Service 等资源实现自动化管理。
- CI/CD:GitHub Actions 实现代码提交到生产的全自动化。
- 可观测性:Prometheus + Grafana 监控性能,Loki 收集日志。
- 弹性扩缩容:HPA 根据负载自动调整资源,保障高可用。
后续可扩展方向:
- 拆分微服务(如用户服务、待办服务),用 Istio 实现服务网格。
- 引入数据库读写分离(主从复制)。
- 添加认证鉴权(OAuth2、JWT)。
- 使用 Helm 打包 K8s 配置,简化多环境部署。