Cloud Run マルチコンテナの構築
プレビュー版の頃に少し使っており、GAされたので投稿することにしました。
Cloud Runでサービスを作りながら説明します。
マルチコンテナの記法のみ知りたい方は構成ファイルの作成・デプロイから見てください。
・ドキュメント
やること
APIにテキストファイルをPOSTするとテキストが音声に変換されるサービスを作成します。
単一のCloud RunインスタンスにREST APIと音声変換(VOICEVOX)のコンテナを構築します。
VOICEVOXについて
VOICEVOXとは無料で使えるテキスト読み上げソフトウェアです。
CUIやGUIソフトウェア、dockerイメージなど幅広いサービスから利用できます。
本記事ではdocker hubのイメージを利用します。
事前準備
API有効化
以下のAPIを有効化します。
既にしている場合はスキップ。
Artifact Registry API
Cloud Resource Manager API
Cloud Run Admin API
# API 有効化
gcloud services enable artifactregistry.googleapis.com
gcloud services enable cloudresourcemanager.googleapis.com
gcloud services enable run.googleapis.com
サービスアカウントの作成
Cloud Runはサービスアカウントを設定しなければ、
デフォルトの Compute Engine サービス アカウント が使用されます。
サービスアカウントを設定する場合は以下のロールを設定してください。
Cloud Runのサービスアカウントについて
Artifact Registry 読み取り(roles/artifactregistry.reader)
Cloud Run デベロッパー(roles/run.developer)
GARリポジトリの作成
イメージの配置先を作成します。
REST API用のみで大丈夫です。
REPOSITORY_NAME, REGION, DESCRIPTION
は適宜編集してください。
# リポジトリ作成
gcloud artifacts repositories create REPOSITORY_NAME \
--repository-format=docker \
--location=REGION \
--description="DESCRIPTION" \
--async
ソースコードの作成
Dockerfile/
├ src/
│ └ access_voicevox.sh
├ cloudrun.yaml
├ Dockerfile
├ usr/
└ main.py
REST API
import uvicorn
from fastapi import FastAPI, File, UploadFile ,APIRouter
from fastapi.responses import FileResponse
from pathlib import Path
from datetime import datetime
import os
import logging
import subprocess
import shutil
app = FastAPI()
def process_cmd(cmd) :
result = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode == 0 :
return result
elif result.returncode == 1 :
logging.warning(result)
return result
else :
logging.warning(result)
return result
def post_voicevox(src_path,filename,response_file):
result=process_cmd(f"cd {src_path} ; sh access_voicevox.sh {filename} {response_file}")
return result
@app.post("/")
def res_chat(file: UploadFile | None = None):
if not file:
return {"message": "No file sent"}
else:
current = Path()
src_path= current / "src"
# 受信ファイル配置
filename=file.filename
fileobj = file.file
upload_dir = open(os.path.join(src_path, filename),'wb+')
shutil.copyfileobj(fileobj, upload_dir)
upload_dir.close()
# Voicevox実行
response_file="audio.wav"
result = post_voicevox(src_path,filename,response_file)
if not result.returncode == 0:
return result
response = FileResponse(
path=src_path/response_file,
filename=f"{response_file}"
)
return response
speaker_idは声を担当するキャラクターのIDなので変更可能です。
speaker_id
VOICEVOX Ver. 0.14.5
ID | 名前 | スタイル |
---|---|---|
0 | 四国めたん | あまあま |
1 | ずんだもん | あまあま |
2 | 四国めたん | ノーマル |
3 | ずんだもん | ノーマル |
4 | 四国めたん | セクシー |
5 | ずんだもん | セクシー |
6 | 四国めたん | ツンツン |
7 | ずんだもん | ツンツン |
8 | 春日部つむぎ | ノーマル |
9 | 波音リツ | ノーマル |
10 | 雨晴はう | ノーマル |
11 | 玄野武宏 | ノーマル |
12 | 白上虎太郎 | ふつう |
13 | 青山龍星 | ノーマル |
14 | 冥鳴ひまり | ノーマル |
15 | 九州そら | あまあま |
16 | 九州そら | ノーマル |
17 | 九州そら | セクシー |
18 | 九州そら | ツンツン |
19 | 九州そら | ささやき |
20 | もち子さん | ノーマル |
21 | 剣崎雌雄 | ノーマル |
22 | ずんだもん | ささやき |
23 | WhiteCUL | ノーマル |
24 | WhiteCUL | たのしい |
25 | WhiteCUL | かなしい |
26 | WhiteCUL | びえーん |
27 | 後鬼 | 人間ver. |
28 | 後鬼 | ぬいぐるみver. |
29 | No.7 | ノーマル |
30 | No.7 | アナウンス |
31 | No.7 | 読み聞かせ |
32 | 白上虎太郎 | わーい |
33 | 白上虎太郎 | びくびく |
34 | 白上虎太郎 | おこ |
35 | 白上虎太郎 | びえーん |
36 | 四国めたん | ささやき |
37 | 四国めたん | ヒソヒソ |
38 | ずんだもん | ヒソヒソ |
39 | 玄野武宏 | 喜び |
40 | 玄野武宏 | ツンギレ |
41 | 玄野武宏 | 悲しみ |
42 | ちび式じい | ノーマル |
43 | 櫻歌ミコ | ノーマル |
44 | 櫻歌ミコ | 第二形態 |
45 | 櫻歌ミコ | ロリ |
46 | 小夜/SAYO | ノーマル |
47 | ナースロボ_タイプT | ノーマル |
48 | ナースロボ_タイプT | 楽々 |
49 | ナースロボ_タイプT | 恐怖 |
50 | ナースロボ_タイプT | 内緒話 |
51 | †聖騎士 紅桜† | ノーマル |
52 | 雀松朱司 | ノーマル |
53 | 麒ヶ島宗麟 | ノーマル |
voicevoxへの接続はlocalhost:50021
にしています。
ポートは後述の構成ファイルで設定します。
#! /bin/sh
filename=$1
response_file=$2
speaker_id=8
curl -s \
-X POST \
"localhost:50021/audio_query?speaker=${speaker_id}"\
--get --data-urlencode text@${filename} \
> query.json
curl -s \
-H "Content-Type: application/json" \
-X POST \
-d @query.json \
"localhost:50021/synthesis?speaker=${speaker_id}" \
> ${response_file}
Dockerfile
FROM python:3.10-alpine
WORKDIR /mnt/
COPY requeirment.txt /mnt/
RUN apk add curl
RUN pip install \
-U pip \
-r requeirment.txt
COPY . /mnt/
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
uvicorn
fastapi[all]
構成ファイルの作成・デプロイ
を参考に構成ファイルの作成とデプロイをします。
SERVICE_NAME, SERVICE_ACCOUNT, IMAGE_NAME, TAG
は適宜編集してください。
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
annotations:
name: SERVICE_NAME
spec:
template:
metadata:
annotations:
run.googleapis.com/execution-environment: gen1
spec:
serviceAccountName: SERVICE_ACCOUNT
containers:
- image: IMAGE_NAME:TAG
name: main
ports:
- containerPort: 80
- image: voicevox/voicevox_engine:cpu-ubuntu20.04-latest
name: voicevox
startupProbe:
tcpSocket:
port: 50021
timeoutSeconds: 120
periodSeconds: 240
resources:
limits:
cpu : "2"
memory: 2Gi
cloudrun.yamlが存在するディレクトリ内から以下のコマンドでデプロイします。
#Deploy Cloud Run
gcloud run services replace cloudrun.yaml --region asia-northeast1
Cloud Runのコンソールからに移動しサービスが作成されているのを確認。
2つのコンテナで構成されていれば成功です。
テスト
セキュリティから認証設定を変更、サービスのURLをコピーします。
以下の音声に変換するテキストを用意します。
※文言が長いとVOICEVOXコンテナが落ちる場合があります
テストです
以下のコマンドでREST APIにPOSTします。
https://URL
はコピーしたURLに変更してください。
curl -X 'POST' \
'https://URL' \
-H 'accept: application/json' \
-H 'Content-Type: multipart/form-data' \
-F 'file=@text.txt;type=text/plain' > result.wav
出力した音声ファイル(result.wav)が再生できれば成功です。
以上で完了です。
問題点
・GARイメージの更新・反映について
gcloud run services replace
はyamlに変更が無ければ再デプロイされません。
そのためGARイメージを更新してもCloud Runのイメージ参照先が更新されずサービスに反映されませんでした。
原因はCloud Runのイメージ参照方法がタグではなくダイジェストを参照しているためです。
私はymalを一部編集して再デプロイしています。
(フラグなどで強制的に再デプロイできるかも)
Discussion