GMOペパボ – GMO Developers https://developers.gmo.jp 「GMO Developers」は GMOインターネットグループの開発者向け情報をお届けしています。 Mon, 02 Jun 2025 01:26:41 +0000 ja hourly 1 「どうやって来たんですか?」 〜フェリーによる移動イベント Rubyist Bulk Load を実施しました〜 https://developers.gmo.jp/technology/64253/ Fri, 30 May 2025 01:00:00 +0000 https://developers.gmo.jp/?p=64253

GMOインターネットグループは2025年4月16日〜18日、愛媛県松山市で開催されたRubyKaigi 2025にカスタムスポンサーとして協賛いたしました。カスタムプランについてはGMOペパボが全面協力のもと、東京から徳島へのフェリー移動を“出会いと交流の場”に変えることを目的に、「Ferry Sponsor」を拝命しオーシャン東九フェリーを活用した約18時間の船旅を提供いたしました。
本編ではFerry Sponsorの実施レポートをお送りいたします。

「どうやって来たんですか?」

京都で開催された RubyKaigi 2016 以来、首都圏外で RubyKaigi が開催されるようになって 10 年近くが経ちますが「ところで、どうやって来たんですか?」という会話は珍しいかも知れません。今どき、Google Maps などで開催地を入力すれば、最速、あるいは最安の経路を一瞬で知ることができます。お互いにどこから来たのかさえ分かれば、そのようにして来たのだろうと思って、わざわざ話題には出さないでしょう。

申し遅れました。GMOペパボ株式会社で働いている yancya と申します。本稿では、いつもとはちょっと違った移動を皆さんに広め、もっと面白くするイベントを開催した件についてレポートします。

私はここ数年、RubyKaigi 2022 の三重県津市、2023 の長野県松本市、2024 の沖縄県那覇市 の3回にわたって、フェリーと小型自動二輪車(原付バイク)を使って RubyKaigi の会場まで移動し、帰ってきました。そのように、毎年のように船とバイクの旅を繰り返していると、それを見ているみなさんから「今年はどうやって来たんですか?」あるいは「来年はどうやって行くんですか?」そのように訊かれることが増えてきました。

「どうやって行くか」はもっと面白くできる。その可能性を私は信じています。

Rubyist Bulk Load

今年の 5 月に愛媛県松山市で行われた RubyKaigi 2025 において、GMO インターネットグループ主催、GMO ペパボの運営によって Rubyist Bulk Load というイベントを開催しました。このイベントは関東から四国までフェリーで移動したい Rubyist に運賃をサポートするイベントです。

以下のようなスケジュールで進行しました。

2025/04/14 (月)

18:00 集合・受付開始・乗船
19:00 出港
19:30 懇親会(軽食をご用意いたします)
21:30 入浴・就寝(あくまで目安です)

2025/04/15 (火)

07:00 起床(あくまで目安です)
13:20 徳島港着 下船開始
13:40 徳島駅ゆきバス(250円/人)に乗車(車・バイク利用の方は解散)
14:00 徳島駅到着 解散(自由に松山まで向かってください)

今回、RubyKaigi の前日に徳島港に着くフェリーのチケットを手配しましたが、徳島から松山への道のりは、電車、高速バスや車、どんな手段を使っても4時間くらいかかるので、参加者の皆さんは結構大変だったと思います。お疲れさまでした。

企画の背景

Rubyist Bulk Load を企画するにあたって、過去に実施された RubyKaigi のスポンサーイベントが参考になりました。主に以下の3つです。

福岡で行われた RubyKaigi 2019 にて、永和システムマネジメントさんが博多湾をクルーズ船で周遊しながらドリンクアップを行う Night Cruise Sponsor というスポンサーイベントを開催されました。「こういうのに船とか使ってもいいんだ」と衝撃を感じたのを覚えています。

長野県松本市で開催された RubyKaigi 2023 にて、クックパッドさんが Rubyists on Rails という企画を実施されていて、私はそれにも参加しました。新宿駅から長野県松本市まで、隣に座った Rubyist と RubyKaigi 2023 でどのセッションを見る予定か、とか、普段どんな仕事をしているのか、などの話題で盛り上がりながら移動して、あっという間の2時間半でした。中高生時代の修学旅行を思い出す良いイベントだったと思います。

沖縄県那覇市で開催された RubyKaigi 2024 で、5年ぶりの Night Cruise が催されました。洋上という非日常の空間で Rubyist たちと歓談した思い出は心に残るものでした。

「船でもなんでも使う」「交通機関による移動を支援する」が「アリ」だったということは「船による移動を支援する」も「アリ」ということです。大変参考になりました。

実は存在したプランA

プログラミング言語 Ruby の作者まつもとゆきひろさんが名誉市民である島根県松江市では、毎冬に RubyWorld Conference というイベントが行われているのですが、私はほぼ毎年参加しています。東京から松江市に行く方法は基本的には羽田空港から出雲縁結び空港、もしくは米子鬼太郎空港まで飛行機になると思いますが、私はあえて東京駅から松江駅まで寝台特急サンライズ出雲を使っています。もう10年程続けていますが、次第に「寝台特急で松江に行きたい」という Rubyist が増え、同じ列車に乗り合わせた Rubyist 同士で Sunrise.rb というミートアップを行うようになりました。時間をかけて移動する最中に同じ目的地を目指している仲間と取るコミュニケーションはとても良いものです。

RubyKaigi 2025 の開催地が愛媛県松山市に決まったとき、移動手段について最初に考えたのは寝台特急サンライズ瀬戸を利用する事でした。サンライズエクスプレスは東京駅から岡山駅まで移動した後、山陰方面のサンライズ出雲、四国方面のサンライズ瀬戸に分離するのですが、四国に行くならサンライズ瀬戸で Sunrise.rb 特別版を実施したら楽しいだろうなと思ったのです。
Sunrise.rb を個人的に企画して、賛同する人達とでそれぞれ自腹で開催することも考えたのですが、スポンサー企画として何かできるかもしれないと思い、検討を開始しました。通常、サンライズ瀬戸は高松が終点ですが、予算さえあれば貸し切りの特別編成が出せるかもしれない。そうなれば、もしかして松山まで行って貰えるのではないかと思い、ペパボの社員旅行などを手伝ってくれている東武トップツアーズさんに相談して実現可能性について探り始めました。
しかし、サンライズ瀬戸は乗車の1ヶ月前にみどりの窓口などでチケットを取り合うような人気の寝台列車なので、特別編成はおろか、車両の貸し切りすら難しいという事が分かってきました。そこで、当初のアイディアに代わる何かを検討するにあたって、趣旨を「移動に長い時間をかけて目的地を同じくする仲間とのコミュニケーションを楽しむ企画」としたときに、必ずしも寝台特急にこだわる必要はないと考えました。そして、特急、新幹線、飛行機以外の方法としてフェリーがあるという事を思いだしたのです。
東京近郊から出ている長距離フェリー航路は2つあり、いずれも目的地は福岡県北九州市の新門司港ですが、そのうちの1つであるオーシャン東九フェリーは途中で四国の徳島港に立ち寄ります。東京港を 19:00 頃出て、翌日の 13:30 頃到着するので、18時間程の移動を共有する事が出来ます。しかも、寝台特急と違ってラウンジも広いですし大浴場まであります。むしろ寝台特急よりも良いかもしれません。車やバイクも載せることができるので、フェリー以外の旅路は参加者が自分で自由に設計することもできます。
先述のように、船を使ったドリンクアップ企画と、移動手段を提供し移動中のコミュニケーションを楽しむ企画が既に存在してるので、船を使った移動を企画にする事は実現出来るものだと思いました。あとは東武トップツアーズさんと相談しながら具体的な計画を練り、Rubyist Bulk Loadとなったのです。

ドリンクアップイベントの時間

ところで、カンファレンスにおける一般的なドリンクアップイベントは、会場近くの飲食店を貸し切り、18:30 Open, 19:00 Start, 21:00 Close というようなタイムテーブルで運営されている事が多いと思います。私もたくさんのドリンクアップイベントにお世話になって来ましたし、とてもありがたいとは思っているのですが、どうしても2時間あまりでは物足りなさを感じてしまいます。もちろん、ドリンクアップで仲良くなった人や、今、この瞬間に話すべき事がある人達が連れ立って2次会に行けばよいというのはあるのですが、店を探す時間がもったいなかったり、地域によっては夜遅くまでやっている店がないなどの問題があり、受け皿不足は否めません。
その点、フェリーのラウンジで行った懇親会は夜が更けるまでゆったりと、心ゆくまで交流が出来たと思います。乗船から下船まで合わせて18時間もあるイベントならではのメリットだったと言えるでしょう。

思わぬ副作用

普段、フェリーに車やバイク、自転車を乗せて移動するのはハードルが高いと思うんですが、今回のイベントではそれらの費用も無料にして、積極的に車両での利用を推奨しました。その結果、全体の1/3くらいの方がフェリーに車両を持ち込んでご参加いただきました。

下船時、懇親会などで仲良くなった参加者同士で「私は徳島から電車で行くので松山までスーツケースを運んで欲しい」とか「松山まで一緒に乗っていく?」というようなイベントが発生していました。これは予想していませんでしたが、それくらい皆さんが仲良くなれたイベントを実施できて嬉しかったです。ちなみに、私も自動車で参加していたのですが、スーツケースを3つ運びました。

さいごに

今回、Rubyist Bulk Load を実施したことにより、RubyKaigi 2025 では「どうやって来たんですか?」とか「あのフェリーで来たんですか?」という会話が増えていたんじゃないでしょうか。そうであれば嬉しいです。こういうイベントの存在が、今後のみなさんの移動の選択肢を広げることを期待しております。

来年、RubyKaigi 2026 は北海道の函館で開催されますね。まだ分かりませんが、もしかしたら、来年もフェリーのイベントが実施出来るかもしれません。もしくは、もっと面白い何かをやるかもしれません。是非、チェックしていただければと思います。

]]>
Goで実現する効率的なメトリクス取得 https://developers.gmo.jp/technology/59415/ Mon, 23 Dec 2024 15:00:00 +0000 https://developers.gmo.jp/?p=59415

この記事は GMOインターネットグループ Advent Calendar 2024 24日目の記事です。

はじめに

こんにちは!GMOペパボ株式会社に新卒入社した永田です。社内ではてつをと呼ばれています。普段はロリポップ! for GamersのエンジニアとしてGoを用いた開発に携わっています。今回は12月17日にリリースされたばかりの「リソース可視化機能」について実装を担当したので、考えた点と工夫した点について紹介します!普段、Goを用いた開発を行っている方や、ロリポップ! for Gamersがどのような技術で作られているか興味がある方はぜひご覧ください!

ロリポップ! for Gamers とは?

はじめにサービスについて紹介させてください。弊サービスはGMOペパボ株式会社が運営する、簡単に構築できるマルチプレイ専用ゲームサーバーを提供するサービスです。リリースの話やテスト戦略に関しては GMOインターネットグループ Advent Calendar 2024 15日目の記事である「短期間での新規プロダクト開発における「コスパの良い」Goのテスト戦略」で詳しく記載されているのでぜひご覧ください。

https://gamers.lolipop.jp

リソース可視化機能とは?

さて、私が今回実装した機能である「リソース可視化機能」はサーバーのコントロールパネルにおいてユーザーが使用しているサーバーのリソース情報(CPU使用率、メモリ使用量、ディスク使用量)を確認できる機能です。

リリース前の課題

現状の課題としてユーザーが「サーバー重いな〜」と感じた時にそれが「ネットワークの原因」なのか、「サーバーのリソース不足」なのか問題の切り分けをすることができず、お問い合わせにつながってしまうという問題がありました。

そこでサーバーのリソースを「見える化」することで、ユーザーに解決の手助けをすることができるかつ、プランアップの訴求にも繋がるようになります。

要件定義と設計

ここから実装するにあたって考えたことと工夫したことをお話ししていきます。

下の図はサービスのアーキテクチャの簡略図です。実装範囲のみを示しているので、今回はこちらの図を用いて説明します。

先述した通りロリポップ! for GamersのバックエンドはGoで実装されておりユーザーがコントロールパネルで設定した内容はバックエンドからLXDを経由してユーザーが実際にゲームをするサーバーに反映されます。ここにある、LXD(Linux Container Daemon)とはコンテナ、VMのオーケストレーターのことです。

メトリクスの収集方法

サーバーのリソース情報を表示するためにはまず、メトリクスの収集を行う必要があります。メトリクスとはシステムやサービスの状態やパフォーマンスを示す指標でありますが、ここでは特にサーバーのリソースやパフォーマンス情報、例えばCPU使用時間やメモリ使用量といったデータを指します。

そのメトリクス収集方法には以下の方法があります。

1.LXDが提供しているAPIを使用する方法

LXDのREST APIエンドポイントに lxc query /1.0/metrics があります。このAPIを使用することで、LXDが管理するコンテナやVM、ホストシステムのメトリクス情報を取得することが可能です。

ただし、このAPIで得られるメトリクスは以下の例に示すように累積値であり、時系列でデータを取得することはできません。例えばCPU使用率は、ある時間におけるCPUの稼働状況を示し、タスク処理に費やした時間とそうでない時間(idle時間など)を基に算出されます。これを正確に把握するには少なくとも2点以上でデータを取得して、その経過時間の差分を基に算出する必要があるため実際の値とズレが生じたり、制約が生じる可能性がありました。

teppei0717@lxdcluster1-1:~$ lxc query /1.0/metrics | grep 01234567890ABCDEFGHIJKLMNO | grep cpu | grep system | sort
lxd_cpu_seconds_total{cpu="0",mode="system",name="01234567890ABCDEFGHIJKLMNO",project="default",type="virtual-machine"} 46.49
lxd_cpu_seconds_total{cpu="1",mode="system",name="01234567890ABCDEFGHIJKLMNO",project="default",type="virtual-machine"} 45.99
lxd_cpu_seconds_total{cpu="2",mode="system",name="01234567890ABCDEFGHIJKLMNO",project="default",type="virtual-machine"} 32.4
lxd_cpu_seconds_total{cpu="3",mode="system",name="01234567890ABCDEFGHIJKLMNO",project="default",type="virtual-machine"} 42.62
lxd_cpu_seconds_total{cpu="4",mode="system",name="01234567890ABCDEFGHIJKLMNO",project="default",type="virtual-machine"} 47.06
lxd_cpu_seconds_total{cpu="5",mode="system",name="01234567890ABCDEFGHIJKLMNO",project="default",type="virtual-machine"} 47.26 # <- 累積CPU使用時間

2.Prometheusサーバーを用いる方法

LXDのAPIでは時系列データの取得ができない制約を補う方法として、Prometheusサーバーからメトリクスを収集する方法を考えました。Prometheusは、定期的にメトリクスを収集してCPU使用率やメモリ使用量などのメトリクスを時間の経過とともに追跡でき、瞬間的な値だけでは得られない情報を取得することが可能です。

さらに、Prometheusでは独自のクエリ言語であるPromQLを用いることで、柔軟かつ高度なデータ取得や分析が可能です。たとえば、ある期間における特定のCPUの特定のコアの平均使用時間を求めたり、特定のリソースの異常値を検出したりするような複雑な分析も簡単に行うことができます。これにより、システムの状態やリソース使用状況を詳細に把握することができます。

以上の理由から、LXDが提供するAPIでのメトリクス取得ではなく、Prometheusサーバーを用いる方法を採用しました。

しかし、既存のPrometheusサーバーをリソース情報の取得に活用するには、以下の課題がありました。

  • このサーバーは、主にシステムを監視し、安定した運用を支援する目的で構築されている。
  • ユーザーに対してデータを提供する用途は想定されていない。

そのため、このサーバーはユーザーからの大量のリクエストを処理することを前提としていません。特に、必要以上にリクエストを送信すると、想定外の負荷がかかり、監視や運用に支障をきたす可能性があるため、この点に配慮する必要がありました。

そこでバックエンドで必要以上のリクエストを抑制するためにキャッシュとレートリミットを実装しました。

バックエンドで工夫したこと

キャッシュ

まず、キャッシュの手法としてGoのin-memoryキャッシュパッケージ(bigcachefreecachegolang-lruなど)を検討しました。これらのパッケージを使用することで、高速かつ効率的なデータキャッシュが可能です。しかし、Podが複数存在する環境では、各Pod間でキャッシュを同期することが困難となります。この問題が原因でデータの一貫性が保てなくなる可能性があるため、in-memoryキャッシュの利用は採用しませんでした。

次に、キャッシュの手法としてRedisの利用を検討しました。Redisは高性能なインメモリデータストアであり、分散環境でも問題なく動作するため有力な選択肢でした。しかし、将来的には取得したメトリクスをグラフとして可視化する予定があり、そのために時系列データを保持できる専用のデータベースを別途導入する予定があります。そのため、Redisは今回のスコープ内でしか使用されない一時的な選択肢となり、長期的な視点では適していないと判断しました。この理由から、Redisの採用は見送りました。

最終的に、キャッシュの手法としてDB(MySQL)を利用することにしました。DB(MySQL)は既に運用中の環境で利用されており、今回のスコープ内で新たな導入コストを抑えることができます。また、リリース期日を考慮した際、DB(MySQL)を用いることで迅速に実装を進められることも大きなメリットです。

これらの理由から、今回の要件に対してDB(MySQL)を利用することが最善の選択であると判断し、取得したリソース情報をキャッシュする手法として以下の方針としました

①基本はDB参照するだけ

バックエンド(Goアプリケーション)は基本的にDBを参照することでデータを取得します。
これにより、Prometheusへのリクエストを極力減らし、サーバーに過剰な負荷をかけないようにしています。キャッシュがDBに保存されているため、繰り返しのアクセスや同じデータの取得要求に対して、即座に応答することが可能になります。

②キャッシュが有効期限を過ぎているものだけ取得しにいく

DBに保存されているキャッシュの有効期限が切れている場合のみ、バックエンドはPrometheusに新しいデータを取得しに行きます。これにより、データが最新であることを担保しつつ、Prometheusへのリクエスト数を最小限に抑えることができます。

具体的には、

  • キャッシュの有効期限が切れていない場合 → DBからデータを返す
  • 有効期限が切れている場合 → Prometheusからデータを取得し、DBを更新する

この2つの方針を組み合わせることで、Prometheusへのアクセスを最小限に抑えつつ、必要なリソース情報を効率的に取得・提供できる仕組みを構築しました。

レートリミット

複数のリクエストが同時に発生し、キャッシュの有効期限が切れている場合に一斉にPrometheusへアクセスが集中してしまうため、サーバーに過負荷がかかる問題が生じます。

そこで、リクエストの集中を防ぐためにレートリミットとして golang.org/x/time/rate ライブラリを採用しました。このライブラリはトークンバケットというアルゴリズムを用いており、一定間隔で発行されるトークンを消費する形でリクエストを制限します。これにより、短時間に過剰なアクセスが発生することを防ぎ、Prometheusへの負荷を抑えることが可能になります。

トークンバケットアルゴリズム(https://scrapbox.io/study-hiroki  )

以下は、レートリミットのコード例です:

// レートリミットの設定
limiter := rate.NewLimiter(5, 2)

// リクエスト処理
for i := 0; i < 10; i++ {
	go func(id int) {
		limiter.Wait(context.Background()) // トークンを取得するまで待機
		fmt.Printf("Request %d: allowed\n", id) // リクエスト処理
	}(i)
}

rate.NewLimiter(5, 2) は、1秒間に最大5回のリクエストを許可し、同時に実行可能なリクエスト数を2つに制限します。

limiter.Wait(ctx) は、トークンが発行されるまで待機し、取得後にリクエストを実行します。これにより、リクエストの集中を防ぎながら、過負荷を軽減します。

レートリミットの実装により、Prometheusへのリクエスト数と同時実行数を制限しました。これにより、リクエストが集中する状況でもPrometheusサーバーへの過負荷を防ぎつつ、安定したデータ取得が実現できるようになりました。

まとめ

リソース可視化機能の実装においては、サーバーの負荷をアプリケーションの実装で対処する方針を取りました。特にGo言語には、レートリミットを実装するための扱いやすいライブラリとメソッドが揃っており、比較的簡単に機能を組み込むことができました。今後も運用を通して得た知見をもとに、より効率的で安定したシステムを目指して改善を続けていきます!

]]>
被写体を切り抜いて、簡単に背景を簡単に変更・生成できるアプリケーションを作ってみた話 https://developers.gmo.jp/technology/58409/ Mon, 16 Dec 2024 15:00:00 +0000 https://developers.gmo.jp/?p=58409

この記事は GMOインターネットグループ Advent Calendar 2024 17日目の記事です。

みなさん、おはようございます!こんにちは!こんばんは!
GMOペパボ株式会社の横山です。
今回のアドベントカレンダーでは、被写体を切り抜いて、簡単に背景を簡単に変更・生成できるアプリケーションを学習しつつ作ったときの記録を残したいと思います。

はじめに

こんにちは!GMOペパボ株式会社所属の横山です。社内ではあだ名で呼び合う文化があり、はるおつと呼ばれています。普段は誰でも簡単にマルチプレイ用ゲームサーバーが立てられる機能を提供する、ロリポップ for gamersでエンジニアをしています。

さて、みなさんAIしていますでしょうか。大AI時代のビッグウェーブに常に乗っていたいですね。
今回は私がそんな気持ちでふと、画像生成AIを使ってみたい、どうせなら実応用できそうなもので試してみるかという気持ちで学習しつつ作ったアプリケーションについて紹介します。

SNSやフリマアプリに画像をアップロードする際、次のような経験はありませんか?

  • 背景がごちゃごちゃしていて見栄えが悪い。
  • テーマに合った背景や撮影環境を整えるのが大変。

今回私が作ったアプリケーションはこんな悩みを解決できるかもしれません。

どんなものを作ったのか?

今回、yahoo-inc/photo-background-generationモデルを使用し被写体を切り抜いてその背景を自由に生成・変更できるようなアプリケーションを作成しました。

ユーザーが画像をアップロードした際に、その画像のメインとなる被写体を切り抜いて、

  • 背景画像が用意されている場合には、それと合成する
  • 背景画像が用意されていない場合には、プロンプトに基づいた背景を生成して合成する

仕様になっています。(カッコつけてProdDiffuserという名前を付けました。)

https://github.com/hrt-ykym/ProdDiffuser

動いているところを見てみましょう

例えば、被写体として以下のようなカメラの画像があるとします。

この被写体に対して、背景画像が用意されている(左図)場合、カメラ部分だけを切り抜いて、指定された背景と合成します(右図)。

次に、背景画像がなく、AIによって生成したい場合について、例えば「on the beach」というプロンプトを入れたとします。すると以下のような画像が生成されます。このようにAIが生成した背景画像を、被写体画像と合成することで、テーマに合った画像が簡単に作れる仕様になっています。

実装のポイント

背景生成に使用した yahoo-inc/photo-background-generation

背景生成には、Hugging Faceで提供されているyahoo-inc/photo-background-generationモデルを使用しました。このモデルは、背景生成に特化したDiffusion Modelであり、被写体と背景の境界部分が滑らかに融合するため特徴を持っています。

背景生成のみに焦点を当てたDiffusion Model

  • 通常のDiffusion Modelは、画像全体を生成対象とします。しかし、このモデルは背景部分に特化した生成を行い、被写体を破壊しないという独自の設計思想を持っています。
  • 背景領域のみに制約をかけることで既存の生成モデルでみられる、被写体と背景の区別が曖昧になり被写体と合成したときの不自然になってしまう問題を解決しています。

ControlNetによる領域制御

ControlNetは、条件画像(ここでは被写体の形状や配置を反映したマスク)を基に生成領域をピクセル単位で指定できるのが特徴です。

  • 他の生成モデルでは背景を制御する手段が限られていますが、本モデルはControlNet技術を採用し、被写体を除外した領域(背景)にのみ生成を限定します。これにより、背景部分をピクセル単位で精密に制御可能です。
  • 被写体の形状や配置に基づくマスクを入力することで、生成対象を意図的に限定できるため、より正確で自然な背景を作成できます。

テキストプロンプトとマスクのハイブリッド活用

  • 一般的な生成モデルは、テキストプロンプトだけ、または画像の構造だけを条件として利用します。しかし、本モデルテキストプロンプトとマスク画像を組み合わせることで、背景に対して物理的な制約を同時に満たす画像を生成することができます。つまり、物理的にありえない画像は生成しないということです。
  • 例えば、「on the beach」というプロンプトと被写体の形状マスクを組み合わせることで、背景全体に砂浜の風景を適用しつつ、被写体周囲の細かいエッジ処理を自然に行います。影などが生成されるのもこの影響と考えられます。

被写体の切り抜き

製品画像から被写体を切り抜くには、transparent-backgroundライブラリを使用しています。このライブラリは画像を解析して背景を透明化するシンプルなインターフェースを提供します。

from transparent_background import Remover
from PIL import Image

def process_foreground(product_image_path):
    remover = Remover()
    product_image = Image.open(product_image_path).convert("RGB")
    return remover.process(product_image).convert("RGBA")

これにより、被写体部分のみを切り抜いた画像が得られます。この結果はRGBA形式の画像として保存され、次の合成処理で透明部分を考慮した操作が可能になります。

工夫した点

今回、yahoo-inc/photo-background-generationモデルをそのまま使用するだけでなく、実用性を高めるためにいくつかの工夫を施しています。これにより、ユーザーがより簡単かつ直感的に利用できるようになっています。

背景生成と用意された背景画像の両対応

公式モデルでは背景生成が主なユースケースですが、本アプリケーションでは、すでに用意された背景画像を使用できるオプションを追加しました。

def combine_foreground_with_background_centered(
    foreground,
    background_image_path,
    target_size=(512, 512),
    scale=1.0,
    position=None
):
    background_image = Image.open(background_image_path).convert("RGBA")
    # 背景画像をリサイズしてアスペクト比を維持
    background_image = resize_with_aspect_ratio_and_padding(background_image, target_size)
    
    # 被写体画像をスケール調整
    new_width = int(foreground.width * scale)
    new_height = int(foreground.height * scale)
    foreground = foreground.resize((new_width, new_height), Image.Resampling.LANCZOS)

    # 被写体画像を中央に配置
    if position is None:
        x = (background_image.width - foreground.width) // 2
        y = (background_image.height - foreground.height) // 2
    else:
        x, y = position

    # 背景と被写体を合成
    combined_image = background_image.copy()
    combined_image.paste(foreground, (x, y), foreground)
    return combined_image

パラメータ

背景生成において、ユーザー自身でのカスタマイズ性を保つために、関数の引数をもたせました。しかし、すべてユーザーに設定させるのは非効率であるため、デフォルト値も一般的なユースケースに合わせて設定しました。

result_image = generate_background_with_prompt_and_mask_or_combine(
    product_image_path="assets/product_image.jpg",
    prompt="on the beach",  # 背景生成用プロンプト
    background_image_path=None,  # 背景画像がある場合はパスを指定
    seed=13, # シード値
    target_size=(512, 512),
    scale=0.3,  # 被写体スケール
    position=None,  # 被写体の配置
    num_inference_steps=20,  # 推論ステップ
    controlnet_conditioning_scale=1.0  # ControlNetの条件付け
)

被写体マスクの自動生成とエッジ融合の工夫

被写体の輪郭を正確に保持するため、被写体のアルファチャンネルを反転してマスクを生成することで、背景生成が不要な領域を除外し、自然な合成が可能です。

from PIL import ImageOps

def generate_mask(image):
    # アルファチャンネルを反転し、背景領域を明示
    return ImageOps.invert(image.split()[-1])  # 最後のチャンネルを反転

被写体切り抜きを調整したい場合

デフォルトの設定でも十分な被写体切り抜きが可能ですが、もしどこからどこまでが被写体なのかを判別するかをチューニングしたい場合には、以下のように背景と被写体の境界を判定するしきい値を動的に変更することで、より柔軟な抽出が可能であることを確認しています。

def tune_mask(mask_image, threshold=0.5):
    mask_array = np.array(mask_image)
    # しきい値処理
    adjusted_mask = (mask_array > threshold * 255).astype(np.uint8) * 255
    return Image.fromarray(adjusted_mask)

この処理をtransparent_backgroundのRemoverクラスの後処理としてこの関数を組み込むことで、ユーザーが簡単にしきい値を設定できるようになります。例えば、被写体が背景に溶け込みやすい場合は、しきい値を下げるなどして対応が可能となります。

GPUとCPU環境での柔軟な動作

だれもがGPU (CUDA)を積んだPCを持っているとは限らないため、モデルの高性能を活かすため、NVIDIA GPUがある場合は自動で切り替え、ない場合はCPUで動作するように設計しました。

device = "cuda" if torch.cuda.is_available() else "cpu"
pipeline = DiffusionPipeline.from_pretrained(model_id, custom_pipeline=model_id)
pipeline = pipeline.to(device)  # 使用可能なデバイスに移行

なお、もしCUDAを用いたGPUの設定に困っている場合は別途記事を書いたのでこちらを参考にしてください。

類似ソフトウェアとの違い

被写体切り抜きと背景生成を組み合わせたアプリケーションは、近年さまざまなプラットフォームやツールで提供されていますが、今回のアプリケーションは以下のような特徴を持っています。

  • ControlNet技術を活用して、背景生成領域をピクセル単位で制御するため、被写体と背景の統合精度が非常に高い。
  • 被写体と背景の合成を行った際に境界が不自然になりがちだが、自然なエッジ融合を可能にしている。
  • 背景をすでに用意している場合としていない場合の柔軟な対応が可能にしている。
  • 背景をすでに用意している場合としていない場合の柔軟な対応が可能にしている。
    GPU/CPUのいずれの環境でも動作可能で、オンプレミスやクラウド上での運用がすぐに可能にしている。

まとめ

今回の記事では、画像生成AIを組み合わせた被写体の切り抜きと背景生成・変更を簡単に行えるアプリケーションを作った時の記録を紹介しました。SNSやフリマアプリ、さらにはECサイトの商品画像など、幅広いシーンで活用されれば嬉しいなと思います。

今後やりたいこと

ここまでお読みいただきありがとうございます!最後に今後やってみたいことを書いておきます。ぜひ他にも「こんな機能があったらいいな」「こうしたらもっと使いやすくなる」などのアイデアがあれば、ぜひ教えてください!GitHubのリポジトリやSNSを通じてコメントをいただけると嬉しいです。

日本語プロンプト

今回、yahoo-inc/photo-background-generationモデルを使用した都合上、背景生成の際に入力するプロンプトは英語のみとなっています。これを日本語にも対応させることがもし実際の場面で使用する場合には必要になるかと思います。手法としては、実現方法としては以下を検討中です。

  • ユーザーが入力した日本語のプロンプトを自動で英語に翻訳し、それを生成モデルに渡す仕組み。Google TranslateやDeepL APIを活用する対応
  • 日本語プロンプトを直接解釈できる生成モデルを用いたり、本モデルのアルゴリズムを用いて日本語で再学習したりする日本語対応モデルによる対応

複数画像のバッチ処理

現在は一枚ずつ画像をアップロードして加工する形をとっていますが、複数の画像を一括で処理できる機能を実装すれば、商品画像やSNS投稿用画像の一括加工といった場面での活用が期待されます。

]]>
短期間での新規プロダクト開発における「コスパの良い」Goのテスト戦略 https://developers.gmo.jp/technology/57747/ Sat, 14 Dec 2024 15:00:00 +0000 https://developers.gmo.jp/?p=57747

この記事は GMOインターネットグループ Advent Calendar 2024 15日目の記事です。

はじめに

GMOペパボでは、マルチプレイ用ゲームサーバーを簡単にセットアップできる「ロリポップ! for Gamers」を展開しています。今年の2月に生まれたこのプロダクトですが、なんと開発開始から13営業日でユーザーへの提供を開始するというスピード感で誕生した歴史を持ちます。開発当初から関わっていたエンジニアの目線から、この短期間での初期リリースを支えたバックエンドの「テスト戦略」について、うまく行ったことや、今後の課題についてお話ししていきたいと思います。

ロリポップ! for Gamersとは?

テスト戦略についてお話しする前に、テスト対象である弊サービスについてお話しさせてください。

ロリポップ! for Gamersとは、2024年2月29日に無料モニターへの提供を開始し、4月15日に正式リリースされた、「サーバーの専門知識がなくても簡単にゲームのマルチプレイ専用サーバーが立てられるサービス」です。マインクラフトをはじめ、パルワールド、FiveM(GTA V)、ARKなどといった人気ゲームのサーバーを簡単に立てて遊ぶことができます。現在は有料提供をしており、マインクラフトのサーバーは月額800円(2GBプラン)から立ち上げることができます。

開発開始

ロリポップ! for Gamersは2月9日にプロジェクトが発足され、さっそく開発が始まりました。バックエンドの開発言語はGo、またAPIを作成するためのライブラリとしてConnectを選定しました。Connectを選定したのはprotobufによるスキーマ駆動開発を行いたかったことと、protovalidateをはじめとしたprotobufのエコシステムを使って、容易に制約をかけつつ安全な開発を進めることができるという理由からです。より詳しい技術選定に関しては「ロリポップ! for Gamersの立ち上げ/lolipop for gamers launch」もご覧ください。

テスト戦略の決定

技術選定後、ユーザーへの価値提供を担保するためにバックエンドAPIのテスト戦略を決定する必要がありました。

https://speakerdeck.com/takumakume/lolipop-for-gamers-infrastructure?slide=9

そこで、我々は以下の方針に決定しました。

定義したシナリオ(「ログインできる」、「サーバーを立ち上げる」など)に沿って、実際にちゃんと動くのかを主軸にテストする。

テスト戦略決定の理由

こうした方針に決定した理由を説明します。

今回は初期リリースのため、内部の構造や実装がしっかりと定まっておらず、小さいテストは書いてもすぐ使えなくなる可能性がありました。そのため、外側から実際にちゃんと動くのかを中心にテストすることで、内部の変更に強くしたいというニーズがありました。

また、「実際にちゃんと動くのか」をしっかり担保することによって、安心してユーザーに価値提供できる状態に持っていきたいという思いもありました。そのため、ここを担保するためにさまざまなテスト手法を使い、それに伴うトレードオフも受け入れるという判断をしています。

とはいえ、トレードオフは小さい方が良いです。そのため、どうすればより効果的にこの方針に従ってテストを書いていけるのか、「コスパの良い」方法を考え、実行していくことにしました。

コスパの良い方法を考える

「実際にちゃんと動くのか」をテストしようとした場合、テストスコープとしては、ユニットテストより統合テスト、統合テストよりE2Eテストが良いはずです。比較した時に、実際の環境への忠実性が高いからです。

E2Eテストで動作を担保できる範囲

しかし、そのまま書くのではなく、まずはコスパの良い書き方の方針を考えることにしました。コスパを考える上でわかりやすい考え方として、テストサイズの考え方があります。

テストサイズとは

GoogleのTestingブログで説明されている、テストの種類の定義の曖昧さをなくすことで、認識のブレを無くそうという考え方の一つです。ユニットテスト、インテグレーションテスト、E2Eテストといったテストスコープの解釈のブレを無くすために、分類の軸を設けて複数のサイズに分けるのが基本的な考え方になります。

https://testing.googleblog.com/2010/12/test-sizes.html

上記の図の通り、Googleの開発チームでは、ミディアムテストは、「1つのマシンの中に閉じている」テストと定義されています。つまり、外部に疎通せず、そのマシンで完結するテストのことを指します。逆に言えば、テストの中で外部のテストサーバーに接続をしに行ったりしたら、それはラージテストという別の分類になります。

ラージテストになると、さらに実際に近い環境になるため忠実性が上がりますが、保守にかかるコストがかかったり、実行時間の増加などがトレードオフとなります。

スモールテスト、ミディアムテスト、ラージテストの3分類はあくまでGoogleの開発チームで使っているものではありますが、分かりやすいためこの記事ではこのテストサイズの定義を使ってコスパについて説明していきたいと思います。

テストサイズとテストスコープからコスパを考える

https://levtech.jp/media/article/column/detail_496/

t-wadaこと和田卓人さんの図が「コスパの良い」テストについて説明するのに分かりやすかったので引用させていただきます。

外部サービスへの疎通をするテストは、テストサイズで言うと「ラージテスト」になります。外部サービスへの疎通を含むため、先述した通り各コストが増大するのがトレードオフとなります。

「実際にちゃんと動くのか」だけを考えてテストをそのまま書こうとすると、図の一番右であるラージテストになってしまいます。しかし、ミディアムテスト、つまり図の左の方に落とすことでコスパ良く実現できないかを考えました。

そこでスタブサーバーです。

外部サービスのスタブサーバーをhttpstubgrpcstubといったGoのパッケージを使って作成し、外部サービスへの疎通をテストから排除できるようにしました。このスタブサーバーについては、テスト対象が「実際にちゃんと動くのか」を本番と同様に、リクエストからレスポンスまで通して確認できるように作成しています。これにより、テストが単一マシンの中で実行できるようになるので、テストサイズが「ミディアムテスト」に落ちます。また、これらのスタブサーバーはgoroutineで実現できるので、同一のGo Runtime上で動かすことができ、軽量で安定した運用が可能です。このスタブサーバーにより、安定性があり、速度も速く、「コスパ良く」テストを実行できるようになりました。

goroutineベースのミディアムテストを使うことによるメリットに関しては、 弊社エンジニアの k1LoWnet/http/httptest.Server のアプローチをテスト戦略に活用する / Go Conference 2023 にも詳しく書かれているのでぜひご覧ください。

一方で、初期リリース後、「実際にちゃんと動くのか」を担保するために、ラージテストを書くことを許容したケースもありました。例えば決済を伴うテストです。決済サービスがテストサーバーを用意してくれているため、それを用いることで簡単にテストを書くことができることが分かっていました。そのため、スタブサーバーの実装コストも含め考えた結果、ラージテストを選択することによるコスト増を受け入れ、決済サービスのテストサーバーを使ってテストを書く決断をしました。

runnでコスパ良くテストを書いていく

テストサイズをコスパ良く書けるような方法を考えたところで、同時にどのように人間がテストを書いていくとコスパが良いかを考えました。そこで、 runn というツールで実行できるシナリオテストを書いていく方針を取りました。

runn は k1LoW が開発した、YAMLに記述されたシナリオを実行するためのツールです。主にシナリオベースのテストや、特定のワークフローを自動化するためのツールとして使われています。複数のRPC、DBを、外からシナリオに従って実行できるため、「実際ちゃんと動くのか?」をテストするのに有効なツールの一つであると言えます。また、Goのテストヘルパーパッケージも提供されているため、これをうまく使うことでGoのテストに組み込んで実行することができます。

また、もう一つのrunnの優れた点はそのテストの書き心地です。runnではテストシナリオをYAML(runnにおいてはランブックと呼称)として記述します。弊社のエンジニアは別のプロジェクトでGitHub ActionsやKubernetesを使っており、YAMLを書くことに慣れていたので、一つのサンプルさえあれば簡単にテストを書き始めることができました。

そして副次的なメリットですが、テストが分かりやすいドキュメントとして機能するところも強みです。下記は実際に開発で使っている「サーバー情報更新」のシナリオのファイルですが、どのような流れでサーバー情報更新ができるようになるかが分かりやすいと思います。

ロリポップ! for Gamersのサーバー情報更新のシナリオテスト(一部折りたたみ)

こうしたメリットから、シナリオテストをランブックに書いていくことにより、テストの実装コストを下げ、より「コスパ良く」テストを書けるようにしました。

以上の方針に従うことで、

  • 「実際にちゃんと動くのか」のテストの一部をミディアムテストに落とせる
  • テストを書く際の学習コストが低い

この2つが満たせる、初期リリースにおいて「コスパの良い」戦略を取ることができました。

うまくいったこと

実際に上記のテスト戦略を使っていきましたが、初期開発時の各フェーズにおいてこの戦略はうまく機能していました。想定していた通りテストの記述はすぐに慣れますし、include: などのrunnの機能を活用することでうまく記述の工数削減をすることができました。

レビュー側からしても、 ランブックに desc: という項目があるおかげで各ステップで何をやっているかが分かりやすく、テスト対象が明確だったので、レビューのリードタイムも短かったと思います。

そして、フロントエンドを含めた手動での検証作業がスムーズでした。バックエンドの動作は正常系はほぼシナリオテストで担保できているので、フロントエンドや繋ぎ込みの部分にバグがなければ特に問題なく検証を終えることができました。

このようにテスト戦略がうまく行ったことが、13営業日でリリースを完了できた一要因であると思っています。

また、初期リリース後に恩恵を受けたこともあります。

1つ目は、リリース後のバグ発生率が体感的に低かったことです。機能数が少なかったとはいえ、これだけの短期間でリリースしたものが大きな問題なく動いたことで、運用負荷が減り次の開発へすぐに着手することができました。インシデントもリリース後1ヶ月間は0件でした。

2つ目は、書いたテストがほぼ構造の変更なしで現在まで動いていることです。内部の変更に強い、外からのテストを主軸に採用したおかげで、変更に強くメンテナンスコストを下げることができたと考えています。

生まれた課題

初期開発においてこの方針はうまく機能し、ユーザーにいち早く機能を提供する一助になりました。しかし、一部は予想していたことではありますが、以下のような課題も抱えています。

  • テスト実行時間の増加
  • Flaky testの増加
  • ラージテストの増加

初期リリースでテスト戦略を決定した後、その後はテストに関してあまり意思決定がなく、そのまま同じテスト戦略を暗黙的に使ってしまっていました。そのため、本来であればスモールテストでカバーできるテスト内容であっても、書き慣れた方法でテストを書いてしまい、結果としてテストサイズがミディアムになる、「コスパの悪い」テストを書いてしまうことが続いていました。

その結果、テスト実行時間の増加が著しく、初期リリース時は短かったもの、10分〜20分ほどかかるようになってしまい、開発効率や体験が落ちることになりました。

また、ミディアムテストは実際に同一マシン内の複数サービスをみてテストを行うため、適切に並行処理などが行われないことで、Flaky testが増える原因になりました。runnには concurrency: というオプションがあり、これを適切に付与することで、同じデータリソースを操作・参照するランブックを同時に実行しないようにするなどしてFlaky testを防ぐことができるので、これを活用するべく人間やシステムがオプションを適切に付与するための仕組み作りが必要となりました。

そして、初期リリースで選択したラージテストの許容が、現在は負荷になっている部分があります。特に、決済システムのテストサーバーを含めたラージテストを使うテストが増大してしまっています。最初こそ数が少なく安定して動いていましたが、テストが増えるにつれテストサーバーのRate limitに触れる機会も増え、こちらもFlaky testの一因となってしまっています。

次のテスト戦略へ

リリース初期のテスト戦略は、初期リリースの短期間の実現に大きな貢献をしました。しかし、プロダクトの規模が大きくなることに合わせて、テスト戦略も調節する必要があると感じています。

ここで、テストピラミッドという考え方があります。

https://levtech.jp/media/article/column/detail_496/

こちらはGoogleの開発チームのテストサイズの定義に基づき、t-wadaさんが表現した望ましいとされているテストの比率の図です。これを我々のチームに当てはめて考えると、

  • ラージテストが増えている
  • スモールテストで書けるテストをミディアムテストで書いている部分がある

ということで、明らかに三角形になっていないと思います。

当時は実装が固まっていないこともあり、逆三角形型も許容する判断をしていたのですが、初期リリースが終わったため、今後は下記のような方針をとりたいと考えています

  • スタブサーバーを実装コストを投じて実装し、ラージテストを削減する
  • 既存のミディアムテストを整理し、スモールテストでカバーできる範囲は移行していく
  • 新規のテストに関しては、どのサイズでテストを書くか適切に判断できる仕組み・文化づくりをする

これにより適切なテスト比率に近づけ、理想的なテストピラミッドを目指すことで、現状発生している課題の解決、そしてよりコスパ良くユーザーへの価値を担保できるテストを書いていけるようにしていきたいです。

また、自らがテスト戦略にオーナーシップを持つエンジニアになることで、定期的にテスト戦略の見直しと、テストの改善を行うようにしていきたいと考えています。

まとめ

今回は短期間での新規プロダクト開発において選択した、「実際にちゃんと動くのか」を主軸としたテスト戦略と、その結果と今後の課題についてお話しました。

新規プロダクトの着手から1年ほど経った今改めて感じるのは、テスト戦略は定期的にブラッシュアップが必要だということです。そのときに選択したテスト戦略は、仮にその先の開発を見通したものであったとしても、事業や開発のフェーズにおいて、テストのトレードオフとなる値は常に更新されていきます。定期的にテスト戦略を見直すことで、より強固で効率の良いプロダクト開発が行うことができ、結果としてユーザーに大きな価値を提供できるようになるのではないでしょうか。

この記事がテスト戦略を考えている開発者の一助となれば幸いです。

]]>
iOS/Androidで同じUI体験をネイティブで作成する際に気をつけたい落とし穴 https://developers.gmo.jp/technology/57444/ Tue, 10 Dec 2024 15:00:00 +0000 https://developers.gmo.jp/?p=57444

この記事は GMOインターネットグループ Advent Calendar 2024 12日目の記事です。
こんにちは、GMOペパボでシニアエンジニアを務めている酒井です。
私は現在、minneの開発を担当しているモバイルアプリエンジニアとして活動しています。
今回は、11月14日に開催された「Spectrum Tokyo Meetup #15」での登壇内容を振り返り、改めてまとめました。この時のテーマは「デザインエンジニアリングにまつわるお話」でした。
そこで使用した登壇資料にいくつかの補足を加えて、皆様にポイントをご紹介していきます。

登壇のきっかけ

私が今回の登壇を決意したきっかけは、これまでの経験にありました。過去にデザイナーとしての経験があり、その後はモバイルエンジニアとしてUI実装に携わってきました。このため、業務内外で得られた知見や事例を共有することで、少しでも皆様のお役に立てればと考えたのです。

iOSとAndroidにおいて、同じUI体験をネイティブで実現しようとする場面は、日常の業務や個人開発で頻繁に直面する課題です。例えば、実装の難易度を見誤ったり、無意味に複雑な実装をしてしまったりして、標準の動きと比較して違和感が生じることがあります。

本発表では、この様な課題にどのように向き合い、上手に付き合いながらプロジェクトを進めていくかという考え方とアプローチをお伝えしたいと思いました。

登壇時に利用した全スライドはこちら

意外とこの様なケースってありませんか?

iOSとAndroidのネイティブアプリ開発において、振る舞いが似ているように見えても、実際には異なる方向性が必要になることがあります。例えば、1つのOSではスムーズに進む一方で別のOSでは苦戦するケースや、純正部品が提供されていない場合があります。同様に、他のアプリで見られるような表現でも難解で複雑な構造が問題となることもあります。こうした状況では、異なるプラットフォーム間での注意深い対応が求められます。

アプリ開発でのiOSとAndroid間の違いを減らすには、いくつかの着眼点が重要です。まず、両プラットフォームで提供されている純正部品への理解を深めること。また、新旧を問わず、実現方法やOSSの活用法についても理解が求められます。さらに、デザインデータを実際に構築する際のイメージを具体化することも有用です。こうした視点を持つことで、予期しない問題に対処しやすくなります。

ネイティブアプリ開発において、iOSとAndroidそれぞれのガイドラインに立ち返ることが重要です。特に、機能を考える際に対応するUIを設計するためには、各OSの純正部品もこのガイドラインに沿って提供されるので、Human Interface GuidelineやMaterial Designの確認が不可欠です。やむを得ずカスタマイズが必要な場合でも、ガイドラインから逸脱しないよう注意し、保守性の高い実装を心がけることが大切です。

実際に出会したややこしいケースをご紹介

私は2023年と2024年に、DroidKaigi公式アプリでiOS側のコントリビューションを行いました。

特に私が担当したUI実装に関連する部分では、AndroidとiOSでそれぞれのプラットフォームに応じた異なるアプローチが必要でした。本稿では2023年に取り組んだ事例を紹介します。

Android版のUI構築では、純正Componentを活用する方針を採用していました。UI要素はJetpack Composeの基本Componentを組み合わせることで実現し、スクロールの変化量をScrollStateで管理していました。また、NestedScrollの特性を活用して、Tab要素とContents要素におけるスクロール動作を実現していました。

一方iOS版では、SwiftUIを使用したUI構築においてカスタマイズが必要でした。難しかったのは、ScrollViewの拡張でOffset値を取得し、LazyVStackを基にしてTab要素の動きを再現する工夫が必要な点でした。取得したスクロール変化量を利用して、SectionHeaderに配置したTab要素の状態を変化させる処理がポイントになりますが、この表現はAndroid版の振る舞いを参考にし、できる限り近い形となる様に自作しました。

スライド内で紹介したiOS版の再現実装コード

複雑なGrid表現の例として、iOSとAndroidのアプローチを比較します。

iOSでは、UICollectionViewを使用し、高さやアスペクト比を元に工夫が必要です。一方、AndroidではStaggeredGridLayoutManagerを用いることで、よりシンプルに実現できます。

これにより、プラットフォーム間での考え方や実装方法の相違が明確になります。

Swipe切替表現の実装に関して、iOSとAndroidではアプローチが異なります。

iOSでは、UIKitを利用する事で表示要素や位置変化を活用し、工夫次第で多様な表現が可能です。一方、Androidでは、ViewPagerとTabLayoutの組み合わせが基本で、細かなデザインのカスタマイズは難しい場合があります。

カスタマイズの余地を考慮し、どちらのプラットフォームにも適した実装を選ぶことが重要です。

iOSに存在しないがAndroidにはある表現(またはその逆)を実装する際は、慎重にアプローチを検討する必要があります。特に、汎用的な形で多用途に活用される場合には、特に注意が必要です。

用意されていない表現を自作し、汎用的で活用しやすい形に整えるには、かなりの工数がかかります。そのため、こうした場合にはOSSの活用も視野に入れると良いでしょう。

端末固有機能である動画再生やプッシュ通知の実装には、プラットフォームごとに大きな違いがあります。同じ機能でもiOSとAndroidでは振る舞いが異なるため、実装方針には注意が必要です。また、OS準拠機能の違いを理解し、必要な知識を把握することも重要です。

スライドで紹介しているのは、アプリがバックグラウンド状態になる際も音声が途切れることなく再生できる機能の例です。一見、同様の機能に見えても、実装方針は全く異なります。それぞれのOSに応じた対応が求められる点に注意が必要です。

落とし穴をうまく回避するために

デザインから逆算して実装の難易度を正確に見積もることが、落とし穴を回避する鍵になると考えています。

デザインからは、実装イメージやハマりそうな点、想定工数など、実装に役立つ様々な情報を読み取ることができます。その情報を見逃さず、曖昧な見積もりを避けるために、見えない情報を具体化することが重要だと思います。

モバイルアプリ開発における難易度の見積もりで重要な観点として、UIコンポーネントの複雑さやプラットフォーム間の差異に加え、データフロー・パフォーマンス・API連携・セキュリティ・アクセシビリティ・ローカライズ・テスト・メンテナンス性等の項目挙げられると思います。

これらの要素を考慮してデザインから逆算し、実装難易度を正確に見積もることが、長期的なプロジェクトの成功と円滑な進行につながると考えています。

iOSとAndroidのUI実装では、それぞれのプラットフォーム間で考え方やコンポーネントにおける相違点を理解することが重要だと考えています。

アニメーションやトランジション・カスタムUIコンポーネントの実装・システムUIとの統合・アプリのライフサイクルなどが異なるため、アジャイル開発の観点からもこれらの違いを把握して計画することが求められます。これにより、スプリント計画の精度が向上し、リソースを最適に配分することが可能になります。

正確な見積もりを行うと同時に、「実現しやすい手段や方法」を模索すると良いでしょう。スライドで紹介しているのは、従来の手法では難しかったUI実装を、宣言的UIを活用してシンプルに実現する例です。

SwiftUIやJetpack Composeを用いてSection構造を工夫することで、視覚的に分かりやすいデザインが可能になります。選択する方針次第では、View構造が簡素化され、実装が一層容易になることもあります。

1つのUI実装に対して様々なアプローチを知っておくことで、選択肢を広げることができ、見積もりや実装に対する引き出しが増えるため、とても有益だと考えています。

実装イメージを整理して言葉に直してみる

私が個人的に実践していることの一つに、気になったUI実装や表現に関する事例を紐解き、ポイントになり得る点をノートにまとめる作業があります。

情報源としては海外ブログ記事やYouTubeの解説動画など多岐にわたります。動作するサンプルコードにも触れ、自分の言葉でまとめるとともに、図解を用いてイメージを整理し、応用事例を深く理解しながら、今後の実装に活かすことを心がけています。

紹介しているノートはこちらで公開しています

まとめ

本記事を通じて、一見すると簡単そうに見えるものでも、実は複雑であることを認識する重要性をお伝えしました。

iOSとAndroidの共通点と相違点を理解し、困難な事例に備えることで、自分なりの解決策を持つことが重要だと考えています。そして、デザインを基に難易度を逆算し、実装イメージを固めるアプローチを取るためには、実装経験から得た気づきや、目に見えない情報を大切にする姿勢が重要になると思います。

参考資料

円滑なUI&機能実装やデザイナーとの共同作業を進めるために心がけてきた事

iOSエンジニアがAndroid・Kotlinでの開発を加速させた3年間の実践テクニック

Androidアプリでバックグラウンド再生機能を実現するためのヒントとiOSアプリとの見比べた際の特徴を簡単にまとめてみた

Footprints about Contribution of DroidKaigi 2023

あとがき

今回の5分間のスライドは、予想以上に盛りだくさんとなり、当日はかなり早口で話すことになりました。しかし、参加者の方々から共感やお褒めの言葉をいただき、大変嬉しく思いました。

私は、モバイルアプリのUI実装に関連するトピックや事例に触れることが好きなので、今後もこのような活動を続けていきたいと考えています。

]]>
マイグレーション実行時のロック競合によるDBインシデントから学んだこと https://developers.gmo.jp/technology/56790/ Fri, 06 Dec 2024 00:01:42 +0000 https://developers.gmo.jp/?p=56790

この記事は GMOインターネットグループ Advent Calendar 2024 6日目の記事です。
こんにちは。GMOペパボ株式会社のyumuです。
先日、当社のサービスであるminneが約15分間にわたって利用できなくなるインシデントが発生しました。きっかけは一見単純なデータベースマイグレーションの実行でしたが、偶然同じタイミングで動いていた重いクエリとの競合により、MySQLへの新規接続が次々とタイムアウトしてしまいました。
本記事では、このインシデントがどのように発生し、それにMySQLのロック機構がどう影響していたのか、そして同じ問題を繰り返さないためにどのような対策を行ったのかについて解説していきます。
なお、本記事で説明する内容は、minneで使用しているMySQL 8.0を前提としています。

インシデント発生から原因特定までの道のり

ある日の午後、Slackに突如として大量のエラー通知が流れ始めました。

ActiveRecord::AdapterTimeout
Mysql2::Error::TimeoutError: Timeout waiting for a response from the last query. 

MySQLへの接続がタイムアウトしているようです。このエラー自体は時々見かけるものですが、今回は明らかに様子が違います。通知の量が尋常ではなく、minneへのアクセスが全くできなくなってしまっていました。

原因を探るため、まずは直近の変更内容を確認します。すると、ちょうどその時間にデータベースマイグレーションを実行していたことが分かりました。しかし、マイグレーションの内容自体はそれほど複雑なものには見えません。

Mackerel(サーバーやアプリケーションの監視サービス)の監視画面を確認すると、MySQLのDisk IOPSのreadと接続数が急上昇していました。しかし、その後、監視データ自体が取得できなくなってしまっています。監視サービスもMySQLに接続できなくなってしまったようです。

手がかりを求めて、MySQLのスロークエリログを確認することにしました。そこで興味深い発見がありました。

# Query_time: 1383.272262 Lock_time: 0.000002 Rows_sent: 2 Rows_examined: xxx
SET timestamp=1709704141;
SELECT `users`.* FROM `users` WHERE `users`.`deleted_at` IS NULL AND `users`.`name` LIKE '%HOGE%' ORDER BY `users`.`id` desc LIMIT 30 OFFSET 0 /*controller:users,action:index*/;

なんと、あるクエリが23分以上も実行され、数千万行のレコードを走査していたのです。このクエリの完了直後にエラーが収まっていたことから、インシデントの原因としてこのクエリを疑い始めました。

調査を進めるうちに、私は「メタデータロック」という仕組みにたどり着きました。これは、テーブル構造を変更する際に使用されるロックの仕組みです。外部キー制約を追加する際は、親テーブルに対してもこのロックが必要になります。今回のマイグレーションでは、まさにこの外部キー制約の追加が含まれていたのです。

これらのことから、問題の全容が見えてきました。重いクエリの実行中にマイグレーションが走り、ロックの競合が発生。その結果、後続のクエリが待機状態となり、MySQLの接続を消費し続けた結果、ついには新規の接続が全て失敗する状態に陥ったのです。 では、具体的にどのような仕組みでこの問題が起きたのでしょうか?次章では、メタデータロックの仕組みについて詳しく見ていきます。

メタデータロックの仕組み

メタデータロックとは

データベースでは、テーブルの構造を変更する際に整合性を保つ必要があります。例えば、あるテーブルに対してSELECTを実行している最中に、別のセッションがそのテーブルの構造を変更してしまうと問題が発生する可能性があります。 メタデータロック(MDL)は、このようなテーブル構造の整合性を守るための仕組みです。MySQLは、テーブルへの操作に応じて自動的にこのロックを取得します。

2種類のロック

MDLには主に2種類あります。

  • 共有ロック:通常のSELECTやUPDATE(DataManipulationLanguage(DML))時に使用される。複数の処理が同時に共有ロックを取得できる
  • 排他ロック:テーブル構造の変更時(DataDefinitionLanguage(DDL))に使用される。他のすべてのロックと競合する

外部キー制約とMDL

DDLが外部キー制約を追加する際には、制約を追加するテーブルだけでなく、参照先のテーブルに対してもMDLが必要になります。

例えば以下のような外部キー制約を追加する場合、

  • hogesテーブルの排他ロック
  • usersテーブル(参照先)の排他ロック

の両方が必要になります。

ALTER TABLE hoges 
ADD FOREIGN KEY (user_id) REFERENCES users(id);

実際に、ローカル環境でこの動きを確認してみました。

-- ターミナル1: トランザクションを開始し、usersテーブルへの共有ロックを取得
mysql1> BEGIN;
mysql1> SELECT * FROM users;

mysql1> SELECT * FROM performance_schema.metadata_locks\G
*************************** 1. row ***************************
          OBJECT_TYPE: TABLE
        OBJECT_SCHEMA: development
          OBJECT_NAME: users
            LOCK_TYPE: SHARED_READ
        LOCK_DURATION: TRANSACTION
          LOCK_STATUS: GRANTED

-- ターミナル2: 外部キー制約の追加を試みる
mysql2> ALTER TABLE hoges ADD FOREIGN KEY (user_id) REFERENCES users(id);

-- ターミナル1: processlistで確認すると、ALTER TABLEが待機状態であることが分かる
mysql1> SHOW PROCESSLIST;
(省略)
| 144 | root            | localhost         | development | Query   |   34 | Waiting for table metadata lock | ALTER TABLE hoges ADD CONSTRAINT ...

-- ターミナル3: usersテーブルに対するクエリを試みる
mysql3> SELECT * FROM users;

-- ターミナル1: processlistで確認すると、DMLも待機状態であることが分かる
mysql1> SELECT * FROM performance_schema.metadata_locks\G
(省略)
| 107 | root            | localhost         | development | Query   |    4 | Waiting for table metadata lock | SELECT * FROM users  
| 144 | root            | localhost         | development | Query   |   34 | Waiting for table metadata lock | ALTER TABLE hoges ADD CONSTRAINT ...

このように、既存のトランザクションが保持している共有MDLにより、外部キー制約を追加するALTER TABLE文が待機状態になり、さらにそれ以降のusersテーブルに対するDMLも待機状態になることが確認できました。

次の章では、これらの知識を踏まえた上で、今回のインシデントで実際に何が起きていたのかを詳しく説明します。

インシデントの詳細分析

実行されていた処理の詳細

まず、関連するテーブルの構造を見てみます。

usersテーブル構造

column_namedata_typeis_nullabledescription
idintNO主キー
emailvarchar(255)NOメールアドレス
namevarchar(255)YESユーザー名
deleted_atdatetimeYES論理削除用のタイムスタンプ
その他カラムは省略

インデックス

index_nameis_uniquecolumn_name
PRIMARYTRUEid
index_users_on_emailTRUEemail
index_users_on_nameTRUEname

このテーブルに対して、社内向けの管理画面から以下のような検索クエリが発行されました。

SELECT * FROM users
WHERE deleted_at IS NULL
AND name LIKE '%HOGE%'
ORDER BY id desc
LIMIT 30 OFFSET 0;

一見シンプルに見えるこのクエリですが、実際には数千万行あるusersテーブルをフルスキャンする重い処理でした。これには大きく分けて2つの理由があります。

1つ目は、nameカラムに対する検索方法です。nameカラムにはインデックスが設定されているものの、LIKE '%キーワード%'という中間一致での検索では、B-Treeインデックスの構造上インデックスを効果的に使用することができません。前方一致(LIKE 'キーワード%')であればインデックスが活用できますが、中間一致や後方一致(LIKE '%キーワード')の場合は、結局すべての行を走査する必要があります。

2つ目は、論理削除です。usersテーブルは論理削除を採用しているため、時間とともにレコード数が増え続けていました。その結果、インデックスが効果的に機能しない中間一致のLIKE検索が、膨大な数のレコードに対して実行されることになったのです。

一方、この重いクエリが実行されている最中に、以下のマイグレーションが実行されました。

class AddUserIdToHoges < ActiveRecord::Migration[7.0]
  def change
    add_column :hoges, :user_id, :integer, null: false
    add_foreign_key :hoges, :users, column: :user_id, name: 'index_hoges_on_user_id'
  end
end

まだ使用されていないhogesテーブルに対するマイグレーションですが、user_idに対する外部キー制約を追加したことにより、usersテーブルに対する排他MDLが必要になりました。

事態の推移

時系列で見ると、事態は以下のように進行しました。

  • 管理画面でのユーザー検索により、重い検索クエリがusersテーブルの共有MDLを取得
  • その途中でマイグレーションが実行され、usersテーブルの排他MDLを要求
  • 既存の共有MDLが解放されるまで待機状態に
  • この間、新規のクエリも全て待機状態となる
  • MySQLの接続が次々と消費され、ついには上限に到達
  • 監視サービスを含む全ての新規接続がタイムアウト

このように、単純な検索とマイグレーションの組み合わせが、思わぬ形でサービス全体に影響を及ぼすことになりました。

再発防止に向けて

このインシデントの経験から、私たちは複数のレイヤーでの対策を進めました。

マイグレーション実行前の安全確認の徹底

まず取り組んだのは、マイグレーション実行前の確認プロセスの整備です。次のSQLを使用することで、現在のロック待ちの状態を確認できます。

SELECT * FROM performance_schema.metadata_locks;

特に外部キー制約を追加する場合は、関連するテーブルの状態確認が重要です。このクエリを実行することで、実行中の重いクエリを事前に検知し、マイグレーションのタイミングを適切に判断できます。

管理画面の改善

今回の重いクエリは管理画面から実行されたものでした。調査の結果、管理画面では「%」を使用したLIKE検索が多用されており、これが非効率なテーブルスキャンを引き起こしていることがわかりました。

そこで以下の対策を実施しました。

  • 管理画面での曖昧検索の制限
  • 検索条件の最適化(インデックスが効くように)

監視体制の強化

また、スロークエリの監視を強化しました。AWS CloudWatchとLambdaを使い、実行時間が一定を超えるクエリを検知したらSlack通知するようにしました。これにより、潜在的なインシデントの原因に気付きやすくなり、スロークエリの改善が積極的に行われるようになりました。

データ構造の見直し

より根本的な課題として、usersテーブルの構造の見直しも検討しています。先述の通り、usersテーブルは論理削除を使用しているため、時間とともにテーブルサイズが肥大化していく一方です。長期的なプロジェクトとして、物理削除への移行を検討しています。

おわりに

一見単純なマイグレーション作業が思わぬ形でサービス停止につながった今回のインシデントですが、MySQLのロック機構について、改めて理解を深めると同時に、システム設計の基本的な部分まで見直すきっかけとなりました。

今回得られた教訓を活かし、より安定したサービス運用を目指していきます。

]]>
WordPressのフックを理解し「アップデートしたらサイトが壊れる」プラグインを作る https://developers.gmo.jp/technology/55306/ Tue, 03 Dec 2024 15:00:00 +0000 https://developers.gmo.jp/?p=55306

こんにちは。GMOペパボ株式会社の西田です。

WordPressは、非常に人気のあるコンテンツ管理システムの1つで、世界中のウェブサイトで利用されています。この記事では「アップデートしたらサイトが壊れる」という一風変わったプラグインを紹介しつつ、それを実現するWordPressの仕組みについて解説したいと思います。
この記事は GMOインターネットグループ Advent Calendar 2024 4日目の記事です。

アップデートしたらサイトが壊れるプラグイン

今回実装したプラグインはこちらです。
https://github.com/kinosuke01/wp-update-breaker

これをWordPress製のサイトにインストールすると、コントロールパネルにアップデート通知が出ます。そこで「更新」をクリックしてプラグインをアップデートすると。

以下のように、サイトの表示が著しく崩れます。

なぜ作ったのか?

ある社内業務にて「WordPressのプラグインをアップデートしたらサイトが壊れた」を再現する必要があったためです。

WordPressのプラグインアップデートはワンクリックで済むことが多いですが、サイトが壊れたときの復旧が大変という側面があります。なので、WordPressを運用管理している企業の社員研修で、サイトが壊れたときの訓練用途としても使えるかもしれません。

どのように設計したのか?

一般的にWordPressプラグインのアップデートは以下のような流れで実現されます。

このアップデートを仕組みに載るには、wordpress.orgのプラグイン登録審査が必要になります。しかし、社内業務のためだけに、WordPress公式からの承認を得る必要性については検討する必要がありました。

そこで今回は「このプラグインに関してはアップデートのフローが変わるように、プラグイン自体で拡張する」というアプローチを取りました。プラグインのホスト先にはGithubPagesを用いることで、コストもかからないようにしました。

あとは、新しいバージョンのプラグインにのみ、レイアウトを崩すCSSを仕込んでおけば、「アップデートしたらサイトが壊れる」プラグインが完成します。

どのように実装したのか?

ここから実装したコードを見ていきますが、その前にWordPressのフックについて説明をしたいと思います。WordPressはブラウザからのアクセスを契機に複数の処理が実行され、htmlとしてレスポンスを返します。この複数の処理には、それぞれ「フック」が設けられており、プラグインは任意のフックに対して関数を追加することで、WordPressの挙動を拡張することが可能になります。

プラグインアップデートに関する実装は、以下のようになりました。

<?php
if ( ! function_exists( 'get_plugin_data' ) ) {
    require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
}

class WP_Update_Breaker_Updater {
    private $plugin_data;
    private $api_url;
    private $plugin_slug;
    private $version;

    public function __construct() {
        $plugin_file = WP_PLUGIN_DIR . '/wp-update-breaker/wp-update-breaker.php';
        $plugin_data = get_plugin_data($plugin_file);

        $this->plugin_slug = strtolower(str_replace(' ', '-', $plugin_data['Name']));
        $this->version = $plugin_data['Version'];
        $this->api_url = $plugin_data['UpdateURI'];

        add_filter('site_transient_update_plugins', [$this, 'check_for_plugin_update']);
        add_filter('plugins_api', [$this, 'plugin_info'], 10, 3);
    }

    public function check_for_plugin_update($transient) {
        if (empty($transient->checked)) {
            return $transient;
        }

        // Check cache
        $cache_key = 'wp_update_breaker_api_response';
        $api_response = get_site_transient($cache_key);

        if ($api_response === false) {
            // Only request API if cache does not exist
            $response = wp_remote_get($this->api_url);
            if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
                $api_response = json_decode(wp_remote_retrieve_body($response), true);
                // Save cache for 24 hours
                set_site_transient($cache_key, $api_response, 24 * HOUR_IN_SECONDS);
            }
        }

        // Check update information if API response is valid
        if ($api_response) {
            if (version_compare($this->version, $api_response['version'], '<')) {
                $plugin_data = [
                    'slug'        => $this->plugin_slug,
                    'new_version' => $api_response['version'],
                    'package'     => $api_response['download_link'],
                ];

                $transient->response[$this->plugin_slug . '/' . $this->plugin_slug . '.php'] = (object) $plugin_data;
            }
        }

        return $transient;
    }

    // Provide detailed information about the plugin
    public function plugin_info($false, $action, $args) {
        if ($action !== 'plugin_information' || $args->slug !== $this->plugin_slug) {
            return false;
        }

        // Retrieve detailed plugin information from the API
        $response = wp_remote_get($this->api_url);
        if (is_wp_error($response)) {
            return false;
        }

        $api_response = json_decode(wp_remote_retrieve_body($response), true);
        if (!$api_response) {
            return false;
        }

        // Return detailed plugin information
        return (object) [
            'name'          => $api_response['name'],
            'slug'          => $api_response['slug'],
            'version'       => $api_response['version'],
            'author'        => $api_response['author'],
            'download_link' => $api_response['download_link'],
            'sections'      => $api_response['sections'],
        ];
    }
}

// Start update check when the plugin is loaded
new WP_Update_Breaker_Updater();

ポイントとなるフックは「plugins_api 」と「site_transient_update_plugins」の2点です。順に見ていきましょう。

plugins_api

WordPressには、引数で渡したプラグインの情報をwordpress.org APIから取得するplugin_apiというフックと同じ名前の関数があります。これは主にプラグインの情報を管理ページで表示する目的で使われているものです。plugin_apiフックは、plugins_api関数の中で呼ばれていて、処理を上書きできるものになります。なおこの挙動は、WordPress本体のソースコードに丁寧にコメントで記載されています。

今回の実装では、自作したプラグイン名が引数でわたってきたときだけ、自身が準備したサイトにアクセスしてプラグイン情報を取得するように変更しています。

site_transient_update_plugins

このフックについて説明する前に、WordPressのtransientというキャッシュ機構について説明します。transientは、外部のAPIから取得したデータをキャッシュすることにも使用される機構で、プラグインのアップデートの有無は、 update_plugins をキーとした transient に保持されています。このtransientは get_site_transient (‘update_plugins’) のような実装で値を取り出すことが可能で、WordPressは随所でこれを呼び出して、プラグインアップデートの有り無しを判定しています。

さて、site_transient_update_pluginsフックですが、get_site_transient (‘update_plugins’)の結果に対して、変更や追記削除ができるフィルターとして機能するものになります。WordPressのソースコードの該当箇所を読むことでその挙動が把握できます。

今回の実装では、キャッシュされているプラグインアップデート情報を取り出したときに、自作プラグインのアップデート情報を追記することで、WordPressにアップデート情報を与えています。

ここまで記載したところで疑問を持った方もいらっしゃるかと思います。

「プラグインのアップデートの有無を、update_plugins をキーとしたtransient に保持しているといったが、このキャッシュを生成するときに、自作プラグインの情報を含むようにしたらよいのでは?」

その疑問はごもっともです。しかし残念ながら、このアプローチを取ることはできませんでした。キャッシュに保存する処理は wp_update_plugins という関数で実行されているのですが、そこにはhookの実装がないためです。そこでハック気味な使い方にはなるのですが、site_transient_update_plugins フックを活用したという経緯になります。

まとめ

「アップデートしたらサイトが壊れる」という一風変わったWordPressプラグインの紹介をしました。さらに、そのプラグインを紐解くことで、WordPressのフックの仕組みについても解説しました。この記事が、WordPressをより深く活用していきたい方々の一助になれば幸いです。

]]>
[協賛レポート]ETHTokyo 2024 https://developers.gmo.jp/events/52836/ Thu, 17 Oct 2024 02:11:37 +0000 https://developers.gmo.jp/?p=52836

GMOインターネットグループは、イーサリアム開発者たちが世界中から集うハッカソンイベント「ETHTokyo 2024」に、プラチナスポンサーとして協賛いたしました。
イベント当日の様子に加え、出展ブースやスポンサーセッション、またETHTokyo 2024の公式後夜祭としてGMO Yours・フクラスにて開催した『Blockchain Engineers Networking @GMO Yours』の様子についてご紹介いたします。

イベント概要

  • 開催日:
    ⚪︎ハッカソン:2024年8月23日(金)〜2024年8月25日(日)
    ⚪︎カンファレンス:2024年8月26日(月)
  • 会 場:渋谷パルコDGビル
  • 運 営:Ethereum Japan
  • 参加費用:
    ⚪︎ハッカソン:無料
    ⚪︎カンファレンス:有料($90/$120/$250)
  • 公式HP:https://www.ethtokyo.com/

ETHTokyo 2024とは

ハッカソン|2024年8月23日~25日
ETHTokyo 2024」は、イーサリアム開発者たちが世界中から集う大規模なハッカソンイベントです。2024年8月23日から25日までの3日間、渋谷のデジタル・ガレージでイーサリアム開発者たちが約140名ほど参加し、ハッカソンに取り組みました。
参加者の約半数は海外からの参加となり、アジア・欧州・中東・北米など様々な国から集まっていました。会場で会話した中には今回のために来日したという参加者も複数人おり、世界的な視点から見ても本イベントは盛り上がっているのだなと感じました。

カンファレンス|2024年8月26日
イベントの最終日である8月26日には、著名なスピーカーによるカンファレンスが行われました。スピーカーには、イーサリアム創設者のヴィタリック氏(Vitalik Buterin)がオンラインにて登壇を行ったほか、元台湾デジタル発展部部長のオードリー・タン氏などが登壇しました。

また、弊社のメンバーに台湾語を話せる人材がいたため、オードリー・タン氏とイベント中にお話しすることができました!記念写真も撮っていただき、非常に貴重な経験となりました。

スポンサーセッション

1.ハッカソンセッション
登壇者:横井 宗之(GMO AI & Web3株式会社)

日程:8月23日(金)20:30~20:35

ハッカソンセッションとして、GMO AI & Web3株式会社から横井 宗之が登壇しました。ハッカソン会場にて行われるスピーチになり、5分間という短い時間ながらも、Web3テクノロジーで新たなプロダクト開発に挑む、「GMO AI&Web3の取り組み」を紹介し参加者を惹きつけました。

2.カンファレンスセッション
登壇者:栗林 健太郎(GMOペパボ株式会社 取締役CTO)

日程:8月26日(月)16:45~16:55

カンファレンスセッションとして、GMOペパボ株式会社 取締役CTOの栗林 健太郎が登壇しました。
オードリー・タン氏のセッションの次の順番かつ・彼の登壇内容に通ずる部分も多いスピーチだったこともあり、会場には多くの人が集まり、結果130名近くの方に傾聴いただくことができました。

登壇テーマ:「Bidirectional Quadratic Voting Leveraging Issue-Based Matching」(双方向的な二次投票を活用した課題ベースのマッチング)

ブース出展について

GMOインターネットグループのブースでは、ガチャガチャを回してオリジナルグッズを当てるといった企画を行いました。3日間通して約250名の方がブースに来訪され、たくさんの方に企画参加いただくことができました。
ガチャガチャは今、日本独自の文化として海外からも人気のコンテンツとなっており、ブースに来訪された海外の方からも、「Gacha!」と楽しそうにガチャガチャを回していただく場面が多く見受けられました。
また、ブースにはGMOインターネットグループが運営するレンタルサーバーサービス「ConoHa」の応援団長である「美雲このは」の大きなパネルも設置。このはちゃんパネルと写真撮影される方もたくさんいらっしゃいました!

ブース上のモニターにはConoHaや美雲このはの詳細説明や、ガチャガチャ景品などを記載した資料を投影しました。また、イベント当日に想定よりも海外からのご参加が多かったことを受け、急遽スライドの表記を英語に変換する・・といったことも行いながら、ブース来訪者とのコミュニケーションを楽しみました。

ハッカソンテーマを設けスポンサー賞を贈呈

8月23日~25日に開催したハッカソンでは、各協賛企業からスポンサー賞を設けました。GMOインターネットグループは以下3つのスポンサー賞を設定し、ふさわしいチームにそれぞれ賞金$1,000を贈呈しました。

1)DIDに関する何かおもしろプロダクト
 受賞チーム:「ONI-CHAN」

2)アーリーアダプターだけではなく、その外側の層にいる人たちに利用者を広げる方法
 受賞チーム:「Toban -当番-」

3)Vtuber向けのウォレットのUX改善
 受賞チーム:「V-Supporter」

ハッカソンに参加した39チームのうち、約半数のチームがGMOインターネットグループのハッカソンテーマにエントリーしてくださいました。いずれもレベルの高い作品ばかりで、審査チームは頭を抱える一幕も。受賞された皆さん、おめでとうございました!!

公式後夜祭@GMOYours・フクラス

開催日:2024年8月25日(日)18:00~21:00
会 場:GMOインターネットグループ第二本社 GMOYours・フクラス

対 象:ETHTokyo2024参加者
参加者数:約130名(申込298名)
集客ページ:https://lu.ma/0es5oym2

ハッカソン最終日である8月25日(日)の夜に、GMOYours・フクラスにてETHTokyo2024の公式後夜祭「Blockchain Engineers Networking @GMO Yours」を実施いたしました。
公式のアフターイベントという形でETHTokyo本イベント内でもサイネージやセッションスライドなど各所で告知をしていたこともあり、約130名ほどの方にご参加いただくことができました。

ハッカソン3日間を終えたお疲れ様会のような形で、軽食やドリンクとともにイーサリアム開発者たちがネットワーキングを楽しみました。GMOYours・フクラスにあるDJブースも設置し、ETHTokyoの運営の皆様や弊社のDJ部メンバーで音楽をかけながら、イベントをより盛り上げました。

最後に

今回、GMOインターネットグループとしてはETHTokyo 2024に初協賛となりました。
本イベントを通して、世界中から集まったハッカソン参加者のエネルギーを肌で感じ、ここで生まれたアイデアやつながりが今後のイーサリアムの未来を形作るのだなと強く思いました。
来年以降もこの盛り上がりが続くことを期待しております!

]]>
[協賛レポート]セキュリティ・キャンプ 全国大会2024 https://developers.gmo.jp/events/46548/ Tue, 10 Sep 2024 06:00:00 +0000 https://developers.gmo.jp/?p=46548

8/12(月)~8/17(土)5泊6日で開催された、セキュリティ・キャンプ 全国大会 2024に、GMOインターネットグループはシルバーメンバーで協賛しました。

当日は企業紹介イベントへの参加や、GMOインターネットグループの社員が講師として参加してきましたので、その内容をレポートしていきます!

イベント概要

セキュリティ・キャンプ全国大会とは

「セキュリティ・キャンプ」のメインイベントとして主に夏休み期間中に実施する合宿形式の勉強会です。

毎年テーマ別に集められるエキスパートが講師となり、専門的・実践的な講義が受講できるほか、合宿の中で同じ趣味を持った仲間との交流もあります。
全国大会のほか、全国大会修了生の次のステップとなるネクストキャンプ、小中学生を対象としたジュニアキャンプも同時開催いたします。
また会期中には、これまでにセキュリティ・キャンプに参加した修了生がチューターとして学習の支援等も行います。

会場となるクロス・ウェーブ府中は、勉強する部屋、食堂、ホテルがすべて建物内に入っており、
学生たちは過ごしやすそうで勉強に集中しやすい環境が整ってました。

事前配布ノベルティ

毎年参加者の楽しみの1つでもある!?
協賛企業からのノベルティが参加者の各自宅へ、ノベルティボックスとして事前に郵送されました。

GMOインターネットグループからは、
『オリジナルカップ麺(わかめラーメン)× スキミング防止カード』をノベルティとして用意しました!

学生の皆さまがこのノベルティを手に取り、楽しみながらセキュリティについて学び、理解を深めてもらいたいという思いを込めて、作成しました。

  • スキミング防止カード
    クレジットカードやICカードの不正読み取りを防ぐセキュリティガジェット!
    皆さんのお財布やカードケースに入れて、是非たくさん使用していただけたら嬉しいです。
  • オリジナルカップ麺(わかめラーメン)
    デザインにとてもこだわりました!
    皆さん気づいていただけましたでしょうか?

    わかめの広がりをフィッシング攻撃の蔓延と脆弱性の拡大に見立て、フィッシングの危険性・セキュリティの重要性を直感的に理解してもらえるような意味を込めてデザインしました。

    スキミング防止カードを捲ると・・・
    学生の皆さんへ、GMOインターネットグループからのメッセージを添えさせていただきました!

企業紹介イベント

全国大会期間中の実施予定でしたが、台風の影響で延期となってしまい、
後日開催された『アフターイベント』にて、オンラインによる企業紹介を実施いたしました。

ブースセッション / フリータイム

今回は、セキュリティチームの現場で活躍するエンジニア2名と人事が参加させていただきました。
セキュリティに携わるエンジニアより、実際に行っている業務内容や働いていくうえでのモチベーションなど、なかなか聞けないリアルな現場の話をお話しました。

また、学生参加者からの質問にもお答えすることができ、少しでも弊社に興味を持っていただけたら嬉しいです。

全国大会での講師紹介

当日は、GMOインターネットグループより講師として、グループ3社6名が参加いたしました。
実際に参加してみての感想や、学生に向けたインタビューをいただきましたので、是非ご覧ください。

GMOサイバーセキュリティ byイエラエ株式会社 三村 聡志

  • 専門コース│A3『ファイルシステムの理解と記憶チップ内あデータの解析』
  • 開発コース│S08『ハードウェアのリバースエンジニアリングゼミ』

Q:全国大会の講師を終えての感想

三村

昨年に引き続き全国大会の講師を務めまして、 情報セキュリティの分野に対する学生からの関心や受講生のレベルが年々上がっているのを感じています。

もちろん、学生の皆さんが楽しかったと感じてもらえる内容を作るのは大変です。
自分自身の知識の整理や学生の皆さんからの素直なフィードバックを得られ、 フィードバックから次回の講義や業務の質向上に繋がりますので、私自身も参加するたびに達成感や充実感を感じております。

Q:学生へのメッセージ

三村

何事にも「初心忘るべからず」ということ、 そして学習する際は、学んでいること・知っていることに共通項を見つけ、 知識を関連づけることを心がけることをお勧めします。

そして今の学生の時間を使って、好きなことをどんどん掘り下げること。それが将来にわたっての自分自身の「わくわく」を維持してくれる源泉になると思っています。

三村

Developer Experts として、様々なところで活動をしております。
私自身、様々なかたから声を掛けられるのは好きですので、SECCONなど、どこかでお見かけした際には気兼ねなく声を掛けて頂けたらありがたいです。

GMOサイバーセキュリティ byイエラエ株式会社 緑川 志穂

  • 開発コース│S04『暗号フルスタック開発ゼミ』

Q:全国大会の講師を終えての感想

緑川

年々洗練されていく全国大会とレベルの高い受講生にはこちらとしても勉強させられる思いしかありません。そんな中で講師を務めさせていただけていることについて光栄に思います。

今年は特にスピード感があり、具体的には弊ゼミは「TLS1.3のプロトコル・スタックを作る」という目標を全国大会一週間前に達成してしまっていました。応用・発展をそこから考え、会期中は楽しく先を考えながら開発を進められたことは良いところだったかなと思います。

Q:学生へのメッセージ

緑川

何か理論的な記述のあるものに実装をつける、ひいては「何か理想を考えたときに、それを具体的に実現する方法を考える」という行為は、本質的に何かしらの要素を切り落とさなければ不可能な行為です。すなわち、何かを作ろうとするときにまず考えるのは、その具体的な内容よりも具体的に実現できなさそうな部分なのです(勿論最初はわからないものですから、経験者に聞くなり、自分で試しに作ってみて失敗してみたりといったトライも必要でしょう)。

裏を返せば、実現できなさそうな部分を的確に切り取れさえすれば、どんな目標でも到達に向けた道筋を立てられます。切り取ったとしても、切り取っただけなら後から追加実装を考えられるのですから!

巨大なものを作るときによく言われるのはスモールスタートの考え方です。しかし起点や経路の選択が悪いとスモールスタートでもうまく行きません。上記を踏まえ、「ほぼ全てを捨てて実装するなら何を実装するか」と考えると、その選択の手助けになるかもしれません。

セキュリティ・キャンプでこれまで利用していたクロスウェーブ府中はこの8月で閉業してしまいました。今後どこになるのかわかりませんが、願わくは是非その新施設一年目から参加してみたいところです。

GMOペパボ株式会社 紫関 麗王

  • 開発コース│S11『Rustで実装するLinux向けアンチウィルスゼミ』

Q:全国大会の講師を終えての感想

紫関

昨年に引き続き、弊ゼミではLinuxをターゲットとしたアンチウィルスをRustで実装する。という内容を取り扱いました。
テーマは同じですが、目標や方針はガラリと変わっており、今年はeBPFによる検知を実装しました。

講師という立場での参加でしたが、これを期に自分も多くの学びを得られました。
また機会があれば来年以降も担当できたらと思います。

Q:学生へのメッセージ

紫関

セキュリティとは言っても攻撃を行うだけではありません。
セキュリティエンジニアも「エンジニア」である以上、ものや仕組みを作ることが仕事だと考えています。
セキュリティ・キャンプではそういった実装力を身につけるチャンスでもあるので、ぜひ参加してみてください。
また、参加された学生のみなさんは自身を持って、今後も自分のやりたいことをやり、アウトプットしていってください!

さいごに

セキュリティ・キャンプ全国大会 2024にご参加された学生の皆さま
5泊6日の勉強合宿、大変お疲れさまでした!!

皆さんの今後の頑張り・活躍を応援し、心より期待しております!

]]>
【就活攻略】現場のノウハウを学んで就活に役立てよう~ホスティング・EC編~ / 技育CAMPアカデミア https://developers.gmo.jp/events/50224/ Mon, 26 Aug 2024 01:00:00 +0000 https://developers.gmo.jp/?p=50224

GMOインターネットグループは、学生エンジニア向けオンライン勉強会「技育CAMP アカデミア」にて、GMOインターネットグループのシリーズを計6回開催します!

第4回は、GMOペパボからエンジニアが登壇します。
現場で活躍するプロフェッショナルをお招きし、彼らが実際にどのような開発を行っているのかをMCのGMOインターネットグループ 成瀬がヒアリングしながら解明していきます。
学生の皆さま是非ご参加ください!

※タイトル、概要について一部変更ございますが、当初予定していた実施内容と大幅な変更はございません。
旧タイトル:【ハッカソン入門編】成瀬が解説!プロの現場から学ぶプロダクト開発ノウハウ~GMOペパボ編~

技育CAMPとは?

「自ら考え、自ら創る」ことができる未来のエンジニアを育てるキャリア育成プログラム「技育プロジェクト」の一環で、「未来の技術者を育てる」ことを目的とした、エンジニアを志す学生の皆さんにハッカソンと勉強会を通して継続的なインプットとアウトプットの場を提供するスキルアップ支援プラットフォームです。

GMOインターネットグループはその理念に共感し、スポンサーとして年間協賛しています。

イベント概要

【就活攻略】現場のノウハウを学んで就活に役立てよう~ホスティング・EC編~

みなさんを悩ませる就職活動。
その準備は早ければ早いほどいいとはいいますが、何に手を付けるといいかは悩みどころです。
そんなあなたにおすすめな就活対策があります。それは現場を知ることです!
手あたり次第に勉強してもそれが正しい方向に進んでいるかはわかりません。
自己研鑽で開発をしたとしても、現場の実態にそぐわなければ見当違いです。

現場で活躍する開発者をお招きし、彼らが実際にどのような開発を行っているのかをMC(デベロッパーエキスパートの成瀬)がヒアリングしながらノウハウをかみ砕いて解明していきます。

ここで得られたノウハウはいますぐ実践できるものもあれば、数年後に考えることになるようなものもあるでしょう。
またせっかく開発者をお招きしますので、直接就活に役立つようなノウハウもヒアリングしてまいります。

今回のターゲットは『GMOペパボ株式会社』!
オリジナルグッズ作成サービス「SUZURI」、ハンドメイドマーケット「minne」やレンタルサーバー「ロリポップ!」などクリエイターを支援するインターネットサービスを提供する会社です。

いったいどのような技術スタックや開発戦略を掲げているのか、今回の勉強会で解明していきます!
ぜひ奮ってご参加ください。

<注意事項>
申し込み人数が予定数に達した場合、申し込み締切日より前に受付終了いたします。
アーカイブは残しませんので、当日是非ご参加ください!!

登壇者

  • 成瀬 允宣(@nrslib
    プログラマ/GMOインターネットグループ デベロッパーエキスパート

    テックリードとしてWebアプリケーションプロダクト開発に従事するほか、大学講師や小学校プログラミング教育に携わる。
    また、カンファレンス等でソフトウェア開発・設計を主軸に講演活動を行っている。
    著書『ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
  • 黒瀧 悠太 (@kurotaky
    GMOペパボ株式会社 SUZURI事業部 事業部CTO 兼 GMOインターネットグループ デベロッパーエキスパート

    2012年4月よりGMOペパボ株式会社に入社、ソフトウェアエンジニアとして複数のWebサービスの開発を担当している。
    現在はオリジナルグッズ・アイテムを手軽に作成・販売できる「SUZURI」の事業部CTOとして技術経営を担当し、GMOインターネットグループのデベロッパーエキスパートとして、次世代IoTシステムに関する研究も推進している。験後、プログラミング教室ポータルサイトを開発しているコエテコチームに配属。同チーム内で新規プロダクトの開発リードを担当。
成瀬 允宣
黒瀧 悠太

こんな方におすすめ

  • 「本気のプロダクト開発をしたい」
  • 「ハッカソンで勝ち上がりたい」
  • 「リアルな開発現場の雰囲気を味わいたい」
  • 「開発者に将来なりたい」

本気でプロダクトを開発するには運用まで見据えた開発をしなくてはなりません。
リアルな開発現場と遜色のないプロダクト開発はハッカソンの勝利のカギです。
IT業界で働きたい方はぜひともリアルな現場で行われていることを知るべきです。

お知らせ

学生エンジニア向けオンライン勉強会「技育CAMP アカデミア」にて、
GMOインターネットグループシリーズを計6回開催しています!

9月は計3回実施予定!

就活攻略として様々な業界のエンジニアから、技術スタックやリアルな開発現場の話・ノウハウを聞けるチャンスです。
各回に参加し、気になる企業があれば是非チェックしてください!

さいごに

学生の皆さまのご参加をお待ちしております!

]]>