Skip to content

第十六阶段:Kubernetes 部署 ​

docker compose up -d 很好用,但它只管"单机跑起来"。容器挂了没人救、更新要停机、想扩几个副本分不了流量——Kubernetes(K8s)管的就是"跑起来之后的事":自愈、扩缩容、滚动更新。

本阶段在本机(Docker Desktop 自带的 K8s)上把项目搬进 K8s,是路线表第 8 行的最后一块拼图。

一、核心概念(用 Compose 类比) ​

K8s类比 Compose一句话
Pod一个容器实例最小单位,里面跑容器
Deploymentservice(+ replicas)"给我维持 N 个 Pod",挂了自动补
Service服务名 DNS 的升级版给一群 Pod 一个固定地址 + 负载均衡
ConfigMap / Secretenvironment / .env配置放 ConfigMap,LLM_API_KEY 放 Secret
PVCvolumes数据持久化
kubectldocker compose 命令和集群对话的命令行工具

二、准备:打开本机 K8s ​

  1. Docker Desktop → Settings → Kubernetes → 勾选 Enable,点 Apply;
  2. 验证:
bash
kubectl get nodes
# 看到 docker-desktop Ready 即成功
  1. LM Studio 开着(bge-m3 已加载,Start Server,局域网访问已开)。

三、文件总览:k8s/ 目录 ​

text
k8s/
  configmap.yaml   # 非敏感配置:Qdrant/Redis/Embedding 地址
  redis.yaml       # Deployment + Service
  qdrant.yaml      # StatefulSet + Service(有状态数据要 PVC)
  api.yaml         # Deployment(2 副本)+ Service
  worker.yaml      # Deployment(1 副本)
  README.md        # 命令速查

密钥(LLM_API_KEY)不进仓库,手动建一次:

bash
kubectl create secret generic llm-secret --from-literal=LLM_API_KEY='你的key'

四、逐个看 ​

ConfigMap:配置集中放 ​

yaml
data:
  QDRANT_URL: "http://qdrant:6333"
  REDIS_URL: "redis://redis:6379/0"
  CELERY_REDIS_URL: "redis://redis:6379/1"
  EMBED_URL: "http://host.docker.internal:1234/v1"

对应 compose 里的 environment。注意 EMBED_URL 还是 host.docker.internal——容器里 localhost 是自己,这个特殊域名才能穿透到宿主机的 LM Studio。

redis.yaml:Deployment + Service ​

yaml
kind: Deployment
spec:
  replicas: 1
  template:
    spec:
      containers:
        - name: redis
          image: redis:7
---
kind: Service
metadata:
  name: redis
spec:
  selector:
    app: redis
  ports:
    - port: 6379

Deployment 说"我要 1 个 redis Pod";Service 给它一个集群内固定地址,其他 Pod 用 redis:6379 就能找到它——不管 Pod 重启换多少次 IP。

qdrant.yaml:StatefulSet + PVC ​

向量数据不能丢,用 StatefulSet + volumeClaimTemplates 自动申请 5Gi 持久化存储,对应 compose 里的 qdrant_data 卷。

api.yaml:2 副本 + 健康检查 ​

yaml
kind: Deployment
spec:
  replicas: 2          # 跑 2 个 api 分担请求
  template:
    spec:
      containers:
        - name: api
          image: agent-learning:latest
          imagePullPolicy: IfNotPresent  # 本地已有镜像,不去网上拉
          envFrom:
            - configMapRef:
                name: agent-config
          env:
            - name: LLM_API_KEY
              valueFrom:
                secretKeyRef:
                  name: llm-secret
                  key: LLM_API_KEY
          readinessProbe:              # /health 不通就不给它分流量
            httpGet: {path: /health, port: 8000}
            periodSeconds: 10
          livenessProbe:               # /health 长期不通就重启容器
            httpGet: {path: /health, port: 8000}
            periodSeconds: 30

Service 把 2 个副本包成一个地址,请求自动负载均衡。

worker.yaml:同镜像,不同启动命令 ​

yaml
command: ["celery", "-A", "tasks", "worker", "--loglevel=info", "--concurrency=1"]

和 compose 里一样。PDF 不用 hostPath 挂载(Windows 路径映射是坑),部署后用 kubectl cp 拷进去。

五、部署 ​

bash
# 1. 建镜像(K8s 和 Docker Desktop 共用 daemon,直接可见)
docker build -t agent-learning:latest .

# 2. 一键部署
kubectl apply -f k8s/

# 3. 看状态,4 个 Pod 都 Running 才算好
kubectl get pods

六、验证:port-forward + 老三样 ​

K8s 里的 Service 默认只在集群内可访问,拿 port-forward 映射到本机:

bash
kubectl port-forward svc/api 8000:8000
# 新开终端:
curl http://localhost:8000/health

然后跑 stage15 的老三样:POST /ingest → 轮询 /tasks/{id} 看 SUCCESS → /ask 问答正常。Worker 的 PDF 先拷进去:

bash
kubectl cp ./data/. $(kubectl get pod -l app=worker -o jsonpath='{.items[0].metadata.name}'):/app/data/

七、K8s 的真本事:扩容与滚动更新 ​

扩容(Compose 时代要手动 --scale,现在一句话):

bash
kubectl scale deployment/api --replicas=3
kubectl get pods   # 3 个 api Pod,Service 自动分流量

滚动更新(改完代码不断线发布):

bash
docker build -t agent-learning:latest .
kubectl rollout restart deployment/api
kubectl rollout status deployment/api   # 看着它一个一个替换

期间一直 curl /health,请求不断——旧 Pod 要等新 Pod 就绪才下线。

看日志:

bash
kubectl logs -l app=worker
kubectl logs -l app=api

八、清理 ​

bash
kubectl delete -f k8s/
kubectl delete secret llm-secret

九、提交代码 ​

bash
git add k8s/
git commit -m "16 Kubernetes 部署"
git push

项目 4 小结(完结) ​

  • 第十二阶段:FastAPI 把问答做成 HTTP 接口;
  • 第十三阶段:Qdrant 专业向量数据库;
  • 第十四阶段:Dockerfile + Compose 一键拉起;
  • 第十五阶段:Redis 缓存 + Celery 异步入库;
  • 第十六阶段:Kubernetes,自愈、扩缩容、滚动更新。

从 01_hello_llm.py 到 K8s 集群,四个项目走完,一个能真正上线的知识库问答服务已经在你手里了。接下来可以往深里走:Prometheus + Grafana 监控、KEDA 按队列长度自动扩 worker、或者把 embedding 也搬进集群。

👉 附录:踩坑记录