hvccをlogue SDK向けに拡張する(6)

前回の続きです。

前回MVPを作成してから1か月以上経ってしまいましたが、PureData→logue SDK変換のWebアプリ版を以下で公開します。

Pure Data to logue SDK

簡単な説明はこちら。

Pure Data to KORG logue SDK Converter
Pure Data to KORG logue SDK ConverterThis web application allows you to convert Pure Data patches into custom user oscil...

このサーバは、Pure Dataパッチをアップロードすると、hvccを使ってCのコードを生成し、それをlogue SDK用にビルドして、prologue用・minilogue xd用・NTS-1用のオシレータユニットを含むzipファイルを返します。つまり、Pure Dataパッチがあれば、minilogue xdやNTS-1にそのままアップロードできるバイナリ形式のオシレータに直接変換できるわけです。

簡単なチュートリアルビデオを以下で公開しています。(現状英語のみですが、まあ画面を見ていれば手順は分かるかと思います。

このビデオの音声から、最近流行?のNotebookLMを使って生成した日本語の解説音声を以下に置いておきます。残念ながら、「osc~ 440」のようなPure Dataのオブジェクト名をうまく読み上げられない(なぜか「~」を「シャープ」、「tabosc4~」を「たぶはこし?よん」と読んでいます)ので、YouTubeでの公開は断念しました。

音声の代わりに台本原稿を渡してみたりもしましたが、あまり変わりませんでした。音声信号もおそらく内部では文字に起こしてLLMに渡しているはずなので、変わらないのは当然かもしれません。そもそも「osc~」とか辞書に載っていないような単語を読み上げさせるのが難しいのではと思います。

なおこのNotebookLMのポッドキャスト生成機能、自動車の運転中とか、耳で聴くだけで何とかしたい場合には結構役立つかもしれません。単に元のビデオの内容を要約するのではなく、元の音声には全く含まれていない情報を勝手に補足している(しかも意外と適切)のはちょっと驚きました。

ただ、やっぱり人間が話しているのではないからか、いくつか聴くとだんだんトークそのものに飽きてきます。一つには、肯定的な内容しか出てこないので一本調子、という側面がありますし、合成音声も音声そのものには違和感がないのですが、やっぱり聞き続けていると妙な一本調子感はありますね。まあこのへんは今後改良されるのかもしれませんが。

というわけで、以下、MVP版を開発して以降、この1か月あまりのもろもろの作業のログです。もっぱら自分用ですので、興味のない方は飛ばしてください。

VPSを契約する

サーバは、このブログでも使っているドメイン(boochow.com)にサブドメインapp.boochow.comを作り、そこにVPSを1台借りてぶら下げます。当面、Pure Data→logue SDK変換サービスだけですが、将来的には他のWebアプリも載せるかもしれません。

boochow.comというドメインはValue-domainのサービスで管理しているのですが、blogのサーバ自体はlolipopのレンタルサーバを借りています。レンタルサーバは特権ユーザになれないので、Webアプリを動かすにはあまり向いていません。

そのため、新しくVPSを借りることにしました。VPSを使うと、VMwareにイメージを作るように、仮想的な(共有の)ハードウェアの上に自分専用のサーバ環境を作ることができます。

使用するVPSは、ConoHaの2vCPU RAM 1GBのサービスにしました。ConoHaはGMOのサービスです。今回は、あまりアクセスも無いでしょうし、処理自体もそこまで重いわけではないので、とりあえずこのスペックで様子見です。同様のスペックでいくつかサービスを(海外も含め)見てみたのですが、この最低限のスペックでは正直どこも大した違いはありませんでした。ConoHaはちょうどキャンペーン期間中で、1年契約なら月額約500円と、まあまあお手頃感もありました。

DNS関連の設定

これまで、ネームサーバもlolipopのネームサーバを使っていました。レンタルサーバ自体はboochow.kilo.jpというFQDNを持っているのですが、ネームサーバで

blog.boochow.com→boochow.kilo.jp/blog

という書き換えを行っています。

ただlolipopのサービスでは、サブドメインを作ることはできますが、サブドメインに別のサーバを割り当てることができず、常に上記のようなサブディレクトリへのマッピングになってしまいます。

そのため、ネームサーバはValue-domainのサーバ(実際はeNomのサーバ)に切り替えることにしました。Webアプリ用のサブドメインはapp.boochow.comとし、そのAレコードをVPSのアドレスにします。

このブログのドメインについては、Value-domainのネームサーバで

blog.boochow.com/→boochow.kilo.jp/

という書き換えをし、lolipopのサーバ側で

blog.boochow.com→boochow.kilo.jp/blog/

という書き換えをすることになります。

これでDNS関連の設定は整いました。

Webアプリサーバ

Webアプリを提供するサーバは、スタンダードな構成ですがnginxでリバースプロキシを立てて、そこから各Webアプリにディスパッチすることにします。Webアプリごとにサブドメインを切ることも可能ですが、サブドメインごとにTLS証明書を用意するのは手間ばかり増えそうなので、サブドメインは共通にしてWebアプリごとにサブディレクトリを作ることにしました。各Webアプリは特定のポートで待ち受けして、nginxがサブディレクトリを見て各ポートにディスパッチします。こんな感じですね。

app.boochow.com/pd2logue/→localhost:port

今回、WebアプリはFlaskを使って書いたので、nginxとの間にgunicornを挟みます。この辺の動向には疎かったのですが、WebアプリケーションとWebサーバの間のAPIを抽象化したWSGIという仕様があって、その実装の1つがgunicornということらしいです。

Flaskアプリ単体でもHTTPサーバとしては動作しますし、nginxからFlaskアプリを動かすこともできますが、間にWSGIサーバを挟む意味は処理速度と安定性の向上ということのようです。ただ、nginxとFlaskアプリの間にgunicornを挟むことで、直接Flaskアプリを動かしていたときよりも、ブラウザに送っているログの出力に即時性が無くなってしまいました。ここらへんはアプリの作りにもよるようなので、今後調べてみます。

Webアプリ

Webアプリそのものも、複数同時リクエストに対応するために、タスクごとの状態管理をredisに、コンパイルなど重い処理をバックグラウンドタスクとしてceleryに切り出す変更を行いました。(このあたりの現状もあまりよく知らなかったので、実際にはChatGPTにおんぶにだっこでしたが。)

もともとのFlaskアプリとHTML側(JavaScript含む)のやりとりは以下のような流れになっています。

(1)Pure Dataパッチをブラウザからサーバへアップロード
(2)変換・ビルド
 (2A)hvccで変換→logue SDKツールチェインでビルド→ビルド結果のファイルをzipでまとめる
 (2B)(2A)の処理は少し時間がかかるので、その間ユーザ側に進捗ログを返す
(3)完成したzipファイルをブラウザへダウンロード
(4)一定時間経過後、処理済みのファイルを削除

Flask単体だと、タスクを順番に処理していく感じ(2Aと2Bも交互に実行)なのですが、celeryを使うと、Flask側ではリクエストの受付を行い、受け付けたタスクをceleryがキューイングして別プロセスで動かすことができます。

上記の処理の流れで言うと、(2A)の部分が、従来シェルで手作業していた部分ですが、このタスクをceleryに切り出します。といっても、現状の非力なVPS1台では、処理負荷という意味であまり大きな効果は無いのですが、一応は並列化・スケール可能な構成になりました。

この構成では、Flask側とcelery側は、随時情報共有していく必要があります。例えば

・タスクの開始時(アップロードされたPure Dataパッチの所在や元のファイル名、HTML側で渡されたオプションなど)
・タスク実行中(進捗状況やエラーその他)
・タスクの完了時(ダウンロード用のzipファイルの所在、ユーザに渡す際のファイル名など)

などです。そのためにオンメモリDBであるredisを使っています。

コンテナ化

ローカルの環境下で一連の動作ができるようになったら、今度はこれら一式を、Dockerコンテナ化してVPSへデプロイします。

コンテナ化といっても、ざっくり言えばインストールから起動までの完全な手順をDockerfileにまとめれば済むのですが、その記載する手順に抜け漏れがあれば正しく動きません。構築・検証の過程では

・動作確認やデバッグのために部分的にローカルのファイルシステムをコンテナ側へマウントする
・コンテナイメージ構築時にGitHubなどのクレデンシャルを安全に渡す

などなど細かい運用ノウハウはかなりChatGPTにお世話になりました。私はこういったWebアプリのdockerコンテナを作ったことがなかったのですが、今回のWebアプリの構成は非常にスタンダードなので、大体はベストプラクティスをそのまま応用できました。

運用に移すにあたってはログローテーションなどが欲しくなりますが、このへんもChatGPTに作成してもらいました。大規模なものを作るなら、そのまま採用というわけにも行かないかもしれませんが、今回のような小規模なサービス作成では生成AIに足を向けて寝られない感じです。

VPSへのデプロイ

VPSにアプリ稼働用のユーザを追加したり、nginxやdockerなどをインストールしたり、といった作業はシェルスクリプト化しておくと、同じ作業が必要になった時に楽です。というわけでこの辺もあらかじめ生成AIに作成してもらいました。こういう単純な作業は生成AIは得意ですね。

コンテナ化したサーバのデプロイは、無料アカウントでは制約の多いDockerHub経由ではなく、ローカル環境で作成したイメージをscpで直接転送しています。このあたりの細かい手順は、忘れてしまいそうなので簡単な手順書を作成しました。そもそもdocker関連のコマンドにあまり慣れ親しんではいなかったので、その面では生成AIのお世話になっています。

また、ローカル環境で動作確認しているとはいえ、デプロイ先では若干ディレクトリ構成や設定などを変更したくなったり、見落としていた設定があったりしました。そのため、compose.yamlを更新したりしています。

とはいえ、総じてデプロイ自体はスムーズに行きました(行かないと困りますが)。

チュートリアルとマニュアル

これまでも新しく何かリリースしたときにはYouTubeにビデオを出していましたが、冒頭でも書いたように今回もビデオを作成しました(半年ぶり)。単純な紹介ではなく、使い方を紹介する簡単なチュートリアルになっています。この1か月の半分以上はこのチュートリアルの作成に費やしていたように思います。

Windowsでスクリーンキャプチャをするには、Windows Game Barを使えばいいと思っていたのですが、実際に使ってみると結構圧縮率が高く、拡大するとボケボケになってしまっていたので、利用を断念しました。代わりに、これまたWindows標準のsnipping tool(Win+Shift+S)を使いました。いつのまにか画面の動画キャプチャもできるようになっていたんですね。音声も、ミキサーデバイスを有効にすれば問題なく録音できました。

しかし、スクリプトに合わせて画面の一部を拡大したり、再生速度を上げて端折ったり、画像や他の動画と組み合わせるなどの部分は手作業です。AIに頼めれば良いのですが、現状それは難しそうですね。動画のサムネイルも、今回はAIで生成しづらいので手作業で作成しています。

一方チュートリアルの台本は、英語ということもあってかなりChatGPTの手が入っています。基本的なプロットやチュートリアルで行う例題は自分で決めましたが、ざっと書いたプロットにそって台本を構成するところは生成AIにやってもらっていました。正直、英語は私よりChatGPTのほうがはるかに特異で、「こうしたほうが自然」みたいな修正をいろいろ行ってくれます。

音声も音声合成で行うことができますが、ここは自分でしゃべっています。YouTubeには既に音声合成を使ったビデオが氾濫していて、個人的には聞き覚えのあるあの声を聞いただけで視聴意欲が削がれる気がします。

それから、Webアプリ自体のマニュアルも必要です。これは、上記のチュートリアルの台本と、hvccのexternal generatorのマニュアルを資料として参照させて生成AIに下案を作成してもらい、それを適宜編集して作成しました。生成AIの下案は正直ハルシネーションだらけでかなり今一つでした。これはちょっと要求が難しすぎたかもしれません。

サーバ監視

実際にVPSでnginxを稼働させてみると、毎日数百件のアクセスが来ます。URLを告知していないとはいえ、DNSに登録はされているので、不正アクセスを狙ったスキャンなどが、こんな感じで毎日飛んでくるのですね。

cat /var/log/nginx/access.log.1 | grep -v MY_IP_ADDRESS | cut -f 2 -d \" | cut -f1-2 -d" "| sort | uniq -c | sort -nr
    162 GET /
     34 GET /.env
     18 GET /favicon.ico
     12 GET /.git/config
      6 \x05\x01\x00
      5 CONNECT api.ip.pn:443
      4 GET /webui/
      3 POST /boaform/admin/formLogin
      3 GET /www/.git/config
      3 GET /t4
      3 GET /static../.git/config
      3 GET /src/.git/config
      3 GET /settings/.env
      3 GET /server/.git/config
      3 GET /public/.git/config
      3 GET /project/.git/config
      3 GET /prod/.env
(以下略)

このサーバに秘密の情報は一切置かないので、主に注意しなければならないのは不正アクセスの踏み台として利用されることだけですが、いずれにせよ何らかのサーバの監視の仕組みは欲しいところです。

とはいえ元が非力なサーバですので、費用対効果も考えると大掛かりなものは本末転倒です。サーバにログインしてコマンドで実行する代わり、という程度のごく簡単なサーバ監視スクリプトを、生成AIを援用して作成しました。また、標準的な不正アクセス対策としてfail2banと、監視スクリプトへのアクセスの認証モジュールも有効化しています。

監視スクリプトにはnginx用の簡易的なアクセス統計も入れていますが、アクセスログの分析は基本的にはVPS上で行うのではなく、scpで手元にログをコピーしてローカルで行おうと思っています。

コメント