摘要
AWS Lambda函數(shù)現(xiàn)已支持打包和部署容器鏡像,開發(fā)者通過官方提供或自己構建鏡像文件,可以非常方便利用現(xiàn)有的開發(fā)工具,工作流輕松構建基于AWS Lambda的應用程序?;谌萜鞔虬膽猛ㄟ^AWS Lambda可以實現(xiàn)更為簡便的操作部署,相比EC2有著更為快速的啟動時間,更為強大的并發(fā)擴展以及高可用,同時無縫與140余種AWS服務集成。
本文將展示如何基于自建鏡像(public.ecr.aws/bitnami/python:3.7),利用AWS官方提供的運行時接口客戶端(RIC)和運行時接口仿真器(RIE),構建運行在AWS Lambda上的OCR應用。
前言
對于機器學習,圖像處理等依賴庫構建復雜且文件較大的應用,AWS Lambda支持最大10GB的容器鏡像,開發(fā)者可以直接使用熟悉的容器開發(fā)工具(docker)在本地構建測試,并將容器鏡像推送到Amazon ECR(全托管的容器注冊表),之后通過指定Amazon ECR鏡像來部署Lambda函數(shù),免去了以往Lambda Layer構建流程,也無需受限于Lambda Layer的大小限制(250MB)。
伴隨AWS Lambda對容器鏡像支持的特性發(fā)布,AWS官方提供了一組Lambda基礎鏡像,可在Amazon ECR(gallery.ecr.aws/lambda)和Docker Hub(amazon/aws-lambda-python)上獲取,該基礎鏡像預裝了包括Node.js,Python,Java等語言的Lambda運行時,必要組件以及構建基礎鏡像的dockerfile。同時AWS官方還開源了運行時接口客戶端(RIC)和運行時接口仿真器(RIE),方便用戶構建同Lambda兼容的容器鏡像并進行本地測試。
OCR應用我們基于tesseract(最早由HP Lab開發(fā)并于2005年開源)實現(xiàn),其中軟件依賴如pillow,libtesseract我們利用AWS進行本地安裝,或者用戶也可以選取官方鏡像public.ecr.aws/lambda編譯以盡可能保證同Lambda兼容。
流程概覽
本文構建的OCR應用會利用到Python,Shell作為開發(fā)語言,PIP/Docker作為開發(fā)工具,構建流程分成如下部分:1)軟件依賴庫的構建,包括pillow,libtesseract編譯;2)Lambda兼容鏡像的構建,包括RIC/RIE的安裝配置,Lambda業(yè)務代碼的打包;3)Lambda業(yè)務代碼實現(xiàn),通過簡單的代碼調用生成的pytesseract庫返回圖片識別結果;4)本地調試驗證,通過RIE實現(xiàn)本地功能調試和迭代。
其中構建容器鏡像的軟件依賴庫有以下幾個途徑:方案一,直接利用已經(jīng)包含軟件依賴庫的容器鏡像,其dockerfile示例如下:
FROM public.ecr.aws/myrepo/shared-lib-layer:1 AS shared-lib-layer
#Layer code
WORKDIR/opt
COPY--from=shared-lib-layer/opt/.
方案二,利用已經(jīng)構建好的Lambda Layer,通過curl的形式拉取到新的鏡像當中,其dockerfile示例如下:
ARG AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-"cn-northwest-1"}
ARG AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-""}
ARG AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-""}
ENV AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}
ENV AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
ENV AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
RUN apk add aws-cli curl unzip
RUN mkdir-p/opt
RUN curl$(aws lambda get-layer-version-by-arn--arn arn:aws:lambda:us-east-1:1234567890123:layer:shared-lib-layer:1--query'Content.Location'--output text)--output layer.zip
RUN unzip layer.zip-d/opt
RUN rm layer.zip
方案三,完全從零開始的用戶可以考慮直接在容器里面構建軟件依賴庫,其dockerfile示例如下:
FROM python:3.8-alpine AS installer
#Layer Code
COPY extensionssrc/opt/
COPY extensionssrc/requirements.txt/opt/
RUN pip install-r/opt/requirements.txt-t/opt/extensions/lib
FROM scratch AS base
WORKDIR/opt/extensions
COPY--from=installer/opt/extensions.
接下來的OCR方案考慮到tesseract的依賴構建相對復雜,為了構建流程的獨立和依賴庫的共享,我們將采用方案二,即先利用Shell和Docker構建Lambda業(yè)務代碼調用的所有依賴,再將構建完畢后的zip包存放到Lambda Layer中供后續(xù)Lambda鏡像構建調用。
創(chuàng)建步驟
軟件依賴庫的構建
首先安裝pillow,創(chuàng)建requirements文件,寫入以下內容。
pillow
接著創(chuàng)建shell腳本(build_py37_pkgs.sh),寫入以下內容并執(zhí)行,執(zhí)行完畢后會在相同目錄下生成pythonlibs-layer.zip文件。
set-e
rm-rf pythonlibs-layer.zip exit 0
rm-rf python/exit 0
docker run-v"$PWD":/var/task"lambci/lambda:build-python3.7"/bin/sh-c"pip install-r requirements.txt-t python/lib/python3.7/site-packages/;exit"
chmod 777 python/
zip-r pythonlibs-layer.zip python>/dev/null
rm-rf python/
Pillow構建完畢后,開始構建tesseract依賴,創(chuàng)建dockerfile(Dockerfile-tess4),文件內容可以直接參考這里
FROM lambci/lambda-base:build
#Proxy setup if exists
#ENV http_proxy'http://ip:port'
#ENV https_proxy'https://ip:port'
ARG LEPTONICA_VERSION=1.78.0
ARG TESSERACT_VERSION=4.1.0-rc4
ARG AUTOCONF_ARCHIVE_VERSION=2017.09.28
ARG TMP_BUILD=/tmp
ARG TESSERACT=/opt/tesseract
ARG LEPTONICA=/opt/leptonica
ARG DIST=/opt/build-dist
#change OCR_LANG to enable the layer for different languages
ARG OCR_LANG=chi_sim
#change TESSERACT_DATA_SUFFIX to use different datafiles(options:"_best","_fast"and"")
ARG TESSERACT_DATA_SUFFIX=""
ARG TESSERACT_DATA_VERSION=4.0.0
后續(xù)省略
……
創(chuàng)建shell腳本(build_tesseract4.sh),寫入以下內容并執(zhí)行,執(zhí)行完畢后會在相同目錄下生成tesseract-layer.zip
set-e
rm-rf tesseract-layer.zip exit 0
rm-rf configs exit 0
rm-rf tessconfigs exit 0
#Download tessconfigs folder
git clone https://github.com/tesseract-ocr/tessconfigs.git tesseractconfigs
mv tesseractconfigs/configs.
mv tesseractconfigs/tessconfigs.
rm-rf tesseractconfigs
#Build Docker image containing Tesseract
docker build-t tess_layer-f Dockerfile-tess4.
#Copy Tesseract locally
CONTAINER=$(docker run-d tess_layer false)
docker cp$CONTAINER:/opt/build-dist layer
docker rm$CONTAINER
##Zip Tesseract
cd layer/
zip-r../tesseract-layer.zip.
#Clean
cd..
rm-rf layer/
rm-rf tessconfigs/
rm-rf configs/
將前面步驟生成的zip文件(pythonlibs-layer.zip/tesseract-layer.zip)通過AWS Console或者AWS命令行的方式上傳至Lambda Layer,并記錄下對應的ARN,類似arn:aws-cn:lambda:cn-northwest-1:xxxxxxxx:layer:ocrTesseract:1。
Lambda兼容鏡像的構建
運行時接口客戶端(RIC)作為AWS開源項目,實現(xiàn)了Lambda的運行時API,包括調用事件檢索,調用響應返回,調用錯誤處理和初始化錯誤等功能實現(xiàn)Lambda能正確接收處理調用并返回結果。運行時接口仿真器(RIE)實際是一個輕量級的web服務器,代理Lambda的運行時和擴展API,使開發(fā)者可以在本地通過Docker,CURL進行本地測試而不用將Lambda容器鏡像部署上云。
接下來我們基于自建鏡像(public.ecr.aws/bitnami/python:3.7)構建Lambda容器的dockerfile并針對其中部分操作進行解釋。
創(chuàng)建entry.sh文件,用作容器在云上部署和本地調試的自動切換,內容如下:
if [ -z "${AWS_LAMBDA_RUNTIME_API}" ]; then
exec /usr/local/bin/aws-lambda-rie /usr/local/bin/python -m awslambdaric $1
else
exec /usr/local/bin/python -m awslambdaric $1
fi
創(chuàng)建dockerfile(dockerfile-custom-tesseract),內容摘錄如下,原文件參見這里:
安裝必要工具
ARG LAYER_DIR="/opt"
FROM public.ecr.aws/bitnami/python:3.7 as build-image
RUN apt-get update && \
apt-get install -y \
g++ \
make \
cmake \
unzip \
libcurl4-openssl-dev
RUN pip install opencv-python-headless
RUN apt-get install -y libpng-dev
安裝運行時接口客戶端(RIC)
RUN mkdir -p ${LAYER_DIR}
RUN pip install \
--target ${LAYER_DIR} \
awslambdaric
取前面步驟生成的Lambda Layer
ARG AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION:-"cn-northwest-1"}
ARG AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-"xxxx"}
ARG AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-"xxxx"}
ENV AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}
ENV AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
ENV AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
RUN apt-get install-y curl unzip
RUN curl"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"-o"awscliv2.zip"
RUN unzip awscliv2.zip
RUN./aws/install
#ocrTesseract
RUN curl$(aws lambda get-layer-version-by-arn--arn arn:aws-cn:lambda:cn-northwest-1:xx:layer:ocrTesseract:3--query'Content.Location'--output text)--output pythonlibs-layer.zip
#COPY pythonlibs-layer.zip.
RUN unzip pythonlibs-layer.zip-d${LAYER_DIR}
RUN rm pythonlibs-layer.zip
#pythonlibs-layer
RUN curl$(aws lambda get-layer-version-by-arn--arn arn:aws-cn:lambda:cn-northwest-1:xx:layer:pythonlibs-layer:1--query'Content.Location'--output text)--output tesseract-layer.zip
#COPY tesseract-layer.zip.
RUN unzip tesseract-layer.zip-d${LAYER_DIR}
RUN rm tesseract-layer.zip
打包Lambda業(yè)務代碼(代碼邏輯下一小節(jié)會提到)和entry.sh
#Multi-stage build:grab a fresh copy of the base image,use custom image instead of official one
FROM public.ecr.aws/bitnami/python:3.7
#Include global arg in this stage of the build
ARG LAYER_DIR
#Copy in the build image dependencies
WORKDIR${LAYER_DIR}
COPY--from=build-image${LAYER_DIR}.
COPY app.py.
COPY entry.sh/
RUN chmod 755/entry.sh
ENV LD_LIBRARY_PATH="/opt:/opt/lib:${LD_LIBRARY_PATH}"
ENV PATH="/opt:/opt/bin:${PATH}"
#Production env
ENTRYPOINT["/entry.sh"]
開始構建鏡像并推送到ECR,至此OCR業(yè)務的Lambda鏡像構建完畢
docker build-t local-lambda-python3.8-custom-ocr--build-arg AWS_DEFAULT_REGION=cn-northwest-1--build-arg AWS_ACCESS_KEY_ID=xx--build-arg AWS_SECRET_ACCESS_KEY=xx-f dockerfile-custom-tesseract.
docker tag local-lambda-python3.8-custom-ocr:latest xx.dkr.ecr.cn-northwest-1.amazonaws.com.cn/local-lambda-python3.8-custom-ocr
aws ecr get-login-password--region cn-northwest-1|docker login--username AWS--password-stdin xx.dkr.ecr.cn-northwest-1.amazonaws.com.cn
docker push xx.dkr.ecr.cn-northwest-1.amazonaws.com.cn/local-lambda-python3.8-custom-ocr:latest
Lambda業(yè)務代碼實現(xiàn)
代碼的調用邏輯如下,handler接收傳入的圖片文件(u64編碼)并調用pytesseract實現(xiàn)圖片中文字的識別并返回。
import sys
import os
sys.path.append('/opt/python/lib/python3.7/site-packages')
# sys.path.append('/opt/python/lib/python3.7/site-packages/pytesseract')
# sys.path.append('/opt/python/lib/python3.7/site-packages/PIL')
# sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import base64
import pytesseract
import cv2
def write_to_file(save_path, data):
with open(save_path, "wb") as f:
f.write(base64.b64decode(data))
def handler(event, context=None):
write_to_file("/tmp/photo.jpg", event['body'])
img = cv2.imread("/tmp/photo.jpg")
ocr_text = pytesseract.image_to_string(img, config = "eng")
# Return the result data in json format
return {
"statusCode": 200,
"body": ocr_text
}
本地調試驗證
本地安裝RIE,盡量減少Lambda鏡像需要安裝的文件
mkdir-p~/.aws-lambda-rie&&curl-Lo~/.aws-lambda-rie/aws-lambda-rie
https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie
&&chmod+x~/.aws-lambda-rie/aws-lambda-rie
本地運行容器并通過curl測試結果,其中helloWorld文件為顯示有hello world字樣圖片對應的u64編碼文件,成功的話我們可以看到輸出的hello world。
docker run-d-v~/.aws-lambda-rie:/aws-lambda-p 9000:8080--entrypoint/aws-lambda/aws-lambda-rie local-lambda-python3.8-custom-ocr:latest/usr/local/bin/python-m awslambdaric app.handler
curl-X POST"http://localhost:9000/2015-03-31/functions/function/invocations"-d@helloWorld
待功能測試成功后我們可以將該鏡像最終部署上云,無縫對接其他AWS服務實現(xiàn)更加豐富的功能。
對接其他服務
Lambda鏡像構建完畢后,用戶可以結合API Gateway實現(xiàn)HTTP前端調用來整合我們后端的OCR能力,通過SAM(Serverless Application Model)模版快速構建一個無服務器架構的OCR應用,示例模版部分內容如下所示:
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
ImageUrl: ‘xxxx.dkr.ecr.cn-northwest-1.amazonaws.com.cn/local-lambda-python3.8-custom’
ImageConfig:
Command:
- "app.handler"
EntryPoint:
- "/entry.sh"
WorkingDirectory: "/opt"
Metadata:
DockerTag: python3.x-v1
DockerContext: ./hello-world
Dockerfile: Dockerfile
之后通過SAM CLI實現(xiàn)AWS API Gateway,Lambda以及對應IAM的編譯,調試和部署,有關SAM的具體的操作參見這里。待服務部署完畢后,用戶可通過調用類似curl–request POST-H“Content-Type:image/png”–data-binary“@/path/ocrimage.png”https://xxxx.execute-api.cn-northwest-1.amazonaws.com.cn/prod/upload
命令獲取OCR識別結果。
寫在最后
Lambda針對容器鏡像的支持,將無服務器化,容器這兩個熱門的技術領域進行了完美結合,用戶在原有的容器開發(fā)環(huán)境基礎上利用無服務器化架構的低運維,高擴展,高可用等特性,可以更加便捷的構建和開發(fā)諸如機器學習,圖像識別等數(shù)據(jù)密集型負載應用。