開發函數計算的正確姿勢 —— 安裝第三方依賴

前言

首先介紹下在本文出現的幾個比較重要的概念:

函數計算(Function Compute): 函數計算是一個事件驅動的服務,通過函數計算,用戶無需管理服務器等運行情況,只需編寫代碼並上傳。函數計算準備計算資源,並以彈性伸縮的方式運行用戶代碼,而用戶只需根據實際代碼運行所消耗的資源進行付費。函數計算更多信息參考
Fun: Fun 是一個用於支持 Serverless 應用部署的工具,能幫助您便捷地管理函數計算、API 網關、日誌服務等資源。它通過一個資源配置文件(template.yml),協助您進行開發、構建、部署操作。Fun 的更多文檔參考
fun install: fun install 是 fun 工具的一個子命令,用於安裝 pip 和 apt 依賴,提供了命令行接口和 fun.yml 描述文件兩種形式。

備註: 本文介紹的技巧需要 Fun 版本大於等於 2.9.3。

函數計算安裝第三方依賴一大痛點,文章 函數計算安裝依賴庫方法小結 對可能會遇到的問題和解決方法做了細緻總結,fun install 是基於之前的經驗和成果將最佳實踐的方法固化到工具中,方便用戶便捷的安裝依賴。

初始化

使用 fun install init 在當前目錄初始化一個 fun.yml 文件。(這一步不是必須的,如果您打算手寫 fun.yml 然後通過 fun install 命令批量執行 task,init 是一個好的開始。)

在函數計算項目根目錄執行 fun install init 命令,選擇一個 runtime。

$ fun install init
? Select runtime (Use arrow keys)
 python2.7
  python3
  nodejs6
  nodejs8
  java8
  php7.2

然後會在當前目錄生成一個 fun.yml 文件,內容如下:

runtime: python2.7
tasks: []

安裝 pip 包依賴

下面的命令安裝 python 的 tensorflow 包

$ fun install --runtime python2.7 --package-type pip tensorflow
skip pulling image aliyunfc/runtime-python2.7:build-1.2.0...
Task => [UNNAMED]
     => PYTHONUSERBASE=/code/.fun/python pip install --user tensorflow

說明

  • --runtime 指定 runtime,如果已經初始化 fun.yml 文件, 由於 fun.yml 裏聲明瞭 runtime ,該選項可以省略。
  • --package-type 指定安裝依賴的類型,pip 和 apt 是目前的兩個可選值。
  • tensorflow 是一個 pip 包名。

命令執行在 fc-docker 提供的 container 中,容器內部執行的命令會逐行打印出來,比如上面命令中內部真實執行了 PYTHONUSERBASE=/code/.fun/python pip install --user tensorflow 命令。

安裝完成以後會在生成一個 .fun 目錄, 可執行文件會被放置到 .fun/python/bin 目錄下,庫文件放置到 .fun/python/lib/python2.7/site-packages 下。

.fun
└── python
    ├── bin
    │   ├── freeze_graph
    │   ├── markdown_py
    │   ├── pbr
    │   ├── saved_model_cli
    │   ├── tensorboard
    │   ├── tflite_convert
    │   ├── toco
    │   └── toco_from_protos
    └── lib
        └── python2.7
            └── site-packages
                ├── tensorboard
                ├── tensorboard-1.12.2.dist-info
                ├── tensorflow
                ├── tensorflow-1.12.0.dist-info
                ├── termcolor-1.1.0.dist-info
                ...

相比之前的 pip install -t . <package-name> 方式,fun install 安裝文件的存放位置更有組織,依賴文件和代碼文件分離開了,便於清理、拆分後藉助 OSS 或 NAS 初始化依賴文件。但是組織過後也帶來一個新問題,需要用戶自定義環境變量庫文件才能被程序找到。爲了方便用戶使用提供了一個 fun install env 打印出必要的環境變量。

$ fun install env
LD_LIBRARY_PATH=/code/.fun/root/usr/lib/x86_64-linux-gnu:/code:/code/lib:/usr/local/lib
PATH=/code/.fun/root/usr/local/bin:/code/.fun/root/usr/local/sbin:/code/.fun/root/usr/bin:/code/.fun/root/usr/sbin:/code/.fun/root/sbin:/code/.fun/root/bin:/code/.fun/python/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/sbin:/bin
PYTHONUSERBASE=/code/.fun/python

關於如果設定函數計算的環境變量,請參考 https://help.aliyun.com/document_detail/69777.html 。如果您使用 fun localfun deploy 進行調試和部署,您無需關注環境變量問題,已經幫您設定好了。

使用 --save 持久化

install 命令加上 --save 參數,會將命令持久化成 task 保存到 fun.yml 文件中。

$ fun install --runtime python2.7 --package-type pip --save tensorflow
skip pulling image aliyunfc/runtime-python2.7:build-1.2.0...
Task => [UNNAMED]
     => PYTHONUSERBASE=/code/.fun/python pip install --user tensorflow

上面的命令多加了一行 --save 參數,查看 fun.yml 內容:

runtime: python2.7
tasks:
  - pip: tensorflow
    local: true

之後直接執行 fun install 不帶參數,就可以依次執行任務。

$ fun install
skip pulling image aliyunfc/runtime-python2.7:build-1.2.0...
Task => [UNNAMED]
     => PYTHONUSERBASE=/code/.fun/python pip install --user tensorflow

使用 -v 顯示詳細日誌

$ fun install -v
skip pulling image aliyunfc/runtime-python3.6:build-1.2.0...
Task => [UNNAMED]
     => apt-get update (if need)
Ign http://mirrors.aliyun.com stretch InRelease
Get:1 http://mirrors.aliyun.com stretch-updates InRelease [91.0 kB]
Get:2 http://mirrors.aliyun.com stretch-backports InRelease [91.8 kB]
Get:3 http://mirrors.aliyun.com stretch/updates InRelease [94.3 kB]
Hit http://mirrors.aliyun.com stretch Release.gpg
Hit http://mirrors.aliyun.com stretch Release
Get:4 http://mirrors.aliyun.com stretch-updates/main Sources [3911 B]
....

安裝 apt 包依賴

函數計算使用 apt-get 安裝依賴是另一類常見的安裝問題,使用 fun install 也可以方便的安裝。

$ fun install --runtime python3 --package-type apt libzbar0
skip pulling image aliyunfc/runtime-python3.6:build-1.2.0...
Task => [UNNAMED]
     => apt-get update (if need)
     => apt-get install -y -d -o=dir::cache=/code/.fun/tmp libzbar0
     => bash -c 'for f in $(ls /code/.fun/tmp/archives/*.deb); do dpkg -x $f /code/.fun/root; done;'
     => bash -c 'rm -rf /code/.fun/tmp/archives'

使用方法及其參數和 pip 包依賴類似,只需要將 --package-type 設定成 apt, 包名使用日常 apt-get 可以安裝的 deb 包名即可。

使用 fun.yml

fun.yml 由一組 task 組成,執行 fun install 命令時會依次執行 task ,達到批量安裝的效果。

fun.yml 的文件格式如下

runtime: python3
tasks:
  - name: install libzbar0
      apt: libzbar0
    local: true
  - name install Pillow by pip
      pip: Pillow
    local: true
  - name: just test shell task
      shell: echo '111' > 1.txt

runtime 是必填的字段。目前 task 有三種類型:apt, pip 和 shell。fun.yml 文件放置在 template.yml 文件中函數 codeUri 指向的目錄,如果 template.yml 裏聲明瞭多個函數,並且放置在不同的 codeUri 目錄,需要創建多個 fun.yml 文件。

所有 task 的 name 字段是可選的,沒有 name 字段的時候執行的時候會輸出爲

Task => [UNNAMED]

apt/pip task

apt 和 pip 類型的 task 都是 install task 的子類型,描述格式類似

name: install libzbar0
apt: libzbar0
local: true

上面的 task 描述與下面的命令是等價的

fun install --package-type apt libzbar0

在使用 fun install 安裝的過程中,使用 --save 參數可以在當前目錄的 fun.yml 文件中生成上面 task 的描述結構。

local 字段默認爲 true,表示依賴會被裝在當前目錄的 .fun 子目錄下,打包 zip 的時候回一併打包進去。設定爲 false,依賴安裝到系統目錄,這種情況一般用於編譯依賴,比如某個執行文件或者庫是編譯或者構建期需要的,運行期不要,那可以設定 local: false,打包的時候會被忽略,不影響最終 zip 包的文件尺寸。

shell task

shell 類型的 task 是爲基於源碼編碼的安裝場景設計的。

name: install from source
shell: ./autogen.sh --disable-report-builder --disable-lpsolve --disable-coinmp

示例

下面是一個 python3 實現簡單二維碼識別程序部署到函數計算的例子。源碼位於 https://github.com/aliyun/fun/tree/master/examples/install/pyzbar_example

本例子使用 pip 的 pyzbar 庫進行二維碼識別,pyzbar 依賴 apt-get 安裝的 libzbar0 庫。裝載圖片需要 pip 的 Pillow 庫。所以 fun.yml 的文件描述如下

runtime: python3
tasks:
  - apt: libzbar0
    local: true
  - pip: Pillow
    local: true
  - pip: pyzbar
    local: true

使用 fun install 安裝依賴

$ fun install
skip pulling image aliyunfc/runtime-python3.6:build-1.2.0...
Task => [UNNAMED]
     => apt-get update (if need)
     => apt-get install -y -d -o=dir::cache=/code/.fun/tmp libzbar0
     => bash -c 'for f in $(ls /code/.fun/tmp/archives/*.deb); do dpkg -x $f /code/.fun/root; done;'
     => bash -c 'rm -rf /code/.fun/tmp/archives'
Task => [UNNAMED]
     => PYTHONUSERBASE=/code/.fun/python pip install --user Pillow
Task => [UNNAMED]
     => PYTHONUSERBASE=/code/.fun/python pip install --user pyzbar

template.yml 文件內容如下

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  pyzbar-srv:
    Type: 'Aliyun::Serverless::Service'
    pyzbar-fun:
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: index.handler
        Runtime: python3
        Timeout: 60
        MemorySize: 128
        CodeUri: .

index.py 文件內容如下:

from pyzbar.pyzbar import decode
from pyzbar.pyzbar import ZBarSymbol
from PIL import Image

def handler(event, context):
    img = Image.open('./qrcode.png')
    return decode(img, symbols=[ZBarSymbol.QRCODE])[0].data

使用 fun local 在本地執行

fun local invoke pyzbar-fun
skip pulling image aliyunfc/runtime-python3.6:1.2.0...
Thalassiodracon

RequestId: 964980d1-1f1b-4f91-bfd8-eadd26a307b3          Billed Duration: 630 ms         Memory Size: 1998 MB    Max Memory Used: 32 MB

Thalassiodracon 即爲識別後的輸出結果。

小結

本文介紹了 fun 工具的一個新特性 fun install ,使用 fun install 可以方便的安裝 apt 和 pip 軟件包,對於多次安裝的工程化需求可以考慮將安裝步驟持久化爲 fun.yml 文件. fun.yml 文件提供了比命令行更多的功能,可以編寫 shell 類型的 task,以支持源碼安裝的場景。可以通過設定 local: false 將依賴安裝的系統目錄,以解決編譯依賴而非運行依賴的情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章