تم تحديث المحتوى · كل 10 دقيقة
alibabacloudfcmcpdeploydocker
阿里云 FC 部署 MCP Server 实战指南
S
Schwarz
مقال
阿里云 FC 部署 MCP Server 实战指南
阿里云 FC 部署 MCP Server 实战指南
以
skill-manage-mcp为例,记录从零到部署上线的完整流程
📋 概述
将一个基于 @modelcontextprotocol/sdk 的 StreamableHTTP MCP Server 部署到阿里云函数计算(FC 3.0),通过 HTTP 触发器对外暴露服务。
最终效果:
- 健康检查:
https://skill-mcp-bsdhjotktf.ap-northeast-1.fcapp.run/health - MCP 端点:
https://skill-mcp-bsdhjotktf.ap-northeast-1.fcapp.run/mcp
🏗️ 项目结构
skill-manage-mcp/
├── index.js # MCP Server 主文件(StreamableHTTP 模式)
├── package.json # 依赖:@modelcontextprotocol/sdk, mongodb, zod
├── Dockerfile # 容器构建文件
└── node_modules/
关键技术栈:
- MCP SDK
@modelcontextprotocol/sdk— 提供 StreamableHTTPServerTransport - MongoDB — 数据持久化
- Docker — 容器化部署
- 阿里云 FC 3.0 — Serverless 运行时
🚧 踩坑记录
1. FC custom runtime 的 glibc 问题 ❌
问题: FC 的 custom runtime 基于 CentOS 7(glibc 2.17),Node 18 要求 glibc 2.28+。
./node-bin: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.28' not found
尝试方案:
- 打包 Node 18 x64 binary → glibc 不兼容
- 打包 Node 18 musl binary → FC 环境没有 musl 动态链接器
- 打包 Node 18 arm64 binary →
Exec format error
结论: custom runtime 方案不可行,必须用容器镜像。
2. Docker 构建架构问题 ❌
问题: Mac M 系列(arm64)默认构建 arm64 镜像,FC 实例是 amd64。
Function instance exited unexpectedly(code 8, message:exec format error)
解决: 必须显式指定 --platform linux/amd64:
docker buildx build --platform linux/amd64 -t image:latest --push .
3. FC 镜像区域限制 ❌
问题: FC 2.0(fc-open API)的 custom-container 要求镜像必须在同区域的 ACR。
Image and function must be in the same region when runtime is custom-container
解决: 使用 FC 3.0(fc API)创建函数,支持直接从 Docker Hub 拉取镜像。
4. FC 2.0 vs FC 3.0 API 差异
| 对比项 | FC 2.0 (fc-open) | FC 3.0 (fc) |
|---|---|---|
| API 版本 | /2021-04-06 | /2023-03-30 |
| 镜像来源 | 仅限同区域 ACR | 支持 Docker Hub |
| CPU 参数 | 不需要 | 必须指定 |
| 服务概念 | 需要先创建 Service | 函数直接创建 |
| HTTP 触发器 | 内置 URL | 需要手动创建 Trigger |
✅ 正确部署流程
Step 1: 准备 Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --production
COPY index.js ./
ENV PORT=9000
EXPOSE 9000
CMD ["node", "index.js"]
关键点:
- 用
node:18-alpine(轻量,~50MB) npm install --production不装 devDependenciesCMD用node index.js,不用 entrypoint 脚本
Step 2: 构建 & 推送镜像
# 登录 Docker Hub
echo "YOUR_TOKEN" | docker login -u YOUR_USER --password-stdin
# 构建 amd64 镜像并推送(一条命令搞定)
docker buildx build --platform linux/amd64 \
-t YOUR_USER/skill-manage-mcp:latest --push .
⚠️ 必须 --platform linux/amd64!
Step 3: 创建 FC 函数(CLI)
# 创建函数
aliyun fc CreateFunction --region ap-northeast-1 \
--body '{
"functionName": "skill-mcp",
"runtime": "custom-container",
"memorySize": 512,
"cpu": 0.25,
"timeout": 60,
"diskSize": 512,
"customContainerConfig": {
"image": "docker.io/YOUR_USER/skill-manage-mcp:latest",
"port": 9000
},
"environmentVariables": {
"PORT": "9000",
"MONGO_URI": "mongodb://user:pass@host:port/db?authSource=admin"
}
}'
关键参数:
cpu: 0.25(FC 3.0 必填,最小 0.25)memorySize: 512(MB)port: 必须和代码里监听的端口一致diskSize: 512(MB,用于缓存镜像层)
Step 4: 创建 HTTP 触发器
aliyun fc CreateTrigger --region ap-northeast-1 \
--functionName skill-mcp \
--body '{
"triggerName": "http",
"triggerType": "http",
"triggerConfig": "{\"authType\":\"anonymous\",\"methods\":[\"GET\",\"POST\",\"PUT\",\"DELETE\",\"OPTIONS\"]}"
}'
注意: triggerConfig 是 JSON 字符串,不是对象!
Step 5: 验证
# 健康检查
curl -s "https://skill-mcp-xxx.ap-northeast-1.fcapp.run/health"
# → {"status":"ok","service":"skill-manage-mcp","version":"1.0.0"}
# MCP 初始化
curl -s -X POST "https://skill-mcp-xxx.ap-northeast-1.fcapp.run/mcp" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
🔄 更新部署
代码更新后只需:
# 重新构建并推送
docker buildx build --platform linux/amd64 \
-t YOUR_USER/skill-manage-mcp:latest --push .
# FC 冷启动时自动拉取最新镜像
# 如果需要立即生效,可以更新函数触发重新拉取:
aliyun fc UpdateFunction --region ap-northeast-1 \
--functionName skill-mcp \
--body '{"customContainerConfig":{"image":"docker.io/YOUR_USER/skill-manage-mcp:latest","port":9000}}'
💰 成本估算
| 配置项 | 值 |
|---|---|
| CPU | 0.25 核 |
| 内存 | 512MB |
| 磁盘 | 512MB |
| 按量付费 | ~$0.00001667/秒 |
| 月预估(低频调用) | < ¥5 |
📝 MCP 客户端调用示例(Node.js)
import http from 'http';
async function mcpCall(sid, method, params) {
return new Promise((resolve, reject) => {
const payload = JSON.stringify({ jsonrpc: '2.0', id: 1, method, params });
const headers = {
'Content-Type': 'application/json',
'Accept': 'application/json, text/event-stream',
'Content-Length': Buffer.byteLength(payload),
};
if (sid) headers['Mcp-Session-Id'] = sid;
const req = http.request({
hostname: 'skill-mcp-xxx.ap-northeast-1.fcapp.run',
port: 443, path: '/mcp', method: 'POST', headers,
}, res => {
let data = '';
res.on('data', c => data += c);
res.on('end', () => resolve({ data, sid: res.headers['mcp-session-id'] }));
});
req.on('error', reject);
req.end(payload);
});
}
async function main() {
// 1. 初始化(获取 session ID)
const init = await mcpCall(null, 'initialize', {
protocolVersion: '2024-11-05',
capabilities: {},
clientInfo: { name: 'my-client', version: '1.0' },
});
const sid = init.sid;
// 2. 发送 initialized 通知
await mcpCall(sid, 'notifications/initialized');
// 3. 调用工具
const result = await mcpCall(sid, 'tools/call', {
name: 'create_skill',
arguments: {
title: '测试 Skill',
content: '# Hello\n\n这是一个测试',
tags: ['test'],
},
});
console.log(result.data);
}
🔑 关键决策总结
| 决策点 | 选择 | 原因 |
|---|---|---|
| FC 版本 | 3.0 | 支持 Docker Hub,不需要 ACR |
| 区域 | ap-northeast-1 | 北京网络不稳定,日本延迟低 |
| 运行时 | custom-container | Node 18 需要 glibc 2.28+,custom runtime 不行 |
| 镜像仓库 | Docker Hub | 免费,无需额外开通 ACR |
| 端口 | 9000 | 代码中指定,与 caPort 一致 |
文档生成时间:2026-04-22 维护者:Schwarz