从单体到云原生:后端架构演进实战入门

青云直上
2026-05-07 06:37
阅读 2311

大家好,我是开源项目 LlamaServer 的维护者,也是一名带过上百名新手的后端讲师。我当初学后端开发时,面对“微服务”“容器化”“云原生”这些词一头雾水——它们听起来高大上,但没人告诉我怎么一步步走过来。今天这篇教程,就是我想给零基础朋友的一份“地图”:不用先懂所有概念,跟着做就能理解

我们会用 Python 写一个简单的 Web 应用,然后逐步把它从单体架构演进到云原生形态。过程中会用到轻量级 AI 模型 Llama(通过 llama-cpp-python 调用),让你看到架构变化如何影响代码组织和部署方式。


为什么关心架构演进?

想象你开了一家小餐馆:

  • 初期:你一个人负责买菜、炒菜、收银、打扫——这就是单体应用
  • 生意变好:你雇了厨师、服务员、收银员,各自专注一块——这就是微服务
  • 连锁扩张:你用标准化流程开分店,自动补货、智能排班——这就是云原生

后端架构的演进,本质上是为了应对复杂度增长。我们今天的主角是一个能回答问题的简单 API,底层调用本地运行的 Llama 模型。


环境准备:5 分钟搭好开发环境

⚠️ 注意:以下命令假设你使用 macOS 或 Linux。Windows 用户建议启用 WSL2。

第一步:安装 Python 和基础工具

# 确保 Python >= 3.9
python3 --version

# 创建虚拟环境(隔离依赖)
python3 -m venv backend-evolve
source backend-evolve/bin/activate  # Windows: backend-evolve\Scripts\activate

# 升级 pip
pip install --upgrade pip

第二步:安装核心库

我们需要三个关键库:

库名 用途
fastapi 构建 Web API
uvicorn 运行 FastAPI 应用
llama-cpp-python 在本地运行 Llama 模型
pip install fastapi uvicorn llama-cpp-python

💡 新手提示:llama-cpp-python 默认不包含模型文件!我们需要额外下载一个量化版 Llama 模型(约 4GB)。执行:

wget https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGUF/resolve/main/llama-2-7b-chat.Q4_K_M.gguf -O ./models/llama-2-7b-chat.Q4_K_M.gguf

如果网速慢,可跳过模型下载,先用 mock 数据测试 API 结构(后面会说明)。


阶段一:单体架构 —— 所有功能在一个文件里

这是最简单的起点。所有代码写在一个 main.py 文件中:

# main.py
from fastapi import FastAPI
from llama_cpp import Llama

app = FastAPI()

# 初始化 Llama 模型(首次加载较慢)
llm = Llama(
    model_path="./models/llama-2-7b-chat.Q4_K_M.gguf",
    n_ctx=2048,  # 上下文长度
    n_threads=4, # CPU 线程数
)

@app.get("/ask")
def ask_question(q: str):
    """接收用户问题,返回 Llama 的回答"""
    output = llm(f"Q: {q} A:", max_tokens=128, stop=["Q:", "\n"])
    return {"answer": output["choices"][0]["text"]}

运行它:

uvicorn main:app --reload --port 8000

访问 http://localhost:8000/ask?q=你好,你会看到 Llama 的回复!

优点:简单直观,适合学习
缺点:模型加载慢、无法横向扩展、修改任何代码都要重启整个服务

🤔 常见问题:
Q:为什么第一次请求特别慢?
A:因为 Llama 模型要从磁盘加载到内存,后续请求就快了。但在生产环境中,这种“冷启动”是不可接受的。


阶段二:拆分成微服务 —— 把 AI 推理独立出去

现在我们把“AI 推理”拆成独立服务,主 API 只负责路由和业务逻辑。

步骤 1:创建推理服务 inference.py

# inference.py
from fastapi import FastAPI
from llama_cpp import Llama

app = FastAPI()
llm = Llama(model_path="./models/llama-2-7b-chat.Q4_K_M.gguf", n_ctx=2048)

@app.post("/infer")
def infer(prompt: dict):
    text = prompt["text"]
    output = llm(text, max_tokens=128, stop=["Q:", "\n"])
    return {"result": output["choices"][0]["text"]}

启动推理服务(端口 8001):

uvicorn inference:app --port 8001

步骤 2:修改主服务 main.py

# main.py(更新版)
import requests
from fastapi import FastAPI

app = FastAPI()

@app.get("/ask")
def ask_question(q: str):
    # 调用独立的推理服务
    response = requests.post(
        "http://localhost:8001/infer",
        json={"text": f"Q: {q} A:"}
    )
    answer = response.json()["result"]
    return {"answer": answer}

重启主服务(端口 8000):

uvicorn main:app --port 8000

改进

  • 主服务轻量化,启动快
  • 可单独扩容推理服务(比如部署多个实例处理高并发)
  • 模型更新只需重启 inference 服务

新问题

  • 两个服务要分别部署、监控
  • 网络调用带来延迟和失败风险
  • 本地开发要同时启动两个进程

🛠️ 开发技巧:用 & 后台运行服务(Linux/macOS):

uvicorn inference:app --port 8001 &
uvicorn main:app --port 8000

阶段三:迈向云原生 —— 用 Docker 容器化

云原生的核心思想之一是:应用打包成标准单元,随处运行。我们用 Docker 实现。

编写 Dockerfile

为每个服务创建 Dockerfile

# Dockerfile.inference
FROM python:3.10-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY models/ ./models/
COPY inference.py .

CMD ["uvicorn", "inference:app", "--host", "0.0.0.0", "--port", "8001"]
# Dockerfile.main
FROM python:3.10-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY main.py .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

创建依赖文件

# requirements.txt
fastapi==0.104.1
uvicorn==0.24.0
llama-cpp-python==0.2.16
requests==2.31.0

用 Docker Compose 编排服务

# docker-compose.yml
version: '3'
services:
  inference:
    build:
      context: .
      dockerfile: Dockerfile.inference
    ports:
      - "8001:8001"
    volumes:
      - ./models:/app/models  # 挂载模型目录

  main:
    build:
      context: .
      dockerfile: Dockerfile.main
    ports:
      - "8000:8000"
    depends_on:
      - inference

一键启动整个系统

docker compose up --build

现在你的应用:

  • 自动构建镜像
  • 自动启动两个容器
  • 容器间通过服务名通信(main 容器内可直接访问 http://inference:8001

云原生优势显现

  • 环境一致性(开发/测试/生产一致)
  • 资源隔离(一个服务崩溃不影响另一个)
  • 易于集成 CI/CD 流水线

阶段四:真正云原生 —— 引入健康检查与配置管理

生产级云原生应用还需:

  1. 健康检查接口:让 Kubernetes 知道服务是否存活
  2. 外部化配置:避免硬编码端口、路径

添加健康检查

在两个服务中加入 /health 接口:

# 在 main.py 和 inference.py 中都加上
@app.get("/health")
def health():
    return {"status": "ok"}

使用环境变量管理配置

修改 main.py

import os
from fastapi import FastAPI

INFER_URL = os.getenv("INFER_URL", "http://localhost:8001/infer")

app = FastAPI()

@app.get("/ask")
def ask_question(q: str):
    response = requests.post(INFER_URL, json={"text": f"Q: {q} A:"})
    return {"answer": response.json()["result"]}

docker-compose.yml 中注入环境变量:

services:
  main:
    # ...
    environment:
      - INFER_URL=http://inference:8001/infer

这样,未来迁移到 Kubernetes 时,只需改配置,不用改代码!


新手常见问题解答

Q1:我的电脑跑不动 Llama 模型怎么办?
A:完全理解!你可以用 mock 函数代替真实推理:

# 替换 llama_cpp 调用
def mock_infer(prompt):
    return {"result": "这是一个模拟回答,实际应由 Llama 生成。"}

# 在 /infer 接口中调用 mock_infer

Q2:Docker 构建太慢,每次改代码都要重 build?
A:开发时用 volume 挂载代码目录:

# docker-compose.yml 中添加
volumes:
  - .:/app  # 将当前目录挂载到容器

这样修改代码立即生效,无需 rebuild。

Q3:云原生是不是一定要用 Kubernetes?
A:不是!Docker Compose 已经是云原生的第一步。K8s 是大规模场景的选择,小项目用 Compose 足够。


下一步学习建议

你已经走完了从单体到云原生的关键路径!接下来可以:

  1. 深入微服务:学习服务发现(Consul)、API 网关(Kong)
  2. 掌握容器编排:尝试将本项目部署到 Minikube(本地 K8s)
  3. 优化 AI 服务:用 Redis 缓存常见问题答案,减少重复推理
  4. 监控与日志:集成 Prometheus + Grafana 监控服务指标

📌 避坑指南:不要一开始就追求“完美架构”。我见过太多新手卡在“该用 gRPC 还是 REST?”“要不要上 Service Mesh?”——先跑起来,再迭代。你今天的两服务架构,已经比很多创业公司的初期方案更清晰!


最后,这个项目的完整代码我已经放在 GitHub 开源仓库 backend-evolve-demo(示例链接),欢迎 star 和提 issue。有问题随时来问,我们一起把复杂的技术变得简单。

评论 0

最热最新
暂无评论
青云直上Lv.1
0
影响力
0
文章
0
粉丝