- 03-5820-1777平日10:00〜18:00
- お問い合わせ
今回はLambda関数をコンテナイメージ化して、AWS上にデプロイして正常に動作するか確認してみたいと思います。
Lambdaをコンテナ化出来るようになり、コンテナイメージをAWSにデプロイ可能になったので、非常に便利です。
zipファイルでアップロードでLambda関数を作成した時は外部ライブラリを使用した際などに、インストールしたライブラリのヴァージョンが合わない等の実行時にエラーが起きる問題が起きる事もあったので、ローカルでテストしたものが、そのままAWS上でも動くというのはメリットだと思います。
複数関数でライブラリを共有して使用する場合はLayerを使用するのも良いと思います。
Layerに関しては後に記事にしたいと思います。
今回作成するLambdaの機能は、PDFを作成しPDFのデータを返すという機能です。
PDF作成するのにライブラリを使用するので、コンテナ内にあらかじめインストールしておきます。
下記のテンプレートファイルに会社や案件情報、金額などを入力しPDFファイルを作成しそのデータを返します。
テンプレート
作成ファイル
Windows11
Docker20.10.20
Python3.8
reportlab
PyPDF2
ファイル名 | 説明 |
---|---|
app.py | Lambda関数として起動するメインファイル |
createpdf.py | PDFファイル作成処理を実装するファイル app.pyから呼ばれる |
Dockerfile | dockerコンテナイメージ作成手順ファイル |
template.pdf | PDF作成の際のテンプレートとして利用する このファイルの指定位置に特定の値を埋め込んで新規PDFを作成する |
import json
import base64
import createpdf
import os
def handler(event, context):
# PDF作成クラスインスタンス取得
ss = createpdf.ReportlabView()
# PDF作成処理依頼
ss._create_pdf( '/tmp/sample.pdf', 'template.pdf' )
# 作成されたファイルを読み込み、Base64に変換して返す
with open("/tmp/sample.pdf", "rb") as file_data:
text = file_data.read()
text = base64.b64encode( text ).decode('utf-8')
# 返す値はJSON形式の文字列で返す。ファイルダウンロードする際は以下の様な記述にする。
return {
"statusCode": 200,
"headers": {
'Content-Length': len(text),
'Content-Type': 'application/pdf',
'Content-disposition': 'attachment;filename=output.pdf'
},
"isBase64Encoded": True,
"body": text
}
ファイルをダウンロードする際にreturnで返す値は上記の様な値が必要です。
from PyPDF2 import PdfFileWriter, PdfFileReader
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4, portrait
from reportlab.lib.units import inch, mm, cm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.platypus import Table, TableStyle
from reportlab.lib import colors
class ReportlabView():
def _create_pdf(self, output_file, template_file):
# ファイルの指定
tmp_file = '/tmp/tmp.pdf' # 一時ファイル
# Canvasを作成
w, h = portrait(A4)
cv = canvas.Canvas(tmp_file, pagesize=(w, h))
# フォントを登録しCanvasに設定
font_size = 10
font_name = 'HeiseiKakuGo-W5'
pdfmetrics.registerFont(UnicodeCIDFont(font_name))
cv.setFont(font_name, font_size)
# 文字列を描画
cv.drawString(
20 * mm,
264 * mm,
"AAA株式会社御中"
)
cv.drawString(
80 * mm,
257 * mm,
"AA案件"
)
data = [
["8800円"], #合計金額
]
table = Table(data, colWidths=(60 * mm), rowHeights=(7 * mm))
table.setStyle(
TableStyle(
[
("FONT", (0, 0), (-1, -1), font_name, 10),
# ("BOX", (0, 0), (-1, -1), 1, colors.red),
# ("INNERGRID", (0, 0), (-1, -1), 1, colors.black),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("ALIGN", (0, 0), (-1, -1), "RIGHT"),
]
)
)
table.wrapOn(
cv,
70 * mm,
218 * mm,
)
table.drawOn(
cv,
70 * mm,
218 * mm,
)
data = []
data.append(["A案件", "1000", "2", "2000", "", ""])
data.append(["B案件", "2000", "3", "6000", "", ""])
table = Table(
data,
colWidths=(70 * mm, 25 * mm, 25 * mm, 20 * mm, 20 * mm, 20 * mm),
rowHeights=6 * mm,
)
table.setStyle(
TableStyle(
[
("FONT", (0, 0), (-1, -1), font_name, 8),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
("ALIGN", (1, 0), (-1, -1), "RIGHT"),
]
)
)
table.wrapOn(cv, 17 * mm, 184 * mm)
table.drawOn(cv, 17 * mm, 184 * mm)
# 一時ファイルに保存
cv.showPage()
cv.save()
# テンプレートとなるPDFを読む
template_pdf = PdfFileReader(template_file)
template_page = template_pdf.getPage(0)
# 一時ファイルを読んでマージする
tmp_pdf = PdfFileReader(tmp_file)
template_page.mergePage(tmp_pdf.getPage(0))
# 出力用PDFを用意し書き込む
output = PdfFileWriter()
output.addPage(template_page)
with open(output_file, "wb") as fp:
output.write(fp)
FROM public.ecr.aws/lambda/python:3.8
RUN pip install pypdf2
RUN pip install reportlab
COPY template.pdf ./
COPY app.py ./
COPY createpdf.py ./
CMD ["app.handler"]
1.Dockerイメージ作成
docker build -t イメージ名 .
2.作成イメージ起動
ポート番号は空いている番号を適当に使用しています。
docker run --rm -p 9000:8080 イメージ名:latest
3.起動確認
docker psコマンドを実行すると起動しているコンテナを確認できます。
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a9218fac6c25 イメージ名:latest "/lambda-entrypoint.…" 2 days ago Up 2 days 0.0.0.0:9000->8080/tcp hardcore_feistel
Windows環境での確認コマンドは「Invoke-WebRequest」を使用して行います。
URLの2015-03-31/functions/function/invocationsの部分はDockerであらかじめ決まっている部分みたいです。
Invoke-WebRequest http://localhost:9000/2015-03-31/functions/function/invocations -Method POST -Body '{}'
Linux環境での確認コマンドは「curl」を使用して行います。
curl -X POST http://localhost:9000/2015-03-31/functions/function/invocations -d {}
結果
以下の様な値が返ってくれば成功です。しかし、これではbodyの値が期待通りのPDFファイルが分からないので
テスト用プログラムを作成し、確認してみます。
StatusCode : 200
StatusDescription : OK
Content : {"statusCode": 200, "headers": {"Content-Length": 18448, "Content-Type": "application/pdf", "Conten
t-disposition": "attachment;filename=output.pdf"}, "isBase64Encoded": true, "body": "JVBERi0xLjQKJe
Lj...
RawContent : HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/plain; charset=utf-8
Date: Thu, 27 Oct 2022 07:42:15 GMT
{"statusCode": 200, "headers": {"Content-Length": 18448, "Content-Type": "a...
Forms : {}
Headers : {[Transfer-Encoding, chunked], [Content-Type, text/plain; charset=utf-8], [Date, Thu, 27 Oct 2022 0
7:42:15 GMT]}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 18634
次の様なテストプログラムをPythonで作成し、取得した値からファイルを作成してみます。
import requests
import json
import base64
response = requests.post('http://localhost:9000/2015-03-31/functions/function/invocations', '{}')
json_str = json.loads(response.text)
#取得したJSONデータのbody部分はBase64デコードをしてファイルに保存します。
data = base64.b64decode( json_str['body'].encode() )
f = open('sample.pdf', 'wb')
f.write( data )
結果、作成されたファイルが想定通りのものなら正常に動作してる事が確認できます。
次は実際にAWSデプロイして、Api Gateway経由でアクセスしてみましょう。
dockerイメージを登録する為にECRにリポジトリを作成します。
赤枠の部分はECR登録に使用します。
ローカル環境で作成したイメージにタグつけをします。
イメージIDはdocker imagesで見れます。
XXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-docker-sampleの部分は上記の赤枠部分を指定します。
docker tag イメージID XXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-docker-sample:tag
登録する前のECRへのログイン処理を行います。
このコマンドはprofileを指定しない場合は、.awsファイルのデフォルトの認証情報を使用します。
aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin XXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com
登録処理は次のコマンドを実行します。
docker push XXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/lambda-docker-sample:tag
コンテナイメージで関数作成を選択します。
イメージ参照ボタンを押下し、リポジトリとイメージ選択します。
イメージ選択後、関数名やロールを選択し関数を作成すれば完成です。
今回は「CreatePDFSample」という関数名で作成します。
ファイルをダウンロードのテストの為にApiGatewayを設定します。
ApiGatewayを使用すると、Lambdaから返値をファイル形式にしてダウンロードしてくれます。
Lambda関数画面でトリガー追加を押下します。
API Gatewayを選択します。
IntentはCreate a new APIを選択します。
API TypeはHTTP APIを選択します。
追加ボタンを押下するとトリガーにAPI Gatewayが作成されます。
API Gatewayを押下すると下記の画面が出てきます。
API endpointに記述してあるURLにアクセスします。
output.pdfファイルがダウンロードされたら、正常に動作せてる事が確認できます。
以上で、Dockerコンテナを使用してのLambdaの実装、デプロイ方法を紹介しました。