全部产品
云市场

Python 运行环境

更新时间:2019-07-05 15:33:47

函数计算目前支持以下 Python 运行环境:

  • Python 2.7 ( runtime = python2.7 )
  • Python 3.6 ( runtime = python3 )

本文分以下几个部分对 Python 的运行环境进行介绍:

使用logging

函数向 stdout 打印的内容会被收集到创建 service 时指定的 Logstore 中,有两种打 log 的方法:

  1. 直接使用print: 这会把内容原样地输出到日志中。

    1. def my_handler(event, context):
    2. print 'hello world'
    3. return 'done'

    上面的代码输出的日志内容是:

    1. message:hello world
  2. 使用 logging 模块: 这会在每条日志中包含时间 / requestId / 日志级别等信息。

    1. import logging
    2. def my_handler(event, context):
    3. logger = logging.getLogger()
    4. logger.info('hello world')
    5. return 'done'

    上面的代码输出的日志内容是:

    1. message:2017-07-05T05:13:35.920Z a72df088-f738-cee3-e0fe-323ad89118e5 [INFO] hello world

推荐使用 logging 模块打印 log,自动包含 requestId 能信息方便在出错的时候定位问题日志。

使用内置模块

除了 Python 的标准模块,函数计算的 Python 运行环境中还包含了一些常用模块,用户可以直接引用,目前包含的模块有:

模块名称 模块介绍 相关链接
oss2 2.6.0 OSS SDK https://github.com/aliyun/aliyun-oss-python-sdk
tablestore 4.6.0 表格存储 SDK https://github.com/aliyun/aliyun-tablestore-python-sdk
aliyun-fc2 2.1.0 函数计算 SDK https://github.com/aliyun/fc-python-sdk
aliyun-python-sdk-ecs 4.10.1 云服务器 SDK https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-ecs
aliyun-python-sdk-vpc 3.0.2 专用网络 SDK https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-vpc
aliyun-python-sdk-rds 2.1.4 云数据库 SDK https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-rds
aliyun-python-sdk-kms 2.5.0 密钥管理服务 SDK https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-kms
pydatahub 2.11.2 DataHub SDK https://github.com/aliyun/aliyun-datahub-sdk-python
aliyun-mns 1.1.5 消息服务 https://github.com/gikoluo/aliyun-mns
aliyun-python-sdk-cdn 2.6.2 CDN 服务 https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-cdn
aliyun-python-sdk-ram 3.0.0 访问控制 RAM https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-ram
aliyun-python-sdk-sts 3.0.0 访问控制 STS https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-sts
aliyun-python-sdk-iot 7.8.0 物联网平台IOT https://github.com/aliyun/aliyun-openapi-python-sdk/tree/master/aliyun-python-sdk-iot
aliyun-log-python-sdk 0.6.38 日志服务 SLS https://github.com/aliyun/aliyun-log-python-sdk
wand 0.4.4 图片处理库 http://docs.wand-py.org/en/0.4.4/
opencv 3.3.0.10 计算机视觉库 http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_setup/py_intro/py_intro.html
numpy 1.13.3 科学计算库 http://www.numpy.org/
scipy 1.0.0 科学计算库 https://www.scipy.org/
matplotlib 2.0.2 绘图库 https://matplotlib.org/
scrapy 1.4.0 数据抓取库 https://scrapy.org/

例如使用 wand 进行图片翻转的函数如下:

  1. from wand.image import Image
  2. def my_handler(event, context):
  3. with Image(blob=event) as img:
  4. print img.size
  5. with img.clone() as i:
  6. i.rotate(180)
  7. return i.make_blob()

注意:上面的函数直接使用 event 作为图片的二进制数据并且直接把生成的图片作为二进制数据返回。

tips: 还有其他第三方库使用的小 demo 可以查看这里 fc-python-demo

使用自定义的模块

如果用户需要使用自定义的模块,则需要将它们与代码一起打包。下面通过 Fun 来介绍添加访问 mysql 模块 PyMySQL 的步骤:

  1. 建立一个目录用于存放代码和依赖模块:

    1. mkdir /tmp/code
  2. 新建代码文件,例如 /tmp/code/index.py,在代码中使用 pymysql:

    1. import pymysql.cursors
    2. connection = None
    3. # my_initializer: initializer function
    4. def my_initializer(context):
    5. global connection
    6. connection = pymysql.connect(host='localhost',
    7. user='user',
    8. password='passwd',
    9. db='db',
    10. charset='utf8mb4',
    11. cursorclass=pymysql.cursors.DictCursor)
    12. def my_handler(event, context):
    13. global connection
    14. with connection.cursor() as cursor:
    15. # Read a single record
    16. sql = "SELECT count(*) FROM `users`"
    17. cursor.execute(sql)
    18. result = cursor.fetchone()
    19. print(result)
    20. return result
    • 为什么将连接数据库的操作放入 initializer 中?

      Initializer 保证在函数实例的生命周期内成功执行且只执行一次。建立数据库连接的操作属于应用层面的冷启动开销,为维持此连接在函数实例的生命周期内有效,且不因后续多次请求而重复建立连接带来的冷启动开销,故将此操作放在 initializer 中,避免了应用层冷启动开销影响函数性能。

    • 为什么使用 initializer 比使用 global variable 方式来初始化更好?

      Initializer 可以保证在 invoke 请求到来之前执行成功且在函数实例生命周期内仅执行一次,使用 global variable 方式只保证了后一点,在请求触发新的函数实例/代码升级/FC系统周期性回收或更新容器时仍避免不了冷启动开销的影响。

  3. 新建 template.yml 文件,例如 /tmp/code/template.yml,内容为:

    1. ROSTemplateFormatVersion: '2015-09-01'
    2. Transform: 'Aliyun::Serverless-2018-04-03'
    3. Resources:
    4. FunDemo:
    5. Type: 'Aliyun::Serverless::Service'
    6. pythondemo:
    7. Type: 'Aliyun::Serverless::Function'
    8. Properties:
    9. Handler: index.handler
    10. Runtime: python3
    11. CodeUri: './'
  4. 在 /tmp/code 目录下安装依赖。

    如果没有 docker 环境,且不涉及动态链接库(.so)、编译二进制程序等,只是安装语言依赖,那么可以直接使用 pip install -t . PyMySQL 的方式进行安装。这种方式不管函数计算的执行环境中是否安装了这些 python 库,都会下载下来,会增加代码包的大小。

    但是如果有 docker 环境(PS:如果是 windows 系统,要求使用 docker for windows),那么推荐您总是使用 Fun 安装第三方库本地运行调试发布函数,这样能避免由于开发环境和运行环境不一致所引起的一些问题。特别是当您的函数依赖二进制、动态链接库文件时,请务必使用 Fun 编译相关依赖。

    使用 Fun 安装 PyMySQL 的方法很简单,只需要在项目目录下(本例是 /tmp/code)执行下面的命令:

    1. fun install --runtime python3 --package-type pip PyMySQL

    使用 Fun 注意事项

    1. Fun 命令要求您的机器上已安装 docker。docker 的具体安装步骤,请参阅 相关文档
    2. Fun 用到的镜像存储在 docker 官方镜像库上,国内用户访问速度较慢。建议您使用阿里云镜像加速服务,具体设置请参阅 相关文档
    3. 在 linux 下使用 docker,可能要求 root 权限。所以您需要使用 sudo 的方式启动命令行工具;或者您可以参照 相关文档 设置,以非 root 用户管理 docker。

    安装完成之后,/tmp/code目录的内容应该是这样:

    1. $ ls -al /tmp/code
    2. drwxr-xr-x 3 tan wheel 96 Jun 18 17:08 .fun
    3. -rw-r--r-- 1 tan wheel 560 Jun 18 16:13 index.py

    可以看到,多了一个 .fun 目录,该目录就存放了我们刚才安装的 PyMySQL 的依赖。

    如果安装遇到了问题,通过指定 -v 参数,可以看到更详尽的日志:

    1. $ fun install -v --runtime python3 --package-type pip PyMySQL
    2. skip pulling image aliyunfc/runtime-python3.6:build-1.5.3...
    3. Task => [UNNAMED]
    4. => PYTHONUSERBASE=/code/.fun/python pip install --user PyMySQL
    5. Looking in indexes: http://mirrors.aliyun.com/pypi/simple/
    6. Collecting PyMySQL
    7. Downloading http://mirrors.aliyun.com/pypi/packages/ed/39/15045ae46f2a123019aa968dfcba0396c161c20f855f11dea6796bcaae95/PyMySQL-0.9.3-py2.py3-none-any.whl (47kB)
    8. Installing collected packages: PyMySQL
    9. Successfully installed PyMySQL-0.9.3

    fun isntall 的更多用法,请参考这篇文章

  5. 使用 Fun 进行部署

    1. fun deploy

    提示:如果需要安装动态链接库(.so 文件),也可以使用 fun install 来完成,比如安装 libzbar0,可以直接执行:

    1. fun install --runtime python3 --package-type apt libzbar0

    命令执行完成后,依赖依旧是会被安装到 .fun 目录下,只需要通过 fun 进行本地调试、部署即可使用。

    注意: 因为依赖被安装在了.fun 目录,请确保 template.yml 中的 CodeUri 包含该目录,且必须是顶级子目录。

调用外部命令

用户的函数可能会用到一些工具,而这些工具并不是用 Python 写的(例如 shell 脚本 / C++ 或者 go 编译出来的可执行文件)。用户仍然可以将它们与代码一起打包,然后在函数中通过运行外部命令的方法来使用它们。下面的例子中演示了如何运行一个 shell 脚本:

  1. import os
  2. import subprocess
  3. def my_handler(event, context):
  4. script_path = os.environ.get('FC_FUNC_CODE_PATH') + '/script.sh'
  5. ret = subprocess.check_output(['bash', script_path])
  6. return ret

需要注意的是,使用 C / C++ / go 编译出来的可执行文件,需要与函数计算的运行环境兼容。函数计算的 Python 运行环境是:
  • Linux 内核版本:Linux 4.4.24-2.al7.x86_64
  • docker 基础镜像:docker pull python:2.7 ; docker pull python:3.6

推荐使用 Fun 工具,下面以 runtime 为 python2.7,安装 mysql-python (含有 .so 文件)为例:

可以直接在代码目录执行 fun install --runtime python2.7 --package-type pip mysql-python 即可安装好依赖(注:第一次执行这个命令由于要 pull image ,可能要耗费不少时间,请耐心等待…)。依赖(含有 mysql.so)会被装在 .fun 目录下,只需要在 template.yml 中通过 CodeUri 属性配置将 .fun 以及代码包含进来就可以了。

错误处理

函数在执行过程如果抛出异常,那么函数计算会把异常捕获并将异常信息返回。例如下面的代码:

  1. def my_handler(event, context):
  2. raise Exception('something is wrong')

调用时收到的响应为:

  1. {
  2. "errorMessage": "something is wrong",
  3. "errorType": "Exception",
  4. "stackTrace": [
  5. [
  6. "File \"/code/index.py\"",
  7. "line 2",
  8. "in my_handler",
  9. "raise Exception('something is wrong')"
  10. ]
  11. ]
  12. }

发生异常时,函数调用的响应的 HTTP header 中会包含 X-Fc-Error-Type: UnhandledInvocationError。关于函数计算的错误类型请参考文章 错误类型

您在使用过程中遇到任何问题请 联系我们