コンテンツにスキップ

AWS移行計画 — Synology → ECS on Fargate

概要

AEGISが実収益を生み始めた段階で、BCP(事業継続計画)・可用性・運用負荷の観点から Synology NAS → AWS ECS (Fargate) への移行を計画する。 本ドキュメントは移行の技術設計・コスト分析・段階的移行手順を網羅する。

ステータス: 計画段階(AWSアカウント・コスト監視は構築済み)

本移行は収益が安定した段階で実施する。現時点では設計ドキュメントとしてのみ存在する。 実施時期は未定。ただし、AWSアカウント・IAM Identity Center・Budget Alert・ ダッシュボードCostパネルは2026-03-25に構築済み。


移行の動機

なぜSynologyから移行するのか

観点 Synology (現行) AWS (移行後)
物理障害 NAS故障・停電でシステム全停止 AZ冗長で単一障害点なし
ネットワーク 家庭ISP依存(障害時は全停止) AWS Direct Connect不要(API outbound のみ)
OS管理 DSM手動アップデート Fargate: OS管理不要
スケーリング 物理ハードウェア制約 タスク定義変更のみ
DR(災害復旧) バックアップなし(単一NAS) EFS自動バックアップ + Cross-Region複製
セキュリティ SSH + Tailscale(攻撃面あり) SSHポート自体が存在しない
監視 自前 aegis-monitor CloudWatch統合(メトリクス・ログ・アラーム)

BCP上の最大リスク

flowchart TB
    RISK["Synology単一障害点"]
    RISK --> R1["NAS本体故障<br/>全サービス停止"]
    RISK --> R2["ISP障害<br/>Saxo API到達不能"]
    RISK --> R3["停電<br/>UPS容量超過で全停止"]
    RISK --> R4["DSMアップデート失敗<br/>Docker環境破損"]

    R1 & R2 & R3 & R4 --> IMPACT["影響: PT停止<br/>→ ポジション管理不能<br/>→ 損失拡大リスク"]

    style RISK fill:#e74c3c,stroke:#c0392b,color:#fff
    style IMPACT fill:#e67e22,stroke:#d35400,color:#fff

リージョン選定: us-east-1

通信先はほぼ全て米国東海岸

AEGISが通信する外部APIのサーバー所在地とレイテンシ比較:

API サーバー所在地 ap-northeast-1(東京)→ API us-east-1 → API
Polygon.io US East ~170ms ~1-3ms
Unusual Whales US ~150ms ~5-10ms
FRED (St. Louis Fed) US ~180ms ~10-20ms
Finnhub US ~150ms ~5-10ms
Yahoo Finance US ~150ms ~1-5ms
Saxo Bank EU (Copenhagen) ~250ms ~80-100ms

Polygon/UWはintraday収集で15分〜60秒間隔のポーリングを行うため、レイテンシが低い方がスループットに直結する。 Saxo APIはEU所在だが、トークン更新は5分に1回、注文送信は1日数回なので80msと250msの差は無意味。

コスト差: 東京は25-38%割高

サービス us-east-1 ap-northeast-1
Fargate vCPU/h $0.04048 $0.05056 +25%
Fargate Memory/GB/h $0.004445 $0.005553 +25%
NAT Gateway/h $0.045 $0.062 +38%
EFS GB/月 $0.30 $0.36 +20%

Fargateだけで月$150 → \(188程度、総額で**月\)40-50の差**が出る。

開発母艦(Mac)からの通信

CI/CDはGitHub Actions経由なので、Macから直接AWSに通信する場面は通常運用ではない。 デバッグ時のECS Exec(SSHもどき)も数百msのレイテンシがあっても操作に支障はないレベル。

結論

通信先・コストの両面でus-east-1が合理的。東京リージョンを選ぶ積極的理由がない。


ECS on EC2 vs ECS on Fargate

前提: ECSはオーケストレーション層

ECS(Elastic Container Service)はコンテナの配置・管理を行うオーケストレーションであり、 その上で「EC2インスタンスを自前管理する」か「Fargateにインフラを委ねる」かを選択する。

比較マトリクス

観点 ECS on EC2 ECS on Fargate AEGIS判定
OS管理 パッチ適用・Docker更新が必要 完全マネージド Fargate優位
障害復旧 手動でインスタンス復旧 or ASG設定要 自動タスク再配置(AZ跨ぎ含む) Fargate優位
共有ボリューム ホストパスマウント(現行同様) EFSのみ(実装変更要) EC2が楽だがEFSで解決可能
docker.sock マウント可能(monitor用) 不可 EC2が楽だがECS API で解決可能
SSHデバッグ 直接SSH可 ECS Exec経由 EC2が便利だがECS Execで十分
コスト(月額) ~$89-134 ~$200 EC2が安い
セキュリティ SSHポート管理・SG管理必要 攻撃面が極めて小さい Fargate優位
スケーリング ASG設定+容量計画が必要 タスク数変更のみ Fargate優位
起動速度 インスタンス常時起動 タスク起動30-60秒 常時稼働なので差なし
運用負荷(総合) 中〜高 Fargate優位

結論: Fargate を採用

判断理由

月$60-100のコスト増は、実資金を運用するシステムの安定性・BCP保険として十分に合理的。 Fargateの技術的障壁(共有ボリューム、docker.sock、supervisord)はすべて実装で解決可能であり、 解決後はEC2より運用負荷が大幅に低くなる。


技術的障壁と解決策

Fargateには3つの技術的障壁があるが、いずれも実装で解決可能。

1. 共有ボリューム — EFS(Elastic File System)で解決

現状の共有ボリューム分析

ボリューム サイズ I/Oパターン EFS適性
tokens/ <1MB 5分に1回書込み、読取り数回/分 完全に問題なし
wft_state/ <100MB 数秒に1回JSON書込み 問題なし
pt_state/ <100MB 同上 問題なし
quote_samples/ <100MB append-only JSONL 問題なし
live_data_archive/ ~7GB EOD時にparquet書込み、読取り頻繁 問題なし
logs/ <1GB append-only 下記「ログ戦略」参照
polygon_v2/ ~50GB+ バッチ読み書き S3 + DataSync

EFSレイテンシは問題にならない

EFSのレイテンシは ~1-5ms。AEGISのボトルネックはSaxo API応答(数十ms)・Polygon API応答(数十ms)であり、 ファイルI/Oの数msは誤差。tokens/state/は特に小ファイル・低頻度なので全く問題なし。

EFS設計

graph TB
    subgraph EFS["Amazon EFS (リージョナル)"]
        AP1["Access Point<br/>/tokens<br/>UID:1000 GID:1000"]
        AP2["Access Point<br/>/state<br/>UID:1000 GID:1000"]
        AP3["Access Point<br/>/live_data_archive<br/>UID:1000 GID:1000"]
        AP4["Access Point<br/>/quote_samples<br/>UID:1000 GID:1000"]
    end

    subgraph Tasks["ECS Fargate Tasks"]
        TK["aegis-token-keeper<br/>RW: /tokens"]
        WFT["aegis-wft<br/>RW: /tokens, /state<br/>RO: /live_data_archive"]
        QC["aegis-quote-collector<br/>RO: /tokens<br/>RW: /quote_samples"]
        LDAS["aegis-ldas-*<br/>RW: /live_data_archive"]
        DASH["aegis-dashboard<br/>RO: /tokens, /state,<br/>/live_data_archive"]
    end

    TK --> AP1
    WFT --> AP1 & AP2 & AP3
    QC --> AP1 & AP4
    LDAS --> AP3
    DASH --> AP1 & AP2 & AP3

    classDef efs fill:#f39c12,stroke:#e67e22,color:#fff
    class AP1,AP2,AP3,AP4 efs

EFSコスト見積

項目 容量 単価 月額
EFS Standard(頻繁アクセス) ~8GB $0.30/GB $2.40
EFS IA(低頻度アクセス) $0.025/GB
EFS スループット(バースト) 含む $0
合計 ~$3-5

polygon_v2/(~50GB)はEFSではなくS3に配置し、必要時にFargate タスクからS3 APIでアクセスする。 バックテストはMac上で実行するため、S3→Mac同期で対応。

ログ戦略

現行のAEGISコンテナはloguru等でファイルにログ出力している(/var/log/pt/, /var/log/ldas/ 等)。 Fargateではログの扱いに2つの選択肢がある。

方式 仕組み メリット デメリット
A: CloudWatch Logs Fargateのawslogsドライバがstdout/stderrを自動収集 集約・検索・保持期間設定が容易、追加実装なし アプリ側のログ出力先をファイル→stdout/stderrに変更する改修が必要
B: EFS + ファイル 現行同様にファイルに書込み アプリ改修ゼロ ログの検索・集約は自前、ローテーション管理も自前
C: 併用 stdout/stderrに出力しつつ、EFS上にもファイル出力 CloudWatchで集約しつつ、ファイルでの調査も可能 loguru設定の変更が必要(sinksの追加)

推奨: 方式C(併用)

  • loguruのadd()sinkを2つ設定: sys.stderr(→ CloudWatch Logs自動収集)+ EFS上のファイル(現行互換)
  • CloudWatch Logsは保持期間を設定可能(例: 30日)で、古いログは自動削除
  • EFS上のファイルログは即座にgrepできるため、ECS Execでの調査時に便利
  • 改修量は各コンテナのloguru初期化部分にsink追加するだけ(数行)
# 改修例: loguru設定(各コンテナの起動スクリプト)
from loguru import logger
import sys

# CloudWatch Logs用(Fargateが自動収集)
logger.add(sys.stderr, level="INFO", format="{time} | {level} | {message}")

# EFS ファイル用(現行互換、ローテーション付き)
logger.add(
    "/var/log/pt/pt_{time:YYYY-MM-DD}.log",
    level="DEBUG",
    rotation="1 day",
    retention="14 days",  # EFS上は14日保持
)

ログ出力先の変更はアプリ改修が必要

Fargateにすれば自動的にCloudWatch Logsに送られるわけではない。 Fargateのawslogsログドライバはコンテナのstdout/stderrを収集する仕組みであるため、 現行のようにファイルにのみ出力している場合はCloudWatch Logsには何も記録されない。 stdout/stderrへの出力を追加する改修が必要。


2. docker.sock依存 — ECS API + CloudWatch に置換

現行の監視方式(aegis-monitor)

# 現在: docker.sockをマウントしてコンテナ監視
import docker
client = docker.from_env()
for container in client.containers.list():
    status = container.status  # "running" / "exited"
    stats = container.stats(stream=False)  # CPU/メモリ

Fargate移行後の監視方式

# Fargate版: boto3 ECS API でタスク監視
import boto3

ecs = boto3.client('ecs')
cw = boto3.client('cloudwatch')

# タスク死活監視(docker.sock不要)
tasks = ecs.list_tasks(cluster='aegis', desiredStatus='RUNNING')
for task_arn in tasks['taskArns']:
    details = ecs.describe_tasks(cluster='aegis', tasks=[task_arn])
    for task in details['tasks']:
        status = task['lastStatus']          # RUNNING / STOPPED
        health = task['healthStatus']        # HEALTHY / UNHEALTHY
        reason = task.get('stoppedReason')   # 停止理由(自動記録)
        started = task['startedAt']          # 起動時刻

# リソース監視(CloudWatch Container Insights 自動収集)
metrics = cw.get_metric_data(
    MetricDataQueries=[{
        'Id': 'cpu',
        'MetricStat': {
            'Metric': {
                'Namespace': 'ECS/ContainerInsights',
                'MetricName': 'CpuUtilized',
                'Dimensions': [
                    {'Name': 'ClusterName', 'Value': 'aegis'},
                    {'Name': 'ServiceName', 'Value': 'aegis-wft'}
                ]
            },
            'Period': 60,
            'Stat': 'Average'
        }
    }],
    StartTime=datetime.utcnow() - timedelta(minutes=5),
    EndTime=datetime.utcnow()
)

Fargate版で追加で得られるもの

機能 docker.sock版(現行) ECS API + CloudWatch版
コンテナ死活 container.status task.lastStatus + healthStatus
CPU/メモリ container.stats() CloudWatch Container Insights(自動)
ログ ファイルベース CloudWatch Logs(集約・検索・保持)
停止理由 なし(手動調査) stoppedReason 自動記録
再起動回数 なし ECS Service Events 自動記録
アラーム 自前(Slack/Twilio) CloudWatch Alarms → SNS → Slack/SMS
ダッシュボード aegis-dashboard 自前 CloudWatch Dashboards(追加可)

監視の質が向上する

docker.sock版は「コンテナが生きているか」しか見えないが、 CloudWatch統合によりCPU/メモリ推移・ログ集約・停止理由の自動記録・アラーム自動化が得られる。 aegis-monitorの改修は必要だが、結果としてより高品質な監視になる。


3. supervisord(LDAS) — Fargate タスク分割

現行のLDAS構成

graph TB
    subgraph LDAS_Current["aegis-ldas (現行: supervisord管理)"]
        SUP["supervisord"]
        SUP --> CRON["cron<br/>EOD収集 17:15 ET"]
        SUP --> INTRA["intraday<br/>15分間隔スナップショット"]
        SUP --> UWF["uw_flow<br/>60秒ポーリング"]
    end

    style LDAS_Current fill:#3498db,stroke:#2980b9,color:#fff

問題点: 1コンテナに3プロセスを同居させているため、1プロセスの障害が他に波及するリスクがある。

Fargate移行後: 3タスクに分割

graph TB
    subgraph LDAS_New["LDAS (Fargate: 独立タスク)"]
        EOD["aegis-ldas-eod<br/>EventBridge Scheduled Task<br/>17:15 ET 平日のみ"]
        INTRA["aegis-ldas-intraday<br/>ECS Service (常時稼働)<br/>15分間隔スナップショット"]
        UWF["aegis-ldas-uwflow<br/>ECS Service (常時稼働)<br/>60秒ポーリング"]
    end

    EFS[("EFS<br/>/live_data_archive")]

    EOD -->|RW| EFS
    INTRA -->|RW| EFS
    UWF -->|RW| EFS

    style EOD fill:#27ae60,stroke:#1e8449,color:#fff
    style INTRA fill:#2980b9,stroke:#2471a3,color:#fff
    style UWF fill:#8e44ad,stroke:#7d3c98,color:#fff
現行(supervisord内) Fargate移行後 種別 スケジュール
cron (EOD collect) aegis-ldas-eod Scheduled Task EventBridge: cron(15 17 ? * MON-FRI *)
intraday loop aegis-ldas-intraday ECS Service 常時稼働(市場時間に自律的にアクティブ化)
uw_flow polling aegis-ldas-uwflow ECS Service 常時稼働(60秒ポーリング)

メリット:

  • EOD収集が落ちてもintraday収集に影響しない(障害分離)
  • 個別にリソース割当・ログ分離・ヘルスチェック可能
  • EODはScheduled Task(実行時のみ課金 → 月$0.30程度)

4. Cloudflare Tunnel — サイドカーで維持

aegis.llmtradelab.com は Cloudflare で管理されているため、 cloudflared を Fargate サイドカーコンテナとしてそのまま維持する。

graph LR
    USER["ユーザー"] -->|HTTPS| CF["Cloudflare Edge"]
    CF -->|Tunnel| CFD["cloudflared<br/>(サイドカー)"]
    CFD -->|localhost:8080| DASH["aegis-dashboard"]

    subgraph FargateTask["ECS Fargate Task"]
        CFD
        DASH
    end

将来的にALB + ACMに置き換える選択肢もあるが、 既存のCloudflare構成をそのまま使うのが移行コスト最小。


AWS アーキテクチャ設計

全体構成図

graph TB
    subgraph VPC["VPC (10.0.0.0/16) — us-east-1"]
        subgraph PubSub["Public Subnets"]
            NAT1["NAT Gateway<br/>AZ-a"]
            NAT2["NAT Gateway<br/>AZ-b (DR)"]
        end

        subgraph PrivSub["Private Subnets (Fargate タスク配置)"]
            subgraph AZa["AZ-a"]
                WFT_A["aegis-wft"]
                TK_A["aegis-token-keeper"]
                QC_A["aegis-quote-collector"]
                LDAS_I_A["aegis-ldas-intraday"]
                LDAS_U_A["aegis-ldas-uwflow"]
            end
            subgraph AZb["AZ-b (フェイルオーバー先)"]
                DASH_B["aegis-dashboard<br/>+ cloudflared"]
                MON_B["aegis-monitor"]
            end
        end

        EFS[("Amazon EFS<br/>(Multi-AZ)")]
    end

    subgraph External["外部サービス"]
        SAXO["Saxo Bank API"]
        POLY["Polygon.io"]
        UW["Unusual Whales"]
        FRED["FRED"]
    end

    subgraph AWS_Services["AWSマネージドサービス"]
        ECR["ECR<br/>コンテナレジストリ"]
        SM["Secrets Manager<br/>API Keys / OAuth2"]
        CW["CloudWatch<br/>Logs + Metrics + Alarms"]
        EB["EventBridge<br/>EODスケジュール"]
        SNS["SNS<br/>→ Slack / SMS"]
        S3["S3<br/>polygon_v2 / バックアップ"]
    end

    WFT_A & TK_A & QC_A --> SAXO
    LDAS_I_A & LDAS_U_A --> POLY & UW & FRED
    WFT_A & TK_A & QC_A & LDAS_I_A & LDAS_U_A & DASH_B --> EFS
    EB -->|"17:15 ET"| LDAS_EOD["aegis-ldas-eod<br/>(Scheduled Task)"]
    LDAS_EOD --> EFS
    MON_B --> CW
    CW --> SNS

    classDef efs fill:#f39c12,stroke:#e67e22,color:#fff
    classDef ext fill:#95a5a6,stroke:#7f8c8d,color:#fff
    class EFS efs
    class SAXO,POLY,UW,FRED ext

ネットワーク設計

コンポーネント CIDR / 設定 備考
VPC 10.0.0.0/16 us-east-1
Public Subnet AZ-a 10.0.1.0/24 NAT Gateway配置
Public Subnet AZ-b 10.0.2.0/24 NAT Gateway配置(冗長)
Private Subnet AZ-a 10.0.10.0/24 Fargateタスク配置(主)
Private Subnet AZ-b 10.0.20.0/24 Fargateタスク配置(副)
Security Group (egress) All outbound allowed Saxo/Polygon等へのAPI通信
Security Group (intra-task) SG self-reference タスク間通信(EFS経由が主)

NAT Gateway コスト

NAT Gatewayは $32/月/個 + データ転送 \(0.045/GB。 AEGISの外部API通信量は月数GB程度なので、**1 NAT Gatewayで十分**(冗長は将来的に追加)。 月額: ~\)35

ECS クラスタ・サービス設計

タスク定義一覧

タスク名 vCPU メモリ 種別 稼働パターン
aegis-wft 1.0 2GB ECS Service 常時稼働
aegis-token-keeper 0.25 0.5GB ECS Service 常時稼働
aegis-quote-collector 0.25 0.5GB ECS Service 常時稼働
aegis-ldas-intraday 0.5 1GB ECS Service 常時稼働
aegis-ldas-uwflow 0.25 0.5GB ECS Service 常時稼働
aegis-ldas-eod 0.5 1GB Scheduled Task 平日17:15 ET(~15分/回)
aegis-polygon-quotes 1.0 1GB ECS Service 常時稼働
aegis-dashboard + cloudflared 0.5 + 0.25 1GB + 0.5GB ECS Service 常時稼働
aegis-monitor 0.25 0.5GB ECS Service 常時稼働

サービス設定

{
  "serviceName": "aegis-wft",
  "launchType": "FARGATE",
  "desiredCount": 1,
  "deploymentConfiguration": {
    "maximumPercent": 200,
    "minimumHealthyPercent": 100
  },
  "networkConfiguration": {
    "awsvpcConfiguration": {
      "subnets": ["subnet-private-a", "subnet-private-b"],
      "securityGroups": ["sg-aegis-tasks"],
      "assignPublicIp": "DISABLED"
    }
  },
  "enableExecuteCommand": true
}

enableExecuteCommand

ECS Execを有効にすることで、SSHなしでFargateタスク内にシェルセッションを開ける。 デバッグ時: aws ecs execute-command --cluster aegis --task <task-id> --container aegis-wft --interactive --command "/bin/sh"


シークレット管理

現行(Synology)

/volume1/aegis/.env            — POLYGON_API_KEY, UW_TOKEN, etc.
/volume1/aegis/tokens/         — saxobank_tokens_live.json (OAuth2)

AWS移行後

シークレット 保管先 アクセス方法
POLYGON_API_KEY Secrets Manager タスク定義の secrets で注入
UNUSUAL_WHALES_API_TOKEN Secrets Manager 同上
FRED_API_KEY Secrets Manager 同上
TWILIO_ACCOUNT_SID / AUTH_TOKEN Secrets Manager 同上
Saxo OAuth2 tokens EFS /tokens/ token-keeperがファイル書込み(現行同様)
Cloudflare Tunnel Token Secrets Manager cloudflared タスク定義で注入
# ECS Task Definition (抜粋)
containerDefinitions:
  - name: aegis-wft
    secrets:
      - name: POLYGON_API_KEY
        valueFrom: "arn:aws:secretsmanager:us-east-1:ACCOUNT:secret:aegis/polygon-key"
      - name: UNUSUAL_WHALES_API_TOKEN
        valueFrom: "arn:aws:secretsmanager:us-east-1:ACCOUNT:secret:aegis/uw-token"
    mountPoints:
      - sourceVolume: efs-tokens
        containerPath: /data/tokens
        readOnly: false

Saxo OAuth2 トークンはファイルベース維持

Saxo APIのOAuth2トークンはtoken-keeperが5分ごとにファイルに書き込み、他のタスクが読む という現行の方式をEFS上で維持する。Secrets Managerでは更新頻度(5分毎)に適さない。


CI/CD パイプライン変更

現行(SSH → Synology)

sequenceDiagram
    participant Dev as Mac mini
    participant GH as GitHub
    participant GA as GitHub Actions
    participant SYN as Synology

    Dev->>GH: git push
    GH->>GA: Trigger workflow
    GA->>SYN: SSH via Tailscale
    SYN->>SYN: git pull + docker compose rebuild
    GA-->>GH: Deploy status

AWS移行後(ECR + ECS)

sequenceDiagram
    participant Dev as Mac mini
    participant GH as GitHub
    participant GA as GitHub Actions
    participant ECR as Amazon ECR
    participant ECS as Amazon ECS

    Dev->>GH: git push
    GH->>GA: Trigger workflow
    GA->>GA: docker build
    GA->>ECR: docker push (tagged image)
    GA->>ECS: aws ecs update-service --force-new-deployment
    ECS->>ECS: Rolling update (new task → health check → old task drain)
    GA->>ECS: aws ecs wait services-stable
    GA-->>GH: Deploy status ✅/❌

変更点:

項目 現行(Synology) AWS移行後
イメージビルド Synology上でdocker compose build GitHub Actions上でbuild → ECR push
デプロイ SSH → git pull → compose up ECS update-service(ローリングアップデート)
ロールバック 手動 git revert + compose up aws ecs update-service --task-definition <前のリビジョン>
VPN Tailscale必須 不要(AWS API経由)
ヘルスチェック deploy.sh内のスクリプト ECS ヘルスチェック(自動)

GitHub Actions ワークフロー(AWS版)

# .github/workflows/deploy-aws.yml
name: Deploy to AWS ECS

on:
  push:
    branches: [main, 'feat/pt-*']
    paths:
      - 'aegis_v3/**'
      - 'AEGIS/**'

env:
  AWS_REGION: us-east-1
  ECR_REPOSITORY: aegis
  ECS_CLUSTER: aegis

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write   # OIDC
      contents: read

    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::role/github-actions-aegis
          aws-region: ${{ env.AWS_REGION }}

      - name: Login to Amazon ECR
        id: ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build & push images
        run: |
          COMMIT_SHA=${{ github.sha }}
          # 変更があったサービスのみビルド・プッシュ
          for service in wft token-keeper quote-collector ldas dashboard monitor; do
            docker build -t $ECR_REPO/aegis-${service}:${COMMIT_SHA} \
              -f aegis_v3/${service}-docker/Dockerfile .
            docker push $ECR_REPO/aegis-${service}:${COMMIT_SHA}
          done

      - name: Update ECS services
        run: |
          for service in aegis-wft aegis-token-keeper aegis-quote-collector \
                         aegis-ldas-intraday aegis-ldas-uwflow \
                         aegis-polygon-quotes aegis-dashboard aegis-monitor; do
            aws ecs update-service \
              --cluster $ECS_CLUSTER \
              --service $service \
              --force-new-deployment
          done

      - name: Wait for stable
        run: |
          aws ecs wait services-stable \
            --cluster $ECS_CLUSTER \
            --services aegis-wft aegis-token-keeper aegis-dashboard

OIDC認証

AWS認証にはGitHub Actions OIDC(OpenID Connect)を使用し、 長期のアクセスキーをGitHub Secretsに保存しない。セキュリティのベストプラクティス。


監視・アラート設計

CloudWatch統合

flowchart TB
    subgraph Sources["メトリクスソース"]
        CI["Container Insights<br/>CPU / Memory / Network"]
        CL["CloudWatch Logs<br/>全タスクのログ集約"]
        HC["ECS Health Checks<br/>タスク死活"]
        MON["aegis-monitor<br/>カスタムメトリクス"]
    end

    subgraph Alarms["CloudWatch Alarms"]
        A1["CPU > 80% (5分持続)"]
        A2["Memory > 80% (5分持続)"]
        A3["Task count < desired (1分)"]
        A4["Token age > 1800秒"]
        A5["LDAS data stale > 2時間"]
        A6["WFT heartbeat stale > 10分<br/>(市場時間のみ)"]
    end

    subgraph Actions["アクション"]
        SNS["SNS Topic"]
        SLACK["Slack #aegis_管理"]
        SMS["Twilio SMS<br/>(Critical のみ)"]
    end

    CI --> A1 & A2
    HC --> A3
    MON --> A4 & A5 & A6

    A1 & A2 -->|Warning| SNS --> SLACK
    A3 & A4 -->|Critical| SNS --> SLACK & SMS
    A5 & A6 -->|Critical<br/>市場時間のみ| SNS --> SLACK & SMS

aegis-monitor の改修内容

チェック項目 現行方式 AWS移行後
コンテナ死活 docker.sockclient.containers.list() boto3 ecs.describe_tasks()
CPU/Memory docker.sockcontainer.stats() CloudWatch Container Insights(自動)
Saxoトークン有効期限 ファイルmtime確認 EFS上のファイルmtime確認(同じ)
LDASデータ鮮度 parquetファイルmtime EFS上のparquetファイルmtime(同じ)
ホストリソース /proc マウント 不要(CloudWatch Insights が代替)
ログ ファイル読取り CloudWatch Logs Insights クエリ
アラート送信 自前Slack/Twilio CloudWatch Alarms → SNS → Lambda → Slack/Twilio

コスト詳細分析

月額コスト内訳

項目 月額 (USD) 備考
Fargate (常時稼働 8サービス) $170 下記タスク別内訳参照
Fargate (Scheduled: EOD) $0.30 平日22回 × 15分 × 0.5vCPU
EFS $3-5 ~8GB Standard ストレージ
NAT Gateway $35 \(32固定 + データ転送 ~\)3
CloudWatch Logs $5-10 ~10GB/月の取込み・保存
CloudWatch Container Insights $0 ECS基本メトリクスは無料
ECR $1-2 ~5GBのイメージストレージ
Secrets Manager $3 6-8シークレット × $0.40
S3 (polygon_v2 + バックアップ) $2-5 ~60GB S3 Standard
データ転送 $3-5 API通信(月数GB)
合計 ~$225-235

Fargate タスク別コスト内訳

Fargate料金: vCPU = $0.04048/h、メモリ = $0.004445/GB/h(us-east-1)

タスク vCPU メモリ 月稼働 vCPU費 メモリ費 小計
aegis-wft 1.0 2GB 730h $29.55 $6.49 $36.04
aegis-token-keeper 0.25 0.5GB 730h $7.39 $1.62 $9.01
aegis-quote-collector 0.25 0.5GB 730h $7.39 $1.62 $9.01
aegis-ldas-intraday 0.5 1GB 730h $14.78 $3.24 $18.02
aegis-ldas-uwflow 0.25 0.5GB 730h $7.39 $1.62 $9.01
aegis-polygon-quotes 1.0 1GB 730h $29.55 $3.24 $32.79
aegis-dashboard 0.5 1GB 730h $14.78 $3.24 $18.02
cloudflared (サイドカー) 0.25 0.5GB 730h $7.39 $1.62 $9.01
aegis-monitor 0.25 0.5GB 730h $7.39 $1.62 $9.01
aegis-ldas-eod 0.5 1GB ~5.5h $0.22 $0.02 $0.24
Fargate合計 $150.16

比較: Synology vs AWS

項目 Synology (現行) AWS Fargate
ハードウェア DS1823+ ¥180,000(償却中)
電気代 ~¥3,000/月 (~$20)
ISP ~¥5,000/月 (~$33) の一部
AWS費用 ~$230/月
実質月額 ~$30-50 + 故障リスク ~$230
年額 ~$360-600 + 故障リスク ~$2,760
OS管理 手動 不要
障害復旧 手動(数時間〜) 自動(数分)
BCP なし(単一障害点) AZ冗長

コスト差の解釈

年間約$2,000の差額(~30万円/年)は、月に数十万〜数百万円の収益を生むシステムのBCP保険として合理的。 Synologyの物理故障(RAID劣化、PSU障害、NICチップ不良)1回の損失で十分回収できる。

Fargate Savings Plans

1年 or 3年のCompute Savings Plansを適用すると:

プラン 割引率 Fargate月額 総月額
On-Demand $150 ~$230
1yr Savings Plan ~20% ~$120 ~$200
3yr Savings Plan ~35% ~$98 ~$178

段階的移行計画

Phase 0: 準備(AWS基盤構築)

所要期間: 1-2日

作業内容:

  1. AWS基盤をIaC(Terraform or CDK)で構築

    • VPC + Subnets + NAT Gateway
    • ECS Cluster (Fargate)
    • EFS + Access Points
    • ECR リポジトリ
    • Secrets Manager
    • CloudWatch Log Groups
    • IAM Roles (タスク実行ロール、タスクロール)
    • EventBridge Rules (EODスケジュール)
    • SNS Topics + Slack/SMS連携
  2. GitHub Actions OIDCプロバイダ設定

  3. ECRにイメージをpush(全コンテナ)

gantt
    title AWS移行タイムライン
    dateFormat  YYYY-MM-DD
    axisFormat  %m/%d

    section Phase 0 準備
    AWS基盤構築 (IaC)           :p0a, 2026-06-01, 2d
    ECRイメージpush             :p0b, after p0a, 1d

    section Phase 1 非クリティカル
    aegis-monitor移行           :p1a, after p0b, 2d
    aegis-dashboard移行         :p1b, after p1a, 1d
    aegis-polygon-quotes移行    :p1c, after p1b, 1d
    並行稼働検証 (1週間)         :p1d, after p1c, 7d

    section Phase 2 データ収集
    aegis-ldas分割+移行         :p2a, after p1d, 3d
    EFSデータ同期確認           :p2b, after p2a, 2d
    並行稼働検証 (1週間)         :p2c, after p2b, 7d

    section Phase 3 トレーディング
    aegis-token-keeper移行      :p3a, after p2c, 1d
    aegis-wft移行               :p3b, after p3a, 1d
    aegis-quote-collector移行   :p3c, after p3b, 1d
    並行稼働検証 (2週間)         :p3d, after p3c, 14d
    Synology停止                :p3e, after p3d, 1d

Phase 1: 非クリティカルサービス移行

対象: monitor, dashboard, polygon-quotes

移行順序と理由:

  1. aegis-monitor — docker.sock → ECS API 改修が必要。最も改修量が多いため最初に着手
  2. aegis-dashboard + cloudflared — Cloudflare Tunnel設定の動作確認
  3. aegis-polygon-quotes — データバックフィル。失敗しても実運用に影響なし

検証項目:

  • EFS マウント正常動作
  • CloudWatch Logs にログ出力
  • Cloudflare Tunnel 経由でダッシュボードアクセス可能
  • aegis-monitor が ECS API でタスク状態を取得可能
  • polygon-quotes が EFS 上の parquet を読み書き可能

Phase 2: データ収集系移行

対象: LDAS(3タスクに分割)

重要: データ収集はPTの前提条件。ここでの障害はトレーディングに直結する。

  1. LDAS supervisord → 3 Fargate タスクに分割
  2. Synology LDAS と AWS LDAS を並行稼働(1週間)
  3. EFS上のデータをMac BTで読み取り可能か検証(S3経由の同期パス確認)
  4. 並行稼働で問題なしを確認後、Synology LDAS停止

検証項目:

  • EOD Scheduled Task が 17:15 ET に正常起動・完了
  • Intraday が 15分間隔でスナップショット取得
  • UW Flow が 60秒ポーリングで正常取得
  • 全parquetファイルが EFS 上に正しく書込まれている
  • SynologyとAWSのデータが一致(diff検証)

Phase 3: トレーディング系移行(最重要)

CRITICAL: 実資金に直結するため最も慎重に

移行順序:

  1. aegis-token-keeper — Saxo OAuth2トークン更新がEFS上で正常動作するか確認
  2. aegis-wft — WFT/PTの全9シナリオが正常動作するか確認
  3. aegis-quote-collector — LQSサンプリングが正常動作するか確認

並行稼働(2週間):

  • SynologyとAWSの両方で同時にPTを実行(同じシナリオ)
  • 注文はAWS側のみ有効(Synology側はdry-run or WFTモード)
  • 2週間でトレード判定が一致することを確認

検証項目:

  • token-keeperがEFS上にトークンを正常書込み(5分間隔)
  • 全タスクがEFS上のトークンを正常読取り
  • WFTシナリオが全て正常にスキャン・判定を実行
  • LQSが30 req/min でSaxo APIを正常サンプリング
  • 市場時間外にヘルスチェックがhealthyを返す
  • Saxo API注文が正常に送信・約定される
  • CloudWatch Alarmsが正常に発火・通知される
  • タスク異常停止時に自動再起動されることを確認
  • ECS Execでタスク内にシェル接続可能

Phase 4: Synology停止・DR構築

Synology停止後:

  1. Synologyはバックアップ・開発用途のみに転用
  2. EFS 自動バックアップ有効化(日次 → 35日保持)
  3. (将来的に)Cross-Region レプリケーション(us-west-2)

ロールバック計画

各Phaseでのロールバック手順:

Phase ロールバック方法 所要時間
Phase 1 Synology側のmonitor/dashboard/quotesを再起動 数分
Phase 2 Synology LDASを再起動、AWS LDASを停止 数分
Phase 3 Synology PT/token-keeperを再起動、AWS側を停止 数分
全体 全AWSサービスを停止、Synology全コンテナを再起動 10分以内

Synology は移行完了後も一定期間維持

Phase 3完了後も1ヶ月程度はSynologyを「コールドスタンバイ」として維持する。 AWS側で想定外の問題が発生した場合に即座にロールバック可能。


将来的な拡張

Fargate Spot(コスト最適化)

polygon-quotes や monitor など、中断耐性のあるタスクにFargate Spotを適用可能:

タスク Spot適用 理由
aegis-wft 不可 PT中断は損失リスク
aegis-token-keeper 不可 トークン切れはPT停止
aegis-quote-collector 不可 市場時間中のサンプリング欠損
aegis-ldas-intraday 不可 スナップショット欠損
aegis-ldas-uwflow 中断しても次の60秒で復帰
aegis-ldas-eod 失敗してもリトライ可能
aegis-polygon-quotes バックフィルなので中断耐性あり
aegis-dashboard 一時的なダウンは許容
aegis-monitor 60秒間隔なので中断影響小

Spot適用で月$20-30の削減が見込める。

BT on Fargate(オンデマンド計算)

バックテストをFargateで実行する将来構想。sweepで数十〜数百パターンを同時実行できるのが最大の価値。

単発BT実行

# Mac からBTをAWS上で実行
aws ecs run-task \
  --cluster aegis \
  --task-definition aegis-bt \
  --launch-type FARGATE \
  --overrides '{
    "containerOverrides": [{
      "name": "aegis-bt",
      "command": ["python", "scripts/run_backtest_multiple_scenarios.py",
                   "--scenario", "XW_BEST_20260310",
                   "--start", "2019-01-02", "--end", "2026-01-30",
                   "--deterministic", "--seed", "0"]
    }]
  }'

Sweep並列実行(Fargateの真価)

MBP 1台ではCPUコア数・メモリの物理制約で同時実行数が限られる(2並列が上限ルール)。 Fargateならタスク数に物理的な上限がないため、run-taskをN回叩けばN並列で走る。

# 例: TP × CR × DTE の組み合わせ100パターンを一括実行
for config in configs/sweep/*.yaml; do
  aws ecs run-task \
    --cluster aegis \
    --task-definition aegis-bt \
    --launch-type FARGATE \
    --overrides "{
      \"containerOverrides\": [{
        \"name\": \"aegis-bt\",
        \"command\": [\"python\", \"scripts/run_backtest_multiple_scenarios.py\",
                      \"--config\", \"$config\",
                      \"--start\", \"2024-01-02\", \"--end\", \"2026-01-30\",
                      \"--deterministic\", \"--seed\", \"0\",
                      \"--output-s3\", \"s3://aegis-bt-results/\"]
      }]
    }" &
done
wait
echo "全タスク投入完了。結果は S3 に出力される。"

Sweep コスト試算

Fargateは使った分だけ課金。タスクが完了すれば即停止・課金停止。

シナリオ タスク数 vCPU メモリ 実行時間 コスト
単発BT(25ヶ月) 1 2 4GB ~1h $0.10
Sweep 10パターン 10 2 4GB ~1h $1.00
Sweep 50パターン 50 2 4GB ~1h $5.00
Sweep 100パターン 100 2 4GB ~1h $10.00
大規模Sweep 100パターン (7年BT) 100 4 8GB ~3h $50.00

MBPでの待ち時間 vs Fargateコスト

100パターンのsweepをMBP(2並列制限)で実行すると、50ラウンド × 1h = 約50時間(2日以上)。 Fargateなら100並列で約1時間、コストは**\(10**。 月に数回のsweepなら月\)20-50程度の追加コストで、開発速度が劇的に向上する。

BT on Fargateの前提条件

  1. BTデータをS3に配置: QuantData CSVs + polygon_v2 parquets を S3バケットにアップロード
  2. 結果出力もS3: BTの出力(trades CSV, monthly CSV等)をS3に書き出す改修
  3. aegis-bt タスク定義: 2-4 vCPU、4-8GB メモリ(BTの複雑さに応じて)
  4. S3 → Mac同期: 結果をMacにダウンロードして分析(aws s3 sync
sequenceDiagram
    participant Mac as MBP
    participant S3 as S3 (BT Data)
    participant ECS as ECS Fargate
    participant S3R as S3 (Results)

    Mac->>ECS: run-task × N (sweep)
    ECS->>S3: Read BT data (parquets)
    ECS->>ECS: Execute backtest
    ECS->>S3R: Write results (CSV)
    ECS->>ECS: Task exits (課金停止)
    Mac->>S3R: aws s3 sync (結果取得)
    Mac->>Mac: 分析・比較

開発母艦の方針: MBP 1台体制

AWS移行後のMacの役割を整理すると、常時起動の必要がなくなる:

用途 現在の担当 AWS移行後 常時起動必要?
PT/WFT実行 Synology AWS Fargate
LDAS収集 Synology AWS Fargate
Dashboard Synology AWS Fargate
監視・通知 Synology AWS Fargate
開発(コード編集) Mac Mac 作業時のみ
バックテスト Mac Mac → Fargate 実行時のみ → 不要
git push(デプロイ起点) Mac Mac 作業時のみ

据え置き Mac Mini/Studio vs MBP 持ち歩き

観点 Mac Mini/Studio 据え置き(東京) MBP 持ち歩き
BT実行 高性能(M4 Pro/Max据え置き可) M4 Proなら十分、電源・発熱の制約あり
開発 リモート接続が必要(SSH/VNC) ローカルで直接作業
可用性 東京拠点の電源・ネットワーク障害で接続不能 手元にあるので確実
旅行時 別途MBPが必要(結局2台持ち) 1台で完結
BT on Fargate実装後 Macでの重い計算が不要に 同左

結論: MBP 1台で完結させる

  • AWS移行後、Macに常時起動の役割はない
  • BT on Fargateを実装すれば、ローカルで重い計算を走らせる必要もなくなる
  • パーマネントトラベラー生活で東京拠点のMac Miniへのリモート接続に依存するのは単一障害点を増やすだけ
  • 据え置きMacが必要なのは「重いBTをMacで回し続ける必要がある間」だけ
graph LR
    subgraph Current["現在の構成"]
        MM["Mac Mini (東京)<br/>開発 + BT"]
        SYN["Synology (東京)<br/>PT + LDAS + Dashboard"]
    end

    subgraph Future["AWS移行後の構成"]
        MBP["MBP (持ち歩き)<br/>開発 + git push のみ"]
        AWS["AWS us-east-1<br/>全サービス + BT"]
    end

    Current -->|移行| Future

    style SYN fill:#e74c3c,stroke:#c0392b,color:#fff
    style MM fill:#f39c12,stroke:#e67e22,color:#fff
    style MBP fill:#27ae60,stroke:#1e8449,color:#fff
    style AWS fill:#3498db,stroke:#2980b9,color:#fff

段階的な移行

  1. AWS移行完了 → Synology停止、Mac Miniは開発+BT用に残す
  2. BT on Fargate実装 → Mac Miniの役割がなくなる
  3. MBP 1台体制に移行、Mac Miniは売却 or 予備機として保管

AWSアカウント・認証構成(2026-03-25 構築済み)

アカウント情報

項目
アカウントID 677414637283
ルートメール fukutani.ryo@denovo-inc.jp
リージョン us-east-1 (バージニア北部)
組織ID o-ersk9r7lzm
SSO Start URL https://d-9066077395.awsapps.com/start
無料クレジット $100 USD(2026-09-25まで)

IAM Identity Center(SSO)

長期アクセスキーを使わないモダンな認証構成:

コンポーネント 認証方式 備考
人間(Console + CLI) IAM Identity Center SSO aws sso login --profile aegis
GitHub Actions CI/CD OIDC(OpenID Connect) アクセスキー不要
Fargateタスク IAM Task Role タスクに紐づくRoleで自動認証
Dashboard (Synology) IAM User(最小権限) CE読み取り専用 aegis-dashboard-cost-reader
# CLI SSO ログイン
aws sso login --profile aegis

# 認証確認
aws sts get-caller-identity

SSO設定ファイル (~/.aws/config):

[default]
sso_session = aegis-sso
sso_account_id = 677414637283
sso_role_name = AdministratorAccess
region = us-east-1
output = json

[sso-session aegis-sso]
sso_start_url = https://d-9066077395.awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:access

コスト監視体制(構築済み)

AWS Budgets アラート

月間予算 $500 に対して、以下のしきい値でアラート通知:

金額 予算比 メール通知 SMS通知
$30 6% o
$50 10% o
$100 20% o
$150 30% o
$200 40% o o
$250 50% o
$300 60% o o
$350 70% o
$400 80% o o
$500 100% o o
  • メール送信先: fukutani.ryo@a-t.link
  • SMS送信先: SNS Topic aegis-budget-sms+818017435786
  • Budget名: AEGIS-Monthly-Cost

ダッシュボード Cost パネル(実装済み)

Block Bダッシュボードに AWS Cost Explorer 連携パネルを実装済み:

  • Flask API: /api/block-b/aws-cost — 当月コスト・月末予測・Budget消化率・サービス別内訳・過去6ヶ月推移
  • React: AWSCostPanel コンポーネント — 1時間キャッシュ、サービス名自動簡略化
  • 認証: IAM User aegis-dashboard-cost-reader(CE + Budgets 読み取り専用)
  • 環境変数: AWS_COST_ACCESS_KEY_ID, AWS_COST_SECRET_ACCESS_KEY, AWS_ACCOUNT_ID

Cost Explorerの制約

新規AWSアカウントではCost Explorerのデータが利用可能になるまで24-48時間かかる。 データがない期間はパネルにエラーメッセージが表示される。


実装チェックリスト

移行実施時の作業一覧:

事前準備(構築済み)

  • AWSアカウント作成 (677414637283, us-east-1)
  • IAM Identity Center 有効化 + 管理者ユーザー ryo 作成
  • AWS CLI SSO設定 (~/.aws/config)
  • AWS Budgets アラート設定 (\(30〜\)500、メール + SMS)
  • SNS Topic aegis-budget-sms + SMS サブスクリプション
  • IAM User aegis-dashboard-cost-reader (CE読み取り専用)
  • ダッシュボード Cost パネル実装 (Flask API + React)

インフラ(IaC)

  • Terraform/CDK プロジェクト作成
  • VPC + Subnets + NAT Gateway
  • ECS Cluster
  • EFS + Access Points (tokens, state, live_data_archive, quote_samples)
  • ECR リポジトリ (aegis-wft, aegis-token-keeper, 等)
  • IAM Roles (ecsTaskExecutionRole, ecsTaskRole)
  • Secrets Manager (API keys)
  • CloudWatch Log Groups
  • EventBridge Rule (EOD schedule)
  • SNS Topics + Subscriptions (監視用)
  • Security Groups
  • GitHub Actions OIDC プロバイダ設定

アプリケーション改修

  • aegis-monitor: docker.sock → boto3 ECS API
  • aegis-monitor: /proc → CloudWatch Metrics API
  • aegis-monitor: ログ読取り → CloudWatch Logs Insights
  • aegis-ldas: supervisord → 3独立エントリーポイント分割
  • 全コンテナ: loguru sinkにstderr出力を追加(CloudWatch Logs収集用、ファイル出力も併用維持)
  • 全コンテナ: 環境変数からSecrets Manager値を読取り

BT on Fargate

  • S3バケット作成 (aegis-bt-data, aegis-bt-results)
  • BTデータ(QuantData CSVs + polygon_v2)をS3にアップロード
  • aegis-bt タスク定義作成 (2-4 vCPU, 4-8GB)
  • BT出力のS3書出し改修
  • sweep並列投入スクリプト作成
  • 結果集約・比較ツール作成

CI/CD

  • deploy-aws.yml ワークフロー作成
  • ECR push ステップ
  • ECS update-service ステップ
  • Synology用ワークフローは残す(ロールバック用)

検証

  • 全タスクの EFS マウント動作確認
  • CloudWatch Logs 出力確認
  • CloudWatch Alarms 発火テスト
  • ECS Exec 接続テスト
  • タスク異常停止 → 自動再起動テスト
  • ローリングデプロイテスト
  • ロールバック手順テスト

作成日: 2026-03-25 | 最終更新: 2026-03-25 | 担当: Claude Code