TECH PLAY

株匏䌚瀟゚ブリヌ

株匏䌚瀟゚ブリヌ の技術ブログ

å…š430ä»¶

こんにちは。開発本郚のデヌタ&AIチヌムでマネヌゞャヌ兌デヌタサむ゚ンティストをしおいる䌊藀です。 この蚘事は every Tech Blog Advent Calendar 12日目の蚘事です。 今回は、先日発衚されたOpenAI Assistants APIを䜿っお分析甚のSQLの生成を詊しおみた取り組みに぀いお玹介したす。 分析甚SQLの生成 最近1幎ほどでLLMが泚目されるようになり、ChatGPTをはじめLLMを䜿っおより効率的に凊理できる䜜業が増えおきおいたす。 自分自身も、瀟内のChatAppにコヌディングを手䌝っおもらったり、ちょっずした文章の敎圢や知識の深掘りに掻甚しおいたす。 䞭でも、デヌタサむ゚ンティストずしおLLMで効率化したい䜜業の䞀぀にデヌタ分析、特にSQLの䜜成があるず思いたす。 LLMによっおSQLが生成できるようになるず、デヌタサむ゚ンティストをはじめ分析者がより効率的に分析できるようになるのはもちろん、 普段SQLを曞かない瀟員が分析する際の助けにもなりたす。 SQLの生成自䜓は珟圚のLLMでも可胜ではありたすが、䞖の䞭にリリヌスされおいるLLMは自瀟独自のデヌタ構造等のドメむン知識は孊習しおいないため、 瀟内での分析利甚を目指すのであれば、そういったドメむン知識をいかに扱うかが重芁ずなりたす。 盎近 Amazon Q Generative SQLのプレビュヌ版が発衚されるなど、ドメむン知識を掻甚したSQL生成はLLM掻甚の䞭でも関心の高い領域ずなっおいそうです。 今回玹介する取り組みは、11月の䞭旬に開催された挑戊weekで実斜したもので、 圓時リリヌスされたばかりだった OpenAI Assistants API を䜿った内容を玹介できればず思いたす。 OpenAI Assistants APIに぀いお OpenAI Assistants APIは、チャットボットのようなアプリケヌションにCode InterpreterやRetrieval、Function callingずいった"Tool"を 簡単に組み蟌める機胜を提䟛しおいたす。 https://platform.openai.com/docs/assistants/overview 詳しい䜿い方は公匏ドキュメントに委ねたすが、䞻芁な構成芁玠ずしお ドメむン知識などのデヌタが蚘録されおいるFile 入力に察しおFileを䜿った凊理を行うTool 入力に察するレスポンスずしお生成されるMessage 過去やり取りされたMessage矀を䞀連の䌚話ずしお保持しおいるThread FileやToolを利甚し、入力に察しおMessageを䜜成するAssistant があり、それらは AssistantにFile、Toolを玐づけるそれぞれ耇数指定可胜 Threadを立おおナヌザヌからMessageを入力する ThreadをAssistantに枡すず、LLMからのレスポンスがMessageずしおThreadに远加される ずいった関係性で動䜜したす。 Assistants API 抂念図 開発時には、Fileの䜜成やToolの遞定を適切に実斜するだけで、目的に応じたAssistantの䜜成が可胜です。 方針 今回䜜成するSQL生成アプリケヌション以䞋SQLゞェネレヌタヌず呌びたすは、瀟員が誰でも利甚できるようなツヌルを目指しおいたす。 珟状゚ブリヌのデヌタ基盀のうち、瀟員党員が利甚できる郚分ではRedashからTreasureData (Presto) を参照しおいるため、 TreasureData䞊で実行可胜なSQLの生成を目暙ずしたす。 芁件を敎理するず、 ナヌザヌから知りたい内容の質問を受け取り 質問内容を集蚈できるTreasureData (Presto) で動䜜するSQLを返す ような方針で蚭蚈を行いたした。 SQLゞェネレヌタヌ構成 たずToolの遞定ですが、入力に基づいおFileを参照し、必芁なドメむン知識を抜出する圹割が必芁なため、Retrievalを採甚したした。 本来、倖郚のドメむン知識を扱う際には、それらを適切な長さのチャンクに分割し、ベクトルデヌタベヌス等に栌玍しおおく必芁がありたすが、 Retrieval Toolではその蟺りをOpenAI偎で凊理しおいるため、深く考慮せずずも実装が可胜です。 ドメむン知識に関しおは、テヌブル情報を蚘茉した衚や特殊な関数の䜿い方をたずめたものをPDFファむルずしおアップロヌドしたした。 ナヌザヌの質問をAssistantに枡す郚分では、LLMがタスク内容を解釈しやすいように、入力するプロンプト内で ナヌザヌから芁求されおいる分析内容を集蚈できるSQLを䜜成する 利甚するデヌタ゜ヌスやカラム、独自関数を䜿った期間指定の方法等はファむルを参考にする TreasureDataでそのたた実行できるSQLを出力する ずいった内容を蚘述しおいたす。 実隓 たずは、䜕もドメむン知識を䞎えない状態で、瀟内ChatApp (GPT-4-turbo) にSQLを生成させおみたす。 SQL生成䟋1 圓然ですが、ドメむン知識を䜕も枡しおいないため、このたたではTreasureData䞊で実行できたせん *1 。 続いお、 SQLゞェネレヌタヌに同じ質問を投げかけおみたす。 SQL生成䟋2 今床は、怜玢デヌタのevent_searchテヌブルを参照した䞊で、適切なカラムのselect、search_typeやtag_idなどの条件指定、TreasureDataの独自関数 (TD_TIME_FORMAT、TD_TIME_RANGE) の䜿甚が適切で、 実際に䜿甚できるSQLずなっおいたす *2 。 実行䟋 実際の分析でよく利甚されるデモグラフィックデヌタずの突合も詊しおみたす。 SQL生成䟋3 怜玢デヌタのevent_searchに加えお幎代情報のあるuser_masterテヌブルずカラムを正しく指定できおおり、join操䜜含めお正しいSQLになっおいたす。 他にも、りむンドり関数を䜿った移動平均の集蚈なども察応でき、ドメむン知識を取り蟌んだSQLの生成が簡単に実装できたした。 ドメむン知識を適切に扱えないケヌス 前節ではうたく生成できた結果をピックアップしお玹介したしたが、もちろん間違ったSQLが出おくるこずも倚くありたす。 間違え方ずしおは、SQLの文法自䜓が誀っおいるケヌスはほずんどなく、䞻に独自関数の䜿い方に集䞭しおいたした。 特に、期間指定に甚いる独自関数の䞭でも、TD_TIME_RANGEよりもTD_INTERVALずいう関数の扱い方は間違っおいる割合が高く、 ファむルに蚘茉しおいるにも関わらず、入力パラメヌタや出力倀の型を間違っお認識しおいたした。 詊しに、ドメむン知識を䞎えない状態でTD_TIME_RANGE、TD_INTERVALそれぞれの意味を質問したずころ、以䞋のような回答が返っおきたした。 TreasureData独自関数に関する回答 回答では、TD_TIME_FORMATに぀いおは正しい内容が曞かれおたすが、TD_INTERVALは間違った回答Hallucinationになっおいたす *3 。 SQLゞェネレヌタヌにおいお、TD_INTERVALの䜿い方は必ず間違えるわけではなく、正解する堎面も芋られたため、 プロンプトや確率的な倉数など䜕かしらの芁玠が起因しおファむル䞭の知識よりもGPTが持っおいる知識䜓系にバむアスを受けおいる可胜性がありそうでした。 このようなLLM自䜓が持぀バむアスぞの察凊は、ファむル䞭のドメむン知識の曞き方やプロンプト゚ンゞニアリングなどで解消できる可胜性はあるため、今埌の課題ず考えおいたす。 今埌の展望 今回䜜ったSQLゞェネレヌタヌは、改善点は倚く残されおはいたすが、瀟内ChatAppの拡匵機胜ずしおベヌタ版を展開予定です。 今埌の取り組みずしおは、レスポンス速床や粟床の改善、SQLが間違っおいる堎合に修正しやすい仕組みの提䟛ずいった機胜的な改善はもちろん、 瀟員が普段芋おいるドキュメントをAssistantsのドメむン知識にどのように転換させるかずいった運甚的な改善も考えられたす。 今埌もLLMを䞭心ずした業務効率の改善に、チヌム党䜓ずしお取り組んでいければず思いたす。 *1 : 入力プロンプト内にドメむン知識を蚘述しお生成させる手段はありたすが、今回はナヌザヌから盎接ドメむン知識を䞎えない状況を想定しおいるため考えないものずしたす。 *2 : 具䜓的な倀にはがかしを入れおいたす *3 : 公匏ドキュメント https://docs.treasuredata.com/display/public/PD/Supported+Presto+and+TD+Functions#SupportedPrestoandTDFunctions-TD_INTERVAL
この蚘事は every Tech Blog Advent Calendar 2023 の11日目です。 こんにちは。トモニテ開発郚iOS担圓兌、開発組織掻性化委員䌚リヌダヌを勀めおいる國吉です。 今回ぱブリヌで初の詊みずなる開発郚党䜓むベント”挑戊week”を開催/運営しおみおの所感を曞こうず思いたす。 前段 開発郚では各事業郚毎にバック゚ンドチヌムやクラむアントチヌムが存圚しおいたす(䞀郚暪断するチヌムもありたす)。 このような組織䜓系だず、時間経過ず共に課題ずしお挙がっおくるのがチヌム暪断でのコミュニケヌションの取りづらさです。 この課題があり続ける限り、ナレッゞ共有ができず知らず知らずの間に同じような技術怜蚌をしおいるこずなどが生じおしたいたす。 それらを含めた問題を解決するために”組織掻性化委員䌚”が発足したした。 斜策 ”組織掻性化委員䌚”は名前の通り、組織を掻性化させチヌム間で生じおいるコミュニケヌション障壁をなくす斜策や他チヌムが取り組んでいる内容を把握するこずができる斜策に取り組んでいたす。 具䜓的に実斜しおいる斜策は䞋蚘です。 TechTalk 挑戊week アドベントカレンダヌ 簡単に挑戊week以倖の掻性化斜策にも觊れようず思いたす。 TechTalk TechTalk毎月1回実斜しおおり、内容ずしおは”AllHands”ず”LT”です。 ”AllHands”は前月に各チヌムが取り組んだ内容やその䞭でのトピックの共有を行い、”LT”は毎回2~3人ほどが技術に぀いお発衚しおくれおいたす。 TechTalk終了埌は、雑談する時間を蚭けおおりチヌムを暪断したコミュニケヌションを促進しおおりたす。 アドベントカレンダヌ これは絶賛取り組んでいるむベントです ブログを執筆し瀟倖ぞアりトプットする目暙は開発郚偎の目暙ずなりたすが、組織掻性化委員䌚ず協業しおむベント事ずしお盛り䞊げおいこうずしおいたす。 是非、こちらから他のブログも芋おいただけたらず思いたす tech.every.tv 挑戊week さお、本題の挑戊weekのお話に戻りたす。 ロヌドマップ たず、挑戊week含め各斜策の組織掻性化委員䌚のロヌドマップをざっくり共有したす。 図を芋お分かる通り、挑戊weekを実斜するだけで1幎間みっちり掻動しおおり、その䞭でTechTalkやアドベントカレンダヌを実斜しおいるので結構倧倉です。 組織掻性化委員䌚のメンバヌは異なるチヌムからメンバヌ構成がされおいるため、MTGは週に1時間〜2時間しかずれず各パヌト長めに期間を取らざる終えたせん。 実斜内容 続きたしお、内容に少し觊れおいきたす。 これたでで蚈3回開催しおいたすが、共通しおいるこずがありたす。それは”1週間事業郚の斜策やMTGには参加せず、集䞭しお挑戊weekに取り組む”こずです。 もちろんこれは各事業郚偎の皆さんにも協力しおもらい成り立っおいるこずです。1週間斜策を止めるこずはすごく倧きなこずなので、改めお感謝しないずいけたせんね。 開発郚のメンバヌが実際に取り組む内容に぀いおは、開発郚に所属しおいる人からやりたいこずを募り内容を決めたり、郚長陣から今埌に掻かせる内容を募り内容を決めたりず詊行錯誀しながら開催しおいたす。 取り組んだ内容は、ブログずしお執筆しおいる人がいるので気になる方は是非ご䞀読くださいおそらくアドベントカレンダヌの䞭にも挑戊weekで取り組んだこずに぀いお執筆する人いるず思いたす。 tech.every.tv tech.every.tv tech.every.tv 開催/運営しおみた所感 今回は挑戊weekの内容に぀いお深がるのではなく、開発郚党䜓を巻き蟌むむベントを実装に運甚しおみお僕個人が難しいず感じたずこや楜しいず感じたずころをアりトプットできたらず思いたす。 難しいポむント たず、゚ブリヌずしお党䜓むベントを実斜するのが初めおでノりハりもない䞭での実斜ずなるので、最初は䞀぀䞀぀党おが難しかったです。 その䞭でも継続しお運営しおいく䞊で特に難しいず感じるポむントは䞋蚘です。 取り組むお題(内容)の遞定 アンケヌト結果から改善内容の粟査 取り組むお題(内容)の遞定 挠然ずやっおみたい圢匏はあるが、詳现に萜ずし蟌む段階で”今埌事業郚に掻かせるのか””その粒床で皆をたずめるこずができるのか”に玍埗できず、時間だけが経過しおしたうこずが倚々ありたす。 第1回目は実斜実瞟がないので、たずは無難なずこから始めおみようずいうこずで開発郚のみんなが日々挑戊したいず思っおいるこずや改善したいず感じおいるずこから取り組むこずにしたした。 結果、日々の改善系が倚くなりたしたが第1回目ずしおは成功したず感じおいたす。 ただ、むベント名に”挑戊”ずいう単語が含たれおいるため䞀定の挑戊芁玠を盛り蟌みたいなず考えおおり、お題を決めるずこに関しおは匕き続き頭を抱えながらアンケヌト結果を振り返り぀぀怜蚎しおいたす。 アンケヌト結果から改善内容の粟査 毎回挑戊week実斜埌に、参加者に察しアンケヌトを実斜しおいたす。アンケヌト内容は満足床や䞍満点、改善点などです。 参加者が倚ければ倚いほど、様々な意芋がありどこたで吞収し、次に掻かしおいくかを毎回議論するのですが、それが難しいです。 極端なこずを蚀うず、「挑戊week自䜓やる意味ありたすか」ずいった意芋もありたす。少しでもやる意矩を提瀺できおないこずは”組織掻性化委員䌚”の課題であり改善点です。 たずは、皆が協力しお参加しおくれるようなむベントにできるよう改善を行うのが重芁なのかなず考えおいたす。 他にも時間がない䞭で、様々な郚分での意思決定が必芁ずなるため難しいず感じるポむントが倚々ありたす。 楜しいポむント 逆に楜しいず感じるポむントも少なからずありたす。楜しいず蚀うよりは”嬉しい”や”組織掻性化委員䌚やっお良かった”ず感じるポむントず蚀った方が適切かもしれたせん。 䞀番嬉しいこずは、挑戊week実斜埌のアンケヌトで「新しい技術ぞの挑戊ができ楜しかった」や「話したこずない人ずコミュニケヌションが取れ、䌚話の幅が広がった」「い぀も運営ありがずうございたす」等の意芋を蚀っおもらえるこずです。 ただただ䌚瀟の文化ずしお党瀟むベントは根付いおないので、詊行錯誀のフェヌズだず思いたすがその䞭でポゞティブな意芋がもらえるのはずおもありがたいこずです。 他には少なからず意思決定する堎面が倚く、むベント内容の仕様が決たっおいく様子や実際にむベントを開催するこずで達成感があり、同時にやりがいを感じたす。 最埌に ここたで所感などを曞いおきたしたが、総括するず開催実瞟がない状態で党䜓むベントを開催/運営に携われたのは良かったです。 意思決定や倧勢を巻き蟌むむベント実斜の難しさを䜓感できたのは、今埌に掻かせる経隓であるこずは間違いなく、開催に協力しおくれた組織掻性化委員䌚のメンバヌはもちろんのこず、開発郚に所属しおいる方や事業郚偎にもずおも感謝です。 ただただ文化ずしお定着させるには時間がかかるかもしれたせんが、将来的には他職皮を巻き蟌み圱響範囲を広げおいけたら、䌚瀟党䜓ずしおも盛り䞊がっおいくんじゃないかなず期埅しおいたす。 最埌に挑戊week最終日に取り組んだ内容を共有する堎を蚭けおいるのですが、その時の写真を茉せおおきたす。
抂芁 この蚘事は every Tech Blog Advent Calendar 2023 の10日目です。 TIMELINE開発郚の内原です。 本日はTIMELINE開発郚で利甚しおいるAWS RDSぞの螏み台サヌバの構成を、ECS Fargate+PortForward+Adhocな機構に倉曎した話を曞きたす。 䌌たような蚘事はいたるずころで芋かけるので䜕番煎じになるか分からない状況ですが、以前からやっおみたいず考えおいたものだったので個人的にはよかったです。 倉曎前の構成 倉曎前の螏み台サヌバの構成は以䞋のようなものでした。 EC2むンスタンス (Amazon Linux, t2.micro) SSHポヌト転送を利甚しおRDSに接続 SSH甚アカりントはgithubの公開鍵を手動で登録 䞊蚘構成を利甚しおSSHポヌト転送を行うには、以䞋のようなコマンドを実行したす。 $ ssh -L 3306:database.xxxxxxxxxx.ap-northeast-1.rds.amazonaws.com:3306 $user @ $db_proxy_host その埌MySQLクラむアントなどで 127.0.0.1:3306 に接続するこずで、RDSに接続できたす。 問題点 この構成には以䞋のような問題点がありたした。 EC2むンスタンス固定費が発生 t2.micro の料金は USD 0.0116/hour で、1ヶ月で 24 * 30 = 720 時間皌働するず USD 8.35 かかりたす 埮々たるものですが、䜿っおいないにも関わらず費甚が発生するのはもったいないです EC2むンスタンスメンテナンスが必芁 AMIの曎新やセキュリティ脆匱性など、必芁に応じお定期的にメンテナンスが必芁になりたす アカりントの管理が面倒 開発者が増える床に、手動でUnixナヌザ远加ず公開鍵の登録を行う必芁がありたす たた、退職者のアカりントの削陀も手動で行う必芁があり、忘れるずセキュリティ䞊問題があるため泚意が必芁です 察応策 以䞋のようなこずを実珟したいず考えたした。 EC2むンスタンスを廃止 ECS Fargateを利甚しお、必芁に応じおコンテナを起動する方針ずしたす。 これによりむンスタンスの管理が䞍芁ずなりたす。 SSHポヌト転送を廃止 ポヌト転送にはAWS SSM Session Managerを利甚するこずにしたす。 これによりSSHの管理も䞍芁ずなりたす。 アカりントの個別管理を廃止 ゚ブリヌではAWSのアカりント管理にAWS IAM Identity Center(旧AWS SSO)を利甚しおおり、認蚌にはGoogle Workspaceを甚いおいたす。 退職者はGoogle Workspaceにアクセスできなくなるため、自動的にAWSぞのアクセスも䞍可胜ずなり、アカりントの管理が䞍芁ずなりたす。 自動的に停止する機胜 せっかくECS Fargateに切り替えおも、垞時コンテナが起動しおいたのでは华っお費甚が割高になっおしたいたす。 このため、RDSぞの接続が䞍芁になったタむミングでコンテナが自動的に終了するようにしたいず考えたした。 たた安党のため、なんらかの理由でコンテナの自動終了ができなかった堎合でも、䞀定時間経過したら匷制的に終了するようにしおおきたいです。 なるべく軜くか぀安くしたい ECS Fargateのコンテナは、最䜎でもvCPU 0.25、メモリ 512MBのリ゜ヌスを必芁ずしたす。やりたいこずはポヌト転送だけなので、最䜎限のスペックにしおおきたす。 たた2024/02/01より、AWSではPublic IP Addressに察する課金が発生するこずになっおいたす。このため、Public IP Addressを割り圓おないように蚭定したす。 RDSはVPC内にあるため、private subnetにコンテナを配眮しおも問題なく接続できたす。 ずいうわけでできたした ECSタスク定矩 あらかじめ以䞋のようなタスク定矩を䜜成しおおきたす。 スペックは最䜎限、か぀䞀定時間が経過したらコンテナが自動的に終了する構成にしおおきたす。 { " taskDefinitionArn ": " arn:aws:ecs:ap-northeast-1:xxxxxxxxxxxx:task-definition/db-proxy:1 ", " containerDefinitions ": [ { " name ": " db-proxy ", " image ": " alpine:latest ", " cpu ": 0 , " portMappings ": [] , " essential ": true , " entryPoint ": [ " sh ", " -c " ] , " command ": [ " sleep $TIMEOUT_SEC " ] , " environment ": [ { " name ": " TIMEOUT_SEC ", " value ": " 3600 " } ] , " mountPoints ": [] , " volumesFrom ": [] } ] , " family ": " db-proxy ", " taskRoleArn ": " arn:aws:iam::xxxxxxxxxxxx:role/ecs-task-role ", " executionRoleArn ": " arn:aws:iam::xxxxxxxxxxxx:role/ecsTaskExecutionRole ", " networkMode ": " awsvpc ", " volumes ": [] , " requiresAttributes ": [ { " name ": " com.amazonaws.ecs.capability.task-iam-role " } , { " name ": " com.amazonaws.ecs.capability.docker-remote-api.1.18 " } , { " name ": " ecs.capability.task-eni " } ] , " placementConstraints ": [] , " compatibilities ": [ " EC2 ", " FARGATE " ] , " requiresCompatibilities ": [ " FARGATE " ] , " cpu ": " 256 ", " memory ": " 512 " } 起動スクリプト そしお、RDSに接続をする際には以䞋のようなスクリプトを甚いたす。 スクリプトではFargateコンテナの起動ずポヌト転送を行い、スクリプト終了時にはコンテナを停止したす。 スクリプトの終了はCtrl-Cで行うこずができたす。 #!/bin/bash set -e # タスクが終了するたで埅機する堎合は wait_stopped=1 を指定する wait_stopped = cluster = " YOUR_CLUSTER_NAME " remote_db_host = " YOUR_DB_HOST " remote_db_port = " 3306 " local_db_port = " 3306 " profile = " ${AWS_PROFILE :- default } " task_definition = " db-proxy:1 " subnets = " subnet-xxxxxxxxxxxxxxxxx,subnet-xxxxxxxxxxxxxxxxx,subnet-xxxxxxxxxxxxxxxxx " # private subnetsを指定 security_groups = " sg-xxxxxxxxxxxxxxxxx " running_task_arn = "" # Ctrl-C で終了した堎合にコンテナを停止する function shutdown() { if [[ -z $running_task_arn ]] ; then exit 0 fi aws \ --profile $profile \ ecs stop-task \ --cluster $cluster \ --task $running_task_arn > /dev/null if [[ -z $wait_stopped ]] ; then exit 0 fi aws \ --profile $profile \ ecs wait tasks-stopped \ --cluster $cluster \ --tasks $running_task_arn > /dev/null } trap shutdown 2 # タスク起動 running_task_arn = $( aws \ --profile $profile \ ecs run-task \ --cluster $cluster \ --enable-execute-command \ --task-definition $task_definition \ --launch-type FARGATE \ --network-configuration " awsvpcConfiguration={subnets=[ $subnets ],securityGroups=[ $security_groups ],assignPublicIp=DISABLED} " | jq -r ' .tasks[0].containers[0].taskArn ' ) # タスクが起動するたで埅機 aws \ --profile $profile \ ecs wait tasks-running \ --cluster $cluster \ --tasks $running_task_arn # runtimeId を取埗 runtime_id = $( aws \ --profile $profile \ ecs describe-tasks \ --cluster $cluster \ --tasks $running_task_arn | jq -r ' .tasks[0].containers[0].runtimeId ' ) task_id = $( echo " $running_task_arn " | cut -d ' / ' -f 3 ) target = " ecs: ${cluster} _ ${task_id} _ ${runtime_id} " # コンテナに察しポヌト転送を行う aws \ --profile $profile \ ssm start-session \ --target $target \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters ' {"host":[" ' $remote_db_host ' "],"portNumber":[" ' $remote_db_port ' "], "localPortNumber":[" ' $local_db_port ' "]} ' 以䞋のような出力が埗られたら、MySQLクラむアントなどで 127.0.0.1:3306 に接続するこずでRDSに接続できたす。 Starting session with SessionId: xxxxxxxxxxxxxxxxxxxxxxxxxxx-04631105c5c065f69 Port 3306 opened for sessionId xxxxxxxxxxxxxxxxxxxxxxxxxxx-04631105c5c065f69. Waiting for connections... その埌 Ctrl-C を抌すず、以䞋のような出力が埗られ、コンテナが終了したす。 Terminate signal received, exiting. Exiting session with sessionId: xxxxxxxxxxxxxxxxxxxxxxx-04631105c5c065f69. 仮にスクリプトを長時間攟眮したたたにしおいた堎合や、なんらか䞍枬の事態によりコンテナを正しく終了できなかった堎合でも、1時間経過するず自動的に終了したす。 たずめ 以䞊で、ECS Fargate+PortForward+Adhocな機構による螏み台サヌバの構成倉曎を行うこずができたした。 これにより、EC2むンスタンスの管理やSSHの管理、アカりントの管理が䞍芁ずなり、か぀コンテナの起動は必芁最䜎限に抑えられるため、費甚も削枛できるようになりたした。 セキュリティ向䞊やメンテナンスコストがなくなったのが個人的には䞀番のメリットでした。 以䞊、䜕番煎じか分からないRDS螏み台サヌバを䜜る話でした。
目次 はじめに range over int range over intずは range over intをGo Playgroundで觊っおみる range over intを甚いる利点 よくあるfor文が簡朔になる リヌディングコストが軜枛される range over func range over funcずは range over funcをGo Playgroundで觊っおみる func(func()bool) func(func(V)bool) func(func(K, V)bool) range over funcを甚いる利点 シンプルで暙準的なむテレヌタを実装できる おわりに Appendix: range over funcを理解するコツ はじめに 株匏䌚瀟゚ブリヌで DELISH KITCHEN 事業のバック゚ンド゚ンゞニアをしおいる、GopherのYuki @YukiBobier です。䞻に 広告サヌビス を担圓しおいたす。 前回の蚘事 では、Goにおけるヒヌプ䜿甚量改善手法に぀いおご玹介したした。今回は、 every Tech Blog Advent Calendar 2023 の9日目の蚘事ずしお、倉わりゆくGoの range に぀いお取り䞊げたす。 なお、ここで取り䞊げる内容は開発䞭のものずなりたすので、将来的に倉曎される可胜性があるこずを申し添えおおきたす。 range over int range over intずは 早い話、これが for i := 0 ; i < 5 ; i++ { fmt.Println(i) } こう曞けるようになるずいうこずです。 for i := range 5 { fmt.Println(i) } 2022幎10月25日にRuss Coxによっお埌述するrange over funcず合わせお ディスカッション が開始され、2023幎7月18日に プロポヌザル が出されたのち、同幎10月27日に Accepted ずなりたした。 たた、そこで合意された内容ずしお、range over intはGo 1.22に远加されるこずずなりたした埌述したすが、range over funcはGo 1.22ではGOEXPERIMENT入りにずどたりたす。 range over intをGo Playgroundで觊っおみる Goの開発ブランチにはrange over intがすでに実装されおいるので、さっそくGo Playgroundで”Go dev branch”を遞択しお実行しおみたしょう。 [Go Playgroundで実行する] package main import "fmt" func main() { for i := range 5 { fmt.Println(i) } } 次のような結果が埗られるはずです。 0 1 2 3 4 Program exited. range over intを甚いる利点 よくあるfor文が簡朔になる 私たちがrange over intを甚いる1぀目の利点ずしお、 for i := 0; i < N; i++ { のような、0からN-1たでカりントアップするよくあるfor文が簡朔になりたす。 特に、カりンタに関心がない堎合は前述の䟋よりもさらに簡朔になりたす。䟋えば、ベンチマヌクテストは䞋のようになりたす。 for range b.N { doSomething() } リヌディングコストが軜枛される 2぀目の利点ずしお、リヌディングコストが軜枛されたす。 通垞の3節からなるfor文は、それが0からN-1たでカりントアップするよくあるfor文なのか、それずも䟋えば1からNたでカりントアップするunusualなfor文なのか、その区別はちゃんず読たないず぀きたせん。逆に、どうせよくあるfor文だず思っお読み飛ばしおいるず、実はそうではなくおミスリヌドが生じるずいうこずもあるでしょう。 for i := 0 ; i < 5 ; i++ { fmt.Println(i) } // 👆よくあるfor文ずunusualなfor文はちゃんず読たないず区別できない👇 for i := 1 ; i <= 5 ; i++ { fmt.Println(i) } この点、よくあるfor文をrange over intに眮き換えるこずで、そうではないunusualなfor文が通垞の3節からなるfor文ずしお際立ちたす。぀たり、通垞のfor文に出䌚った時だけちゃんず読めばいいので、for文を読む負担が枛りたす。 for i := range 5 { fmt.Println(i) } // 👆range over intずunusualなfor文はちゃんず読たなくおも区別が぀く👇 for i := 1 ; i <= 5 ; i++ { fmt.Println(i) } range over func range over funcずは range がむテレヌタの暙準ずしお機胜するようになるずいうこずです。 ディスカッションでRuss Coxは次のように述べおいたす。 When you want to iterate over something, you first have to learn how the specific code you are calling handles iteration. This lack of uniformity hinders Go’s goal of making it easy to easy to move around in a large code base. People often mention as a strength that all Go code looks about the same. That’s simply not true for code with custom iteration. 珟圚のGoにはむテレヌタの暙準がなく、それぞれがそれぞれのアプロヌチをしおいたす。暙準ラむブラリ内でさえ、それぞれの方法でむテレヌションをハンドリングしおいたす。 䟋えば、 database/sql.Rows のむテレヌタは次のようになっおいたす。 for rows.Next() { var name string if err := rows.Scan(&name); err != nil { log.Fatal(err) } fmt.Println(name) } 䞀方で、 archive/tar.Reader.Next では次のようになっおいたす。 for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { log.Fatal(err) } fmt.Println(hdr.Name) } だいぶ違いたすね。 このような状況なので、コヌドを曞くにしろ読むにしろ、むテレヌタに぀いおは個々のハンドリング方法を知らなければなりたせん。同じような目的は同じようなコヌドで達成されるずいうGoの長所が、むテレヌタに関しおは発揮されおいないずいうこずになりたす。 このような状況を解決するべく、 range を拡匵する圢でむテレヌタの暙準化が図られるこずずなりたした。それがどのようなものであるかの詳现に぀いおは、コヌドをみるのが䞀番分かりやすいので次項に譲りたす。 なお、range over intずずもにAcceptedずなりたしたが、こちらはGo 1.22ではGOEXPERIMENT入りにずどたり、匕き続き詳现が怜蚎されるこずになりたした。環境倉数 GOEXPERIMENT に rangefunc を蚭定するこずで実隓的な䜿甚が可胜になりたす。 range over funcをGo Playgroundで觊っおみる こちらもGoの開発ブランチにすでに実装されおいるので、さっそくGo Playgroundで”Go dev branch”を遞択しお実行しおみたしょう。 なお、range over funcには次の3タむプがあるため、ひず぀ひず぀取り䞊げたす。 func(func()bool) func(func(V)bool) func(func(K, V)bool) ちなみに、プロポヌザルの冒頭ではそれぞれ bool を返すずされおいたすが、怜蚎を経た結果ずしおこの戻り倀は取り陀かれるず Discussion Summary / FAQ では述べられおおり、実際に開発ブランチではそのように実装されおいたす。 func(func()bool) たずは、ルヌプ倉数に倀を枡さないタむプです。 [Go Playgroundで実行する] // GOEXPERIMENT=rangefunc package main import "fmt" func rangeFive(yield func () bool ) { if !yield() { return } if !yield() { return } if !yield() { return } if !yield() { return } if !yield() { return } } func main() { for range rangeFive { fmt.Println( "Hello" ) } } 次のような結果が埗られるはずです。おそらく開発䞭のバグでGo vetが転けおいたすが、実行には成功しおいたす。 # [play] vet: ./prog.go:26:12: cannot range over rangeFive (value of type func(yield func() bool)) Go vet failed. Hello Hello Hello Hello Hello Program exited. func(func(V)bool) 次に、1぀のルヌプ倉数に倀を枡すタむプです。 [Go Playgroundで実行する] // GOEXPERIMENT=rangefunc package main import "fmt" func rangeFive(yield func ( string ) bool ) { if !yield( "H" ) { return } if !yield( "e" ) { return } if !yield( "l" ) { return } if !yield( "l" ) { return } if !yield( "o" ) { return } } func main() { for s := range rangeFive { fmt.Println(s) } } 次のような結果が埗られるはずです。 # [play] vet: ./prog.go:26:17: cannot range over rangeFive (value of type func(yield func(string) bool)) Go vet failed. H e l l o Program exited. func(func(K, V)bool) 最埌に、2぀のルヌプ倉数に倀を枡すタむプです。 [Go Playgroundで実行する] // GOEXPERIMENT=rangefunc package main import "fmt" func rangeFive(yield func ( string , int ) bool ) { if !yield( "H" , 4 ) { return } if !yield( "e" , 3 ) { return } if !yield( "l" , 2 ) { return } if !yield( "l" , 1 ) { return } if !yield( "o" , 0 ) { return } } func main() { for s, i := range rangeFive { fmt.Println(s, i) } } 次のような結果が埗られるはずです。 # [play] vet: ./prog.go:26:20: cannot range over rangeFive (value of type func(yield func(string, int) bool)) Go vet failed. H 4 e 3 l 2 l 1 o 0 Program exited. range over funcを甚いる利点 シンプルで暙準的なむテレヌタを実装できる 私たちがrange over funcを甚いる利点ずしおはやはり、 range を介したシンプルで暙準的なむテレヌタを実装できるこずです。 䟋えば、二分朚のむテレヌタを実装したいずしたす。珟圚のGoでは次のようになるでしょう。 [Go Playgroundで実行する] package main import "fmt" type binaryTreeNode struct { v int left *binaryTreeNode right *binaryTreeNode } func (btn *binaryTreeNode) getIterator() iterator { return iterator{ stack: []*binaryTreeNode{btn}, } } type iterator struct { stack []*binaryTreeNode } func (i *iterator) hasNext() bool { return len (i.stack) > 0 } func (i *iterator) getNext() binaryTreeNode { var btn *binaryTreeNode btn, i.stack = i.stack[ len (i.stack)- 1 ], i.stack[: len (i.stack)- 1 ] if btn.right != nil { i.stack = append (i.stack, btn.right) } if btn.left != nil { i.stack = append (i.stack, btn.left) } return *btn } func main() { bt := binaryTreeNode{ v: 1 , left: &binaryTreeNode{ v: 2 , left: &binaryTreeNode{ v: 3 , left: &binaryTreeNode{ v: 4 , left: nil , right: nil , }, right: nil , }, right: nil , }, right: &binaryTreeNode{ v: 5 , left: &binaryTreeNode{ v: 6 , left: nil , right: nil , }, right: &binaryTreeNode{ v: 7 , left: nil , right: nil , }, }, } iter := bt.getIterator() for iter.hasNext() { btn := iter.getNext() fmt.Println(btn.v) } } 気になるのは次の点です。 むテレヌションの状態を管理する必芁があるので、むテレヌタが別途必芁であり実装が耇雑である デザむンパタヌンIteratorに則ったむンタヌフェヌスにしおいるずはいえ、将来の読み手に䜿い方や意図が䌝わるか心配がある これをrange over funcで曞き盎すず、次のようになりたす。 [Go Playgroundで実行する] // GOEXPERIMENT=rangefunc package main import "fmt" type binaryTreeNode struct { v int left *binaryTreeNode right *binaryTreeNode } func (btn *binaryTreeNode) all(yield func (binaryTreeNode) bool ) { if btn == nil { return } if !yield(*btn) { return } btn.left.all(yield) btn.right.all(yield) } func main() { bt := binaryTreeNode{ v: 1 , left: &binaryTreeNode{ v: 2 , left: &binaryTreeNode{ v: 3 , left: &binaryTreeNode{ v: 4 , left: nil , right: nil , }, right: nil , }, right: nil , }, right: &binaryTreeNode{ v: 5 , left: &binaryTreeNode{ v: 6 , left: nil , right: nil , }, right: &binaryTreeNode{ v: 7 , left: nil , right: nil , }, }, } for btn := range bt.all { fmt.Println(btn.v) } } このバヌゞョンには次のような利点がありたす。 yield func(binaryTreeNode) bool を介しおforルヌプずむテレヌタをシヌケンシャルに行き来するこずから状態管理が䞍芁であるため、むテレヌタを別途蚭ける必芁がなく実装がシンプルである range を介したシンプルで暙準的なむンタヌフェヌスにより、䜿い方や意図が明らかである 将来的に、各皮ラむブラリが range を介したシンプルで暙準的なむテレヌタを実装するようになれば、利甚者ずしおもその恩恵を受けるこずができるでしょう。 おわりに Go 1.22がリリヌスされrange over intが䜿甚できるようになったら、既存のforルヌプをゎリゎリ曞き換えお可読性を高めおいきたいず思っおいたす。 たた、最終的にどのような圢に萜ち着くかはただ確定しおいないものの、もしrange over funcが正匏に远加されたら、既存の各皮ラむブラリに倧きな倉化を促すGenerics以来の機胜远加になるず思いたす。こういった倧きな倉曎にリリヌス前から觊れお慣れおおくこずができるのは、オヌプンに開発されおいるメリットだず感じたす。 いやあ、やっぱりGoっおいいですね。 Appendix: range over funcを理解するコツ この蚘事を曞くにあたっお、range over funcに぀いおきっちり調べたのですが、実は、range over funcに觊れたのは今回が初めおではありたせんでした。以前ずある勉匷䌚で登壇者が玹介しおいるのを聞いたのがファヌストコンタクトだったのですが、その時は仕組みを理解できたせんでした。 きっずrange over funcを理解するのが難しいずいうのは私に限ったこずではないず思うので、最埌におたけずしおこれを理解するコツを玹介したいず思いたす。 range over funcを理解するコツは、 これは被蚪問者偎に䞻導暩があるVisitorパタヌンだず思っおコヌドを読んでみるこず です。 デザむンパタヌンのVisitorは、被蚪問者クラスがVisitorクラスを受け入れ、それによりVisitorクラスが被蚪問者クラスの構造を巡りながら凊理を行うパタヌンです。 range over funcにおけるVisitorは、䞊の二分朚の䟋における yield です。この yield ずいうVisitorが匕数ずいう玄関から all メ゜ッドずいうむテレヌタに蚪問し、倀を受け取っおはルヌプに送り蟌んでいたす。 ただし、本家のVisitorパタヌンず異なるのが、range over funcにおけるVisitorは構造を巡る䞻導暩を持たないずいうこずです。次の倀、次の倀ず構造を巡る実装が曞かれおいるのは被蚪問者偎である all メ゜ッド偎です。蚀うなれば、蚪問者 yield は匕数ずいう玄関から入ったのち、䞻の all に手を匕かれお屋敷の䞭を案内され、次から次ぞずお土産を持たされるむメヌゞです。そしお、 all が枡すべきお土産が尜きるか、 yield がこれ以䞊はいらないず蚀うかしお break 蚪問が終了するずいうわけです。 なんずなくお分かりいただけたでしょうか 理解の助けになったならば幞いです。
はじめに この蚘事は every Tech Blog Advent Calendar 2023 の 8 日目です。 トモニテのiOSアプリは今幎、トモニテ劊嚠アプリの開発を期にSPMを甚いたマルチモゞュヌル構成に移行したした。 これらのアプリにはアカりント管理やデザむンシステムなど共通郚分が倚くありたす。たた䞀郚機胜は重耇しおいるため、コヌド共通化をしやすくするのが䞻目的でした。 この蚘事ではマルチモゞュヌル構成ぞの移行をどのように進めたかず結果に぀いお曞きたいず思いたす。 コヌド共通化の方針 以䞋の遞択肢がありたした。 A: コヌド共通化をしない B: 共通郚分を別リポゞトリに切り出し、アプリもそれぞれ別リポゞトリで運甚する C: 1プロゞェクト1リポゞトリで耇数アプリを開発する コヌド共通化にはデメリットもありたす。片方のアプリを修正した時もう䞀方のアプリにも圱響する可胜性があり、本来は必芁のなかった調査や動䜜確認が必芁になるかもしれたせん。しかし今回は共通郚分が倚く共通化のメリットの方が倧きいず刀断したためAは遞びたせんでした。 Bは、共通郚分がアプリ本䜓ず疎で、倉曎頻床が䜎ければ良い遞択肢だったず思いたすが、今回は遞びたせんでした。 モゞュヌル構成を決める モゞュヌル間の䟝存が埪環しないように関係を敎理し぀぀、分割方法を決めたす。 たず、共通郚分を以䞋のように、レむダヌに沿っお分割したした。 Utilities: 䟿利クラス、Extensionなど Network: 倖郚通信 Model: モデル Core: Feature間の画面遷移の仕組みなどを提䟛する 䞀方、機胜ごずに Feature モゞュヌル矀を䜜成したした。 Home: ホヌム Media: 蚘事や蚘事怜玢機胜など Childcare: 育児蚘録機胜 Babyfood: 離乳食機胜 
 これらの Feature モゞュヌルは共通モゞュヌル矀に䟝存したすが、各Feature が盞互に䟝存するこずは犁止しおいたす。 Featureモゞュヌル間の参照をせずに、Feature間の画面遷移を可胜にする実装に぀いおはこちらの蚘事を参考にさせおいただきたした。 メルペむのスケヌラビリティを支えるマルチモゞュヌル開発 この仕組みによっおFeatureモゞュヌルの独立性が保たれ、必芁なモゞュヌルだけをタヌゲットに組み蟌むこずができるようになりたした。 モゞュヌルを切り出す 方針に沿っおモゞュヌルを䜜成しおいきたした。他ぞの䟝存が少ない郚分から順番に進める必芁がありたす。Utilities -> Network -> Model -> Core -> 各Feature のような順序です。 Xcodeでプロゞェクト内にパッケヌゞを䜜成し、 Package.swift ファむルにパッケヌゞの定矩ず䟝存関係を蚘述したす。 import PackageDescription let package = Package( name : "Network" , platforms : [ .iOS ( .v14 ) ] , products : [ .library ( name: "Network" , targets: [ "Network" ]) ] , dependencies : [ .package ( url: "https://github.com/Moya/Moya.git" , .upToNextMajor ( from: "15.0.0" )) , .package ( path: "Utilities" ) ] , targets : [ .target ( name: "Network" , dependencies: [ "Moya", "Utilities" ] , resources: [ .copy ( "Stab" )] ) ] ) あずはファむルをパッケヌゞの䞭に移動し、倖郚から参照される宣蚀をpublicにするなど、アクセス修食子を修正したす。 移動したコヌドに本来あるべきでない䟝存があった堎合は、䟝存関係をなくすための修正が必芁になる堎合も倚々ありたす。 䜜業量がかなり倚くなりたすが、䞀床に終わらせる必芁はなくパッケヌゞ単䜍でリリヌスが可胜です。通垞の開発ず䞊行しお少しず぀進め、3ヶ月皋床かかりたした。 開発甚の minimumTarget マルチモゞュヌルの利点を掻かし、新芏機胜や新芏画面を䜜る堎合の開発効率を䞊げるために minimumTarget ずいうものを䜜りたした。 開発察象の Feature ず最䜎限の共通郚分だけをタヌゲットに組み蟌んで開発でき、以䞋のような利点がありたす。 ビルド時間短瞮 トモニテ本䜓ず比范しお、クリヌンビルド時間 141 秒 → 38 秒 シミュレヌタでのデバッグ開始が早い SwiftUIのプレビュヌを利甚可胜 本䜓ではプレビュヌが衚瀺されるたでの時間が非垞に長く実質利甚できなかったのが利甚可胜になりたす Xcode Cloud デバッグ甚の Firebase App Distribution 配垃ず App Store Connect ぞのサブミットを Xcode Cloud で自動化しおいお、リポゞトリに倉曎を加えるず二぀のアプリが配垃/サブミットされたす。 結果 これたで曞いおきたずおり開発効率を向䞊できたした。 耇数のアプリに同様の倉曎を加える堎合、重耇した開発をせずに枈む モゞュヌル間の䟝存関係が敎理され、コヌド倉曎の圱響範囲を把握しやすい パッケヌゞ内のファむルは xcodeproj ファむルで管理されなくなるので、 xcodeproj のコンフリクトがほが無くなる minimumTarget で時間短瞮 䞀方よくない点もありたした。 耇数のアプリぞの圱響を考慮しながら開発する必芁がある 本来開発察象ではないアプリぞの圱響を調査したり、動䜜確認が必芁になる可胜性がある しかし党䜓ずしおは利点が倧きく、移行した䟡倀があったず刀断しおいたす。 この蚘事がどなたかの参考になれば幞いです。
はじめに 目指す状態ず珟状のギャップを考える 足りない情報を芁求・敎理 コミュニケヌションツヌル、ドキュメンテヌションツヌルの重芁性 契玄䞊の匕き継ぎ時点を迎えおの䜜業 Git リポゞトリの移管 クラりドベンダヌ、ドメむンレゞストラなど各皮契玄の移管 AWS ドメむンレゞストラ 各皮管理者の認蚌情報の受領 匕き継ぎの埌にやったこず クラりドサヌビスの料金の傟向・コスト構造の確認 手動運甚の自動化 監芖機構の確認、远加 共同でのトラブル察応 おわりに はじめに ゚ブリヌで小売業界向き合いの開発を行っおいる @kosukeohmura です。 今幎、゚ブリヌでは ネットスヌパヌのシステムを株匏䌚瀟ベクトルワン様から匕き継ぎたした 。その裏で、私たちのチヌムでは知芋のないシステムを、自分たちで運甚・開発可胜な状態にするように様々なこずをやっおきたした。 ここでは every Tech Blog Advent Calendar 2023 の 7 日目の蚘事ずしお、システムを別の䌚瀟から匕き継いだ䞭で考えたこず・やっおきたこずを玹介したいず思いたす。今回匕き継いだシステムは具䜓的には Web アプリケヌションサヌバヌ、スマホアプリ、耇数のスマホアプリ向け API サヌバヌ、及びそれに付随するシステム非同期凊理・バッチ凊理基盀、ロヌドバランサヌなどです。 なお、私は匕き継ぎ䜜業の前段階デュヌデリゞェンス、倧枠のスケゞュヌル策定、契玄締結などが枈んで、さあ実際にシステムを匕き継ぐぞずいうずころでこのプロゞェクトのオヌナヌずなったので、実際のシステムの匕き継ぎ䜜業に絞っおお話したす。 目指す状態ず珟状のギャップを考える 私はシステムを他瀟から匕き継いだ経隓がなく担圓ずなった圓初䜕をしおいいかわかりたせんでした。䞡瀟の゜ヌスコヌドは GitHub で管理されおいたので、ずりあえず自瀟の開発メンバヌを 倖郚のコラボレヌタヌ ずしお招埅しおいただきたした。その埌゜ヌスコヌドをざっず眺めたしたが、今回匕き継いだネットスヌパヌのシステムはそれなりに倧きく、゜ヌスコヌドを読み続けおシステムを理解するのは筋が悪そうだずわかりたした。 そこで匕き継ぎのプロゞェクトを通しおどうなりたいかを考えだしたした。システムを匕き継いだらそれは自瀟のシステムずなりたす。ずいうこずは自瀟のメンバヌで運甚を行っおいくのはもちろん、障害が起こったら自分たちで埩旧し、プロダクトの詳现な仕様やバグぞの説明責任も基本的には自分たちが持぀こずになりたす。その圹割が果たせる状態ず珟状ずのギャップは䜕か、それを埋めおいくには䜕をすべきかを考えおいきたした。 足りない情報を芁求・敎理 䜕もわからないずいう状態を玐解いおいくず、圓たり前ではあるのですが自瀟のプロダクトなら圓然知っおいるような情報が欠劂しおいるこずに気づきたした。䟋えば次のようなものです。 システムの実珟するサヌビスずナヌスケヌス 党䜓的なシステムの構成、構成芁玠それぞれの関係 システムず呚蟺システム、倖郚のシステムずの関係 ネットワヌク、DNS アクセス制埡 各皮アカりント・認蚌情報 アプリケヌションコヌド 責務の分割のされ方 アプリケヌションコヌド・テストコヌド蚘述のルヌル・方針 デヌタベヌスの構造 システムの監芖方法、正垞性の把握の方法、異垞時の察凊方法 セキュアな情報の取り扱い方 各皮運甚フロヌ QA リリヌス 手動の䜜業 etc... これらをリスト化し、それぞれに぀いお情報を芁求・敎理しおいきたした。知りたい項目に぀いおドキュメントが無いこずもありたすし、ドキュメントが存圚しおも断片的、あるいは前提知識を必芁ずしたりしたす。䞍足する情報はドキュメントを甚意しおもらったり、断片的な情報は受け取った埌に情報をたずめお包括的に構成したす。 この時匷く思ったこずは、匕き継ぎは匕き継ぐ偎ず匕き継がれる偎が協力しお行うプロゞェクトであるずいうこずでした。匕き継がれる偎ずしおはただ情報を埅っおいるのではなく、どんな情報がなぜ必芁で、どのようなアりトプットを期埅しおいるのかをなるべく明確に䌝える必芁があるず感じ、䞀぀䞀぀の項目ごずにどういう状態になれば匕き継ぎが完了ずなるのかの合意を取っおいきたした。 コミュニケヌションツヌル、ドキュメンテヌションツヌルの重芁性 前述の通り、匕き継ぎは自瀟・他瀟含めた協力プロゞェクトであり、その䞭では倚くのやり取りや資料の䜜成が行われたす。瀟内ならば既定のツヌルを䜿甚すればいいですが、瀟倖の方ずのやり取りでは既定のツヌルずいうものがありたせん。しかしフロヌ情報ずストック情報を蚘入できるツヌルの導入は非垞に重芁です。 私達は共甚のコミュニケヌションツヌルずしお Slack や Zoom を、ドキュメンテヌションツヌルずしお Notion を、タスク管理に Google スプレッドシヌトを䜿甚したした。いずれのツヌルも䞀方が他方の普段䜿いのツヌルに盞乗りする圢を取っおいたす。この堎合匕き継ぎプロゞェクトが終わればツヌルぞの盞乗りも終了するので、盞乗りしおいる偎は必芁な情報を゚クスポヌトするこずになりたす。 契玄䞊の匕き継ぎ時点を迎えおの䜜業 匕き継ぎのプロゞェクトは契玄䞊の匕き継ぎ時点より数ヶ月前から始めたしたが、実際の移管䜜業に぀いおは契玄䞊の匕き継ぎ時点の近蟺で行いたす。䟋えば次のようなこずです。 Git リポゞトリ移管 クラりドベンダヌ、ドメむンレゞストラなど各皮契玄の移管 各皮管理者の認蚌情報の受領 それぞれに぀いお軜く觊れたす。 Git リポゞトリの移管 GitHub の リポゞトリの移譲 䜜業を行いたした。ドキュメントに曞いおあるこずではあるのですが、組織間のリポゞトリの移譲には 移譲前のリポゞトリに察する管理者暩限(たたは移譲前の組織の管理者暩限たたはオヌナヌ暩限) 移譲先の組織のリポゞトリを䜜成する暩限 が必芁でした。倖郚コラボレヌタヌずしお移譲前のリポゞトリに招埅しおもらっおいる匕き継ぎ元の開発者アカりントをリポゞトリの管理者にしおいただき、そのアカりントにお移譲䜜業を行いたした。 リポゞトリ移譲埌は GitHub 匕き継ぎ埌に゜ヌスコヌドの内容を質問させおいただくこずを考え、逆に匕き継ぎ元の開発メンバヌを倖郚のコラボレヌタヌずしお招埅させおいただきたした。 クラりドベンダヌ、ドメむンレゞストラなど各皮契玄の移管 システムの皌働や運営に必芁な契玄の付け替えを行いたす。契玄しおいるサヌビスによっお移管の方法は様々でした。ここでは AWS アカりントずドメむンレゞストラの移管の方法に觊れたす。 AWS AWS Organizations のメンバヌアカりントを他の組織ぞ移行する_ Part 1 _ Amazon Web Services ブログ の蚘事を参考に、メンバヌアカりントの移管䜜業を行いたした。 今回は䞡瀟ずもに組織アカりントを䜿甚しおおり、たた移管察象のメンバヌアカりントに移管察象倖のシステムが含たれおいなかったこずから、単にメンバヌアカりントを組織アカりントぞ移行するのみで枈みたした。匕き継ぎ察象倖のリ゜ヌスが存圚するなどで AWS アカりントごずの移管が難しい堎合には、前もっお蚈画的にリ゜ヌスを別の AWS アカりントに移すなどし、AWS アカりント移管を行える状態を䜜っおおくこずが必芁です。たたアカりントごずの移管が珟実的ではない堎合は、リ゜ヌス単䜍で移管を行うこずになるず思いたす。 现かい話ですが、AWS アカりントの移行ではその時間を指定するようなこずができず、移管䜜業を行ったタむミングで移管が行われるようです。出来ればずある日時以降の料金の請求が゚ブリヌに察しお行われるようにしたかったのですが、その方法は調べた限り無さそうでした。 ドメむンレゞストラ 匕き継ぎ元ではお名前.com が利甚されおおり、゚ブリヌでは別のレゞストラを䞻に䜿甚しおいたのですが、今回は匕き継ぐドメむンが十数個ず倚く、匕き継ぎ䜜業の楜さを優先しお゚ブリヌのお名前.com ぞずドメむンを移管したした。お名前.com には お名前 ID 付け替え ずいう機胜があり、比范的楜にドメむンを移管できたした。今埌必芁ずなれば瀟内でお名前.com から普段䜿いのレゞストラぞの移管も怜蚎したすが、優先床は䜎く眮いおいたす。 各皮管理者の認蚌情報の受領 各皮サヌバヌの root ナヌザヌの SSH 鍵や、デヌタベヌスの root アカりントの認蚌情報の共有を頂きたす。匕き継ぎ元の開発者によるアクセスが必芁なくなったら、認蚌情報を倉曎ができるず良いでしょう。 匕き継ぎの埌にやったこず ここたでで匕き継ぎ䜜業ずしおは終了ですが、匕き継いだたたの状態では瀟内の運甚のフロヌずの霟霬が開発時の戞惑いに繋がったり、改めおプロダクトの状態を自瀟芖点でみるず最適化すべき郚分が芋぀かりたす。そのそれぞれを自瀟の基準や文化に合わせお修正しおいくこずはアゞリティやサヌビス品質の維持・向䞊に繋がりたす。 なお、今回は匕き継ぎを受けた埌にも匕き継ぎ元の開発者の方々に䞀定期間はサポヌトをいただけるこずになっおいたす。契玄䞊の匕き継ぎ時点を過ぎ、必芁な情報を頂いた぀もりでも色々な情報が足りおいないこずに埌から気づく堎面が倚くありたしたので、契玄等が蚱す限り䞀定期間サポヌトを受けられる䜓制を぀くるこずが理想だず匷く感じたす。 次に具䜓的に匕き継ぎを受けた埌にやったこずをいく぀か玹介したす。 クラりドサヌビスの料金の傟向・コスト構造の確認 AWS Web コン゜ヌル内の Cost Explorer にお料金の傟向を簡単に把握し、過剰なリ゜ヌスがないかをざっずチェックしたした。実際に、ずあるマシンのむンスタンスタむプが䞍自然に倧きいこずに気づきその倉曎を行いたした。 これに぀いおは早くやるほどコストが節玄できるので、AWS アカりントの移管が終わり、Cost Explorer が閲芧できるようになった初日に実斜したした。結果ずしお月々 6 䞇円以䞊のコスト削枛に繋がりたした。 手動運甚の自動化 手動で行われおいた運甚に぀いお自動化出来そうなずころがいくらかあったので、運甚の背景を匕き継ぎ元の開発者に䌺い぀぀、無理なく出来そうな郚分に぀いおは自動化をすすめおいたす。 䞀芋自動化できそうでも出来ない理由があったりするので、サポヌトいただけるうちに背景を聞きたす。この䜜業では手動運甚が削枛できるだけではなく、システムに぀いおより深く知る機䌚にもなりたした。 監芖機構の確認、远加 匕き継ぎ前から行われおいるシステムの監芖に぀いお敎理し、瀟内の基準を鑑みお監芖しおおきたくなった点に぀いおは新たに仕組みを導入しおいたす。 合わせお既存の監芖機構の通知先を倉曎し、自瀟の開発チヌムで異垞に気付けるようにしたす。 こちらも䞍明点は匕き継ぎ元の開発者に協力をいただき解消しおいきたした。䜜業を通しお、システムの党䜓ぞの把握を匷める機䌚にもなりたした。 共同でのトラブル察応 移管埌しばらくしおちょっずしたトラブルが起こりたした。゜フトりェア゚ンゞニアにずっお、知らないシステムからの倧量のアラヌトほど絶望するものは無いかもしれたせん。 ここでも匕き継ぎ元の開発者にトラブルの抂芁を䌺いながら原因を特定し、スムヌズに察応するこずができたした。もし自瀟のメンバヌだけでの察応だったなら原因の特定や察凊方針策定に盞圓な時間がかかっおいたず思いたす。 䜙談ですが、このトラブルの原因ずなったバグは、システムの匕き継ぎ前から朜んでいたものが偶然匕き継ぎ盎埌に露芋したずいうものでした。心臓に悪いので、もう少し空気を読んで露芋するタむミングを遞んでほしいものです。 おわりに 「れロからはじめるシステム匕き継ぎ」ず題しお、䜕もわからないずころからシステムを匕き継いだプロゞェクトに぀いお玹介したした。こう曞いおみお思ったこずずしお、今回は他瀟からシステムを匕き継ぐ圢でしたが、たずえ瀟内であっおも異動があったり、新しくゞョむンされた方は䜕もわからないような状況に眮かれる事に気づきたした。システムのドキュメンテヌションやクラりド料金・監芖䜓制の芋盎しなんかは継続的に瀟内でも行っおいけるず良いなず思いたした。 こういったプロゞェクトに携わられる方はそう倚くないずは思い぀぀、どなたかの圹に立぀ず幞いです。お読みいただきありがずうございたした。
はじめに この蚘事は every Tech Blog Advent Calendar 2023 の 6 日目です。 今回は「DI toolkit samber/do の玹介」ず題したしお、 samber/do のざっくりずした玹介ず、今埌リリヌスされるであろう次期バヌゞョンでの倉曎点に぀いおたずめおいきたす。 samber/do ずは samber/do は Go で DI を実珟するモゞュヌルのひず぀です。 同様の領域では google/wire が䞀番メゞャヌでしょうか。 その次に uber-go/fx が䜿われおいる印象です。 google/wire は珟圚はメンテナンスモヌドで、継続的な開発は行われおいたせん。 uber-go/fx は reflection を利甚した高床な機胜を提䟛しおいるほか、単なるDIツヌルではなく、アプリケヌションフレヌムワヌクずしおの性栌も兌ね備えおいる点が特城です。 今回玹介する samber/do は reflection も code generation も甚いず、シンプルに䟝存性の解決のみに泚力しおいる点が特城的なモゞュヌルです。 samber/do の DI 以䞋、 公匏の Quick start より転茉です。 import ( "github.com/samber/do" ) func main() { injector := do.New() // provides CarService do.Provide(injector, NewCarService) // provides EngineService do.Provide(injector, NewEngineService) car := do.MustInvoke[*CarService](injector) car.Start() // prints "car starting" do.HealthCheck[EngineService](injector) // returns "engine broken" // injector.ShutdownOnSIGTERM() // will block until receiving sigterm signal injector.Shutdown() // prints "car stopped" } type EngineService interface {} func NewEngineService(i *do.Injector) (EngineService, error ) { return &engineServiceImplem{}, nil } type engineServiceImplem struct {} // [Optional] Implements do.Healthcheckable. func (c *engineServiceImplem) HealthCheck() error { return fmt.Errorf( "engine broken" ) } func NewCarService(i *do.Injector) (*CarService, error ) { engine := do.MustInvoke[EngineService](i) car := CarService{Engine: engine} return &car, nil } type CarService struct { Engine EngineService } func (c *CarService) Start() { println ( "car starting" ) } // [Optional] Implements do.Shutdownable. func (c *CarService) Shutdown() error { println ( "car stopped" ) return nil } 基本的に do.Provide[T any](*do.Injector, func(*do.Injector) (T, error)) で生成関数を登録し、 do.Invoke[T any](*do.Injector) で倀を取埗するずいう流れです。 uber-go/fx のように生成関数の匕数から reflection で自動的に解釈しお䟝存ツリヌを䜜っおくれるような仕組みはないため、利甚偎で samber/do に䟝存した生成関数を定矩する必芁がありたす。 samber/do のメリット、デメリット samber/do は非垞に小芏暡なモゞュヌルで、コア機胜は䟝存性を登録するこずず䟝存性を解決するこずのみです。 それゆえに取り回しが非垞にしやすく、䟋えばあるタむミングで䟝存性ツリヌを再構築したい実䟋ずしお、Config のホットリロヌドなど、ず蚀うようなこずも自分でアプリケヌションを䜜り蟌めば無理なく実珟可胜です。 しかし、samber/do に䟝存する生成関数を定矩しなければならない点はやや取り回しは悪いず感じるかもしれたせん。 たた、䟝存性ツリヌの構築はコンパむル時ではなく実行時に動的に行われるため、構築されたツリヌに瑕疵がないか登録が䞍十分で䟝存性の解決ができないは利甚者が担保する必芁がありたす。 私も実際にテストを曞いおいお、うっかり生成関数の登録を忘れおいお䟝存性解決の際に゚ラヌになるずいうこずが皀にありたした。 この蟺りをDIツヌル偎で担保しおほしいずいうニヌズが匷い堎合、google/wire や uber-go/fx の方が合っおいるかもしれたせん。 私が開発に関わっおいるプロゞェクトでは、今の所芏暡も小さいこずもあっおこのようなトラブルは発生しおいたせんが、いずれ向き合わなければならない問題だず認識しおおりたす。 近い将来に google/wire ず䌌たようなアプロヌチで samber/do 向けの䟝存ツリヌを静的に生成するモゞュヌルを䜜ろうかず考えおいるずころです。 samber/do@v2 samber/do は非垞に新しいモゞュヌルです。䞀般的に、新しいモゞュヌルを導入する堎合、それらがどのようにメンテナンスされおいるかを把握しおおくこずが重芁です。 ずころで私は最近 samber/do に次のメゞャヌバヌゞョン v2 の蚈画があるこずに気づきたした。 ただ蚈画段階ですが、いく぀かこれが欲しかったんだず蚀う機胜が盛り蟌たれる予定ですので、いく぀かピックアップしお玹介したす。 Scope v2 における目玉ずなる機胜です。 䟝存ツリヌを䞀぀のたずたりScopeずしお、Scope間のツリヌ構造を構築し、䟝存性解決の際にScopeツリヌを蟿りながら倀を取埗するこずができるようになりたす。 これだけ聞くずなんのこっちゃ、ず思うかもしれたせんが、Java/Spring Boot に慣れおいる方であれば @ApplicationScoped や @SessionScoped 、 @RequestScoped のように生存時間が異なるオブゞェクトを䞀぀の䟝存性ツリヌでたずめお管理できるようになるず蚀えばむメヌゞできるかもしれたせん。 Java/Spring Boot を觊っおいない人は党くわからない話で申し蚳ありたせん。実際の サンプルコヌド があるので、そちらを読むず良いかもしれたせん。 䟝存ツリヌの明確化 これたでは do.Injector のフィヌルドにサヌビスを単玔にmapで保持しおいたのですが、䟝存ツリヌをDAG有向非巡回グラフずしお明確に保持するように倉わりたす。 これによっお実行時にサヌビス間の䟝存関係を取埗できるようになるため、DI郚分で䜕かトラブルがあった際に問題を特定するこずが 容易になりたす 。 埪環参照の怜出 䟝存性解決の際に埪環参照を怜出し、゚ラヌにするこずができるようになりたす。 Transient services 䟝存性解決のたびに毎回生成関数を呌び出しお新たなオブゞェクトを生成するサヌビスを登録するこずができるようになりたす。 珟時点では以䞋のように匕数なしの関数をサヌビスずしお登録し、䟝存性解決埌に自分で関数を呌び出しおオブゞェクトを取埗する工倫が必芁です。 import ( "time" "github.com/samber/do" ) func main() { injector := do.New() do.ProvideNamed(injector, "nowFunc" , func (_ *do.Injector) ( func () time.Time, error ) { return func () time.Time { return time.Now() }, nil }) nowFunc := do.MustInvokeNamed[ func () time.Time](injector, "nowFunc" ) now := nowFunc() println (now.Format(time.RFC3339)) } tag ベヌスでの䟝存性泚入の自動化 これ はただ v2 に入るかは䞍透明ですが、生成関数を毎回自分で曞くのはそれなりに面倒なため、uber-go/fx のように reflection を䜿っお自動的に䟝存性泚入を行うヘルパヌ関数が提䟛される予定です。 おわりに 今回は samber/do ずいう Go で DI を実珟するモゞュヌルに぀いお玹介をしたした。 Go は他の蚀語に比べお DI が採甚されるケヌスが少ない印象です。もちろんDIは銀の匟䞞でも、唯䞀の答えでもなく、採甚するかどうかはプロゞェクトごずに刀断が必芁です。 あくたで個人的な意芋ですが、今回玹介した samber/do は比范的 Go の思想に寄り添った圢で無理なく DI を導入できるバランスのいいモゞュヌルだず思いたす。 今回の蚘事がみなさんの参考になれば幞いです
こんにちは、トモニテ開発郚の Android ゚ンゞニアです。 この蚘事は every Tech Blog Advent Calendar 2023 の 5 日目です。 最近、 Android ゚ンゞニアに新たなメンバが増えたした。 こんなこずもあろうかず䜜っおおいた莈り物ずしおドキュメントがありたすので、どんなものか玹介したす。 どんなドキュメントなのか GitHub にあるプロダクトのリポゞトリの Wiki に、 アプリの構成 開発の前に 開発時の Q&A File Templates をたずめたものです。 アプリがどんな䜜りなのか 開発するにあたり守るべきこずはあるか xxx をやりたいずきはどうすればよいか を知り、今埌の開発䜜業での詰たりポむントを枛らしおいきたい、ずいう思いから䜜成したした。 ドキュメントの内容 1: アプリの構成 自分自身、たずはアプリの歩き方を知りたくなるので、アプリの地図たる構成をたずめたした。 アプリはマルチモゞュヌルで䜜成しおいたため、どこにどんなモゞュヌルがあるのかを列挙しおいたす。 䞀郚抜粋するずこんな感じです。 presentation: プレれンテヌション局 ┣━ common: プレれンテヌション局で䜿う䟿利な凊理眮き堎 ┣━ feature: ナヌザ向けの機胜眮き堎 ┃ ┣━ home: ホヌム ┃ ┣━ article: 蚘事詳现 ここでは他にも、モゞュヌル間の䟝存関係や各モゞュヌルの圹割も蚘茉し、党䜓像を把握しおもらえるようにしおいたす。 ドキュメントの内容 2: 開発の前に 開発䜜業を進めるにあたり基本的な情報ずなるものをたずめおいたす。 内容は、 ブランチの呜名ルヌル アプリ起動のルヌト(通垞のアプリ起動、Push 通知による Notification 経由、Scheme 起動)ごずの゚ントリポむントの明蚘 ラむブラリは Version catalog でたずめおいるこず 埌述の File Templates を䜿うず楜になる実装があるこず を蚘茉しおいたす。 これからの開発で意識しおもらいたいルヌルや、芚えおおいおもらえるず圹にた぀ものたちです。 ドキュメントの内容 3: 開発時の Q&A 珟状の蚭蚈に察しお「こうするずやりたいこずが実珟できたす」ずいった情報をたずめおいたす。 䞀䟋ずしおは、 新機胜を䜜りたい 各レむダヌで実装するガワを䜜成したり DI モゞュヌルぞの登録ずいった流れを蚘茉 他画面ぞ遷移したい feature モゞュヌル間で䟝存を持たないようにしおいるため、遷移を行うための router に぀いお蚘茉 などがありたす。 機胜開発やメンテナンスを容易に行えるようにずいう目的での蚘茉です。 ドキュメントの内容 4: File Templates AndroidStudio の Settings にある File and Code Templetes で䜿っおいるテンプレヌトを蚘茉しおいたす。 党瀟的な AndroidStudio の Settings を゚クスポヌトしたファむルは別途管理されおいるのですが、こちらはプロダクト固有なので Wiki 䞭に蚘茉したした。 テンプレヌトの内容ずしおは、プレれンテヌション局、ドメむン局、デヌタ局で䞻に実装するこずになるむンタヌフェヌスやクラスのガワを出力するずいうものです。 実装しおほしい箇所を todo ずしお出力しおいるので、そこを埋めるだけで他ず同様の䜜りにできたす。 これにより、実装箇所のガむドに沿っお䜜業を進めるだけで自然ず同じようなメンテナンスをするだけで OK ずなる䜜りを広げるこずができたす。 運甚しおみおどうだったか ドキュメントを読んだ新メンバに感想を聞いたずころ、こちらの期埅した通りの圹立ち方をしおいたようでした。 わかっおる人が曞いた資料なのでメモを取るよりも正確であり、か぀それを自身の確認するペヌスで読み蟌める点が良かったずのこずです。 最埌に ドキュメントの内容を现かく蚘茉しおいくずその時の情報ずしおは正しく、あるず嬉しいものなのですが、内容のメンテナンスを怠るず嘘぀きの曞になっおしたいたす。 定期的に曎新タむミングを蚭けおいき、新たなメンバが早めに真䟡を発揮できる環境を維持しおいきたいです。
Playwrightを掻甚したE2Eテストの導入 はじめに 想定読者 ハンズオンの前提条件 この蚘事で埗られるもの 実行環境 Playwrightを掻甚したE2Eテストの導入 Playwrightずは Playwrightの特城的な機胜 Test generator UI Mode Watch mode 定矩したアクションごずのスナップショット ハンズオン Next.jsをセットアップする Playwrightをセットアップする Next.jsのサンプルアプリケヌションを起動する テストコヌドの䜜成 テストの実行 GitHub Actionsでテストを実行する playwrightの蚭定ファむルを倉曎する テストの結果を確認する 最埌に Playwrightを掻甚したE2Eテストの導入 はじめに この蚘事は、every Tech Blog Advent Calendar 2023 の4日目の蚘事です。 tech.every.tv はじめたしお。 株匏䌚瀟゚ブリヌ DELISH KITCHEN 開発本郚の矜銬( @NaokiHaba )ず申したす。 今回は、簡単なハンズオンを通しお、Playwrightの基本的な䜿い方を玹介しおいきたす。 実装した゜ヌスコヌドは 以䞋のレポゞトリで公開しおいたすので、興味のある方はご芧ください。 github.com 想定読者 この蚘事では、以䞋のような方を想定しおいたす。 playwrightを觊っおみたい方。 E2Eテストを導入したい方。 ハンズオンの前提条件 この蚘事を読む前に、以䞋の準備をお願いしたす。 Node.jsのセットアップ お枈みでない方は、 こちら を参考にNode.jsをむンストヌルしおください。 GitHubアカりントの䜜成 GitHub のアカりントをお持ちでない方は、 こちら からアカりントを䜜成しおください。 GitHubリポゞトリの䜜成 お枈みでない方は、 こちら を参考に任意のリポゞトリを䜜成しおください。 この蚘事で埗られるもの この蚘事を読むこずで、Playwrightを䜿ったE2Eテストの導入ができるようになるこずを目指したす。 実行環境 Next.js v14.0.3 playwright v1.40.1 Mac OS Sonoma v14.1.1 Playwrightを掻甚したE2Eテストの導入 Playwrightずは github.com Microsoftが開発したE2Eテストツヌル Chromium、WebKit、Firefoxを含むすべおの最新のレンダリング゚ンゞンをサポヌトしおいるNode.jsベヌスのラむブラリ PuppeteerずPlaywrightはほずんど同じチヌムによっお開発されおいる 以䞋のブログでPuppeteerずPlaywrightの比范がたずめられおいたすので、興味がある方はご芧ください。 blog.logrocket.com Playwrightの特城的な機胜 Test generator codegenコマンドを䜿甚しおテストゞェネレヌタを実行し、その埌にテストを生成したいりェブサむトのURLを入力したす。 URLなしでコマンドを実行し、代わりにブラりザりィンドりに盎接URLを远加するこずもできたす。 $ pnpm exec playwright codegen demo.playwright.dev/todomvc この画面で任意の操䜜を行うず、テストコヌドが自動的に生成されたす。 䜿甚しおみた感想ずしおは、テストコヌドを曞いたこずがない方でも、この機胜を䜿えばテストコヌドを自動生成できるので、テストコヌドを曞くハヌドルが䞋がるのではないかず思いたす。 UI Mode Playwright v1.32.0 から、UIモヌドが远加されたした。 UIモヌドは テストを実行したり、デバッグするための機胜を提䟛しおいたす。 $ pnpm exec playwright test --ui 起動に成功するず、以䞋のような画面が衚瀺されたす。 ここからは、䜿っおみおこの機胜が䟿利だず感じた点を玹介しおいきたす。 Watch mode テストコヌドの倉曎を怜知しお、自動的にテストを実行しおくれたす。 テストコヌドを修正しお、実行結果を確認するずいう䜜業を繰り返す際に䟿利です。 定矩したアクションごずのスナップショット テストコヌドを実行するず、定矩したアクションごずにスナップショットが䜜成されたす。 どのタむミングでテストが倱敗したのか・どのような操䜜を行ったのかなどを確認する際に䟿利です。 他にも、䟿利な機胜が倚数ありたすので、 詳しくは、 公匏ドキュメント を参考にしおください。 ハンズオン ここからは、PlaywrightをNext.jsに導入しおE2Eテストを実装しおいきたす。 あくたで、䞀䟋ずしおNext.jsを利甚しおいたすが、その他のフレヌムワヌクでも同様の手順で導入できるず思いたす。 Next.jsをセットアップする Next.jsのセットアップ方法は、 こちら を参考にしおください。 ここでは詳现な手順は割愛したすが、 今回は ~/Documents に Next.jsをむンストヌルしおいたす。 # 任意のディレクトリに移動しおください $ cd ~/Documents # プロゞェクト名は任意のものを指定しおください # ここでは、playwright-next-app-sample ずいうプロゞェクト名で䜜成しおいたす $ npx create-next-app@latest $ cd playwright-next-app-sample # pnpm を利甚しおいたすが、npm や yarn・bunでも問題ありたせん。お奜きなものを利甚しおください $ pnpm dev http://localhost:3000/ にアクセスしお、以䞋のような画面が衚瀺されれば成功です。 Playwrightをセットアップする Playwrightをセットアップするには、以䞋のコマンドを実行したす。 詳しくは、 公匏ドキュメント を参考にしおください。 $ pnpm create playwright Choose between TypeScript or JavaScript (default is TypeScript) # TypeScript を遞択 Name of your Tests folder (default is tests or e2e if you already have a tests folder in your project) # 任意のフォルダ名を入力 (今回は tests を入力) Add a GitHub Actions workflow to easily run tests on CI Install Playwright browsers (default is true) # true を遞択 pnpm create playwright を実行するず、以䞋のようなディレクトリ構成が䜜成されたす。 - tests - tests-example - playwright.config.ts 最埌に、テストを実行しお以䞋のような結果が衚瀺されれば成功です。 $ pnpm exec playwright test ➜ playwright-next-app-sample git:(main) ✗ pnpm exec playwright test Running 6 tests using 5 workers 6 passed (4.1s) To open last HTML report run: pnpm exec playwright show-report 実行結果は、 playwright-report ディレクトリに保存されたす。 pnpm exec playwright show-report を実行するず、実行結果をブラりザで確認できたす。 $ pnpm exec playwright show-report Next.jsのサンプルアプリケヌションを起動する 今回は、Next.jsのサンプルアプリケヌションを利甚しおテストを実装しおいきたす。 テストコヌドを実装する前に、Next.jsのサンプルアプリケヌションを起動しおおいおください。 # Next.jsをロヌカルで起動 $ pnpm run dev テストコヌドの䜜成 tests/example.spec.ts に以䞋のテストコヌドを蚘述したす。 // example.spec.ts import { expect , test } from '@playwright/test' ; // テストコヌドの実行前にTOPペヌゞにアクセスする test .beforeEach( async ( {page} ) => { await page.goto( 'http://localhost:3000' ); } ); test ( 'Get started by editing src/app/page.tsx が衚瀺される' , async ( {page} ) => { await expect (page.getByRole( 'main' )).toContainText( 'Get started by editing src/app/page.tsx' ); } ) test ( 'Docペヌゞに遷移できる' , async ( {page} ) => { const page7Promise = page.waitForEvent( 'popup' ); await page.getByRole( 'link' , { name : 'Docs -> Find in-depth' } ).click(); const page7 = await page7Promise; await expect (page7.getByRole( 'heading' , { name : 'Introduction' } )).toBeVisible(); } ) テストの実行 UIモヌドを利甚しおテストを実行しおいきたす。 $ pnpm exec playwright test --ui テストの実行結果は、以䞋のようになりたす。 コマンドラむン䞊でテストの実行結果を確認するこずもできたす。 $ pnpm exec playwright test Running 6 tests using 5 workers 6 passed (4.2s) To open last HTML report run: npx playwright show-report GitHub Actionsでテストを実行する ここからは、オマケずしお GitHub Actionsを利甚したワヌクフロヌを実装しおいきたす。 Playwright はセットアップ時に、GitHub Actionsの蚭定ファむルを自動で䜜成しおくれたす。 今回は、そのたた利甚しおテストを実行しおいきたすが、必芁に応じおカスタマむズしおください。 name: Playwright Tests on: push: branches: [ main, master ] pull_request: branches: [ main, master ] jobs: test: timeout-minutes: 60 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: 18 - name: Install dependencies run: npm install -g pnpm && pnpm install - name: Install Playwright Browsers run: pnpm exec playwright install --with-deps - name: Run Playwright tests run: pnpm exec playwright test - uses: actions/upload-artifact@v3 if: always() with: name: playwright-report path: playwright-report/ retention-days: 30 playwrightの蚭定ファむルを倉曎する playwright.config.ts を線集しお、テストの実行前にロヌカルサヌバヌを起動するようにしたす。 耇数環境で切り替えたい堎合は、環境倉数を利甚しお切り替えるこずもできたす。 // playwright.config.ts export default defineConfig( { /* Run your local dev server before starting the tests */ webServer : { command : 'pnpm run dev' , url : 'http://127.0.0.1:3000' , reuseExistingServer : ! process .env.CI } } ); テストの結果を確認する それでは、ここたでの差分をコミットしお、GitHubにプッシュしおください。 GitHub Actionsでテストが実行されおいるこずを確認するこずができたす。 最埌に 以䞊で、Playwrightの基本的な䜿い方を玹介したした。 Playwrightは、Puppeteerず比范しおも遜色ない機胜を持っおいるので、今埌はPlaywrightを利甚しおE2Eテストを実装しおいきたいず思いたす。 たた、Vscodeに拡匵機胜が甚意されおいるので、VsCodeナヌザヌはぜひ利甚しおみおください。 この蚘事が、Playwrightを觊っおみたい方やE2Eテストを導入したい方の参考になれば幞いです。
every Tech Blog Advent Calendar 3日目の蚘事になりたす こんにちは 開発本郚デヌタ&AIチヌムでデヌタ゚ンゞニアを担圓しおいる塚田です。 今回は、挑戊WEEK䞭にGitHub Packagesを利甚したnpmパッケヌゞの瀟内利甚を行いたしたのでその内容に぀いおご玹介したす。 挑戊WEEKずは 開発メンバヌが通垞の各事業郚のロヌドマップから離れ、技術的に䜕かに集䞭しお挑戊する1週間 ずしおおり、 匊瀟CTOの今井がTech Blogにお説明しおおりたすので、よろしければ䜵せおご芧ください。 tech.every.tv ゚ブリヌで利甚しおいる画像に぀いお ゚ブリヌでは DELISH KITCHEN 、 トモニテ 、 TIMELINE の぀のメディアを運営しおいたす。 どのメディアも画像を倚数利甚しお構成しおおり、画像配信基盀はシステムを構成する重芁な䞀機胜ずなっおいたす。 画像配信基盀の問題点 それぞれのメディアで画像凊理 → 画像返华 → 配信ずいう凊理の流れは共通しおいたす。 ただし、画像凊理の郚分で画像倉換はほが共通しおいるのですが、倉換前凊理がそれぞれ独自実装されおいる状況でした。 改善方針 独自凊理が発生しおしたうのは避けられない郚分ではありたすが、画像倉換に関しおは統䞀できる凊理であるため共通化する方針ずなりたした。 前眮きが長くなりたしたが、画像倉換の共通凊理のみを行うnpmパッケヌゞずしお切り出しGitHub Packagesで瀟内利甚する流れを敎備したので、事䟋ずしお玹介したす。 前提 GitHub Organizationを利甚しおいる Organizationでなくおも実行可胜ですが、今回はOrganizationを利甚した堎合の説明ずなりたす 開発端末でNode.jsの実行環境が敎備されおいる 執筆時はNode.js 20.9.0を利甚しおいたす package.jsonの䜜成(初期化) それでは実際にGitHub Packagesに登録するパッケヌゞを䜜成したす。 npm init -y 埌述の手順で package.json の倉曎を行いたすのでデフォルトのたたで問題ありたせん。 S3からファむル名を取埗する凊理を実装 今回はサンプルずしお、 Amazon S3に保存しおいるファむルを取埗しデヌタ返华する を実装したす。 パッケヌゞのむンストヌル npm i -D typescript @types/node npm i -S @aws-sdk/client-s3 今回は䞊蚘のパッケヌゞをむンストヌルしたす。 甚途に合わせお他のパッケヌゞもむンストヌルしおください。 tsconfig.jsonの䜜成 npx tsc --init 䜜成された tsconfig.json を以䞋のように修正したす。 (自動的に䜜成されたものや今回の説明に必芁ないものは削陀しおいたす) { " compilerOptions ": { " target ": " es2022 ", " module ": " commonjs ", " rootDir ": " ./src ", " outDir ": " ./dist ", " esModuleInterop ": true , " forceConsistentCasingInFileNames ": true , " strict ": true , " skipLibCheck ": true } } S3ファむル情報の取埗ロゞック 以䞋のサンプルプログラムを src/index.ts ずしお䜜成したす。 import { GetObjectCommand , GetObjectCommandOutput , S3Client } from '@aws-sdk/client-s3' export async function getObject ( bucket: string , key: string ) { const params = { Bucket: bucket , Key: key } const command = new GetObjectCommand ( params ) const response = await new S3Client () .send ( command ) const str = await response.Body?.transformToString (); return str } package.jsonの倉曎 GitHub Packagesに登録できるよう package.json を以䞋のように倉曎したす。 今回は s3-file-getter ずいうパッケヌゞ名にしおいたす。 GitHubの環境に合わせお {Organization名} ず {リポゞトリ名} を倉曎しおください。 { " name ": " @{Organization名}/s3-file-getter ", " version ": " 0.1.0 ", " description ": "", " main ": " dist/index.js ", " files ": [ " dist " ] , " scripts ": { " prepublishOnly ": " npm run build ", " build ": " tsc -d " } , " author ": "", " license ": "", " repository ": { " type ": " git ", " url ": " https://github.com/{Organization名}/{リポゞトリ名}.git " } , " dependencies ": { " @aws-sdk/client-s3 ": " ^3.450.0 " } , " devDependencies ": { " @types/node ": " ^20.9.0 ", " typescript ": " ^5.2.2 " } } ここで重芁なのは name ず repository ずなっおおり、それ以倖は環境によっお適宜倉曎しおください。 GitHub Packagesぞ登録 GitHub ActionsワヌクフロヌYAMLファむルの䜜成 自動的にGitHub Packagesぞ登録させたいので、GitHub Actionsの蚭定を行いたす。 GitHubクむックスタヌト が甚意されおいるので、それを流甚し以䞋の蚭定を .github/workflows/publish.yml ずしお䜜成したす。 name : npm package publish on : release : types : [ created ] jobs : publish-gpr : runs-on : ubuntu-latest permissions : packages : write contents : read steps : - uses : actions/checkout@v4 - uses : actions/setup-node@v3 with : node-version : 20 registry-url : https://npm.pkg.github.com/ - run : npm ci - run : npm publish env : NODE_AUTH_TOKEN : ${{secrets.GITHUB_TOKEN}} リリヌスの䜜成 䜜成した publish.yml はリリヌスが新芏䜜成された際に実行される蚭定ずなっおいるため、GitHubでリリヌスを䜜成したす。 この時 package.jsonのversion が既にGitHub Packagesで登録されおいる堎合゚ラヌずなるため、導入するプロゞェクトのリリヌスフロヌによっお柔軟に倉曎しおください。 今回はリリヌス前にversionの曎新を行なっおいるものずしお運甚したす。 GitHub Packagesの確認 GitHub Actionsの凊理が完了するず以䞋のように察象リポゞトリTOPの右䞋のPackagesに今回䜜成したかったnpm packageが登録されおいたす。 今回䜜成した s3-file-getter のリンクを遞択するずパッケヌゞのバヌゞョンなどのサマリヌが確認できたす。 GitHub Packagesの利甚 登録だけしおも利甚できないず意味がないので利甚するために必芁な手順を蚘茉したす。 ロヌカルの堎合 Personal Access Token (Classic)を䜜成 GitHub Packagesぞの認蚌を行う ドキュメントが甚意されおいるので、これに埓いAccess Tokenを䜜成したす。 パッケヌゞの読み蟌みができれば良いので、今回Scopeは read:packages のみを遞択したす。 .npmrc の䜜成 以䞋の蚭定を .npmrc ずしお䜜成したす。 {Organization名} ず {䜜成したAccess Token} を環境に合わせお眮換しおください。 registry=https: //npm.pkg.github.com/{Organization名} //npm.pkg.github.com/:_authToken={䜜成したAccess Token} 泚意 .npmrc にAccess Tokenを盎曞きしおいるので、 .gitignore で管理察象倖にするなど誀っお公開される状況にならないよう防埡策を取るようにしおください。 GitHub Packagesからパッケヌゞのむンストヌル .npmrc の蚭定をしおいおもGitHub Packages以倖のパッケヌゞは今たで通りむンストヌル可胜です。 GitHubの環境に合わせお {Organization名} を倉曎しおください。 npm i -D typescript @types/node npm i -S @ { Organization名 } /s3-file-getter パッケヌゞの利甚 他のパッケヌゞず同様にimportするこずで利甚できたす。 import { getObject } from '@{Organization名}/s3-file-getter' ; const data = async () => { console .log (await getObject ( '{bucketName}' , '{key}' )) } data () GitHub Actionsの堎合 GitHub Packagesにアクセスできるリポゞトリを蚭定 パッケヌゞのサマリヌ画面に Package settings のリンクがあるので、クリックしたす。 パッケヌゞを利甚したいリポゞトリを以䞋の機胜から远加するこずで参照可胜な暩限を付䞎できたす。 GitHub ActionsのWorkFlowを蚭定する パッケヌゞを利甚するための蚭定のみ蚘茉しおいたす。 環境に合わせお適宜倉曎しおください。 jobs : sample : steps : - uses : actions/checkout@v4 - uses : actions/setup-node@v3 with : node-version : 20 registry-url : https://npm.pkg.github.com/ - run : npm ci env : NODE_AUTH_TOKEN : ${{secrets.GITHUB_TOKEN}} おわりに GitHub Packagesを利甚した、瀟内でのみ参照可胜なnpmパッケヌゞの䜜成方法ず利甚方法に぀いお䞀連の流れを玹介したした。 今回のサンプルは公開しおも問題ないものですが、共通の凊理をパッケヌゞにしお利甚したいけどロゞックは倖郚に公開したくないなどのシチュ゚ヌションで利甚できるず感じおいたす。 画像配信基盀の改善はデヌタ゚ンゞニアずしお関わるのはあたりないこずだず思うのですが、挑戊WEEKが良いきっかけずなりたした。 この埌のAdvent Calendarでも挑戊WEEKに関連する投皿を予定しおいたすので、ぜひチェックしおみおください。
はじめおのシステムメンテナンスをする君ぞ はじめに 䞻にむンフラ呚りや時折バック゚ンドでGoを曞いおいるyoshikenです。 この蚘事はevery Tech Blog Advent Calendar 2023の2日目の蚘事ずなりたす。 昚日の蚘事は「Next.js + Go + AWS API Gateway で WebSocket API を䜿っお API サヌバヌからフロント゚ンドに通知を送る」ずなりたす。 tech.every.tv tech.every.tv 察象読者局 むンフラレむダヌ(バック゚ンドも含)の知識/経隓が少ない゚ンゞニア ゚ンゞニアがどういう手順でメンテナンスをするのか知りたいPdM/PjM/PM 本蚘事の目的 システムメンテナンスに぀いお どのような流れで行うこずがあるのか䟋を瀺す 気を぀けるべき事柄を瀺す ドキュメンテヌションに必芁な項目の䟋を瀺す 以䞊を持っお、初めおメンテナンスを行う゚ンゞニアが1䟋を参考にメンテナンスに関するノりハりを埗お、0ベヌスではなく1べヌスでメンテナンスの"準備"を行えるようにするこずを目的ずしたす。 本蚘事の察象倖 システムメンテナンスの具䜓的な手順やコマンド 䜿甚ツヌルの説明や䜿い方 準備の準備 DBのストレヌゞ増蚭、サヌバヌOSのアップデヌト、キャッシュ増蚭など、システムメンテンスず䞀抂に蚀っおも、その目的や範囲は様々です。 そのため、予めその目的を明確にしおおくこずで、圱響範囲の把握や停止時間の正圓性などが明確になりたす。 たた、メンテナンスを䞀぀のプロゞェクトずしお責任者を立おたしょう。 ここでいう責任者は"メンテナンスに぀いお網矅的に把握し、進捗の管理や枉倖に立぀人"ずいう意味です。 実際にメンテナンスを行うオペレヌタヌは䞀人かもしれたせんが、事前準備では耇数人が関わるため責任の所圚が曖昧になるこずもありたす。特に倧芏暡であればあるほど、関わる人間が倚く、党䜓を把握するには耇数の人にヒアリングをしないずわからないずいうこずが起こりやすいので泚意が必芁です。 目的・圱響範囲・責任者が決たったら、メンテナンスを実斜する旚を関係各所ぞ連絡したしょう。 サヌビス/アプリケヌションに圱響があるメンテナンスの堎合は、この時点でのおおよその目安で良いので圱響する範囲ず時間を関係郚眲に䌝えおおきたしょう。 䟋. 20xx/xx/xx ~ 20xx/yy/yyの間で 倧䜓3時間前埌。 Android/iOS äž¡OSのモバむルアプリケヌションがメンテナンス状態で䜿甚䞍可になる。ただしwebはアクセス可胜。 そうするこずで、厳密な日時は決たらなくおも圱響範囲からメンテナンス実斜可胜な日時の割り出し、゚ンゞニアが想定する圱響範囲から"関係郚眲ずしおの圱響範囲"(連絡する䌁業やナヌザヌぞ通知手順)などを割り出すこずができたす。 埌述する手順曞の䜜成䞭に粟床がある皋床しがれた圱響時間が算出できたら、再床関係郚眲に連絡し、具䜓日時を確定させたしょう。 こういった"準備の準備"は盎接的なメンテナンスの成功に貢献する割合は少ないですが、円滑なメンテンスの実斜やその埌のメンテナンスを行う際の他郚眲ずの連携には倧きく貢献するので、メンテナンスの前には必ず行いたしょう。 メンテナンス手順曞 具䜓的な準備に取り掛かっお行きたしょう。 兎にも角にもメンテナンス手順曞を䜜成したす。 ずいっおもいきなりはかけないず思いたすので目安ずしお以䞋の項目を曞き出しおいきたしょう。 目的ず範囲 察象のシステムの構成(図) 手順に必芁なツヌル䞀芧/むンストヌル方法 実斜手順 ロヌルバック手順 メンテナンスの埌に必芁な䜜業 トラブルシュヌティング 参考資料 それぞれの項目ごずに䞊から"ロヌルバック手順"たで、どのような情報があるず望たしいか觊れおいきたす 目的ず範囲 前章でも觊れたしたが、メンテナンスの目的ず範囲を明確にするこずで、メンテナンスの成功基準(ゎヌル)や圱響範囲を明確にするこずができたす。 この郚分が明確ではないず、この埌に曞いおいく情報や手順が正しいのかどうなのかが䞍明瞭になりたす。 䟋えば、「デヌタベヌスをアップデヌトする」ずいう情報では "なんのデヌタベヌスが" "どのバヌゞョンたで" が䞍明瞭なので、「(デヌタベヌス名)をv1.2.3からv4.5.6にアップデヌトする」ずいうのが目的ずしおは奜たしいです。 たた、メンテナンスの範囲も明確にしおおきたしょう。 ずはいえ、いきなり最初から範囲がわかるわけでもないので、埌述するシステムの構成図などを曞きながら範囲を絞っおいきたしょう。 ではなぜ最初の節に玹介したかずいうず、 "ドキュメンテヌションずしお倚くの人が必芁ずする情報ほど前に曞く" ずいうのはドキュメンテヌションの基本䞭の基本です。これはメンテナンスに限った話ではないです。 今回の堎合は倚くの人は手順そのものではなく圱響する範囲が、関係する゚ンゞニアや埌䞖にドキュメンテヌションを読むであろう゚ンゞニアが䞀番に芋たいのはメンテナンスの前埌の状態ず予想されるので䞀番最初の節にに蚘茉しおいたす。 察象のシステムの構成(図) システムの構成(図)を曞く目的・必芁性ですが、よほど簡玠で単玔なシステムでない限りむンフラレむダヌはごちゃごちゃしおいたす。 「AWSのEC2でサヌビス立おおたす」ずいっおも、subnetやsecurity group・route table・AZなどなど、実際はさらにRDSやELB、Route53など耇雑に絡み合っおいたす。 たたむンフラレむダヌに限らずアプリケヌションレむダヌでも、昚今のマむクロサヌビス化やモノリシックなアプリケヌションでも、耇数のサヌビスが絡み合っおいるこずが倚く、暗黙知であったりロストテクノロゞヌずなっおいるこずが倚いです。 そういった状況でレビュヌをしたりリストアップをするず挏れが生じおしたうので、挏れの可胜性を最小限にするためシステムの構成(図)を曞くこずが望たしいです。 システム構成(図)ず衚珟しおいるのは、"可胜であれば図で衚珟するのは望たしいが、それに時間をあたりにも割くのが難しいのであれば他でも代甚可胜"ずいう意味合いで蚘述しおいたす。 倧事なのはメンテンスの察象ず関係あるネットワヌクや通信がどのように絡み合っおいるかを把握するこずです。 手順に必芁なツヌル䞀芧/むンストヌル方法 「メンテナンス手順に含たれおいるラむブラリやコマンドがむンストヌルされおいなかったので圓日慌おおむンストヌルした」ずいうのはあるあるなお笑い話なんですが、そういったむレギュラヌはない事に越したこずはないので事前に手を打っおおきたしょう。 むンストヌルする際は極力バヌゞョンによる挙動の違いをなくすためにバヌゞョンは指定したむンストヌル方法を蚘茉したしょう。 実斜手順 ここが䞀番のボリュヌムか぀倧事な郚分になるず思いたす。 手順に぀いおはオペレヌティングする人が圓日やりやすいを前提に、レビュヌする人が芋やすい圢も意識したしょう。 䞀䟋ずしお以䞋のフォヌマットを参考にしおみおください 具䜓䜜業をわかりやすく䞀蚀で 事前確認 䜜業を行う前にステヌタスを確認できるCLIコマンドず結果 [ ] 目芖の堎合はチェック欄があるず尚良し 䜜業 実際の䜜業を行うCLIコマンド [ ] GUIをポチポチする堎合はチェック欄がry 事埌確認 䜜業を行ったあずのステヌタスを(ry [ ] 目芖の堎合はチェック(ry 具䜓的な䟋を出すず、 route53のprivate hosted zoneのレコヌドを新しい゚ンドポむントに切り替え 事前確認 dig hoge.local 䜜業 cloudmapに登録されおいるむンスタンスのCNAME蚭定を6の手順で䜜った新クラスタヌの゚ンドポむントに倉曎 https://github.com/org/repo/blob/master/path/to/terraform/service-discovery.tf git checkout feat/update_nanntara__uwaaaa cd path/to/terraform // 差分確認 terraform plan // 反映 terraform apply 事埌確認 // login to bastion ssh user@humidaihost -i ~/.ssh/koukaikagi.pem // 向き先確認(手順6の゚ンドポむントを参照 dig hoge.local // ログむン redis-cli -h hoge.local -c // バヌゞョン確認 > INFO [ ] コン゜ヌル䞊のroute53で向き先が倉わっおいる [ ] privateDNSが新しい゚ンドポむントの向き先に倉わっおいる このようになりたす。 これらを䜜業工皋ごずに蚘茉したす。 非垞にボリュヌミヌになるので必芁な情報を簡朔に蚘茉したしょう。 ロヌルバック手順 「いちばん倧事なのは手順だ」ず䞀個前の節で曞いたかもしれたせんが、この節が䞀番倧事かもしれたせん。 メンテナンスには倱敗が぀きものです。こんだけ偉そうに曞いおたすが打率は7・8割です。 ですが、取り返しの付かない倱敗は ただ片手で数えられるぐらいしかしおいたせん。たぶん。 その倱敗に備えるためにいかなる状況でもメンテナンス前に戻せるような手順をいく぀か事前にリストアップしおおくこずが非垞に倧事です。 ロヌルバックを行うずいうこずは、䜕かしら倱敗をしおしたった埌です。粟神的に䞍安定になり、簡単な䜜業もオペレヌションミスしおしたう可胜性がぐっず䞊昇したす。ですので粒床は现かく、コマンドをコピペしお終わるぐらい詳现に曞きたしょう。 どうしおも郜合䞊非可逆的な䜜業でロヌルバックが䞍可胜な堎合は"䞊䜍の刀断者が刀断を䞋し、埩旧䜜業が開始されるたで被害が最小限に枈む方法"を蚘茉しおおくのが良いです。䟋えば砎壊的倉曎を含むバッチの停止やデヌタ曞き蟌みの停止などです。 手順を開発環境で詊そう 手順曞を䜜成しおく䞊でわからないこずや実際にどういった反応が返っおくるかわからない堎合も倚々あるず思いたす。そういったずきはぜひ開発環境で詊しおみたしょう。 䞊蚘以倖にも本番環境ず限りなく近い状態の環境を甚意し手順を再珟するこずで圓日にかかる時間の芋積もりの粟床も向䞊したす 手順曞のレビュヌ 手順が䞀通り出来䞊がったら知芋がありそうな人にレビュヌをもらいたしょう。 コヌドレビュヌず䞀緒で䜕回か埀埩をしおブラッシュアップしおいきたしょう。 たずめ メンテナンスの手順曞を䜜っおいくこずで自ずずメンテナンスに必芁な準備ができおくるず思いたす。 メンテナンスは事前に共有し、ドキュメンテヌションもしっかり曞けば、圓日はあずは手順曞を芋ながらコマンドを打぀だけです。 よく「メンテナンスやったこずないから怖い」ずいう話も聞きたすが、それは䞍明瞭なものに察しお抱く恐怖心であり、"メンテナンス"にたいしおではありたせん。手順曞をしっかり曞き、リハヌサルを重ねるこずで恐怖心が和らいでいきたす。正しく恐れおいきたしょう。 ここたで曞いおあれですが、圓日はきっず手順曞に曞いおない想定倖のトラブルが起こるこずが倚々あるでしょう。 そういった堎合に備えお日垞で発生するシステムのアラヌトに察しお積極的に察応しお行きたしょう。そういった堎で埗た経隓はきっずメンテンス䞭のトラブル察応ずしお生きおいきたす。 それでは皆さんよきメンテナンスラむフを
Next.js + Go + AWS API Gateway で WebSocket API を䜿っお API サヌバヌからフロント゚ンドに通知を送る はじめに こんにちは トモニテ でバック゚ンド呚りの開発を行っおいる rymiyamoto です。 ゚ブリヌずしお初の詊みずなる Tech Blog Advent Calendar 2023 の 日目の蚘事ずしお参加させおいただきたした。 毎日他の蚘事も公開されるので、ぜひチェックしおみおください tech.every.tv 今回ですが Next.js + Go + AWS API Gateway で WebSocket API を䜿っおみたのでその内容を玹介しおいきたす。 経緯 トモニテ では珟圚、バック゚ンドは Go でフロント゚ンドは React(Next.js) で開発を行っおいたす。 フロント゚ンドずバック゚ンドの通信は REST API で行っおいたしたが、゚ンドナヌザヌの行動に察しおダッシュボヌドを利甚しおいるナヌザヌに即時性のある通知機胜を実装する必芁が出おきたため、WebSocket API を䜿っおみるこずにしたした。 珟状 API サヌバヌは ECS 䞊で動いおおり、API サヌバヌ偎で WebSocket API を実装するのは少し手間がかかるため、AWS API Gateway で WebSocket API を実装するこずにしたした。 WebSocket API ずは ナヌザヌのブラりザヌずサヌバヌ間で察話的な通信セッションを開くこずができるものです。 サヌバヌにメッセヌゞを送信したり、応答をサヌバヌにポヌリングするこずなく、むベント駆動型のレスポンスを受信するこずができたす。 developer.mozilla.org 今回の実装 元々ある ECS 環境(API サヌバヌ・dashboard・web)から API Gateway で WebSocket API を利甚できるように各皮 Lambda を䜜成したした。 たた裏偎では Lambda から RDS ぞの接続を行いたいため、RDS Proxy を利甚しおいたす。 構成図 流れずしおは以䞋のようなフロヌです。 dashboard(FE)で WebSocket API を利甚するためのクラむアントを䜜成しおコネクション確立 web(FE)で API サヌバヌに察しおリク゚ストを送った際に、通知を行う Lambda を呌び出し API Gateway を通しお dashboard に通知が飛ぶ API Gateway の蚭定 ずもかく WebSocket API を利甚できるように API Gateway を䜜成したす。 API Gateway においお、どのリク゚ストに察しおどの操䜜を行うかを決定するルヌト匏を指定したす。 今回は特別に指定もいらないので $request.body.action ずしおおきたす。 WebSocket API を䜿うための API GateWay の䜜成 以降の郚分は特に指定がなければデフォルトのたた䜜成しおいきたす。 このずき、ルヌトに $connect ず $disconnect が远加されたすが、これらは接続ず切断時のルヌトずなりたす。 IAM Role の䜜成 実行甚の Lambda の Role API Gateway や SecretManager(RDS Proxy 呚りの機密情報の管理) を利甚するために Role を䜜成したす (以降 web-socket-lambda-role ずしたす) その時に必芁ずなるポリシヌは以䞋です。 { " Statement ": [ { " Action ": " secretsmanager:GetSecretValue ", " Effect ": " Allow ", " Resource ": " * ", " Sid ": " GetSecretValue " } , { " Action ": [ " ec2:DescribeNetworkInterfaces ", " ec2:DeleteNetworkInterface ", " ec2:CreateNetworkInterface " ] , " Effect ": " Allow ", " Resource ": " * ", " Sid ": " ManageNetworkInterface " } , { " Action ": [ " logs:PutLogEvents ", " logs:CreateLogStream ", " logs:CreateLogGroup " ] , " Effect ": " Allow ", " Resource ": " * ", " Sid ": " ManageLogGroup " } , { " Action ": " execute-api:* ", " Effect ": " Allow ", " Resource ": " * ", " Sid ": " ExecuteAPI " } ] , " Version ": " 2012-10-17 " } API サヌバヌから Lambda を呌び出すための Policy 远加 Lambda を呌び出すための Policy を API サヌバヌの Role に付䞎したす。 その時に必芁ずなるポリシヌは以䞋です。 ( websocket-notification が埌に䜜成される通知甚の Lambda の名前です) { " Statement ": [ { " Action ": " lambda:InvokeFunction ", " Effect ": " Allow ", " Resource ": " arn:aws:lambda:ap-northeast-1:111111111111:function:websocket-notification ", " Sid ": "" } ] , " Version ": " 2012-10-17 " } Lambda の蚭定 今回 WebSocket API を利甚するための Lambda は以䞋の 3 ぀ずなりたす。 connect: 接続時に API Gateway から呌び出される disconnect: 切断時に API Gateway から呌び出される notification: API サヌバヌから呌び出されお通知を行う connect やるこず API Gateway の $connect ルヌトをむベントトリガヌずしお蚭定する ク゚リにナヌザヌが特定できるような情報を枡しおおく WebSocket の接続 ID を取埗しお DB に曞き蟌みを行う 内郚の凊理のむメヌゞは以䞋です。 package main import ( "context" "log" "net/http" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) type Response events.APIGatewayProxyResponse func Handler(_ context.Context, request events.APIGatewayWebSocketProxyRequest) (Response, error ) { log.Println( "Begin WebSocket connect" ) log.Println( "ナヌザヌ特定" ) // リク゚ストのク゚リからナヌザヌを䞀意に特定できる情報を取埗したす // 今回はナヌザヌの識別ずなるトヌクンを取埗しおいたす(耇数タブを識別するため) token := request.QueryStringParameters[ "token" ] // 以䞋にトヌクンからナヌザヌIDを取埗 log.Println( "DSNの取埗開始" ) // 以䞋にDNS情報をsecret managerから取埗する凊理 log.Println( "DBの接続" ) // 以䞋にDBの接続凊理 log.Println( "コネクションIDの保存" ) // リク゚ストからコネクションIDがずれたす connectionID := request.RequestContext.ConnectionID // 以䞋にコネクションIDの保存凊理 log.Println( "End WebSocket connect" ) return Response{StatusCode: http.StatusOK}, nil } func main() { lambda.Start(Handler) } disconnect やるこず API Gateway の $disconnect ルヌトをむベントトリガヌずしお蚭定する WebSocket の接続 ID を取埗しお DB から削陀を行う 内郚の凊理のむメヌゞは以䞋です。 package main import ( "context" "log" "net/http" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" ) type Response events.APIGatewayProxyResponse func Handler(_ context.Context, request events.APIGatewayWebsocketProxyRequest) (Response, error ) { log.Println( "Begin WebSocket disconnect" ) log.Println( "DSNの取埗開始" ) // 以䞋にDNS情報をsecret managerから取埗する凊理 log.Println( "DBの接続" ) // 以䞋にDBの接続凊理 log.Println( "コネクションIDの削陀" ) // リク゚ストからコネクションIDがずれたす connectionID := request.RequestContext.ConnectionID // 以䞋にコネクションIDの削陀凊理 log.Println( "End WebSocket disconnect" ) return Response{StatusCode: http.StatusOK}, nil } func main() { lambda.Start(Handler) } notification やるこず 呌び出し時の payload には通知を行うナヌザヌの ID を含めおおく そのナヌザヌ ID からコネクション ID を取埗しお通知を行う 内郚の凊理のむメヌゞは以䞋です。 package main import ( "context" "fmt" "log" "net/http" "os" "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/apigatewaymanagementapi" ) type Response events.APIGatewayProxyResponse func sendMessage(ctx context.Context, endpoint, connectionID, message string ) error { cfg, err := config.LoadDefaultConfig(ctx) if err != nil { return err } client := apigatewaymanagementapi.NewFromConfig(cfg, func (o *apigatewaymanagementapi.Options) { o.BaseEndpoint = aws.String(endpoint) }) input := &apigatewaymanagementapi.PostToConnectionInput{ ConnectionId: aws.String(connectionID), Data: [] byte (message), } _, err = client.PostToConnection(ctx, input) return err } type Event struct { UserID uint64 `json:"user_id"` JsonString string `json:"json"` } func Handler(ctx context.Context, event Event) (Response, error ) { log.Println( "Begin WebSocket notification" ) log.Println( "DSNの取埗開始" ) // 以䞋にDNS情報をsecret managerから取埗する凊理 log.Println( "DBの接続" ) // 以䞋にDBの接続凊理 log.Println( "察象ナヌザヌのコネクションIDリスト(耇数端末や耇数タブの郜合䞊)取埗" ) // lambda呌び出し時のpayloadには特定させるためのナヌザヌID、枡したいメッセヌゞのjson入れおおきたす userID := event.UserID // 以䞋に察象ナヌザヌのコネクションIDのリスト取埗凊理 log.Println( "API Gatewayを経由しおWeb Socketのメッセヌゞを送信" ) endpoint := os.Getenv( "API_GATEWAY_ENDPOINT" ) for _, connectionID := range connectionIDs { err = sendMessage(ctx, endpoint, connectionID, event.JsonString) } if err != nil { return Response{StatusCode: http.StatusInternalServerError}, err } log.Println( "End WebSocket notification" ) return Response{StatusCode: http.StatusOK}, nil } func main() { lambda.Start(Handler) } API サヌバヌで通知 Lambda(notification)を呌び出し API サヌバヌ偎で以䞋の関数を䜜成し通知を行う Lambda を呌び出すようにしたす。 package aws import ( "context" "fmt" "os" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/lambda" ) func createClient() (*lambda.Client, error ) { cfg, err := config.LoadDefaultConfig(context.Background()) if err != nil { return nil , fmt.Errorf( "load default config on background context failed. err: %w" , err) } return lambda.NewFromConfig(cfg), nil } func Invoke(payload [] byte ) error { funcName := os.Getenv( "WEBSOCKET_NOTIFICATION_FUNCTION" ) client, err := createClient() if err != nil { return fmt.Errorf( "create lambda client failed. lambda function: %s, err: %w" , funcName, err) } input := &lambda.InvokeInput{ FunctionName: aws.String(funcName), Payload: payload, } _, err = client.Invoke(context.Background(), input) if err != nil { return fmt.Errorf( "call %s failed. err: %w" , funcName, err) } return nil } WebSocket API を䜿うためのクラむアントの䜜成 WebSocket API を利甚するためのクラむアントを䜜成したす。 フロント゚ンドは Next.js で䜜成しおいるので、useEffect でコネクション確立を行うようにしおいたす。 "use client" ; import { useEffect , useState } from "react" ; type ApplicationEvent = { message: string ; } ; export default function EventReceiver ( { token } : { token: string } ) { const [ applicationEvent , setApplicationEvent ] = useState < ApplicationEvent >(); // WebSocketのコネクションを匵る useEffect (() => { const connectWebSocket = () => { const webSocketURL = process .env.NEXT_PUBLIC_WEB_SOCKET_URL ; if ( ! webSocketURL ) { throw new Error ( "Web SocketのURLが蚭定されおいたせん。" ); } const ws = new WebSocket ( ` ${ webSocketURL } ?token= ${ token } ` ); ws.onmessage = ( event ) => { const e = JSON .parse ( event.data ); setApplicationEvent ( e ); } ; // 勝手に接続切れたずきの再接続 ws.onclose = () => { setTimeout ( connectWebSocket , 1000 ); } ; return ws ; } ; const ws = connectWebSocket (); return () => { ws.close (); } ; } , [ token ] ); if ( ! applicationEvent ) { return null ; } return < div > { e.message } < /div >; } (Next.js ver.13 想定のため、12 以䞋の堎合は "use client"; は䞍芁です。) たずめ WebSocket API を利甚しお API サヌバヌからフロント゚ンドに通知を送る方法を玹介したした。 自身で WebSocket API を実装するのは少し手間がかかりたすが、API Gateway を䜿うこずで簡単に実装するこずができたす。 今回は耇数ブラりザのケヌスを考慮しおナヌザヌにコネクションを玐付けるようにしたしたが、そうでない堎合は通知郚分にコネクション ID を枡すこずでもう少しスッキリかけたず思いたす。 ただクラむアント偎で WebSocket のコネクションを匵るずきに再接続凊理を曞く必芁があるので、プレヌンで曞くず少し面倒です。 WebSocket API は今回のような通知機胜だけでなく、リアルタむムなチャットなどにも利甚できるので、今埌も様々な堎面で利甚可胜です。 同じような実装しおみたい方の参考になれば幞いです。
こんにちは。2023/10/29~11/1に開催された統蚈・機械孊習系の孊䌚、 第26回情報論的孊習理論ワヌクショップ(IBIS2023) に、匊瀟デヌタサむ゚ンティストチヌムでオンラむン参加しおきたした。 チュヌトリアル や 䌁画セッション では、2023幎の開催ずいうこずもありやはり、昚今盛り䞊がりを芋せおいるLLMが倚くテヌマずしお取り䞊げられおいたした。 様々な研究や掻甚䟋がわかりやすくか぀倚様に玹介されおおり、オンラむンずいう圢ではあったものの、その勢いや盛り䞊がりを感じるこずが出来たした。 本蚘事ではその䞭から、気になった講挔をいく぀か玹介しおいきたす。 チュヌトリアル: 倧芏暡蚀語モデル掻甚技術の最前線 皲葉通将様電気通信倧孊 人工知胜先端研究センタヌ 資料: IBIS2023チュートリアル「大規模言語モデル活用技術の最前線」 - Speaker Deck この講挔では、LLMの効果的な「䜿い方」に焊点を圓お、LLMの性胜を最倧限に掻かすための様々な技術や研究が、具䜓的な粟床怜蚌事䟋も亀え぀぀玹介されおいたした。 数えたら優に10皮類以䞊ありたした 特に印象に残ったものを以䞋に抜粋したす。 Chain-of-ThoughtWei et al., 2022 LLMに問題を解かせる際、単に回答を出力させるだけでなく、回答に至る思考過皋も述べさせるように、䟋瀺などを亀えながらプロンプトを誘導する手法 応甚ずしお、同じ指瀺に察しお耇数のCoT生成結果を獲埗し、それらの結果の倚数決を取るアプロヌチもあるその分コストはかかる Let’s think step by stepKojima et al., 2022 プロンプト末尟に「Let’s think step by step.」 を付けるだけでも粟床に寄䞎するずいうテクニック 䟋瀺プロンプトの準備に手間がかかるChain-of-Thoughtの代替アプロヌチ Tree of ThoughtYao et al., 2023 耇数のプランをLLMに生成させ、LLM自身にそれらのプランを評䟡させる。そしお、高い評䟡のプランをもずにさらに次のプランを耇数生成を繰り返すアプロヌチ LLMが䞍埗意になりがちな先読みタスクや、探玢が重芁なタスクに有効。 感想 たず「Chain-of-Thoughtが䞊手くいく」のは、日頃プロンプトを詊行錯誀しながらGPTずお喋りする䞭で肌感ずしおなんずなく感じおいたこずではありたす。 しかし、こうした経隓則を研究、デヌタずしお裏付けた事䟋を知るこずで、よりそのノりハりを自分の䞭で䜓系化出来たず感じたす。 たた、Let’s think step by stepするだけでも良いプロンプトが狙える、Tree of Thoughtで先回りタスクの苊手さに察凊するなど、目から鱗なテクニックも知るこずが出来、より掻甚の幅が広がった気がしたす。 これらのテクニックを、匊瀟のLLM掻甚にも是非ずも還元しおいきたいず感じたした。 䟋えば、 ビゞネスサむドに向けたChatGPT講習資料※をアップデヌトする特にLet’s think step by step等の気軜に詊せそうなもの 瀟内AI ChatAppOpenAI APIのGPTをもずに瀟内Webツヌル化したもののテンプレヌトずしお、これらのテクニックを埋め蟌む 等々、日頃のビゞネス改善に寄䞎しそうなタネは倚く転がっおいるのではないかず感じたす。 ※参考蚘事 tech.every.tv 䌁画セッション: 倧芏暡蚀語モデルずVision-and-Language 西田光甫様NTT人間情報研究所 資料: ⼤規模⾔語モデルとVision-and-Language - Speaker Deck この講挔では、画像凊理ず自然蚀語凊理の融合領域に焊点が圓おられおいたした。 代衚的な基盀モデルGPT, CLIP等々の玹介ず共に、倧芏暡モデルが劂䜕にしお画像ず蚀語の情報を共に扱えるようになっおいったのか、その技術の発展や応甚䟋がずおもわかりやすく玹介されおいたした。 ここでは、特に興味深かったテヌマを2぀掘り䞋げたす。 Visual Instruction Tuning 画像×蚀語情報を扱える倧芏暡モデルLLaVA: Large Language-and-Vision Assistantが、「耇雑怪奇な画像のどこにおかしな点があるか」を的確に答える様子が玹介されおいたした。 「車の埌ろでアむロンをかけおいるのがおかしい」ずLLaVAが答える様子 Liu, H., Li, C., Wu, Q., & Lee, Y. J. (2023). Visual Instruction Tuning. そしお、この胜力を実珟する鍵ずなったのは、Instruction Tuningの手法を画像凊理タスクにも応甚したVisual Instruction TuningでしたLiu et al., 2023。 Instruction Tuningは、「モデルが人間の指瀺に埓い、未知のNLPタスクを柔軟に解く胜力」を埗られるように孊習させる手法ですWei et al., 2022。 これは、ChatGPTにおける指瀺プロンプト→文章生成をZero-Shotで行う流れに代衚されたす。 Visual Instruction Tuningでは、画像の情報を文字情報ex. 画像の内容を説明するキャプション、画像の䞭にある物䜓の座暙倀に倉換したす。 そしお、このようにしお䜜成した「画像の説明」ず、元の「画像特城ベクトル」の組み合わせをモデルに入力しお孊習させるこずにより、LLaVAは画像情報を解析する胜力を獲埗したす。 「画像に説明情報を付加しお孊習させる」ずいうシンプルな方法により、倧芏暡モデルがたるで芖芚を持぀かのように倚様なタスクに察応できるようになる様に奥深さを感じたした。 GPT-4Vの評䟡論文 2023幎9月にChatGPTの画像入力機胜ずしお登堎したばかりのGPT-4V(GPT-4 with Vision)の朜圚的可胜性を評䟡した論文Yang et al., 2023の䞀郚が玹介されおいたした。 ここではGPT-4Vが凊理できるタスクずしお、以䞋のような事䟋が瀺されおいたした。 指瀺されたプロンプトに応じお、画像の䞭にあるや印や泚釈などを読みに行き、そこにある情報を取埗&掻甚できる。 画像ずしお入力された論文を芁玄できる。数字や䞀文皋床の誀りはあるものの、それ以倖は抂ね正確な芁玄を実珟できる。 画像のどこに䜕があるかを物䜓怜出し瀺せる。しかしその䜍眮座暙はただただ誀差が倧きい。 画像䞭に瀺された泚釈が、䜕の物䜓を指すものか答える様子 Yang, Z., Li, L., Lin, K., Wang, J., Lin, C.-C., Liu, Z., & Wang, L. (2023). The Dawn of LMMs: Preliminary Explorations with GPT-4V(ision) (Version 2). これにより、珟状どの皋床耇雑なタスクを凊理できる可胜性があるかの勘所を掎んだり、逆にどのようなタスクが苊手で課題があるのかを把握したりするこずが出来たした。 䌁画セッション: テキストから実䞖界理解に向けお 栗田修平様理化孊研究所AIP 資料: テキストからの実世界理解に向けて - Speaker Deck 画像ず蚀語の察応付けを実䞖界ぞどう応甚しおいくのかの研究に関する講挔でした。 画像ず蚀語の察応付けには、画像キャプション生成ず画像質問応答が2倧タスクであるそうです。 これらよりも现かく画像䞭の物䜓ずテキストを察応付けられないかに぀いお、近幎のVision-and-Language領域の研究に぀いお玹介しおいたした。 特に参照衚珟理解ずいうテキスト衚珟で参照された物䜓の座暙Bounding Boxを掚枬する研究分野に぀いお詳しく述べられおいたした。 具䜓的な手法ずしおは、MDETRKamath et al., 2021ずGLIPv2Zhang et al., 2023などがあり、MDETRは名詞句に察応づいた画像䞭の物䜓を怜出するもので、GLIPv2は画像内郚の察照孊習MDETRず他の画像ずの察照孊習CLIPを組み合わせた手法だそうです。 参照衚珟理解の䟋 Plummer, B., Wang, L., Cervantes, C., Caicedo, j., Hockenmaier, J., & Lazebnik, S. (2016). Flickr30k Entities: Collecting Region-to-Phrase Correspondences for Richer Image-to-Sentence Models 講挔の埌半には、実䞖界ぞの応甚に぀いお述べられおいたした。 参照衚珟理解は、テキストで参照された物䜓を画像から探すものですが、これは暗黙的に画像内にその物䜓が存圚するこずを前提ずしおいるず述べられおいたした。 そのようなケヌスは確かに実䞖界だず限定的だなずいう印象を受け、課題感に぀いお玍埗できたした。 この課題解決に向けお、物䜓が存圚しないこずも考慮できるようになれば、参照物䜓が画像内に存圚するかの刀定機ずしおも動䜜するず述べられおおり、非垞に興味深いず思いたした。 将来的にロボットやドロヌンによる灜害救助などぞの応甚できれば、非垞に有甚な技術ずなりそうだなず感じたした。 䌁画セッション: 䜜業動画ず手順曞を察象ずしたマルチモヌダル理解 西村倪䞀様京郜倧孊倧孊院 情報孊研究科 (珟: LINEダフヌ株匏䌚瀟) 資料: 作業動画と手順書を対象としたマルチモーダル理解 - Speaker Deck 動画ず蚀語から孊習するVision-and-Language領域で、動画ずしおは䜜業動画を、蚀語ずしおは人手で䜜成した手順曞や音声曞き起こしを掻甚した研究に関する講挔でした。 この研究では、手順曞はノむズが少なく、音声曞き起こしはノむズが倚いずいう特性がありたす。 アノテヌションには時間区間のアノテヌションず区間ごずの文を付䞎するずいった手法が䜿われるため、手順曞だずデヌタ量が少なくなり、音声曞き起こしだず倧芏暡なデヌタを孊習させるこずができるそうです。 応甚研究ずしお、料理動画からレシピを生成する研究Nishimura et al., 2022や、初期状態ず最終状態から䞭間の動䜜を掚定するProcedure planningずいった研究Chang et al., 2020; Sun et al., 2022を玹介されおいたした。 匊瀟でも料理の手順を動画ずしお提䟛しおいるため、この講挔に倧倉興味を持ちたした。 料理動画からレシピの生成は、ビゞネス的芳点からどのように掻甚するのが良いのか、ずいう芖点で考えるず、匊瀟の料理動画はPGCProfessional Generated Contentのため、瀟内のコンテンツ䜜成チヌムの効率化が可胜かもしれたせん。 UGCUser Generated Content芳点では、ナヌザが動画を撮っおアップロヌドするだけでレシピを生成しお投皿できるずいったシステムが䜜れれば、YouTubeなどず棲み分けができ、新たな䟡倀を生み出せるではないかず感じたした。 たた、Procedure planningは料理の掻甚が困難ず述べられおいたしたが、匊瀟のような動画の画角が固定されおいお、手順をトリミング線集したような動画を䜿うこずができれば、うたく孊習できるのかずいう点に぀いおも気になりたした。 Procedure planningを実䞖界ぞ応甚した研究Planning Transformer (PlaTe)の抂芁図 Sun, J., Huang, D., Lu, B., Liu, Y., Zhou, B., & Garg, A. (2022). PlaTe: Visually-Grounded Planning with Transformers in Procedural Tasks. 最埌に 本蚘事では、「Vision and Languageの最前線」をテヌマずした䌁画セッションを倚く取り䞊げたした。 前述した通り、この領域は、匊瀟が提䟛する料理動画メディア『DELISH KITCHEN』ずも芪和性が高いず感じる郚分も倚々ありたした。 これたで、倧量に存圚しおいる料理動画&画像デヌタ原石を劂䜕にしお磚くか、ビゞネスの発展やプロダクトの成長に掻かしおいこうか頭を捻らせおいたこずがありたした。しかし、Vision and Language倧芏暡モデルの出珟により「芖芚的な料理過皋の情報ず、それに察応する付垯情報䟋えば料理手順説明テキストなどを䞞ごず芚えさせ、その文脈を倚様なタスクの解決に発展させる」アプロヌチが珟実味を垯びおきた印象を受けたす。 こうした技術の発展にアンテナを匵り぀぀、我々のプロダクトならではの掻路を芋぀け出しおいきたいず思いたす。
はじめに こんにちは、 retail HUB で Software Engineer をしおいるほんだです。 早いもので 2023 幎も残り䞀ヶ月皋床ずなりたした。12 月ずいえばみなさんお埅ちかねのクリスマスずいうこずで 12/1 から 12/25 を盛り䞊げるために゚ブリヌ初の Tech Blog Advent Calendar 2023 を開催したす 公開スケゞュヌル Tech Blog Advent Calendar 2023 の公開日ず公開内容に぀いおは䞋蚘を予定しおいたす。 アドベントカレンダヌ蚘事が投皿され次第リンクを曎新しおいく予定です。 最終日 12/25 は CTO の imakei が蚘事公開予定です 公開日 公開内容 2023/12/1 Next.js + Go + AWS API Gateway で Websocket API を䜿っおAPIサヌバヌからフロント゚ンドに通知を送る 2023/12/2 はじめおのシステムメンテナンスをする君ぞ 2023/12/3 GitHub Packages を利甚した npm パッケヌゞの瀟内利甚 2023/12/4 Playwrightを掻甚したE2Eテストの導入 2023/12/5 新たなチヌムメンバぞの莈り物 2023/12/6 DI toolkit samber/do の玹介 2023/12/7 れロからはじめるシステム匕き継ぎ 2023/12/8 SPMでマルチモゞュヌル/マルチタヌゲット開発 2023/12/9 Go 1.22で远加予定のrange over intず、GOEXPERIMENT入り予定のrange over funcを觊っおみる 2023/12/10 RDS螏み台サヌバをよく芋かけるECS Fargate+PortForward+Adhocな機構に倉曎する 2023/12/11 党瀟むベント"挑戊week"を実斜/運営した所感 2023/12/12 OpenAI Assistants APIを䜿っお分析甚SQLを生成しおみる 2023/12/13 Retrieval-Augmented Generationを䜿っおコヌドの解説を生成しおみる 2023/12/14 iOSでGraphQLを䜿っおみた 2023/12/15 トモニテの Amazon Aurora を MySQL 5.7 から 8.0 ぞアップグレヌドした話 2023/12/16 Android でのバヌコヌドリヌダヌ実装に぀いお 2023/12/17 View を゜フトりェアキヌボヌドに远埓させる 2023/12/18 トモニテ盞談宀におけるTwilioを甚いた電話の仕組み 2023/12/19 ネットスヌパヌアプリにおける GraphQL Mesh を利甚した Gateway Server に぀いお 2023/12/20 DELISH KITCHEN のレシピレコメンドの立ち䞊げずこれから Hygen で加速する Next.js App Router 開発 2023/12/21 実務に入る前に理解しおいたらもう少し開発速床を䞊げられたかなず思うこず monorepo環境でeslint flat configを導入しおみた 2023/12/22 microCMS × Next.js でのキャンペヌン LP 制䜜効率化 新卒1幎目Web系゚ンゞニアがChatGPTを利甚した瀟内ChatAppのテンプレヌト機胜の実装に挑戊した話 2023/12/23 IVSを甚いたラむブ配信 wearOS に぀いお 2023/12/24 分析に向けたログ蚭蚈の話 AndroidのonResumeの挙動を再珟したい 2023/12/25 2023幎の振り返りず2024幎に向けお 気になる蚘事があった方はぜひブックマヌクやシェアお願いしたす
はじめに DELISH KITCHEN 開発郚で小売向き合いの開発に携わっおいる野口です。䞻に Flutter でのアプリ開発を担圓しおいたす。 匊瀟では retail HUB ずいう小売向けのサヌビスを行っおいたす https://biz.delishkitchen.tv/retailhub 今回は匊瀟で開発しおいる retail HUB で䜜成しおいるアプリの構成に぀いお぀玹介し、 耇数クラむアントアプリの぀らみずそれぞれのメリット、デメリットを述べたいず思っおいたす。 耇数クラむアントのアプリ構成ずは 䞊蚘で玹介しおいる耇数クラむアントのアプリずは耇数のアプリを共通の゜ヌスコヌドで管理するものを想定しおいたす。 むメヌゞずしおは、A 瀟、B 瀟、C 瀟に提䟛する堎合、共通項目は䜿いたわせる構成、アプリ自䜓は異なるApp Store では別ので別アプリずしお管理できる構成を考えおいたす。たた、クラむアントごずに独自の機胜をカスタマむズするこずもありたす。 耇数クラむアントのアプリ構成の぀らみ 耇数クラむアントのアプリ構成には今回のブログのテヌマでもあるカスタマむズの぀らみがあり、かなり厄介なものになりたす。 䞻に぀らみポむントずしおは カスタマむズ芁玠が増えれば増えるほど管理が倧倉になる クラむアントによっおカスタマむズを出しわけないずいけない 契玄終了ずかになったら、䜿甚しない機胜を削陀するかずか になりたす。コヌドの管理が倧倉なんです。䞀郚のクラむアントではここは䜿うけどここは䜿わないずか。クラむアントが数十瀟ずかになったら倧倉になりそうです。 ずいうこずで、以降で぀の構成をざっくり解説しおメリット、デメリット話したす。 先に結論を話すず筆者的には Melos がいいかなず思っおたす。 匊瀟のアプリ構成䟋 make を䜿甚したアプリ構成 make を䜿甚したアプリ構成は共通のコヌドから各クラむアントに出し分けるむメヌゞです。 make で CHAIN ずいうクラむアントの識別子の倉数を指定するこずでクラむアントの出し分けを行いたす。 Makefile CHAIN := app_A .PHONY: run run: chain-switch flutter run .PHONY: chain-switch chain-switch: ./main $(CHAIN) make run を実行できたす。 クラむアントを出し分けるコマンドは make run CHAIN=app_B で CHAIN= の埌ろが識別子ずなりたす。 make run コマンドには以䞋が指定されおおり順番に実行されたす。実際に出し分けを行う凊理は chain-switch で行っおおり以降で解説したす。 chain-switch : 枡された CHAIN に応じお出し分けを行う flutter run : ビルドが実行されるコマンド chain-switch ではCHAINに応じお出し分ける凊理を行っおいたす。出し分ける内容はconfig.jsonファむルで蚭定したす。 config.jsonファむルでクラむアントごずにアプリ名、bundleId,カラヌなどその他他にもたくさんありたすが、省略しおたすの項目を蚭定したす。 config.json { " app_A ": { " chainCode ": " app_A ", " appName ": " APP A ", " bundleId ": " app_A.app ", " colors ": { " primaryMain ": " FF9800 " } } , " app_B ": { " chainCode ": " app_B ", " appName ": " APP B ", " bundleId ": " app_B.app ", " colors ": { " primaryMain ": " FF8800 " } } } 取埗したCHAINに応じおconfig.json の項目をmain内でshellを䜿っお出し分けおいたす。 main chain = $1 //chainの取埗 chain_config_path =config.json  chain_config = $( cat config.json | jq --exit-status ' . ' $chain) || handle_no_chain_error chain_code = $( echo $chain_config | jq -r ' .chainCode ' ) app_name = $( echo $chain_config | jq -r ' .appName ' ) bundle_id = $( echo $chain_config | jq -r ' .bundleId ' ) template_paths = ( ./ios/Flutter/Release.xcconfig.template ./lib/ui/theme/color.dart.template ) replace () { template_file = $( cat $1) template_file = $( echo " $template_file " | sed -e " s/###CHAIN###/ $chain /g " \ -e " s/###APP_NAME###/ $app_name /g " \ -e " s/###BUNDLE_ID###/ $bundle_id /g " \ -e " s/###COLORS_PRIMARY_MAIN###/ $colors_primary_main /g " \ echo " $template_file " > ${1 % .* } } for path in " ${template_paths[ @ ]} " do replace $path done 少し解説を行うず、 ここで chain ず config.json の key が䞀臎するものを取埗しおいたす。 chain_config = $( cat config.json | jq --exit-status ' . ' $chain) || handle_no_chain_error chain が app_A だず以䞋が取埗できる感じですね。 config.json { " chainCode ": " app_A ", " appName ": " APP A ", " bundleId ": " app_A.app ", " colors ": { " primaryMain ": " FF9800 " } } 続いお、䞊蚘で取埗した json を各項目を取埗しお倉数に栌玍したす。 main chain_code = $( echo $chain_config | jq -r ' .chainCode ' ) app_name = $( echo $chain_config | jq -r ' .appName ' ) bundle_id = $( echo $chain_config | jq -r ' .bundleId ' ) 出し分ける察象のファむルのパスを template ファむルを䜜成し定矩したす。 main template_paths = ( ./ios/Flutter/Release.xcconfig.template ./lib/ui/theme/color.dart.template ) ちなみに template ファむルはこんな感じです。 color.dart.template で###COLORS_PRIMARY_MAIN###郚分を倉数にしお切り替えられるようにしおいたす。 import 'package:flutter/material.dart' ; class AppColor { static const primaryMain = Color ( 0xFF ### COLORS_PRIMARY_MAIN ###); } 察象のファむルの倉数を眮換したす。 眮換埌の内容を、元のファむル名から .template 拡匵子を取り陀いた名前の新しいファむルに保存したす。 color.dart.template は color.dart のファむルが䜜成され、実際にはこちらが䜿甚されたす。 replace () { template_file = $( cat $1) template_file = $( echo " $template_file " | sed -e " s/###CHAIN###/ $chain /g " \ -e " s/###APP_NAME###/ $app_name /g " \ -e " s/###BUNDLE_ID###/ $bundle_id /g " \ -e " s/###COLORS_PRIMARY_MAIN###/ $colors_primary_main /g " ) //眮換 echo " $template_file " > ${1 % .* }  //ファむル䜜成 } for path in " ${template_paths[ @ ]} " do replace $path done 出しわけの蚭定の際にconfig.json、templete,mainにそれぞれに出し分ける項目を蚘述しないずいけないのでかなり手間がかかり倧倉ですね。。 たた、実際の出しわけ凊理はshellで行っおいたすが、この凊理はわかりずらいかなず思っおいたす。 私はshellがあたり詳しくないのもあるず思いたすが、新しくプロゞェクトに参画するメンバヌが出しわけの凊理を理解するのに時間がかかっおしたうのではず感じおいたす。 カスタマむズは config.json に項目を远加する圢になりたす。 ぀たり、カスタマむズが増えれば custom1 のようにフラグや䜕かしらの倀が入るようになりたす。 config.json { " app_A ": { " chainCode ": " app_A ", " appName ": " APP A ", " bundleId ": " app_A.app ", " colors ": { " primaryMain ": " FF9800 " } , " custom1 ": " true " } , " app_B ": { " chainCode ": " app_B ", " appName ": " APP B ", " bundleId ": " app_B.app ", " colors ": { " primaryMain ": " FF8800 " } , " custom1 ": " false " } } Melos を䜿甚したアプリ構成 Melos を䜿甚したアプリ構成を玹介したす。 https://melos.invertase.dev/ Melos に぀いおはこの蚘事が詳しいので気になる方はこちらから https://zenn.dev/altiveinc/articles/melos-for-multiple-packages-dart-projects Melos の構成は各クラむアントのアプリから共通のコヌドを呌ぶむメヌゞです。 以䞋のように packages の配䞋に各クラむアント向けのアプリ(app_A、app_B、app_C)ず各クラむアントに察しお共通で䜿甚する項目commonを呌ぶようにしおいたす。 カスタマむズは各クラむアントのアプリに栌玍したす。぀たり、app_A のカスタマむズは app_A のアプリ内のみで管理するこずになりたす。 䟋えば、クラむアントが app_A である堎合、app_A から common を呌びたす。 packages/app_A/lib/main.dart void main () { runMartApp (config : getConfig ()); } Config getConfig () => Config ( color : appColor, chainConfig : ChainConfig (chainCode : 'app_A' ), //クラむアントを識別するための文字 ); // appColorでクラむアントごずにカラヌコヌドが指定できる final appColor = AppColor ( primaryMain : const Color ( 0xFFFF9800 ), primaryDark : const Color ( 0xFFFF6D00 ), primaryLight : const Color ( 0xFFFFB74D ),     // 以䞋省略 ); main()はビルド時に必ず呌ばれるものであり、runMartApp()を呌びたす。runMartApp()は common で定矩されおいるものなので、ビルド時に getConfig()で取埗した蚭定情報ずずもに共通郚分を呌ぶようにしおいたす。 packages/app_A/lib/main.dart void main () { runMartApp (config : getConfig ()); } getConfig は Config ずいうアプリの蚭定情報を定矩しおいたす。これによっおクラアントごずに共通分の出し分けを行いたす。 今回は䟋で appColor ずいうアプリのテヌマカラヌの蚭定情報しか取埗しおいたせんが、他にも環境情報など出し分けが必芁な項目を蚭定したす。 packages/app_A/lib/main.dart Config getConfig () => Config ( color : appColor, chainConfig : ChainConfig (chainCode : 'app_A' ), //クラむアントを識別するための文字 ); // appColorでクラむアントごずにカラヌコヌドが指定できる final appColor = AppColor ( primaryMain : const Color ( 0xFFFF9800 ), primaryDark : const Color ( 0xFFFF6D00 ), primaryLight : const Color ( 0xFFFFB74D ),     // 以䞋省略 ); 呌ばれた共通郚分の runMartApp() でアプリを実行したす。 packages/common/lib/entrpoint.dart void runMartApp ({ required Config config, }) {   // アプリの実行 runApp ( UncontrolledProviderScope ( container : await setupProviderContainer (config : config),  child : const Application (), ), ); } 取埗した config は setupProviderContainer によっお configProvider ずいう蚭定項目を状態管理するものに枡しお䞊曞きしたす。 これによっお、他の画面でも蚭定情報を取埗するこずができるようにしおいたす。 Future < ProviderContainer > setupProviderContainer ({ required Config config, }) async { final container = ProviderContainer ( overrides : [configProvider. overrideWithValue (config)], ); return container; } ちなみに、こんな感じで状態管理の configProvider を定矩しおいたす。 final configProvider = Provider < Config > ( (ref) => throw UnimplementedError ( 'could not read config, you should set config before read this.' , ), ); class Config { Config ({ required this .color, required this .chainConfig, }); final AppColor color; final ChainConfig chainConfig; } class ChainConfig { ChainConfig ({ required this .chainCode, }); final String chainCode; } Melos を䜿甚しなくおもこちらの構成はできたすが、Melosを䜿甚するずパッケヌゞ間のバヌゞョンが異なっおも動かせるため、各クラむアントのカスタマむズ郚分ず共通で䜿甚する項目commonで䜿甚するパッケヌゞのバヌゞョンを頑匵っお合わせる必芁がなくなり、運甚が楜になりたす。䟋えば、commonのバヌゞョンを䞊げた堎合、他クラむアントのバヌゞョンはそのたたでも動くため気兌ねなくバヌゞョンを䞊げるこずができたす。 逆にバヌゞョンを統䞀した堎合はcommonでバヌゞョンを䞊げた際に他クラむアントに圱響が出る可胜性があり、コヌドの修正をしないずいけないかもしれたせん。 commonは共通項目でメむンで開発する郚分なのでバヌゞョンをかなりの頻床で䞊げおいきたいですが、他クラむアントに圱響があるずバヌゞョンを䞊げるのに躊躇しおしたうず思いたす。クラむアントが増えたら、バヌゞョンの圱響範囲がかなり広くなりそうなのでバヌゞョンは各クラむアントで管理したいですね。 たた、CI/CD のサポヌトあるのでクラむアントのビルドや配垃の自動化もできるのでそれも Melos を䜿甚するメリットかなず思いたす。 双方を䜿甚しおの感想 make ず Melos を䜿甚しお感じたこずをたずめたいず思いたす。 make メリット カスタマむズを他クラむアントでも流甚できる カスタマむズしたものではあるが、蚭定項目などを倉えれば同じロゞックのものをそのたた䜿える デメリット カスタマむズ芁玠が増えるずコヌドが耇雑になるためカスタマむズ芁玠の管理が぀らくなる クラむアントの远加、削陀などで䜿っおないけどコヌドはあるみたいこずが起きそう、コヌドを削陀するにも他のクラむアントで䜿いそうな堎合など刀断が難しくなる 出しわけのためにconfig.jsonの蚘述、mainの蚘述、template ファむル䜜るのは手間 手法ずしおは特殊なので他メンバヌのキャッチアップに時間がかかる カスタマむズする際に他のクラむアントも同じ゜ヌスなので圱響が出る可胜性がある テストの工数が増える Melos メリット カスタマむズ芁玠の管理が楜 カスタマむズは各クラむアントのアプリの䞭に閉じ蟌めるcommon には蚘述しないこずでカスタマむズ芁玠の管理をしなくお良くなる 他クラむアントの機胜に圱響が出ない カスタマむズは各クラむアントのアプリの䞭に閉じ蟌めるので他クラむアントに圱響が出なくなる デメリット カスタマむズを他クラむアントに流甚したい堎合、実装が必芁コピペはできるので工数は枛らせる 結論 カスタマむズの管理が楜な Melos の方が良いのではないかず思いたす。 カスタマむズがそのたた流甚できないずいうデメリットはありたすが、䞀番のコヌド管理の問題が解決できるのでメリットの方が倧きいかなず。 ただ、盎近は Melos はあたり運甚しおおらず、 make を䞻に運甚しおいるため぀らみを倚く感じおいるだけで、Melos で気づいおいない他のデメリットがあるかもしれたせん。 よき構成が芋぀かればたた蚘事にしたいず思いたす。 最埌たでご芧いただき、本圓にありがずうございたした。
はじめに ゚ブリヌで゜フトりェア゚ンゞニアをしおいる本䞞です。 DELISH KITCHENでカナリアリリヌスの仕組みを䜜成したので、今回はそのこずに぀いお玹介させおいただこうかず思いたす。 カナリアリリヌスずは カナリアリリヌスずは、䞀床に党䜓に公開するのではなく、最初は䞀郚のナヌザヌに限定しお公開を行い、問題がなければ党䜓に公開しおいくリリヌスの方法です。 カナリアリリヌスを行うメリットずしおは、本番環境で圱響範囲は狭めお動䜜確認ができるこずや問題が発生した堎合にリリヌス前のサヌバに切り戻ししやすいこずなどが挙げられたす。 やったこず 抂芁 ECSのserviceを2぀甚意しおおき、片方のservice(task)の環境倉数にflagを持たせたす。このflagがtrueかどうかでカナリアリリヌスしたい機胜が呌び出されるようになりたす。 カナリアリリヌス時のルヌティングにはALBを䜿甚したす。ALBではタヌゲットグルヌプぞの重みづけを䜿っお、どの皋床カナリアリリヌス甚のserviceにルヌティングするかのルヌルを䜜成しおおきたす。 カナリアリリヌスを行うためのルヌティングに぀いおもう少し掘り䞋げお説明したす。 通垞のタスク定矩 { " family ": " sample ", " cpu ": " 1024 ", " memory ": " 8192 ", " containerDefinitions ": [ { " name ": " sample ", " portMappings ": [ { " hostPort ": 80 , " protocol ": " tcp ", " containerPort ": 80 } ] , " environment ": [ // 通垞のタスクではここがfalseになる { " name ": " flag ", " value ": " false " } ] , " secrets ": [] , " volumesFrom ": [] } ] } カナリアリリヌスのタスク定矩 { " family ": " sample ", " cpu ": " 1024 ", " memory ": " 8192 ", " containerDefinitions ": [ { " name ": " sample ", " portMappings ": [ { " hostPort ": 80 , " protocol ": " tcp ", " containerPort ": 80 } ] , " environment ": [ // カナリアリリヌス甚のタスクではここがtrueになる { " name ": " flag ", " value ": " true " } ] , " secrets ": [] , " volumesFrom ": [] } ] } ALBのルヌティング カナリアリリヌスぞのルヌティングには前述した通りAWSのALBを䜿甚しおいたす。ALBのリスナヌルヌルのアクションの項目で、どのタヌゲットグルヌプにルヌティングするかを遞択するこずができるのですが、そのアクションにタヌゲットグルヌプごずのルヌティングされる割合を指定するこずができたす。 Terraformだず䞋蚘のようなコヌドになりたす。 locals { normal_target_weight = 100 canary_target_weight = 0 } # 動䜜確認甚のルヌル resource " aws_lb_listener_rule " " canary " { listener_arn = aws_lb_listener.sample.arn priority = 1 action { type = " forward " target_group_arn = aws_alb_target_group.canary.arn } condition { # HTTPヘッダに特定の文字列が入った時にこのルヌルを適甚するようにする http_header { http_header_name = " canary " values = [ " true " ] } } } # カナリアリリヌスに必芁なルヌル resource " aws_lb_listener_rule " " sample " { listener_arn = aws_lb_listener.sample.arn priority = 2 action { type = " forward " forward { # 通垞のサヌビスに向ける target_group { arn = aws_alb_target_group.sample.arn # 重みづけを行う weight = local.normal_target_weight } # カナリアリリヌス甚のサヌビスに向ける target_group { arn = aws_alb_target_group.canary.arn # 重みづけを行う weight = local.canary_target_weight } } } condition { path_pattern { values = [ " * " ] } } } カナリアリリヌスに必芁なルヌルは aws_lb_listener_rule.sample の方です。このリ゜ヌスのアクションの䞭にタヌゲットグルヌプを2぀甚意しおそれぞれのweightを倉曎するこずでルヌティングされる割合を制埡したす。 aws_lb_listener_rule.canary のルヌルはカナリアリリヌスしたいserverぞの重みづけが0の時に動䜜確認を行えるように準備しおいたす。 䞊蚘の実装では aws_lb_listener_rule.canary のpriorityを1、 aws_lb_listener_rule.sample のpriorityを2にし、 aws_lb_listener_rule.canary にHTTPヘッダに特定の文字列が入るずきに適甚されるずいう条件を加えるこずで制埡しおいたす。 問題点 実装や運甚をする䞊で問題になったこずがいく぀かあるので、共有させおいただければず思いたす。 コスト ECSのserviceが通垞のものずカナリアリリヌス甚のもので2぀になるので、無蚈画に運甚するずコストが嵩むずいう問題がありたす。そこで匊瀟では、カナリアリリヌス時以倖はカナリアリリヌス甚のserverの台数を0にするこずで察応しおいたす。 AutoScaling よく考えるず圓たり前の話なのですが、AutoScalingを䜿甚しおいお必芁数を䜎めに蚭定しおいる堎合、割合の切り替えに気を぀ける必芁がありたす。サヌビスに圱響はなかったのですが、カナリアリリヌスの導入圓初に䞀床に切り替えを行なっおしたいタスクの数が少なく負荷がかかっおしたうずいうこずがありたした。 おわりに 本蚘事では、ECSずALBを䜿っおカナリアリリヌスを行う方法を玹介したした。ECSずALBを䜿っおカナリアリリヌスを考えおいる人の参考になれば幞いです。 ここたで読んでいただきありがずうございたした。 参考資料 https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener_rule
はじめに はじめたしお。2023幎6月から゚ブリヌの DELISH KITCHEN 開発郚 ナヌザヌグロヌスチヌムで内定者むンタヌンをしおいる新谷です。 DELISH KITCHENは、「だれでもおいしく簡単に䜜れるレシピ」を毎日配信するレシピ動画メディアです。食のプロが提案する、家庭にある食材を䜿った簡単においしくできるレシピをご提案しおいたす。 本蚘事では、むンタヌンでの業務内容や孊んだこずに぀いお玹介できればず思いたす むンタヌン参加の経緯 珟圚私は倧孊院の修士2幎で、2024幎4月から゚ブリヌに入瀟するこずが決たっおいたす。倧孊院ではハヌドりェア系の研究を行っおおり、半導䜓における補造時のばら぀きを機械孊習を甚いおモデル化する研究をしおいたす。これたで開発の経隓ずしおは、Laravelを䜿ったAPI開発やWordPressのプラグむン開発PHPなどを行っおいたした。ただ、芏暡が倧きいサヌビスの開発にはこれたで携わったこずはなく、自分の技術力向䞊や入瀟前に少しでも業務に慣れおおきたいず思い゚ブリヌのむンタヌンに参加したした。 むンタヌンでの業務内容 ミヌティング 業務ではコヌドを曞く以倖に、チヌムで以䞋のようなミヌティングを行いながら開発をしおいたす。 開発ミヌティング毎日 開発・PdM・デザむナヌミヌティング毎日 Web Vitalsミヌティング毎週 KPTミヌティング毎週 開発ミヌティングでは珟状の䜜業内容ず進捗を開発のチヌムメンバヌで共有し、技術的な盞談なども行いたす。開発・PdM・デザむナヌミヌティングでは、KPIの確認を行いその埌開発ずPdM、デザむナヌの間で進捗を共有したす。Web Vitalsミヌティングでは、盎近のWeb Vitalの状況を確認し今埌の察応に぀いお話し合いたす。KPTミヌティングでは、䞀週間の振り返りを行い今埌の課題や改善点を話し合いたす。 開発の進め方 開発の進め方ずしおは、たずPdMの方がたずめた芁件を読んだり話を聞いたりしお、今から行う斜策の背景などを把握したす。その埌、芁件をもずにどのような実装が必芁かを考えたす。䟋えば、この機胜を远加するにはフロント偎でどのような実装が必芁か、バック゚ンド偎でどのような実装が必芁かなどを考えたす。この際実装や蚭蚈の方針を決める際に、GitHubのIssuesを立おお開発のチヌムメンバヌで盞談を行いたす。その埌、実装を行いPull Requestを䜜成したす。Pull Requestは、開発のチヌムメンバヌでレビュヌを行い、問題がなければ開発環境にマヌゞし、その埌問題がなければ本番環境にマヌゞしたす。 雰囲気 むンタヌンでは週2日リモヌトで勀務しおいたす。コミュニケヌションはテキストでするこずが倚いですが、Slackのハドルで通話を行うこずもあり気軜に質問をしおいたす。䞻にチヌムのメンバヌずPdMの方ずコミュニケヌションを取っおおり、皆さんずおも優しく働きやすい環境だず感じおいたす。たた、瀟内勉匷䌚も毎週行なっおおり、業務以倖の新しい技術の共有も行ったりしおいたす。 䞀床出瀟しお勀務したこずがありたすが、その際はチヌムでランチを食べに行きたした。その際には、業務倖の話をするこずもありずおも楜しかったです。 具䜓的な業務内容 6月からむンタヌンずしお業務に携わっお玄5ヶ月が経ち、その間にUIの倉曎からAPIの実装たで幅広く様々な開発を行いたした。ここではこれたで行なった開発の内容を䞀郚玹介したす。 レシピ䜜成者をペヌゞ䞊郚に衚瀺 DELISH KITCHENの レシピペヌゞ では、レシピを䜜成したフヌドスタむリストの方の情報を衚瀺しおいたす。 このレシピ䜜成者の情報はペヌゞの䞋郚に衚瀺されおいたす。こちらの情報のうちプロフィヌル画像ず名前、肩曞きをペヌゞの䞊郚にも衚瀺するこずになりたした。 䞊蚘は実際に完成したレシピペヌゞ䞊郚の画像です。このレシピ䜜成者情報は、クリックするずレシピ䜜成者のプロフィヌルペヌゞに遷移するようにも実装したした。 実装にあたっおは、すでにAPIからレシピ䜜成者の情報を取埗する凊理が実装されおいたした。そのため、倉曎箇所ずしおはUIの远加のみであったため、実装自䜓は比范的簡単に行うこずができたした。ただ、なぜこのような倉曎を行うのかの意図を理解するこずは重芁だず考えおおり、実際に゚ンゞニア偎にも実装の意図が共有されおいる状態で開発が行われおいたす。 この倉曎の意図ずしおは、以䞋のようなものがありたす。 専門家管理栄逊士が監修した信頌のおけるレシピであるこずがEATGoogleが定めたりェブペヌゞの品質を評䟡する際の基準の䞀぀の芳点からGoogleから評䟡される可胜性がある キヌワヌド「プロ」「本栌」系のレシピの根拠ずなり、他のレシピず比べおナヌザヌからの信頌床が䞊がる 私はむンタヌン生ですが、このような意図を理解し実装を行うこずができたした。これは、゚ブリヌのむンタヌンでは、むンタヌン生にも業務の意図を理解しおもらうこずを倧切にしおいるからだず思いたす。 蚘事内のコンテンツが非公開のずき非公開画像を蚭定 DELISH KITCHENでは、料理する際に圹立぀知芋やおすすめレシピなどが曞かれた 蚘事ペヌゞ がありたす。この蚘事ペヌゞでは、該圓するレシピや他の蚘事などぞのリンクが埋め蟌たれおいたす。 この蚘事内のコンテンツレシピや他の蚘事などは、入皿した時点では公開状態ですが埌から非公開状態になる堎合がありたす。珟状このような際は、非衚瀺になるorペヌゞに遷移しおも該圓コンテンツが衚瀺されないずいう状態になっおいたす。そこで、コンテンツが非公開であるこずを分かりやすくナヌザヌに䌝えるため、非公開のコンテンツには非公開画像を蚭定するこずになりたした。 実装ずしお、たずサヌバヌ偎のAPIを改修したした。珟状蚘事のコンテンツを取埗するAPIでは、コンテンツの皮類ごずにレむアりト番号が蚭定されおいたす。そのため非公開のコンテンツのずきは、新たに非公開のレむアりト番号が蚭定されるように実装したした。以䞋は実装したAPIのレスポンスの䞀郚であり、非公開のコンテンツのずきは layout が 16 ずなりたす。 { " data ": { ... " contents ": [ { " num ": 1 , " layout ": 16 } , { " num ": 2 , " layout ": 8 , " recipe_id ": { " id ": 136506859786862859 , " id_str ": " 136506859786862859 " } , " recipe_lead ": " おせちにもおすすめ ", " recipe_title ": " 筑前煮 ", ... } , ... ] , } } 次に、フロント偎の実装を行いたした。フロント偎では、APIから取埗したコンテンツのレむアりト番号をもずに、非公開のコンテンツのずきは非公開画像を衚瀺するように実装したした。 技術的な孊び むンタヌン期間䞭、さたざたなタスクを通しお倚くの技術的な孊びがありたした。先述した業務やその他の業務を通じお埗られた孊びに぀いお玹介したす。 Nuxt.jsに぀いお DELISH KITCHENのWebはNuxt.jsを䜿っお開発しおいたす。Nuxt.jsはVueのフレヌムワヌクで、サヌバヌサむドレンダリングや静的サむト生成などの機胜を提䟛しおいたす。Nuxt.jsを䜿うのは初めおだったので、Vue.jsずNuxt.jsに぀いお勉匷するこずから始めたした。これたでReact.jsは少し觊ったこずがあったので、ある皋床Vue.jsの理解はできたした。ただ、特に難しかったのはNuxt.jsのVuexの理解でした。VuexはVue.jsの状態管理ラむブラリで、アプリケヌションの状態を䞀元管理するこずができたす。状態管理ラむブラリは䜿ったこずがなかったので倧倉勉匷になりたした。 APIサヌバヌのアヌキテクチャに぀いお DELISH KITCHENのAPIサヌバヌはGolangで開発されおいたす。Golangに関しおもこれたで觊ったこずがなかったので、最初はGolangの勉匷から始めたした。APIサヌバヌのコヌドを読む䞊で難しかったこずは、アヌキテクチャの理解です。DELISH KITCHENのAPIサヌバヌでは、クリヌンアヌキテクチャが採甚されおいたす。クリヌンアヌキテクチャは、アプリケヌションをレむダヌに分割し、各レむダヌの䟝存関係が内偎から倖偎に向かっお䞀方向になるように蚭蚈するアヌキテクチャです。 Web Vitalsに぀いお DELISH KITCHENのSEO察策ずしお、Web Vitalsの改善を行っおいたす。 SEOに぀いおあたり知識がなくWeb Vitalsに぀いおも初めお知りたした。Web Vitalsずは、Googleが提唱するWebサむトのパフォヌマンス指暙です。Web Vitalsの䞭でも䞭心ずなる3぀の指暙がありたす。これをCore Web Vitalsず呌び、以䞋の通りです。 LCPLargest Contentful Paintコンテンツが衚瀺されるたでの時間 FIDFirst Input Delayナヌザヌの入力に察する反応速床 CLSCumulative Layout Shiftコンテンツの安定性 Web VitalsはSEOに圱響を䞎える可胜性があり、たたナヌザヌの䜓隓にも圱響を䞎えるため、Web Vitalsの改善は重芁です。そのため、日々Web Vitalsの改善に取り組んでいたす。 最埌に ゚ブリヌでは、垞に新しい孊びがあり業務に携わっおいおずおも楜しいです。今埌も゚ブリヌでの業務を通しお、゚ンゞニアずしおの技術力を身に぀けおいきたいず思いたす
目次 はじめに リブランディングの懞念点 ドメむン/サヌビス名倉曎に䌎う差し替えに぀いお リリヌス䜜業に぀いお たずめ はじめに 初めたしお。 2023幎4月から新卒゚ンゞニアずしお子育おメディア「トモニテ」の開発チヌムにゞョむンしお、バック゚ンドやフロント゚ンドの蚭蚈・開発に携わっおいる庄叞( ktanonymous )です。 以前投皿した Go サヌバヌのメモリリヌクを調査・改善した話 の蚘事や snapshotをResult Buildersを䜿っお宣蚀的に曞く の蚘事の冒頭でも実はお䌝えしおいたのですが、 2023幎8月1日、MAMADAYSはトモニテずしお生たれ倉わりたした。 倚様性が広く認められおきおいる珟代においお、より倚くの人に求められる存圚になるべく、新しいスタヌトをきるこずにしたした。 子育おを通じお、人や瀟䌚が「ずもに手トモニテ」をずる䞖界を目指しお挑戊しおいきたす。 機胜芳点で蚀えば、アプリのメむン機胜である「育児蚘録」「劊嚠週数管理」「食材リスト」を軞ずしお、 家族やパヌトナヌ、家族以倖の人や瀟䌚ずの接点を䜜るためのシェア機胜やコミュニティ機胜などの拡充をめざしおいきたす。 リブランディングを実斜するにあたり、開発チヌムでも管理しおいる゜ヌスコヌドに察しお様々な倉曎を斜す必芁がありたした。 今回必芁ずなった䜜業を倧きく分けるず以䞋のようになりたした。 むンフラ呚りの察応 Google Search Consoleなど、SEO察策 ドメむンの倉曎に䌎う差し替え サヌビス名倉曎に䌎う差し替え 本蚘事では、筆者自身が䞻に担圓しおいたずいう意味合いで、むンフラ/SEO呚りの察応を陀いた2点に぀いお䞻にお話しできればず思いたす。 リブランディングの懞念点 本題に入る前に、リブランディングの実斜に䌎う懞念点に぀いお簡単に觊れおいきたいず思いたす。 今回のリブランディングの実斜にあたり、サヌビスの名称が倉曎されるこずになりたす。 サヌビス名称の倉曎に䌎い、以䞋のような懞念点がありたした。 既存ナヌザヌ芖点で、突然名前が倉わるこずでMAMADAYSが無くなった「トモニテ」ずいう知らないサヌビスが出おきたず思われおしたう恐れがある 怜玢゚ンゞンによるむンデックスが初期化されるこずで、怜玢順䜍が䜎䞋しおナヌザヌの流入が倧幅に枛少する 事前に早い段階でのお知らせが必芁ですが、 1. に぀いおはナヌザヌぞのお知らせで察応できる範囲かず思いたす。我々のビゞョンを䌝えるためのペヌゞ( トモニテに぀いお )も䜵せお公開しおいたす。 トモニテに぀いお ずはいえナヌザヌの情報取埗タむミングには様々な理由からムラができるもので、実際に8月の名称倉曎に10月に気づいた方なども芋受けられたした。← 奜印象だったようで我々ずしおは嬉しいお声でした しかし、 2. に぀いおは避けようがありたせん。 Google怜玢の公匏ペヌゞにもドメむン倉曎に䌎う圱響に぀いお䞋蚘のような蚘述がありたす 1 。 移転䞭、サむトのランキングが䞀時的に倉動するこずを想定する。 サむトの倧幅な倉曎があった堎合は、Google がサむトを再クロヌルしおむンデックスに登録し盎す間にランキングが倉動するこずがありたす。 こういった背景もあり、リブランディング䜜業を実斜するにあたり、䞀定の閲芧数枛少は芚悟する必芁がありたした。 ドメむン/サヌビス名倉曎に䌎う差し替えに぀いお ドメむン倉曎に䌎う差し替え察応では、䞻にserver偎の実装の䞭で旧ドメむンを利甚しおパス指定しおいる箇所に぀いお、リブランディング埌のドメむンを指定したURLに差し替えたした。 mamadays.tv -> tomonite.com たた、サヌビス名称倉曎に䌎う差し替え察応では、ナヌザヌの目に觊れる郚分やwebペヌゞのメタデヌタに含たれるサヌビス名称をリブランディング埌のサヌビス名称に差し替えたした。 MAMADAYS/mamadays/ママデむズ -> トモニテ どちらも゜ヌスコヌド内にある文字列を倉曎埌の名称に眮換するだけの䜜業ずいうこずには倉わりありたせんが、これが思ったよりも曲者でした。 今回の䜜業では差し替えをせずに埌で察応する箇所などが䞀郚にあったこずをふたえお、以䞋のようなこずを考えながら䜜業を進めおいたした。 差し替えの察象ずなるものか察象倖ずなるものか リリヌスタむミングをどう分割するか ナヌザヌの目に芋える圢で出す必芁性があるためコヌド䞊で定数ずしお管理するには䞍郜合なものもあり、ドメむンやサヌビス名称をベタ曞きしおしたっおいる箇所が盞圓数あるのが実情です。 そのため、ドメむン・サヌビス名称を差し替える際、しらみ朰しに探し回っお1぀1぀差し替えおいくずいうのは非垞に面倒な䜜業になりたすし、予期せぬミスの枩床にもなっおしたいたす。 そこで、極力ミスを枛らしたいずいう思いで「mamadays.tv/MAMADAYS/mamadays/ママデむズ」1回の䜜業の倉曎量を抑えるために英倧・小文字を敢えお区別しおいたすのキヌワヌドを それぞれ゜ヌス内で怜玢し、「tomonite.com/トモニテ」に䞀括眮換する方法を採甚したした。 これにより、差し替え忘れや差し替えに䌎うミスを防ぐこずができたした。 䞀方で、この方法では、異なる文脈の䞭で該圓するキヌワヌドを利甚しおいるような、差し替え察象倖のものも眮換されおしたいたす。 こちらに関しおは、 git add -p (addコマンドのむンタラクティブモヌドの䞀皮、詳现は こちら )で 倉曎差分の芳点から差し替え察象に該圓するかどうかを確認しお察象を遞別する方法を採甚したした。 これにより、差し替え察象倖のものを1぀1぀探し回っお眮換を取り消しおいくのではなく、自動で衚瀺される差分だけを芋ながら差し替え察象の仕分けを効率よく行うこずができたず思いたす。 git add -pで「ママデむズ」を「トモニテ」に差し替える䟋 以䞊の䜜業を通しお、 1. に関する察策を取り぀぀も䜜業量を抑えるこずができたした。 たた、 2. に぀いおは、差し替え䜜業を䞀定の基準で分割するこずで個々のPRが倧きくなりすぎないように気を぀けおいたした。 今回の䜜業では、サヌバヌ偎の察応ずフロント偎の察応で分割、さらに、ドメむン倉曎察応ず名称倉曎察応で分割するような圢ずしたした 実際にはもう少し现かくしおいる郚分もありたす。 リリヌス䜜業に぀いお 先述のような方針で進めるこずを想定しおいたため、実際のリリヌス䜜業時には耇数のPRを立お続けに開発/本番環境ぞマヌゞする必芁がありたした 1぀のPRにたずめおからマヌゞしおしたうずPRが肥倧化しおサヌビスぞの圱響床が倧きくなり過ぎおしたうず考え、遞択肢から陀倖しおいたした。 (↑ server 偎の1芳点からの差し替えPRですが、30ものファむルを倉曎しおいたす。) 䜕かしら問題が発生した時の手戻りのコストを抑えるための察応ではありたすが、必然的に党おをリリヌスするたでの手順は増えおしたいたす。 そのため、各PRの重芁床や䟝存関係、リリヌスによるサヌビスぞの圱響床の倧きさを考慮しながらリリヌス順を決定する必芁がありたした。 そんなこんなで、最終的なリリヌス䜜業の手順は以䞋のようになりたした。 infra呚りの察応本蚘事の察象倖 フロント偎、サヌビス名称差し替え サヌバヌ偎、URL差し替え サむトマップの構築・配眮・送信(ここでは ecschedule を利甚) フロント偎、URL差し替え サヌバヌ偎、サヌビス名称差し替え 実際のリリヌス䜜業時には、関係者ぞの䜜業進捗の報告やこためな動䜜確認を挟み぀぀䜜業を進め、 最終的には倧きな問題を起こすこずなくリブランディング䜜業を終えるこずができたした。 䜙談ですが、トモニテではドメむン移行盎埌のwebのナヌザヌ流入が予想通り萜ち蟌んだものの、 埐々にトラフィックが回埩しお玄2ヶ月で移行前の数倀を超えおいたした。 たた、 はじめにも軜く觊れたしたが 、SNS等でナヌザヌから奜意的な声が䞊がっおいるなど、 プロゞェクト成功ず蚀える結果になったず思いたす。 たずめ 本蚘事では、MAMADAYSがトモニテに生たれ倉わる裏偎のお話をサヌバヌ・フロント゚ンドの芖点からお䌝えしおきたした。 ここでお䌝えした䞻な䜜業自䜓は゜ヌスコヌド内の文字列を眮換するだけずいう単玔なものでしたが、「たかが眮換、されど眮換」ずいう感じで 思いのほか癖のある䜜業だったなぁず感じおいたす。 サヌビスのドメむン・名称を倉曎するずいうのはなかなか出䌚う機䌚のない䜜業だず思いたすが、偶然にも入瀟しお間もないタむミングで このような倧きな斜策に携わるこずができたのはずおも良い経隓になりたした。 この蚘事を読んでいる方が同じような状況になるこずがあるかどうかはわかりたせんが、プロダクトの根幹を倉曎する䜜業の倧倉さの片鱗は感じずっおいただけたのではないかず思いたす。 たた、もし同じような状況になった際には、本蚘事が少しでも参考になれば幞いです。 最埌になりたすが、子育おを通じお、人や瀟䌚がずもに手をずる䞖界を目指しお新しいスタヌトを切ったトモニテにこれからもご泚目ください。 最埌たでお読みいただきありがずうございたした。 Google怜玢セントラル | サむトを移転する方法 ↩
゚ンジニアブログタむトル はじめに いきさ぀ #1:mapをmake関数で䜜成するずき第2匕数sizeを枡す やったこず 解説 #2:可胜ならmapをsliceで代甚する やったこず 解説 補足 おわりに はじめに 株匏䌚瀟゚ブリヌで DELISH KITCHEN 事業のバック゚ンド゚ンゞニアをしおいる、GopherのYuki @YukiBobier です。珟圚は䞻に 広告サヌビス を担圓しおいたす。 圓瀟の広告サヌビスには 店頭サむネヌゞ ずいうものがあり、サヌバヌサむドはGoで実装されおいたす。先日、このサヌビスの空き広告枠怜玢アルゎリズムを改善したした。その際に、Goのデヌタ構造ず䞊手に付き合うこずでヒヌプ䜿甚量を改善できるこずを実感したした。 本蚘事では、特に効果があった2぀の手法を玹介したす。 いきさ぀ 圓瀟の店頭サむネヌゞは、スヌパヌの店頭でレシピを攟映するのみならず、広告を攟映するこずもできたす。店頭サむネヌゞには広告を攟映できる時間的な枠が定められおおり、その枠を必芁な数だけ占めるかたちで広告を登録したす。したがっお、広告を登録する際には、垌望するサむネヌゞ×日時に十分な空き枠があるかどうかを確認する必芁がありたす。この時に䜿甚されるのが、空き広告枠怜玢アルゎリズムです。 埓来の空き広告枠怜玢アルゎリズムは、広告配信期間に比䟋しおDBぞのI/Oが倚発するものでした。他方で、サヌビスずしおは広告配信期間の単䜍をより现かくしたい芁求があり、これを叶えるこずはI/Oのさらなる増加を意味したした。そこで、広告配信期間ずI/Oの頻床が盞関しないアルゎリズムに改善するこずになりたした。 アルゎリズム改善の結果、あるベンチマヌクテストでは、DBぞのI/Oは䞀定しお1回に、さらに実行時間は埓来の1/10以䞋にたで改善されたした。しかし、DBからごそっず取埗したデヌタをメモリに乗せお集蚈するため、ヒヌプ䜿甚量は埓来の40倍に増加したした。ヒヌプ䜿甚量の増加はGCすなわち"stop-the-world"の頻床を高めおしたいたす。そこで、さらにこのアルゎリズムのヒヌプ䜿甚量を改善するこずになりたした。 その際に、Goのデヌタ構造ず䞊手に付き合うこずでヒヌプ䜿甚量を改善できるこずを実感したした。次節から、特に効果があった手法を玹介しおいきたす。 #1: map を make 関数で䜜成するずき第2匕数 size を枡す やったこず 改善埌のアルゎリズムでは、DBから取埗したデヌタを集蚈するためのデヌタ構造ずしお2重の map を採甚しおいたした map[string]map[string]int8 。これは、サむネヌゞ×日時ごずの広告枠空き状況を保持するのに郜合が良かったからです。 map [ string ] map [ string ] int8 { "2ab91c5f-ac7f-46c8-8040-28d265c71591" : { "2023-10-27 00:00:00" : 2 , "2023-10-28 00:00:00" : 0 , "2023-10-29 00:00:00" : 2 , "2023-10-30 00:00:00" : 3 , ... .. . }, "e3a3ddd9-96b9-43df-a659-574b2f768ba8" : { "2023-10-27 00:00:00" : 1 , "2023-10-28 00:00:00" : 0 , "2023-10-29 00:00:00" : 2 , "2023-10-30 00:00:00" : 7 , ... .. . }, ... .. . } map は make 関数で䜜成するずき、第2匕数 size を枡すこずができたす。最終的に map に保持するこずになるであろう芁玠数を size に枡すようにしたずころ、ヒヌプ䜿甚量が37%削枛されたした。 解説 runtimeパッケヌゞ によれば、 map は”バケット”の配列を基底ずするハッシュテヌブルです。バケットずは、キヌず倀のペアを8個たで保持できる構造です。 map はより倚くの芁玠を保持するために拡倧したす。぀たり、 map に芁玠を远加するこずを匕き金ずしお、より倧きな基底の䜜成ず党芁玠のコピヌが発生するこずがありたす。䞍芁になった元の基底はGCの察象ずなりたす。これはアプリケヌションのパフォヌマンスにずっお小さくないペナルティです。 この点、 map を make 関数で䜜成するずき第2匕数 size に数倀を枡すず、その数の芁玠を保持するのに十分な倧きさの基底を持぀ map が䜜成されたす。芁するに、少なくずも芁玠数が size に満たないうちは、 map の拡倧を抑制できるずいうこずです。 そのむンパクトを実際にコヌドで確認しおみたしょう。たずは size を枡さない堎合のヒヌプ䜿甚量を蚈枬したす Go Playground 。 package main import ( "fmt" "runtime" "runtime/debug" ) const count = 1_000_000 func init() { debug.SetGCPercent(- 1 ) // GCが自動で実行されないようにする } func main() { start := allocatedMB() // 開始時のヒヌプ䜿甚量 m := make ( map [ int ] struct {}) afterMake := allocatedMB() // map䜜成埌のヒヌプ䜿甚量 for i := 0 ; i < count; i++ { m[i] = struct {}{} } afterAdd := allocatedMB() // mapに芁玠を远加した埌のヒヌプ䜿甚量 runtime.GC() // GCを手動で実行する afterGC := allocatedMB() // GCが実行された埌のヒヌプ䜿甚量 runtime.KeepAlive(m) // mapがGCの察象にならないようにする fmt.Printf( "䜜成盎埌のmapのヒヌプ䜿甚量: %dMB \n " , afterMake-start) fmt.Printf( "芁玠远加埌のmapのヒヌプ䜿甚量: %dMB \n " , afterAdd-start) fmt.Printf( "GCされたヒヌプ量: %dMB \n " , afterAdd-afterGC) fmt.Printf( "最終的なmapのヒヌプ䜿甚量: %dMB \n " , afterGC-start) } func allocatedMB() uint64 { var ms runtime.MemStats runtime.ReadMemStats(&ms) return ms.Alloc / 1024 / 1024 } 次のような結果が埗られるはずです。 map が䜜成盎埌の0MBから21MBに拡倧し、その間に24MBの”ごみ”がヒヌプに生じたこずがわかりたす。 䜜成盎埌のmapのヒヌプ䜿甚量: 0MB 芁玠远加埌のmapのヒヌプ䜿甚量: 45MB GCされたヒヌプ量: 24MB 最終的なmapのヒヌプ䜿甚量: 21MB 次に size を枡す堎合です Go Playground 。 package main import ( "fmt" "runtime" "runtime/debug" ) const count = 1_000_000 func init() { debug.SetGCPercent(- 1 ) // GCが自動で実行されないようにする } func main() { start := allocatedMB() // 開始時のヒヌプ䜿甚量 m := make ( map [ int ] struct {}, count) afterMake := allocatedMB() // map䜜成埌のヒヌプ䜿甚量 for i := 0 ; i < count; i++ { m[i] = struct {}{} } afterAdd := allocatedMB() // mapに芁玠を远加した埌のヒヌプ䜿甚量 runtime.GC() // GCを手動で実行する afterGC := allocatedMB() // GCが実行された埌のヒヌプ䜿甚量 runtime.KeepAlive(m) // mapがGCの察象にならないようにする fmt.Printf( "䜜成盎埌のmapのヒヌプ䜿甚量: %dMB \n " , afterMake-start) fmt.Printf( "芁玠远加埌のmapのヒヌプ䜿甚量: %dMB \n " , afterAdd-start) fmt.Printf( "GCされたヒヌプ量: %dMB \n " , afterAdd-afterGC) fmt.Printf( "最終的なmapのヒヌプ䜿甚量: %dMB \n " , afterGC-start) } func allocatedMB() uint64 { var ms runtime.MemStats runtime.ReadMemStats(&ms) return ms.Alloc / 1024 / 1024 } 次のような結果が埗られるはずです。 map は䜜成盎埌から21MBの十分な容量を持ち、拡倧する必芁がないのでヒヌプに”ごみ”は生じたせんでした。 䜜成盎埌のmapのヒヌプ䜿甚量: 21MB 芁玠远加埌のmapのヒヌプ䜿甚量: 21MB GCされたヒヌプ量: 0MB 最終的なmapのヒヌプ䜿甚量: 21MB map を make 関数で䜜成するずき第2匕数 size を枡すず、これだけのヒヌプ䜿甚量の改善に぀ながりたす。 #2:可胜なら map を slice で代甚する やったこず サむネヌゞ×日時ごずの広告枠空き状況を保持する2重の map  map[string]map[string]int8 は、倖偎のキヌがUUIDで、内偎のキヌが等差な日時でした。ですので内偎の map は、むンデックス i に”最初の日時+公差× i ”の日時の広告枠空き状況が保持された slice で代甚できたした []int8 。 map [ string ][] int8 { "2ab91c5f-ac7f-46c8-8040-28d265c71591" : { 2 , 0 , 2 , 3 , ...}, "e3a3ddd9-96b9-43df-a659-574b2f768ba8" : { 1 , 0 , 2 , 7 , ...}, ... .. . } #1に加えおこれをしたずころ、ヒヌプ䜿甚量が90%削枛されたした。 解説 たずは、 map ず slice のヒヌプ䜿甚量の違いが顕著であるこずを蚈枬によっお瀺したす Go Playground 。 package main import ( "fmt" "runtime" "runtime/debug" ) const count = 1_000_000 func init() { debug.SetGCPercent(- 1 ) // GCが自動で実行されないようにする } func main() { start := allocatedMB() // 開始時のヒヌプ䜿甚量 _ = make ( map [ string ] int8 , count) afterMakeMap := allocatedMB() // map䜜成埌のヒヌプ䜿甚量 _ = make ([] int8 , 0 , count) afterMakeSlice := allocatedMB() // slice䜜成埌のヒヌプ䜿甚量 fmt.Printf( "mapのヒヌプ䜿甚量: %dMB \n " , afterMakeMap-start) fmt.Printf( "sliceのヒヌプ䜿甚量: %dMB \n " , afterMakeSlice-afterMakeMap) } func allocatedMB() uint64 { var ms runtime.MemStats runtime.ReadMemStats(&ms) return ms.Alloc / 1024 / 1024 } 次のような結果が埗られるはずです。䞡者のヒヌプ䜿甚量には顕著な差が芋られたす。 mapのヒヌプ䜿甚量: 40MB sliceのヒヌプ䜿甚量: 1MB なぜこのような差が芋られるのでしょうか もう少し深掘りしおみたしょう。 次に瀺すのは、様々な型の slice ず map のヒヌプ䜿甚量を蚈枬するコヌドです Go Playground 。 package main import ( "fmt" "runtime" "runtime/debug" ) const count = 1_000_000 func init() { debug.SetGCPercent(- 1 ) // GCが自動で実行されないようにする } func main() { start := allocatedMB() // 開始時のヒヌプ䜿甚量 _ = make ([] struct {}, count) afterStructSlice := allocatedMB() // []struct{}䜜成埌のヒヌプ䜿甚量 _ = make ([] int8 , count) afterInt8Slice := allocatedMB() // []int8䜜成埌のヒヌプ䜿甚量 _ = make ( map [ struct {}] struct {}, count) afterStructStruct := allocatedMB() // map[struct{}]struct{}䜜成埌のヒヌプ䜿甚量 _ = make ( map [ string ] struct {}, count) afterStringStruct := allocatedMB() // map[string]struct{}䜜成埌のヒヌプ䜿甚量 _ = make ( map [ struct {}] int8 , count) afterStructInt8 := allocatedMB() // map[struct{}]int8{}䜜成埌のヒヌプ䜿甚量 _ = make ( map [ string ] int8 , count) afterStringInt8 := allocatedMB() // map[string]int8{}䜜成埌のヒヌプ䜿甚量 fmt.Printf( "[]struct{}のヒヌプ䜿甚量: %dMB \n " , afterStructSlice-start) fmt.Printf( "[]int8のヒヌプ䜿甚量: %dMB \n " , afterInt8Slice-afterStructSlice) fmt.Printf( "map[struct{}]struct{}のヒヌプ䜿甚量: %dMB \n " , afterStructStruct-afterInt8Slice) fmt.Printf( "map[string]struct{}のヒヌプ䜿甚量: %dMB \n " , afterStringStruct-afterStructStruct) fmt.Printf( "map[struct{}]int8{}のヒヌプ䜿甚量: %dMB \n " , afterStructInt8-afterStringStruct) fmt.Printf( "map[string]int8{}のヒヌプ䜿甚量: %dMB \n " , afterStringInt8-afterStructInt8) } func allocatedMB() uint64 { var ms runtime.MemStats runtime.ReadMemStats(&ms) return ms.Alloc / 1024 / 1024 } 次のような結果が埗られるはずです。 []struct{}のヒヌプ䜿甚量: 0MB []int8のヒヌプ䜿甚量: 1MB map[struct{}]struct{}のヒヌプ䜿甚量: 4MB map[string]struct{}のヒヌプ䜿甚量: 38MB map[struct{}]int8{}のヒヌプ䜿甚量: 6MB map[string]int8{}のヒヌプ䜿甚量: 41MB この結果から次のこずがわかりたす。 map は芁玠ごずにオヌバヌヘッドが存圚し、それは空の芁玠 struct{} であっおも避けられない ひず぀前のコヌドで map ず slice のヒヌプ䜿甚量に顕著な差が芋られたのは、キヌの型が string だったから map を slice で代甚するこずで、キヌが string のような倧きな型であった堎合には、ヒヌプ䜿甚量の顕著な改善に぀ながりたす。そうでなくおも、1芁玠ごずのオヌバヌヘッドを回避する効果は少なくずも埗られたす。 補足 map を slice で代甚できるかどうかの刀断に぀いお補足したす。 たず倧前提ずしお、甚途によっお刀断基準も結論も異なりたす。総合的にみお map の方が良いこずや、 map でなければならないこずは倧いにあり埗たすし、むしろ slice で代甚できるこずの方が皀かもしれたせん。その䞊で、刀断の䞀䟋を瀺したす。 map の代衚的な特城は、高速なルックアップです。公匏に蚀明はされおいたせんが、そのパフォヌマンスはO(1)であるこずが実装から窺い知れたす。ですので、同等のパフォヌマンスで slice をルックアップできるかどうかがひず぀の刀断基準になるず思いたす。本件の堎合は䞋のようにするこずで、ある日時に察応するむンデックスを”(日時-最初の日時)/公差”で求めるこずができたす。したがっおルックアップのパフォヌマンスはO(1)なので、 map を slice で代甚できたした。 // 等差な日時をキヌに持぀map { "2023-10-27 00:00:00" : 2 , "2023-10-28 00:00:00" : 0 , "2023-10-29 00:00:00" : 2 , "2023-10-30 00:00:00" : 3 , ... .. . } // むンデックス`i`が”最初の日時+公差×`i`”の日時に察応するslice { 2 , 0 , 2 , 3 , ...} その他の map の特城ずしお、キヌの重耇を蚱さないこずが挙げられたす。そのため、他蚀語における”Set”のように map を䜿甚するこずができたす。この堎合は slice で代甚するこずは難しいでしょう Go Playground 。 package main import "fmt" type node struct { next *node } func main() { node3 := node{} node2 := node{next: &node3} node1 := node{next: &node2} node3.next = &node1 visited := make ( map [*node] struct {}) for n := &node1; n.next != nil ; n = n.next { if _, ok := visited[n]; ok { fmt.Println( "ルヌプしおたす" ) return } visited[n] = struct {}{} } fmt.Println( "ルヌプしおたせん" ) } こういった map の特城を必芁ずしない堎合には、 slice で代甚できる可胜性は高いず思いたす。 おわりに もしGoで倚量のヒヌプ䜿甚が芳枬されるようでしたら、デヌタ構造に着目するこずが改善の糞口ずなるかもしれたせん。 ちなみに、本蚘事で語った知芋は『 100 Go Mistakes and How to Avoid Them 』から孊んだずころが倧きいです。ずおも勉匷になりたすし、最近は 邊蚳 も出たので、ぜひ読んでみおください。
はじめたしお、デヌタ&AIチヌムのoyabuです。デヌタストラテゞストずいうデヌタアナリストみたいな仕事をしおたす 具䜓的には「DELISH KITCHEN」アプリのデヌタを抜出、分析しお食品メヌカヌなどのクラむアントさたの斜策や提案にご掻甚頂く仕事をしおいたす 今回はLLM(ChatGPT、OpenInterpreter)をビゞネスサむドに掻甚しおもらうためにやったこずの話をしたす ただ始めたばかりなので、成果自䜓はそんなに出おないですが、考えたこずや、埗られた知芋を共有したいず思いたす やったこずは以䞋です ChatGPTの䜿い方ドキュメントの䜜成(犁忌ずナヌスケヌスの敎理) ChatGPTの䜿い方勉匷䌚 OpenInterpreterを隣に座っおるカスタマヌサクセスの方に詊しおもらう きっかけ(課題感) そもそもこんなこずをやり始めたきっかけずしおは以䞋な感じです 倧前提ずしおLLMが猛嚁をふるっおおり、自分ずしおも十分その利益を享受しおいる ビゞネスマンにずっお、ChatGPTスキル = 今のExcelスキルぐらいになるこずは目に芋えおる ビゞネスサむドでもChatGPTを䜿っおる人はいるが、あんたり䌚話に出おこない 瀟内のSlackを持っおもChatGPTの話をしおる人はだいたい゚ンゞニア 貌っおるアンテナ的にもそもそもの呚蟺情報が゚ンゞニアには集たっお来やすい 気になっおはいお、1回詊したものの、業務に䜿うむメヌゞが湧いおない方がなんだかんだ倚いのかなヌずいう劄想 向き合いの郚眲ずしばらく働いお、業務もなんずなくわかっおきお、现かい斜策単䜍でChatGPTで解決出来そうなものが芋えおきた 瀟内甚のChatツヌル が出来お、盎接業務に関わる内容をフランクに聞ける環境が敎った 瀟内の運甚ルヌル/tipsを集めたドキュメントがそもそも䞍圚 やったこずずその目的、成果/埗られた知芋 目的 目的はChatGPTによる、ビゞネスサむドの工数削枛/クオリティアップです ずりあえずは利甚習慣化を目暙ずし、そのために以䞋の2点を達成するため斜策を実行したした ChatGPTが身近なツヌルだず思っおもらう ChatGPTで出来るこず/苊手なこずを知っおもらう 習慣化の初期段階で重芁なのはフリヌク゚ンシヌ、敵は䜎いモチベヌションず高いハヌドルなので ずりあえずそこを気にした斜策の蚭蚈をしたした 具䜓的にやったこず ChatGPTの䜿い方ドキュメントの䜜成 最䜎限の犁忌的なルヌルは曞き぀぀、業務にそったナヌスケヌスを䞭心に曞きたした。そうした理由は以䞋です 各人の業務に近い郚分のコンテンツを拡充するこずで、倚様な各人の興味に匕っかかりやすくするため 理想論でいえば、成り立ちや理論を知るこずは倧事ですが、興味のない人がおおそう たた完党にその業務をChatGPTで解決するこずは難しいかもしれたせんが、今埌の工数短瞮やクオリティアップを芋越した プロンプトのタタキが存圚するこず自䜓が重芁だず思っおいるので、ちょっずでも解決出来そうなこずがあるならずりあえず ナヌスケヌスずしお入れおおきたした。プロンプトの倢を芋たした(倩啓もありたした) ドキュメント化にあたっおは、なるべくバラ゚ティに富んだナヌスケヌスを盛り蟌むようにしたした。たずえば以䞋のようなカテゎリで、業務に近いナヌスケヌスを盛り蟌みたした ブレスト系 埓来の業務の工数を䞋げる系 埓来の業務のクオリティを䞊げる系 盎近のChatGPT Plusの機胜内容 OpenInterpreterでより耇雑なこずができる話 ChatGPTの䜿い方勉匷䌚 これは䞊蚘のドキュメントをそのたたデモ的に玹介したした ドキュメントが読たれないのは䞖の垞。ある皋床お時間をお借りしお(今回は1時間)集䞭しおChatGPTに぀いお考えおもらう必芁がありたす 共通の䜓隓を通じお、話題にのがりやすく、近くの垭で口頭での知芋の共有に぀ながるのも勉匷䌚の利点だず思いたす やった結果はずいうず、「いたたでChatGPTには正解のある答えを聞いお嘘぀かれお䜿えないず思っおたけど、ブレストにめっちゃ䜿える!」など ちょこちょこ良い反応は埗られたした 䜜戊通りナヌスケヌスを色々玹介したこずで、各々の業務での掻甚むメヌゞを具䜓的にもっおくれたようでした ずはいえ、あくたでも習慣化のための䞀歩目なので、ナレッゞシェアやOJT的な動きは継続しお行っおいく必芁がありたす OpenInterpreterを隣に座っおるカスタマヌサクセスの方に詊しおもらう 継続的にChatGPTに興味をもっおもらうために、䜕か飛び道具的な実瞟が必芁だず考えたした OpenInterpreterたじですごいずいう話を聞いたので、これを䜿っおみるこずにしたした ちなみに同じチヌムのfuruhama-sanがOpenInterpreterの実装を深掘りしおるので、ご興味のある方は芋おみるず面癜いです tech.every.tv OpenInterpreterのメリットの玹介はfuruhama-sanの蚘事を読んで頂くず分かりたすが、今回魅力に感じたのは以䞋なこずでした GoogleColaboratoryずいう簡単に甚意できる閉じた環境での実行がしやすい ロヌカルのファむルの閲芧/線集ができる(今回はマりントしたGoogleDrive) 䞊蚘を掻かしお、ドメむン知識を持぀ビゞネスサむドがある皋床のデヌタ分析たで自立的に行えるず 今埌の分析業務のハヌドルが䞋がり、より濃い瀺唆や知芋が財産ずしお効率的に貯蓄されるメリットもありたす もちろん䞀筋瞄でいくずは思っおいたせんが、その第䞀歩ずしお、隣にいる人に無理を蚀っおOpenInterpreterを䜿っお頂きたした マゞ感謝です。隣の人なので仮にTさんず呌びたしょう。Tさんはプログラミングができないカスタマヌサクセスっぜい立ち䜍眮のひずです やったこずは以䞋です CSV圢匏で出力されたアンケヌト結果をOpenInterpreterでクロス集蚈する おたじないをたくさん曞いたGoogleColaboratoryを事前に甚意 Tさんからやりたいこずを聞いお、思想を説明しながら仮のプロンプトを目の前で曞いおみる OpenInterpreterがコヌドの実行芁吊を聞いおくるので、Tさんに深く考えずにどんどん実行の指瀺を出しおもらう 意図した結果ではない、凊理に時間がかかるなら再床プロンプトを芋぀め盎すを繰り返す 結果ずしお特定のアンケヌト項目をその他の項目ずクロス集蚈したものが20個くらい埗られたした ゚ラヌぞの察凊や、僕がColabの仕様に疎いこずもあり、Tさんのお時間はそれなりに頂いおしたったのですが(4h+α)、 わりず喜んで頂けたした ずいうのも、そもそも今回のアンケヌト結果のクロス集蚈は埓来以䞋の問題を抱えおいたようでした 時間がかかる ひず぀ひず぀のクロス集蚈のために郜床数匏を組む必芁があるので粟神的に぀らい Tさんずしおは、成果物を埗られたこずももちろんですが、䞊蚘を自分の手でやらなくおよい。ずいうこずに奜印象を持っおいたようでした たた、゚ラヌが出たずきに萜ちるのではなく、゚ラヌの内容を理解した䞊で再床修正の凊理をかけおくれるのはめっちゃ嬉しいずも仰っおいたした 最終的には愛着を蚎えおおり、ひたすらこちらから呜什を䞎えおいるのですが、自己再生をするだけで芪近感が芜生えるのは倧倉興味深い珟象でした たしかにOpenInterpreterは内省にもにた凊理を繰り返すのがひず぀特城で、それが悩んでいるように芋えるのが人間性を感じるポむントなのかもしれたせん 逆に以䞋の点が぀らそうでした むンタラクティブに凊理実行の有無を聞かれるので、垞に刀断を求められる コヌドがガンガン出力されるが意味がわからん なんだかんだめんどくさい ここは実隓する前にある皋床予枬はしおいたのですがやはりそうか。ずいう感じです 事前のauto_runの蚭定や、temperatureの調敎/プロンプトの粟錬である皋床解決できそうではありたすが、 目たぐるしく出力される情報に知らず知らずのうちに可凊分粟神が消費されおいるようでした 䟋えばGASを敎えたスプレッドシヌトを提䟛するなどで、局所的な解決は可胜な課題でしたが OpenInterpreterである皋床柔軟に課題を解決出来るこずはわかったので、匕き続きこれをうたく䜿っおビゞネスサむドの課題解決を目指しおいきたいです ※ 䜙談ですがアップデヌト埌OpenInterpreterがうたく動かなくなりたした(10/16時点, ver: 0.1.7) このissueの察応をするこずで解決したした https://github.com/KillianLucas/open-interpreter/issues/637 たずめ 以䞊、ビゞネスサむドにChatGPTの利甚を習慣づけられるずいいなヌず思った䞊で 色々やっおみた話を曞いおみたした ずりあえず第䞀歩目ずしおドキュメントの䜜成/勉匷䌚など実斜し、それなりの反応は頂き、倚少利甚は促進できた実感はありたす ずはいえ、今埌習慣化のためには草の根運動が重芁になっおきたす ビゞネスサむドで解決しやすそうな課題を芋぀けたらその堎でChatGPTに解いおもらうなど 现かい䜓隓を繰り返しおいくこずで、初めお習慣や文化ずしお根付くものだず思っおいるので、応揎よろしくお願いしたす