TECH PLAY

株式会社エニグモ

株式会社エニグモ の技術ブログ

247

こんにちは! WEBアプリケーションエンジニア の小松です! この記事は[ Enigmo Advent Calendar 2024 ]の15日目の記事です。   PHPの場合 呼び方の具体例 文字列キーを使ったハッシュアクセス 文字列キーアクセスを使う場面 シンボルキーを使ったハッシュアクセス オブジェクト形式(OpenStruct)のアクセス 確認方法 クラス内のインスタンス変数の確認方法 シンボルはRubyのみ存在? PHP の場合 model['full_name'] こういう変数の持ち方を 連想配列 と呼んでいただけなので、シンボルキーという言葉と発想がなかったです。 呼び出し方でも model['full_name'], model[:full_name], model.full_name などがあり、 <%= debug model %> やログでオブジェクト全体の値が取れてもキーの持ち方が違って取得するのに時間がかかることがありました。 なので、呼び方の具体例や呼び出し方を記載しました。 model['full_name'] のような形式は、文字列キーを持つハッシュから値を取得する方法です。このような形式でのアクセスは、 Ruby の基本的なハッシュオブジェクトの操作です。 呼び方の具体例 文字列キーを使ったハッシュアクセス Ruby では、ハッシュのキーとして文字列(String)を使う場合に、この形式でアクセスします。 model = { 'full_name' => ' AIR JORDAN 13(エアジョーダン13)' } puts model['full_name']  # => " AIR JORDAN 13(エアジョーダン13)" 文字列キーアクセスを使う場面 JSON パーサーからのデータ: JSON を Ruby でパースした場合、デフォルトではハッシュのキーが文字列になります。 シンボルキーを使ったハッシュアクセス Ruby では、ハッシュのキーとしてシンボル(Symbol)を使うことも一般的です。この場合は、:full_name のようにしてアクセスします。 model = { full_name: ' AIR JORDAN 13(エアジョーダン13)' } puts model[:full_name]  # => " AIR JORDAN 13(エアジョーダン13)" オブジェクト形式(OpenStruct)のアクセス OpenStruct を使うと、ドット 演算子 (.)でプロパティにアクセスできます。 require 'ostruct' model = OpenStruct.new(full_name: ' AIR JORDAN 13(エアジョーダン13)') puts model.full_name  # => " AIR JORDAN 13(エアジョーダン13)"   確認方法 model.class を実行することで、オブジェクトがハッシュなのか、OpenStruct なのか、あるいは別の型なのかを確認できます。その型に応じたアクセス方法を選びましょう。   クラス内の インスタンス 変数の確認方法 インスタンス 変数の取得方法も初めは時間がかかりました。 docs.ruby-lang.org p obj.instance_variable_get(:@foo)  このように取得するみたいです。   シンボルは Ruby のみ存在? Python 、 PHP 、C、Go、 JAVA などには存在せず、 Javascript には存在するけど、挙動が少し違うようです。 qiita.com 慣れればメリットもあるとの事ですが、他の言語にない概念なので、 Ruby 使い始めは正直紛らわしかったです。  
こんにちは、インフラグループの片桐です。 この記事は Enigmo Advent Calendar 2024 の 14 日目の記事として、サーバ機器管理台帳を 表計算 ツールから OSS の「NetBox」に移行した取り組みについて紹介します。 はじめに サーバ機器やネットワーク機器、各機器に付随する IPアドレス の情報など、サーバ機器や関連情報をまとめる為に「機器管理台帳」は欠かせないものになっています。その中で、機器管理台帳は Excel や Google スプレッドシート 等の 表計算 ツールで管理されているケースもよく見られます。 表計算 ツールを台帳として機器管理している場合、台帳の規模が拡大するにつれて、管理すべき各種情報が複数のシートやファイルに分散しがちです。これにより管理コストが増大し、整合性の維持も難しくなります。こうした状況に心当たりのある方も多いのではないでしょうか。 弊部署も同様に、 表計算 ツールを用いた煩雑な機器管理に悩まされていました。そこで、データセンターのインフラ管理(DCIM)と IPアドレス 管理(IPAM)を兼ねるツールであるNetBoxを導入し、機器情報管理の一元化・整合性維持を試みました。本記事では、機器管理台帳の 表計算 ツール管理からNetBox移行について、取り組み内容、移行に際する知見等を紹介します。  用語の整理 DCIM DCIM(Data Center Infrastructure Management)は、データセンターに関連するサーバ機器やネットワーク機器、ラック、配電や配線、設備など、さまざまなリソースを管理・可視化するためのツールや手法のことです。DCIMおよびDCIMツールを導入することで、ハードウェア配置や容量、ネットワーク接続状況などを一元管理することが可能となります。 IPAM IPAM(IP Address Management)は、ネットワーク内で使用される IPアドレス を一元的に管理し、その割り当てや利用状況、変更履歴などを可視化・追跡するためのツールや手法のことです。IPAM及びIPAMツールを導入することで、利用可能な アドレス空間 のシステム的な管理と、管理に伴ったIP重複割り当ての予防等が可能となります。 NetBox NetBoxは、DCIMとIPAMの機能を併せ持つ OSS です。サーバ機器やラック構成、 IPアドレス 、ケーブル接続など、データセンターまわりの情報を一元的に、かつわかりやすく管理することに特化しています。 Webインターフェースや REST API を通して各機器やネットワーク関連のデータを登録・編集・参照できるため、機器情報の管理をするだけでなく、管理の簡略化・自動化も可能です。 お試しで触りたい方向けの公式デモ版サイトも公開されています。 github.com 移行の背景と課題 これまで、弊部署の機器管理台帳には複数ファイル/複数シートを跨ぐ Google スプレッドシート を使用しており、以下をはじめとする各種情報を管理していました。 ハードウェア関連:機材情報、シリアル番号、ラック配置、納品日/保守期限、各機材のLAN接続状況 ホスト関連:ホスト名、各 NIC ごとの IPアドレス 割り当て状況 etc... 台帳上の各機器について、あるシートではホスト名と IPアドレス の紐づけ、あるシートではホスト名とラック上の設置位置の紐づけ...等、1箇所のデータを更新した際に関連するデータを複数シート上で合わせて更新する運用でした。 一方、この管理方式には以下の様な課題もあります。 台帳更新の煩雑さ: 1つの機器について複数ファイル/複数シートを跨いで情報が登録されている場合。 シート上のある箇所を変更した際、別シートや別セルの依存箇所も特定して修正する必要がある。 依存箇所を漏れなく特定できる運用フローや仕組み構築が必要になる。 整合性管理の困難さ: 台帳更新の煩雑さ にも関連。 依存箇所の修正漏れが起きやすい状況下にて、依存データ間で整合性の取れていないデータが発生した場合に事実確認の手間が発生する。 外部連携の困難さ: 機器管理台帳に限らず、 表計算 ツールで作成された各種データファイルが完全な CSV 形式で構築されていない場合。 台帳上に存在する情報を読み込んで プログラマブル に利用したい場合や、外部のシステムにデータを取り込む際、各環境が扱える状態に一度変換する手間がある。 これらの課題に対してNetBoxを導入し、対応を試みました。 NetBoxのデプロイ NetBoxのデプロイ先として、社内向けツール用に構築しているEKS( Amazon Elastic Kubernetes Service) クラスタ 上に構築しました。 インストールには、Netbox Communityの GitHub 上で公開されている Helmチャート(netbox-chart) を使用しました。 github.com helmを利用する場合は 利用ガイド を読みながら、ご自分の環境に合わせて values.yaml の各値を変更してセットアップする流れとなります。 特に 管理者ユーザ周りの設定値 はログイン時に使用するため、ご利用の環境に合わせて事前設定することをおすすめします。 NetBoxへのデータ登録 NetBoxに登録できる項目について データの登録を始める前に、既存の機器管理台帳では何の情報を管理しているのか、NetBoxには何の情報を登録できるのかを一通り把握しておくことを推奨します。 ドキュメントをご参照いただくと分かる通り、NetBoxに登録できる項目は多岐にわたり、かつきめ細かい情報を登録であるため、最初から全ての情報を入れ込むことが難しい場合も想定されます。 (以下NetBox ドキュメントの models 配下の大項目、並びに大項目配下の詳細情報を各機器/各グループ毎に入力可能となっています。) github.com もしスモールスタートで「データセンタ内の機器情報、ラックへの機器マウント情報、各機器へのLAN配線、 VM ホストとゲスト VM の紐づけ、各 NIC への IPアドレス アサイ ン」程度で運用を開始したい場合は、以下図のmodel項目を参考に入力していくことで簡素版な環境を構築可能です。 データの登録 NetBoxの導入を検討した当初は スプレッドシート 上に散在していた情報を1件ずつ確認しながら手動で登録することも考えていましたが、登録すべき情報を整理していく中で、数百台以上の機器や IPアドレス 、接続情報等をすべて手動で移行するのはあまりにも現実的ではない事に気付きました。 一方、NetBoxには機器追加/変更/削除等のデータ操作を楽にする為の仕組みが複数整備されており、一括でのデータ操作が可能となっています。 CSV / YAML 形式での一括インポート: NetBoxのWEBインターフェースの機能として、 CSV / YAML 形式でのデータの一括インポートに対応しています。 登録するデータを各形式に整形して事前に整備出来ている場合、各model毎のWEB UI上から楽にデータをインポート可能です。 1行目に各modelに対応するヘッダを、2行目以降に登録するデータを入力する形で定義します。 CSV 形式インポートの入力例 REST API でのデータ操作: NetBoxに実装されている REST API を活用することで、各種データ操作を プログラマブル に行う事も可能です。 CSV でのインポートはデータの登録のみ可能ですが、 API を利用することで CRUD 各処理を複雑な条件式を絡めて実行することも可能となります。 Netbox API Document 上記2種類の一括操作方法を実施して、既存の機器管理台帳上のデータを移行していきました。 スプレッドシート から移行する際の知見 移行前の情報を整理した上で、完全に正規化された CSV を作成するべき 移行前の スプレッドシート には現状との乖離として、例えば以下の様な不整合が発生していました。 新しいホストに付け替えた IPアドレス が古い機器に付随されたままになっている。 ホスト名を変更したが、旧ホスト名のまま台帳に登録されている。 不整合を一つ一つ修正するのは非常に手間の掛かる作業となる上、独自管理の形式から完全な状態の CSV 形式に落とし込むことは、手間と時間の掛かる作業となります。 一方、NetBoxには強力な CSV インポート機能が備わっているため、一度完全な状態の CSV を作成してしまえば、NetBoxの運用は直ぐにでも開始可能です。 私は不整合の解消作業は実施したものの、完全な状態の CSV を作成する前に移行を始めてしまった為、変更が必要な所は REST API を使用して適宜修正する形での対応となりました。 再度移行作業を実施する機会のある際には、完全に正規化された CSV を作成してから実施すべきと実感しました。 NetBoxに登録する情報の精査と入力先カラムの確認 前述の完全に正規化された CSV を作成した後は、 CSV 上のカラムをNetBox上のどのモデルに入れるかを予め検討しておくべきでした。 ラックの情報は dcim > racks に、機器のモデル名やシリアルは dcim > devices に、IPは ipam > ip-addresses に登録出来る、あるいはこの項目について入力できそうな項目が存在しない、等を先行して把握しておくべきでした。 例えば、NetBoxには機器の納品日や保守期限といった項目を入力出来る箇所が標準項目としては存在しません。 そのため、機器情報の自由記述欄となる Comment カラムに入力するか、 カスタムフィールド を追加することで、標準機能外の情報管理もNetBox上で可能になります。 一括登録を実施する前に、整理された CSV の項目とNetBoxの入力項目を比較して、このデータは何処に入るのか?を明確にしておくとスムーズなデータの投入が可能でした。 カスタムフィールドで機材納品日の入力欄を追加する例 NetBox導入によって感じたメリット 情報の一元管理と可視化: NetBox移行後は IPアドレス 、機器モデル、シリアルナンバー、物理配置、接続関係など、あらゆる情報をNetBoxで一元的な管理が可能となりました。 スプレッドシート を利用していた頃は、ある箇所を修正した際依存する情報の修正を、複数シート/複数ファイルに跨って手動で行う必要がありました。 NetBox移行後には依存情報の修正も行われるため、整合性保持にも寄与します。 エクスポート/インポートの柔軟性: 前述の通り、NetBoxにデータを登録する際は CSV / YAML 形式でのインポートが可能です。 また、NetBoxに登録されている情報は、 CSV / YAML 形式でのエクスポートも可能であるため、将来的に別の管理基盤へ移行する際にもデータ移行が容易になることが見込まれます。 拡張性と自動化: NetBoxには REST API や各種WebHookが実装されています。 API による台帳上のデータの一括操作が可能になった他、WebHookによって台帳に新規機材が追加された際にSlackなどチャットツールに通知する等の対応が可能となりました。別途仕組み作りを実施することで柔軟な対応が可能となるため、今後の拡張性として期待が高まりました。 サー バラック の情報可視化例。ラックの色のついている部分には登録した機器が紐づいている まとめ スプレッドシート からNetBoxへと台帳管理を移行した結果、情報一元化や更新作業の効率化が期待できる基盤が整いました。初期構築やデータ整備に手間はかかりましたが、その分、長期的な整合性維持と運用自動化の可能性が大きく広がりました。 一方、現時点では、NetBoxの基本的な機能を用いて、ハードウェアと IPアドレス 、簡易な接続情報を一元管理する段階にとどまっています。今後は運用を通じて API やWebHookの活用や NetBoxのプラグイン導入 など、さらなる効率化と可視化を進めていきたいと考えています。 明日の記事の担当はWEBアプリケーションエンジニアの小松さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは!インフラエンジニアの森田です! この記事は Enigmo Advent Calendar 2024 の12日目の記事です。 また、11日目の Googleスプレッドシート編 の続きであるため未読であればそちらからご一読いただければと思います。 前回の記事ではZapierで扱うデータのインプットとなる スプレッドシート の解説を行いましたので、今回は処理とアウトプットを行うZapの解説を行います。 現在以下のZapが稼働していますが、 毎月22日に翌月の発表者へまとめてリマインド 毎週月曜日に発表者へリマインドし、発表予定がなければ募集する 発表があれば開始のアナウンスをし、発表予定がなければスキップのアナウンスをする 今回は翌月の発表者へまとめてリマインドするZapの解説を行いたいと思います。 毎月22日に翌月の発表者へまとめてリマインド 1.毎月22日にトリガー まず、翌月頭の発表者の準備期間が取れるように毎月22日にトリガーするようにしています。 キャプチャと同じように設定すれば毎月のトリガーとなります。 2.翌月が何月か計算 次の工程で使用するために翌月が何月なのかを取得しています。 具体的には1のトリガーが次に動く日付(翌月の22日)から 正規表現 で何月かを抽出しています。 import re print (input_data[ 'next' ]) result = re.findall( r'-\d{2}-' , input_data[ 'next' ]) return { 'result' : result[ 0 ].replace( '-' , '' )} 3.翌月の行を取得 翌月発表の行をまとめて取得してきています。 具体的には「リマインド管理シート」に開催月という列があったと思いますが、開催月が2で取得したものと同値かつ開催フラグがTRUEの行を取得しています。 注意が必要なのがEventの設定で、「Lookup Spreadsheet Rows」と「Lookup Spreadsheet Rows (Advanced)」という似たイベントが存在しますが、「Lookup Spreadsheet Rows」では取得できる行が1行のため、翌月の発表全てというような複数行取得したい場合は「Lookup Spreadsheet Rows (Advanced)」を選択する必要があります。 4.Slack送信メッセージ作成 3で取得した要素をSlackのメッセージへ整形するため python で実装しています。 date_list=input_data[ 'date' ].split( ',' ) title_list=input_data[ 'title' ].split( ',' ) SlackID1_list=input_data[ 'SlackID1' ].split( ',' ) result = f ''' 【{input_data['month']}月 Hacker’s Delightスケジュールのお知らせ】 {input_data['month']}月のスケジュールの予定はこちらです。発表者の方は準備をよろしくお願いします。 何かあれば @devrel へご連絡ください ''' for i in range ( len (date_list)): string = f " \n {date_list[i]}:{title_list[i]} <@{SlackID1_list[i]}>" result = result + string print (result) return { 'result' : result} それぞれの発表の部分はリストの要素分ループで回して戻り値へ加算(追記)していく形になっています。 5.Slackへ送信 4で整形したメッセージ(戻り値)をシンプルにSlackのチャンネルに送信しています。 実際に以下のようなメッセージが送信されます。 Zapの解説は以上になります。 おわりに 今回は1つのZapを用いて解説を行いましたが、11日目の記事で解説したインプットがあれば 毎週月曜日に発表者へリマインドし、発表予定がなければ募集する 発表があれば開始のアナウンスをし、発表予定がなければスキップのアナウンスをする 上記のようなリマインドも実装することができます。 また、今回 エニグモ で行っている アドベントカレンダー のリマインドも同じ要領で自動化しています。 スケジュールを把握して色々なメンバーにリマインドをするというのは思いの外意識を割かれてしまうため、 Google スプレッドシート とZapierが利用できる環境にあれば是非自動化にチャレンジしていただければと思います。 2記事に分かれて長かったですが、お読みいただきありがとうございました。 明日の担当はRDチームの 廣島 さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは!インフラエンジニアの森田です! この記事は Enigmo Advent Calendar 2024 の11日目の記事です。 私は社内の勉強会チーム(DeveloperRelationsチーム)としても活動しており、 人力で行なっていたリマインドをZapierで自動化したのでそのご紹介をしたいと思います。 今回は毎週金曜日に実施している軽い勉強会( Hacker'sDelight )のリマインドの自動化を例に記載します。 元々は以下のようなスケジュール管理シートがあり、こちらを人の目で見て翌月の発表者やその週の発表者にSlackでリマインドを送るという運用をしていました。 ただずっと人力でリマインドを送るというのはツラいため、社内で自動化ツールのZapierを契約しているため折角なら自動化してしまおうということで自動化に着手しました。 調べながらZapを作成する中で意外にZapierの日本語のドキュメントが少なかったため、この記事が備忘録かつ同じような自動化をやりたい方の参考になればと思います。 今回の自動化を構成しているのは大きく分けて以下の3要素になります。 スケジュール管理シート(パブリック) リマインド管理シート(クローズド) Zap 全ての要素の解説を1記事で行うと長くなってしまうため今回は「スケジュール管理シート」と「リマインド管理シート」の解説を行います。 Zapの解説は12日目の記事で解説したいと思います。 スケジュール管理シート(パブリック) 以下が「スケジュール管理シート」です。 基本的に上で貼ったスケジュール管理シートと同様ですが、「リマインド管理シート」でSlackIDをメールアドレスから自動検索するために列が追加されています。 こちらのシートは基本的にパブリックになっており、一般メンバーもHacker'sDelightのカレンダーとして見ることができます。 全て手動で管理しているので特に言及するところはありませんが、この後の解説で関連してくる列は以下となります。 内容 発表者1,2(敬称略) 発表者1,2メアド 日程 リマインド管理シート(クローズド) 「リマインド管理シート」は「Zapier通知用」と「Slackメンション用IDリスト」の2つのシートからなります。 Zapier通知用 このシートは「スケジュール管理シート」から参照した値から計算して自動的に埋められるため、手動での管理は不要になっています。 それぞれの列の解説をします。 日程 発表内容 発表者1,2 発表者1,2メアド これらの列は全て「スケジュール管理シート」からそのまま参照しています。 別の スプレッドシート から値を参照してくるには IMPORTRANGE関数 を使用します。 例) =IMPORTRANGE("https://docs.google.com/spreadsheets/d/xxxx","2025!G4") また、発表者の部分は敬称を スプレッドシート 側で入れておきたいので カスタム数値形式 で「@さん」と入れることで自動で敬称を付与するようになっています。 開催月 MONTH関数 を用いて何月に開催されるのかを抽出しています。 これは翌月の発表をまとめてリマインドするのに使用します。 例) =MONTH(A2) 当日までの日数 日程のセルから TODAY関数 で今日の日付をマイナスすることで発表当日まで何日なのかを算出することができます。 例) =A2-TODAY() メンション用ID VLOOKUP関数 で「Slackメンション用IDリスト」から発表者メアドに対応した IDを取得してきています。 元々人力でメンション用IDを探してコピペしていましたが辛すぎるうえミスが分かりずらいため、IDは自動で取得されるようにした方が良いと思います。 例) =VLOOKUP(F2,'Slackメンション用IDリスト'!$A$3:$B$1435,2,FALSE) 開催フラグ 発表があるのかないのかを判別するために ISBLANK関数 で発表内容のセルを空白判定しています。 また、そのままだと発表がある(セルが埋まっている)とFALSEと判定され違和感があるため、 NOT関数 で逆の論理値が返されるようにしています。 例) =NOT(ISBLANK(D2)) Slackメンション用IDリスト 以下のドキュメントを参考にメンバーリストを取得すると、ユーザーのメールアドレスとUserIDが記載された csv ファイルが取得できます。 ワークスペースのメンバーリストをダウンロードする こちらのシートは csv の内容をそのままコピペしたものです。 Zapierを使ってユーザーにメンションをする際にはこのUserIDが必要になります。 注意が必要なのはこのメンバーリストがSlackのオーナーまたは管理者権限を持っていないと取得できない点です。 不特定多数が見られる場所に書かれるのは好ましくないため、「リマインド管理シート」の参照権限はクローズドで必要のあるメンバーのみが見られるようにするべきです。 Zapierでリマインドを行うためのインプットとなる スプレッドシート の解説は以上となります。 明日の記事では実際にリマインドを行う部分であるZapの解説を行います。 株式会社 エニグモ すべての求人一覧 hrmos.co
この記事は 私は、株式会社 エニグモ でチームのマネージャをしている後藤です。 マネージャ業務の傍ら開発作業を行うこともあります。 この記事は Enigmo Advent Calendar 2024 の10日目の記事です。 この記事では多くの Ruby を使っている人が、使っているであろう ruby-lsp を少し便利にする ruby-lsp-addons について紹介しながら、 VSCode でどのように活用できるのかについて紹介したいと思います。 ruby -lspとは ruby-lsp とは、 Ruby 言語向けに Microsoft が定義している、 Language Server Protocol を実装したサーバになります。 Language Server Protocol (以下 LSP ) は、プログラム中に定義されたクラス名、関数名、変数名などを取得できる API とデータ型を定義したものになります。プログラム中のデータを取得する API を定義しておくことで、エディタから対象言語に対応した LSP サーバーを通して 関数定義情報を表示、関数/変数の定義箇所へのジャンプ、関数名/変数名を使った入力補間ができるようになります。 LSP の登場以前は universal-ctags + GNU Global などを用いて同様の機能を実現することが多かったように思いますが、 VSCode の登場と合わせて LSP の提案と普及が進みました。その結果、多くのエディタで LSP がサポートされるようになり、最近は補間や関数ジャンプのための機能が LSP に集約されつつあるように思います。 Visual Studio Code ( VSCode ) の場合は、 ruby-lsp プラグイン を VSCode に追加することで ruby ソース編集次に ruby -lsp を使った関数ジャンプや入力補間が使えるようになります。 ruby -lsp-addons ruby -lsp ですが、当然ながら ruby の文法しかサポートしていません。ただ、 Ruby on Rails の場合はモデルの has_many、blongs_to、validates などなど多数の DSL がありこれらもOUTLINEなどに表示して欲しいところですが、その機能を ruby -lsp に実装するのも違っている気がします。 そんな問題を解決しようとしているのが、 ruby-lsp-addons 機構になります。 公式のドキュメントは Add-ons | Ruby LSP にあります。ただ、公式ドキュメントにもあるように実験的な取り組みのようで、 ruby-lsp のメジャーバージョンが変わると、 インターフェイス が変わって addons が動かなくなることもありますし、将来a addons のサポートが終わってしまう可能性があることを心にとめて置いてください。 それでは、 ruby-lsp-rails ruby-lsp-rspec について紹介していきたいと思います。 ruby-lsp-rails こちらの add-on を使えるようにするには、 ruby-lsp-rails gem を追加します。 名前からわかる通り、 rails 固有の機能サポートを追加するadd-onになります。 今回は、 gitlabhq/gitlabhq のソースの一部を表示しながら、 公式ドキュメントからいくつかの機能を抜粋して紹介したいと思います。 modelの DSL サポート # frozen_string_literal: true module Achievements class Achievement < ApplicationRecord include Avatarable include StripAttribute belongs_to :namespace , inverse_of : :achievements , optional : false has_many :user_achievements , inverse_of : :achievement has_many :users , through : :user_achievements , inverse_of : :achievements strip_attributes! :name , :description validates :name , presence : true , length : { maximum : 255 }, uniqueness : { case_sensitive : false , scope : [ :namespace_id ] } validates :description , length : { maximum : 1024 } def uploads_sharding_key { namespace_id : namespace_id } end end end のようなコードがあると、 VSCode の OUTLINE には以下のように Rails 固有の DSL で定義された項目も表示されます。 コントローラーメソッドからviewへのジャンプ コントローラのメソッド画面にviewを開くリンクが追加されます。 上の絵の赤枠で囲ったリンクが表示されるようになります。 この、 Jump to view をクリックすることで view が表示されます。 地味に、便利です。 その他の機能 公式ドキュメントを読むと、モデルの belongs_to のアソシエーション先にジャンプできる機能、コントローラーのメソッド定義箇所から ルーター の定義にジャンプできる機能などがあることになっていますが、上手く機能していないようです。この辺は実験的な取り組みということを温かく見守ることしましょう。 ruby-lsp-rspec 次は、 ruby -lsp- rspec です。 こちらも add-on を使えるようにするには、 ruby-lsp-rspec gem を追加します。 こちらも名前から想像できる通り、 rspec 向けの機能拡張を行う add-on になります。 以下のような specファイルを開くと、 # frozen_string_literal: true require " spec_helper " RSpec .describe Diffs :: StatsComponent , type : :component do include RepoHelpers subject( :component ) do described_class.new( diff_files : diff_files) end let_it_be( :project ) { create( :project , :repository ) } let_it_be( :repository ) { project.repository } let_it_be( :commit ) { project.commit(sample_commit.id) } let_it_be( :diffs ) { commit.raw_diffs } let_it_be( :diff ) { diffs.first } let_it_be( :diff_refs ) { commit.diff_refs } let_it_be( :diff_file ) { Gitlab :: Diff :: File .new(diff, diff_refs : diff_refs, repository : repository) } let_it_be( :diff_files ) { [diff_file] } describe " rendered component " do subject { page } let( :element ) { page.find( " .js-diff-stats-dropdown " ) } before do render_inline component end ... VSCode の OUTLINE に以下のように、 examples のテキストが表示されるため、 spec ファイルの流れを追うのも、修正をするのも楽になります。 また、以下のようにspecファイル上に Run | Run In Terminal | Debug リンクが追加されます。 このRunなどをクリックすることで、 rspec のテストを実行できます。また、これらのテスト機構が、 VSCode の テスト機能に統合されているため、 以下のような VSCode 標準の、 Test: Run Test at Curosr などの機能が利用できるようになります。 また、テスト結果についても GUI で表示られるようになります。 テスト結果がエラーになっているのは、DBなどを構築せずテストだけ実行したためで、gitlab のバグではありません。 使いたいんだけど、プロジェクトの Gemfiles に組み込めないんだけど VSCode の ruby -lsp ですが、インストール時の設定では、プロジェクトに bundle install されているものを使う設定になっていると思います。そのため、プロジェクトの他のgemとの依存関係の問題で使いたくても最新の ruby-lsp が使えない、なんて問題に出会う事があると思います。 そのような場合は、 ruby-lsp などの ruby 開発サポート系ツールだけをインストールした リポジトリ を準備し、bundler でインストールした bin ディレクト リへ PATH を通しておいて、 VSCode 上の設定を変更することで、プロジェクトは独立した形で、 ruby-lsp を使うことができます。 手順は以下のようなGemfileを準備し # frozen_string_literal: true source ' http://rubygems.org ' gem ' ruby-lsp ' gem ' ruby-lsp-rails ' gem ' ruby-lsp-rspec ' 対象 ディレクト リで、 bundle config path vendor bundle install しておき、 VSCode の settings.json にいかの項目を追加します。 " rubyLsp.bundleGemfile ": "$ { インストールディレクトリ } / Gemfile " 私の場合は、 asdf を使って ruby をいれているのもあってrubocop、erblint などをひとまとめにしたチェック系ツールをまとめた リポジトリ を作り、homebrewと asdf 用の環境セットアップをするラッパー スクリプト を通して、bundle exec する シェルスクリプト から各種ツールを起動する リポジトリ を作って使っています。 また、PCが変わっても VSCode の設定が変わらないよう上記 リポジトリ を /opt/ruby_tool へcloneしています。 参考のために、 ruby_tool へのリンクも貼っておきます。頻繁にバージョンを上げたり、ツールを入れ替えたりしているので、ご利用される際は、forkしてお使いください。 最後に 最後までお読みくださりありがとうございました。 明日の記事の担当は 森田 さんです。お楽しみに。 また、株式会社 エニグモ では、ツールやエディタのカスタマイズを 愛する人 エンジニアもそうでないエンジニアも広く募集しております。興味のある方は、以下の求人一覧をご覧ください。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、データアナリストの井原です。 この記事は Enigmo Advent Calendar 2024 の9日目の記事です。 私は、普段データアナリストとして エニグモ で働いています。データからビジネスの意思決定を行うための示唆を出し、関係者に正しく伝えることが主要な業務です。 最近、チームの勉強会でも話題になったのですが、データアナリストは伝え方って大事だよねという話がありました。その場ではさらっと話が進んだのですが、確かに、データアナリストはいつもポジティブな結果ばかりを伝えるわけではありません。企画者が苦労をしてリリースにこぎつけた施策で、結果も期待されていてという状況で、数字がよくなかった、または、懸念点が上がってきたということも多いです。 この記事では個人のこれまでの経験を振り返って、ネガティブなことを伝えなければいけない時に、どのようなことに気をつけたらよいかを考えてみたいと思います。 この1年を振り返ってみても、結果を伝えにくいと思う案件はいくつかありました。「事前検証ではそれなりに効果が出そうであると(自分が)押した案件が、蓋を開けてみると効果はかなり微量だった。」「これまで重点的に企画が行われてきた施策について、利益性が低そうだと伝えなければいけなかった。」などなど。 そんな自分の経験を通して、どんなことに気を付けるとよさそうか、5つ洗い出してみました。挙げてみた要素はあくまでも個人の意見ではありますし、自分も常にうまくやれているわけではないのですが、自戒もこめて書いていきたいと思います。 1.言いやすい関係性を作っておく いきなり、そもそもといった内容にはなりますが、元々の関係性はかなり重要な要素だと思います。 これは、言う側だけでなく言われる側の身を想像しても、それまで関係性の薄い第 三者 から言われるよりは、自身の取り組みを知ってくれている人の方が受け入れやすくなると考えられます。 そのためには、可能な限り、企画の段階でデータアナリストも企画に入っていけるようにするとよいと思います。また、分析の時点から入らざるを得ないとしても、こまめに質問や進捗を伝えるなどして、コミュニケーションを増やしておくといざという時には伝えやすくなります。 巨大な組織だと難しいところもあるかもしれませんが、 エニグモ は比較的少人数の組織なので、その辺りはやりやすい気はしています。 2.言葉遣いに気を付ける 当たり前ではあるのですが、伝える時の言葉遣いは気を付ける必要があります。特に意識をしていないと、さらっと否定的な言葉が(そこまで否定的だと意識せず)つい出てしまう場合があります。 例えば、「意味がない」とか「ダメですね」のような全否定の言葉は使わないように自分は気をつけます。「この数字だとちょっと厳しいかもしれません」や「想定と違うかもしれませんが、どうですか?」のような、相手と会話していく姿勢を出すだけでも建設的に話せるのではないかと思います。 前項とは反対になりますが、言いやすい関係を作りすぎてしまうと、ぽろっと否定的な言葉も出やすくなってしまう(気軽に話せるので言葉を意識しなくなりやすい)側面もあるので、この辺りはバランスを考えてうまく調整出来るとよいのではないかと思います。 3.最初に「言いづらい結果なのですが」と言ってしまう これはテクニック的な話ではありますが、会議の最初に言ってしまうと、言う方も聞く方も受け入れる準備が出来るので楽になるのではないかと思います。あるいはタイミングがあれば、「今分析しているのですが、厳しい結果が出るかもしれません」と雑談的に話しておくのもよいでしょう。 根本的にはもう少し他の項目の方が大事ではないかとも思いますが、相手に結果を受け入れてもらうということも、アナリストの重要な仕事の一つです。こういった地味なテクニックを意識しておいて損はないと思います。 4.相手に労いと尊敬の気持ちを持つ 言葉遣いなどに繋がる話ですが、施策を進めているメンバーはその企画に対してかけた労力や思いがあります。その労力や思いに対しては、労いと尊敬の気持ちを持つことが大事です。 相手の企画に対しての思いを知る過程で関係性も作れると思いますし、言葉遣いも下手な言葉遣いはしにくくなると思います。例えば、相手が大切に思っていることに対して、「意味ないよ」などとは、ある程度の良識があれば言いにくいですよね。 5.それでも遠慮はしない、自分の分析結果には自信を持つ 相手を思いやることは大事だと思いますが、一方で、データアナリストとして伝えるべきことは伝えなければなりません。 そのため、最終的には自分の分析結果に対して自信を持つことも大事です。 伝えづらい分析結果が出てきた時、多くの分析者は分析内容を振り返ったり、よさそうな点を見つけようとデータをさらに詳細に区切って細かい分析を行ったりなど、よくあるのではないかなと思っています。しかし、持てる知識をフル活用して分析を行った結果なのであれば、下した結論がネガティブな内容であっても、それはしっかりと伝える必要があります。 伝え方自体は上で書いたように気を付ける必要がありますが、結論はしっかりと相手に伝わるように、準備できればよいと思っています。 おわりに 自分の一年間を振り返りつつ、ネガティブなことを伝えなければならない時に気を付けていること(きたこと)を洗い出してみました。 世の中には、データから新しい知見を発見して、それを活用して成功しました!というポジティブな話が多いと思いますが、現実のデータ分析では、新しい発見やポジティブな結果が確認できることばかりではありません。むしろ、特に目新しくない結果、ネガティブな結果が出てくる方が多いと思われます。 データアナリストという立ち位置は、時に第 三者 的な視点での意見が求められる職種ですので、データの解釈は冷静に客観的に行わなければなりません。しかし、それを伝えるところまで責任を持つという点では、合理性や客観性以外の人情的なコミュニケーションも必要になってくると思います。 少しでも、今回の話がデータを取り扱う方々の参考になったら幸いです。お読みいただきありがとうございました。 明日の記事の担当はエンジニアの後藤さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは!Webアプリケーションエンジニアの レミー です! この記事は Enigmo Advent Calendar 2024 の 8日目の記事です。 Ruby on Rails 8が新しくリリースされ、Kamalという迅速かつ便利なデプロイツールが統合されました。私はこれまで Ruby on Rails アプリケーションのデプロイに Capistrano を使用していましたが、Kamalを試してみると、その便利さと簡単さに魅了されました。 この記事では、Kamalを使用して Ruby on Rails 8アプリケーションを AWS EC2サーバーにデプロイする手順を詳しく説明します。 Kamalとは? Kamalは 37Signals のチームによって開発された新しいデプロイメントツールです。このツールを使用すると、デプロイメントプロセスを1つのファイルで定義でき、複雑な手順を簡素化できます。ほとんどのアプリケーションで考慮する必要がない複雑な部分を省略することが可能です。Kamalは、DockerコンテナやTraefikなどのソフトウェアを組み合わせて動作します。セットアップ段階から包括的なソリューションを提供し、 Rails アプリケーションを最短時間で本番環境にリリースできます。 Kamalはどのように動作するのか? Kamalは、サーバー上でDockerコンテナ内にWebアプリケーションを実行し、Traefikを組み合わせてネットワーク トラフィック を処理します。新しいバージョンをデプロイする際、Kamalは以下の手順を実行します: 新しいDockerイメージをビルドする。 新しいイメージからコンテナを起動する。 新しいコンテナが正常に動作しているかを検証する。 Traefikを更新して、 トラフィック を新しいコンテナにルーティングする。 古いコンテナを停止する。 特に注目すべき点は、ダウンタイムなしのデプロイメントを実現し、blue/greenデプロイメントをサポートしていることです。 Ruby on Rails アプリケーションのサーバーの準備 ここでは、 AWS EC2でサーバーを準備します。 EC2 インスタンス の作成 Ubuntu Server 24.04 を選択します。 Keyペアは SSH とサーバーへのアクセスに使用します。 セキュリティグループでは、以下のポートを開放してください: 22 : SSH 用 80 : HTTP用 443 : HTTPS 用 SSH 権限の設定 まず、ローカルの公開鍵をコピーします。 cat ~/.ssh/id_rsa.pub インスタンス を起動したら、先ほど作成したKeyペアを使って、 ubuntu ユーザーでサーバーに SSH 接続します。 IPアドレス は新しく作成した インスタンス のものを使用します。 ssh ubuntu@ 18.182 . 197.19 -i /path/to/key.pem 次に、ローカルでコピーした公開鍵をサーバーの ~/. ssh /authorized_keys ファイルに貼り付けます。 ssh-rsa AAAABEAAAADAQABAAABgQ...AAAAB3Nzac2EAAAADAQABA Yuto MacBook Pro これでサーバーの準備は完了です。以降は以下のコマンドでサーバーに SSH 接続できます。 ssh ubuntu@ 18.182 . 197.19 Ruby on Rails 8アプリケーションの作成 まず、kamaltest という名前で Ruby on Rails 8アプリケーションを作成します。 rails new kamaltest プロジェクト ディレクト リに移動します。 cd kamaltest このアプリケーションは、タイトルと内容を持つ簡単なブログになります。そのため、scaffoldを使用して構築します。 rails g scaffold article title content:text データベースを作成します。 rake db:migrate routes.rb ファイルでホームページを設定します。 root "articles#index" アプリケーションを起動します。 bin/dev その後、 http://localhost:3000 にアクセスすると、アプリケーションが起動していることが確認できます。 deploy.ymlファイルでKamalを設定する Ruby on Rails 8ではKamalが既に統合されています。それ以前のプロジェクトにKamalを追加する場合は、Gemfileに kamal を追加し、以下のコマンドを実行します。 kamal init このコマンドを実行すると、Kamalがアプリケーションをデプロイするための設定を含む config/deploy.yml ファイルが生成されます。 Kamalの基本設定 デプロイのための設定は以下のようになります。 image: yutoyasunaga/kamaltest servers: web: - 18.182.197.19 # server IP proxy: ssl: true host: kamaltest.sampleapp.net registry: username: yutoyasunaga password: - KAMAL_REGISTRY_PASSWORD ssh: user: ubuntu image : Docker Hub上に保存されるDockerイメージの場所 servers web : サーバーの IPv4 アドレス proxy ssl : true に設定すると SSL が自動的に設定されます host : 使用する ドメイン registry username : Dockerアカウントのユーザー名 password : Dockerアカウントのログ インパス ワード。セキュリティのため、 KAMAL_REGISTRY_PASSWORD シークレットを通じて取得します。 ssh user : サーバーに SSH 接続する際のユーザー Kamalのシークレット 機密情報は .kamal/secrets ファイルに配置します。以下はその例です。 # 環境変数からレジストリパスワードを取得 KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD シークレットは 環境変数 から取得します。例えば、Dockerのパスワードが hogehoge の場合、以下のように設定します。 export KAMAL_REGISTRY_PASSWORD= 'hogehoge' 以下のコマンドで値を確認できます。 echo $KAMAL_REGISTRY_PASSWORD Kamalでデプロイを開始 config/deploy.yml の設定が完了したら、以下のコマンドを使用して初回のデプロイを開始します。 kamal setup このコマンドは以下の処理を行います: SSH キーを使ってサーバーに接続。 サーバーにDockerがインストールされていない場合は、get.docker.com を使ってDockerをインストール( SSH 経由でrootアクセスが必要)。 レジストリ にローカルおよびリモートでログイン。 アプリケーションのルートにある標準的なDockerfileを使用してDockerイメージをビルド。 イメージを レジストリ にプッシュ。 レジストリ からイメージをサーバーにプル。 kamal-proxy がポート80および443で トラフィック を受け入れることを確認。 現在のGitバージョンのハッシュに一致するアプリケーションバージョンで新しいコンテナを起動。 新しいコンテナが GET /up リク エス トに対して200 OKを返す場合、kamal-proxy に トラフィック を新しいコンテナにルーティングさせる。 前バージョンのアプリケーションを実行している古いコンテナを停止。 使用されていないイメージや停止したコンテナを削除して、サーバーの容量を確保。 デプロイが成功すると、次のような結果が表示されます。 Finished all in 70.0 seconds 初回のデプロイは kamal setup を使用しますが、2回目以降のデプロイでは kamal deploy のコマンドを使用します。 permission denied エラーの解決方法 初回のデプロイで以下のようなDockerに関連するエラーが発生した場合: Releasing the deploy lock... Finished all in 47.9 seconds ERROR (SSHKit::Command::Failed): Exception while executing on host 54.250 . 243.158 : docker exit status: 1 docker stdout: Nothing written docker stderr: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.47/images/create?fromImage=yutoyasunaga%2Fkamaltest&tag=4985d03bae739286203ce1185efdd4b2c71f90a9" : dial unix /var/run/docker.sock: connect: permission denied 次の手順で解決できます。 サーバーに SSH 接続します。 ssh ubuntu@ 18.182 . 197.19 以下のコマンドを実行して、現在のユーザーをDockerグループに追加します。 sudo usermod -aG docker $USER && newgrp docker Dockerが正常に動作するか確認します。 docker ps 以下のような出力が表示されれば成功です。 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES デプロイ後の結果を確認 まず、 https://hub.docker.com にアクセスして、イメージがプッシュされていることを確認します。 Route53を使用して ドメイン を管理している場合、以下のようにホストゾーン内でレコードを作成し、 ドメイン をサーバーのIPに向けます。 設定が正しい場合、 https://kamaltest.sampleapp.net にアクセスすると、アプリケーションの画面が表示されます。 このように、1分ほどで Ruby on Rails アプリケーションをサーバーにデプロイし、 ドメイン と SSL の設定まで完了しました。便利ですね! 🎉 よく使われるKamalコマンド aliases: console: app exec --interactive --reuse "bin/rails console" shell: app exec --interactive --reuse "bash" logs: app logs -f dbc: app exec --interactive --reuse "bin/rails dbconsole" デフォルトの deploy.yml ファイルでは、以下の4つの便利なコマンドが定義されています。 kamal console : Rails コンソールにアクセス kamal shell : サーバー上のコンテナにアクセス kamal logs : サーバーログを確認 kamal dbc : データベースコンソールにアクセス Kamalでのアセットのデプロイ RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile Dockerfile にはすでにアセットをプリ コンパイル するコマンドが定義されています。そのため、デプロイ時にアセットも自動的に処理されます。 Kamalでの 環境変数 (Environment Variable) 開発環境の 環境変数 の設定 開発環境では、 dotenv gem を使用します。 公式 リポジトリ : https://github.com/bkeepers/dotenv Gemfileに dotenv を追加 以下のコードを Gemfile に記載します。 group :development, :test do gem 'dotenv' end 環境変数 を管理する .env ファイルを作成し、例は以下のように記載します。 TEST_ENV_CLEAR=env_clear_local TEST_ENV_SECRET=env_secret_local 環境変数 が正しくロードされているかを Rails コンソールで確認します。 ENV .select { |key, _| key.start_with?( " TEST_ENV " ) } => { " TEST_ENV_CLEAR " => " env_clear_local " , " TEST_ENV_SECRET " => " env_secret_local " } 本番環境の 環境変数 の設定 現時点では以下の方法を使用していますが、より良い方法が見つかれば変更する予定です。 本番環境用の 環境変数 を管理する .env.production ファイルを作成します。 TEST_ENV_SECRET=env_secret_prod deploy.yml ファイルに 環境変数 を設定します。 env: secret: - TEST_ENV_SECRET clear: TEST_ENV_CLEAR: env_clear_prod clear : deploy.yml ファイルに直接記載される 環境変数 。 secret : 機密情報を含む 環境変数 で、 .kamal/secrets ファイルから読み込まれます。 .kamal/secrets ファイルで 環境変数 を以下のように設定します。 TEST_ENV_SECRET=$(cat .env.production | grep TEST_ENV_SECRET | cut -d '=' -f 2) 上記の設定は、 .env.production ファイルから情報を抽出します。 例えば、 .env.production に以下の行が含まれている場合: TEST_ENV_SECRET=env_secret_prod コマンド cut -d '=' -f 2 によって env_secret_prod が抽出されます。 デプロイ後、 Rails コンソールにアクセスして 環境変数 を確認できます。 kamal console ENV .select { |key, _| key.start_with?( " TEST_ENV " ) } => { " TEST_ENV_SECRET " => " env_secret_prod " , " TEST_ENV_CLEAR " => " env_clear_prod " } Kamalは、 Ruby on Rails アプリケーションのデプロイを大幅に簡素化し、効率化する強力なツールです。ぜひKamalを使ったデプロイに挑戦してみてください! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは!UIUXデザイナーの和田です。 こちらは Enigmo Advent Calendar 2024 の7日目の記事です。 この記事では、定期的に実施している BUYMA のユーザーインタビューについてご紹介します。 また、ユーザーインタビューで得た気付きをもとに新設した【 まるわかりBUYMA公式スタートガイド 】についても後述します。 www.buyma.com 1. ユーザーの声を直に聞くメリット エニグモ では、社内のデータアナリストがBUMYAの利用データを分析して日々サービス改善に努めています。 定量 的な分析に加えて、的確なUX改善・向上を進めていくためには、ユーザーさまの声を直にお聞きして、なかなか数値からは解明できない 【ユーザーの行動の背景・ インサイト 】 をより深く理解することが重要だと考えています。 BUYMA では、ユーザーさまの声を直にお聞きするために、 定期的にオンライン形式(Zoom)のユーザーインタビューを実施しています。 社内のUIUXデザイナーとデータアナリストがインタビューアーをすることで、ユーザーさまからいただいたご意見やヒントをもとに「すぐに詳細な分析をおこない、施策化して改善を進められる」ようにしています。 2. BUYMA のユーザーインタビュー BUYMA では、ユーザーさまを対象に実施しているユーザーアンケートの中でインタビューにご協力いただける方を募っています。 毎回とても多くの方に、インタビューへのご協力に前向きな回答をいただいており、全国男女問わず幅広い年代の方にご参加いただいています! ユーザーインタビューの内容についてご紹介します。 🌟 2-1.ユーザーさまご自身について ユーザーさまご自身についてのお話を広くお聞きしています。 主に、『普段の生活スタイル』や『どのような「価値観・嗜好」をお持ちなのか』といった点や、 BUYMA との接点になりそうな「ファッション情報にふれる機会」「お買い物傾向」に関するお話などを伺っています。 🌟 2-2.サービス利用に関する ヒアリ ング ユーザーさまが普段 BUYMA を『どのように利用して』『どのように感じていらっしゃるか』、実際の利用エピソードをもとにプラスとマイナスの両側面から率直な話をお聞きできるようにしています。 ・ BUYMA の利用をはじめたきっかけ ・ BUYMA を使う理由( BUYMA の価値) ←→  BUYMA を使わなくなった理由(離脱理由) ・ BUYMA の気に入っているところ ←→ いまいちだと感じるところ、残念なところ ・ BUYMA を安心して利用できるポイント ←→  BUYMA に不安を感じるポイント ・ BUYMA でのよかった・満足できたエピソード ←→  BUYMA でのマイナスエピソード BUYMA において価値を感じていただいているポイント → より多くの方にその価値を広げていけるように BUYMA における課題・ペイン → 課題の背景を理解して適切な改善につなげていけるように 上記のように、プラス・マイナスそれぞれの気付きをもとに、UX改善・向上にむけた検討を進めています。 🌟 2-3.利用を見せていただく(行動観察) 実際にアプリやサイトを利用している様子を画面共有いただいて、どのように BUYMA をご利用いただいているか見せていただきつつ、お話を伺っています。 例えば、ユーザーさまの普段の利用(検索や商品閲覧・比較検討など)に即したシナリオをベースに、どのような操作をどのような手順でされているのか、利用の中の些細なつまずきやペインがどういったところにあるか、などを操作いただきながら ヒアリ ングを進めています。 🌟 2-4.サービスに関する認知・理解に関する ヒアリ ング BUYMA サービス・機能における「認知(知っていたか)」や「理解(理解できていたか)」についてもお聞きしています。 『どこで認知・理解したか』『どのように解釈していたか』なども率直にお聞きすることで、【対象サービス・機能の露出や導線が十分であるか】など、改善のヒントをいただけるようにしています。 🌟 2-5.検討中の施策に関するコンセプト ヒアリ ング 検討中の施策に関するプロトタイプをZoomミーティングの中で投影させていただきながら、率直な印象やご意見をお伺いしています。 施策を進める前に、初見の印象やニーズにマッチしているかなどを確認することができ、よりよいかたちに細かな調整をかけることができます。 BUYMA のUXリサーチについては、昨年の記事でもご紹介しておりますので、ぜひこちらもご覧ください。 tech.enigmo.co.jp 3. ユーザーの声から「気付くこと」「改めてわかること」がある ユーザーにとっての「ペイン・課題」「 BUYMA の価値」など、ユーザーの声から「気付くことができること」「改めてわかること・理解が深まること」がたくさんあります。 🌟ユーザーにとっての「ペイン・課題」に関する気付き BUYMA 利用におけるつまずきがどんなタイミングで起こっているのか、「 BUYMA 利用におけるペイン・課題」がどのくらいUXに影響を及ぼしているか、など、ユーザーインタビューを通じて具体的に理解を深めることができます。また、サービス提供側として想定していた捉え方と、ユーザーさまの捉え方が異なっていたり、思った以上に浸透していない情報についての気付きを得ることもあります。 🌟ユーザーにとっての「 BUYMA の価値」に関する気付き 一方で、ユーザーさまにとって「 BUYMA の価値」がどのように認識されていて、ユーザーさまのまわり(ご家族やご友人)に浸透しているか、などの気付きを得ることもできます。 以下では、ユーザーインタビューを通じて、改めて BUYMA の価値だと再認識することができた機能についてご紹介します。 世界中からさがす|リクエスト一覧 BUYMA には『リク エス ト』という機能があります。 世界中にいる BUYMA の出品者に、ほしい商品を探してもらうことができる機能ですが、 インタビューでは、この機能を利用して購入経験のある方は「リク エス ト機能の満足度がとても高い」ことがわかりました。 「どこを探しても見つからなかった国内完売商品がほしくて、 リク エス トを利用してみたら見つかって購入できて本当に嬉しかった!」 「ずっと探していたバッグがなかなか見つからず、中古は状態がいいものがなく諦めかけていたけれど、リク エス トを使って購入することができて非常に満足。」 また、インタビューの対象者の中でリク エス ト機能を使ったことがある方は、複数回リク エス トを利用しており、 「探したいと思うアイテムをリク エス トして世界中から探してもらう」体験を気に入ってくださっている傾向がありました。 今後は、「探したいアイテムがあるけれどリク エス ト機能を知らない・使ったことがない方」に、 このプラスの体験をより広げていけるように検討を進めていければと考えています。 4. ユーザーの声からまるわかり BUYMA 公式スタートガイドを作った話 ユーザーインタビューが終わったあとは、まとめ作業を行って主要な気付き( BUYMA の価値、ペイン・課題、仮説検証の結果など)を社内に共有しています。 そこから、UX改善・向上につなげる施策化をおこなっています。 今回は、ユーザーインタビューで得た気付きからコンテンツ作成を行った事例を紹介します。 まるわかりBUYMA公式スタートガイド 4-1.まるわかり BUYMA 公式スタートガイド新設の経緯 インタビューの中では、 BUYMA に関する分からないことや不安なことについてもお伺いしています。 その中で、以下のようなご質問・ご意見をいただくことがありました。 「 BUYMA の運営会社って日本企業なんですか?」 「 BUYMA の商品ってなんで安いんですか?」 「 BUYMA でほしい商品があるけれど購入して大丈夫なのか不安があります。」 このようなご質問・ご意見をいただいたことをきっかけに『 BUYMA を安心してご利用できると判断するための情報の重要性』について改めて認識することができました。 その後、社内で検討を進め「 BUYMA とはどういったサービスなのか」「安心して利用できるのか」などの疑問や不安を解消いただくために、【 まるわかりBUYMA公式スタートガイド 】というページを新設することになりました。ぜひこちらもご覧ください! www.buyma.com インタビューの中で BUYMA に関する分からなさや不安があるとお話されていたユーザーさまから、 いろいろと会話させていただいたインタビューの最後に 「思い切ってインタビューに参加してみてよかった。これまでより安心して買い物ができそう。」 とおっしゃっていただいたことがありました。 ユーザーインタビューを通して、ユーザーさま1人1人に向き合うことの大切さを実感するとともに、 UX改善・向上に向けて一歩ずつ前進するためのエネルギーをいただけていると感じています! 4-2. BUYMA ユーザーの声をよりたくさんの人に広げたい ユーザーインタビューの中で、積極的にご利用いただいているユーザーさまは、「まわりのご家族やご友人も BUYMA をご利用いただいている方が多い」という傾向が見えてきました。 一方で、 BUYMA でのお買い物を躊躇される理由として、「まわりに BUYMA を利用している人がいない」といった方が多くいらっしゃいました。 そこで、『どんな人が BUYMA を利用しているかイメージがつくことで親近感がわくようにするとよいのではないか』という仮説から、 BUYMA をご利用いただいているユーザーさまのお声を BUYMA のコンテンツとして掲載させていただくことにしました。 🌟 BUYMA ユーザーが感じる BUYMA のよさ BUYMA の利用者が「どんなところに BUYMA の魅力を感じてくださっているのか?」より多くの人に知っていただくために、 BUYMA を使う理由についてユーザーさまのご意見をピックアップしています。 BUYMAユーザーに聞く|なぜBUYMAを使ってるの? 🌟 BUYMA ユーザーが安心して利用できる理由 BUYMA を安心して利用してくださっている利用者が「どうして BUYMA を安心して利用できると判断してくださったのか?」より多くの人に知っていただくために、 BUYMA を安心についてお聞きした際のご回答をピックアップしています。 BUYMAユーザーに聞く|安心して利用できてる? www.buyma.com BUYMA についてより深く知っていただき、安心してご利用を続けていただけるように、今後も BUYMA のことをもっと深くわかりやすい情報を発信していければと考えています。 明日の記事の担当は・・・ 【 BUYMA サイトの開発と運用保守をされている レミー さん】です! お楽しみに! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、データサイエンティストの髙橋です。業務では企画/分析/ 機械学習 モデル作成/プロダクション向けの実装/効果検証を一貫して行っています。 この記事では類似画像検索システムの内製化にあたり、システム面での課題をどのように解決したかについて紹介します。内製化の背景や 機械学習 部分などについては 以前作成した記事 で説明しており、この記事はその続きとなります。 この記事は Enigmo Advent Calendar 2024 の 5 日目の記事です。 内製化の目的・事業インパクト 類似画像検索のシステム化における課題 システム化における各課題の解決策 「商品画像ダウンロード > セグメンテーション > Embedding 計算 > アップロード」の高速化 Embedding 同士の類似度計算処理の高速化 画像ハッシュ計算処理の高速化 複数 GCP サービスを連携させてスケジュール実行するアーキテクチャ作成 実装時の工夫 Cloud Composer からの GCP サービス呼び出し方法 Vertex AI Vector Search での類似度計算時にリトライ まとめ 追記:2025/01/21 第48回 MLOps 勉強会で登壇しました! 内製化の目的・事業 インパク ト 内製化の目的は、弊社が運営する CtoC EC サイト BUYMA において、商品 名寄せ で利用している他社製の類似画像検索システムの精度を保ちながらコストを削減することでした。既に内製化後のシステムに移行しており、同等の精度を維持しつつ年間数百万円規模(約8〜9割)のコスト削減を見込んでいます。 また、 名寄せ 以外にも類似画像検索システムを利用している施策があり、その移行も進めることでさらなるコスト削減の可能性があります。さらに、他社製の類似画像検索システムではコストが高く断念していた EC サイト上での画像起点のレコメンドや、画像による類似商品検索機能なども検討出来るようになりました。 より詳細な説明については 前編である機械学習編の記事 を参照ください。 類似画像検索のシステム化における課題 類似画像検索のシステム化における課題として、非常に大規模なデータを現実的な時間で処理する必要がありました。 機械学習編の記事に記載した類似画像検索の各ステップ に対して、毎月処理するデータ量は以下の通りでした。 ステップ 毎月処理するデータ量 商品画像をダウンロードし、物体存在箇所をセグメンテーションして切り抜き 約 200 万画像 切り抜いた画像の Embedding 計算 約 1000 ∼ 2000 万画像 切り抜いた画像・Embedding ファイルを GCS ( Google Cloud Storage) にアップロード 約 2000 ∼ 4000 万ファイル Embedding による画像同士の類似度計算 数十〜数千億の組合せ 画像ハッシュによる画像同士の類似度計算 数十万の組合せ 単純に単一のサーバー上で各ステップを実行する方法では、全体で1ヶ月以上かかる見込みであり、毎月定期的に処理を行うのは現実的ではありませんでした。 また、データ量以外の課題として、弊社では 機械学習 基盤として Vertex AI Pipelines を利用していましたが、今回のシステムはその基盤上に実装できない課題がありました。先述した規模のデータを処理するにはそれに特化した複数の GCP ( Google Cloud Platform) サービスを組み合わせる必要がある一方で、現行の基盤は VM インスタンス 上で Python コードを実行する用途を想定していたためです。そのため、複数の GCP サービスを連携して毎月スケジュール実行する アーキテクチャ を作る必要性がありました。 システム化における各課題の解決策 前述した課題をどのように解決したかを説明します。ただし、前提として類似画像検索の各ステップのうち、以下は1つのかたまりとして処理することにしました。 商品画像をダウンロードし、物体存在箇所をセグメンテーションして切り抜き 切り抜いた画像の Embedding 計算 切り抜いた画像・Embedding ファイルを GCS にアップロード 理由は、セグメンテーションおよび Embedding 計算の両方に GPU が必要であったため、また GCS とのデータのやり取りに時間がかかることから、「商品画像ダウンロード > セグメンテーション > Embedding 計算 > アップロード」の一連のステップを同一メモリ上で行いたかったためです。 「商品画像ダウンロード > セグメンテーション > Embedding 計算 > アップロード」の高速化 Dataflow  という並列分散処理が行える GCP サービスを利用することで高速化を実現しました。Dataflow とは、 GCP が提供するマネージドのバッチ・ストリーミングデータ処理サービスであり、並列分散処理により大量のデータを効率的に処理することが可能です。 今回 Dataflow を選択するにあたり、 Ray on Vertex AI も候補に上がりました。いずれも並列分散処理は実現できそうでした。Ray はその構文がネイティブの Python に近く、既存の Python コードに大きな変更を加える必要がなさそうだったため、実装コストが低そうに見えました。一方で、Dataflow は Apache Beam の構文を理解し覚える必要があり一定の学習コストが伴いそうでしが、社内で利用実績があり困ったときに既存の資産を参考に出来そうであったため Dataflow を選択しました。 実際に、 Dataflow の実装で困ったときに他プロジェクトでの ソースコード を参考にして解決することができ、この選択は正解であったと考えています。また、 Apache Beam の構文もそれほど複雑ではなく、初期の学習コストは多少ありましたが慣れれば実装に大きく手間取ることはなかったです。 約 200 万枚の画像に対して、並列分散処理無しでは約 30 日かかる見込みでしたが、Dataflow により約 56 時間に短縮することができました。 Dataflow の設定は ワーカーマシンタイプ: n1-highmem-4 (vCPU 4 、メモリ 26 GB) ワーカー数: 4 GPU :  nvidia -tesla-t4 としました。ハイメモリのマシンタイプを利用した理由は、メモリ枯渇でジョブが途中で停止してしまうことがあったためです。 Embedding 同士の類似度計算処理の高速化 Vertex AI Vector Search というサービスを利用しました。Vertex AI Vector Search とは、 GCP が提供するマネージドサービスで、膨大な数の Embedding 同士の類似度計算を高速に行うことができます。 Vertex AI Vector Search を利用することで約 1700億個の Embedding の組合せを約 8 時間で処理することができました。単一のサーバー上で処理した場合の処理時間は見積もっていませんが、おそらくこの規模のデータをこの速度で処理するシステムを作るにはそれなりの 工数 がかかったと思います。 Vertex AI Vector Search の設定としては、Algorithm type 、 Shard size はそれぞれデフォルト値である tree-AH 、 Medium で速度やコストに問題がなかったためそのままとしました。 Approximate neighbors count は値を変えて実験したところ検索速度に大きな違いが生じました。具体的には、 約 1 千万件の 768 次元のベクトルに対して類似度計算を 100 回行い、処理速度の統計量を算出したところ以下の通りでした。 num_neighbors mean (sec) std (sec) min (sec) max (sec) 10 0.031 0.013 0.021 0.076 100 0.189 0.034 0.146 0.266 1000 0. 218 0.034 0.17 0.297 10000 0.519 0.149 0.39 1.109 上記実験結果より可能な限り低い値にすることで処理時間が大きく短縮できそうでした。今回の用途では 1 画像に対して同一と検知される見込み画像数は数件程度であったため、それをカバーできる 10 としました。 画像ハッシュ計算処理の高速化 このステップでも Dataflow を利用しました。 約 27 万件の画像の組合せに対して、並列分散処理無しでは約 54 時間かかる見込みでしたが、Dataflow により約 45 分に短縮することができました。 Dataflow の設定は ワーカーマシンタイプ: n1-standard-1 (vCPU 1 、メモリ 3.75 GB) ワーカー数: 120 としました。Dataflow を採用したことで、ワーカー数を自由に変更することができ、120 ワーカーで並列分散処理を容易に実現できました。 複数 GCP サービスを連携させてスケジュール実行する アーキテクチャ 作成 Cloud Composer という Airflow のマネージドサービスを利用しました。Cloud Composer は、 GCP が提供するワークフロー オーケストレーション サービスで、複数の クラウド サービスを連携してスケジュール実行することができます。 今回 Cloud Composer を選択するにあたり Cloud Workflows も候補に上がりましたが、 Airflow の社内での利用実績が豊富であったため Cloud Composer を採用しました。こちらでも困ったときに他プロジェクトの既存の ソースコード が参考になる場面が多く、また移行前の類似画像検索システムの一部で Airflow を利用しており、既存システムの理解がスムーズに出来たメリットもありました。 以下のようなフローで Dataflow や Vertex AI Vector Search などを連携し、類似画像検索の各ステップを実行するシステムを Cloud Composer で実装しました。 開発生産性や保守性を向上させるために、 Dataflow では各ステップごとに Docker Image と Flex Template を、 Cloud Batch でも Docker Image を利用しました。これにより各ステップを独立に開発・テスト・デプロイ出来るようにしました。この部分の詳細については、別途機会があれば記事として執筆する予定です。 実装時の工夫 ここでは、システム実装時の工夫を2つ紹介します。同じようなシステム構成の実装をされる方の参考になれば幸いです。 Cloud Composer からの GCP サービス呼び出し方法 Cloud Composer から 各 GCP サービスを呼び出すに当たり、Dataflow には Airflow に専用のクラス が存在しましたが、 Vertex AI Vector Search には存在しませんでした。そこで、 GCP の REST API ( 例:インデックス作成 API )を呼び出すことでリソースの作成や作成状況のポーリングを行うクラスをカスタムで実装しました。具体的には Airflow の Sensor クラスを利用して以下のようなイメージで実装しました(あくまで ソースコード のイメージとして簡易化したものであり、実際のものとは異なります)。 from typing import Any, Dict from airflow import models from airflow.decorators import task from airflow.sensors.base import BaseSensorOperator @ task (task_id= "create_resource" ) def create_resource (args1: int , args2: str ): """GCP のリソースを作成する(Vertex AI Vector Search のインデックスなど)。 Args: args1 (int): 引数1。 args2 (str): 引数2。 """ # call_create_resource_api 関数は別途実装。内部で GCP の REST API を呼び出す。 response = call_create_resource_api(args1, args2) return response class ResourceSensor (BaseSensorOperator): """GCP のリソース操作の状況をポーリングする。 Attributes: poke_task_id (str): ポーリング対象のタスクID。 args1 (int): 引数1。 args2 (str): 引数2。 """ def __init__ (self, *, poke_task_id: str , args1: int , args2: str , **kwargs): """GCP のリソース操作の状況をポーリングするクラスを初期化。 Args: poke_task_id (str): ポーリング対象のタスクID。 args1 (int): 引数1。 args2 (str): 引数2。 """ super ().__init__(**kwargs) self.poke_task_id = poke_task_id self.args1 = args1 self.args2 = args2 def poke (self, context: Dict[ str , Any]) -> bool : """ リソース操作の状況をポーリングして、完了したかどうかチェックする。 Args: context (Dict[str, Any]): Airflow のコンテキスト。どのような値が格納されているかは以下参照。 https://airflow.apache.org/docs/apache-airflow/stable/templates-ref.html Returns: bool: リソース操作が完了したかどうか。 """ # 作成したリソース情報(IDなど)を取得。 response = context[ "ti" ].xcom_pull(task_ids=self.poke_task_id) # call_get_resource_status_api 関数は別途実装。内部で GCP の REST API を呼び出す。 status = call_get_resource_status_api(response, args1=self.args1, args2=self.args2) # status = {"done": True} のような値と仮定 return status[ "done" ] with models.DAG( "dag_name" , schedule_interval= "0 0 * * mon" , ) as dag: create_resource_task = create_resource(args1, args2) wait_create_resource_task = ResourceSensor( task_id= "wait_create_resource" , poke_interval= 60 * 10 , timeout= 3600 * 3 , poke_task_id= "create_resource" , args1=args1, args2=args2, ) create_resource_task >> wait_create_resource_task ここで、 Airflow のコンテキストを利用してポーリング時に必要なリソース情報(ID など)を Sensor クラスで取得するようにしました。 Vertex AI Vector Search での類似度計算時にリトライ Vertex AI Vector Search で類似度計算を行う際に、 Exponential backoff アルゴリズム によるリトライ処理を入れるようにしました。理由は、実際に運用していると Vertex AI Vector Search の呼び出し時に google.api_core.exceptions.InternalServerError: 500 Failed to call Service Control Check. や google.api_core.exceptions.Unknown: None Stream removed というエラーが稀に発生することがあったためです。Vertex AI Vector Search による類似度計算は1回の定期実行あたり約 8 時間かかるため、途中で停止するとリトライにかかる時間が大きいという問題がありました。 Exponential backoff アルゴリズム によるリトライは backoff ライブラリ を利用し、リトライ対象のエラーは google.api_core.exceptions.ServerError としました。 ソースコード を見ると今回発生した google.api_core.exceptions.InternalServerError や google.api_core.exceptions.Unknown がこのクラスの子クラスであり、他の子クラスも GCP 側のサーバーエラー起因のものであるためリトライする方が良いと判断したためです。 以下が実際の ソースコード のイメージです。(簡易化したものであり、実際のものとは異なります)。 from typing import List import backoff from google.api_core.exceptions import ServerError from google.cloud.aiplatform import MatchingEngineIndexEndpoint from google.cloud.aiplatform.matching_engine.matching_engine_index_endpoint import ( MatchNeighbor, ) # エラーが解消するまでの待ち時間が不明なため、リトライの最大時間は1時間半とした。 # 類似度計算は1回の定期実行あたり約 8 時間かかるため、1時間半の待ち時間は許容する。 @ backoff.on_exception (backoff.expo, ServerError, max_time= 5400 ) def find_neighbors ( index_endpoint: MatchingEngineIndexEndpoint, deployed_index_id: str , queries: List[List[ float ]], num_neighbors: int , ) -> List[List[MatchNeighbor]]: """デプロイされたインデックスで与えられた Embedding に対して近似最近傍探索を実行。 Args: index_endpoint (MatchingEngineIndexEndpoint): Vertex AI Vector Search のインデックスエンドポイントクラス。 deployed_index_id (str): インデックスのデプロイID。 queries: List[List[float]]: Embedding のリスト。 num_neighbors (int): 近似最近傍探索で取得する Embedding 数。 Returns: List[List[MatchNeighbor]]: 類似度トップ `num_neighbors` の id と類似度のリスト。 """ return index_endpoint.find_neighbors( deployed_index_id=deployed_index_id, queries=queries, num_neighbors=num_neighbors ) 実際にこのリトライ処理を入れたことで、その後の運用時に同じエラーが発生することがありましたが、無事にリトライされることで途中で停止せずに実行完了していました。 まとめ 本記事では、類似画像検索システムの内製化におけるシステム面での課題と、それをどのように解決したかについて説明しました。大規模なデータを毎月現実的な時間で処理しなければならない課題を、複数の GCP サービスを組み合わせて高速化することで解決しました。また、複数の GCP サービスを連携してスケジュール実行する必要がある課題を、 Cloud Composer を利用して実装することで解決しました。 今後は他の機能への応用を検討しています。本記事が類似のシステムを構築されている方々の参考になれば幸いです。 明日の記事の担当は UI/UXチーム の飯沼さんです。お楽しみに。 追記:2025/01/21 第48回 MLOps 勉強会で登壇しました! 第48回 MLOps 勉強会 にてこのブログの内容を発表いたしました。勉強会ページには配信 アーカイブ も掲載されておりますため、ご興味ある方はご覧いただけますと幸いです。 speakerdeck.com エニグモ では一緒にデータを利用したサービス価値向上を実現していただけるデータサイエンティストを募集中です!世界178ヶ国に1100万人超の会員を有し、出品数は630万品を超える BUYMA には膨大なデータが蓄積されており、データ活用の余地はまだまだあります。ご興味ある方はカジュアル面談からでもお話できますと幸いです。 他の職種も絶賛募集中です! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは!フロントエンドエンジニアの張です! この記事はEnigmo Advent Calendar 2024の3日目の記事です。 エンジニアは日頃のタスクを対応するためにいろんなツール駆使していますが、絶対になくてはいけないツールと言いますと、やはり テキストエディタ ー一択だと、私は思っています。 その テキストエディタ ーですが、実は何種類もあって、側から見ると、どれも同じように見えるかもしれませんが、毎日何時間もそれで仕事をするエンジニアにとって、自分に合うエディターを選ぶことは実は仕事の効率とモチベーションに大いに関わっています。 本記事では、ここ約一年半、私が毎日使っている最強エディター Neovim について、ご紹介します。 Neovim とは もしかしたら Neovim を知らない方も多いかもしれませんが、それがエンジニアなら誰しも一回は使ったことがある、名高い Vim エディターをリファクターしたものです。 そのため、 Neovim は Vim の特性と独自の機能を備える、モダンな コマンドライン エディターになっています。 特性 Vim から受け継いだもの ベースが Vim になっている Vim を fork して開発したもの Vim motion Vim command 反応速度が早い ミニマル 独自の特性 Lua でコンフィグ可能 LSPのネイティブサポート 強み Neovim について簡単に説明しましたが、それは一体ほかの主流のエディターと比べて、何の強みがあるでしょう。 Vim Motion Vim Motionというのは Vim 、あるいは Neovim 上で使える、カーソルを移動させたり、テキストを編集したり、できるショットカットです。 Motionを使いこなせると、マウスなしでテキストを高速で編集、ナビゲートすることが可能になります。 それは日々大量なテキスト、あるいはコードを処理するエンジニアにとって、とても貴重なスキルです。 実例 個人的に最も使っている Normal Mode 下の基礎Motionが以下となります Motion 機能 [count] h カーソルを( count 行)左に移動させる [count] j カーソルを( count 行)下に移動させる [count] k カーソルを( count 行)上に移動させる [count] l カーソルを( count 行)右に移動させる [count] w カーソルを次/ count個後の単語に移動させる [count] b カーソルを前/ count個前の単語に移動させる i insert mode に移行して、カーソルを左に一行移動させる a insert mode に移行して、カーソルを右に一行移動させる d [motion] motionの終着点までの文字を削除する x カーソルの位置の文字を削除する y [motion] motionの終着点までの文字を複製する p yで複製した内容をペーストする Text object selection を利用したMotion Motion 機能 ciw カーソルが位置する単語を丸ごと削除して、insert modeに移行する diw カーソルが位置する単語を丸ごと削除する dap カーソルが位置する段落を削除する yap カーソルが位置する段落を複製する 使えるMotionは実際まだまだありますが、量が多いのもありますし、 公式ガイド を参考した方が的確なので、ここでは割愛させていただきます。 効率的で楽しい 以上の実例を見ると、 Vim / Neovim が高速なテキスト編集を実現できることがわかると思いますが、それは実は私にとって、一番大事なメリットではありません。 では、一番のメリットは何でしょうか! 楽しい! そう!楽しくテキストを編集できること自体が一番のメリットだと思います。 よく見ますと、 Vim Motionってゲームのコンボに見えないでしょうか? Vim / Neovim なら一日中コードを書いても退屈することはありません! それがエンジニアのモチベーションを引き上げて、効率を改善できることを私はこの一年半ですごく実感できました。 PDE(Personalized Development Environment) Neovim は一部のエンジニアから、 PDE 、つまり 個人的開発環境 とも呼ばれています。 それは Neovim のコンフィグ自由度がとても高くて、ユーザーがそれを思い通りに自分好みにできるからです。 具体的に言うと、UIの表示、文字を打つ時のエフェクト、ユーザー入力への反応など、その気になれば全部自分で実装できます。 結果として、ユーザーは自分のワークフローにピッタリな開発環境を構築できて、開発の効率を大幅に上げられます。 効率改善以外、PDEはユーザーのモチベーションの向上にもつながると思います。 なぜかと言うと、エンジニアが仕事のタスクをPDEで対応することで、自分が設計したツールをテストできるからです。 開発が好きなエンジニアなら、それだけで仕事へのモチベーションが大幅に上がるでしょう。 多彩な プラグイン 近年、開発を支援したり、ワークフローをスムーズにしたり、できる プラグイン がプログラミングエディターの必要不可欠な要素だと思われる傾向が強まりつつあります。 当然、 プログラマー のためのエディターである Neovim にも充実した プラグイン エコシステムがあります。 ここでは、重点的に、何個か人気な プラグイン についてご紹介します。 Telescope Telescopeは Neovim コアの機能を利用した、高性能な Lua 製 Fuzzy Finder です。 簡単に言うと、指定した範囲以内のコンテンツをキーワードで検索するツールです。 例えば、コードベース内のファイルを大まかな文字列で検索したり、特定な文字列がコードベースのどこに記載されているかを探したり、することが可能です。 それだけでも結構便利なツールなんですが、もっとすごいのはTelescopeの拡張性です。 Neovim と同様に、Telescopeは拡張性に重きを置いてデザインされました。 そのため、Telescopeの既存機能を利用して、新しい機能を作成することも比較的に簡単にできます。それに、純 Lua 製の プラグイン であるため、 Lua でカスタマイズするのもとても便利です。 もちろん、原作者と他のコミュニ ティー メンバーによって、もうすでにたくさんの 拡張機能 が作られたので、まだ自分で機能を作成したくないユーザーでも充実した既存機能を利用できます。 私がよく使っている機能をリストアップすると、以下となります。 builtin.buffer 開いたバッファの中からファイルを検索する builtin.find_files コースベース内のファイルを検索する builtin.live_grep コースベース内で特定の文字列やパターンを検索する 詳細に興味がある方はぜひ 公式GitHubページ をご覧ください! LSP(Language Server Protocol) 関連 プラグイン Neovim と Vim の違いの話になると、ビルトインのLSPサポートの有無が必ずといっていいほど話題に出されます。実際、LSPのサポートを目当てで Neovim に移行したユーザーも結構います。 では、LSPとは一体何なのでしょうか? 一言で言うと Microsoft 社が開発した、エディター/ IDE とLanguage Serverの間のコミュニケーションを可能にする プロトコル です。 Visual Studio Code などのエディターがコードのオートコンプリート、定義元への移動、コードの診断(Diagnostic) ができるのもLSPのおかげです。(LSPは元々 Visual Studio Code のために作られています) 詳細は こちらの公式サイト で閲覧できるので、興味がある方はぜひご確認ください。 前にも話した通り、 Neovim にもそのLSP機能を搭載されています。そして、それを中心に作られた プラグイン もたくさんあります。それらも利用すれば、簡単かつ自由度が高いLSP設定ができるため、 Neovim でのLSP体験はいろんなエディターの中でもトップクラスです。例えば、Language Serverから受信した情報に基づいて、画面上にコードに関する情報を表示するのも簡単にできます。 複雑だと思うかもしれませんが、以下のLSP プラグイン を利用すれば、大体のLSP設定ができます。 nvim-lspconfig いろんなLanguage Serverのデフォルトコンフィグを提供する mason.nvim Neovim 用のLanguage Serverパッケージマネージャー 簡単に主流のLanguage Serverをインストールできる mason-lspconfig nvim-lspconfig と mason を繋げるもの LSPを管理する mason. vim 今ではLSPを利用するエディターも結構増えましたが、設定の手軽さ、自由度、エコシステムの大きさのため、 Neovim を選択するユーザーもまだまだ増えています。 おわりに 以上が私が約一年半 Neovim を使って、未使用の方にも知ってほしい Neovim の見どころでした。 まだまだ説明しきれてないことが多いですが、 Neovim が素晴らしいプログラミングエディターであることが伝われたら嬉しいです! 文章を読むだけではピンと来ないかもしれないので、時間がある時、ぜひ、 Neovim を使ってみてください! 明日の記事の担当は 採用広報担当 の戸井さんです。お楽しみに。 おまけ 初心者が自分で0から Neovim を始めるのは結構ハードルが高いので、自分が最初に使ったリソースを幾つか共有したいと思います。 Neovim公式 kickstart.nvim Neovim のコア開発者の一人が開発した Neovim コンフィグ clone したらすぐ使える コードの説明がとても詳しくて、初心者に優しい vimtutor Vim Motionを勉強できる cli tool vim をインストールすれば、一緒にインストールされる Unix-like のシステムでは大体デフォルトでインストールされている
こんにちは!Webアプリケーションエンジニアの 川本 です! 最近は BUYMA の出品者向けのチームでパフォーマンス改善に取り組んでおります。 この記事は Enigmo Advent Calendar 2024 の 2日目の記事です。 少し日が経ってしまいましたが、2024年10月16に開催された「Datadog Summit Tokyo 2024」に参加してきました。 www.datadoghq.com 直近の業務でパフォーマンス改善に取り組む機会が多かったのですが、その際にオブザーバビリ ティー の向上を支えてくれる、Datadogに興味を持ちました。開発エンジニアにとってもこれらを活用できると視野が広がると感じ参加を決めました。 印象に残ったセッション Datadog ダッシュ ボードで 見える化 する、新たなビジネス価値創造のチャンス www.datadoghq.com このセッションでは、リリースした機能が意図通りに使われているかを分析し、期待されるスコアを可視化する ダッシュ ボードの活用例が紹介されました。 ダッシュ ボードから課題を発見し、改善につなげることでビジネス価値を創造していくという内容でした。 特に印象に残ったのは、エンジニアがビジネス観点を持つ重要性です。 ダッシュ ボードを作成するためには、案件の目的や必要なデータを理解する必要があり、それがビジネス観点を養うきっかけになると感じました。 弊社でも新機能をリリースする際に、エンジニアが ダッシュ ボードを準備し、ビジネス側と連携しながら価値創造に貢献していきたいです。 開発者の生産性向上 www.datadoghq.com このセッションでは、Datadogを活用した開発生産性向上に関するパネルディスカッションが行われました。 最も印象に残ったことは、 Wantedly 社の市古さんがおっしゃっていた「オブザーバビリティは開発を加速させる」という点です。 www.wantedly.com 大規模 リファクタリング やライブラリのバージョンアップ等を行った際の影響範囲はとても広いので、ステージング環境でなるべく確認しようとしても、全てを完璧に把握することは難しいことが多いかと思います。そのため、本番環境にリリースする際はどうしても慎重になりスピード感が損なわれてしまいます。 しかし、オブザーバビリティを向上させると、問題が発生しても素早く発見・対応できるという安心感が得られます。これにより、適切なリスクを取れるようになり、レビュー 工数 の削減や認知負荷の軽減につながるため、スピードと品質の両立が可能になるという内容はとても納得できました。 弊社で運営しているサービスの「 BUYMA 」も歴史の長いサービスで、これから大規模リプレイス、大規模 リファクタリング 、ライブラリの大幅なバージョンアップを控えております。その際に今回学んだことを活かして、オブザーバビリティを向上させながら開発生産性を維持しながら問題と向き合っていきたいです。 導入したいDatadogの機能 今回のサミットを通して様々なDatadogの機能について知ることができました。 その中でも自社で導入したいと考えている機能は Datadog Continuous Profiler です。 www.datadoghq.com Datadog Continuous Profilerとは? Datadogの公式ページでは以下のように紹介されております。 Datadog Continuous Profiler を使用すると、最小限のオーバーヘッドでスタック全体にわたって本番環境でのコードパフォーマンスを分析できます。コードプロファイリングを利用して、アプリケーションで最もリソースを消費するメソッドまたはクラスをすばやく検出および最適化できます。これにより、コードの効率が向上し、 クラウド プロバイダーのコストが削減されます メソッド単位でどれだけ時間がかかっているかや、CPU時間、メモリ使用量等のリソースの消費量が可視化されるので、パフォーマンスの ボトルネック を発見するのに最適です。 導入したい理由 この機能を導入したいと思ったのは、弊社で運用されはじめて長年経過した機能のパフォーマンス改善に取り組んだことがきっかけでした。 私は ボトルネック を見つけるのにまず該当するコード全体を読んでいたのですが、これは根拠のない推測をしているだけで効率が悪かったなと反省しております。 そういった際に Continuous Profiler で計測してコードレベルで可視化して ボトルネック になっている箇所を明確にし、修正の目処を立てるといったことができていればより効率的かつ効果的なアプローチがとれていたなと感じております。 おわりに 今回のイベントを通してDatadog、オブザーバビリティに関して新たな知見を得ることができたのと同時によりこの分野に興味を持つことができました。 今回ワークショップには参加できなかったので Datadog Learning Center で興味のある講座を受講して実際に手を動かしてDatadogに関する知見を深めていこうと思います。 最後に、帰り際にいただいたDatadogのかわいいグッズも大切に使わせていただきます! 運営の皆様、素晴らしいイベントをありがとうございました! 明日の記事の担当は フロントエンドエンジニア の張さんです。お楽しみに。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは!WEBアプリケーションエンジニアの 川本 です! この記事はEnigmo Advent Calendar 2024の1日目の記事です。 弊社の運営する BUYMA では、社内システムよりタイムセールを毎週 約100万商品 に設定しています。しかし従来のシステムでは、この処理に 約100時間 もかかっており、運用負荷が大きな課題となっていました。本記事では、パフォーマンス改善によって処理時間を 約5時間 に短縮し、運用効率を向上させた事例をご紹介します。 タイムセール設定機能について 処理フロー タイムセール設定機能はざっくり以下のような処理フローになっています。 CSV ファイルのアップロード タイムセール設定依頼情報を記載した CSV ファイルを S3 にアップロードします。 SQSへのエンキュー S3にファイルがアップロードされると、SQSにメッセージがエンキューされます。 Redisに保存 常駐しているデーモンがSQSキューをポーリングし、SQSから取得したメッセージをRedisに保存します。 Sidekiqジョブの実行 Redisに保存されたメッセージをSidekiqがデキューして、タイムセール設定の処理を実行します。 性能 タイムセール設定を行うSidekiqの性能は以下の通りです。 専用プロセスの使用 タイムセール設定ジョブは専用のSidekiqプロセスで実行。 並列処理数 1プロセスあたりの並列処理数は10。 冗長化 2台のサーバー構成で冗長性を確保。 設定例(sidekiq.yml) --- :labels : - default :concurrency : 2 :pidfile : tmp/pids/sidekiq.pid :logfile : ./log/sidekiq.log production : :concurrency : 10 :queues : - [ setting_timesale, 1 ] 課題の発見 従来の仕組みでは、タイムセール設定依頼の CSV ファイル1つを1スレッドで処理していました。これにより、以下のような問題が発生していました。 スレッド活用不足 Sidekiqはマルチスレッド対応で高い並列処理性能を持っていますが、1つのスレッドが1つの CSV を丸ごと処理していたため、マルチスレッドであること効果的に活用できていませんでした。 長時間実行による運用負荷 1つの CSV には約100万件の商品データが含まれており、これを1スレッドで処理することで、処理時間が長時間に及び、運用上現実的でない時間になっていました。 CSV 分割の運用負荷 スレッドを有効活用するには、 CSV をあらかじめスレッドの数だけ手動で分割する必要があり、これが運用上の手間となっていました。 改善アプローチ 上記の課題を解決するため、システム側で1つの CSV データをバッチ単位(100商品)で分割し、各バッチを複数スレッドで並列処理する方式に変更しました。これにより、処理の効率化と運用負荷軽減を同時に実現することが可能になりました。 改善後の処理の流れ 親ジョブ が CSV データをバッチ単位に分割し 子ジョブ を作成・エンキューする。 子ジョブ が実際のタイムセール設定処理を実行する。 監視ジョブ が 子ジョブ の進捗を追跡し、全て完了したら設定完了の処理を実行する。 登場してきたジョブについて↓ 親ジョブ CSV ファイルから取得したタイムセール設定依頼データをバッチ単位に分割。 各バッチについて 子ジョブ をエンキューし、ジョブIDを記録。 最後に 監視ジョブ をエンキューし、 子ジョブ の進捗を監視。 実装例↓ module SidekiqWorker # 親ジョブ class Parent include Sidekiq :: Worker sidekiq_options queue : :setting_timesale BATCH_SIZE = 100 def initialize (args) # 省略 end def perform child_job_ids = [] # バッチ単位でタイムセール依頼データを分割 timesale_request_data.each_slice( BATCH_SIZE ) do |batch| # バッチ単位でタイムセール設定を行う 子ジョブ をエンキュー child_job_ids << SidekiqWorker :: Child .perform_async(batch) end # 子ジョブの進捗状況を管理する 監視ジョブ をエンキュー SidekiqWorker :: Monitoring .perform_async(child_job_ids) end private # s3からタイムセール設定依頼データを取得 def timesale_request_data # 省略 end end end 子ジョブ バッチ単位(100件)の設定依頼に対してタイムセール設定の処理を実行。 ジョブの進捗状況をRedisに記録。 子ジョブの状態管理に sidekiq-status というgemを使用しました。 github.com sidekiq-status は、Sidekiqで実行中のジョブの状態を追跡するためのGemです。ジョブの状態(例: queued, working, completeなど)をRedisに保存し、進捗をリアルタイムで確認できるようになります。 module SidekiqWorker # 子ジョブ class Child include Sidekiq :: Worker include Sidekiq :: Status :: Worker sidekiq_options queue : :setting_timesale # 24時間ジョブの状態をRedisに保持する def expiration @expiration ||= 60 * 60 * 24 end def perform (timesale_request_data) SetTimeSaleService .new(timesale_request_data).call end end end 監視ジョブ 子ジョブの進捗を追跡し、全ての子ジョブが完了した場合に完了処理を実行 追跡できない子ジョブが存在した場合はアラートを飛ばす。 sidekiq-statusの API よって以下のように子ジョブの状態を確認することができます。 job_id = SidekiqWorker :: Child .perform_async # :queued, :working, :complete, :failed or :interrupted, nil after expiry status = Sidekiq :: Status ::status(job_id) # <- ジョブ状態を確認 Sidekiq :: Status ::queued? job_id # <- キューにあるか? Sidekiq :: Status ::working? job_id # <- 実行中か? Sidekiq :: Status ::retrying? job_id # <- リトライ中か? Sidekiq :: Status ::complete? job_id # <- 完了したか? Sidekiq :: Status ::failed? job_id # <- 失敗したか? Sidekiq :: Status ::interrupted? job_id # <- 中断されたか? 実装例↓ module SidekiqWorker # 監視ジョブ class Monitoring include Sidekiq :: Worker include Sidekiq :: Status :: Worker sidekiq_options queue : :setting_timesale SLEEP_TIME = 15 def perform (job_ids) @job_ids = job_ids loop do @job_ids .reject! do |job_id| job_complete?(job_id) end break if @job_ids .empty? sleep SLEEP_TIME end TimeSaleSettingCompletionService .new(args).call end private # ジョブが完了したか? def job_complete? (job_id) Sidekiq :: Status .complete?(job_id) end end end 学んだこと 性能要件の見直しの重要性 長年運用されているシステムは、リリース当時の性能要件がそのまま適用されていることが少なくありません。しかし、システムの利用状況や運用環境は時間の経過とともに変化します。今回の事例でも、リリース当初は妥当だった処理速度が、現在では運用負荷を引き起こす大きな要因になっていました。 そこで重要なのは、現状の運用方法をしっかりと把握し、必要に応じて性能要件を再定義することです。運用者に ヒアリ ングを行い、現在の問題点を明確にすることで、改善に向けた具体的な指針を得ることができます。 並列化によって増す複雑性 並列化はシステムの処理速度を大幅に向上させる一方で、複雑性を増す側面があります。一連の処理が複数のジョブに分散されるため、それぞれのジョブの状態を適切に管理する必要が生じます。今回の事例でも、Sidekiqを用いた並列化に伴い、以下のような課題が明らかになりました。 ジョブの状態管理コスト 並列化することで、ジョブの進捗や完了状態を追跡する仕組みが必要になります。このため、Redisを活用したジョブの状態管理が不可欠となりましたが、それには追加の開発コストと運用リスクが伴います。 ジョブの状態欠損のリスク 例えば、Redisに障害が発生した場合、一部のジョブの状態が欠損する可能性があります。このリスクを考慮し、ジョブの再実行や障害時の リカバリ ープロセスを検討する必要があります。 並列化の インパク トは大きいですが、システムの複雑性は増してしまいます。まずは 処理内容自体のパフォーマンス改善 に目を向けることが大切です。今回のプロジェクトでは、並列化に先立ち、子ジョブ内で実行されるタイムセール設定処理の最適化を行いました。 おわりに パフォーマンス改善は、単に処理を「速くする」ことが目的ではなく、システムの要件や複雑性を考慮し、最適なバランスを見つけることが重要だなと思いました。 今回学んだことを活かしてこれからもパフォーマンス改善に取り組んでいきたいです! 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、データサイエンティストの髙橋です。業務では企画/分析/ 機械学習 モデル作成/プロダクション向けの実装/効果検証を一貫して行っています。 この記事では類似画像検索システムを内製化したことで、既に社内で利用していた他社製のものと比較して精度を維持しながらコスト削減が実現できたことについて紹介します。 なお、類似画像検索システムの 機械学習 部分と システム開発 部分( GCP を利用した処理の高速化)との2つに分けて紹介します。今回は前者の 機械学習 部分についての記事となります(後者の システム開発 部分の記事は こちら )。 内製化の目的 内製化による事業インパクト 内製化成功のポイント BUYMA における類似画像検索の課題 類似画像検索方法の概要 類似画像検索各ステップの詳細 商品画像内の物体存在箇所をセグメンテーションし切り抜き 切り抜いた画像の Embedding 計算 Embedding により画像同士の類似度計算 画像ハッシュにより画像同士の類似度計算 Embedding 類似度と画像ハッシュ類似度を組合せ同一商品の可能性判定 まとめ 内製化の目的 弊社が運営している CtoC EC サービス BUYMA は、CtoC という特性上同じ商品が異なる出品者から出品されている場合が多いです。そのため、どの商品が今売れ筋なのか、その商品のサイト全体での在庫状況はどうなのか、価格差はどの程度あるのかなどを正確に把握するには異なる出品者から出品されている同じ商品の 名寄せ が必要です。 それを実現するために、商品画像同士の類似度を計算し一定以上のものを人手で アノテーション することで商品の 名寄せ を社内で行っていました。ここで商品画像同士の類似度を計算するために他社製の類似画像検索システムを利用していました。しかし、その費用がそれなりに高く、内製化することでコスト削減を実現したいというのが目的でした。 内製化による事業 インパク ト 内製化したシステムは既に稼働しており、これまでと比較して精度は維持したまま年間数百万円規模のコスト削減が見込まれています。 加えて、他社製の類似画像検索システムではコストが高く断念していた EC サイトでの購入者向けの画像を起点にしたレコメンドや、画像で類似商品を検索できる機能なども検討出来るようになりました。 内製化成功のポイント 以下2点が内製化成功のポイントであったと考えています。 ト レーニン グ不要で十分な精度が出る 機械学習 モデルの登場を待ち、それを利用したこと 精度と実現スピードのバランスを考えビジネスで活用できるレベルの結果を素早く出したこと 1点目について、私の入社前から社内では 名寄せ に対する要望があり 機械学習 モデル開発を検討したことがあったようですが、 BUYMA には数百万件以上の膨大な商品があり 機械学習 モデルのト レーニン グ・評価の イテレーション に時間がかかる上に中々期待する精度までは至らずストップしている状態でした。 そこで、どうにかして精度を上げる方法は検討せず、世の中では大規模な深層学習モデルの開発が活発であったため、何か利用できそうなものがないか定期的に調査していました。すると、 Contrastive Language-Image Pre-Training (CLIP) や Segment-Anything Model (SAM) のようなト レーニン グ不要で高精度に画像の類似度計算やセグメンテーションが出来るモデルが登場し、このタイミングであれば時間をかけすぎずに内製化出来るのではないかと思い PoC をスタートさせました。 2点目については、精度が期待する水準に至らない場合に、 Fine-Tuning のような時間がかかることよりも先に、後述する簡易な画像処理 アルゴリズム を利用して運用上問題ない精度まで向上させました。これにより、この PJ はほぼ1人での担当でしたが企画から本番運用まで約1年で完遂することが出来ました。おそらくですが、あまり時間をかけすぎてしまうとビジネス上の課題の優先度の変化などから ペンディング する必要性が生じた可能性が高かったと考えています。 BUYMA における類似画像検索の課題 具体的にどのような方法で類似画像検索を実現したかの前に、 BUYMA における類似画像検索の課題について説明します。 BUYMA の商品画像は出品者の方が自由に設定できるものであり、商品単体よりも様々なものをコラージュしている場合が多いです。例えば、色違い/角度違いの商品、着用画像や文字などをコラージュしていることが多いです。 そのため、単純に何らかの手法で画像同士の類似度を計算しても、同じ商品の画像にも関わらず類似度が低く出てしまう課題がありました。 類似画像検索方法の概要 課題に対応するために、画像から商品部分を切り抜いてその部分同士で画像類似度を計算する方法を考えました。はじめは Object Detection が出来る深層学習モデルをト レーニン グし、それにより商品部分を検知し切り抜く方法を考えましたが、 BUYMA には様々な商品がありラベル作成の手間がかかる懸念がありました。 そこで、企画検討当時に発表された Segment-Anything Model (SAM) というト レーニン グ不要で画像から物体をセグメンテーション出来るモデルの利用を検討しました。詳細は後述しますが、 SAM と画像ハッシュを組み合わせることで既存の他社製の類似画像検索システムと同等の精度を実現しました。 類似画像検索各ステップの詳細 類似画像検索は以下のステップに分かれています。 商品画像内の物体存在箇所をセグメンテーションし切り抜き 切り抜いた画像の Embedding 計算 Embedding により画像同士の類似度計算 画像ハッシュにより画像同士の類似度計算 Embedding 類似度と画像ハッシュ類似度を組合せ同一商品の可能性判定 ここで、Embedding とは以下のように画像を数値の列に変換したものです。 似た画像同士が何らかの類似度計算関数(cos 類似度など)で高い値を持つようにト レーニン グされた 機械学習 モデルを利用することで、画像同士の類似度を計算することが出来ます。 各ステップの詳細について説明します。 商品画像内の物体存在箇所をセグメンテーションし切り抜き 課題でも述べた通り、 BUYMA の商品画像はコラージュされている場合が多く、例えば以下のような同じ商品であるが2つの異なる出品者の画像をそのまま Embedding 化し類似度を計算すると 0.496 となり、低い類似度となってしまいました。 そこで、SAM というモデルを利用して商品画像内から商品を含む物体存在箇所をセグメンテーションし切り抜きました。SAMとは、あらゆる物体をセグメンテーションするための汎用的なモデルであり、さまざまな画像に対して物体を抽出することが可能です。大量のデータで訓練されており、Fine-Tuning なしでも高い精度を発揮します。 実際にSAM を利用してそれぞれの画像から物体部分を切り抜くと以下のような画像群が作成されました。この中で黒の財布部分を切り抜けている画像をそれぞれ Embedding し類似度を計算すると0.975 と非常に高い値となりました。 切り抜いた画像の Embedding 計算 前ステップで商品画像から切り抜かれた各画像について、 機械学習 モデルを利用し Embedding を計算します。ここでもラベル作成やト レーニン グの手間を削減するために、事前学習済みの深層学習モデルを利用しました。 事前学習済みモデルとしては、 Japanese-CLIP(Contrastive Language-Image Pre-Training) を利用しました。 CLIP とは、大量の画像とテキストの対応関係を学習することにより、画像と 自然言語 の両方での検索や類似度計算が可能なモデルです。このモデルを選んだ理由は、PoC で実際の BUYMA 画像で精度を検証してみて問題なさそうであったのと、日本語での 自然言語 による画像検索の拡張性も考慮してのことです(CLIP ベースのモデルであるため、 自然言語 と画像の類似度も計算可能です)。 しかし、ある程度開発が進んだ段階で open_clip のいくつかの学習済みモデルと比較した結果、より精度が良いモデルがあり場合によってはそちらを採用しても良かったかもしれないです。今後、さらに精度向上が必要な場合には再度検証を行う予定です。 Embedding により画像同士の類似度計算 前ステップにより1商品画像に対して複数の Embedding が作成されます。画像同士の類似度を1つの値として算出するために、商品内の切り抜かれた部分の Embedding の全ての組み合わせの類似度を計算しその中の最大の値を画像同士の類似度としました。 前の説明で用いた商品画像を例とすると、以下のように切り抜かれた部分同士の全ての組み合わせの類似度を計算し、その中の最大の値(ここでは黒の傾いていない財布部分同士の類似度が 0.975 で最大であった)をもとの商品画像同士の類似度としました。 この方法を試してみた時点で、既存の他社製の類似画像検索システムに近い精度が出せましたがやや及ばない状態でした。そこで後述する画像ハッシュによる類似度計算も組み合わせることとしました。 画像ハッシュにより画像同士の類似度計算 前述したように Embedding よる類似度計算では既存のシステムに精度がやや及ばない状態であり、その主な原因は商品内の一部分での切り抜き同士で過剰に類似度が高くなってしまうパターンが主でした。 具体的な例としては、以下のように商品の一部と背景部分のみを切り抜いてしまい、それら画像同士の類似度が 0.9 以上と高くなるケースがありました。 これを防ぐために、Embedding による画像同士の類似度が一定以上である組み合わせについて、画像ハッシュによる類似度も計算しました。画像ハッシュとは、画像の視覚的特徴を数値化し簡略化した表現で画像を比較する手法です。これにより、細部の違いや大まかな構造の類似性を把握できます。例えば、 Average Hash (aHash) では大まかに言うと画素値が平均よりも高いかで2値化して比較を行います。 実際に上記のようなケースにおいて画像ハッシュによる類似度を計算すると非常に高い値(画像ハッシュでは値が高いほど類似度が低い)となりました。 Embedding 類似度と画像ハッシュ類似度を組合せ同一商品の可能性判定 Embedding による類似度と画像ハッシュによる類似度を組み合わせた しきい値 を設定し、それを超えた商品画像同士のペアを同一商品である可能性が高いと判定するようにしました。既存の他社製のものでも同様に類似度の しきい値 が一定以上を同一商品の可能性が高いとしていました。 しきい値 は、既存のものと判定数をおおよそ同じにした方が移行後の運用がしやすいと考え、そうなるように決めました。また、このとき既存のものとの比較も行い精度に大きな乖離がないことを確認しました。具体的には、 しきい値 調整用のデータ(約704億の画像ペア)とテスト用のデータ(約1785億の画像ペア)を用意し、既存のものでの判定結果を正としたときに Precision 、 Recall を計算したところ以下の値でした。 Precsion Recall しきい値 調整用のデータ 0.7 0.72 テスト用のデータ 0.8 0.77 Precision 、 Recall を悪化させる要因である既存システム/内製化システムでのみ同一の可能性が高いと判定された画像ペアをそれぞれ目視で確認してみると、既存システムでのみ同一と判定されたペアにも実際には異なる商品同士があったり、内製化システムでのみ判定されたペアには実際に同一であるペアがあったりし、既存の外部ツールでも誤検知や見逃しが多くありました。 また、人手で アノテーション いただく方々に既存システムと内製化システムを一定期間並行運用していただき、運用上の精度も問題ないことを確認いただいたため、現在内製化システムに移行しています。 まとめ 内製化により精度を維持しながら大幅なコスト削減を実現し、さらなる類似画像検索機能の活用可能性も広がりました。 次回は、 GCP のマネージドサービスを利用して推論方法の各ステップを高速化した システム開発 部分の詳細を紹介する予定です。実運用では数十〜数千億規模の画像の組合せについて定期的に類似度計算する必要があり、高速化も非常に重要なポイントでした。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、 エニグモ 嘉松です。 BUYMA のプロモーションや マーケティング を担当している事業部に所属しており、その中のデータ活用推進室という部署で会社のデータ活用の推進や マーケティング ・オートメーションツール(MAツール)を活用した販促支援、 CRM などを担当しています。 さて、SELECT文で得た結果のデータを(そのまま、直接)テーブルに挿入する INSERT SELECT や、SELECT文で得た結果からテーブルを作成して更にデータまで挿入する CREATE TABLE AS は何かと便利な機能(文法、技?)ですが、 WITH句 (SELECT文による結果を一時的に名前を付けてテーブルのように利用する便利な機能(文法、技?))と併用、一緒に利用しようとした時に、ちょっとした注意点があるので備忘録として記載しておきたいと思います。 例えば、以下のようなにWITH句で複数の(仮想的な)テーブルを定義して、それらを結合(JOIN)して結果を得るような SQL があったとします。(サンプルなのでとてもシンプルな SQL にしていますが、通常では WITH句 を使う場合はもっと複雑な SQL になることが多いと思います。) ※以下の SQL はBigQueryで検証していますので、他の DBMS では異なる結果やエラーになる場合があることをご了承ください。 WITH emp AS ( SELECT 7369 AS empno, ' SMITH ' AS ename, 20 AS deptno, UNION ALL SELECT 7499 AS empno, ' ALLEN ' AS ename, 10 AS deptno, UNION ALL SELECT 7521 AS empno, ' WARD ' AS ename, 30 AS deptno, ), dept AS ( SELECT 10 AS deptno, ' DEVELOPMENT ' AS dname, ' MAYNARD ' AS loc UNION ALL SELECT 20 AS deptno, ' SALES ' AS dname, ' HOUSTON ' AS loc UNION ALL SELECT 30 AS deptno, ' RESEARCH ' AS dname, ' PALO ALTO ' AS loc ) SELECT e.empno, e.ename, e.deptno, d.dname, d.loc FROM emp e JOIN dept d ON e.deptno = d.deptno ; この SQL の結果は以下のようになります。 empno ename deptno dname loc 7369 SMITH 20 SALES HOUSTON 7499 ALLEN 10 DEVELOPMENT MAYNARD 7521 WARD 30 RESEARCH PALO ALTO この SQL の結果を INSERT SELECT でテーブルに挿入しようとした時に、うっかり以下のような SQL を書くとエラーになります。 WITH emp AS ( SELECT 7369 AS empno, ' SMITH ' AS ename, 20 AS deptno, UNION ALL SELECT 7499 AS empno, ' ALLEN ' AS ename, 10 AS deptno, UNION ALL SELECT 7521 AS empno, ' WARD ' AS ename, 30 AS deptno, ), dept AS ( SELECT 10 AS deptno, ' DEVELOPMENT ' AS dname, ' MAYNARD ' AS loc UNION ALL SELECT 20 AS deptno, ' SALES ' AS dname, ' HOUSTON ' AS loc UNION ALL SELECT 30 AS deptno, ' RESEARCH ' AS dname, ' PALO ALTO ' AS loc ) INSERT dataset.emp_dept -- 最終的な結果を得るSELECTの直前に記載 SELECT e.empno, e.ename, e.deptno, d.dname, d.loc FROM emp e JOIN dept d ON e.deptno = d.deptno ; BigQueryでは以下のようなメッセージが表示されます。 Syntax error: Unexpected keyword INSERT at [39:1] 最終的に結果を得るSELECT文の前に INSERT を記載するという、ごく自然な、直感的な、あたかも正しそうな方法ですが、エラーとなります。 正しくは、以下のようにWITH句の前に INSERT を記載する必要があります。 INSERT dataset.emp_dept -- WITH句の前にを記載する必要がある WITH emp AS ( SELECT 7369 AS empno, ' SMITH ' AS ename, 20 AS deptno, UNION ALL SELECT 7499 AS empno, ' ALLEN ' AS ename, 10 AS deptno, UNION ALL SELECT 7521 AS empno, ' WARD ' AS ename, 30 AS deptno, ), dept AS ( SELECT 10 AS deptno, ' DEVELOPMENT ' AS dname, ' MAYNARD ' AS loc UNION ALL SELECT 20 AS deptno, ' SALES ' AS dname, ' HOUSTON ' AS loc UNION ALL SELECT 30 AS deptno, ' RESEARCH ' AS dname, ' PALO ALTO ' AS loc ) SELECT e.empno, e.ename, e.deptno, d.dname, d.loc FROM emp e JOIN dept d ON e.deptno = d.deptno ; 同じように CREATE TABLE AS においても、WITH句の前に CREATE TABLE AS を指定する必要があります。 CREATE TABLE dataset.emp_dept AS -- WITH句の前にを記載する必要がある WITH emp AS ( SELECT 7369 AS empno, ' SMITH ' AS ename, 20 AS deptno, UNION ALL SELECT 7499 AS empno, ' ALLEN ' AS ename, 10 AS deptno, UNION ALL SELECT 7521 AS empno, ' WARD ' AS ename, 30 AS deptno, ), dept AS ( SELECT 10 AS deptno, ' DEVELOPMENT ' AS dname, ' MAYNARD ' AS loc UNION ALL SELECT 20 AS deptno, ' SALES ' AS dname, ' HOUSTON ' AS loc UNION ALL SELECT 30 AS deptno, ' RESEARCH ' AS dname, ' PALO ALTO ' AS loc ) SELECT e.empno, e.ename, e.deptno, d.dname, d.loc FROM emp e JOIN dept d ON e.deptno = d.deptno ; 考え方としては、 WITH句はあくまでもSELECT文の一部である。(WITH句も含めてSELECT文) INSERT SELECT 、 CREATE TABLE AS の後にはSELECT文を記載する必要があるので、その SQL の一部であるWITH句も同じく INSERT SELECT 、 CREATE TABLE AS の後に記載する必要がある。 とうことでしょうか。 以上です。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは、インフラグループ Kubernetes チームの福田です。 突然ですが、Webアプリケーションでユーザの認証にOIDCを使うことはよくあると思います。 弊社でも様々な箇所でOIDCが利用されてます。 自社で開発しているWebアプリケーションや最近のログイン機能を持つ OSS の多くは、OIDC Providerさえ用意すればOIDCを利用することができます。 しかし、現実的にはログイン機能を持たない OSS のWebアプリケーションでOIDC認証を使いたいケースや自前で開発したWebアプリケーションにおいてもわざわざOIDCのクライアント機能を追加実装するのが面倒なケースがあります。 そんな時に使えるのがOAuth2Proxyです。 OAuth2Proxyはリバースプロキシとして動作しながら、OIDCの認証をしてくれます。 具体的にはクライアントからのアクセスに対してOIDCの認証を行い、認証されたクライアントからのアクセスのみをバックエンドに通過させるといったことが可能です。 サンプル構成の構築 サンプル構成を通して Kubernetes 上での構築方法を紹介していきます。 サンプル構成ではOAuth2Proxyのバックエンドにnginxを利用し、OIDCプロバイダにはoktaを使いたいと思います。 okta(OIDC Provider)の設定 oktaのApplicationを作成します。 Sign -in methodは OIDC を選択し、Application typeとして Web を選択します。 Grant typeでは Authorization Code と Refresh Token を選択します。 Login Redirect URIs はお使いの環境に合わせて設定してください。(ここでは https://corp.example.com/oauth2/callback としておきます。) また、作成後に生成された Client ID と Client Secret をメモしておきます。 最後にユーザのアクセス許可設定をします。 シークレット情報の作成 シークレット情報をSecretとして保存します。 kind : Secret apiVersion : v1 metadata : name : my-credential type : Opaque data : client_id : ********** client_secret : ********** cookie_secret : ********** client_id と client_secret は"okta(OIDC Provider)の設定"のところでメモした値を使います。 cookie_secret にはランダムな値を使います。 Podの作成 OAuth2Proxyとnginxを構築します。 apiVersion : apps/v1 kind : Deployment metadata : labels : app : sample name : sample spec : replicas : 1 selector : matchLabels : app : sample template : metadata : labels : app : sample spec : containers : - name : redis image : redis volumeMounts : - name : cache mountPath : /data - name : nginx image : nginx - name : oauth2-proxy image : bitnami/oauth2-proxy ports : - name : oauth-proxy containerPort : 80 args : - --http-address - 0.0.0.0:80 env : - name : OAUTH2_PROXY_UPSTREAMS value : http://localhost/ - name : OAUTH2_PROXY_PROVIDER_DISPLAY_NAME value : okta - name : OAUTH2_PROXY_PROVIDER value : oidc - name : OAUTH2_PROXY_OIDC_ISSUER_URL value : https://sample.okta.com/oauth2/default - name : OAUTH2_PROXY_CLIENT_ID valueFrom : secretKeyRef : name : my-credential key : client_id - name : OAUTH2_PROXY_CLIENT_SECRET valueFrom : secretKeyRef : name : my-credential key : client_secret - name : OAUTH2_PROXY_PASS_ACCESS_TOKEN value : 'true' - name : OAUTH2_PROXY_EMAIL_DOMAINS value : '*' - name : OAUTH2_PROXY_REDIRECT_URL value : https://corp.example.com/oauth2/callback - name : OAUTH2_PROXY_COOKIE_SECURE value : 'false' - name : OAUTH2_PROXY_COOKIE_SECRET valueFrom : secretKeyRef : name : my-credential key : cookie_secret - name : OAUTH2_PROXY_SKIP_PROVIDER_BUTTON value : 'true' - name : OAUTH2_PROXY_COOKIE_NAME value : SESSION - name : OAUTH2_PROXY_COOKIE_SAMESITE value : lax - name : OAUTH2_PROXY_SESSION_STORE_TYPE value : redis - name : OAUTH2_PROXY_REDIS_CONNECTION_URL value : redis://localhost startupProbe : initialDelaySeconds : 5 periodSeconds : 5 tcpSocket : port : 6379 volumes : - name : cache emptyDir : {} nginxのPodに対して サイドカー としてOAuth2Proxyコンテナを差し込んでいます。 redisコンテナがありますが、これはOIDCプロバイダーへのアクセスを減らすためのセッション情報のキャッシュとして使ってます。 サンプル構成の完了 Ingress やServiceなどのリソース作成の説明は省略しますが、これで構築は完了です。 対象のURL(この記事の場合は https://corp.example.com )にアクセスするとoktaのサインイン画面が表示されます。 そして、サインインして認証をPASSするとnginxにアクセスできます。 参考リンク OAuth Provider Configuration | OAuth2 Proxy Add Auth to Any App with OAuth2 Proxy | Okta Developer まとめ 今回はOAuth2Proxyについて紹介させていただきました。 本記事を通して構築がとても簡単であることが分かったと思います。 OAuth2Proxyのようなツールを自前で社内開発しているところも多いのではないでしょうか。 メンテナンスコストの理由からそういった社内開発のアプリを OSS へ移行することを検討している場合はOAuth2Proxyは選択肢の1つとしてありかと思います。 株式会社 エニグモ すべての求人一覧 hrmos.co
article p { text-align: justify; } .entry-content h3 { font-size: 140%; } Hello, this is Fernand from the Global Buyma Team. As an engineer, I am responsible for maintaining the English version of Buyma . I recently had the opportunity to attend an AI conference along with my manager. I’m excited to share our experiences and insights with you. So fasten your seatbelt, relax, and enjoy the journey through the world of AI that we embarked upon. Merhaba! On May 10-11, 2024, I had the privilege of immersing myself in the vibrant intersection of technology and tradition at the two-day AI conference organized by Mad Street Den in Istanbul. This city, which uniquely straddles two continents—Europe and Asia—offered the perfect backdrop for an event that brought together global leaders to explore the forefront of artificial intelligence across various sectors. AI on Global Buyma Global Buyma employs the capabilities of Vue.ai's AI service to curate the most suitable product images for display in search results. The AI evaluates and identifies the best quality image from those uploaded by sellers to represent each product. Bridging Cultures, Advancing Technology The conference kicked off with an insightful tour around Istanbul's historic centre, embodying the perfect blend of cultural heritage and modernity. Then we headed to the Sarnıç, a 1500-year-old cistern lying at the heart of Istanbul, which set the stage for an evening of engaging dialogue. Over dinner, we forged meaningful connections with fellow attendees, delving into spirited discussions about the transformative potential of AI and sharing diverse perspectives on its future impact. ・Dinner at The Sarnıç. Honoring the special guests of the event. Key Highlights from the Conference The following day, we journeyed to the magnificent Sait Halim Pasha Mansion, where leaders from diverse sectors, including pharmaceuticals, e-commerce, and logistics, shared their insights on implementing AI within their industries. Ashwini Asokan and Anand Chandrasekaran, the founders of Mad Street Den, provided a comprehensive overview of how their tools are automating the way businesses operate, shifting the focus from infrastructure to innovation. Then, it was followed by a thought-provoking panel discussion that shed light on the banking industry's cautious stance on migrating to cloud services, highlighting the need for policy changes and compliance measures to ensure security. And much wide array of topics, extending from Zenyum's innovative application of AI in dental health assessments to the exciting reveal of Vue.ai's advanced personalization engine, designed to elevate the shopping experience. Their presentations vividly demonstrated the revolutionary capabilities and the expansive future of AI technologies. ・Panelists delved into AI's transformative impact and its common use cases ・Timo Weiss, Nithya Subramanian, and Sandal Kakkar engaged in a compelling discussion about "Gen AI: Existential Crisis or Competitive Advantage?" Evening Soirée: A Sunset to Remember After absorbing a plethora of information, it was time to let loose. The soirée, set against a breathtaking Istanbul sunset, provided a relaxed atmosphere for us to unwind, dance, and revel in the sumptuous dinner prepared for us. ・Turkish performing their traditional dance, Sufi whirling Looking Ahead for Global Buyma Discussions about these AI applications sparked ideas for enhancing the Global Buyma system, such as implementing auto- tagging based on product page content. Additionally, the personalization engine where AI analyzes shoppers' interactions would be perfect, allowing for dynamically tailored product displays and recommendations to cater to individual preferences. However, some guests noted that the use of AI for language translation in page displays requires further refinement.   Bye for now! ・From right: Ashwini Asokan, CEO & Founder of Mad Street Den, Hibaru Maywood, Head of Global Buyma 株式会社 エニグモ すべての求人一覧
Argo Workflowsを使ったPersistentVolumeの定期バックアップ こんにちは。 インフラグループ Kubernetes チームの福田です。 今回はPV(PersistentVolume)の定期バックアップシステムについて紹介したいと思います。 PVのバックアップについて PVのバックアップといっても色々とありますが、本記事ではスナップショットの取得を意味します。 スナップショットの取得は CSI が対応していれば、 external-snapshotter を利用することで、CustomResourceを書くだけで実現できます。 extenal-snapshotterを全く知らない方は以下の AWS の記事が概要を掴む参考になるかと思います。 aws.amazon.com バックアップシステムの仕組み 概要 システムの概要は以下のようになっています。 Argo Workflowsが定期的にVolumeSnapshotリソースを作成し、それをGitLabへプッシュする。 GitLabのCIが マニフェスト をバリデーションする。 Argo CDが マニフェスト のあるコード リポジトリ の変更を検出して、それを Kubernetes にデプロイする。 詳細 定期的に実行する処理 定期的な処理の実行はCronWorkflowで行っています。 単にCronJobとせず、CronWorkflowとした理由は、一度だけの実行や、定期処理の一時停止などが GUI で簡単にできるからです。 VolumeSnapshotリソースの生成 VolumeSnapshotリソースの生成を行うアプリケーションは Golang で実装した自前のコンテナアプリになります。 コンテナアプリは前述したCronWorkflowで実行されます。 世代管理機能 コンテナアプリはVolumeSnapshot マニフェスト を生成すると共に、古いVolumeSnapshot マニフェスト を削除するローテーション機能を持っています。 何世代分までのVolumeSnapshot マニフェスト を維持するかは 環境変数 で指定可能になっています。 VolumeSnapshot マニフェスト 間の新旧の比較はVolumeSnapshot マニフェスト 生成時のタイムスタンプを独自の アノテーション に記載し、それを比較することで実現しています。 管理対象フラグ ローテーション管理の対象とする否かのフラグを表現する独自の アノテーション もあります。 原則、コンテナアプリで生成されたVolumeSnapshotはこのフラグが立っています。 手動で作成したVolumeSnapshotやコンテナアプリによって作成された永続的に残しておきたいスナップショットについては、このフラグを下ろすことでローテーションの管理対象から外れて自動で削除されないようにできます。 生成される マニフェスト のサンプル apiVersion : snapshot.storage.k8s.io/v1 kind : VolumeSnapshot metadata : name : SAMPLE_NAME namespace : SAMPLE_NAMESPACE annotations : snapshot-tools.enigmo.co.jp/rotation : "true" snapshot-tools.enigmo.co.jp/rtime : 12345678 spec : source : persistentVolumeClaimName : SAMPLE_PVC volumeSnapshotClassName : SAMPLE_VSC snapshot-tools.enigmo.co.jp/rtime アノテーション は マニフェスト 生成時のタイムスタンプを表現している。 snapshot-tools.enigmo.co.jp/rotation アノテーション は世代管理の対象とするか否かのフラグになっている。 コードプッシュ コンテナアプリはVolumeSnapshot マニフェスト を作成、削除するとその変更をプッシュします。 プッシュ時のオプションでMergeRequest( github でいうPullRequest)を作成し、CIが成功したら自動でMRをマージするようにしています。 CIの実行 MRに対しては手動による マニフェスト のデプロイと同じ条件でCIが周り、問題がなければそれをマージするようになっています。 これにより、システムが生成した マニフェスト コードを特別扱いすることなく、統一したポリシー(例えば、フォーマッタやセキュリティチェック等の マニフェスト に対するバリデーション)を適用できます。 Argo CDによるデプロイ masterにマージされた変更を検出し、それを クラスタ へ自動で適用するようにauto-syncを有効化しています。 また、世代管理機能によってVolumeSnapshot マニフェスト の削除される場合もあるので、auto-pruneも有効化しています。 以下がArgoCD Applicationのサンプルになります。 apiVersion : argoproj.io/v1alpha1 kind : ApplicationSet metadata : name : snapshots spec : generators : - list : elements : - namespace : SAMPLE_NS1 - namespace : SAMPLE_NS2 - namespace : SAMPLE_NS3 template : metadata : name : '{{namespace}}-snapshot' spec : project : SAMPLE-PROJECT source : repoURL : GITLAB_URL targetRevision : master path : SAMPLE_PATH destination : server : K8S_ENDPOINT namespace : '{{namespace}}' syncPolicy : automated : prune : true 純粋なApplicationでなく、ApplicationSetとしている理由は我々は複数ネームスペースの運用をしているためです。 (Argo CDのApplicationは1つのネームスペースしかデプロイ先として指定できない。) まとめ Argo Workflowsを使ったPersistentVolumeの定期バックアップシステムについて紹介させていただきました。 実装方法としてオペレータパターンで開発する選択肢もありましが、cronの一時停止やカウントダウンタイマーなどを GUI で管理できる点からArgo WorkflowsのCronWorkflowを利用する実装を選択しました。 今回の記事が Kubernetes ネイティブな自動化システムの開発の参考になれば幸いです。 エニグモ ではエンジニアを含む各種ポジションで求人を募集しております。 株式会社 エニグモ すべての求人一覧 hrmos.co
「安心して何度も利用したくなる マーケットプレイス 」を作る!UXデザイングループを紹介 エニグモ でTech職種の採用や、採用広報を担当している 廣島 です。 この記事は、 エニグモ で新入社員向けのオンボーディング研修として実施する 部署紹介プログラム の中で プロダクトマネージャーやディレクター、UI/UXデザイナーが所属するグループであるUXデザイングループマネージャーの山田さんがグループの説明をした内容をまとめた記事です。 グループのミッションや、チーム体制、カルチャー、どのように他チームと連携しながらプロジェクトを推進しているかについて説明します。最後に、 BUYMA サービスやUXデザイングループの今後の展望や、UXデザイングループで仕事を行う魅力についてもお話しします。 ※この記事は Enigmo Advent Calendar 2023 の25日目の記事です※ BUYMA サービスについて まずは、 エニグモ が運営するサービスの BUYMA について説明します。 BUYMA は累計会員数1000万人超えの海外ファッションNo.1の通販(EC)サイトです。 世界中に在住する約20万人のパーソナルショッパー(出品者)から、ファッションを中心とした世界中のアイテムを購入できる「売り手」と「買い手」が個人であるCtoCのグローバル マーケットプレイス です。 主に個人の海外在住の日本人のネットワークを構築して海外から商品を購入することが可能で、多種多様な購入者の要望に答えています。商品の約半分は海外から届きます。 BUYMA が間に入ることで安心安全に海外の買い物ができるプラットフォームとして運営しています。 出品アイテムの特徴としては高級品×希少品の商品のラインナップが充実しており、海外限定デザインや、国内未上陸ブランド、国内完売入手困難品など高付加価値のアイテムを世界中から購入することができます。 BUYMA はファッションを主力にしつつも、インテリア・アウトドア、旅行などカテゴリーを拡大しています。 BUYMA のビジネスモデルの詳細は下記をご覧ください。 https://enigmo.co.jp/business/ UXデザイングループのミッション・業務内容とは? エニグモ で ディレクション を担うグループはUXデザイングループ(以下UXDグループ)と呼びます。 (UXとは「ユーザーエクス ペリエ ンス」のことで、ユーザーが商品やサービスを通じて得られる体験を指します) UXDグループとは名前の通り、ユーザーのPain(痛み)に向き合いながら、サービスをより使いやすく・より良くしていくために解決すべきことを考え、実行するグループです。 上記により、ユーザー体験を向上させ、安心して何度も利用したくなるような マーケットプレイス へ BUYMA を成長させることがミッションです。 さらに、UXDグループは BUYMA サービスのプロダクト側(エンジニア・デザイナー)とビジネス側(MD、 マーケティング ・広告、データ、CS、オペレーション等)のちょうど中間に位置して両者を支えサービスを前進させる役割を担います。 具体的な業務領域は? 実際にどういった業務領域を担当しているかについてお話しします。 BUYMA の新機能・既存機能の改修・新サービスの企画・ ディレクション からUI/UXと幅広く担当します。一般的なWeb業界の言葉でいうと具体的には下記のような業務です。 プロダクトマネジメント プロジェクトマネジメント 制作 ディレクション / 進行管理 各施策・プロジェクトの効果検証 UX視点からの企画提案 / サービスデザイン 定性 / 定量 調査 UIデザイン フロントエンドプログラム(主にインタ ラク ションデザイン部分)の実装 上記からも分かるようにUXDグループは ディレクション を行うグループではあるが、UIデザインやフロントエンドプログラムなどの制作も行っていることが特徴的です。 フロントエンドプログラムは、Reactでの実装などはエンジニア部署のフロントエンドエンジニアが行いますが、インタ ラク ションデザインなど実際に画面上で動く CSS や JavaScript の実装はUXDグループがおこないます。 その為、企画から制作までをシームレスにグループ内で行えユーザーの反応や外部環境に合わせてスピーディーに柔軟に対応することが可能です。 色々言いましたがまとめると業務内容は下記となります。 BUYMA の様々な課題に向き合い最適なスコープでユーザーに価値を提供する ユーザーの反応を定性、 定量 の両側面から分析して企画・施策に繋げる スキルとスキル(企画と開発)の間に立ち仲介・翻訳を行い、場合によっては制作も行う グループの体制について Webサービス における様々なスキルを持ったメンバーが集まっています。特徴としては、ディレクター部門であるが、制作するメンバーもいることです。一般的な職種で言うと、プロダクトマネージャー、ディレクター、 SEO ディレクター、UI/UXデザイナー、フロントエンドエンジニアが所属します。 前項で説明した業務領域全てを1人のメンバーが網羅しているのではなく、それぞれのメンバーの得意分野を活かしながら多岐にわたる領域をカバーし業務を行っています。 さらにグループは下記2つのチームに分かれています。 ・ プロダクトマネジメント チーム : マーケティング戦略 系の施策を担当するチーム ・ UI/UXチーム :UX戦略系の施策を担当するチーム グループの体制を示した図です。 明確にチームが別れているのではなく、UXDグループのマネージャーがUI/UXチームのマネージャーを兼任していることもあり、定例は一緒に行っています。週1回グループの定例の他、プロジェクトベースで進めています。 実際の・ プロダクトマネジメント チームとUI/UIXチームの業務は下記のインタビュー記事をご覧いただくと、よりイメージできるかと思います。 ・ BUYMAのWebディレクター(PdM)紹介/エニグモの魅力は?UXDグループとは?インタビューしました! ・ UI/UXデザイナーインタビュー/ユーザーによりよい体験を届けプロダクト価値の向上に取り組む! 開発フロー・プロジェクトの中でのUXDグループの役割 プロジェクトの開発のフローの中で、UXDグループのメンバーが他職種メンバーと協力しながらどういった役割を担うのかについてお話しします。 プロジェクトや施策に伴う開発はエンジニアやビジネスサイドなど他チームメンバーと連携してプロジェクト型チームで進行します。 開発全体の流れは、UXDグループメンバー(ディレクター)が企画をまとめプロジェクトがスタートします。ディレクターとエンジニアのリードメンバーやビジネスサイドのメンバーで企画をどのようにシステムに落とし込むのかを要件定義します。その後、エンジニアのリードメンバーがシステム設計を担当し、タスクを分解してエンジニ アメンバー を アサイ ンします。 プロジェクトや施策はディレクターから発案されることもあれば、 マーケティング やカスタマーなど他の部門からの提案もあります。 要件定義はプロジェクトの特性により異なりますが、通常は主にディレクターが他の ステークホルダー との ヒアリ ングを通じて要求を洗い出し、システムをどう作るかをエンジニアと共に要件定義を進めるのが主流です。 プロジェクトの目標やKPIの策定もディレクターが中心に行います。各プロジェクトでは、新規機能を開発する際に、「なぜこの新機能を開発するのか」「新機能リリースにより、ユーザーにどのような体験や行動を期待するか」「新機能リリース時の成功の定義は何か」などを、各プロジェクトの ステークホルダー と明確にしています。 最後に サービスの課題や今後の展望、 エニグモ でUXDにジョインする魅力、どういった方がマッチするかについて、マネージャーの山田さんにインタビューしました。 サービスの課題や今後の展望 今期の重点テーマである「 BUYMA サービスをより安心できる体験にする」ことは引き続き進めていきます。安心と一言でいっても様々な側面が存在します。 たとえば、高級ブランドを扱っているため、ユーザーが商品が本物であるか不安に感じることもあります。これに対処するため、来期も偽物の不安を払拭するための施策を推進していきます。 また、ビジネスモデル上、高級品および希少品を扱っていることが強みですが、日用品と比べて購入頻度が低い傾向があります。そのため、サイトに訪れるタイミングが少なくサイトにユーザーが定着しにくく、サービスとユーザーの距離が遠くなることが課題です。 この課題に焦点を当て、ユーザーがサイトに定期的に訪れる動機づけや、サービスとの継続的な関係を築くための取り組みにも注力していきたいと考えています。 UXデザイングループの魅力 プロジェクトごとに各職種のメンバーが アサイ ンされ、小規模チームが結成されます。そのためメンバー、一人ひとりが大きな裁量を持ち、エンジニア、デザイナー、ビジネスメンバーと密に連携し、距離が近く風通しも良く別部門という感じはしません。良い人(互いを尊重し、前向きなメンバー)が多いため、仕事上の人間関係による変なストレスがあまりなく、プロダクト開発方法や体制を共に進化させるマインドが根付いています。 また、グローバル×CtoCサービスという、 BUYMA 独自の仕組みやUXを構築するフェーズに携わることができます。出品者側と購入者側の双方のデータやユーザ―行動に基づくデータドリブンな開発が可能です。プロジェクトの成果を数値(売上、CVRなど)やユーザーの声によって直接実感することができます。 どういった方がマッチするか 経験やスキルも大事ですが、熱意やマインド面、カルチャーへの共感、 エニグモ へのバリューへのマッチを大切にしています。※バリューについての詳細は こちら 業務領域が幅広いため、俯瞰して物事を柔軟にアプローチできる方や、自らイニシアチブを取り推進できる方が活躍できるかと思います。 決まった案件をこなすだけでなく若手から裁量を持って働け、新規機能や新規事業の企画を自ら発案しユーザーに届け、世の中を変える可能性があります。 課題を見つけて積極的にア イデア を提案し進めていく方がマッチすると思います! BUYMA や関連サービスのサービス品質を常に進化させ、国内および世界各国のユーザーがますますサービスを利用して喜びや満足を得られるような機能を、楽しみながら共に考え抜いていただける方にジョインいただけると嬉しいです。
こんにちは、 エニグモ 嘉松です。 BUYMA のプロモーションや マーケティング を行っている事業部に所属、その中のデータ活用推進室という部署で会社のデータ活用の推進や マーケティング ・オートメーションツール(MAツール)を活用した販促支援、 CRM などを担当しています。 この記事は Enigmo Advent Calendar 2023 の 25 日目の記事です。 はじめに この記事では Google から提供されているBigQueryのオンライマニュアル「関数のベストプ ラク ティス(Best practices for functions)」を試してみた結果を紹介していきます。 「関数のベストプ ラク ティス」(日本語版) https://cloud.google.com/bigquery/docs/best-practices-performance-functions?hl=ja 「関数のベストプ ラク ティス」では、以下の4つのベストプ ラク ティスが紹介されています。 文字列の比較を最適化する 集計関数を最適化する 分位関数を最適化する UDF を最適化する はじめは4つ全てを1つの記事で紹介するつもりでしたが、記事を制作していく中で1つでもそれなりの記事ボリュームがあることが分かったので、読みやすさを重視して1つの記事で1つのベストプ ラク ティスを紹介していくことにしました。 ということで、この記事は「その1」として「文字列の比較を最適化する」を試した結果を紹介していきます。 文字列の比較を最適化する ベストプ ラク ティス 可能であれば、 REGEXP_CONTAINS ではなく LIKE を使用します。 BigQuery では、 REGEXP _CONTAINS 関数または LIKE 演算子 を使用して文字列を比較できます。 REGEXP _CONTAINS は多くの機能を提供しますが、実行に時間がかかります。 REGEXP _CONTAINS ではなく LIKE を使用すると、処理時間が短くなります。 特に、 ワイルドカード 一致など、 REGEXP _CONTAINS が提供する 正規表現 をフルに活用する必要がない場合には処理時間が短くなります。 次の REGEXP _CONTAINS 関数の使用を検討してください。 SELECT dim1 FROM `dataset.table1` WHERE REGEXP_CONTAINS(dim1, '.*test.*'); このクエリは次のように最適化できます。 SELECT dim1 FROM `dataset.table` WHERE dim1 LIKE '%test%'; なるほど。やはり機能の多い関数、複雑なパターンに対応できる関数よりも、単純なことしか出来ない関数の方が処理は軽い(速い)と。言われてみれば当たり前といえば当たり前ではありますが。単純なことしてできない関数が利用できるケースであれば、そちらを使ったほうが良い、 正規表現 による検索が必要ない場合は LIKE を使いましょう、ということですね。そもそも REGEXP_CONTAINS より LIKE を使うことを最初に考えるとは思うけどww( REGEXP_CONTAINS の方が汎用性が高いので常に REGEXP_CONTAINS を使っています、みたいな人は注意が必要です!) 試してみた 検証方法 REGEXP_CONTAINS と LIKE を使ったクエリを実行して比較していきます。 比較する候補となる値は 経過時間 と 消費したスロット時間 の2つ、クエリの「実行の詳細」から取得できます。 それぞれの意味は以下の通りです。 経過時間 クエリが開始されてから完了するまでの時間 サーバの使用状況などによる待機時間も含まれます 消費したスロット時間 「スロットとは、 SQL クエリの実行に必要な演算能力の単位です。」(「BigQueryのコンソールの(?)」より) スロットについての詳細は以下の Google のマニュアル「スロットについて」を参照ください。 https://cloud.google.com/bigquery/docs/slots?hl=ja BigQueryスロットは、BigQueryでSQLクエリを実行するために使用される仮想CPU ということで、ざっくり言うとクエリ( SQL の実行)に使用したCPU使用量です。 今回の検証では 消費したスロット時間 を比較ます。( 経過時間 だと Google のその時のサーバの負荷といった外部要因が加わるため) クエリはそれぞれ5回ずつ実行してその平均値を比較します。 クエリを実行するとクエリの結果がキャッシュされるので、キャッシュを使用しないように設定を行います。 なお、対象のテーブルは約1億件のテーブルを対象にしました。 キャッシュクリアの方法 検証中はキャッシュを使用してしまうと正しい計測ができないので、キャッシュを使用しないように設定を行います。 https://cloud.google.com/bigquery/docs/best-practices-performance-functions?hl=ja#optimize_string_comparison 検証結果 消費したスロット時間(時間:分:秒) 試行回数 REGEXP _CONTAINS LIKE 1 0:03:06 0:02:50 2 0:03:22 0:02:29 3 0:03:02 0:02:36 4 0:03:06 0:02:20 5 0:02:56 0:02:18 平均 0:03:06 0:02:31 REGEXP_CONTAINS を使用した場合の平均の「消費したスロット時間」は 3分6秒 だったのに対して、 LIKE では 2分31秒 と35秒短縮、減少率は 80.79% と約80%に短縮、約20%改善されました。 この20%の改善をどう取るか。かなり短縮できたと取るか、さほど変わらないと取るか。 個人的には、思ったほど変わらないな、という印象でした。 といのも、 SQL ではテーブルの結合方法(JOIN)や、絞り込み条件(WHERE句)をチューニングすると実行時間が半分になったり、場合によっては1/10になったりすることもざら、珍しくないため。 ただ、単純に LIKE を使うだけで20%削減されるのであれば、それは価値ありますよね。 このクエリで検索の対象としているカラムの平均の文字数は33文字と少なかったため大きな差が出なかった、もう少し文字数が多いカラムを対象にしたらもう少し差が出るのでは、と考え文字数の多いカラムを使って検証を実施しました。 追加検証結果(時間:分:秒) 平均文字数が758文字のカラムを使って比較 消費したスロット時間(時間:分:秒) 試行回数 REGEXP _CONTAINS LIKE 1 0:59:53 0:55:26 2 1:02:00 0:53:43 3 1:02:00 0:51:51 4 1:01:00 0:50:43 5 1:04:00 0:52:02 平均 1:01:47 0:52:45 まずカラムの平均文字数が増えたことで「消費したスロット時間」も増加しています。 平均文字数が33文字では約3分だったのに対して、758文字は約60分と約20倍に。 当然ですが検索する文字数が増えればその分として処理の時間も増えますね。 REGEXP_CONTAINS を使用した場合の平均の「消費したスロット時間」は 1時間1分47秒 だったのに対して、 LIKE では 52分45秒 と9分2秒短縮、減少率は 85.39% と約85%に短縮、15%改善されました。 平均文字数が33文字のときは約80%に短縮されたのに対して、758文字では約85%と短縮の率は減少しました。 この辺りの差は文字数のバラツキや検索する文字によっても差が出るのかもしれません。 文字数の多いカラムでは LIKE を使うことでよりパフォーマンスに差が出ると思いましたが、大きな違いはありませんでした。 まとめ REGEXP_CONTAINS と LIKE を使ったクエリを実行して比較してみた結果、LIKEを使った方がクエリのパフォーマンスは確かに改善された! その改善率は今回の検証では約20%だった!! 可能であれば、 REGEXP_CONTAINS ではなく LIKE を使用しましょう!!! 本日の記事は以上になります。 エニグモ Advent Calendar 2023 もこの記事で最後になります。 最後まで読んでいただきありがとうございました。 株式会社 エニグモ すべての求人一覧 hrmos.co
こんにちは! 今年7月に中途入社しました、コーポレートエンジニア(コーポレートIT[CO-IT]チーム) のフルセです! 今年も終盤(早いですねぇ、、)ということで、 Enigmo Advent Calendar 2023 の季節になりました!! クリスマスイブである 24 日目 を担当する私は入社エントリ・振り返りなど中心に自由に書きたいと思います! なお、この記事が少しでもコーポレートエンジニアに興味がある方や入社を検討してくださっている方の参考になれば幸いです。 Embed from Getty Images window.gie=window.gie||function(c){(gie.q=gie.q||[]).push(c)};gie(function(){gie.widgets.load({id:'eGD2T_vEQIpzxevVRkRvfQ',sig:'E4aHXJwgZtXDaAqpL9J550lu8QN5ONksi1IwL5kG1WY=',w:'556px',h:'311px',items:'531047383',caption: true ,tld:'com',is360: false })}); 簡単な経歴紹介 そもそもコーポレートエンジニアって何? 実際に入社してみて感じたこと 入社してからの仕事とその感想 内容 感想 こんな方に向いているかも(個人見解) 最後に 簡単な経歴紹介 私の経歴についてざっくり下記のようにまとめました。 ① 新卒でシステムインテグレータ企業にSEとして入社 →ITの基礎知識・技術を獲得! and IT業界の厳しさを痛感、、(大袈裟) ② システムインテグレータ企業を退職 →主にシステム相手の業務だったので、より人と接する仕事がしたいと考え決意 ③ 某アパレル商社の情シスへ転職 →培った知識とスキルを活かし新たな業種へチャレンジしたかった! →情シスが意外と性に合っていることに気づくきっかけに! ④ カナダへ留学 →ずっと挑戦したかった海外留学に挑戦! →語学はもちろん人との出会いや経験から人生にプラスになった! ⑤ エニグモ にコーポレートエンジニアとして入社 →帰国後にご縁があり、経験を活かしコーポレートエンジニアとして入社! そもそもコーポレートエンジニアって何? コーポレートエンジニアとは? コーポレートエンジニアとは、企業内のIT活用や運用を担当するエンジニアのことです。 企業のIT環境の変化は著しく、テレワークの普及などによって クラウド サービスの利用も活発になり、 SaaS 型のID管理や統合認証サービスを利用する企業が増えてきました。そのなかでコーポレートエンジニアは、社内ITの構築・運用をはじめとして、社内業務の課題解決のための企画立案や部門間の調整まで幅広い業務を担います。 もっと詳しく知りたい方は、チームメンバーの記事を読んでみてください! tech.enigmo.co.jp 実際に入社してみて感じたこと コーポレートエンジニアとして働く以上、社員の方々とのコミュニケーションをかなり重要だと考えていました *1 。 しかし、 エニグモ はリモートワーク中心の会社のため、Face to Face でのお話しする機会が少なくどのように交流の輪を広げていこうか少し不安(そもそも入社直後というのもあり。。)に感じていました。 そんなときに、2M(Monthly Meet-up) *2 に参加させていただきました。 そこでは色々な部署や役職の方と分け隔てなくフランクにお話しすることができ、一瞬にして不安が和らぎました。 やはり、新しい環境でのスタートはどうしても孤独感や不安がつきものだと思いますが、こういった交流会があると気持ちよくスタート ダッシュ が切れますよね!そもそも、こういった交流会を定期的に行なっている企業というのは少ないと思うので、これぞ エニグモ の良いところだと思っております。 それからというもの、毎月可能なかぎり参加し交流の輪を広げております!(無料で美味しいお酒とご飯が得られることも理由の一つなのですが笑) 2Mについて気になる方は、レポート記事がありますので是非ご覧いただければと! www.wantedly.com 入社してからの仕事とその感想 入社してからのざっくりとした仕事内容とその感想を書かせていただきました! 内容 社内ヘルプデスク対応 内容:PCの故障や不具合、各 種IT サービスのトラブル対応等 対応頻度:1〜2件/日 アカウント管理 内容:アカウントの付与・削除から数量管理・購入 対応頻度:1〜2件/週 IT機器の管理・キッティング 内容:PCやモバイルデ バイス の発注・管理・キッティング 対応頻度:10〜20件/月 社内ナレッジの作成・整備 内容:手順書やルールの新規作成・ブラッシュアップ 対応頻度:2回/週 中途入社者向けのオリエン 内容:PCのセットアップ方法の説明、社内ITサービスルールの共有 対応頻度:1〜2回/月 社内不要OA・IT機器廃棄対応 内容:廃棄物回収対応 対応頻度:2回/年 カオスマップ作成 内容:社内で利用中サービスを整理と今後導入が必要なサービスの洗い出し 対応頻度(見直し):2回/年 感想 一覧にしてみると、今年は色々なことをやらせていただいたと改めて感じています。 裁量を任せていただいているため、日々に マルチタスク をこなしておりますが、 それもまた自分のタスク管理力や遂行力の向上につながっていると感じます。 個人的にやりがいがあった業務としては、「社内不要OA・IT機器廃棄対応」です。 業者選定から始まり、やりとり(契約締結や回収日の調整など)、社内ナレッジ作成、各方面との連携など、これぞコーポレートエンジニアと感じるようなタスクだったため、とてもやりがいがありました。 それと山積みだった廃棄端末類をスッキリさせることができたという達成感が大きかったですね笑 またタスクとして面白かったものとしては、「カオスマップ作成」です。 初めてカオスマップというものを作成したので、そもそもカオスマップとは?からスタートし、他企業のカオスマップの調査、社内ITサービスの整理、今後導入が必要なITサービスの洗い出しを行いました。その後は、カオスマップをパズルのように作成する大変な作業がスタートし...と、ここでは書ききれないので、詳しい作成プロセスなどは機会があればまた書かせていただきたいと思います。(今回は割愛させていただきます。) それからというもの、試行錯誤を繰り返し、レビューを重ねてやっとの思いで完成させることができました。 結果として、時間をかけて取り組んだからこそ、サービスに対する理解や知見を広くできましたし、社内の課題(足りていないサービス)も炙り出すことができました。とはいえ、まだまだ未熟なカオスマップだと思っているので、これからもっと成熟させていきたいと思います。(楽しみ!) こんな方に向いているかも(個人見解) コーポレートエンジニアとしてのキャリアをスタートしたばかりの私ですが、 そんな私だからこそ考えるコーポレートエンジニアにはこんな人が向いているのではないかというのをまとめてみました! 私が思うに下記の3つの要素が肝になってくると思います。 コーポレートエンジニアの必要要素 新しい情報・技術に興味があり探求するのが好きな方(好奇心) 日々進化する IT技術 やサービスをいち早く自社へ展開・導入するため 最新の情報に敏感であれば、サービスなどがアップデートされたとしても柔軟に対応することができるため 人とコミュニケーションするのが得意・好きな方(コミュニケーション力) 業務上(問い合わせ対応・情報共有など)何かとコミュニケーションが必要になるため 問題を切り分けて解決することが得意・好きな方(柔軟性) コーポレートエンジニアは企業の多くの課題を与えられるので、一つ一つ解決し柔軟に対応することが求められるため ここで書かせていただいた3つの要素は、かなり重要なポイントになると考えます。 コーポレートエンジニアの業務は、IT関連の何でも屋のようになりがちだと思っております。 そのためタスクを挙げ出したらキリがないうえ、日々変化します。 そのため、柔軟性はもちろん、社内外連携のためのコミュニケーション力やITサービスに関する知見を広げるための好奇心が重要になってきます。 もちろん、足りてないと感じている要素があったとしても、業務を通じてレベルアップすることは可能だと思います!(私も絶賛レベルアップ中...) ※あくまで個人の見解なのでご了承ください。 最後に 私もまだまだ未熟なので、日々進化する IT技術 ・情報に置いていかれないよう自己研鑽を続けつつ、 社内の課題を一つずつ着実に解決し、より良い社内環境づくりに励みたいと思います! 最後まで読んでいただき、ありがとうございました!! 株式会社 エニグモ すべての求人一覧 hrmos.co *1 : 情シス時代に業務上のコミュニケーションがかなり重要だと理解していたため *2 : 毎月月初に開催される社内交流会