GCP(Google Cloud Platform)のGCR(Google Cloud Run)でDockerコンテナを動かしてみる。動かすサービスはPDFファイルをJPEGの画像ファイルに変換するという簡単なもの。言語はPython、フレームワークはFlask、PDF→JPEG変換ライブラリはpopplerをpdf2imageで呼び出す。
ディレクトリ構成
├── Dockerfile
├── main.py
└── templates
└── main.html
コンテナ作成
コンテナの構成を記入したDockerfileはGoogle Cloud Runのドキュメントのクイックスタートから持ってきて必要なものを付け加えていく。
# Use the official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.9-slim
# Allow statements and log messages to immediately appear in the Knative logs
ENV PYTHONUNBUFFERED True
# Copy local code to the container image.
ENV APP_HOME /app
WORKDIR $APP_HOME
RUN apt update && apt install -y poppler-utils
# Install production dependencies.
RUN pip install Flask gunicorn pdf2image
COPY . ./
# Run the web service on container startup. Here we use the gunicorn
# webserver, with one worker process and 8 threads.
# For environments with multiple CPU cores, increase the number of workers
# to be equal to the cores available.
# Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
PDF変換ライブラリのインストール
RUN apt update && apt install -y poppler-utils
この行を追加して、poplerをコンテナにインストールする。
RUN pip install Flask gunicorn pdf2image
pipでインストールするpythonライブラリに pdf2image も追記。
Webアプリ作成
メインのプログラムをpythonで作成
import os
from flask import Flask, render_template, request, make_response, jsonify, send_file
import werkzeug
from datetime import datetime
from pathlib import Path
from pdf2image import convert_from_path
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024
UPLOAD_DIR = os.getenv("UPLOAD_DIR_PATH", "/tmp")
@app.route("/")
def hello_world():
return render_template('main.html', result='PDFファイルを選択してください')
@app.route("/pdftojpeg", methods=['POST'])
def pdftojpeg():
if 'uploadFile' not in request.files:
return render_template('main.html', result='uploadFile is required.')
file = request.files['uploadFile']
fileName = file.filename
if '' == fileName:
return render_template('main.html', result='filename must not empty.')
saveFileName = werkzeug.utils.secure_filename(fileName)
pdf_path = Path(UPLOAD_DIR , saveFileName)
file.save(str(pdf_path))
pages = convert_from_path(str(pdf_path), 300)
for i, page in enumerate(pages):
file_name = pdf_path.stem + "_{:02d}".format(i + 1) + ".jpg" if i>0 else pdf_path.stem + ".jpg"
image_path = Path(UPLOAD_DIR , file_name)
page.save(str(image_path), "JPEG")
attachmentmode = True if request.form.get("attach") else False
return send_file(str(image_path), mimetype='image/jpeg', as_attachment = attachmentmode)
@app.errorhandler(werkzeug.exceptions.RequestEntityTooLarge)
def handle_over_max_file_size(error):
return render_template('main.html', result='file size is overed.')
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
テンプレートファイル。装飾は省略。
<html>
<head>
<title>PDFをJPEGに変換</title>
</head>
<body>
<h1>PDF→JPEGコンバーター</h1>
<div>{{ result }}</div>
<form method="post" action="./pdftojpeg" enctype="multipart/form-data">
<input type="hidden" name="attach" value="1">
<input type="file" name="uploadFile" accept=".pdf">
<button type="submit">変換する</button>
</form>
</body>
</html>
コンテナイメージの作成とデプロイ
コンテナイメージの作成
gcloudコマンドでGCPの操作をコマンドラインで行えるようにする。Google Cloud Shellであればすでにgcloudコマンドが使える状態になったコンソールをブラウザから利用できるのでお手軽。もちろん自前のWindowsやMac上にGoogle cloud SDKをインストールして環境を整えることも可能。
gcloudコマンドを使える環境で下記のコマンドを実行して、コンテナイメージを作成する。Project IDとコンテナイメージ名は適宜変更する。(GCPプロジェクトを作成していない場合はまず作成しておく)
gcloud builds submit --project [PROJECT ID] --tag gcr.io/[PROJECT ID]/helloworld
ビルドが行われ、成功するとイメージ名(gcr.io/PROJECT ID/helloworld)とSUCCESSと表示される。コンテナイメージが作成され、Container Registry(実際の保存場所はCloud Storage)へ保存された。
デプロイ
作成したコンテナイメージをCloud Runで実行できるようにする作業がデプロイ。次のコマンドを実行することでデプロイが行える。プロジェクトIDやイメージ名を実際のものに置き換える。
gcloud run deploy --project [PROJECT ID] --image gcr.io/[PROJECT ID]/helloworld
Service nameを聞かれたらデフォルトのままEnter。リージョンを設定していない場合はリージョンを聞かれる場合もあるのでus-central1などを選択。未認証の呼び出しを許可するように尋ねられたらy
しばらく待つとデプロイが完了する。Service URL:の後にURLが表示されるのでブラウザでアクセスするとWebアプリが実行される。
やってみて
Dockerコンテナで動くように作っておけば、あっけないほど簡単に動かすことができた。サーバーのことは考えなくてもコンテナだけ乗っけとけば後はスケーリングとか自動でやってくれるのでとてもお手軽だなと思った。
Comments