技術Fast APIをDockerコンテナ化しCloudrunに継続的デプロイする

はじめに

Web サービスで最適化を行う際、データベースからデータをとってきて、最適化を実行しその結果をフィードバックするというような一連のサイクルを考慮する必要があります。

今回は最適化を行うにあたり、ざっと以下のような要件がありました。

  1. 最適化実行のトリガーはクライアントでの操作
  2. データベースとの接続(CRUD)
  3. 処理時間が長いため、バックグランドでのタスク実行
  4. 結果の通知
  5. 非同期処理

今回はこれら要件を踏まえた上で扱いやすそうな FastAPI を使用することにしました。 FastAPI を用いた API の実装は別の記事にするとして、今回は構築した API の Docker 化から Cloud Run へのデプロイと CI/CDパイプライン までやります。

盛りだくさん!!

Docker 化

まずはサービスを Docker 化していきます。 今回は python のパッケージ管理に pip ではなく、poetry を使用したのでそこが一番詰まりました。

  処理の流れはシンプルです。

  1. 使用する PORT を開放
  2. ソースをコンテナ内にコピー
  3. コンテナ内で poetry の環境構築
  4. python の Path を通す
  5. uvicorn サーバーを実行

以下のような Dockerfile に落ち着きました。

FROM tiangolo/uvicorn-gunicorn-fastapi:python3.8 ENV PYTHONDONTWRITEBYTECODE 1 EXPOSE 8080 COPY ./src /src COPY ./src/pyproject.toml ./src/poetry.lock* WORKDIR /src/ RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/opt/poetry python3 - && \ cd /usr/local/bin && \ ln -s /opt/poetry/bin/poetry && \ poetry config virtualenvs.create false ARG INSTALL_DEV=false RUN bash -c "if [ $INSTALL_DEV == 'true' ] ; then poetry install --no-root ; else poetry install --no-root --no-dev ; fi" ARG INSTALL_JUPYTER=false RUN bash -c "if [ $INSTALL_JUPYTER == 'true' ] ; then pip install jupyterlab ; fi" ENV PYTHONPATH=/src CMD [ "uvicorn", "app.main:app", "--reload","--host", "0.0.0.0", "--port", "8080"]

また同じディレクトリ内に、run.sh を作成し、ローカルで簡単に Docker プロセスを実行できるようにしています。 optimize_service というコンテナが local になければビルド後実行し、あればそのまま実行するというスクリプトです。

#!/bin/bash NAME='optimize_service' trap "docker stop $NAME" SIGINT if [ "$(docker ps -q -a -f name=$NAME)" ];then echo 'run local docker container' docker start $NAME && docker logs -f $NAME exit 1 else docker build -t $NAME . docker run --log-opt max-size=10m --log-opt max-file=3 --name $NAME -v ${PWD}/src/:/src -p 8008:8080 $NAME fi

デプロイ

Docker 化が終わったので、早速デプロイをしていきます。 GCP のコンテナ管理は Container Registry を拡張したAritifact Registry を使用します。 ざっくり全体の流れは以下です。

  1. gcp で使用するサービスの開放(今回は Artifact Registry と Cloud Run です)
  2. サービスの IAM で必要な権限を持ったユーザーを作成する
  3. 作成したアカウントの service-account.json をダウンロード
  4. 作成したアカウントを認証する
  5. build して Artifact Registry へプッシュ
  6. Artifact Registry から Cloud Run へ Deploy gccloud build を使って、ローカルのイメージを Aritifact Registry 上に配置します。その後、gccloud deploy で Artifact Registry 上のイメージを Cloud Run にデプロイします。

1,2,3 は GCP の管理画面をぽちぽちします。そのほかは gcloudコマンドを使います。具体的なコマンドは下記 gitlab CI のスクリプトを参考にしてみてください。 ここまでやってしまえば、あとはローカルで実行したコマンドをベースに CI/CD のためのスクリプトを書けば OK です。

gitlab CI を使って、自動デプロイ設定

下記のような ci スクリプトを作成しました。やっていることは各種環境変数の設定と上記ローカルでデプロイした一つ一つの処理の記述です。

image: google/cloud-sdk stages: - deploy deploy-prot: stage: deploy services: - name: docker:19.03.8-dind variables: DOCKER_HOST: tcp://docker:2375 DOCKER_TLS_CERTDIR: "" environment: name: prot only: - develop script: - echo GMAP_KEY=$GMAP_KEY > .env - echo SLACK_URL=$DEV_SLACK_URL >> .env - echo CLIENT_URL=$DEV_CLIENT_URL >> .env - echo $DEV_SERVICE_ACCOUNT > service-account.json - cp service-account.json src - cp .env src - gcloud auth activate-service-account --key-file service-account.json - gcloud config set project $PROJECT_NAME - gcloud auth configure-docker $REGION_URL - docker build -t $IMAGE_PATH . - docker push $IMAGE_PATH - gcloud run deploy $CLOUD_RUN_SERVICE_NAME --image $IMAGE_PATH --region $REGION --allow-unauthenticated --timeout 3000s --memory 4Gi --cpu 4

必要に応じて、ブランチによる環境切り替えやテストの実行などを記述すれば、それなりの CI/CD パイプラインが組めてしまいます!

参考になった qiita 記事

まとめ

トライ&エラーをたくさんしましたが、CI スクリプトは一度書いてしまえば開発体験はめちゃめちゃ上がるので早めに書いておいた方がいいですね! また Serverless といえば、AWS Lambda や Cloud Functions ばかり使ってましたが、最適化のような処理時間がネックになるようなケースでは Cloud Run という選択肢はとても良い気がします。 以上、お疲れ様でした!

Topics

tech

カテゴリー

profile

たつまる

福岡出身社会人6年目文系エンジニア。毎日必死に生きています。 技術キャッチアップ、考えの整理や経験をアウトプットしていきます。 文系学部卒→新卒未経験エンジニア→中国深センで転職→中国系ロボット会社勤務→独立