使用 initializer 接口解决函数计算上传代码包大小受限问题

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
函数计算FC,每月15万CU 3个月
简介:

背景

未引入初始化函数功能前如何解决函数计算对上传代码包大小的限制问题?

阿里云 函数计算 为了保证函数计算系统的可用性更高,对用户上传的代码包做了 大小限制,要求原始代码包大小不超过 250MB,压缩后的代码包大小不超过 50MB。由于用户的函数逻辑可能需要大量的依赖库,所以代码包很容易达到函数计算设定的阈值。在未引入 initializer 接口之前,为解决这个问题,需要对代码包进行分类,除项目代码和少量依赖库可以在创建函数时上传,其他依赖库都需要预先上传到 OSS 中,当函数被触发时,再从 OSS 上下载并存放到磁盘指定目录。

解决了函数计算对代码包大小的限制后对用户有什么影响?

用户将部分依赖库预先上传到 OSS,并在函数被触发执行时开始从 OSS 上加载依赖, 这类依赖的加载操作均可定义应用层冷启动,当加载依赖结束后,应用层冷启动才结束,函数的处理逻辑才开始执行,应用层冷启动的开销往往会导致毛刺的产生,影响函数的性能。

如何做到既可以解决函数计算对上传代码包的限制问题,又不影响函数的性能呢?

为同时解决上述功能和性能问题,函数计算推出 initializer 功能。您可以将从 OSS 加载代码包的函数逻辑放在 initializer 函数中。函数计算保证在处理函数执行之前,initializer 函数成功执行过一次并且只有一次,从而可以保证在处理请求到达之前代码已全部加载完成,并且不会对处理函数逻辑的功能和性能产生影响。

案例实践

大部分场景中,用户代码包过大都是由于所依赖的库过大导致的,下文通过 python2.7 演示一个对图片旋转的案例。

图片旋转需要用到 tensorflow 和 opencv 库,两个库的大小还不及函数计算设置上传代码包大小的阈值,这里我们可假设已经超过了代码包大小的限制。

上传依赖库

本案例需要安装的依赖库有 tensorflow 和 opencv-python,需要提前在本地下载并打包上传到 OSS。推荐使用 fcli 工具的 sbox 命令,下面以 runtime 为 python2.7 进行操作:

cd <'此项目的根目录中'>
mkdir applib      // 创建存储所有应用依赖的目录
fcli shell        // fcli version >= 0.25
sbox -d applib -t python2.7
pip install -t $(pwd) tensorflow==1.8.0
pip install -t $(pwd) opencv-python

完成之后 exit 退出沙盒环境,并执行 exit 退出fcli。

编写函数

编写函数需要注意以下几点:

  • 定义 initializer 入口并实现其接口,将从 OSS 加载代码包的逻辑放入初始化函数中。
  • 由于对图片操作所用到的两个库需要在初始化函数中临时加载,所以可以把对函数处理的操作单独放到一个文件中,在处理函数中调用即可。
    综合上面两点考虑,我们的代码结构如下:
project root
└─ code
        ├─ loader.py     # 处理函数逻辑和 initializer 逻辑
        └─ index.py      # 图片旋转逻辑
        └─ pic
                 └─ e2.jpg  # 被操作图片

loader.py 代码如下:

# -*- coding:utf-8 -*-
import sys
import zipfile
import os
import oss2
import imp
import time

app_lib_object = os.environ['AppLibObject']
app_lib_dir = os.environ['AppLibDir']
model_object = os.environ['ModelObject']
model_dir = os.environ['ModelDir']

local = bool(os.getenv('local', ""))
print 'local running: ' + str(local)

def download_and_unzip(objectKey, path, context):
    creds = context.credentials
    if (local):
        print 'thank you for running function in local!!!!!'
        auth = oss2.Auth(creds.access_key_id,
                         creds.access_key_secret)
    else:
        auth = oss2.StsAuth(creds.access_key_id,
                            creds.access_key_secret,
                            creds.security_token)

    endpoint = os.environ['Endpoint']
    bucket = os.environ['Bucket']

    print 'objectKey: ' + objectKey
    print 'path: ' + path
    print 'endpoint: ' + endpoint
    print 'bucket: ' + bucket

    bucket = oss2.Bucket(auth, endpoint, bucket)

    zipName = '/tmp/tmp.zip'

    print 'before downloading ' + objectKey + ' ...'
    start_download_time = time.time()
    bucket.get_object_to_file(objectKey, zipName)
    print 'after downloading, used %s seconds...' % (time.time() - start_download_time)

    if not os.path.exists(path):
        os.mkdir(path)

    print 'before unzipping ' + objectKey + ' ...'
    start_unzip_time = time.time()
    with zipfile.ZipFile(zipName, "r") as z:
        z.extractall(path)
    print 'unzipping done, used %s seconds...' % (time.time() - start_unzip_time)

def initializer(context):
    if not local:
        download_and_unzip(app_lib_object, app_lib_dir, context)
        download_and_unzip(model_object, model_dir, context)
    sys.path.insert(1, app_lib_dir)

def handler(event, context):
    desc = None
    fn, modulePath, desc = imp.find_module('index')
    mod = imp.load_module('index', fn, modulePath, desc)
    request_handler = getattr(mod, 'handler')
    return request_handler(event, context)

index.py 代码如下:

# -*- coding:utf-8 -*-

import cv2
import oss2
import tensorflow as tf

def handler(event, context):
    filename="pic/e2.jpg"
    image = cv2.imread(filename, 1)
    x = tf.Variable(image, name='x')
    model = tf.initialize_all_variables()
    with tf.Session() as session:
        x = tf.transpose(x, perm=[1, 0, 2])
        session.run(model)
        result = session.run(x)

    cv2.imwrite("/tmp/pic.jpg", result)
    cv2.waitKey (0)
    auth = oss2.Auth(<'Your access_key_id'>, <'Your access_key_secret'>)
    bucket = oss2.Bucket(auth, <'Your endpoint'>, <'Your bucket'>)
    bucket.put_object_from_file('picture', '/tmp/pic.jpg')
    return 'success'

pic/e2.jpg 如下:
image.png

部署

你可以通过 SDKAPIfun控制台 等多种方式进行部署,这里直接通过控制台上传代码包。

  • 首先选择一个 region 并创建一个函数。
  • 在新建函数的代码配置中选择文件夹上传,点击你的项目目录全部上传即可。
    image.png
  • 在环境配置中配置初始化函数,将函数入口和 initializer 入口分别设置为 loader.handlerloader.initializer,并配置合理的处理函数超时时间和初始化超时时间,配置结束后点击下一步即可。
    image.png

测试

功能测试

  • 在函数计算控制台执行函数,发现当引入 initializer 功能后,首次执行时间为 3205ms:
    image.png
  • 为验证函数是否执行成功,我们到 OSS 控制台查看指定 bucket 下面是否存在名为 picture 的 object,并验证是否旋转成功,图片如下,由此可见函数执行成功。
    image.png

性能测试

为验证 initializer 函数不仅可以解决上传代码包大小受限的问题,且可以规避加载代码包的冷启动时间,下面将对函数进行改造,关闭初始化功能。

  • 在函数概览页面中点击修改函数,并关闭 开启初始化功能 按钮:
    image.png
  • 将 initializer 函数中的逻辑放到到处理函数中,并删除原有初始化函数,点击执行查看运行结果:
    image.png
  • 函数产生日志如下:
    image.png

从上图中可以发现,在未引入 initializer 函数之前,首次函数函数执行时间约为 14159ms,从 OSS 加载过大的代码包会占用大量的冷启动时间,并且影响函数的性能。

总结

从测试的结果可以发现,initializer 接口的引入解决了文章开篇提到的问题。即解决函数计算对上传代码包的限制问题,又规避了加载代码包的冷启动时间,极大的提升函数性能。

最后欢迎大家通过扫码加入我们用户群中,使用过程中有问题或者有其他问题可以在群里提出来。函数计算官网客户群(11721331)。

相关实践学习
【文生图】一键部署Stable Diffusion基于函数计算
本实验教你如何在函数计算FC上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。函数计算提供一定的免费额度供用户使用。本实验答疑钉钉群:29290019867
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
目录
相关文章
|
2月前
|
消息中间件 JavaScript 中间件
函数计算产品使用问题之WebIDE编写的Node.js代码是否会自动进行打包部署
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
2月前
|
存储 运维 Serverless
函数计算产品使用问题之如何解决代码需要多个gpu的问题
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
2月前
|
存储 运维 Serverless
函数计算产品使用问题之代码上传记录如何查看
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
3月前
|
弹性计算 Java Serverless
Serverless 应用引擎操作报错合集之上传自定义JAR包,启动时报错,是什么导致的
Serverless 应用引擎(SAE)是阿里云提供的Serverless PaaS平台,支持Spring Cloud、Dubbo、HSF等主流微服务框架,简化应用的部署、运维和弹性伸缩。在使用SAE过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
3月前
|
缓存 运维 Java
函数计算产品使用问题之如何在函数代码的根目录中执行命令
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
3月前
|
存储 固态存储 Serverless
函数计算操作报错合集之创建云函数并设置代码从Bucket获取时,返回403错误,该如何解决
Serverless 应用引擎(SAE)是阿里云提供的Serverless PaaS平台,支持Spring Cloud、Dubbo、HSF等主流微服务框架,简化应用的部署、运维和弹性伸缩。在使用SAE过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
3月前
|
负载均衡 Serverless API
函数计算操作报错合集之如何解决上传二进制包时报错: "Permission denied (os error 13)"
在使用函数计算服务(如阿里云函数计算)时,用户可能会遇到多种错误场景。以下是一些常见的操作报错及其可能的原因和解决方法,包括但不限于:1. 函数部署失败、2. 函数执行超时、3. 资源不足错误、4. 权限与访问错误、5. 依赖问题、6. 网络配置错误、7. 触发器配置错误、8. 日志与监控问题。
|
3月前
|
弹性计算 Serverless API
函数计算产品使用问题之如何安装插件和配置依赖包
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
1月前
|
人工智能 自然语言处理 Serverless
阿里云函数计算 x NVIDIA 加速企业 AI 应用落地
阿里云函数计算与 NVIDIA TensorRT/TensorRT-LLM 展开合作,通过结合阿里云的无缝计算体验和 NVIDIA 的高性能推理库,开发者能够以更低的成本、更高的效率完成复杂的 AI 任务,加速技术落地和应用创新。
109 14
|
2月前
|
机器学习/深度学习 机器人 Serverless
FaaS 的应用场景
FaaS 的应用场景

相关产品

  • 函数计算