Cloud RunでPDFをJPEGに変換するPython+Flaskなコンテナを実行する

GCP(Google Cloud Platform)のGCR(Google Cloud Run)でDockerコンテナを動かしてみる。動かすサービスはPDFファイルをJPEGの画像ファイルに変換するという簡単なもの。言語はPython、フレームワークはFlask、PDF→JPEG変換ライブラリはpopplerをpdf2imageで呼び出す。

使用技術
  • Google Cloud Run → サーバーレス
  • Google Cloud Build → コンテナイメージの作成
  • Docker → コンテナ作成
  • Python + Flask → Webアプリ
  • poppler + pdf2image → PDFからJPEGへの変換ライブラリ

ディレクトリ構成

├── 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

タイトルとURLをコピーしました