背景
label-studioというアノテーションツールがあり、まずは個人利用のためローカルで利用していたのもつかの間、あっという間に利用ユーザ数が増えたのでちゃんとしたサービスにしようということでGoogle Cloud PlatformのCloud Runで運用しようというきっかけでlabel-studioサービスを立ち上げることになりました。
課題と対策
label studioのgitリポジトリを読んでいると「Run on Google Cloud」とあるので簡単にサービス立ち上げできるかなと思いきや、コスト最小化の制約をつけると以下の課題がわかった。
- コスト最小化のため、CloudRunのインスタンス数を0にするとLabel-Studioサービス内で保存しているデータが削除される
- データ永続化のために、CloudSQLを使うと月あたり100ドルを余裕で超えてくる
こういった課題からどうやったらデータを永続化しつつコスト最小でサービスを立ち上げることができるか?と検討した結果、Litestream + CloudRunという構成でCloudRunサービスのデータ永続化できることがわかったので、これをlabel-studioに適用してみた。
フロー
以下本サービス立ち上げに関して実施した手順である。
方針の検討と決定
クラウドサービスを作るにあたって、やっぱりgitリポジトリを作成しその中にソースを入れ・・・といったことをやるととっても面倒なので、今回はGoogle Cloudコンソール上で必要なソースを取得し、そのソースの一部だけを変更しDockerイメージを作成する。
作成したDockerイメージをArtifactRegistryに登録し、それをCloudRunサービスとしてデプロイするというとってもシンプルな進め方にした。
カスタムlabel-studioイメージの作成
以下の順番通り実施することで、カスタムlabel-studioイメージを作成しました。
Google Cloudコンソールの立ち上げ
ブラウザ経由で進めてもいいが、ターミナルソフトで立ち上げるのが気に入っているので以下のコマンドでGoogle Cloudコンソールを起動する
gcloud cloud-shell ssh --authorize-session
label-studioのソースを取得
以下のコマンドでlabel-studioのソースを取得する
git clone https://github.com/HumanSignal/label-studio
litestreamの導入
label-studioはデフォルトでSQLite動作するのでそのあたりの変更は全くせず、デフォルトで保存されているSQLiteファイルの場所を確認し、以下のパスに保存されていることを確認した。
/label-studio/data/label_studio.sqlite3
この内容を用いて以下のようなlitestream.ymlをローカルPC内に作成する。Google Cloud Storage(以後:GCS)のパスは自分の環境に合わせて決めてください。
dbs:
- path: /label-studio/data/label_studio.sqlite3
replicas:
- url: gcs://[Google Cloud Storageのバケット名]/[保存したいフォルダ名]
その後、litestream経由でlabel-studioを起動するシェルスクリプトentrypoint.shを以下のような感じでローカルPCに作成する。
#!/bin/bash
set -e
# when launching instance, remove sqlite file if it exists.
if [ -f /label-studio/data/label_studio.sqlite3 ]; then
rm /label-studio/data/label_studio.sqlite3
fi
# Restore the database
litestream restore -if-replica-exists -config /etc/litestream.yml /label-studio/data/label_studio.sqlite3
# Run litestream with label-studio as the subprocess.
exec litestream replicate -exec "label-studio" -config /etc/litestream.yml
gitクローンによって取得したlabel-studioディレクトリ内にあるDockerfileを以下のように修正する。これはGoogle Cloud コンソール内なので、viを用いるかGoogle Cloud Shellエディタを用いて修正する。
修正した内容は以下の通り。
- litestreamをgithubから取得→解凍→Dockerイメージ内/usr/local/binに展開
- 先ほど作成したlitestream.ymlおよびentorypoint.shをDockerイメージ内にコピー
- 環境変数「LABEL_STUDIO_ONE_CLICK_DEPLOY」を1に設定
必要に応じて「LABEL_STUDIO_DISABLE_SIGNUP_WITHOUT_LINK」や「LABEL_STUDIO_USERNAME」、「LABEL_STUDIO_PASSWORD」を設定して不用意にユーザ追加できないようにするのもあり。 - 起動時にlabel-studioを立ち上げるのではなく、entorypoint.shを立ち上げるように変更
# syntax=docker/dockerfile:1
ARG NODE_VERSION=18
:
(省略)
:
RUN python3 label_studio/manage.py collectstatic --no-input && \
chown -R 1001:0 $LS_DIR && \
chmod -R g=u $LS_DIR
### =====================================================
# add litestream for SQLite replication
### -----------------------------------------------------
ADD https://github.com/benbjohnson/litestream/releases/download/v0.3.13/litestream-v0.3.13-linux-amd64.tar.gz /tmp/litestream.tar.gz
RUN tar -C /usr/local/bin -xzf /tmp/litestream.tar.gz
COPY --chmod=755 web/new/entrypoint.sh /usr/bin/entrypoint.sh
COPY web/new/litestream.yml /etc/litestream.yml
ENV LABEL_STUDIO_ONE_CLICK_DEPLOY=1
### =====================================================
ENV HOME=$LS_DIR
:
(省略)
:
ENTRYPOINT ["./deploy/docker-entrypoint.sh"]
### =====================================================
# add litestream for SQLite replication
### -----------------------------------------------------
#CMD ["label-studio"]
CMD ["/usr/bin/entrypoint.sh"]
### =====================================================
作成したファイルをコピーや編集対象ファイル
litestream.ymlとentorypoint.shをGoogle Cloud Shellエディタに対して直接D&Dすることで所定のディレクトリにコピーできる。
ここでコピーする場所は以下の通りである。また編集するDockerfileの位置も同様に示す。
(補足:web以下に配置した理由は私のDocker知識があまりないせいでどうもCOPYでエラーになるため、COPYでエラーにならないディレクトリを探した結果webディレクトリ以下だったからである。これは後程解決したい問題の一つです)
/label-studio
|___/web
| |____/new <---新規追加
| |___litestream.yml <---新規追加
| |___entorypoint.sh <---新規追加
|___Dockerfile <---上書き修正
Dockerイメージファイルの作成
カレントディレクトリを/label-studioに移動し、以下のコマンドを実行しDockerイメージを作成する
docker build -f Dockerfile -t label-studio .
起動確認など
実際に作ったDockerイメージを動かして無事litestreamのサブプロセスとしてlabel-studioが起動していることを確かめた。
docker run -it label-studio
ArtifactRegistryへの登録
リポジトリ作成
以下のように、Google Cloudコンソール上でArtifactRegistryサービスに移動し適切なリポジトリ名でリポジトリを作成する。ここではリージョンはasia-northeast1、リポジトリ名はlabel-studioとする。
リージョンasia-northeast1のDockerリポジトリに対する認証を設定
以下のコマンドを実行し、Dockerリポジトリに対する認証設定をする。
gcloud auth configure-docker asia-northeast1-docker.pkg.dev
タグ付け
ビルドしたDockerイメージに対してArtifactRegistry用にタグ付けをする。
以下のコマンドを実行し、作成したDockerイメージのIMAGE IDを取得する
docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
label-studio latest 5064a45a61ea 16 seconds ago 1.46GB
そのIMAGE IDを用いて以下のコマンドでタグ付けをする。
docker tag 5064a45a61ea asia-northeast1-docker.pkg.dev/[GCPのプロジェクト名]/label-studio/202407261200
docker tag [IMAGE ID] [ArtifactRegistryの場所]/[GCPのプロジェクト名]/[リポジトリ名]/[リポジトリで利用するイメージ名]
ArtifactRegistryへ登録
タグ付け後、以下のコマンドでArtifactRegistryに登録する。
docker push asia-northeast1-docker.pkg.dev/[GCPのプロジェクト名]/label-studio/202407261200
CloudRunのデプロイ
以下のコマンドで直接Cloud Runサービスを立ち上げることができる。
gcloud run deploy label-studio --image asia-northeast1-docker.pkg.dev/[GCPのプロジェクト名]/label-studio/202407261200:latest
gcloud run deploy [リポジトリ名] --image [ArtifactRegistryの場所]/[GCPのプロジェクト名]/[リポジトリ名]/[リポジトリで利用するイメージ名]:[tag]
GCPのアクセス権関係の付与
CloudRunで動作するlabel-studioサービスはlitestream.ymlで設定したGCSにアクセスするため、デプロイ後にGoogle Cloudコンソール上でCloudRunサービスにアクセスし、YAML内のserviceAccountNameからサービスアカウント名を確認し、そのサービスアカウントに対して権限を付与する。
storage.buckets.get
storage.buckets.getIamPolicy
storage.buckets.update
storage.objects.create
storage.objects.delete
storage.objects.get
storage.objects.getIamPolicy
storage.objects.list
storage.objects.update
resourcemanager.projects.get
resourcemanager.projects.list
storage.managedFolders.get
storage.managedFolders.list
カスタムドメインでの運用
以下CloudRunサービスにある統合で「インテグレーションを追加」を押して「カスタムドメイン-Google Cloud Load Balancing」を選択する。あとは追加したいドメインを記述して作成することで、カスタムドメイン接続に必要な情報が生成されるので、それを利用する。
カスタムドメインの設定後はLoad BalancerやCloud ArmorやIAPを設定すれば必要最低限のネットアクセスが可能なのでセキュアなサイト構築ができるので、そこは環境に合わせて設定するように。
Label-Studioの設定
アクセス権の設定
label-studioのマニュアルを参照し、クラウドストレージを利用する場合、必要に応じてアクセス権を付与する必要がある。
GCSに対するCORSの設定
label-studioのマニュアルを参照し、こちらの環境下であるGCSのバケットに対してCORS設定を更新した。
それでもソースファイルを見に行った時に以下のエラーが生じた場合、ADCの設定を見直すとできるときがある。
実際に私が実施した内容は、設定したADC設定は以下の図に「Paste the contents of credentials.json here OR ...」書いてあるので、サービスアカウントのJSONを作成し、そのファイルを開いて中身をすべて選択しコピーした内容を「Google Application Credentials, leave blank to use default credentials(ADC)」にペーストした。結果、上記エラーが起きなくなった
その後
以下のように環境変数を組み合わせることで、デプロイするだけで特定snapshotに復元してから立ち上げたりとかできるので、用途に合わせて組み込んでいくといい感じだった。
# (抜粋)
if [ "$USE_TARGET_GENERATION" = 'true' ]; then
litestream restore -if-replica-exists -generation $TARGET_GENERATION -o /label-studio/data/label_studio.sqlite3 gcs://(GCSのパス)
else
litestream restore -if-replica-exists -config /etc/litestream.yml /label-studio/data/label_studio.sqlite3
fi
./litestream snapshots gcs://(GCSのパス)
replica generation index size created
gcs 0d97977e3789e113 0 780271 2024-08-13T07:07:22Z
gcs 28b7e7f8c4753701 0 780272 2024-08-20T01:24:12Z
以下のようにコマンドで実行してもよいし、Cloud Runコンソールで環境変数を設定してデプロイしても良い。
gcloud run deploy [リポジトリ名] --image [ArtifactRegistryの場所]/[GCPのプロジェクト名]/[リポジトリ名]/[リポジトリで利用するイメージ名]:[tag] --set-env-vars "USE_TARGET_GENERATION=true" --set-env-vars "TARGET_GENERATION=(generationの値)"
※Cloud Runの環境変数に設定されるので、一度立ち上げた後はCloud Runコンソールからその環境変数を削除しておこう。
動作確認環境 (2024/9/11追記)
label-studio:1.13.1.dev0
確認したブラウザ:Google Chrome 127.0.6533.0
上記追加した理由は新しいGoogle Chrome 128.x.x.xではプロジェクトの内容が描画されなかったため、label-studio側のアップデートが必要になったため
label-studio側をアップデートしてどうなるかは今後検討する。