Pepabo Tech Portal

https://tech.pepabo.com/

GMOペパボのエンジニア・デザイナーによる技術情報のポータルサイト

フィード

記事のアイキャッチ画像
集約ジョブパターンで解決するモノレポCIのrequired check問題
Pepabo Tech Portal
こんにちは!ロリポップ・ムームードメイン事業部ムームードメイングループの中村(@litencatt)です。今回は、ムームードメインのモノレポで運用していたCIの統合ゲート(複数のCIの結果を1つの required check に集約する仕組み)を、GitHub Actionsネイティブの集約ジョブ方式に移行した話です。約500行のカスタムJavaScript + checks API + ポーリングで構成されていたプロキシ方式を廃止して、保守対象のコードは約335行に、障害モードは6種類まるごと消えました。集約ジョブ方式は GitHub Actions の標準機能のみで構成されており、GitHub.com / GHES(GitHub Enterprise Server)を問わず、どのランナー環境でも導入できます。TL;DRモノレポで paths: フィルタ付き CI を required check(マージ前に成功が必須のチェック)にすると、該当パスに変更がない PR で skipped のまま永久ブロックされる(GitHub の既知制約)これを解決するために checks API + ポーリングのプロキシを自前実装していたが、~500行のコードに6種の障害モードを抱える状態にneeds + if: always() の集約ジョブパターンに移行し、プラットフォームのネイティブ機能だけで required check 問題を解消既存 CI は workflow_call(再利用可能ワークフロー呼び出し)で再利用し、コードの重複なく移行移行時の注意点: concurrency group 衝突、workflow_run 非発火、check run 名の変化TL;DR背景:モノレポCIの「required check問題」CI統合ゲートの変遷 第0世代:個別CIをrequired checkに登録(〜2026年3月)第1世代:ポーリング方式のプロキシ(2026年3〜4月)第2世代:イベント駆動化(2026年6月)理想形の発見とPoC PoCで動作検証新アーキテクチャ:CI オーケストレータ 主要な設計決定実装で遭遇した落とし穴と解決 concurrency group 衝突問題workflow_run が発火しなくなる問題check run 名の変化変更検知の整合性移行
19日前
記事のアイキャッチ画像
【レポート】毬藻企画とGMOペパボでWebアクセシビリティのイベントを開催しました!
Pepabo Tech Portal
この記事についてイベントの背景・概要当日の様子 アクセシビリティウォークスルー改善提案ディスカッションイベントを振り返ってさいごにこの記事についてGMOペパボのtarutaruです。2026年3月24日、アクセシビリティエキスパート集団である毬藻企画合同会社さんとの共催で、「【GMOペパボ・毬藻企画】スクリーンリーダーによる実践的アクセシビリティウォークスルー 〜SUZURI byGMOペパボ編〜」というイベントを開催しました。GMOペパボにとっては初めてのアクセシビリティイベント。この記事では、イベント当日の内容・様子など振り返っていきます。イベントの背景・概要GMOペパボでは、「人類のアウトプットを増やす」というミッションのもと、誰もが表現活動の機会を持てる世界を目指してアクセシビリティ推進にも取り組んでいます。ただ、組織としての浸透も各サービスの改善も、まだ「道半ば」というのが正直なところで、SUZURIもその例外ではありませんでした。そんな折に、SUZURIでグッズ販売をしてくださっている毬藻企画の森田さんからお声がけいただきました。お話を重ねるなかで、「SUZURIのアクセシビリティ改善に取り組むなら、まずは現状の課題を知るところから。せっかくならオープンにやってみるといいんじゃないか」と、今回のイベントをご提案いただきました。お話を受けて、弊社の事例を公開することが、アクセシビリティに取り組まれている方の学びや刺激になれば。そして、その外に向けた発信が巡り巡って社内のアクセシビリティ推進の後押しにもなれば。そんな期待もあって、今回共催という形でイベントを開催することにしました。本イベントは、弊社のサービス「SUZURI byGMOペパボ」に対して、アクセシビリティウォークスルーを実施いただくイベントとして開催されました。あまり聞き馴染みのないワードだと思いますが、アクセシビリティウォークスルーは、認知的ウォークスルー(初見のユーザーがUIを一手順ずつ操作していくプロセスを追体験し、つまずきを抽出する手法)とエキスパートレビューを組み合わせたもので、毬藻企画さんによる造語になります。当日は、全盲のエンジニアであるcatさん(野澤 幸男さん)をゲストとしてお招きし、SUZURIをスクリーンリーダー(NVDA)で操作しながらウォークスルーを実施いただきました。
1ヶ月前
記事のアイキャッチ画像
GA 直後の Amazon S3 Files を SUZURI の本番 EKS に投入し コンテナイメージを 1/20 に圧縮した
Pepabo Tech Portal
はじめに課題:assets が コンテナイメージに焼き込まれる構造的問題 lens/lens2 が扱う assets とは移行前の状況S3 Files という選択肢 当初の候補: EFS + DataSync2026 年 4 月 7 日 GA: Amazon S3 Filesアーキテクチャ設計 移行後の全体像設計のポイント実装: lens2(Rust)で先行検証 EKS への S3 Files マウントPhase 2a: 動作確認(並列マウント)Phase 2b: ASSETS_DIR 切り替えPhase 2c: Dockerfile から assets を除外ハマりどころ: EFS CSI Driver を動かしてわかったこと 1. inline (Ephemeral) CSI volume は非対応2. accessModes: ReadOnlyMany が非対応3. volumeHandle に s3files: プレフィックスが必須4. mountOptions: [iam] の明示指定が必要5. controller SA と node SA に別々の IAM ロールが必要補足: S3 Files 障害時の挙動と監視計測結果 lens2(Rust)本番実測lens(JavaScript)本番実測S3 Files の読み取りレイテンシ(lens 本番実測)lens(JavaScript)へのロールアウト lens ならではの差異: 上書きマウント方式lens2 のノウハウがそのまま活きたまとめ 成果サマリS3 Files を選んでよかった点さいごにはじめにこんにちは、技術部技術基盤グループで SUZURI / minne / カラーミーショップなどのインフラをサービス横断で担当している shibatch です。SUZURI は、オリジナルグッズを手軽に作れる・購入できるサービスです。ユーザーがアップロードした画像とあらかじめ用意した商品テンプレート(assets)を合成して、Tシャツやマグカップの完成イメージを生成する「画像合成サービス」が中核を担っています。この処理を担うのが lens と lens2 という 2 つのサービスです。lens: JavaScript + ImageMagick 製の画像合成サービス(主力)lens2: Rust + Imag
2ヶ月前
記事のアイキャッチ画像
GitHub Actionsの実行遅延をCloud SchedulerとCloud Workflowsで解消する
Pepabo Tech Portal
こんにちは、技術部データ基盤チームの zaimy です。GitHub Actionsの schedule: トリガーが大幅に遅延する問題を、Cloud SchedulerとCloud Workflowsで解消した話を書きます。最終的に採用した構成だけでなく、検討して棄却した構成と棄却理由も合わせて紹介します。背景検討した構成 案A: Cloud SchedulerからGitHub APIを直接叩く案B: Cloud Schedulerから起動するCloud WorkflowsでGitHub Actionsを置き換える案C (採用): Cloud Schedulerから起動するCloud WorkflowsがGitHub ActionsをdispatchするなぜCloud KMSが必要なのか実装 Cloud KMS asymmetric keyの作成 (terraform)GitHub Appのprivate keyをCloud KMSにimportCloud WorkflowsのYAMLハマりどころ Import methodの選択 (-aes-256 の有無)PEM → PKCS#8 DERへの変換結果その他の構成案まとめ背景データ基盤チームでは、毎日の昼会で「前日のチーム内アップデート」を共有しています。GitHub Projectsのカードを更新者やステータスに基づいてアップデート種別 (例: 話題にすべき / 軽く触れる / ステータス移動) を自動分類してラベル付けするGitHub Actionsを、昼会の少し前に走らせる運用にしていました。このAction自体はラベル付与にClaudeを使う仕組みになっていて中身も面白いのですが、本記事の本題はそこではなく、起動タイミングの話です。このActionは schedule: トリガー (cron) で平日11:55に実行するよう設定していましたが、実際の実行時刻は以下の通り、毎日60分以上の遅延がある状態でした。日付 cron 設定 (UTC) 実際の実行時刻 (UTC) 遅延 4/22 Wed 02:55 03:55 +60min 4/21 Tue 02:55 03:57 +62min 4/20 Mon 02:55 04:02 +67min GitHub Actionsの schedule: はベストエフォ
2ヶ月前
記事のアイキャッチ画像
データ基盤のワークフロー構成変更によるコスト84%削減とCI 34倍高速化
Pepabo Tech Portal
技術部データ基盤チームの@zaimyです。ペパボの社内データ基盤「Bigfoot」では、2020年にDigdagから移設して以来約5年間にわたりCloud Composer(Managed Apache Airflow)をワークフローエンジンとして運用してきましたが、2026年1Qをもって別構成に移行しました。本記事では、Cloud Composerから3つのマネージドサービスへ移行した経緯と、その効果について紹介します。Cloud Composerで動いていた3つのワークロードなぜ撤退したのか 1. Agentic AI readyになるためにdbtを身軽にしたい2. コストの膨張3. 運用負荷の高さ移行先コンポーネントの紹介 dbt CloudBigQuery Data Transfer Service (DTS)Cloud Workflowsコスト比較開発効率比較運用面の比較エラーレート比較まとめCloud Composerで動いていた3つのワークロードデータ基盤ではCloud Composer上で主に以下の3つのワークロードを動かしていました。dbtによるデータ変換(Cosmos経由でAirflow DAG上で実行)事業DBからBigQueryへの同期dbt以外のデータ変換や機械学習パイプラインなどその他のオーケストレーションなぜ撤退したのか3つの課題が重なったため、2026年1Qでの撤退を決定しました。1. Agentic AI readyになるためにdbtを身軽にしたい技術部の方針「Agent Ready」に基づき、AIエージェント前提の技術基盤づくりを進めるにあたって、dbtでSSoTなデータマートを作った上でAgentic AIによる分析を実現したいと考えています。そのためにdbtをより身軽に使いたいのですが、Airflow上でdbtを実行する構成はオーバーヘッドが大きく、AIを用いた開発運用においてフィードバックループが遅い状態でした。2. コストの膨張Cloud Composer 2からCloud Composer 3への移行や、使用量の増加により、月額コストが数十万円に膨張していました。3. 運用負荷の高さAirflowはPythonで作り込めるがゆえに、5年間の運用を経てコードベースが複雑になっていました。さらにCloud Composer/
2ヶ月前
記事のアイキャッチ画像
ORDER BY id DESC が招くインデックス誤選択 — 直近のレコードを N 件取り出すクエリを実測 約 658 倍に高速化
Pepabo Tech Portal
はじめに「ある所有者の直近のレコード(注文・投稿・取引など)から、最新の N 件を取りたい」というユースケースは、Web アプリケーションを書いていれば頻出するパターンです。Rails 風に書けば次のような形になります。current_user.orders .where.not(id: @order.id) # 自分自身は除外する .order(id: :desc) # 「直近」を id 降順で表現 .limit(n)このパターンが、minne では特定のユーザーで安定的に MySQL のクエリタイムアウトを起こしていました。Mysql2::Error: Query execution was interrupted, maximum statement execution time exceeded調査の結果、原因は ORDER BY id DESC + LIMIT N の組み合わせによる MySQL optimizer のインデックス誤選択 で、worst case で 2,300 万行をスキャン して max_statement_time を踏み抜いていることが判明しました。ORDER BY のカラムを既存インデックスに合わせて変えるだけで、スキャン推定行数: 23,297,383 行 → 65,024 行(約 358 倍削減)実測実行時間: 56.6ms → 0.086ms(約 658 倍高速化)まで縮みました。本記事では、その原因分析と修正アプローチを EXPLAIN ANALYZE とあわせて紹介します。「直近のレコードを N 件取り出す」クエリを書くすべての人に還元できる知見となることを願っています。はじめに何が起きていたか原因 — ORDER BY id DESC + LIMIT N で optimizer が PRIMARY を選ぶ 「LIMIT を外せば直る」のではない修正 — ORDER BY を既存インデックスのソート順に揃える 補足: id DESC と ordered_at DESC の意味的な差比較サマリ学び参考何が起きていたか問題のあったコードは、ある作家の 直近の注文を N 件取り出す ためのクエリでした。本筋に関係しない部分を削ぎ落とすと、概形は次のようになります。current_user.orders .where.not(i
3ヶ月前
記事のアイキャッチ画像
生成AIの従量課金とどう付き合うか?AIサイトエージェント開発で実践した段階的コスト見積もり
Pepabo Tech Portal
はじめにこんにちは。ロリポップ・ムームードメイン事業部でエンジニアリングリードをしています kinosuke01 といいます。生成AIを組み込んだアプリケーションを事業として提供するとき、避けて通れないテーマが コスト です。事業としてやっている以上、売上・利益・粗利率には当然ながら目標値があります。開発者としても「技術的に動けばOK」ではなく、その数字を前提にしたうえで、お金のことも勘案して設計や実装を進める必要があります。ところが生成AIをAPI経由で使う場合、消費トークン数による従量課金となるため、事前にどの程度のコストになるのかを見積もるのが一筋縄ではいきません。とくに生成AIが自律的に判断・分岐するワークフローだと、入力も出力もユーザーの指示内容に大きく依存するため、「このケースで n トークン」と簡単には言い切れません。本記事では、AIサイトエージェント開発プロジェクトで実施した、段階的にコスト試算の精度を上げていくアプローチを紹介します。完璧な見積もりを最初から作ろうとするのではなく、プロジェクトのフェーズごとに見積もり方法を変えていく話です。前提:AI サイトエージェントというサービス私たちが開発している AI サイトエージェント は、「カフェのサイトを作りたい」「フリーランス向けのポートフォリオが欲しい」といった自然言語の指示を投げると、ページ構成・デザインテーマ・コンテンツまでを一括で生成してくれる Web サイト制作サービスです。生成したあとも、チャット越しに「トップのキャッチを変えて」「このセクションの写真を差し替えて」と伝えれば、AI が編集を代行してくれます。内部のワークフローは、決定論的なワークフローをベースに、一部のステップで生成AIが自律的に判断・分岐する 構造になっています。全部を生成AIに任せるのではなく、「ここは構造が決まっている」「ここは自由度を持たせたい」をフェーズごとに使い分けることで、品質と予測可能性を両立させる狙いです。ざっくり図にすると、このような流れです。ラベルに「生成AI:」と書かれているブロックが生成AI呼び出しで、それ以外は決定論的な処理です。たとえば PageAndSectionPlanner は「ページ何枚構成にするか」「各ページにどのセクションを置くか」を、ユーザーの指示に応じて柔軟に決めます。一方で
3ヶ月前
記事のアイキャッチ画像
Claude Code Skillでメール障害対応を実施
Pepabo Tech Portal
こんにちは、技術部 技術基盤グループのkmsnです。GMOペパボが運営するECサイト構築サービス「カラーミーショップ」で、Outlook/Hotmail/Live宛メールがブロックされる障害が発生しました。この記事では、Claude Code の Skill を活用して障害の初動対応を効率化した事例を紹介します。結論:Skill で障害対応の初動が変わった先に結論をお伝えします。今回の障害対応で最も効果的だったのは、Claude Code の Skill によって状況把握のスピードが大幅に上がったことです。カラーミーショップのメールサーバーは数十台あります。従来であれば、障害発生時に1台ずつ SSH して sudo postqueue -p を叩き、キューの状態を目視で確認していく必要がありました。台数が多いため状況把握だけで時間がかかり、その間もメールは滞留し続けます。今回は colorme-mailq Skill を使い、「メールキュー確認して」の一言で全台の状態を一括取得しました。サーバー active deferred 合計 server-1 0 3,842 3,842 server-2 12 0 12 ※ 上記は全数十台のうち抜粋特定サーバーの deferred が突出して積み上がっていることが一目でわかり、対応する方針をすぐに決められました。従来の手動確認では状況把握に時間を要していた工程が、Skill によって数秒で完了し、対応方針の決定までの時間を大幅に短縮できました。さらに、この Skill は社内のプライベートリポジトリに登録されているため、自分だけでなくチームの誰でも同じように使えます。個人のスクリプトではなく チーム共有の Skill として整備しておくことで、次に同様の障害が起きたときにも誰でも素早く初動に入れるという点が大きな価値です。何が起きたかカラーミーショップのユーザーが送信したメールが、Outlook/Hotmail/Live宛に届かずブロックされる事象が発生しました。Microsoft(Outlook.com)のような大手プロバイダーは、スパム判定によるブロック時に 5xx 系(主に 550)のエラーを返すことがあります。ただし、Postfix 側の設定や一時的なレスポンスにより deferred キューに滞留するケースもあり、
3ヶ月前
記事のアイキャッチ画像
後付け可能な認証を NextAuth で設計する — ログイン機構が決まらないまま、ログイン前提のプロダクトを作った話
Pepabo Tech Portal
はじめにこんにちは。ロリポップ・ムームードメイン事業部でエンジニアリングリードをしています kinosuke01 といいます。「この機能はログインしたユーザーのものとして扱いたい」というのは、ほとんどのプロダクトで当たり前の要件となります。ところが、プロダクト本体の開発を進めたいタイミングで、ログインの仕組みがまだ決まっていないという状況に直面することがあります。後から差し替え可能にしておくというのは一つの手です。しかし「あとで差し替え」を甘く見ていると、いざ差し替えるときに思いのほか大がかりな書き換えが発生してしまう場合もあるのではないでしょうか。この記事では、AIサイトエージェント というプロダクトの開発で実際に直面したこの状況と、そこで取った方針について紹介していきます。要点を先にまとめると、以下の一点になります。決まっていない領域を、差し替え可能なレイヤーに封じ込める。そのレイヤーだけを「本物と同じ形の偽物」で置き、他のコードからは本物と区別できない状態で先に作り切る。具体的には、NextAuth.js を土台にした「本物と同じ形の偽物ログイン」を用意することで、後から本番のログイン機構(OIDC)に NextAuth インスタンスの差し替えだけで移行できるようになりました。以降の節で、この構造を順に分解していきます。前提:AIサイトエージェントとは本題に入る前に、舞台となるプロダクトの輪郭を簡単に共有しておきます。AIサイトエージェントは、「カフェのサイトを作りたい」「フリーランスのポートフォリオが欲しい」といった自然言語の指示を投げると、ページ構成・デザインテーマ・コンテンツまでを一括で生成してくれる Web サイト制作サービスです。生成したあとも、チャット越しに「トップのキャッチを変えて」「このセクションの写真を差し替えて」と伝えれば、AI が編集を代行してくれます。このサービスは、ロリポップ!レンタルサーバー と ムームードメイン のどちらからも利用できるようになっています。ロリポップのユーザーとムームードメインのユーザー、それぞれが AIサイトエージェントのコンパネに入ってサイトを作れる、というのが本番のユースケースとなります。技術スタックこの記事のコード例を読む前提として、プロダクトの技術スタックにも軽く触れておきます。フレームワーク: Next
3ヶ月前
記事のアイキャッチ画像
プロンプトのtypoをCIで弾く ── TypeScriptの型でAIへの指示を守る
Pepabo Tech Portal
はじめにこんにちは。ロリポップ・ムームードメイン事業部でエンジニアリングリードをしています kinosuke01 といいます。先日ゴジラ-0.0のティザー映像が解禁されましたね。公開が楽しみです。さて、GMOペパボでは、ユーザーとの対話をもとにWebサイトをまるごと自動生成するAIサイトエージェントを提供しています。ユーザーが「カフェのサイトを作りたい」「コーポレートサイトがほしい」と伝えるだけで、ページ構成からデザインテーマ、コンテンツまでを一括で生成し、すぐに公開できるWebサイトを作り上げます。この仕組みの裏側では、Webサイトの構造をすべてJSONで表現しています。生成AIに対して「このJSONを埋めてください」と指示し、Structured Outputでフォーマットを固定することで、安定した出力を得ています。しかし、JSONの構造を固定するだけでは不十分でした。各プロパティに何を入れるべきかを正しく伝えるために、システムプロンプトでフィールドごとの説明を与えています。ここで問題になったのが、プロンプトに書いたプロパティ名と実際のコードの型定義がズレるリスクです。この記事では、TypeScriptの型システムを活用してプロンプトの正しさをコンパイル時に保証する仕組みを紹介します。サイト生成の仕組みJSONでWebサイトを表現する私たちのシステムでは、Webサイトを「セクション」の組み合わせで表現しています。ヒーローセクション、特徴紹介セクション、料金表セクションなど、複数のセクションを用意しており、それぞれがJSON構造を持ちます。たとえば、ヒーローセクションはこのような構造です。{ "component": "HeroSection", "props": { "headline": { "text": "想いを、かたちに。" }, "title": { "text": "あなたの「やりたい」を実現するために" }, "primaryButton": { "label": "お問い合わせ", "href": "/contact" }, "layout": "split-content-image", "image": { "imageId": "hero-1", "alt": "メインビジュアル" } }}3つのエージェントによる段階的な生成サイト生成は、
3ヶ月前
記事のアイキャッチ画像
flaky testの原因は、無関係なファイルの1行にあった
Pepabo Tech Portal
SUZURI Webアプリケーションエンジニアのarumaです。昨日の記事では、SUZURIで遭遇したflaky testの事例をいくつかご紹介しました。本記事では、その中でも特に原因の特定が難しかった1件を深掘りします。発生していた現象手がかり1: 自前のメソッドが呼ばれていない手がかり2: フレームワークに同名メソッドが追加されていた手がかり3: ancestorsチェーンの順序が変わっている手がかり4: トップレベルでのinclude真相おわりに発生していた現象ApplicationHelper に定義された画像表示用のヘルパーメソッド picture_tag のテストが、時々failしていました。fail時のログを確認すると、picture_tag が期待と異なるHTMLを生成していました。CIの実行ログからpass時とfail時それぞれのseed値を確認し、ローカルで同じseedを指定して実行してみると、seed値に応じてpassとfailが再現しました。手がかり1: 自前のメソッドが呼ばれていない試しに ApplicationHelper#picture_tag の中身に raise を加えてそれぞれのseedで実行してみると、passしていたseedで実行 → 例外が発生failしていたseedで実行 → 例外は発生せず、同じようにfailという結果になりました。つまり、failするseedでは、テスト対象 ApplicationHelper#picture_tag がそもそも呼ばれていないということです。では、代わりに何が呼ばれているのでしょうか?手がかり2: フレームワークに同名メソッドが追加されていたpicture_tag で検索してみると、Rails 7.1で ActionView::Helpers::AssetTagHelper に同名の picture_tag メソッドが新規追加されていたことがわかりました。ApplicationHelper#picture_tag とは異なる動作をするメソッドです。fail時の出力は、この AssetTagHelper#picture_tag の動作と一致していました。failするseedでは、ApplicationHelper#picture_tag ではなく AssetTagHelper#pictur
3ヶ月前