TECH PLAY

電通総研

電通総研 の技術ブログ

814

フィジカルAIの実力は?SO-ARM101でSmolVLAを検証してみた フィジカルAIの実力は?SO-ARM101でSmolVLAを検証してみた 1.自己紹介 2.はじめに 3.VLAとは? 元となるChatGPTなどのAI VLAはアクションを出力 4.今回の検証の狙い 5. 検証環境 環境: ハードウェア: 6.検証内容:SmolVLAによる複数タスクの実行 SmolVLAとは 目指したこと 設定したタスク 7.検証結果 できたこと 検証から見えた課題 8.まとめ 1.自己紹介 HS本部Open Innovationラボ(通称イノラボ)の奥野です。 もともとは製造業で精密機器や家電のメカ設計・機能/制御設計・企画などに携わっていました。 2018年に 電通 総研に入社し、現在は様々な先端技術のR&Dに取り組むイノラボで、ロボット等の研究開発に取り組んでいます。 2.はじめに 最近ニュースで ヒューマノイド 、フィジカルAIといった言葉を目にすることが増えました。年明けに開催されたCES2026でも Hyundai Motor Groupのグループ企業であるBoston Dynamicsが研究開発を続けていた Atlas の商用化に向けた戦略が発表され盛り上がっていました。 イノラボでは長くロボットに関する研究開発に取り組んでおり、特に 最近はフィジカルAIの技術検証を進めております。 今回、その取り組みとして、フィジカルAIの根幹技術であるVLA( Vision Language Action)モデルでアームロボットを動かす検証を行った内容を執筆します。 細かい実行コードなどは記載せず、本記事ではVLAを試してみて何ができるのか・どんな課題がありそうかといったことをお伝えしたいと思います。 なお今回の検証では Hugging FaceのLeRobotとSO-ARM101を組み合わせた環境 で、VLAモデルの事後学習からロボット制御までを行っています。 3.VLAとは? 元となるChatGPTなどのAI 多くの人が使うようになったChatGPTはLLM(Large Language Model)でテキスト入力からテキストを出力したり、 Vision を組み込んだVLM( Vision -Language Model)で画像内容を理解し説明するなど視覚と言語を統合したモデルが組み込まれています。 VLAはアクションを出力 VLAは画像とテキストの入力から、Action(ロボットの制御)を出力するモデルとなります。 LLMやVLMで発展してきたTransformerやDiffusion modelといった技術が拡張され、ロボット制御である アクションを出力するよう発展したものがVLA です。 これまでシステムに閉じていたAIが、現実世界に直接関与できるようになったという点で インパク トのある技術進化だと言えます。 以下は Google が2023年に発表し注目を集めたVLAモデル RT-2 から引用した図です。 モデル内部に視覚と言語を統合したVLMを内包し、その推論結果から直接ロボットを制御するActionを生成することで汎化性を実現しています。 4.今回の検証の狙い 今回の検証目的は大きく3つです。本記事では狙い1と2についての結果を述べます。 狙い3についてはSmolVLAと Physical Intelligenceのπ0.5 を比較しましたので、別の記事で改めて書きたいと思います。 - 狙い1 : VLAの実行環境を構築して理解する - 狙い2 : VLAでアームロボットを動かして精度感や課題を確認する - 狙い3 : モデルによる差異を確認する 5. 検証環境 環境: マシンスペック Windows 11 Pro AMD Ryzen 9 9950X3D NVIDIA RTX5090 ソフトウェア : Lerobot v0.41 最初はv0.33の環境で検証していましたが、途中でv0.41がリリースされ環境を変更しました。 このバージョンアップでLerobot Datasetがv3.0となり、 Dataset Tools が使えるようになりました。 ただしv0.33で記録済みのデー タセット をv0.41環境(Lerobot Dataset v3.0)で使うためには、データ変換が必要となる点に注意です。 ハードウェア: ロボット : SO-ARM101 カメラ : USBカメラ3台(空間に2台、ロボットリストに1台) 以下は構築した実環境の様子です。 以下は3つの各カメラの様子です。 6.検証内容:SmolVLAによる複数タスクの実行 SmolVLAとは 今回の検証で使用した SmolVLA はHugging Faceが開発した軽量なVLAモデルです。他のVLAがパラメータ数Bなどと巨大であるのに対して、 SmolVLAはパラメータ数が450Mと小型化され、軽量な環境でも扱いやすいモデル です。 今回の環境でも問題なくファインチューニングから実行までできました。 目指したこと 今回の検証の前に ACT(Action Chunking with Transformer) による模倣学習でアームロボットにタスクを実行させる検証を実施していました。 ACTでも単独タスクであればある程度できるようになりますが、複数のタスクを連続して行うような複雑なものになると上手く学習させることができない結果でした。そこで今回はもう少し難しいことを目指してトライしました。 単独タスクが高い精度で実行できる 1つのモデルで複数の単独タスクが実行できる 複数の単独タスクを連続実行できる 設定したタスク 以下4つのアイテムをそれぞれ黄色のカゴに入れる、ピック&プレースのタスクを4種類設定しました。 それぞれの初期位置は固定として、目印をテーブル上につけた環境としました。 英文がSmolVLAの入力指示文です。 紫色のボール:Pick up only purple ball and put in yellow basket 黄色のボール:Pick up only yellow ball and put in yellow basket 赤色のキューブ:Pick up only red cube and put in yellow basket 青色のキューブ:Pick up only blue cube and put it in the basket 7.検証結果 できたこと 以下は今回の検証でチューニングしたSmolVLAモデルで、4つのタスクを連続実行している様子です。当初目指していた3つの目標は概ね達成することができました。 この動画では 4つのタスクを連続実行 していますが、 1つのモデルでそれぞれの単独タスクのみを実行することも実現 できています。 またそれぞれの単独タスクは 8~9割と高い成功率でタスクを実行することができました 。 検証から見えた課題 データ取得の大変さ 最終的な今回のモデルでは合計550回、1時間以上の学習データでファインチューニングしています。データ取得は人間による繰り返し作業となるため、 精神的にも肉体的にも非常にハード 。 SmolVLAにおいてはタスクを成功させるためには起きうる 条件全ての学習データが必要な傾向 で、条件に対する汎化性を持たせようとすると指数的に学習コストが高まります。例えば初期状態が常に4つ全アイテムがある状態だけのデータで学習すると、1つでもアイテムがない状態で実行した場合にタスクは失敗(動かないなど)となります。 モデル学習時間・コスト 軽量なSmolVLAでも今回の環境では1つのモデルの学習に10時間以上かかりました。今回は学習データの変化に対する結果を試行錯誤しながら検証を進めたため、この 学習時間が大きな ボトルネック でした。 一方で クラウド 環境でH100を使った場合6時間程度まで削減できることが確認できましたが、時間削減のために ハイスペックな GPU を使うと相応の利用コスト がかかります。やはりフィジカルAIには潤沢な GPU インフラが必要で、それだけコストもかかることが分かりました。 モデルの限界感 位置変動への汎化性は低いと感じました。学習データを増やすことである程度精度を高められそうではありますが、かなりのデータ量が必要になると想定されます。 紫と黄色のボール位置を交換するといった条件を混同しそうな変化に対しては、それぞれの条件データをどれだけ入れても成功できませんでした。 AIではそのままロボットを制御してしまうため、アーム先端がずっと振動する、急激な軌道変動が頻繁に起きる、先端が机に激突するといったことが多発しました。結果として筆者の環境では2ヶ月程度でモータ故障が発生しています。ロボットの最終的な制御部分にはAIをそのまま適応させるのではなく、ロボット制御を考慮したフィルタリングなどの工夫が必要となりそうです。 8.まとめ 検証を通して軽量なSmolVLAでも 一定のタスク実行精度や複数タスクの適応性があることを確認 できました。一方で多くの課題や限界感も見えた結果となりました。 VLAはフィジカルAIの重要な技術で急速に発展しています。日々新しいロボット・モデルが発表されており、期待が高まっていますが、それぞれ できることもあれば出来ないこともあり、落ち着いて見極めることが重要 と考えます。 実業務で使っていくためには解決すべき課題や、ソフト・ハード両面でシステム的な落とし所を設計する必要がありそうです。 しかし今回見えた課題については、様々な解決策の研究が進んでいたり米中を中心に巨額な投資がされており、ChatGPTの精度が急激に高まったようにフィジカルAIも周辺技術含め急激に進化する日が近いかもしれません。 新しい動向をキャッチしながら、引き続き検証を続けていきたいと思います。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @okuno_takahiro レビュー: @azeta.takuya ( Shodo で執筆されました )
アバター
検証環境 題材としたアプリ 主な機能 技術スタック SDDとは 1. cc-sddでSDDを試す 良かった点 課題に感じた点 2. spec-workflow-mcpを試してみる 実際に使ってみた流れ 良かった点 課題に感じた点 3. SDDで実装品質をどこまで引き上げられるか 3.1 バックエンドのリファクタリング Steeringの改善 生成されたタスク リファクタリングの結果 3.2 UI/UX課題の改善を要件定義からAIに任せる 発見した課題 AIに要件定義から任せる 改善結果 おわりに こんにちは、クロス イノベーション 本部の 北涼 太です。 最近、コーディングエージェントと協力して開発する手法が主流になりつつあると思います。 ただ、ざっくりした指示だと思ったような実装にならないことも多いですよね。 そんな中で気になっていたのが、SDD(Spec-Driven Development)という開発スタイルです。 「仕様を先に固めてから実装する」アプローチで、AIとの協業と相性が良いとされています。 今回はこのSDDを使って、実際にWebアプリを開発してみました。 今回の検証で分かったのは、 SDDは強力だが、仕様レビューの負荷が想像以上に高い ということでした。 そこで、レビュー体験を改善するSDDツールへの切り替えも試しています。 この記事では、以下の3点についてお伝えします。 CLI ベースのSDDツール(cc-sdd)を試して感じた課題 GUI でレビューできるツール(spec-workflow- mcp )への切り替え SDDで実装品質をどこまで引き上げられるか 検証環境 項目 内容 SDDツール cc-sdd → spec-workflow- mcp AIエージェント Codex(gpt-5.2-codex) 題材としたアプリ 今回は「 RSS ニュースをユーザーの趣味嗜好、フィードバックに合わせてスコアリングするWebアプリ」を題材にしました。 アプリ自体が目的ではないので、シンプル(かつギリギリ役に立ちそう)なアプリとしています。 主な機能 RSS URLとユーザーの趣味嗜好を入力 記事ごとにGood/Bad評価をつけられる 各ニュース記事についてAIエージェントに質問可能 ニュース取得ボタンで収集とスコアリングを実行 成果物のイメージは以下です。 ・メイン画面 ・AIエージェントとのチャット画面 技術スタック Frontend : TypeScript + React / Vite + TailwindCSS Backend : Python + FastAPI Infra : AWS CDK + API Gateway + Lambda SDDとは 本題に入る前に、SDDについて簡単に説明します。 SDDとは、要求・設計・タスクを先に固めてから実装に進むことで、曖昧さを減らす開発スタイルです。 今回は、以下のような手順を採用して開発を進めました。 フェーズ 内容 Steering プロダクト/技術/構成の前提をそろえる(product.md / tech.md / structure.md) Requirements ユーザーストーリーと受け入れ基準を定義する Design UI・ API ・データモデルなどの設計を詰める Tasks 実装タスクに分解し、順序と依存を整理する Implementation タスクを順番に実装する(必要に応じて設計へ戻る) コーディングエージェントに開発を任せられる粒度まで仕様を先に固める分、 レビュー対象となるドキュメントの量が多くなりやすい のが特徴です。 1. cc-sddでSDDを試す まずは CLI ベースのSDDツールであるcc-sdd( リポジトリ : https://github.com/gotalab/cc-sdd )を試してみました。 cc-sddは、Kiro 1 の設計思想( AWS では、 AI-DLC と呼んでいます)を他のAIエージェント上で再現するための CLI ツールです。成果物は Markdown で出力されます。今回はCodex CLI と組み合わせて運用しました。 ツールの仕組みについては詳しい記事がたくさん出ていますので、ここでは実際に使ってみた所感を中心に書きます。 良かった点 設計漏れが起きにくい : 不完全な設計書を渡しても、LLMが不足分を指摘してくれる 仕様を充実させるほど安定する : 前提条件を詳しく書くほど、出力のブレが減る 課題に感じた点 「仕様として十分か」の判断が難しい : どこまで設計すればAIに渡す前提として足りているのか、慣れが必要 AIの柔軟さが損なわれる可能性 : スクラッチで書く場合、最初から仕様を詰め切れないことも多い。中途半端に詳しい仕様を渡すと、かえってAIの判断を縛ってしまう Markdown のレビューが大変 : 自動生成された詳細な仕様を読み通すのに時間がかかる 特に負担だったのが、 Markdown ベースのレビュー でした。 仕様として十分な設計をAIが生成してくれるため、ドキュメント量が多くなるのは仕方ないかもしれません。しかし、指摘箇所を日本語で記述するコストが想像以上にかかりました。 たとえば「2.4章の〇〇について、××と書いているが、△△と書いてほしい」のような書き方をしなければならず、レビューのたびにこの作業が発生します。 そこで、SDDの流れは維持しつつ、仕様レビューを GUI で完結できる「spec-workflow- mcp 」に切り替えてみました。 2. spec-workflow- mcp を試してみる spec-workflow- mcp ( リポジトリ : https://github.com/Pimzino/spec-workflow-mcp )は、SDDワークフローを MCP (Model Context Protocol)経由で進めるための仕組みです。成果物は Markdown で管理しつつ、 レビューや承認はWebの GUI 上で完結 できます。 実際に使ってみた流れ 基本的な流れ(requirements → design → tasks → implementation)はcc-sddと同じです。 また、コーディングエージェントとのやり取りも、他のSDDツールと同様、 CLI から行います。 他の CLI ツールと異なるのは、 MCP サーバーとしてローカルにWebフロントエンドが立ち上がることです。 Webからはプロジェクトの様々な情報が確認できるのですが、特に注目すべきなのは、コーディングエージェントの作業を GUI 上でレビューできる機能です。 まず、requirementsなどの各ワークフローが完了すると、Web上に承認依頼が届きます。 承認依頼を開くと、 Markdown が レンダリング された状態で仕様を確認できます。 気になる箇所があれば、範囲選択をしてそのままコメントを残せます。 修正依頼を出すと、AIが仕様を直してくれます。変更点はDiffで確認できます。 requirements → design → tasks と順番に承認していき、最後にタスク開始を依頼すると、カンバンボードで進捗が自動更新されていきます。 良かった点 行範囲に対してコメントを残せる : 指摘箇所を日本語で説明する必要がなく、直接該当部分にコメントできる 通知が分かりやすい : 視覚化されるべき情報が適切に視覚化されている 課題に感じた点 Webと CLI の行き来が若干面倒 : 純粋な CLI ツールと比較して、どうしても操作回数は多くなる 3. SDDで実装品質をどこまで引き上げられるか ここからは、SDDを使って実装品質をどこまでコン トロール できるかを検証します。 3.1 バックエンドの リファクタリング 今回の検証では、要件定義に重きを置き、実装方針はほとんどAIに任せていました。 Steeringでクリーン アーキテクチャ に準じることをルールとして明記していたのですが、実際に生成された実装は以下のような構造でした。 backend/ ├── rss_news_agent/ │ ├── __init__.py │ ├── adapters.py │ ├── api.py │ ├── errors.py │ ├── llm.py │ ├── logging_service.py │ ├── models.py │ ├── repositories.py │ ├── services.py │ └── settings.py ├── tests/ ├── app.py ├── lambda_handler.py ├── pyproject.toml ├── requirements.txt └── uv.lock たとえば repositories.py を見ると、本来Provider層に書くべき実装が混在していました。 class DynamoDbClient (Protocol): def put_item ( self, TableName: str , Item: dict [ str , dict [ str , str ]], **kwargs: object , ) -> dict [ str , object ]: ... def delete_item ( self, TableName: str , Key: dict [ str , dict [ str , str ]], ConditionExpression: str , ) -> dict [ str , object ]: ... また llm.py を見ると、LLMに関連する様々なデータ型やクラスが一つのファイルに詰め込まれていました。 class LlmFeedbackSummaryDto (BaseModel): good_ids: list [ str ] = Field(default_factory= list ) bad_ids: list [ str ] = Field(default_factory= list ) class BedrockRuntimeClientAdapter : def __init__ (self, model_id: str , region: str | None = None ) -> None : self._client = boto3.client( "bedrock-runtime" , region_name=region) self._model_id = model_id これは良い実装とは言いにくいと思います。そこで、SDDを使って リファクタリング を依頼してみました。 Steeringの改善 方針として、Steering(今回は structure.md )のコード規約を詳しく追記し、具体的な実装はAIに任せます。 structure.mdの変更(抜粋) ## Directory Organization project-root/ ├── backend/ # FastAPIバックエンド -│ ├── rss_news_agent/ # ドメインロジック(API/サービス/リポジトリ) +│ ├── rss_news_agent/ # ドメインロジック(Clean Architecture) +│ │ ├── handler/ # ルーティング/入力変換 +│ │ ├── model/ # ドメイン/DTOモデル +│ │ ├── repository/ # 永続化インターフェイスと実装 +│ │ ├── service/ # ビジネスロジック +│ │ ├── provider/ # 外部依存(DB/HTTP等)の生成 +│ │ └── usecase/ # エンドポイント単位のユースケース │ ├── tests/ # pytestテスト │ ├── app.py # 本番/デプロイ用エントリ │ ├── local_app.py # ローカル開発用エントリ │ └── lambda_handler.py # Lambdaハンドラ ## Code Organization Principles 5. **クリーンアーキテクチャ指向**: バックエンドは層分離を意識し、依存性注入(DI)を徹底する +6. **責務の抽象化**: 外部依存はproviderに閉じ、ビジネスロジックはservice/usecaseに集約する +7. **境界の明確化**: 抽象(Port/Protocol等)は内側のレイヤーに置き、実装は外側に限定する ## Module Boundaries * **Repositories vs Services**: 永続化ロジックとビジネスロジックを分離 +* **Endpoint vs Usecase**: 原則として1エンドポイントに対して1usecaseを用意する +* **Provider vs Service**: providerは外部アクセスに限定し、ビジネスルールはserviceに置く +* **Port vs Implementation**: 抽象は内側、実装は外側に配置する 生成されたタスク この変更を反映すると、以下のようなタスクが生成されました。 - [-] 1. 現状のAPI/サービス/リポジトリの依存関係を整理して移行方針を確定する - [-] 2. model層を新設し、ドメインモデル/DTO/入力出力スキーマを整理する - [-] 3. repository層を新設し、永続化インターフェイスと実装を整理する - [-] 4. service層を新設し、ドメインロジックを整理する - [-] 5. usecase層を新設し、1エンドポイント=1usecaseの実装を追加する - [-] 6. handler層を新設し、ルーティングと入出力変換を移行する - [-] 7. provider層を新設し、外部依存の生成を集約する - [-] 8. 既存エントリポイントの接続を更新する リファクタリング の結果 実装を進めてもらった結果、各レイヤーがしっかり整理された構造になりました。 backend/ ├── rss_news_agent/ │ ├── handler/ │ ├── model/ │ ├── provider/ │ ├── repository/ │ │ ├── __init__.py │ │ ├── article_feedback_repository.py │ │ ├── rss_source_repository.py │ │ └── user_profile_repository.py │ ├── service/ │ ├── usecase/ │ │ ├── __init__.py │ │ ├── add_rss_source.py │ │ ├── delete_rss_source.py │ │ ├── fetch_articles.py │ │ ├── list_rss_sources.py │ │ ├── save_article_feedback.py │ │ ├── save_user_profile.py │ │ ├── send_article_chat.py │ │ └── update_rss_source.py │ └── __init__.py ├── tests/ ├── app.py ├── lambda_handler.py ├── pyproject.toml ├── requirements.txt └── uv.lock 実装内容も改善されました。たとえば user_profile_service.py では、純粋な ビジネスロジック だけが実装されています。 class UserProfileService : def __init__ ( self, repository: UserProfileRepository, clock: Callable[[], datetime] | None = None , ) -> None : self._repository = repository self._clock = clock or datetime.utcnow def save_personal_text (self, personal_text: str , user_id: str = "default" ) -> UserProfile: profile = UserProfile( user_id=user_id, personal_text=personal_text, updated_at=self._clock(), ) return self._repository.save(profile) def get_personal_text (self, user_id: str = "default" ) -> str | None : profile = self._repository.get(user_id) if profile is None : return None return profile.personal_text この検証から、 Steeringの質がコーディングエージェントの出力品質に直結する ことが確認できました。 3.2 UI/UX課題の改善を要件定義からAIに任せる 基本的なアプリケーションは構築できましたが、実際に触ってみると使いにくい点がいくつかありました。 発見した課題 1. RSS ソースの編集ボタンが効かない 「Edit」ボタンを押しても何も起きません。これは使いにくい点というより実装漏れですね。 2. パーソナル情報の保存状態が分からない ユーザーの趣味嗜好を入力する部分で、「SAVE」を押すと入力した文字が消えます。保存できたのか分かりません。 「SAVE」を押すと、バックエンドには保存処理が飛んでいるものの、画面上では文字が消えただけに見えます。 3. チャットの応答待ちが分かりづらい 各記事に対してAIエージェントと会話できる機能がありますが、返答を待っている間のインジケータが分かりにくいです。 AIに要件定義から任せる これらの課題を具体的には指摘せず、「UI/UX上の課題を自分で見つけて改善方針を仕様化せよ」と指示を出してみました。 以下は、生成された requirements.md の抜粋です。 ## Requirements ### Requirement 1: RSSソース管理の編集体験強化 (中略) ### Requirement 2: パーソナルシグナルの保存状態の可視化 (中略) ### Requirement 3: 記事一覧の可読性とスキャン性向上 (中略) ### Requirement 4: 収集アクションと進行状況の明確化 (中略) ### Requirement 5: チャット導線の発見性と文脈維持 (中略) WHEN チャット送信中 THEN システム SHALL 送信中の状態を明確に示し、二重送信を防ぐ。 こちらが指摘しなくても、明らかに使いにくい部分は要件として言及されていました。それ以外の項目も、改善案として納得できる内容でした。 改善結果 このまま機能設計 → タスク設計 → 実装と進めました。実装段階では期待どおりでない部分もあったため、都度フィードバックを与えています。 1. RSS ソースの編集 UIにはまだ改善の余地がありますが、編集できるようになりました。 2. パーソナル情報の保存状態 保存されたことが分かりやすくなりました。 3. チャットの応答待ち インジケータが表示されるようになり、分かりやすくなりました。 この検証から、 明らかに問題がある挙動については、AIに要件定義から任せても一定の改善が可能 なことが分かりました。 ただし、今回の検証アプリは コンポーネント 数が少なく、比較的シンプルな構成でした。より複雑なアプリケーションでは、同じようにうまくいくとは限りません。 おわりに 今回SDDを試してみて、個人的に印象に残ったことを書いておきます。 レビューの負荷は想像以上だった Markdown で生成された仕様を読んで、日本語で指摘を書いて…という作業は、耐えがたいものがありました。spec-workflow- mcp に切り替えてからは、行を選択してコメントするだけで済むようになり、だいぶ楽になったと思います。SDDを続けるなら、レビュー体験は重要だと感じました。 Steeringは可能な限り詳細に書いたほうが良い 当たり前かもしれませんがSteeringはプロジェクトの根本となる部分であり、品質を担保する意味で非常に重要です。 「クリーン アーキテクチャ で」と書くだけでは不十分で、 ディレクト リ構成や各レイヤーの責務まで書いてあげると、期待に近い実装が出てきました。ここは試行錯誤しながら調整していく部分かなと思います。 AIに要件定義から任せるのは、ものによる 明らかな問題点は拾ってくれましたが、細かいUI/UXの調整は結局人間がフィードバックを重ねる必要がありました。 まだ手探りな部分も多いですが、SDDとAIエージェントの組み合わせには可能性を感じています。 同じようにSDDを試している方の参考になれば幸いです。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @kita.ryota レビュー: @yamashita.tsuyoshi ( Shodo で執筆されました ) Kiroとは、 AWS が開発したAIエージェント型 IDE で、SDDのアプローチを採用しています。 ↩
アバター
XI本部 クラウド イノベーション センター所属、2年目の米田です。 別ブログにて Amazon ECR と FutureVulsを用いたコンテナイメージ運用について紹介させていただきましたが、実際に使っていく中で、 Amazon ECR には想像以上に多くの便利な機能が備わっていることに気づきました。本記事では、その中でも 日々の運用を効率化したり、セキュリティや管理性を高めたりするうえで役立つ ECR の機能について、実体験を交えながらいくつかご紹介します。 これから Amazon ECR に触れていく方や、すでに利用しているものの「なんとなく使っている」という方にとっても、本記事が少しでも参考になれば幸いです。 まずは復習 Amazon ECR の主な機能 タグフィルタ アーカイブストレージクラス プルタイム更新の除外 まとめ 参考 まずは復習 以前のブログでは、代表的なコンテナ オーケストレーション サービスである Amazon Elastic Kubernetes Service( Amazon EKS) と Amazon Elastic Container Service( Amazon ECS) の 2 つのサービスについてご紹介しました。これらのサービスは、コンテナのデプロイやスケーリング、可用性の確保など、アプリケーション実 行基 盤として重要な役割を担っています。 また、それらのサービスへアプリケーションをデプロイするための コンテナイメージの管理先 として、 Amazon Elastic Container Registry( Amazon ECR) についても取り上げました。 Amazon ECR は、主に Docker コンテナイメージを管理するためのフルマネージドな レジストリ サービスであり、 Amazon ECS、 Amazon EKS、 AWS Lambda(コンテナイメージ形式での実行)など、 AWS の主要なコンテナ関連サービスと高い互換性を持っています。そのため、 AWS 上でコンテナを利用する際の標準的なイメージ管理基盤として利用されるケースも多く、セキュリティ、可用性、運用性の観点からも安心して利用できるサービスとなっています。 Amazon ECR の主な機能 Amazon ECR には、単なるコンテナイメージの保管場所にとどまらず、セキュリティや運用効率を高めるためのさまざまな機能が備わっています。例えば、イメージのスキャンによる 脆弱性 検出、ライフサイクルポリシーによる不要なイメージの自動削除、IAM と連携した細かなアクセス制御など、実運用において役立つ機能が充実しています。 私自身の理解を整理する意味も込めて、本記事では面白いと感じた機能について、実際の利用シーンも想定しながらご紹介します。 タグフィルタ Amazon ECRでコンテナイメージを管理する際、イメージタグの上書き可否を制御できることをご存知でしょうか。ECRのイメージタグミュータビリティは、同じタグ名で異なるイメージを上書きできるかどうかを制御する機能です。 MUTABLE(ミュータブル) - デフォルトの設定 同じタグで何度でも上書き可能 例えば latestタグ を更新し続けるような運用に適している IMMUTABLE(イミュータブル) 一度プッシュしたタグは二度と上書きできない セキュリティとトレーサビリティを重視する運用に最適 IMMUTABLE_WITH_EXCLUSION(除外設定付きイミュータブル) - 比較的新しい機能 基本的にイミュータブルだが、特定のタグパターンだけミュータブルにできる 柔軟性とセキュリティのバランスが取れた設定 例えば、latest タグを使ってコンテナイメージを運用しているプロジェクトでは、latest を継続的に更新するために イメージをミュータブル(上書き可能) に設定しているケースが多く見られます(いわゆるlatest運用)。確かにこの設定にしておくと、同じタグ名であれば常に最新のイメージを参照できるという利便性はありますが、意図せず古いバージョンが上書きされてしまったり、本番環境と開発環境でイメージ内容が一致しなくなるといったリスクが高まるという危険性も伴います。 このような運用による問題を避けるために、 Amazon ECR では タグフィルタ機能 が登場しました。タグフィルタを利用することで、特定のタグ名や形式に対してのみ更新を許可したり、逆に更新を制限することができます。例えば、latest タグはミュータブルのままにしておきつつ、その他の重要なタグはイミュータブルに固定する、といった運用が可能になります。 タグフィルタ機能は コンソール上から簡単に設定できるだけでなく、Terraform の AWS プロバイダーでもすでにサポートされています。インフラをコードとして管理している場合でも、Terraform で柔軟にルールを定義・再利用できるため、安定したタグ運用を実現しやすくなっています。 resource "aws_ecr_repository" "this" { name = "blog-app" image_tag_mutability = "IMMUTABLE_WITH_EXCLUSION" image_tag_mutability_exclusion_filter { filter = "latest" filter_type = "WILDCARD" } } アーカイブ ストレージクラス Amazon ECRを使い続けていると、過去のバージョンのコンテナイメージが蓄積され、ストレー ジコス トが気になってきます。削除するには コンプライアンス 上の保持が必要だったり、いざというときの ロールバック 用に残しておきたかったりと、なかなか削除できないイメージも多いはずです。 そんな課題を解決するために、 AWS は2024年に アーカイブ ストレージクラス(Archive Storage Class)機能をリリースしました。この機能を使うと、アクセス頻度の低いコンテナイメージを通常ストレージの約3割削減されたコストで保管できます。厳密には、最初の150TBまでは標準ストレージと同じコスト($0.10)なのですが、それ以上の アーカイブ がある場合はディスカウントされるようです(以下リファレンスですが、日本語版では情報が掲載されておりませんでしたのでご注意ください)。 https://aws.amazon.com/jp/ecr/pricing/ 項目 標準ストレージ アーカイブ ストレージ 月額コスト(ap-northeast-1) $0.10/GB $0.07/GB(約3割減) アクセス時間 即座 復元が必要(数時間) 最小保存期間 なし 90日間 使用シーン 頻繁にpull/deploy 長期保管・ コンプライアンス 復元コスト なし pull時に復元料金が発生 過去バージョンの長期保管 ロールバック 用の古いバージョン メンテナンス終了した古いバージョン などで用意しておくべきイメージに対して、本機能を駆使することが多いようです。 アーカイブ は手動で実施することもできますし、もちろん CLI やライフサイクルポリシーを使ってイメージを遷移させることもできます。以下はそれぞれの設定例です。 コンソール CLI aws ecr put-image-lifecycle-policy \ --repository-name blog-app \ --image-ids imageTag=v1. 0 . 0 ライフサイクルポリシー resource "aws_ecr_lifecycle_policy" "this" { repository = aws_ecr_repository.this.name policy = jsonencode ( { rules = [ { rulePriority = 1 description = "90日以上前のイメージをアーカイブ" selection = { tagStatus = "any" countType = "sinceImagePushed" countUnit = "days" countNumber = 90 } action = { type = "archive" } } , ] } ) } 簡単に設定できそうですね。 アーカイブ するとプルすることはできなくなり、エラーが返ってきます。 $ docker pull <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/<リポジトリ名>@tag Error response from daemon: unknown: The requested image is in an inaccessible state. Please restore if needed プルする(使用する)ことはできませんが、 describe-images や list-images などをつかった詳細表示は可能です。 こうなると、めったに使わなさそうなイメージはすべて アーカイブ してしまった方が良い気もしますが、復元には一定のコストがかかります。また復元には20分ほど時間がかかるようです(とはいえ自分が試してみたときは数分で復元されてました)。何時間もかかるようなものではないようなので、障害復旧とかでもプロジェクトの要件次第では利用できるかもしれません。 # 復元リクエスト aws ecr restore-archived-images \ --repository-name blog-app \ --image-ids imageTag=v1.0.0 アーカイブ 後は削除も可能ですが、 アーカイブ されたイメージの最小ストレージ期間は 90 日間なようで、ライフサイクルポリシーで削除するには最低でも90日を設定する必要があるようです。もし90日未満のイメージを削除したい場合は、 batch-delete-image API を使った手動削除 になるので、もし短期間のみの アーカイブ を望むのであれば アーカイブ 設定は不適切になります。 ちなみにですが、2025年8月よりECRの リポジトリ あたり 100,000 のイメージまでがサポートされているようです。 https://aws.amazon.com/jp/about-aws/whats-new/2025/08/amazon-ecr-supports-100000-images/ このサポートが保証するイメージ数に、 アーカイブ したイメージが含まれるのかはわかりませんでした...。 コストだけでなく、キャパシティも最適化されると良いですね。 プルタイム更新の除外 Amazon ECRでは、コンテナイメージがプルされるたびに「最終プル時刻(LastRecordedPullTime)」が更新されます。この情報は、sinceImagePulledを使ったライフサイクルポリシーで「実際に使われていないイメージを自動削除する」という賢い運用に活用できます。 そんな中で、この時刻の更新を実施しないようにする方法も存在します。それが プルタイム更新の除外機能(Pull Time Update Exclusion) です。特定のIAMロールからのプルをLastRecordedPullTimeの更新対象から除外することで、より正確なライフサイクル管理が可能になります。 最近のアップデート機能のため、現時点ではまだ詳細な情報は多くありませんが、例えばセキュリティスキャナー(Snyk、Trivy、CrowdStrike など)や CI/CD パイプライン上のテスト処理が頻繁にコンテナイメージを pull すると、実際には本番環境で利用されていないイメージであっても「最近使用された」と判定されてしまうといった課題が想定されます。 その結果、本来であれば削除対象となるはずの古いイメージが、ECR のライフサイクルポリシーによって削除されず、不要なイメージが溜まり続けてしまう可能性があります。 こうした課題に対して、今回制御した制御機能を活用することで、スキャンやテスト用途の pull を「実運用の利用」とは区別して扱えるようになり、ライフサイクルポリシーをより正確に機能させることができます。 まとめ 今回は、 Amazon ECR における運用をより安全かつ効率的にするための機能として、 ミュータブル・イミュータブルのタグ制御(タグフィルタ)、 アーカイブ 機能、 プルタイムの更新除外機能 の 3 つを紹介しました。 これらの機能を活用することで、 タグの誤更新によるリスクを抑えられる 利用頻度の低いイメージを適切に保管・整理できる ライフサイクルポリシーをより意図通りに運用できる といったようなメリットが得られます。 ECR は単なるイメージ保管庫ではなく、工夫次第で運用負荷やコスト、セキュリティリスクの低減にも大きく貢献してくれるサービスだと感じました。 紹介できなかった機能もあるので第二弾としてまた掲載できればと思っております。 本記事が、これから ECR を本格的に活用していく方の参考になれば幸いです。 参考 https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/archive-image.html https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/pull-time-update-exclusions.html https://aws.amazon.com/jp/ecr/pricing/ https://aws.amazon.com/jp/ecr/ 執筆: @yoneda.kosuke レビュー: @taguchi.kazutoshi ( Shodo で執筆されました )
アバター
こんにちは! エンタープライズ 第三本部 マーケティング IT部の母壁です。 この度Lookerが MCP に対応し、外部のLLM(生成AIエージェント)からのLookerのデータ探索および活用が可能となりました。この記事ではLooker MCP の導入による利便性と、データ分析・ダッシュボード作成自動化における効果について検証した内容をご紹介します。 まず初めに、 MCP およびLookerの基礎的な知識をご説明します。 MCPとは MCP Toolbox Lookerについて 【検証】Gemini CLIからLooker MCPを使ってみる 1. MCP Toolboxのインストール 2. Gemini CLIのインストール 3. MCPクライアントの構成 4. ダッシュボードの自動生成 5. プロンプトの調整 おわりに:実用性はあるのか? MCP とは MCP (Model Context Protocol)は、 生成AIと外部データソースとの連携を標準化するためのオープンスタンダードな 通信プロトコル です 。 簡単にいうと今まで各社がそれぞれの方法で開発していたLLMと外部ツールの接続方式を統一したら色々と便利だよね、ということです。 MCP を公開したAnthropic社は「AIのためのUSB-Cのような存在」と表現しています。(今や iPhone や Android が同じType-Cコネクタになったように、LLMと外部ツール間の統一された接続規格として機能するということですね!) MCP が オープンソース 化されてから続々と対応するツールやサービスが登場しており、今後さらなる広がりが予想されます。 MCP Toolbox MCP Toolbox とは、AIアプリケーションとデータベースなどの連携を効率化するための Google が提供する オープンソース ツール群です 。 AIエージェントが企業データに安全にアクセスし、 自然言語 でのデータ検索や分析といったタスクを効率的に実行するための基盤を提供します。このToolboxの一部として、2025年7月に「Looker MCP Server」が提供開始されました 。 Lookerについて Looker は Google が提供するビジネスインテリジェンス(BI)ツールです。単なるデータの可視化にとどまらず、ガバナンスの効いた統合的なデータプラットフォームとしての側面も持ち合わせていますが、今回はデータソースから収集、加工、分析したデータをダッシュボード上で可視化できるツールとしてご紹介します。 【検証】Gemini CLI からLooker MCP を使ってみる ここから本題となりますが、 Looker MCP を使用した生成AIによるダッシュボードの自動生成 についてご紹介します。 本来Lookerでダッシュボードを作成するには GUI 上でデータを探索し、可視化したタイル(グラフなど)を一つずつ作成・配置をする必要があります。これを生成AIによって自動化できたら便利そうですよね、、? そこでLooker MCP の登場によりダッシュボード自動生成の機能がどこまで実用的なものなのか検証してみました。 以下、こちらの 公式ドキュメント を参考に作業を行いました。 1. MCP Toolboxのインストール まず MCP Toolboxの最新バージョンをダウンロードします。 curl -O https://storage.googleapis.com/genai-toolbox/v0.14.0/darwin/arm64/toolbox バイナリを実行可能にします。 chmod +x toolbox 最後にバージョンを確認しておきましょう。(検証時はV0.12で実施しています) ./toolbox --version 2. Gemini CLI のインストール 今回はローカル環境からGemini CLI を使用して MCP サーバーを構成しました。 Gemini CLI の利用は、 npx コマンドを使って一時的に実行することも可能ですが、 settings.json の設定など繰り返し利用する想定なので、今回は npm でグローバルにインストールしています。 npm install -g @google/gemini-cli 3. MCP クライアントの構成 続いて MCP クライアントを構成します。 以下のように settings.json ファイルを定義することで、Geminiが MCP クライアントとして機能し、起動時に MCP サーバーを立ち上げてくれます。この時ファイルは .gemini フォルダ直下に配置する必要がある点に注意してください。 各要素を少し解説します。 looker-toolbox :Looker MCP Serverを使用する場合は指定します。 command :インストールした MCP Toolboxのパスを指定します。 env :Looker インスタンス に接続する MCP クライアントを構成するための 環境変数 です。 LOOKER_BASE_URL :接続するLookerのURLを指定します。 LOOKER_CLIENT_ID :LookerにログインするユーザーのIDです。 LOOKER_CLIENT_SECRET :Lookerにログインするユーザーのシークレットです。 設定は以上です!ローカル環境という点もありますが、想定よりも簡単に設定が完了しました。 ではいよいよプロンプトを投げてダッシュボードを作ってもらいます。 4. ダッシュボードの自動生成 Gemini CLI を起動してみます。 MCP クライアントが正常に構成されていれば「Using: 1 MCP server」と表記されます。 ダッシュボードを表示する元データとして、BigQueryのパブリックデー タセット に用意されている ECサイト の売上データを使用しました。 事前準備としてBigQueryからLookerにデータを取り込み、LookMLで エクスプローラ ー等の定義を行ったうえで作業しています。 またLookMLでは以下のようにメジャーを記述し、日本語のラベルを追加することでLLMが探索をしやすくなるように メタデータ を補填する工夫を行いました。メジャー名に略称を使用している場合や、専門的な単語がある場合に、日本語のラベルを定義することで日本語のプロンプトに対して探索の精度が上がることは事前に確認しています。 この状態でLLMにダッシュボード生成を依頼してみましょう。 まずは事前に用意した エクスプローラ ー「hahakabe02」を指定して、ダッシュボードの中身については特に指定せずに生成を依頼してみます。 hahakabe02を使用して、2023年のセールスダッシュボードを作成してください。 依頼をするとまずはログインするユーザーの参照できる範囲でどのデータを探索するか逐一確認されます。参照するデータを選択することでダッシュボードに不要なデータを弾くことができます。 LLMが使用できるツールには例えば以下のようなものがあります。 get_models :LookML モデルの一覧を取得 get_explores : エクスプローラ ーの一覧を取得 get_dimensions :指定された Explore のディメンションの一覧を取得 get_measures :指定された Explore のメジャーの一覧を取得 query :クエリを実行してデータを返却 make_dashboard :ダッシュボードを作成しURLを返却 add_dashboard_element :ダッシュボードにタイルを追加 一連の探索が終わるとクエリを実行してダッシュボードに表示するタイルを作成してくれます。できあがったダッシュボードを確認してみましょう。 月ごとの売上の推移や 都道 府県別の売上など、お題に合うように指標を決めてタイルを作成してくれています。 一方で各タイルの集計条件を細かく確認してみると、例えば総売上の指標については発注情報のうち、「購入日時」のディメンションではなく「製造日時」のディメンションでフィルタリングしているようでした。このような集計条件の指定はプロンプトで指定してあげるほうが良さそうです。 5. プロンプトの調整 改めてプロンプトを調整してダッシュボード生成を依頼してみます。タイルの種類、参照するメジャー、フィルターの条件など各タイルに指定してみます。 Explore「hahakabe02」を使用して、新しいダッシュボード「2024年セールスダッシュボード demo」を作成してください。 以下のビジュアライゼーションをダッシュボードに追加してください: 総売上 タイプ:単一値 メジャー:[総売上] 日付フィルタ:[購入日時]が2024年である 値の形式:$#,##0 平均注文金額 タイプ:単一値 メジャー:[平均注文金額] 日付フィルタ:[購入日時]が2024年である 値の形式:$#,##0 ... できあがったダッシュボードを見てみるとフィルターが正常に適用され、2024年の購入データのみが表示されていることが確認できました!またグラフの形式や使用するメジャーを指定することである程度望んでいた表示になっていました。 ただし「値の形式」で表示桁数を整数値にするようにプロンプトで指示しましたが、LLMがそこを設定することは現状できないようでした。やはり作成されたグラフが正しいデータを参照しているのかの確認や、最終的な見た目の仕上げにはまだ人の手が必要です。 おわりに:実用性はあるのか? MCP はデータ統合・分析基盤やLLMの外部システム連携において、急速的に浸透しつつあります。Lookerが MCP に対応したことで実務で使える場面も増えてくると大いに期待できます。 1. ダッシュボード作成の初動を効率化 先述したように最終的なダッシュボードの正確性の確認や表示スタイルのチューニングなどには人力のコストがある程度必要なものの、作業の初動を早められることは確実だと感じました。また対象のデータに精通していない人でもAIとの会話でデータの探索、グラフの作成ができるため、人員調達のハードルも下がることが期待されます。 2. より高度な インサイト の導出 従来のLookerの組み込み会話分析機能では、通常1つの エクスプローラ ーの範囲内でデータをクエリしますが、複数の エクスプローラ ーを探索しデータを統合することでより柔軟で高度なデータ分析やレポート作成ができるようになると思います。また MCP サーバーを複数立てることでLookerだけでなく CRM やGoogleDriveなど外部サービスと連携することも可能です。例えば今回のように顧客データや売上だけでなく、営業活動のデータやウェブサイト行動のデータなどを集約し、横断的な分析と インサイト の提示を得ることも可能だと思われます。 このようなメリットが期待できる一方で、改めてLookMLにおけるデータの定義を充実させることが会話分析の精度向上に不可欠であることを実感しました。 最後までお読みいただきありがとうございました。 本記事ではLooker MCP を中心にご紹介をしましたが、弊社ではLookerの導入実績も豊富にありますので、Lookerの導入を検討されている、あるいはLookerの次の活用フェーズをお考えの際にはぜひご相談ください! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @hahakabe.kiichi レビュー: @kinjo.ryuki ( Shodo で執筆されました )
アバター
XI本部 クラウド イノベーション センター所属、2年目の米田です。 この度、2026 Japan All AWS Certifications Engineers クライテリア の応募基準を満たしました。約1年間(厳密には1年1か月)という比較的短期間で全冠でき、資格取得のための学習でかなり クラウド への理解が深まったと感じております。 今回は、 クラウド 初心者(ひいてはIT初心者)が、どのようにして AWS 資格を全冠得を進めたかなどを紹介できればと思います! これから AWS 資格に挑戦する人や、「何から取ればいいかわからない」「モチベーションが続かない」という人の参考になれば嬉しいです。 (同様に新卒1年目で条件を満たした大岡さんのブログも合わせてご覧ください!1年目で全冠を達成されたのはすごいですね、、!全冠を目指されたきっかけや思いが綴られておりますのでぜひ。) https://tech.dentsusoken.com/entry/2025/12/23/%E3%80%90AWS%E8%B3%87%E6%A0%BC%E3%80%91%E6%96%B0%E5%8D%921%E5%B9%B4%E7%9B%AE%E3%81%8CAWS%E8%B3%87%E6%A0%BC%E3%82%92%E5%85%A8%E5%86%A0%E3%81%97%E3%81%9F%E3%81%AE%E3%81%A7%E6%8C%AF%E3%82%8A%E8%BF%94 はじめに 他の方のブログでも紹介していただいていますが、そもそも 「2026 Japan All AWS Certifications Engineers」 とは、 AWS Partner Network(APN)に参加している会社に所属し、2026年度のクライテリアで定義された AWS 認定資格をすべて保持している AWS エンジニアを対象とした 表彰プログラム です。 2026年度のクライテリアでは、以下の AWS 認定資格を すべて有効な状態で保持していること が条件です。 資格は申し込み時点で有効であり、2026年4月30日までに有効期限が切れないことが応募要件となっています(うっかり失効しそう)。 レベル 必須資格 Foundational AWS Certified Cloud Practitioner Foundational AWS Certified AI Practitioner Associate AWS Certified Solutions Architect – Associate Associate AWS Certified SysOps Administrator – Associate または AWS Certified CloudOps Engineer – Associate Associate AWS Certified Developer – Associate Associate AWS Certified Data Engineer – Associate Associate AWS Certified Machine Learning Engineer – Associate Professional AWS Certified Solutions Architect – Professional Professional AWS Certified DevOps Engineer – Professional Specialty AWS Certified Security – Specialty Specialty AWS Certified Machine Learning – Specialty または AWS Certified Generative AI Developer – Professional Specialty AWS Certified Advanced Networking – Specialty なお「SysOps Administrator – Associate」か「CloudOps Engineer – Associate」のどちらか1つの取得でもOKです。 AWS Certified Machine Learning – Specialty は 2026年3月31日をもって受験終了予定 ですが、取得していない場合は AWS Certified Generative AI Developer – Professional を取得することでクライテリアを満たすことができます。 https://aws.amazon.com/jp/blogs/psa/2026-japan-all-aws-certifications-engineers-criteria/ 受験してみた感想ですが、SysOps Administrator – Associate と CloudOps Engineer – Associate にそこまで大きな差は感じられませんでした。最近アップデートがあった資格なだけあって身構えていましたが、これまで対象範囲でなかったECSやEKS、そしてCDKについて簡単に対策していけば、十分に合格できるかと思います。 ロードマップ 気になる取得までのロードマップですが、私は以下のような間隔と順番で資格取得いたしました。 資格 レベル 取得時期 AWS Certified Cloud Practitioner Foundational 2024年12月 AWS Certified Solutions Architect – Associate Associate 2025年1月 AWS Certified Solutions Architect – Professional Professional 2025年3月 AWS Certified Security – Specialty Specialty 2025年5月 AWS Certified Advanced Networking – Specialty Specialty 2025年7月 AWS Certified AI Practitioner Foundational 2025年8月 AWS Certified Machine Learning – Specialty Specialty 2025年9月 AWS Certified Machine Learning Engineer – Associate Associate 2025年10月 AWS Certified Data Engineer – Associate Associate 2025年11月 AWS Certified Developer – Associate Associate 2025年12月 AWS Certified DevOps Engineer – Professional Professional 2026年1月 AWS Certified CloudOps Engineer – Associate Associate 2026年1月 こちらも感想になりますが、やはり AWS に慣れてない最初は「受かりそう!」と思えるまで時間がかかりました。(特にSAP合格までは聞き慣れないサービスばかりで本当に苦労しました……)。 ただ、学習を続けていくうちに少しずつ理解が積み重なり、実際に受験してみると 「意外と受かるな」という感覚も持てるようになりました。 特に後半は、 AWS のサービス全体像や設計思想への理解が深まってきたことで、問題文を読んだ瞬間に「何を問われているのか」が掴める場面が増え、試験への 心理的 なハードルもかなり下がったように思います。 ただ落ちると 2週間は再受験できない ので、適当に受けるのではなく、しっかりと準備をしてから挑むことも大事ですね。 進め方 資格取得までの進め方についてですが、基本的には多くの方のブログで紹介されているとおり、オンライン問題集や教材を中心に学習していました。 これはあくまで個人的な学習スタイルですが、Udemyなどの動画講座を「視聴する」よりも、自分で実際に手を動かして調べたり、検証したりする方が圧倒的に理解が深まると感じました。実際に AWS 環境を触りながらサービスの挙動を確認し、設定を試して失敗し、なぜそうなるのかを調べる、みたいなプロセスを繰り返すことで、知識が単なる暗記ではなく、実感を伴った理解として身についていったと思います。 ただ一方で、自分の知らないサービスを知るきっかけを得る、 AWS のベストプラクティスを体系的に学ぶ、という意味では、動画講座や解説コンテンツをチェックする方法も非常に有効だと感じています。インプット(講座)とアウトプット(実践)をバランスよく組み合わせることが、結果的に一番の近道かと。 AIの活用 分からない内容が出てきた際にネット検索で調査するのも良いですが、個人的には近年発展したAIに頼ってみるのも有効だと思います。 2025年になっていろいろ出てきた AWS の MCP サーバについてご存じでしょうか。 MCP (Model Context Protocol)は、LLMアプリケーションと AWS サービスやリソースを接続するための標準化された プロトコル です。こちらは ユースケース に応じて AWS 環境の構築・運用・ トラブルシューティング など、様々なシーンで活用できる強力なツールになります。普段は開発などで使用しますが、たまに資格勉強でも利用していました。 https://awslabs.github.io/mcp/servers/ 利用方法についてはいろいろブログで記載されているので割愛しますが、例えば以下のような MCP サーバは使ってみても良いかなと思います。 AWS Documentation MCP Server AWS 公式ドキュメントをリアルタイムで検索・参照してくれる MCP サーバーです。 常に最新の情報をもとに回答してくれるため、特に最近アップデートされた資格や新サービス周りを調べる際に非常に有効だと感じました。 AWS IaC MCP Server CloudFormation や AWS CDK、Terraform などの Infrastructure as Code(IaC) に関する設計・記述を支援してくれる MCP サーバーです。 リソース定義の書き方を調べたり、テンプレートのレビューをしたりする用途に向いており、「この構成をコードでどう書けばよいか?」といった疑問を解消するのに役立ちます。実務でIaCを触っている人はもちろん、SAP や DOP など設計系試験の理解を深める用途でも使えると感じました。 AWS Pricing MCP Server リアルタイムの AWS 料金情報にアクセスし、コスト分析を行ってくれる MCP サーバーです。 個人的には使用頻度はそこまで高くありませんでしたが、 AWS 資格の試験では「コスト最適化」の観点でベストプラクティスを問われることが多いため、ケースによっては十分活用できるツールだと思い、紹介しています。 AWS の公式サイトやブログなどを調べて理解を深めてもらっても良いですが、一旦概要を知りたい際や、調べ方がわからない場合には上記 MCP サーバなどを活用して一旦AIに投げてしまっても良いですね! まとめ 改めて、 AWS 資格を全冠し、「2026 Japan All AWS Certifications Engineers」の応募クライテリアを満たすことができました。 もちろん資格取得そのものも目標のひとつでしたが、それ以上に、学習を通して クラウド 全体への理解が深まり、自信を持って AWS に向き合えるようになったことが何より大きな成果だったと感じています。 なお、All Certs を達成すると、 AWS Summit Japan で表彰されるようで、年によっては副賞として景品がもらえることもあるようです。 クラウド に少しでも興味がある方であれば、ぜひ一度、 AWS 資格へのチャレンジ、そして全冠を目標にしてみてほしいと思います。 (来年も全冠維持します...) 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @yoneda.kosuke レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
XI本部 クラウド イノベーション センター所属、2年目の米田です。 今回は、 AWS サービス上でのコンテナイメージの運用と、それをより効率的に実現するFutureVulsについて、基礎的な部分を中心に共有させていただきます。 本ブログは、 10月下旬にあった社内向けの勉強会 にて発表させていただいた内容を含んだものになっており、少し振り返りながら改めてまとめさせていただきました。 勉強会当日は100名以上の参加者となり、そのような場での発表は私自身とても良い経験でした。勉強会にご参加いただいた方、コメントいただいた方、取りまとめていただいた方、そしてフューチャー社の皆様、改めてありがとうございました。 はじめに コンテナ制御 コンテナイメージ管理 リポジトリの種別 自動継続的スキャン 脆弱性管理 自動トリアージ FutureVuls AI 未然防止 まとめ 参考 はじめに 普段、皆さんはどのような方法でアプリケーションを開発・実行されていますでしょうか。 クラウド 上でアプリケーションを起動する方法にはさまざまな選択肢があります。 仮想マシン 上で直接実行する方法、PaaS を利用する方法、サーバレス アーキテクチャ など、用途や規模に応じて複数のアプローチが存在します。 その中でも、近年特に多く利用されているのが コンテナサービス です。 コンテナは、アプリケーションとその実行に必要なライブラリや設定をまとめて扱えるため、開発環境と本番環境の差異が生まれにくく、デプロイやスケーリングを容易に行えるという特徴があります。また、移行性の高さも大きなメリットかと思います。別環境への移行や、新しい環境の構築時などで効率的に作業を進めることができ、個人的にはこのあたりは非常に有用だと感じています。 AWS ではコンテナを扱うのに特化したさまざまなサービスが用意されており、それぞれの特性を理解して適切に使い分けることが重要です。ここではその代表的なものを取り上げます。 コンテナ制御 AWS でコンテナ オーケストレーション を検討する際、代表的な選択肢として Amazon EKS と Amazon ECS の 2 つのサービスが挙げられます。 Amazon EKS は、 Kubernetes をマネージドサービスとして提供するもので、 Kubernetes のエコシステムや標準的な運用手法をそのまま利用できる点が特徴です。一方、 Amazon ECS は AWS 独自のコンテナ オーケストレーション サービスであり、 Kubernetes を直接意識することなく、 AWS に最適化された形でコンテナを管理・実行することができます。そのため、ECS は比較的シンプルな構成でコンテナを扱えるという利点があります。 また、これらのコンテナサービスでは、コンテナをどこで起動するか という点も重要な要素になります。 AWS では主に、 Amazon EC2 や AWS Fargate といった起動方式が利用されます。 Amazon EC2 を利用する場合は、 仮想マシン 上でコンテナを実行するため インスタンス のスペックや台数を自分で管理する必要がありますが、その分柔軟な構成が可能です。一方、 AWS Fargate はサーバレスな起動方式であり、 インスタンス 管理を意識することなく、必要なリソースを指定するだけでコンテナを実行できます。 以下で、EC2 と AWS Fargate について簡単に整理してみました。 項目 Amazon EC2 AWS Fargate 起動方式 仮想マシン 上でコンテナを実行 サーバレスでコンテナを実行 インスタンス 管理 必要 不要 OS管理 必要 不要 スケーリング 自前設定 自動 課金方式 インスタンス 単位 vCPU / メモリ使用量 自由度 高い ある程度制約あり また、ご存じの方も多いかもしれませんが、 AWS Lambda でもコンテナイメージを利用してアプリケーションを実行することができます。(コンテナLambdaと呼ぶこととします) AWS Lambda は FaaS(Function as a Service)に分類されるサービスで、従来は ZIP ファイル形式でアプリケーションコードをデプロイする方法が一般的でした。この方式は手軽である一方、デプロイ可能なファイルサイズに制限(最大250MB)があり、ライブラリや依存関係が多いアプリケーションでは制約になることがあります。 一方で、Lambda では コンテナイメージを用いたデプロイ にも対応しており、この場合は10 GB までのサイズのイメージをデプロイすることができます。さらにLambda web adapterの登場により、従来は ECS や EC2 上で動かすことが一般的だった Web アプリケーションを、ほぼそのままの構成で Lambda 上に載せることが可能になりました。さらにコンテナサービスの幅が広がりました。 コンテナイメージ管理 上記のように、 AWS にはコンテナを制御・起動するためのさまざまなサービスが存在しますが、それらを支える重要な コンポーネント として Amazon Elastic Container Registry(ECR) が用意されています。 Amazon ECR は、主にDocker コンテナイメージを管理するためのマネージド レジストリ サービスであり、 Amazon ECS、 Amazon EKS、 AWS Lambda(コンテナイメージ) など、 AWS の主要なコンテナ関連サービスと高い互換性を持っています。これにより、同じコンテナイメージを複数のサービス間で再利用しながら、安全かつ一元的に管理することが可能です。 そんな Amazon ECR には、いろいろ面白い機能がありそうですので、また別の記事で詳しく紹介させていただきます。今回は一部分のみ抜粋して紹介します。 リポジトリ の種別 Amazon Elastic Container Registry(ECR)には、プライベー トリポジ トリ と パブリック リポジトリ の 2 種類が用意されています。どちらもコンテナイメージを管理するためのサービスですが、用途や設計思想には明確な違いがあります。 以下自分の理解です。 プライベー トリポジ トリ AWS アカウント内でのみ利用できるコンテナイメージの保管場所で、社内向けアプリケーションや、外部に公開したくない独自のアプリケーションイメージを扱う場合には、プライベー トリポジ トリが適しています。 パブリック リポジトリ インターネット上に公開されたコンテナイメージを配布するための仕組み。 AWS アカウントを持っていないユーザーでもイメージを取得できるため、 OSS の配布やサンプルアプリケーションの公開などに向いています。 パブリック リポジトリ であれば基本的に認証なしでイメージ取得が可能ですが、プライベー トリポジ トリでは認証が必要になります。実際に認証なしでプライベー トリポジ トリへpullを試みると、以下のように拒否されてしまいます。 $ docker pull <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/<対象リポジトリ>:tag Error response from daemon: pull access denied for <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/<対象リポジトリ>, repository does not exist or may require 'docker login': denied: Your authorization token has expired. Reauthenticate and try again. 認証してトライしてみます。 # 認証 aws ecr get-login-password --region ap-northeast-1 \ | docker login \ --username AWS \ --password-stdin <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com $ docker pull <アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/<対象リポジトリ>:tag tag: Pulling from <対象リポジトリ> 11b72ab0c7e9: Pull complete 940530de9b36: Pull complete 711f628928ef: Pull complete 75131756b8ac: Pull complete ... 今度はうまく接続できてそうです。 CI/CDで認証ステージを組み込んであげれば、自動的にECRへログイン→ECRへのイメージプッシュが行えます。 自動継続的スキャン コンテナは軽量で再利用しやすいという利点がある一方で、ベースイメージやライブラリをそのまま使い回すことが多く、知らないうちに 脆弱性 を含んだ状態で運用してしまうケースも少なくありません。そのため、コンテナイメージに含まれる 脆弱性 をどのように管理するかは、運用において避けて通れない課題となっています。 特に、複数のサービスで同じコンテナイメージを利用している場合、1 つの 脆弱性 が複数のシステムに影響を及ぼす可能性があります。こうしたリスクに対応するためには、単発のチェックに留まらず、継続的に 脆弱性 を把握し、適切に対応していく仕組みを整えることが重要です。 そこで AWS ではコンテナイメージの 脆弱性 を管理するための仕組みとして、プライベート レジストリ を対象に 基本スキャン と 拡張スキャン の 2 種類の 脆弱性 スキャン方式が用意されています。 基本スキャンでは、イメージのプッシュ時またはオンデマンドでのスキャンが可能であり、OSに含まれるパッケージの既知の 脆弱性 を検出することができます。こちらは基本的に無料で使用することができ、スキャン結果についてはECRのコンソール上から確認することができます。 スキャンには共通 脆弱性 識別子 (CVE) データベースを使用する2種類の設定があったようですが、Clairと呼ばれる オープンソース の 脆弱性 データベースを用いたスキャンは廃止され、現在は AWS ネイティブスキャンという方式がGAとなっている状態でした。利用するためにはオプトインが必要なようですね。 もう一つのスキャン方法は、拡張スキャンと呼ばれる方式です。こちらは有料になりますが、 Amazon Inspector と統合され、より詳細な 脆弱性 検出が可能になります。また、スキャン対象もOSだけでなく、 プログラミング言語 のパッケージやライブラリにも対応するため、より網羅的に 脆弱性 管理が可能となります。オンデマンドではなく継続的なスキャンがサポートされる点も良いですね。 Inspector統合なこともあり、検出結果は以下画像のようにInspectorのダッシュボードから確認できます。大まかに 脆弱性 が検知されたかも確認できますし、画像にはありませんがスキャン結果詳細を確認することももちろん確認可能です。 拡張スキャンによる料金は、東京リージョンで以下のようになっているようです。 スキャンタイミング コスト プッシュ時 対象イメージにつき $0.11 継続スキャン スキャンごとに $0.01 こうしてみると、そこまで高額なわけでもないので性能を考えると拡張スキャンを採用しておくのが良いのかなと思います。 脆弱性 管理 Amazon Inspector のみを利用した 脆弱性 管理も有効な選択肢ですが、コンテナイメージの数が増えてきたり、複数チーム・複数サービスで同時に運用している場合、「検知したあと、どう管理・運用するか」 という点も重要になってくると思います。 こうした中、 脆弱性 管理ツールの「FutureVuls」を用いることでECR上のイメージの 脆弱性 管理をより効率化することができます。 詳細な設定方法については公式ドキュメントを参照いただければと思いますが、おおまかな流れは以下になります。 FutureVuls で AWS 連携の設定を実施 FutureVulsで 脆弱性 管理対象とする ECR リポジトリ を選択 以降、 AWS 側で実行されるスキャン結果を定期的に FutureVuls に取り込み、 脆弱性 ・タスクとして管理 (公式) help.vuls.biz 手順としては非常に簡単で、個人的にはUIも直感的でわかりやすいなと思っています。 FutureVuls にはECRイメージの 脆弱性 管理を支えるいくつかの機能があり、深ぼっていきたいところですが、ここでは重要な機能と、大変便利な機能を一つずつピックアップし紹介します。 自動 トリアージ FutureVuls の大きな特徴の一つが、SSVC(Stakeholder-Specific Vulnerability Categorization) という フレームワーク に基づいた 脆弱性 管理を実現できる点です。FutureVuls ではこの フレームワーク を用いてリスクを評価し、優先順位付けを行う、自動 トリアージ 機能を搭載しており、「out of cycle(重大な検出)」や「Immediate(緊急性の高い検出)」などと自動分類することで、セキュリティチームが比較的重要な脅威に迅速に対応できるようになります。 さらに 脆弱性 対応のタスク管理もFutureVulsにて実施可能で、対応ステータス等の把握が容易になります。 SSVCについて詳しく知りたい方はFutureVuls様のこちらの記事をご参照ください: www.vuls.biz FutureVuls AI FutureVuls には、 脆弱性 管理をさらに効率化するための機能としてFutureVuls AI が提供されています。 FutureVuls AI を簡単に紹介すると、 検出された 脆弱性 に対して分かりやすいサマリを提示し、対応判断を支援してくれる AI アシスタント です。単に 脆弱性 を検知するだけでなく、「この 脆弱性 は何が問題で、どこに影響があるのか」を把握するまでの時間を大きく短縮してくれます。 脆弱性 対応を行う際には、CVE 情報や各種アドバイザリを確認することが一般的ですが、これらの情報は 英語で記載されており、数ページに及ぶことも少なくありません。そのため、内容を正しく理解するまでに想像以上の時間がかかるケースもあります。 FutureVuls AI は、こうした 大量かつ専門的な情報を要約し、ポイントを整理して提示してくれるため、 脆弱性 の種類 影響範囲の有無 対応の必要性や優先度の判断材料 といった情報を、短時間で把握することが可能になります。 未然防止 AWS Inspector や FutureVuls を活用することで、 脆弱性 を効率的に検知・管理することは可能ですが、理想を言えば 「 脆弱性 が検知されない状態」を目指したい ところです。つまり、問題が発生してから対応するのではなく、そもそも 脆弱性 を含まないイメージを作るための仕組みづくりが重要になります。 その取り組みの一例として、ECR にコンテナイメージを push する前の段階で、依存関係のアップデートを自動で検知・提案してくれるツールを導入する方法があります。具体的には、 GitHub 上で利用できる Renovate や Dependabot などの自動化ツールが代表的です。 これらのツールを導入すると、アプリケーションや Dockerfile で使用しているライブラリ、ベースイメージの新しいバージョンが公開された際に、変更内容を反映した Pull Request を自動で作成してくれます。開発者はその PR をレビューしてマージするだけでよく、手作業でバージョンチェックを行う必要がありません。 さらに、利用するコンテナイメージのベースとして Distroless イメージを採用することで、セキュリティリスクをより一層抑えることができます。Distroless イメージは、OS やシェルといった不要な コンポーネント を含まず、アプリケーションの実行に必要な最小限のライブラリのみで構成されたイメージです。そのため、攻撃対象となり得るパッケージの数を減らすことができ、結果として 脆弱性 が検知される可能性そのものを低減できます。 このように、 依存関係の自動アップデートによる「事前対策」 Distroless イメージの採用による「攻撃対象の最小化」 といった取り組みを組み合わせることで、コンテナイメージに含まれる 脆弱性 を大幅に削減し、より安全で持続可能なコンテナ運用を実現することが可能になります。 まとめ 今回は、コンテナイメージの管理方法として AWS が提供する Amazon Elastic Container Registry(ECR) を中心にご紹介しました。 ECS や EKS、Lambda など複数のコンテナ関連サービスと親和性が高く、イメージを一元的に管理できる点は、ECR の大きな強みと言えます。 また、実運用における 脆弱性 管理の観点では、FutureVuls を活用することで、検知から対応までをより効率的に実施できることをご紹介しました。 単にスキャン結果を確認するだけでなく、優先度に基づいた対応判断や、チケット管理・共有まで含めた運用を行うことで、継続的な 脆弱性 管理体制を構築しやすくなります。 勉強会の中でもコメントをいただきましたが、自動化できる部分は積極的に自動化し、可能な限り手動での設定や対応を減らしていくことは、今後ますます重要になっていくと感じています。今後は外部サービスとのさらなる連携も検討されているとのことで、より実践的な運用が実現できる点に期待が高まります。 参考 https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/image-scanning.html https://aws.amazon.com/jp/inspector/pricing/ https://help.vuls.biz/manual/scan/image/ https://docs.aws.amazon.com/whitepapers/latest/overview-deployment-options/amazon-elastic-kubernetes-service.html https://aws.amazon.com/jp/ecr/ 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @yoneda.kosuke レビュー: @taguchi.kazutoshi ( Shodo で執筆されました )
アバター
はじめに 金融IT本部 2年目の坂江 克斗です。 今回は11月にプレビューが公開された AWS Network Firewall Proxy の検証結果について紹介します。 プロキシの位置づけから丁寧に解説できればと思います。 本記事は 2025年12月時点(プレビュー) の仕様に基づきます。正式リリース時に仕様が変更される場合もあるため、参考情報としてご覧ください。 はじめに プロキシの概要 よくある疑問 ざっくりと説明 プロキシの位置づけ AWS Network Firewall Proxyの概要 アーキテクチャ 動作イメージ AWS Network Firewall Proxyに関わるリソースの詳細 ルールグループ・ルール プロキシ設定・プロキシ AWS Network Firewall Proxy の検証 前提 Terraformの実装 手動設定 ルールグループ・ルールの作成 プロキシ設定の作成 プロキシの作成 VPCエンドポイントのセキュリティグループ修正 EC2へのプロキシ設定追加 動作検証 まとめ おわりに プロキシの概要 よくある疑問 社内で開発をしていると、「モジュールがインストールできない」「このポートで通信したいのに、なぜか拒否される」といった場面に遭遇することがあります。 こうしたときに周囲に相談すると、「それ、プロキシの問題じゃない?」と言われることも少なくありません。 ひとまず「なるほど」と頷きつつ対処をしてみますが、そもそもプロキシとは何をしているものなのでしょうか? ざっくりと説明 自分の中で整理した結果、プロキシとは 「通信の一元的な制御と監査・接続元情報の隠蔽(・キャッシュによる高速化)を目的とした通信中継用サーバ」 と考えています。 よく使用されるプロキシ(Webプロキシ)を基に作成したイメージは以下となります。 指定の プロトコル (HTTP/ HTTPS )の通信時、 クライアントは名前解決をせずプロキシにリクエストを送信 します。その後、プロキシによって DNS の名前解決を行い、プロキシサーバとクライアント・プロキシサーバと接続先のサーバでそれぞれ 個別に通信が確立 し、中継が行われる仕組みとなっています。 上記の説明だけではわかりづらい部分もあるため、混同しやすい他の用語と比較しながらその特徴をつかんでいきます。 プロキシの位置づけ 個人的に混同しやすいと感じたのは、NATやNAPT機器、パケットインスペクションになります。 例えば、 AWS においてNAT Gateway を使用した場合は、アウトバウンド通信をする際にパケットのIPやポートを書き換えることで、内部のネットワーク空間つまり接続元のIP情報を隠蔽する役割があるといえますし、Network Firewall を使用すればルートテーブルの設定をすることで通信を統一的に制御・監査することが出来ます。 では、プロキシとこれらのサービスの違いはどこにあるのでしょうか。 結論としては、目的とそれに伴い対応するレイヤー、中継の性質が異なります。 以下の表に各サービスの情報を記載いたします。 用語 レイヤー 目的 中継レベル 対応する AWS サービス NAT, NAPT L3-4(IP / Port) アウトバウンド通信の成立、それに伴うプライベートIPの隠蔽(NAPTの場合、 グローバルIP の使用効率向上) アウトバウンド用。パケットのIPやポート情報を書き換えるのみで、クライアントと接続先サーバは 任意の4層以上の プロトコル で直接セッションを確立 しやり取り。 NAT ゲートウェイ パケットインスペクション L3-7(IP / Port, UDP / TCP 対応) 通信内容に基づくセキュリティ制御・検知 インバウンド/アウトバウンド用。通信内容を書き換えず、 通過する全パケットを透過的に検査・制御。 AWS Network Firewall , AWS Gateway Load Balancer + サードパーティ ソフト リバースプロキシ DNS + L3-7(IP / Port, HTTP / TLS 対応) 外部公開サービスの集約・防御・負荷分散 インバウンド用。パケットの書き換えではなく、プロキシが通信主体となり、クライアントとプロキシ・プロキシと接続先サーバとの間で 個別の通信(指定の4層以上の プロトコル )を確立。 Amazon CloudFront , Elastic Load Balancing(ELB), Amazon API Gateway フォワ ードプロキシ DNS + L3-7(IP / Port, HTTP / TLS 対応) アウトバウンド通信の一元的な制御・監査 アウトバウンド用。パケットの書き換えではなく、プロキシが通信主体となり、クライアントとプロキシ・プロキシと接続先サーバとの間で 個別の通信(指定の4層以上の プロトコル )を確立。 ※ ただし、クライアントの設定によりプロキシを回避可能。 AWS Network Firewall Proxy ※ 表を簡易にするため、SOCKS などの L4 プロキシ(主に TCP コネクションの中継のみで、アプリケーションレイヤの制御をしないもの)は本表では対象外としました。 表に記載しましたが、プロキシはインバウンド/アウトバウンドの制御によってそれぞれリバースプロキシ/ フォワ ードプロキシとも呼ばれます。 このように、NAT・パケットインスペクション・プロキシは、それぞれ得意とするレイヤーや適用範囲が異なります。 また中継の性質として、 L4以上のセッションを個別に確立する点がプロキシの大きな特徴である と整理できます。 AWS re:Invent 2025 - AWS Network Firewall Proxy (NET216) ではクライアントと接続先サーバそれぞれ個別に通信を確立するプロキシを明示的なプロキシ(Explicit Proxy)と表現し、パケットインスペクションのような透過的な制御ツールを透過的なプロキシ(Transparent Proxy)と表現していました。 一方で、業務上「プロキシ」と呼ばれるものは、通信の中継主体となる 明示的なプロキシ( フォワ ードプロキシ/リバースプロキシ)を指すケースがほとんどである印象を受けます。 さらに近年では、Network Firewall 自体にも、 TLSインスペクション という、プロキシのように TLS 通信を一度終端して内容を検査する機能が追加されており、用語と実装の対応関係が直感的に分かりづらくなっていると感じます。 AWS Network Firewall Proxyの概要 AWS Network Firewall Proxy はいわゆるWebプロキシと同様に、 HTTP / HTTPS 通信専用のマネージドな フォワ ードプロキシサービス であり、Route 53 Resolver DNS Firewall や Network Firewall と同様にアウトバウンドセキュリティを構成するための AWS サービスの一つです。 現在プレビュー提供中であり、料金は無料となっていますが、利用可能なリージョンは オハイオ リージョン(us-east-2)のみに限定されています。 アーキテクチャ 以下の構成( Securing Egress Architectures with Network Firewall Proxy より引用)に示すように NAT ゲートウェイ と統合されたプロキシが作成され、クライアントは専用の インターフェース VPC エンドポイント( AWS PrivateLink)を経由してプロキシへアクセス する構成となっています。 プロキシの作成時、プロキシを紐づけたNAT ゲートウェイ と同じサブネットに、 VPC エンドポイントが自動的に1つ作成されます。 任意で他のサブネットや他の VPC に VPC エンドポイントを作成可能なため、マルチ VPC 構成においても以下の図( Securing Egress Architectures with Network Firewall Proxy より引用)に示すように VPC エンドポイント経由での使用が可能となります。 (もちろん、PrivateLinkを用いたアクセスではなく、Cloud WANやTransit Gateway を使用する構成も可能です。) 一方、 AWS Network Firewall Proxy が登場する以前に想定されていたユーザマネージドなプロキシ構成では、小規模なサービスであれば単体の EC2 で実装したプロキシでも対応可能ですが、可用性や性能を考慮する場合には Auto Scaling 付きの EC2 を配置し、NLB によって負荷分散を行う必要があり、運用負荷の高い構成となりがちでした。 今回マネージドなプロキシが提供されたことで、少なくともプロキシ基盤そのものに関するインフラ管理の負荷は大きく軽減されたと感じます。 動作イメージ 本サービスでは、以下の図( Securing Egress Architectures with Network Firewall Proxy より引用)に示すように 名前解決前(Pre- DNS )、名前解決後の接続先サーバへのHTTPリクエスト(Pre-request)、HTTPレスポンス(Post-response)の3段階 で評価を行います。 また、名前解決に使用する ドメイン 名からL3-L7(IP / Port, HTTP / TLS ヘッダ) までの情報を評価に使用可能です。 ただし、上記の評価フローは TLS インターセプト ( Squid における SSL Bump)と呼ばれる方式を有効化した場合の挙動を示しています。 TLS インターセプト では、クライアントと接続先サーバ間で直接 TLS セッションを確立するのではなく、 クライアントとプロキシ、プロキシと接続先サーバの間でそれぞれ独立した TLS セッションを確立 します。 TLS インターセプト の実体はシンプルです。外部公開するアプリケーションが、信頼された CA により発行された証明書を用いて TLS 通信を行うのと同様に、Network Firewall Proxy はプライベート CA を使用し、初めて通信が発生した宛先 ドメイン に対して証明書を動的に発行します。 クライアント側は上記のプライベート CA を信頼している(CAの公開鍵を持つ)ことで、クライアントとプロキシ間の TLS 通信が成立します。 詳細な設定手順については後述します。 一方で、以下の図( Securing Egress Architectures with Network Firewall Proxy より引用)に示すように、 TLS インターセプト を無効化した場合には、Network Firewall Proxy を経由して TCP セッションは個別に確立 されるものの、 TLS セッションおよび HTTP セッションは クライアントと接続先サーバ間でエンドツーエンドに確立 されます。 そのため、 TLS インターセプト を無効化した場合、 TLS 通信の完全性と機密性をエンドツーエンドに維持することができますが、評価に利用できる情報は ドメイン 名、IP、ポート、SNI ヘッダなどの暗号化されていない情報 に限定されます。 AWS Network Firewall Proxyに関わるリソースの詳細 Network Firewall Proxyで使用するリソースの構造は以下の図に示すようになっています。 各リソースの詳細に関して説明します。 ルールグループ・ルール ルールには、それぞれ 評価フェーズ(Pre- DNS / Pre-request / Post-response)・評価条件・Allow/Deny/Alertアクション を設定します。(ルールにマッチしなかった通信は、後述するプロキシ設定のデフォルトアクションに基づき評価されます。) 一方で、ルールグループはルールを複数(最大1000個)設定できる単なる箱としてのリソースとなります。 ルールの評価条件として使用できる条件キーを以下に示します。 下線付きの条件キー は、 TLS インターセプト が無効化の状態でも使用可能な値となります(HTTPヘッダの値は TLS により暗号化されるため)。 リクエスト request:SourceAccount :送信元の AWS アカウントID request:SourceVpc :送信元のVPCID request:SourceVpce :送信元の VPC エンドポイントID request:Time request:SourceIp request:DestinationIp request:SourcePort request:DestinationPort request:Protocol request:DestinationDomain : DNS クエリ or SNI request:Http: Uri request:Http:Method request:Http:UserAgent request:Http:ContentType request:Http:Header/{CustomHeaderName} レスポンス response:Http:StatusCode response:Http:ContentType response:Http:Header/{CustomHeaderName} プロキシ設定・プロキシ プロキシ設定はそれ自体が AWS リソースとして作成されるものであり、これをプロキシの作成時に紐付けます。 プロキシ設定の作成時には、 複数のルールグループを紐付けつつ、デフォルトアクション(ルールにマッチしなかった通信へのアクション)をAllow/Deny/Alertで設定 します。 プロキシの作成時には、 プロキシ設定やNAT ゲートウェイ 、評価フェーズ毎のログ出力先、 TLS インターセプト を使用する場合はプライベートCA を設定します。 実際に通信を評価するプロセスを、プロキシ設定の例を用いて以下に示します。 ルールグループやルールの優先順と評価フェーズの順序が存在する点に注意が必要です。 AWS Network Firewall Proxy の検証 前提 検証のためローカルで実装します。ただし、Network Firewall Proxy用のTerraform Providerが未実装のため、一部手動で設定します。 概要で紹介した以下の構成( Securing Egress Architectures with Network Firewall Proxy より引用)を作成します。 EC2をプライベートサブネットに配置し、NAT ゲートウェイ +Network Firewall Proxy経由での AWS サービス・外部サービスへのアクセスを想定。 Network Firewall Proxyの設定 Pre- DNS 、Pre-request、Post-Responseの3段階それぞれの評価を設定し、動作を確認します。(デフォルトアクションは全て Allow) Pre- DNS : ドメイン tech.dentsusoken.com をDeny。 Pre-Request: URI */files/* をDeny(StringLike)。 Post-Response:レスポンスステータス 404 をDeny。 TLS インターセプト を有効化します。 本検証では、公開されている Web サイトに対して、 通信制 御の挙動を確認する目的で少数のリクエストを送信しており、サービスの可用性や機密性に影響を与える行為は行っていません。 本記事では、検証のシンプルさや負荷の低さ(数リクエスト程度)からテックブログ用 ドメイン による検証を行いました。 しかし、本来はテスト手法に問題があり予期せず過大な負荷をかけてしまった場合など、 規約違反 となる可能性があるため、 IANA(現在はICANNの一部)が管理するテスト用ドメイン ( example.com 等)を使用し検証を行うことが最も適切となります。 Terraformの実装 VPC 、NAT ゲートウェイ 、検証用のEC2を以下のように定義します。 ただし、 TLS インターセプト の有効化に伴いクライアントEC2側には、プロキシの動的な証明書作成に使用するプライベートCAの親、つまり ルートCAへの信頼( トラストアンカー ) を設定する必要があります。 そのため、後述するプライベートCAの作成時に生成される ルート証明書 を、user_dataを使用して インスタンス 起動時に自動的にトラストストアに設定されるようにShellコマンドを記載しています。 これまで皆さんが google.com や yahoo.co.jp などにアクセスする際、サイトの証明書が信頼できるCAにより発行されたものとして認識され、意識せずとも TLS 接続が成立していたのは、 OS(PC)にあらかじめ設定されたトラストストア内に、主要な公開CAの証明書が含まれていたためです。 今回も同様に、プロキシが利用するプライベートCAの証明書をトラストストアへ登録することで、クライアントはプロキシが生成した証明書を信頼できるようになります。 terraform { required_version = "~> 1.14.0" required_providers { aws = { version = "6.23.0" source = "hashicorp/aws" } } } provider "aws" { region = local.regions.primary } locals { account_id = "677276073034" regions = { primary = "us-east-2" } availability_zones = { primary = [ "$ { local.regions.primary } a" , "$ { local.regions.primary } b" , "$ { local.regions.primary } c" ] } } data "aws_caller_identity" "current" {} ########################################################################################### # VPC ########################################################################################### ## VPC resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true enable_dns_support = true tags = { Project = "example" } } resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.main.id tags = { Project = "example" } } ## Subnet resource "aws_subnet" "public_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.1.0/24" availability_zone = local.availability_zones.primary [ 0 ] map_public_ip_on_launch = true tags = { Project = "example" } } resource "aws_subnet" "private_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.2.0/24" availability_zone = local.availability_zones.primary [ 1 ] map_public_ip_on_launch = false tags = { Project = "example" } } ## Root Table resource "aws_route_table" "public_a" { vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } tags = { Project = "example" } } resource "aws_route_table" "private_a" { vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.ngw.id } tags = { Project = "example" } } resource "aws_route_table_association" "public_a" { subnet_id = aws_subnet.public_a.id route_table_id = aws_route_table.public_a.id } resource "aws_route_table_association" "private_a" { subnet_id = aws_subnet.private_a.id route_table_id = aws_route_table.private_a.id } ## NAT resource "aws_eip" "ngw" { } resource "aws_nat_gateway" "ngw" { depends_on = [ aws_internet_gateway.igw ] allocation_id = aws_eip.ngw.id subnet_id = aws_subnet.public_a.id tags = { Project = "example" } } ########################################################################################### # EC2 ########################################################################################### ## Instance data "aws_ami" "amazon_linux_2023" { most_recent = true owners = [ "amazon" ] filter { name = "name" values = [ "al2023-ami-*-x86_64" ] } } resource "aws_instance" "amazon_linux" { ami = data.aws_ami.amazon_linux_2023.id instance_type = "t2.micro" subnet_id = aws_subnet.private_a.id iam_instance_profile = aws_iam_instance_profile.ec2.name vpc_security_group_ids = [ aws_security_group.ec2.id, ] user_data = <<-EOF #!/bin/bash cat > /etc/pki/ca-trust/source/anchors/nfw-proxy-root-ca.pem <<PEM-EOF $ { aws_acmpca_certificate.root.certificate } PEM -EOF update-ca-trust EOF tags = { Project = "example" } } ## Security Group resource "aws_security_group" "ec2" { name = "ec2-sg" vpc_id = aws_vpc.main.id egress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0" ] } egress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0" ] } } ## IAM resource "aws_iam_instance_profile" "ec2" { name = "ec2-profile" role = aws_iam_role.ec2.name } resource "aws_iam_role" "ec2" { name = "ec2-role" assume_role_policy = jsonencode ( { Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } Action = "sts:AssumeRole" }] } ) } resource "aws_iam_role_policy_attachment" "ssm" { role = aws_iam_role.ec2.name policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" } TLS インターセプト 設定用のプライベートCAおよびログ出力先として使用するCloudWatchロググループを定義します。 ただし、プライベートCAに関しては以下の方針で作成します。 ベストプラクティス に基づきルートCAと中間CAを作成し、プロキシ作成時には中間CAを指定する。 ただし、プロキシは中間CAから直接証明書を発行するのではなく、 プロキシ内部で中間CAから派生させた「Proxy専用の下位CA(PathLen0)」を自動生成し、以降の動的 サーバ証明書 はこのProxy専用CAから発行 する仕様と考えられる。 そのため、中間CAには下位CAを1段作成できる設定( SubordinateCACertificate_PathLen1/V1 )を適用する。 また、プロキシが中間CAを利用してProxy専用下位CA(派生不可)を作成できるよう、 AWS RAM による共有( AWSRAMSubordinateCACertificatePathLen0IssuanceCertificateAuthority )を設定する。 使用する鍵 アルゴリズム や署名 アルゴリズム については、 AWS コンソールから作成した場合のデフォルト値(KeyAlgorithm: RSA _2048、SigningAlgorithm:SHA256WITHRSA)を使用。 リソースポリシーを使用し、プライベートCAへのアクセスは指定のプロキシ(サービス プリンシパル : proxy.network-firewall.amazonaws.com 、プロキシ名: example-proxy )のみに限定( 公式ドキュメント )。 また、プライベートCA用の各Terraformリソースの概要を以下に示します。 aws _acmpca_certificate_authority プライベート CA(ROOT / SUBORDINATE)の本体。 ルートCAの情報をクライアントEC2のトラストストアに登録(トラストアンカー)。 aws _acmpca_certificate CA 証明書(ROOTの場合は自己署名、SUBORDINATEの場合は親CAが署名して発行)。 aws _acmpca_certificate_authority_certificate 発行されたCA証明書をCAにインポートして有効化。 aws _acmpca_permission ACM 専用のプライベートCAに対する権限設定。 ACM による証明書の自動更新向け。 aws _acmpca_policy IAMリソースポリシー。 ## Private Certificate Authority (PCA) - Certificate Authority ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acmpca_certificate_authority resource "aws_acmpca_certificate_authority" "root" { type = "ROOT" usage_mode = "GENERAL_PURPOSE" certificate_authority_configuration { key_algorithm = "RSA_2048" signing_algorithm = "SHA256WITHRSA" subject { common_name = "nfw-proxy-root-ca.internal.root" country = "JP" organization = "Example Org" } } key_storage_security_standard = "FIPS_140_2_LEVEL_3_OR_HIGHER" permanent_deletion_time_in_days = 30 # 削除後復旧可能な日数:7-30日 } resource "aws_acmpca_certificate_authority" "example" { type = "SUBORDINATE" usage_mode = "GENERAL_PURPOSE" certificate_authority_configuration { key_algorithm = "RSA_2048" signing_algorithm = "SHA256WITHRSA" subject { common_name = "nfw-proxy-root-ca.internal.middle" country = "JP" organization = "Example Org" } } key_storage_security_standard = "FIPS_140_2_LEVEL_3_OR_HIGHER" permanent_deletion_time_in_days = 30 # 削除後復旧可能な日数:7-30日 } ## PCA Certificate ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acmpca_certificate resource "aws_acmpca_certificate" "root" { certificate_authority_arn = aws_acmpca_certificate_authority.root.arn certificate_signing_request = aws_acmpca_certificate_authority.root.certificate_signing_request signing_algorithm = "SHA256WITHRSA" # https://docs.aws.amazon.com/ja_jp/privateca/latest/userguide/template-definitions.html#RootCACertificate-V1 template_arn = "arn:aws:acm-pca:::template/RootCACertificate/V1" validity { type = "YEARS" value = 2 # ValidationException: The certificate validity specified exceeds the certificate authority validity.に注意 } } resource "aws_acmpca_certificate" "example" { depends_on = [ aws_acmpca_certificate_authority_certificate.root ] certificate_authority_arn = aws_acmpca_certificate_authority.root.arn certificate_signing_request = aws_acmpca_certificate_authority.example.certificate_signing_request signing_algorithm = "SHA256WITHRSA" # https://docs.aws.amazon.com/ja_jp/privateca/latest/userguide/template-definitions.html#SubordinateCACertificate_PathLen1-V1 template_arn = "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen1/V1" validity { type = "YEARS" value = 1 } } ## PCA Certificate Authority Certificate ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acmpca_certificate_authority_certificate resource "aws_acmpca_certificate_authority_certificate" "root" { certificate_authority_arn = aws_acmpca_certificate_authority.root.arn certificate = aws_acmpca_certificate.root.certificate certificate_chain = aws_acmpca_certificate.root.certificate_chain } resource "aws_acmpca_certificate_authority_certificate" "example" { certificate_authority_arn = aws_acmpca_certificate_authority.example.arn certificate = aws_acmpca_certificate.example.certificate certificate_chain = aws_acmpca_certificate.example.certificate_chain } ## PCA Permission ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acmpca_permission resource "aws_acmpca_permission" "root" { certificate_authority_arn = aws_acmpca_certificate_authority.root.arn actions = [ "IssueCertificate" , "GetCertificate" , "ListPermissions" ] principal = "acm.amazonaws.com" } resource "aws_acmpca_permission" "example" { certificate_authority_arn = aws_acmpca_certificate_authority.example.arn actions = [ "IssueCertificate" , "GetCertificate" , "ListPermissions" ] principal = "acm.amazonaws.com" } ## PCA Resource Policy ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acmpca_policy resource "aws_acmpca_policy" "example" { resource_arn = aws_acmpca_certificate_authority.example.arn policy = jsonencode ( { "Version" : "2012-10-17" , "Statement" : [ { "Effect" : "Allow" , "Principal" : { "Service" : "proxy.network-firewall.amazonaws.com" } , "Resource" : "$ { aws_acmpca_certificate_authority.example.arn } " , "Action" : [ "acm-pca:GetCertificate" , "acm-pca:DescribeCertificateAuthority" , "acm-pca:GetCertificateAuthorityCertificate" , "acm-pca:ListTags" , "acm-pca:ListPermissions" ] , "Condition" : { "ArnEquals" : { "aws:SourceArn" : "arn:aws:network-firewall:$ { local.regions.primary } :$ { local.account_id } :proxy/example-proxy" } } } , { "Effect" : "Allow" , "Principal" : { "Service" : "proxy.network-firewall.amazonaws.com" } , "Action" : [ "acm-pca:IssueCertificate" ] , "Resource" : "$ { aws_acmpca_certificate_authority.example.arn } " , "Condition" : { "StringEquals" : { "acm-pca:TemplateArn" : "arn:aws:acm-pca:::template/SubordinateCACertificate_PathLen0/V1" } , "ArnEquals" : { "aws:SourceArn" : "arn:aws:network-firewall:$ { local.regions.primary } :$ { local.account_id } :proxy/example-proxy" } } } ] } ) } ## Log resource "aws_cloudwatch_log_group" "example" { name = "/aws/vpc/network-firewall/proxy/example" tags = { Project = "example" } } 手動設定 上記のTerraformリソースをapplyした後に、 AWS コンソールからNetwork Firewall Proxyの設定を行います。 ルールグループ・ルールの作成 以下のルールを含むルールグループを作成します。 Pre- DNS : ドメイン tech.dentsusoken.com をDeny。 Pre-Request: URI */files/* をDeny(StringLike)。 Post-Response:レスポンスステータス 404 をDeny。 VPC コンソールのプロキシルールグループ画面より、「プロキシルールグループを作成」ボタンからルールグループを作成します。 ルールグループの作成時は単一のルールのみ作成可能なため、まずはPre- DNS のルールを作成します。 フェーズとアクションを選択し、「条件を追加」ボタンから条件キーと値を設定します 「作成」ボタンを押して作成を完了します。 「条件を追加」ボタンから、Pre-RequestとPost-Responseの条件を同様に追加します。 結果、以下のルールが設定できれば完了です。 プロキシ設定の作成 VPC コンソールのプロキシ設定画面より、「プロキシ設定を作成」ボタンからプロキシ設定を作成します。 デフォルトアクションとして、Pre- DNS / Pre-request / Post-Response全てにAllowを設定します。 「ルールグループをアタッチ」ボタンから、先ほど作成したルールグループを選択します。 「作成」ボタンを押して作成を完了します。 プロキシの作成 VPC コンソールのプロキシ画面より、「プロキシを作成」ボタンからプロキシを作成します。 以下のパラメータを設定し、「作成」ボタンを押して作成を完了します。 名前: example-proxy (Terraform実装にてプライベートCAのリソースポリシーに指定したプロキシ名) プロキシ設定:本章で作成したプロキシ設定 NAT ゲートウェイ :TerraformによりデプロイしたNAT ゲートウェイ プライベート証明書:Terraformによりデプロイした 中間CA ポート番号(クライアントがプロキシにアクセスする際の、プロキシ側のリスニングするポート番号) HTTPS :443 HTTP:1080 ログ配信 Pre- DNS / Pre-Request / Post-Request :TerraformによりデプロイしたCloudWatchロググループ 作成後のアタッチ処理は 10 分程度で完了します。 ここで設定の不備によりアタッチが失敗した場合、「失敗メッセージ」項目にエラー内容が表示されます。 Internal Error 例)中間CAをこれ以上派生不可な設定(PathLen0)で作成していた場合に、プロキシが内部でProxy専用下位CAを生成できず発生。 Access Denied 例)プライベートCAのリソースベースポリシーが、作成したプロキシからのアクセスを許可していない場合に発生。 VPC エンドポイントのセキュリティグループ修正 先ほどのプロキシ作成に伴い、自動で VPC エンドポイントが作成されています。 しかし、以下に示すように VPC エンドポイントに付与されたセキュリティグループはデフォルト設定であり、クライアントEC2からのアクセスに非対応となっています。 そのため、インバウンドルールにEC2セキュリティグループからのポート443への通信の許可を追加します。 EC2へのプロキシ設定追加 EC2にSSM接続を行い、プロキシを経由するように設定します。 念のため、Terraformで定義したとおり、クライアントEC2に正しくルートCAに対するトラストアンカーが設定されていることを、 trust list コマンド で確認します。 sh-5.2$ trust list | grep nfw-proxy-root-ca -C 2 pkcs11:id=%BE%58%DE%C4%B8%D2%49%D3%05%8F%DF%C6%2F%0E%1C%D7%7B%4A%BF%05;type=cert type: certificate label: nfw-proxy-root-ca.internal.root trust: anchor category: authority sh-5.2$ 次に、作成したプロキシのプライベート DNS 名・ポート番号を使用して、 http_proxy ・ https_proxy 環境変数 を設定します。(プロキシとEC2間の通信を保護するために、HTTPプロキシに対しても HTTPS 用のエンドポイントを指定) これらの 環境変数 を設定することで、 クライアントからのHTTPおよび HTTPS 通信のみ が、指定したプロキシエンドポイント経由で送信されるようになります。 sh-5.2$ export https_proxy="https://0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443" sh-5.2$ export http_proxy="https://0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443" sh-5.2$ 動作検証 リソースが全てデプロイできたため、検証に入ります。 アクセスを制限していない https://www.dentsusoken.com へのアクセスは、以下のように許可されました。 sh-5.2$ curl https://www.dentsusoken.com <!DOCTYPE html> (中略) <meta property="og:site_name" content="電通総研" /> <meta property="og:type" content="article" /> <meta property="og:url" content="https://www.dentsusoken.com/top" /> (中略) </html> Pre- DNS のDenyルールにマッチする通信 https://tech.dentsusoken.com の場合、以下のように403レスポンスが返ってきました。 sh-5.2$ curl -v https://tech.dentsusoken.com * Uses proxy env variable https_proxy == 'https://0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443' * Host 0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443 was resolved. * IPv6: (none) * IPv4: 10.0.1.29 * Trying 10.0.1.29:443... * ALPN: curl offers http/1.1 * TLSv1.3 (OUT), TLS handshake, Client hello (1): (中略) < * CONNECT tunnel failed, response 403 * closing connection #0 curl: (56) CONNECT tunnel failed, response 403 CloudWatch ログでは、拒否(Deny)された通信について、 final_rule_name および final_rule_group_name により、マッチしたルールが特定できるログが出力されていることを確認できました。 { " event_timestamp ": 1767152710 , " proxy_name ": " example-proxy ", " client_src_ip ": " 10.0.2.23 ", " final_action ": " deny ", " src_vpc ": " vpc-0ee3a513a9f07e326 ", " dest_domain ": " tech.dentsusoken.com. ", " http_method ": "", " dest_ip ": " <nil> ", " http_status_code ": -1 , " final_rule_name ": " test-dns ", " final_rule_group_name ": " example-rule-group " } Pre-Request の Deny ルールにマッチする通信 https://www.dentsusoken.com/files/ の場合、以下のように403レスポンスが返ってきました。 sh-5.2$ curl -v https://www.dentsusoken.com/files/ * Uses proxy env variable https_proxy == 'https://0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443' * Host 0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443 was resolved. * IPv6: (none) * IPv4: 10.0.1.29 * Trying 10.0.1.29:443... * ALPN: curl offers http/1.1 * TLSv1.3 (OUT), TLS handshake, Client hello (1): (中略) > * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * Request completely sent off < HTTP/1.1 403 Forbidden < Connection: close < * TLSv1.3 (IN), TLS alert, close notify (256): * shutting down connection #0 * TLSv1.3 (OUT), TLS alert, close notify (256): * TLSv1.3 (IN), TLS alert, close notify (256): * TLSv1.3 (OUT), TLS alert, close notify (256): 想定通りPre-Request用のルールにマッチしていることが確認できました。 { " event_timestamp ": 1767152715 , " proxy_name ": " example-proxy ", " client_src_ip ": " 10.0.2.23 ", " final_action ": " deny ", " src_vpc ": " vpc-0ee3a513a9f07e326 ", " dest_domain ": " www.dentsusoken.com. ", " http_method ": " GET ", " dest_ip ": " 3.160.22.80 ", " http_status_code ": -1 , " final_rule_name ": " test-uri ", " final_rule_group_name ": " example-rule-group " } Post-Response の Deny ルールにマッチする通信 https://httpbin.org/status/404 ( httpbin )の場合、以下のように403レスポンスが返ってきました。 sh-5.2$ curl -v https://httpbin.org/status/404 * Uses proxy env variable https_proxy == 'https://0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443' * Host 0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443 was resolved. * IPv6: (none) * IPv4: 10.0.1.29 * Trying 10.0.1.29:443... * ALPN: curl offers http/1.1 * TLSv1.3 (OUT), TLS handshake, Client hello (1): (中略) * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * Request completely sent off < HTTP/1.1 403 Forbidden < Connection: close < * TLSv1.3 (IN), TLS alert, close notify (256): * shutting down connection #0 * TLSv1.3 (OUT), TLS alert, close notify (256): * TLSv1.3 (IN), TLS alert, close notify (256): * TLSv1.3 (OUT), TLS alert, close notify (256): sh-5.2$ 想定通りPost-Response用のルールにマッチしていることが確認できました。 { " event_timestamp ": 1767152721 , " proxy_name ": " example-proxy ", " client_src_ip ": " 10.0.2.23 ", " final_action ": " deny ", " src_vpc ": " vpc-0ee3a513a9f07e326 ", " dest_domain ": " httpbin.org. ", " http_method ": " GET ", " dest_ip ": " 23.21.107.74 ", " http_status_code ": 404 , " final_rule_name ": " test-status ", " final_rule_group_name ": " example-rule-group " } ただし、本検証のように Network Firewall Proxy を単体で利用している場合、 --noproxy オプション等によりクライアント側でプロキシ設定を無効化すると、プロキシ経由の制御を回避して通信が成立し得ます。 そのため、 Network Firewall Proxy 単体での運用というよりも、 ファイアウォール 系サービスやNetwork Firewall を組み合わせることによって、監査面とセキュリティを両立できると感じました。 まとめ Network Firewall Proxy は、Network Firewall や Route 53 Resolver DNS Firewall と並ぶ AWS におけるアウトバウンドセキュリティの選択肢の一つであり、 HTTP/ HTTPS のアウトバウンド制御を、プロキシ型でマネージドに実現 するサービスです。 実際に検証してみると、 アーキテクチャ がシンプルかつマネージドであるため構築・運用の負荷は低い一方、未発表である正式リリース時のコストや、 TLS インターセプト を利用する場合の証明書管理など、運用面で考慮すべき点もあることが分かりました。 他のアウトバウンドセキュリティサービスとの要件に応じた使い分けとしては、 ドメイン ベースの制御には Route 53 Resolver DNS Firewall 、 HTTP/ HTTPS に限定した 通信制 御には Network Firewall Proxy 、 UDP / TCP を含めたより広範な通信評価が必要な場合には Network Firewall を主軸にして組み合わせていく、といった使い分けが考えられます。 ただし、前章で触れた通り、プロキシはクライアント側の設定によって回避可能であるため、 Network Firewall Proxy を単体で利用するのではなく、Network Firewall 等と組み合わせてネットワークレベルで制御することで、監査面とセキュリティを両立する設計が重要 と感じます。 おわりに 本記事では、プロキシの基本的な役割を整理したうえで、 AWS Network Firewall Proxy の概要と挙動について実際に検証を行いました。 正式リリースや今後の更新によって、マネージドの ドメイン リストやIPリストも増えていくととても嬉しいですね。 また、アウトバウンドセキュリティに関して理解できた部分も増えてきましたが、理論として説明できていても感覚的に落とし込めていない部分はまだ多く残っているため、実際の業務経験を通じて検証と運用を重ねながら理解を深めていきたいと思います。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @sakae.katsuto レビュー: @akutsu.masahiro ( Shodo で執筆されました )
アバター
はいどーもー! コーポレート本部の宮澤響です! (部署が変わりました!) 本記事では、昨年に引き続き、大学入学共通テストの「情報Ⅰ」の試験問題を解いてみた個人的な感想をお伝えします! なお、昨年の記事は こちら ! 問題・正解 全体の感想 問題ごとの感想 大問1 問1 問2 問3 問4 大問2 A B 大問3 大問4 おわりに 問題・正解 本記事の対象は、令和8年度の本試験問題となります。 問題および正解は、以下に公開されています。 問題PDF (遷移先: 毎日新聞 デジタルWebサイト) 正解PDF (遷移先: 独立行政法人 大学入試センター Webサイト) 全体の感想 情報を読み取る能力や活用する能力が問われている印象なのは昨年と同様ですが、昨年よりも時間設定がかなりシビアになったと感じました。 私の実力不足や、冊子かディスプレイかの環境差異によるところもあるかもしれませんが、60分間ではかなりギリギリで、見直しの時間が全くとれないレベル(解き終わった時点で既に59分経過程度)でした。 そのため、個々の問題を解くために必要な知識レベルはそれほど上昇していないにもかかわらず、時間内に全ての問題を解くために求められる読解・思考スピードが大きく上昇したことで、試験としては難化したのではないかと感じました。 問題ごとの感想 大問1 問1 ITパスポート試験 や 基本情報技術者試験 で出題されていそうな雰囲気がありますよね。 問2 私は8×8マスの図案を全て紙に描き殴りましたが、もっとスマートな解き方もありそうです。 問3 生年の初期値が2000になっているサービス、増えてきましたよね。 私の年齢では、数年前までは、生年を選択する際には下に(年++方向に)スクロールすることが当たり前だったんですが、最近は上に(年--方向に)スクロールすることが増えてきて、私も年を取ったんだなぁと感じたりします。 問4 こちらも ITパスポート試験 や 基本情報技術者試験 で出題されていそうな雰囲気ですが、aについてはこれまであまり考えたことがなかったため、解いていて自分でも「なるほどな」と思いました。 大問2 A ほのかに情報処理安全確保支援士試験の風味を感じました。 ちょうど先月、コンビニで マイナン バーカードを利用して住民票の写しを取得したばかりだったので、個人的にはタ イムリ ーな問題設定でした。 B キャラクター、ちょっと可愛い。笑 2箇所ある演算選択の解答がどちらも単なるAND演算だと、少し不安になりますよね。 大問3 昨年の大問3でも別の部が文化祭に向けて頑張っていましたよね。(文化祭はいいぞおじさん「文化祭はいいぞ」) 【エ】〜【オ】で開始時刻、終了時刻、待ち時間のそれぞれの求め方を改めて日本語で整理しているところが、とても丁寧な誘導だなと思いました。 ここに立ち返れば【カ】〜【コ】も解けるようになっていますし。 大問4 問3-aの 任意の二つの観測点を比較して、緯度が高い観測点の方が、400度開花差の値か600度開花差の値の少なくとも一方が必ず大きい。 という選択肢は、サッと目で追うだけでは理解できず、心の中で「なんて???」とコメントしながら3回くらい読み直しました。 これがあの「全部聞き取れたのに!」のときの気持ちなんですかね。笑 おわりに 本記事では、令和8年度大学入学共通テストの「情報Ⅰ」の本試験問題を解いてみた感想をお伝えしました。 IT業界のみなさまはもちろんですが、それ以外の業界のみなさまや学生のみなさまも、お時間のあるときに解いてみてはいかがでしょうか。 最後までお読みいただき、本当にありがとうございました! 私たちは共に働いていただける仲間を募集しています! みなさまのご応募、お待ちしています! 株式会社電通総研 新卒採用サイト 株式会社電通総研 キャリア採用サイト 執筆: @miyazawa.hibiki レビュー: @miyazaki.hirotoshi ( Shodo で執筆されました )
アバター
電通 総研 クロス イノベーション 本部の山下です。2025年11月-12月にかけて開催されたKiroの Hackathon イベントであるKiroweenに参加しましたので、そのレポートをお送りします。 このイベントはKiroを使ってアプリケーションを開発することを目的とした ハッカソン イベントです。 作るもののテーマがハロウィンをモチーフにしたイベントになっています。 参加要件など 以下のような参加要件になっていました。 実際の詳細は 公式サイト をご覧ください。 基本的にKiroを使ってアプリケーション開発をすればよいのですが、テーマが指定されているのが特徴です。 Resurrection: お気に入りの技術を復活させる Frankenstein: 複数の技術を組み合わせてアプリを作る Skeleton Crew: ス ケルト ンを作成し、それから複数のアプリを作る Costume Contest: 洗練された不気味なデザインのアプリを作る といったテーマのようです(日本語訳は筆者による)。 自分はResurrectionを選びました。参加するにあたりテーマ選定にかなり悩んだのですが、知人から自分が普段 Common Lisp を使っていて、それは十分に古い技術なのではという指摘を受けて、確かにその通りだなということで決めました。 作ったもの Kabotanというアプリケーションを実装しました。Kabotanは Common Lisp を使って作った、HTMXとLLMを組み合わせたアプリケーションです。ハロウィンにちなんだ機能を提供していて、質問に答えたり、ハロウィンに関する文章を生成したりすることができます。 なぜ Common Lisp を採用したかというと、古い技術と見なされており、テーマのResurrectionにも合っているためです。一方で自分は普段それなりに Common Lisp を使っているので少しでも Common Lisp の良さを知ってもらえたらと思い選びました。 Kabotanは以下のURLで公開しています。 https://github.com/dentsusoken/kabotan/ Kabotanは以下のような アーキテクチャ になっています。 フロントエンド: HTMX + Tailwind CSS バックエンド: Common Lisp (clack + hunchentoot) LLM: llama.cppを利用したローカルモデル(gpt- oss -120bなどを想定) モダンなアプリケーションではフロントエンドにReactやVue.jsなどの JavaScript フレームワーク を使うことが多いですが、今回はシステムの大部分を Common Lisp で実装したかったため、HTMXを採用しました。 フロントエンドにHTMXを使うことでフロントエンドの JavaScript コードを最小限に抑え、アプリケーションの大部分を Common Lisp で実装することができました。 実際の画面の例を以下に示します。 特に各 コンポーネント 間のやり取りではServer Sent Event(SSE)を利用して、LLMからの応答をリアルタイムに受け取れるようにしています。これにより、ユーザはLLMが応答を生成している間も進捗を確認でき、より インタラクティブ な体験が可能となっています。 個人的には Common Lisp でも現代的なアプリケーションの実装は十分に可能ということを示せたのではないかと思います。 ちなみに、 Common Lisp を含む Lisp 系の言語は括弧が多いことで有名です。慣れるとS式は読みやすいのですがなれないと苦労するかもしれません。例えばKabotanのindex.htmlを返す部分は以下のようなコードになっています。 ( defun serve-index ( env ) "Serve the main index.html page. The Lack session middleware automatically handles session cookies, so we don't need to manually set them here." ( declare ( ignore env )) ( let (( html ( uiop:read-file-string "public/index.html" :external-format :utf-8 ))) `( 200 ( :content-type "text/html; charset=utf-8" ) ( ,html ) ) )) Lisp 系言語ではこのS式と呼ばれる (関数名 引数1 引数2 ... 引数N) というような記法でプログラム自体を記述します。このデータもプログラム本体も全てこのS式で表現することで、非常に強力なマクロを作れたりするのが特徴となっています。 実装するうえで苦労したところ Common Lisp をKiroで利用するにあたって苦労した点、工夫した点がいくつかありました。 Common Lisp をKiroで利用するための整備 まず、 Common Lisp をKiroが利用できるようにするための整備です。例えば、 Common Lisp には標準でデバッガが実装されており、エラー発生時などには自動的にデバッガが起動します。 Common Lisp で広く使われている開発環境のSLIMEではこれを便利に利用することができます。しかし、この機能はKiroなどのAIにとっては対話的な操作が必要になってしまいAIの操作を阻害してしまいます。 また、ASDF(Another System Definition Facility)という Common Lisp の デファクトスタンダード なビルド管理システムがあります。これも事前に定義を行っておきひな形のアプリケーションが動作するような状態まで整備を行いました。その上で、makeを利用して常にデバッガを起動しないオプションを付けて起動するようにし、Kiroからもmake経由で実行するような形にしました。 最終的には以下のような Makefile のエントリとなりました。 --disable-debugger を実行時に引数で渡し、ASDFを使ってKabotanをビルド、実行する形になっています( ql:quickload がASDFを内部で呼ぶ仕組みになっています)。 ROS = ros LISP_IMPL = sbcl SYSTEM = kabotan TEST_SYSTEM = kabotan-test run: $(ROS) -L $(LISP_IMPL) run -- \ --disable-debugger \ --eval '(ql:quickload :$(SYSTEM))' \ --eval '(uiop:quit (kabotan:main))' Server Sent Eventへの対応 Kiroでのアプリケーション開発において、Server Sent Event(SSE)に対応させるのに苦労しました。SSEはサーバからクライアントへリアルタイムにデータを送信するための技術であり、LLMの応答をリアルタイムに受け取るために必要でした。 ブラウザ-Kabotan間のSSE対応 Common Lisp のWebフレームワークであるclackやhunchentootは直接このSSEをサポートしておらず、独自に実装する必要がありました。これはclackのソケットを直接操作する機能を利用して、SSEに対応させることができました。 Kabotan-llama.cpp間のSSE対応 llma.cppのサーバにとってKabotanはSSEのクライアントとして振る舞う必要があります。 これも Common Lisp のHTTPクライアントライブラリのdexadorを利用して独自に実装する必要がありました。dexadorは通信に利用しているソケットを扱うことができ、これを操作することでSSEに対応させることができました。 その他苦労した点 HTMX周りはKiroに色々指示を出さないとうまく対応できないことがあり苦労しました。HTMXはフロントエンドの JavaScript コードを減らすことができる利点がありますが、Kiroにその利点を理解してもらうのが難しい場合があり、何も指示を行わないとフロントエンドの JavaScript でほとんどの実装を行ってしまい、HTMXの利点がない構成になってしまうことがありました。 またLLMを利用するアプリケーションはテストに時間がかかってしまいます。 そしてKiroはコマンドの応答待ち時間が最大で20分になっていますが、稀にこれを超えてしまうことがありました。こうなってしまうと、Kiroはテストを途中で打ち切ったり問題がないのに問題があると判定して編集作業を行おうとしたり、逆に問題があるのに問題ないと判断してしまったりすることがあり、開発効率が低下することがありました。 Kiroの使い方について Hackathon 全体を通じてどのようにKiroを活用したのかについても紹介します。 Kiroを使ううえで重要だと感じたポイントは以下のとおりです。 Spec、Steeringの活用 Hookの活用 テストの工夫 特に、SpecとSteeringの使い分けは重要だと感じました。 Specという名前を見るとSpec側に詳細な仕様を書くべきだと考えがちですが、実際にはSteering側に詳細な仕様を書く方が効果的でした。例えば、 アーキテクチャ に関する指示、設計上の選択といったものはSteeringに記載し、実装が進むにつれて状況が変わるたびにSteeringはプロジェクトの実際の状況を表すように更新する必要がありました。 そして、Specは実際の小さな作業を行うために必要な最小限の仕様に留めておく方が効果的でした。基本的な動作の概要を伝えて、Design.mdを作成してもらい、Task.mdを生成してもらうようにしました。つまり、Specは スクラム 開発などでいうところの「ユーザーストーリー」に近い役割を果たし、Steeringが「詳細な要件定義書」や「設計書」に近い役割を果たす形です。 これらを前提に置き、詳細な設計などはVibe CodingでKiroと相談しながら進め随時Steeringを更新したり、簡単なバグ修正などは直接修正したりして進めました。一定規模を超える作業になりそうな場合はSpecを作成して対応してもらい、 リファクタリング などの作業もSpecとして作成して随時実施するようにしました。 以下は開発時のKiroの画面の様子です。Agent Steeringに色々設計上を指定しておき、作業ごとにSpecを作り開発していきました。 また、Hookも積極的に活用しました。Hookを使うことで、Kiroが生成したコードに対して自動的に追加の処理を行うことができます。Kabotanでは lisp ファイルが更新されたときに自動的にテストが実行されるようにHookを設定しました。Hookは便利なのですが、TaskとしてKiroが実行してしまうためHookを実行している間新しいタスクの着手が出来ないという欠点もあります。つまり、タスクが完了したとKiroが報告してくるので次のタスクを実行しようとするが、Hookが動作している間は新しいタスクに着手できないということです。しかもKiroは現在実行中のタスクを一望するインタフェースが分かりづらい位置にあるので最初は苦労しました。 以下のUIで実行中のタスクなどが確認できます。クリックして初めて詳細が分かるようになっています。常に表示されていると便利なのですが今後是非改善してほしいですね。 テストの書き方も簡単な 単体テスト であればKiroに生成してもらうようにして、実際の動作を確認するような総合テストについては細かく指示を出してKiroに生成してもらうようにしました。総合テストでは受け入れのためのテストを作るような指示を出し、それをこまめに実行するような運用を行いました。これは最終的な動作だけはちゃんと確認したいという意図でした。 Kiroが良くなっていた点 Kiroが発表されてから時間が経過しており、その間にKiro自体も改善されていました。今回の Hackathon を通じて特に良くなっていたと感じた点は以下のとおりです。 利用できるモデルが増え、特にClaude Sonnet 4.5が利用できるようになりました。これにより、生成されるコードの品質が向上しています。また、利用中にKiroがGA(General Availability)になりQ Developer CLI がKiro CLI になったという変化もありました。これに合わせてアカウント管理などもKiro側で行うことが可能になり、より使いやすくなっていました。特に上限に達した場合にも追加で課金を行うことで利用が可能になるのはとても便利になった点です。Q Developerを試していたころは上限に達すると利用できなくなってしまい、開発が中断されてしまうことがありました。新規アカウントをその都度発行するという手段もあるのですが、会社のアカウントで利用している場合は難しい場合もあるので、追加課金で対応できるのは便利です。 また、プロパティベースのテストが生成できるようになりました。以前は 単体テスト などの具体的な値を使ったテストが中心でしたが、今回はプロパティベースのテストを生成するように指示を出すことで、より広範囲な動作確認が可能になります。受入テストなどでは特に有効だと感じました。 まとめ Kiroの Hackathon イベントであるKiroweenに参加し、 Common Lisp を使ったHTMX+LLMアプリケーションであるKabotanを開発しました。Kiroを活用することで、効率的に開発を進めることができ、 Common Lisp でも近代的なアプリケーションの実装が可能であることを示せたと感じています。 またKiroは言語の限定なく利用できるということが 公式ドキュメント で記載されています。 Common Lisp でも問題なく対応出来ました。採用する機会が少ない言語も含めて色々な言語でアプリケーション開発可能であることも確認できました。 Kiro自体も改善されており、より使いやすくなっていました。今後もKiroを活用して様々なアプリケーション開発に挑戦していきたいと考えています。 以上、Kiroween参加レポートでした。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @yamashita.tsuyoshi レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
こんにちは、クロス イノベーション 本部の大岡叡です。 2025年12月19日(金)に NEC ソリューションイノベータ株式会社、キャップ ジェミニ 株式会社と合同で新卒1年目の AWS 勉強会を開催しました。この記事では、その勉強会の背景や内容をご紹介します。 背景 目的 内容 気づき・参加者の反応 開催してみての気づき 参加者の反応 まとめ・今後の展望 背景 とある外部の勉強会にて、 NEC ソリューションイノベータ株式会社の上田賢哉様、キャップ ジェミニ 株式会社の遊佐康平様と出会いました。お二人とも新卒1年目です。 その勉強会の休憩時間に私から「3社合同で1年目だけの AWS 勉強会をやってみませんか」と提案したところ、その場でお二人とも賛同してくれました。正直なところ、その時は話を合わせてくれているだけかなと思っていたのですが、翌日にお二人から「勉強会をやりましょう」というメールをいただいたので、これは本当に実現させようと思い、開催に至りました。 私が勉強会を提案した理由は、近い年齢の方々との勉強会が有意義だと考えているからです。 私は社内で同期8人と隔週で勉強会を実施しています。同期同士だと遠慮なく疑問をぶつけ合えますし、近い年齢だからこそ得られる刺激もあります。この社内勉強会が良い場になっているので、会社の枠を超えてやってみたらもっと面白いんじゃないか?と思い、提案しました。 ( AWS である理由は、お二人と出会った勉強会が AWS 関連の勉強会だったからという単純な理由です。) 目的 勉強会の一番の目的は、 3社の若手エンジニアの技術力・プレゼン力の向上 です。勉強会の形式は、ハンズオンやブックリーディングではなく、登壇者が発表して参加者が聞くというオーソドックスなスタイルを採用しました。 また、他社の同期から刺激を受けたり、同業他社の同期とのつながりを作れることもこの勉強会で期待している効果です。 内容 まずは勉強会の基本情報です。 日時:12/19(金)19:00 - 21:00 場所: 電通 総研 品川本社 形態:対面のみ 参加者:16名 NEC ソリューションイノベータ株式会社:3名 キャップ ジェミニ 株式会社:6名 弊社:7名 続いて、勉強会の アジェンダ です。発表者は全部で9名で、各社から3名ずつ登壇しました。 時間 タイトル 発表者 19:00-19:05 開会 - 19:05-19:15 1. 初めて AWS 触ってみた 〜テスト環境サーバを定時起動/停止できるようにしてみた〜 水谷元紀( 電通 総研) 19:15-19:25 2. 増えたのは技術ではなく選択肢 溝上木綿( NEC ソリューションイノベータ) 19:25-19:35 3. 権限管理の落とし穴 〜Allowにしたのにアクセスできない?〜 遊佐康平(キャップ ジェミニ ) 19:40-19:50 4. 11月の個人的に気になったアップデートを調査・検証してみた 大岡叡( 電通 総研) 19:50-20:00 5. CloudWatch Agentから始める初めての AWS ログ監視 上田賢哉( NEC ソリューションイノベータ) 20:00-20:10 6. 実質2日で AWS SAAに合格した話 〜おすすめはしない勉強法〜 渡邊篤弥(キャップ ジェミニ ) 20:15-20:25 7. Amazon Route 53を使って名前解決とルーティングを学ぶ 伊藤梨子( 電通 総研) 20:25-20:35 8. S3署名付きURLは発行できた!…でも API Gateway でCORSエラーにハマった話 蒲弘大( NEC ソリューションイノベータ) 20:35-20:45 9. Glueジョブ設計で知っておくべき同時実行・DPU制限 千葉理緒(キャップ ジェミニ ) 20:45-21:00 閉会 - ※ 敬称略 ※ 19:35-19:40と20:10-20:15は休憩時間としました。 ※ 休憩時間が短かったため21:00-21:10でネットワーキングの時間を設けました。 ※ 開会、閉会、司会は私が務めました。 以下、各発表の内容を簡単に紹介します。 1. 初めて AWS 触ってみた 〜テスト環境サーバを定時起動/停止できるようにしてみた〜 EventBridgeとLambdaを使ってEC2の定時起動・停止を実装し、Teamsへの通知もWebhookで実現。実装中につまずいたポイントを分かりやすく、かつ面白く紹介してくれました。 2. 増えたのは技術ではなく選択肢 AWS を勉強して実際のプロジェクトに入り、選択肢が増えたと感じた話。 アーキテクチャ に正解はなく、色々な構成が考えられるという気づきを共有してくれました。re:Inventで発表された新サービスの紹介もありました。 3. 権限管理の落とし穴 〜Allowにしたのにアクセスできない?〜 IAMでS3へのアクセスを許可したはずなのに、 バケット 間でファイルをコピーできない…原因は バケット ポリシーでした。パブリックアクセスの設定や、セキュリティ事故につながる バケット ポリシーの例をクイズ形式で紹介してくれました。 4. 11月の個人的に気になったアップデートを調査・検証してみた 11月に気になって触れていなかったアップデートを調査・検証した話。 aws loginコマンド、リージョナルNAT Gateway 、M2MのALBでのJWT検証の3つについて話しました。 5. CloudWatch Agentから始める初めての AWS ログ監視 EC2からCloudWatch Logsにログを送信する手順について解説。EC2にCloudWatch Agentをインストールしたのにログが送信できなかったトラブルとその解決についても話してくれました。 6. 実質2日で AWS SAAに合格した話 ChatGPTを活用したチェックリスト勉強法を紹介。試験結果から各分野の習熟度を振り返って分析していたのが印象的でした。 7. Amazon Route 53を使って名前解決とルーティングを学ぶ DNS の基礎から丁寧に解説。Route 53、ALB、EC2の構成で アーキテクチャ を組み、実際に名前解決を試した内容を紹介してくれました。 8. S3署名付きURLは発行できた!…でも API Gateway でCORSエラーにハマった話 地図上にお気に入りの場所と画像を紐づけて共有できるアプリを、 API Gateway ・Lambda・S3・RDSを使った構成で構築。その過程で遭遇したCORSエラーの解決について話してくれました。 9. Glueジョブ設計で知っておくべき同時実行・DPU制限 AWS Glueの概要からジョブ・ワーカー・DPUの説明、アカウントごとの制限やジョブの同時実行制限について解説。DPUの性能を上げるか、ワーカー数を増やすかといった設計上の考慮点も紹介してくれました。 気づき・参加者の反応 開催してみての気づき 勉強会を開催してみて、いくつか気づいたことがあったのでご紹介します。 1. 年末は人を集めにくい 年末の時期ということもあり、「行きたいけど予定が合わない!」と連絡をくれた方が多くいました。また、体調不良や忘年会が重なり、直前で不参加になった方もいました。 2. アジェンダ はゆとりをもって組むべき 今回は2時間で9名が登壇するタイトなスケジュールでした。21:00〜21:10のネットワーキングの時間以外はあまり交流ができなかったので、次回は休憩時間を少し長めにとったり、ネットワーキングの時間をしっかり設けたりして参加者同士の交流を増やしたいと考えています。 3. 学び続けるモチベーションになる 社外の同期が勉強している姿を見ると、自分も負けないようにもっと頑張ろうという気持ちになります。近い年齢の人同士が互いに良い刺激を与え合えるという点がこの勉強会の価値だと感じました。 参加者の反応 弊社参加者からは以下のような感想をもらいました。嬉しい反応ばかりでした! 「いい刺激になりました!」 「登壇できて良い経験になりました!」 「他の会社の人たちと話せていい経験になりました!」 「 AWS の勉強を始めようと思いました!」 まとめ・今後の展望 NEC ソリューションイノベータ株式会社、キャップ ジェミニ 株式会社の1年目の方々と AWS 勉強会を実施したお話を紹介しました。 今回の勉強会が参加者から好評だったため、第2回の開催を検討中です。上田様、遊佐様と振り返りを行い、次の勉強会をより良いものにしたいと考えています。 最後までお読みいただきありがとうございました! 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @ooka.toru レビュー: @miyazawa.hibiki ( Shodo で執筆されました )
アバター
はじめに 金融IT本部 2年目の坂江 克斗です。 業務にて ドメイン ベースでのアウトバウンド 通信制 限を考えるタイミングがあったため、本記事を書きました。 DNS に関する基本的な内容は こちらの記事 に、アウトバウンドセキュリティの概要に関しては こちらの記事 に記載しているので、気になる方はぜひ参照してみてください。 はじめに 概要 ドメインベースのアウトバウンド通信制御の検証 前提 Amazon Route 53 Resolver DNS Firewallの検証 Terraformの実装 デプロイ後の検証 AWS Network Firewallの検証 Terraformの実装 デプロイ後の検証 AWS Network Firewall Proxyの検証 デプロイ後の検証 まとめ おわりに 概要 以下の表に AWS におけるアウトバウンドセキュリティサービスの一覧を示します。 レイヤ 制御観点 AWS サービス 料金 特徴 DNS (名前解決) ドメイン 名 Amazon Route 53 Resolver DNS Firewall 低( DNS クエリ) 早期に遮断可能。ローカルでの名前解決やIP直打ち、独自 DNS 使用等の Route 53 Resolverを経由しない通信は防御不可 L3–4 IP / Port Security Groups / NACL 無料 ネットワークセキュリティのベース L3–7 IP / Port, TCP ヘッダ / HTTP ヘッダ / TLS ヘッダ AWS Network Firewall , AWS Gateway Load Balancer + サードパーティ ソフト 高(AZ毎の常時稼働エンドポイント+処理量) 柔軟な制御が可能で、マネージドにも運用可能。 ドメイン 制御において、Host ヘッダのスプーフィングは防御不可、SNIスプーフィングには対応可能※1。 DNS + L3–7 IP / Port, HTTP ヘッダ / TLS ヘッダ AWS Network Firewall Proxy 未発表 マネージドな フォワ ードプロキシサービス(通信主体は Proxy)。現状はプレビュー公開中でボディ解析は未提供 ※1 Network Firewall の TLS インスペクション機能を利用することで対策可能 今回は業務要件で挙がった ドメイン ベースのアウトバウンド制御手法に関して、 Amazon Route 53 Resolver DNS Firewall ・ AWS Network Firewall ・ AWS Network Firewall Proxy で比較検証を行います。 ドメイン ベースのアウトバウンド 通信制 御の検証 前提 検証のためローカルで実装します。 EC2をプライベートサブネットに配置し、NAT ゲートウェイ 経由での AWS サービス・外部サービスへのアクセスを想定します。 Allow List により検証します。 SSM接続用の AWS ドメイン と本テックブログ tech.dentsusoken.com へのアクセスのみ許可し、他 ドメイン をすべて拒否。 本検証では、公開されている Web サイトに対して、 通信制 御の挙動を確認する目的で少数のリクエストを送信しており、サービスの可用性や機密性に影響を与える行為は行っていません。 本記事では、検証のシンプルさや負荷の低さ(数リクエスト程度)からテックブログ用 ドメイン による検証を行いました。 しかし、本来はテスト手法に問題があり予期せず過大な負荷をかけてしまった場合など、 規約違反 となる可能性があるため、 IANA(現在はICANNの一部)が管理するテスト用ドメイン ( example.com 等)を使用し検証を行うことが最も適切となります。 Amazon Route 53 Resolver DNS Firewall の検証 Terraformの実装 以下に示すシンプルな構成で検証します。 初めに VPC およびEC2の定義をします。 terraform { required_version = "~> 1.14.0" required_providers { aws = { version = "6.23.0" source = "hashicorp/aws" } } } provider "aws" { region = "ap-northeast-1" } data "aws_region" "current" {} data "aws_partition" "current" {} data "aws_caller_identity" "current" {} ########################################################################### ## VPC resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true enable_dns_support = true tags = { Project = "example" } } resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.main.id tags = { Project = "example" } } ## Subnet resource "aws_subnet" "public_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.1.0/24" availability_zone = "ap-northeast-1a" map_public_ip_on_launch = true tags = { Project = "example" } } resource "aws_subnet" "private_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.2.0/24" availability_zone = "ap-northeast-1a" map_public_ip_on_launch = false tags = { Project = "example" } } ## Root Table resource "aws_route_table" "public_a" { vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } tags = { Project = "example" } } resource "aws_route_table" "private_a" { vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.ngw.id } tags = { Project = "example" } } resource "aws_route_table_association" "public_a" { subnet_id = aws_subnet.public_a.id route_table_id = aws_route_table.public_a.id } resource "aws_route_table_association" "private_a" { subnet_id = aws_subnet.private_a.id route_table_id = aws_route_table.private_a.id } ## NAT resource "aws_eip" "ngw" { } resource "aws_nat_gateway" "ngw" { depends_on = [ aws_internet_gateway.igw ] allocation_id = aws_eip.ngw.id subnet_id = aws_subnet.public_a.id tags = { Project = "example" } } ########################################################################### ## Instance data "aws_ami" "amazon_linux_2023" { most_recent = true owners = [ "amazon" ] filter { name = "name" values = [ "al2023-ami-*-x86_64" ] } } resource "aws_instance" "amazon_linux" { ami = data.aws_ami.amazon_linux_2023.id instance_type = "t2.micro" subnet_id = aws_subnet.private_a.id iam_instance_profile = aws_iam_instance_profile.ec2.name vpc_security_group_ids = [ aws_security_group.ec2.id, ] tags = { Project = "example" } } ## Security Group resource "aws_security_group" "ec2" { name = "ec2-sg" vpc_id = aws_vpc.main.id egress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0" ] } egress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0" ] } } ## IAM resource "aws_iam_instance_profile" "ec2" { name = "ec2-profile" role = aws_iam_role.ec2.name } resource "aws_iam_role" "ec2" { name = "ec2-role" assume_role_policy = jsonencode ( { Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } Action = "sts:AssumeRole" }] } ) } resource "aws_iam_role_policy_attachment" "ssm" { role = aws_iam_role.ec2.name policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" } 次に、Route53 Resolverのログを定義します。 Route53 Resolverは VPC 単位で適用されるため、ログの紐付けも VPC 単位となります。 ## Resolver Log resource "aws_cloudwatch_log_group" "example" { name = "/aws/route53/resolver-firewall/example" tags = { Project = "example" } } ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_query_log_config resource "aws_route53_resolver_query_log_config" "example" { name = "example" destination_arn = aws_cloudwatch_log_group.example.arn tags = { Project = "example" } } ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_query_log_config_association resource "aws_route53_resolver_query_log_config_association" "example" { resolver_query_log_config_id = aws_route53_resolver_query_log_config.example.id resource_id = aws_vpc.main.id } Route53 Resolver Firewall を定義します。 使用するリソースは以下の5つです。Route53 Resolverログと同様に、 VPC 単位での紐付けを行います。 aws _route53_resolver_ firewall _config ファイアウォール の基本設定 aws _route53_resolver_ firewall _rule_group 複数の ファイアウォール ルールを紐付けるルールグループ aws _route53_resolver_ firewall _rule_group_association VPC とルールグループを紐付け aws _route53_resolver_ firewall _rule (Allow List / Deny List方式で変動) ドメイン リストを基に許可・拒否・アラートのアクションを設定したルール aws _route53_resolver_ firewall _domain_list (Allow List / Deny List方式で変動) フィルタリングに使用する ドメイン のリスト ## Firewall Config ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_firewall_config resource "aws_route53_resolver_firewall_config" "main" { resource_id = aws_vpc.main.id firewall_fail_open = "DISABLED" } ## Firewall Rule Group ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_firewall_rule_group resource "aws_route53_resolver_firewall_rule_group" "example" { name = "example" tags = { Project = "example" } } ## Firewall Rule Group Association with VPC ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_firewall_rule_group_association resource "aws_route53_resolver_firewall_rule_group_association" "example" { name = "example" vpc_id = aws_vpc.main.id firewall_rule_group_id = aws_route53_resolver_firewall_rule_group.example.id priority = 101 # 100は予約済み } Allow List では、広範囲の ドメイン に対するBlockを定義後に、Allowする設定を追加することで実装することが出来ます。 今回は、SSM接続用の AWS ドメイン と本テックブログ tech.dentsusoken.com へのアクセスのみを許可し、その他の ドメイン をすべて拒否するように設定します。 Allow Listの設定負荷を減らすため firewall_domain_redirection_action = "TRUST_REDIRECTION_DOMAIN" とすることで、初めに解決しに行った ドメイン を信頼し CNAME 後の検証はしない設定にしています。 ## Firewall Domain List ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_firewall_domain_list resource "aws_route53_resolver_firewall_domain_list" "block_example" { name = "block-example" domains = [ "*." ] tags = { Project = "example" } } resource "aws_route53_resolver_firewall_domain_list" "allow_example" { name = "allow-example" domains = [ "tech.dentsusoken.com" , "*.amazonaws.com" , "*.cloudfront.net" ] tags = { Project = "example" } } ## Firewall Rule (Associated with Firewall Rule Group) ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_resolver_firewall_rule resource "aws_route53_resolver_firewall_rule" "block_example" { name = "block-example" action = "BLOCK" block_response = "NXDOMAIN" firewall_domain_list_id = aws_route53_resolver_firewall_domain_list.block_example.id firewall_rule_group_id = aws_route53_resolver_firewall_rule_group.example.id firewall_domain_redirection_action = "INSPECT_REDIRECTION_DOMAIN" priority = 200 } resource "aws_route53_resolver_firewall_rule" "allow_example" { name = "allow-example" action = "ALLOW" firewall_domain_list_id = aws_route53_resolver_firewall_domain_list.allow_example.id firewall_rule_group_id = aws_route53_resolver_firewall_rule_group.example.id firewall_domain_redirection_action = "TRUST_REDIRECTION_DOMAIN" priority = 100 } デプロイ後の検証 terraform apply の実行後、エラーなく3分程度で完了しました。 Allow List において、digコマンドによる名前解決を見ると以下の結果となります。 想定通り tech.dentsusoken.com の名前解決は成功し、 www.dentsusoken.com の名前解決は拒否されていることが確認できました。 sh-5.2$ dig tech.dentsusoken.com ; <<>> DiG 9.18.33 <<>> tech.dentsusoken.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62350 ;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;tech.dentsusoken.com. IN A ;; ANSWER SECTION: tech.dentsusoken.com. 300 IN CNAME hatenablog.com. hatenablog.com. 54 IN A 35.75.255.9 hatenablog.com. 54 IN A 54.199.90.60 ;; Query time: 0 msec ;; SERVER: 10.0.0.2#53(10.0.0.2) (UDP) ;; WHEN: Wed Dec 17 12:52:28 UTC 2025 ;; MSG SIZE rcvd: 106 sh-5.2$ dig www.dentsusoken.com ; <<>> DiG 9.18.33 <<>> www.dentsusoken.com ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 17641 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;www.dentsusoken.com. IN A ;; Query time: 0 msec ;; SERVER: 10.0.0.2#53(10.0.0.2) (UDP) ;; WHEN: Wed Dec 17 12:52:40 UTC 2025 ;; MSG SIZE rcvd: 48 ローカルでの名前解決として、digコマンドで取得した IPアドレス を基にhostsの書き換えによる ドメイン アクセスを検証します。 Route 53 Resolverを経由せずに名前解決が可能になったことで、拒否されるはずの www.dentsusoken.com への通信が可能になっていることが確認できました。 sh-5.2$ curl https://www.dentsusoken.com curl: (6) Could not resolve host: www.dentsusoken.com sh-5.2$ sudo vi /etc/hosts sh-5.2$ sudo cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost6 localhost6.localdomain6 3.173.219.57 www.dentsusoken.com sh-5.2$ curl https://www.dentsusoken.com <!DOCTYPE html> (中略) <link rel="canonical" href="https://www.dentsusoken.com/" /> <meta name="description" content="株式会社電通総研の公式ホームページです。2024年1月1日に社名をISID(電通国際情報サービス)から変更しました。 お客様の業務課題に対応するソリューションや導入事例のほか、企業情報、IR情報、採用情報等をご紹介しています。" /> <meta property="og:site_name" content="電通総研" /> (中略) </html> AWS Network Firewall の検証 Terraformの実装 以下に示すシンプルな構成で検証します。 初めに VPC およびEC2の定義をします。 VPC に関して、Network Firewall 用のサブネットが存在すること、EC2とNAT ゲートウェイ の通信において Firewall Endpoint を経由するルーティングを設定することに注意が必要です。 terraform { required_version = "~> 1.14.0" required_providers { aws = { version = "6.23.0" source = "hashicorp/aws" } } } provider "aws" { region = "ap-northeast-1" } data "aws_region" "current" {} data "aws_partition" "current" {} data "aws_caller_identity" "current" {} ########################################################################### ## VPC resource "aws_vpc" "main" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true enable_dns_support = true tags = { Project = "example" } } resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.main.id tags = { Project = "example" } } ## Subnet resource "aws_subnet" "public_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.1.0/24" availability_zone = "ap-northeast-1a" map_public_ip_on_launch = true tags = { Project = "example" } } resource "aws_subnet" "private_nfw_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.2.0/24" availability_zone = "ap-northeast-1a" map_public_ip_on_launch = false tags = { Project = "example" } } resource "aws_subnet" "private_a" { vpc_id = aws_vpc.main.id cidr_block = "10.0.3.0/24" availability_zone = "ap-northeast-1a" map_public_ip_on_launch = false tags = { Project = "example" } } ## Root Table resource "aws_route_table" "public_a" { vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.igw.id } route { cidr_block = "10.0.3.0/24" vpc_endpoint_id = tolist ( tolist ( tolist (aws_networkfirewall_firewall.example.firewall_status) [ 0 ] .sync_states) [ 0 ] .attachment) [ 0 ] .endpoint_id } tags = { Project = "example" } } resource "aws_route_table_association" "public_a" { subnet_id = aws_subnet.public_a.id route_table_id = aws_route_table.public_a.id } resource "aws_route_table" "private_nfw_a" { vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" nat_gateway_id = aws_nat_gateway.ngw.id } tags = { Project = "example" } } resource "aws_route_table_association" "private_nfw_a" { subnet_id = aws_subnet.private_nfw_a.id route_table_id = aws_route_table.private_nfw_a.id } resource "aws_route_table" "private_a" { vpc_id = aws_vpc.main.id route { cidr_block = "0.0.0.0/0" vpc_endpoint_id = tolist ( tolist ( tolist (aws_networkfirewall_firewall.example.firewall_status) [ 0 ] .sync_states) [ 0 ] .attachment) [ 0 ] .endpoint_id } tags = { Project = "example" } } resource "aws_route_table_association" "private_a" { subnet_id = aws_subnet.private_a.id route_table_id = aws_route_table.private_a.id } ## NAT resource "aws_eip" "ngw" { } resource "aws_nat_gateway" "ngw" { depends_on = [ aws_internet_gateway.igw ] allocation_id = aws_eip.ngw.id subnet_id = aws_subnet.public_a.id tags = { Project = "example" } } ########################################################################### ## Instance data "aws_ami" "amazon_linux_2023" { most_recent = true owners = [ "amazon" ] filter { name = "name" values = [ "al2023-ami-*-x86_64" ] } } resource "aws_instance" "amazon_linux" { depends_on = [ aws_networkfirewall_firewall.example ] ami = data.aws_ami.amazon_linux_2023.id instance_type = "t2.micro" subnet_id = aws_subnet.private_a.id iam_instance_profile = aws_iam_instance_profile.ec2.name vpc_security_group_ids = [ aws_security_group.ec2.id, ] tags = { Project = "example" } } ## Security Group resource "aws_security_group" "ec2" { name = "ec2-sg" vpc_id = aws_vpc.main.id egress { from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0" ] } egress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = [ "0.0.0.0/0" ] } } ## IAM resource "aws_iam_instance_profile" "ec2" { name = "ec2-profile" role = aws_iam_role.ec2.name } resource "aws_iam_role" "ec2" { name = "ec2-role" assume_role_policy = jsonencode ( { Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Service = "ec2.amazonaws.com" } Action = "sts:AssumeRole" }] } ) } resource "aws_iam_role_policy_attachment" "ssm" { role = aws_iam_role.ec2.name policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" } Network Firewall の定義をします。 今回は他 VPC への共有もなく、シンプルな構成となるため必要なリソースは以下の4種類となります。 Firewall はサブネット単位で紐付けます。 また、 aws_networkfirewall_logging_configuration のログタイプをALERTに設定することで、ALERTまたは DROP ルールにマッチした通信のみログに出力されるようになります。 aws _networkfirewall_ firewall Network Firewall の実体となるリソース。 VPC やサブネットへの関連付けを行い、ポリシーやログ設定を適用するための起点 aws _networkfirewall_logging_configuration ログ設定 aws _networkfirewall_ firewall _policy (Allow List / Deny List 方式で変動) 1つ以上のルールグループを紐付け、適用する順序や優先度を設定可能なポリシー aws _networkfirewall_rule_group (Allow List / Deny List 方式で変動) 1つ以上のステートレス/ステートフルルールを設定可能なルールグループ ## Network Firewall (+ Firewall Endpoint) ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_firewall#enabled_analysis_types-1 resource "aws_networkfirewall_firewall" "example" { name = "example" firewall_policy_arn = aws_networkfirewall_firewall_policy.example.arn vpc_id = aws_vpc.main.id enabled_analysis_types = [] # HTTP、HTTPS通信の解析をしてレポートを出力、ドメインリスト作成に活用可能 firewall_policy_change_protection = false # 検証のため subnet_change_protection = false # 検証のため subnet_mapping { subnet_id = aws_subnet.private_nfw_a.id } tags = { Project = "example" } } ## Log Config resource "aws_cloudwatch_log_group" "nfw" { name = "/aws/vpc/network-firewall/example" tags = { Project = "example" } } ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_logging_configuration resource "aws_networkfirewall_logging_configuration" "example" { firewall_arn = aws_networkfirewall_firewall.example.arn logging_configuration { log_destination_config { log_destination = { logGroup = aws_cloudwatch_log_group.nfw.name } log_destination_type = "CloudWatchLogs" log_type = "ALERT" # FLOWの場合はPassルールもログに出力 } } } Allow List の場合は、明示的にSTRICT_ORDERかつ drop :establishedにすることでデフォルトがDenyになることから、以下の実装となります。 この時、SSM接続のために AWS サービス用の ドメイン 名を許可しておく必要があります。 ## Firewall Rule Group ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_rule_group resource "aws_networkfirewall_rule_group" "example" { capacity = 100 # ルール毎のキャパシティに合わせて手動設定必要 name = "example" type = "STATEFUL" rule_group { rules_source { rules_source_list { generated_rules_type = "ALLOWLIST" target_types = [ "HTTP_HOST" , "TLS_SNI" ] targets = [ "tech.dentsusoken.com" , ".amazonaws.com" , ".cloudfront.net" ] } } stateful_rule_options { rule_order = "STRICT_ORDER" } } tags = { Project = "example" } } ## Network Firewall Policy ### https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/networkfirewall_firewall_policy#stateful-rule-group-reference resource "aws_networkfirewall_firewall_policy" "example" { name = "firewall-policy-example" firewall_policy { stateless_default_actions = [ "aws:forward_to_sfe" ] stateless_fragment_default_actions = [ "aws:forward_to_sfe" ] stateful_engine_options { rule_order = "STRICT_ORDER" } stateful_default_actions = [ "aws:drop_established" ] stateful_rule_group_reference { priority = 1 resource_arn = aws_networkfirewall_rule_group.example.arn } } tags = { Project = "example" } } デプロイ後の検証 terraform apply の実行後、エラーなく6分程度で完了しました。 Allow List において、 curl コマンドによるサイトへのアクセスを実行すると、以下の結果となります。 想定通り tech.dentsusoken.com へのアクセスは許可され、 www.dentsusoken.com へのアクセスはブロックされていることが確認できました。 sh-5.2$ curl https://tech.dentsusoken.com <!DOCTYPE html> <html lang="ja" data-admin-domain="//blog.hatena.ne.jp" data-admin-origin="https://blog.hatena.ne.jp" data-author="dentsusoken" data-avail-langs="ja en" data-blog="isid.hatenablog.com" data-blog-host="isid.hatenablog.com" data-blog-is-public="1" data-blog-name="電通総研 テックブログ" data-blog-owner="dentsusoken" data-blog-show-ads="" data-blog-show-sleeping-ads="" data-blog-uri="https://tech.dentsusoken.com/" (中略) </html> sh-5.2$ curl --max-time 10 https://www.dentsusoken.com curl: (28) Connection timed out after 10002 milliseconds DNS Firewall と同様に、hostsファイルの書き換えによる ドメイン アクセスも検証してみると、当然ですが DNS 解決の有無に関係なく、最終的なパケットのHost・SNIヘッダでブロックしていることが確認できました。 sh-5.2$ sudo vi /etc/hosts sh-5.2$ sudo cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost6 localhost6.localdomain6 3.173.219.15 www.dentsusoken.com sh-5.2$ curl --max-time 10 https://www.dentsusoken.com curl: (28) Connection timed out after 10002 milliseconds 次に以下の2パターンでスプーフィングを検証しました。 Hostヘッダ詐称: curl http://www.dentsusoken.com -H "Host: tech.dentsusoken.com" SNIヘッダ詐称: curl https://tech.dentsusoken.com --resolve tech.dentsusoken.com:443:3.173.219.15 -H "Host: www.dentsusoken.com" -k いずれのケースでも、Allow List の判定上は許可され、ブロックされない(=フィルタを通過する)ことを確認しました。 一方で、Host ヘッダや SNI の不整合により、HTTP/ TLS の処理上は正しく応答できず、期待したページ表示には至りませんでした。 以上より、スプーフィングによって「許可判定そのもの」を通過できることが分かりました(ただし、通信の成立や正しいページ表示まで保証されるわけではありません)。 sh-5.2$ curl http://www.dentsusoken.com -H "Host: tech.dentsusoken.com" <html> <head><title>301 Moved Permanently</title></head> <body> <center><h1>301 Moved Permanently</h1></center> <hr><center>CloudFront</center> </body> </html> sh-5.2$ curl https://tech.dentsusoken.com --resolve tech.dentsusoken.com:443:3.173.219.15 -H "Host: www.dentsusoken.com" -k <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> (中略) <H1>421 ERROR</H1> <H2>The request could not be satisfied.</H2> <HR noshade size="1px"> The distribution does not match the certificate for which the HTTPS connection was established with. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner. (中略) </BODY></HTML> 今回はCloudFrontを使用したWebサイトであることから、CloudFrontが HTTPS 接続時に実装している ドメインフロンティング対策(SNI・Host・証明書・AWSアカウントの整合性検証) が動作した形となります。 この仕組みにより、本検証ではHostヘッダとSNIヘッダの不整合、またはHTTPのHostヘッダが TLS ハンドシェイク時に提示された証明書に含まれていないことが検知され、CloudFront側で正しく防御されました。 本記事の主題であるアウトバウンドセキュリティの観点とは直接関係しませんが、サービスを公開する際には、CloudFront や ALB などのエッジサーバ、リバースプロキシによる前面での防御を組み合わせることが有効であることが分かります。 AWS Network Firewall Proxyの検証 本記事では記事のボリュームを考慮しデプロイの手順は割愛します。 こちらの記事 でNetwork Firewall Proxyのデプロイ手順を含め詳細を記載していますのでご参照お願いします。 以下に示すシンプルな構成( Securing Egress Architectures with Network Firewall Proxy より引用)を作成します。 (プレビュー中のサービスでありTerraform Providerでは未提供のリソースのため、手動での設定も必要となります。) ルールの設定値としては以下になります。 Pre- DNS : ドメイン tech.dentsusoken.com ・ *.amazonaws.com をAllow。デフォルトアクションはDeny。 Pre-Request:ルールなし。デフォルトアクションはAllow。 Post-Response:ルールなし。デフォルトアクションはAllow。 デプロイ後の検証 Allow List において、 curl コマンドによるサイトへのアクセスを実行すると、以下の結果となります。 想定通り tech.dentsusoken.com へのアクセスは許可され、 www.dentsusoken.com へのアクセスはブロックされていることが確認できました。 sh-5.2$ export https_proxy="https://0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443" sh-5.2$ export http_proxy="https://0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443" sh-5.2$ curl https://tech.dentsusoken.com <!DOCTYPE html> <html lang="ja" data-admin-domain="//blog.hatena.ne.jp" data-admin-origin="https://blog.hatena.ne.jp" data-author="dentsusoken" data-avail-langs="ja en" data-blog="isid.hatenablog.com" data-blog-host="isid.hatenablog.com" data-blog-is-public="1" data-blog-name="電通総研 テックブログ" data-blog-owner="dentsusoken" data-blog-show-ads="" data-blog-show-sleeping-ads="" data-blog-uri="https://tech.dentsusoken.com/" (中略) </html> sh-5.2$ curl https://www.dentsusoken.com curl: (56) CONNECT tunnel failed, response 403 sh-5.2$ DNS Firewall と同様に、hostsファイルの書き換えによる ドメイン アクセスも検証してみると、当然ですが DNS 解決の有無に関係なく、最終的なパケットのHost・SNIヘッダでブロックしていることが確認できました。 sh-5.2$ sudo vi /etc/hosts sh-5.2$ sudo cat /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost6 localhost6.localdomain6 3.173.219.15 www.dentsusoken.com sh-5.2$ curl https://www.dentsusoken.com curl: (56) CONNECT tunnel failed, response 403 sh-5.2$ 次に以下の2パターンでスプーフィングを検証しました。 Hostヘッダ詐称: curl http://www.dentsusoken.com -H "Host: tech.dentsusoken.com" SNIヘッダ詐称: curl https://tech.dentsusoken.com --resolve tech.dentsusoken.com:443:3.173.219.15 -H "Host: www.dentsusoken.com" -k Hostヘッダ詐称の場合は、Allow List の判定にマッチせずに拒否されたことが確認できました。 SNIヘッダ詐称の場合は、そもそも名前解決がプロキシ側で発生するため実質的に curl https://tech.dentsusoken.com -H "Host: www.dentsusoken.com" -k と同じ挙動となります。そのため、 tech.dentsusoken.com への名前解決が発生しながら、Hostヘッダが www.dentsusoken.com になっていることで409エラーを返したと考えられます。 sh-5.2$ curl -v http://www.dentsusoken.com -H "Host: tech.dentsusoken.com" * Uses proxy env variable http_proxy == 'https://0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443' * Host 0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443 was resolved. * IPv6: (none) * IPv4: 10.0.1.29 * Trying 10.0.1.29:443... * TLSv1.3 (OUT), TLS handshake, Client hello (1): (中略) * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * Request completely sent off < HTTP/1.1 403 Forbidden (中略) Forbidden * Connection #0 to host 0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com left intact sh-5.2$ curl -v https://tech.dentsusoken.com --resolve tech.dentsusoken.com:443:3.173.219.15 -H "Host: www.dentsusoken.com" -k * Added tech.dentsusoken.com:443:3.173.219.15 to DNS cache * Uses proxy env variable https_proxy == 'https://0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443' * Host 0a776294cb5ae3886.proxy.nfw.us-east-2.amazonaws.com:443 was resolved. * IPv6: (none) * IPv4: 10.0.1.29 * Trying 10.0.1.29:443... * ALPN: curl offers http/1.1 * TLSv1.3 (OUT), TLS handshake, Client hello (1): (中略) < HTTP/1.1 409 Conflict < Connection: close < HTTP/1.1 400 Bad Request Connection: close * TLSv1.3 (IN), TLS alert, close notify (256): * shutting down connection #0 * TLSv1.3 (OUT), TLS alert, close notify (256): * TLSv1.3 (IN), TLS alert, close notify (256): * TLSv1.3 (OUT), TLS alert, close notify (256): sh-5.2$ ただし、本検証のように Network Firewall Proxy を単体で利用している場合、 --noproxy オプション等を使用しプロキシを経由しない通信をした場合には、Network Firewall Proxyのルールを無視して通信しに行くことが可能です。 そのため、Network Firewall Proxy単体ではなく他の ファイアウォール 系サービスとの組み合わせが重要となります。 まとめ ドメイン ベースのアウトバウンド 通信制 御は、サブネット単位での制御要件がない限り、 コストと防御効果(スプーフィングにも対応可能)のバランスに優れる Route 53 Resolver DNS Firewall を主軸に据える構成が推奨 だと考えられます。 そのうえで、hosts ファイルの書き換え、IP 直打ち、独自 DNS の利用などにより Route 53 Resolver を経由しない通信が成立し得る点を踏まえ、 Network Firewall や Network Firewall Proxyを併用して補完的にカバー することが重要です。 おわりに 本記事では、 AWS における具体的な ドメイン ベース 通信制 御を検証してみました。 実運用においては、 マネージドルールや推奨される Suricata ルールの導入、ログの活用による継続的な改善 が重要となります。 今後も、実際の運用や検証を通じて理解を深めていきたいと思います。 余談ですが、Network Firewall のapply後に削除することを忘れてしまい8日間放置してしまいました。 シングルAZ構成で Firewall エンドポイントは1つだけでしたが、それでも エンドポイント料金 だけで約76$(12,000円程)のコストが発生してしまいました。 apply 後の削除忘れには改めて注意が必要ですね。 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: @sakae.katsuto レビュー: Ishizawa Kento (@kent) ( Shodo で執筆されました )
アバター
.mermaid { background-color: #ffffff !important; } import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; mermaid.initialize({ startOnLoad: true }); はじめに こんにちは。 エンタープライズ 第三本部 データマネジメントユニット マーケティング IT部の藤澤です。 先日、 API プラットフォームのリーディング企業であるKong株式会社様のパートナー向け認定資格プログラム「 Partner Delivery Specialist 」に参加し、無事に 電通 総研第一号の認定を取得することができました。 この資格の認定には、対面で行われた2日間のワークショップへの参加以外にも、事前のオンライン学習、Kong Gateway Associate資格の取得が必要でかなりハードでした。 ワークショップではインフラ周りの知識もかなり必要とされたのですが、普段はアプリケーション開発を中心に行っているため、苦労する場面が多々ありました。 しかし今回のプログラムを通じて、普段の業務であまり触れることがなかったインフラ周りの知識やAPIOpsの思想などについて体系的に学ぶことができ、非常に有意義な経験となりました。 本記事では、Kongの概要に少し触れた後、ワークショップでも取り扱った、Kong Gateway で Kubernetes のマイクロサービスをプロキシする方法について共有させていただきます。 なお本記事の内容は、すべて2025年12月時点のものですのでご了承ください。 対象読者 Kongを触ったことがない方、 Kubernetes について クラウド サービスの試験などで名前は知っているが実際に使ったことがない方。 私自身がそうだったので、詰まった点、学びになった点や、実際のニーズに対してどのように活用できるのかについて詳しく書きました。 技術的な内容については、普段Kongや Kubernetes を使っている方にとっては簡単すぎるかもしれません。 はじめに 対象読者 Kongの概要 題材 EKSクラスターとサンプルバックエンドAPIをセットアップする コマンドインストール Kubernetesクラスターの作成 Bookinfoのデプロイ Kong のインフラをセットアップする CPのセットアップ DPのデプロイとCP・DP間連携 Kong Gatewayを設定する Gateway Serviceの作成 Routeの作成 動作確認 片付け おわりに Kongの概要 Kongは、マイクロサービス アーキテクチャ における API ゲートウェイ として広く採用されているプラットフォームです(日本ではデジタル庁から推奨 API ゲートウェイ に認定されています)。 API 開発ライフサイクルの管理機能、セキュリティ、認証、レート制限、ロギングなどの API ゲートウェイ 機能を統合的に提供し、複雑な API 運用を効率化します。 Kongエコシステムの代表的な コンポーネント は以下の3つです。 Kong Insomnia : API の設計・開発・テストを効率化するための強力なクライアントツールです。Postmanの競合にあたり、デザインファーストな開発を支援します。 Kong Gateway : 外部からのリクエストを受け付ける、世界で最も利用されている オープンソース の API ゲートウェイ です。高速な通信処理と豊富な プラグイン 拡張が特徴です。 Kong Mesh : マイクロサービス間の複雑な通信(内部通信)を制御・可視化するためのサービスメッシュです。サービス間の暗号化や トラフィック 制御を担います。 出典: Kong Inc. 公式サイト より引用 そして、これらの コンポーネント を統合的に管理・運用できる枠組みを SaaS として提供している Kong Konnect という製品があります。 Konnectを利用することで、Insomniaで設計した API スペックの自動公開(開発者ポータル)、 Gateway /Meshの統合監視、チーム間でのコラボレーション機能などが提供され、 API ライフサイクル全体の効率化を実現できます。 本ブログでは、上記のうち主に Kong Gateway と Kong Konnect を使います。 Kong Gateway の アーキテクチャ には、主に以下の3種類があります。 DB lessモード : Kong Gateway の設定情報を、各ノードごとに YAML 形式の設定ファイルで管理する軽量なモードです。 出典: Kong Inc. 公式サイト より引用 Traditionalモード : PostgreSQL などのデータベースを使用して設定を管理するモードで、大規模な環境や複数のノードを運用する際に適しています。 出典: Kong Inc. 公式サイト より引用 Hybridモード : データベースと接続して設定を管理する専門のコン トロール プレーン(CP)と実際に トラフィック をさばく専門のデータプレーン(DP)を分離し、管理を一元化しながら トラフィック 処理を分散できる構成です。スケーラビリティとセキュリティの両面で大きなメリットが得られます。 出典: Kong Inc. 公式サイト より引用 本記事では、 Hybrid アーキテクチャ を採用し、CPはKonnectで管理します 。 この場合、CPのノード作成、DBとの接続などの手順は不要で、 API の開発者が気にする必要はありません。各 API サーバとの通信が可能な場所にDPのノードを配置し、Konnectとの通信を確立すればOKです。 API ゲートウェイ のポリシーを変更する際は、Konnectのエンドポイントに対して設定を行えば、自動的に各DPに設定が反映されます。 これにより、オンプレミス・ クラウド のハイブリッド構成やマルチ クラウド 構成を柔軟に構築することが可能となり、ビジネス要件に応じた最適なインフラ設計を実現できます。 出典: Kong Inc. 公式サイト より引用 Kongそのものの説明はここまでにしておきます。詳しくは 公式ドキュメント などを参照してください。 題材 サン プルバック エンド API として、Istioの Bookinfo というマイクロサービスを使用します。 Bookinfoは4つのマイクロサービスで構成されており、そのうち productpage サービスの /api/v1/products 以下のエンドポイントはBackend For Frontend(BFF)として実装されています。 今回は Kubernetes クラスタ ーにBookinfoとKong Gateway をデプロイして、BookinfoのBFFに対してプロキシしてみます。 具体的な案件のシナリオとしては、レガシーなモノリシックアプリケーションからマイクロサービス アーキテクチャ へ段階的に移行していく際に、まずは4つのマイクロサービスを Kubernetes に切り出した、というものが想定されます。 本ブログでは1つのサービスにしかプロキシしませんが、今後他のマイクロサービスへのプロキシ機能も順次追加していくことを考えると、公開する各サービスに認証認可やログ記録などの共通処理を重複して実装するよりも、共通基盤で一元管理する方が保守性が高く、セキュリティの一貫性も保ちやすいです。 これが、まさにKong Gateway の得意とするところです。 ここからは以下の手順で実装します。 EKS クラスタ ーとサン プルバック エンド API をセットアップする Kong のインフラをセットアップする Kong Gateway を設定する EKS クラスタ ーとサン プルバック エンド API をセットアップする 今回は、 AWS のElastic Kubernetes Service(EKS)上に Kubernetes クラスタ ーを立てます。 ローカルでDocker Desktop, Kind, minikubeあたりを使ってもいいのですが、Podのオートスケーリングなどをやりたかったので、設定が簡単なEKSにしました。 ※ 1日あたり$7程度かかるので、作業が終わったら クラスタ ーを削除することをおすすめします。 コマンドインストール まず、以下のコマンドをローカルにインストールします。 コマンド 説明 kubectl Kubernetes クラスタ ーを操作するための コマンドライン ツール helm Kubernetes のパッケージマネージャー k9s Kubernetes クラスタ ーの状態をターミナル上で視覚的に確認・操作できるツール aws AWS のサービスを操作する コマンドライン ツール eksctl Amazon EKS クラスタ ーを簡単に作成・管理できる コマンドライン ツール hey HTTP負荷テストツール Mac の場合、以下のコマンドでインストールできます。 brew install kubernetes-cli helm k9s awscli eksctl hey aws コマンドを使う際、IAMユーザーのクレデンシャルが必要なので、あらかじめ AWS Management ConsoleでIAMユーザーを作成してポリシーをアタッチし、アクセスキーとシークレットアクセスキーを発行しておきます。 ポリシーは検証用なので AdministratorAccess にしてしまいました。 以下のコマンドで、クレデンシャルなどを登録します。 aws configure 実行すると、4つの質問が順番に表示されるので入力してEnterを押します。 AWS Access Key ID [ None ] : < 取得したアクセスキーIDを貼り付け > AWS Secret Access Key [ None ] : < 取得したシークレットキーを貼り付け > Default region name [ None ] : ap-northeast-1 Default output format [ None ] : json 以下のコマンドで、設定したクレデンシャルが紐づくIAMユーザーが出力されればOKです。 aws sts get-caller-identity Kubernetes クラスタ ーの作成 それでは、EKSに クラスタ ーを作成します。 Management Consoleから作成すると、EC2や VPC などの設定が大変なので、 eksctl コマンドで一気に作ります。 15分程度待ちます。 ※ eksctl を使っておくと、 クラスタ ーを削除するときにも関連リソースが全部消えるので、こちらを推奨します。 eksctl create cluster \ --name kong-techblog \ --region ap-northeast-1 \ --version 1 . 34 \ --nodegroup-name kong-techblog-spot \ --node-type t3.medium \ --nodes 3 \ --spot \ --managed オプションについては、以下のとおりです。 オプションの説明 オプション 説明 name クラスタ ー名なので、何でもOKです。 region 東京リージョンにしました。 version Kubernetes クラスタ ーのバージョンです。 nodegroup-name ノードグループ名なので、何でもOKです。 node-type t2.mircoでやったら、pod数制限にかかり起動しませんでした。 nodes ノード数です。 spot スポット インスタンス を使います。本番ではNGですが、検証用なので安いほうがいいと思います。 managed Managed Instance Groupを使います。 実行が終わったら、Management ConsoleでEKS クラスタ ーが正常に作成されたことを確認します。 ※東京リージョンが選択されていることを確認してください。 kong-techblog クラスタ ーが作成されています。 作成された クラスタ ーをクリックし、詳細画面を開いて[コンピューティング]タブを開きます。 Nodeは1つの計算リソースを表す Kubernetes の概念です。 今回で言うと、EC2 インスタンス が1つのNodeに相当します。 先ほど eksctl コマンドで クラスタ ーを作成する際に node-type=t3.medium と nodes=3 オプションを設定したので、EC2にt3.mediumの インスタンス が3つ立ち上げられています。 次に[リソース]タブを開くと、デフォルトで「リソースタイプ > ワークロード > ポッド」が選択されています。 ここに表示されているのは、コンテナをグループ化して管理するための最小単位となるPodという Kubernetes の概念です。 Podは複数のコンテナからなり、いずれかのNode上で実行され、Pod単位でスケーリングやヘルスチェックなどが行われます。 後でBookinfoやKong Gateway をデプロイすると、Podとしてデプロイされます。 その他の Kubernetes のリソースについての説明は省略します。詳しくは他のブログや公式ドキュメントを参照してください。 Bookinfoのデプロイ さて、 クラスタ ーがデプロイできたので、次にBookinfoをデプロイします。 デプロイに関する定義は、 yaml ファイルに記述します(Bookinfo公式の yaml ファイルを一部改変したものです)。 bookinfo.yaml ################################################################################################## # Namespace ################################################################################################## apiVersion : v1 kind : Namespace metadata : name : bookinfo --- ################################################################################################## # Details service ################################################################################################## apiVersion : v1 kind : Service metadata : name : details namespace : bookinfo labels : app : details service : details spec : ports : - port : 9080 name : http selector : app : details --- apiVersion : apps/v1 kind : Deployment metadata : name : details-v1 namespace : bookinfo labels : app : details version : v1 spec : replicas : 1 selector : matchLabels : app : details version : v1 template : metadata : labels : app : details version : v1 spec : containers : - name : details image : docker.io/istio/examples-bookinfo-details-v1:1.20.1 imagePullPolicy : IfNotPresent ports : - containerPort : 9080 securityContext : runAsUser : 1000 --- ################################################################################################## # Ratings service ################################################################################################## apiVersion : v1 kind : Service metadata : name : ratings namespace : bookinfo labels : app : ratings service : ratings spec : ports : - port : 9080 name : http selector : app : ratings --- apiVersion : apps/v1 kind : Deployment metadata : name : ratings-v1 namespace : bookinfo labels : app : ratings version : v1 spec : replicas : 1 selector : matchLabels : app : ratings version : v1 template : metadata : labels : app : ratings version : v1 spec : containers : - name : ratings image : docker.io/istio/examples-bookinfo-ratings-v1:1.20.1 imagePullPolicy : IfNotPresent ports : - containerPort : 9080 securityContext : runAsUser : 1000 --- ################################################################################################## # Reviews service ################################################################################################## apiVersion : v1 kind : Service metadata : name : reviews namespace : bookinfo labels : app : reviews service : reviews spec : ports : - port : 9080 name : http selector : app : reviews --- apiVersion : apps/v1 kind : Deployment metadata : name : reviews-v1 namespace : bookinfo labels : app : reviews version : v1 spec : replicas : 1 selector : matchLabels : app : reviews version : v1 template : metadata : labels : app : reviews version : v1 spec : containers : - name : reviews image : docker.io/istio/examples-bookinfo-reviews-v1:1.20.1 imagePullPolicy : IfNotPresent env : - name : LOG_DIR value : "/tmp/logs" ports : - containerPort : 9080 volumeMounts : - name : tmp mountPath : /tmp - name : wlp-output mountPath : /opt/ibm/wlp/output securityContext : runAsUser : 1000 volumes : - name : wlp-output emptyDir : {} - name : tmp emptyDir : {} --- apiVersion : apps/v1 kind : Deployment metadata : name : reviews-v2 namespace : bookinfo labels : app : reviews version : v2 spec : replicas : 1 selector : matchLabels : app : reviews version : v2 template : metadata : labels : app : reviews version : v2 spec : containers : - name : reviews image : docker.io/istio/examples-bookinfo-reviews-v2:1.20.1 imagePullPolicy : IfNotPresent env : - name : LOG_DIR value : "/tmp/logs" ports : - containerPort : 9080 volumeMounts : - name : tmp mountPath : /tmp - name : wlp-output mountPath : /opt/ibm/wlp/output securityContext : runAsUser : 1000 volumes : - name : wlp-output emptyDir : {} - name : tmp emptyDir : {} --- apiVersion : apps/v1 kind : Deployment metadata : name : reviews-v3 namespace : bookinfo labels : app : reviews version : v3 spec : replicas : 1 selector : matchLabels : app : reviews version : v3 template : metadata : labels : app : reviews version : v3 spec : containers : - name : reviews image : docker.io/istio/examples-bookinfo-reviews-v3:1.20.1 imagePullPolicy : IfNotPresent env : - name : LOG_DIR value : "/tmp/logs" ports : - containerPort : 9080 volumeMounts : - name : tmp mountPath : /tmp - name : wlp-output mountPath : /opt/ibm/wlp/output securityContext : runAsUser : 1000 volumes : - name : wlp-output emptyDir : {} - name : tmp emptyDir : {} --- ################################################################################################## # Productpage services ################################################################################################## apiVersion : v1 kind : Service metadata : name : productpage namespace : bookinfo labels : app : productpage service : productpage spec : # type: LoadBalancer ports : - port : 9080 name : http # - port: 80 # targetPort: 9080 # name: http selector : app : productpage --- apiVersion : apps/v1 kind : Deployment metadata : name : productpage-v1 namespace : bookinfo labels : app : productpage version : v1 spec : replicas : 1 selector : matchLabels : app : productpage version : v1 template : metadata : annotations : prometheus.io/scrape : "true" prometheus.io/port : "9080" prometheus.io/path : "/metrics" labels : app : productpage version : v1 spec : containers : - name : productpage image : docker.io/istio/examples-bookinfo-productpage-v1:1.20.1 imagePullPolicy : IfNotPresent ports : - containerPort : 9080 volumeMounts : - name : tmp mountPath : /tmp securityContext : runAsUser : 1000 volumes : - name : tmp emptyDir : {} --- デプロイするには、この内容を ./bookinfo.yaml に保存して、以下のコマンドを実行します。 kubectl apply -f ./bookinfo.yaml 30秒ほど経ったら、以下のコマンドでPodの状態を確認します。 kubectl get pods -n bookinfo 以下のように、6つのPodが READY: 1/1 , STATUS: RUNNING となっていることが確認できます。 READY: 0/1 や STATUS: ContainerCreating などとなっている場合は、もう少し待機します。 STATUS: Pending の場合は、Podに対してNodeのリソースが不足している可能性があるので、使用するEC2 インスタンス タイプを見直すか、ノード数を増やすことを検討します。 まだ productpage は クラスタ ー外部からアクセスできるようにしていないので、 /api/v1/products にアクセスするには、 クラスタ ー内部からリクエストを送る必要があります。 kubectl run curl-test --image = curlimages/curl --rm -it --restart = Never -- \ curl http://productpage.bookinfo:9080/api/v1/products このコマンドでは、一時的に curl-test というPodを立てて、そこから curl コマンドを叩いて、終わったらPodを削除しています。 このようにレスポンスが返って来ればOKです。 これでEKS クラスタ ーとサン プルバック エンド API (Bookinfo)のデプロイは完了です! Kong のインフラをセットアップする 前置きが長かったですが、ここから本題の Kong の話になります。 CPのセットアップ まず、 Kong Konnect にサインアップします。 ※(2025年12月現在)サインアップ後30日間は、基本的な機能を無料で使用することができます。 サインアップ後、ログイン直後にはこのような画面が表示されます。 左のメニューで、[ API Gateway ]をクリックして、右上の[New gateway ]で新しく ゲートウェイ を作成します。 ポップアップで、[Self-Managed Hybrid]を選択して、 ゲートウェイ の名前を決め、右下の[Next Step]をクリックします。 クリックすると画面遷移します。これで CP の作成が完了しました。 DPのデプロイとCP・DP間連携 次に、EKSにDPをデプロイし、CPと連携させる設定を行います。 この画面に遷移しているはずなので、左側のメニューの[Data Plane Nodes]をクリックします。 [Configure data plane]をクリックします。 ポップアップで、 Gateway Versionは Self-Managed Gateway 3.12 を、Platformは Kubernetes を選択し、下にスクロールします。 「Advanced Kubernetes Setup」が表示されるので、基本的にはその手順に従って設定を進めます。 以下のコマンドで、Kong Gateway 関連のPodをデプロイする kong Namespaceを作成し、Helm リポジトリ にKong Gateway を登録します(手順2 Set up Helm)。 kubectl create namespace kong helm repo add kong https://charts.konghq.com helm repo update この時点では、まだKong Gateway のインストールは行われていません。Helmチャートという、 Kubernetes のデプロイ定義の雛形のようなものをダウンロードして登録しただけです。 Helmのコマンドは、初回実行時少し時間がかかります。 次に、CPとDPがmTLSにより安全に接続できるよう、CPから認証情報を払い出します(手順3 Generate certificates)。 [Generate certificate]ボタンをクリックすると「Cluster Certificate」と「Certificate Key」が表示されるので、それぞれ新しく ./tls.crt と ./tls.key ファイルを作成し、コピペして保存します。 以下のコマンドで、 Kubernetes クラスタ ーにSecretリソースを作成します。 kubectl create secret tls kong-cluster-cert -n kong --cert = ./tls.crt --key = ./tls.key 続いて、Helmチャート(雛形)の変数部分を定義した values.yaml ファイルを作成します(手順4 Configuration parameters)。 後で詳しく説明しますが、Kong Gateway のPodのオートスケールの確認のため、 Konnectに表示されているものから少し変更しています。 以下の設定をコピペし、 env.cluster_control_plane 、 env.cluster_server_name 、 env.cluster_telemetry_endpoint 、 env.cluster_telemetry_server_name をKonnectが自動生成したvalues. yaml の設定と同じものに書き換えて、 ./values.yaml に保存します。 values.yaml image : repository : kong/kong-gateway tag : "3.12" secretVolumes : - kong-cluster-cert admin : enabled : false env : role : data_plane database : "off" cluster_mtls : pki cluster_control_plane : <Konnectが自動生成したvalues.yamlの設定と同じにする> cluster_dp_labels : "type:docker-kubernetesOS" cluster_server_name : <Konnectが自動生成したvalues.yamlの設定と同じにする> cluster_telemetry_endpoint : <Konnectが自動生成したvalues.yamlの設定と同じにする> cluster_telemetry_server_name : <Konnectが自動生成したvalues.yamlの設定と同じにする> cluster_cert : /etc/secrets/kong-cluster-cert/tls.crt cluster_cert_key : /etc/secrets/kong-cluster-cert/tls.key lua_ssl_trusted_certificate : system konnect_mode : "on" vitals : "off" nginx_worker_processes : "1" upstream_keepalive_max_requests : "100000" nginx_http_keepalive_requests : "100000" proxy_access_log : "off" dns_stale_ttl : "3600" router_flavor : expressions ingressController : enabled : false installCRDs : false resources : requests : cpu : 500m memory : "512Mi" limits : cpu : 2 memory : "2Gi" autoscaling : enabled : true minReplicas : 1 maxReplicas : 5 metrics : - type : Resource resource : name : cpu target : type : Utilization averageUtilization : 10 behavior : scaleDown : stabilizationWindowSeconds : 30 manager : enabled : false その後、以下のコマンドを実行し、Kong Gateway をEKSにデプロイします。 helm install my-kong kong/kong -n kong --skip-crds --values ./values.yaml このコマンドは、Kong公式のHelmチャート( Kubernetes のデプロイ定義の雛形)に対して、その変数部分を定義した values.yaml を埋め込み、 Kubernetes のデプロイ定義を完成させて、それをもとに Kubernetes クラスタ へのデプロイを実行します。 うまくいくと、ポップアップの一番下が「Data Plane Node has been found」に変わり、右下の[Done]ボタンが押せるようになります。 これでKongのインフラのセットアップは完了です! ここまでで構築してきたインフラの構成図は以下のとおりです。 %%{init: {'theme':'base', 'themeVariables': {'fontSize':'18px'}}}%% graph LR Users["🌐<br/><b>ユーザー</b>"] Developer["👨‍💻<br/><b>開発者</b>"] subgraph Konnect["<b>Kong Konnect (SaaS)</b>"] CP["<b>Control Plane</b><br/>設定管理"] end subgraph AWS["<b>AWS (ap-northeast-1)</b>"] NLB["<b>Network Load Balancer</b>"] subgraph EKS["<b>EKS Cluster: kong-techblog-cluster</b>"] subgraph NS_Kong["<b>Namespace: kong</b>"] KongPods["<b>Kong Gateway Pods</b><br/>Data Plane<br/>HPA: 1-5 replicas"] end subgraph NS_Bookinfo["<b>Namespace: bookinfo</b>"] ProductPage["<b>productpage</b><br/>:9080"] Details["<b>details</b><br/>:9080"] Reviews["<b>reviews</b><br/>:9080"] Ratings["<b>ratings</b><br/>:9080"] ProductPage --> Details ProductPage --> Reviews Reviews --> Ratings end end end Developer -.->|"Admin API<br/>(ルート/サービス/プラグイン)"| CP Users -->|HTTP| NLB NLB --> KongPods KongPods --> ProductPage KongPods -.->|mTLS 認証<br/>設定同期| CP style Konnect fill:#e1f5ff,stroke:#333,stroke-width:3px style AWS fill:#fff5e1,stroke:#333,stroke-width:3px style EKS fill:#ffffff,stroke:#333,stroke-width:2px style NS_Kong fill:#ffe1f5,stroke:#333,stroke-width:2px style NS_Bookinfo fill:#e1ffe1,stroke:#333,stroke-width:2px style KongPods fill:#ffd700,stroke:#333,stroke-width:2px style ProductPage fill:#99ff99,stroke:#333,stroke-width:2px style Details fill:#99ff99,stroke:#333,stroke-width:2px style Reviews fill:#99ff99,stroke:#333,stroke-width:2px style Ratings fill:#99ff99,stroke:#333,stroke-width:2px style NLB fill:#ff9999,stroke:#333,stroke-width:2px style CP fill:#99ccff,stroke:#333,stroke-width:2px style Users fill:#ffffff,stroke:#333,stroke-width:2px style Developer fill:#ffccff,stroke:#333,stroke-width:2px 自動的にNetwork Load Balancerが追加されていますが、これはKongのHelmチャートで、Kong Gateway のDPをデプロイすると自動的に作成されるように設定されています。EKS環境で実行すると、EC2にLoad Balancerが自動的にプロビジョニングされ、インターネットからの トラフィック を受け付けられるようになります。 またKong Gateway のPodのオートスケール(Horizontal Pod Autoscaler:HPA)についてですが、先ほどの values.yaml に以下の記述を追加していました。 resources : requests : cpu : 500m memory : "512Mi" limits : cpu : 2 memory : "2Gi" autoscaling : enabled : true minReplicas : 1 maxReplicas : 5 metrics : - type : Resource resource : name : cpu target : type : Utilization averageUtilization : 10 behavior : scaleDown : stabilizationWindowSeconds : 30 これは、初めはKong Gateway Podを1つだけデプロイしておき、PodのCPU使用率が10%を超えると、最大5個までPodをスケールアウトせよ、という命令です(あとでHPAを試すために、各Podで使用できる最大のCPUリソースと 閾値 となる利用率は低めに設定しています)。 また、30秒間CPU使用率が10%を下回った状態が続くと、そのPodは自動的にスケールインされます。 Kong Gateway を設定する CPで ゲートウェイ の設定に変更を加えるには、Konnectで ゲートウェイ ごとに払い出されるAdmin API というエンドポイントを使用します。 Konnectの GUI 上で設定を変更すると、裏側でAdmin API が叩かれます。 CPとDPはmTLSで接続され、CPで ゲートウェイ の設定に変更を加えると、順次DPにも設定が反映されていきます。 ちなみにこの仕組みではDP( Kubernetes )からCP(Konnect)に向けてポーリングされるので、 Kubernetes へのインバウンド トラフィック を許可する必要がないセキュアな設計になっています。 GUI から設定して、インターネット上からBookinfoを叩けるようにします。 Gateway Serviceの作成 左側のメニューから[ Gateway Services]を選択します。 [New gateway service]をクリックします。 Full URL にはKong Gateway DPから見た、BookinfoのアプリケーションエンドポイントURLを入力します。 今回の場合は、 http://productpage.bookinfo:9080/api/v1 を入力します( ドメイン の部分は、 <Service名>.<Namespace名> とすれば、 クラスタ ー内の DNS Podが名前解決してServiceの IPアドレス を引いてきてくれます)。 Name は適当に bookinfo-v1 として、[Save]をクリックします。 なぜ http://productpage.bookinfo:9080 にしないかというと、後でRouteの Path に誤って / を登録してしまうと、 /api/v1 以下以外の /productpage や /login エンドポイントなどにアクセスできるようになってしまうためです。確実に /api/v1 以下だけにアクセスを限定するために、このようにしました。 Routeの作成 次に、遷移先のページでRouteを設定します。 左のメニューの[Routes]ではなく、画面中央に並んでいる[Routes]タブをクリックし 、[New Route]を選択します。 ※左のメニューの[Routes]からだと、別途作成したRouteを、先ほど作成した Gateway Serviceに紐づける作業が必要になります。 設定画面で、 Name を products 、Route ConfigurationはBasicで、 Path に /products を入力します。 Strip Path からチェックを外して[Save]をクリックします。 Routeは、条件に当てはまるリクエストをDPが受けたときに、関連付けてある Gateway Serviceにリクエストをプロキシします。 今回は Path に /products を設定しているので、 DPに対して /products から始まるパスでリクエストが来た場合 に、関連付けた bookinfo-v1 Gateway Serviceにプロキシされます。 Strip Path にチェックをしていると、このときマッチした部分を削除してから、 Gateway Serviceにプロキシします。 Gateway Serviceは、設定したURL productpage.bookinfo:9080/api/v1 の 末尾にRouteから送られてきたパスをくっつけて 、バックエンドに転送します。 以下に、どのようなパスのDPへのリクエストに対して、どのようなパスでDPがバックエンド(Bookinfo)にプロキシするかを示します。 DPへのリクエストのパス Strip Path: true の場合 Strip Path: false の場合 /products productpage.bookinfo:9080/api/v1 ( 404 Not Found ) productpage.bookinfo:9080/api/v1/products (200 OK) /products/1 productpage.bookinfo:9080/api/v1/1 ( 404 Not Found ) productpage.bookinfo:9080/api/v1/products/1 (200 OK) /products/1/ratings productpage.bookinfo:9080/api/v1/1/ratings ( 404 Not Found ) productpage.bookinfo:9080/api/v1/products/1/ratings (200 OK) /api/v1/products/1/ratings Serviceにプロキシされない( 404 Not Found ) Serviceにプロキシされない( 404 Not Found ) DPへのリクエストのパスにも /api/v1 を含めたい場合は、いくつか方法が考えられますが、例えばRouteで Strip Path: true として、 Path に /api/v1 を設定すると、Serviceはそのままで動きます。 動作確認 これでインターネット上からKong Gateway 経由で、Bookinfoを叩けるようになりました。 先ほどの ロードバランサー のURLを取得し、アクセスしてみます。 このコマンドでKong Gateway が使用しているLoadBalancerの EXTERNAL-IP を確認し、コピーしておきます(以下、 your-domain.ap-northeast-1.elb.amazonaws.com とします)。 kubectl get svc -n kong ブラウザや curl で http://your-domain.ap-northeast-1.elb.amazonaws.com/products にアクセスし、以下のように返ってくれば成功です! たくさんリクエストを投げてKong Gateway DPのCPU負荷を上げ、HPAが動作するかどうか見てみます。 k9s コマンドで、Podの状態をリアルタイムで監視できます。 k9s -n kong 今は1Podしか起動していません。 別のターミナルを開き、以下のコマンドを実行し、負荷をかけます。 hey -z 120s -c 100 http://your-domain.ap-northeast-1.elb.amazonaws.com/products しばらくすると、k9s側でPodの状態が更新されていることを確認できます。 5Podまでスケールアウトしました。 hey コマンドの実行が完了してから30秒ほど経つと、スケールインが始まります。 再び1Podに戻りました。 片付け EKS クラスタ ーを削除します。EKS クラスタ ーを作成したときに eksctl を使っていれば、関連リソースが一括ですべて削除されます。 15分ほど待ちます。 eksctl delete cluster --name kong-techblog 念のため、Management ConsoleからNAT ゲートウェイ 、 ロードバランサー 、EC2 インスタンス 、EKS クラスタ ーが削除されていることを確認しておいてください。 これらの課金が大半なので、削除されていれば高額な請求が来ることはありません。 おわりに 本記事では、Kong Gateway で Kubernetes のマイクロサービスをプロキシする方法について解説しました。 今回は AWS 上の Kubernetes 環境の1つのサービスにしかプロキシしませんでしたが、プロキシ先のサービスが増えたり、マイクロサービスのインフラ環境がハイブリッドやマルチ クラウド になったりしても、大きな変更なくスケーラブルに対応できることがKong Gateway の強みです。 また、本記事では書ききれませんでしたが、 yaml ファイルによる宣言的定義をもとにAdmin API を実行してくれるdecK コマンドライン や、 Kubernetes 設定ファイルから自動的にKong Gateway の設定を構築するKong Ingress Controller、 API 利用者に向けてOpen API Specを公開するポータルを作れるDeveloper portal などをはじめとして、APIOpsの構築をサポートするツールが多数提供されています。 API のサイロ化や、品質のばらつきといった課題をお持ちの方は、ぜひKongの導入を検討されてみてはいかがでしょうか? 私たちは一緒に働いてくれる仲間を募集しています! 電通総研 キャリア採用サイト 電通総研 新卒採用サイト 執筆: 藤澤 大世 (@ftuajii) レビュー: @kumakura.koki ( Shodo で執筆されました )
アバター
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 で執筆されました )
アバター