2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

MLモデル実行APIを GCP Run + Docker + Flask で実装してみた (M1 Mac)

Last updated at Posted at 2022-06-29

はじめに

今回はハッカソンでAI(自然言語処理)を使ったモバイルアプリを開発することになり、私はUIとサーバー(未経験)を担当したのでサーバーサイドでの備忘録を残したいと思います。

ゴール

製作物のゴールは以下のイメージです。

スクリーンショット 2022-06-29 15.48.27.png

”ML班が作ってくれた学習済みモデルコードを実行できるコンテナイメージをGCP Runにデプロイし運用、モバイル側からエンドポイントを叩いてレスポンスを取得”という流れです。

flaskアプリの作成(app.py)

app.pyはシンプルな構成で作りました。詳細は割愛ですが、GCPはデプロイ時に環境変数(PORT = 8080)を自動で入れてくれるので'PORT'を使っています。

app.py
from re import U
from flask import Flask, jsonify
import os
import main
import asyncio
import scrapeNews
import json
import codecs

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False

@app.route('/collect')
async def collect():
    await scrapeNews.main()
    json_data = json.load(open("articleList.json", "r"))
    articleList = []
    for news in json_data:
        title = news["title"]
        text = news["text"]
        url = news["url"]
        score = await main.predict(title, text)
        articleList.append({"title":title,"url":url, "score": score})
    articleListJson = json.dumps(articleList,ensure_ascii=False )
    fileName = "article_with_score_list.json"
    file = codecs.open(fileName, 'w','utf-8')
    file.write(articleListJson)
    return articleListJson
    

@app.route('/')
async def provide():
    if os.path.isfile("article_with_score_list.json"):
        json_data = json.load(open("article_with_score_list.json", "r", encoding='utf-8'))
        return jsonify(json_data)
    else:
        await collect()
        json_data = json.load(open("article_with_score_list.json", "r", encoding='utf-8'))
        return jsonify(json_data)


if __name__ == "__main__":
    app.run(debug=True, threaded=True, host="0.0.0.0", port=int(os.environ.get('PORT', 8080)))

Docker

Dockerでコンテナのビルド

Pytorch, transformer などML用のライブラリを使うことになったのですが、alpineや-slimなど軽いイメージではうまく動作しなかったので普通(?)のPythonイメージを使いました。以下が構成とDockerfile。

/HackServer
	- requirement.txt
	- docker-compose.yml
	- Dockerfile
	- opt
	   - model
	   - app.py
	   - main.py

Docerfile
FROM python:3.9

# mecabの導入
RUN apt-get -y update && \
  apt-get -y upgrade && \
  apt-get install -y mecab && \
  apt-get install -y libmecab-dev && \
  apt-get install -y mecab-ipadic-utf8 && \
  apt-get install -y git && \
  apt-get install -y make && \
  apt-get install -y curl && \
  apt-get install -y xz-utils && \
  apt-get install -y file && \
  apt-get install -y sudo

ADD . /app
WORKDIR /app

RUN apt-get install -y vim less
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools

#Rustのコンパイラがないと怒られてしまうので以下でインストール
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile default --component rls rust-analysis
ENV PATH="/root/.cargo/bin:$PATH"
RUN pip install -r requirements.txt
RUN pip install gunicorn
RUN sudo cp /etc/mecabrc /usr/local/etc/

WORKDIR /app/opt
VOLUME ./opt:/app

#非常に処理が重いのでworkers:4にした
#$PORTはGCP Runが用意してくれる(8080)のでそれとコンテナをバインドする
CMD exec gunicorn --bind :$PORT --workers 4 --threads 8 app:app
CMD python app.py

ローカル用イメージの作成

以下のコマンドでイメージを作ります。
今回チーム名がオムライスに関連してたので、作成するイメージ名を"omlette-server"にしています

$ docker build -t omlette-server .    

ローカルで実行

flaskで8080をポートにしているので、ホスト側の8080とコンテナ側の8080をバインドします。

$ docker run -e PORT=8080 -p 8080:8080 omlette-server

うまくできたら次はGCP Runにデプロイする準備です

GCP

イメージにタグをつける

GCP Runにイメージをデプロイするには Container registry (今はArtifact registoryが推奨だそうです)にプッシュしなければなりません。プッシュする前に前章で作成したイメージにタグ(latestやv1など)をつけます。

$ docker tag omlette-server asia.gcr.io/[プロジェクトID]/omlette-server:latest

プッシュする

$ docker push asia.gcr.io/hackuomletteserver/omlette-server:latest   

あとはコンソールでイメージ登録して終わり!!と思ったのですが、コンソールでデプロイする時にGCPでエラーが出てしまいました…

Failed to start and then listen on the port defined by the PORT environment variable. Logs for this revision might contain more information

どうやらM1の影響らしいです…(参考にさせていただいた記事

GCP Run の公式にもありましたがMチップMacではローカルでビルドするとうまくいかないよう。


GoogleはCloud Buildを使えと言っていますが、課金リスクは避けたいし認証まわりがめんどくさそうだったので、上記の記事の方同様にDocker のbuildxコマンドでイメージを作成し直すことに。

$ docker buildx build --platform linux/amd64 -t omlette-server-x64 .

区別しやすくするため、イメージ名を変更しています。

デプロイ設定

私はメモリの大きさやCPU数、セッション時間など細かく設定したかったのでわかりやすいコンソールで行いました(いつかコマンドでサクサクできるエンジニアになりたい…)
スクリーンショット 2022-06-28 17.52.57.png

終わりに

今回普段使わないものばかり触れていた、かつ時間がなかったので荒作業感が否めませんでした…
何かアドバイス等あればご教示いただけると幸いです。

参考にさせていただいた他の記事

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?