最近剛好用到 AWS Lambda 上跑 Python + pandas 的情境,寫一下 library 準備的 tips
因為 pandas 這類科學計算用的 library,大多包有 native library 在裡面。在官方手冊上有教如何準備 library。 不過目前環境是 linux x86_64 的機器,那我們其實可以簡單地運用 docker 來準備它:
mkdir -p data
docker run -it --rm -v `pwd`/data:/data -w /data python:3.6 pip install pandas -t /data
上面的指令其實就是,建立一個 data 資料夾,並在 docker 啟動時 mount 進去而已。再指定 pip 要裝到 data 資料夾內,執行完後你可以發現它抓的版本就是給 linux 使用的:
qty:data qrtt1$ find . -name "*.so" |head
./numpy/.libs/libopenblasp-r0-39a31c03.2.18.so
./numpy/core/_dummy.cpython-36m-x86_64-linux-gnu.so
./numpy/core/multiarray.cpython-36m-x86_64-linux-gnu.so
./numpy/core/multiarray_tests.cpython-36m-x86_64-linux-gnu.so
./numpy/core/operand_flag_tests.cpython-36m-x86_64-linux-gnu.so
./numpy/core/struct_ufunc_test.cpython-36m-x86_64-linux-gnu.so
./numpy/core/test_rational.cpython-36m-x86_64-linux-gnu.so
./numpy/core/umath.cpython-36m-x86_64-linux-gnu.so
./numpy/core/umath_tests.cpython-36m-x86_64-linux-gnu.so
./numpy/fft/fftpack_lite.cpython-36m-x86_64-linux-gnu.so
總大小其實蠻巨大的,要注意一下它不可以超過 AWS Lambda Limits 的限制:
qty:data qrtt1$ du -h -s
170M .
若是 zip 檔超過限制,時則有另一招可以用來 work around:
- python 的 module search path 是 runtime 可以變更的
- 將超出限制的部分,放在 s3 上,下載回來解壓縮,並加入 module search path (也就是 sys.path)
- aws lambda 的
/tmp內是可以寫入的空間
以下是以 pandas library 實驗的程式:
import os, sys, time
import boto3
import logging
__bucket_name = 'the_bucket_you_put_library'
logging.basicConfig()
logger = logging.getLogger('perf')
logger.setLevel(logging.INFO)
def stopwatch(func):
def with_logging(*args, **kwargs):
start_time = time.time()
try:
return func(*args, **kwargs)
finally:
end_time = time.time()
logger.info("func[ {:16s} ] :: {:.3f}".format(func.__name__, end_time - start_time))
pass
return with_logging
def dir(p, filename = None):
target = os.path.join('/tmp/extra', p)
if not os.path.exists(target):
os.makedirs(target)
if filename:
return "{}/{}".format(target, filename)
else:
return target
@stopwatch
def prepare_dir():
s3 = boto3.resource('s3')
if not os.path.exists(dir('downloads', 'pandas.zip')):
s3.Object(__bucket_name, 'pandas.zip').download_file(dir('downloads', 'pandas.zip'))
cmd = "bash -c 'cd {}; unzip {} -d {} '".format(dir('libs'), dir('downloads', 'pandas.zip'), dir('libs'))
os.system(cmd)
def lambda_handler(event, context):
prepare_dir()
sys.path.append(dir('libs'))
import pandas as pd
print(pd.__doc__)
return 'Hello from Lambda'結果為:
- 需要約 30 秒準備 library,其中 6 秒大約是由 s3 下載的時間
- 雖然需要很久,但至少能將 library 放進去了
Response:
"Hello from Lambda"
Request ID:
"75f55322-2bef-11e8-823a-fb6239c1b277"
Function Logs:
START RequestId: 75f55322-2bef-11e8-823a-fb6239c1b277 Version: $LATEST
[INFO] 2018-03-20T03:34:01.587Z 75f55322-2bef-11e8-823a-fb6239c1b277 func[ prepare_dir ] :: 30.103
pandas - a powerful data analysis and manipulation library for Python
=====================================================================
**pandas** is a Python package providing fast, flexible, and expressive data
structures designed to make working with "relational" or "labeled" data both
easy and intuitive. It aims to be the fundamental high-level building block for
doing practical, **real world** data analysis in Python. Additionally, it has
the broader goal of becoming **the most powerful and flexible open source data
analysis / manipulation tool available in any language**. It is already well on
its way toward this goal.
Main Features
-------------
Here are just a few of the things that pandas does well:
- Easy handling of missing data in floating point as well as non-floating
point data
- Size mutability: columns can be inserted and deleted from DataFrame and
higher dimensional objects
- Automatic and explicit data alignment: objects can be explicitly aligned
to a set of labels, or the user can simply ignore the labels and let
`Series`, `DataFrame`, etc. automatically align the data for you in
computations
- Powerful, flexible group by functionality to perform split-apply-combine
operations on data sets, for both aggregating and transforming data
- Make it easy to convert ragged, differently-indexed data in other Python
and NumPy data structures into DataFrame objects
- Intelligent label-based slicing, fancy indexing, and subsetting of large
data sets
- Intuitive merging and joining data sets
- Flexible reshaping and pivoting of data sets
- Hierarchical labeling of axes (possible to have multiple labels per tick)
- Robust IO tools for loading data from flat files (CSV and delimited),
Excel files, databases, and saving/loading data from the ultrafast HDF5
format
- Time series-specific functionality: date range generation and frequency
conversion, moving window statistics, moving window linear regressions,
date shifting and lagging, etc.
END RequestId: 75f55322-2bef-11e8-823a-fb6239c1b277
REPORT RequestId: 75f55322-2bef-11e8-823a-fb6239c1b277 Duration: 34092.77 ms Billed Duration: 34100 ms Memory Size: 128 MB Max Memory Used: 128 MB
- 由於解壓縮的時間會很久,最好是能避勉使用 Work Around
- 若真的需要 Work Around,那僅將超出的範圍使用 Work Around 以獲得最佳的啟動速度
隨著 AWS 提供越來越多好用的功能,蠻多開發者漸漸將 CI/DI 環境搬上 AWS 上,像是
- 將原程碼放入 codecommit
- 啟用 codepipeline 來管理 CI/CD
- 使用 codebuild 編譯,打包,測試
- 透過 codedeploy 部署服務
若是你的 python 應用程式,有用到 native library 對好是要能配得上 python 的 minor version (特別是透過 cpython 提昇效的)。例如下列版本對 native library 是有差異的:
- python 3.4
- python 3.5
- python 3.6
- python 3.7
舉例來說,若是在 docker python:3.5 與 python:3.6 下進行 pip install,你會得到不同預編版本的 .so:
./pandas/_libs/json.cpython-35m-x86_64-linux-gnu.so
./pandas/_libs/json.cpython-36m-x86_64-linux-gnu.so
它們需要對應的版本才能使用,其中 35m 就是代表 python 3.5;36m 就是 3.6。以撰文的時間來說,目前 codebuild 提供的 aws managed docker image 最高支援到 3.5:
而 aws lambda 目前支援的最高版本為 3.6:
考慮下列情境:
- 在 codebuild 打包 lambda 用的 python package 給 lambda 用
- 在 codebuild 內使用 pip install 安裝相關的 native library
都選擇內建最新的 python 支援版,那就會變成包了 3.5 的 native library 給 3.6 使用。最後,在 lambda invocation 時發生了 import error。
要解決版本不一致而發生的問題,有幾個選項:
- 使用 codebuild 與 lambda 都有的 2.7 版 (這不推薦,因為大方向是朝 3.x 版前進)
- 讓 codebuild 吃開發者預包的 docker image 提供與 aws lambda 對應版本的 Python
- pip install 的動作,不要在 codebuild 上執行,而是預包好,在 codebuild 時下載回來使用而已
後二者是比較推薦的方案,而提供客製的 docker image 的優點就是,能在 codebuild 環境下編譯,打包,測試。缺點是得自己維護 docker image。
另一個方案是預包 Library,在 codebuild 內只是將它合併起來。舉例來說,我能在 s3 上先準備好 native library:
Pandas_cpython-36m-x86_64-linux.zip
在 codebuild 時,將它下載回來,並將需要包進去的檔案,透過 zip 指令附加進去:
zip -u Pandas_cpython-36m-x86_64-linux.zip a.py b.py …
這麼做是省下 pip 的時間,但缺點就是無法在 codebuild 內跑測試,因為 python 版本對不上。但也能透過在 codebuild 的過程中,建立臨時的 lambda function 部署上去,跑一次整合測試來達成目標。
- 2018.09.21 review 了一下 code build 支援的 runtime,它已經支援 Python 3.6 版本



