OpenWebUI × OpenAI API のAIチャットボットを ECS × Fargate 環境で動かす
まずlocalでやってみる
docker run -d -p 3000:8080 -e OPENAI_API_KEY=[secret_key] -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
http://localhost:3000/
にアクセス
test@example.com
でsign upする
順番前後するがAPI keyは
から作るpermissionはよくわからんので強気のALLで(一般的なチャットでの応答はReadOnlyだと使えない)
あまりにも簡単すぎる
AWSに乗せる前に、認証周りやデータがどう保管されてるか把握しておきたい
volumeは起動時のテンプレオプションで指定していた
open-webui というvolumeが存在しなければ作る
-v open-webui:/app/backend/data
2人目のアカウントを作ると
open_webui.apps.webui.models.auths
の insert_new_auth
が動いている様子
特にメールの送信などはしないので、ID, Password認証みたいなものか。
1人目が管理者になり、2人目以降は管理者の承認が必要なようだ
管理者アカウントでログインし、http://localhost:3000/admin
にアクセスすると管理画面になる
「役割」の「保留中」を押すと「ユーザー」に切り替わる。
何度か押すと管理者にしたり保留中に戻したりもできる。
insert_new_auth ではUsersテーブルにemailやpassword情報をinsertしている
デフォだとSQLiteが使われている
SQLiteのデータは/app/backend/data/webui.db
に保存されている
取り出して見てみる
% docker cp open-webui:/app/backend/data/webui.db .
webui.db を DB Browser for SQLiteで開く
emailとpassword(暗号化済み)の対応はauthテーブルに、その他のユーザー情報はuserテーブルに記録されている
とりあえず、WebUI単独で最低限認証が行えることがわかったのでこのままAWSで稼働させてみる
これはアウトバウンドだ
こんな感じになりそうか
* IGW → VPC → public subnet → ALB → private subnet → Fatgate(private subnet指定)
まずECS clusterを作成
cluster名とtagは適当につけ、インフラストラクチャはFargateを選択
ECS taskを作っていく
インフラストラクチャの要件
起動タイプはFargate、CPUとメモリは最弱にしてみる
コンテナ
イメージURIはghcr.io/open-webui/open-webui:main
ポートマッピングは8080
※ Fargateのネットワークモードはawsvpc
のみであり、これはDocker組み込みネットワークを経由せず直接外部と通信する。hostポートはランダムなものが自動で割り当てられるので、containerポートだけ指定する。
ストレージ
ユーザーなどのデータはこのコンテナ内のSQLiteに保存されるので、ボリュームを新規作成してアタッチする
ECS サービスも作ったが、タスク作成におけるDocker Imageのpullに失敗した。
VPC subnetがインターネットに接続していないからっぽい。
VPCにIGWをアタッチする
- IGWをVPCにアタッチ
- ALB用のSGを作成し、任意のIPからの80番リクエストを受け付けるインバウンドルールを作成
- ECS用のSGは8080へのリクエストを受け付ける
SGのルール参考
セキュリティグループ
ALB用
Ingress : 80 : ALL
Egress : ALL : ALL
ECS用
Ingress : 80 : from ALB sg
Egress : ALL : ALL
う〜ん、だるいので大人しくPublicIP割り当てするか
IP固定化したいときはこのへん参考にする。
public IPオプション+IGWでやってるがERR_CONNECTION_REFUSEDになる。
続きは後日
SGのインバウンドルールを変更したらERR_CONNECTION_REFUSED から ERR_CONNECTION_TIMED_OUT に変わった
Fargateのスペック上げてみる
public subnetのインバウンドで8080を開放する必要があった(ブラウザから:8080
をつけてアクセスする)
ロードバランサーは、ターゲットグループ内のターゲット間でトラフィックを分散します。ターゲットグループが Amazon ECS サービスに関連付けられている場合、Amazon ECS はターゲットグループにコンテナを自動的に登録および登録解除します。Amazon ECS がターゲットの登録を処理するので、ターゲットグループにターゲットを手動で追加する必要はありません。
https化したいのでドメイン登録する
お名前.comで1年無料で取ろうと思ったけどなんかいろいろダークパターンで微妙な印象だったのでRoute53で金払って取る
- Route53でドメイン取得
- Route53でホストゾーン -> レコード作成 -> ElasticIPのIPアドレスを登録
- AWS Certificate Manager で Route53で取ったドメイン名に証明書を設定
NLBだけだとHTTPS化できないので、やっぱALB × マルチAZ VPCで諸々やり直す
- 「VPC"など"を作成」から、AZ2つ、パブリックサブネット2つ、プライベートサブネットなしで作る
- IGWをVPCにアタッチする
- パブリックサブネットのルートテーブルに、0.0.0.0 -> IGW へのルートを追加
- あとなんか毎回GitHubから公式imageをpullしてくるの変な気がしたのでECRに保存しておく
- M1 MacでpullしたimageをECRに単にpushすると、CPUアーキテクチャの違いからFargateで動かない
- docker pull のオプションで platform linux/x86_64 を指定
- ECSのタスク定義を作っておく(DockerImageのURIをさっきpushしたECRのものにする)
- ECS用のTGをつくる(ターゲットタイプはIP。ただIP設定不要)
- ALB作成 SGはインバウンド/アウトバウンドともHTTPSのanywhere
- ECSのサービスをパブリックサブネット内で作成
- ECS用のSGでインバウンド8080を許可(ソースはALBのSG)
- ECSのサービスとその中のタスクが立ち上がる
ElasticIPはいらない(ALBに割り当てられない)
Route53のレコード作成で、Aレコードの転送先エイリアスとしてALBを選択
503になっている
どこでエラーが返ってるのか調べないとだが、ALB→TG(ECS)部分な気がする
ECSのTGはポート80や443ではなく、8080を指定するのか?
ECSのサービスを作るときにALBの設定ができた(ECS Serviceにロードバランサーが紐づく)
503 -> 504 (Gateway Timeoutになる)
ALBのリソースマップ
TG -> ターゲット(ECS Service)が「異常」
ターゲットがunhealthyなのがだめなのか
ECS Service作り直す。SGがうまく設定できていなかったかも
ALB用SGからのport 8080を受け付ける
ヘルスチェック猶予期間も300秒にしてみる
なんかタスクがECRへのタイムアウトらしきエラーで起動しなくなった。
なんで?
AmazonEC2ContainerRegistryReadOnly
のポリシーを ecsTaskExecutionRole
に許可してみる
やっぱ違うな。timeoutだもんな。一回policyは外す
起動Taskをパブリックサブネットに配置して直接InternetGateWay経由で外に
アクセスしてECRからイメージを取得する方法。
注意点としてはSerivceを構築する際に「パブリックIPの自動割り当て」を
「Enable」にして起動TaskにパブリックIPを割り振る必要がある。
あ〜やっぱり?
あとは料金削減のための手段を考える
ただCPUは低すぎるかも
正直一人でやるならChatGPTに課金したほうがいいからterraformingしたら消すかも
あとECSのタスク作り直したらボリュームが作り直されてるのかユーザー情報が消えてる
いろいろやった結果、ChatbotUIのSelfHosted版あるやん!これでええやん!
完