Jekyll2024-03-26T18:24:11+09:00/feed.xmlあーありがち「今やり直せよ。未来を。」かつて 2ch という掲示板に貼られた有名なコピペですが、努力の瞬間だけこの言葉を胸にしつつ、気づいたこと、調べこと、日々のことなどを連ねています。wtnabeユースケースによって分割し、データのコード体系と構造で統合する2024-03-24T07:37:18+09:002024-03-24T07:37:18+09:00/diary/2024/0324/decoupled-by-usecase-united-by-data-code-and-structure<p><img src="/assets/images/theme/lance-anderson-QdAAasrZhdk-unsplash.jpg" alt="" /></p> <p>自分が何らかのソフトウェアシステムを考える際に意識していることを平易に、直感的に書き起こしておく試み。</p> <p>何を当たり前のことを、と思われたなら成功。</p> <h2 id="やりたいこととシステムの立ち位置">やりたいこととシステムの立ち位置</h2> <h3 id="本来システムはユーザーと目的によって最適解が異なる">本来システムはユーザーと目的によって最適解が異なる</h3> <figure class="jekyll-kroki" data-kroki-type="plantuml" data-kroki-format="svg"> <img src="https://kroki.io/plantuml/svg/eJyNkDEKwkAQRfs5xVwgYNbU4lWGuEYx2cjOBhGx2EQsbD2AVuIJFMHbzEXcxICd2gyf-Z__4MOYHVlXFTmqgUoGQ5VEpdFRxTol1p3mNTtdAC_mBm3JulNLslQgz2hSruYmwynlwQFKXWlRmos0T6lv4SIxhjYbg9WpI5PlGqW-S_2QZi_NufXfhBg3gNiTQ2Yn9UH8SfxO_FH8tU22Vgxb6BoxqqJR__pCVj_J6k-y6snqQ1Yw1mYS9oMXHz6KVQ%3D%3D" alt="" /> </figure> <p>目的の達成をシステムは支援する。</p> <h3 id="たまたま同じデータを利用すると同じシステムを解としたくなる">たまたま同じデータを利用すると同じシステムを解としたくなる</h3> <figure class="jekyll-kroki" data-kroki-type="plantuml" data-kroki-format="svg"> <img src="https://kroki.io/plantuml/svg/eJx9kMFKA0EMhu95itD7QDftufRVwna6FndnZDJLEfGwO0Varz6APYkgXhWhb5MH8BXcWSr0IF5C_vzfT0JgKZFDbJsaaUrz6YzmZhu8q4x31sitRNuAXG8cBi927G44cINyxSu_3bgK11wPDnAZfcCJphdNJ-0_hlp8n_aa3rNOb4M4TJAFW7Gh-AunS5wucYJgy8iuqi1q_6n9l6YHTUe8A8xAyZKNnfaP2j1rt9PuSbvXIuezV_yL0S9GcA8wnofGLM7JcT-a9jwgWFq3Gv4FP9ASib4%3D" alt="" /> </figure> <ul> <li>例えばライン工のように何らかのデータが流れてきて決められた入力や処理をすればいいだけであれば問題ないかもしれない</li> <li>よくあるケースとしては役割が異なればそのデータに対する目的も異なるので、上手に作らないと無理が生じる</li> </ul> <h3 id="ダメな例">ダメな例</h3> <p>こういうものはだいたい、最終的に利用したい成果物を扱う人が用意する。</p> <ul> <li>最も極端な例は<strong>巨大な Excel の共同編集</strong>。極端な例とは言ったが、実際にはとてもよくあるケースのはず</li> <li><strong>神Excel</strong> もこの例。これはデータで活用する気はなく、紙で提出されていればよいことから、まずその書類を作る業務に最も近かった人が、手元の OS、手元の Excel、手元のプリンタでテンプレートとなるファイルを用意し、その運用を(悪気なく)押し付けてしまう<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></li> <li><strong>大統一フォーマット</strong>。最終的な成果物を扱う人はデータのフォーマットが整っていないと困る。しかしそのまま全ユーザーに使ってもらおうとした結果、例えばある人はある項目の入力の際に100ある選択肢から特定の3つしか使いません、みたいなものができあがる。これの積み重ねは単純にストレスでしかない。</li> </ul> <p>これらは適切に分割されておらず、関係者全員が同じものを使っているため、当然<strong>変更すると関係者全員に影響するのでおいそれと変更できなくなってしまう</strong>。</p> <p>短期で終了する作業のためのものならよいが、長期的な運用を始めてしまうと地獄の始まり。</p> <h2 id="やりたいこととuiの不満">やりたいこととUIの不満</h2> <h3 id="行う作業によってuiの最適解は異なる">行う作業によってUIの最適解は異なる</h3> <p>以下は目的と求められることの例。</p> <table> <thead> <tr> <th>やりたいこと</th> <th>大切にしたいこと</th> </tr> </thead> <tbody> <tr> <td>入力</td> <td>ひたすら高速に入力したい、チェックも速く</td> </tr> <tr> <td>進捗確認</td> <td>全体の件数、入力の進み具合を確認したい</td> </tr> <tr> <td>レポート<br />(いわゆるBIの領域)</td> <td>個々の入力の具合ではなく、統計データだけが欲しい</td> </tr> </tbody> </table> <p>例えば巨大 Excel で実現するとやたらあちこちスクロールしないといけなくなったり、入力する場所に気を使ったり、様々な制約が生まれる。</p> <p>これを毎日毎回使わされる側はたまったものではない。ミスしやすいし、ミスったらみんなが使うデータを壊す可能性もある。</p> <h3 id="ノーコードツールで困ること">ノーコードツールで困ること</h3> <p>システムを「開発」せずに実現できるノーコード、ローコードツールが増えている。しかしこれらは以下の点で最初から課題を抱えていることを承知しておいた方がよい。</p> <ul> <li>いわゆるノーコードツールは概ね UI の作り込みを省略、簡略化する<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>ので特に最適化してほしい場合に UI の不満が出やすい</li> <li>データ構造にも無理が出やすいのでエンジニアからも不評を買いやすい</li> <li>特に多くの種類の業務を一つのフォーマットに落とし込もうとするとどんどん無理が出てくる <ul> <li>これはノーコードツールに限らない話だが、上のような分割の視点がない<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>から起きることであり、こうなると詰みやすい</li> </ul> </li> </ul> <h2 id="解決策">解決策</h2> <h3 id="データコード体系によって分割と統合を行う">データ、コード体系によって分割と統合を行う</h3> <ul> <li>データの突き合わせが可能なら一つのデータベース、一つのシステムにこだわる必要はない</li> <li>突き合わせ可能なデータ(コード体系)をどう設計し、どうシステムを分解するかが腕の見せどころ</li> </ul> <figure class="jekyll-kroki" data-kroki-type="plantuml" data-kroki-format="svg"> <img src="https://kroki.io/plantuml/svg/eJyNkT1OAzEQhfs5xVzAUtakRikouAH1sDZJhNcb2V4hhCjsjRA_DVIOABVCKShBSNxmhLgG9rKIDihsjf3em88_MPOBXOgag3Iip5MdORVK1223MloJf-qDbgRZJRoKi6WdC0WBwB8vLbrW66FakaMG_YJUe5IteEQmK0B1aB1y_8D9G6fnPCN57Lx2FThdB7Jzo5HTC6dX7i-4vy_6F7LCM8DirckXz5rTNcc7jmuOG46PxVmkCs5h6IjCiN1x6xey_JMs_0mWI1n-kCVAeZ3DIRi3nDacrjjecHz62Mb328uS3TvYh-87ihws6zyGejwBzLRV-UfgE7olqwg%3D" alt="" /> </figure> <p>ただし、これをスマートに実現するためには<strong>データを中心に、データベースに近い位置での制御が可能であること</strong>が望ましい。</p> <p>全体で大きなシステムだがユースケースごとに適切に最適化できているなら一つのシステムであってもよい。(伝統的な開発ではこのパターンが多そう)</p> <p>実は、この「データに近い位置で」が苦手なツールの場合、そもそも無理が出やすい。</p> <h2 id="特定のツール特定の言語を使えるか使えないかはそこまで重要ではない">特定のツール、特定の言語を使えるか使えないかはそこまで重要ではない</h2> <p>ここまでを考えるに当たっては、少なくともどんなプログラミング言語が扱えるかといった点は重要ではない。あえて言うなら SQL DB の考え方は重要。</p> <ul> <li>データの可搬性、可用性を上げるのはツール、言語ではなく「コード体系」や「ID」、「データ構造」 <ul> <li>この知識、スキルは陳腐化しない</li> </ul> </li> <li>「このツールは使いにくいからあのツールの方がいいかも?」は基本的には隣の芝が青く見えているだけ <ul> <li>乗り換えても乗り換えた先で多かれ少なかれ別な苦労が生まれる</li> </ul> </li> </ul> <p>Google Apps Script が使えるからとか Python が使えるから、とかそういうところで有利不利はあまり出ない。少なくとも全体の整合性<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>を保ちつつ最適化できる余地を残す、みたいな設計力と特定の言語の利用経験はあまり関係がないと思う。たまたま目の前にあるのが Microsoft Office だから、Google Workspace だから、以上には有利にはならないと思う。</p> <p>もちろんこれらのライセンス契約の領域に踏み込む場合は単にコードが書けるとかツールが使える、だけでは済まない交渉になったりするわけだけど、データとユースケースを中心に考えたらツールの持ち替えや追加がベスト、という判断は十分あり得る。</p> <p>言語、ツールに必要以上に縛られず、選択肢を増やせる方が重要。もっとも、本当に選択肢を増やすためにはそれなりに経験を積まないといけないけど。</p> <h3 id="これから開発を専門としない人がシステムデータに関わるために">これから開発を専門としない人がシステム、データに関わるために</h3> <p><a href="https://direct.gihyo.jp/view/item/000000000323">はじめよう!プロセス設計 ~要件定義のその前に | プログラミング・システム開発,開発技法・ソフトウェアテスト・UML | Gihyo Direct</a></p> <p>が参考になる。</p> <ul> <li>なぜそのデータが生まれるのか?</li> <li>このプロセスはどこに繋がるのか?</li> </ul> <p>この視点はとても重要で、特定の製品を知っている知っていないよりもはるかに寿命の長いスキルになる。</p> <p>ただし、実際には自分でゼロベースでデータを中心としたシステムの構築経験なしにこの勘所だけを掴もうとするのはかなり無理があると思う。残念ながら。</p> <p>特定のツールを導入して解決できるのは恐らくその時点で瞬間的に見える範囲だけであり、何らかのシステムを導入したらその瞬間から新たな課題が生まれ始めるだろう。</p> <p>現実ってのはそういうものだ。</p> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>あくまで本人は利用環境の多様さを理解していないだけで悪気はない <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>UIの作り込みはとても手間が掛かるし、最近はデータの型によって欲しいUIは大まかには決まるので、省略させやすいと思ってしまう <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>「データ」の可搬性についての理解が足りない <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:4" role="doc-endnote"> <p>整合性にも種類があることを知っていることも重要。すべてがリアルタイムに解決できるとは限らない。 <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p> </li> </ol> </div>wtnabeCloud Load Balancingを使い、Cloud RunサービスにIAPで認証を追加する2024-02-18T09:00:00+09:002024-02-18T09:00:00+09:00/diary/2024/0218/cloud-run-and-iap-with-load-balancing<p><img src="/assets/images/theme/silas-kohler-C1P4wHhQbjM-unsplash.jpg" alt="" /></p> <p>今回やりたかったのは Cloud IAP という Google がいい具合に処理してくれる認証用の proxy サーバを Cloud Run の前段に置き、特定の人しかアクセスできないようにすること。</p> <p>Cloud Run には直接 IAP を設定することができず、必要な準備がいくつかある。今回はこの実現のために必要な準備を確認した。</p> <h2 id="cloud-iap-cloud-load-balancingをおさらい">Cloud IAP, Cloud Load Balancingをおさらい</h2> <p>Cloud Run は Cloud Functions 同様、そもそも認証情報付きでしかアクセスできないようにすることもできるが、Cloud IAP ( Indentity-Aware Proxy ) はアカウント単位で許可する人を登録したり、Google Workspace と組み合わせてドメイン丸ごとに対して設定することで、<strong>社内限定公開</strong>のようなものをお手軽に実現できるスグレモノだ。ただし Cloud IAP を直接設定できるのは App Engine ( GAE ) だけで、それ以外は Cloud Load Balancing のアプリケーションロードバランサと組み合わせる必要がある。</p> <p><img src="https://cloud.google.com/iap/images/iap-ingress-app.png?hl=ja" alt="Cloud IAP を通じて Cloud Run へのアクセスを保護する構成" /></p> <p>Cloud Load Balancing には L7 アプリケーションロードバランサ(外部/内部)、L4 ネットワークロードバランサ(外部/内部、プロキシ/パススルー)があるが、今回利用するのは外部アプリケーションロードバランサになる。VPN を使わず、インターネット越しのアクセスに対して特定の人のみアクセスできるようにしたい。</p> <p>この辺は Load Balancing 側のドキュメントではなく IAP 側のドキュメントから参照していくと寄り道が減って分かりやすい。</p> <p><a href="https://cloud.google.com/iap/docs/load-balancer-howto?hl=ja">外部 HTTP(S) ロードバランサの設定 | Identity-Aware Proxy | Google Cloud</a></p> <p>※ 意識が発散しやすい人は各ロードバランサの役割の違いなどが気になるだろうが、グッと我慢しないと帰ってこれないので注意。</p> <h2 id="まとめ">まとめ</h2> <p>Cloud Run はよくできてる。欲しいものは service の設定画面からポチポチでだいたい追加できそうな勢い。App Engine と違ってサービス単位で Logging などへの遷移がしやすいようになっているし、とても気が利いている。</p> <p>IAP を利用するための Load Balancing の設定も 2024-02 時点では preview だが、Integration からサクッと設定できてしまう。</p> <h2 id="確認したこと">確認したこと</h2> <ol> <li>Cloud Run の Integration ( preview ) 機能で Custom domains - Google Cloud Load Balancing を追加</li> <li>その後別途手作業で Load Balancing ( Frontend + Backend ) + Serverless NEG</li> </ol> <p>両方試した。2 は 加えて Frontend に</p> <ul> <li>Custom Domain</li> <li>Google-managed Certificate</li> </ul> <p>を設定し、Backend に</p> <ul> <li><strong>I</strong>dentity-<strong>A</strong>ware <strong>P</strong>roxy</li> </ul> <p>を設定した。いずれも手作業であり、自動化はしていない。</p> <p><img src="https://cloud.google.com/load-balancing/images/lb-serverless-run-ext-https.svg" alt="" /></p> <h2 id="分かったこと">分かったこと</h2> <ul> <li>IAP は Load Balancer の Backend service 単位に設定できる。これは GAE に対する IAP の設定の様子と似ている。GAE は service を複数持つことができ、service ごとに IAP の設定を分割できるが、それと同じと考えることができそうだ。Load Balancer が前に立つので GAE に限定されない。 <ul> <li>ただし Cloud Storage に対しては IAP をセットできないので静的サイトのアクセス制限は GAE の static_files の設定を利用するなど工夫が必要</li> </ul> </li> <li>URL map は host も path もなんでもこい <ul> <li>逆に他の Web サービスのように独自の名前を持たず、ユーザーとの間の単一エンドポイントを提供するための IP アドレスの払い出ししかしないので、<strong>ドメインは最初に自分たちで確保が必要</strong>(これがネックになるなら Cloud Run などではなく GAE でまかなえないか考えるのが吉)</li> </ul> </li> <li>Cloud Run 側の ingress を絞らないと誰でもアクセスできて意味がない</li> <li>固定 IP が振り出されるので IP アドレス代も掛かってるはず</li> </ul> <p>cf.</p> <ul> <li><a href="https://cloud.google.com/iap/docs/load-balancer-howto?hl=ja">外部 HTTP(S) ロードバランサの設定 | Identity-Aware Proxy | Google Cloud</a></li> <li><a href="https://cloud.google.com/load-balancing/docs/https/setting-up-https-serverless?hl=ja">Cloud Run、App Engine、または Cloud Functions を使用して従来のアプリケーション ロードバランサを設定する | 負荷分散 | Google Cloud</a></li> <li><a href="https://cloud.google.com/appengine/docs/standard/hosting-a-static-website?hl=ja">App Engine で静的ウェブサイトをホストする | Google App Engine スタンダード環境のドキュメント | Google Cloud</a></li> <li><a href="https://medium.com/google-cloud-jp/private-cloud-storage-bucket-for-load-balancing-3975c1d2b743">Google Cloud Storage をロードバランサーのバックエンドにしつつ、直接はアクセスさせたくない場合 | by Seiji Ariga | Medium | google-cloud-jp</a></li> </ul> <h2 id="思ったこと">思ったこと</h2> <ul> <li>Serveless NEG だけ作る方法はないのか? (Load Balancing 側の設定項目のように見える)</li> <li>なんで Network Endpoint Group と呼ぶのに group 設定ができるように見えないんだ?</li> </ul> <h2 id="試していないこと">試していないこと</h2> <ul> <li>異なるプロジェクトにまたがる Backend を設定すること</li> </ul>wtnabe低頻度アクセスサイトのworkerプロセスのコスパが悪いよ問題にSolidQueueが使えるかも2024-02-12T16:08:48+09:002024-02-12T16:08:48+09:00/diary/2024/0212/solid-queue-for-fewer-access-site<p><img src="/assets/images/theme/barn-images-t5YUoHW6zRo-unsplash.jpg" alt="" /></p> <h2 id="背景">背景</h2> <p>ActiveJob の backend は意外と悩ましい。</p> <p>ActiveJob は本来 Rails 専用でも ActiveRecord 専用でもないはずだが、いざ adapter を選んでみると Rails + PostgreSQL 前提です、みたいなことが意外にあったりする。また人気のバックエンドだった Redis も近年はクラウドベンダー側と折り合いが悪かったり、意外と扱いやすくない。<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> 特に Rails 5, 6 が Webpacker をはじめゴテゴテしつつも変化の早い時期で、別にこれくらいなら Rails じゃなくても Hanami でも Sinatra でもいいんじゃないの?と思っていた頃は特に悩ましかった。</p> <p>2024-02 現在、Rails 7 は十分シンプルになり、素の cold start の遅さがいわゆるサーバレスとイマイチ相性が悪いが、それ以外は概ね Rails でよいかもと思う状況に戻りつつある。</p> <h2 id="activejobバックエンド選択時の課題">ActiveJobバックエンド選択時の課題</h2> <p>上記のような背景を踏まえ、現時点で自分が感じている ActiveJob 選択時の課題は以下のように整理できる。</p> <ol> <li>Redis 以外の安定したバックエンドが使えないか <ul> <li>Resid のホスティングは以前ほどお手軽価格ではなくなっている。特にメガクラウドに寄せようと思うと、中小規模にとってはだいぶコスパが悪い状態</li> </ul> </li> <li>ActiveRecord を使うのであれば PostgreSQL, MySQL など特定の DB にはできれば依存したくない(細かいバージョンの違いなどはあまり気にしたくない)</li> <li>できれば Worker dyno, Worker コンテナを使わずに済むならその方がありがたい</li> <li>クラウド独自のツールは development 環境の再現で手間取るので、なんかもっと楽なものはないか</li> </ol> <p>えーと簡単に言うと<strong>手間もお金も掛からず ActiveRecord 任せにできるとそれが最高だな</strong>、ですね😁</p> <h2 id="低頻度アクセスのサイトのバックグラウンドジョブの課題">低頻度アクセスのサイトのバックグラウンドジョブの課題</h2> <p>特に上記 3, 4 が課題になってくるのが<strong>低頻度アクセスのサイト</strong>である。簡単に言うと</p> <p><strong>めったにジョブを処理しないのにサーバ(コンテナ)2つ分、ずっとリソースをキープしておくのもったいなくない?</strong></p> <p>という話だ。</p> <p>基本的に ActiveJob でバックグラウンドジョブを処理する場合、ユーザーからのアクセスを受け付けるプロセスとは別にバックグラウンドジョブ用のプロセス(コンテナ)を用意して、ジョブ用のプロセス(コンテナ)で polling して処理する方法が一般的なのだが、こうなるとめったにアクセスがないのに24時間起きてる人を最低2人用意しなきゃいけない、という話になり、「なんかコスパ悪くない?」という状態になる。<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></p> <p>もちろんジョブの負荷自体がメモリ使用量的にも実行時間的にも軽いものであれば、全部 inline で処理してしまうというのも一つの手ではある。ちゃんと計測してリソースが足りるのであれば問題ないと思う。しかし現実には</p> <ul> <li>頻度は高くないがコストは一定以上大きい</li> </ul> <p>ケースはままあるし、例えば簡単なメール1通送るだけだったはずの仕組みが、関係各所に送ることになりましたとか、コストの嵩むことを往々にしてやりたくなる。そうなると inline や async adapter で処理するのはインフラのリソースの制限上難しくなってくる<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>。</p> <p>いちばん確実な解決方法は 4 である。Google Cloud で言うところの Cloud Task を利用する<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>。これは完全にサーバレスで従量課金なので、バックグラウンドジョブが発生しない限り余計なコストは発生しない。</p> <p>ただ今度は手元で完全に同じ環境を再現するのが難しいという新たな課題が生まれる。</p> <h2 id="そんなあなたにsolidqueue">そんなあなたにSolidQueue</h2> <p>SolidQueue は 37 Signals が新たに開発した ActiveRecord を利用した ActiveJob adapter.<sup id="fnref:5" role="doc-noteref"><a href="#fn:5" class="footnote" rel="footnote">5</a></sup></p> <ul> <li><a href="https://dev.37signals.com/introducing-solid-queue/">37signals Dev — Introducing Solid Queue</a></li> <li><a href="https://github.com/basecamp/solid_queue">basecamp/solid_queue: Database-backed Active Job backend</a></li> </ul> <p>で、なぜ SolidQueue かと言うと SolidQueue には puma plugin があるので、SolidQueue 用に別途プロセス(コンテナ)を用意しなくても puma の worker の一つとして SolidQueue を管理させることができるから。</p> <p>もちろんメモリ使用量や実行時間がシビアな場合に気にしなければいけないことは変わらないが、少なくとも揮発しないストレージに情報を残しつつ処理できるので、ジョブを細分化するジョブを作って緩和しつつ、万一メモリ不足でプロセスが異常終了するようなことがあってもジョブが失われないという安心感がある。</p> <h2 id="簡単に試してみた">簡単に試してみた</h2> <p><a href="https://github.com/wtnabe/example-rails7-sqlite3-solid-queue">wtnabe/example-rails7-sqlite3-solid-queue</a></p> <p>上のリポジトリは Cloud Run で FUSE を使って SQLite を読み書きするというリポジトリなのでノイズが多いが、キモは</p> <p>Procfile.dev の</p> <pre><code class="language-procfile:Procfile.dev">web: mkdir -p storage/sqlite3 && ./bin/rails s </code></pre> <p>と ( worker なし ) puma の設定の</p> <pre><code class="language-ruby:config/puma.rb">plugin :solid_queue </code></pre> <p>だけで<sup id="fnref:6" role="doc-noteref"><a href="#fn:6" class="footnote" rel="footnote">6</a></sup>、solid_queue が polling してバックグラウンドジョブを処理できることである。</p> <p>※ 今回は実験用に SQLite3 で動かしているが、どうも時々エラーが起きたりするので ActiveRecord-backed とは言っているが、基本的には PostgreSQL or MySQL が前提になっているのだと思われる。</p> <h3 id="メモ">メモ</h3> <ul> <li>設定が Rails 本体側というか configure 側に断りなしに漏れていて、solid_queue.yml には一部の設定しか書けないのはちゃんとドキュメントにしてほしい</li> <li>一応 development では SQLite3 でも動くので手元でサクッと動かす分にはよさそう(InlineやThreadだと何らかの外部ストレージを経由する場合と挙動が変わってしまうので)</li> <li>Rails 7 + SQLite 3 + FUSE ( Cloud Storage ) は基本的な機能だけなら動く</li> <li>同環境だと SolidQueue の動作の何かが FUSE の制限を踏み抜いてしまうのか、必ず SQLite3::BusyException: database is locked (ActiveRecord::StatementInvalid) か SQLite3::CorruptException: database disk image is malformed (ActiveRecord::StatementInvalid) で死ぬので Cloud SQL でないと無理かも</li> </ul> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>メガクラウドは結局生のサーバを1台使う、みたいな感じになったりしてまったくお手頃価格にならなかったりする。Heroku や Redis 専門の低価格サービスと組み合わせるといった方法はあるが、今度は管理が煩雑になったりする。 <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>オンプレならリソースさえ足りれば特に問題はないが、そうなると今度はインフラ管理のコストが乗ってくる <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>メモリ使用量の制限やWebプロセスの実行時間制限など <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:4" role="doc-endnote"> <p>実際には特定の routing の Web プロセスにジョブを投げる設定の場合、Web プロセスの実行時間制限に引っかかる可能性は残る。 <a href="#fnref:4" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:5" role="doc-endnote"> <p>実際には恐らく PostgreSQL と MySQL の該当バージョン以降のもの以外は満足に動作検証されていないと思う。 <a href="#fnref:5" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:6" role="doc-endnote"> <p>リポジトリのものはコメントアウトされてしまっているので戻す必要アリ <a href="#fnref:6" class="reversefootnote" role="doc-backlink">↩</a></p> </li> </ol> </div>wtnabeCloud Run + Cloud Storage FUSE試してみた2024-02-11T19:00:00+09:002024-02-11T19:00:00+09:00/diary/2024/0211/tried-cloud-storage-fuse<p><img src="/assets/images/theme/jason-pofahl-6AQY7pO1lS0-unsplash.jpg" alt="" /></p> <p><a href="https://cloud.google.com/storage/docs/gcs-fuse?hl=ja">Cloud Storage FUSE | Google Cloud</a></p> <h2 id="cloud-storage-fuseとは">Cloud Storage FUSEとは</h2> <p>FUSE が何か分かっている人は Cloud Storage FUSE という名前だけでピンと来ると思うが、よく分からない人のために乱暴に言うと</p> <p><strong>アプリケーションから見えるファイルシステムの中に Cloud Storage を mount できるもの</strong></p> <p>になる。図にするとこんな感じ。</p> <figure class="jekyll-kroki" data-kroki-type="plantuml" data-kroki-format="svg"> <img src="https://kroki.io/plantuml/svg/eJzjciguSSwqKc3N4SrOzsxTKMovTgWzChKLEnMVijMSU_LLM_PSFdISc4AyXCWZJTmpCm6hwa6PG5fn5pfmlTxunP64cb5zTn5pikJwSX5RYnrq48Z1L1atfdy4_9nsHY8be4HcZ8tnPF07gYurKDW5JDEvHWiEY0EBEk_JLRNIBlcWl6TmKikkFiukFStUcykoIKlAsQGsJrmYq5aLC2iQgq4dUAOXQ2peCsgjANP5VBI%3D" alt="" /> </figure> <p>※ もちろん本物のファイルシステムとまったく同じにはならないが。</p> <p><a href="https://cloud.google.com/storage/docs/gcs-fuse?hl=ja">Cloud Storage FUSE | Google Cloud</a></p> <h2 id="これまでのオブジェクトストレージを読み書きするコードの課題">これまでのオブジェクトストレージを読み書きするコードの課題</h2> <p>Cloud Storage に限らずオブジェクトストレージは容量を気にする必要がないし、自動でレプリケーションされたり、とても便利な反面、通常はファイルシステムはファイルシステム、オブジェクトストレージはオブジェクトストレージで、それぞれの I/O にそれぞれ異なるコードが必要になる。</p> <figure class="jekyll-kroki" data-kroki-type="plantuml" data-kroki-format="svg"> <img src="https://kroki.io/plantuml/svg/eJxNUMFKw0AUvL-vePQu3kWkpSAIHoR-wZps4-JmE3Y3lCIesntpD4JgKYJ41Gqx1aMF0Y95JPoZ3QQPvQwz7w3DMNA1lmlbpBLMpVCoM8NbljPNUjQXLM5GQiU4ZDJ8wAorOVbfd_XjE5Vrckvyc3Kf5Bbk3slPyG0a9G_kv5p7uf5brqj8qR8CvwmyXtxXq1sAzSPLVBLSenm-ozrHIuBgbCxPO8gMDg1ewfWuoy-zIsaBzTRLeOuJDECIwcO9o-D_p0UQkcEDbJu9knsmP60-3O_sJfTonZ3gPval4MriqTjXTI-hy1XcbLEFJup7wA%3D%3D" alt="" /> </figure> <p>ではオブジェクトストレージの読み書きの動作をどう実現してどう確認するかというと、以下のような工夫が必要だった。</p> <ol> <li>emulator を使う(emulator が用意されていて API Client が emulator に対応していれば簡単)</li> <li>API Client を直接利用せず、いい具合に I/O を pluggable に切り替えてくれるライブラリを利用する</li> <li>実際のクラウド環境にコードを deploy する</li> </ol> <p>3 は間違いがないが、何か試すたびにいちいち deploy しないといけないし、クラウドの利用料金が掛かる(微々たるものかもしれないが)。</p> <p>つまりオブジェクトストレージ(というかクラウドネイティブの機能)の I/O が入ると testability が落ちてしまうという問題を抱えている。</p> <p>個人的には上の選択肢の 2 を必ず利用するようにしてきた。(自分で書き始めたコードでない場合は 1 もあり得る。)オブジェクトストレージの読み書きのライブラリの選定の際に差し替え可能で安定していそうなものを選ぶところから始め、読み書きの差し替えを行うコードを挟むところから始めるようにしている。</p> <p>正直面倒ではあるけど、その後何回も結果を確認する際にいちいちクラウド環境で動かさないといけない方が面倒なので仕方がない。</p> <h2 id="オブジェクトストレージをmountするとはどういうことか">オブジェクトストレージをmountするとはどういうことか</h2> <p>これがオブジェクトストレージをファイルシステムの一部に mount できるということは、インフラ側の設定さえ済めば、アプリケーションコード側からはファイルの読み書きのコードを書けばそれがローカルのファイルシステムに対するものか FUSE を通じたオブジェクトストレージに対するものかは気にする必要がないということを意味する。</p> <p>ローカルの開発環境で動いている場合はローカルのファイルシステムを読み書きするし、クラウド環境で動いている場合はクラウドのオブジェクトストレージを読み書きしてくれる。</p> <p>もちろん全部自分で作っているのであればファイルシステムとオブジェクトストレージの切り替えくらい自分でやれば済む、という場合もあると思うが、例えば DBM 的なライブラリを使いたいとか、利用するツールが暗黙のうちにファイルシステムに依存している場合はそうもいかない。以下の GCP 公式の対応状況では Cloud Storage FUSE 自体が AI 向けに売り出しているように見えるが、これにはそういう事情もあるのかもしれない。</p> <h2 id="gcpでの公式の状況">GCPでの公式の状況</h2> <p>2024-02-11 時点で</p> <p>Cloud Storage FUSEとの統合をサポートしている実行環境として挙げられているのは</p> <ul> <li>GKE</li> <li>Vertex AI</li> <li>Deep Learning VM Images</li> <li>Deep Learning Containers</li> <li>Batch jobs</li> <li>Cloud Composer</li> </ul> <p><a href="https://cloud.google.com/storage/docs/gcsfuse-integrations?hl=ja">Cloud Storage FUSE と Google Cloud プロダクトの統合</a></p> <p>になっているが、Cloud Run 側のドキュメントには</p> <p><a href="https://cloud.google.com/storage/docs/gcsfuse-integrations?hl=ja">Cloud Storage FUSE と Google Cloud プロダクトの統合</a></p> <p>あれもこれも知っていれば使えるよ、という情報がある。</p> <h2 id="cloud-runでの利用は実はめちゃくちゃ簡単になっている">Cloud Runでの利用は実はめちゃくちゃ簡単になっている</h2> <p>公式の GA の状況はまだまだっぽく見えるが、実際には</p> <p><a href="https://zenn.dev/google_cloud_jp/articles/cloudrun-gcs-fuse">Cloud Run と Cloud Storage FUSE (GCS FUSE) の基本</a></p> <p>にあるように、gcloud SDK では alpha components という扱いだが、</p> <ul> <li><code class="language-plaintext highlighter-rouge">--add-volume</code></li> <li><code class="language-plaintext highlighter-rouge">--add-volume-mount</code></li> </ul> <p>フラグを利用することで gen2 実行環境<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>においてはめちゃくちゃ簡単に deploy できる</p> <p><a href="https://cloud.google.com/sdk/gcloud/reference/alpha/run/deploy">gcloud alpha run deploy | Google Cloud CLI Documentation</a></p> <p>し、Cloud Run のコンソール画面では普通に Cloud Storage バケットを Volume に追加する機能がある。</p> <p>Docker で例えるとホスト OS 側のパスを指定しておくことで Docker 環境からホスト OS のコードを読み込み、ストレージに書き込めるような、あんな感じで利用できる。</p> <p>自分の感覚では</p> <p><strong>生のサーバで cron で動作させて、成果や認証の情報なんかをちょろっとファイルに書き出して終わる、みたいな伝統的なツールを Cloud Run に持ってくるとか簡単にできそう</strong></p> <p>という状態にきている。ようやく VPS を完全に解約してオッケーかも、と考えている。</p> <h2 id="やってみたこと">やってみたこと</h2> <p>以下を組み合わせて試してみた。</p> <ul> <li>実行環境 <ul> <li>Cloud Run Service gen2</li> <li>Cloud Run Jobs ( gen2 )</li> </ul> </li> <li>試したツール <ul> <li>生 Ruby</li> <li>Rails</li> <li>GDBM</li> <li>RocksDB</li> <li>SQLite</li> </ul> </li> </ul> <p>データの整合性のレベルで確認したわけではないし、並列度も全然厳密にテストしていないが、動作するという意味ではどれも問題なく動かすことができた。唯一ダメだったのは</p> <p><strong>FUSE 上の Cloud Storage に置いた SQLite を利用して Rails で SolidQueue を puma 管理下で動かすこと</strong></p> <p>だった。これは起動すらしなかった。</p> <p>development 環境でもたまに SolidQueue worker プロセスがエラーを吐いて再起動するという現象は起きていたので、SQLite + SolidQueue 自体がそこまで実績がなさそうなのと、それをさらに FUSE の制限上で動かそうとするのが無理だったのかと予想している。</p> <p>使ったコードは以下。</p> <ul> <li><a href="https://github.com/wtnabe/example-cnbp-builder-extension-and-extended-run-image">wtnabe/example-cnbp-builder-extension-and-extended-run-image</a></li> <li><a href="https://github.com/wtnabe/example-rails7-sqlite3-solid-queue">wtnabe/example-rails7-sqlite3-solid-queue</a></li> </ul> <p>Rails については SolidQueue 云々より FUSE + SQLite というだけで cold start に 15s くらい掛かるので、これはちょっとさすがになんか別な方法考えた方がいいかも。</p> <p>RocksDB についてはそこそこのサイズのデータベースを作ってもある程度(64MBとか)のサイズの chunk に分割されるので、key-value ストアとして Cloud Run のリソースの制限 ( 512 MB ) 内で利用するにはなかなかよいかも、と思った。当然速さは出ないけど、それでも十分機能します、みたいなシーンはそれなりにありそう。</p> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>2023年に GA になった Cloud Run Jobs は最初から gen2 で、従来からある service の方はオプション –execution-environment gen2 を与えると gen2 になる。 <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p> </li> </ol> </div>wtnabeCloud Native Buildpacksのextension完全に理解した2024-01-28T13:44:04+09:002024-01-28T13:44:04+09:00/diary/2024/0128/extend-cloud-native-buildpacks<p><img src="/assets/images/theme/ben-allan-BIeC4YK2MTA-unsplash.jpg" alt="" /></p> <p><a href="/diary/2023/1231/extend-run-image-of-cloud-native-buildpacks-with-giving-up-extension/">Cloud Native Buildpacksでextensionを諦めつつrun-imageを拡張する (2023-12-31) | あーありがち</a> の続き。</p> <h2 id="確認環境">確認環境</h2> <p>2024-01-28 時点</p> <ul> <li>macOS ( arm64 )</li> <li>colima 0.6.7</li> <li>Docker <ul> <li>Client 25.0.0</li> <li>Server 24.0.7</li> </ul> </li> <li>pack 0.32.1 および main ブランチ HEAD</li> <li>gcr.io/buildpacks/builder:google-22 Builder</li> <li>Google Cloud Run</li> </ul> <p>で確認。</p> <p>この時点の pack CLI が suggest してくる trusted builder はどうも –extension オプションを受け取って意図通りに動作することはなさそうだった。</p> <h2 id="やりたかったこと--実現したこと">やりたかったこと / 実現したこと</h2> <p>Cloud Native Buildpacks の build image, run image に用意されていないパッケージを独自に追加し、それを利用する rubygems をインストールし、クラウドインフラ(Cloud Run)上で実行する。</p> <p>image の拡張の部分は具体的には</p> <ul> <li>build image に対しては pack コマンドの –pre-buildpack オプションと –extension オプションを用いてパッケージの追加を行う</li> <li>run image に対しては <a href="/diary/2023/1231/extend-run-image-of-cloud-native-buildpacks-with-giving-up-extension/">前回</a> 同様、事前に build しておいた image を –run-image で与える</li> </ul> <p>方法になった。この辺り何をしたかは <a href="https://github.com/wtnabe/ example-cnbp-builder-extension-and-extended-run-image">成果物</a> の sh script にまとめてあるので参考になれば幸い。</p> <h2 id="目指さなかったこと">目指さなかったこと</h2> <p>オリジナル Builder の作成。</p> <h2 id="分かったこと">分かったこと</h2> <p>ゼロから Builder を作らなくてもなんとか既存の image を拡張して必要な機能を追加することができる。</p> <p>一方でそのためには<strong>ある程度以上の Docker の知識と Cloud Native Buildpacks の知識が必要になる</strong>。</p> <p>特に</p> <ul> <li>Dockerfile を自分で書ける</li> <li>Builder, Buildpack, Extension に対する理解が整理できている</li> <li>docker build 中の USER と Cloud Native Buildpacks の USER について理解できている</li> </ul> <p>辺りは必須になる。「Dockerfile を書かずに Docker image を作れる」という謳い文句からは急に遠くなり、正直言うと少々ハードルが高い気がする。Buildpack という言葉がすでに長い歴史を持ち、調べ方によっては正しい情報に辿り着くことも難しい。</p> <p>こうなると逆にゼロから Builder を作る経験はあった方がよいかもしれない<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>し、むしろ Docker と配布 Docker image に対する知識、multi-stage build に対する知識があるなら素直に自分で Dockerfile を書いた方が楽かもしれない。</p> <h3 id="cloud-native-buildpacksを導入しやすいパターン">Cloud Native Buildpacksを導入しやすいパターン</h3> <ul> <li>development 環境の再現に際し docker compose を前提としない <ul> <li>ツールのインストール、複数バージョンの切り替え以外については Procfile でプロセスを管理すれば問題ない</li> </ul> </li> <li>開発開始時に最終的な実行環境に必要な Docker 周りの準備を決めきっておく必要がない <ul> <li>特殊な拡張が必要かどうかの決断は先送りしてよい</li> </ul> </li> </ul> <p>いわゆる普通の Web アプリだと比較的この構成に近い気がする。どうしても開発機の OS が混ざるのでコンテナに閉じ込めておきたいということでなければ、それほど導入は苦しくないように思う。</p> <h3 id="extensionを利用してまで既存のbuilderを利用するメリット">extensionを利用してまで既存のbuilderを利用するメリット</h3> <ul> <li>開発者がインフラに詳しくなくてもまずはそのまま動くものを作ってもらうことができる</li> <li>docker image の選定などは先送りにできる</li> <li>ある程度以上の期間使うようになると Docker image のメンテナンスはそれなりに面倒になるので、クラウドベンダーの準備してくれる Docker image の上に乗れるのは、長期的には考えることが減って助かる</li> </ul> <h2 id="ハマったこと--注意点">ハマったこと / 注意点</h2> <ul> <li>project.toml の schema-version をちゃんと “0.2” にしないと exclude は機能しない。schema-version は optional なので無指定でもエラーにならない(これは正しい動作)が、0.1 で対応していない exclude を指定してもやはりエラーにならない <ul> <li><a href="https://github.com/buildpacks/pack/issues/1922">Pack silently fails to use `project.toml` if the `schema-version` is not specified · Issue #1922 · buildpacks/pack</a></li> </ul> </li> <li>今のところ自分が動作を確認できた Builder は gcr.io/buildpacks/builder:google-22 のみ。例えば同じ手法で heroku/builder:22 に extension でパッケージを追加することはできなかった。また今後対応 builder が増えるのかどうなのかもよく分からない。(そういうプランを見つけることはできなかった。)</li> <li>Google のリファレンス <a href="https://cloud.google.com/docs/buildpacks/build-run-image">Configure your build and run images | Buildpacks | Google Cloud</a> によると builder image はもっとカジュアルに Dockerfile で拡張できそうに見えるのだが、実際に試すとできあがった builder image は “could not parse reference” と言われて使えない</li> </ul> <p>考えると schema-version に限らず、Buildpack API などのバージョン情報も意味がよく分からない。どのバージョンにどのような schema 情報が紐づくのか、分かりやすい資料がない。これはぜひ改善してほしい。</p> <h2 id="おまけ---cloud-native-buildpacksのextensionとは">おまけ - Cloud Native Buildpacksのextensionとは</h2> <p>Cloud Foundry / Heroku 時代の buildpack と違い、登場人物が多くてややこしいが、公式ドキュメントのサンプルのコードを読み、実際に動かしてみての自分の理解は、こんな感じになった。</p> <figure class="jekyll-kroki" data-kroki-type="plantuml" data-kroki-format="svg"> <img src="https://kroki.io/plantuml/svg/eJxdkkFywyAMRfecQuO9cwAvkjQzXXTbC8RyUFwmGAiYtJ2O715M4rHMDr6evj4aRNu2TqMZ46DFMYzo8ynclAFvA-WTQ48DhC-U9luZHq6oU0V4uoxoek1wikpL8vAnAFa1WuSPAXuqAAN0T-WsZiXjvCHzDi-3V2Uq6k_D0m5jNrdMYmLZqjfnWAumWxHzMxoG-GiY4-zFzZynultiZj4p544F57Pff0YyQVmT9fV5db1ntSaFuUflSaxaHROyNjRpjH0oSWI7LmObpZYAG1Ta8-01MFiprr-M2cXdnm1jIeAgjmTk_EvS3xH_45XEMA%3D%3D" alt="" /> </figure> <p>ちょっと分かりにくいが、</p> <ul> <li>Extension は image に何らかの機能、役割を provide するもの</li> <li>Extension も Buildpack のように detect, generate というフェーズで image に改変を加える <ul> <li>この際、公式ドキュメントとサンプルコードを読むに build image にも run image にも改変を加えられそうだったのだが、自分が試したところ build image にしか上手く改変を加えることはできなかった</li> <li>run image は <a href="/diary/2023/1231/extend-run-image-of-cloud-native-buildpacks-with-giving-up-extension/">前回と同じ手法</a> を採用することにした</li> </ul> </li> <li>ただし、provide したものを require する Buildpack が必要</li> <li>Buildpack は本来 Builder image に含まれるものだが、pack コマンド 0.32.1 には –pre-buildpack / –post-buildpack というオプションで image 化していない生ファイルの Buildpack を追加することができる <ul> <li>今回は rubygems のインストール前に必要なパッケージを追加したいので –pre-buildpack を利用した</li> </ul> </li> </ul> <h2 id="成果物">成果物</h2> <p><a href="https://github.com/wtnabe/ example-cnbp-builder-extension-and-extended-run-image">wtnabe/example-cnbp-builder-extension-and-extended-run-image</a></p> <h2 id="参考">参考</h2> <ul> <li><a href="https://buildpacks.io/docs/extension-guide/">Extension Guide · Cloud Native Buildpacks</a></li> <li><a href="https://future-architect.github.io/articles/20201002/">Buildpacksのビルダーをスクラッチから作ってみる | フューチャー技術ブログ</a></li> </ul> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>自分は Heroku / Cloud Foundry 時代の古い buildpack の知識を援用してごまかした。 <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p> </li> </ol> </div>wtnabeannoy-rbで初めてのベクトル検索をめちゃくちゃ手軽に試す2024-01-08T22:45:28+09:002024-01-08T22:45:28+09:00/diary/2024/0108/my-first-vector-search-with-annoy-rb<p><img src="/assets/images/theme/vackground-com-up8ooQ1Pm2s-unsplash.jpg" alt="" /></p> <h2 id="背景">背景</h2> <p>ベクトル検索は興味あるけど、ベクトル化するところでいつもつまずく<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>ので、「いちばん簡単なの緯度経度ちゃうの?」と思って探してみたら実は意外と緯度経度情報をベクトル検索するという話題がない<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>のでやってみた。</p> <h2 id="今回の成果">今回の成果</h2> <p>データベースも使わず、embedding などの技法も何も要らない、めちゃくちゃ素朴なベクトル検索ができた。</p> <p>ベクトル検索に関する各要素についてなんとなくの理解だったが、この素朴なものができたおかげで理解がよりクリアになった。(理解したとは言っていない)</p> <h2 id="おさらい">おさらい</h2> <ul> <li>最近AIとかで有名なベクトル検索は、与えられたベクトルが候補となるベクトルとどの程度近いかを比較し、近いものから抽出とかしてくれる <ul> <li>これに特化したデータベースがベクトルデータベース</li> </ul> </li> <li>ベクトルの「近さ」にはいくつかある</li> <li>ベクトルの「近さ」を計算するライブラリがある。ミニマムなものはこの辺り。 <ul> <li><a href="https://github.com/spotify/annoy">spotify/annoy: Approximate Nearest Neighbors in C++/Python optimized for memory usage and loading/saving to disk</a></li> <li><a href="https://github.com/facebookresearch/faiss">facebookresearch/faiss: A library for efficient similarity search and clustering of dense vectors.</a></li> </ul> </li> </ul> <h2 id="準備">準備</h2> <p>今回は例によって Ruby でやるので、Ruby 用の binding を探すと以下のような感じ。</p> <ul> <li><a href="https://github.com/ankane/faiss-ruby?tab=readme-ov-file">ankane/faiss-ruby: Efficient similarity search and clustering for Ruby</a></li> <li><a href="https://github.com/yoshoku/annoy-rb?tab=readme-ov-file">yoshoku/annoy-rb: annoy-rb provides Ruby bindings for the Annoy (Approximate Nearest Neighbors Oh Yeah).</a></li> </ul> <p>AI 関係調べるとよく出てくる安定の ankane さんと洋食さんなので安心でしょう。</p> <p>ベクトルの類似度、近似度についてイチから学習する気は今回はないので、どっちも軽く触ってみて、<strong>なんとなく分かりやすそう</strong>な annoy の方を使ってみる。インデックス方式の違いがよく分からん。</p> <h2 id="作ったもの">作ったもの</h2> <ul> <li>日本の各都道府県の人口上位3都市とその緯度経度(たぶん普通は庁舎のある地点だとオム)を ChatGPT 3.5 さんで適当に生成</li> <li>その ID と緯度経度情報(ベクトル)だけを annoy-rb に与えてビルド</li> <li>緯度経度を与えたらそれを検索するだけの CLI アプリ</li> </ul> <p><a href="https://github.com/wtnabe/example-simple-vector-search-with-annoy-rb">wtnabe/example-simple-vector-search-with-annoy-rb</a></p> <h2 id="分かったこと">分かったこと</h2> <ul> <li>annoy-rb は DBM っぽい何かのように使える</li> <li>ベクトルの次元と距離計算の方法を先に決める必要がある</li> <li>データを入れたらビルドが必要</li> <li>ベクトル検索はベクトルしか持っていないので ID で元データと突き合わせが必要 <ul> <li>逆に言うと突き合わせさえできるなら別に専用の大掛かりなベクトル検索の仕組みがなくてもベクトル化、embedding さえなんとかなれば、意外と導入はローコストにできるのかも<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup></li> </ul> </li> <li>単純に<strong>緯度経度で検索</strong>する場合、コサイン類似度などではなく、<strong>ユークリッド距離</strong>で比較しないと直感に反した結果になります(そりゃそうだ)</li> </ul> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>興味が発散しすぎて帰ってこれない <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>たぶん簡単すぎるんでしょう <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:3" role="doc-endnote"> <p>ただし、ファイルシステムベースのものはクラウド環境とあまり相性がよくないので、そこは考える必要がある <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p> </li> </ol> </div>wtnabeCloud Native Buildpacksでextensionを諦めつつrun-imageを拡張する2023-12-31T21:00:00+09:002023-12-31T21:00:00+09:00/diary/2023/1231/extend-run-image-of-cloud-native-buildpacks-with-giving-up-extension<p><img src="/assets/images/theme/jason-pofahl-6AQY7pO1lS0-unsplash.jpg" alt="" /></p> <h2 id="要望">要望</h2> <p><strong>Cloud Native Buildpacks の Builder が提供するイメージに含まれないツールを使いたい</strong></p> <p>例えば ImageMagick. いや ImageMagick くらい入っているか。例えば ffmpeg. さすがに普通入ってなさそう。</p> <p>今回はそういうツールを使いたいと思った場合の対処方法を調べてみた。</p> <h2 id="まとめ">まとめ</h2> <ol> <li>利用したい Builder の Run image の base image に対して<strong>Dockerfile で追加設定</strong>を行い、<code class="language-plaintext highlighter-rouge">docker build</code></li> <li><code class="language-plaintext highlighter-rouge">pack build --run-image <1でできたイメージ></code> でコンテナイメージを作る</li> </ol> <h2 id="cloud-native-buildpacksのbuild-imageとrun-image">Cloud Native BuildpacksのBuild imageとRun image</h2> <p>まず前提として押さえておくべき知識について。</p> <p>今回考えているのは</p> <p><strong>実行時に、標準的でないツールを導入したい</strong></p> <p>ということなので、</p> <p><a href="https://buildpacks.io/docs/concepts/operations/build/">Build · Cloud Native Buildpacks</a></p> <p>で言うところの app image の中に「標準的でないツール」を導入したい、という要望である。</p> <p>すると<strong>工夫が必要なのは Run image</strong>ということになる。</p> <h2 id="run-imageに変更を加える手順">Run imageに変更を加える手順</h2> <p>run imageの拡張については非常にコンパクトだが、以下がまとまっている。</p> <p><a href="https://cloud.google.com/docs/buildpacks/build-run-image?hl=ja">ビルドと実行のイメージを構成する | Buildpacks | Google Cloud</a></p> <h3 id="1-run-imageのbase-imageからdocker-buildで拡張する">1. run imageのbase imageからdocker buildで拡張する</h3> <ul> <li>Builder の提供している run-image 用の base image を FROM に持つ <strong>Dockerfile を書いて</strong>、そこに必要な変更を加える</li> <li>USER の切り替えに注意。例えば Heroku Builder を使う場合は <code class="language-plaintext highlighter-rouge">USER root</code> で変更を加えたあと、<code class="language-plaintext highlighter-rouge">USER heroku</code> に戻す必要があった。</li> <li>run-image の base は Builder ごとに違うのでそれぞれで確認すること</li> </ul> <p>で、<code class="language-plaintext highlighter-rouge">docker build</code> を実行する。</p> <p>例えば Heroku Builder の Heroku-22 イメージに対して ffmpeg を追加したい場合は以下のような Dockerfile になる</p> <div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> heroku/heroku:22-cnb</span> <span class="k">USER</span><span class="s"> root</span> <span class="k">RUN </span>apt-get update <span class="o">&&</span> <span class="se">\ </span> apt-get <span class="nb">install</span> <span class="nt">-y</span> ffmpeg <span class="k">USER</span><span class="s"> heroku</span> </code></pre></div></div> <h3 id="2-できたimageをpack-build時に利用する">2. できたimageをpack build時に利用する</h3> <p>こうゆうこと</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ pack build -B <builder> --run-image <さっき作ったimage> </code></pre></div></div> <h3 id="あれ結局dockerfile書いてない">あれ、結局Dockerfile書いてない?</h3> <p>そうなんです。</p> <p>ということは想定外の入力が入る可能性のある実行環境でこの run-image を利用する場合は、常にフレッシュで安全な状態を保つように独自に CI/CD で build し続けておく必要があります。</p> <h2 id="諦めたimage-extension">諦めたImage Extension</h2> <p>実は最初</p> <p><a href="https://buildpacks.io/docs/extension-guide/create-extension/">Create an extension · Cloud Native Buildpacks</a></p> <p>を参考に CNB image extensions という考え方でなんとかなるんじゃないかと思ったんだけど、全然情報はないし、サンプルのリポジトリを見ても分からないしで、諦めました。</p> <p>Heroku の Builder を利用したところ、</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>failed to build: executing lifecycle: builder has an order for extensions which is not supported when using the creator </code></pre></div></div> <p>と言われて、調べてもさっぱり情報が見つからず、Google の Builder はどうも無視してるっぽいので、</p> <p><strong>今回は諦めです。</strong></p>wtnabeGemfile.lockでRubyのversionを指定する方法2023-12-31T09:00:00+09:002023-12-31T09:00:00+09:00/diary/2023/1231/how-to-specify-ruby-version-with-gemfile-lock<p><img src="/assets/images/theme/alexander-sinn-KgLtFCgfC28-unsplash.jpg" alt="" /></p> <h2 id="まとめ">まとめ</h2> <p>Gemfile.lock に利用する Ruby のバージョンをセットする方法</p> <ul> <li>Gemfile に <code class="language-plaintext highlighter-rouge">ruby</code> でバージョンを指定</li> <li><code class="language-plaintext highlighter-rouge">bundle update --ruby</code> を実行</li> </ul> <h2 id="きっかけ">きっかけ</h2> <p><a href="/diary/2023/1216/have-tried-cloud-native-buildpacks/">HerokuのCloud Native Buildpacksを使ってCloud RunでSinatraアプリを動かしてみた (2023-12-16) | あーありがち</a></p> <p>で初めて経験したんだけど、なんかどうもこの方面は Gemfile.lock でのバージョン指定は普通っぽいので、整理しておいた。</p> <h2 id="注意">注意</h2> <p>Google の Buildpack だったか、<code class="language-plaintext highlighter-rouge">.ruby-version</code> に改行が含まれてたら Gemfile, Gemfile.lock 側のバージョン指定と矛盾しているという警告が出た。いやもう、そんくらいいい具合に処理してくれよ。なんでみんなそんな繊細なのよ。</p> <p>そういう意味では、この辺はまだまだ発展途上なのかも。ただあんまりフィードバックしてる人いなさそう(自分も含め)</p>wtnabeCreative Stage SE miniを買ってた2023-12-30T16:00:00+09:002023-12-30T16:00:00+09:00/diary/2023/1230/i-had-bought-a-creative-stage-se-mini<p><img src="/assets/images/theme/chris-grafton-3FgOk2RdMCQ-unsplash.jpg" alt="" /></p> <h2 id="背景">背景</h2> <p>以前からたまに Bluetooth イヤホンがうまく充電されていないことがあり、ミーティングの際にイヤホンが使えずに PC 本体のスピーカーで対応することがあったのだが、ノート PC をクラムシェルモードで使っているため、音がこもってとても聞き取りにくく、困っていた。ということでなんとなくスピーカーあった方がいいかなぁ、とは思ってはいたものの、選ぶのが面倒くさくて先送りにしていたのだが、たまたまブラックフライデーのタイミングで同じことが起きたため、頑張って調べてみた。</p> <h2 id="判断基準">判断基準</h2> <p>とりあえずぶわっと眺めたうえで自分なりの判断基準を整理。</p> <ul> <li><strong>USB で接続でき、別途電源を必要としないこと</strong> <ul> <li>現在ノート PC 本体へは Thunderbolt 4 ( Type-C ) ケーブル 1本で給電、AV 出力、Ether すべてをまかなっているので、スピーカーもここにそのまま加えたい</li> </ul> </li> <li><strong>人の声の聞き取りを重視</strong>、別に本格的な音楽や映画などでの迫力は考えていない、ってゆーかドンシャリはきらい</li> <li>あまり<strong>存在感が強くないこと</strong>。つまりゲーミング系は自動的に除外されるし、できれば高さがあまりないこと</li> </ul> <p>を前提に、</p> <ul> <li>サウンドーバーの価格はけっこう極端で、2000 円くらいのものがあるかと思えば普通に1万円超えたりする。音についてはレビュー動画なんかもあるが、結局のところ買ってみないとほぼ分からないので、失敗の可能性を考慮し、<strong>中間くらいの価格帯を検討</strong></li> <li>ブラックフライデーの割引がないものは慌てて買う必要がない <ul> <li>これを踏まえるとだいたい本当に良い高いものとものすごく安いものは除外される</li> </ul> </li> </ul> <p>という考え方でざっくり探した。</p> <p>するとちょうどよい価格帯で Creative Stage SE mini が見つかった。ただし、もう Type-C ポートが空いていなかったので増やすために Belkin の USB ハブも追加で購入することになった。(USB-C ハブは割引対象外。)</p> <h3 id="おまけ---現在の構成">おまけ - 現在の構成</h3> <figure class="jekyll-kroki" data-kroki-type="plantuml" data-kroki-format="svg"> <img src="https://kroki.io/plantuml/svg/eJxlkLFLw0AYxff7K47sWZxDhxZ3IXVzuTRHE5Km4S6ZRPDuJq0gtFI3ERERCrqJgtI_5inof2GahPSsyzf8vve99_hIERcppzB3MDPoNdTTod93B1CPMDfQ99DPFftZLqBWzcZcwiyh59_rj6_zW6hrnKqjDGoBfVGJoF6gzz5fZ1BzqAfoN5grmHfoVT3bO0JkEmc5E2xCBR8VLBunvD8VIRfDKB4lGZfS81ie8yzkYa9H9wjpdNu-9JicWHwYlZVcBNO0aHpSJmkRRGWwIzwY1IA0O89N3QqRrW1N6qUd61gfcDbWpWzu7aJ_g7ov7nDHL9iYU3-fTuIsrs1kzlnCxX83q5fr9tpQ0mZvSBdiw9aO_AIadNXs" alt="" /> </figure> <h2 id="使ってみて">使ってみて</h2> <!-- START MoshimoAffiliateEasyLink --> <script type="text/javascript"> (function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a; b[a]=b[a]||function(){arguments.currentScript=c.currentScript ||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)}; c.getElementById(a)||(d=c.createElement(f),d.src=g, d.id=a,e=c.getElementsByTagName("body")[0],e.appendChild(d))}) (window,document,"script","//dn.msmstatic.com/site/cardlink/bundle.js?20220329","msmaflink"); msmaflink({"n":"Creative Stage SE Mini Bluetooth USB DAC ヘッドホン端子付 サウンドバー SP-STGESM-BK","b":"CREATIVE","t":"SP-STGESM-BK","d":"https:\/\/m.media-amazon.com","c_p":"\/images\/I","p":["\/21KR9jOikUL._SL500_.jpg","\/51TkjhEb7sL._SL500_.jpg","\/41ausF-HEnL._SL500_.jpg","\/51st8B7J69L._SL500_.jpg","\/41ZY4tM8BbL._SL500_.jpg","\/41HG92g1zYL._SL500_.jpg"],"u":{"u":"https:\/\/www.amazon.co.jp\/dp\/B0CJ26G9RL","t":"amazon","r_v":""},"v":"2.1","b_l":[{"id":1,"u_tx":"Amazonで見る","u_bc":"#f79256","u_url":"https:\/\/www.amazon.co.jp\/dp\/B0CJ26G9RL","a_id":1440812,"p_id":170,"pl_id":27060,"pc_id":185,"s_n":"amazon","u_so":1},{"id":2,"u_tx":"楽天市場で見る","u_bc":"#f76956","u_url":"https:\/\/search.rakuten.co.jp\/search\/mall\/Creative%20Stage%20SE%20Mini%20Bluetooth%20USB%20DAC%20%E3%83%98%E3%83%83%E3%83%89%E3%83%9B%E3%83%B3%E7%AB%AF%E5%AD%90%E4%BB%98%20%E3%82%B5%E3%82%A6%E3%83%B3%E3%83%89%E3%83%90%E3%83%BC%20SP-STGESM-BK\/","a_id":1439134,"p_id":54,"pl_id":27059,"pc_id":54,"s_n":"rakuten","u_so":2}],"eid":"a4j8G","s":"s"}); </script> <div id="msmaflink-a4j8G">リンク</div> <!-- MoshimoAffiliateEasyLink END --> <!-- START MoshimoAffiliateEasyLink --> <script type="text/javascript"> (function(b,c,f,g,a,d,e){b.MoshimoAffiliateObject=a; b[a]=b[a]||function(){arguments.currentScript=c.currentScript ||c.scripts[c.scripts.length-2];(b[a].q=b[a].q||[]).push(arguments)}; c.getElementById(a)||(d=c.createElement(f),d.src=g, d.id=a,e=c.getElementsByTagName("body")[0],e.appendChild(d))}) (window,document,"script","//dn.msmstatic.com/site/cardlink/bundle.js?20220329","msmaflink"); msmaflink({"n":"Belkin Connect™ USB-C to 4ポートUSB-Cハブ(4-in-1) 100W PD タイプCポート10Gbps 超高速データ転送 FRS技術搭載 データ破損防止 全ポートオーディオ対応 マイク\/スピーカー接続 コンパクトサイズ M1\/M2チップセット MacBook\/iPad\/Chromebook\/Windows対応 テレワーク Vlog向け ブラック AVC018btBK","b":"Belkin","t":"AVC018BTBK","d":"https:\/\/m.media-amazon.com","c_p":"\/images\/I","p":["\/31HhTitFjCL._SL500_.jpg","\/51gORs9mwKL._SL500_.jpg","\/41i7CvqtexL._SL500_.jpg","\/41HtQpt0WeL._SL500_.jpg","\/41ulW4OI+dL._SL500_.jpg","\/41Kl0g0b1GL._SL500_.jpg","\/41ij8-MagvL._SL500_.jpg","\/41K9G6pdCgL._SL500_.jpg","\/51Po9Zs2hpL._SL500_.jpg","\/41LYHfl8BrL._SL500_.jpg","\/51sx0jJtWFL._SL500_.jpg","\/51zmLBrnCAL._SL500_.jpg"],"u":{"u":"https:\/\/www.amazon.co.jp\/dp\/B0BY27XSJ3","t":"amazon","r_v":""},"v":"2.1","b_l":[{"id":1,"u_tx":"Amazonで見る","u_bc":"#f79256","u_url":"https:\/\/www.amazon.co.jp\/dp\/B0BY27XSJ3","a_id":1440812,"p_id":170,"pl_id":27060,"pc_id":185,"s_n":"amazon","u_so":1},{"id":2,"u_tx":"楽天市場で見る","u_bc":"#f76956","u_url":"https:\/\/search.rakuten.co.jp\/search\/mall\/Belkin%20Connect%E2%84%A2%20USB-C%20to%204%E3%83%9D%E3%83%BC%E3%83%88USB-C%E3%83%8F%E3%83%96(4-in-1)%20100W%20PD%20%E3%82%BF%E3%82%A4%E3%83%97C%E3%83%9D%E3%83%BC%E3%83%8810Gbps%20%E8%B6%85%E9%AB%98%E9%80%9F%E3%83%87%E3%83%BC%E3%82%BF%E8%BB%A2%E9%80%81%20FRS%E6%8A%80%E8%A1%93%E6%90%AD%E8%BC%89%20%E3%83%87%E3%83%BC%E3%82%BF%E7%A0%B4%E6%90%8D%E9%98%B2%E6%AD%A2%20%E5%85%A8%E3%83%9D%E3%83%BC%E3%83%88%E3%82%AA%E3%83%BC%E3%83%87%E3%82%A3%E3%82%AA%E5%AF%BE%E5%BF%9C%20%E3%83%9E%E3%82%A4%E3%82%AF%2F%E3%82%B9%E3%83%94%E3%83%BC%E3%82%AB%E3%83%BC%E6%8E%A5%E7%B6%9A%20%E3%82%B3%E3%83%B3%E3%83%91%E3%82%AF%E3%83%88%E3%82%B5%E3%82%A4%E3%82%BA%20M1%2FM2%E3%83%81%E3%83%83%E3%83%97%E3%82%BB%E3%83%83%E3%83%88%20MacBook%2FiPad%2FChromebook%2FWindows%E5%AF%BE%E5%BF%9C%20%E3%83%86%E3%83%AC%E3%83%AF%E3%83%BC%E3%82%AF%20Vlog%E5%90%91%E3%81%91%20%E3%83%96%E3%83%A9%E3%83%83%E3%82%AF%20AVC018btBK\/","a_id":1439134,"p_id":54,"pl_id":27059,"pc_id":54,"s_n":"rakuten","u_so":2}],"eid":"8C5K6","s":"s"}); </script> <div id="msmaflink-8C5K6">リンク</div> <!-- MoshimoAffiliateEasyLink END --> <ul> <li>USB 1本で繋がるのはやはりとても快適。PC 本体側は何も変わらない</li> <li>音が入らないと自動的に接続が切れるっぽく、再接続にやや時間が掛かる(スタンバイからの復帰時などは特に)</li> <li>ボリュームの調整を物理ダイヤルでできるようになったので、これがけっこうありがたい <ul> <li>ノート PC 本体のキーボードを使っている場合はともかく、クラムシェルモードではボリューム調整に難儀していたのでマジ助かる</li> </ul> </li> <li>当たり前だがやや距離のあるクラムシェルモードのノート PC スピーカーよりは目の前に置いたサウンドバーは格段に聞きやすい。ただし、なんとなくこもった感じはする<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></li> <li>まだミーティングはしていないので肝心の部分の評価はできていないが、YouTube のニュース、音楽、ゲーム実況などを本体スピーカーと聴き比べる限りはまぁ心配なさそう。男性の低い声はちょっと聞き取りにくいかもしれないけど、まぁブラウザ拡張にイコライザありそうだし、どーしても困ったらそういうのも考えるか。</li> </ul> <p><strong>USB-C ハブの方が高かった</strong>ことを除けば、とてもよい買い物をしたと思う。</p> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>まったくの主観だが、自分はこのこもった感じが気になりやすい方だと思う。ただ https://av.watch.impress.co.jp/docs/series/zooma/1536312.html レビュー記事でも同様の評価なので、まぁそんなもんなんだと思う <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p> </li> </ol> </div>wtnabeOllamaですごく簡単にLLMを動かせたけど…2023-12-29T09:00:00+09:002023-12-29T09:00:00+09:00/diary/2023/1229/get-llm-working-with-ollama-very-easily-but<p><img src="/assets/images/theme/5c0f0648-3690-4f25-832b-3b9c5022be3c.jpeg" alt="" /></p> <p>※ 今回の画像は DALL E3 で作ってみた。Transformer を ChatGPT に説明させて、その要旨を短く拾い上げ(これは自分でやった)、それを元に画像生成の指示とした。</p> <h2 id="まとめ">まとめ</h2> <p>とにかくでかい</p> <h3 id="環境">環境</h3> <ul> <li>AppleSillicon Mac メモリ 16GB</li> <li>macOS 13.6</li> <li><a href="https://github.com/abiosoft/colima">colima</a> 0.6.7 ( limactl 0.19.1 )</li> <li><a href="https://github.com/docker/cli">docker</a> 24.0.2 ( via Homebrew )</li> <li><a href="https://ollama.ai/">Ollama</a> 0.1.15</li> <li><a href="https://github.com/ggerganov/llama.cpp">llama.cpp</a> 1709 (ea5497d) built with clang-1403.0.22.14.1 for arm64</li> </ul> <h3 id="やったこと--できたこと">やったこと / できたこと</h3> <ul> <li>ローカルで LLM を動かすために Ollama を動かしてみた。めちゃくちゃ簡単だった</li> <li>neural-chat モデルを pull して動かした <ul> <li>→ おぉ、なんかそれっぽいチャットができる!</li> </ul> </li> <li><a href="https://huggingface.co/">Hugging Face</a> からモデルをダウンロードするために初めて <a href="https://git-lfs.com/">Git LFS</a> 使った</li> <li>Ollama は必要なタイミングで LLM を読み込み直したり、不要になったら止めたりと賢く動いてくれるので常時メモリがカツカツということはない</li> </ul> <h2 id="背景とか思っていたことなど">背景とか思っていたことなど</h2> <ul> <li>ChatGPT を個人で利用する分にはあんまりプロンプトエンジニアリングとか盛り上がらなかったけど、RAG には興味ある <ul> <li>なんならベクトル検索だけでも興味ある</li> </ul> </li> <li>Ruby だと <a href="https://github.com/andreibondarev/langchainrb">Langchain.rb</a> と <a href="https://github.com/pgvector/pgvector-ruby">pgvector</a> でなんとかなるっぽい</li> <li>AI エージェントってなんだ? <ul> <li>タスクに分解するところからやってくれるのか。全部うまいこと連携するかどうかはともかく、むしろフローを分解、整理する部分は曖昧な知識補完よりも最新の情報も必要なく AI の得意分野っぽい(エキスパートなんとか的な考え方かもしれない)</li> </ul> </li> <li>思っていたよりタスクはもっと細かく分けられるみたい <ul> <li>チャット形式ではなく埋め込みの AI なんとかは自分たちのプロダクトのコンテキストを前提にタスクを最適化することで気の利いた動作を実現しようとしているのかもしれない</li> </ul> </li> </ul> <p>あれ、思ったより面白いかもしれないぞ?</p> <ul> <li>チューニングもそれなりに時間が掛かるだろうし、LLM はともかく RAG も含めて小規模なセットを手元で動かせるようにしておきたい</li> <li>ローカル LLM は llama.cpp が定番ぽいけど、野良ビルドしかインストール方法がないのがイヤだなぁとウダウダ調べてたらなんとか Ollama にたどり着いた</li> </ul> <h2 id="分かったこと">分かったこと</h2> <ul> <li>AppleSillicon はメモリ 16GB と内蔵 GPU だけでも十分 LLM を動かせる(思ったより速い)</li> <li>Ollama は(恐らく)内蔵の llama.cpp とプロンプトなどの追加データで Ollama 用のモデルを成立させている <ul> <li>だから GGUF に標準対応しているので、GGUF で量子化されているモデルならそのまま利用できる</li> </ul> </li> <li>世の中 OpenAI の API 利用を前提にしたツールは多いが、<a href="https://docs.litellm.ai/">LiteLLM</a> を使って OpenAI サーバに仕立てればよさそう</li> <li>LLM は量子化前提でもクソでかい <ul> <li>git clone でカジュアルに 100GB 降ってきて手元のストレージが残り 4GB 切ってさすがに焦った<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></li> </ul> </li> <li>フルサイズのモデルはさすがにやってられないので quantize を Ollama の用意してくれている Docker イメージでやろうとしたが、進捗が途中で止まって終わらない <ul> <li>生の llama.cpp でやったら割とすんなり終わったので、memory-bound な処理を Docker で動かすのはやっぱイマイチっぽい<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup></li> </ul> </li> <li>量子化もまだ過渡期で、llama.cpp の GGML / GGUF、HuggingFace の GPTQ、MIT の AWQ などまだまだ枯れていない</li> <li>ローカルで LLM を動かしてみると、チャットが思った通りに成り立たない。AI がぐわーっとまくし立てたり、逆に勝手に会話を切り上げようとしたりする。<strong>結局プロンプトやインタラクションを決定するテンプレートはかなり重要</strong></li> <li>デカイ、とにかくデカイ。</li> </ul> <p>ブライックフライデーで買っておいた NAS を繋いで NAS を基本に作業させることにしましたとさ。(めっちゃ遅くなるけど仕方ない)</p> <p>全体像はこの辺に話があるのと、日本国内の日本語の事例もそこそこ増えているので、基本的な部分でちゃんと追いつければ、そこからはなんとかなるかなぁ?という印象。</p> <p><a href="https://ollama.ai/blog/building-llm-powered-web-apps">Building LLM-Powered Web Apps with Client-Side Technology · Ollama Blog</a></p> <h2 id="結局よく分かっていないこと">結局よく分かっていないこと</h2> <ul> <li>llama.cpp でローカルで LLM を動かすことができるよ、って記事が多いんだけど llama.cpp は Llama の runtime って書かれてて、なのに他のモデルも使えるとか、何を言ってるんだ?って感じ。ビルド後の llama.cpp のディレクトリを見るとかなり雑多なツールがごちゃ混ぜに含まれているのは分かる <ul> <li>Ollama はこれに対していい具合の API を提供しているということか?</li> </ul> </li> <li>LLM のファイルフォーマット、データフォーマットとかランタイムって何?状態だったんだけど、ChatGPT さんによると Pytorch とかのフレームワークでの利用が前提で、基本的にはこうしたフレームワークから使うようになっているが、HuggingFace Transformers はそれはそれでいい具合に揃ってるのでアレコレ考える必要がない? HuggingFace Transformer っていう runtime があるならなんとなく理解はできるが、LLM を動かすことしかしてないのでそこもよく分からず</li> </ul> <h2 id="参考">参考</h2> <ul> <li><a href="https://zenn.dev/turing_motors/articles/f5f19f875bd8ba">llama.cpp の動かし方と量子化手法</a></li> <li><a href="https://note.com/bakushu/n/n1badaf7a91a0">【ローカルLLM】llama.cppの量子化バリエーションを整理する|Baku</a> <ul> <li>q4_0 は legacy らしい</li> </ul> </li> <li><a href="https://zenn.dev/derbuihan/articles/e1b60c180ee938">llama.cppでHuggingFaceにあるモデルを自分で量子化して動かす方法</a> <ul> <li>デカイまま真正面から取り組むのすごい大変そう</li> </ul> </li> <li><a href="https://tech.preferred.jp/ja/blog/prompt-tuning/">日本語LLMベンチマークと自動プロンプトエンジニアリング - Preferred Networks Research & Development</a></li> </ul> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1" role="doc-endnote"> <p>量子化はされているが、各種バージョンが取り揃えられていて合計100GB <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>もしかしたら amd64 だったらうまく動くのかもしれないけど Ollama が arm64 で動いているので、そこがズレるのがとりあえずイヤだった <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p> </li> </ol> </div>wtnabe