TECH PLAY

電通総研

電通総研 の技術ブログ

822

1. 自己紹介 金融IT本部 融資ソリューション2部末永です。 入社20年目、42歳です。これまで金融機関向けスクラッチ開発、PKG導入、ユーザ支援ツール導入、保守などに携わってきました。 担当業務領域としては、銀行市場 リスク管理 領域・融資領域・決済領域、生命保険会社ALM領域などを担当してきました。 2. 融資ソリューション2部の取り組み 私の所属する融資ソリューション2部では、金融機関様向けに自社パッケージであるBANK・Rのノウハウを活用した、融資支援システムのスクラッチ開発を行っています。 ITコンサル、新規 システム開発 、エンハンス、保守、などフェーズによって関わり方がありますが、要件定義で合意した仕様のシステムをお客様に納品する活動を行っています。 3. これまでの取り組み、改善していきたい課題 これまで自分が担当してきたお客様の多くは、弊社が長くお付き合いしているお客さまで十分に深耕できている先が多かった印象があります。お客様の困りごとや提案訴求ポイントを把握しやすい距離感であるメリットを活かした提案を行ってきました。 一方で、お客様が気づいていない課題などに直面するケースや深耕しきれていないお客様へのアプローチをしてビジネスを拡大するには、自身の対顧 ヒアリ ング・深堀、提案能力のさらなる向上が必要と考えています。 具体的には、新規のお客様に対してBPR業務コンサルからご支援をして新規システムの提案をするケースにおいては、お客様の困りごと・優先順位の整理、訴求ポイント、 電通 総研の優位性の検討、お客様向けの説明能力などが必要となってくると考えています。 4. 電通 総研の部署横断の新たな取り組みへの参加 2024年1月から社名が 電通 総研にかわり、「SI× コンサルティング × シンクタンク 」として新たな価値提供を目指しています。 私自身も視座や能力を改めて開発する必要性を感じていた中、組織横断選抜のワークショップ企画があり参加してきました。 【企画概要 ~某コンサルファーム講師を招いてのワークショップ形式~ 】  テーマ:脱“受託型 SIer ”思考研修 ⁻課題解決型コミュニケーションの実践 期間:半日×4セット(全4回) 研修での学び ・  ヒアリ ング設計の在り方。(課題仮説を持ったうえで、お客様の困りごと・方向性整理を併走、推進する) ・ 課題の定義にあたって視座をあげ、ストーリーを構築すること。 ・ 我々オリジナルの訴求ポイントの追求。 ・ 上記を共有・推進、訴求するために必要な会議設計。 【ポイント・特徴】 顧客訴求・顧客を動かすための思考ト レーニン グ:  従来の SIer 思考からのステップアップを狙う。 (1) SIer 思考:相手の困りごと → 自分たちができることは何か? (2)コンサル思考:相手の困りごと → 相手に必要なことは何か? → それをできる方法は? 部署横断参加: 年次、顧客、扱う商品(SI、PKG、製品開発、コンサル)など、背景が違うメンバの参加でやり方の吸収や気づきを得ていく。 ワーク中心の研修: 講師から最小限の考え方・ツール説明を聞いて、後はひたすらグループワーク。仮実践を通して、ノウハウ・気づきを創出する。 5. 研修概要・取り組みのご紹介 演習風景 「我々オリジナルの訴求ポイントの追求」  ワーク課題設定 「顧客課題 ヒアリ ング後、顧客にとっての提供価値・TOBE像を考え、 電通 総研が選ばれるシナリオを検討する」  演習を通しての学び 現在地点の共有 顧客から出る困りごとは氷山の一角。顧客の困りごと・現在地点を整理するための問いかけ自体が、顧客への提供価値になる。 ゴールの共有 WHY 電通 総研?を磨くために、TOBE像の解像度を上げること、顧客から見た優先度の整理が重要。 顧客にない発想の引き出しによる価値提供 顧客担当部では思いつかない指摘の検討、第 三者 だからできる発想が価値を生む。 ・同じ課題は他部署にもあるのでは? フォーカスされている調達以外のフロー上にも別の問題があるのでは? ・情報システム部(横串管理部署)の視点での指摘や、マネジメント視点での優先・懸念ポイントは何か?  他社優位性を見出すためのコミットメント 製品価値や実績だけでは他社優位性が見いだせないケースは多々ある。 スコープ外への対応やコミットメント姿勢など自社が最も顧客訴求できる点を検討したうえで、付加価値をどこに置くか、検討・調整していくことが価値を生む。 演習風景 「会議設計の検討」 ワーク課題設定 「顧客向けBPR提案において、初回会議~提案受注までのシナリオを検討する」 演習を通しての学び 全体計画の整理 そもそも売る側の立場として、案件受注を目的に置き受注に向けた経路設計から始める。 顧客への依頼事項の整理 後回しにしがちだが、とても重要。顧客から見た前提やWHY 電通 総研を輝かすための素材 ヒアリ ングを意識すること。 各回のストーリー・ポイントの整理 打合せ回数、期間はどの程度? それぞれの回でどう進めるかを検討する。 例:①(課題共有、TOBE検討ネタの提供、ミニワークショップ)→ ②(顧客持ち帰り事項の共有、TOBE検討の議論) → ③(TOBE像優先順位の議論)→ ④(提案) 6. まとめ 対顧 ヒアリ ング・深堀、提案能力向上に向けては、日々の活動の中での声がけや気づき・検討が重要と思っていますが、研修を通して視点や考え方、 マインドセット を体感して、実務での学びを大きくするための活動をご紹介させていただきました。 「顧客課題の深堀・訴求活動」と「品質の良いシステムを作り上げる活動」は、それぞれの分野のエキスパートによってなされることがありますが、私はそれぞれが業種・職位を超えた視点を持って、提案・開発を進めていくことが重要と思っています。 BPRコンサル・上流提案というと一部のメンバが携わる活動と思われるかもしれませんが、営業・ コンサルタント ・PM・SEがそれぞれの視点で考え、互いに議論を重ねていくことで、付加価値・顧客体験価値が上がっていくものと思い日々活動しています。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @suenaga.asuka レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
こんにちは。クロス イノベーション 本部エンジニアリングテクノロ ジー センターの宮原です。 本記事は 電通総研 Advent Calendar 2025 の19日目の記事になります。12/1から始まっている 電通 総研 Advent Calendarもいよいよ最終日となります。執筆されたみなさん、お疲れ様でした! さて、本記事では AWS re:Invent 2025で発表された新機能の一つである AWS Lambda Durable Functionsの機能をご紹介できればと思います。 AWS re:Invent 2025に関連する記事は宮崎さんの『 AWS re: Invent2025で登場した3つのFrontier Agentsの1つである「AWS DevOps Agent」を触りつつ、概要について整理してみる。』 や『 AWS re:Invent2025 Keynote現地速報 』もありますので、あわせてご覧いただけると嬉しいです。 速報の記事でも記載したように今年の AWS re:InventではAI関連のアップデートが多くあった一方で AWS Lambdaなどのコンピューティング関連のアップデートは少なかった印象です。 そんな中で AWS Lambda Durable Functionsの機能がリリースされています! 本記事では注目の新機能 AWS Lambda Durable Functionsの機能についてサンプルコードを共有しながら、ご紹介できればと思います。 AWS Lambda Durable Functionsとは 製品ドキュメントのページでは AWS Lambda Durable Functionsについて以下の記載があります。 AWS Lambda の既存のプログラミングモデル内で、Lambda Durable Functions を使って複数ステップのアプリケーションや AI ワークフローの構築を簡単にします。Durable Functions は、進行状況を自動的にチェックポイントし、長時間実行されるタスクの間に最大1年間実行を一時停止し、障害から回復します。追加のインフラを管理したり、独自の状態管理やエラー処理コードを書く必要はなく、より早く イノベーション を進めることができます。 この文面だけだとわかりにくいのですが、以下が重要だと認識しています。 AWS Lambdaの中でチェックポイントを設けることができる 実行待ち時間を指定し、チェックポイントから処理を再開できる 例えば、EC2を起動して、一定時間待機し、その上でEC2 インスタンス を停止するなどのオペレーションが AWS Lambda Durable Functionsを利用することで可能になります。 Lambdaといえば最大実行時間が15分という制約がありますが、 AWS Lambda Durable Functions内で指定した待ち時間は実行時間には含まれず、課金も行われません。 また、複数ステップの処理の中でチェックポイントを設け、特定のステップで失敗した際に、失敗したステップからリトライ、再開することも可能です。 AWS Lambda Durable Functionsのサンプルコード ドキュメントを読むだけではわかりにくい部分もあったため実際にサンプルコードを書きながら理解を深めてみました。 今回最初に実行した AWS Lambda Durable Functionsのサンプルコードは以下になります。 import { type DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js"; export const handler = withDurableExecution ( async ( _event : unknown , context : DurableContext ): Promise < void > => { await context.step( "step1" , async () => console .log( "step 1 executed" )); await context.wait( { seconds : 5 } ); await context.step( "step2" , async () => console .log( "step 2 executed" )); } ); console.logを実行し、5秒待ち、再度console.logを実行するだけのシンプルなものになっています。Lambdaのテスト機能経由で関数を実行したところDurable execution detailsというリンクが表示されました。クリックすると永続オペレーションとイベント履歴が確認できる以下のような画面が表示されます。 こちらの画面からstep1、待機、step2の処理が成功していることを確認できます。 次にサンプルコードについてです。 AWS Lambda Durable Functionsを実行する際には別途@ aws /durable-execution- sdk -jsをインストールし、通常のLambda関数のハンドラをwithDurableExecution関数でラップします。 context.step関数を利用し、チェックポイントを作成しながら、処理を記述します。 context.wait関数を利用すると指定した実行時間だけ処理を待つことができます。 冒頭にも記載の通り、この待ち時間はLambdaの実行時間には含まれず、課金の対象外になります。 処理を失敗させてみる 今度は処理の途中でエラーが発生した際にどのような挙動になるかを確かめてみます。 以下のコードをLambda関数にデプロイし、テストを実行しました。 このコードでは2番目のステップで乱数を生成し、数字が偶数の場合に例外を送出するようにしています。 import { type DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js"; export const handler = withDurableExecution ( async ( _event : unknown , context : DurableContext ): Promise < void > => { await context.step( "step1" , async () => { console .log( "step 1 executed" ); } ); await context.step( "step2" , async () => { const num = Math . floor ( Math . random () * 100 ); if (num % 2 === 0 ) { throw new Error ( "Random failure occurred" ); } console .log( "step 2 executed" ); } ); await context.step( "step3" , async () => { console .log( "step 3 executed" ); } ); } ); エラーが発生した場合の永続オペレーションとイベント履歴は以下のようになっていました。 step2で処理が失敗し、リトライが自動で実行され、step2から処理が再開されていることがわかります。このように AWS Lambda Durable Functionsを利用することでエラー発生時に処理が自動リトライされ、失敗したステップから処理を再開することが可能になっています。 また、リトライロジックの制御は、context.step関数の第3引数にオプションとして渡すことが可能です。試行回数やエラーのクラスを受け取り、値に応じてリトライロジックを細かく制御できます。以下はリトライロジックを制御する場合のサンプルコードになります。 await context.step( "step2" , async () => { const num = Math . floor ( Math . random () * 100 ); if (num % 2 === 0 ) { throw new Error ( "Random failure occurred" ); } console .log( "step 2 executed" ); } , { retryStrategy ( _error : Error , attemptCount : number ) { if ( 3 < attemptCount) { return { shouldRetry : false } ; } return { shouldRetry : true } ; } , } , ); 実践例 今回は、この AWS Lambda Durable Functionsの機能を利用して、EC2 インスタンス を起動して、20分経過後に、再度EC2 インスタンス を停止する スクリプト を実装して、動かしてみました。EC2 インスタンス の計画停止を想定した簡単な スクリプト になっています。 こちらも待ち時間を含めるとLambdaの15分の実行時間上限を超えていますが、問題なく動作しています。 このような状態を持った スクリプト は、 AWS Step Functionsなどを利用して実行することが多かったと思いますが、 AWS Lambda Durable Functionsを利用することで AWS Lambdaのロジックとして実装できるようになったのは嬉しいポイントだと思います。 import { type DurableContext, withDurableExecution } from "@aws/durable-execution-sdk-js"; import { DescribeInstanceStatusCommand, EC2Client, StartInstancesCommand, StopInstancesCommand } from "@aws-sdk/client-ec2"; const ec2 = new EC2Client( { region : "ap-northeast-1" } ); const instanceId = process .env.EC2_INSTANCE_ID; if (!instanceId) { throw new Error ( "EC2_INSTANCE_ID environment variable is not set." ); } export const handler = withDurableExecution( async ( _event : unknown , context : DurableContext ): Promise < void > => { await context.step( "startInstance" , async () => { console .log( `Starting instance: ${ instanceId } ` ); const startResp = await ec2. send ( new StartInstancesCommand( { InstanceIds : [ instanceId ] } )); console .log( "Start response:" , JSON . stringify (startResp.StartingInstances, null , 2 )); } ); await context.wait( { minutes : 20 } ); await context.step( "stopInstance" , async () => { console .log( `Stopping instance: ${ instanceId } ` ); const statusResp = await ec2. send ( new DescribeInstanceStatusCommand( { InstanceIds : [ instanceId ] , IncludeAllInstances : true } )); if (statusResp.InstanceStatuses?. [ 0 ] .InstanceState?.Name !== "running" ) { console .log( `Instance ${ instanceId } is not in running state` ); return ; } const stopResp = await ec2. send ( new StopInstancesCommand( { InstanceIds : [ instanceId ] } )); console .log( "Stop response:" , JSON . stringify (stopResp.StoppingInstances, null , 2 )); } ); } ); 所感 AWS Lambda Durable Functionsを利用してみた感想です。 context.step関数でラップすることで処理が自動的にリトライされるようになり、エラーが発生しても特定のステップからリトライ、再開されるようになります。 通常のLambda関数のようなロジックでもリトライを想定し、 AWS Lambda Durable Functionsの形式で実装していくことで AWS Lambdaの処理がより安定すると思います。 また、context.wait関数を利用して、Lambdaの実行上限を超えて、一定時間待ち、処理を再開するようなロジックが書けるようになったのも大きいと思います。 今回はcontext.wait関数を呼び出しましたが、context.createCallback関数やcontext.waitForCallback関数なども用意されており、callbackが呼び出されるまで実行を停止するようなロジックも書けるようです。 AIエージェントのHuman-In-the-Loopもこちらの機能を使うことで簡単に実装できそうです。 終わりに 今回は AWS Lambda Durable Functionsの新機能を試してみました。待ち時間や処理の再開などの機能が追加され、 AWS Lambdaの利用範囲がまた拡大したのではないでしょうか。 私自身も AWS Lambda Durable Functionsにマッチする実際の ユースケース を考えていきたいと思いました。 最後までお読みいただきありがとうございました! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @miyahara.hikaru レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
Merry Christmas! エンタープライズ 第一本部 業務変革ソリューション2部の倉内です! 本記事は 電通総研 Advent Calendar 2025 18日目の記事です! 17日目である昨日の記事は、クロス イノベーション 本部 大岡叡さんの「 【AWS資格】新卒1年目がAWS資格を全冠したので振り返る 」でした! 新卒1年目で AWS 全12資格を取得するまでの背景や、挑戦を通じて得られたこと・つらかったことを振り返った記事です。 新卒1年目で全冠…1つ上の先輩として負けていられないなと思いつつ、ただただ尊敬です。ぜひご一読ください! さて、私は現在新卒2年目で、普段はエンジニアとして金融領域のソリューション開発に携わっています。 タイトルの通り、実は来年から働きながら大学院に通うことにしました。 今回はそこで学ぼうとしている「システム×デザイン思考」という考え方について書いてみます。なぜこれを学ぼうと思ったのか、そしてこの考え方がITエンジニアにとってどう役立ちそうか、自分なりに整理してみました。 なぜ大学院に行くのか 「難しい問題」と「複雑な問題」 「システム思考」とは何か 「デザイン思考」とは何か 2つを組み合わせる エンジニアとの相性 これから学ぶこと おわりに なぜ大学院に行くのか 学部時代から、いつか大学院で研究したいという気持ちはありました。ただ、当時は「研究よりも社会実装に近いところでやりたい」という思いがあり、まずは就職する道を選びました。 では、なぜ今なのか。きっかけは「地方創生」への興味でした。 地方出身の人間として、学生時代から地域の課題に関心があり、社会人になってからもその思いは変わりませんでした。ただ、地方創生について調べたり、関わっている人の話を聞いたりする中で、壁にぶつかりました。 地方創生が複雑な問題であることは、誰もが知っていることだと思います。私自身もそれは理解していたつもりでした。しかし、「複雑だ」とわかっていることと、「その複雑さにどう向き合えばいいか」がわかっていることは、まったく別の話でした。 人口減少、産業の衰退、若者の流出、行政と民間の連携……。これらが互いに影響し合っていることは頭では理解していました。でも、「だから何をすればいいのか」となると、途端にわからなくなる。一つの課題に手を打とうとすると、別のところに影響が出る。関係者によって「何が問題か」の認識も違う。 エンジニアとして技術を学んできましたが、技術だけではこの複雑さを扱えないと感じました。 「複雑な問題を扱うための方法論が必要だ」 そう思って調べる中で出会ったのが「システム×デザイン思考」という考え方でした。 「難しい問題」と「複雑な問題」 この考え方を学ぶ中で、最初に腑に落ちたのが「難しい問題」と「複雑な問題」の違いです。 難しい問題というのは、たとえば アルゴリズム の実装やパフォーマンスチューニングのようなものです。専門知識が必要で、時間もかかりますが、正解があります。手順を踏めば解けます。再現性もあります。 一方、複雑な問題というのは、正解がありません。要素が多く、それぞれが影響し合っていて、一つを変えると別のところに影響が出る。しかも、関わる人によって「何が問題か」の認識が違っていたりします。 地方創生はまさにこれです。「若者が出ていくのが問題だ」という人もいれば、「仕事がないのが問題だ」という人もいる。「そもそも人口が減っても別にいいのでは」という意見もある。何を解決すべきかすら、合意を取るのが難しい。 ITの世界でも、似たような状況はあります。DX推進や レガシーシステム の刷新、組織をまたいだプロジェクトなどは、技術的な正解を出しても、それだけではうまくいかないことが多いと聞きます。 私たちエンジニアは、難しい問題を解く訓練を受けています。でも、複雑な問題には、別のアプローチが必要らしい。そのことに気づいたのが、最初の発見でした。 「システム思考」とは何か では、複雑な問題にはどう向き合えばいいのか。その一つの答えが「システム思考」です。 システム思考とは、物事を個別の要素としてではなく、全体として捉える考え方です。要素と要素がどのように影響し合っているか、その関係性に注目します。 たとえば、地方の人口減少を考えてみます。 若者が都市部に出ていく。地元の働き手が減る。企業が撤退する。さらに仕事がなくなる。ますます若者が出ていく。 これは悪循環です。どこか一箇所を切り取って対処しても、構造が変わらなければ同じことが繰り返されます。「若者に残ってもらおう」と呼びかけても、仕事がなければ残れない。「企業を誘致しよう」としても、働き手がいなければ来てくれない。 システム思考では、こうした因果関係を図にして可視化します。これを「因果ループ図」と呼びます。図にすることで、どこに手を打てば全体が良くなるか、いわゆる「 レバレッジ ポイント」が見えてきます。 たとえば、上の例で言えば、「リモートワーク可能な仕事を増やす」というのは一つの レバレッジ ポイントかもしれません。都市部の企業に勤めながら地方に住めるなら、「仕事がないから出ていく」という構造を変えられる可能性があります。 もちろん、これが正解かどうかはわかりません。でも、構造を可視化することで、「どこに手を打つと効果がありそうか」を議論できるようになります。 【図1:地方の人口減少の悪循環を示す因果ループ図の例】 「デザイン思考」とは何か 一方、システム思考だけでは足りない部分もあります。 構造を分析することはできても、「そもそも何が問題なのか」を発見するのは得意ではありません。また、論理的に正しい解決策を導いても、それが人の心に響くとは限りません。 そこで出てくるのが、デザイン思考です。 デザイン思考は、人間を中心に置いた問題解決のアプローチです。まず現場に行き、人の話を聞き、観察する。言葉にならない困りごとや、本人も気づいていない 潜在的 なニーズを探る。そこから問題を定義し、解決策を考え、素早く形にして試す。 地方創生の文脈で言えば、「住民が本当は何に困っているのか」「行政の人は何を考えているのか」を、データや報告書ではなく、直接会って聞くことから始める。そういうアプローチです。 デザイン思考でよく使われるプロセスとして、「共感→問題定義→ア イデア 創出→プロトタイプ→テスト」という5つのステップがあります。いきなり解決策を考えるのではなく、まず相手の立場に立って共感することから始めるのが特徴です。 エンジニアの感覚からすると、少しふわっとした印象を受けるかもしれません。私も最初はそうでした。 でも、考えてみると、要件定義書に書かれていることが本当にユーザーの求めていることなのか、疑問に思った経験は誰にでもあるのではないでしょうか。 2つを組み合わせる システム思考とデザイン思考。 この2つは、一見すると異なるアプローチに見えます。片方は構造を分析し、もう片方は人間に寄り添う。片方は全体を俯瞰し、もう片方は個人に深く入り込む。 でも、複雑な問題を解くには、両方が必要です。 デザイン思考で現場の声を集め、問題を発見・定義する。システム思考でその問題の構造を可視化し、どこに手を打つべきかを考える。 再びデザイン思考で解決策を発想し、プロトタイプを作る。そしてシステム思考で、その解決策が全体にどんな影響を与えるかを検証する。 この行き来が重要だと言われています。 システム思考の提唱者の一人であるピーター・センゲは、著書『学習する組織』の中で、全体(wholes)・相互関係・変化のパターンに目を向ける視点(システム思考)を組織学習の土台として示しています。実践面については、別著『The Dance of Change』の中で、現場に近い人々の小さな試行と対話の重要性を論じています。 またデザイン思考の公式フレームでは、問題の理解・定義を飛ばしてア イデア に走ると“誤った問題”を解きやすいと明確に示されています。 どちらか一方だけでは不十分で、両者を行き来することで、複雑な問題に対処できるようになる。これがシステム×デザイン思考の基本的な考え方です。 【図2:システム思考とデザイン思考の行き来】 たとえば、地方創生で「移住促進」を考えるとします。 まず、デザイン思考で移住を検討している人や、実際に移住した人に話を聞く。すると、「仕事の不安」だけでなく、「子どもの教育」「医療へのアクセス」「地域コミュニティへの馴染みやすさ」など、さまざまな要素が出てくる。 次に、システム思考でそれらの要素の関係性を整理する。「教育環境が整うと、子育て世代が移住しやすくなる」「子育て世代が増えると、地域の活気が出る」「活気が出ると、さらに移住者が増える」といった好循環が見えてくるかもしれない。 そして、再びデザイン思考で「では、教育環境を整えるために何ができるか」を発想し、小さく試してみる。 正直なところ、私はまだこれを実践できているとは言えません。でも、この枠組みを知っているだけで、物事の見え方が少し変わってきた気がします。 エンジニアとの相性 この考え方を学んでいて思うのは、エンジニアは意外とこの思考法と相性がいいのではないか、ということです。 まず、システム思考について。私たちは日常的に、システムを設計しています。要素を分解し、関係性を定義し、全体として動くように組み立てる。 コンポーネント 間の依存関係を意識し、一箇所を変えたときの影響範囲を考える。これはまさにシステム思考そのものです。 また、デザイン思考について。私たちは「動くもの」を作れます。ア イデア を形にして試すことができる。これはデザイン思考で言うプロトタイピングです。紙に書いた企画書ではなく、実際に触れるものを作って見せられるのは、エンジニアの大きな強みだと思います。 日々の業務でも、この考え方は使えそうです。 たとえば、プロジェクトで問題が起きたとき。「誰が悪い」という犯人探しではなく、「どういう構造がこの問題を生んでいるのか」と考える。因果ループ図を描いてみると、個人の努力ではどうにもならない構造的な問題が見えてくるかもしれません。 あるいは、新しい機能を開発するとき。要件定義書をそのまま実装するのではなく、「この機能を使う人は、本当は何がしたいのか」を考える。可能であれば、実際に使う人に話を聞いてみる。 つまり、必要なスキルの土台はすでに持っている。足りないのは、それを技術以外の領域にも適用するという発想なのかもしれません。 これから学ぶこと 来年から大学院で システムエンジニア リング、デザインプロジェクト、システム ダイナミクス などを学ぶ予定です。座学だけでなく、実際のプロジェクトを通じて実践する機会もあると聞いています。 特に楽しみなのは、異なるバックグラウンドを持つ人たちと一緒に学べることです。エンジニアだけでなく、デザイナー、 コンサルタント 、行政職員、医療従事者など、さまざまな分野の社会人が集まると聞いています。 複雑な問題に取り組むには、多様な視点が必要です。自分とは違う考え方に触れることで、視野が広がることを期待しています。 おわりに 本記事では、私が大学院で学ぼうとしている「システム×デザイン思考」について、なぜ興味を持ったのかという背景も含めて書いてみました。 まだ学び始めたばかりで、偉そうなことは言えませんが、「技術だけでは解決できない問題が数多くある」ということに気づけたのは、自分にとって大きな一歩でした。 来年からは実際に大学院で学びながら、この思考法を実務にどう活かせるか試行錯誤していく予定です。学んだことや気づいたことは、本ブログで共有していければと思います。 この記事が、同じようなもやもやを感じているエンジニアの方にとって、何かのきっかけになれば幸いです。 さて、 電通 総研 Advent Calendar 2025もいよいよ明日が最終日! 明日の記事は、クロス イノベーション 本部 宮原光さんの「 AWS re:Invent2025 Keynote 現地速報」です! アドベントカレンダー のトリを飾るにふさわしい、現地からの熱気が伝わってくる記事になること間違いなしです!お楽しみに! 最後までお読みいただき、ありがとうございました。 それでは、よいクリスマスを。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @kurauchi.tatsuya レビュー: @miyazaki.hirotoshi ( Shodo で執筆されました )
アバター
はじめに クロス イノベーション 本部、新卒1年目の大岡叡です。 本記事は 電通 総研 Advent Calendar 2025 17日目の記事です。 12月上旬に12個目の AWS 資格を取得しました。これにより、 AWS 資格全種類を取得したことになり、 2026 Japan All AWS Certifications Engineers クライテリア を満たしました。 本記事では新卒1年目で全冠を目指した背景、目指して・達成して得られたこと、つらかったことを振り返っていきたいと思います。なお、資格取得のスケジュールや具体的な勉強方法については、すでに多くの良記事がありますので本記事では割愛します。 はじめに 全冠を目指そうと思った背景 一言で言うと 詳しく 全冠を目指して・達成して良かったこと 1. 「できるやつ」と思われる → 期待に応えようと成長できる 2. 上司の説明がすぐ理解できる → スムーズな業務遂行 3. 周囲から褒めてもらえる → 仕事ももっと頑張ろうと思える つらかったこと まとめ・今後の展望 全冠を目指そうと思った背景 一言で言うと 全冠を達成すると得られる All AWS Certifications Engineer(以下、All Cert)という称号に魅力を感じたため目指しました。 詳しく 就職活動で出会った方から、 AWS Jr. Championsという表彰プログラムがあるという話を聞いたのが最初のきっかけでした。 AWS Jr. Championsとは、APN参加企業に所属する若手向けの表彰プログラムです。以下が公式の説明です。 AWS Partner Network (APN) 参加企業に所属し、現在社会人歴 1~3 年目で突出した AWS 活動実績がある若手エンジニアを「Japan AWS Jr. Champions」として表彰します。これは、 AWS を積極的に学び、自らアクションを起こし、周囲に影響を与えている APN 若手エンジニアを選出しコミュニティを形成する、日本独自の表彰プログラムです。 ( AWS Japan APN ブログ より引用) この AWS Jr. Championsを調べていく中で、All AWS Certifications Engineerという別の表彰があることを知りました。All Certは AWS の全資格を取得すると表彰されるプログラムです。以下が公式の説明です。 「Japan All AWS Certifications Engineers」とは、 AWS Partner Network (APN) に参加している会社に所属し、下記クライテリアの「 AWS 認定資格を全て保持している」 AWS エンジニアの皆様を対象にした表彰プログラムです。 ( AWS Japan APN ブログ より引用) さらに調べていくと、All Certを達成すると以下のようなメリットがあることが分かりました。 AWS の公式サイトに会社名と名前が掲載される 例: 2025 Japan All AWS Certifications Engineers の発表 会社のプレスリリースに名前が掲載される 例: 電通総研グループ社員9名がAWSの「2025 Japan AWS Top Engineers」「2025 Japan AWS All Certifications Engineers」「2025 Japan AWS Jr. Champions」に選出 (当たり前だが) AWS の知識を体系的かつ幅広く身に付けることができる 特に1と2のように対外的な実績として示せる点に魅力を感じました。 そして、1年目で達成できればより強い実績になると考え、全冠取得を決意しました。 全冠を目指して・達成して良かったこと AWS 公式サイトや会社のプレスリリースに掲載されるのは来年になるので、ここではそれ以外に実感している良かったことを紹介します。 1. 「できるやつ」と思われる → 期待に応えようと成長できる 同期に対して全冠を目指していることや、複数の資格を持っていることを公言していたので、新人の システム開発 研修で AWS を使ってインフラ構築する際に、同期からたくさんの質問を受けました。 質問の中には資格勉強で得た知識から回答できるものもあれば、わからないものもたくさんありました。質問してくれた人の期待に応えられるように、わからない質問が来てもわかるまで調べて回答するようにしました。その過程で調査力と実践的な知識が身についたと感じています。 2. 上司の説明がすぐ理解できる → スムーズな業務遂行 業務上、上司との会話に AWS の用語が当たり前に登場しますが、資格勉強をしていたおかげで苦労せずに理解できています。その結果、業務におけるコミュニケーションが円滑に進み、スムーズに仕事ができていると感じています。 3. 周囲から褒めてもらえる → 仕事ももっと頑張ろうと思える 全冠を達成したことを部署のTeamsチャネルで報告したところ、部長や仕事で関わっている上司に加えて、普段話していない方からも「すごいね!」「おめでとう!」という言葉をいただきました。頑張って良かったと思うと同時に、仕事でも期待に応えられるようにもっと頑張ろうと思えました。また、部署の温かさも感じられて嬉しかったです。 つらかったこと 「1年目でAll Cert」のために資格取得を決意したので、資格勉強の一番の目的を「合格」に置いていました。そのうえで、最短距離で合格するには問題演習を繰り返すことだと考えていました。(全冠した現在でもその考えは変わりません。)そのため、問題演習の中で「実際に触って試したい」と思う場面があっても、ぐっとこらえてひたすら問題を解き続けていました。このように好奇心を抑えながら勉強を続けるのは、なかなか苦しいものでした。 加えて、資格勉強は1人で取り組むため、孤独感もつきまといます。私の場合は、同期を誘って一緒に勉強するようにしていたのでなんとかやっていけました。 とはいえ、資格勉強自体がつらかったわけではありません。たくさんの知識を吸収できる楽しさがありましたし、All Certという目標に向かって努力すること自体にやりがいがありました。また、実務で得た知識が問われる問題に出会えたときは、小さな喜びも感じられました。 まとめ・今後の展望 新卒1年目で AWS 資格全冠を達成した経験を振り返りました。 次は AWS Jr. Championsを目指します。 ここまでお読みいただきありがとうございました。 この記事がAll Certを目指すきっかけになれば幸いです。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @ooka.toru レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
こんにちは! グループ経営ソリューション事業部 アドバンストテクノロ ジー 部の佐藤です。 突然ですが、日々の技術学習で、皆さんはどのように AI を活用しているでしょうか? Copilot Chat と対話的に疑問を紐解いていく方もいれば、Genspark のような AI 検索エンジン 派の方、最近だと ChatGPT Atlas といった AI ブラウザを駆使する方もいるでしょう。 このように、新しい知識を調べ、学習するために AI を活用することはもはや当たり前になりつつあります。 しかしながら、こうした「インプット」のノウハウは、AI 駆動開発のように何かを作り出す「アウトプット」の話題と比べると、語られる機会が少ない印象を受けます。 そこで、 電通総研 Advent Calendar 2025 、16 日目の今日は、技術知識の学習における「 メンタルモデル 」の重要性を押さえつつ、自分だけのナレッジベースを AIと共創 することでメンタルモデルの構築を効率化する実践的な学習スキームを紹介したいと思います。 技術知識を学ぶ理由 意思決定への道標 RTKB - Reviewable and Traceable Knowledge Base - ドメインと品質の定義 TDL(Test-Driven Learning) 抽象と具体の分離 問題空間の明確化 トレーサビリティの確保 RTKB の学習フロー さいごに:AI 時代における意思決定の重要性 技術知識を学ぶ理由 学習スキームの話に入る前に、少し立ち止まって「そもそも論」を考えてみたいと思います。 そもそも、私たちはなぜ技術知識を学ぶのでしょうか? 多くのエンジニアに共通する理由の一つとして 「興味や関心といった"知識欲"を満たすため」 があるかと思います。 これは、エンジニアにとって最も純粋かつ強力な、尊重されるべき動機です。エンジニアとして長く働き続けるための、必須要件とすら言えるかもしれません。 しかし、ビジネスの観点からは、これ だけ では不十分です。 所属する組織、ひいては社会に対し価値を提供するために「知識をどう使うのか」という視座から、技術知識を学ぶ本質的な理由を考えなければなりません。 本稿では、それを 「意思決定の質を高めるため」 と捉えてみたいと思います。 意思決定への道標 ソフトウェア開発は、意思決定の連続です。 アーキテクチャ の選定から変数の 命名 に至るまで、あらゆる活動は意思決定により駆動されます。 語弊を恐れずに言えば、あらゆる知識は、この「意思決定」に寄与することで、初めてビジネス上の価値を与えられます。 「技術知識は意思決定の根拠ではなく、 課題解決の手段 ではないか」 と思う方もいるかもしれません。 確かに、多くの技術知識の本質は手段です。しかし、知識が手段としての価値を発揮するか否かは、意思決定に依存しています。 例えば、Strategy パターンを 知って いたとしても、「この コンポーネント は Strategy パターンで実装する」という意思決定が出来なければ、その知識は一行のコードも生み出すことはありません。 つまり、いかなる知識も、 意思決定の土俵に上げられない限りは宝の持ち腐れ となってしまいます。 では、意思決定において適切な知識を動員し、質の高い決断を下すにはどうすれば良いでしょうか。 そのためのカギとなるのが、 「メンタルモデル」 です。 メンタルモデルについては、弊社のアーキテクトである 米久 保さんの過去記事でも解説されています。 tech.dentsusoken.com メンタルモデルの構築には、知識を点として暗記するのではなく、対象が持つ多面性を捉え、それが「何を解決するのか」「いつ適用できるのか」といった様々なコンテキスト上で概念化を行うことが重要です。 そのようにして構築された個々のモデルは、やがてアナロ ジー や対比などの認知作用によって相互に関連付けられ、言わば 「認知の地図」 を描き始めます。 この地図を心に広げることができれば、知識は 私たちを意思決定へと導いてくれる、確かな道標 に変貌するのです。 次章では、この「認知の地図」を描く際のパレットとなる自分だけのナレッジベースを、AIと共創する学習スキームを紹介します。 RTKB - Reviewable and Traceable Knowledge Base - 技術知識を AI で調べるとき、最も手軽で一般的なアプローチは、Copilot Chat などの AI チャットとの対話的な学習だと思います。 しかし、チャット上での対話は基本的に 使い捨てのフロー情報 であり、チャット履歴は知識の墓場になりがちです。 RTKB は、そんな漫然としたチャットから脱却し、AI からの回答を Reviewable かつ Traceable な 形式知 として管理することで学習効果を高めるアプローチです。 AI チャットに「聞くだけ」の学習に対する課題感から、筆者自身が実際に試行錯誤しつつ実践してきたアプローチを、本稿執筆にあたりブラッシュアップし、スキームとして定義しました。 大仰な名前を付けてはみましたが、 仕組みはいたってシンプル です。 まずは、ローカル環境にナレッジベースのルート ディレクト リを作成し、Cursor などの AI コーディングエディタで ワークスペース として開きます。 あとは、エージェントに学びたい知識の技術ドキュメントを作成してもらい、Git で構成管理するだけです。 これだけであれば、ただのドキュメントのフォルダ管理ですが、RTKB の要はプロジェクトに配置する AGENTS.md にあります。 このファイルに、 メンタルモデル構築に適したドキュメントの仕様 を厳格に定義することで、AI チャットにただ「聞くだけ」では得られない、学習効果の高い出力を引き出します。 ここからは実際の AGENTS.md を一部引用しつつ、ポイントを解説します。 ※ AGENTS.md の全文や、実際に出力されたサンプルドキュメントは GitHub リポジトリ にて公開していますので、あわせてご確認ください ドメイン と品質の定義 AGENTS.md では、まずこのナレッジベースが扱う「技術的概念」の ドメイン を定義しています。 ## Glossary - 技術的概念: - Computer Science のコンテクストにおける汎ゆる概念を示す - 技法・原理・原則・プロトコル・パターン・プラクティス・仕様・パラダイム などを含む ここで「技術的概念」という言葉をあえて広義に定めることで、RTKB は特定の要素技術から抽象度の高い概念まで幅広く適用可能な汎用スキームとして機能します。 また、 NON-NEGOTIABLE な規約として以下を明記することで、ハルシネーションを可能な限り抑制し、学習教材としての信頼性向上を狙います。 ## 全ての文書において NON-NEGOTIABLE な遵守事項 - 技術的概念について記述する際は、**必ず厳密な技術用語を利用**する - 厳密とは、その言葉が対象の技術領域において広く一般的に使われる正確な言葉であるという syntactic な側面と同時に、意味的にも定義通りであるという semantic な側面の両方を満たす事を指す - 技術的概念に関して不明確な点がある場合に、推察や推論、憶測による根拠のない断定を行ってはならない。全ての文章は、事実に基づき記述される必要がある TDL (Test-Driven Learning) TDD のように、学習もテストから始めましょう。 ## Comprehension Check 記載時の遵守事項 - `Comprehension Check` 章には、主要な技術的概念、技術文書の重要な論点/要点の理解度を問う簡潔なテストを箇条書きで記載する - 正誤を問う形式や選択肢問題ではなく、自由回答方式の設問とする - 技術文書の主題ならびに主要な技術的概念について、それ自体(WHAT)ではなく、それに関する WHAT FOR / WHY / WHEN の理解度を問う設問が望ましい - 重要な技術的概念と同じ問題空間を対象とした、異なる技術的概念が存在する場合、それとの違いを問う設問を含める RTKB では、ドキュメントの冒頭に必ず Comprehension Check を設けています。 これにより、ドキュメントが Reviewable (=復習可能)になると同時に、読者は「何を理解しなければならないか」を明確化したうえで学習を開始することができます。 なお、各設問では意思決定に向けて重要な観点である "WHY" や "WHEN" を 自由回答方式 で問うことを推奨しています。 この回答を通して、対象の概念を「自分の言葉」で説明するという営みは、メンタルモデルの構築にとって重要なファクターとなります。 ただドキュメントを読み返すのではなく、 復習として Comprehension Check に取り組むことで、より効果的にメンタルモデルを構築することができます。 抽象と具体の分離 アーキテクト思考:メンタルモデル構築の重要性 でも説明されているとおり、メンタルモデルの構築には「抽象と具体の往来」が効果的です。 RTKB は、 概念の解説(=抽象)と、その適用事例の解説(=具体)が必ずドキュメントに含まれる ようにス ケルト ンを定義しています。 ## {WHAT/WHAT FOR/WHY/WHERE the CONCEPT IS FROM (the ORIGIN):主題と関わる技術的概念の解説} - 文章形式での記述を優先し、必要に応じて項への分割、箇条書き・表を用いる ## {HOW/USAGE:技術的概念の使い方/機序/運営・運用/活動/メカニズム/アプローチ} - 文章形式での記述を優先し、必要に応じて箇条書き・表を用いる また、それらを異なる章に分離することで段落内の抽象度の揺らぎを回避し、概念理解から適用への思考フローと読書体験の摩擦を低減します。 問題空間の明確化 多くの技術的概念は、何らかの課題を解決する手段として機能します。 RTKB では、技術的概念が対象とする 問題空間 についても説明することを定めています。 また、ある概念の適用事例と併せて、その概念を「適用しない場合」を対比的に解説することで、問題空間をより鮮明に浮かび上がらせるよう図ります。 ## 全ての文書において NON-NEGOTIABLE な遵守事項 - 技術的概念は、何らかの問題空間上の課題に対する解決空間で機能する。技術文書には、必ずこの問題空間上の課題の詳説と、それを理解するための関連する技術的概念まで含めて説明されなければならない ## {HOW/USAGE:技術的概念の使い方/機序/運営・運用/活動/メカニズム/アプローチ} - 理解の促進のため、その技術的概念{を使わない場合/が存在しなかった時代}における具体例を初めに提示する ### {HOW/USAGE WITHOUT the CONCEPT:技術的概念 {を使わない/が存在しなかった時代} のアプローチ} <!-- optional/if applicable:主題の技術的概念が存在しなければ解決不可能な課題である場合、本章は省略可能 --> - コードによる実装例や、具体的な事例を示すことで、主題の技術的概念が存在しない場合にどのような問題があるかを詳説する これにより、その技術的概念が「なぜ必要とされるのか」「いつ適用すべきか」といった 実践的なコンテキストにおける理解 が深まります。 加えて、その概念が対象とする問題空間を把握することにより、その空間を対象とする他の技術的概念とのアナロジカルな関連付け、すなわち 「認知の地図」の広がりも期待 されます。 トレーサビリティの確保 上述の通り、 AI とのチャット履歴は使い捨ての「フロー情報」であり、ナレッジベースに蓄積するストック情報とは一般的には相容れません。 しかし少し見方を変えると、 AI への質問履歴は理解に至るまでの軌跡 そのものであり、捨て去るには惜しい情報とも思えてきます。 そこで RTKB では、質問履歴をチャットに埋もれさせず Traceable に文書化するための規約も定めています。 ## 質問回答時の遵守事項 既存の技術文書に関して、ユーザーからの質問を受領した場合は、以下の方針に従う。 ### WHAT IS THIS:技術的概念の定義に関する質問 - 技術的概念そのものの定義を確認する質問の場合は、`Glossary` 章に追記する ### FOLLOW UP QUESTIONS:方法や理由、目的、意図を問う質問 - 技術文書に記載された内容に関する、理解を深めるための follow up questions に対しては、`Dialogue for Understanding` に質問の要約と回答を追記する - 質問が、`Dialogue for Understanding` の回答に対する追加の質問の場合は、新たな"Q"節は設けず、既存の回答に追記する RTKB の学習フロー ここまで解説してきた AGENTS.md により作成される技術ドキュメントを軸に、RTKB では以下のフローで学習を行います。 学習トピックの バックログ 管理 仕事やプライベートで出会った「ちゃんと説明できないな」「詳しく理解したいな」といったトピックを、任意のツールで バックログ 管理しておきます マイナーな技術や、複数のバージョンが存在するライブラリにおける特定バージョンの API の使い方など、AI が誤りやすいトピックについては、RTKB の対象外として管理することを推奨します 問いかけ 学習トピックについて「分からないことが分からない」状態であれば、学ぶべき技術的概念を対話的に定めます 自身の理解の程度を示しつつ対話することで、ドキュメントが扱うスコープの最適化が期待されます ドキュメント作成依頼 学習したい対象が明らかになったら、ドキュメントの作成をエージェントに依頼します 実装例で使用する言語の指定や、 AGENTS.md 記載ルールの範囲内における詳細度などの調整は可能です TDL 冒頭の Comprehension Check を確認し、学習のゴールをイメージしたうえで、読み始めます 回答例は ▶ Answers を押下すると展開表示されます 深堀り 読み進めていくなかで、疑問点があればエージェントに質問します エージェントは、ユーザーからの質問に合わせてドキュメントを適切に追記/修正してくれます Review 空き時間を使って Comprehension Check による復習を実施します ナレッジベースを GitHub 上で管理していれば、アプリを使って 移動中などの隙間時間で復習が捗る のでお勧めです さいごに:AI 時代における意思決定の重要性 冒頭に記載したとおり、ソフトウェア開発は意思決定の連続です。 これをもう少し解像度を上げて見ると、日常の活動は、意思決定に先行する「方針検討」と、意思決定に基づく「遂行」から構成されると言えるでしょう。 今日の AI は、詳細設計に基づくコーディングのような具体的な「遂行」作業だけでなく、抽象度が高く複雑な「方針検討」タスクでさえも、その高度な推論能力により高い精度でこなせるようになりつつあります。 意思決定の前後が急激に「時短」されたことにより、私たちが ある期間に下さなければならない意思決定の数は、数年前とは比べ物にならない程増加 しています。 そして、この 「意思決定の時間密度」はこれからも高まり続けていく と考えられます。 だからこそ、来る AI 時代において、より迅速かつ的確な決断を下せるよう、私たちはこれからも様々な技術を学び続け、「認知の地図」を拡張していく必要があるのではないでしょうか。 その一つのアプローチとして、本稿で紹介した RTKB が少しでも皆さんのご参考となれば幸いです。 * 本稿に掲載された画像は AI により生成されました 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @satorin レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
こんにちは、グループ経営ソリューション事業部の 米久 保です。本記事は 電通総研 Advent Calendar 2025 15日目の記事です。 アーキテクト思考とは この記事において、 アーキテクト思考 とは ITアーキテクト が仕事をするうえで身につけておくべき思考方法、ツールや フレームワーク 、その利用方法を総称するものとします。その有用性はアーキテクトの枠を超えて、他の職種にも大いに役立つものです。 アーキテクト思考と呼べるものには、今回取り上げる「雲」「 弁証法 」の他にも、「 メンタルモデルの構築 」「システム思考」「推論」「 トレードオフ 分析」「分割統治」などさまざまなものがあります。 対立の解消は課題解決の鍵 アーキテクトの仕事とは、上位目的を達成するための全体構想を描き、実現への道筋を計画し、その実行を主導することです。その過程では大小さまざまな問題が発生し、それらを乗り越えてゴールへ向かうために、日々課題解決が求められます。 課題解決が一筋縄ではいかないのは、相互に対立する要件や事象がしばしば存在するためです。AとBを両立させることが困難な、「あちらを立てればこちらが立たず」という状況です。英語でいうとTrade-off( トレードオフ )ですね。 このようなとき、双方のメリットとデメリットを挙げて比較評価したうえで、選択という意思決定を下すのが トレードオフ 分析です。アーキテクトとして、 トレードオフ 分析を適切に行う技術を身につけることの重要性は言うまでもありません。が、 トレードオフ 分析に入る前に、一呼吸入れて クリティカル・シンキング で問いかけてみましょう。 本当にその対立は、二者択一で選択しないといけないのでしょうか? 対立を乗り越える思考法 私たちは、対立している(と思われる)二つの主張を目にしたとき、どちらかを選ばなければならない、すなわち二律背反の関係であると考えがちです。思い込み、あるいは思考のバイアスを克服するためには、それに適した思考ツールを活用するのがよいでしょう。 この記事では「雲」と「 弁証法 」をご紹介します。 雲 「雲」は、書籍『ザ・ゴール』で有名な エリヤフ・ゴールドラット 博士の生み出した TOC 思考プロセスの道具の一つです。「 クラウド 」「対立解消図」と呼ばれることもあります。雲では、対立関係を図に描くことで、その解消の糸口を見出します。 具体例で考えましょう。ソフトウェア 開発プロセス において、レビューのリードタイムや 工数 、レビューアの負荷はしばしば問題となります。 開発者 「レビューがなかなか返ってこない」「指摘事項の対応に時間がかかる」「レビューをもっと簡素化してほしい」 レビューア 「品質担保のためにしっかりと厳格なレビューをしなくては」「指摘すべき点が多くてどうしても時間がかかる」 対立関係にある、「レビューを厳格化したい」という主張(D)と「レビューを簡素にしたい」という主張(D')を起点に雲を描くと、次の図のようになります。 Dからの矢印の先には、Dを必要条件(つまり上位の目的)とする項目を描く(B) 同様にD'を必要条件とする項目を描く(C) BとCの両方を必要条件とする、共通の目標を描く(A) 雲は、対立関係は解消できるはずというスタンスを取ります。どうやって解消するのでしょうか? まずは、DとD'が暗黙の前提としている事柄や仮定を洗い出します。 レビューを厳格化(D) 厳格なレビューを行うには時間がかかる レビューを簡素化(D') レビューを簡素化すればリリースサイクルの短縮が可能 これらの前提、仮定を疑ってみます。 厳格なレビューには必ず時間がかかるのだろうか。時間を削減する方法があるのでは? レビューを簡素化して品質の悪いソフトウェアをリリースしたら、問合せ対応や障害対応に時間を要して全体としてはリリースサイクルが長くなるのでは? 以上を踏まえて、たとえば以下の施策を打つことで、厳格なレビューを行い、なおかつリードタイムを短くしよう、というのが雲による対立解消のアプローチです。 静的解析ツールや、AIレビューを導入する Pull Requestのサイズを小さくする 弁証法 弁証法 の歴史は古く、 古代ギリシャ の哲学者 ソクラテス が対話を通じて真理に近づく方法として用いた問答法は、 弁証法 の一種とされています。現代で使われる 弁証法 の基礎論理を確立したのはドイツの哲学者 ヘーゲル (1770年~ 1831年 )です。 ヘーゲル の 弁証法 は以下のプロセスを取ります。 命題(テーゼ:正)の提示 それと矛盾する、または否定する命題(アンチテーゼ:反)の提示 それらを本質的に統合した命題( ジンテーゼ :合)の考案 先と同じ例を使って考えてみましょう。 テーゼ(正) レビューを厳格に行う アンチテーゼ(反) レビューを簡素化する これらを統合した、高次の解決策を考えます。そのためには、それぞれのテーゼの本質は何かを考えることが必要です。 テーゼ(正) レビューを厳格に行うのはなぜか? どのような価値観に基づくのか? 品質が重要である ソフトウェアの一貫性が大事である 良いソフトウェアを作る人、組織を育てていきたい アンチテーゼ(反) レビューを簡素化するのはなぜか? どのような価値観に基づくのか? 顧客に素早く価値を届ける必要がある フィードバックループ を短縮して継続的に学習し改善すべき それぞれが大事にしている価値や目指す方向性を踏まえて、 目的そのものから問い直す のが 弁証法 的アプローチです。 ジンテーゼ (合) レビューの目的を再定義する:学習する組織としてのレビュー 具体的な施策としては、たとえば以下のようなア イデア が考えられるでしょう。 対面でレビューを行い、ペア設計/ ペアプロ で設計の改善や リファクタリング を行う 対面レビュー実施時のトラン スクリプト やgit差分データを生成AIに要約させ、レビュー ガイドライン を更新する レビュー ガイドライン を活用して生成AIによる事前レビューを実施する まとめ この記事では、アーキテクト思考として「雲」と「 弁証法 」をご紹介しました。これらは、二律背反のトラップから逃れ、対立を解消したり、対立を乗り越えたりする方法として利用できる思考ツールです。 日々の業務でうまく活用することができれば、強力な武器となることでしょう。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @tyonekubo ( Shodo で執筆されました )
アバター
はじめに こんにちは、グループ経営ソリューション事業部の永井です。 私は、2017年から aiuola(あいうぉーら) と Ci*X(さいくろす) という自社プロダクトの開発を担当しています。 aiuola および Ci*X のファーストリリースは 2018年 10月。それ以降は、エンハンスバージョンアップを重ね、製品の機能を強化してきました。 エンハンス開発の要件定義~設計フェーズでは、 Google の Design Docs を参考にした独自のドキュメントを用いて設計をしています。 この記事では、その具体的な内容と運用方法についてご紹介します。 ※ 本記事は 電通総研 Advent Calendar 2025 14日目 の記事です。 aiuola 、 Ci*X の開発スタイル まず、 aiuola 、 Ci*X チームのエンハンス開発の流れを簡単に説明します。 aiuola 、 Ci*X は、半年ごとに新しいバージョンをリリースしています。開発期間 6ヶ月は、大まかに、 要件定義~設計:1~2ヶ月 実装:2~2.5ヶ月 テスト:1~1.5ヶ月 出荷準備:0.5~1ヶ月 とフェーズが分かれています。 全体としてはウォータフォール型のように見えますが、 アジャイル の手法を取り入れ、2週間ごとのスプリントを回しながら開発を進めています。 (詳しくは、以前の記事もご覧ください! → エンタープライズアプリケーションプラットフォーム「aiuola」におけるアジャイル開発 ) エンハンス開発で対応する案件には、お客様からの要望や、社内で企画した新機能、不具合の修正などが含まれます。 それらの案件は、開発チームの バックログ に登録されます。そして優先度付けを行ったうえで、 6ヶ月 の開発期間に収まるよう対応案件を決定します。 Design Docs とは Design Docs は、直訳すると「設計書」ですが、ソフトウェア開発界隈では、 Google の Design Docs を指すことが多いでしょう。詳細はリンク先のブログ投稿を参照していただきたいのですが、ここではその内容を簡単に説明します。 Design Docs の目的は、ソフトウェアの目的(どのような課題を解決するか)を明確にし、その実現方法と トレードオフ について議論することで、チーム内での認識の統一を図ることです。また、あとから、「なぜこのような意思決定をしたのだろう?」と疑問に思ったとき、過去の Design Docs を参照することでその経緯をたどることも可能となります。 Design Docs には決まったフォーマットはありませんが、一般的に以下のような項目が含まれます。 コンテキスト:このドキュメントを読むにあたり必要となる知識 目標とやらないこと:ソフトウェアや機能が何を目的としたものか、どんな課題を解決するか。逆に、対象外とする課題は何か 設計:システム アーキテクチャ やデータモデル、 API 設計、制約など 代替案:検討した他の設計案と、評価(採用しなかった理由、 トレードオフ など) その他の関心ごと:開発の進め方やテストの手法、セキュリティ対策、パフォーマンス最適化、運用・保守など 作成したドキュメントは、レビュープロセスを経て実装フェーズに進みますが、実装中に設計の見直しが必要になった場合は、ドキュメントを修正し、軌道修正をしながら開発を進めていくことができます。 aiuola 、 Ci*X の Design Docs ここでは、私たちが使っている Design Docs のフォーマットや作り方、レビュープロセスについて、ご紹介します。 (ちなみに、チーム内では「Design Docs」ではなく、「検討資料」と呼んでいます。なぜ「検討資料」にしたのかは忘れてしまいました 笑) Design Docs のフォーマット aiuola 、 Ci*X では、エンハンス開発を行う際、案件ごとに Design Docs を作成することとしています。 Design Docs のフォーマットは、試行錯誤の結果、以下の項目を含めることになっています。項目とその書き方を以下にまとめました。 (実物とは少し異なりますが、大まかな構成は同じです) 概要 要望の背景・解決したい課題 このエンハンス案件によって、どのような課題を解決するかを明らかにします。お客様のご要望はいったん受け止めますが、 aiuola 、 Ci*X はパッケージ製品なので、製品としてどのような対応をするのがベストかを慎重に検討する必要があります。 要件 このエンハンス案件の要件を定義します。必須な要件(Must)、可能なら実現したい要件(Want)をまとめるとともに、実現しないことも(あれば)記載します。 仕様検討 前提知識 このドキュメントを読む人が知っておくべき用語や、既存機能の簡単な説明を記載します。 他社製品の調査 市場に既に存在する製品に、今回の要件を満たせる類似機能が存在する場合、どのような方法で実現しているのか、公開情報を調査します。先行している製品を参考に、よりよい仕様を実装することで、競争力を高めます。 法律・制度の調査 エンタープライズ アプリケーションは、法律や制度によって仕様が自ずと決まってしまう場合があります。法律や制度を正確に理解し、それを遵守した実装を行うことが求められます。 業務の検討 エンタープライズ アプリケーションの場合、業務プロセス内にシステムが登場することを想定した設計を行う必要があります。 仕様案 アプリケーションの仕様案をまとめます。ここでは、いわゆる外部設計に相当する内容を記載します。複数の仕様案がある場合、それらを比較し、このエンハンスバージョンで最適な案を選定します。 制約 例えば、エンハンスバージョンアップ後に 後方互換 がなくなる箇所など、ユーザーに影響のある制約事項があれば、まとめておきます(なるべく、そういった制約が生まれないような仕様案を模索します)。 技術調査 新しい要素技術を導入するなど、実装に入る前に調査・検証が必要となる場合は、その内容と結果を記載します。 設計 この章には、いわゆる内部設計に相当する内容を記載します。 DB設計、 アーキテクチャ 設計、画面設計、バッチ設計、 API 設計、非機能要件の設計など、必要となる項目を洗い出し、記載します。 改修を行わないが、当該案件の影響を受けるため リグレッション テストを実施しておいた方がよい機能も明記しておきます。 その他 外部サービスとの連携で必要な調整など 社外のサービスと連携するような案件の場合、先方との調整が発生することもあるため、そういった情報はまとめておきます。 他チームとの連携で必要な調整など 例えば、インフラ構成の変更が必要な案件など、社内の他のチームにも作業を依頼しなければならない場合、その内容をまとめておきます。 リリース時・リリース後の注意事項 本番環境へのリリース作業中、特別に実施する必要がある作業があれば、その内容を記載しておきます。また、リリース後に利用者側で必ずやらなければならない作業がある場合も、その内容を記載しておきます。 仕様相談やレビューの記録 ドメイン エキスパートやテッ クリード と行う仕様の相談ミーティングや、レビュー時のやり取りを記録に残しておきます。この項があることによって、どうしてこのような仕様になっているのか、あとから追いかけることができます。 私たちは、 Confluence というツールで Design Docs を管理しており、小規模な案件であれば1ページで書ききってしまうのですが、大規模な案件の場合、項目ごとにページを分割したり、階層化したりして、読みやすいドキュメントになるよう工夫しています。 また、上記項目はすべて必須というわけではなく、案件内容によって必要な箇所のみ記載することにしています。 Design Docs のレビュー Design Docs を作成したら、 ドメイン エキスパートやテッ クリード によるレビューを実施します。 案件の規模や内容によっては、すべてを書き終えてレビューを依頼するのではなく、少人数での仕様相談会や途中段階でのレビューを実施することもあります。 仕様相談会やレビュー時のやり取りは、意思決定の根拠として重要であるため、 Design Docs の 仕様相談やレビューの記録 の項に記録しておきます。 実装中にも修正を加えていく レビュープロセスを通過し、いよいよ実装、となりますが、まだ Design Docs の役目は終わりません。 実装中に、設計の見直しが行われたり、新たな影響範囲が見つかったりすることはよくあります。 その場合、 Design Docs を修正し、新たな方針のもと実装を進めていくこととします。 テストと Design Docs テスト担当者は、開発者の作成した Design Docs を参照し、テスト仕様書を作成します。 Design Docs に、影響機能が網羅的に記載されていることで、テスト範囲が明確になります。 なお、実装中も Design Docs の修正は続いているため、実装完了後、テスト実施に入る前に、必ずテスト仕様書にも Design Docs 最終版の内容を反映させることとしています。 Design Docs と仕様ドキュメント Design Docs は、エンハンス案件の開発時に利用するもので、作り捨てのドキュメントです。 完成したアプリケーションの仕様は、 別途バージョン管理している、仕様ドキュメント(画面定義書、バッチ仕様書、 API 仕様書 など)への反映を行います。 過去の Design Docs の管理 ここまで来てようやく Design Docs はその役目を終えることとなります。 過去の Design Docs は、 当該エンハンスバージョンの Design Docs 置き場に格納されます。 おわりに 今回は、私たち aiuola 、 Ci*X チームで利用している Design Docs についてご紹介しました。 元の Google の考え方を参考にしつつ、チームにフィットするようカスタマイズした結果、現時点ではこのような形になっていますが、チームの状況によってこれからもどんどん改善されてよりよいものになっていくと思います。 本記事が、皆様の設計ライフの一助となれば幸いです。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 執筆: @nagai.satoshi レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
はじめに エンタープライズ 第一本部の佐藤です。 本記事は 電通 総研 Advent Calendar 2025 13日目の記事です。 さくらのクラウド がアプリケーションの実 行基 盤となる「AppRun」等の正式なサービスを2025年12月9日から開始しています。 この記事は、AppRunの使用を通じて国産 クラウド の現在地を把握してみるのが本旨となります。 はじめに AppRun 概要 コンテナレジストリのデモ 概要 イメージの作成 レジストリの作成 ユーザーの作成 イメ―ジのpush AppRunのデモ 起動の設定 起動確認 デプロイ戦略 今回作成したリソースの料金 感想 AppRun 概要 「AppRun」は、コンテナイメージをもとにアプリケーションを自動デプロイし、スケーリングや運用管理をシームレスに実行できるマネージドサービスです。 引用元: https://www.sakura.ad.jp/corporate/information/newsreleases/2025/12/09/1968222395/ このAppRunのデモの前に さくらのクラウド 上にイメージをpushしておく必要があるので、以下の手順を実施しています。 コンテナ レジストリ のデモ 概要 「コンテナ レジストリ 」はDockerなどのコンテナエンジンが扱うイメージファイルを保管する レジストリ 機能を さくらのクラウド 上で提供するサービスです。 引用元: https://manual.sakura.ad.jp/cloud/appliance/container-registry/index.html イメージの作成 デモで使用するコンテナのイメージを作成します。 今回試すサーバーはgoで以下のように記述しました。 package main import ( "fmt" "net/http" "log" ) func main() { http.HandleFunc( "/" , func (w http.ResponseWriter, r *http.Request){ log.Printf( "%s %sのリクエストがありました" , r.Method, r.URL.Path) fmt.Fprintf(w, "こんにちは!さくらのクラウド!" ) }) http.HandleFunc( "/health" , func (w http.ResponseWriter, r *http.Request){ log.Printf( "%s からヘルスチェック" , r.RemoteAddr) w.WriteHeader(http.StatusOK) fmt.Fprintf(w, "OK" ) }) log.Println( "Starting server on : 30080" ) err := http.ListenAndServe( ":30080" , nil ) if err != nil { log.Fatal(err) } } イメージをビルドし、 server-demo と名前を付けています。 レジストリ の作成 次にこのイメージをpushする レジストリ を作成します。 手順 通りに作成します。 以下のような画面で作成しました。 📝(余談)アイコンは自分で作成できるので マニュアル を参考に手書きの実家の犬アイコンを自作しました。 ユーザーの作成 以下の画像のユーザーのタブで新規に作成しました。 ユーザー作成時に指定したユーザー名とパスワードでDockerのログインを実施します。 イメ―ジのpush 次にdockerコマンドで server-demo イメージにタグを付与します。 docker tag server-demo:latest <レジストリ名>/server-demo:latest レジストリ へのログインを実施します。 コマンドは以下を実施します。 docker login [コンテナレジストリ名] Username: [ユーザ名] Password: [パスワード] タグ付けした server-demo イメージをpushします。 docker push <レジストリ名>/server-demo:latest このイメージが GUI のコンテナ レジストリ から確認できるのかなと思いましたが、私は見つけられませんでした。 AppRunのデモ 起動の設定 アプリケーションの作成画面で共有型を選択し、起動します。 以下のようにアプリケーションの設定をしました。 コンテナの設定は以下のとおりです。 IPアドレス の指定等も項目でありますが、家のネットワークは IPアドレス が固定できないのでスキップしました。 ログの送信はfluentbit, opentelemetry-collectorを使用するとありましたが、標準/エラー出力はそのままログに記録されるので今回はこれを有効にして確認することにしました。 メトリクスの送信はPrometheus, opentelemetry-collectorが使用できるのですが、検証のための実装が手間だったので省きました。 ログやメトリクスを収集するモニタリングスイートの詳細は https://manual.sakura.ad.jp/cloud/appliance/monitoring-suite/index.html を参照してください。 この設定でアプリケーションを作成し、起動を確認してみます。 起動確認 画像のように起動を確認することができました。 ブラウザで公開URLにアクセスしたところレスポンスも表示されました。 ちなみにログでは以下のようになっています。 10秒間隔でヘルスチェックをしていますが、2025-12-15 00:27:11を最後にこれが止まっており、 トラフィック がないので最初に設定したゼロスケールが実行されていることが確認できます。 この状態で再度URLにアクセスするとレスポンスが返るまで体感で5~6秒かかりました。 デプロイ戦略 アプリケーションの構成を変更してデプロイから、新たなイメージをデプロイします。 新たなイメージは以下の記述を変えただけです。 fmt.Fprintf(w, "こんにちは!さくらのクラウド!v2" ) バージョン情報を確認するとv2が増えました。 この時点ですでに変更済みですが、操作の項目から トラフィック 管理を押下し、50%で分配するようにしました。 2回に1回は以下のようになりました。 今回作成したリソースの料金 サービス 料金 補足 コンテナ レジストリ 月額220円 ストレージ5GiB/1 レジストリ (超過分は1GiBごと22円) AppRun(共有型/0.5vCPU × 1GiB メモリ) 約5円/1時間 - モニタリングスイート(ログ) 月額110円 ユーザ領域 ストレージ1GiB/1件 参照: https://cloud.sakura.ad.jp/products/container-registry/ https://www.sakura.ad.jp/corporate/information/newsreleases/2025/12/09/1968222395/ https://cloud.sakura.ad.jp/news/2025/11/11/3service_formalization/ 感想 ベータ版の時から結構良く見られた感想ですが、設定項目が少なくて簡単でした。 ドキュメントが日本語話者が書いた日本語なのも理解しやすさに貢献していると思います。 設定項目が少ないメリットの一方で、それは柔軟性・拡張性との トレードオフ なのでここは手放しに素晴らしいとは言えないですね。 IaC化も Terraform for さくらのクラウド(v2) で、できます。 個人的にはゼロスケールしてくれるのがいいなと感じており、個人レベルのサービス立ち上げ時に トラフィック がないのに ランニングコスト だけがかかる問題を解消できそうです。 今回の検証で興味が出てきたので、体系的にサービスの概要をつかめるように さくらのクラウド検定 を受けてみようかなと思います。 受験費用が一般 11,000円(税込)で「 さくらのクラウド 」クーポン 50,000円分(一般・学生共通)がもらえるのがいいですね。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @sato.yu レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
こんにちは、 電通 総研のグループ経営ソリューション事業部でエンジニアをしている大浦です。 本記事は 電通総研 Advent Calendar 2025 12日目の記事です。 昨日の記事は、上野さんによる TanStack AI × Amazon Bedrockで作るAIチャットボット でした。 フロントエンドを起点に AI を組み立てる アーキテクチャ の考え方が興味深く面白かったです。 今回は、 モノリス で作られたアプリから、1つの機能をマイクロサービスとして切り出す判断についてお話します。 1 はじめに 1.1 注意点 2 そもそもマイクロサービスを切り出すべきか 2.1 モノリスで得られるPros 2.1.1 安定性 2.1.2 デプロイ容易性 2.1.3 実装容易性 2.2 モノリスのCons 2.2.1 認知負荷の増加 2.2.2 リードタイムの増加、リリースサイクルの長期化 2.2.3 柔軟なスケールが実施できない 2.2.4 ライブラリの衝突 3 切り出すと判断したら何を切り出すか 3.1 切り出す価値が高い機能 3.1.1 リリースサイクルや設定変更の周期が製品本体とは違う 3.1.2 機能固有の依存を持つ 3.1.3 機能単位でスケールアウトなどの構成を取りたい 3.1.4 特定の環境でのみ利用される機能 3.2 切り出しやすい機能 4 どのように切り出すか 4.1 モノリスで実現した場合 4.1.1 通知サービスのインターフェース 4.1.2 通知サービスの実行クラス 4.1.3 build.gradleの依存 4.2 マイクロサービスによる切り出しを行った場合 4.2.1 (補足) マイクロサービスの実装言語としてGoを選んだ理由 4.2.1.1 Goは単一バイナリとして配布できる 4.2.1.2 高並行な I/O 処理を素直に書ける 4.2.1.3 起動が速く、リソース特性が読みやすい 4.2.2 モノリス側(Spring) 4.2.3 切り出したマイクロサービス側 4.3 この例についての補足 (本番運用を考えた場合) 5 まとめ 1 はじめに SaaS アプリケーションの構成として、マイクロサービス化を進めているというサービスについて目にする機会が増えました。 あわせて「最初からマイクロサービスで作るのではなく、 モノリス で作り始めて必要になったらマイクロサービス化しましょう」という意見を聞くことも多いかと思います。 Martin Fowlerも以下のように述べています。 「成功したマイクロサービスのほとんどは、最初からマイクロサービスだったわけではない。 まずモノリスとして作られ、それが肥大化して“割らざるを得なくなった”結果として生まれている。」 「逆に、最初からマイクロサービスとして作られたシステムで、うまくいった例はほとんど聞かない。 私の知る限り、そうした多くは深刻な問題を抱えることになっている。」 ※原文は以下の2点(著者意訳) 1.Almost all the successful microservice stories have started with a monolith that got too big and was broken up 2.Almost all the cases where I've heard of a system that was built as a microservice system from scratch, it has ended up in serious trouble. ref) https://martinfowler.com/bliki/MonolithFirst.html マイクロサービスに分割断面を見出し「分割する」という話は書籍やネットで見つけることができます。 ( ドメイン 境界で切りましょう。チーム構成で切りましょう。など) 本記事では、大きな分割断面を見つけるのではなく、 モノリス をマイクロサービス化するための第一歩として 「機能を切り出す」ということにフォーカスした話をします。 ビジネスの拡大に合わせて成長してきた モノリス アプリを前提に以下の3点を扱います。 そもそもマイクロサービスを切り出すべきか 切り出すと判断したら何を切り出すか どのように切り出すか 1.1 注意点 以降は筆者が今までに モノリス から機能を切り出す or 切り出さない判断をしてきた中で考えてきたことを元に記載しています。 正解を提示するよりは、判断のための問いや観点を共有することを目的としています。 もし、誰かにとって議論のきっかけになればとても嬉しいです。 2 そもそもマイクロサービスを切り出すべきか 「プロジェクト開始早々にマイクロサービスにするな。」という意見が広く納得されている背景として、 モノリス ならではの Pros/Cons があります。 マイクロサービス化は、「現状の モノリス から得られるProsを失ってでも、Consを解消する価値がある」と合意出来ている場合に実施を検討します。 2.1 モノリス で得られるPros 2.1.1 安定性 エンタープライズ アプリでは基本的に、ACID特性を持つ トランザクション が重要です。 モノリス 構成(=多くの場合単一のDBで構成)である限り、 トランザクション の整合性をシンプルに保てます。 もし機能を 疎結合 にするために複数のDBにまたがった トランザクション を構成する場合には、分散 トランザクション や結果整合性など、より複雑な判断が必要です。 2.1.2 デプロイ容易性 モノリス は通常デプロイ単位が1つであり、実行や停止、スケールをまとめて実施できます。ログも集めやすいです。 特にオンプレミス環境での動作が求められる場合には、デプロイ単位が1つであることで、運用における各種手順を容易にできるなどのメリットもあります。 2.1.3 実装容易性 モノリス は基本的に、 コンパイル 時にアプリ全体が含まれます。 Java などの静的型付け言語では、型やインターフェースの不一致を実行前に コンパイル エラーとして検知できます。 2.2 モノリス のCons 2.2.1 認知負荷の増加 製品のコードベースが大きくなるにつれて、1つの機能変更の影響範囲が広がりやすくなります。 これは、コードベースの認知負荷の増加に繋がります。 2.2.2 リードタイムの増加、リリースサイクルの長期化 変更時の影響範囲の広がりは、修正時のビルド時間、検証事項の増加、変更時の 回帰テスト 範囲の増加に繋がります。 この結果、リードタイムが伸び、変更が慎重に実施されることでリリースサイクルが長くなる、という問題に繋がります。 これは、新機能の知見を持ったチームがPoCを実施する場合の足かせになります。 2.2.3 柔軟なスケールが実施できない 単一ファイルとしてデプロイする以上、特定の機能だけスケールさせる。といったことはできません。 例えば、特定の時期にだけ重い処理がある場合でも、アプリケーション全体をまとめてスケールアップさせる必要があります。 また、単一デプロイ単位で大きくなるとメモリフットプリントが増加し、スケール時に重要な起動時間の増加にもつながります。 2.2.4 ライブラリの衝突 外部ライブラリなどの依存追加により、ライブラリの衝突などがおこるようになる可能性が高まります。 3 切り出すと判断したら何を切り出すか では、製品が育ってきて モノリス の辛みが顕在化してきた場合に、どの機能を切り出すかを考えてみます。 切り出しは多くの場合、少なくとも以下の2軸を検討する必要があります。 切り出す価値があるか 切り出しやすいか 以下に、切り出す価値が高いと考える機能の性質を記載します。 切り出す価値が、切り出しやすさやマイクロサービス化の辛みを引き受けてでも実現する価値があるかを検討し、切り出しポイントを決めましょう。 3.1 切り出す価値が高い機能 3.1.1 リリースサイクルや設定変更の周期が製品本体とは違う 競争優位性を得るために迅速な フィードバックループ を回すことが求められる機能は、切り出して本体と独立してリリースできるとビジネス上の効果が高いです。 「○○機能チーム」など特定のチームが担当している機能の場合には、他チームとのコミュニケーションパスを限定することで生産性を高めることにつながることもあります。 3.1.2 機能固有の依存を持つ サードパーティー SDK が必要である機能や、外部サービスへの依存が強い機能を切り出すと、製品本体の依存関係を単 純化 できます。 依存ライブラリが増えると、ライブラリ衝突のような問題に突き当たることがあり、依存が増えるごとにリスクは高まります。 また、もしライブラリの 脆弱性 などが出た場合にも影響範囲の局所化や修正の反映が迅速になります。 ライブラリ依存を増やさないようにするための手段としての切り出しも価値があると考えています。 3.1.3 機能単位でスケールアウトなどの構成を取りたい BI処理など、データの増加に伴いリソース使用量や時間の増加が大きい機能は、切り出すと個別のワークロードを見たスケール判断ができます。 また、月末だけ高負荷がかかるなど ピーキー な振る舞いを持つ機能は、メインのアプリはそのままでスケールアップ/スケールアウトが実現できます。 3.1.4 特定の環境でのみ利用される機能 アドオンやオプション機能など、利用有無が顧客や環境によって異なる機能を切り出せると、製品本体品質の維持につながります。 本体から分離することで、認知負荷やテスト範囲を抑えられます。 もし モノリス の中で実現する場合、アドオンやオプションの変更が製品本体の再テストやバージョンアップの足かせとなることがあります。 3.2 切り出しやすい機能 切り出しやすさについても記載します。 ただ、切り出しやすさよりも切り出す価値の方が重要です。 切り出す価値が高い機能であっても、以下のような性質がある場合には切り出しコストに見合わないことが多く、切り出しを行わない根拠になると考えます。 データの所有権が分離できない 厳密な同期 トランザクション が必須 境界仕様が頻繁に変わる 4 どのように切り出すか ここでは、具体例として Spring Framework で作成されている経費申請アプリケーションの通知機能を切り出します。 Slack, Teams, LINE, メールと、複数の通知先をもつケースを想定しています。 最初に留意点を記載します。 以下の通知は、マイクロサービス分割の中でも比較的易しい題材となります。 = 状態をほぼ持たず、結果整合性を許容しやすく、失敗しても業務停止になりにくい だからこそ、 モノリス からの最初の切り出しを試すうえで有効と考えます。 また、本番運用をするうえで考慮が必要なポイントについては、「4.3 この例についての補足 (本番運用を考えた場合)」として後述しています。 4.1 モノリス で実現した場合 まずは、Spring Bootで作られた モノリス アプリです。 マイクロサービスとして切り出す前の状態として提示しています。 ※今後の説明に重要な部分のみ記載しています。 ※通常のSpringアプリの範囲なので全ては説明していません。 もし動くコードを見たい場合は、以下のコードと共に生成AIに聞くことで生成してくれると思います。 4.1.1 通知サービスのインターフェース Java においては以下のようなインターフェースがあります。 public enum NotificationChannel { SLACK, TEAMS, LINE, EMAIL } public record NotificationRequest( NotificationChannel channel, String to, // SlackのチャンネルID / メールアドレス / LINEのuserId など String subject, // 表題 String message // 本文 ) {} public interface NotificationService { void send(NotificationRequest request); } 4.1.2 通知サービスの実行クラス NotificationServiceを実装したクラスNotificationServiceImplは以下です。 ※大規模 エンタープライズ アプリケーションであればStrategyパターンを用いて、各通知先ごとにクラスを作る構成にすることが多いと考えますが、ここでは簡単のため、全ての実処理をNotificationServiceImpl内に記載しています。 // package jp.co.example.notification.infrastructure; @Service public class NotificationServiceImpl implements NotificationService { @Override public void send(NotificationRequest request) { switch (request.channel()) { case SLACK -> sendSlack(request); case TEAMS -> sendTeams(request); case LINE -> sendLine(request); case EMAIL -> sendEmail(request); default -> throw new IllegalArgumentException( "Unsupported channel: " + request.channel()); } } private void sendSlack(NotificationRequest request) { // Slack用の通知処理 } private void sendTeams(NotificationRequest request) { // Teams用の通知処理 } private void sendLine(NotificationRequest request) { // Line用の通知処理 } private void sendEmail(NotificationRequest request) { // Eメール用の通知処理 } } 4.1.3 build.gradleの依存 各種通知処理に SDK を利用する場合、build.gradleやpom. xml にSlackやLineなどの SDK への依存が追加されます。 この通知のためにしか使わない多くの SDK が追加され、Jar Hellの危険が増します。 4.2 マイクロサービスによる切り出しを行った場合 ここでは、実際の通知処理をGoで書かれたマイクロサービスに切り出してみます。 4.2.1 (補足) マイクロサービスの実装言語としてGoを選んだ理由 マイクロサービス側の実装言語は、必ずしも Go である必要はありません。 Java 、Kotlin、Node.js、 Python など、要件を満たす選択肢は複数あります。 チームの技術スタックを統一したい場合や、重厚な ドメイン ロジックを持つ機能を切り出す場合には、 Java を選ぶことも十分に妥当と考えます。 本記事の例でGoを選んでいる理由は、 「通知機能を切り出したマイクロサービス」という文脈において、運用・構成のコストが低い と判断しているためです。 具体例をいくつか挙げます。 4.2.1.1 Goは単一バイナリとして配布できる Goでビルドしたアプリケーションは、単一の実行バイナリとして配布できます。 JVM やランタイム、依存ライブラリを別途考慮する必要がなく、以下のような運用上のメリットがあります。 デプロイ、プロセス常駐のための運用手順が単純 実行環境の差異によるトラブルが起きにくい 4.2.1.2 高並行な I/O 処理を素直に書ける 通知処理は、多くの場合(CPUバウンドではなく) I/O バウンドです。 Webhookや外部 API 呼び出しを多数並列に処理するケースでは、goroutineによる軽量スレッドが実装と保守の両面で扱いやすいと感じています。 (また主観ですが、async/awaitや.then()などを駆使するよりも個人的にgoroutineの方が読みやすいです)。 4.2.1.3 起動が速く、リソース特性が読みやすい Goアプリケーションは起動が速く、メモリ使用量も比較的予測しやすいです。 これは、以下のような場面で効いてきます。 オートスケール時の立ち上がり 障害復旧時の再起動 4.2.2 モノリス 側(Spring) まず、 Java 側は通知を呼び出すだけにします。 = 通知処理を切り出したマイクロサービスに委譲する。 @Service public class NotificationServiceImpl implements NotificationService { private final RestTemplate restTemplate; private final String baseUrl = "http://localhost:8081" ; // goプロセスのURL, 本来は設定から読み込む @Override public void send(NotificationRequest request) { try { String url = baseUrl + "/api/v1/notify" ; restTemplate.postForLocation(url, request); log.info( "Notification delegated: channel={}, to={}" , request.channel(), request.to()); } catch (RestClientException e) { log.error( "Notification delegation failed: channel={}, to={}" , request.channel(), request.to(), e); // 恐らくここは設計判断が必要、ログだけ出して握り潰す?業務エラーにする? throw new RuntimeException( "Failed to delegate notification" , e); } } } 通知を依頼するためのHTTPリクエストを投げるだけになっています。 実際に通知をする処理を委譲したことで、通知先毎の各種 SDK はアプリ本体側では不要になりました。 実際に送信されるリクエストは以下のような構造になる想定です 例) Slackへの承認依頼Post { " channel ": " SLACK ", " to ": " U0456ABCD ", " subject ": " 経費申請の承認依頼 ", " message ": " 【承認依頼】 \n 申請者: 山田太郎 \n 申請番号: EXP-2025-00123 \n 金額: 12,480円 \n 申請日: 2025/02/10 \n\n 以下のリンクから内容を確認し、承認または差戻しをお願いします。 \n https://expense.example.com/approval/EXP-2025-00123 " } 4.2.3 切り出したマイクロサービス側 Go側では、通知リクエストから適切な通知先に通知を送ります。 package main import ( "encoding/json" "log" "net/http" ) // 通知のリクエスト構造 type NotificationRequest struct { Channel string `json:"channel"` To string `json:"to"` Subject string `json:"subject"` Message string `json:"message"` } func main() { http.HandleFunc( "/api/v1/notify" , NotifyHandler) log.Println( "Notification Service running on :8081" ) if err := http.ListenAndServe( ":8081" , nil ); err != nil { log.Fatal(err) } } func NotifyHandler(w http.ResponseWriter, r *http.Request) { var req NotificationRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { log.Println( "Invalid request:" , err) http.Error(w, "invalid request" , http.StatusBadRequest) return } // 即レスポンスを返す(Spring側を待たせない) w.WriteHeader(http.StatusAccepted) // 通知を非同期で処理(goroutine利用) go func (req NotificationRequest) { switch req.Channel { case "SLACK" : sendSlack(req) case "TEAMS" : sendTeams(req) case "LINE" : sendLine(req) case "EMAIL" : sendEmail(req) default : log.Printf( "[goroutine] unsupported channel: %s \n " , req.Channel) return } log.Printf( "[goroutine] completed notification: channel=%s to=%s \n " , req.Channel, req.To) }(req) } func sendSlack(req NotificationRequest) { // Slack SDK or Webhook を呼ぶ処理 log.Printf( "[Slack] sending to %s \n message: \n %s \n " , req.To, req.Message) } func sendTeams(req NotificationRequest) { // Teams SDK or Webhook を呼ぶ処理 log.Printf( "[Teams] sending to %s \n message: \n %s \n " , req.To, req.Message) } func sendLine(req NotificationRequest) { // Line SDK or Webhook を呼ぶ処理 log.Printf( "[LINE] sending to %s \n message: \n %s \n " , req.To, req.Message) } func sendEmail(req NotificationRequest) { // Eメールを送信する処理 log.Printf( "[Email] sending to %s \n subject=%s \n message: \n %s \n " , req.To, req.Subject, req.Message) } 4.3 この例についての補足 (本番運用を考えた場合) 前述のGo実装は説明用の最小構成です。 もしこのまま本番に出してしまうと、以下のような問題が起きます。 goroutine実行中にプロセスが落ちた場合、 モノリス 側では成功したように見えるが実際には通知されない。(アプリ側のみ正常終了など) 二重送信が発生しても止められない。(通知処理完了後にアプリ側の処理が異常終了、マイクロサービス側のみ正常終了など) 障害時に「どの申請の通知が失敗したか」追えない。 上記に対応しようとする場合は、以下のような対応が検討対象になります。 相関IDの追加 申請番号など、 モノリス 側との一連の処理を紐づける情報を加える。 リトライ + バックオフ + デッドレターキュー(DLQ)の追加 通知先サービスが一時的に落ちたときのために再処理を可能にする仕組みを加える。 マイクロサービス側のプロセスが落ちても適切に動作するように、マイクロサービス側でDBを持つなど。 べき等キーの追加 申請書番号 + イベント種別 + 通知先 などを覚えておき、通知済みの場合は通知しない。 メトリクスとアラートの設定 通知の成功率や遅延、DLQ件数 などを監視対象にする。 このように、割と簡単な例でも本番で運用する場合には考慮すべきポイントが数多くあります。 この辺りは、 モノリス のConsとマイクロサービスのProsだけを見ている場合に見落とすことがあります。 できる限り、切り出した場合の大変さについても切り出しの検討段階で考慮したいですね。 5 まとめ 今回は、 モノリス で作られたアプリから、1つの機能をマイクロサービスとして切り出す判断について書いてきました。 そもそもマイクロサービスを切り出すべきか → マイクロサービス化する場合は、 モノリス のPros / Consを意識する。 → 特に、 モノリス のProsを考える。 切り出すと判断したら何を切り出すか → 切り出すことで得られる価値が高い機能を切り出す。 → 価値が高い機能であっても、切り出しやすさを含め トレードオフ を見る。 どのように切り出すか → 第一歩は、状態をほぼ持たず、結果整合性を許容しやすく、失敗しても業務停止になりにくい機能を対象にする。 → 本番運用において、マイクロサービス化で問題となる特別な考慮ポイントについて十分に検討する。 最後に、ここまではおおむねアーキテクトの観点でのみ切り出す判断を書いてきています。 実際のプロダクト開発においては、失うProsが運用に直撃します。 そのため、切り出し判断はアーキテクトだけでなく、保守/運用・SRE・QA・プロダクトチームなどを巻き込んで合意することになると思います。 今後も、製品の成功のために出来ることを考え周囲と議論しながら、製品開発を推進していきたいと考えています。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @oura.osamu レビュー: @nagamatsu.yuji ( Shodo で執筆されました )
アバター
こんにちは。 電通 総研の金融IT本部(兼XI本部)の上野です。 本記事は 電通総研 Advent Calendar 2025 11日目の記事です。 最近一番嬉しかったことと言えば、 AWS のコンソール検索で「ECR」と入力すると一発と入力するとコンテナ レジストリ へ飛べるようになったことです。 「ECSからの遷移」や「container registry」と検索していたのは私だけではないはずです。 さて、本日は最近登場したTanStackエコシステムの一つ、 TanStack AI を利用してチャットボットを作ってみました。様々なエコシステムで知られるTanStackですが、AIアプリケーション開発の分野に参入していました。 本記事では、TanStack AI の特徴や Vercel AI SDK との違いを解説し、 Amazon Bedrock と組み合わせてファイル添付対応のチャットボットを構築する方法を紹介します。 TanStack AIとは 主な特徴 TanStack AI が生まれた背景 TanStack AI vs Vercel AI SDK TanStack AI を選ぶべきケース Vercel AI SDK を選ぶべきケース TanStack AI の実装例 クライアント実装例 サーバー実装例 Vercel AI SDKの利用例(比較用) クライアント側の実装例 サーバー側の実装例 Amazon Bedrock とは Amazon Bedrock の特徴 Amazon Bedrock を使うメリット 他のクラウド AI サービスとの比較 利用可能なモデル 主な機能比較 選定の指針 TanStack AI × Amazon Bedrockとの接続 サーバー側の実装例 Amazon Bedrock アダプタークラス Express サーバー クライアント側の実装例 カスタム Connection Adapter useChat フックの使用 実装にあたってのポイント(1)StreamChunk の形式 実装にあたってのポイント(2)Extended Thinking(思考プロセス)のサポート ファイル添付のサポート デモ まとめ TanStack AIとは TanStack AI は、複数の AI プロバイダーに対応した統一インターフェースを提供する オープンソース SDK です。 TanStack AI Alpha: Your AI, Your Way において「AI ツールのスイスを目指す」と述べており、「特定のベンダーやプラットフォームに依存しない中立的な設計を意識して作られた」という意味だと理解しています。 TanStack AI takes a different approach. We're building the Switzerland of AI tooling. An honest, open source set of libraries (across multiple languages) that works with your existing stack instead of replacing it. 主な特徴 ベンダーロックインなし: OpenAI、Anthropic、 Google Gemini、Ollama など複数のプロバイダーに対応 完全な型安全性: TypeScript でエンドツーエンドの型安全を実現 フレームワーク 非依存:React、Solid、Vanilla JS など任意の環境で利用可能 マルチモーダル対応:テキスト、画像、音声、動画、ドキュメントをサポート MIT ライセンス: 完全な オープンソース TanStack AI が生まれた背景 TanStack の創設者 Tanner Linsley 氏は、既存の AI SDK (Vercel AI SDK ) に対する懸念から TanStack AI を開発しました。 TanStack AI は、これらの課題を解決する「中立的な」 SDK として設計されています。Cloudflare や Netlify がスポンサーとして参加しているのも、オープンで移植可能なツールが彼らのプラットフォームにも恩恵をもたらすからだと考えています。 TanStack AI vs Vercel AI SDK 項目 TanStack AI Vercel AI SDK 哲学 中立・ オープンソース Next.js/Vercel エコシステム統合 プロバイダー数 4(OpenAI, Anthropic, Gemini, Ollama) 多数 ストリーミング制御 ステート管理して明示的に管理 HTTP ストリーム生成までを強くサポート 成熟度 アルファ版(2025年後半〜) 安定版 デプロイ先 どこでも( AWS , Cloudflare, Netlify 等) どこでも、ただしVercel 最適化 現時点では圧倒的にVercel AI SDK が高機能ですが、Vercel提供の SDK はどうしてもプラットフォーム依存部分が出てきます。 標準 OSS として利用可能ではありますが、Next.jsと密接に統合されており、Vercelデプロイ時に最適化されています。例えばVercel Functionsの タイムアウト 指定などもセットで利用可能になっている等、Vercelデプロイしない場合は不要な機能がいくつかあります。 TanStack AI を選ぶべきケース Vercel 以外の環境( AWS 、Cloudflare 等)にデプロイする 将来的にプロバイダーを切り替える可能性がある TanStack エコシステムを使用している Vercel AI SDK を選ぶべきケース Next.js + Vercel で開発している 今すぐ本番環境で使いたい(TanStack AI はまだアルファ版) 多数のプロバイダーに対応したい 豊富なドキュメントと事例が必要 TanStack AI の実装例 クライアント実装例 import { useChat } from '@tanstack/ai-react' import { fetchServerSentEvents } from '@tanstack/ai-react/connection' function Chat () { const { messages , sendMessage , isLoading } = useChat( { connection : fetchServerSentEvents( '/api/chat' ), } ) const handleSubmit = ( e : React.FormEvent ) => { e. preventDefault () const input = e. currentTarget . querySelector ( 'input' ) if (input?.value) { sendMessage(input.value) input.value = '' } } return ( < div > < div className = "messages" > { messages. map (( msg ) => ( < div key = { msg. id } className = { `message ${ msg.role } ` } > { msg.content } </ div > )) } </ div > < form onSubmit = { handleSubmit } > < input type = "text" placeholder = "メッセージを入力..." /> < button type = "submit" disabled = { isLoading } > 送信 </ button > </ form > </ div > ) } サーバー実装例 import { chat, toStreamResponse } from '@tanstack/ai' import { openai } from '@tanstack/ai-openai' export async function POST ( request : Request ) { const { messages } = await request.json() const response = await chat( { provider : openai( { apiKey : process .env.OPENAI_API_KEY } ), model : 'gpt-4o' , messages , } ) return toStreamResponse(response) } Vercel AI SDK の利用例(比較用) TanStack AIとの差分 submit時の sendMessage(input.value) 相当の処理は、 useChat フックがよしなにやってくれる。 クライアント側の実装例 import { useChat } from 'ai/react' export function Chat () { const { messages , input , handleInputChange , handleSubmit , isLoading , } = useChat( { api : '/api/chat' , } ) return ( < div > < div className = "messages" > { messages. map (( msg ) => ( < div key = { msg. id } className = { `message ${ msg.role } ` } > { msg.content } </ div > )) } </ div > < form onSubmit = { handleSubmit } > < input type = "text" placeholder = "メッセージを入力..." value = { input } onChange = { handleInputChange } /> < button type = "submit" disabled = { isLoading } > 送信 </ button > </ form > </ div > ) } サーバー側の実装例 import { streamText } from 'ai' import { openai } from '@ai-sdk/openai' export async function POST(req: Request) { const { messages } = await req.json() const result = streamText({ model: openai('gpt-4o'), messages, }) // ストリーミングレスポンスをそのまま返す return result.toDataStreamResponse() Amazon Bedrock とは Amazon Bedrockは、 AWS が提供するフルマネージド AI サービスです。Anthropic Claude、Meta Llama、 Amazon Titanなど、複数の基盤モデルを統一された API で利用できます。 Amazon Bedrock の特徴 マルチモデル : 複数ベンダーのモデルを同一 API で利用 IAM 統合 : AWS の認証・認可基盤を活用 VPC 対応 : PrivateLink でセキュアな接続 リージョン : 東京(ap-northeast-1)で利用可能 従量課金 : AWS 請求に統合、予測可能なコスト管理 Amazon Bedrock を使うメリット AWS の IAM による認証・認可を利用したい VPC PrivateLink でセキュアな接続をしたい AWS の請求に統合してコスト管理をしたい CloudTrail で アクセスログ を取得したい 他の クラウド AI サービスとの比較 マルチモデル AI サービスは各 クラウド ベンダーが提供しています。 AWS の Bedrock、 Google Cloud の Vertex AI、 Microsoft の Azure AI Foundry(旧 Azure AI Studio)を比較してみます。 ※名称や提供モデル・機能は逐次変化するため、公式の情報を参照してください。 利用可能なモデル プロバイダー Amazon Bedrock Vertex AI Azure AI Foundry Anthropic Claude ✅ Claude 4, 3.5, 3 系 ✅ Claude 3.5, 3 系 ❌ OpenAI GPT ❌ ❌ ✅ GPT-4o, GPT-4, GPT-3.5 Google Gemini ❌ ✅ Gemini 2.0, 1.5 ❌ Meta Llama ✅ Llama 3系 ✅ Llama 3系 ✅ Llama 3系 Mistral ✅ Mistral Large, Small ✅ Mistral ✅ Mistral Large 自社モデル Amazon Nova, Triton Gemini Phi-4, Phi-3 主な機能比較 機能 Amazon Bedrock Vertex AI Azure AI Foundry 統一 API Converse API Gemini API / Model Garden Azure OpenAI + Azure AI Inference ファインチューニング 可 可 可 RAG 機能 Knowledge Bases Vertex AI Agent Builder(統合中?Vertex AI Search) Azure AI Search 連携 エージェント Bedrock Agents Vertex AI Agent Builder Azure AI Foundry Agent Service ガードレール Guardrails for Bedrock Responsible AIツール群 Content Safety ※ファインチューニングはモデルによって可不可があります。 選定の指針 現時点ではモデルベースで利用基盤を選ぶことになるかと思います。 実際は各サービスのモデルカタログを見ながら、要件にあったモデルが使えるかどうか、対象のリージョンで提供されているかどうかを加味して比較する必要があります。 Claude を使いたい : Amazon Bedrock または Vertex AI OpenAI GPT を使いたい :Azure AI Foundry Gemini を使いたい :Vertex AI 今回は使い慣れた AWS 環境で Claude を利用したかったため、 Amazon Bedrock を選択しました。 TanStack AI × Amazon Bedrockとの接続 TanStack AI と Amazon Bedrock を接続する方法を説明する前に、TanStack AI の 2 層構造+呼び出すAIプロバイダーで、合計3つの役割に分かれていることを理解する必要があります。 クライアント層 useChat + Connection Adapter (SSE/HTTP/WebSocket) SSE ストリームのリクエストとレスポンスハンドリング fetchServerSentEvents 、 fetchHttpStream など サーバー層 chat() + Provider Adapter (OpenAI/Anthropic/etc) @tanstack/ai-openai 、 @tanstack/ai-anthropic など AI プロバイダー OpenAI / Anthropic / Google Gemini / Ollama サポートされていない場合は自前で実装が必要 TanStack AI は現在アルファ版であり、公式にサポートされているAIプロバイダーは以下の 4 つです: OpenAI ( @tanstack/ai-openai ) Anthropic ( @tanstack/ai-anthropic ) Google Gemini ( @tanstack/ai-gemini ) Ollama ( @tanstack/ai-ollama ) Amazon Bedrock 用のアダプターは公式には提供されていません 。Anthropic の Claude を使いたい場合、 @tanstack/ai-anthropic で直接 Anthropic API に接続することは可能ですが、 Amazon Bedrock 経由で利用したい場合はカスタムアダプターを実装する必要があります。 TanStack AIは公式にカスタム可能と謳っているため、本来クライアント層/サーバー層ともに Bedrockのアダプターを作成して接続したかったのですが、TanStack AIが未だα版であり、公開情報も少ないためにサーバサイドは自前で接続処理を設けます。 今回の実装方針は以下のとおりです。 サーバー側 : Amazon Bedrock SDK を直接使用し、TanStack AI の StreamChunk 形式で通信する クライアント側 :TanStack AI の useChat フック + カスタム Connection Adapter でレスポンスを受け取る この方法により、TanStack AI の useChat フックの恩恵(メッセージ管理、ローディング状態、型安全性)を受けながら、 Amazon Bedrock と接続できます。 サーバー側の実装例 Amazon Bedrock アダプタークラス AWS SDK を使用して Amazon Bedrock の Converse API を呼び出し、TanStack AI の StreamChunk 形式でストリームを返します。 import { BedrockRuntimeClient, ConverseStreamCommand, } from '@aws-sdk/client-bedrock-runtime' export interface StreamChunk { type : 'content' | 'thinking' | 'done' | 'error' id ?: string model ?: string timestamp ?: number delta ?: string // 増分トークン content ?: string // 累積コンテンツ role ?: string // 'assistant' finishReason ?: string error ?: string } // Adapterと言っているが、TanStack AIとのアダプタではなく単にawsのsdkから接続するBedrockへのAdapter export class BedrockAdapter { private client : BedrockRuntimeClient private modelId : string async * chatStream ( messages , options ): AsyncGenerator < StreamChunk > { const command = new ConverseStreamCommand( { modelId : this .modelId, messages : this .formatMessages(messages), additionalModelRequestFields : options.enableThinking ? { thinking : { type : 'enabled' , budget_tokens : 5000 } } : undefined , } ) const response = await this .client. send (command) for await ( const event of response.stream!) { if (event.contentBlockDelta?.delta) { const delta = event.contentBlockDelta.delta if ( 'thinking' in delta) { yield { type : 'thinking' , delta : delta.thinking, ... } } else if ( 'text' in delta) { yield { type : 'content' , delta : delta.text, role : 'assistant' , ... } } } if (event.messageStop) { yield { type : 'done' , finishReason : 'end_turn' } } } } } Express サーバー Bedrock アダプターを使用し、SSE 形式でクライアントに送信します。 import express from 'express' import { bedrock } from './adapters/bedrock-adapter.js' const app = express() const adapter = bedrock( { region : process .env.AWS_REGION || 'us-east-1' , modelId : process .env.BEDROCK_MODEL_ID || 'claude-sonnet-4' , } ) app.post( '/api/chat' , async ( req , res ) => { const { messages } = req. body // SSE ヘッダー設定 res.setHeader( 'Content-Type' , 'text/event-stream' ) res.setHeader( 'Cache-Control' , 'no-cache' ) res.setHeader( 'Connection' , 'keep-alive' ) const stream = adapter.chatStream(messages, { systemPrompt : 'Hello World' , enableThinking : true , } ) for await ( const chunk of stream) { res. write ( `data: ${ JSON . stringify (chunk) } \n\n ` ) if (chunk. type === 'done' || chunk. type === 'error' ) break } res.end() } ) クライアント側の実装例 カスタム Connection Adapter TanStack AI の ConnectionAdapter インターフェースを実装し、サーバーからのレスポンスを処理します。 import { useChat, type ConnectionAdapter } from '@tanstack/ai-react' import type { UIMessage, ModelMessage, StreamChunk } from '@tanstack/ai' // カスタム Connection Adapter function createBedrockConnection(): ConnectionAdapter { return { async *connect( messages: UIMessage[] | ModelMessage[], _data?: Record < string , unknown > , abortSignal?: AbortSignal ): AsyncIterable < StreamChunk > { // メッセージを Bedrock 形式に変換 const chatMessages = messages.map (( msg ) => ( { role : msg.role, content : extractTextContent(msg), } )) const response = await fetch ( '/api/chat' , { method : 'POST' , headers : { 'Content-Type' : 'application/json' } , body : JSON . stringify ( { messages : chatMessages } ), signal : abortSignal, } ) const reader = response. body ?.getReader() const decoder = new TextDecoder () let buffer = '' while ( true ) { const { done , value } = await reader!.read() if (done) break buffer += decoder. decode (value, { stream : true } ) const lines = buffer. split ( ' \n ' ) buffer = lines. pop () || '' for ( const line of lines) { if (line. startsWith ( 'data: ' )) { const data = JSON . parse (line. slice ( 6 )) as StreamChunk yield data } } } } , } } useChat フックの使用 カスタム Connection Adapter を useChat に渡して使用します。 function App () { const connection = useMemo(() => createBedrockConnection(), [] ) // TanStack AI の useChat フック const { messages , sendMessage , isLoading } = useChat( { connection , } ) return ( < div > { messages. map (( message ) => ( < div key = { message. id } className = { message.role } > { message.parts. map (( part , idx ) => { if (part. type === 'thinking' ) { return < div key = { idx } className = "thinking" > { part.content } </ div > } if (part. type === 'text' ) { return < div key = { idx } > { part.content } </ div > } return null } ) } </ div > )) } < form onSubmit = { ( e ) => { e. preventDefault () sendMessage(input) } } > < input type = "text" /> < button disabled = { isLoading } > 送信 </ button > </ form > </ div > ) } 実装にあたってのポイント(1)StreamChunk の形式 TanStack AI の useChat が正しく動作するためには、サーバーから返す SSE が以下の StreamChunk 形式に準拠している必要があります: // ContentStreamChunk { type : 'content' , delta: '新しい' , content: '新しい' , role: 'assistant' , id, model, timestamp } // ThinkingStreamChunk { type : 'thinking' , delta: '考え中...' , content: '考え中...' , id, model, timestamp } // DoneStreamChunk { type : 'done' , finishReason: 'end_turn' , id, model, timestamp } // ErrorStreamChunk { type : 'error' , error: 'エラーメッセージ' } この形式に準拠していれば、 useChat は自動的にメッセージを UIMessage に変換し、 thinking は ThinkingPart として、 content は TextPart として管理されます。 実装にあたってのポイント(2)Extended Thinking(思考プロセス)のサポート Claude Sonnet 4 / Opus 4 では Extended Thinking 機能が利用可能です。この機能を有効にすると、Claude が回答を生成する前の「思考過程」をストリーミングで取得できます。 // Thinking 有効時のレスポンス例 { type : 'thinking' , delta: 'ユーザーは...' , content: 'ユーザーは...' } { type : 'thinking' , delta: 'について質問して' , content: 'ユーザーは...について質問して' } { type : 'content' , delta: 'こんにちは!' , content: 'こんにちは!' } { type : 'content' , delta: 'ご質問に' , content: 'こんにちは!ご質問に' } { type : 'done' , finishReason: 'end_turn' } UI 側で thinking タイプのチャンクを別途表示することで、AI の思考過程を可視化できます。 ファイル添付のサポート これだけだと面白くないので、ファイル添付をサポートしてみます。 こちらもサーバーサイドが TanStack AI の場合はカスタムが難しそうですが、今回はサーバについてはBedrockの SDK を利用しているだけなので、クライアント側でファイル情報を管理する変更がほとんどです。 なお、Bedrock では Claude 系のモデルの場合以下の形式をサポートしています。 カテゴリ 形式 画像 JPEG , PNG , GIF, WebP ドキュメント PDF, CSV , DOC, DOCX, XLS, XLSX, HTML, TXT, MD 1. 添付ファイルの管理 TanStack AI の sendMessage はテキストのみを受け取るため、添付ファイルは別途管理します。 // 添付ファイルの型定義 interface Attachment { type : 'image' | 'document' mediaType : string // 'image/png', 'application/pdf' など data : string // Base64 エンコード済みデータ name : string } function App () { const [ pendingAttachments , setPendingAttachments ] = useState< Attachment []>( [] ) const attachmentsRef = useRef< Attachment []>( [] ) // ... } 2. ref を使った添付ファイルの受け渡し // カスタム Connection Adapter(添付ファイル対応) function createBedrockConnection ( getAttachments : () => Attachment []): ConnectionAdapter { return { async * connect ( messages , _data , abortSignal ) { const chatMessages = messages. map (( msg , index ) => ( { role : msg.role, content : extractTextContent(msg), // 最後のユーザーメッセージにのみ添付ファイルを付与 ...(msg.role === 'user' && index === messages. length - 1 ? { attachments : getAttachments() } : {} ), } )) // fetch でサーバーに送信... } , } } // 使用側 const connection = useMemo( () => createBedrockConnection(() => attachmentsRef. current ), [] ) 3. 送信時のタイミング制御 sendMessage を呼ぶ 前に attachmentsRef で管理するファイルを送信します。 const handleSendMessage = async () => { // 送信前に attachmentsRef を設定(connect 関数から参照される) attachmentsRef. current = [ ...pendingAttachments ] // UI 表示用に保存 setAttachments(( prev ) => [ ...prev, ...pendingAttachments ] ) // 入力をクリア setInput( '' ) setPendingAttachments( [] ) // メッセージ送信(この時点で connect が呼ばれ、attachmentsRef を参照) await sendMessage(messageText) // 送信完了後にクリア attachmentsRef. current = [] } 4. サーバ側での添付 サーバー側では、添付ファイルを追加する formatMessages関数を設け、 Converse API に渡す前にメッセージを整形します。 private formatMessages(messages: ChatMessage[]): BedrockMessage[] { return messages. map (( msg ) => { const content: ContentBlock [] = [] // テキストコンテンツ if (msg.content) { content. push ( { text : msg.content } ) } // 添付ファイル if (msg.attachments) { for ( const attachment of msg.attachments) { if (attachment. type === 'image' ) { content. push ( { image : { format : this .getImageFormat(attachment.mediaType), source : { bytes : Buffer .from(attachment.data, 'base64' ) } , } , } ) } else if (attachment. type === 'document' ) { content. push ( { document : { format : this .getDocumentFormat(attachment.mediaType), name : sanitizeDocumentName(attachment. name ), source : { bytes : Buffer .from(attachment.data, 'base64' ) } , } , } ) } } } return { role : msg.role, content } } ) } デモ まとめ TanStack AI と Amazon Bedrock を組み合わせることで、以下のメリットが得られます TanStack AI による型安全性、React フック、ストリーミング制御 Bedrock の エンタープライズ 機能(IAM 認証、 VPC 統合、ガバナンス) カスタムアダプターの実装は手間がかかりますが、TanStack AI の設計思想(AsyncGenerator パターン、StreamChunk 型)に従えば比較的シンプルに実装できます。 TanStack AI はまだアルファ版ですが、将来的には公式で Bedrock アダプターがサポートされる可能性もあります。それまでは、今回紹介したようなカスタムアダプターで対応できます。 ただし結論として、現時点で Bedrock等公式に接続が提供されていないAI基盤を利用する場合はVercel AI SDK を利用したほうが簡潔な構成にできます。 TanStack AIの一番の課題として、接続先のアダプタを自前で構える必要がある点が現時点でかなりの ボトルネック となります。 しかし、Vercel エコシステムにロックインされてしまわないためにも、TanStack AI の今後の進歩には期待しています。 以上、拝読ありがとうございました。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 執筆: @kamino.shunichiro レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
はじめに こんにちは。 エンタープライズ 第一本部・テクノロ ジー ビジネスユニット・テクノロ ジー ビジネス2部の多田圭佑です。 掲題の構成でシステムを構築するにあたり、基本的な構成であるにもかかわらず思わぬところでつまずいてしまったので、将来同じような問題に遭遇した方向けに書いています。 5分程度でサクッと読めますので、お付き合いいただければ幸いです。 本記事は 電通総研 Advent Calendar 2025 10日目の記事です。 つまずいたこと2つ 先に私がつまずいたことの2つを述べます。 CloudFront経由だと画面遷移でエラーになる 画像が表示されない システム構成 以下のような、 AWS では一般的な構成です。 (Internet) -> CloudFront -> ALB -> ECS ECS上にReact Router v7(SSR。ビルドツールはVite)を使用したシステムが稼働しており、それをCloudFront経由で公開しています。 CloudFrontはこのシステム以外でも使用しており、特定のパスパターンに合致した場合のみこのシステムにルーティングするよう設定しています。 1. CloudFront経由だと画面遷移でエラーになる 事象 前提としてCloudFrontでのパスパターンは後段で維持されます。 例えば、パスパターンを「aaa/bbb」としたときは以下のように転送されます。 ( HTTPS の終端はALBで、バックエンド(ECS)との通信はプライベートネットワーク内のHTTPです) AWS サービス URL CloudFront https://example.cloudfront.net/aaa/bbb ALB https://hogehoge.region.elb.amazonaws.com/aaa/bbb ECS http://localhost:8888/aaa/bbb 逆に言えば、CloudFrontを経由した場合は http://localhost:8888/aaa にはアクセスできません。 当初、私がルーティングを実装した際は以下のように routes.ts を設定しました。 こうすることで /aaa/bbb をベースとしてその配下のパスにアクセスできます。 export default [ route( '/healthcheck' , 'routes/healthcheck.tsx' ), // healthcheck用 ...prefix( 'aaa/bbb' , [ index( 'routes/index.tsx' )) route( '/ccc' , 'routes/ccc.ts' ), route( '/ddd' , 'routes/ddd.ts' ), ] ), ] satisfies RouteConfig; ただ、CloudFront経由だとインデックスへのアクセスは問題ないのですが、そこからの画面遷移に失敗していました。 ローカルやALB経由だと問題ありませんでした。 原因 React Router v7のルート・コードスプリット( 遅延ルート )では、クライアントは起動時に /__manifest というエンドポイントから「ルートID → チャンクURL」のマップを取得します。 今回はパスパターン経由で公開しているため /__manifest にアクセスしようとするとパスパターンに合致しないため404となってしまい、その結果チャンクのURLが分からずに動的 import が失敗していました。 ローカルやALB経由だとパスパターンの制約がないため問題がありませんでした。 対応 React Router v7では、アプリケーションのベースパスを指定する basename という設定があります。 今回の構成では、CloudFrontから /aaa/bbb というパスパターン経由でこのWebアプリケーションにアクセスしているため、この basename を設定することで問題を解決しました。 具体的には、 react-router.config.ts に以下のように設定を追加しました。 import type { Config } from "@react-router/dev/config" ; export default { ssr : true , basename : "/aaa/bbb" , // その他の設定... } satisfies Config; routes.ts は以下のようにシンプルになりました。 export default [ route( '/healthcheck' , 'routes/healthcheck.tsx' ), // healthcheck用。/aaa/bbb/healthcheck route( '/ccc' , 'routes/ccc.ts' ), route( '/ddd' , 'routes/ddd.ts' ), ] satisfies RouteConfig; この設定により、React Routerは /__manifest へのアクセスを /aaa/bbb/__manifest として解決するようになり、CloudFront 経由でも正しく マニフェスト ファイルを取得できるようになりました。 また、リンクやナビゲーションも自動的に /aaa/bbb を基準としたパスで動作するようになるため、ルーティング全体が正常に機能するようになりました。 2. 画像が表示されない 事象 ルーティングの問題は解決しましたが、今度は画像が表示されない問題が発生しました。 /images/icon.svg を取得しようとして404となっていました。 原因 原因を調査したところ、画像などの静的アセットファイルはReact Routerのルーティングとは別に扱われるため、 basename の設定だけでは対応できないことがわかりました。 静的ファイルは通常 public ディレクト リに配置されますが、これらのファイルへのパスも /aaa/bbb の プレフィックス を考慮する必要があります。しかし、ビルド時に生成される静的アセットへの参照パスは、 basename の設定では反映されません。 解決方法 この問題を解決するために、以下の対応を行いました。 1. Vite の base 設定を追加 vite.config.ts に base オプションを追加して、静的アセットのベースパスを明示的に設定しました。 ( react-router.config.ts とは若干異なるため注意です) import { defineConfig } from "vite" ; import { reactRouter } from "@react-router/dev/vite" ; export default defineConfig( { base : "/aaa/bbb" , plugins : [ reactRouter() ] , // その他の設定... } ); この設定により、ビルド時に生成される静的アセットへの参照パスに /aaa/bbb の プレフィックス が自動的に付与されるようになります。 2. 画像パスの設定 コンポーネント 内で画像を参照する際は、以下のように画像を直接インポートすることで、Vite が自動的に適切なパスに変換してくれます。 import iconSvg from "/images/icon.svg" ; function MyComponent () { return < img src = {iconSvg} alt = "Icon" />; // <img src="/aaa/bbb/images/icon.svg" /> } 以下のように 環境変数 を使用して文字列ベースで解決する方法もあります。こちらは動的に画像パスを生成する必要がある場合に有効です。 function MyComponent () { return < img src = { ` $ { import . meta . env . BASE_URL} / images / icon.svg `} alt="Icon" />; } まとめ CloudFrontのパスパターンを使用している場合は basename の設定が必要でした。この設定により /__manifest がprefix付きとなり、 マニフェスト ファイルの取得やルーティングが正常に動作します。 また、静的ファイルの参照についても同様に basename を考慮した設定が必要になります。 これから同様の構成でシステムを構築される方の参考になれば幸いです。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @tada.keisuke レビュー: @kinjo.ryuki ( Shodo で執筆されました )
アバター
はじめに こんにちは。クロス イノベーション 本部 クラウド イノベーション センター3年目の宮崎です。 AWS re:Invent 2025 で発表された AWS DevOps Agent を触ってみたので、概要を記載したいと思います。 AWS re:Invent 2025については、同じくクロス イノベーション 本部エンジニアリングテクノロ ジー センターの宮原さんが、速報記事を書いているので、是非下記の記事もご覧ください! AWS re:Invent2025 Keynote現地速報 また、 AWS re:Inventがそもそもどのようなものか把握したい方は、過去の私の記事もご覧いただけると幸いです。 AWS re:Invent 2024とAWS GameDayについて 本記事は 電通総研 Advent Calendar 2025 9日目の記事です! 記事の目的 新しいエージェントについての紹介 AWS DevOps Agentの機能やセットアップの流れの紹介 AWS DevOps Agentの位置づけ まず前提として、 AWS は re:Invent 2025で「Frontier Agents」という新しいコンセプトを掲げ、次の3つの新しいエージェントサービスを発表しました。 Kiro autonomous agent AWS Security Agent AWS DevOps Agent Frontier Agentsは、 AWS 公式のページでは「介入を必要とせずに何時間も動作する、自律的で大規模にスケーラブルなAIエージェント」という、新しいクラスのエージェントと定義しています。 従来の 「人間をサポートする AI アシスタント」から一歩進み、「チームのメンバーとして成果を出すことを期待されるデジタルワーカー」的な位置づけのサービス群だと捉えています。 引用:自律的で大規模に拡張可能なAIエージェント - Frontier agents – AWS フロンティアエージェントは、目標達成のために独立して動作し、同時実行タスクに対応するために大規模にスケールし、介入なしに数時間または数日間持続的に動作する自律システムです。個々のタスクを支援する従来のAIアシスタントとは異なり、フロンティアエージェントはチームの延長として機能し、多様なユースケースにわたって包括的な成果をもたらします。 この中で、AWS DevOps AgentFrontier agent – AWS DevOps Agent – AWSはシステムの障害対応(インシデントレスポンス)を加速し、将来の問題を未然に防ぐための運用エージェントで、具体的には、チケットなどをトリガーに自動でエージェントが起動し、メトリクス、ログ、最近のデプロイ履歴(GitHub)、トレース情報などを自動で分析し根本原因を特定・ユーザーに通知を行います。 また、システムの構成要素とその相互関係、デプロイ履歴を含むマップを自動構築し、影響範囲を正確に把握することが可能になります。 AWS DevOps Agent は、その中でも運用・インシデント対応を担う役割を持つ Frontier Agent です。 監視ツールやログを横断して自動で調査・分析し、インシデントの原因特定と復旧案、さらに再発防止策まで提案してくれる AWS の生成AIエージェントサービスとなっています。 インシデントの対応において、様々な情報を人間が行き来して疲弊するのではなく、人間は判断に注力できる未来はすぐそこに来ていると感じています。 インシデント対応における、以下のような課題を人間の介入無しに解決できることを狙っていると思われます。 課題 インシデント調査に時間がかかる CloudWatch / Datadog / New Relic / その他監視ツール / デプロイ履歴 / Runbook などを人がいちいち行き来して調べるのは大変。 ⇒ AWS DevOps Agentがこれらをまとめて見て、根本原因候補を自動で絞り込む。 夜間・休日オンコール負荷が高い問題 24/7 で人を張り付けるのはしんどいしコストも高い。 ⇒ AWS DevOps Agentが常時起動のオンコール要員として自動で調査開始し、対応案まで出してくれるので、 MTTR (復旧時間)を短縮しつつ負荷も軽減。 再発防止・改善の振り返りが十分にできていない インシデント後に振り返りや今後に向けた改善等をきちんとやる時間がない。 ⇒ AWS DevOps Agentが過去インシデントを横串で分析し、監視や構成・デプロイの改善案を提案してくれる。 AWS DevOps Agentの機能 参考: https://docs.aws.amazon.com/ja_jp/devopsagent/latest/userguide/what-is.html 上記の公式ドキュメントベースでご説明します: 1. 常時稼働の自動インシデント対応 アラートやチケットをトリガーに、自動で調査開始し、メトリクス、ログ、トレース、デプロイ履歴、コード、Runbook などを突き合わせ、原因候補と影響範囲を特定することができます。具体的な緩和策や復旧手順、 ロールバック 案までプランとして提示することが可能です。 2. チャットベースの インタラクティブ 調査 専用の DevOps Agent Web アプリ(Agent Space)から、視覚的に調査の状況や、人間が追加チャットでどこまで調査したか、他に影響はないか等も確認ができます。また、別チャットにて追加の調査や指摘もすることが可能です。 3. トポロジ学習とマルチツール連携 AWS リソースや各サービスの トポロジ(関係性)を自動で学習します。 CloudWatch、Datadog、New Relic、Dynatrace、Splunk などのオブザーバビリティツール、監視設定と連携し横断的に相関をとることも可能なようです。 4. 予防・再発防止のための改善提案 調査後に改善の提案を確認できます。また、過去インシデントのパターンを分析し、改善案を自動で提示することが可能です。 AWS DevOps Agentの管理・認証 今回、実際の検証を実施した際はIAM認証(既存のマネジメントコンソールのセッション)にて初期設定を実施しました。一方で、少し経つと AWS DevOps Agent Spaceウェブアプリのセッションが切れてしまいました。そこで、 AWS の公式ドキュメントを確認すると、IAM認証でのアクセスは30分までに制限されていました。 そのため、本番環境等において複数人に展開する場合はIAM Identity Centerが推奨されているようです。外部プロバイダーとの連携により集中管理することが可能になり、最大12時間のセッションが提供されます。 Setting Up IAM Identity Center Authentication AWS DevOps Agentのセットアップ手順 ※現状パブリックプレビュー期間中のため、 AWS DevOps Agent自体の使用料は発生しませんが、他のサービスへのクエリや API 呼び出し時に当該サービスから料金が掛かる場合は使用料金が発生します。 引用:Public preview pricing and limits マネジメントコンソールの検索欄に、 AWS DevOpsを入力すると出てきました。 現在はプレビュー版になっておりリージョンが制限されているので、 バージニア 北部に遷移します。 サービスページから「Begin setup」を押下し、エージェントスペースを作成します。 Agent Spaceの名前を設定し、画面に従って設定をします。 問題なく作成後、Agent Spaceの画面に遷移すれば完了です。 機能検証前の実施内容 AWS DevOps Agentを試す前に、調査対象となる環境、および仮トラブルを AWS DevOps Agentの公式ドキュメントの内容に基づき準備しました。 環境準備の詳細手順は以下のリンクからご覧ください。 Creating a test environment 内容は、準備されているIaCコードを用いてCloud FormationでEC2やLambdaの環境をデプロイ後、用意されたトラブルに対して AWS DevOps Agentを利用し、代表機能を見ていけるというものになっています。 AWS DevOps Agentを試したいが、対象のシステム環境がない方 が試す場合に打って付けです。 ※実際のアプリケーション等の検証環境がある方はそちらで検証した方が、実際の業務で何がどこまでできそうか把握しやすいと思います。 実際の調査の流れ 作成したAgent Spacesから画面右上の「Operator access 」を押下します。 すると別タブで AWS DevOps Agentの画面に遷移します。erator access から作成したエージェントに質問をします。 質問内容 以下を項目ごとに入力します。 (実際のプロンプトは対象の環境やエラーの状況によって変えてください。 今回は AWS DevOps Agentの公式ドキュメントの内容に基づき準備した場合のプロンプトになっています。) ※今後、日本語対応されるまでは調査したい内容を日本語から英語へ翻訳する1アクションが必要になりそうです。 Investigation details: Investigate high CPU utilization on test EC2 instance and Lambda error rate spikes generated by test stacks. Investigation starting point: Start from CloudWatch alarms AWS -AIDevOps-EC2-CPU-Test and AWS -AIDevOps-Lambda-Error-Test which are currently in ALARM. 内容を確認後「Start Investigation」を押下し、調査を開始します。 タイムライン形式で何の情報をインプットに、どのようなステップで調査をしているか、最終的に何を根本原因と判断したかまで等が把握できます。 視覚的で内容も把握しやすいものとなっています。 調査結果が約5分~10分ほどで出ます。 詳細を確認すると根本原因について記載しています。 実際のインシデントが起こった流れについても、詳細に記載されていました。 また、調査は並行で走らせることができました。 ※現状、同時に実行できるインシデント解決調査タスクの数は 3 までとなっています。 実際の本番利用においては、エラーに関連するリソースの指定や環境の指定により調査対象をプロンプトによって指定する等で、異なる視点からの調査を並行して実施することもできそうです。 まとめ AWS DevOps Agentは設定が簡単で、実際のエラーの 調査内容/理由 を時系列に沿ってUIで分かりやすく、詳細に解説してくれました。調査は並行で走らせることができ、根本原因が約5分~10分で直ぐに把握が可能です。 AWS DevOps Agentはインシデントの調査・根本原因の特定はもちろん、ポストモーテム(システムにトラブルや障害が発生したあとに行う事後分析)に非常に有効だと思いました。 そして何より、「チームのメンバーとして成果を出すことを期待されるデジタルワーカー」的な位置づけのサービスとして AWS DevOps Agentで一次調査を任せることができるレベルだと言うことが分かりました。 現状、二次対応で実際のリソースの設定変更などについてはKiro CLI (旧 Amazon Q Developer CLI )で AWS CLI の認証情報との組み合わせにより設定変更や環境参照なども可能になっています。 日本語対応やリージョンの拡大を期待しつつ、 AWS DevOps Agentがインシデント対応のプロセスにおいてどの部分まで担っていけるのか、 AWS の他サービス(Kiro CLI , Amazon Q Develper等)やAI機能が備わってきているオブザーバビリティ・インシデント対応周りの SaaS サービスとの棲み分け等について、引き続き Watch して確認して行ければと思っています。 参考記事 AWS公式資料:Frontier agents AWS公式資料:AWS DevOps Agent helps you accelerate incident response and improve system reliability (preview) AWS公式資料:AWS DevOps Agent (Preview) AWS公式ドキュメント:AWS DevOps Agent パブリックプレビューの価格と制限 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @miyazaki.hirotoshi レビュー: @handa.kenta ( Shodo で執筆されました )
アバター
はい、こんにちはー!コーポレート本部サイバーセキュリティ推進部の福山です。 本記事は 電通総研 Advent Calendar 2025 10日目の記事です。 今回は、当社もThird Sponsorとして協賛させていただいた、国内最大級の国際的なセキュリティカンファレンスである 「CODE BLUE」 に初めて参加してきたので、そのレポートになります。 CODE BLUEをあまり知らない方向けに、簡単ではありますが紹介いたします。 CODE BLUEとは Contests/Workshops Hacking Active Directory インターポールAIクエスト TRAPA Cyber Range Exercise 最後に CODE BLUEとは CODE BLUEは、世界トップクラスの情報セキュリティ専門家が集まり、最先端の技術や脅威動向に関する講演・情報交換が行われる、国際的なセキュリティカンファレンスです(会場は ベルサール高田馬場 で2日間開催)。 海外の著名な研究者や国内のトップエンジニアによるセッションが用意されており、最終日には登壇者および参加者同士で交流するPartyも設けられています。 また、実践的なスキルを磨けるコンテストおよびワークショップが用意されており、実際に手を動かして学べる点も、CODE BLUEの大きな魅力だと感じました。 今回はコンテストおよびワークショップに焦点を当てて紹介します。 Contests/Workshops 今回は3つのコンテストおよびワークショップに参加させていただきました。どれも非常に学びの深い内容でした! Hacking Active Directory このワークショップでは、攻撃者目線で Active Directory (AD)環境に侵入し、特権昇格や横展開を行う手法をハンズオン形式で体験できます。 また、攻撃に対する緩和策についても触れられていました。 ※こちらのワークショップは人数制限があり、当日現地予約という形式になっていました。 初日の午前中に2日間全ての枠が埋まるほどの人気ぶりでした。 参加者は Webブラウザ から Apache Guacamole環境にアクセスし、Kali Linux を操作します。 (※図は IBM X-Force Red提供資料より引用) 外部公開された脆弱なWebサーバが侵入の起点というシナリオで、大まかに以下のフェーズで内容が構成されていました。 フェーズ 技法 使用ツール 主な目的 初期侵入 (Initial Access ) LLMNR / NBT-NS Poisoning responder, hashcat ネットワーク上の認証ハッシュを窃取し、平文パスワードを取得。 内部偵察 (Reconnaissance) SMB Recon & AD偵察 NetExec, BloodHound 共有フォルダの探索、ADの構成や攻撃経路の可視化。 権限昇格 (Privilege Escalation) Kerberoasting Impacket GetUserSPNs, hashcat SPNを持つアカウントのサービスチケットを収集・復号し、サービスアカウントのパスワードを取得。 攻撃者がどのようにAD環境で足場を築き、権限を広げていくのかを実体験できるようになっています。 また、攻撃者視点を学んだ後、それに対する具体的な緩和策が解説されました。 攻撃技法 緩和策 LLMNR / NBT-NS Poisoning グループポリシーでLLMNRを無効化し、 ネットワークアダプタ 設定でNBT-NSを無効化する。 Kerberoasting サービスアカウントのパスワードを30文字以上の複雑なものにする。 RC4 暗号化を無効化し、より強固な暗号化方式を使用する。 ※対策が難しい場合は、 Windows イベントID 4768(Kerberos認証チケット要求)や4769(サービスチケット要求)を監視し、 RC4 -HMACの使用や短時間での複数のSPN要求を検知する。 「攻撃者がどのようにしてADを狙うのか」「この緩和策がなぜ必要なのか」といった点が深く腹落ちするワークショップでした。 インターポールAIクエスト こちらは過去に 国際刑事警察機構 (インターポール)向けに提供されたCTFをアレンジしたものとなっており、AIとサイバー犯罪がテーマとなっていました。 基調講演でも取り上げられた「AIとセキュリティ」に焦点が当てられているだけに、興味をそそる内容だったと思います。 会場の Wi-Fi 経由で、2日間どこからでもアクセスできました。 AIクエスト世界の被害者(生成AI)に、チャットベースで被害状況などを ヒアリ ングしながらサイバー犯罪を読み解いていく、対話型のCTFです。 被害者のお孫さんとの家族写真が暗号化されて見られなくなってしまったという相談から始まり、1問目からTorへのアクセスを要求されるという、非常に興味深い問題設定でした。 もちろん、実際の生成AIサービスのように聞けばなんでも教えてくれるノリではなく、質問をクリアするとヒントを1個聞く権限が付与されるといったルールになっていました。 最後まで到達できず厳しい結果に終わりましたが、自力での解決を強いられるところに、現実のサイバー捜査の厳しさ・情報収集の難しさを実感できました。 TRAPA Cyber Range Exercise こちらはTRAPA が提供するサイバーレンジを利用した、ブルーチーム向けの実践的な演習に参加しました。 APTグループに攻撃を受けた仮想環境で、ログ解析、インシデント対応、被害拡大の防止措置を行うという、非常にリアルなシミュレーションとなっています。 こちらも会場の Wi-Fi 経由で、2日間どこからでもアクセスできました。 侵害を受けた環境はオンプレ構成となっており、インターネットとの間に DMZ があり、外部からのリクエストによって Proxyサーバ が侵入の起点になるといったシナリオでした。 SOCルームというダッシュボードで攻撃事象の確認やインシデントの調査結果を記録し(正解したらポイントを獲得)、 ログ調査にはSplunkを使って、 ファイアウォール ログや Windows ホストモニターなどを活用して演習を進めていきました。 複数の資産にまたがるログと時間制限の中では、ログを読むスピードが求められます。 こちらも最後まで到達できず厳しい結果に終わりましたが、調査に行き詰ったときにはTRAPA提供のAIに聞くとアド バイス がもらえたので面白かったです。 最後に 簡単ですが、Contests/Workshopsの紹介でした。 改めて自身の実力不足を痛感しつつも、初めてのCODE BLUEは結果として想像以上に楽しく、まるで海外カンファレンスに参加しているような刺激を受けました。 運営の皆様に心から感謝します。 本記事では紹介しませんでしたが、各セッションについても普段は聞けない興味深い内容が多く、大きな学びになりました。 ワークショップやコンテストについては開催する側も面白そうだなと思ったので、次回以降チャンスがあればチャレンジしてみたいです。 それでは、来年のCODE BLUE 2026でお会いしましょう! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @fukuyama.kenta ( Shodo で執筆されました )
アバター
はじめに はじめまして。グループ経営ソリューション事業部の三谷です。 この記事では、 電通 総研で監視ツールとして利用しているDatadogについてどのようなサービスなのか基本的な情報と、導入手順をまとめた記事になります。 Datadogとは Datadog は、 クラウド やコンテナ環境向けの「監視・可観測性プラットフォーム」です。 サーバ・コンテナ・ ミドルウェア ・アプリケーション・ クラウド サービスからデータを集めて、1 つの画面で横断的に可視化・分析できる SaaS 型のサービスです。 Datadog が扱う主なデータは次の 4 種類に整理できます。 メトリクス CPU 使用率、メモリ使用量、ディスク I/O、リクエスト数、レイテンシなどの時系列データ。 ログ アプリケーションログ、 アクセスログ 、システムログ、 ミドルウェア ログなど。 トレース ( APM ) 1リクエストがどのサービス・コンテナを通過したか、その経路と各 区間 のレイテンシ。 つまり、アプリケーションの動作をトレースして、どこで遅延やエラーが発生しているかをDatadogから調査が可能です。 イベント デプロイ、スケールアウト/イン、障害、設定変更などの「出来事」を表す情報。 私は今まで、監視は主にCloudWatchを利用していました。CloudWatchだと、 AWS アカウントを跨いだ一元監視が難しく「ほかの AWS アカウントではどうなっているんだっけ・・・」と調査のために比較したいとき、 AWS アカウントを切り替えながら確認が必要で非常に手間がかかっていました。 Datadogではその問題は発生せず、複数の AWS アカウントのデータを一目で確認することができます。 Datadogでなにができるか(どのように監視するか) Datadog による監視の全体像は、大きく分けて4つの段階があります。 1. データを集める Agent による収集 監視対象のサーバやコンテナにDatadog Agentを導入すると、標準で次の情報を収集できます。 OS レベルのメトリクス CPU、メモリ、ディスク、ネットワーク I/O など コンテナメトリクス コンテナごとの CPU / メモリ / ネットワーク利用状況 ミドルウェア メトリクス nginx, Redis, PostgreSQL , MySQL などの各種インテグレーションを通じたメトリクス ログ・トレースの収集 Datadog はログ・トレースも一元的に扱えます。 ログの収集の例として下記の構成があります。 Datadog Agentでのログ収集 CloudWatch Logsや FireLens + Fluent Bit でのログ収集 それを Datadog に転送して保存・検索・解析 2. タグで関連づける Datadog では、すべてのメトリクス・ログ・トレース・イベントをタグで管理しています。 タグ設計が監視の品質を左右する、重要な設計要素になります。 たとえば、以下の調査を行いたい場合、Datadogだけで調査が完結します。 「ECSの特定タスクで500エラーが増えたタイミングで、CPU・メモリ利用率はどうだったか」 「その直前にデプロイやスケールイベントは発生していなかったか」 「どのリリースバージョンからエラー率が上がっているか」 代表的なタグ(例): 環境別 env:prod , env:staging , env:dev サービス別 service:my-api , service:batch-worker クラスタ ー別 cluster:my-ecs-cluster 3. 可視化・分析する 収集したデータはDatadogのコンソール上でダッシュボードを作成することが可能です。 ダッシュボードでの管理例 ECS / Fargate 向けの標準ダッシュボードを作る クラスタ 全体の CPU / メモリ使用率 サービスごとの runningタスク数 カスタムダッシュボード 本番環境 env:prod に特化したダッシュボードを作る リクエスト数 エラー率 CPU / メモリ使用率 4. モニター(アラート)で通知する 可視化して満足していても意味がないので、 閾値 や条件を定義して必要な通知を行う必要があります。Datadogではモニターと呼ぶ機能がこれに該当します。 代表的なモニターの例(ECS タスクの場合) ECS サービスのタスク数監視 条件例: running_tasks < desired_tasks が 5 分以上継続したらアラート エラー率監視 条件例: service:my-api AND env:prod の 5xx 発生率が一定以上でアラート 通知先 各種ツールへの通知が可能です。なおJiraと連携することで、Datadogにてアラート発生時にJiraのチケットを自動起票することも可能です。 https://www.atlassian.com/ja/devops/observability-tutorials/jira-datadog-integration 社内でJira自動起票について少し検討したことがありました。便利ではあるものの、長時間アラートが出た場合連続して同じチケットが起票されるのでは等、導入における考慮すべき点が多く、導入は一旦ステイとなりました。 また、オンコールについてもDatadog On-Callというサービスが存在しており一元管理することが可能です。 https://docs.datadoghq.com/ja/service_management/on-call/ ECSサービスの監視を実際にやってみる Datadogの基本的な機能が分かったところで、今回はDatadog Agentを導入してECSサービスの監視を行いたいと思います。 前提条件 AWS アカウントを持っていること Datadogアカウントを作成済みであること Datadogの API キーを取得済みであること Datadog AgentとDatadogが通信可能であること Datadogは収集するデータによって使用するポートが異なります。今回利用するAgentは TCP /443を使用します。 詳細はDatadogのドキュメントをご確認ください。 https://docs.datadoghq.com/ja/agent/configuration/network/ AWS での作業 タスク定義の作成 まず、ECSで使用するタスク定義を作成します。 今回のタスク定義では、以下の2つのコンテナを設定しました。 Datadog Agentコンテナ イメージ URI : public.ecr.aws/datadog/agent:latest Apache コンテナ イメージ URI : public.ecr.aws/docker/library/httpd:latest ※今回はECRのパブリックイメージを使用しました。 Datadogでの監視を有効化するため、Datadog Agentコンテナに以下の 環境変数 を設定します。 DD_API_KEY : Datadogの API キー DD_SITE : Datadogのサイト(例: datadoghq.com ) ECS_FARGATE : true クラスタ ーの作成 次に、ECS クラスタ ーを作成します。 任意の クラスタ ー名を入力し、その他デフォルトの設定で作成します。 サービスの作成 作成した クラスタ ー上でサービスを起動します。 サービス作成時の設定項目: タスク定義ファミリー : 先ほど作成したタスク定義を選択 環境 : 「起動タイプ」を選択 ネットワーキング : VPC を選択 サブネットを選択 適切なセキュリティグループを選択 (添付画像は入力前のものです) その他の項目はデフォルトのまま作成 サービスが正常に開始すると上図のようになります。 Datadogコンソールでの確認 監視を開始するためにDatadog側で必要な作業はほぼありません。 API キーさえ用意できれば、Agent導入後すぐに監視が始まります。 AWS マネジメントコンソールからサービス起動は確認できたので、Datadogからも確認してみます。 DatadogコンソールにてInfrastructure→Containers→ECS Explorer から起動したサービスが確認できます。 監視対象のサービスが見えました。CPU/メモリも取得できていそうです。 これで基本的な監視設定が完了しました。 APM など高度な監視には詳細な設定が必要ですが、基本的な監視であれば簡単に導入して監視を始められることがわかりました。 さいごに 今回はDatadogの仕組みについての基本的機能の概要と簡単なサービス監視までを実施しました。 Datadogは、最初の14日間はトライアル期間であり、すべての機能が無料で使用できます。 皆様もよいDatadogライフをお過ごしください。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @mitani.kyoka レビュー: @kinjo.ryuki ( Shodo で執筆されました )
アバター
はじめに こんにちは、XI本部エンジニアリングテクノロ ジー センターの徳山広士です。 この記事では、データ基盤/データ分析基盤におけるデータパイプラインのAI駆動開発の手法を提案します。 「 ODCS (Open Data Contract Standard) 」フォーマットの"Data Contract"を仕様書として生成AIへ与えてデータパイプラインのコードを生成させる手法です。 Data Contractは、データの仕様(テーブル構造、カラム定義、データ型など)と品質要件を YAML 形式で定義する標準フォーマットです。 当記事の主な内容は、Data Contractやローカル開発環境などの基礎的な説明から当件のAI駆動開発方法、コード生成に成功した検証結果についてです。 想定読者: データエンジニア、データマネジメント業務担当者 当記事で得られる知見: データパイプラインのAI駆動開発の勘所 前提知識: データパイプライン(ETL/ ELT 処理)の基礎知識 一般的に労力と時間のかかるデータマネジメント・サイクルをAI駆動開発によって加速できればと考えて、データエンジニアリングのAI駆動開発手法を探求し、この記事にまとめました。 データパイプラインのAI駆動開発実現の課題 生成AIに何を伝えるべきか アプリケーションのAI駆動開発においては、 ドメイン や ユースケース 、機能、制約などを生成AIに説明することでコード生成するアプローチを見聞きします。では、 データエンジニアリングにおいては何を説明すれば良いのか?どのように指示を出せば包括的に開発してくれるのか? これが最初に直面した課題でした。 自然言語 のプロンプトで目的の SQL クエリについて説明する方法も考えましたが、複雑なロジックの表現が困難であり、細かい要件が伝わらず、プロンプトの再現性・再利用性にも疑問がありました。 アプローチ: データ アーキテクチャ とデータモデルを伝える この課題に対して、データエンジニアリングの観点から「データ アーキテクチャ 」と「データモデル」の2つの軸で情報を整理し、データ基盤/データ分析基盤の全体像と開発方法を生成AIに伝えるアプローチを採用しました。 データ アーキテクチャ の説明 生成AIが全体像を理解するための情報: データレイヤー構造と各レイヤーの責務: raw, staging, core, martなど データストアの構成: データレイク, データウェアハウス, データマートなど 各データストアのデータフォルダ構成: "raw/システム名/YYYYMMDD"など データ処理アプローチ: ELT など インフラストラクチャ構成: 各データストアの物理実装で利用の SaaS など データパイプライン構成技術: dagster , dbt core など これらの内容を プロジェクトドキュメント(DESIGN.md等) として整備しました。 データモデルの説明 生成AIがデータパイプラインで実現すべき対象の情報: データの構成: テーブル, カラム, リレーションシップなど データ仕様: データ型, 主キー等の制約, 変換ロジックなど データ品質仕様: 満たすべき基準, テストルールなど これらを Data Contract(ODCS形式) としてドキュメント整備しました。 この2つを組み合わせることで、生成AIはデータパイプラインの実行環境全体の設計意図を理解し、データ基盤/データ分析基盤に適した具体的な実装を行えるようになりました。 データパイプライン作成依頼 生成AIへの開発依頼は、データパイプラインの要件を ADR (Architecture Decision Record) に記述し、その ADR で定義されたデータパイプラインを関連するData Contractにもとづいて開発するように生成AIへ依頼する方法を採用しました。 ADR に少なくとも以下の内容を書くようにしました。 データパイプラインの作成対象のデータ名 データ提供の背景や目的, データ利用の ユースケース ADR の記載内容の参考イメージ タイトル: P001 ECビジネスデータパイプライン構造 ステータス: Accepted コンテキスト: Eコマースビジネスパイプラインのデータ統合対象のデータを決める必要がある。 決定事項: Eコマースビジネスパイプラインは、web_salesとweb_returnsを対象とした構成にする。 検証内容の詳細 実際の検証環境や要件、作成対象のデータなどを説明します。 検証では TPC-DS (*1)のデータを使用し、生成AIには Claude Code(Sonnet 4.5, Anthropic) を使用しました。 web_salesテーブルの更新用のソースデータを変換してweb_salesへ統合するデータパイプラインの SQL クエリを生成します。 ※1 TPC -DS は Transaction Processing Performance Council ( TPC ) の商標です。 本記事は TPC ベンチマーク 結果の公表を目的としたものではありません。 ローカル開発環境 データパイプラインのデプロイ先は AWS やAzure、 Google Cloudなどの SaaS ですが、AI駆動開発を効率化するためにデプロイ先を模したローカル完結型の開発環境を整備しました。 データレイク兼データウェアハウス: DuckDB データ モデリング : dbt core オーケストレータ: dagster データ品質テスト: Soda Core DuckDBで構成 : SaaS への外部通信を排除し、生成AIの生産性を最大化。 SaaS で巨大になりがちなデータレイクやデータウェアハウスをコンパクトなDuckDBで代用しました。 コードベースのツール選定 : 生成AIがコードを直接参照・実装できるよう、全てコードベースのツールを採用。 GUI のETLツールは、生成AIとの連携で独自 SDK / API 経由が必要となり生産性の低下が懸念されるため使用しませんでした。 ツールによるインフラストラクチャの抽象化 : デプロイ先の SaaS とは物理的な環境構成が異なりますが、dbt coreなどのソフトウェアが抽象化し環境差分を吸収してくれます。 データ アーキテクチャ 概要 データパイプラインのデプロイ先のデータ分析基盤のデータ アーキテクチャ を図にしたものです。論理構成の主要部分のみを抜粋し、簡潔な内容を掲載しております。 データレイヤー: "raw", "staging", "core", "mart" の4層 データストア: データレイクとデータウェアハウス データ処理アプローチ: ELT データパイプライン構成技術: Dagster, dbt core, Soda Core インフラストラクチャ: AWS とAzure, Google Cloudの3大 クラウド サービスでそれぞれ実施、詳細は割愛 データモデル web sales ※一部のカラムは割愛 ディメンショナル モデリング されたスター スキーマ 構造となっており、複数の サロゲートキー を持っています。データ構造は把握しやすいもののETL/ ELT 処理の観点では10個以上のテーブル結合や中間テーブルを介したテーブル結合、一部カラムの演算処理が必要な複雑な構造になっています。 作成対象のデータパイプライン rawレイヤー 生データのデータファイルが外部テーブル (External table) としてデータウェアハウスに既にテーブル化されており、そのテーブルを参照すれば良いため、処理の実装は不要。 stagingレイヤー rawレイヤーのデータをもとにマスターデータとの結合によるデータ取得や演算処理などの ビジネスロジック をdbtの SQL クエリモデルとして実装 SELECT対象のカラム数: 35個 テーブル結合数: 12個 coreレイヤー stagingレイヤーのデータをもとに増分処理でデータ更新する処理をdbtの SQL クエリモデルとして実装 SELECT対象のカラム数: 38個 テーブル結合数: 0個 実装ルール SELECT文でカラムの型キャストを明記 SELECT文でカラムの名称をAS文で指定 CTE (Common Table Expression) でソースデータ定義 coreレイヤーでは増分処理を実装 生成AIへの依頼 前述の「 アプローチ: データアーキテクチャとデータモデルを伝える 」に記載のドキュメントに加えて、開発 ガイドライン も整備し開発依頼を行いました。 生成AIへ提供した情報 プロジェクトドキュメント Data Contract 開発 ガイドライン (開発規約や トラブルシューティング など) ADR (Architecture Decision Record) プロンプト 実際のプロンプト 以下のファイルを読み込んで、当プロジェクトについて理解してください。 - ./docs/DESIGN.md - ./docs/DATAMODEL_GENERATION_GUIDELINES.md ADRで定義されたデータパイプラインを関連する`Data Contract`にもとづいて作成します。 今回は、ADRの"./docs/adr/pipelines/P001-ecommerce-business-pipeline-structure.md"で決定したデータパイプラインを作成してください。 実際のData Contractのサンプル "ws_item_sk"という主要なカラムのData Contractを一部抜粋したものです。より詳しい内容と解説は 詳細解説 にて後述します。 - name : ws_item_sk businessName : ウェブ販売商品サロゲートキー logicalType : string physicalType : STRING primaryKey : true primaryKeyPosition : 1 transformSourceObjects : - core_tpcds.item.i_item_sk transformLogic : SELECT i_item_sk FROM s_web_order JOIN s_web_order_lineitem ON (word_order_id = wlin_order_id) LEFT JOIN item ON (wlin_item_id = i_item_id AND i_rec_end_date IS NULL ) relationships : - type : foreignKey to : - core_tpcds.item.i_item_sk quality : - type : library rule : duplicateCount name : "主キー重複チェック(ws_item_sk)" dimension : uniqueness mustBe : 0 severity : error businessImpact : "重複レコードによりファクトテーブルのデータ整合性が崩れ、売上集計に誤差が生じる" 生成されたコード stagingレイヤー 12テーブルを結合し、35カラムを生成する SQL クエリです。Data Contractの transformLogic で定義した変換ロジックが正確に反映されています。 with s_web_order as ( select * from {{ source( ' raw_tpcds ' , ' s_web_order_1 ' ) }} ), s_web_order_lineitem as ( select * from {{ source( ' raw_tpcds ' , ' s_web_order_lineitem_1 ' ) }} ) SELECT d1.d_date_sk::STRING AS ws_sold_date_sk, t_time_sk::STRING AS ws_sold_time_sk, d2.d_date_sk::STRING AS ws_ship_date_sk, i_item_sk::STRING AS ws_item_sk, c1.c_customer_sk::STRING AS ws_bill_customer_sk, c1.c_current_cdemo_sk::STRING AS ws_bill_cdemo_sk, c1.c_current_hdemo_sk::STRING AS ws_bill_hdemo_sk, c1.c_current_addr_sk::STRING AS ws_bill_addr_sk, c2.c_customer_sk::STRING AS ws_ship_customer_sk, c2.c_current_cdemo_sk::STRING AS ws_ship_cdemo_sk, c2.c_current_hdemo_sk::STRING AS ws_ship_hdemo_sk, c2.c_current_addr_sk::STRING AS ws_ship_addr_sk, wp_web_page_sk::STRING AS ws_web_page_sk, web_site_sk::STRING AS ws_web_site_sk, sm_ship_mode_sk::STRING AS ws_ship_mode_sk, w_warehouse_sk::STRING AS ws_warehouse_sk, p_promo_sk::STRING AS ws_promo_sk, word_order_id::STRING AS ws_order_number, wlin_quantity:: INTEGER AS ws_quantity, i_wholesale_cost:: DECIMAL ( 7 , 2 ) AS ws_wholesale_cost, i_current_price:: DECIMAL ( 7 , 2 ) AS ws_list_price, wlin_sales_price:: DECIMAL ( 7 , 2 ) AS ws_sales_price, ((i_current_price:: DECIMAL ( 7 , 2 ) - wlin_sales_price:: DECIMAL ( 7 , 2 )) * wlin_quantity:: INTEGER ):: DECIMAL ( 7 , 2 ) AS ws_ext_discount_amt, (wlin_sales_price:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER ):: DECIMAL ( 7 , 2 ) AS ws_ext_sales_price, (i_wholesale_cost:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER ):: DECIMAL ( 7 , 2 ) AS ws_ext_wholesale_cost, (i_current_price:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER ):: DECIMAL ( 7 , 2 ) AS ws_ext_list_price, (i_current_price:: DECIMAL ( 7 , 2 ) * web_tax_percentage:: DECIMAL ( 7 , 2 )):: DECIMAL ( 7 , 2 ) AS ws_ext_tax, wlin_coupon_amt:: DECIMAL ( 7 , 2 ) AS ws_coupon_amt, (wlin_ship_cost:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER ):: DECIMAL ( 7 , 2 ) AS ws_ext_ship_cost, (wlin_sales_price:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER - wlin_coupon_amt:: DECIMAL ( 7 , 2 )):: DECIMAL ( 7 , 2 ) AS ws_net_paid, (((wlin_sales_price:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER ) - wlin_coupon_amt:: DECIMAL ( 7 , 2 )) * ( 1 + web_tax_percentage:: DECIMAL ( 7 , 2 ))):: DECIMAL ( 7 , 2 ) AS ws_net_paid_inc_tax, (((wlin_sales_price:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER ) - wlin_coupon_amt:: DECIMAL ( 7 , 2 )) - (wlin_quantity:: INTEGER * i_wholesale_cost:: DECIMAL ( 7 , 2 ))):: DECIMAL ( 7 , 2 ) AS ws_net_paid_inc_ship, ((wlin_sales_price:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER ) - wlin_coupon_amt:: DECIMAL ( 7 , 2 ) + (wlin_ship_cost:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER ) + i_current_price:: DECIMAL ( 7 , 2 ) * web_tax_percentage:: DECIMAL ( 7 , 2 )):: DECIMAL ( 7 , 2 ) AS ws_net_paid_inc_ship_tax, (((wlin_sales_price:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER ) - wlin_coupon_amt:: DECIMAL ( 7 , 2 )) - (i_wholesale_cost:: DECIMAL ( 7 , 2 ) * wlin_quantity:: INTEGER )):: DECIMAL ( 7 , 2 ) AS ws_net_profit, s_web_order.processing_date::STRING AS processing_date, current_timestamp :: TIMESTAMP AS ingested_at FROM s_web_order LEFT JOIN {{ ref( ' date_dim ' ) }} d1 ON ( cast (word_order_date AS date ) = d1.d_date) LEFT JOIN {{ ref( ' time_dim ' ) }} ON (word_order_time = t_time) LEFT JOIN {{ ref( ' customer ' ) }} c1 ON (word_bill_customer_id = c1.c_customer_id) LEFT JOIN {{ ref( ' customer ' ) }} c2 ON (word_ship_customer_id = c2.c_customer_id) LEFT JOIN {{ ref( ' web_site ' ) }} ON (word_web_site_id = web_site_id AND web_rec_end_date IS NULL ) LEFT JOIN {{ ref( ' ship_mode ' ) }} ON (word_ship_mode_id = sm_ship_mode_id) JOIN s_web_order_lineitem ON (word_order_id = wlin_order_id) LEFT JOIN {{ ref( ' date_dim ' ) }} d2 ON ( cast (wlin_ship_date AS date ) = d2.d_date) LEFT JOIN {{ ref( ' item ' ) }} ON (wlin_item_id = i_item_id AND i_rec_end_date IS NULL ) LEFT JOIN {{ ref( ' web_page ' ) }} ON (wlin_web_page_id = wp_web_page_id AND wp_rec_end_date IS NULL ) LEFT JOIN {{ ref( ' warehouse ' ) }} ON (wlin_warehouse_id = w_warehouse_id) LEFT JOIN {{ ref( ' promotion ' ) }} ON (wlin_promotion_id = p_promo_id) coreレイヤー stagingレイヤーのデータを増分処理でcoreレイヤーに取り込む SQL クエリです。dbtのincrementalモデルを使用しています。 {{ config( materialized= ' incremental ' , unique_key=[ ' ws_item_sk ' , ' ws_order_number ' ], on_schema_change= ' fail ' , incremental_strategy= ' merge ' if target. type != ' duckdb ' else ' delete+insert ' ) }} -- Incrementalモデル:staging層からデータを取得 select ws_sold_date_sk::STRING as ws_sold_date_sk, ws_sold_time_sk::STRING as ws_sold_time_sk, ws_ship_date_sk::STRING as ws_ship_date_sk, ws_item_sk::STRING as ws_item_sk, ws_bill_customer_sk::STRING as ws_bill_customer_sk, ws_bill_cdemo_sk::STRING as ws_bill_cdemo_sk, ws_bill_hdemo_sk::STRING as ws_bill_hdemo_sk, ws_bill_addr_sk::STRING as ws_bill_addr_sk, ws_ship_customer_sk::STRING as ws_ship_customer_sk, ws_ship_cdemo_sk::STRING as ws_ship_cdemo_sk, ws_ship_hdemo_sk::STRING as ws_ship_hdemo_sk, ws_ship_addr_sk::STRING as ws_ship_addr_sk, ws_web_page_sk::STRING as ws_web_page_sk, ws_web_site_sk::STRING as ws_web_site_sk, ws_ship_mode_sk::STRING as ws_ship_mode_sk, ws_warehouse_sk::STRING as ws_warehouse_sk, ws_promo_sk::STRING as ws_promo_sk, ws_order_number::STRING as ws_order_number, ws_quantity:: INTEGER as ws_quantity, ws_wholesale_cost:: DECIMAL ( 7 , 2 ) as ws_wholesale_cost, ws_list_price:: DECIMAL ( 7 , 2 ) as ws_list_price, ws_sales_price:: DECIMAL ( 7 , 2 ) as ws_sales_price, ws_ext_discount_amt:: DECIMAL ( 7 , 2 ) as ws_ext_discount_amt, ws_ext_sales_price:: DECIMAL ( 7 , 2 ) as ws_ext_sales_price, ws_ext_wholesale_cost:: DECIMAL ( 7 , 2 ) as ws_ext_wholesale_cost, ws_ext_list_price:: DECIMAL ( 7 , 2 ) as ws_ext_list_price, ws_ext_tax:: DECIMAL ( 7 , 2 ) as ws_ext_tax, ws_coupon_amt:: DECIMAL ( 7 , 2 ) as ws_coupon_amt, ws_ext_ship_cost:: DECIMAL ( 7 , 2 ) as ws_ext_ship_cost, ws_net_paid:: DECIMAL ( 7 , 2 ) as ws_net_paid, ws_net_paid_inc_tax:: DECIMAL ( 7 , 2 ) as ws_net_paid_inc_tax, ws_net_paid_inc_ship:: DECIMAL ( 7 , 2 ) as ws_net_paid_inc_ship, ws_net_paid_inc_ship_tax:: DECIMAL ( 7 , 2 ) as ws_net_paid_inc_ship_tax, ws_net_profit:: DECIMAL ( 7 , 2 ) as ws_net_profit, processing_date::STRING as processing_date, ingested_at:: TIMESTAMP as ingested_at, current_timestamp :: TIMESTAMP as inserted_at, current_timestamp :: TIMESTAMP as updated_at from {{ ref( ' web_sales_view ' ) }} {% if is_incremental() %} -- incrementalの場合、processing_dateでフィルタリング where processing_date = ' {{ var("processing_date", "20240101") }} ' {% endif %} 詳細解説 Data Contractとは? Data Contractは、データの提供者と利用者の間でデータ仕様やデータ品質要件を合意するための標準フォーマットです。 フォーマットの既定の項目を使ってデータ仕様やデータ品質要件を定義することができます。 既定の項目がサポートしているものは、テーブル スキーマ や各種の論理名、データの粒度などの メタデータ に加えて、サンプルデータ、データ品質ルールなど多岐に渡ります。 当検証では「 ODCS (Open Data Contract Standard) 」という YAML 形式のフォーマットを使用します。ODCSは Linux Foundation傘下のBitolプロジェクトがサポートする代表的な仕様です。 当記事では、Data Contractを生成AIへ与えるコンテキストとして活用します。Data Contractの作成自体は設計プロセスで生成AIに行わせることも可能です(別記事で説明予定)。 今回使用のData Contractの主要項目 主に使用した項目と各項目の簡単な説明を記載します。 name: カラム物理名 businessName: カラム論理名 logicalType: 論理データ型 physicalType: 物理データ型 transformSourceObjects: カラムのデータを作成する際に必要なソースデータに関する情報 transformLogic: カラムのデータを作成する際に必要なデータ変換ロジック relationships: カラムのリレーションシップ情報 quality: カラムのデータ品質要件 実際のData Contract 以下、代表的な2つのカラムのData Contractを抜粋します。 主キー兼外部キーの例: ws_item_sk こちらは前述のData Contractの詳述です。 複合主キーの一部であり外部キーでもあるカラムです。3テーブルの結合と、3種類のデータ品質テストが定義されています。 - name : ws_item_sk businessName : ウェブ販売商品サロゲートキー logicalType : string physicalType : STRING primaryKey : true primaryKeyPosition : 1 transformSourceObjects : - core_tpcds.item.i_item_sk transformLogic : SELECT i_item_sk FROM s_web_order JOIN s_web_order_lineitem ON (word_order_id = wlin_order_id) LEFT JOIN item ON (wlin_item_id = i_item_id AND i_rec_end_date IS NULL ) relationships : - type : foreignKey to : - core_tpcds.item.i_item_sk customProperties : - target_contract_id : "core_tpcds.item" - target_contract_path : "./core_tpcds/item.yaml" - target_property : "i_item_sk" quality : - type : library rule : duplicateCount name : "主キー重複チェック(ws_item_sk)" dimension : uniqueness mustBe : 0 severity : error businessImpact : "重複レコードによりファクトテーブルのデータ整合性が崩れ、売上集計に誤差が生じる" - type : library rule : nullCount name : "主キーNULLチェック(ws_item_sk)" dimension : completeness mustBe : 0 severity : error businessImpact : "NULL値が存在すると商品別売上分析が不可能になる" - type : sql name : "商品マスタ参照整合性チェック" dimension : consistency query : | SELECT COUNT(*) FROM ${object} ws LEFT JOIN item i ON ws.ws_item_sk = i.i_item_sk WHERE ws.ws_item_sk IS NOT NULL AND i.i_item_sk IS NULL mustBe : 0 severity : error businessImpact : "参照整合性が崩れると商品マスタとの結合で欠損が発生し、商品情報を取得できない売上データが生じる" 複雑な計算ロジックの例: ws_net_profit 純利益は複数のソースから計算され、計算整合性と平均値の妥当性の両方がチェックされます。 - name : ws_net_profit businessName : ウェブ純利益 logicalType : number physicalType : DECIMAL(7, 2) transformSourceObjects : - raw.s_web_order_lineitem.wlin_sales_price - raw.s_web_order_lineitem.wlin_quantity - raw.s_web_order_lineitem.wlin_coupon_amt - core_tpcds.item.i_wholesale_cost transformLogic : SELECT (((wlin_sales_price::DECIMAL(7,2) * wlin_quantity::INTEGER) - wlin_coupon_amt::DECIMAL(7,2)) - (i_wholesale_cost::DECIMAL(7,2) * wlin_quantity::INTEGER))::DECIMAL(7,2) FROM s_web_order JOIN s_web_order_lineitem ON (word_order_id = wlin_order_id) LEFT JOIN item ON (wlin_item_id = i_item_id AND i_rec_end_date IS NULL ) quality : - type : sql name : "純利益計算整合性チェック" dimension : accuracy query : | SELECT COUNT(*) FROM ${object} WHERE ws_net_profit IS NOT NULL AND ws_net_paid IS NOT NULL AND ws_ext_wholesale_cost IS NOT NULL AND ABS(ws_net_profit - (ws_net_paid - ws_ext_wholesale_cost)) > 0.01 mustBe : 0 severity : error businessImpact : "計算式が不正確な場合、利益分析の信頼性を損なう" - type : sql name : "純利益平均値チェック" dimension : accuracy query : | SELECT AVG(ws_net_profit) FROM ${object} WHERE ws_net_profit IS NOT NULL mustBeBetween : [ -100, 1000 ] severity : warning businessImpact : "平均利益が異常値の場合、価格設定や原価データに問題がある可能性" transformSourceObjectsとtransformLogicの重要性 当検証で特に試行錯誤したのが、この2つのプロパティの記述でした。 当初の仮説 : 各プロパティでデータ仕様を記述し、relationshipsプロパティでデータ間の関係性を記述すれば、生成AIがJOIN処理やカラム毎の計算ロジック、変換ロジックを類推できるのではないか 実際の課題 : データ仕様やリレーションシップ(データモデル上の関係性)とETL/ ELT 処理(データ変換ロジック)の間には隔たりがある 例えば、 ws_item_sk カラムは item テーブルと外部キー関係にありますが、実際のデータ取得には s_web_order → s_web_order_lineitem → item という3テーブルの結合が必要です。 これは生成AIに分かってもらえそうで分かってもらえませんでした。 s_web_order と item の2つのテーブルを無理に結合しようとしてしまいますし、チャットで説明して改善しても後で忘れて問題が再発し、忘れないように記録してもらっても個別のケースの実装方法として記録されて他ケースへの応用が懸念されました。 他に、生成AIは サロゲートキー どうしでテーブル結合したり、主キーで結合するように指示して改善しても今度は肝心の サロゲートキー を取得してくれなかったりしました。 また、カラムの演算処理は間違った内容が実装されるか全く実装されないかでした。 そのため、以下の2つのプロパティを使うようにしました: transformSourceObjects : データの出所(ソーステーブル・カラム)を明示 transformLogic : 具体的な変換ロジック(JOIN条件、フィルタ、計算式など)を記述 これにより、生成AIは「何を参照して、どう変換するか」を正確に理解し、意図通りの SQL を生成できるようになりました。 transformSourceObjectsとtransformLogicの作成方法 transformSourceObjectsプロパティとtransformLogicプロパティの作成自体をデータモデルの設計過程で生成AIと共に行います。 もし、データソース側のシステムで業務知識 (例. 純利益の計算式) が何かドキュメントなどに整理されてあれば、その内容をもとに生成AIが両プロパティを定義できる可能性があります。 また、例えばディメンショナル モデリング であれば、データ粒度 (Grain) の定義やConformance Matrixなどを機械判読可能なファイル形式で生成AIと共に作成し、それら成果物をソースとして生成AIが両プロパティを作成できる可能性があります。 relationshipsプロパティは ODCS v3.1.0 にリリース予定 ODCSの現在の最新公式バージョンはv3.0.2ですが、このバージョンには relationshipsプロパティが存在しません 。 ODCS v3.1.0でrelationshipsプロパティが実装される予定で、当検証ではODCSの v3.1.0 を先行的に使用しています。 v3.1.0での追加予定: ODCS v3.1.0 RFC0013 その他のプロパティの作成方法 nameプロパティやphysicalTypeプロパティなどの基礎的なプロパティの作成は、データソースのDBの スキーマ 情報や生データのデータファイルをソースとして生成AIに与えることで半自動生成もしくは自動生成が可能です。 Excel などの固有ソフトウェアのファイル形式の場合は、機械判読可能なオープンなファイル形式への変換が必要です。 さいごに 検証の振り返り 本記事では、Data Contract(ODCS形式)を活用したデータパイプラインのAI駆動開発手法を検証しました。 成果として得られたこと : 12テーブル結合、35カラムの複雑な SQL クエリを生成AIが正確に生成 Data Contractに定義した型キャスト、計算ロジック、JOIN条件が意図通りに実装 増分処理やdbtのincremental設定も適切に生成 成功要因 : データ アーキテクチャ とデータモデルの2軸でコンテキストを整理 データ項目やデータ型、制約などの スキーマ 情報に加えて、 transformSourceObjects と transformLogic でデータリネージを明示 ローカル完結型の開発環境で生成AIの試行錯誤を高速化 所感 データエンジニアリングのAI駆動開発に向けたドキュメント整備方法の1つを見出すことができて良かったと思います。 データ基盤/データ分析基盤を概念的に分解し、既存の専門用語に当てはめて構造化して説明することで生成AIが体系的かつ詳細に理解するようになることが実感できました。 また、特定の製品・サービスに依存せずに進められましたので、再利用性が比較的に高いと考えており、今後の応用として関連の製品・サービスと連携しDataOpsやAIOpsへ昇華できればと期待しています。 データマネジメントのより広い範囲への活用の可能性 本記事ではデータパイプラインのコード生成に焦点を当てましたが、Data Contract自体の生成やデータ品質テストの自動生成など、より広い範囲への活用も考えられます。実際にData Contractの quality プロパティを応用したデータ品質テストコードの生成を検証しており、その内容は別途記事化の予定です。 また、データの意味的な定義やデータ統合に関する マッピング 情報などを整備してData Contractを拡張することで、例えばデータマネジメント全般における機械判読可能なドキュメントとしての活用やAI伴走型の分析データモデル開発への応用も検討しています。 データエンジニアとして、データマネジメントにおけるData Contractと生成AIの活用可能性については、今後も継続的に検証を進めていく予定です。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @shikarashika レビュー: @yamada.y ( Shodo で執筆されました )
アバター
はじめに 金融IT本部 2年目の坂江 克斗です。 業務にてDNSSECを使用する機会があり、とても面白い内容だったのでまとめてみました。 初学者の視点で疑問に感じる部分も含め、基本的な概念から丁寧に解説できればと思います。 (概要説明が不要な方は、Terraformによる実装の章のみご参照ください) 本記事は 電通総研 Advent Calendar 2025 5日目の記事です。 はじめに DNSの概要 DNSとは 名前解決の流れ (DNSサーバとリゾルバ、レジストラの関係) DNSSECの概要 通常のDNSの脆弱性 DNSSECで登場するリソース DNSSECの署名検証フロー (補足)DNSSECが確認できるサイト AWSにおけるDNSSECの実装方法 (Terraform) 前提 Terraformの実装 Applyの結果 Destroyの結果 おわりに DNS の概要 DNS とは DNS (Domain Name System)は、インターネット上で人間が読みやすい名前( ドメイン 名)と IPアドレス を対応付ける仕組みです。 コンピュータは通信の際に使用する IPアドレス (数値やビット形式)の方が便利ですが、人間にとっては「 example.com 」のような文字列( ドメイン 名)の方が読みやすく便利です。 DNS は、こうした人間に分かりやすい ドメイン 名から機械が扱う IPアドレス に変換( 名前解決 )する役割を担っています。 なお、 ドメイン 名を省略せずに完全な形で記載したものを FQDN (Fully Qualified Domain Name) と呼びます。 名前解決の流れ ( DNS サーバとリゾルバ、 レジストラ の関係) 全ての ドメイン 名と対応する IPアドレス を一つのサーバで管理するのは、データ量や処理速度の面から現実的ではありません。 そのため DNS では、「.」で区切られた各 ドメイン ごとに専用のサーバ(後述する権威サーバ)が分散して管理を行っています。 各権威サーバが独立して情報を持つだけでは、名前解決をする際に直接目的の権威サーバにアクセスする必要があり、全ての権威サーバのアドレスを把握しておかなければならず、現実的ではありません。 そのため、親 ドメイン の権威サーバが子 ドメイン の権威サーバのアドレスを持つ、つまりある ドメイン (例:com)に「○○.」を加えた ドメイン (例:sample.com)の権威サーバの場所を持つことで、親から子を辿り目的の権威サーバまでアクセスする仕組みが使用されています。 親 ドメイン から子 ドメイン に向かって根が張っているようなこの仕組みにより、どんなに長い ドメイン 名に対しても目的の権威サーバまで効率的にたどり着ける構成になっています。 上記の内容を現実のリソースに合わせて具体的に示します。 ドメイン 解決で登場するリソース名とその関係は、以下の図に示す形になります。 権威サーバに聞きまわって解決するのはフルサービスリゾルバ( 再帰 リゾルバ とも呼ばれます)が担当していることが分かります。 特に、権威サーバの中でも最初にアクセスする「.」 ドメイン を管理するサーバを ルートサーバ と呼び、ルートサーバがレコードとして管理する「.com」や「.jp」等を TLD ( トップレベルドメイン ) と呼びます。 また、先述した権威サーバの宛先情報を持つのが NSレコード であり、 ドメイン 名と IPアドレス を紐付けるのが A/AAAAレコード ( IPv4 だとA、 IPv6 だとAAAA)となります。 NSレコードの補足 NSレコードには ドメイン 名が値として設定されます。そのため、子 ドメイン にアクセスする場合は、親 ドメイン に登録されたNSレコードを取得した後、NSレコードに設定されていた ドメイン 名をさらに名前解決する必要があります。 もし、その ドメイン 名が子 ドメイン と同じゾーンに属している場合、再度親 ドメイン から解決する循環参照に陥ってしまいます。この問題を防ぐために、親 ドメイン 側にNSレコードとともに Glueレコード (権威サーバの ドメイン に対応するA/AAAAレコード) を設定する必要があります。 ルートサーバや TLD 用の権威サーバは特に名前解決の根幹となるサーバであり、ゾーン情報が適切に管理される必要があります。 現在は ICANN (The Internet Corporation for Assigned Names and Numbers) によりルートサーバを管理、各 TLD の権威サーバ・ゾーン情報の管理を レジストリ が担当し、消費者への仲介を レジストラ が担当しています。 細かい話になりましたが、ここでは ドメイン のレコードを管理・応答するのが権威サーバ、権威サーバが管理する論理的な ドメイン 単位の管理領域をゾーン( AWS ではホストゾーン)、名前解決を頑張るのがフルサービスリゾルバとだけ覚えていただければ十分です。 DNSSECの概要 通常の DNS の 脆弱性 DNS によって、ユーザは ドメイン から情報 ( IPアドレス 等) を取得します。 しかし、セキュリティ攻撃によってレコード情報が改ざんされると、例えば ドメイン 解決により取得した IPアドレス を使用した場合に悪意のあるWebサイト(フィッシングサイト等)にアクセスさせられる可能性もあります。 改ざんが発生するセキュリティ攻撃の例としては以下の図に示す、 DNS ハイジャックによるゾーン情報の改ざん、 DNS キャッシュポイズニング、中間者攻撃(の一例)等があげられます。 では、レコード情報が改ざんされていることをどのように検出すればよいでしょうか? 今回紹介するDNSSEC (Domain Name System Security Extensions) は、まさにこの問題を解決するための仕組みになります。 以下は JPRS によるDNSSECの説明となります。 DNS 応答に 電子署名 を追加し、問い合わせ側で検証することで DNS の攻撃耐性を向上させる、セキュリティ 拡張機能 です。DNSSECは、受け取った DNS レコードの出自(送信元で登録したデータであること)・完全性(データの欠落や改ざんのないこと)を問い合わせ側で検証できるようにするための機能を DNS に追加します。 ざっくりしたイメージとしては、親から子への信頼の連鎖(信頼チェーン)を構成し、改ざんされたとしても信頼が崩れた箇所で改ざんを検知できるものとなっています。 ただし、以下に示す前提の上でDNSSECがセキュリティを担保している点に注意してください。 クライアントから正しいフルサービスリゾルバへ接続していること フルサービスリゾルバから正しいルートサーバへ接続していること ルートサーバが正しい情報を持っていること 次節から具体的に説明します。 DNSSECで登場するリソース おさえるべきリソースは、2種類の鍵と3種類のレコードとなります。 リソースの一覧表および設定フローのイメージ図を以下に示します。 種類 名称 親/子 役割 鍵 ZSK (Zone Signing Key) 子 ゾーン内の リソースレコードに署名 するための非対称鍵(公開鍵・ 秘密鍵 のペア) KSK (Key Signing Key) 子 ZSKの公開鍵自体を署名 するための非対称鍵 レコード RRSIG (Resource Record Signature) 子 リソースレコードセット(同じレコード名、Typeでまとめた塊)毎に、 ZSKの 秘密鍵 により作成した署名 を持つレコード。署名とともに署名元のレコード、鍵の情報も持つ。 DNSKEY 子 ゾーンの公開鍵(ZSKとKSKの公開鍵) を持つレコード。レコードのFlagが256のものがZSK、257のものがKSK DS (Delegation Signer) 親 子ゾーンのKSK の ハッシュ値 を持つレコード。信頼チェーンの肝 ZSKとKSKの補足 DNSKEYレコード(ZSK) をそのままDSレコードとの紐付けに使用すれば、KSKが必要ないと思われます。しかし、実際にはセキュリティとメンテナンス性の観点から役割が分かれています。 RFC 4641 を参考に、私はZSK・KSKの推奨方針を以下のように解釈しています。 ・ 署名(暗号化)の管理観点で鍵をこまめにローテーションすべきである。しかし、ローテーション毎に親ゾーンのDSレコード更新が発生すると運用コストが高くなる。 ・ そのため、ZSKとKSKの2つの鍵ペアを用い、ZSKを頻繁にローテーションし、KSKを長期間使用する方針が推奨される。これにより、親ゾーンのDSレコード更新頻度を下げる。 ・ KSKはZSKの署名に使用されるのみであり、大量のデータ署名には使用しない。そのため、強度の高い鍵(より大きなビット長のキーマテリアル)を採用できる。 ・ KSKの使用頻度の低さから、ZSKよりも安全な場所(HSM)に保管できる。 DNSSECの署名検証フロー 前節で示した設定フローイメージを基に考えると、aa.comのAレコードを解決する際の署名検証は以下のような流れで行われます。 親ゾーンのDSレコードと子ゾーンのDNSKEYレコード(KSK)を検証し、子ゾーンのKSKの正当性を確認します。 子ゾーンのKSKを使用して、子ゾーンのDNSKEYレコード(ZSK)とRRSIGレコードを検証し、子ゾーンのZSKの正当性を確認します。 子ゾーンのZSKを使用して、子ゾーンのAレコードとRRSIGレコードを検証し、子ゾーンのAレコードの正当性を確認します。 上記は1組の親子ゾーンに対しての検証フローになりますが、ルートゾーンからの検証も同様の仕組みで行われます。 ただし、ルートゾーンのKSK、ZSK、DSレコードは信頼できる前提となっています。 このDSレコードによる署名検証の連鎖こそがDNSSECの信頼チェーンの肝であり、DNSSECで一番面白いと思うポイントになります。 以上がDNSSECの概要となります。 (補足)DNSSECが確認できるサイト DNSSECが適用されているかどうかは、digコマンドでレコードを確認していくことでも可能ですが、 DNSViz がオススメです。 確認したい ドメイン 名を入力し、以下に示すAnalyzeタブにてAnalyzeボタンを押下します。 ※明示的にAnalyzeボタンを押下しない限り、情報が更新されないことに注意 処理が終わった後に、Continueボタンを押下すると、DNSSECの設定状況が図でわかりやすく表示されます。 AWS におけるDNSSECの実装方法 (Terraform) 前提 Route53( レジストラ としての AWS )で購入した ドメイン とその サブドメイン にDNSSECを設定します。 Route53の場合は、 ZSKをAWSが管理している ため、以降の手順でZSKに関する設定はありません。 コンソールでの設定方法は AWS公式ドキュメント に示されているため、今回はTerraformに絞っています。 Terraformの実装 小規模なのでlocalで構成します。基本設定は以下になります。 terraform { required_version = "~> 1.14.0" required_providers { aws = { version = "6.23.0" source = "hashicorp/aws" } } } provider "aws" { region = "ap-northeast-1" } provider "aws" { alias = "global" region = "us-east-1" } locals { domain = "ks-sample.com" subdomain = "subdomain.${local.domain}" } data "aws_caller_identity" "current" {} ホストゾーンの定義をします。Route53の購入 ドメイン に対しては、自動でホストゾーンが作成されているためdata sourceを使用して取得します。 また、ホストゾーンを作成するだけでは、親 ドメイン 側に サブドメイン の権威サーバの宛先情報(NSレコード)が存在せず サブドメイン の名前解決ができないため、明示的に親ホストゾーンに サブドメイン のNSレコードを追加します( サブドメイン 委譲)。 # Hosted zone data "aws_route53_zone" "registered_domain" { name = local.domain } resource "aws_route53_zone" "subdomain" { name = local.subdomain } resource "aws_route53_record" "subdomain-ns" { zone_id = data.aws_route53_zone.registered_domain.zone_id name = local.subdomain type = "NS" ttl = "30" records = aws_route53_zone.subdomain.name_servers } KMSカスタマー管理キーを定義します。 DNSSECで使用するカスタマー管理キー は、以下の4つの設定が必須となります。 バージニア 北部(us-east-1)で作成 署名用の非対称鍵: SIGN _VERIFY 鍵の形式: ECC _NIST_P256 内部的には署名 アルゴリズム ECDSA_SHA_256を使用。 RFC 8624 を参照すると、 RSA 系に比べて短い署名長かつより高い暗号強度を持つことからECDSA系が推奨されています。 Route53サービスがKMSキーにアクセスするための キーポリシー # KMS key resource "aws_kms_key" "kms_key_global" { provider = aws.global description = "Key for DNSSEC" key_usage = "SIGN_VERIFY" customer_master_key_spec = "ECC_NIST_P256" enable_key_rotation = false policy = jsonencode ( { Statement = [ { Action = [ "kms:DescribeKey" , "kms:GetPublicKey" , "kms:Sign" , ] , Effect = "Allow" Principal = { Service = "dnssec-route53.amazonaws.com" } Sid = "Allow Route 53 DNSSEC Service" , Resource = "*" Condition = { StringEquals = { "aws:SourceAccount" = data.aws_caller_identity.current.account_id } ArnLike = { "aws:SourceArn" = "arn:aws:route53:::hostedzone/*" } } } , { Action = "kms:CreateGrant" , Effect = "Allow" Principal = { Service = "dnssec-route53.amazonaws.com" } Sid = "Allow Route 53 DNSSEC Service to CreateGrant" , Resource = "*" Condition = { Bool = { "kms:GrantIsForAWSResource" = "true" } } } , { Action = "kms:*" Effect = "Allow" Principal = { AWS = "arn:aws:iam::$ { data.aws_caller_identity.current.account_id } :root" } Resource = "*" Sid = "Enable IAM User Permissions" } , ] Version = "2012-10-17" } ) } KSKの作成、DNSSECの有効化(内部的にDNSKEYレコードやRRSIGレコードの作成・署名設定を行う処理をトリガーするものと考えられます)を定義します。 DNSSECの有効化時にKSKが作成済みになっている必要があるため、 hosted_zone_id = aws_route53_key_signing_key.registered_domain.hosted_zone_id のように設定して暗黙的に依存関係を定義します。 # KSK : https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_key_signing_key resource "aws_route53_key_signing_key" "registered_domain" { provider = aws.global hosted_zone_id = data.aws_route53_zone.registered_domain.id key_management_service_arn = aws_kms_key.kms_key_global.arn name = "ksk-$ { replace (local.domain, "." , "-" ) } " } resource "aws_route53_key_signing_key" "subdomain" { provider = aws.global hosted_zone_id = aws_route53_zone.subdomain.id key_management_service_arn = aws_kms_key.kms_key_global.arn name = "ksk-$ { replace (local.subdomain, "." , "-" ) } " } # DNSSEC resource "aws_route53_hosted_zone_dnssec" "registered_domain" { hosted_zone_id = aws_route53_key_signing_key.registered_domain.hosted_zone_id } resource "aws_route53_hosted_zone_dnssec" "subdomain" { hosted_zone_id = aws_route53_key_signing_key.subdomain.hosted_zone_id } 最後にDSレコードの定義をします。 購入 ドメイン の場合は親 ドメイン が TLD となるため、専用の aws_route53domains_delegation_signer_record を使用してDSレコードの作成を行います。一方、 サブドメイン 側は親 ドメイン がRoute 53ホストゾーンの管理内のため、DSレコードを直接作成する形になります。 また、depends_on で定義している依存関係は、以下の状態を満たしたうえでDSレコードを作成するためのものです。 対象ホストゾーンで DNSSEC が有効化されていること NS レコードが作成され、 サブドメイン の名前解決が可能な状態になっていること 親ゾーンから順番に DNSSEC の信頼チェーンを繋げていく(DSレコードを作成する)こと # DS Record # Registered domain TLD専用:https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53domains_delegation_signer_record resource "aws_route53domains_delegation_signer_record" "registered_domain" { depends_on = [ aws_route53_hosted_zone_dnssec.registered_domain ] domain_name = local.domain signing_attributes { algorithm = aws_route53_key_signing_key.registered_domain.signing_algorithm_type flags = aws_route53_key_signing_key.registered_domain.flag public_key = aws_route53_key_signing_key.registered_domain.public_key } } # Subdomain resource "aws_route53_record" "subdomain-ds" { depends_on = [ aws_route53_hosted_zone_dnssec.subdomain, aws_route53domains_delegation_signer_record.registered_domain, aws_route53_record.subdomain-ns ] zone_id = data.aws_route53_zone.registered_domain.zone_id name = local.subdomain type = "DS" ttl = "30" records = [ aws_route53_key_signing_key.subdomain.ds_record ] } Applyの結果 terraform apply を実行すると、エラーなく数分で完了しました。 PS C:/path> terraform apply (中略) Plan: 9 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes (中略) Apply complete! Resources: 9 added, 0 changed, 0 destroyed. Apply直後にDNSVizを確認すると、正常にDNSSECが設定されていることが確認できます。(黄色のWARNマークに関しては、AAAAレコードに関する注意であり今回は関係ありません) Destroyの結果 terraform destroy を実行すると、同様にエラーなく数分で完了しました。 PS C:\path> terraform destroy (中略) Plan: 0 to add, 0 to change, 9 to destroy. Do you really want to destroy all resources? Terraform will destroy all your managed infrastructure, as shown above. There is no undo. Only 'yes' will be accepted to confirm. Enter a value: yes (中略) aws_route53_zone.subdomain: Destruction complete after 33s Destroy complete! Resources: 9 destroyed. destroy直後に ks-sample.com に対してDNSVizを確認すると、DNSSEC設定が削除されていることが確認できます。 NSEC3レコードは「ある名前やタイプのレコードが存在しない」ことを証明するための特殊なレコードで、今回は親ゾーン側でDSレコードが存在しないことを示しています。 おわりに DNSSECに関連するレコードだけを見ていると一見とても難しく感じますが、今回のように図に落としてみると、署名検証の流れがシンプルかつ美しく整理されていることが分かります。 また、実装において依存関係に迷う部分もありましたが、現在は AWS を使用することでDNSSECを簡単に設定できますので、セキュリティ要件にある方は是非参考にしていただければと思います。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @sakae.katsuto レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
こんにちは。コーポレート本部 サイバーセキュリティ推進部の耿です。 本記事は 電通総研 Advent Calendar 2025 4日目の記事です。 AWS ではSecurity Hub CSPMのセキュリティ基準を有効にすると、何もリソースを作成していなくても非準拠の検出結果が複数発生します。この記事では新しいリージョンを使い始める初期に、どのような設定を行うとSecurity Hub CSPMの検出結果になるべく対応できるかをまとめます。対象とするセキュリティ標準は一般的によく使われる AWS Foundational Security Best Practices (以下「FSBP基準」)です。 初期のSecurity Hub CSPMの検出結果に対応する理由 新しい AWS アカウント・リージョンが使えるようになるといきなり システム開発 を始めたくなりますが、その前にまずはConfigとSecurity Hub CSPMのFSBP基準を有効にし、検出結果に対応していきましょう。理由は2つあります。 設定が推奨される項目がある GuardDutyの有効化など、リージョン単位で設定すべき/しておいた方が良い項目を見逃さないようにしておきたいです。 今後作成するイン フラリ ソースに対する検出結果に気づきやすくする 何もリソースを作成していない状態でも、FSBP基準は20個前後の非準拠の検出結果を生成します。これをキレイにし、初期のセキュリティスコアを100に近づけると、今後作成するリソースに対する非準拠の検出結果に気づきやすくなります。非準拠の検出結果に気づきやすいと、検出結果の対応が進みやすくなり、結果としてセキュリティリスクが低減され、 システム開発 に好循環を生み出すことが期待できます。 AWS アカウントレベルですべき設定 新しい AWS アカウントについては、以下の設定をしておきましょう。この記事は以下の対応はされている前提で割愛します。 CloudTrailのマルチリージョンで有効化( CloudTrail.1 ) S3のアカウントレベルのブロックパブリックアクセスを設定( S3.1 ) それでは新しいリージョンでConfigとSecurity Hub CSPMのFSBP基準を有効にしたときに、検出されるセキュリティコン トロール と対応の考え方を見ていきましょう。 (組織や AWS アカウントの設定により、非準拠となる項目が異なる場合があります。また、2025/11の執筆時点の情報です。今後Security Hub CSPMのアップデートにより非準拠となるコン トロール が増える可能性があります。) 初期のSecurity Hub CSPMの検出結果に対応する理由 AWSアカウントレベルですべき設定 GuardDutyを有効にする EBS のデフォルト暗号化を有効にする EBSスナップショットのパブリック共有をブロックする SSM Automationのログ記録を有効にする SSMドキュメントのパブリック共有ブロックを有効にする Inspectorの各種機能を有効にする Macieを有効にする デフォルトVPCを削除する VPCブロックパブリックアクセスを設定する CloudTrail.5を抑制する IAM.6を抑制する 全リージョンに対してループさせる さいごに GuardDutyを有効にする 対応推奨度:必須レベル 対応できるコン トロール : GuardDuty.1 GuardDuty を有効にする必要があります GuardDuty.7 GuardDuty EKS ランタイムモニタリングを有効にする必要があります GuardDuty.11 GuardDuty Runtime Monitoring を有効にする必要があります GuardDuty.12 GuardDuty ECS Runtime Monitoring を有効にする必要があります GuardDuty.13 GuardDuty EC2 Runtime Monitoring を有効にする必要があります AWS アカウントへの脅威を検出するために必須のサービスです。全てのリージョンで必ず有効にしましょう。 GuardDutyには標準機能の他に、任意に有効化・無効化できる 保護プラン があります。該当するリソースがある場合は追加で利用料金がかかりますが、許容できる限り利用をお勧めします。現在はGuardDutyを有効にすると、デフォルトでRuntime Monitoring以外の保護プランも同時に有効になります。 GuardDuty標準機能および、Runtime Monitoringも含めた全ての保護プランを有効にする AWS CLI コマンドです:(CloudShellから実行可) aws guardduty create-detector --enable --finding-publishing-frequency FIFTEEN_MINUTES --features '[{"Name" : "RUNTIME_MONITORING", "Status" : "ENABLED", "AdditionalConfiguration": [{"Name" : "ECS_FARGATE_AGENT_MANAGEMENT", "Status" : "ENABLED"}, {"Name" : "EC2_AGENT_MANAGEMENT", "Status" : "ENABLED"}, {"Name" : "EKS_ADDON_MANAGEMENT", "Status" : "ENABLED"}]}]' 検出結果をS3やEventBridgeに送信する間隔もデフォルトの6時間から15分に変更しています。 Runtime Monitoringを有効にしない場合の AWS CLI コマンドは以下です: aws guardduty create-detector --enable --finding-publishing-frequency FIFTEEN_MINUTES EBS のデフォルト暗号化を有効にする 対応推奨度:やった方が良い 対応できるコン トロール : EC2.7 EBS のデフォルト暗号化を有効にすることをお勧めします そのリージョンで作成されるEBSボリュームをデフォルトで暗号化することができます。EBSボリュームを使うかどうかはシステムに依存しますが、とりあえず設定だけしておくと良いでしょう。 AWS CLI コマンドは以下です: aws ec2 enable-ebs-encryption-by-default EBSスナップショットのパブリック共有をブロックする (2025/12/8に追加されたコン トロール です) 対応推奨度:やった方が良い 対応できるコン トロール : EC2.182 Amazon EBS Snapshots should not be publicly accessible リージョンレベルの設定であり、そのリージョンのEBSスナップショットの意図しないパブリック公開を全てブロックできます。 AWS CLI コマンドは以下です: aws ec2 enable-snapshot-block-public-access --state block-all-sharing SSM Automationのログ記録を有効にする 対応推奨度:やった方が良い 対応できるコン トロール : SSM.6 SSM Automation では CloudWatch ログ記録が有効になっている必要があります SSM Automationを利用するかどうかはシステムに依存しますが、とりあえず設定だけしておくと良いでしょう。 AWS CLI コマンドは以下です: aws ssm update-service-setting --setting-id /ssm/automation/customer-script-log-destination --setting-value CloudWatch SSMドキュメントのパブリック共有ブロックを有効にする 対応推奨度:やった方が良い 対応できるコン トロール : SSM.7 SSM ドキュメントでは、パブリック共有ブロック設定を有効にする必要があります これもとりあえず設定しておいて損はありません。 AWS CLI コマンドは以下です: aws ssm update-service-setting --setting-id /ssm/documents/console/public-sharing-permission --setting-value Disable Inspectorの各種機能を有効にする 対応推奨度:やった方が良い 対応できるコン トロール : Inspector.1 Amazon Inspector EC2 スキャンを有効にする必要があります Inspector.2 Amazon Inspector ECR スキャンを有効にする必要があります Inspector.3 Amazon Inspector Lambda コードスキャンを有効にする必要があります Inspector.4 Amazon Inspector Lambda 標準スキャンを有効にする必要があります 利用しているソフトウェアパッケージや開発したコード上の 脆弱性 を検出するサービスです。特にLambda関数については数が多いと利用料金が高くなる可能性があるため、アカウントの使い方に応じて各種機能を利用するか検討してください。 EC2スキャン、ECRスキャン、Lambda標準・コードスキャンを全て有効にする AWS CLI コマンドは以下です: aws inspector2 enable --resource-types EC2 ECR LAMBDA LAMBDA_CODE ※Lambdaコードスキャンは 利用可能なリージョン がまだ少ないため、利用不可のリージョンでは LAMBDA_CODE 以外の機能を有効にします。 Macieを有効にする 対応推奨度:やった方が良い 対応できるコン トロール : Macie.1 Amazon Macie を有効にする必要があります S3 バケット やS3オブジェクトの数が多いと利用料金が高くなる可能性があるため、アカウントの使い方に応じて利用するか検討してください。 Macieを有効化する AWS CLI コマンドは以下です: aws macie2 enable-macie デフォルト VPC を削除する 対応推奨度:やった方が良い 対応できるコン トロール : EC2.2 VPC のデフォルトのセキュリティグループでは、インバウンドトラフィックまたはアウトバウンドトラフィックを許可しないようにすることをお勧めします EC2.6 すべての VPC で VPC フローログ記録を有効にすることをお勧めします EC2.10 Amazon EC2 サービス用に作成された VPC エンドポイントを使用するようにAmazon EC2 を設定することをお勧めします EC2.15 Amazon EC2 サブネットは、パブリック IP アドレスを自動的に割り当てないことをお勧めします EC2.55 VPCs は ECR API のインターフェイスエンドポイントで設定する必要があります EC2.56 VPCsは Docker Registry のインターフェイスエンドポイントで設定する必要があります EC2.57 VPCsは Systems Manager のインターフェイスエンドポイントで設定する必要があります デフォルト VPC にまつわる様々なコン トロール が非準拠になっているはずです。最近は VPC を使わないシステム構成もよくありますし、必要になったら VPC を作成すれば良いので、各リージョンのデフォルト VPC は削除しておくことをお勧めします。そうすることでこれらの非準拠の検出結果は全て消えてなくなります。 CLI で VPC を削除するにはIGWの削除、サブネットの削除と段階を踏んで行う必要があります。( https://dev.classmethod.jp/articles/delete-default-vpcs-by-cloudshell/ を参考にさせていただきました) AWS CLI コマンドは以下です: aws ec2 describe-vpcs --output text --query "Vpcs[?IsDefault].[VpcId]" | while read vpc; do aws ec2 describe-internet-gateways --output text --filters Name=attachment.vpc-id,Values=${vpc} \ --query "InternetGateways[].[InternetGatewayId]" \ | while read igw; do echo "# deleting igw: ${igw} in ${vpc}" aws ec2 detach-internet-gateway --internet-gateway-id ${igw} --vpc-id ${vpc} aws ec2 delete-internet-gateway --internet-gateway-id ${igw} done aws ec2 describe-subnets --output text --filters Name=vpc-id,Values=${vpc} \ --query "Subnets[].[SubnetId]" \ | while read subnet; do echo "# deleting subnet: ${subnet} in ${vpc}" aws ec2 delete-subnet --subnet-id ${subnet} done echo "# deleting vpc: ${vpc}" aws ec2 delete-vpc --vpc-id ${vpc} done VPC ブロックパブリックアクセスを設定する 対応推奨度:使い方による 対応できるコン トロール : EC2.172 EC2 VPC ブロックパブリックアクセス設定はインターネットゲートウェイトラフィックをブロックする必要があります VPC ブロックパブリックアクセスはややこしい機能です。設定する場合は仕組みをよく理解したうえで設定しましょう( このドキュメント などを参考に)。 VPC 利用の有無、利用有の場合の通信方向に応じて設定の可否を判断してください。 双方向モードのブロックを設定する場合の AWS CLI コマンドは以下です: aws ec2 modify-vpc-block-public-access-options --internet-gateway-block-mode block-bidirectional Ingress -Onlyモードのブロックを設定する場合の AWS CLI コマンドは以下です: aws ec2 modify-vpc-block-public-access-options --internet-gateway-block-mode block-ingress CloudTrail.5を抑制する 対応推奨度:使い方による 対応できるコン トロール : CloudTrail.5 CloudTrail 証跡は Amazon CloudWatch Logs と統合する必要があります 特定のオペレーションを監視する場合などは、CloudTrail証跡をCloudWatch Logsに連携すると設定しやすいです。 特にそのような要件がない場合は検出結果を抑制しておきましょう。 CloudTrail.5 を抑制する AWS CLI コマンドは以下です: aws securityhub get-findings --output json --filters '{"ComplianceSecurityControlId":[{"Value":"CloudTrail.5","Comparison":"EQUALS"}]}' \ | jq -c '.Findings[]' \ | while read -r finding; do id=$(echo ${finding} | jq -r '.Id') productArn=$(echo ${finding} | jq -r '.ProductArn') aws securityhub batch-update-findings --finding-identifiers Id=$id,ProductArn=$productArn --workflow '{"Status": "SUPPRESSED"}' done IAM.6を抑制する 対応推奨度:使い方による 対応できるコン トロール : IAM.6 ルートユーザーに対してハードウェア MFA を有効にする必要があります ルートユーザにMFAを設定するのは必須ですが、 ハードウェア MFAを使っていない場合はこのコン トロール が非準拠になります。 ハードウェア MFAでなくても良い場合は検出結果を抑制しましょう。 IAM.6 を抑制する AWS CLI コマンドは以下です: aws securityhub get-findings --output json --filters '{"ComplianceSecurityControlId":[{"Value":"IAM.6","Comparison":"EQUALS"}]}' \ | jq -c '.Findings[]' \ | while read -r finding; do id=$(echo ${finding} | jq -r '.Id') productArn=$(echo ${finding} | jq -r '.ProductArn') aws securityhub batch-update-findings --finding-identifiers Id=$id,ProductArn=$productArn --workflow '{"Status": "SUPPRESSED"}' done 全リージョンに対してループさせる 以上のコマンドを単一リージョンではなく、 AWS アカウント内で有効な全リージョンに対して一気に実行したい場合は、各 AWS CLI コマンドに --region オプションを付与してループさせれば良いです。 (サービスが対応していないリージョンがあればエラーになるかもしれません) aws ec2 describe-regions --output text --query "Regions[].[RegionName]" \ | while read region; do # 実行したいコマンドを --region ${region} オプションを付けて書く done さいごに システム開発 中にSecurity Hub CSPMの検出結果を効率的に対応していくために、 AWS アカウントやリージョンを使い始める初期段階でできる限り非準拠の検出結果をなくしていきましょう! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 執筆: @kou.kinyo レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
みなさん、こんにちは。XI イノベーション 本部エンジニアリングテクノロ ジー センターの宮原です。 アメリ カ、ラスベガスにて12/1(月)から始まった AWS re:Invent2025に参加しております。 現地時間の12/2(火)の8:00からのキーノートに先ほど参加し、キーノート終了後速報でこちらのテックブログを書いております! キーノート内での新サービス・機能の発表の中でいくつかピックアップし、サマリー形式でお伝えできればと思います! 所感 新サービス・機能の発表 Amazon Nova 2 Amazon Nova Forge Policy in AgentCore AgentCore Evaluations Kiro autonomous agent AWS Security Agent AWS DevOps Agent Database Saving Plan まとめ 所感 今回のキーノート2時間のうちの9割がAIについての内容でした。 例年のキーノートではコンピューティングやストレージ、データベースについての新サービス・機能の発表が多くある印象ですが今回は9割がAIに関連するものでありAIの躍動を感じております。 AI以外のリリースもあったのですが、そちらの発表に割かれた時間は2時間のキーノートのうち最後のわずか10分だけでした。。。 (タイマーが表示されて10分でその他のリリースを発表するぜ、となり会場が湧きました) キーノートの発表から AWS でもAI関連の開発や発表が最優先となっていることが伺えました。 新サービス・機能の発表 キーノートの中で発表のあった新サービス・機能の中でも個人的に注目度の高いものをピックアップしてお伝えできればと思います。 Amazon Nova 2 Amazon Bedrockで選択できる AWS モデルで有名な Amazon Novaの新しいバージョンの発表がありました。 Amazon Nova 2はコストと精度の観点でバランスがいいモデルとして紹介があり今回の Amazon Nova 2でも速度の点やワークロードに応じてモデルが選択可能であることを強調していました。 従来 Amazon NovaはLite、Proなどいくつかモデルの種類があり、 Amazon Nova 2でも複数の種類のモデルがあります。発表スライドの中から Amazon Nova 2でも以下のモデルがあることを確認しています。 Lite コスト、速度重視 Pro 複雑な推論、精度重視 Sonic Speach to Speachなどの用途 Omni マルチモーダルの入出力 新種のモデルとして Amazon Nova 2 Omni が発表されています。 リリースのドキュメント を確認したところこちらのモデルではテキスト、画像、動画、音声を受け取り、画像、テキストを出力可能なようです。 Amazon Nova 2 Omni を利用して音声や画像などのマルチモーダルの入力を受けとり即時で要約するような事例も紹介されていました。 また、マルチモーダルな入出力に対応するモデルは業界初であることも強調されていました。 Amazon Nova Forge Amazon Nova Forgeは企業独自のカスタムモデルを開発するためのモデルです。 Amazon Nova Forgeのベースのモデルを Amazon SageMakerなどでト レーニン グし独自のカスタムモデルを開発します。 ドメイン に特化した情報などモデルの知識ではカバーできない領域でRAGを利用することは広く広まっていますが、 Amazon Nova ForgeではRAGのアプローチとは別の方法(カスタムモデル)で課題を解決します。 RAGとの精度比較のスライドはなく、今後もRAGとカスタムモデルでどちらの方が精度が出るかの議論が続きそうです。 Policy in AgentCore Amazon Bedrock AgentCoreの機能としてPolicy in AgentCoreの発表がありました。 こちらは Amazon Bedrock Agent Coreの上で動作するAIエージェントを コンプライアンス 要件に基づいて制御するための機能になります。 多くのAIエージェントは動作の中で様々なToolを呼び出しますがこのToolを実行条件に応じて制御します。 制御ポリシーについてはAamazon Verified Permissionでも採用されているCederの形式で記述します。 Amazon Bedrock AgentCoreでToolは AWS Lambdaとして実装されることが多いですが、少なくとも AWS Lambdaの実行制御は可能であると感じました。 AgentCore Evaluations Amazon Bedrock AgentCoreの機能としてAgentCore Evaluationsの機能も発表されました。 こちらは Amazon Bedrock AgetCoreで動作するAIエージェントの評価を実施するための機能になります。 Amazon Bedrock Knowledge Baseでも似たような機能がありますが、同様にLLM as a judgeを利用しながら開発したAIエージェントの品質を評価します。 Kiro autonomous agent Kiroに関連する発表も後半にありました。キーノート会場にはエンジニアが多いということもありKiroに関連する発表も注目度が高いように感じました。 Kiroを利用することで仕様駆動開発が促進され生産性の向上が期待できるという謳い文句です。 仕様駆動開発を利用して生産性が最も向上するのは仕様を確定させた後でKiroに長時間コードを生成させる、さらにそれらを並列に実行させることであると話していました。 そのような長時間の生成に特化したものがKiro autonomous agentです。 AWS Security Agent Kiroに関連する発表の中で AWS Security Agentの発表もありました。 AWS の開発でもセキュリティの要件は最も重要視されるべき要件であるとの前置きがあり、 AWS Security Agentの発表がありました。 こちらの機能を利用することで生成したコードのセキュリティチェックを実行するようです。 実際にどのようにAIが利用されているかまでは発表の中では把握しきれませんでしたが、特定のセキュリティ要件を満たしているかどうかをチェックしてくれそうです。 AWS DevOps Agent こちらもKiroに関連する発表の中で発表がありました。 AWS DevOps Agentは本番環境でのインシデントに対して、 Amazon CloudWatchなどと連携し、エラーの原因を自律的に特定し修正するための機能です。 こちらを利用することでオンコールの際のアラート対応の負荷も軽減されそうです。 Database Saving Plan 2時間の発表の最後の10分の発表の中でも最後の発表がこちらのDatabase Saving Planでした。 EC2などSaving Plan同様に一定の利用をコミットを確定することでコストメリットを享受できるものです。 データベースは途中で利用をやめることも少ないため、Database Saving Planの恩恵は大きそうです。 まとめ キーノートで発表があった新サービス・機能の中から独断でピックアップして概要レベルでまとめてみました。 個人的にも利用したことがあるサービス、興味のあるサービスである、Bedrock、Bedrock AgentCore、Kiroの発表が多くワクワクするキーノートでした。 また、我々エンジニアもフロントエンド、バックエンド、インフラ・SREなどのロール関係なくAIをより活用しなくてはならないなと改ためて思いました。 引き続き現地でAIに関連する技術のキャッチアップをしてこようと思います!以上、現地からの速報でした! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @miyahara.hikaru レビュー: @miyazaki.hirotoshi ( Shodo で執筆されました )
アバター
こんにちは、HCM事業部の中西です。POSITIVEというHRソリューションのUXデザイナーをしています。 この記事では、 Figma Makeの機能について検証した結果をお届けします。 この記事は、 電通総研Advent Calendar 2025 12月3日の投稿です 🎄 昨日は @エンタープライズ第一本部の宮澤響 さんの「 Amazon ECS操作時に「おや?」となった3つの事象 」 でした!ぜひご覧ください🥳 Figma Makeとは 背景・課題 目指したゴールは2点 検証内容 プロンプトでどのくらい精度が変わるのか検証 簡易的なプロンプト 生成画面 より詳細な情報を詰め込んだプロンプト 生成画面 コードの検証 考察 要件整理 / 情報アーキテクチャフェーズでの使いどころ ワイヤーフレームフェーズでの使いどころ 良いところと今後の課題 Pros Cons おわりに Figma Makeとは Figma Make は、 Figma 社が提供するAIデザイン生成ツールです。 テキストで指示(プロンプト)を入力すると、UIデザインや画面構成、 コンポーネント などを自動生成してくれます。 他のデザイン生成ツールと比較したときに特に優れていると感じた点は以下のとおり。 デザインシステムの公開ライブラリをデザインに反映できる 生成したデザインデータを Figma のフレームに還元できる この機能により、製品のエンハンスプロジェクトの デザイン ガイドライン に沿った形でAIがデザイン案を生成してくれて、デザインを変更したいところはデザイナーが手を加えて修正できる ようになります。 背景・課題 私たちのプロジェクトでは、現在下図のような流れでPM、エンジニア、デザイナーが協力してプロダクトを作り上げていきます。 要件整理と 情報アーキテクチャ のフェーズは担当範囲がグラデーションになっており、案件によって各メンバーが共創することもあれば、デザイナーがワイヤーから形にすることもあり、効率的なコラボレーションが課題となっていました。 また、要件整理や 情報アーキテクチャ 段階では、 画がないために具体的なイメージを共有しづらく、認識のズレが生じる こともありました。 Figma Makeではアイディアを即時に画面に起こしてくれるため、この機能を活用して要件整理から ワイヤーフレーム までの検証ができたら、専門性の違うメンバー間でのディスカッションの場にも役立つと思いました。 目指したゴールは2点 検討段階のアイディアを可視化し、UIの認識合わせをスピーディに行う(認識違いや制約・構造上できないを早めに把握) プロンプトだけで業務アプリの高精度なワイヤーを作り、ワイヤー制作における 工数 を削減する 検証内容 まず、プロンプトの質によって精度はどう変わるのかを実際に試してみました。 Figma 公式では、使用にあたり 8つのTips を提案しています。 要約:8つの重要な使い方 初回プロンプトに詳細を詰め込む 既存のデザインファイルを整理してから持ち込む 複雑なアプリケーションは小さなステップに分解して進める 自社/既存の コンポーネント を活用して一貫性を保つ “Point & Edit”機能で視覚的な微調整を行う コードタブを活用して視覚外の編集も行う(開発スキル不要でも扱える) リアルまたは疑似データを統合してプロトタイプのリアリティを高める Figma Make をハンドオフ支援ツールとして使う プロンプトでどのくらい精度が変わるのか検証 まずはプロンプトの詳細度によってどの程度精度に変化があるのか検証してみました。 はじめに、簡易的なプロンプトで試してみます。 Figma Makeにはデザインシステムのライブラリを読み込ませる機能もありますが、今回はUI コンポーネント の「shadcn/ui」を読み込ませました。 簡易的なプロンプト 生成画面 一見すると良さそうなUIが表示されましたが、よく見るとナビゲーションなどはなく、各カード コンポーネント に「開く」ボタンが配置され、 実際のアプリケーションとしてはまだ不完全な状態 です。一度生成されたUIに関してひとつひとつ指摘し修正を重ねるより、 最初のプロンプトでできるだけ詳細な要件を伝えておく方が、効率的に理想のUIを実現できる ことが分かります。 より詳細な情報を詰め込んだプロンプト 公式に記載されている情報や、 Figma Makeのブログなどを参考に詳細なプロンプトは以下の情報をまとめました。 ## プロジェクト概要 -従業員がさまざまな人事・ 労務 手続きをするためのWebアプリのUIデザインを作成します。 # プラットフォーム仕様 - Web(PC画面) # 目的と使用例 - 申請・通知・評価・組織情報・個人情報・誓約書・ 雇用契約 が一画面でアクセス - 従業員の“1点起点”としてのポータル設計 - 他社サービスとのシームレスな連携 - 入力の正確さに対する直感的な視覚フィードバック # 主要な機能の一覧 - 画面左側にサイドナビゲーション - 見出し「ダッシュボード」 - サイドナビゲーション内のカテゴリは2種類。「よく使うメニュー」と「すべてのメニュー」 - 画面中央上部によく使うアプリケーションをアイコン+カード型で表示 - 画面中央下部に会社からの通知を表示 # ヘッダー内の要素 - ロゴ(左上、24pxマージン) - ログインユーザーの会社名選択セレクトボックス - FAQページへの「?」アイコンを表示 ## サイドナビゲーションメニュー項目一覧 # 1.「よく使うメニュー」項目一覧 -勤怠管理 -申請 -給与明細 -経費精算 # 2.「すべてのメニュー」項目一覧 -勤怠管理 -通知 -申請 -年末調整 -給与明細 -経費精算 # トーク ン - カラー: theme.primary, theme.error - フォント: body-md, heading-lg # 技術仕様・制約 - レイアウトは垂直方向に中央寄せ - コンテナ幅720px - デザインはデザインシステム「Shadcn UI」を使用 # 期待する動作 - 「サイドナビゲーション」メニューをクリックすると アコーディオン が開き、サブメニューが表示される 生成画面 ナビゲーションメニューが追加され、会社からの通知欄では、通知内容からテキストリンクで詳細ページに遷移できる仕様になりました。ワイヤーとしては十分ですが、さらにデザインカンプの品質に近づけるため、プロンプトを追加してみます。 カーソルを変更したい箇所に合わせて、プロンプトを入力するだけで変更ができる仕様になっています。 ですが、カラーの変更にはHEXやバリアントの 命名 を渡す必要があり、 「これは Figma のデザインファイルでやったほうが早い」 というのが率直な感想です。 また、小さなことですがカーソルの入力フィールドは確定の意味で一度「Enter」を押すとプロンプトが送信されてしまい、もう一度最初からやり直しということが何度かありました。 コードの検証 これまでデザインカンプを「 CSS Variables」などの Figma プラグイン で生成したことがありましたが、一部の変数だけが :root { --... } に出力されず、値が欠落・空になる、またはVariablesの変数の参照先を取得できず、異なる値を返すため出力された色が異なる、という事象がありました。今回は 生成されたグローバル css が正しく出力できているか も確認しました。 Figma Makeではすべてのグローバル css が正しく出力されていました。 考察 今回 Figma Makeを実際に触ってみて、その可能性と実務での使いどころが見えてきました。 要件整理 / 情報アーキテクチャ フェーズでの使いどころ まだ固まっていないアイディアを可視化させ、必要な機能の抽出に活用できる と感じました。 実際にPMやエンジニアと、ユーザー行動のシナリオについてMakeを囲みながら議論を深めることで、プロダクトの方向性や優先順位を明確にすることができました。 ※余談ですが、こういうとき誰かが「やりましょう!」と声をかけると、みんな面白そうに集まってくれるのが、弊社のいいところだなと思います。 ワイヤーフレーム フェーズでの使いどころ 最初にしっかり情報を渡すことで、初期段階での画面構成の検討や、複数のレイアウトパターンを素早く比較したい場合に威力を発揮してくれます。 詳細プロンプトを毎回入力するのは大変なので、 トーク ンや技術仕様・制約などはテンプレート機能を活用するとさらに他メンバーも触りやすいと感じました! なにより、デザインデータに引き継げるのはデザイナーの作業にとって非常に時短になり助かります。 良いところと今後の課題 Pros アイディア出しに活用できる ワイヤーを爆速で作れる 非デザイナーでも使える Cons 品質を保つにはデザイナーの細かい調整が必要 おわりに Figma Make は「デザイン業務を効率化するツール」という印象が強かったのですが、実際に使ってみると、 他職種のメンバーとのコミュニケーションをスムーズにし、アイディア出しを活発にするための“共通の思考ツール”としても活用できると感じました。 今回は生成されたコードが開発環境でどの程度使えるか、という検証までには至りませんでしたが、デザイナーだけでなく、エンジニアも実際の開発フローに組み込めるのかといった点については、今後さらに以下の内容を検証していこうと思います。 電通 総研Advent Calendar 2025はまだまだ続きます! 明日の アドベントカレンダー は、 @Kinyo Kou さんの「 新しいAWSリージョンを使い始める初期のSecurity Hub CSPM検出結果に対応したい 」です!お楽しみに😆✨ 最後までお読みいただきありがとうございました! 電通 総研では仲間を募集しています✨興味を持っていただけた方は、ぜひ採用サイトをご覧ください! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @nakanishi.yoko レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター
はいどーもー! エンタープライズ 第一本部の宮澤響です! 本記事は 電通総研 Advent Calendar 2025 2日目の記事です! 記念すべき1日目である昨日の記事は、米田光佑さんの「 AWS Well-Architected のオートメーション(自動化)に触れてみる 」でした! IaCのコードを AWS Well-Architected Frameworkのベストプラクティスに照らして自動的に評価・診断できるツール「Well-Architected IaC Analyzer」の特徴や活用方法が分かりやすく解説されている記事ですので、ぜひご一読ください! ということで、本記事では、 AWS マネジメントコンソール上から Amazon Elastic Container Service(以下、ECS)を操作する際に「おや?」となった3つの事象をご紹介します! なお、本記事の情報は、2025年11月末時点のものとなりますので、ご承知おきください。 初回のECSクラスター作成時にエラー 事象 原因 解決方法 雑感 ECSタスクがプロビジョニングと停止を繰り返すもログが出力されていない 事象 原因 解決方法 雑感 ECSサービス作成画面でALBを新規作成するとエラー 事象 原因 解決方法 雑感 おわりに 初回のECS クラスタ ー作成時にエラー 事象 過去に一度もECS クラスタ ーを作成したことのない AWS アカウントでECS クラスタ ーの新規作成を実行した際、以下のようなエラーが発生しました。 クラスタ ー xxx の作成中にエラーが発生しました。 Resource handler returned message: "Invalid request provided: CreateCluster Invalid Request: Unable to assume the service linked role. Please verify that the ECS service linked role exists. (Service: AmazonECS; Status Code: 400; Error Code: InvalidParameterException; Request ID: xxx; Proxy: xxx)" (RequestToken: xxx, HandlerErrorCode: InvalidRequest) エラーメッセージを確認する限り、ECSサービスリンクロールを引き受けられていないようです。 しかし、ECSサービスリンクロールは、ECS クラスタ ー作成時に自動作成されるものです。[ 参考 ] 実際、今回も正しい設定値で自動作成されています。 原因 タイミングの問題でした。 ECS クラスタ ーを作成する際、裏ではECSサービスリンクロールを作成する処理やECS クラスタ ーを作成する処理などが並行して実行されます。 そのため、タイミングによってはECSサービスリンクロールの作成完了よりも先にECS クラスタ ーの作成処理が開始されてしまい、ECSサービスリンクロールが存在しないと判定されてしまうことがある、ということのようです。 解決方法 再実行すればOKです。 初回のエラーが発生した直後には既にECSサービスリンクロールが作成されているため、それ以降は特に問題なくECS クラスタ ーを作成できます。 雑感 私が新人研修用の AWS アカウントを16アカウント準備した際、16アカウント全てで上記のエラーに遭遇したため、かなり高い確率で発生するエラーなのではないかと考えられます。 ECSタスクがプロビジョニングと停止を繰り返すもログが出力されていない 事象 下図のように、ECSタスクがプロビジョニングと停止を繰り返すにもかかわらず、CloudWatch Logsにログが一切出力されていない、という事態が発生しました。 なお、デプロイ失敗のエラーとして画面に表示されたメッセージは以下のとおり「サーキットブレーカーが作動したよ」という内容だったため、サーキットブレーカーが止めてくれなければ、永遠にプロビジョニングと停止を繰り返していたと思われます。 miyazawa-test-service-xxx のデプロイ中にエラーが発生しました Resource handler returned message: "Error occurred during operation 'ECS Deployment Circuit Breaker was triggered'." (RequestToken: xxx, HandlerErrorCode: GeneralServiceException) 原因 ECSタスク定義を作成する際に、 awslogs-create-group : true という設定を削除していたためでした。 awslogs-create-group に true を設定すると、 awslogs-group で指定した名称のロググループが存在しない場合、ロググループを自動作成してくれます。[ 参考 ] 今回、私は、最低限の設定で検証したかったため、「削除」ボタンが表示されている(=必須ではない)項目は削除してしまおう、と安易に削除していました。 なお、ログは出力されなかったものの、 前回のステータス の部分にカーソルを重ねてみると、ヒントが隠されていました。 解決方法 以下のいずれかの方法で、無事にECSタスクが起動され、ログも出力されました。 awslogs-create-group : true を設定する awslogs-group の値と同名のロググループを事前に作成しておく 雑感 まずはログを確認する、という感覚で生きているため、そもそもログが出力されない本事象では、少し困惑しました。 AWS マネジメントコンソール上のどこにどのような情報が表示されるのかを把握しておくことの重要性を改めて実感しました。 ECSサービス作成画面でALBを新規作成するとエラー 事象 ALB+ECSの構成で各リソースを作成する場合、ALBやターゲットグループを先に作成してから、ECSサービスを作成するのが一般的です。 しかし、ECSサービス作成画面には、ALBやターゲットグループをその場で新規作成できる欄が存在します。 そのため、そこからの作成を実際に試してみたところ、想定どおりの疎通が確認できない、という事態が発生しました。 なお、構成の概要や設定値は以下のとおりです。 各種リソースの設定が想定どおりであれば、問題なく疎通できるものとします。 ALBはパブリックサブネットに配置 ECSタスクはプライベートサブネットに配置 ALBのSGでは、インターネットからのアクセスを許可 ECSタスクのSGでは、ALBのSGからのアクセスを許可 原因 ALBに対して、ECSタスクと同一のサブネット配置、同一のSGが設定されてしまうことが原因でした。 つまり、以下のような状態となっていました。 想定していた設定 実際の設定 サブネット パブリック プライベート SG ALB用 ECSタスク用 解決方法 前述のとおり、ALBやターゲットグループを先に作成してから、ECSサービスを作成するしかなさそうです。 雑感 改めて考えてみたら、確かに画面上にALBのサブネットやSGを設定できる項目がなかったんですよね。 他にも、ALBのヘルスチェックの成功コードなども設定できないようでした。 そのため、「ALBとECSタスクに同一のサブネット配置、同一のSGが設定され、ヘルスチェックの成功コードも200固定でOK」といった前提条件を満たす状況に限り、ECSサービス作成画面からのALB作成が有効なようです。 …そう多くはない状況な気はしますが。笑 おわりに 本記事では、 AWS マネジメントコンソール上からECSを操作する際に「おや?」となった3つの事象をご紹介しました。 昨日の記事がIaCに関する内容だったこともあり、イマドキ手動で環境構築すな!IaCで管理せい!というお声が聞こえてきそうですが、実際にIaCのコードを実装する前に、 AWS マネジメントコンソール上で手順や設定値を確認することはあると思います。 その際に、たまたま同じような事象に遭遇されましたら、本記事の内容を思い出していただけると幸いです。 さて、 電通 総研 Advent Calendar 2025 3日目となる明日の記事は、中西陽子さんの「【新機能】 Figma Makeの使いどころ」です! 2日連続で AWS 関連の記事でしたが、明日は心機一転、AIデザイン生成ツールに関する興味深い記事となります! お楽しみに! 最後までお読みいただき、本当にありがとうございました! 私たちは共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! 株式会社電通総研 新卒採用サイト 株式会社電通総研 キャリア採用サイト 執筆: @miyazawa.hibiki レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました )
アバター