微信扫码
与创始人交个朋友
我要投稿
本文主要讨论GPU的生产环境如何降低成本,和各类saas服务、免费账号等不是一个维度。
生产环境中,通常会遇到几个问题:
1、业务不稳定。例如早上高峰期队列非常高,而凌晨又几乎没有流量
2、大模型的基建通常在github、huggingface等平台上,国内容易timeout
3、过去业务常用的docker镜像承重不了模型动辄几十g的容量,例如flux.1的环境中,torch+flux.1的模型最低也要32g的大小,按照传统运维流程,再精简也无法做到常见业务镜像几十m的体积
除了生产环境,个人如果想要体验下最新的模型,但没有4090。或者4090目前正在炼丹,临时需要再来一套环境把玩,都可以采用下面的方案进行。
根据以上场景,大模型的生产场景需要有不同于过去的运维方案,docker装天下的时代在大模型这里不适用了。
下面的示例用腾讯云进行示例(@腾讯云 请打广告费),其中用到的功能在所有云服务厂商都有,可以任意选择成本最低的一家。但是要说明的是尽量选择通用型的云服务厂商,虽然有的垂直领域GPU服务商的成本可能更低,但网络、ip、存储的稳定性也是在生产环境中比较重要的部分。
下面均采用comfyui作为推理示例。
场景一:个人及公司内部ai应用
内部使用的场景中,是典型的分时场景,下班了就没人用,并且还有几个优先级较高的特点:
成本尽可能低
允许一定程度的推理延迟,例如标准推理3秒,这个场景下10秒也可以
方案:
使用竞价实例降低成本。竞价实例有时候会遇到没机器的情况,大概1月1次
监控任务使用来定时删除机器
环境和数据都保留在数据盘中,机器的删除对环境无影响
按需使用,如果没人使用只有硬盘费用
成本试算:
按flux.1-dev bf16所需的32g显存V100机型计算,3.754每小时(价格是腾讯云法兰克福)。工作日21天,每天9-12点,13-18点共8个小时,成本共计
如果在公司或家里放一台4090的电脑(按500w计算),仅电费(1元1度计算)
电脑也可以定时或者按需开关机,但是稳定性和云服务毕竟没法比
构建流程:
1. 构建数据盘
将comfyui、conda环境、模型文件放入一个独立的数据盘中。还是拿flux.1为例,默认开100g应该够了(需要留一些给输出结果、custom node及依赖),在云厂商的环境下,扩容是很方便的
创建一个sh文件,为comfyui自动启动做准备
#!/bin/bash
while true
do
/data/anaconda3/envs/flux/bin/python /data/ComfyUI/main.py --listen=0.0.0.0
done
2. 构建启动镜像
主要的执行数据都在数据盘中了,所以只需要一个包含gpu驱动的的启动镜像。腾讯云40g以下都是一个价,所以选40g就好了。
启动镜像的制作,不需要安装conda之类的环境,仅需要增加一个开机启动项,默认启动固定目录下的一个文件就好,这样方便数据盘将来进行变更。
数据盘默认挂载在/data目录下。
3. 构建任务输入输出端
虽然comfyui有队列功能,但还是需要做一个接口/界面进行任务接入及结果输出,因为在有任务时需要调用接口启动推理节点
4. 构建自动销毁任务
几乎可以免费使用函数计算进行定时任务,账户免费的额度完全足够此场景使用。
const secretId= "你的id";
const secretKey= "你的key";
const region= "地区名称";
const client = (endpoint, version) => {
const clientConfig = {
credential: {
secretId: secretId,
secretKey: secretKey,
},
region: region,
profile: {
httpProfile: {
endpoint: endpoint,
},
},
};
return new CommonClient(
endpoint,
version,
clientConfig
);
}
function params(type, cvmid) {
return {
"Namespace": "QCE/CVM",
"MetricName": type,
"Period": 300,
"Instances": [
{
"Dimensions": [
{
"Name": "InstanceId",
"Value": cvmid
}
]
}
],
//可以调整监控的时间
"StartTime": moment().subtract(30, 'minutes').format('YYYY-MM-DDTHH:mm:ss+00:00'),
"EndTime": moment().format('YYYY-MM-DDTHH:mm:ss+00:00'),
}
}
const isInUse = async (cvmid) => {
//只监控了cpu和gpu的使用量,多数场景下够了
const result = await Promise.all(['CPUUsage', 'Gpuutil'].map(async (type) => {
const result = await client("monitor.tencentcloudapi.com", '2018-07-24').request("GetMonitorData", params(type, cvmid))
console.log(type, JSON.stringify(result))
return result.DataPoints[0].Values.filter(v => v > 5).length > 0
}))
return result.filter(v => v).length > 0
}
const moment = require('moment');
//选择特定的标签,不影响其他环境使用
const params = {
"Filters": [
{
"Values": [
"testing"
],
"Name": "tag-key"
},
],
};
const main = async () => {
const vms = await client("cvm.tencentcloudapi.com", "2017-03-12").request("DescribeInstances", params)
const instance = vms.InstanceSet[0];
if (moment().diff(moment(instance.CreatedTime), 'minute') < 30) {
console.log('cvm create less than 30 min')
return
}
const id = instance.InstanceId
const inUse = await isInUse(id)
console.log(`isUse result ${inUse}`)
if (inUse === false) {
console.log(`deleted ${id}`)
console.log(await client("cvm.tencentcloudapi.com", "2017-03-12").request("TerminateInstances", {
InstanceIds: [id]
}))
}
}
if (require.main === module) {
(async () => {
await main()
})()
}
module.exports = main
部署后设置每x分钟运行一次,如果机器在设置的30分钟内都没人使用,会自动删除机器+系统盘,但数据盘完好无损。
5. 构建启动任务
const TencentCloudCommon = require("tencentcloud-sdk-nodejs-common");
const CommonClient = TencentCloudCommon.CommonClient
const secretId= "你的id";
const secretKey= "你的key";
const region= "地区名称";
const imageId = "系统盘镜像id";
const dataDiskId = "数据盘id";
const password = "机器密码";
const zone = ""; //实际是地区名称+可用区名称
const InstanceType = "GN10X.2XLARGE40";
const SecurityGroupIds = [""];//安全组id
const client = (endpoint, version) => {
const clientConfig = {
credential: {
secretId: secretId,
secretKey: secretKey,
},
region: region,
profile: {
httpProfile: {
endpoint: endpoint,
},
},
};
return new CommonClient(
endpoint,
version,
clientConfig
);
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const params = {
"Filters": [
{
"Values": [
"testing"
],
"Name": "tag-key"
},
],
};
const main = async () => {
const vms = await client("cvm.tencentcloudapi.com", "2017-03-12").request("DescribeInstances", params)
if (vms.InstanceSet.length > 0)
return { result: false, ip: vms.InstanceSet[0].PublicIpAddresses[0], msg: 'already exists' }
const created = await client("cvm.tencentcloudapi.com", "2017-03-12").request("RunInstances", {
"InstanceChargeType": "SPOTPAID",
"Placement": {
"Zone": zone
},
"InstanceType": InstanceType,
"ImageId": imageId,
"SystemDisk": {
"DiskType": "CLOUD_SSD",
"DiskSize": 40
},
"InternetAccessible": {
"InternetMaxBandwidthOut": 100,
"PublicIpAssigned": true
},
"LoginSettings": {
"Password": password
},
"SecurityGroupIds": SecurityGroupIds,
"TagSpecification": [
{
"ResourceType": "instance",
"Tags": [
{
"Key": "testing",
"Value": "1"
}
]
}
]
})
let vm = null;
const id = created.InstanceIdSet[0]
let i = 0
while (i++ < 10) {
//等一下,有延迟
await sleep(3000)
const vms = await client("cvm.tencentcloudapi.com", "2017-03-12").request("DescribeInstances", {
InstanceIds: [id]
})
vm = vms.InstanceSet[0];
if (vms.InstanceSet[0].InstanceState == 'RUNNING') {
break;
}
}
console.log(vm)
const ip = vm.PublicIpAddresses[0]
const disk = await client("cbs.tencentcloudapi.com", "2017-03-12").request("AttachDisks", {
"InstanceId": id,
"DiskIds": [
dataDiskId
]
})
console.log(disk)
return { result: true, ip: ip, msg: 'success' }
}
if (require.main === module) {
(async () => {
console.log(await main())
})()
}
module.exports = main
同样部署为一个web类型的云函数,每次调用可以获取到机器的ip,方便调用接口。
另外,可以在这个流程中增加自动更新dns的操作,这样以后统一访问一个域名就可以了
以上就完成了自动开关机的部署,在享受32g vram的同时保持了较低的成本
场景二:商用某c端应用
上面的场景中描述的方法是单机应用的方法,只会开单节点进行推理,在商业场景下调用量更加不均匀,需要能够承载更大业务量才行。
以上的基础逻辑不变,但是对于并发问题,需要有更加”云“的解决方案:
更高的并发度
更高效的资源管控。结果产物、模型数据等
更高效的部署模式,有模型变更时可以保证版本确定
方案:
采用上面的系统盘+数据盘的模式,但数据盘需要是nfs,方便并行开机
结果产物上传对象存储,保持数据盘的文件版本稳定。数据盘需要全球同步,因为一个地区的竞价实例很可能无货
做好日志收集,可以采用”传统“的filebeat之类的文件监控方案
构建流程:
1. 构建数据盘
数据盘需要用nfs,建议使用对象存储作为nfs的后端,虽然很慢。公有云基本都提供了对象存储作为nfs的方法,对象存储在内网调用没有网络费用,虽然速度比较慢,但这个场景对于速度其实没有太多要求,模型是第一次就载入了内存或显存的(特别是在商用场景下)
对象存储需要在每个目标可用区都增加桶,并配置自动同步,因此结果产物不能放在数据盘中,会增加同步负担。
2. 构建启动镜像
启动镜像和上面一样,不需要有什么变化。构建好的镜像可以进行全球分发,保证在每个可用区都有实例存在
3. 构建任务输入输出端
这一步也和上面一样
4. 构建自动销毁任务
上面的代码只检查了第一个结果,只考虑了有一个实例的情况。在商用场景下需要循环检查所有实例
5. 构建启动任务
启动任务需要判断队列长度,寻找可购买的可用区,补足实例数量。这里也可以不采用接口的部署方式,直接用定时任务检查队列长度即可
以上就完成了商业版本的部署,在低成本的前提下开启了全自动全球开机的可能性。
最后
GPU推理服务和传统的业务在运维层面差异较大,运维技能急需提升以适应新的环境,更希望能看到有k8s这样的通用解决方案出现。然而目前,在大模型应用爆发的前夕,我们也可以优化运维体验来降低成本。
ps:封面图片是flux.1-pro生成的
53AI,企业落地应用大模型首选服务商
产品:大模型应用平台+智能体定制开发+落地咨询服务
承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-07-11
2024-07-11
2024-07-09
2024-09-18
2024-06-11
2024-07-23
2024-07-20
2024-07-12
2024-07-26
2024-07-23
2024-11-18
2024-11-16
2024-11-16
2024-10-31
2024-10-31
2024-10-27
2024-10-26
2024-10-25