《AgentX 专栏》10-生产部署:3台2C4G云服务器把企业级Agent真正跑起来的完整方案
生产部署:3 台 2C4G 云服务器,把企业级 Agent 真正跑起来的完整方案|AgentX 专栏⑩
本文是 AgentX 技术专栏第十篇,也是收官篇。基于真实项目部署产物(
Dockerfile/docker-compose.yml/deploy.sh/server-init.sh/.env.example),从技术简介到设计思路,从核心脚本到生产踩坑,循序渐进拆解 AgentX 如何用 3 台 2C4G 低配云服务器,把一个企业级 Agent 平台真正跑起来——让 Java 程序员一次性看懂"从代码到上线"的最后一公里。
本文速览:
- 为什么"本地能跑"和"生产能跑"是两回事?AI 应用部署的独特难点
- 三节点拓扑:推理节点 / 存储节点 / 监控节点,每台机器干什么
- Dockerfile 多阶段构建:为什么编译镜像和运行镜像要分开
- ZGC + MaxRAMPercentage=75%:低配机器上的 JVM 调优
server-init.sh裸机初始化:动态 Swap、Docker 镜像加速、NVIDIA 残留清理deploy.sh一键部署:git pull → 构建 → 健康验证的完整闭环- 三个生产部署大坑:容器访问宿主机 Ollama、内存超配 OOM、健康检查误杀
文章约 40 KB,是整个专栏的落地收尾——建议从头读。读完文末有完整部署代码包(含全部脚本 + 三节点配置)获取方式。
一、先抛个问题:为什么"本地能跑"不等于"能上线"
很多人做 AI 应用,本地 mvn spring-boot:run 跑得好好的,一到部署就傻眼。因为本地和生产之间隔着一条鸿沟:
| 维度 | 本地开发 | 生产环境 |
|---|---|---|
| 依赖服务 | 都在一台机器(localhost) | 分散在多台服务器,跨网络 |
| 资源 | 16G 内存随便造 | 2C4G,抠着用 |
| 启动 | IDE 点一下 | 要能一键部署、自动重启 |
| 故障 | 重启 IDE | 要能自愈、要有监控 |
| 配置 | 写死在代码里 | 要能按环境注入 |
| GPU | 无所谓 | 没有 GPU,CPU 跑大模型 |
尤其是 AI 应用,还有几个独特的部署难点:
- 大模型推理吃资源——Ollama 跑 qwen2.5 即使是 CPU 版也要好几 G 内存
- 依赖组件多——LLM、向量库(Milvus)、缓存(Redis)、追踪(Jaeger),一个都不能少
- 启动慢——大模型加载 + 向量库初始化,冷启动可能要分钟级
- 长连接——SSE 流式对话,健康检查不能误判
这篇文章就来拆解 AgentX 怎么在3 台 2C4G 的低配云服务器上,把这些难点逐个解决,做到"一行命令上线、挂了自动重启、出问题能追踪"。
这是整个专栏的收官篇。前面 9 篇讲的是"怎么写",这一篇讲"怎么上线"——没有这一步,再好的代码也只是个 demo。
二、技术简介:AI 应用的生产部署需要什么
2.1 部署的本质是"环境复制 + 依赖编排 + 故障自愈"
一个生产级部署方案,至少要回答三个问题:
| 问题 | 解决手段 |
|---|---|
| 怎么保证哪台机器都能跑? | 容器化(Docker)—— 把运行环境打包进镜像 |
| 多个依赖组件怎么协同? | 编排(docker compose)—— 一份配置拉起所有服务 |
| 挂了怎么办? | 健康检查 + 自动重启 + 监控告警 |
2.2 AgentX 的部署技术栈
| 技术 | 作用 | 为什么选它 |
|---|---|---|
| Docker 多阶段构建 | 打包应用 | 编译/运行分离,镜像瘦身 |
| docker compose | 单机编排 | 比 K8s 轻,2C4G 跑得动 |
| Shell 脚本(deploy/init) | 自动化 | 无依赖,任何 Linux 都能跑 |
| 环境变量(.env) | 配置注入 | 一份镜像跑多环境 |
| ZGC | JVM 垃圾回收 | 低延迟,停顿亚毫秒级 |
2.3 为什么不用 K8s?
这是很多人的第一反应——“上生产不就该上 K8s 吗?”
| 方案 | 资源开销 | 学习成本 | 适合规模 |
|---|---|---|---|
| docker compose | 极低 | 低 | 单机 / 几台机器 |
| K8s | 高(光控制面就吃 1-2G) | 陡峭 | 几十台 + 弹性伸缩 |
对 3 台 2C4G 的机器来说,K8s 的控制面开销就能吃掉一台机器的资源。 个人项目、中小企业的 Agent 平台,docker compose 足够了。技术选型的核心是"匹配规模",不是"追新"——这也呼应了专栏②讲的预算约束哲学。
三、设计思路:四条低成本生产部署的核心原则
原则一:职责分离,三节点拓扑
把所有东西堆一台机器上,2C4G 必然撑不住。AgentX 按职责把服务拆到三台机器:
┌─────────────────────────────────────────────────────────────────┐
│ Server A · 推理节点(124.223.22.34) │
│ ├── AgentX 后端(Spring Boot 容器) │
│ └── Ollama(同机独立部署,qwen2.5 + bge-m3) │
│ 特点:CPU 密集,大模型推理的主战场 │
├─────────────────────────────────────────────────────────────────┤
│ Server B · 存储节点(10.0.0.15,内网) │
│ ├── Redis(短期记忆 / 会话) │
│ └── Milvus(长期记忆 / RAG 向量库) │
│ 特点:内存 + 磁盘密集,数据持久化的主战场 │
├─────────────────────────────────────────────────────────────────┤
│ Server C · 监控节点(8.140.221.150,北京) │
│ ├── Jaeger(链路追踪,呼应专栏⑦) │
│ └── Nginx(反向代理 / 入口) │
│ 特点:独立隔离,监控不与业务争资源 │
└─────────────────────────────────────────────────────────────────┘
为什么这样分? 三类负载的资源特征完全不同:
- 推理(A)吃 CPU
- 存储(B)吃内存和磁盘 IO
- 监控(C)需要独立,不能业务一崩监控也跟着崩
分开之后,每台 2C4G 各司其职,互不抢资源。
原则二:容器化,但只容器化"无状态"的部分
AgentX 后端是无状态的(状态都在 Redis/Milvus),适合容器化。但有两个东西故意不放进容器:
| 组件 | 是否容器化 | 原因 |
|---|---|---|
| AgentX 后端 | ✅ 容器 | 无状态,随便重启 |
| Ollama | ❌ 宿主机独立部署 | 大模型文件几个 G,容器化反而麻烦;且要复用宿主机的 CPU 优化 |
| Redis/Milvus | ❌ 独立节点 | 有状态,数据要持久化,单独管理更稳 |
容器化不是越多越好。 有状态、重量级、需要复用宿主机资源的组件,独立部署往往更省心。
原则三:配置外置,一份镜像跑多环境
绝不把 IP、密码写死在代码或镜像里。所有环境相关配置走 .env 文件 + 环境变量注入:
镜像(不变) + .env(按环境变化) = 跑在任何环境
开发环境一份 .env,生产环境一份 .env,同一个镜像不用重新构建。这是十二要素应用(12-Factor App)的核心原则之一。
原则四:一切自动化,从裸机到上线两条命令
部署不应该是"照着 100 步文档手敲"。AgentX 把整个流程压缩成两个脚本:
# 1. 裸机第一次:初始化服务器环境
bash server-init.sh
# 2. 之后每次部署/更新:一键
bash deploy.sh
自动化的价值不只是省事,更是消除"人肉操作的不确定性"——脚本每次执行结果一致,不会因为忘了某一步而出错。
四条原则讲完,看具体落地。
四、代码解析:从裸机到上线的完整链路
4.1 Dockerfile:多阶段构建
第一块拼图是把 AgentX 后端打包成镜像。关键是多阶段构建:
# ── Stage 1: 编译(Maven + JDK 21)──
FROM maven:3.9-eclipse-temurin-21 AS builder
# 配置阿里云 Maven 镜像,加速依赖下载
COPY maven-settings.xml /root/.m2/settings.xml
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B -q 2>/dev/null || true # 先下依赖(利用缓存层)
COPY src ./src
RUN mvn clean package -DskipTests -B
# ── Stage 2: 运行(最小的 JRE 镜像)──
FROM eclipse-temurin:21-jre
WORKDIR /app
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 只复制编译产物,不带 Maven 和源码
COPY --from=builder /build/target/agentx-1.0.0.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -s --fail http://localhost:8080/api/v1/agents/health || exit 1
ENTRYPOINT ["java", \
"-server", \
"-XX:+UseZGC", \
"-XX:MaxRAMPercentage=75.0", \
"-Djava.security.egd=file:/dev/./urandom", \
"-jar", "app.jar"]
为什么要分两个 Stage?
| 单阶段(错误做法) | 多阶段(正确做法) | |
|---|---|---|
| 镜像内容 | Maven + JDK + 源码 + 依赖 + jar | 只有 JRE + jar |
| 镜像大小 | 800MB+ | 200MB 左右 |
| 安全性 | 源码、构建工具全暴露 | 只有运行时,攻击面小 |
Stage 1 用完整的 Maven 镜像编译,Stage 2 用最小的 JRE 镜像运行,最终镜像只保留 Stage 2 的内容。编译用的 Maven、源码全部丢弃。
三个 JVM 参数的讲究:
-XX:+UseZGC # ZGC 垃圾回收器,停顿亚毫秒级
-XX:MaxRAMPercentage=75.0 # 最多用 75% 容器内存(留 25% 给堆外/系统)
-Djava.security.egd=file:/dev/./urandom # 用非阻塞随机源,加速启动
- ZGC:传统 G1 在大堆下 GC 停顿可能几百毫秒,对 SSE 流式对话是灾难。ZGC 停顿亚毫秒级,无论堆多大。
- MaxRAMPercentage=75%:容器里绝不能用
-Xmx写死内存。容器内存限制变了,写死的-Xmx不会跟着变,容易 OOM。用百分比让 JVM 自适应容器内存。留 25% 给 Metaspace、线程栈、堆外内存。 - egd=urandom:JVM 启动时初始化
SecureRandom会读/dev/random,没有足够熵时会阻塞。换成/dev/urandom非阻塞,启动快好几秒。
4.2 docker-compose.yml:编排与配置注入
有了镜像,用 compose 拉起服务并注入配置:
services:
agentx-app:
build: .
container_name: agentx-app
ports:
- "${SERVER_PORT:-8080}:8080"
environment:
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-prod}
# Redis(Server B: 10.0.0.15)
- REDIS_HOST=${REDIS_HOST:-10.0.0.15}
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
# 统一数据节点 IP(application.yml 通过 DATA_SERVER_IP 引用)
- DATA_SERVER_IP=${DATA_SERVER_IP:-10.0.0.15}
# Milvus(Server B)
- MILVUS_HOST=${MILVUS_HOST:-10.0.0.15}
- MILVUS_PORT=${MILVUS_PORT:-19530}
# 监控节点(Server C: 北京 Jaeger)
- MONITOR_SERVER_IP=${MONITOR_SERVER_IP:-8.140.221.150}
# OTel 语义标签(呼应专栏⑦)
- OTEL_SERVICE_NAME=agentx
- OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=production
# Ollama(同机独立部署,通过 host.docker.internal 访问宿主机)
- AI_SERVER_IP=${AI_SERVER_IP:-host.docker.internal}
- OLLAMA_HOST=${OLLAMA_HOST:-http://host.docker.internal:11434}
volumes:
- ./logs:/app/logs # 日志挂出来,容器删了日志还在
restart: unless-stopped # 自愈:非人为停止就自动重启
extra_hosts:
- "host.docker.internal:host-gateway" # 关键:让容器能访问宿主机
healthcheck:
test: ["CMD", "curl", "-s", "--fail", "http://localhost:8080/api/v1/agents/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 60s # 关键:给冷启动 60s 宽限期
networks:
default:
name: agentx-network
几个关键设计:
| 配置 | 作用 |
|---|---|
${VAR:-default} |
环境变量优先,没有就用默认值——配置外置 |
restart: unless-stopped |
容器挂了自动重启,除非人为停止 |
extra_hosts: host.docker.internal |
让容器访问宿主机的 Ollama(坑一会细讲) |
start_period: 60s |
冷启动宽限期,避免健康检查误杀(坑三会细讲) |
volumes: ./logs |
日志持久化到宿主机 |
注意 DATA_SERVER_IP、MONITOR_SERVER_IP 这些——三节点的 IP 全部通过环境变量注入,application.yml 里用 ${DATA_SERVER_IP} 引用。改 IP 不用动代码。
4.3 server-init.sh:裸机一键初始化
一台刚买的云服务器,什么都没有。server-init.sh 把它变成能跑 AgentX 的环境。核心片段:
#!/bin/bash
set -euo pipefail
# ── 动态 Swap:低配机器的救命稻草 ──
TOTAL_MEM=$(awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo)
if ! swapon --show | grep -q .; then
if [ "$TOTAL_MEM" -le 4096 ]; then
SWAP_SIZE="4G" # 4G 内存配 4G swap
elif [ "$TOTAL_MEM" -le 8192 ]; then
SWAP_SIZE="2G"
else
SWAP_SIZE="0" # 内存充足不配
fi
if [ "$SWAP_SIZE" != "0" ]; then
fallocate -l "$SWAP_SIZE" /swapfile
chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
sysctl vm.swappiness=10 # 尽量用内存,swap 兜底
echo "vm.swappiness=10" >> /etc/sysctl.conf
fi
fi
# ── Docker 安装:走阿里云源 ──
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor \
-o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [...signed-by=...] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
$(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
apt update -y && apt install -y docker-ce docker-ce-cli containerd.io
# ── 阿里云 ECS 镜像加速 ──
cat > /etc/docker/daemon.json <<EOF
{ "registry-mirrors": ["https://registry.cn-hangzhou.aliyuncs.com"] }
EOF
systemctl restart docker
为什么这些细节重要?
- 动态 Swap:2C4G 机器跑 Java + 偶尔的内存尖峰,没有 swap 极易 OOM。但 swap 太多又会拖慢,所以
swappiness=10——优先用物理内存,swap 只兜底。 - swap 大小按内存动态算:4G 内存配 4G swap,8G 配 2G,16G 不配。不是一刀切。
- 阿里云镜像加速:国内拉 Docker Hub 镜像慢到崩溃,配了加速器才能正常拉镜像。
- NVIDIA 残留清理(脚本里还有):阿里云 ECS 有时带 NVIDIA 驱动残留,会干扰 apt,开头先清理。
这些都是"国内低配云服务器"才会踩的真实坑,脚本把它们一次性处理掉。
4.4 deploy.sh:一键部署闭环
环境就绪后,deploy.sh 负责"拉代码 → 构建 → 启动 → 验证"的完整闭环:
#!/bin/bash
set -e
PROJECT_PATH="/usr/service/agentx"
CONTAINER_NAME="agentx-app"
# ── 1. 拉取最新代码 ──
if [ ! -d "$PROJECT_PATH" ]; then
git clone --branch main git@github.com:SuniaW/agentx.git "$PROJECT_PATH"
else
cd "$PROJECT_PATH"
git fetch --all
git reset --hard origin/main # 强制同步到最新,避免本地冲突
fi
cd "$PROJECT_PATH"
# ── 2. 准备环境变量 ──
[ -f set-env.sh ] && source set-env.sh
[ ! -f .env ] && cp .env.example .env
# ── 3. 构建并启动 ──
docker compose down 2>/dev/null || true
docker compose up -d --build --force-recreate
# ── 4. 健康验证 ──
sleep 10
STATUS=$(docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME")
HEALTH=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}running{{end}}' "$CONTAINER_NAME")
if [ "$STATUS" == "true" ]; then
echo "✅ 部署成功!(health: $HEALTH)"
docker logs --tail 5 "$CONTAINER_NAME"
else
echo "❌ 容器未能正常启动,查看:docker logs $CONTAINER_NAME"
fi
# ── 5. 清理过期镜像 ──
docker image prune -f >/dev/null 2>&1 || true
这个脚本体现了"幂等部署"的思想:
- 首次和更新统一:目录不存在就 clone,存在就
git reset --hard强制同步。不管是第一次还是第一百次,跑同一个命令。 --force-recreate:强制重建容器,确保新代码生效(避免 Docker 缓存导致跑旧代码)。- 部署后自动验证:不是
up -d完就完事,还要docker inspect确认容器真的 Running + Healthy。 - 自动清理:
docker image prune删掉旧镜像,防止磁盘被占满(低配机器磁盘也小)。
一行 bash deploy.sh,从拉代码到验证上线全自动。
4.5 整体部署流程串起来
裸机服务器
│ bash server-init.sh
▼
环境就绪(Docker / Swap / 镜像加速 / Git)
│ 配置 GitHub SSH key
▼
bash deploy.sh
│ git pull → docker compose up --build
▼
容器启动 → HEALTHCHECK 探测(60s 宽限)
│ 健康
▼
✅ 上线(access http://124.223.22.34:8080)
│ 挂了?
▼
restart: unless-stopped 自动拉起
从一台空服务器到生产可用,两条命令。
五、问题解决:三个生产部署的实战大坑
坑一:容器里的 AgentX 连不上宿主机的 Ollama
现象
本地开发时 OLLAMA_HOST=http://localhost:11434 一切正常。打成容器部署后,AgentX 疯狂报 Connection refused,连不上 Ollama。
原因
容器有自己独立的网络命名空间。容器里的 localhost 指的是容器自己,不是宿主机。而 Ollama 跑在宿主机上(我们故意不容器化它,见原则二)。容器里访问 localhost:11434,等于在容器内部找 Ollama——当然找不到。
解决
用 Docker 提供的特殊域名 host.docker.internal 访问宿主机,并在 compose 里显式声明映射:
environment:
- OLLAMA_HOST=http://host.docker.internal:11434 # 不是 localhost!
extra_hosts:
- "host.docker.internal:host-gateway" # Linux 上必须显式声明
| 写法 | 容器里指向 | 结果 |
|---|---|---|
localhost:11434 |
容器自己 | ❌ 连不上 |
host.docker.internal:11434 |
宿主机 | ✅ 连得上 |
注意:host.docker.internal 在 Docker Desktop(Mac/Win)上自带,但Linux 上必须加 extra_hosts: host-gateway 才能用——这是最容易漏的一步。
教训:容器化一个组件时,要想清楚它依赖的其他服务在哪。容器的网络隔离是把双刃剑——隔离带来干净,也带来"localhost 不再是你以为的 localhost"。
坑二:JVM 内存超配,容器被 OOMKilled
现象
容器跑着跑着突然消失,docker inspect 显示 OOMKilled: true。但应用日志里没有任何 Java 的 OutOfMemoryError——是被系统直接杀掉的。
原因
最初 Dockerfile 里写死了 -Xmx3g。但 2C4G 机器实际可用内存可能只有 3.5G(系统占一部分),JVM 堆 3G + Metaspace + 线程栈 + 堆外内存(ZGC 也用堆外)+ Ollama 偶尔的内存尖峰,加起来超过物理内存。Linux OOM Killer 直接把容器进程杀了——它不管你 Java 内部怎么想,物理内存不够就杀。
解决
绝不写死 -Xmx,用 MaxRAMPercentage 让 JVM 按容器内存的百分比自适应:
# ❌ 危险:写死,容器内存变了它不变
ENTRYPOINT ["java", "-Xmx3g", "-jar", "app.jar"]
# ✅ 安全:按容器内存的 75% 自适应,留 25% 给堆外和系统
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
为什么是 75% 而不是 90%?因为堆只是 JVM 内存的一部分:
容器内存 100%
├── JVM 堆(MaxRAMPercentage=75%)
└── 剩余 25%:Metaspace + 线程栈 + ZGC 堆外 + JIT 缓存 + ...
留 25% 给"堆之外的一切",才不会被 OOM Killer 盯上。
教训:容器里的 JVM 内存管理和裸机完全不同。裸机你知道总内存多少,容器里内存是被 cgroup 限制的。永远用百分比(MaxRAMPercentage),永远留足堆外空间。
坑三:冷启动太慢,健康检查把刚启动的容器误杀
现象
部署后容器反复重启,日志显示应用其实正常启动了,但每次刚启动就被 restart 拉走重来。陷入"启动→被杀→重启→被杀"的死循环。
原因
AgentX 冷启动慢——要连 Redis、初始化 Milvus 集合、加载 LangChain4j、连 Ollama,全部就绪可能要 40-50 秒。但健康检查从容器一启动就开始探测:
healthcheck:
test: [...]
interval: 30s
retries: 3
# 没有 start_period!
容器启动后 30s 第一次探测——这时应用还没起好,失败。再 30s、再 30s,连续 3 次失败,Docker 判定 unhealthy,restart 把它杀了重启。但重启后还是同样的慢启动,再次被杀。死循环。
解决
加 start_period——给冷启动一个"宽限期",这段时间内的探测失败不计入失败次数:
healthcheck:
test: ["CMD", "curl", "-s", "--fail", "http://localhost:8080/api/v1/agents/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 60s # ✅ 前 60s 是宽限期,探测失败不算数
| 配置 | 含义 |
|---|---|
start_period: 60s |
启动后 60s 内,健康检查失败不计数、不触发重启 |
interval: 30s |
宽限期过后,每 30s 探测一次 |
retries: 3 |
连续 3 次失败才判 unhealthy |
start_period 要设得比"最坏冷启动时间"略长。AgentX 冷启动约 40-50s,设 60s 留足余量。
教训:AI 应用冷启动普遍慢(大模型加载、向量库初始化)。健康检查一定要配 start_period,否则慢启动会被自愈机制误判成"启动失败",陷入重启死循环。自愈机制本是好事,配错了反而成了帮凶。
六、总结:一张表 + 五条经验
设计决策回顾
| 设计决策 | 解决什么问题 |
|---|---|
| 三节点拓扑(推理/存储/监控) | 三类负载资源特征不同,分开互不抢资源 |
| Docker 多阶段构建 | 编译/运行分离,镜像从 800MB 瘦到 200MB |
| Ollama/Redis/Milvus 不容器化 | 有状态、重量级组件独立部署更稳 |
| 配置全外置(.env + 环境变量) | 一份镜像跑多环境,改 IP 不动代码 |
| ZGC + MaxRAMPercentage=75% | 低延迟 GC + 容器内存自适应,防 OOM |
| server-init.sh 裸机初始化 | 动态 Swap + 镜像加速,国内低配云的坑一次处理 |
| deploy.sh 幂等部署 | 首次/更新统一,拉代码到验证全自动 |
| start_period + restart 自愈 | 冷启动宽限 + 自动重启,挂了能起来又不误杀 |
五条核心经验
- "本地能跑"和"生产能跑"差着一整套工程 —— 容器化、编排、配置外置、健康检查、自愈,一个都不能少
- 技术选型要匹配规模 —— 3 台 2C4G 用 docker compose 而非 K8s,K8s 的控制面开销能吃掉一台机器
- 容器里的 JVM 必须用百分比内存 —— 写死
-Xmx在容器里是 OOM 的头号元凶 - 容器网络的 localhost 不是宿主机的 localhost —— 访问宿主机服务用
host.docker.internal+host-gateway - AI 应用冷启动慢,健康检查必须配 start_period —— 否则自愈机制会误杀慢启动的容器
部署演进路线
如果你要部署自己的 AI 应用,建议这样推进:
- 第一阶段:单机 docker compose 把所有组件跑起来,先通
- 第二阶段:拆分有状态组件(Redis/Milvus/Ollama)到独立节点
- 第三阶段:写 server-init.sh + deploy.sh,把部署自动化
- 第四阶段:配健康检查 + 自愈 + 监控(接专栏⑦的 Jaeger)
- 第五阶段:规模上来后再考虑 K8s + CI/CD 流水线
七、写在最后:专栏收官
这是 AgentX 技术专栏的第十篇,也是收官篇。
回顾这一路,从专栏②的技术选型,到架构、工具、RAG、记忆、可观测、工作流、MCP,再到这一篇的生产部署——我们完整地走过了一个企业级 Agent 平台"从 0 到上线"的全过程:
| 篇 | 解决的问题 |
|---|---|
| ②③ | 怎么选型、怎么设计架构 |
| ④⑤⑥ | 工具、知识、记忆——Agent 的核心能力 |
| ⑦⑧ | 可观测、工作流——让它可控、可编排 |
| ⑨ | MCP——接入全球工具生态 |
| ⑩ | 部署上线——让前面所有的努力真正跑起来 |
写这个专栏最大的感受是:做 AI 应用,难的从来不是调用大模型那一行 API,而是把它工程化——让它稳定、可观测、可部署、可演进。而这些恰恰是我们 Java 程序员的看家本领。大模型时代不是要抛弃工程经验,而是把工程经验用在新场景。
如果这个专栏对你有帮助,希望它能成为你做 AI 工程的一份参考。代码全部开源,每一篇都有配套代码包。
专栏会暂告一段落,但 AgentX 项目会持续演进——后续可能会写一些进阶专题(多 Agent 协作、Agent 评估、成本优化等)。欢迎关注,我们下个专题再见。
- 本文部署代码包 — 公众号回复「部署」获取(含全部脚本 + 三节点配置模板)
- 完整专栏代码 — 每篇都有独立代码包,回复对应关键词获取
- 欢迎交流 — 评论区或公众号私信,一起把 AI 工程做扎实
感谢一路读到这里的你。🙏
💬 互动话题:你的 AI 应用部署在什么环境?踩过哪些部署的坑?或者你最想看的下一个专题是什么?评论区聊聊。
关注公众号 【SuniaCoder-AI全栈架构实战】,回复「部署」获取本文完整部署代码包,回复「MCP」获取 MCP 互通代码,回复「工作流」获取工作流引擎代码。
关于作者 & 联系方式
汪旭 / Sunia — Java 全栈开发者,AI 应用工程化实践者
专注企业级 AI 落地,擅长极限资源优化,有 RAG、Agent、知识图谱方向的完整实战经验。
| 平台 | 地址 / 说明 |
|---|---|
| CSDN | SuniaCoder-AI|13.5 万+ 阅读,RAG/Agent 系列持续更新 |
| 微信公众号 | 搜索【SuniaCoder-AI全栈架构实战】|关注回复「部署」获取本文完整代码包 |
| 掘金 | SuniaCoder-AI |
| 知乎 | SuniaCoder-AI |
| 合作咨询 | 提供企业私有化大模型部署与定制开发(基础部署 / 企业定制 / 年度维保)欢迎私信洽谈 |
如果内容对你有帮助,点赞 + 收藏 + 关注是最大的支持,也能让更多需要的人看到这篇文章。
AgentX 专栏导航(完结)
| 篇 | 标题 | 核心内容 |
|---|---|---|
| ① | 一个 Java 开发者的 Agent 实践之路(前言) | 专栏总览 / 选题思路 |
| ② | 没有 GPU、只有 3 台低配云服务器,我如何选出 AgentX 的技术栈 | LangChain4j / Ollama / Milvus / Redis 选型 |
| ③ | AgentX 架构设计全解析:一个请求是如何从 HTTP 走到 LLM 再回来的 | 六层架构 / SSE 流式 / 虚拟线程 / TraceId |
| ④ | 工具系统深度实现:从 @Tool 注解到 MCP 协议,构建企业级 Agent 工具体系 | ToolRegistry / McpToolServer / @Tool |
| ⑤ | RAG 进阶:用 Milvus + bge-m3 构建比 ES 更懂语义的企业知识库 | 向量检索 / bge-m3 / MilvusV2 |
| ⑥ | 记忆系统:用 Redis + Milvus 给 AI 配上短期 + 长期双层记忆 | ChatMemoryStore / 语义召回 / 多轮上下文 |
| ⑦ | 全链路可观测:用 OpenTelemetry + Jaeger 让每次 AI 对话都可追踪可复盘 | OTel / Jaeger / SpanExporter / TraceId |
| ⑧ | 工作流引擎:AgentWorkflow 怎么把工具、记忆、流程串成一条流水线 | AgentWorkflow / LangGraph / 虚拟线程 / SSE |
| ⑨ | MCP 协议双向打通:让 AgentX 既能被 Claude 调用,又能调度全球工具生态 | MCP / JSON-RPC / 双源合并 / Schema 转换 |
| ⑩ | 生产部署:3 台 2C4G 云服务器,把企业级 Agent 真正跑起来的完整方案(本文 · 完结) | Docker / 三节点 / ZGC / deploy.sh |
↑ 上一篇:[MCP 协议双向打通:让 AgentX 既能被 Claude 调用,又能调度全球工具生态|AgentX 专栏⑨]
🎉 专栏完结:感谢一路相伴。AgentX 全系列 10 篇,从选型到上线,希望对你做 AI 工程有所帮助。
Tags:#AgentX #生产部署 #Docker #DockerCompose #JVM调优 #ZGC #运维部署 #SpringBoot3 #Java21 #云服务器
更多推荐


所有评论(0)