Djangoアプリを、Coogle Cloud の Cloud Run + Cloud Storage + Litestream な環境で動かしてみた

以前、Djangoで作った個人アプリを Python 3.10 & Django 4.1 へとアップデートしました。
Python3.7 & Django 2.1 な個人アプリを Python 3.10 & Django 4.1 へとアップデートした - メモ的な思考的な

その時、データベースをPostgreSQLからSQLiteに切り替えました。

 
元々Herokuで動かしていることもあり、再びDBをPostgreSQLに戻そうと思ったところ、以下のTweetに出会いました。

 
Litestreamの公式サイトを見たところ、SQLiteのレプリカは各種クラウドストレージに置けそうでした。
How-To Guides - Litestream

 
機会があれば Google Cloud を使ってみたいと思っていたこともあり、ためしに Google Cloud で Django + SQLite + Litestream環境を構築する方法を調べてみました。

すると、Cloud Run + Litestreamで環境構築をしている記事が見つかりました。

 
そこで今回、DjangoアプリをCoogle Cloud の Cloud Run + Cloud Storage + Litestream な環境で動かしてみることにしました。

 
目次

 

環境

  • 開発環境
  • Djangoアプリ
  • Litestream 0.3.9
  • Google Cloud
    • 開発用には何も設定していない状況
    • 使用する主なサービスは以下
      • Cloud Run
      • Cloud Storage

 

Google Cloud まわりの設定

Google アカウントまわり

Google Cloudの開発で使うアカウントを用意

個人で利用するため、Googleアカウントを用意します。

 

Googleアカウントへ二要素認証を設定

アカウントが乗っ取られると困るため、二要素認証を設定します。

 

請求先アカウントの作成

クレジットカードなどを登録します。

 

予算とアラートの設定

請求額が増えた時に気づけるよう、予算とアラートを設定します。
予算と予算アラートを作成、編集、削除する  |  Cloud Billing  |  Google Cloud

 

範囲

項目
名前 任意
期間 月別
サービス すべてのサービス
クレジット - 割引 チェックする
クレジット - プロモーションなど チェックする

 

金額

項目
予算タイプ 指定額
目標金額 100円

 

操作

予算の割合 金額 トリガー対象
1% 1円 予測
10% 10円 予測
30% 30円 予測
60% 60円 予測
100% 100円 実値

また、通知は 課金管理者とユーザーに送信されるメールアラート にチェックを入れます。

 

Litestreamを使うために、Google Cloudまわりを設定

冒頭で上げた記事を参考にして、Google Cloudまわりを設定します。

 

プロジェクトの作成

Djangoアプリ用に、新しくプロジェクトを作成します。

項目
プロジェクト名 任意の名前
場所 組織なし

 

Cloud Storageのバケットを作成

公式ドキュメントを参考に、Cloud Storage のバケットを作成します。
ストレージ バケットの作成  |  Cloud Storage  |  Google Cloud

 
今回はコンソールからバケットを作成します。
https://console.cloud.google.com/storage/browser

 
バケットは、東京に単一リージョン、Standardな形で用意します。

項目
バケット 任意
ロケーションタイプ Region (単一リージョン)
Region asia-northeast1 (東京)
ストレージクラス Standard
このバケットに対する公開アクセス禁止を適用する チェック
アクセス制御 均一
保護ツール なし

 
バケットが用意できたところで、いったん Google Cloud Console での作業は終わりです。

 

開発環境の設定

続いて WSL2上のUbuntuにて、必要な開発環境設定を行います。

 

WSL2に gcloud コマンドをインストール

公式ドキュメントに従い、Cloud SDKを WSL2 の Ubuntu にインストールします。
Cloud SDK のインストール  |  Google Cloud

 
なお、この記事ではログを残すという目的で、実行当時のコマンドを記載しています。

その後コマンドが新しくなっている可能性もあるため、公式ドキュメントのコマンドをコピペしたほうが良いです。

 

必要なパッケージを追加

$ sudo apt install apt-transport-https ca-certificates gnupg

実行すると

Daemons using outdated libraries

Which services should be restarted?

と画面に表示されるため、デフォルトのまま <了解> を選択します。

すると処理が続行します。

Restarting services...
 /etc/needrestart/restart.d/systemd-manager
 systemctl restart cron.service packagekit.service polkit.service ssh.service systemd-networkd.service systemd-resolved.service systemd-udevd.service
Service restarts being deferred:
 systemctl restart ModemManager.service
 /etc/needrestart/restart.d/dbus.service
 systemctl restart docker.service
 systemctl restart networkd-dispatcher.service
 systemctl restart systemd-logind.service
 systemctl restart unattended-upgrades.service

No containers need to be restarted.

User sessions running outdated binaries:
 <user> @ user manager service: systemd[2102]

 

source listを追加

$ echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list

 

Google Cloudの公開鍵を追加

$ curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -

...
OK

 

Cloud SDK をインストール

$ sudo apt update && sudo apt install google-cloud-sdk

を実行すると、再び画面に以下が表示されます。

Daemons using outdated libraries

Which services should be restarted?

今回もデフォルトのまま <了解> を選択すると、処理が続行します。

Restarting services...
Service restarts being deferred:
 /etc/needrestart/restart.d/dbus.service
 systemctl restart docker.service
 systemctl restart networkd-dispatcher.service
 systemctl restart systemd-logind.service
 systemctl restart unattended-upgrades.service

No containers need to be restarted.

User sessions running outdated binaries:
 <user> @ user manager service: systemd[2102]

 

WSL2上で gcloud init --console-only を実行

gcloudコマンドのインストールが終わったため、次は WSL2 上で gcloud コマンドによる初期設定を行います。

今回は画面のないWSL2上で実行するため、公式ドキュメントにある通り --console-only フラグを使用します。
Cloud SDK の初期化  |  Cloud SDK のドキュメント  |  Google Cloud

なお、画面には WARNING と出ていますが、現時点では使えるみたいです。

$ gcloud init --console-only

WARNING: The `--console-only/--no-launch-browser` are deprecated and will be removed in future updates. Use `--no-browser` as a replacement.
Welcome! This command will take you through the configuration of gcloud.

Your current configuration has been set to: [default]

You can skip diagnostics next time by using the following flag:
  gcloud init --skip-diagnostics

Network diagnostic detects and fixes local network connection issues.
Checking network connection...done.
Reachability Check passed.
Network diagnostic passed (1/1 checks passed).

 
そのうち以下が表示されるため、 y を入力します。

You must log in to continue. Would you like to log in (Y/n)?  y

 
次は

Go to the following link in your browser:

    https://accounts.google.com/o/oauth2/auth?***

Enter authorization code:

が表示されます。

Windows上のブラウザに表示されているURLをコピペし、冒頭で用意した Google Cloud 開発用のGoogleアカウントでログインします。

ログイン後に操作を進めていくと、verification code が表示されている Sign in to the gcloud CLI 画面になります。

この verification codeを、WSL2のターミナルにある Enter authorization code へ入力します。

 
入力後、対話型で設定が進みます。

使用するプロジェクトを聞かれるため、先ほど用意した Google Cloud のプロジェクトの番号を指定します。

You are logged in as: [***@example.com].

Pick cloud project to use:
 [1] <project id>
 [2] Enter a project ID
 [3] Create a new project
Please enter numeric choice or text value (must exactly match list item):  1

 
プロジェクトを指定すると、処理が進んでいき、初期設定が完了します。

Not setting default zone/region (this feature makes it easier to use
[gcloud compute] by setting an appropriate default value for the
--zone and --region flag).
See https://cloud.google.com/compute/docs/gcloud-compute section on how to set
default compute region and zone manually. If you would like [gcloud init] to be
able to do this for you the next time you run it, make sure the
Compute Engine API is enabled for your project on the
https://console.developers.google.com/apis page.

Created a default .boto configuration file at [/home/<user>/.boto]. See this file and
[https://cloud.google.com/storage/docs/gsutil/commands/config] for more
information about configuring Google Cloud Storage.
Your Google Cloud SDK is configured and ready to use!

* Commands that require authentication will use ***@example.com by default
* Commands will reference project `<project id>` by default
Run `gcloud help config` to learn how to change individual settings

This gcloud configuration is called [default]. You can create additional configurations if you work with multiple accounts and/or projects.
Run `gcloud topic configurations` to learn more.

Some things to try next:
* Run `gcloud --help` to see the Cloud Platform services you can interact with. And run `gcloud help COMMAND` to get help on any gcloud command.
* Run `gcloud topic --help` to learn about advanced features of the SDK like arg files and output formatting
* Run `gcloud cheat-sheet` to see a roster of go-to `gcloud` commands.

 
画面にもある通り /home/<user>/.boto ファイルが作成されています。

 

Djangoアプリの設定変更

Cloud Run で実行するため、Djangoアプリの設定を変更します。

 

Dockerfileの修正

以前の記事で用意したDockerfileを修正します。
WSL2 + Ubuntu 22.04.1 な環境上のDockerへ、既存のDjangoアプリを載せてみた - メモ的な思考的な

 
修正後の全体像はこんな感じです。

FROM python:3.10.7-slim

# Djangoアプリの8080番ポートを公開するおしらせ
EXPOSE 8080

# 必要なライブラリをインストール
# 実行時に `debconf: delaying package configuration, since apt-utils is not installed` が出るが、無視してよさそう
RUN apt-get update \
    && apt-get install -y --no-install-recommends sqlite3 \
    && apt-get -y clean \
    && rm -rf /var/lib/apt/lists/*

# Download the static build of Litestream directly into the path & make it executable.
# This is done in the builder and copied as the chmod doubles the size.
# ref: https://github.com/steren/litestream-cloud-run-example/blob/main/Dockerfile
ADD https://github.com/benbjohnson/litestream/releases/download/v0.3.9/litestream-v0.3.9-linux-amd64-static.tar.gz /tmp/litestream.tar.gz
RUN tar -C /usr/local/bin -xzf /tmp/litestream.tar.gz

# working directoryの設定
WORKDIR /app

# リポジトリのファイルをコピー
COPY . /app

# パッケージインストール
RUN pip install --no-cache-dir -r requirements.txt

# Copy Litestream configuration file & startup script.
COPY ./litestream.yml /etc/litestream.yml
COPY ./run.sh /app/run.sh

#CMD ["python", "manage.py", "runserver", "0.0.0.0:8080"]
CMD ["/bin/bash", "/app/run.sh"]

 

run.sh ファイルの作成

以下の記事をベースに、「Cloud RunでDjangoアプリを起動する際に、Litestreamにより Cloud Storage から SQLite をリストアする」処理を書いた run.sh ファイルを作成します。
SQLite + Litestream + CloudRun で「個人開発並みの予算でもSQLを捨てない」バックエンド構築(Next.jsを例にして) - Qiita

#!/bin/sh
set -e

# コンテナ起動時に持っているSQLiteのデータベースファイルは、
# 後続処理でリストアに成功したら削除したいので、リネームしておく
if [ -f /app/ringo.db ]; then
  mv /app/ringo.db /app/ringo.db.bk
fi

# Cloud Storage からリストア
litestream restore -if-replica-exists -config /etc/litestream.yml /app/ringo.db

if [ -f /app/ringo.db ]; then
  # リストアに成功したら、リネームしていたファイルを削除
  echo "---- Restored from Cloud Storage ----"
  rm /app/ringo.db.bk
else
  # 初回起動時にはレプリカが未作成であり、リストアに失敗するので、
  # その場合には、冒頭でリネームしたdbファイルを元の名前に戻す
  echo "---- Failed to restore from Cloud Storage ----"
  mv /app/ringo.db.bk /app/ringo.db
fi

# マイグレーションを実行
python manage.py migrate

# レプリケーションしながらDjangoを起動
exec litestream replicate -exec "python manage.py runserver 0.0.0.0:8080" -config /etc/litestream.yml

 

Litestreamの設定ファイルを作成

必要な設定ファイルになります。

なお、 REPLICA_URL は gcloud コマンドでデプロイする時に与える値となります。

実際には、バケットの名前 (+ 必要に応じてディレクトリ名) になります。

dbs:
  - path: /app/ringo.db
    replicas:
      - url: ${REPLICA_URL}

 

.gcloudignore ファイルの作成

今回、Dockerfileの中で COPY を行っています。

ただ、 Cloud Run 向けの場合、COPY対象外のファイルは .dockerignore ではなく、 .gcloudignore ファイルが使われます。

 
今回は、以下の記事に方針に従い、必要なものだけを指定します。
.gcloudignore で全部無視して必要なものだけ指定する - ぽ靴な缶

また、指定した後は、WSL2上で gcloud meta list-files-for-upload を実行し、余計なファイルが含まれていないか確認します。

なお、DBは ringo.db ファイルなので、忘れないように指定します。

# ignore all
*

# upload list
# directories
!apps
!dj_ringo_tabetter
!static
!templates

# files
!apples.yaml
!Dockerfile
!litestream.yml
!manage.py
!requirements.txt
!ringo.db
!run.sh

# ignore
**/*.pyc

 

Djangoの settings.py にある ALLOWED_HOSTS を修正

初めてデプロイするまでは Cloud RunのURLが決まらないっぽいので、ひとまず ALLOWED_HOSTS = ['*'] にしておきます。

URLが決まったら、そのホスト名に修正し、再度デプロイします。

 
以上で設定変更は完了です。

 

gcloud beta run deploy コマンドでデプロイ

以下を参考に、 gcloud beta run deploy コマンドでデプロイします。

今回使用するコマンドはこんな感じです。

gcloud beta run deploy ringo-tabetter \
  --source .  \
  --set-env-vars REPLICA_URL=gcs://<バケット名>/<ディレクトリ名> \
  --max-instances 1 \
  --execution-environment gen2 \
  --no-cpu-throttling \
  --allow-unauthenticated \
  --region asia-northeast1 \
  --project <プロジェクト名>

 
コマンドの引数は色々ついていますが、

な設定です。

 
なお、 execution-environment オプションはbetaにしか存在しないようで、 beta なしだとエラーになります。

公式ドキュメントにも beta の記載があります。
Selecting an execution environment (services)  |  Cloud Run Documentation  |  Google Cloud

ERROR: (gcloud.run.deploy) unrecognized arguments:

 --execution-environment flag is available in one or more alternate release tracks. Try:

  gcloud alpha run deploy --execution-environment
  gcloud beta run deploy --execution-environment

  --execution-environment
  gen2
  To search the help text of gcloud commands, run:
  gcloud help -- SEARCH_TERMS

 
gcloud beta run deploy を実行すると、以下が表示されます。 y を入力して進みます。

API [artifactregistry.googleapis.com] not enabled on project [***]. Would you like to enable and retry (this will take a few minutes)? (y/N)?  y

 
次の表示も、 y を入力します。

Enabling service [artifactregistry.googleapis.com] on project [***]...
Operation "operations/***" finished successfully.
Deploying from source requires an Artifact Registry Docker repository to store built containers. A repository named
[cloud-run-source-deploy] in region [asia-northeast1] will be created.

Do you want to continue (Y/n)?  y

 
次も y です。

This command is equivalent to running `gcloud builds submit --pack image=[IMAGE] .` and `gcloud run deploy <app> --image [IMAGE]`

API [run.googleapis.com] not enabled on project [***]. Would you like to enable and retry (this will take a fewminutes)? (y

 
すると、ビルドが走ります。質問があった時は y で続けます。

Enabling service [run.googleapis.com] on project [91644765501]...
Operation "operations/acf.***" finished successfully.
Building using Buildpacks and deploying container to Cloud Run service [ringo-tabetter] in project [***] region [asia-northeast1]
⠼ Building and deploying new service... Uploading sources.
⠹ Building and deploying new service... Uploading sources.
  ✓ Uploading sources...
  . Building Container...
  . Creating Revision...
  . Routing traffic...
  . Setting IAM Policy...
API [cloudbuild.googleapis.com] not enabled on project [***]. Would you like to enable and retry (this will take a few minutes)? (y/N)?

 
デプロイが正常に終わると、以下の表示になります。

Service [ringo-tabetter] revision [***] has been deployed and is serving 100 percent of traffic.
Service URL: https://ringo-tabetter-syqtxyot6q-an.a.run.app

 
なお、手元の環境では一度以下のエラーになりました。ただ、再実行したところデプロイが完了しました。

Enabling service [cloudbuild.googleapis.com] on project [***]...
Operation "operations/acf.***" finished successfully.
Deployment failed
ERROR: (gcloud.beta.run.deploy) INVALID_ARGUMENT: could not resolve source: googleapi: Error 403: ***@cloudbuild.gserviceaccount.com does not have storage.objects.get access to the Google Cloud Storage object., forbidden

 

動作確認

Service URLとして表示されている https://ringo-tabetter-syqtxyot6q-an.a.run.app にアクセスすると、いつものアプリが表示されました。

 

状況確認

請求の確認

以下のページより請求額を確認します。
https://console.cloud.google.com/billing

今のところ請求は発生していないようです。

 

Cloud Storage の使用量確認

Cloud StorageのConsoleでは確認できなかったため、Metrics Explorerより確認します。
hawksnowlog: GCP のクラウドストレージでバケットの使用量を確認する方法

デプロイ用バケットは約130MB、Litestream用のバケットは 0.07MBでした。

Cloud Storageは5GBまで無料枠があるため、当分は大丈夫そうです。
https://cloud.google.com/storage/pricing?hl=ja#cloud-storage-always-free  
 

Artifact Registoryへの登録確認

今回デプロイしたイメージが登録されています。
https://console.cloud.google.com/artifacts

Artifact Registoryの使用量はAPIとサービスで確認すればよいのかな。。。
https://console.cloud.google.com/apis/api/artifactregistry.googleapis.com/quotas

 

その他

Cloud Runの停止方法について

Cloud Runを停止するのではなく、ロールを外すことで実現できるようです。
Cloud Runでサービスを停止する

 

WSL2で gcloud auth login する場合の注意点

--no-launch-browser が使えなくなるようです。
WSL2でのCloud SDKツールの承認を行う方法(gcloud auth login)がちょっとややこしくなっているらしい - Qiita

ただ、上記QiitaでリンクされているIssue Trackerを見ると

update to what I have observed - using Google Cloud SDK 398.0.0: - the "--no-launch-browser" switch no longer gives a deprecation warning and continues to work the way it always did. That's awesome - much appreciated to the Google devs!

So anyone using the "--no-browser" switch, just change to "--no-launch-browser" and you should be good to go AFAIK.

https://issuetracker.google.com/issues/224754679?pli=1

というコメントが 8/25にあったため、また状況が変わっているのかもしれません。

 

ソースコード

Githubに上げました。
https://github.com/thinkAmi/dj_ringo_tabetter

今回のプルリクはこちらです。
https://github.com/thinkAmi/dj_ringo_tabetter/pull/19