将 Django 框架部署到腾讯云云函数

2022/08/03 posted in  Serverless
Tags:  #云函数 #实践

一直想尝试云函数,于是抽空将之前写的一个 Django 项目部署到腾讯云云函数上,顺便体验了一把腾讯云的 CODING DevOps。
主要参照官方文档,过程中踩了一些坑。
最后比较发现不同部署方法冷启动时间相差较大

  • 通过容器镜像部署,冷启动时间达到8秒(不能接受),瓶颈出在拉取镜像和 Django 启动
  • 通过代码文件部署,冷启动则降到了2秒,瓶颈出在 Django 启动,换用 Flask 应该会更快

当有请求到来而又没有可用的云函数实例时,会新建一个实例,称为冷启动,之后会持续处理请求,直到空闲一定时间后销毁。如果冷启动的时间够短,面对大量请求到来时,便能快速启动多个实例进行处理响应,适应高并发场景。
云函数是无状态的,要保存状态需要借助外部数据库。

下面分别是手动部署和利用 CODING DevOps 自动集成部署云函数的记录。

通过手动上传 zip 文件部署

本地新建 Django 项目

参考官方文档,新建并部署一个 Django 项目

  1. 执行以下命令,确认您本地的环境已安装好 Django。
    python -m pip install Django
    
  2. 在本地创建 Hello World 示例项目并进入项目目录。
    django-admin startproject helloworld && cd helloworld
    
  3. 在本地执行 python manage.py runserver 命令运行。示例如下:
    $ python manage.py runserver
    July 27, 2021 - 11:52:20
    Django version 3.2.5, using settings 'helloworld.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CONTROL-C.
    
  4. 打开浏览器访问 http://127.0.0.1:8000 ,即可在本地完成 Django 示例项目的访问。如下图所示:

安装依赖

  1. 安装依赖包
    1. 建议在 Linux 环境下用 Python 3.6、3.7 版本进行,因为云函数的运行环境是 CentOS 7,目前仅支持 Python 3.6、3.7 版本。
    2. 由于 SCF 云上标准环境内未提供 Django 依赖库,此处您必须将依赖文件安装完成后,与项目代码一起打包上传。请先新建 requirements.txt 文件,在其中填写项目依赖,文件内容如下:
      Django
      
    3. 执行以下命令安装依赖:
      -r 从指定文件读取依赖
      -t 将依赖安装到指定目录
      pip install -r requirements.txt -t .
      
  2. 新增 scf_bootstrap 启动文件
    在 Web 函数内,限制了监听端口必须为 9000,因此需要修改监听地址端口,在项目根目录下新建 scf_bootstrap 启动文件,在该文件添加如下内容(用于完成环境变量配置,指定服务启动命令等自定义操作,确保您的服务可以通过该文件正常启动):
    #!/bin/bash
    /var/lang/python3/bin/python3 manage.py runserver 0:9000
    
    注:如果上一步安装依赖时使用 Python 3.7,则需要将 /var/lang/python3/bin/python3 替换为 /var/lang/python37/bin/python3,因为前者是云函数环境中的 Python 3.6 解释器,后者是 3.7,不一致会导致依赖导入失败。这是我踩的一个坑。
    详情见 标准语言环境绝对路径
  3. 创建完成后,还需执行以下命令修改文件可执行权限,默认需要 777 或 755 权限才可以正常启动。示例如下:
    chmod 777 scf_bootstrap
    
  4. 本地配置完成后,执行以下命令启动服务(如下命令为在 scf_bootstrap 目录下执行时示例),确保您的服务在本地可以正常启动。

    本地测试时注意将 Python 路径改为本地路径。

    ./scf_bootstrap
    
  5. 将项目文件夹采用 zip 格式压缩,压缩包内的根目录要包含项目文件、依赖文件、启动文件,不要多余出目录。

部署上云

  1. 登录 Serverless 控制台,单击左侧导航栏的函数服务。
  2. 在主界面上方选择期望创建函数的地域,并单击新建,进入函数创建流程。
  3. 选择自定义创建新建函数,根据页面提示配置相关选项。如下图所示:

    函数类型:选择 “Web函数”。
    函数名称:填写您自己的函数名称。
    地域:填写您的函数部署地域,例如成都。
    运行环境:选择 “Python 3.7”。
    提交方法:选择 “本地上传zip包”,上传您的本地项目。
    其他可保持默认

查看云函数链接

  1. 云函数创建完成后,点击函数名称,会进入到函数管理--函数代码页面,在 Web IDE 中可以看到刚刚上传的代码,IDE 中的运行环境可以认为和云函数运行环境一致,云函数启动有问题的时候,可以在这里进行调试。
  2. 函数代码页面下滑,能看到云函数的外部链接,发到外部链接的请求会透传到云函数环境的 9000 端口,这也是我们要监听 9000 端口的原因。

通过 CODING DevOps 自动集成部署

创建代码仓库并推送代码

  1. 在腾讯云控制台上方搜索 CODING DevOps 并进入到 CODING DevOps
  2. 点击项目创建项目,进行项目创建
  3. 进入创建的项目,点击代码仓库创建代码仓库
    和 GitHub 的操作一致,创建一个仓库,并将本地 Django 代码推送上去

创建构建计划

  1. 点击持续集成构建计划创建构建计划自定义构建过程
    代码仓库选择刚刚创建的仓库,其他可保持默认,点击确定创建并跳转到流程配置
  2. “2-1自定义构建过程” 中添加3个步骤实现自动构建部署,分别是:
    1. 打包 zip 文件
      添加构建步骤 “执行 Shell 脚本”,内容如下

      # 默认的构建环境包含了多种工具,比如 Python 3.7
      # 安装依赖
      python3.7 -m pip install -r requirements.txt -t .
      
      # 添加启动文件
      echo '#!/bin/bash
      /var/lang/python37/bin/python3 manage.py runserver 0:9000' > scf_bootstrap
      chmod 777 scf_bootstrap
      
      # 压缩
      zip -r 1.zip *
      
    2. 上传到腾讯云对象存储
      直接用 SDK 上传 zip 文件更新云函数,会对 zip 文件大小有限制
      所以选择先上传到对象存储,再用 SDK 从对象存储更新云函数
      如果没有对象存储桶,则要先在腾讯云控制台中创建一个
      添加构建步骤 “COS - 上传文件/文件夹”

      本地路径:$WORKSPACE 表示构建环境中的当前路径,1.zip 是上一步生成的压缩文件
      远程路径:上传到对象存储桶的路径,以 / 开头
      Bucket名:对象存储桶的名字
      Bucket地域:对象存储桶所在的地域
      SecretId:腾讯云的密钥id,在“账号信息”、“访问管理”、“访问密钥”中可以创建
      SecretKey:腾讯云的密钥

    3. 利用 SDK 从对象存储更新云函数
      通过腾讯云 API 浏览器,找到云函数中的更新函数代码,填写“Region”、“FunctionName”、“CosBucketName”、“CosObjectName”四个参数

      在右侧选择对应语言,如 Python,复制得到的代码
      添加构建步骤 “Python 脚本”
      粘贴刚复制的代码,修改其中的 SecretId、SecretKey

      import json
      from tencentcloud.common import credential
      from tencentcloud.common.profile.client_profile import ClientProfile
      from tencentcloud.common.profile.http_profile import HttpProfile
      from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
      from tencentcloud.scf.v20180416 import scf_client, models
      try:
          # 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
          # 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
          cred = credential.Credential("SecretId", "SecretKey")
          # 实例化一个http选项,可选的,没有特殊需求可以跳过
          httpProfile = HttpProfile()
          httpProfile.endpoint = "scf.tencentcloudapi.com"
      
          # 实例化一个client选项,可选的,没有特殊需求可以跳过
          clientProfile = ClientProfile()
          clientProfile.httpProfile = httpProfile
          # 实例化要请求产品的client对象,clientProfile是可选的
          client = scf_client.ScfClient(cred, "ap-guangzhou", clientProfile)
      
          # 实例化一个请求对象,每个接口都会对应一个request对象
          req = models.UpdateFunctionCodeRequest()
          params = {
              "CosBucketName": "your-CosBucketName",
              "CosObjectName": "/1.zip",
              "FunctionName": "DjangoDemo"
          }
          req.from_json_string(json.dumps(params))
      
          # 返回的resp是一个UpdateFunctionCodeResponse的实例,与请求对象对应
          resp = client.UpdateFunctionCode(req)
          # 输出json格式的字符串回包
          print(resp.to_json_string())
      
      except TencentCloudSDKException as err:
          print(err)
      

      点击“显示高级选项”,在 requirements.txt 框中填入 tencentcloud-sdk-python,表示在运行 Python 脚本前先安装腾讯云 Python 的 SDK

测试自动构建部署

在本地修改 Django 代码,push 到远程仓库后查看是否自动进行构建,云函数是否被成功更新