AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


超低成本的生产GPU环境-大模型推理业务的运维优化
发布日期:2024-09-26 18:13:42 浏览次数: 1586


本文主要讨论GPU的生产环境如何降低成本,和各类saas服务、免费账号等不是一个维度。

生产环境中,通常会遇到几个问题:

  1. 1、业务不稳定。例如早上高峰期队列非常高,而凌晨又几乎没有流量

  2. 2、大模型的基建通常在github、huggingface等平台上,国内容易timeout

  3. 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 truedo/data/anaconda3/envs/flux/bin/python /data/ComfyUI/main.py --listen=0.0.0.0done


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 = 0while (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+中大型企业

联系我们

售前咨询
186 6662 7370
预约演示
185 8882 0121

微信扫码

与创始人交个朋友

回到顶部

 
扫码咨询