TECH PLAY

株式会社ラクス

株式会社ラクス の技術ブログ

919

2026年1月21日(水)、ラクス主催のテックイベント 「RAKUS AI Meetup Vol.2」 をオンライン開催いたしました。 ラクスの開発組織では、「顧客志向」を大切にしています。 新しい技術を導入すること自体を目的とするのではなく、「この技術が、誰のどんな業務をどれだけ楽にできるのか」を起点に、日々の開発や改善に取り組んでいます。 RAKUS Meetupは、そうした顧客志向の開発実践から得られた知見を、エンジニア自身の言葉で共有する場として開催しています。 今回の「RAKUS AI Meetup Vol.2」では、ラクスが全社を挙げて取り組んでいるAI活用について、「プロダクトAI実装」「AI駆動開発」「AI組織体制」の3つの観点から発表しました。 本記事では、当日の発表内容をダイジェストでお届けします。 1. 全エンジニアのAI活用状況を可視化する~Lookerを用いたアンケート分析と今後の推進策~ セッションのポイント AI活用によって生まれた価値 2. 仕様駆動開発の組織的定着に向けた取り組み セッションのポイント AI活用によって生まれた価値 3. 出してみてわかったAIエージェントプロダクトの舞台裏 セッションのポイント AI活用によって生まれた価値 まとめ 1. 全エンジニアのAI活用状況を可視化する~Lookerを用いたアンケート分析と今後の推進策~ 登壇者:野間 由貴(開発管理課) 最初のセッションは、ラクス開発本部全体でAI活用をどう推進しているかという組織的なアプローチについてのお話です。 speakerdeck.com セッションのポイント AI技術の進化が速く「理想」の定義が困難であるため、無理に理想を追わず、全エンジニア300名超の「現在地」を可視化することに方針転換した。 「人・こと・気持ち」の3軸でアンケートを設計し、Looker Studioを用いて誰もが使える分析基盤を構築した。 可視化により各チームごとの活用度合いの違いが明確になり、マネジメント層が優先順位を決定するための判断材料として機能した。 AI活用によって生まれた価値 開発組織全体のAI活用レベルを底上げすることで開発効率が高まり、お客様への価値提供のスピードと質が向上した。 2. 仕様駆動開発の組織的定着に向けた取り組み 登壇者:小栗 朗(開発課長) / 山田 智史(バックエンドエンジニア) 続いては、「楽楽電子保存」開発チームによる、開発プロセスそのものをAIに適応させた事例です。 speakerdeck.com セッションのポイント 実装フェーズにおいて、レイヤーごとに13種類のカスタム指示を用意することで実装品質の標準化とハルシネーションの抑制を図った。 オフショア開発(ベトナム)との連携において、AIでPRの説明文やアーキテクチャ図を自動生成することで、日本側のレビュー負荷を大幅に軽減した。 これらの取り組みにより、PRのサイクルタイムを約1/3に短縮し、開発量自体も170%増という大幅な生産性向上を実現した。 「要件定義~設計~実装」を分断しない世界観を目指してプロセス改善に取り組んでいる。 AI活用によって生まれた価値 AIによる開発効率の向上や開発量増加により、お客様が求める機能や改善をよりスピーディかつ大量にお届けできるようになった。 3. 出してみてわかったAIエージェントプロダクトの舞台裏 登壇者:石田 浩章(AIエージェント開発課 課長) 最後のセッションでは、2025年にリリースされた「楽楽AIエージェント for 楽楽精算」クローズドβ版の運用知見が共有されました。 speakerdeck.com セッションのポイント AIエージェント機能により、従来の経費精算業務の30%~50%の工数を削減した。 開発中に「LLMコスト」「精度」「応答速度」という3つの壁を乗り越えた。 コストの壁:過去の経費精算書を活用してLLMの推論量を大幅に削減し、コストを数十分の一まで削減した。 精度の壁:企業ごとに異なる複雑な経費精算ルールに対応するため、AIは高速なドラフト作成に特化し、正当性の担保は既存の「規定違反チェック機能(ルールベース)」に任せるハイブリッド構成を採用した。 応答速度の壁:お客様ヒアリングの結果をもとに、フローの一部を非同期処理に変更することで体感速度の課題を解消した。 AI活用によって生まれた価値 AIエージェント機能によりお客様の経費精算の手間を削減できるようになった。 まとめ 今回のミートアップでは、ラクスにおけるAIの取り組みを 「プロダクトAI実装」「AI駆動開発」「AI組織体制」の3つの観点でご紹介しました。 ご紹介した取り組みはいずれも、AIを導入すること自体を目的としたものではなく、 「お客様の業務をどうすればもっと楽にできるか」という顧客志向の視点から生まれたものです。 ラクスでは、働く人をもっと「楽!」にするため、 これからも顧客志向を軸に、AIの活用と開発プロセスの進化に取り組んでいきます。
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp 「HORIZON OF KHUFU 古代エジプトへの旅」というVR作品を体感してきました。 immersivejourney.jp の方も感想を掲載していますが、非常に素晴らしい体験ができました。 note.com 自分はコロナ禍前の2019年11月エジプトのカイロへ旅をして、ピラミッドやスフィンクスを見てきました。 そんなつながりもあって、世界最大の建造物やその中を見ながら、自分の仕事と紐づけて少し書いてみようと思います。 それは『ピラミッド』を『プロダクト』として捉えて色々考えてみようという試みです。 目次 ピラミッドは史上最大のプロダクトだった ピラミッド建設における役割分担は、驚くほど現代的だった ピラミッドは「超巨大ウォーターフォール型プロジェクト」だった スクラムでピラミッドは作れたのか? プロダクトマネージャーへの最大の学び ――「完成」ではなく「残る価値」を見る AIが発達した今、ピラミッド作成はどう変わるか? おわりに ピラミッドは史上最大のプロダクトだった エジプトのピラミッドは、「なぜ作れたのか」「どうやって作ったのか」という技術的な驚きと同時に、 なぜ4500年以上経っても価値を失っていないのか という問いを私たちに投げかけてきます。 私は現在、ラクスというSaaS 企業で、プロダクトマネージャーとプロダクトデザイナーが所属するプロダクト部のマネージャーを務めています。 プロダクト開発に携わる立場としてピラミッドを見たとき、それは単なる古代建築ではなく、 史上最大級・最長寿命の「プロダクト開発事例」 に見えてきました。本記事では、 ピラミッド建設における役割分担 作成工程・プロセス スクラムは適用できるのか AI時代にどう変わるのか を整理しながら、 現代のプロダクトマネージャーに向けた学び としてまとめてみます。 ピラミッド建設における役割分担は、驚くほど現代的だった ピラミッドは「王の命令で人が動いた」という単純な話ではありません。実際には、明確な役割分担と責任構造が存在していたようです。 諸説ありますが、現代のプロダクト組織に当てはめると、次のように整理して捉えると学びがあります。 プロダクトオーナー :ファラオ(王) なぜ作るのか、何を象徴するのかを決める存在 プロダクトマネージャー :宰相(ヴィジエ) 国家全体のリソースを動かし、成功責任を負う プロジェクトマネージャー :王の建築家・建設監督官 工程・品質・リスクを管理する実務責任者 専門職チーム :測量官、石工、運搬班 PMO / 管理 :書記官 人数、資材、配給、進捗を記録し「見える化」する 重要なのは、 「ビジョンを守る役割」と「現実を成立させる役割」が分離されていたこと です。これは現代の プロダクトマネージャー(価値・WHYを守る) プロジェクトマネージャー(納期・実行を守る) という関係性と、本質的に同じだったのではと思います。 ピラミッドは「超巨大ウォーターフォール型プロジェクト」だった ピラミッド建設は、現代で言えば典型的なウォーターフォールだったように思います。 高さ・方位・形は最初に決まっている 途中での作り直しはほぼ不可能 完成しない限り価値が出ない 王の治世という明確なデッドラインがある 一方で、完全な一発勝負でもなかったのでは?とも思います。 小型ピラミッドでの試行 途中で角度を変えた「屈折ピラミッド」 失敗の学習を次世代に引き継ぐ つまり、 全体はウォーターフォール、部分は反復的改善 これは現代の大規模プロダクト開発と非常によく似ています。 スクラムでピラミッドは作れたのか? 結論から言えば、 スクラム「だけ」でピラミッドを作ることは不可能 だったのではと自分は思っています。 要件変更が許されない 失敗コストが致命的 インクリメント単位で価値を出せない ただし、 スクラム的な考え方がなければ、現代では確実に失敗する とも言えます。スクラムが有効なのは、 工法・設計の検証 精度・安全の実証 デジタルツインや管理システム といった 不可逆性の低い領域 です。 本体はウォーターフォール、「考える・試す」部分はアジャイル。 これはtoB SaaSにおいても、 コア価値・約束は固定 体験・実装は反復改善 という構造と重なります。 プロダクトマネージャーへの最大の学び ――「完成」ではなく「残る価値」を見る クフ王の大ピラミッドは、 当時のKPI(王の復活・権威の象徴)を満たし 4500年後の私たちにとっても価値を持ち続けています プロダクトマネージャーは、 今四半期のKPI 今月のリリース 直近の改善要望 に引っ張られがちです。しかし同時に、 「このプロダクトは、数年後にも意味を持つか?」「社会や顧客の前提が変わっても、価値は残るか?」 という問いを、誰よりも引き受ける役割でもあります。 私たちのBtoB SaaSでも、“社内都合のKPI”ではなく、“お客様の業務がどう良くなるか”を起点に、数年後も残る価値を選び続けたいなと思います。 AIが発達した今、ピラミッド作成はどう変わるか? もし現代でピラミッドを作るなら、AIは確実に「PjM・PdMの仕事」を変えます。 構造解析・設計シミュレーションの高速化 スケジュール・リスクの自動最適化 進捗・品質・安全のリアルタイム監視 過去プロジェクト知見の即時活用 一方で、AIが代替できないものも明確です。 なぜ作るのかを定義すること どの価値を守り、何を捨てるか決めること 社会・顧客にとっての意味を言語化すること AIは「どう作るか」を加速するが、「なぜ作るか」は人間にしか決められない おわりに ピラミッドは、史上最大級の建築物であると同時に、史上最長寿命のプロダクトでもあります。 プロダクトマネージャーという仕事は、「作ること」よりも 「価値を未来まで運ぶこと」 に本質があります。 4500年前の人類がそれを成し遂げた事実は、現代のプロダクト開発に携わる私たちにとって、非常に示唆的ではないでしょうか。 この記事を読んで、やラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp 今回は自分がかなり苦手なテーマ 「目標設定と評価は難しい」 について書こうと思います。(カジュアル面談をすると、ここを聞かれることも多いので。) 私は現在、株式会社ラクスでPdMとデザイナー組織を見ていますが、ここに至るまでにSIer、技術サポート、そしてBtoCプラットフォームの企画・開発と、多様な立場で「目標」と向き合ってきました。 いずれの環境においても難しさは感じていますが、現在のラクスでは自分がこれまで経験したことがない状況でしたので、より難しさを感じています。 もし今、あなたが自社の評価制度にモヤモヤしていたり、これからPdMとしてキャリアを積む上で「どう評価されるのか」に不安を感じていたりするのであれば、本記事はきっと何かのヒントになるはずです。 私がラクスで過ごした4年間の試行錯誤と、現在運用している評価の仕組みについて、かなり生々しい部分まで踏み込んでお話しします。 ※本記事は自分のの経験に基づく一例で、制度や運用は組織・フェーズにより異なります 目次 比較してわかる「目標設定」の難易度格差 比較的シンプルだったSIと技術サポート 「全員野球」で乗り切れたBtoCプラットフォーム時代 なぜBtoB SaaS(SLG)の評価は難しいと感じたのか 1. ビジネスモデルの壁(SLG vs PLG) 2. 担当領域の範囲の壁(機能別組織の宿命) 3. 個人目標への落とし込み(MBOの厳格さ) ラクス流:納得感を生む目標設定のアプローチ 「あるべき姿」からのトップダウン設計 評価の「ガイドライン」を作る 具体論:PdMは「何を」目標にするのか? 1. 事業・プロダクト貢献目標(仮説と品質の評価) 2. 組織貢献・自己成長目標(仕組みと基盤の評価) パフォーマンス評価とコンピテンシー評価の両輪 比較してわかる「目標設定」の難易度格差 まず、前提としてお話ししたいのは、業種や職種によって「目標設定のしやすさとしにくさ」が存在するという事実です。 私が経験したキャリアを振り返ると、その違いは明白でした。 比較的シンプルだったSIと技術サポート SIer時代や技術サポート時代、目標設定にそこまで頭を抱えた記憶はありません。なぜなら、見るべき指標が極めて明確だったからです。 SIer: プロジェクトの売上、利益率、納期遵守。 技術サポート: 問い合わせ対応数、解決までのリードタイム、顧客満足度(CS)。 特に外資系企業にいた頃は、グローバルで定義されたメトリクスが決まっているため、それを達成するか否かという非常にシンプルな世界でした。 「全員野球」で乗り切れたBtoCプラットフォーム時代 前職のファッションEC(BtoC)では、企画・開発・デザイナー合わせて40名弱を管掌していました。ここでは「個人目標」という細かい粒度での設定は行わず、組織全体でのゴールを掲げていました。 BtoC、かつECというビジネスモデル上、ユーザーの行動と売上がダイレクトに結びつきます。 売上直結KPI: 商品ページへの遷移率、カート投入率、購入率。 システム基盤: サイト遅延率、障害頻度。 これらを企画とエンジニアのセットチームに割り当て、 「全員で売上を作らないとサービスが終了してしまう」という危機感(良い意味での当事者意識) を共有できていました。 数字が動きやすく、フィードバックループが速いため、「ざっくりとした方向性」でも組織が機能していました。 しかし、現在私が身を置く「ラクス」のようなBtoB SaaS企業では、この勝手が大きく異なるなと感じています。 なぜBtoB SaaS(SLG)の評価は難しいと感じたのか ラクスに入社し、PdM組織のマネジメントを担うようになって直面したのは、前職までの経験が活用しづらい「3つの壁」を感じました。 1. ビジネスモデルの壁(SLG vs PLG) 最大の違いは、 ラクスがSLG(Sales-Led Growth:営業主導の成長)モデルであること です。 BtoCやPLG(Product-Led Growth:プロダクト主導の成長)のSaaSであれば、「機能改善→ユーザー行動変容→売上増」のパスが比較的短く、KPIツリーも描きやすい。 しかし、SLGモデルの「楽楽シリーズ」では以下の特性があります。 ユーザー ≠ 決裁者: 機能が良くても、導入を決めるのは別の担当者であることが多い。 リードタイムの長さ: 1つの施策をリリースしてから、それが営業活動を経て「受注」という成果になるまでに長い時間がかかる。 因果関係の遠さ : 受注が増えた要因が、新機能のおかげなのか、営業のトーク力なのか、市場要因なのかの切り分けが困難。 つまり、「購入率=受注率」「訪問者数=リード数」といった単純な図式が成り立たず、 「プロダクトの価値づくり」単体での貢献を定量的に測るのが極めて難しい ということを感じました。 2. 担当領域の範囲の壁(機能別組織の宿命) 前職では開発まで含めた全体を見ていましたが、現在はPdMとデザイナーという「企画・上流」に特化した組織を見ています。エンジニアリングは別の本部が管掌しており、私の組織の目標に「開発効率」や「システム安定性」を直接組み込むことはできません。 また、事業戦略そのものは事業部長の管掌であり、PdMはその事業戦略を元にプロダクト戦略や戦術に落とし込む役割が主として担っています。関与できる範囲が限定されている中で、いかに納得感のある目標を立てるかという難しさがありました。 3. 個人目標への落とし込み(MBOの厳格さ) これが最も大きな違いかもしれません。ラクスでは半期に一度、 MBO(目標による管理) によるパフォーマンス評価を行います。つまり、 給与や賞与に直結する評価制度 です。 そのため、「なんとなく頑張った」や定性的なアピールだけでは不十分で、第三者(人事や上位層)が見ても納得できる客観性と公平性が求められます。「恣意性が疑われない」ためにも、しっかりとしたロジックが必要です。 この「担当領域の成果が見えにくい中」で「厳格な個人評価」を行う。これが、私がこの4年間向き合ってきた難題の正体です。 ラクス流:納得感を生む目標設定のアプローチ では、この難題に対して私たちはどうアプローチしているのか。4年間の試行錯誤を経て、現在運用している「型」についてお話します。 「あるべき姿」からのトップダウン設計 戦略が未整理な状態でボトムアップだけに寄せるとブレやすいと考えています。個人の想いは大切ですが、組織としてのベクトルが揃わなければ成果は出ないからです。 私は必ず以下の手順を踏んでいます。 組織目標の策定 : 半年や1年で終わる数値目標だけでなく、数年先を見据えた「組織のあるべき姿」と言語化された「現状」をセットで定義 マネージャー(私)の目標設定 : 組織目標を達成するための自身の目標を策定 リーダーへの展開と差配 : 私の目標を噛み砕き、各リーダーに割り振る メンバー目標への落とし込み : ここで初めて個人の目標 このプロセスを経ることで、メンバーは「自分の仕事が組織のどこに繋がっているのか」を迷わずに認識できます。 評価の「ガイドライン」を作る メンバーが目標設定に迷う時間を極小化するために、等級ごとの難易度や方向性のガイドラインを策定しました。 事業・プロダクト貢献目標(70-80%) : いわゆる業務の成果。 組織貢献・自己成長目標(20-30%) : 採用、育成、仕組み化など。 リーダー層には組織貢献の比重を高めるなど、グレードに応じたチューニングを行っています。これにより、目標設定にかかるコストを下げつつ、質の高い目標設定が可能になりました。 具体論:PdMは「何を」目標にするのか? ここが皆さんが一番知りたいポイントかと思います。「売上に直結しない」「結果が出るのに時間がかかる」中で、PdMは何を目標に置いているのか。 結論から言えば、私たちは「 アウトカムが出る蓋然性の高いアウトプット」 を評価対象にしています。そして、“アウトカムの蓋然性が高いアウトプット”を評価する狙いは、顧客課題の解像度を上げ続け、提供価値の質を落とさないことにあります。 1. 事業・プロダクト貢献目標(仮説と品質の評価) SLGモデルにおいて、リリース直後の「売上」や「利用率」を個人の短期評価にするのは酷です(前述の通りタイムラグがあるため)。そのため、以下のプロセスを完遂し、その質を担保することを目標とします。 担当重要テーマのディスカバリーと要求仕様策定 : 顧客課題を深く理解し、解決策としてのPRD(製品要求仕様書)を高い品質で仕上げたか。 デリバリーまでの完遂 : エンジニアやデザイナーと協働し、仕様を落とすことなくリリースまで導いたか。 次期バージョンの計画策定 : 確度の高いロードマップを描けているか。 ここで重要なのは、 「アウトカム(結果)」そのものではなく、「アウトカムが出る可能性があるという仮説」の精度と、それを実行する力 を評価している点です。リリースサイクルが1〜3ヶ月かかる環境では、この「仕込み」の質こそが、将来の事業成長を左右するからです。 2. 組織貢献・自己成長目標(仕組みと基盤の評価) こちらは、組織全体の生産性や再現性を高めるための活動です。 共通指標(NSM [North Star Metric:長期的な成長と成功を示す最重要指標]など)の設定 : 開発・事業が同じ方向を向くための指標作り。 AI活用による業務の標準化 : 生成AIを用いたPRD作成の効率化など、個人の知見を組織の資産にする活動。 ナレッジシェア : 勉強会の主催やドキュメント化。 これらは、直接的な売上ではありませんが、 「組織が継続的に勝つための基盤」として明確に評価 します。 パフォーマンス評価とコンピテンシー評価の両輪 ここまでお話ししたのは、半期ごとの「パフォーマンス評価(MBO)」の話です。しかし、ラクスにはもう一つ、「コンピテンシー評価(行動評価)」が存在します。 パフォーマンス評価 : 「何を」成し遂げたか(What) コンピテンシー評価 : 「どのように」行動したか(How) note.com たとえ短期的な数値目標が未達でも、そのプロセスにおいて、その等級に求められる再現性のある行動(周囲を巻き込む力、課題発見力など)が発揮されていれば、コンピテンシー評価で報われる仕組みになっています。 この「成果」と「行動」の二軸があるからこそ、難易度の高いPdMという職種でも、納得感を持ってチャレンジし続けられるのです。 最後に:ラクスでPdMとして働くということ 「目標設定が難しい」と嘆くだけでなく、私たちはその難しさを因数分解し、システムとして運用可能な形に落とし込んできました。 もちろん、これが完成形ではありません。事業フェーズの変化に合わせて、組織目標も評価の仕方もアップデートし続けています。 ラクスのPdM組織は、単に機能を作るだけの工場ではありません。 ビジネスの複雑性と向き合い、不確実性の中で仮説を立て、組織全体を巻き込んで価値に変えていく。そのプロセス自体を楽しみ、正当に評価される環境を作ろうとしています。 もしあなたが、 「目標があいまいで、自分の成果がどう評価されているかわからない」 「BtoBの難しさに直面しながらも、より本質的なプロダクトづくりに挑戦したい」 と感じているなら、ぜひ一度カジュアルにお話ししませんか? ここでは書ききれなかった「泥臭い試行錯誤のリアル」や、あなたのキャリアにおける悩みについても、マネージャー対マネージャーとして、あるいは先輩PdMとして、ざっくばらんにお話しできると思います。私たちの組織は、こうした「難しさ」を一緒に面白がれる仲間を待っています。 ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
はじめに こんにちは、ヤマウチです。 担当しているサービスではサーバ認証に加えてクライアントの認証も行う相互認証(mTLS)も使えるようになっています。相互認証を使う場合、Webサーバにサーバ証明書、クライアントにクライアント証明書を設定することになりますが、証明書の有効期間が切れる前に証明書の更新を行う必要があります。 この記事では、クライアント証明書の更新作業を具体的な設定例を交えて説明します。また、相互認証のうちクライアントの認証を行う部分の処理をクライアント認証と呼ぶことにします。 目次 はじめに 目次 サーバ認証とクライアント認証の比較 表1. サーバ認証とクライアント認証の比較 クライアント証明書の更新作業 クライアント証明書の更新作業の例 サーバ証明書の作成 クライアント証明書の作成 nginxにサーバ証明書と新旧のCA証明書を設定 curlによるアクセス確認 終わりに サーバ認証とクライアント認証の比較 サーバ認証とクライアント認証を比較することでクライアント証明書の更新で気をつけるべきポイントが分かりやすくなると思いますので、両者を比較した表を示します。 表1. サーバ認証とクライアント認証の比較 項目 サーバ認証 クライアント認証 認証される対象 サーバ クライアント 証明書を提示する側 サーバ クライアント 証明書を検証する側 クライアント サーバ 証明書・秘密鍵の設定場所 サーバ クライアント 証明書の更新作業 サーバ クライアントとサーバ サーバ認証では証明書をサーバに設定するため、証明書を更新する場合にクライアント側の作業は必要ありません。一方、クライアント認証ではクライアントとサーバ両方の作業が必要となります。 また、対象のクライアント数が多くなるとクライアント証明書の更新を一斉に行うことが難しくなるため、新旧のクライアント証明書を使える並行運用期間を設ける必要があります。その期間中は両方のクライアント証明書を検証できるようにサーバを設定しておく必要があります。 クライアント証明書の更新作業 クライアント証明書に署名したCA証明書の有効期間により必要な作業が変わるため、長い場合と短い場合に必要な作業を説明します。 ※2026/01/01時点の状態として説明します。 (1) CA証明書の有効期間が長い場合 CA証明書の有効期間が2年残っているため、既存のCAで新しいクライアント証明書を発行します。 (2) CA証明書の有効期間が短い場合 CA証明書の有効期間が残り半年のため新しいCAを作成し、そのCAで新しいクライアント証明書を発行します。 グラフではCA証明書の有効期間を3年、クライアント証明書の有効期間を1年というイメージで記載していますが、実際の有効期間は運用ポリシーに合わせる必要があります。 クライアント証明書の更新作業の例 「(1)CA証明書の有効期間が長い場合」の新クライアント証明書の発行とWebサーバの設定方法は通常のクライアント認証を設定する場合と変わらないため、「(2)CA証明書の有効期間が短い場合」の新クライアント証明書の発行とWebサーバの設定方法を説明します。 また説明では以下のツールを使用します。 作業 ツール 証明書の作成と署名 OpenSSL Webサーバ nginx クライアント curl サーバ証明書の作成 検証用にlocalhostというサーバ名でWebサーバを立てるため、プライベートCAでサーバ証明書を発行します。 1.作業ディレクトリの作成 mkdir -p server_ca server 2.プライベートCAの秘密鍵と証明書を作成 # 秘密鍵を作成 openssl genrsa -out server_ca/ca.key 4096 # CA証明書を作成 openssl req -x509 -new \ -key server_ca/ca.key \ -days 1095 \ -sha256 \ -out server_ca/ca.pem \ -subj "/C=JP/O=Local Dev/OU=Dev CA/CN=local-dev-ca" 3.サーバの秘密鍵とサーバ証明書のCSRを作成 # 秘密鍵を作成 openssl genrsa -out server/localhost.key 2048 # CSRを作成 openssl req -new \ -key server/localhost.key \ -out server/localhost.csr \ -subj "/C=JP/O=Local Dev/CN=localhost" # SAN定義ファイルを作成 cat > server/server_san.ext <<'EOF' subjectAltName = DNS:localhost,IP:127.0.0.1 EOF 4.プライベートCAでサーバ証明書を発行する openssl x509 -req \ -in server/localhost.csr \ -CA server_ca/ca.pem \ -CAkey server_ca/ca.key \ -CAcreateserial \ -out server/localhost.crt \ -days 365 \ -sha256 \ -extfile server/server_san.ext クライアント証明書の作成 既存のCA証明書の有効期間が残り半年のため新しいCAを作成するというシナリオを再現するため、有効期間が半年と3年のCAを作成します。また、それぞれのCAでクライアント証明書を発行します。 1.作業ディレクトリの作成 mkdir -p client_ca client 2.新旧のCAの秘密鍵と証明書を作成 旧CAの秘密鍵と証明書を作成 - 証明書の有効期間は半年(180日) ※検証用に「残存期間が半年のCA」を再現するため、この旧CA証明書は -days 180 で作成します。 # 旧CAの秘密鍵を作成 openssl genrsa -out client_ca/old_ca.key 4096 # 旧CAの証明書を作成 openssl req -x509 -new -key client_ca/old_ca.key \ -days 180 -sha256 \ -out client_ca/old_ca.pem \ -subj "/C=JP/O=Example Inc/OU=Client CA/CN=example-client-ca-v1" 新CAの秘密鍵と証明書を作成 - 証明書の有効期間は3年(1095日) # 新CAの秘密鍵を作成 openssl genrsa -out client_ca/new_ca.key 4096 # 新CAの証明書を作成 openssl req -x509 -new -key client_ca/new_ca.key \ -days 1095 -sha256 \ -out client_ca/new_ca.pem \ -subj "/C=JP/O=Example Inc/OU=Client CA/CN=example-client-ca-v2" 3.クライアントの秘密鍵とクライアント証明書のCSRを作成 # 秘密鍵を作成 openssl genrsa -out client/client.key 2048 # クライアント証明書のCSRを作成 openssl req -new \ -key client/client.key \ -out client/client.csr \ -subj "/C=JP/O=Customer A/CN=customer-a" # SAN定義ファイルを作成 cat > client/client_san.ext <<'EOF' subjectAltName = URI:urn:customer:a EOF 4.新旧のCAでクライアント証明書を発行する 旧CAでクライアント証明書を発行する - 有効期間は半年(180日) # シリアルファイルを作成 echo 01 > client_ca/old_ca.srl # クライアント証明書を作成 openssl x509 -req \ -in client/client.csr \ -CA client_ca/old_ca.pem \ -CAkey client_ca/old_ca.key \ -CAserial client_ca/old_ca.srl \ -out client/old_client.pem \ -days 180 -sha256 \ -extfile client/client_san.ext 新CAでクライアント証明書を発行する - 有効期間は1年(365日) # シリアルファイルを作成 echo 01 > client_ca/new_ca.srl # クライアント証明書を作成 openssl x509 -req \ -in client/client.csr \ -CA client_ca/new_ca.pem \ -CAkey client_ca/new_ca.key \ -CAserial client_ca/new_ca.srl \ -out client/new_client.pem \ -days 365 -sha256 \ -extfile client/client_san.ext nginxにサーバ証明書と新旧のCA証明書を設定 サーバ認証用にサーバの秘密鍵と証明書を設定します。また、新旧のクライアント証明書を検証できるように新旧のCA証明書も設定します。 1.秘密鍵と証明書を配置するディレクトリを作成 mkdir -p /etc/nginx/tls 2.サーバの秘密鍵と証明書を配置する cp server/localhost.key /etc/nginx/tls cp server/localhost.crt /etc/nginx/tls 3.新旧のCA証明書を配置する nginxの ssl_client_certificate にはCA証明書を1つしか設定できないため新旧のCA証明書を結合したファイルを作成します。 cat client_ca/old_ca.pem client_ca/new_ca.pem > /etc/nginx/tls/old_new_ca.pem ※ Nginx(OpenSSL)は、ssl_client_certificate で指定されたファイルの中に、提示されたクライアント証明書の署名元(Issuer)と一致するCA証明書が含まれているかを順番にチェックします。そのため、新旧のCA証明書を結合したファイルを指定すると新旧のクライアント証明書を検証できるようになります。 4./etc/nginx/nginx.confを作成する cat > /etc/nginx/nginx.conf <<'EOF' user nginx; worker_processes auto; include /usr/share/nginx/modules/*.conf; events { worker_connections 1024; } http { server { listen 443 ssl; server_name localhost; # サーバの秘密鍵を設定する ssl_certificate_key /etc/nginx/tls/localhost.key; # サーバ証明書を設定する ssl_certificate /etc/nginx/tls/localhost.crt; # CA証明書を設定する ssl_client_certificate /etc/nginx/tls/old_new_ca.pem; # クライアント認証を有効にする ssl_verify_client on; location / { return 200 "ok\n"; } } } EOF ※既存の nginx.conf がある場合は、http コンテキストや server コンテキストの中に、ssl関連の各ディレクティブを追記してください。 5.nginxを起動する systemctl start nginx curlによるアクセス確認 クライアント証明書を指定せずにWebサーバにアクセス curl -s -o /dev/null -w "%{http_code}\n" \ --cacert server_ca/ca.pem \ https://localhost 400 ※ サーバ証明書の検証のために --cacert オプションでサーバ証明書に署名したCA証明書を指定しています。 レスポンスコードが400となりクライアント証明書を指定しないとアクセスに失敗します(設定によってはレスポンスコードが400以外になる場合があります)。 旧クライアント証明書を指定してWebサーバにアクセス curl -s -o /dev/null -w "%{http_code}\n" \ --cacert server_ca/ca.pem \ --cert client/old_client.pem \ --key client/client.key \ https://localhost 200 レスポンスコードが200となりアクセスに成功します。 新クライアント証明書を指定してWebサーバにアクセス curl -s -o /dev/null -w "%{http_code}\n" \ --cacert server_ca/ca.pem \ --cert client/new_client.pem \ --key client/client.key \ https://localhost 200 レスポンスコードが200となりアクセスに成功します。 上記の結果から新旧のクライアント証明書の並行運用ができていることが分かります。 終わりに ユーザの認証強化では多要素認証やパスキーを使った認証の方が便利なため、相互認証(mTLS)を使う機会は減っていると思います。 一方、システム間の連携やデバイス認証では今後も相互認証は使われていくと思いますので、相互認証を使用した運用の参考になれば幸いです。
アバター
目次 目次 1. はじめに 解決したかった課題 2. アーキテクチャ 3. プレビュー環境の作成・更新・削除 作成・更新フロー 削除フロー パターンA: PRクローズ or ラベル削除 パターンB: TTLによる定期クリーンアップ プレビュー環境へのアクセス PRコメント例 4. 実装のポイント Pull Request Generator の実装 PRごとに異なるValuesの命名規則 GitHub Actions Argo CD 再コミット時の自動イメージ更新 仕組み 環境数の上限制御 ResourceQuota によるリソース使用量の制御 5. おわりに 参考 1. はじめに こんにちは!SRE課のモリモトです。 今回は、 プレビュー環境基盤 をKubernetes上に構築した話をご紹介します。 解決したかった課題 弊社のあるサービスでは、フロントエンド開発において以下のような課題がありました。 現状では、ある程度修正がまとまったタイミングでないと検証環境に上げることが難しいため、ローカル環境以外で気軽に動作確認できる環境がない デザイナーや企画/要件を詰めるチームに画面仕様を確認してもらうのに検証環境にデプロイする必要があるが、上記の理由からリードタイムが発生している 検証環境にデプロイせずに確認してもらうためには画面のスクショを送ったりする必要があるが、それだと正確な仕様を把握してもらいにくい PRレビュー時に、レビュアーがローカルでアプリケーションを起動して動作確認する必要があり、無駄な時間が発生している そこで、 PRにラベルを付けるだけで自動的にプレビュー環境がデプロイされる 仕組みを構築することにしました。 具体的には以下のような体験を実現しています。 開発者がPRを作成し、PRに対して preview ラベルを付与 数分後、PRにプレビューURLがコメントされる そのURLにアクセスすると、PRの内容が反映されたUIを確認できる PRをクローズすると、環境が自動削除される 2. アーキテクチャ アーキテクチャ図 本構成では、PRを起点にGitHub ActionsとArgo CDがそれぞれ独立して動作します。 GitHub Actionsはイメージの生成と通知を、Argo CDはKubernetesリソースの生成と削除を担います。 両者を疎結合にすることで、責務を明確に分離しています。 ポイントは、Argo CD ApplicationSetの Pull Request Generator を活用している点です。 ApplicationSet は、同一構成の Argo CD Application を動的に複数生成するための仕組みです。 Pull Request Generator を使うことで、GitHub の PR 情報を元に Application を自動生成できます。 これにより、 preview ラベルが付いたPRを自動検知し、対応するKubernetesリソースを動的に生成・削除できます。 3. プレビュー環境の作成・更新・削除 すべての操作がPRに連動しており、開発者が意識的に環境管理を行う必要がないように設計しました。 作成・更新フロー 作成・更新のシーケンス図 作成・更新の流れは以下のとおりです。 開発者がPRを作成し、 preview ラベルを付与 GitHub Actionsがワークフローを起動 まず、すでに作成済みのプレビュー環境数( preview ラベルが付いたPR数)を確認します 上限(1リポジトリにつき最大10環境)に達している場合は、その旨をPRにコメントし、 preview ラベルを自動で外します 並行して2つの処理が実行 GitHub Actions側: コンテナイメージをビルド・プッシュし、プレビューURLをPRにコメント Argo CD側: preview ラベルの付いたPRを検知(3分おきにポーリング)し、プレビュー環境のNamespaceとリソースを自動作成 同一PRに対して再コミットが行われた場合は、GitHub Actions側で再度イメージがビルド・プッシュされ、Argo CD側でデプロイされるイメージタグが自動更新されるため、 追加の操作なしでプレビュー環境が最新化 されます。 削除フロー 削除は2つのパターンがあります。 パターンA: PRクローズ or ラベル削除 Argo CDがPRのクローズまたはラベル削除を検知し、Namespaceごと環境を自動削除します。 パターンB: TTLによる定期クリーンアップ プレビュー環境が溜まり続けることを防ぐために、TTLを設けて更新から10日間経過した環境を削除するようにしています。 削除のシーケンス図 GitHub Actionsのスケジュール実行(毎日0:00)で以下を実行します。 preview ラベルの付いたPR一覧を取得 最終更新日から10日以上経過したPRから preview ラベルを削除 不要になった古いコンテナイメージも合わせて削除 Argo CDがラベル削除を検知し、Namespaceごと環境を自動削除 プレビュー環境へのアクセス プレビュー環境のURLは以下の形式で動的に発行され、PRにコメントされます。 https://pr<PR番号>-<サービス名>.preview-example.com 例): PR #123 の場合 → https://pr123-serviceA.preview-example.com なおプレビューURLは社内ネットワークの範囲でのみアクセス可能とし、外部公開はしない前提で運用しています。 PRコメント例 PRコメント例 4. 実装のポイント Pull Request Generator の実装 Argo CD ApplicationSetでPull Request Generatorを使用することで、 preview ラベルが付いたPRを自動検知し、動的に環境を作成できます。 apiVersion : argoproj.io/v1alpha1 kind : ApplicationSet metadata : name : serviceA-preview-applications-applicationset namespace : preview-applications spec : goTemplate : true goTemplateOptions : [ "missingkey=error" ] generators : - pullRequest : github : owner : "my-org" repo : "serviceA-frontend-repo" appSecretName : app-secret # GitHub Apps認証用のSecret名 labels : - preview # previewラベルがついたPRのみを対象にフィルタリング requeueAfterSeconds : 180 # イメージビルドに3~4分かかることを考慮して3分に設定 template : metadata : name : 'pr{{ .number }}-serviceA-preview-applications' spec : project : preview-project source : repoURL : https://github.com/my-org/manifest-repo.git path : preview-frontend targetRevision : develop helm : releaseName : 'preview-frontend' valueFiles : - "values/develop.yaml" valuesObject : namespace : "pr{{ .number }}-serviceA" image : repository : 'ghcr.io/my-org/serviceA-frontend' tag : 'preview-pr{{ .number }}-{{ .head_sha }}' ingress : app : hosts : - host : 'pr{{ .number }}-serviceA.preview-example.com' paths : - path : / pathType : Prefix destination : name : "dest-cluster-name" namespace : "pr{{ .number }}-serviceA" syncPolicy : automated : prune : true selfHeal : true preview-frontend/ に汎用的に利用できるフロントエンドのhelm chartを実装し、それをプレビュー環境としてPRの数だけ複製するようなイメージになります。 その際に、イメージタグやプレビューURLなど、PRごとに異なるValuesを渡したくなりますが、 valuesObject から {{ .number }} や {{ .head_sha }} などの値を渡すことでPRごとに別々の値をhelm chartに渡すようにしています。 PRごとに異なるValuesの命名規則 命名規則は以下のようにしました。 イメージタグ: preview-pr<PR番号>-<コミットSHA> Namespace名: pr<PR番号>-<サービス名> ドメイン: pr<PR番号>-<サービス名>.preview-example.com 特にイメージタグとドメインについては、GitHub Actions側とArgo CD側で整合性を保つためにルールを決めておく必要があります。 このルールに沿って、GitHub Actions側でのイメージビルドやPRコメント、Argo CD側でのアプリケーションデプロイを実施しています。 GitHub Actions # イメージタグ生成側の例 - name : Set up parameters id : set-params run : | IMAGE_TAG=preview-pr${{ inputs.pr-number }}-${{ inputs.pull-request-head-sha }} # PRコメント時のプレビューURL生成の例 - name : Comment preview URL on PR env : GH_TOKEN : ${{ github.token }} run : | PREVIEW_URL="https://pr${{ inputs.pr-number }}-serviceA.preview-example.com" Argo CD # イメージタグ使用側の例 valuesObject : image : repository : 'ghcr.io/my-org/serviceA-frontend' tag : 'preview-pr{{ .number }}-{{ .head_sha }}' # プレビューURL指定の例 valuesObject : ingress : app : hosts : - host : 'pr{{ .number }}-serviceA.preview-example.com' 再コミット時の自動イメージ更新 同一PRに再コミットした場合、同一URLのままで自動でプレビュー環境が更新される仕組みになっています。 仕組み 再コミットすると、GitHub Actionsが新しいイメージをBuild&Push(タグ: preview-pr<PR番号>-<新コミットSHA> ) Argo CD ApplicationSetが {{ .head_sha }} を参照しているため、新しいコミットSHAでDeploymentのイメージタグが自動更新され、新しいPodがデプロイされる これにより、開発者は再コミットするだけで最新の変更がプレビュー環境に反映されます。 環境数の上限制御 1リポジトリあたりの環境数を10に制限し、環境が作られすぎないようにしています。 制御はGitHub Actions側で行い、上限に達した場合はPRにコメントで通知し、 preview ラベルを自動で外すようにしています。 # GitHub Actions Workflow内での制御イメージ - name : Check preview environment count limit id : check-limit env : GH_TOKEN : ${{ github.token }} run : | # previewラベルが付いているオープンPRの数を取得 PREVIEW_PR_COUNT=$(gh pr list \ --repo ${{ github.repository }} \ --state open \ --label preview \ --json number \ --jq 'length' ) echo "プレビュー環境数:" echo " - 現在: $PREVIEW_PR_COUNT" echo " - 最大値: ${{ inputs.max-preview-environments }}" # 上限チェック if [ "$PREVIEW_PR_COUNT" -gt "${{ inputs.max-preview-environments }}" ] ; then echo "[FAIL] 環境数上限に達しています。" echo "should-continue=false" >> $GITHUB_OUTPUT else echo "[PASS] 環境数は上限内です。" echo "should-continue=true" >> $GITHUB_OUTPUT fi - name : Comment on PR if limit reached if : steps.check-limit.outputs.should-continue == 'false' env : GH_TOKEN : ${{ github.token }} run : | gh pr comment ${{ inputs.pr-number }} \ --repo ${{ github.repository }} \ --body "## ⚠️ **プレビュー環境数の上限に達しています!** 現在、プレビュー環境の最大数(${{ inputs.max-preview-environments }}環境)に達しているため、新しいプレビュー環境を作成できません。 他のPRのプレビュー環境が不要になった場合は、そのPRから \`preview\` ラベルを削除してください。" - name : Remove preview label if limit reached if : steps.check-limit.outputs.should-continue == 'false' env : GH_TOKEN : ${{ github.token }} run : | gh pr edit ${{ inputs.pr-number }} \ --repo ${{ github.repository }} \ --remove-label preview ResourceQuota によるリソース使用量の制御 プレビュー環境は多数同時に立ち上がる可能性があるため、 ResourceQuota でリソース使用量を制限しています。 これにより、1つのプレビュー環境が過剰にリソースを消費することを防いでいます。 apiVersion : v1 kind : ResourceQuota metadata : name : preview-frontend-resource-quota spec : hard : requests.cpu : 20m requests.memory : 64Mi limits.cpu : 100m limits.memory : 128Mi pods : 2 # (...省略...) 5. おわりに GitHub ActionsとArgo CDのApplicationSetを活用することで、 ラベル付与だけで自動的にプレビュー環境が立ち上がる 仕組みを構築できました。 本記事では詳しく説明できませんでしたが、GitHub ActionsのワークフローをReusable Workflow化するなど横展開が容易な形で設計しているため、社内にどんどん展開して 複数チームで共通利用できる基盤 となっています。 現在はNginxを前提にプレビュー環境の仕組みが構築されていますが、今後の展望としては他のミドルウェアにも対応を広げていきたいと考えています。 プレビュー環境の構築を検討されている方の参考になれば幸いです! 最後まで読んでいただき、ありがとうございました。 参考 tech-blog.monotaro.com
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp これまで組織やマネジメントの話を書くことが多かったのですが、以前から少し気になっていたことがあります。 年末年始の休みに買い物をしている際、やはりそのことが気になったので、今回はそれについて書いてみようと思います。それはセルフレジについてです。 目次 「セルフレジのUIは、なぜあんなに不自由なのか?」 リアルUIとWEB UIは、そもそも戦っている場所が違う 利用者の「状態」の違い コンビニのセルフレジUIに見る「運用者優先」の設計 ファーストフードのセルフオーダーUIが示す別の最適解 なぜリアルUIでは「運用者視点」が強くなるのか この構造は、そのまま toB SaaS に当てはまる toB SaaSでよく見る失敗 セルフレジUIと toB SaaS UI の対応関係 プロダクトチームとして、この話をどう使うか AIが入ることで、ネットのUI/UXはどう変わるのか UIから「操作」を減らし、UXを「対話」に移す それでも消えない「運用者視点」 toB SaaSにおけるAI時代のUI/UX AI時代だからこそ、セルフレジの学びは生きる おわりに 「セルフレジのUIは、なぜあんなに不自由なのか?」 初めてセルフレジを使ったとき、そう感じたことがある方は多いと思います。操作の順番は決められていて、戻ることはできず、選択肢も少ない。 WEBサービスやアプリに慣れているほど、この不自由さは違和感として強く残ります。しかし、この違和感こそが、 toB SaaSのUI/UXを考える上で極めて重要なヒント になるのではと思っています。 私は現在、ラクスというSaaS 企業で、プロダクトマネージャーとプロダクトデザイナーが所属するプロダクト部のマネージャーを務めています。日々の業務の中で繰り返し立ち返る問いがあります。 それは、 「良いUI/UXとは誰にとってのものなのか」 という問いです。 プロダクト開発の現場では、UI/UXという言葉がしばしば「使いやすさ」「分かりやすさ」「シンプルさ」と同義で語られます。しかし、toB SaaSにおいてそれだけを追い求めると、運用が破綻する、問い合わせが増える、結果として顧客価値を下げてしまう──そんな経験をした方も多いのではないでしょうか。 今回、セルフレジやファーストフードのセルフオーダーといった リアルのUI/UX を整理して、私は「これはそのまま toB SaaS に当てはまる構造だ」と強く感じました。本記事では、セルフレジUI/UXの具体例を交えながら、 リアルUIとWEB UIの違いがどこから生まれ、なぜ toB SaaS のUI設計にとって重要なのか を整理していきます。 リアルUIとWEB UIは、そもそも戦っている場所が違う セルフレジやセルフオーダー端末に触れると、WEBやSaaSとは明らかに違う設計思想を感じます。この違いは、UIデザインのトレンドやデザイナーの好みではありません。 UIが置かれている環境と、背負っている責任構造の違い から必然的に生まれています。 利用者の「状態」の違い リアルUI(セルフレジ・飲食店端末)の利用者は、次のような状態に置かれています。 立ったまま操作している 後ろに行列ができている 周囲が騒がしく、集中しづらい 初見利用者が多い 操作ミスが即トラブルにつながる 一方、WEBやSaaSのUIはどうでしょうか。 座って操作できる 基本的に一人で使う 自分のペースで進められる 戻る・やり直しが可能 学習しながら使う前提 この違いが、設計思想を真逆の方向へ引っ張ります。 リアルUIは「考えさせない・迷わせない」ことが最優先 であり、 WEB UIは「ある程度の自由や探索」を許容 します。ここを混同すると、UIは破綻します。 コンビニのセルフレジUIに見る「運用者優先」の設計 セブンイレブン  セルフレジ ローソン  セルフレジ ファミリーマート  セルフレジ コンビニのセルフレジには、実は複数のUIパターンが存在します。 レジ袋選択 → 商品スキャン → ポイント → 決済 決済方法選択 → レジ袋 → 商品スキャン → 完了 一見すると「なぜこんな順番なのか分かりにくい」と感じる方も多いでしょう。しかし、これらはすべて 運用上の事故を防ぐための設計 のように思います。 例えば、 レジ袋を最後に聞くと、袋代金の取り忘れが発生する 決済直前に袋選択を挟むと、操作ミスが起きやすい ポイント提示を忘れると、店員呼び出しが発生する セルフレジでは、 一人の迷いが行列全体を止める という前提があります。そのため、UIは「直感的であること」よりも「事故が起きないこと」を優先します。これは決してユーザー軽視ではありません。 店舗全体のUXを守るための設計 だと思います。 ファーストフードのセルフオーダーUIが示す別の最適解 一方、ファーストフードのセルフオーダー端末では、ほぼ例外なく次のような流れが採用されています。 商品選択 セット・サイズ・カスタマイズ 注文内容確認 決済 ここで重要なのは、 決済が必ず最後に来る という点です。なぜなら、飲食においては 「何を食べるか決めるプロセス」そのものが体験価値 だからです。 マクドナルドのセルフオーダーを思い浮かべてください。画面には大きな写真、セット提案、期間限定商品が並びます。これはUXのためだけでなく、 客単価を最大化するための設計 でもあります。しかし同時に、 カスタマイズは段階的にしかできない 戻り操作は最小限 注文確定後の変更は難しい といった制約も多く存在します。ここでも、 運用者視点(厨房・提供フロー) が強く効いているように思います。 なぜリアルUIでは「運用者視点」が強くなるのか セルフレジやセルフオーダーは、UXツールであると同時に業務装置です。1人の操作ミスは、 行列停止 店員の割り込み対応 提供遅延 クレーム といった形で即座に表面化します。そのためリアルUIでは、 操作順序の固定 選択肢の制限 確認ダイアログの多用 が「正義」になるのでは推測しています。WEBの世界で嫌われがちなこれらの設計は、リアルでは 現場を守るための合理 です。 この構造は、そのまま toB SaaS に当てはまる ここで話を toB SaaS に戻します。toB SaaS もまた、 日々オペレーションを行う利用者 全体を管理する管理者・組織 という 二重構造のユーザー を持っています。 toB SaaSでよく見る失敗 「利用者にとって分かりやすいUI」を優先しすぎる 設定の自由度を上げすぎる 業務フローを柔軟にしすぎる 結果として、 管理画面が複雑化 設定ミスが多発 問い合わせが増加 運用ルールが守られない これは、 セルフレジにWEB的UI思想を持ち込んだ時と同じ失敗構造 ではと思っています。 セルフレジUIと toB SaaS UI の対応関係 ここで重要なのは、 制限=悪ではない ということです。制限は、運用を守るためのUXです。 セルフレジのUI/UXを観察して得た最大の学びは、UXとは「画面を触る個人」だけのものではないという事実です。 toB SaaSにおけるUXは、 その画面を触る人 そのデータを管理する人 その業務を回す組織 全体最適として成立して初めて良いUX になります。 UIを改善するとき、「もっと分かりやすく」「もっと自由に」と言いたくなったら、ぜひセルフレジを思い出してほしいと思います。その不自由さには、必ず理由があります。 プロダクトチームとして、この話をどう使うか ここまでセルフレジのUI/UXを題材に、リアルUIとWEB UI、そしてtoB SaaSの共通構造を整理してきました。最後に、プロダクト組織の責任者という立場から、この話をどう使ってほしいかをまとめます。 UXは「画面単体の使いやすさ」ではなく、「業務が止まらないこと」まで含めて考える 制限はUXの敵ではない。むしろ運用を守るためのUXであることが多い 利用者視点と運用者視点のどちらを優先しているのかを、常に言語化する これらは、UIレビューや仕様議論の場で、必ず立ち返るべき問いなのかなと思いました。 AIが入ることで、ネットのUI/UXはどう変わるのか 最後に、ネットのUI/UXにおいて AIが入ることで何が変わるのか について触れておきたいと思います。これは、セルフレジや toB SaaS の話とも深くつながるテーマです。 これまでのWEB UI/UXは、「人が画面を操作する」ことを前提に設計されてきました。ボタンを押し、入力欄に文字を入れ、選択肢の中から選ぶ。そのためUIは、 どの順番で操作させるか どこまで自由を与えるか どこで制限をかけるか を画面上で細かく設計する必要がありました。しかしAI、とりわけ生成AIや対話型UIが入ることで、この前提が少しずつ崩れ始めています。 UIから「操作」を減らし、UXを「対話」に移す AIが入ることで、ユーザーは必ずしも画面構造を理解する必要がなくなります。「こうしたい」「これをやってほしい」と自然言語で伝えれば、AIが裏側の操作や設定を肩代わりしてくれるからです。 これは、WEB UIを 画面中心のUX 手順中心のUX から、 意図中心のUX 結果中心のUX へと変えていきます。 それでも消えない「運用者視点」 ただし、ここで重要なのは、 AIが入っても運用者視点は消えない という点です。むしろ、AIが自由度を一気に引き上げるからこそ、 何をAIに任せてよいのか どこは人やルールで縛るのか AIの判断をどう制御するのか という設計が、これまで以上に重要になります。これは、セルフレジで「自由を削ることで現場を守ってきた」構造と非常によく似ています。AIはWEB UIを一気に自由にしますが、その裏側では 運用を守るための強いガードレール が必要になります。 toB SaaSにおけるAI時代のUI/UX toB SaaSにおいては、AIによって 設定画面が不要になる 操作フローが短縮される 専門知識がなくても使える といった変化が起きていくでしょう。一方で、 誤った設定をAIが自動でしてしまう 誰がどこまで責任を持つのか分からなくなる 組織ルールが崩れる といった新しいリスクも生まれます。だからこそ、これからの toB SaaS のUI/UXでは、 AIに任せる自由 人が守る制約 を意識的に分離して設計する必要があります。 AI時代だからこそ、セルフレジの学びは生きる AIはUIを消し去る存在ではありません。むしろ、UI/UX設計の難易度を一段階引き上げます。自由度が増えるほど、運用を守る設計が重要になる。この構造は、セルフレジやセルフオーダーがすでに私たちに示してくれています。AI時代のUI/UXを考えるときこそ、 誰のUXを守るのか どこまでをUXの責任範囲とするのか を問い続ける必要があります。 セルフレジ → toB SaaS → AI時代のUI/UX これらは別々の話ではなく、 同じ構造の延長線上にある と、私は考えています。 おわりに リアルのUI/UXは不自由です。しかしその不自由さは、現場を守り、業務を成立させるための合理です。セルフレジやセルフオーダーを観察すると、 なぜ手順が固定されているのか なぜ自由度が低いのか が腹落ちします。そしてそれは、そのまま toB SaaS のUI/UXにも通じます。UXを語るとき、「誰のUXか」「どこまでをUXの範囲とするか」を誤ると、プロダクトは壊れます。画面を触る人だけを見て設計すると、現場や組織が苦しみます。 セルフレジは、toB SaaSを作る私たちにとって、 現場視点と組織視点を同時に学べる、非常に優れた教材 です。もし今後、UI/UXの議論で迷ったときは、ぜひ近くのコンビニやファーストフード店に立ち寄ってみてください。その不自由なUIの中に、プロダクト設計の本質が詰まっています。 この記事を読んで、やラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp PdM(プロダクトマネージャー)って、企業によってやることがバラバラですよね。 「仕様書を書く人」みたいになってる会社もあれば、 「戦略を決める人」だったり、「なんでも屋」だったりする。その中で、よく出てくるモヤモヤがこれです。 顧客インタビューをしていないPdMって、本当にPdMなの? 逆に言えば、 エンジニアやデザイナーでも、顧客理解しながら動いていたらPdM的じゃない? この記事では、toB SaaS という文脈に絞って、この疑問をカジュアルに掘っていきます。 こんな方が対象: PdMを目指している人 いま PdM をやっているけどあまり顧客に会えていない人 「自分はPdMと言えるのか?」と不安になっている人 ✋ 結論から言うと… 🧩 PdMの仕事って結局こういう構造 💡 toB SaaSではなぜ顧客インタビューが必須級になるのか? 🧩 顧客理解なしのPdMが陥る罠 👀「インタビューをしているエンジニア/デザイナーはPdM行為をしている」という話 🧩 肩書きPdMと実質PdMの違い 🤔 では「なぜ顧客に会わないPdM」が生まれるのか? 🧩PdMの価値が最大になるポイント 🌱 とはいえ、「PdMが必ずインタビューすべき」とも限らない 👉私の経験上、国内toB SaaSでは“仕組みが成熟していてPdMが会わなくても回る”ケースはまだ多くありません。 🔚 最後に:PdMとは「顧客理解から逃げない人」 🧩 PdMとは何か?(一言で) 🌟 最後に PdMを目指すあなたへ ✋ 結論から言うと… toB SaaSでは、顧客接点がないと、PdMとしての意思決定が難しくなりやすい/PdM的になりづらい これは辛口でもなんでもなく、 プロダクトマネジメントの本質が “顧客理解に基づいた意思決定” だから です。 じゃあこれで決まり? いや、もちろん例外もあります。 ちゃんと後半で書きます。 🧩 PdMの仕事って結局こういう構造 上の段に行くほど “PdMである必要性が高まる” のですが、意思決定の源泉が 顧客の一次情報 である以上、 一次情報を持っていないPdMは、そもそも材料不足で判断できない。 となるわけです。 💡 toB SaaSではなぜ顧客インタビューが必須級になるのか? toCと違って、toB SaaSは 業務の中に深く入り込むタイプのプロダクト です。 つまり、顧客側の現実がめちゃくちゃ複雑。 ① 顧客の業務は会社ごとに全然違う フローも違う。 権限も違う。運用ルールも違う。 机に向かって仕様を書くより、5分の会話のほうが100倍早い。 ② 営業やCSの声は貴重だが「偏り」がある 営業:売りたい人  /  CS:困りごとを聞く人 なので、本質はその奥にあることが多い。 ③ 機能の一つが全体の業務に影響する toB SaaSでは、以下みたいなことがよくある。 承認フローが変わる 会計処理に影響する セキュリティに関わる 別部署の仕事まで変わる つまり、 「ちょっとした仕様判断」が全社レベルの業務変更につながる。 これは顧客理解なしには危険すぎる。 🧩 顧客理解なしのPdMが陥る罠 はい、負のスパイラルのできあがりです。 👀「インタビューをしているエンジニア/デザイナーはPdM行為をしている」という話 これも多くの現場で起きています。 デザイナーがユーザーに話を聞いて課題を整理してる エンジニアが仕様理解のために顧客にヒアリングしてる CS が課題を深掘りして問題の本質に迫ってる これ全部、PdMの本質的な行為です。 肩書きがなんであれ、 顧客理解を元に意思決定に影響を与えているなら、それはPdM的な動き です。 逆に、肩書きがPdMでも顧客理解がなく、意思決定していないなら、 PdM行為をしているとは言えない 。 🧩 肩書きPdMと実質PdMの違い 【肩書きPdM】 社内調整 仕様書作成 会議進行 タスク管理 → 顧客理解なしでもできてしまう 【実質PdM】 顧客理解(一次情報) 課題定義 仮説検証 優先順位の意思決定 → 顧客理解がないと成立しない あなたが見てきた「名ばかりPdM」が前者であることは多いです。 🤔 では「なぜ顧客に会わないPdM」が生まれるのか? 理由はいくつもあります。 ● PdMのロール定義が曖昧(仕様担当にされがち) ・PdMの歴史が浅い会社でよく起きる。 ● 組織として顧客インタビューの文化が薄い ・営業主導企業だと、とにかく営業が顧客を抱え込みがち。 ● 社内会議が多すぎて外に出られない ・PdMが“社内の渋滞ポイント”になっているケース。 ● リサーチャー不在でPdMが全部やる必要がある ・でも仕組みがないから後回しになる。これ、PdM個人の怠惰というより構造の問題です。 🧩PdMの価値が最大になるポイント 顧客理解 × 課題の定義 × 意思決定の責任 この3つの重なる部分が、PdMが最も価値を出せるゾーン。 顧客理解が抜けると、一気に価値が落ちます。 🌱 とはいえ、「PdMが必ずインタビューすべき」とも限らない 少し補足しておくと、例外は存在します。 ■ 顧客理解の仕組みが成熟している場合 UXリサーチャーが常に一次情報をとってくれる データ分析基盤が整っていて行動が全部見える CS Ops が定性情報を構造化してくれる こういう企業では、PdMは意思決定に集中できる。ただし、 👉私の経験上、国内toB SaaSでは“仕組みが成熟していてPdMが会わなくても回る”ケースはまだ多くありません。 だから、PdMが顧客に会う価値はやっぱり高い。 🔚 最後に:PdMとは「顧客理解から逃げない人」 肩書きがPdMかどうかより、 顧客を理解しているか 課題から逃げていないか 一次情報に触れているか 仮説と学習を回しているか ここがPdMを定義します。 🧩 PdMとは何か?(一言で) PdM = 顧客理解を起点に意思決定する人 だからこそ、 顧客に会うデザイナーはPdM的 顧客に会うエンジニアはPdM的 顧客に会わないPdMはPdM的ではない という状態が生まれる。 🌟 最後に PdMを目指すあなたへ 最後に一言だけ。 顧客と話すこと。それが PdM への最短ルートです。 経験年数より、肩書きより、スキルセットより、 一次情報に触れて意思決定していく行為こそが あなたをPdMにします。 以前にイベントで以下のような話をしましたので合わせて見てもらえればと思います。 speakerdeck.com この記事が、あなたの「PdMとは何か?」を考えるきっかけになれば嬉しいです。 ラクスの開発組織では『顧客志向』を大切にしています。 PdMはもちろん顧客を向いて業務をしていた方はラクスはオススメです。 また、本記事を読んでラクスのプロダクト部に興味を持ってくださったデザイナー / PdM の方は、ぜひカジュアル面談からご応募ください。 ※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! ●採用情報 プロダクトマネージャー career-recruit.rakus.co.jp デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
明けましておめでとうございます。 こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp 私は2014年から、仕事・プライベートを問わず、毎年個人で目標を立て、その目標をさらに3カ月単位に分解して取り組むことを続けてきました。大きな方向性を定め、短いサイクルで振り返り、修正しながら前に進む。このやり方は、今のプロダクトづくりや組織づくりにも強く影響しています。 新年最初の記事ということで、今回は昨年度の簡単な振り返りと、2026年に向けた抱負を書こうと思います。 はじめに 2025年に新しく始めたこと 2024年から継続して強化してきたこと 『穏跳』に込めた意味 PdMとして担うべきこと 1. 統合型ベストオブブリード戦略の実現 2. エンタープライズ強化 3. AIの製品活用 デザイナーとして担うべきこと 2026年に個人として取り組む目標 1. プロダクト成長・貢献 2. 組織強化・認知 穏やかに整え、次の3年へ跳ぶ はじめに 2025年のキーワードは『変革』でした。プロダクトと組織の両面で、これまでの前提を見直し、作り直す一年でした。振り返ると、主に以下のような取り組みを行ってきました。 2025年に新しく始めたこと YOUTRUSTの発信開始(フォロワー1,800突破) note「Rakus ProductManager」開始(全体20本/個人14本) デザイナー組織とPdM組織を統合し、プロダクト部へ再編 新規サービス「楽楽債権管理」を担当 2024年から継続して強化してきたこと Xフォロワー数:約1,800増、投稿数:約3,000投稿 登壇・記事露出:2024年比で約2倍(pmconf 東京・大阪に各1名登壇) PdM採用:2025年に3名採用、組織を2つに分割し新マネージャーが誕生 なお、ラクスの決算期は3月末のため、ここから書く2026年のキーワードや挑戦は、 あくまで私個人としての考え・目標 として受け取っていただければ幸いです。 2026年は、ラクスにとって 現在の中期計画から、次の中期計画(3年)へと切り替わる節目の年 です。 前回の中期計画策定時、私は入社直後ということもあり、十分に関与することができませんでした。ただ、プロダクトと組織がどのような意思決定で形づくられていくのかを、外側から見てきた3年間でもありました。 そして今回、 中期計画の策定に関与し、リードしていける立場 になりました。これは役割上の変化であると同時に、自分自身の覚悟の変化でもあります。 その決意を込めて、2026年のキーワードとして掲げるのが 『穏跳(おんちょう)』 です。 『穏跳』に込めた意味 穏やかであることと、跳ぶこと。 急激な変化や過度なジャンプは、短期的な成果は出せても、組織やプロダクトに歪みを残します。一方で、整えることだけに終始すれば、成長は止まります。『穏跳』は、 基盤を整えながら、跳ぶべきところでは確実に跳ぶ という姿勢です。 2025年の「変革」が作り直すフェーズだったとすれば、2026年は、 次の3年を見据えて、整え、意思を持って前進するフェーズ です。 PdMとして担うべきこと 次の中期計画において、PdMとして特に注力したいテーマは以下の3つです。 1. 統合型ベストオブブリード戦略の実現 単一プロダクト最適ではなく、 複数プロダクトが連携し、全体として最適な価値を提供する状態 を目指します。 それぞれが尖り続けながら、使う側から見たときには「一つの体験」として成立している。そんな統合を、戦略として実装していきます。 2. エンタープライズ強化 中堅・中小向けで培ってきた強みを活かしながら、 より複雑な業務・組織構造にも耐えられるプロダクト へと進化させます。 これは機能追加の話ではなく、 情報設計 権限設計 運用負荷の低減 といった、プロダクトの“設計思想”そのものを問う挑戦だと考えています。 3. AIの製品活用 AIを「試す」フェーズは終わり、 製品価値として組み込むフェーズ に入ります。 PdMとしては、 どこに使えばユーザー価値が最大化されるのか どこは人がやるべきなのか その線引きをし、継続的に改善できる形でプロダクトに落とし込むことを担います。 デザイナーとして担うべきこと デザイナーとしては、 UXを担い、より長く利用し続けてもらえるプロダクトづくり を担っていきたいと思います。見た目を整えることや、一時的な使いやすさだけではなく、 使い続けるほど理解が深まる 組織に定着し、業務の一部になる そんな体験を設計することが、次の3年でより重要になります。PdMと並走しながら、 課題設定 仮説検証 プロダクト全体の体験設計 に深く関与できる環境を作っていきます。 2026年に個人として取り組む目標 1. プロダクト成長・貢献 製品貢献実感:70%以上   ※アンケート内容や詳細については「 (2)製品貢献実感は、まだまだ伸びしろあり 」を参照してください。 プロダクト間連携リリースによるお客様への付加価値向上を複数件実現する 2. 組織強化・認知 社外認知 Xフォロワー数(個人):3,500 YOUTRUSTつながり数(個人):2,500 note公開数(プロダクト部):50本 社外登壇・外部媒体記事(プロダクト部):30件 穏やかに整え、次の3年へ跳ぶ 2026年は、「変革」で整えた地盤の上で、心は穏やかに、視座は高く。 闇雲に動くのではなく、静かなる自信と確固たる戦略を持って、大きく跳躍する。 組織体制やプロセス(AI活用など)で足元を「穏」やかに盤石にし、その上でプロダクトの連携や新規領域へ果敢に「跳」ねていければと思います。 一緒にこの 「穏跳」 の景色を見たい方、少しでも興味を持ってくださった  デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp おそらくこれが2025年最後の記事になるので、まずはこの一年を振り返ってみようと思います。 🗓 2025年の振り返り ■ 組織・採用まわりの変化 ■ 発信・外部登壇 外部登壇(モデレーター含む) note/ブログ記事 🏛 「文化」を構成するものは何か ハイレイヤーの意思決定の優先順位 なぜ「文化」が重視されるのか 🗣 文化は“言葉”に表れる ■ ラクスのカルチャー 🎁 採用で工夫している「4P」の話 📝 最後に 🗓 2025年の振り返り ラクスに入社して4年半。毎年さまざまな変化がありましたが、2025年は特に大きな転換点の年になりました。 ■ 組織・採用まわりの変化 デザイナー/プロダクトマネージャーが所属する プロダクト部が誕生 PdM組織が 1課→2課体制 へ、稲垣以外のPdMマネージャーが初めて誕生(内部昇格) 2025年採用(=入社) PdM +3名(リーダー・シニアクラス含む) デザイナー +7名(リーダークラス含む) ■ 発信・外部登壇 外部登壇(モデレーター含む) 稲垣単体:16回 組織全体:19回  自分はEMconfへ登壇 当社から2名が大阪・東京のpmconfへ登壇 note/ブログ記事 稲垣単体:12本(組織全体:25本)  ※2024年はほぼゼロからのスタート その他、取材対応の記事なども増加 このまま終わるのはさすがに寂しいので、「文化(カルチャー)」について、少し掘り下げて書きます。 🏛 「文化」を構成するものは何か 「文化(Culture)」=組織に根付いた考え方や行動の型。これをさらに分解すると、 風土 :文化が行動や感情レベルでどう体感されているか 人材 :文化・風土を形成する主体 この2つに行きつきます。 採用に長く関わってきましたが、特にエンジニア・デザイナー・PdMのハイレイヤーほど、 文化・人材・課題の3つ を重視し、しかもその優先順位は次のようになります。 ハイレイヤーの意思決定の優先順位 1. 誰がいるのか(People) 2. どんな課題があるのか(Problem) 3. どんな文化なのか(Culture) しかもレベルが上がるほど、 Culture > Problem >People の重み付けになっていきます。より「文化」が重要視されます。 なぜ「文化」が重視されるのか 採用の現場で多くの方と話す中で、次の理由が大きいのではと感じています。 一人では変えられない 変えるには年単位の時間がかかる 人や課題の進め方に強く影響する 文化は「知ってから入る」だけではなく、「入社してから痛感する」ものでもあります。 🗣 文化は“言葉”に表れる 私自身、これまで6社のIT企業で働いてきましたが、会社ごとに文化は全然違いました。長く働いた会社は、自分と価値観が近かったのだと思います。 tech-blog.rakus.co.jp そして文化は、 役員・マネージャーの発言、社員の口癖、日々の判断基準 に現れます。 たとえば、自分がいた会社でよく飛び交っていた言葉としてはこんなものがあります。 その目的は? 誰のため? 成果に対してやり切った? お客様はどう考える? Unless you know a better one Working Backwards 仕組化できる?誰でもできるようになってる? One-Way Door?Two-Way Door? Dive Deepしてる? Have Backbone; Disagree and Commit ラクスでも、同様に文化が明文化されており、役員やマネージャーだけでなく、社員一人ひとりが日々体現しています。 ■ ラクスのカルチャー ※ラクスの成長の源泉、「ユニークネス」・「リーダーシッププリンシプル」  career-recruit.rakus.co.jp より その中でも開発組織では「顧客志向」を価値観としておいています。 これを体現できるようにするために様々な取り組みがありますので、興味がある方は是非『「顧客志向」を実践する開発本部の取組み 』のnoteをご覧ください note.com 🎁 採用で工夫している「4P」の話 最後に、私が採用のオファー面談で意識していることを書いて締めようと思います。 面談で使う資料は、「 採用の4P 」(= 『THE TEAM 5つの法則』 にある「エンゲージメントの4P」) をベースに作っています。 Philosophy(理念・目的) People(人材・風土) Process(仕組み・方法) Performance(成果・実績) この中で「文化」は、 Philosophy と People の両面で表現できるため、明文化している内容をしっかり伝えるようにしています。 この構成に変えてから、 内定承諾率も少し向上した ように感じています。 もし採用に携わっている方がいれば、この「4P」をアジェンダとして使うのはおすすめです。 📝 最後に 本記事を読んでラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! それでは、良いクリスマスをお過ごしください! ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp はじめに 1. 成果の大きさに限界がある 2. 成果を出すための「筋力」がつかない 【1年目:インプット期】「やり方」を学ぶ 【2年目:実践・改善期】「自分で回す」 【3年目:成果・貢献期】「型」ができる 3. 年収の伸びは“過去3年の積み上げ”で決まる 4. 採用側からの見え方がポジティブに映りにくい とはいえ、「無理に3年働くべき」と言いたいわけではない はじめに 今回のテーマはすごい反響のあるこの noteのテーマについて書こうと思います。 ※本記事は筆者個人の経験・価値観に基づくものであり、ラクスの評価制度やキャリア方針を示すものではありません。 note.com 自分もかなりうなずきながら読ませてもらいました。 このnoteを読んで思い出しました。 そう言えば自分は前職で、新卒や若手メンバーにたびたび 「3年は一つの場所で成果を出すべき」 伝えていました。 もちろん、世の中には本当に劣悪な環境もあるので「逃げるな」と言うつもりはありません。ただ、一般論として“3年はやってみるべき”という考えが自分のキャリア感にも強く根付いています。 なぜかというと、初期キャリアで以下のような価値観に触れて育ってきたからです。 「石の上にも三年」という日本的価値観 自分が入社した最初の会社でも、この価値観を強く求められた(同期200人中、3年で3割が離職) 当時は「長く働く=誠実」「短期離職=忍耐不足」という評価軸が強く、今とは少し状況が違います。 それでも今、自分は 「3年間で“成果”を出す経験はキャリアの基盤になる」 と感じています。ここからは、3年未満で転職したときに起こりやすいデメリットを、自分の経験も踏まえて整理します。 1. 成果の大きさに限界がある 先の note でも触れられていますが、「主体的に動いて実績を出す」には最低でも数年単位の時間が必要です。 偉そうに書いている私自身、実は過去に3年未満で2社を辞めています。 その2社でも「自分はこれをやったぞ!」という仕事はありましたが、正直かなり小粒でした。 その後の採用面接で「こんなことを成し遂げました」と胸を張って言えるレベルかというと、疑問が残ります。 特に入社時の職能や立場にもよりますが、 製品づくりや事業成長に携わる職種において、短期間で大きな成果を出すことは構造的にかなり難しい と感じています。 2. 成果を出すための「筋力」がつかない ここで言う「 成果を出す筋力 」とは、 仕事を構造化して進める力 課題の真因を特定する分析力 関係者を巻き込み、調整する力 自ら改善点を見つけて回すPDCA力 継続して成果を出すための習慣・再現性 こうした“型”のことです。 これらは本や研修では身に付かず、実務で失敗や改善を重ねながら形成されるもの。しかも、その形成スピードは 環境 × 個人の適性 × 労働条件 × マネジメント品質 によって大きく変わります。 とはいえ、平均的にはこんなイメージになるのではと思っています。 【1年目:インプット期】「やり方」を学ぶ 業務の全体像やルール、仕組みを覚える。 基本的には先人に教わりながら動く段階。 まだ「自分で考えて成果を出す」フェーズではない。 【2年目:実践・改善期】「自分で回す」 任される仕事が増え、失敗と改善のサイクルが回り始める。 ステークホルダーとの関係構築が進む。 ようやく「再現性のある成果」への第一歩を踏み出す。 【3年目:成果・貢献期】「型」ができる 自分の強みを活かした成果が出る。 年度をまたいだ継続的な改善が見える。 * ここで初めて「再現性のある価値提供」ができるようになる。 自分自身、3年未満で辞めた2社からは学びも成長も得られました。ただ、いま振り返ると 「成果を出すための筋力」までは育っていなかった と感じます。 3. 年収の伸びは“過去3年の積み上げ”で決まる これは完全に持論ですが「 今の年収は、3年前の努力の結果 」だと思っています。多くの企業では、評価や給与改定は過去の実績をベースに決まります。 スキルや実績には「蓄積効果」があり、行動してからそれが数字としての成果になり、評価され、給与に反映されるまでには1〜2年のラグが生まれます。 つまり、 年収の本質は「現在の価値 × 過去の蓄積」。 今の年収は、 過去3年(またはそれ以上)の投資に対するリターン なのです。だからこそ「短期間で転職を繰り返す=積み上げが途切れる」ことになり、年収の伸びを自ら止めてしまうリスクがあります。 また、同じ会社に5年以上いると“ 在籍ボーナス ”が効いてくるのも事実です。おそらくこれは以下のような“ 無形の価値 ”が積みあがるからです。 業務知識の深さ 上司からの信頼の積み重ね 高難易度の仕事のチャンス 組織内での reputational capital これらは「社内」では強力な武器になりますが、転職市場にそのまま持ち出せるわけではありません。 ちなみに私は、転職時の最低希望年収を「当時の年収の2-3年前の金額」に設定するようにしています。 新しい環境で、今の年収に見合う価値をすぐに出す自信がないからです。(エージェントには「実際に下がると税金面で苦労しますよ」とかなり止められますが……) 4. 採用側からの見え方がポジティブに映りにくい 最後に、採用する側(面接官)としての客観的な視点です。 書類選考で3年未満の退職が連続している場合、どうしても以下のような懸念を抱いてしまいます。 仕事や会社を理解する前に辞めているのでは? 本人側に理由があるのか判断しにくい 入社してもすぐ辞めるリスクがある スキルの積み上げが十分ではない可能性がある もちろん面接で事情を聞けば納得できるケースも多いのですが、複数回続くとポジティブには見られません。キャリアの一貫性がない場合は、さらに印象が悪化しやすいです。 とはいえ、「無理に3年働くべき」と言いたいわけではない ここまでデメリットについて書きましたが、3年未満で転職すること自体を否定したいわけではありません。無理に3年働こうとすることで、逆に以下のようなリスクも生じます。 合わない環境で心身をすり減らす キャリア最適化は早いほどよい(特に20代は貴重) 3年後には市場の状況が変わっている可能性がある つまり、 「3年未満で辞めるデメリット」と 「無理に3年を目指すリスク」 この両天秤をしっかりと理解した上で、ご自身の「3年間」をどうデザインするかを考えるきっかけになれば幸いです。 この記事を読んで、現在の場所を飛び出そうと思った方やラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。※プロダクトマネージャーのカジュアル面談は、基本的に私(稲垣)が担当します! ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
はじめに こんにちは。楽楽販売の開発を担当しているuemuraです。 楽楽販売では11月に、初のAI機能をリリースしました。 楽楽販売をご契約いただいたお客様が導入準備をスムーズに進められるように支援する、チャット形式の機能となっています。 プレスリリースはこちら 。 本機能の開発PJは楽楽販売にとって(また私自身にとっても) 初のAI機能開発 、 初のアジャイル×スクラム開発 となっており、新しいこと尽くめでした。 AI機能を開発する難しさもさることながら、アジャイル×スクラム開発にもなかなか苦戦したため、その学びを残しておこうと思います。 目次 体制紹介 どのような流れで開発が進んだか フェーズ1:立ち上がり フェーズ2:仮説ドリブンの機能開発 フェーズ3:品質改善とリリース準備 良くなかった点 自転車操業に陥った インクリメントの品質を担保できていなかった 生成AIによるコーディングとの付き合い方 それでもスクラム開発して良かったポイント 最後に 体制紹介 従来の楽楽販売の開発の流れはいわゆるウォーターフォール型です。 プロダクトサイド(組織図の"事業本部サイド")から要求が下りてきて、エンジニアがそれを実現します。 プロダクトサイドとエンジニアは組織構造的にも離れています。 関連部署だけを抜粋した組織図 ただ、市場に対して開発スピードのUPを図るためにも、本開発ではプロダクトサイドのメンバーとエンジニアで以下のようなクロスファンクショナルなスクラムチームを組みました。 プロダクトオーナー プロダクト戦略から1人 カスタマーサクセス(CS)から1人 エンジニア4人(私はココ) スクラムマスター(所属はエンジニアサイド) ※プロダクトオーナーが2人いる体制はスクラムの原則からズレているのでオススメしません どのような流れで開発が進んだか 開発期間は2か月半ほどでしたが、ざっくりと3つのフェーズで開発は進みました。 フェーズ1:立ち上がり ターゲット顧客の設定 楽楽販売はご利用いただいているお客様の業種や企業規模が多岐にわたります。 AI機能を開発するにあたって、全てのお客様に届く機能を作るというよりは、価値を提供したいメインターゲットを決めそのお客様に使っていただくことを第一に想定して開発を進めました。 CS業務の理解 ターゲットとなるお客様群に対して、楽楽販売の契約~導入までの間にCSがどんな支援を行っているのかをエンジニアがキャッチアップしました。 主に導入支援時のCSとお客様のやり取りの録画を確認することで、ターゲット顧客への解像度も上がりましたし、導入支援時のお客様、CSのペインを知ることができました。 とりあえず動くもの作成 ターゲット顧客にどんな体験を提供するかはこの段階では未定でしたが、まずはステークホルダーにこれから作ろうとしているAIチャット機能のイメージを持ってもらうために、最低限の会話と導入支援ができるシンプルなチャットを作ることを最優先しました。 フェーズ2:仮説ドリブンの機能開発 ターゲット顧客にどんな体験を提供すればいいかは探り探りだったので、主にCSの現場目線から必要な機能の仮説を立てプロダクトバックログ化していました。 エンジニアは仮説に対するフィードバックを高速にもらうために、バイブコーディング的にとりあえず動くインクリメントを毎スプリント積み重ねてスプリントレビューで披露しました。 ステークホルダーやCSの方々、実際のお客様からもフィードバックをいただき、実運用で課題になりそうな点を集め改善を繰り返しました。 最初は順調に進んでいるように見えていましたが、だんだんと、 プロダクトオーナーは集めた課題や新たな仮説のバックログ化 エンジニアはできたてのバックログを次のスプリントレビューに間に合わせるための短納期な開発作業 に追われることになりました。 このフェーズで高速にサイクルを回したおかげで探り探りだった機能のスコープを定めることができましたが、一方で開発内部ではバイブコーディングを繰り返したことによる負債がどんどんたまっていくことになりました。 フェーズ3:品質改善とリリース準備 これまでの負債を返すように、終盤はリリース品質の確保が中心になってしまいました。 フェーズ1,2と「スプリントレビューに間に合わせるためにとりあえず動くもの」を優先したことで、リリース品質に満たないインクリメントが積み重なってしまっていました。 そういった品質負債を解消し、リリースできる品質まで持っていくことに終始することになりました。 (スクラムを始める段階で、品質改善の予定は確保していましたが、当初の計画よりずっと大きなボリュームとなってしまいました。) 品質改善に追われつつも、何とかリリースを迎えることができました。 良くなかった点 開発を進める中でいかにもスクラム初心者がハマってしまう落とし穴にハマってしまったので、簡単に改善点を振り返ってみます 自転車操業に陥った 要件が探り探りだったため、開発期間中に 仮説⇒実装⇒検証 を繰り返すことになりました。 その結果、各スクラムイベントとスクラムメンバーは互いに首を絞め合うような負のサイクルに陥ってしまいました。 プロダクトオーナーは、 毎スプリント出てくるフィードバックの整理や新たな仮説のバックログ化に追われました。 結果、 将来のための計画や先を見据えたプロダクトバックログの整備ができない状態に陥りました。 スプリントプランニングでは、 整理され切っていないプロダクトバックログがスプリントゴールに入りました。 結果、スプリントバックログも具体性に欠けたものになってしまい、スプリントが始まってからプロダクトバックログを具体化する必要がありました。 エンジニアは、 「まず動くものを作らないとレビューに出せない」という意識のもと、短期開発に追われました。 結果、品質が犠牲になり インクリメント と リリース可能な品質 の乖離 が広がっていきました。 また、エンジニアはプロダクトオーナーへの支援として技術的な面からプロダクトバックログの整理を手伝うべきだったのですが、そこまで意識や手が回っていませんでした。 結果、未整理のプロダクトバックログが積み重なったまま次のスプリントへ…という負のスパイラルに陥ってしまいました。 原因と対策 PJ終了後にスクラムチームで振り返りを行ったのですが、いくつかの原因と対策が挙がりました。 要求があいまいだった アジャイル開発という名のもと、仮説と検証を繰り返す前提で要求があいまいなままスクラムが始まりました。 プロダクトオーナーは要求の整理をしつつも、エンジニアにバックログを用意しないといけないので将来よりも目先のことに時間を使わざるを得ない状況が続きました。 アジャイルとはいえ、スクラム開始前にターゲット顧客や届ける価値の方針を固めておき、スクラム中は方針実現のための仮説・検証に注力できるようにするべきでした。 1スプリントの期間が短すぎた 1スプリントを1週間で回していましたが、一度負のスパイラルに入ってしまうとリカバリーが難しかったです。 プロダクトオーナーやエンジニアに時間的な余裕を生むためにも次回以降は1スプリントの期間をもう少し長めに置くことを検討しています。 インクリメントの品質を担保できていなかった PJ終盤はインクリメントの品質向上に追われてしまいました。 これの原因は 完了の定義 を用意していなかったことに尽きると考えています 完了の定義とは 何をどこまでやれば「プロダクトバックログを完了とする」かを定義したリストです。 具体的には、以下のようなものが挙げられます コードレビュー 機能テストに合格 単体テスト合格 統合テストに合格 リグレッションテストが作成され合格している テスト環境にデプロイされている POはユーザーストーリーの完了を確認している 引用元: スクラムにおける完了の定義(Doneの定義)とは? 完了の定義を置いておくことでリリース可能な品質を保ちながらインクリメントを重ねる効果が見込めます。 今回のPJでは「とりあえず動くものを作ってスプリントレビューに臨んでいること」にエンジニアが疑問を持つこともできていなかったので、 そういった意識レベルの改善のためにも完了の定義は必要でした。 生成AIによるコーディングとの付き合い方 上記2点の改善点の根本的な原因は アジャイル×スクラム に対する知識や準備不足であることは間違いありませんが、 個人的には、そこに 生成AIの活用方法の未熟さ も重なり、結果として課題が大きくなったと感じています。 生成AIによるバイブコーディングは「とりあえず動くもの」を作るのには強力です。 今回のPJでは 仮説⇒実装⇒検証 を繰り返すタイミングでバイブコーディングの力を借りることで、 高速にサイクルを回し、探り探りだった機能要件を定めることができました。 一方で、高速に実装できてしまうが故に負債もどんどんたまってしまったとも言えます。 どこかのタイミングでバイブコーディング的なAI活用から 「リリースを見越し、仕様や内部設計をしっかり固めながら実装するAI活用(強いて名前を挙げるなら、仕様駆動開発が近いでしょうか?)」 に切り替えるべきでした。 今回はそういった生成AIの活用方法の未熟さもあり、結果的に開発の負荷を高めてしまったと感じています。 (ただし、 仮説⇒実装⇒検証 を十分繰り返せていなければ、そもそも未だにリリースできていたかどうかも怪しいので、バイブコーディングしたこと自体が間違いだったとは思っていません) それでもスクラム開発して良かったポイント 何かと大変な点もありましたが、クロスファンクショナルなチームによるスクラム開発には明確に良かった点もあります 顧客志向が付いた ビジネスサイドの方と同じチームで働くことで、ビジネス課題や中長期の市場への向き合い方、またお客様や導入プロセスへの解像度が上がりました。 AI機能を開発をする。と最初に聞いたとき、自分のエンジニア的好奇心から「いかにもAIっぽい機能を作りたい」気持ちも生まれたんですが、スクラムチームの人と会話し、顧客の解像度を上げることで、「作りたいものを作る」ではなく顧客の困りごとを解決するための手段を考えるマインドにシフトできました。 スピード感をもってリリースできた 初の アジャイル×スクラム に苦労はしましたが、楽楽販売の従来の開発スタイルではもっとリリースまでに時間がかかっていたと思います。 世に出さないと価値は生まないので、これからもスピード感は大事にしていきたいです。 最後に この文章を書きながら 追加機能のリリースも先日行いました 。 追加機能の開発の中でも新たな課題も生まれましたが、一方で上記に挙げていた課題を一定解消することもできました。 この記事が、スクラム開発を始めようとしている人に少しでも役立てば幸いです。
アバター
目次 目次 1. はじめに 前提条件 免責 2. Application Controllerの役割 3. Application Controllerのアーキテクチャ Application Controllerの起動処理(ctrl.Run()) App Refresh Processor App Operation Processor 該当箇所 Reconciliation Loop(内部メカニズム) Phase 1: Refresh 該当箇所 Phase 2: Sync Operation 該当箇所 4. Shardingの仕組み Shardingとは? Shardingのコアメカニズム ArgoCDで選択できるシャーディングアルゴリズム Legacy 特徴 該当箇所 Round Robin 具体例 特徴 該当箇所 Consistent Hashing 特徴 該当箇所 シャードIDの決定 パターンA: StatefulSet (静的割り当て) パターンB: Deployment (動的割り当て) 5. まとめ 1. はじめに こんにちは!SRE課のモリモトです。 ArgoCDはk8sエコシステムの1つであり、GitOps基盤として多くの会社で採用されています。 ラクスのk8s環境でも採用しており、私が所属するSRE課でも運用しています。 しかし、私を含め、Argo CDの内部構造を十分に理解しないまま使っている方も多いのではないでしょうか。 そこで本記事では、ややニッチなテーマではありますがArgoCDのApplication Controllerに特化して、ソースコードをベースに内部実装を解説してみたいと思います。 前提条件 ArgoCDの全体アーキテクチャや各コンポーネント間の繋がりについては、すでにある程度理解している前提で解説します。 もしあまり理解できていないって方がいらっしゃいましたら、他の方のブログではありますが↓が大変わかりやすいのでまずこちらを読んでいただければと思います! hiroki-hasegawa.hatenablog.jp 免責 本記事の内容は v3.2.1 時点の情報に基づいています。 一部、端折って解説している部分が多々あります。正確な挙動は実際にソースコードをご確認ください。 可能な限り正確な記述を心がけていますが、誤りがあればご指摘いただけると幸いです。 2. Application Controllerの役割 まずはArgoCD全体像における立ち位置をおさらいします。 ArgoCDにおいて、Application Controllerはまさに「心臓部」と言えるコンポーネントです。 以下2つの状態を継続的に 監視・比較 し、差異が検出された場合、設定に応じて 同期 を実行します。 GitHubリポジトリ上のマニフェストが定義する「あるべき姿 (Desired State)」 k8sクラスタ上の「現在の姿 (Live State)」 3. Application Controllerのアーキテクチャ アーキテクチャ構成図は以下のようになります。(だいぶ簡略化しています) ArgoCDApplicationControllerのアーキテクチャ 主要なコンポーネントには以下のようなものがあります。 カテゴリ コンポーネント名 役割・説明 Work Queues App Refresh Queue アプリケーションの状態を確認し、ステータス更新(Refresh)を行うイベントのためのWorkqueue App Operation Queue k8s クラスタに対して実際の同期操作(Sync)を行うイベントのためのWorkqueue Project Refresh Queue Project設定の変更を反映するイベントのためのWorkqueue Processors App Refresh Processor App Refresh Queue からイベントを取得し、アプリケーションの状態確認とステータス更新(Refresh)を実行するWorker App Operation Processor App Operation Queue からイベントを取得し、k8sクラスタへの同期操作(Sync)を実行するWorker Project Processor Project Refresh Queue からイベントを取得し、Projectの設定変更を反映するWorker Core Components App State Manager Controllerの脳にあたる部分。GitHub上のマニフェストとクラスタのLive Stateを比較したり、実際に kubectl apply による同期を実行したりする内部コンポーネント Live State Cache 管理対象クラスタ内の全リソース情報をメモリ上に保持する内部のキャッシュコンポーネント Cluster Sharding Cache コントローラーが複数台構成の場合に、自インスタンスがどのクラスタを担当すべきかのマップを保持する内部コンポーネント Application Controllerは、一般的なKubernetes Controllerと同様に、「イベントをためるWorkqueue」と「実際に処理を行うWorker(goroutine)」を内部に持っています。 Applicationリソースの変更などのイベントが発生すると、まずこのWorkqueueにタスクが積まれます。 そして、WorkerがWorkqueueからタスクを取り出して実際に処理を行います。 ここで重要なのが、 Refresh処理とSync処理がそれぞれ別のWorkqueue、Workerに分かれている点 です。 これにより、特定のアプリケーションの同期処理(Sync)に時間がかかっても、他のアプリケーションのステータス更新(Refresh)がブロックされることを防いでいます。 例えば、大規模なリソース作成やPre-Sync Hookの待ちによってSync処理が長時間ブロックされた場合でも、 別レーンで動作するRefresh処理は影響を受けず、UI上のアプリケーション状態は常に最新に保たれます。 また、負荷状況に応じて、それぞれの並列数(Processor数)を個別にチューニングできるというメリットもあります。 Application Controllerの起動処理(ctrl.Run()) Runメソッドは、Application Controllerのメインエントリーポイントであり、コントローラーの起動、初期化、そして各Worker(goroutine)の起動を行います。 重要な点はワーカーの起動です。 以下のように、 statusProcessors や operationProcessors で定義した数だけWorker(goroutine)を起動し、 processAppRefreshQueueItem() や processAppOperationQueueItem() の中でキューに積まれたタスクを実行していきます。 App Refresh Processor アプリケーションの比較・ステータス更新を担当。 for i := 0 ; i < statusProcessors; i++ { go wait.Until( func () { for ctrl.processAppRefreshQueueItem() { } }, time.Second, ctx.Done()) } App Operation Processor 実際の同期処理(kubectl apply)を担当。 for i := 0 ; i < operationProcessors; i++ { go wait.Until( func () { for ctrl.processAppOperationQueueItem() { } }, time.Second, ctx.Done()) } 該当箇所 github.com Reconciliation Loop(内部メカニズム) ArgoCDの「同期」は、大きく分けて Refresh と SyncOperation の2つのフェーズで構成されています。 ArgoCDApplicationControllerのアーキテクチャ(再掲) Phase 1: Refresh イベントの検知: 「Applicationリソースの変更」や「監視対象リソースの変更」等のイベントを検知し、 App Refresh Queue にアプリケーションキーをエンキュー キューからの取得: App Refresh Processor が、 App Refresh Queue から処理対象のアプリケーションキーを取得。 アプリケーションの取得: App Informer のキャッシュからApplicationリソースを取得。 状態の取得: App State Manager が、以下2つの状態を取得。 Target State (Git): Repo Server からマニフェストを取得。 Live State (K8s): Live State Cache から現在のクラスタ上のリソース状態を取得。 状態の比較: App State Manager が、取得した2つの状態を比較しステータスを計算。 Syncステータス: Synced か OutOfSync かを判定。 Healthステータス: リソースが健全(Healthy)かどうかを判定。 AutoSync判定: ApplicationリソースがAutoSync設定で、かつ OutOfSync の場合、Applicationリソースの .Operation フィールドをセット。( .Operation フィールドが同期処理のトリガーになる) ステータス更新: Applicationリソースの Sync ステータスと Health ステータスをセットし、 Kube API Server にPatchリクエストを送信して永続化。 Refresh処理が完了した直後に、必ず appOperationQueue にアプリケーションキーをエンキュー。 8については少し補足で、以下のようにworkerの処理に defer で App Operation Queue にappKeyをエンキューするようになっています。 defer func () { if r := recover (); r != nil { log.Errorf( "Recovered from panic: %+v \n %s" , r, debug.Stack()) } // App Operation QueueにappKeyをエンキューする ctrl.appOperationQueue.AddRateLimited(appKey) ctrl.appRefreshQueue.Done(appKey) }() 該当箇所 github.com Phase 2: Sync Operation キューからの取得: App Operation Processor が、 App Operation Queue から処理対象のアプリケーションキーを取得。 アプリケーションの取得: App Informer のキャッシュからApplicationリソースを取得。 最新状態の取得: 操作を二重に行ったり古い情報で判断したりするのを避けるため、直接 Kube API Server から最新のApplicationリソースを取得。 同期処理の実行: Applicationリソースの .Operation フィールドがnilではない場合、 kubectl apply 相当の処理を実行してK8sクラスタに変更を適用。 完了後のリフレッシュ: 操作が正常に完了した場合、UI上の表示(SyncステータスやHealthステータス)を即座に最新にするために App Refresh Queue にアプリケーションキーをエンキューし、強制的なリフレッシュをトリガー。 該当箇所 github.com このように、複数のコンポーネントが相互作用することで、同期操作を実現しています。 4. Shardingの仕組み Application数や管理対象クラスタ数が増加すると、単一の Application Controller Pod では Refresh / Sync のスループットが頭打ちになります。 Processor 数を増やすだけでは限界があり、このスケール問題を解決するために導入されているのが Application Controller の Sharding(水平分割) です。 弊社でもApplication ControllerのShardingを行っています。 このShardingの中身について見ていきます。 Shardingとは? k8s Controller における Shardingとは、複数のControllerで管理対象リソースを分担して処理する手法のことです。 通常、Controllerは単一インスタンスで全リソースを watch / reconcile しますが、Shardingでは何らかの値を元に管理対象を複数Controllerに分割し、各Controllerは「自分のshard」に属するリソースのみを処理します。 これにより、以下のようなメリットがあります。 大規模クラスタでのスケーラビリティ向上 Reconcile負荷・Kube API Server負荷の分散 ArgoCD Application ControllerでもShardingを設定することができます。 ※ より詳細が気になる方は公式ドキュメントをご参照ください argo-cd.readthedocs.io Shardingのコアメカニズム ArgoCDのShardingの大きな特徴は、Application単位ではなく「デプロイ先クラスタ単位」で担当シャードが決まる点です。 つまり、「あるクラスタAにデプロイされる100個のApplication」は、全て同じ1つのControllerシャードによって処理されます。 Application ControllerのメインのReconcileループでは、各Applicationを処理すべきかどうかを canProcessApp() メソッドで判断しています。 func (ctrl *ApplicationController) canProcessApp(obj any) bool { // ... (省略) ... // Applicationのデプロイ先クラスタ情報を取得 destCluster, err := argo.GetDestinationCluster(context.Background(), app.Spec.Destination, ctrl.db) if err != nil { return ctrl.clusterSharding.IsManagedCluster( nil ) } // そのクラスタが自身の担当かどうかをチェック return ctrl.clusterSharding.IsManagedCluster(destCluster) } github.com ここで呼び出されている IsManagedCluster() メソッドが、割り当て判定の核心部分です。 // IsManagedCluster returns whether or not the cluster should be processed by a given shard. func (sharding *ClusterSharding) IsManagedCluster(c *v1alpha1.Cluster) bool { sharding.lock.RLock() defer sharding.lock.RUnlock() if c == nil { // nil cluster (in-cluster) is always managed by current clusterShard return true } clusterShard := 0 // Shardsマップには、各クラスタに対して割り当てられたシャード番号が格納されている if shard, ok := sharding.Shards[c.Server]; ok { clusterShard = shard } else { log.Warnf( "The cluster %s has no assigned shard." , c.Server) } // クラスタに割り当てられたシャード番号が、自身のシャード番号(sharding.Shard)と一致するか確認 return clusterShard == sharding.Shard } github.com このように、ApplicationそのもののID等で判定しているのではなく、「そのApplicationが属するCluster」がどのシャードに割り当たっているかを確認していることがわかります。 ArgoCDで選択できるシャーディングアルゴリズム では、その「各クラスタが割り当てられるシャード」はどのように決定されるのでしょうか。 ArgoCDでShardingする際に利用できるアルゴリズムは以下の3つです。 Legacy Round Robin Consistent Hashing Legacy デフォルトで選択される 従来型のアルゴリズムです。 このアルゴリズムは「クラスタID」を基に、そのクラスタを担当すべき「シャード番号」を決定します。 具体的には、 FNV-1a ハッシュアルゴリズムを使用してクラスタIDを数値化し、 その数値をControllerのレプリカ数で割った余りを計算して担当シャードを決定します。 特徴 項目 内容 計算式 hash(cluster.ID) % replicas 特徴 単純な剰余計算のため、ハッシュ結果の偏り次第では特定のシャード(レプリカ)にクラスタが集中する可能性がある。均等分散は保証されない。 スケーリング時の影響 レプリカ数が変わると計算結果が大きく変化するため、スケールアウト/イン時に多数のクラスタ担当が入れ替わる。そのため再配置コストが高い。 該当箇所 github.com Round Robin このアルゴリズムは、均等な分散を保証するアルゴリズムです。 全クラスタをクラスタIDでソートしたリストを作成し、クラスタIDをキーとしたインデックスマップを作成します。 そして、そのマップ(各クラスタの順位)に基づいて担当シャードを割り当てます。 具体例 以下のように、全クラスタをIDでソートし、各クラスタに連番のインデックスを付与します。 そして、 インデックス % レプリカ数 で担当シャードを決定しています。 クラスタID Index 計算式 担当シャード cluster-a 0 0 % 3 = 0 Shard 0 cluster-b 1 1 % 3 = 1 Shard 1 cluster-c 2 2 % 3 = 2 Shard 2 cluster-d 3 3 % 3 = 0 Shard 0 cluster-e 4 4 % 3 = 1 Shard 1 特徴 項目 内容 計算式 clusterIndex % replicas (clusterIndexはソート済みリスト内の順位) 特徴 各シャードに割り当てられるクラスタ数が均等になることが保証される。Legacyと異なり、偏りが発生しない。 スケーリング時の影響 クラスタの追加・削除があるとソート順が変化し、多くのクラスタで担当シャードが再計算される。結果として、クラスタリストの変更時にシャッフルが発生しやすい。 該当箇所 github.com Consistent Hashing このアルゴリズムは、ほぼ均等な分散を実現しつつ、スケーリング時の影響を最小化するアルゴリズムです。 Consistent Hashing with Bounded Loads(負荷制限付き一貫性ハッシュ)アルゴリズムを使用し、他のアルゴリズムと異なり担当するクラスタのアプリケーション数も考慮したクラスタ割り当てを行います。 このあたりは自分自身も理解しきれていない部分があるので、ふんわりとした解説になってしまいますが、、、シャード割り当てのポイントは以下です。 各シャードをリング上に配置する(コンシステントハッシングの考え方) 負荷上限を考慮して、最も負荷の低いシャードにクラスタを割り当てる クラスタ割り当て後、そのクラスタのアプリケーション数分だけシャードの負荷を更新 func createConsistentHashingWithBoundLoads(replicas int , getCluster clusterAccessor, getApp appAccessor) map [ string ] int { clusters := getSortedClustersList(getCluster) appDistribution := getAppDistribution(getCluster, getApp) // クラスタごとのアプリ数 consistentHashing := consistent.New() // 各シャードをハッシュリングに追加 for i := 0 ; i < replicas; i++ { consistentHashing.Add(strconv.Itoa(i)) } for _, c := range clusters { // 最も負荷の低いシャードを取得 clusterIndex, _ := consistentHashing.GetLeast(c.ID) // シャードにクラスタを割り当てる shardIndexedByCluster[c.ID] = clusterIndex // シャードの負荷を更新(アプリ数を加算) appsIndexedByShard[clusterIndex] += appDistribution[c.Server] consistentHashing.UpdateLoad(clusterIndex, appsIndexedByShard[clusterIndex]) } } これにより、クラスタ数だけでなくアプリケーション数まで考慮して、シャードへのクラスタ割り当てが可能になります。 また、コンシステントハッシングアルゴリズムを利用することで、クラスタの増減が発生しても再割り当てのコストを抑えることができます。 特徴 項目 内容 計算方法 Consistent Hashingアルゴリズム + 負荷上限による再配置 特徴 各シャードに割り当てられるクラスタ数がほぼ均等になる。さらに、クラスタ単位ではなくアプリケーション数も考慮するため、実際の処理負荷がより均等化される。 スケーリング時の影響 レプリカ数やクラスタ数が変化しても、影響を受けるのは一部のクラスタのみ。Consistent Hashingの特性により、再配置コストが最小限に抑えられる。 該当箇所 github.com シャードIDの決定 最後に、各Application ControllerのPodはどのようにして「自分はシャードN番である」と認識するのかについて触れておきます。 これには大きく分けて2つのパターンがあります。 パターンA: StatefulSet (静的割り当て) デフォルトの設定で利用している場合、Application ControllerはStatefulSetでデプロイされ、シャード番号はPodのホスト名から推論されます。 InferShard() メソッドがそのロジックです。 func InferShard() ( int , error ) { hostname, err := osHostnameFunction() if err != nil { return - 1 , err } parts := strings.Split(hostname, "-" ) // ... (省略) ... // ホスト名の最後のハイフンの後ろの数字をパースする //  例) argocd-application-controller-0 → 0 shard, err := strconv.Atoi(parts[ len (parts)- 1 ]) // ... (省略) ... return int (shard), nil } github.com 非常にシンプルで、 argocd-application-controller-0 ならシャード 0 、というようにPod名から決定されます。 パターンB: Deployment (動的割り当て) Dynamic Cluster Distribution 機能を利用している場合、StatefulSetではなくDeploymentでデプロイされるためホスト名がランダムになり、パターンAの方法は使えません。 この場合、 ConfigMap を使った動的な割り当てとハートビートにより、各Controllerが自律的に担当シャードを決定します。 Controllerは、起動時および定期的に argocd-controller-shard-cm ConfigMapを確認します。 自身がまだシャードを担当していない場合、「空いているスロット」 または 「ハートビートがタイムアウトしているスロット」 を探します。 条件に合うスロットがあれば、そこに自分(Pod名)を登録して担当者となります。 担当が決まった後は定期的に HeartbeatTime を更新し、自分が生存していることを知らせます。他のControllerはこの時間が古くなると、そのシャードの担当がダウンしたとみなしてスロットを奪い取れるようになります。 github.com この仕組みにより、Argo CDはStatefulSetに依存せずとも、各コントローラが一意なシャードIDを持ち、役割分担を行うことができます。 5. まとめ 今回は、ArgoCDのApplication Controllerに特化し、内部の仕組みをソースコードを読み解きながら整理しました。 まだ理解しきれていない部分が多々ありますが、少しは理解が深まった気がします。 AIの進化でコードリーディングが格段にしやすくなりましたね。私もかなり助けられました。 長くなりましたが、最後まで読んでくださりありがとうございました!
アバター
この記事は ラクス Advent Calendar 2025 の20日目の記事です。 今回は、日々の開発や運用の中で「これ意外と便利だったな」と感じた小さな改善を紹介します。 背景・課題:CI 内でタグの定義場所が散らばる 解決策:タグ指定にGitHub Actions の出力値を使う 実際の構成:タグ生成ジョブ → 他ジョブが参照する 共通化:reusable workflow 化でタグ生成を完全に中央集約 タグ生成用 workflow(共通化) 呼び出す側 (補足)docker compose にも応用可能 .envファイルを使わない・使えない場合 まとめ 背景・課題:CI 内でタグの定義場所が散らばる GitHub Actions のジョブでコンテナイメージを指定する場合、たとえば以下のように container.image を書くことが多いと思います。 jobs : test : runs-on : ubuntu-latest container : image : ghcr.io/ORG/REPO:v1.0.0 しかし、複数ジョブが存在するWorkflowだと、同じタグをあちこちで書くことになります。 更新時にすべて直すのが面倒で、修正漏れが発生しやすいのが悩みどころです。 また漏れが発生すると、テストやビルドが意図しないバージョンで動いてしまい、検知できるはずの不具合が見逃されるリスクもあります。 解決策:タグ指定にGitHub Actions の出力値を使う こうしたタグ管理の悩みを解決するために、さまざまな手段を探していました。 そんな中、別件の改善をするにあたってWorkflowドキュメントを読んでいると、 container.image にも変数が使えそうだ、ということがわかってきました *1 。 これまで漠然と container.image はベタ書きするしかないと思い込んでいたので、この発見は大きな転機でした。 実際に動作するか試してみたところ、問題なく動作したため、これを活用してタグ管理を一元化することにしました。 実際の構成:タグ生成ジョブ → 他ジョブが参照する jobs : resolve_tag : runs-on : ubuntu-slim outputs : tag : ${{ steps.resolve.outputs.tag }} steps : - name : resolve tag id : resolve run : | TAG="v1.0.0" echo "tag=$TAG" >> "$GITHUB_OUTPUT" test : needs : resolve_tag runs-on : ubuntu-latest container : image : ghcr.io/ORG/REPO:${{ needs.resolve_tag.outputs.tag }} これで test ジョブの image が resolve_tag ジョブの出力を使うようになります。 ジョブを追加するときも、 needs: resolve_tag から参照するだけです。 共通化:reusable workflow 化でタグ生成を完全に中央集約 Workflowファイル数が多い場合、resolve_tag ジョブを各ファイルにコピペするのも面倒です。 そこで、タグ生成部分を reusable workflow 化して共通化しました。 共通化という観点ではcomposite action にする方法もありますが、composite actionはジョブ内のステップとして展開されるような仕組みです。 container.image はJOBレベルでの指定なので、reusable workflow を採用しました。 ※ステップ内に展開されるcomposite actionでも対応はできますが、 outputs などJOBレベルの構造を利用側に毎回書く必要があり、共通化のメリットが薄い タグ生成用 workflow(共通化) # .github/workflows/resolve_tag.yml name : Resolve Image Tag on : workflow_call : outputs : tag : value : ${{ jobs.resolve_tag.outputs.tag }} jobs : resolve_tag : runs-on : ubuntu-slim outputs : tag : ${{ steps.resolve.outputs.tag }} steps : - name : resolve tag id : resolve run : | TAG="v1.0.0" echo "tag=$TAG" >> "$GITHUB_OUTPUT" 呼び出す側 jobs : resolve_tag : uses : ./.github/workflows/resolve_tag.yml test : needs : resolve_tag runs-on : ubuntu-latest container : image : ghcr.io/ORG/REPO:${{ needs.resolve_tag.outputs.tag }} こうすることで、タグは完全に一元管理でき、変更が楽になります。 (補足)docker compose にも応用可能 もしローカル開発用に docker compose でも同じタグを使いたい場合はタグの管理ファイルを作成すると、CIとの共通化が可能です。 例えば .env ファイルであれば、そこに記載すればdocker composeのコマンド引数を変更することなく反映できます。 .env IMAGE_TAG=v1.0.0 docker-compose.yml services : app : build : context : . args : - IMAGE_TAG=${IMAGE_TAG} Dockerfile ARG IMAGE_TAG FROM ghcr.io/ORG/REPO:${IMAGE_TAG} ... .github/workflows/resolve_tag.yml jobs : resolve_tag : runs-on : ubuntu-slim outputs : tag : ${{ steps.resolve.outputs.tag }} steps : - uses : actions/checkout@v6 - name : resolve tag id : resolve shell : bash run : | source .env # .env から読み込み echo "tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT" .env ファイルを使わない・使えない場合 .env ファイルを使わない・使えない場合、docker composeのコマンド引数を変更しない、というのは難しいようでした。 私たちのケースではdocker composeをラップするような Makefile を用意し、その中で環境変数を設定する形で対応しました。 # Makefileの機能(include, export)で環境変数を設定 include xxx.env export IMAGE_TAG docker-build: docker compose build --build-arg IMAGE_TAG=${IMAGE_TAG} まとめ GitHub Actions の needs.<job>.outputs.* は container.image に使える タグ生成ジョブを 1 箇所に置くだけで CI 全体のタグ管理が楽になる ローカル(docker compose)に応用することも可能 小さな工夫ですが、導入すると CI の見通しがかなり良くなりました。 同じ悩みを持つ方の参考になれば幸いです。 *1 : Contexts reference - GitHub Docs
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp 2025年12月4日(木)に当社ラクスの紀井が登壇し、『AI時代の「ジュニア不要論」に異議あり!未経験から戦力PdMを生み出すOJT戦略とは?』 2025.pmconf.jp というテーマで「PdM育成」について登壇しました。 そこで私も、現在、直下で1名のPdMを「再育成」していることもあり、 ラクスにおけるPdM育成のリアル を少しフィクションを交えつつお伝えします。 ■ はじめに メンバー概要 ラクスのプロダクトマネージャーの選考プロセス 採用時点の評価 ■ 入社後 → 再育成に至るまで ■ オンボーディングについて バディ体制 ■ PdM担当後に起きた課題 ■ 再育成の進め方 カリキュラム概要 1.課題図書 → レポート作成 → メンターと議論 2.社内e-learning受講 3.「自分のイシューは何か?」ワーク ■ 「自分のイシュー」課題の狙い チェックポイント(育成側) ■ 次の育成課題へ 最後に ■ はじめに メンバー概要 年齢:20代後半 PdM歴:約3年(ラクス入社前) 経歴: 新卒で大手IT企業のPdMとして入社。学生時代はベンチャーでエンジニアとして3年以上アルバイト経験。インバウンド向けのトラベル系プロダクトを担当。 ラクスのプロダクトマネージャーの選考プロセス 1. 書類選考 2. カジュアル面談 稲垣が15〜20分ほど会社と業務内容の説明 選考要素なし。その場で辞退してもOK 3. 一次面接(オンライン) 稲垣とリーダーPdMで実施 4.最終面接(対面) 部長が担当 このメンバーの採用時点での評価は以下です。 採用時点の評価 新卒PdMとして3年で一定成果を出していた ビジネス側・エンジニア側双方と連携経験あり 担当していた業務内容の整理・意義を高い解像度で説明できた (※言語化は今後の課題として認識) 行動力が非常に高く、インタビューでも多くのエピソードあり ラクスの開発組織が大事にする顧客志向で業務をしていた 複数のSaaS企業と比較した結果、 「サービスの成長性」「PdM組織の成熟度」「Biz/Dev バランス」 の3点を理由にラクスを選んでくれました。 ■ 入社後 → 再育成に至るまで 2024年夏 :入社(1か月オンボーディング) 2024年夏 :「楽楽明細」でPdM業務開始(当時は担当PdM 1名+本人) 2025年冬 :軽微な案件を担当開始。ステークホルダーとのコミュニケーション齟齬が発生  → 一部クレームもあり役割を縮小 2025年春 :リーダー判断で「一度プロダクト担当を外す」決定  → 再育成フェーズへ移行 2025年秋 :チーム規模拡大により、再育成担当者を私に変更 ラクスでは入社後1〜2ヶ月をオンボーディング期間としています。 入社半年以内に大きな成果を求めることはありません。特に入社1ヶ月目は 「会社の理解」「製品解像度の習得」 に集中してもらいます。 ■ オンボーディングについて ラクスでは「新入社員受入チェックシート(約50項目)」を進めてもらいます。 バディ体制 メインバディ :最初の業務フォローを担当 サブバディ :メイン不在時の相談先 また、1ヶ月間はマネージャーと週1〜2回の1on1を実施し、初期の6ヶ月目標もリーダー・メインバディで設定。 早期に「製品解像度」を上げる環境を整えます。 1〜2週目からはチャットや各種MTGにも参加し、 実務の情報もキャッチアップしてもらいます。多くのPdMは1ヶ月後半で 「そろそろ案件を担当したい」と意欲を見せる方が多いため、そこから軽微な案件をアサインする流れが一般的です。 今回のメンバーも同様でした。 ■ PdM担当後に起きた課題 担当後2〜3ヶ月は特に問題ありませんでしたが、3ヶ月目以降から以下のような課題が顕在化しました。 案件PRDの質 課題の解像度が粗い ステークホルダーとの認識齟齬が複数回発生 振り返ると、主因は以下の 「アサインミス」 だと判断しています。 それまでとは次元の違う難易度の案件を担当させてしまった その案件は、メインバディが担当しても難しかったレベルの内容だった PdMはステークホルダーとの信頼が生命線です。ここで信頼が揺らぐと、その後の業務が非常に進めづらくなります。そのため、本案件から外し、完全に再育成に入る決断をしました。 再育成を選んだ理由は以下です。 これまでの経験は「デリバリー寄り」で、「ディスカバリー」が弱かった 担当プロダクトは今後も高難易度案件が発生し続ける(OJTでは限界がある) ■ 再育成の進め方 ラクスでは、若手エンジニアをPdMへキャリアチェンジさせる際に 育成カリキュラム を作成しました。 今回はそれをベースに進めています。 カリキュラム概要 1.課題図書 → レポート作成 → メンターと議論 ■課題図書 『 問題解決――あらゆる課題を突破する ビジネスパーソン必須の仕事術 』 『 ロジカル・プレゼンテーション――自分の考えを効果的に伝える戦略コンサルタントの「提案の技術」 』 ■レポート内容 書籍を読んで印象に残ったことや気づき 自分ができている/できていない部分の整理 これまでの実務事例との紐付け※フォーマット自由 2.社内e-learning受講 ロジカルシンキング ラクスの事業理解 ラクスには豊富なe-learningコンテンツがあり、必須なものもあればそうでないものもあります。その中で、ピックアップしたものを受講してもらっています 3.「自分のイシューは何か?」ワーク ワーク:書籍『 イシューからはじめよ 』を読んだ上で「自分自身のイシューとは何か?」を提示せよ これは難易度が高く、平均して4〜5回のレビュー・議論を行います。 今回は当初リーダーがメンターとして実施しておりましたが、リソース逼迫により途中から私が直接担当しました。 ■ 「自分のイシュー」課題の狙い 狙いは以下の4点です。 1. ディスカバリーには「なぜ?」を問う力が重要 2. 多くの課題を見つけても「イシュー」を特定しないことにはディスカバリーできたとは言えない 3. 自分自身を対象にするため、他者への迷惑がかかりにくい 4. 成長の鍵である「メタ認知」を強化できる 育成側・育成される側双方に「評価基準」を最初から提示しています。  ※育成される側に徐々に開示している チェックポイント(育成側) 本人なりの「あるべき姿」が定義されている 単なる現状改善ではなく、自分の頭で考えた言語化になっている 「現状」と「あるべき姿」のギャップが構造化され、MECEになっている 課題と問題が区別できている 一次情報を元に事実と解釈を分離できている 最終的なイシューが以下を満たす 本質的な選択肢である(解決すれば価値が出る) 答えを出せる(解決策を示せる) イシューまでの流れがストーリーとして一貫している ここまで読んでお気づきの方もいると思いますが、 これはそのまま 「プロダクトマネジメント」 です。 つまり、 「自分自身」をプロダクトと見立てて、プロダクトマネジメントしてもらう というのが本課題の本質です。 ■ 次の育成課題へ このメンバーは10月に本課題をクリアしました。業務と並行しながら 約3ヶ月で完了 した点は素晴らしい進捗だと思います。 次に取り組んでもらっているのは—— 「PdM・デザイナーの業務をプロダクトマネジメントせよ」 具体的には、 PdM/デザイナー業務の中から課題を発見し、それを解決することでチームに貢献する というテーマです。 この課題の狙いは以下の3点です 1. 「自分のイシュー」より範囲が広くなり、難易度が格段に上がる 2. 成功すればチーム貢献となり、関係者からの信頼を獲得できる 3. チーム内でバラバラに取り組んでいたAI活用・生産性改善の棚卸しができる 私が育成を直接見ているため、難易度調整も可能で、成功確率を高められます。再育成を担当してまだ2ヶ月ほどですが、今の課題を確実にクリアし、2026年4月からは高いレベルで実案件に戻ってほしいなと思っています。 最後に 採用・オンボーディング・育成について紹介しましたが、 ラクスは「人への投資」を非常に大切 にしています。 また、記事を読んでラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
この記事は ラクスアドベントカレンダー2025 17日目 の記事です。 はじめに こんにちは! エンジニア3年目のTKDSです! 今回はgo-gitについて紹介します。 はじめに go-gitとは 基本的な操作 応用的な操作 実例 差分があったファイルのみSQL実行 まとめ go-gitとは https://github.com/go-git/go-git goで書かれたgit操作用ライブラリです。 git操作とGoコードを組み合わせかけるので、非常に汎用性が高く便利です。 基本的な操作 まず基本的な操作を紹介します。 今回はPublicリポジトリなので、認証部分は省いてます。 一部抜粋して載せてきます。 全文は リポジトリ に載せてあるので、main.goにコピペして実行してください。 clone リモートリポジトリをcloneする操作は以下のとおりです。 r, err := git.PlainClone( "poc/" , &git.CloneOptions{ URL: "https://github.com/tkeshun/go-git-poc" , Progress: os.Stdout, // 進捗を表示したくなければ nil でOK }) これを使って、cloneしてからlogを表示するコードを実行すると以下のようになります。 add addしてステータス確認 if _, err := wt.Add(addpath); err != nil { slog.Error( "Add Error: " + err.Error()) return } st, _ := wt.Status() print (st.String()) addして取り消すコードを実行すると以下のようになります。 なぞの文字列とファイル名が表示されてますが、Aがadd, ??がステージング前を表しているようです。 commit commitする操作は以下のとおりです。 if _, err = wt.Commit( "test" , &git.CommitOptions{ Author: &object.Signature{ Name: "go-git-invalid" , Email: "" , When: time.Now(), }, }); err != nil { slog.Error( "commit Error: " + err.Error()) } commitするコードを実行するして git log でみるとcommitが追加されてるのがわかります。 このcommitを消したいときはheadを親commitに戻して、indexをリセットすれば良いです。 ※ Errorハンドリング書くのめんどくさくなって省略してますが、実際はだめです!!! headRef, _ := r.Head() headCommit, _ := r.CommitObject(headRef.Hash()) parent, _ := headCommit.Parent( 0 ) wt.Reset(&git.ResetOptions{ Commit: parent.Hash, Mode: git.MixedReset, }) さっきのcommitが消えて親commitに戻ってます。 ここまででよく使う操作は紹介できました。 push(※番外) 一応公式のexampleにPushもあります。 現状私があまり必要としてないため飛ばします。 興味がある人は 公式example みて試してみてください。 このように、goコード上からclone, add, commit, pushなどの基本の操作が可能です。 応用的な操作 次に応用的な使い方です。 指定したcommitハッシュ間の比較 Hash値を指定して比較することで差分の比較が可能です。 ファイル差分を単純に比較することや差分内容を取得することができます。 commitが隣合わせでなくても差分は出すことができ、1commitで複数のファイルをcommitしていたとしても1ファイルずつ差分を取り出せます。 t1, _ := r.CommitObject(commit1Hash) t2, _ := r.CommitObject(commit2Hash) p, _ := t1.Patch(t2) この3つの処理で差分を取り出せ、 p.FilePatches() に差分が入ってるので、それをループで回すとファイルごとに2点のcommit間の差分を取り出せます。 for _, fp := range p.FilePatches() { } 色々試したサンプルを載せおきます。 package main import ( "fmt" "log/slog" git "github.com/go-git/go-git/v6" "github.com/go-git/go-git/v6/plumbing" ) func main() { r, err := git.PlainOpen( "./poc" ) if err != nil { slog.Error(err.Error()) return } commit1Hash := plumbing.NewHash( "cacf82fd8a097b8266fb7a7c84f02a9a1a599a4c" ) commit2Hash := plumbing.NewHash( "b20da7389753009b3cd6be1ae0cc66e5551e8916" ) t1, _ := r.CommitObject(commit1Hash) t2, _ := r.CommitObject(commit2Hash) p, _ := t1.Patch(t2) print (p.String()) for _, fp := range p.FilePatches() { from, to := fp.Files() if from != nil { f := from.Path() fmt.Println(f) } if to != nil { t := to.Path() fmt.Println( "file: " + t) } for _, c := range fp.Chunks() { fmt.Println(c.Content()) } } } ブランチ間の比較 Reference関数でブランチとハッシュが取れるので、Reference関数が要求する plumbing.ReferenceName を plumbing.NewBranchReferenceName を作ります。 Reference関数を使うと返り値で *plumbing.Reference が取得できます。 *plumbing.Reference.Hashを使うとハッシュ値が取得できるので、あとは同じようにcommitハッシュの差分をとれば、ファイル差分の取得ができます。 r, err := git.PlainOpen( "./poc" ) if err != nil { slog.Error(err.Error()) return } ref1, err := r.Reference(plumbing.NewBranchReferenceName( "main" ), true ) if err != nil { slog.Error(err.Error()) return } fmt.Println(ref1) ref2, err := r.Reference(plumbing.NewBranchReferenceName( "test" ), true ) if err != nil { slog.Error(err.Error()) return } // あとは同じ 最新版と旧版の比較 fetchと組み合わせて使うことで、現在あるremoteの状態と最新のremoteの状態の比較が可能です。 コードは以下のようになってます。 package main import ( "fmt" "log/slog" git "github.com/go-git/go-git/v6" "github.com/go-git/go-git/v6/config" "github.com/go-git/go-git/v6/plumbing" ) func main() { r, err := git.PlainOpen( "./poc" ) if err != nil { slog.Error(err.Error()) return } remoteRefName := plumbing.NewRemoteReferenceName( "origin" , "test" ) beforeRef, err := r.Reference(remoteRefName, true ) if err != nil { slog.Error(err.Error()) return } beforeHash := beforeRef.Hash() err = r.Fetch(&git.FetchOptions{ RemoteName: "origin" , RefSpecs: []config.RefSpec{ "+refs/heads/test:refs/remotes/origin/test" , }, }) if err != nil && err != git.NoErrAlreadyUpToDate { slog.Error(err.Error()) return } afterRef, err := r.Reference(remoteRefName, true ) if err != nil { slog.Error(err.Error()) return } afterHash := afterRef.Hash() if beforeHash == afterHash { fmt.Println( "diff: no changes" ) return } beforeCommit, err := r.CommitObject(beforeHash) if err != nil { slog.Error(err.Error()) return } afterCommit, err := r.CommitObject(afterHash) if err != nil { slog.Error(err.Error()) return } patch, err := beforeCommit.Patch(afterCommit) if err != nil { slog.Error(err.Error()) return } for _, fp := range patch.FilePatches() { _, to := fp.Files() if to != nil { t := to.Path() fmt.Println( "file: " + t) } for _, c := range fp.Chunks() { fmt.Print(c.Content()) } } } 実行してみると下記画像のようになります。 2回目はもう更新済みなのでdiffがでません。 このように、単純なcommit間の差分、ブランチ間の差分、最新化した差分など様々な差分が取得できます。 実例 使用例を出してみました。 以前、登壇したことのある発表で出したツールの簡易版です。 差分があったファイルのみSQL実行 応用例で紹介したように、現状のHeadのcommit Hashを保持しておいてfetchをしたあとまたHeadのcommitを取得することで、更新差分が取得できます。 これを使うと、ブランチに新しく追加されたファイルを検知し、実行することができます。 コードは長いのでリポジトリをみてください。 差分検出にsql実行部分をくっつけます。 commitとpushして実行します。 init.sqlは以下のようになってます。 CREATE TABLE IF NOT EXISTS demo_items ( id bigserial PRIMARY KEY, name text NOT NULL UNIQUE , note text NOT NULL DEFAULT '' ); go run main.goで実行したあと、DBにつないで実行するとテーブルが作成されていることがわかります。 今回は単純な実行コードですが、もう少し作り込むならSQLファイルの内容チェックなどがあったほうがよいですね。 insertとselectも順番にやってみましょう 投入と検索もうまくいきました。 もし差分検知して自動実行したい場合は、これを定期起動すれば差分の自動実行もうまくいきそうです。 まとめ ここまでみていただきありがとうございました。 今回はgo-gitの紹介と簡単な実用例を紹介しました! 実際に社内用のプロダクトでも使われており非常に便利なライブラリです。 この記事でgo-gitに興味持った人は、ぜひgitを活かして色々やっててみてください!
アバター
こんにちは。株式会社ラクスで「楽楽債権管理」のPdMをしている柴、PMMの江良です。 「楽楽債権管理」は 請求(債権)データと入金データの照合・消込を自動化するクラウドサービスで、入金の過不足、まとめ入金、名義違いといった実務で頻発する複雑なケースにも対応します。消込後の仕訳データも自動生成し、手作業では時間のかかる債権管理業務を正確かつスピーディーに進められるようにします。 https://www.rakus.co.jp/rakurakucloud/saikenkanri/ 「楽楽債権管理」は2025年7月、「楽楽明細」のオプション機能からスピンアウトし、単独プロダクトとして販売を開始しました。単独化によって事業のポテンシャルは大きく広がりましたが同時に、ステークホルダーの増加や組織の拡大により、立ち上げ期のような少人数の阿吽の呼吸だけではスケールしなくなりました。 そこで必要になったのが、体系的で再現可能なプロダクトマネジメント、そしてチーム全員が「同じ地図」を持って進むための仕組みでした。この記事では、「楽楽債権管理」が新たなフェーズに向かう中で、どのようにその「地図」が整備され、プロダクトと組織が変わっていったのかをご紹介します。 このnoteは、プロダクトの立ち上げ期〜成長前夜にいるチームに向けて書いています。戦略や役割がまだ曖昧で、「自分たちはどこへ向かっているのか」「どうやって再現性のある組織を作るのか」に悩む方に、少しでも参考になれば幸いです。 立ち上げ期にあった課題感 役割を明確化し、組織の“ハブ”をつくる PdMは「開発・デザイン」のハブ PMMは「営業・CS」のハブ 戦略設計を「共通言語」に 顧客の声(VoC)の仕組みづくり データ活用による顧客行動の可視化 ロードマップを“見える化”する協働 おわりに 採用情報 立ち上げ期にあった課題感 単独化前後のチームには、次のような課題がありました。 データが整備されておらず、お客様の課題や利用状況を定量的に把握できない 戦略がなく、商談でよく出る要望を“積み上げ式”に対応していた チーム内の役割が曖昧で、意思決定の軸が人によって異なっていた こうした状況では、「プロダクトとしてどこへ向かうべきか」も「組織としてどう動くべきか」も不明瞭になります。ここから、再現性のある意思決定・役割分担・戦略共有に向けた取り組みを進めていきました。 役割を明確化し、組織の“ハブ”をつくる これらの課題に向き合ううえで、最初に取り組んだのが役割の整理でした。組織が拡大する中で、誰がどの領域をリードするのかを明確にすることも重要な課題でした。役割が曖昧なままでは情報が滞り、意思決定が遅くなってしまうためです。ラクスでは職能別組織体制を採用しており、それぞれが持つ専門性を最大限に活かしながらプロダクトを前に進めています。一方で、職能が分かれているからこそ、情報をつなぐためのコミュニケーションコストが一定かかるという側面もあります。そこで「楽楽精算」や「楽楽明細」と同様にPdMとPMMは役割を明確にし、“分断”ではなく“補完”として機能するよう再定義しました。 PdMは「開発・デザイン」のハブ エンジニアとの仕様検討 デザイナーとの構造・UI議論 技術的制約と顧客価値のバランス調整 PdMはプロダクトを「どう作るか」の中心に入りこみ、ビジネスの意図を開発に翻訳する橋渡し。 PMMは「営業・CS」のハブ 商談やデモからの学び CSが拾った利用実態や課題 市場動向・競合比較 PMMは顧客や市場のリアルをプロダクトへ返し、現場とプロダクトの温度差をなくす存在。 このハブ構造により、開発/デザイナー ←→ PdM ←→ PMM ←→ 営業/CSという情報の流れが生まれ、意思決定が早く、滞留しない組織へ変わりました。 戦略設計を「共通言語」に 最初に取り組んだのは PMF(Product-Market Fit)の定義 でした。当時は「PMFを目指す」という言葉だけが独り歩きし、何をもって達成と言えるのかチーム内で解釈がバラバラでした。そこで他プロダクトの状況や事業状況、プロダクトの利用データを整理し、いくつかの定量指標を設定。チーム全員が “PMFとは何か” を同じ言葉で語れる状態 にしました。さらに ICP(Ideal Customer Profile)を定義 し、「誰に価値を届けるのか」を明確化。業種、規模、利用システム、入金件数などの基準でターゲットを整理することで、ロードマップ、優先順位、マーケ施策の判断軸が一貫するようになりました。戦略を明文化したことで、議論のスタートラインが揃い、チームとして“同じ方向を向く”状態を作りました。 顧客の声(VoC)の仕組みづくり 次に取り組んだのが、顧客の声(VoC)を意思決定に活かす仕組み です。私たちはLookerによるデータ可視化に加え、VoCを毎月1回、PdMとPMMがあえて手動で整理する運用を取り入れています。自動集計ではなく全件に目を通すことで、数値化しづらいニュアンスや深層の課題を捉えられるためです。 VoCとPBIはすべて Notionで一元管理。PdM・PMMはもちろん、開発・営業・CSまで誰でもリアルタイムにアクセスできます。さらに、RICEスコアを参考にVoCを以下のような軸でスコアリングしています。 顧客規模 課題の頻度 代替手段の可否 その課題が事業成長に与える強さ(失注や解約につながる課題か) ICPとのマッチ度 定量的に整理することで、「大口顧客の1件の声」と「中堅顧客の複数の声」を公平に比較でき、単なる“要望リスト”ではなく戦略と一貫したロードマップに落とし込めるようにしています。 データ活用による顧客行動の可視化 VoCとは別に、実際の利用行動データから顧客の状態を捉える仕組みも構築しました。お客様ごとの利用ログを取得し、主要機能の利用状況を業務フロー単位で可視化。これによって、 主要業務がどれだけ実施されているか どの機能が活用されていないか どの工程で“詰まり”が生じているか といったプロダクトの実態が明確になりました。さらに、これらをスコアリングし利用が停滞しているお客様を自動検知。CSチームが能動的にフォローできる体制が生まれました。「ログの可視化 → スコアリング → CSアクション」の流れによって、プロダクトの価値をお客様の業務に定着させる循環を作りやすい仕組みにしています。 ロードマップを“見える化”する協働 ロードマップ策定は、PdM・PMMに限らず営業・CS・開発まで巻き込みながら進めています。 セグメントごとのニーズ整理 競合比較 商談だけでは見えにくい非定量な課題感の抽出 顧客価値の大きい領域を優先する判断 機能開発における規模や難易度 上記のような情報を現場から吸い上げ、プロセスを透明化することで、「このタイミングで、この顧客セグメントに、こういう価値を届ける」という共通理解が生まれ、プロダクトの方向性が揺らぎにくくなりました。 おわりに こうした取り組みの積み重ねにより、PdMとPMMが互いの領域に自然と“越境”しながら協働する文化が育ってきました。役割は分担しつつも、共通言語と共通の地図を持ち、チーム全体でプロダクトを育てる文化へと変化しました。 楽楽債権管理は今まさに成長フェーズへ踏み出しているプロダクトです。その中で積み重ねてきた仕組みと文化は、これからステークホルダーが増えても迷わず進んでいくための土台になると信じています。プロダクトが変わると組織が変わり、組織が変わるとまたプロダクトが変わる。この循環をこれからも回し続けていきたいと思います。 採用情報 career-recruit.rakus.co.jp
アバター
こんにちは、プロダクト部 部長の稲垣です。(自己紹介やこれまでのキャリアについて↓をご覧ください。) tech-blog.rakus.co.jp ■ 「Noを伝える技術」の感想 ■ イエスと言って「誠実に合意形成する」ための技術とは? ① まずは「強いイエス」を返す ② ただし、本当にやるのは「徹底的なリサーチ」 ③ そのまま“事実として”レポートする ④ 相手は自然と「今回はやめるか」と判断する ■この技術の良いところ 📝 まとめ:本気で向き合う姿勢が、結果的に“ノーを機能させる”あるいは“イエスのコミットメントをあげる” ■ 「Noを伝える技術」の感想 先日出版された 『 Noを伝える技術 プロダクトマネージャーが教える「敵を作らずに断れるようになる」作法 』 について触れたいと思います。 この書籍は、PdM界隈では知らない人はいないと言われる「Noを伝える技術 #pmconf2021」の登壇内容をもとにしています。 speakerdeck.com ただし登壇は20分、スライドは25枚ほど。 一方で書籍は、内容がより広く深く、そしてプロダクトマネージャーに寄り添った実践的な話が多く収録されています。 私は2021年8月にPdMを名乗り始め、pmconfを知ったのは2023年頃。登壇の存在もそれと同時でしたが、それだけ有名で、そして現場で刺さる内容です。本書で印象的だった一文があります。 『良かれと思ってやったこと』が積み重なった結果、まったく望まない方向に進んでしまうことがある 私はこれを何度も見てきましたし、自分自身も経験してきました。 だからこそ「Noを伝える」技術は、どの会社・どの立場でも必要で、多くの人が“心ではNO”と思っていることを言語化できるようになることは大きな価値があります。 ちなみに私は、読書の際に必ずメモを取るのですが、この書籍は2025年に読んだ約100冊の中でも、かなり上位のメモ量でした。 最近はこのメモをまとめて社内展開することで、書籍を読む文化を作ろうとしています。…と、思ったより書籍の話を書いてしまいましたが、ここからが本題です。 私自身が現場で使ってきた“イエスと言って「誠実に合意形成する」ための技術” を紹介します。 タイトルにもある通り、私がよく使うのは—— まずは「イエス」と言う。 そして、事実を積み上げることで"イエス”か"ノー"か相互に合意形成をしていく方法です。これを進めると自分から“ノー“と言う前に相手が“ノー“というケースもあります。 という方法です。 ■ イエスと言って「誠実に合意形成する」ための技術とは? 前職はわりとハードなベンチャーでは「イエスかハイかどっち?」と言われるような環境でした。ベンチャーでは珍しくない話ですが、この環境では「ノー」を直接言うと、話がややこしくなることがあります。 特に相手が強い自信を持っている提案や依頼に対して、正面からノーを言うのは得策ではありません。 ひどい場合、もしその後うまくいかないと「最初にノーと言ったせいだ」「本気でやっていなかった」とさえ言われかねません。 そこで生まれたのが、次の方法です。 ① まずは「強いイエス」を返す 相手が自信満々のときに正面からノーを言っても勝てません。なので私はまず、あえて 力強いイエス を返します。 ✔ 「いいですね、やりましょう」 ✔ 「確認します。まず真剣に調べます」 “乗り気である”姿勢を見せることで、相手はこちらを信頼し、前向きに期待してくれます。 ② ただし、本当にやるのは「徹底的なリサーチ」 イエスと言ったからには、最初に 本気でリサーチ します。 経営者の直感はまとはずれなことは少ないです。 調べてみると、想像以上に価値があることも多い。しかし、深掘りしていくと… 大きなリスク 想定外の障害 無謀さを示すエビデンス が出てきます。 ③ そのまま“事実として”レポートする ここが最重要ポイントです。私は、そのリサーチ結果を淡々と報告します。 ✔ 依頼を本気で実行しようとした際に見えてきたリスク ✔ プロジェクトを阻害する障害 ✔ 実行時の工数・コスト・競合状況 ✔ 成功確率が低い理由(エビデンス付き) “やらない理由”ではなく “本気でやるにはここが問題です” というスタンスで伝えるのが大事。 ④ 相手は自然と「今回はやめるか」と判断する ここまで来ると相手は、 「これは一度立ち止まったほうが良いかもしれない」 と 自分自身で判断 してくれます。つまり、私がノーと言うのではなく、 相手がノーと言ってくれる のです。 しかも、こちらは“最初にイエスと言っている”ので、関係性にマイナスが一切ありません。 むしろ信頼が高まります。また、仮にそれでもやるとなった場合には、最初に「ノー」と言わず進めるべきものだったのだと思います。 ■この技術の良いところ ① 相手との関係性が傷つかない 最初に前向きに受ける姿勢を示すので、否定の空気が残りません。 ② 事実ベースの議論になる 感情論ではなく、リスク・データ・実態という“事実”で判断できます。 ③ 意外な「価値の芽」にも出会える 本気でリサーチすると、 「これ、いけるじゃん」「思ったより価値あるな」というケースもあります。 ④ 結局は“やるべきこと”だけが残る 取り下げられなかったものは、本当にやる価値があるものだったと後からわかる。 📝 まとめ:本気で向き合う姿勢が、結果的に“ノーを機能させる”あるいは“イエスのコミットメントをあげる” 私はこの方法で、多くの場面を乗り切ってきました。 そして副産物として、依頼者からの信頼が下がることはありませんでした。 どんな提案でも本気で向き合う。そして事実で会話する。 この姿勢が、もっとも健全で、もっとも誠実な「ノーの伝え方」になると感じています。 ぜひ皆さんも、機会があれば試してみてください。 また、記事を読んでラクスのプロダクト部に興味を持ってくださった デザイナー/PdM の方 は、ぜひカジュアル面談からご応募ください。 ●採用情報 デザイナー career-recruit.rakus.co.jp   └ デザインマネージャー career-recruit.rakus.co.jp /アシスタントマネージャー career-recruit.rakus.co.jp
アバター
この記事は ラクス Advent Calendar 2025 12日目の記事です。 はじめに こんにちは。ラクス フロントエンド開発課のたぐちです。 今回は、私たちフロントエンド開発課で取り組んでいる 学習文化の醸成に関する取り組み をご紹介します。 はじめに 取り組み内容 情報共有会の良いところ 1. 学習のハードルが下がる 2. メンバーの価値観や思考を知る機会が増える 3. 気になる技術をその場で試せる 今後の改善ポイント 1. ファシリテーターが固定化している問題 2. 記事を投稿するメンバーが偏りがち おわりに 取り組み内容 私たちのチームでは Notion を活用し、メンバーが日頃気になった記事や情報を随時ストックしています。 その中から、他のメンバーも気になった記事に「マーク」が付いたものを選び、 週次の情報共有会(1時間) で内容を確認したり議論したりしています。 記事共有の様子 以前は隔週開催としていましたが、最近は AI をはじめ技術の変化が非常に速いため、 「隔週ではキャッチアップが追いつかない」という声を受け、 毎週開催 へと変更しました。 また、Notion に追加された記事は Notion と Gemini の API を活用し、チャットツールへ要約を通知しています。 記事要約の例 情報共有会の良いところ 1. 学習のハードルが下がる 「エンジニアは日々の勉強が大事」というのは多くの人が感じていることだと思いますが、常に最新情報を一人で追い続けるのは、慣れていないと負担が大きいものです。 特にフロントエンドは移り変わりが激しいと言われていますが、AI はそれをさらに上回る速度で変化しています。追従しようとすると目が回りそうになります(私だけでしょうか…?)。 そこで、チーム全体で学習を共有する場をつくることで、 個々がキャッチアップし続ける負荷を抑えながら学習を進められる ようになっています。 2. メンバーの価値観や思考を知る機会が増える 業務中にも相談や議論はありますが、 開発とビジネスの距離をどう縮めるか 不要な機能の削減(いわゆる「機能の引き算」)をどう進めるか プロダクトの本質的な価値をどう捉えるか といった根源的なテーマを語り合う機会は意外と多くありません。 情報共有会ではジャンルを限定していないため、記事によってはこうしたテーマに踏み込んだ 普段は出てこない深い議論 が生まれることもあります。メンバーの考えを知る貴重な場になっています。 3. 気になる技術をその場で試せる 記事の確認が早く終わった場合は、紹介された内容を実際に触ってみる時間にしています。 たとえば、 新しい MCP Claude の Skills 機能 Playwright Test Agents など、多岐にわたります。 記事を読むだけでなく、その場で手を動かすことで理解が深まり、議論もより活発になる と感じています。 今後の改善ポイント 1. ファシリテーターが固定化している問題 現在は言い出しっぺである私がずっとファシリテーターを担当していますが、 私が休むと運用が止まってしまう メンバーがファシリテーションを練習する良い機会になる という理由から、 今後はローテーション制にしたい と考えています。 2. 記事を投稿するメンバーが偏りがち フロントエンドや AI 関連など、全員が興味を持ちやすい領域では記事が被りやすく、結果として毎週ほぼ同じメンバーが投稿してしまいがちです。 解決策としては、たとえば その週に投稿していない人の意見をファシリテーターが優先して聞く 投稿されていないジャンルを意識的に拾う といった工夫が考えられ、より多様で活発な議論につながるのではないかと思っています。 おわりに 以上、私たちの学習文化の醸成に関する取り組みをご紹介しました。 「受験は団体戦」とよく言われますが、 エンジニアリングの学習もチームで取り組むことでハードルが下がり、継続しやすくなる と実感しています。 もし技術の移り変わりの速さにキャッチアップが追いつかないと感じている場合は、ぜひチーム内で試してみてください。
アバター
はじめに 請求書の明細表をOCRによって自動で読み取ることができると、経理業務自動化の実現に役立ちます。 ところが実際には、多様なフォーマットの存在や OCR の誤読が積み重なり、AIモデルとルールベース後処理だけでは思った以上に精度が出ない、という壁にぶつかることがあります。 AI モデルそのものの改修となると、学習データの追加やモデル更新など、時間もコストも必要になります。また、ルールベース後処理を増やし続けるのは、将来の保守を重くする心配がつきまといます。 そこで、「大規模言語モデル(LLM)を後処理に加えたら、より柔軟に・より構造的に・まるで人間が行うように誤りを補正できるのではないか」という発想のもと、既存処理に GPT による補正ステップを追加し、精度改善ができるか検証しました。 はじめに LLM による後処理の追加 追加した処理の流れ GPT に与えたプロンプト 検証結果 まとめ LLM による後処理の追加 今回の検証では、ルールベース処理の“あと”に GPT による補正ステップを入れています。 使うのは画像入力に対応した gpt-4.1。表画像と OCR 結果(CSV)を突き合わせ、表構造と内容の整合性を取る役目を担います。 追加した処理の流れ 具体的にGPT が担うのは次のような作業です。 明細表の画像と既存処理の OCR 結果(CSV)を受け取る 画像と CSV を突き合わせ、 行・列のズレ修正 分割/結合ミスの調整 欠損・誤認文字の補正 修正済み CSV を返却 座標情報の扱いは精度を安定させるのが難しく、今回は見送りましたが、将来的には実装したい部分です。 GPT に与えたプロンプト 今回の補正処理は、次のような指示文(抜粋)を与えて実施しました。 表構造の統一、セル内容の整理、誤認識文字の補正など、発生しやすいゆがみを吸収するよう工夫しています。 refine_table_prompt = """表のOCR結果が次のようにCSV形式のテキストで与えられています。 {} 同時にOCR対象となった表の画像も与えられています。 表の画像を参照しながら、CSV形式のテキストを修正して下さい。 その際、以下の条件に従って修正を行って下さい。 条件: 1. 画像内の表の構造に基づいてCSV形式の行や列の構造を修正して下さい。 2. すべての行で同じ列数になるように修正して下さい。 3. 「||」は改行箇所を示しています。セル内の改行は「||」で表現してください。 4. 誤って複数行が1つのセルに入っている場合は、適切に分割してください。 5. ~ """ 検証結果 検証データ 165 件 を使い、 既存処理のみ GPT 補正を追加した処理 の比較を行いました。 結果として、 38 件で明確な精度向上が確認されました。 特に目視では、 列境界の誤りが正しく補正される 1 件の明細データが複数行に分離される誤りの統合 誤った行分割の修復 など、「人が見たらこう直したい」という形にかなり近づく改善が見られました。 一方でデメリットもあり、 コスト:検証データでは 1 リクエストあたり0.4~0.6 円(明細表の内容による) 処理時間:1〜5 秒 程度の追加(これも明細表の内容に依存) 後処理ルールをメンテナンスし続けるよりは負担が軽い?ものの、低コストの明細表読み取り実現やリアルタイム処理には少し工夫が必要かと思われます。 まとめ LLM を既存の OCR パイプラインに組み合わせることで、“ルールで吸収しにくい構造的なズレ” を補正でき、一定の精度向上が確認できました。 特に、 多様な書式の請求書を扱う ルールのメンテが重くなっている といった課題に対して、相性の良い手法かと思います。 一方で、「コストと処理時間の増加」という課題もあるため、実運用にはもうひと工夫が要りそうです。 今後は、座標情報を扱えるように処理を改修したり、より高性能なモデル(gpt-5.1のような推論モデル)を利用したりすることで、明細表読み取り処理をより優れたものにしていければと考えています。
アバター
※この記事は ラクスアドベントカレンダー 2日目の記事です はじめに こんにちは! エンジニア3年目のTKDSです! 今回はEKSでOtel Collectorを用いたContainer Insightsメトリクスの取得についてご紹介します。 ぜひ最後まで見ていってください! はじめに Container Insights Otel Collectorによるメトリクスの収集 Otel Collectorのマニフェスト例 まとめ Container Insights Container Insightsを使うとEKSクラスタからnodeやPodなどからCPU、メモリ、ディスク、ネットワークなどさまざまなメトリクスが取得可能です。 ※詳しくは AWSのドキュメント をご覧ください。 ※収集されるメトリクスについて知りたい方は こちらのドキュメント を見てください。 Otel Collectorによるメトリクスの収集 色々なメトリクスが取れるContainer Insightメトリクスですが、実はOtel Collectorでも取得することが可能です。 さらに、Otel Colectornにあるprocesserを使うことができるので、不要なメトリクスを落としてCloudWatchに送信することが可能です。 取得する流れは以下の図のようになってます。 まずAWS Container Insights Receiver(awscontainerinsightreceiver)がcollection_intervalに従って、定期的にメトリクスを取得します。 そのメトリクスに対して、processorでfilterを使い不要なメトリクスを落とします。ここで不要なメトリクスを落としておくことでCloudWatchの課金を抑えられます。 最後にexportersでawsemfを使って、 埋め込みメトリクスフォーマット というCloudWatch logsにメトリクスを取得するように指示する形式に変換して送信します。 Otel Collectorのマニフェスト例 次に実際に使ってるマニフェストの一部を下記に示します。 このマニフェストでは、 nodeとpodのCPUとメモリを取るようにしてます。 同じことをしたい人はぜひ参考にしてください。 apiVersion : v1 data : collector.yaml : | receivers : awscontainerinsightreceiver : collection_interval : 300s exporters : awsemf : dimension_rollup_option : NoDimensionRollup log_group_name : /aws/containerinsights/{ClusterName}/performance log_stream_name : performance/{NodeName} metric_declarations : - dimensions : - - Namespace - ClusterName metric_name_selectors : - pod_cpu_utilization - pod_memory_utilization - dimensions : - - NodeName - ClusterName metric_name_selectors : - node_cpu_utilization - node_memory_utilization namespace : ContainerInsights parse_json_encoded_attr_values : - Sources - kubernetes resource_to_telemetry_conversion : enabled : true debug : {} processors : batch : {} filter/drop_unused : metrics : metric : - name != "pod_cpu_utilization" and name != "pod_memory_utilization" and name != "node_cpu_utilization" and name != "node_memory_utilization" service : telemetry : logs : encoding : json level : error metrics : readers : - pull : exporter : prometheus : host : 0.0.0.0 port : 8888 pipelines : metrics : exporters : - awsemf processors : - filter/drop_unused - batch receivers : - awscontainerinsightreceiver CloudWatchでの保存先は以下の部分で指定しています。 自動作成されるので、事前の作成は不要です。 {ClusterName}と{NodeName}は自動で埋めてくれます。 ただ、"{NodeName}"のように書くとエラーで自動で埋めるのが機能しなかったため、適当に performance/ とつけました。 最後のnamespaceで指定した名前で、CloudWatchMetricsのnamespaceが作成されます。 log_group_name: /aws/containerinsights/{ClusterName}/performance log_stream_name: performance/{NodeName} namespace: ContainerInsights この設定で動かすと、CloudWatchに保存されます。 レコードは以下のような形で記録されます。(見せられない部分は伏せてます) { "AutoScalingGroupName": "xxxx", "ClusterName": "xxxx", "InstanceId": "xxxx", "InstanceType": "xxxx", "NodeName": "xxxx", "Sources": [ "cadvisor", "/proc", "pod", "calculated" ], "Type": "Node", "Version": "1", "_aws": { "CloudWatchMetrics": [ { "Namespace": "ContainerInsights", "Dimensions": [ [ "ClusterName", "NodeName" ] ], "Metrics": [ { "Name": "node_memory_utilization", "Unit": "Percent", "StorageResolution": 60 } ] } ], "Timestamp": 1764157664000 }, "kubernetes": { "host": "xxxx" }, "node_memory_utilization": 31.46575974804524 } 絞り込んだメトリクスだけ取れてるのがわかります。 この形式でロググループに入るとCloudWatch logsが自動でメトリクスに転送してくれます。 CloudWatchのページに行きContainerInsights→指定したDimension→メトリクスを選んで表示してみるとメトリクスを観察することができます。 ちなみにメトリクスとして取り込んだあとはemf形式のログは消しても問題ありません。 まとめ ここまで読んでいただきありがとうございました! 今回はEKSにおけるContainer InsightsメトリクスのOtel Collectorでの収集についてご紹介しました! filterでの柔軟なメトリクス削減は需要が高いのではないでしょうか? ぜひ興味ある方はお試しください!
アバター