feat: 添加 K8S 部署功能

- 新增 src/k8s-client.ts: K8S 客户端封装
- 新增 src/deploy.ts: 部署/删除/状态查询逻辑
- 新增 scripts/deploy.ts: 命令行部署工具
- 更新 .env.example: 添加 K8S 配置项
- 安装 @kubernetes/client-node@1.3.0 依赖

Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
D8D Developer
2026-03-20 10:19:37 +00:00
parent df81143d5c
commit fec235eade
7 changed files with 1288 additions and 1 deletions

173
scripts/deploy.ts Normal file
View File

@@ -0,0 +1,173 @@
#!/usr/bin/env node
/**
* K8S 部署命令行工具
* 用于部署 Claude API 代理服务到 ECI
*
* 用法:
* pnpm deploy <name> <image> [options] - 部署代理服务
* pnpm deploy delete <name> - 删除代理服务
* pnpm deploy status <name> - 查询服务状态
*
* 选项:
* --replicas <n> 副本数 (默认: 1)
* --port <port> 容器端口 (默认: 3000)
* --eip-bandwidth <n> EIP 带宽 Mbps (默认: 5)
*/
import { config } from 'dotenv';
config();
import { deployProxy, deleteProxy, getProxyStatus } from '../src/deploy';
// 解析命令行参数
function parseArgs() {
const args = process.argv.slice(2);
if (args.length === 0) {
printUsage();
process.exit(1);
}
const command = args[0];
// 解析选项
const getOption = (name: string, defaultValue: string): string => {
const idx = args.indexOf(name);
if (idx !== -1 && idx + 1 < args.length) {
return args[idx + 1];
}
return defaultValue;
};
const getOptionNumber = (name: string, defaultValue: number): number => {
return Number(getOption(name, String(defaultValue)));
};
return {
command,
name: args[1],
image: args[2],
options: {
replicas: getOptionNumber('--replicas', 1),
port: getOptionNumber('--port', 3000),
eipBandwidth: getOptionNumber('--eip-bandwidth', 5),
},
};
}
function printUsage() {
console.log(`
K8S 部署命令行工具 - Claude API 代理服务
用法:
pnpm deploy <name> <image> [options] 部署代理服务
pnpm deploy delete <name> 删除代理服务
pnpm deploy status <name> 查询服务状态
选项:
--replicas <n> 副本数 (默认: 1)
--port <port> 容器端口 (默认: 3000)
--eip-bandwidth <n> EIP 带宽 Mbps (默认: 5)
示例:
# 部署单个代理实例
pnpm deploy claude-proxy-1 registry.cn-beijing.aliyuncs.com/d8dcloud/claude-api-proxy:v0.0.1
# 部署 3 个副本
pnpm deploy claude-proxy-1 registry.cn-beijing.aliyuncs.com/d8dcloud/claude-api-proxy:v0.0.1 --replicas 3
# 删除服务
pnpm deploy delete claude-proxy-1
# 查询状态
pnpm deploy status claude-proxy-1
`);
}
async function main() {
const { command, name, image, options } = parseArgs();
// 检查环境变量
if (!process.env.K8S_API_URL || !process.env.K8S_API_TOKEN) {
console.error('❌ 错误: 请配置 K8S_API_URL 和 K8S_API_TOKEN 环境变量');
console.error(' 可以在 .env 文件中配置或通过环境变量传入');
process.exit(1);
}
try {
switch (command) {
case 'delete': {
if (!name) {
console.error('❌ 错误: 请指定服务名称');
process.exit(1);
}
console.log(`🗑️ 正在删除服务 ${name}...`);
const result = await deleteProxy(name);
if (result.success) {
console.log(`${result.message}`);
} else {
console.error(`${result.message}`);
process.exit(1);
}
break;
}
case 'status': {
if (!name) {
console.error('❌ 错误: 请指定服务名称');
process.exit(1);
}
console.log(`📊 查询服务状态 ${name}...`);
const status = await getProxyStatus(name);
if (status.exists) {
console.log(`✅ 服务状态: ${status.status}`);
if (status.hosts && status.hosts.length > 0) {
console.log(`🌐 访问地址: ${status.hosts.join(', ')}`);
}
} else {
console.log(`⚠️ 服务 ${name} 不存在`);
}
break;
}
default: {
// deploy 命令
if (!name || !image) {
console.error('❌ 错误: 请指定服务名称和镜像');
process.exit(1);
}
console.log(`🚀 正在部署 ${name}...`);
console.log(` 镜像: ${image}`);
console.log(` 副本: ${options.replicas}`);
console.log(` 端口: ${options.port}`);
console.log(` EIP 带宽: ${options.eipBandwidth} Mbps`);
const result = await deployProxy({
name,
image,
replicas: options.replicas,
port: options.port,
eipBandwidth: options.eipBandwidth,
});
if (result.success) {
console.log(`\n✅ ${result.message}`);
if (result.hosts && result.hosts.length > 0) {
console.log(`\n🌐 访问地址:`);
result.hosts.forEach(host => console.log(` ${host}`));
}
} else {
console.error(`\n❌ ${result.message}`);
process.exit(1);
}
break;
}
}
} catch (error) {
console.error('❌ 执行失败:', error instanceof Error ? error.message : error);
process.exit(1);
}
}
main();