Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/runtime-guard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Runtime Guard

on:
workflow_dispatch:
inputs:
lookback_minutes:
description: "Cloud Logging lookback window in minutes."
required: false
type: string
default: "180"
require_success:
description: "Alert if no successful Cloud Run request exists in the lookback window."
required: false
type: choice
default: "false"
options:
- "false"
- "true"
fail_workflow_on_alert:
description: "Fail this workflow when an alert is emitted."
required: false
type: choice
default: "true"
options:
- "true"
- "false"
schedule:
- cron: "19,49 * * * *"

env:
GCP_PROJECT_ID: interactivebrokersquant
GCP_WORKLOAD_IDENTITY_PROVIDER: projects/303168642265/locations/global/workloadIdentityPools/github-actions/providers/github-main
GCP_WORKLOAD_IDENTITY_SERVICE_ACCOUNT: ibkr-platform-deploy@interactivebrokersquant.iam.gserviceaccount.com

jobs:
guard:
name: Check Cloud Run runtime
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
env:
RUNTIME_GUARD_NAME: InteractiveBrokersPlatform
RUNTIME_GUARD_CLOUD_RUN_SERVICES: ${{ vars.RUNTIME_GUARD_CLOUD_RUN_SERVICES }}
RUNTIME_GUARD_LOOKBACK_MINUTES: ${{ inputs.lookback_minutes || vars.RUNTIME_GUARD_LOOKBACK_MINUTES || '180' }}
RUNTIME_GUARD_REQUIRE_SUCCESS: ${{ inputs.require_success || vars.RUNTIME_GUARD_REQUIRE_SUCCESS || 'false' }}
RUNTIME_GUARD_FAIL_WORKFLOW_ON_ALERT: ${{ inputs.fail_workflow_on_alert || vars.RUNTIME_GUARD_FAIL_WORKFLOW_ON_ALERT || 'true' }}
RUNTIME_GUARD_SCHEDULER_JOB_PATTERN: ${{ vars.RUNTIME_GUARD_SCHEDULER_JOB_PATTERN }}
CLOUD_RUN_SERVICE: ${{ vars.CLOUD_RUN_SERVICE }}
CLOUD_RUN_SERVICES: ${{ vars.CLOUD_RUN_SERVICES }}
CLOUD_RUN_SERVICE_TARGETS_JSON: ${{ vars.CLOUD_RUN_SERVICE_TARGETS_JSON }}
GLOBAL_TELEGRAM_CHAT_ID: ${{ vars.GLOBAL_TELEGRAM_CHAT_ID }}
CRISIS_ALERT_TELEGRAM_CHAT_IDS: ${{ vars.CRISIS_ALERT_TELEGRAM_CHAT_IDS }}
CRISIS_ALERT_TELEGRAM_API_BASE_URL: ${{ vars.CRISIS_ALERT_TELEGRAM_API_BASE_URL }}
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
CRISIS_ALERT_TELEGRAM_BOT_TOKEN: ${{ secrets.CRISIS_ALERT_TELEGRAM_BOT_TOKEN }}
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v3
with:
workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ env.GCP_WORKLOAD_IDENTITY_SERVICE_ACCOUNT }}

- name: Set up gcloud
uses: google-github-actions/setup-gcloud@v3

- name: Check Cloud Scheduler and Cloud Run logs
run: python scripts/cloud_run_runtime_guard.py
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,35 @@ Important:
- GitHub now authenticates to Google Cloud with OIDC + Workload Identity Federation, so `GCP_SA_KEY` is no longer required for this workflow.
- GitHub deploy uses the repository Dockerfile and Artifact Registry. The deploy service account needs Artifact Registry writer, Cloud Run admin, and service-account user permissions for the runtime service account.

### Runtime guard alerting

`.github/workflows/runtime-guard.yml` is a second notification layer for failures
outside the IBKR Flask handler. It reads Cloud Logging for recent Cloud Scheduler
errors and Cloud Run request/runtime failures, then sends Telegram directly
through `CRISIS_ALERT_TELEGRAM_BOT_TOKEN` + `CRISIS_ALERT_TELEGRAM_CHAT_IDS` or
the fallback `TELEGRAM_TOKEN` + `GLOBAL_TELEGRAM_CHAT_ID`.

The guard does not invoke Cloud Run trading routes. It is meant to catch cases
where Scheduler cannot reach the service, OIDC/IAM/audience is wrong, Cloud Run
returns 4xx/5xx, or the container fails before app-level Telegram fallback code
can run.

Required setup:

- keep `CLOUD_RUN_SERVICES`, `CLOUD_RUN_SERVICE`, `CLOUD_RUN_SERVICE_TARGETS_JSON`,
or `RUNTIME_GUARD_CLOUD_RUN_SERVICES` populated with the service names to monitor
- grant the GitHub deploy service account `roles/logging.viewer` on
`interactivebrokersquant`
- keep Telegram chat/token variables or secrets configured in GitHub
- optionally set `RUNTIME_GUARD_SCHEDULER_JOB_PATTERN` to a regex that limits
Scheduler log checks to this deployment's jobs

The scheduled guard runs every 30 minutes. For a missed-run heartbeat, set
`RUNTIME_GUARD_REQUIRE_SUCCESS=true` and choose
`RUNTIME_GUARD_LOOKBACK_MINUTES` so the window covers the expected Scheduler run.
The default leaves the heartbeat check off to avoid false alerts outside the
active trading window.

### Deployment unit and naming

- `QuantPlatformKit` is only a shared dependency; Cloud Run now deploys `InteractiveBrokersPlatform`.
Expand Down Expand Up @@ -617,6 +646,29 @@ IB_GATEWAY_IP_MODE=internal
- GitHub 现在通过 OIDC + Workload Identity Federation 登录 Google Cloud,这个 workflow 不再需要 `GCP_SA_KEY`。
- GitHub 部署路径使用仓库里的 Dockerfile 和 Artifact Registry。部署服务账号需要 Artifact Registry 写入、Cloud Run 管理,以及对 runtime service account 的 service-account user 权限。

### Runtime Guard 告警

`.github/workflows/runtime-guard.yml` 是 IBKR Flask handler 之外的第二层通知。它只读取
Cloud Logging 中最近的 Cloud Scheduler 错误和 Cloud Run 请求/运行失败,然后直接通过
`CRISIS_ALERT_TELEGRAM_BOT_TOKEN` + `CRISIS_ALERT_TELEGRAM_CHAT_IDS` 或 fallback 的
`TELEGRAM_TOKEN` + `GLOBAL_TELEGRAM_CHAT_ID` 发 Telegram。

这个 guard 不会调用 Cloud Run 的交易路由,主要覆盖 Scheduler 没打到服务、
OIDC/IAM/audience 配错、Cloud Run 返回 4xx/5xx、或容器在 app-level Telegram fallback
执行前就失败的情况。

需要的配置:

- `CLOUD_RUN_SERVICES`、`CLOUD_RUN_SERVICE`、`CLOUD_RUN_SERVICE_TARGETS_JSON` 或
`RUNTIME_GUARD_CLOUD_RUN_SERVICES` 中至少有要监控的服务名
- GitHub deploy service account 需要 `interactivebrokersquant` 项目级 `roles/logging.viewer`
- GitHub 中继续配置 Telegram chat/token 变量或 secrets
- 可选设置 `RUNTIME_GUARD_SCHEDULER_JOB_PATTERN`,用正则把 Scheduler 日志限制到本部署的 job

默认计划每 30 分钟检查一次。若要做 missed-run 心跳,设置
`RUNTIME_GUARD_REQUIRE_SUCCESS=true`,并把 `RUNTIME_GUARD_LOOKBACK_MINUTES` 设成覆盖预期
Scheduler 运行时间的窗口。默认不强制心跳,避免非交易窗口误报。

### 部署单元和命名建议

- `QuantPlatformKit` 只是共享依赖,不单独部署;Cloud Run 现在部署的是 `InteractiveBrokersPlatform`。
Expand Down
Loading