Skip to content

Commit 8c2b6fb

Browse files
committed
infra: simplify staging control-plane and baremetal deploys
1 parent 949c185 commit 8c2b6fb

54 files changed

Lines changed: 3085 additions & 3230 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 66 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,104 @@
1+
# Baremetal Production Deploy
2+
#
3+
# Builds the agent + control-plane binaries, bakes a VM image on the production
4+
# host via Packer, and launches the new agent VM.
5+
#
6+
# Trigger: automatic on push to main (after PR has been validated on staging).
7+
# Dispatch inputs let you override GPU PCI address, memory, and CPU count —
8+
# but sensible defaults come from infra/ansible/inventory/production.yml.
9+
#
10+
# App deployment (private-llm etc.) is handled by the private-llm repo,
11+
# not here. This workflow owns infra only.
12+
113
name: Baremetal Production Deploy
214

315
on:
16+
push:
17+
branches: [main]
18+
paths:
19+
- "agent/**"
20+
- "control-plane/**"
21+
- "images/**"
22+
- "infra/**"
23+
- ".github/workflows/baremetal-*"
424
workflow_dispatch:
525
inputs:
626
vfio_device:
7-
description: "PCI address for GPU passthrough"
27+
description: "PCI address for GPU passthrough (e.g. 0d:00.0)"
828
default: "0d:00.0"
929
agent_memory:
10-
description: "Agent VM memory"
11-
default: "128G"
30+
description: "Agent VM memory (overrides inventory default)"
31+
default: ""
1232
agent_cpus:
13-
description: "Agent VM CPUs"
14-
default: "32"
33+
description: "Agent VM CPUs (overrides inventory default)"
34+
default: ""
1535

1636
concurrency:
1737
group: dd-baremetal-production
18-
cancel-in-progress: false
38+
cancel-in-progress: false # Never cancel an in-progress production deploy
1939

2040
permissions:
21-
id-token: write
2241
contents: read
2342

2443
jobs:
2544
deploy:
2645
runs-on: ubuntu-latest
2746
environment: production
2847
steps:
48+
# ── Build binaries ──────────────────────────────────────────────────
2949
- uses: actions/checkout@v4
50+
3051
- uses: dtolnay/rust-toolchain@stable
3152
- uses: Swatinem/rust-cache@v2
53+
54+
# Build both dd-agent and dd-cp in release mode
3255
- run: cargo build --workspace --release
56+
3357
- run: pip install ansible
3458

59+
# ── SSH setup ───────────────────────────────────────────────────────
60+
# Write the deploy key to the path the inventory expects (/tmp/deploy-key)
3561
- name: Set up SSH
3662
run: |
37-
mkdir -p ~/.ssh
38-
echo "${{ secrets.BAREMETAL_SSH_KEY }}" > ~/.ssh/deploy_key
39-
chmod 600 ~/.ssh/deploy_key
40-
ssh-keyscan -H "${{ vars.BAREMETAL_PRODUCTION_HOST }}" >> ~/.ssh/known_hosts 2>/dev/null
63+
echo "${{ secrets.BAREMETAL_SSH_KEY }}" > /tmp/deploy-key
64+
chmod 600 /tmp/deploy-key
65+
ssh-keyscan -H 162.222.34.121 >> ~/.ssh/known_hosts 2>/dev/null || true
4166
67+
# ── Deploy agent VM ─────────────────────────────────────────────────
68+
# Runs the baremetal-agent-deploy playbook against the production inventory.
69+
# Build-time vars and GPU passthrough are passed with -e.
70+
# Workflow dispatch inputs override inventory defaults when provided.
4271
- name: Deploy agent to production host
4372
env:
4473
ANSIBLE_HOST_KEY_CHECKING: "false"
4574
run: |
4675
sha12="$(echo "${{ github.sha }}" | cut -c1-12)"
76+
EXTRA_VARS=(
77+
-e "agent_binary_local=${GITHUB_WORKSPACE}/target/release/dd-agent"
78+
-e "cp_binary_local=${GITHUB_WORKSPACE}/target/release/dd-cp"
79+
-e "packer_template_dir=${GITHUB_WORKSPACE}/images/packer"
80+
-e "image_name=dd-baremetal-${sha12}"
81+
-e "vfio_device=${{ inputs.vfio_device || '0d:00.0' }}"
82+
)
83+
# Only override inventory defaults when the dispatch input is non-empty
84+
if [ -n "${{ inputs.agent_memory }}" ]; then
85+
EXTRA_VARS+=(-e "agent_memory=${{ inputs.agent_memory }}")
86+
fi
87+
if [ -n "${{ inputs.agent_cpus }}" ]; then
88+
EXTRA_VARS+=(-e "agent_cpus=${{ inputs.agent_cpus }}")
89+
fi
90+
EXTRA_VARS+=(
91+
-e "dd_intel_api_key=${{ secrets.INTEL_API_KEY }}"
92+
-e "dd_cp_admin_password=${{ secrets.DD_CP_ADMIN_PASSWORD }}"
93+
-e "dd_cp_cf_api_token=${{ secrets.DD_CP_CF_API_TOKEN }}"
94+
-e "dd_cp_cf_account_id=${{ secrets.DD_CP_CF_ACCOUNT_ID }}"
95+
-e "dd_cp_cf_zone_id=${{ secrets.DD_CP_CF_ZONE_ID }}"
96+
)
4797
ansible-playbook infra/ansible/playbooks/baremetal-agent-deploy.yml \
48-
-i "${{ vars.BAREMETAL_PRODUCTION_HOST }}," \
49-
-u "${{ vars.BAREMETAL_PRODUCTION_USER }}" \
50-
--private-key ~/.ssh/deploy_key \
51-
-e "agent_binary_local=${GITHUB_WORKSPACE}/target/release/dd-agent" \
52-
-e "cp_binary_local=${GITHUB_WORKSPACE}/target/release/dd-cp" \
53-
-e "packer_template_dir=${GITHUB_WORKSPACE}/images/packer" \
54-
-e "image_name=dd-baremetal-${sha12}" \
55-
-e "dd_env=production" \
56-
-e "cp_url=https://app.${{ vars.DD_CF_DOMAIN || 'devopsdefender.com' }}" \
57-
-e "dd_skip_attestation=true" \
58-
-e "agent_node_size=llm" \
59-
-e "agent_memory=${{ inputs.agent_memory }}" \
60-
-e "agent_cpus=${{ inputs.agent_cpus }}" \
61-
-e "vfio_device=${{ inputs.vfio_device }}"
62-
63-
- name: Deploy private-llm (H100 GPU mode)
64-
env:
65-
GH_TOKEN: ${{ github.token }}
66-
run: |
67-
CP_URL="https://app.${{ vars.DD_CF_DOMAIN || 'devopsdefender.com' }}"
68-
OIDC_TOKEN=$(curl -s -H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
69-
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=devopsdefender" | jq -r '.value')
70-
gh api repos/devopsdefender/private-llm/contents/docker-compose.h100.yml \
71-
-H "Accept: application/vnd.github.raw" > /tmp/compose.yml
72-
COMPOSE_B64=$(base64 -w0 /tmp/compose.yml)
73-
curl -sf -X POST "${CP_URL}/api/v1/deploy" \
74-
-H "Authorization: Bearer ${OIDC_TOKEN}" \
75-
-H "Content-Type: application/json" \
76-
-d "{\"app_name\":\"private-llm\",\"app_version\":\"${{ github.sha }}\",\"compose\":\"${COMPOSE_B64}\",\"node_size\":\"llm\"}"
98+
-i infra/ansible/inventory/production.yml \
99+
"${EXTRA_VARS[@]}"
77100
78-
- name: Cleanup
101+
# ── Cleanup ─────────────────────────────────────────────────────────
102+
- name: Cleanup SSH key
79103
if: always()
80-
run: rm -f ~/.ssh/deploy_key
104+
run: rm -f /tmp/deploy-key
Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1+
# Baremetal Staging Deploy
2+
#
3+
# Builds the agent + control-plane binaries, bakes a VM image on the staging
4+
# host via Packer, and launches the new agent VM.
5+
#
6+
# Trigger: automatic on pull_request targeting main (validates before merge).
7+
# Host/user/memory/cpus come from infra/ansible/inventory/staging.yml — no
8+
# inline overrides needed.
9+
#
10+
# App deployment (private-llm etc.) is handled by the private-llm repo,
11+
# not here. This workflow owns infra only.
12+
113
name: Baremetal Staging Deploy
214

315
on:
4-
push:
16+
pull_request:
517
branches: [main]
618
paths:
719
- "agent/**"
@@ -16,67 +28,57 @@ concurrency:
1628
cancel-in-progress: true
1729

1830
permissions:
19-
id-token: write
2031
contents: read
2132

2233
jobs:
2334
deploy:
2435
runs-on: ubuntu-latest
25-
environment: staging
36+
2637
steps:
38+
# ── Build binaries ──────────────────────────────────────────────────
2739
- uses: actions/checkout@v4
40+
2841
- uses: dtolnay/rust-toolchain@stable
2942
- uses: Swatinem/rust-cache@v2
43+
44+
# Build both dd-agent and dd-cp in release mode
3045
- run: cargo build --workspace --release
46+
3147
- run: pip install ansible
3248

49+
# ── SSH setup ───────────────────────────────────────────────────────
50+
# Write the deploy key to the path the inventory expects (/tmp/deploy-key)
3351
- name: Set up SSH
3452
run: |
35-
mkdir -p ~/.ssh
36-
echo "${{ secrets.BAREMETAL_SSH_KEY }}" > ~/.ssh/deploy_key
37-
chmod 600 ~/.ssh/deploy_key
38-
ssh-keyscan -H "${{ vars.BAREMETAL_STAGING_HOST }}" >> ~/.ssh/known_hosts 2>/dev/null
53+
echo "${{ secrets.BAREMETAL_SSH_KEY }}" > /tmp/deploy-key
54+
chmod 600 /tmp/deploy-key
55+
ssh-keyscan -H 57.130.10.246 >> ~/.ssh/known_hosts 2>/dev/null || true
3956
57+
# ── Deploy agent VM ─────────────────────────────────────────────────
58+
# Runs the baremetal-agent-deploy playbook against the staging inventory.
59+
# Secrets are passed as Ansible extra-vars (never written to disk).
4060
- name: Deploy agent to staging host
4161
env:
4262
ANSIBLE_HOST_KEY_CHECKING: "false"
4363
run: |
4464
sha12="$(echo "${{ github.sha }}" | cut -c1-12)"
65+
EXTRA_VARS=(
66+
-e "agent_binary_local=${GITHUB_WORKSPACE}/target/release/dd-agent"
67+
-e "cp_binary_local=${GITHUB_WORKSPACE}/target/release/dd-cp"
68+
-e "packer_template_dir=${GITHUB_WORKSPACE}/images/packer"
69+
-e "image_name=dd-baremetal-${sha12}"
70+
-e "dd_cp_admin_password=${{ secrets.DD_CP_ADMIN_PASSWORD }}"
71+
-e "dd_cp_cf_api_token=${{ secrets.DD_CP_CF_API_TOKEN }}"
72+
-e "dd_cp_cf_account_id=${{ secrets.DD_CP_CF_ACCOUNT_ID }}"
73+
-e "dd_cp_cf_zone_id=${{ secrets.DD_CP_CF_ZONE_ID }}"
74+
-e "dd_intel_api_key=${{ secrets.INTEL_API_KEY }}"
75+
-e "dd_cp_git_sha=${sha12}"
76+
)
4577
ansible-playbook infra/ansible/playbooks/baremetal-agent-deploy.yml \
46-
-i "${{ vars.BAREMETAL_STAGING_HOST }}," \
47-
-u "${{ vars.BAREMETAL_STAGING_USER }}" \
48-
--private-key ~/.ssh/deploy_key \
49-
-e "agent_binary_local=${GITHUB_WORKSPACE}/target/release/dd-agent" \
50-
-e "cp_binary_local=${GITHUB_WORKSPACE}/target/release/dd-cp" \
51-
-e "packer_template_dir=${GITHUB_WORKSPACE}/images/packer" \
52-
-e "image_name=dd-baremetal-${sha12}" \
53-
-e "dd_env=staging" \
54-
-e "cp_url=https://app-staging.${{ vars.DD_CF_DOMAIN || 'devopsdefender.com' }}" \
55-
-e "dd_skip_attestation=true" \
56-
-e "agent_node_size=standard"
57-
58-
- name: Deploy private-llm (CPU mode)
59-
env:
60-
GH_TOKEN: ${{ github.token }}
61-
run: |
62-
CP_URL="https://app-staging.${{ vars.DD_CF_DOMAIN || 'devopsdefender.com' }}"
63-
OIDC_TOKEN=$(curl -s -H "Authorization: bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \
64-
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=devopsdefender" | jq -r '.value')
65-
gh api repos/devopsdefender/private-llm/contents/docker-compose.yml \
66-
-H "Accept: application/vnd.github.raw" > /tmp/compose.yml
67-
COMPOSE_B64=$(base64 -w0 /tmp/compose.yml)
68-
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST "${CP_URL}/api/v1/deploy" \
69-
-H "Authorization: Bearer ${OIDC_TOKEN}" \
70-
-H "Content-Type: application/json" \
71-
-d "{\"app_name\":\"private-llm\",\"app_version\":\"${{ github.sha }}\",\"compose\":\"${COMPOSE_B64}\",\"node_size\":\"standard\"}")
72-
HTTP_CODE=$(echo "${RESPONSE}" | tail -1)
73-
BODY=$(echo "${RESPONSE}" | sed '$d')
74-
echo "HTTP ${HTTP_CODE}: ${BODY}"
75-
if [ "${HTTP_CODE}" -lt 200 ] || [ "${HTTP_CODE}" -ge 300 ]; then
76-
echo "::error::LLM deploy failed: ${BODY}"
77-
exit 1
78-
fi
78+
-i infra/ansible/inventory/staging.yml \
79+
"${EXTRA_VARS[@]}"
7980
80-
- name: Cleanup
81+
# ── Cleanup ─────────────────────────────────────────────────────────
82+
- name: Cleanup SSH key
8183
if: always()
82-
run: rm -f ~/.ssh/deploy_key
84+
run: rm -f /tmp/deploy-key

.github/workflows/staging-deploy.yml

Lines changed: 0 additions & 77 deletions
This file was deleted.

CLAUDE.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,8 @@ Central management API for agent registration, deployment orchestration, health
106106

107107
**Deployment:** Multi-stage Dockerfile (Rust builder → debian bookworm-slim runtime with cloudflared).
108108

109-
**Infrastructure** (`infra/`): Ansible playbooks for GCP and baremetal deployments. Key playbooks:
110-
- `gcp-control-plane-new.yml` — launch TDX-enabled GCP VM for control plane
111-
- `gcp-vm-fleet-new.yml` — launch agent fleet
112-
- `gcp-image-bake.yml` — build VM images
113-
- `baremetal-deploy.yml` — bare metal deployment
109+
**Infrastructure** (`infra/`): Ansible playbooks for deploying agent VMs via KVM on dedicated OVH hardware. Key playbook:
110+
- `baremetal-agent-deploy.yml` — build image via Packer + deploy agent as KVM VM on OVH dedicated server
114111

115112
### images/ (Packer)
116113

@@ -201,9 +198,9 @@ cd private-llm && docker compose up
201198

202199
All infrastructure is managed through GitHub Actions. **Never SSH into hosts, run Ansible locally, or attempt manual fixes on VMs.** If staging or production is down, trigger the appropriate GitHub Actions workflow.
203200

204-
**Staging** (`staging-deploy.yml`) — auto-deploys on push to `main`. Pipeline: build → bake GCP agent image → cleanup old VMs → deploy via Ansiblesmoke check `app-staging.devopsdefender.com/health`.
201+
**Staging** (`baremetal-staging-deploy.yml`) — auto-deploys on push to `main`. Deploys the agent as a KVM VM on a dedicated OVH server. Pipeline: build → Packer bake on host → deploy agent VM via KVMdeploy private-llm.
205202

206-
**Production** (`production-deploy.yml`) — manual trigger only (`workflow_dispatch`). Inputs: `num_tiny_agents`, `num_standard_agents`, `num_llm_agents`. Same pipeline as staging.
203+
**Production** (`baremetal-production-deploy.yml`) — manual trigger only (`workflow_dispatch`). Deploys the agent as a KVM VM on dedicated OVH hardware with GPU passthrough. Inputs: `vfio_device`, `agent_memory`, `agent_cpus`.
207204

208205
**Health check URLs:**
209206
- Staging: `https://app-staging.devopsdefender.com/health`
@@ -212,18 +209,18 @@ All infrastructure is managed through GitHub Actions. **Never SSH into hosts, ru
212209
**To deploy or fix an environment**, use `gh workflow run` or the GitHub Actions UI:
213210
```bash
214211
# Trigger staging deploy
215-
gh workflow run staging-deploy.yml --repo devopsdefender/control-plane
212+
gh workflow run baremetal-staging-deploy.yml
216213

217214
# Trigger production deploy
218-
gh workflow run production-deploy.yml --repo devopsdefender/control-plane \
219-
-f num_tiny_agents=0 -f num_standard_agents=0 -f num_llm_agents=1
215+
gh workflow run baremetal-production-deploy.yml \
216+
-f vfio_device=0d:00.0 -f agent_memory=128G -f agent_cpus=32
220217
```
221218

222219
## CI/CD Pipelines
223220

224221
Each component has its own GitHub Actions workflows:
225222
- **agent**: `ci.yml` (check/test/fmt/clippy), `release.yml` (build binary + GitHub release)
226-
- **control-plane**: `ci.yml`, `staging-deploy.yml`, `production-deploy.yml`, `release.yml`
223+
- **control-plane**: `ci.yml`, `baremetal-staging-deploy.yml`, `baremetal-production-deploy.yml`, `release.yml`
227224
- **images**: `baremetal-image.yml` (Packer build on self-hosted runner)
228225
- **private-llm**: `deploy.yml` (OIDC-authenticated deployment to DD platform)
229226
- **website**: `pages.yml` (GitHub Pages deployment)

0 commit comments

Comments
 (0)