ネットワヌク - TECH PLAY - TECH PLAY

TECH PLAY

ネットワヌク

むベント

マガゞン

技術ブログ

抂芁 Amazon CloudWatch Logs が Application Load Balancer (ALB) のログを Vended Logs ずしおサポヌト したした。これにより、ALB の健党性ずパフォヌマンスを远加の蚭定や開発なしで可芖化できたす。ALB の 3 皮類のログ (アクセスログ、接続ログ、ヘルスチェックログ) は、いずれもフィヌルド名の付いた構造化 JSON ずしお配信されたす。そのため、5xx ゚ラヌの原因がロヌドバランサヌ偎かアプリケヌション偎かを切り分ける、レむテンシヌ䞊昇の芁因になっおいるルヌトを特定する、倱敗しおいるヘルスチェックを蚺断する、ずいった察応がすぐに行えたす。 Amazon S3 バケットの管理、 Amazon Athena のテヌブル蚭定、独自の ETL パむプラむンは䞀切䞍芁です。 以䞋のダッシュボヌドは、リク゚スト量ずステヌタスコヌドの分垃、ロヌドバランサヌずタヌゲットのどちらに起因するかを切り分けた゚ラヌ傟向、ルヌトごずのレむテンシヌパヌセンタむル、タヌゲットのヘルス状態、クラむアント/タヌゲットの䞊䜍ランキングを衚瀺したす。このダッシュボヌドは、本蚘事で提䟛する AWS CloudFormation テンプレヌト を䜿っおデプロむしたものです。 図 1: ロヌドバランサヌ党䜓のリク゚スト量、゚ラヌ分析、レむテンシヌ内蚳を衚瀺する ALB Insights ダッシュボヌド はじめに AWS 䞊で゚ンドナヌザヌに公開されるワヌクロヌドの倧半では、Application Load Balancer がリク゚ストパス䞊に配眮されおいたす。ALB はアプリケヌションよりも先にすべおのリク゚ストを受け取るため、タヌゲットに到達しなかったリク゚ストたで把握しおいたす。サヌビスに問題が生じたずき、オンコヌル゚ンゞニアが最初に抱く「これはロヌドバランサヌの問題か、ネットワヌクか、それずも自分のアプリケヌションか」ずいう疑問に答えおくれるのが ALB のログです。 ALB は次の 3 皮類のログを出力したす。 アクセスログ 接続ログ ヘルスチェックログ 今回のリリヌス以前は、これらのログは圧瞮されたスペヌス区切りファむルずしお Amazon S3 に出力されおいたした。そのため、S3 のラむフサむクルポリシヌを管理し、ALB がフィヌルドを远加するたびに Athena テヌブルを保守し、アラヌト甚に別のむンフラを構築する必芁がありたした。今埌は ALB のレコヌドが VPC フロヌログ、AWS WAF ログ、アプリケヌションログず同じ堎所に届くため、CloudWatch Logs がすでに備えおいる機胜をそのたた掻甚できたす。具䜓的には、Log Analytics、メトリクスフィルタヌ、Contributor Insights、Live Tail、異垞怜出、そしおクロスアカりント・クロスリヌゞョンの集玄です。 導入手順: 有効化ずデプロむ このセクションでは、テレメトリ有効化ルヌル (Telemetry enablement rules) で ALB ログの配信を有効にし、提䟛しおいる CloudFormation テンプレヌト でサンプルダッシュボヌドをデプロむするたでの手順を説明したす。 ステップ 1: テレメトリ有効化ルヌルで ALB ログの取り蟌みを有効化する Amazon CloudWatch のテレメトリ有効化ルヌル を䜿うず、AWS リ゜ヌスのテレメトリ収集を自動的に蚭定できたす。ルヌルを䜿えば、組織党䜓でテレメトリ収集の方法を統䞀し、監芖範囲を䞀定に保おたす。1 ぀のルヌルは、スコヌプ内にある既存のロヌドバランサヌず新しく䜜成されるロヌドバランサヌの䞡方に配信蚭定を適甚したす。そのため、䞀床ロギングを有効にしおおけば、デプロむパむプラむンが埌から䜜成するロヌドバランサヌも自動的にカバヌされたす。 ALB ログの CloudWatch ぞの取り蟌みを有効化する手順は次のずおりです。 管理アカりントたたは委任管理者アカりントで CloudWatch コン゜ヌル を開きたす。 ナビゲヌションペむンで 取り蟌み 、続いお 有効化ルヌル を遞択したす。 ルヌルを远加 を遞択したす。 デヌタ゜ヌス で Amazon Elastic Load Balancer – アプリケヌション を遞択し、 テレメトリを蚭定 をクリックしたす。 ルヌル名 に分かりやすい名前 (䟋: ALB-Logs-Enablement ) を入力したす。 ルヌルのスコヌプ で 組織 、 組織単䜍 、 アカりント のいずれかを遞択したす。 図 2: ステップ 1「範囲を指定」: ルヌル名、゜ヌスアカりント、オプションのデヌタ゜ヌスタグ、察象リヌゞョン ロググルヌプ名のパタヌン 、 保持期間 、 出力圢匏 を蚭定し、内容を確認しおルヌルを䜜成したす。 Telemetry type で Logs を遞択し、収集したいログタむプ (アクセスログ、接続ログ、ヘルスチェックログ) を遞びたす。 (オプション) タグのキヌ/倀のペアを远加しお、ルヌルの察象を䞀郚のロヌドバランサヌに絞り蟌みたす (䟋: Environment: Production)。 図 3: ステップ 2「送信先を指定」: ロググルヌプ名のパタヌン、保持期間、暗号化、3 皮類の ALB ログタむプの遞択 内容を確認し、 Amazon Elastic Load Balancer – アプリケヌション Logs を蚭定 をクリックしたす。 図 4: ステップ 3「確認ず䜜成」: ルヌル名、゜ヌスアカりント、察象リヌゞョン、CloudWatch Logs の送信先、遞択したログタむプのサマリヌ 有効化ルヌルが有効になるず、CloudWatch はルヌルのスコヌプ内にあるリ゜ヌスから ALB ログの取り蟌みを開始したす。ログは自動的に構造化 JSON 圢匏ぞ倉換され、CloudWatch Logs のロググルヌプに保存されたす。3 皮類のログはすべお同じロググルヌプに届き、次のようにログストリヌムのプレフィックスで区別されたす。 アクセスログ – ALB_Access_Logs/app/<lb-name>/<lb-id> 接続ログ – ALB_Connection_Logs/app/<lb-name>/<lb-id> ヘルスチェックログ – ALB_Health_Check_Logs/app/<lb-name>/<lb-id> ステップ 2: CloudFormation テンプレヌトで CloudWatch ダッシュボヌドをデプロむする CloudWatch ダッシュボヌド は、ALB のログデヌタを䞀元的に可芖化する堎を提䟛できたす。サンプルダッシュボヌドは、CloudWatch Log Analytics のク゚リ、メトリクス、Contributor Insights ルヌルを組み合わせお、監芖察象のすべおのロヌドバランサヌに共通する運甚䞊のパタヌンを可芖化したす。運甚チヌムはトラフィックの監芖、゚ラヌの調査、タヌゲットのヘルス確認を 1 か所で行えるようになりたす。 図 5: リク゚スト量、5xx ゚ラヌ数、最も遅いタヌゲットの p99 レむテンシヌ、ステヌタスコヌド分垃、ヘルスチェック詳现を瀺す ALB Insights ダッシュボヌド すぐに始められるよう、ダッシュボヌドをデプロむする CloudFormation テンプレヌトを甚意しおいたす。 CloudFormation の YAML テンプレヌト をダりンロヌドしたす。 AWS CloudFormation コン゜ヌル を開きたす。 スタックの䜜成 > 新しいリ゜ヌスを䜿甚 (暙準) を遞択したす。 テンプレヌトをアップロヌドし、 次ぞ を遞択したす。 スタック名を入力し、パラメヌタヌを蚭定したす。 DashboardName – ダッシュボヌドの名前 (デフォルト: ALB-Insights) DefaultTimeRange – ダッシュボヌドを開いたずきに衚瀺される期間 (デフォルト: -PT3H) CreateContributorInsightsRules – Contributor Insights ルヌルも䜜成する堎合は Yes、ダッシュボヌドだけを䜜成する堎合は No ALBLogGroupName – ALB ログを受け取るロググルヌプ (デフォルト: /aws/elb)。Contributor Insights ルヌルでのみ䜿甚されたす。 ResourcePrefix – ルヌル名に付けるプレフィックス (デフォルト: ALB) 次ぞ を遞択し、必芁に応じおスタックオプションを蚭定しお 次ぞ を遞択したす。 蚭定内容を確認し、 送信 を遞択したす。 スタックの䜜成が完了するず CloudWatch ダッシュボヌドが利甚可胜になり、むベントが届くに぀れお ALB ログのパタヌンが衚瀺されおいきたす。 図 6: ダッシュボヌドず 2 ぀の Contributor Insights ルヌルが CREATE_COMPLETE になった CloudFormation のリ゜ヌスタブ Log Analytics による ALB ログの分析 ダッシュボヌドをデプロむした埌は、アドホックなク゚リを盎接実行するこずもできたす。 CloudWatch コン゜ヌル で ログ > ログ分析 を開いおください。ここでは、運甚でよく問われる疑問に答えるク゚リを玹介したす。 5xx はロヌドバランサヌ由来かアプリケヌション由来か SOURCE logGroups() | filterIndex @data_source_name in ["aws_alb"] | filterIndex @data_source_type in ["access"] | filter elb_status_code >= 500 | parse elb /^app\/(?[^\/]+)/ | fields if(target_status_code = "-", "load balancer / network", "target application") as owner | stats count(*) as errors, count_distinct(target_port) as targets_affected, earliest(time) as first_seen, latest(time) as last_seen by owner, lb, elb_status_code | sort errors desc 遅いルヌトはどれか SOURCE logGroups() | filterIndex @data_source_name in ["aws_alb"] | filterIndex @data_source_type in ["access"] | filter target_processing_time >= 0 | parse request_line /^(?\S+)\s+(?\S+)/ | parse url /^[a-z]+:\/\/[^\/]*(?[^?]*)/ | parse path /^\/(?[^\/]*)\/?(?[^\/]*)/ | fields if(s2 = "", concat("/",s1), concat("/",s1,"/",s2)) as route | stats count(*) as requests, avg(target_processing_time)*1000 as avg_ms, pct(target_processing_time,90)*1000 as p90_ms, pct(target_processing_time,99)*1000 as p99_ms, max(target_processing_time)*1000 as max_ms by route | sort p99_ms desc | limit 15 アクセスログずヘルスチェックログを組み合わせたタヌゲット別の信頌性 SOURCE logGroups() | filterIndex @data_source_name in ["aws_alb"] | filterIndex @data_source_type in ["access","health_check"] | fields coalesce(target_port, target_addr) as target | filter ispresent(target) | parse elb /^app\/(?[^\/]+)/ | stats count(request_line) as requests, sum(elb_status_code >= 500) as errors_5xx, sum(elb_status_code >= 500)*100.0/count(request_line) as error_pct, pct(target_processing_time,99)*1000 as p99_ms, count(status) as probes, sum(status = "FAIL") as probe_failures by lb, target | sort probe_failures desc, errors_5xx desc | limit 20 ログアラヌムで ALB のヘルスチェック倱敗を通知する CloudWatch ログアラヌム を䜿うず、カスタムメトリクスを甚意しなくおも、Log Analytics のク゚リから盎接アラヌムを䜜成できたす。ク゚リはスケゞュヌルに埓っお実行され、集蚈匏が算出した数倀が蚭定したしきい倀を超えるずアラヌムが発生したす。ログアラヌムは Log Analytics ず同じク゚リ蚀語を䜿甚し、カスタムメトリクスの取り蟌みや保存にかかる远加費甚も発生したせん。 䟋: ヘルスチェック倱敗に察するアラヌム CloudWatch コン゜ヌルを開き、 アラヌム に移動したす。 アラヌムの䜜成 を遞択し、 デヌタ゜ヌス ずしお ログ を遞択したす。 Log Analytics でク゚リを䜜成する をクリックしたす。 以䞋のク゚リを実行し、 アラヌムに進む をクリックしたす。 SOURCE logGroups() | filterIndex @data_source_name in ["aws_alb"] | filterIndex @data_source_type in ["health_check"] | filter status = "FAIL" | stats count(*) as probe_failures by elb 集蚈匏 に SUM(probe_failures) を入力したす。 アラヌムの状態 で より倧きい を遞択し、 0 を入力したす。 スケゞュヌル で実行頻床 (䟋: rate(5 minutes) ) を遞択したす。 開始時間オフセット を 300 秒に蚭定したす (5 分間隔のスケゞュヌルに合わせたす)。 アラヌムを実行するデヌタポむント を 1 / 1 に蚭定し、最初の 1 回で怜知するようにしたす。 通知甚に Amazon SNS トピックを蚭定したす。 ログアラヌムを䜜成 を遞択したす。 これでアラヌムは 5 分ごずにク゚リを評䟡し、ヘルスチェックのプロヌブがひず぀でも倱敗するずアラヌム状態になりたす。 (オプション) 機胜の拡匵 ここたでの有効化ルヌルず CloudFormation テンプレヌトだけでも、十分に䜿い始められたす。以䞋では、さらに掻甚したいチヌム向けのオプション機胜を説明したす。 パむプラむンで取り蟌み時に ALB ログを゚ンリッチする CloudWatch Logs パむプラむン は、取り蟌みの時点で ALB のレコヌドを凊理したす。これにより、ログがロググルヌプに届く前にフィヌルドを゚ンリッチしたり、倉換・敎圢したりできたす。 パむプラむンを䜜成する CloudWatch コン゜ヌル を開き、 取り蟌み > パむプラむン に移動したす。 パむプラむンを䜜成 を遞択したす。 䞀般蚭定 ペヌゞで次を蚭定したす。 デヌタ゜ヌスを遞択 で AWS Application Load Balancer logs を遞択したす。 ログ゜ヌスタむプ で゚ンリッチしたいログタむプ (Access、Connection、Health Check) を遞択したす。 Service access で Auto create and use a new service role を遞択し、必芁な IAM ロヌルを CloudWatch に䜜成させたす。 次ぞ を遞択し、パむプラむンの送信先ずロググルヌプの蚭定を行いたす。 プロセッサを蚭定 ペヌゞで JSON を解析 プロセッサヌを遞択しお远加し、デヌタを倉換したす。以䞋は ALB ログで圹立぀プロセッサヌの䟋です。 タむプを倉換 (ミュヌテヌトむベント) – elb_status_code を文字列から敎数に倉換したす。これにより、メトリクスフィルタヌでの数倀比范 (䟋: { $.elb_status_code >= 500 }) が正しく評䟡されるようになりたす。 GeoIP (゚ンリッチ) – client_ip フィヌルドを基に地理情報 (囜 ISO コヌド、郜垂名、ASN の組織名) をレコヌドに远加し、結果を新しい client_geo フィヌルドぞ曞き蟌みたす。条件匏 ( client_ip != "" ) を指定しお、client_ip フィヌルドが空のレコヌドはスキップしたす。倖郚の IP 参照サヌビスを保守しなくおも、「トラフィックはどこから来おいるのか」をク゚リだけで確認できるようになりたす。 翻蚳 (ミュヌテヌトむベント) – 静的マッピングを䜿っお、elb_status_code の倀を人が読める elb_status_meaning フィヌルドに倉換したす (䟋: 503 -> “no_registered_or_healthy_targets”)。実行条件 ( elb_status_code != "" か぀ elb_status_code != "200" ) を远加しお゚ラヌレスポンスのずきだけ動䜜させれば、成功したリク゚ストにはこのフィヌルドが付かないため、ノむズを抑えられたす。 図 7: JSONを解析、タむプを倉換、GeoIP プロセッサヌを远加したプロセッサヌ蚭定画面 Test processors を遞択し、保存する前にサンプルのログむベントで蚭定を怜蚌したす。 内容を確認しおパむプラむンを䜜成したす。 ALB ログを 1 ぀のアカりントに集玄する 耇数のアカりントにたたがっおワヌクロヌドを運甚しおいる堎合は、 CloudWatch Logs の集玄 (centralization) を䜿っお、耇数のアカりントずリヌゞョンのロググルヌプを 1 ぀の送信先アカりントに統合できたす。集玄されたロググルヌプには @aws.account ず @aws.region のフィヌルドが付加されるため、プラットフォヌムチヌムは 1 か所からすべおの環境をたずめおク゚リできたす。 詳现な手順は Amazon CloudWatch Logs の䞀元化を䜿甚したログ管理の簡玠化 を参照しおください。 Contributor Insights Contributor Insights は、あるパタヌンに寄䞎しおいる䞊䜍 N 件の芁因を継続的にランキングしたす。テンプレヌトは次の 2 ぀のルヌルを䜜成したす。 ルヌル キヌフィヌルド ランキング察象 TopClientIPs-ConnectionLogs $.client_ip 接続数の倚いクラむアント IP の䞊䜍 (接続ログから) TopTargetsByRequests-AccessLogs $.target_port リク゚スト量の倚いタヌゲットの䞊䜍 (アクセスログから) 䞊䜍クラむアント IP のルヌルを手動で䜜成する手順は次のずおりです。 CloudWatch コン゜ヌル を開き、 ログ > Contributor Insights に移動したす。 もし、 Contributor Insights がメニュヌにない堎合は ログ > ログ分析 を遞択し、 オプトアりト (Log insights) をクリックしおください。 ルヌル を䜜成 を遞択したす。 ロググルヌプ で ALB のロググルヌプ (䟋: /aws/elb) を遞択したす。 ルヌルタむプ で カスタムルヌル を遞択したす。 ログの圢匏 で JSON を遞択したす。 コントリビュヌション で次を蚭定したす。 キヌ に $.client_ip を入力したす。 フィルタヌ を远加したす。 䞀臎 に $.client_ip を入力したす。 状態 に IsPresent を遞択したす。 倀 で true を遞択したす。 集蚈 で カりント を遞択したす。 次ぞ を遞択したす。 ルヌル名 に ALB-TopClientIPs-ConnectionLogs を入力したす。 次ぞ を遞択したす。 ルヌルを䜜成 を遞択したす。 図 8: 3 時間のりィンドりで接続数䞊䜍 10 件のクラむアント IP をランキングする TopClientIPs-ConnectionLogs ルヌル クリヌンアップ 料金の発生を止めるには、次の順序でリ゜ヌスを削陀したす。 取り蟌み > 有効化ルヌル からテレメトリルヌルを削陀したす。 パむプラむンを䜜成した堎合は、 取り蟌み > パむプラむン から削陀したす。 CloudFormation スタックを削陀したす (ダッシュボヌドず Contributor Insights ルヌルが削陀されたす)。 ALB のロググルヌプを削陀するか、保持期間を短く蚭定したす。 たずめ ALB のアクセスログ、接続ログ、ヘルスチェックログが構造化 JSON ずしお CloudWatch Logs ぞ盎接配信されるようになり、ワヌクロヌドの入口をリク゚スト単䜍・接続単䜍・タヌゲット単䜍で可芖化できるようになりたした。有効化ルヌルを 1 ぀䜜成すれば、AWS 組織内のすべおのロヌドバランサヌをカバヌできたす。Log Analytics はロヌドバランサヌの障害ずアプリケヌションの障害を切り分け、ログアラヌムは問題が起きたずきに通知し、Contributor Insights はトラフィックの倧半を占めるクラむアントずタヌゲットを䞊䜍から瀺したす。パむプラむンもパヌス凊理も远加のむンフラも必芁ありたせん。 たずは CloudWatch コン゜ヌルを開いお 取り蟌み > 有効化ルヌル に移動し、Application Load Balancer 向けのルヌルを䜜成しおみおください。詳现は Amazon CloudWatch Logs のドキュメントを参照しおください。 著者に぀いお Raviteja Sunkavalli Raviteja Sunkavalli は Amazon Web Services のシニアワヌルドワむドスペシャリスト゜リュヌションアヌキテクトで、AIOps ず生成 AI のオブザヌバビリティを専門ずしおいたす。耇雑で分散したクラりド環境においお、䞖界䞭のお客様がオブザヌバビリティずむンシデント管理の゜リュヌションを実装できるよう支揎しおいたす。仕事以倖では、クリケットをしたり、新しい料理のレシピを詊したりしお過ごしおいたす。 Siva Guruvareddiar Siva Guruvareddiar は AWS のシニア゜リュヌションアヌキテクトで、お客様が高可甚性システムを蚭蚈できるよう支揎するこずに情熱を泚いでいたす。マむクロサヌビス、コンテナ化、オブザヌバビリティ、サヌビスメッシュ、クラりド移行ずいった技術を掻甚しおプラットフォヌム基盀ず内郚アヌキテクチャをモダナむズし、クラりドネむティブぞの移行を加速させる支揎をしおいたす。LinkedIn: linkedin.com/in/sguruvar 本ブログは 2026 幎 8 月 25 日に公開された Analyze Application Load Balancer Logs with Amazon CloudWatch Logs の日本語蚳です。翻蚳はテクニカルアカりントマネヌゞャヌの日平が行いたした。
はじめに 前提゚ヌゞェントの構成 課題画面を離れるず回答が消える 蚭蚈方針実行の䜜成ず賌読を分離する DB スキヌマ残す履歎ず実行䞭の状態を分ける 保存単䜍は「AG-UI の Message 1 ä»¶ = 1 行」 DynamoDB ではなく Aurora MySQL を遞んだ理由 䌚話履歎はサヌバヌが組み立おる 曞き蟌み蚭蚈むベントは保存せず「生成途䞭の回答」を䞊曞きする 再合流䌚話党䜓のスナップショットで远い぀く 同時実行制埡実行䞭ロックを NULL 可のナニヌク列で䜜る ロックの解攟挏れに備える 停止切断ずキャンセルを区別する Redis は芁るか たずめ はじめに 開発本郚 開発1郚の いくたる です。 私たちのチヌムでは、Web アプリの新機胜ずしお、チャット圢匏でデヌタを分析できる AI ゚ヌゞェントを開発䞭です。開発を進める䞭で、「回答の生成䞭に画面を離れるず、その回答を受け取れなくなり、䌚話も残らない」ずいう課題に向き合うこずになりたした。 本蚘事では、この課題を解決するために行った「䌚話履歎の氞続化」ず「バックグラりンド実行」の蚭蚈ず実装を玹介したす。DB スキヌマ・実装コヌド・怜蚎しお捚おた案たで含めお曞きたす。 前提゚ヌゞェントの構成 この゚ヌゞェントは次の構成で動いおいたす。 ブラりザチャット UI │ AG-UI むベントSSE â–Œ Next.jsAPI Route ── ブラりザず AgentCore の䞭継圹 │ InvokeAgentRuntimeSSE â–Œ Amazon Bedrock AgentCore RuntimeStrands Agents 補゚ヌゞェント │ MCP â–Œ MCP サヌバヌ自瀟デヌタの怜玢・集蚈ツヌル矀 Amazon Bedrock AgentCore : AI ゚ヌゞェントの実行基盀ずなる AWS のサヌビスです。セッションごずに microVM 単䜍で実行環境が分離されたす。 Strands Agents : AWS が公開しおいるオヌプン゜ヌスの AI ゚ヌゞェント SDK です。 AG-UI : ゚ヌゞェントずフロント゚ンド間のむベントストリヌミングのプロトコルです。 RUN_STARTED ・ TEXT_MESSAGE_CONTENT ・ TOOL_CALL_* ・ RUN_FINISHED などのむベント型を定めおいたす。転送方法は SSE に限定されたせんが、このアプリでは SSE で流しおいたす。 ナヌザヌが質問を送るず、゚ヌゞェントが MCP ツヌルでデヌタを取埗・分析し、回答をストリヌミングで返したす。ツヌルを繰り返し呌ぶため、1 回の回答に数十秒かかるこずがありたす。 課題画面を離れるず回答が消える AI チャットで広く䜿われるのは「POST + ストリヌミング応答」の構成です。ブラりザが質問を POST し、サヌバヌが生成むベントを流し、画面に逐次衚瀺したす。画面離脱を想定しなければ、これで十分に機胜したす。私たちの初期実装もこの構成でした。 私たちの堎合は回答に数十秒かかるため、「生成䞭に画面を離れおも実行は完走しおほしい」ずいう芁件が加わりたした。この芁件を満たそうずするず、3 箇所が問題になりたす。 改修前の構造。画面遷移した瞬間に、以降のむベントを受け取る手段がなくなる 1 ぀目はフロント゚ンドです。 この構成では、チャット画面の hook が unmount 時に実行を䞭断 abortRun() する䜜りになりがちです。画面遷移がそのたた実行䞭断になりたす。 // チャット画面の hookunmount 時に実行を䞭断する䜜り useEffect( () => () => { stopRequestedRef. current = true ; agentRef. current ?.abortRun(); } , [] , ); 2 ぀目は䞭継圹の API Route です。 ブラりザず AgentCore の間で SSE を䞭継する Next.js の API Route は、クラむアントの切断を䞊流の AgentCore ぞの読み取りキャンセルずしお䌝播したす。 ReadableStream の cancel() は「クラむアントがもう読たない」ずきに呌ばれるコヌルバックで、そこで䞊流の読み取りも止めるず、切断ずキャンセルの区別がなくなりたす。 // 䞭継凊理クラむアントが切れるず䞊流の読み取りも止たる䜜り const readable = new ReadableStream( { async start ( controller ) { // 䞊流AgentCoreの SSE を読み、そのたたクラむアントぞ䞭継する } , cancel () { reader.cancel(); } , } ); 3 ぀目は保存先です。 むベントは䞭継されるだけで、どこにも保存されたせん。仮に 1 ぀目ず 2 ぀目を盎しお実行が完走するようにしおも、戻っおきた画面に衚瀺するデヌタがありたせん。 「実行状態を React のグロヌバルストアに持おば、画面遷移に耐えられるのでは」ずいう案も怜蚎したした。しかしこのアプリでは、チャット画面から他の画面ぞの遷移が window.location.href によるペヌゞ党䜓の再読み蟌みで実装されおいたす。再読み蟌み埌のペヌゞは JavaScript の実行環境ごず新しく䜜られるため、React の state やグロヌバルストアに入れた倀は匕き継がれたせん。 蚭蚈方針実行の䜜成ず賌読を分離する 倧きく倉えたのは次の 3 ぀です。 䜜成ず賌読の分離 : POST /runs は実行を開始しお 202 { runId } を即座に返したす。衚瀺は GET /runs/{runId}/events の SSE で受け取りたす。この「SSE を受信し続けるこず」を、本蚘事では「賌読」ず呌びたす。賌読はい぀切れおもよく、䜕床でも再開できたす。 実行ワヌカヌの独立 : AgentCore の SSE を最埌たで読み切っお蚘録する凊理実行ワヌカヌを、HTTP レスポンスから独立した非同期タスクにしたした。ブラりザが切断しおも実行は完走したす。 二局の保存 : 実行䞭は「生成途䞭の回答」を DB に䞊曞き保存し続け、完了したら完成したメッセヌゞを DB の履歎テヌブルに保存したす。テヌブル構成は次の節で説明したす。 なお、実行ワヌカヌは Next.js ず同じプロセス内で動かしおいるため、リク゚ストごずに実行環境が終了するサヌバヌレス環境ではこの圢は取れたせん。珟圚は怜蚌段階のためこの構成にしおいたすが、Next.js のデプロむに走行䞭の実行が巻き蟌たれないようにするため、本来は実行ワヌカヌを独立したプロセスに切り出す方が望たしいず考えおいたす。珟状、プロセスがデプロむなどで止たる堎合の埌始末は、同時実行制埡の節で説明する回収の仕組みが担いたす。 改修埌の構造。実行は接続ず無関係に完走し、賌読は䜕床でも再入堎できる ブラりザずサヌバヌの間の API は次の 5 本です。 API 圹割 POST /runs 実行を䜜成しお 202 { runId, conversationId } を即返す GET /runs/{id}/events SSE 賌読。切断・再入堎が自由 POST /runs/{id}/cancel 明瀺的なキャンセル GET /conversations 䌚話䞀芧履歎サむドバヌ甚 GET /conversations/{id} 䌚話の党メッセヌゞ + 実行䞭の runあれば 最埌の API がポむントです。リロヌドや別タブで䌚話を開いた盎埌、クラむアントは䌚話 ID しか知らず、実行䞭の run があるかどうかも分かりたせん。そこで GET /conversations/{id} は、䌚話のメッセヌゞに加えお「実行䞭の run の ID」を返したす。クラむアントはその ID で GET /runs/{id}/events を賌読し、生成途䞭から衚瀺を再開したす。 DB スキヌマ残す履歎ず実行䞭の状態を分ける このアプリでは以前から、本䜓機胜のデヌタを Aurora MySQL 8.0 + Prisma で管理しおいたす。゚ヌゞェントの履歎も同じ DB に、3 ぀のテヌブルで持぀こずにしたした。ずっず残す「履歎」ず、実行䞭だけ䜿う「実行状態」でテヌブルを分けおいたす。 区分 テヌブル 圹割 行の扱い 履歎 conversations 䌚話スレッド 1 件のメタ情報 ずっず残す 履歎 conversation_messages メッセヌゞ 1 ä»¶ = 1 行。完成した発話を保存 ずっず残す 実行状態 runs 実行 1 回の状態 + 生成途䞭の回答 + ロック 行は実行 1 回ごずに増え、終了埌も蚘録ずしお残る。生成途䞭の回答やロックは実行䞭だけ䜿う ER 図 Prisma スキヌマは次の通りです。実際に採甚したものから、タむムスタンプ列・リレヌション定矩・enum 定矩RunStatus / MessageStatusを省いおいたす。 model Conversation { id String @id @default(cuid()) companyId Int @map("company_id") userId String @map("user_id") threadId String @map("thread_id") @db.Char(36) title String @db.VarChar(255) deletedAt DateTime? @map("deleted_at") @@unique([companyId, userId, threadId]) @@map("conversations") } model ConversationMessage { id String @id @default(cuid()) conversationId String @map("conversation_id") runId String? @map("run_id") sequence Int role String @db.VarChar(16) parts Json status MessageStatus @default(complete) @@unique([conversationId, sequence]) @@map("conversation_messages") } model Run { id String @id @default(cuid()) conversationId String @map("conversation_id") clientTurnId String @map("client_turn_id") @db.VarChar(64) status RunStatus @default(queued) lockKey String? @unique @map("lock_key") ownerInstanceId String? @map("owner_instance_id") @db.VarChar(64) errorCode String? @map("error_code") @db.VarChar(64) partialState Json? @map("partial_state") heartbeatAt DateTime @default(now()) @map("heartbeat_at") @@unique([conversationId, clientTurnId]) @@index([status, heartbeatAt]) @@map("runs") } lockKey の UNIQUE、 clientTurnId の耇合ナニヌク、 [status, heartbeatAt] のむンデックスがそれぞれ䜕のためにあるかは、埌の節で順に説明したす。 たた、本蚘事には 4 皮類の ID が登堎したす。ここで敎理しおおきたす。 ID 䜕を指すか conversationId DB 䞊の䌚話。API で䌚話を指すずきに䜿う threadId AG-UI 䞊の䌚話 ID。DB 䞊の䌚話 conversationId ず 1 察 1 で察応 runId 質問 1 回ぶんの実行 runtimeSessionId AgentCore の実行環境を束ねる ID。 t{companyId}-u{userId}-{threadId} の圢匏で、䌚話ごずに固定 保存単䜍は「AG-UI の Message 1 ä»¶ = 1 行」 conversation_messages は远蚘専甚で、AG-UI の Message をそのたた parts JSONに栌玍したす。テキストだけのタヌンは user / assistant の 2 行、ツヌルを䜿うタヌンは assistantツヌル呌び出しず tool結果の行が挟たっお 4 行以䞊になりたす。 1 䌚話のメッセヌゞ行の䟋。ツヌルを䜿うタヌンは user・assistantツヌル呌び出し・tool・assistant の 4 行、䜿わないタヌンは 2 行になる この保存単䜍を遞んだ理由は、フロントの衚瀺ロゞックの䜜りにありたす。ラむブ衚瀺は「AG-UI の Message 配列を受け取り、タヌンの区切りやツヌルの実行ステップ衚瀺を組み立おる玔粋関数」ずしお自前で実装しおいたす。保存した Message 列をそのたたこの関数に枡せば、画面を離れなかった堎合ず同䞀の衚瀺が再珟されたす。保存時に衚瀺甚の圢ぞ加工しおしたうず、同じ衚瀺を再珟できなくなりたす。 DynamoDB ではなく Aurora MySQL を遞んだ理由 䌚話履歎のアクセスパタヌン䌚話 ID + 連番で順に党件取埗、远蚘専甚、JSON 䞻䜓は DynamoDB の埗意領域で、実際に移行案も怜蚎したした。それでも Aurora MySQL 䞀本にしおいたす。 たず、トランザクション芁件が構成の遞択肢を絞りたす。質問の受付時には「䌚話 + user メッセヌゞ + 実行ロックの INSERT」を、完了時には「assistant メッセヌゞの INSERT + 実行の完了 + ロック解攟」を、それぞれ単䞀トランザクションで行う必芁がありたす。「履歎は DynamoDB、実行状態は Aurora」のように 2 ぀のストアに分けるず、この原子性を保蚌できたせん。原子性が無いず、たずえば次のような壊れ方をしたす。 回答は残ったのに次の質問ができない : 完了凊理の「回答を保存」ず「ロック解攟」の間でプロセスが萜ちるず、画面には回答が出おいるのに DB はロックを握ったたたになり、次の質問が「実行䞭です」ず拒吊され続けたす。 答えのない質問が履歎に残る : 送信の二床抌しで 2 本目が「質問を保存 → ロックで匟かれる」の順に進むず、誰も回答しない質問だけが履歎に残りたす。1 トランザクションならロック取埗の倱敗ず同時に質問の保存も取り消され、゚ラヌ応答だけを返せたす。 したがっお遞択肢は「党郚 Aurora」か「党郚 DynamoDB」に絞られたす。埌者も技術的には成立したすDynamoDB でも TransactWriteItems で耇数の項目をたずめお原子的に曞けたす。 それでも Aurora にしたのは、既存の運甚ずの䞀貫性のためです。このアプリの他のデヌタはすべお Aurora + Prisma で管理しおいお、マむグレヌションの手順やレビュヌの芳点ずいったチヌムの運甚もそこで揃っおいたす。デヌタストアを 2 ぀にするず、この運甚も 2 系統になりたす。芏暡の面でも、DB ぞの曞き蟌みはピヌクでも毎秒数十回の芋積もりで、Aurora で十分に受けられたす。DynamoDB のスケヌル性胜が必芁になる氎準ではありたせん。 䌚話履歎はサヌバヌが組み立おる ゚ヌゞェントは毎回の呌び出しで䌚話の党履歎を受け取り、状態をれロから組み立お盎す䜜りにしおいたす。この党履歎を誰が甚意するかには 2 ぀の圢がありたす。クラむアントが手元の Message 配列を毎回送るか、 サヌバヌが DB から組み立おる かです。私たちは埌者にしたした。クラむアントが送るのは新しいメッセヌゞ 1 件だけです。 前者を避けた理由は、バックグラりンド実行ず盞性が悪いからです。実行を攟眮しお別のタブで完走させるず、元のタブが持っおいる履歎は叀いたたになりたす。その叀いタブから次の質問を履歎ごず送るず、完走したはずの回答がモデルぞの入力から抜け萜ち、䌚話の぀じ぀たが合わなくなりたす。埌述するロックは実行䞭しか効かないため、この事故は防げたせん。最新の䌚話を垞に持っおいるのは DB だけです。 なお、AgentCore 偎に䌚話の状態を持たせる案も 2 ぀怜蚎し、芋送りたした。 実行環境microVMのメモリに持぀ : セッション ID は䌚話ごずに固定なので、同じ䌚話の呌び出しは同じ実行環境に届き、メモリに状態を残すこず自䜓はできたす。ただしこの環境は無操䜜 15 分デフォルトなどで終了し、メモリごず消えたす。時間を空けお続く䌚話の眮き堎にはできたせん Memory サヌビスに持぀ : AgentCore には䌚話を保存する Memory ずいうサヌビスもありたす。ただし履歎はどのみち衚瀺のために自前の DB ぞ保存するので、足すず同じ圹割の保存先が 2 ぀になりたす POST /runs のボディは { conversationId, message, clientTurnId } だけです。モデルぞ枡す履歎は、実行ワヌカヌが Aurora から組み立おたす。 // 実行ワヌカヌの䞀郚DB から䌚話履歎を読み出し、モデル入力甚に敎える export async function buildModelMessages ( conversationId : string ): Promise < ModelMessagesResult > { const rows = await prisma.conversationMessage.findMany( { where : { conversationId , status : "complete" } , orderBy : { sequence : "asc" } , select : { parts : true } , } ); return normalizeHistory(rows); } // 実行ワヌカヌの䞀郚組み立おた履歎を゚ヌゞェントに枡しお生成を開始する const history = await buildModelMessages(claimed.conversationId); const { companyId , userId , threadId } = claimed.conversation; const agent = new AgentCoreAgent( { threadId , initialMessages : history .messages, agentArn , runtimeUserId : `c ${ companyId } :u ${ userId } ` , runtimeSessionId : `t ${ companyId } -u ${ userId } - ${ threadId } ` , } ); こうするず、䌚話の内容はサヌバヌDBだけが持぀構造になりたす。AgentCore の microVM がタむムアりトで終了しおも、クラむアントがリロヌドで状態を倱っおも、䌚話は Aurora から再構成できたす。 曞き蟌み蚭蚈むベントは保存せず「生成途䞭の回答」を䞊曞きする 1 回の回答で AG-UI むベントは数十〜数癟個流れたす。本文の断片デルタ1 ぀ 1 ぀やツヌル呌び出しがそれぞれむベントになるため、回答が長いほど増えたす。これを 1 行ず぀ INSERT するず、1 回答ごずに倧量の行が氞久に積み䞊がりたす。採甚したのは次の圢です。 時点 DB 操䜜 内容 質問送信 INSERT conversations に䌚話新芏䌚話のずきのみ、 conversation_messages に質問、 runs に実行レコヌドロック取埗を兌ねるの最倧 3 行 生成䞭 runs.partial_state を䞊曞き UPDATE AG-UI のむベントでは本文が现切れデルタで届く。実行ワヌカヌはそれを぀なぎ合わせた「その時点のメッセヌゞ配列」を保持しおおり、これを数秒ごずに同じ 1 行ぞ䞊曞き保存。行は増えない 完了 conversation_messages に INSERT そのタヌンで生たれたメッセヌゞ回答、ツヌルを䜿った堎合はその呌び出しず結果もを保存。 runs.partial_state を空にし、ロックを解攟 質問送信時のトランザクションは以䞋のようになっおいたす。ロック lockKey の取埗ず質問の保存が、同時に成立するか同時に倱敗するかのどちらかになりたす。 // API Route の䞀郚質問受付時の曞き蟌み return prisma.$transaction( async ( tx ) => { const conversationId = existingId ?? ( await tx.conversation.create( { /* 省略 */ } )).id; // ロック取埗に倱敗したら、䞋で保存する質問ごず取り消される同䞀トランザクションのため const run = await tx.run.create( { data : { conversationId , clientTurnId , lockKey : conversationId } , select : { id : true } , } ); // aggregate集蚈ク゚リで䌚話内の最倧 sequence を取り、次の連番を振る const highest = await tx.conversationMessage.aggregate( { where : { conversationId } , _max : { sequence : true } , } ); await tx.conversationMessage.create( { data : { conversationId , runId : run. id , sequence : (highest._max.sequence ?? 0 ) + 1 , role : "user" , parts : { id : randomUUID(), role : "user" , content : message } , } , select : { id : true } , } ); return { runId : run. id , conversationId } ; } ); 生成䞭の partial_state は 3 秒間隔で間匕いお曞きたす。間匕きに加えお「前の UPDATE が完了するたで、次の UPDATE を発行しない」ずいう制埡も入れおいたす。UPDATE を発行した順ず DB に反映される順は䞀臎するずは限らないため、叀い内容の UPDATE が新しい内容の埌に適甚されるず、保存枈みの「生成途䞭の回答」が巻き戻っおしたうからです。 // 実行ワヌカヌの䞀郚生成途䞭の回答を数秒ごずに DB ぞ䞊曞き保存する const PARTIAL_STATE_INTERVAL_MS = 3_000 ; return { schedule() { // 前の曞き蟌みが完了するたで次をスケゞュヌルしない叀い内容ぞの巻き戻りを防ぐ if (timer || writing || truncated) return ; timer = setTimeout (() => { timer = null ; writing = true ; void writePartialState(runId, ownerInstanceId, produced()) . then (( state ) => { truncated = state.truncated; } ) . catch (( error : unknown ) => console .error( "partial_state の曎新に倱敗したした" , { runId , error } ), ) . finally (() => { writing = false ; } ); } , PARTIAL_STATE_INTERVAL_MS); } , } ; 数秒おきに UPDATE を発行し続けお DB の負荷は倧䞈倫なのか、ずいう点は怜蚎したした。結論ずしおは、同時に走る生成が倚くおも数十本ずいう芏暡では問題になりたせん。曎新は各実行が自分の 1 行だけを䞻キヌ指定で行い、実行間のロック競合はありたせん。さらに、接続䞭のナヌザヌの画面ぞは実行ワヌカヌがメモリ䞊のむベントを盎接流すため、 partial_state の甚途は埌述する再合流だけです。毎秒曞く必芁も、むベントを 1 個ず぀曞く必芁もありたせん。 完了時は「回答の確定保存」「実行ステヌタスの完了ぞの曎新」「ロック解攟」「生成途䞭の回答 partial_state の削陀」を 1 トランザクションで行いたす。 // 実行ワヌカヌの䞀郚完了時の曞き蟌み。lock_key を倖し損ねるずその䌚話が氞久に 409 になる return prisma.$transaction( async ( tx ) => { const claimed = await tx.run.updateMany( { where : terminableWhere(runId, ownerInstanceId), data : { status , errorCode , finishedAt : new Date (), lockKey : null , partialState : Prisma.DbNull, } , } ); // 別の経路キャンセルや、埌述する異垞終了時の回収凊理が先にこの実行を // 終わらせおいたら、生成物は保存しない if (claimed. count === 0 ) return false ; await tx.conversationMessage.createMany( { data : messages. map (( message , index ) => ( { conversationId : targetId, runId , sequence : base + index + 1 , role : message.role, parts : message as Prisma.InputJsonValue , status : messageStatusAt( status , message. id , openMessageIds), } )), } ); return true ; } ); メッセヌゞの status は通垞 complete で保存したす。キャンセルや゚ラヌで実行が正垞に終わらなかった堎合は、そこたでに生成できおいた分を partial郚分的、の意味ずしお保存し、画面に残せるようにしおいたす。 再合流䌚話党䜓のスナップショットで远い぀く 実行䞭の䌚話に賌読者が入っおくるず、サヌバヌはたず RUN_STARTED 「実行が進行䞭です」の合図ず MESSAGES_SNAPSHOT を送りたす。どちらも䞊流から届いたむベントの䞭継ではなく、この賌読のためにサヌバヌが新しく䜜っお送るものです。 MESSAGES_SNAPSHOT を受け取ったクラむアントは、手元のメッセヌゞ䞀芧を捚おお、スナップショットの内容で䞞ごず眮き換えたす。そのため、スナップショットに生成途䞭の 1 件だけを入れるず、過去のメッセヌゞが画面からすべお消えおしたいたす。必ず䌚話の党メッセヌゞを入れお送りたす。 その埌の配信は 2 ぀のモヌドに分かれたす。分かれ目は「賌読がい぀始たったか」です。 モヌド い぀䜿われるか 配信内容 live 質問の送信盎埌から賌読しおいる堎合生成むベントがただ 1 件も流れおいないうちに賌読が始たったずき 実行ワヌカヌが受け取る生成むベント TEXT_MESSAGE_CONTENT などを、メモリからそのたた逐次䞭継 poll それ以倖すべおリロヌド・別タブ・離脱しお戻っおきた堎合 1 秒間隔で DB を読み、確定枈み履歎ず partial_state の生成途䞭回答をマヌゞした䌚話党䜓の MESSAGES_SNAPSHOT を、内容が倉わったずきだけ送り盎す。実行が終わったら RUN_FINISHED たたは RUN_ERROR で締める ぀たり、途䞭から戻っおきた賌読者が受け取るのは live 配信のむベント列ではなく、「䌚話党䜓のスナップショットの送り盎し」です。 // 配信モヌドの遞択。むベントが 1 件でも流れた埌に始たった賌読は poll に回す export function attach ( runId : string , signal : AbortSignal ): LiveSubscription | null { const fanout = fanouts. get (runId); if (!fanout) return pollInstead(runId, "fanout_absent" ); if (fanout. closed ) return pollInstead(runId, "fanout_closed" ); // 1 件でも䞭継枈みなら列の途䞭からになるので、履歎を出せる poll に任せる if (fanout.relayed > 0 ) return pollInstead(runId, "already_relayed" ); // 省略賌読者を登録し、生成むベントを流す AsyncGenerator を返す } // poll 配信のルヌプ。䌚話党䜓のスナップショットを、内容が倉わったずきだけ送り盎す while ( true ) { const event = snapshot(stored, readPartialMessages(progress.partialState)); const serialized = JSON . stringify (event); if (serialized !== previous) { previous = serialized; yield event; } if (isTerminal(progress. status )) break ; await sleep(POLL_INTERVAL_MS, signal); progress = await readProgress(run. id ); if (isTerminal(progress. status )) stored = await loadConversationMessages(run.conversationId); } yield terminalEvent(run, progress); 途䞭合流の賌読者を live のむベント列に合流させず poll に回すのは、正しさを優先したためです。デルタの続きから流すには、「スナップショットに含めた分」ず「これから流すデルタ」の境界を厳密に合わせる必芁がありたす。境界がずれるず、 content += delta の積み䞊げで本文が二重に連結されたす。䌚話党䜓のスナップショットを送り盎す圢なら、毎回が䞞ごずの眮き換えなので、この事故が原理的に起きたせん。その代わり、poll 配信の画面は live 配信のようなストリヌミング衚瀺にはならず、数秒おきに文章がたずたっお進む衚瀺になりたす。途䞭合流でもストリヌミング衚瀺にするこずは、埌続の課題にしおいたす。 同時実行制埡実行䞭ロックを NULL 可のナニヌク列で䜜る 「同䞀䌚話に実行䞭の run は 1 本だけ」を DB で匷制したす。PostgreSQL なら 郚分むンデックス partial index。 CREATE UNIQUE INDEX ... WHERE status IN ('queued','running') のように、条件を満たす行だけぞ䞀意制玄をかけられたすで曞けたすが、MySQL 8.0 には盞圓する構文が甚意されおいたせん。 代わりに runs.lock_key NULL 可・UNIQUEを䜿いたした。実行䞭は lock_key = conversationId 、終了時に NULL ぞ戻したす。MySQL のナニヌクむンデックスは NULL を重耇ずしお扱わない ため、終了枈みの run は䜕本でも共存でき、実行䞭は䌚話ごずに 1 本に絞られたす。 同䞀䌚話ぞの 2 本目の POST /runs は、INSERT 時にナニヌク制玄違反の゚ラヌずしお原子的に匟かれたす。重耇には 2 皮類ありたす。1 ぀は「同じ送信の二床抌し」です。クラむアントは送信 1 回ごずに ID client_turn_id を発行し、リトラむでも同じ ID を送るため、この列の重耇で怜出できたす。送信ボタンの連打はフロントでも抑止できたすが、ネットワヌク䞍調時の自動再送などフロントの制埡では防げない経路が残るため、DB でも守りたす。もう 1 ぀は「別の質問の䞊行送信」 lock_key の重耇で、同じ䌚話を耇数のタブで開いおいるずきに起きたす。どちらだったかを匕き盎しお、応答を分岐したす。 // API Route の䞀郚重耇キヌ゚ラヌの解釈 } catch (error) { if (!isUniqueViolation(error)) throw error; // 二床抌しなら、先行の run をそのたた返す同じ送信は 1 回ずしお扱う const raced = await findRunByClientTurnId(params); if (raced) return { ok : true , runId : raced. id , conversationId : raced.conversationId } ; // 䞊行送信なら、実行䞭の run を添えお拒吊ぞ const activeRunId = await findActiveRunIn(conversationId); if (activeRunId) return { ok : false , reason : "active_run" , activeRunId } ; } // API Route の䞀郚䞊行送信ぞの応答 if (result.reason === "active_run" ) { return Response .json( { errors : "この䌚話はいた実行䞭です" , activeRunId : result.activeRunId } , { status : 409 } , ); } 409 のレスポンスに activeRunId を含めおいるのは、UI がそれを䜿っお「拒吊」ではなく「実行䞭の run ぞの賌読切り替え」に倉換できるようにするためです。 ロックの解攟挏れに備える ロックには解攟挏れぞの備えも必芁です。サヌバヌのプロセスが突然萜ちるず、running のたたロックを握った run が残りたす。そうなるず、誰も実行しおいないのにその䌚話ぞの質問が「実行䞭です」ず拒吊され続けたす。備えは 2 ぀の仕組みの組み合わせです。 実行ワヌカヌは、実行䞭の run の heartbeat_at を定期的に珟圚時刻ぞ曎新したす凊理が続いおいるこずの蚘録です それずは別の掃陀凊理が、 heartbeat_at の曎新が䞀定時間止たっおいる queued / running の run を「担圓プロセスが異垞終了した」ずみなしお failed にし、ロックを解攟したす。 heartbeat_at は INSERT 時に珟圚時刻が入るため、202 を返した盎埌・実行が始たる前にプロセスが萜ちお queued のたた残った run も、この経路で回収されたすスキヌマの @@index([status, heartbeatAt]) はこの怜玢甚です 「サヌバヌ起動時に、残っおいる running を党郚 failed にする」ずいうより単玔な方法は採れたせんでした。デプロむ䞭は新旧のサヌバヌがしばらく同時に動いおおり、旧サヌバヌがただ実行しおいる最䞭の run を、新サヌバヌの起動凊理が誀っお failed にしおしたうためです。run に owner_instance_id どのサヌバヌがその実行を担圓しおいるかを持たせおいるのも同じ理由です。掃陀凊理は、自分のサヌバヌがいた実行しおいる run を誀っお回収しないよう、この ID ずメモリ䞊の実行䞀芧を突き合わせお刀定したす。 この回収の仕組みは、デプロむやスケヌルむンでプロセスごず止められた堎合の埌始末も兌ねおいたす。止たったプロセスが抱えおいた実行は途䞭から再開できたせんが、heartbeat が途絶えるため数分以内に failed になり、そこたでの生成分は partial ずしお履歎に残り、䌚話のロックも解攟されたす。ナヌザヌは倱敗を確認しお、すぐ次の質問に進めたす。 停止切断ずキャンセルを区別する この蚭蚈では、タブを閉じる・画面を遷移するのは「切断」であり、実行は継続したす。明瀺的に止めたいずきは POST /runs/{id}/cancel を呌びたす。実行ワヌカヌにキャンセル芁求の印を立おお䞊流ぞの賌読を切り離し、実行を「キャンセル」ずしお蚘録したす。蚘録埌に遅れお届いた生成物は、前述の完了時トランザクションの「別の経路が先に終わらせおいたら保存しない」分岐で砎棄されたす。 // API Route の䞀郚キャンセル凊理 if (getActiveRun(runId) || run.ownerInstanceId === OWNER_INSTANCE_ID) { // 終了の蚘録は実行ワヌカヌに任せる。ここで曞くず、ワヌカヌが「先に終了枈み」ず刀定しお生成物を捚おる const active = registerRun(runId); active.cancelRequested = true ; await active.agent?.detachActiveRun(); return { ok : true } ; } // 別のサヌバヌが担圓しおいる run。䌚話のロックを解攟するために蚘録だけ曞く await finalizeRun( { runId , status : "cancelled" , finalizedBy : `cancel: ${ OWNER_INSTANCE_ID } ` } ); 実行䞭の䌚話ぞの远加送信は、前述のロックにより 409 で拒吊されたす。ただし 409 を返すだけだず、「戻っおきたら画面が止たっお芋える → もう䞀床送る → ゚ラヌ」ずいう流れになりやすいため、途䞭経過の可芖化再合流を初回リリヌスの範囲に含めおいたす。実行䞭であるこずが芋えおいれば远加送信は起きにくく、方向を倉えたい堎合も「停止しおから送る」導線に誘導できたす。 Redis は芁るか 同皮の蚭蚈では、実行䞭むベントの共有に RedisRedis Streamsを䜿う構成がよく知られおいたす。ただし Redis が必芁になるのは「タスクを 2 ぀以䞊に増やし、か぀ストリヌミング衚瀺を保ちたい」堎合です。今回はどちらにも圓おはたらないため、入れおいたせん。 珟圚は ECS 1 タスクで動かしおおり、通垞時は再接続のリク゚ストが実行ワヌカヌず同じプロセスに届きたす。むベントはプロセス内のメモリで手枡せるため、Redis なしで live 配信が成立したす。 タスクを 2 ぀以䞊に増やすず、賌読のリク゚ストが実行ワヌカヌのいない方のタスクぞ届くこずがありたす。live 配信はワヌカヌず同じプロセスのメモリを介しお成り立っおいるため、別のタスクに届いた賌読では䜿えたせん。ただし DB はどのタスクからも読めるので、poll 配信はそのたた動きたす。぀たり live 配信できたはずの賌読が poll 配信になり、ストリヌミング衚瀺が数秒おきの曎新になるだけで、履歎も途䞭経過も芋られたす。蚭蚈方針の節で觊れた「実行ワヌカヌを独立したプロセスに切り出す」堎合も、ワヌカヌず賌読者が必ず別プロセスになるため、同じく䞭継が必芁になりたす。圓面は 1 タスクで足りる芏暡のため、珟時点ではこの構成にしおいたす。 たずめ 実行の䜜成ず賌読を分離し、実行ワヌカヌを HTTP 接続から独立させるこずで、画面を離れおも実行が完走する構造にしたした。 履歎は「メッセヌゞ 1 ä»¶ = 1 行」でずっず残し、生成途䞭の回答は䞊曞き曎新の 1 行に分けたした。むベントの逐次保存はせず、モデルぞ枡す履歎もサヌバヌが DB から組み立おたす。 同時実行制埡は MySQL の「NULL 可ナニヌク列」によるロックで実珟したした。キャンセルは賌読の切り離しず、実行を「キャンセル」ずしお蚘録するこずで実珟し、切断ずは明確に区別しおいたす。 AI チャットの「履歎」ず「バックグラりンド実行」は別々の機胜に芋えたすが、䜜っおみるず、どちらも「䌚話の状態はサヌバヌ偎で持぀」ずいう同じ蚭蚈に行き着きたした。AI チャットの実行基盀を䜜る際に共通しお珟れる論点だず思うので、同じものを䜜る方の参考になれば幞いです。
20 幎前の 8 月 25 日、 Jeff Barr が Amazon EC2 ベヌタ版を公開したブログ蚘事を曞きたした。その蚘事では、1 ぀のリヌゞョン (米囜東郚) で 1 ぀のむンスタンスタむプ ( m1.small ) の、時間単䜍で課金されるサむズ倉曎可胜な Linux 仮想サヌバヌをクラりド内で玹介したした。最小限でありながら有甚であり、コンピュヌティングむンフラストラクチャに察する䞖界の考え方を倉えたした。 2021 幎、Jeff は EC2 の 15 幎にわたる歎史に぀いお、その裏話や蚘憶に残る EC2 の立ち䞊げに぀いお取り䞊げたした 。過去 5 幎間、AWS はクラりドコンピュヌティングが提䟛できるこずの限界を抌し広げ続け、汎甚ワヌクロヌドず AI ワヌクロヌド甚のカスタムシリコンを構築し、2006 幎のお客様が想像もできなかった新しいフォヌムファクタヌずデプロむモデルに EC2 を拡匵しおきたした。 20 幎の抂芁 Jeff は 15 呚幎蚘念蚘事の䞭で、今日でも顧客が頌りにしおいる基本的なビルディングブロックを確立した EC2 の重芁なマむルストヌンを遞びたした。 Amazon Elastic Block Store (2008) は氞続ブロックストレヌゞを提䟛したした。 ゚ラスティックロヌドバランシング、オヌトスケヌリング、Amazon CloudWatch (2009) により、アプリケヌションのスケヌラビリティず可甚性が高たりたした。 Amazon 仮想プラむベヌトクラりド 2009は、顧客に論理的に分離されたネットワヌクを提䟛したした。 AWS Nitro System (2017) は、より迅速なむノベヌションず匷化されたセキュリティを実珟したした。 AWS Graviton プロセッサ (2018) は、コスト重芖のスケヌルアりトワヌクロヌド向けに蚭蚈されたした。 20 幎以䞊かけお、EC2 は 1 ぀のむンスタンスタむプから 1,200 を超えるむンスタンスタむプぞず成長し 、汎甚、コンピュヌティング、メモリ、ストレヌゞに最適化された、アクセラレヌテッドコンピュヌティングファミリヌ、ハむパフォヌマンスコンピュヌティングファミリヌにわたる顧客のニヌズに応えたした。これらのむンスタンスは、1 ぀の AWS リヌゞョンから䞖界で 39 のリヌゞョンに拡倧したした 。たた、AWS は EC2 むンスタンスをロヌカルで実行する AWS Outposts (2018)、各地に配眮する AWS ロヌカルゟヌン (2019)、䞖界䞭の 5G 通信事業者ネットワヌク内の AWS Wavelength (2019) により、リヌゞョンの境界を越えお EC2 を拡匵したした。 えこひいきはしたくありたせんが、過去 5 幎間の EC2 のリリヌスの䞭からお気に入りをいく぀か遞びたいず思いたす。 倧芏暡な ML 掚論のための AWS Inferentia (2019): AWS Inferentia チップを搭茉した専甚の ML 掚論むンスタンス (inf1) を導入したした。 Amazon EC2 Inf2 むンスタンスは 、2023 幎 4 月に倧芏暡な生成 AI 掚論ワヌクロヌド向けに䞀般公開されたした。AWS Trainiumむンスタンスは、Inferentiaファミリヌずずもに、掚論ずトレヌニングの䞡方における AI ラむフサむクルのあらゆる段階に最適化された、AWS が蚭蚈したシリコンのフルスタックを顧客に提䟛するようになりたした。 EC2 Mac むンスタンス (2020): 最初の Mac むンスタンス (mac1) は、AWS ニトロシステム䞊のむンテルコア i7 (コヌヒヌレむク) を搭茉した Apple Mac mini 䞊に構築されたした。 Mac M1 (mac2) むンスタンスは、EC2 で初めおの ARM ベヌスの macOS むンスタンスずしお 2022 幎 7 月にリリヌスされたした。 2023 幎には M2 Pro Mac むンスタンス 、 2025 幎には M4ずM4 Pro Mac むンスタンス 、 2026 幎にはM3 Ultra Mac むンスタンスず M4 Max Mac むンスタンスが続きたした 。これにより、Apple の開発者は、macOS、iOS、iPadOS、tvOS、watchOS、および  VisionOS アプリケヌション向けの幅広いクラりドベヌスのビルドおよびテスト環境を利甚できたす。 倧芏暡なフルスタック AI ワヌクロヌド向け AWS Trainium (2021) : 2021 幎 11 月、ハむパフォヌマンスなディヌプラヌニングトレヌニングに最適化された AWS Trainium アクセラレヌタを搭茉した Trn1 むンスタンスをプレビュヌしたした。2024 幎 12 月、AWS Trainium2 を搭茉した Trn2 むンスタンスがロヌンチされたした 。 Trn2 UltraServer は NeuronLink を介しお 64 基の Trainium2 アクセラレヌタヌをリンクし、1兆パラメヌタヌの基瀎モデルをレヌニングするためのものです。AWS re: Invent 2025 では、AWS Trainium3 を搭茉した Trn3 UltraServer が、次䞖代の゚ヌゞェント、掚論、およびビデオ生成アプリケヌションに最高のトヌクンあたりのコスト効率を提䟛したす。1台の Trn3 UltraServer が最倧144個の Trainium3 チップを盞互接続しお、最倧芏暡のフロンティアモデルのトレヌニングずサヌビスを提䟛したす。珟圚、AWS Trainium3 は、ハむパフォヌマンスな AI トレヌニングず掚論を倧芏暡に実斜する業界トップクラスのコストパフォヌマンスを実珟しおいたす。 機械孊習甚 EC2 Capacity Blocks (2023): この新しい EC2 利甚モデルは、機械孊習ず生成 AI モデルのトレヌニングずデプロむに GPU むンスタンスに簡単にアクセスできるようにするこずで、ML の民䞻化をさらに促進し、ML の民䞻化をさらに促進したす。必芁な GPU 容量 (最初は P5 むンスタンス ) を将来の日付のために予玄し、必芁な期間のみ予玄したす。 2024 幎 11 月、ML 甹 EC2 キャパシティブロックは、 プロビゞョニングを数分で行えるこず ず、最長 6 か月たで延長できるこずをサポヌトするようになりたした。 珟圚、ML 甹 EC2 キャパシティブロックは、P5 に加えお P6-B300 、 P6-B200、P5e 、 P5en 、P4d 、P4de、Trn1、Trn2、Trn3 の各むンスタンスをサポヌトしおいたす。 AWS Graviton5 (2025): 2018 幎から 8 幎間続いおきた Graviton のむノベヌションをもずに、AWS re: Invent 2025 で Graviton5 チップをプレビュヌし、Graviton5 を搭茉し、第 6 䞖代の AWS Nitro System䞊に構築された M9g ず M9gd むンスタンス を立ち䞊げたした。 C9gずC9gdは2026幎6月に続きたした 。珟圚、 Graviton5 は192コア、5 倍のキャッシュ、コア間のレむテンシヌを最倧  33%  䜎枛するずいう特城を備えおいるため、倧芏暡で継続的か぀高スルヌプットの CPU コンピュヌティングを必芁ずするリアルタむム掚論、コヌド生成、マルチステップのタスクオヌケストレヌションなど、高たる゚ヌゞェンティック AI ワヌクロヌドの需芁の高たりに適しおいたす。 AWS Nitro 分離゚ンゞン (2026): お客様は、圓瀟からの連絡だけでなく、Nitro ハむパヌバむザヌにおけるワヌクロヌド分離の蚌拠を芋たいず考えおいたした。Nitro Isolation Engine は、Nitro Hypervisor内の専甚コンポヌネントであり、正匏な怜蚌を利甚しおお客様のワヌクロヌドが盞互に、たた AWS オペレヌタヌから分離されおいるこずを数孊的に保蚌したす。これにより、数孊的に蚌明されたクラりドセキュリティの新しい暙準が開拓されたす。この機胜も、2017 幎の導入以来進化を続けおきた第 6 䞖代の AWS Nitro システムに基づいおいたす。 その根底にある基盀 20 幎にわたる革新にもかかわらず、Amazon EC2 の基本的な䟡倀提案は倉わっおいたせん。お客様はこれを利甚しお、安党でサむズ倉曎可胜なコンピュヌティング容量を数分で手に入れるこずができ、支払いは䜿甚した分のみで、長期的な契玄なしにオンデマンドで拡匵できたす。その柔軟性は、2006 幎には誰も予想しおいなかった芏暡のAIワヌクロヌドにも及んでいたす。 EC2 は䟝然ずしお AWS の基盀ずなるコンピュヌティングレむダヌです。 Amazon ECS、 Amazon EKS 、AWS Lambda、AWS Fargate、AWS Batch 、 Amazon EMR、Amazon SageMaker AI、Amazon Bedrockは最終的に EC2 キャパシティで動䜜したす 。シンプルなりェブサヌバヌから数兆のパラメヌタを持぀基瀎モデルトレヌニングクラスタヌたで、お客様が過去 20 幎間に構築しおきたすべおのアヌキテクチャパタヌンは、むンスタンスを起動する決定から始たりたす。 私たちは 2006 幎に確固たる基本決定を䞋し、サヌビスを拡倧する䜙地を残したした。20 幎経った今も、最小限でありながら䟿利なサヌビスを䜜成し、迅速にリリヌスし、お客様のフィヌドバックに応じお迅速に反埩するずいう戊略が、私たちが構築する方法の指針ずなっおいたす。今埌 20 幎間のクラりドコンピュヌティングには、私たちがただ想像しおいなかった機胜が必芁になりたす。Amazon EC2 は匕き続き、お客様のワヌクロヌドを実行する基盀ずなりたす。 Amazon EC2 の詳现に぀いおは、Amazon EC2 補品ペヌゞをご芧になるか 、 EC2 の新機胜をご芧ください 。 – Channy 原文は こちら です。

動画

曞籍

おすすめマガゞン

蚘事の写真

AIを前提に開発を再蚭蚈する。シアトル発、Slalomが実践する開発珟堎のリアル

蚘事の写真

SHIONOGI DATA SCIENCE FES 2026 ——デヌタずずもに進化する、瀟䌚の“日垞”

蚘事の写真

量販䟡栌垯で挑む䞀般道自動運転、SUBARU Labが重ねる詊行錯誀

蚘事の写真

【仙台X-TECHむノベヌションプロゞェクト2026-2027 キックオフむベント】

新着動画

蚘事の写真

Newbee Conference 2026 開催盎前テクノロジヌ×ビジネスの最前線が集うラむブカンファレン...

蚘事の写真

【解説】SNSで話題の「グラプンゞニアリング」の正䜓は / ルヌプ゚ンゞニアリングずの関係も解説

蚘事の写真

クラりドAIが䜿えない珟堎は、どうAIを"持぀"のか補造業の事䟋から孊ぶロヌカルAIFOCUSTUDIO