Some checks failed
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
mdBook Build & Deploy / Build mdBook (push) Has been cancelled
Nickel Type Check / Nickel Type Checking (push) Has been cancelled
mdBook Build & Deploy / Documentation Quality Check (push) Has been cancelled
mdBook Build & Deploy / Deploy to GitHub Pages (push) Has been cancelled
mdBook Build & Deploy / Notification (push) Has been cancelled
327 lines
11 KiB
YAML
327 lines
11 KiB
YAML
name: Deploy to Kubernetes
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
mode:
|
|
description: 'Deployment mode'
|
|
required: true
|
|
default: 'multiuser'
|
|
type: choice
|
|
options:
|
|
- solo
|
|
- multiuser
|
|
- enterprise
|
|
dry_run:
|
|
description: 'Perform dry-run (no actual deployment)'
|
|
required: false
|
|
default: 'true'
|
|
type: choice
|
|
options:
|
|
- 'true'
|
|
- 'false'
|
|
environment:
|
|
description: 'Target environment'
|
|
required: true
|
|
type: choice
|
|
options:
|
|
- staging
|
|
- production
|
|
rollout_timeout:
|
|
description: 'Rollout timeout in seconds'
|
|
required: false
|
|
default: '300'
|
|
type: string
|
|
|
|
concurrency:
|
|
group: k8s-deployment-${{ github.ref }}-${{ inputs.environment }}
|
|
cancel-in-progress: false
|
|
|
|
jobs:
|
|
deploy-kubernetes:
|
|
name: Deploy ${{ inputs.mode || 'multiuser' }} to K8s
|
|
runs-on: ubuntu-latest
|
|
environment: ${{ inputs.environment || 'staging' }}
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Download artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: deployment-artifacts
|
|
path: artifacts/
|
|
|
|
- name: Install Nushell
|
|
run: |
|
|
cargo install nu --locked
|
|
nu --version
|
|
|
|
- name: Install kubectl
|
|
uses: azure/setup-kubectl@v3
|
|
with:
|
|
version: 'latest'
|
|
|
|
- name: Configure kubeconfig
|
|
run: |
|
|
mkdir -p ~/.kube
|
|
echo "${{ secrets.KUBE_CONFIG_STAGING }}" | base64 -d > ~/.kube/config
|
|
chmod 600 ~/.kube/config
|
|
kubectl cluster-info
|
|
if: ${{ inputs.environment == 'staging' }}
|
|
|
|
- name: Configure kubeconfig (production)
|
|
run: |
|
|
mkdir -p ~/.kube
|
|
echo "${{ secrets.KUBE_CONFIG_PRODUCTION }}" | base64 -d > ~/.kube/config
|
|
chmod 600 ~/.kube/config
|
|
kubectl cluster-info
|
|
if: ${{ inputs.environment == 'production' }}
|
|
|
|
- name: Create VAPORA namespace
|
|
run: |
|
|
kubectl create namespace vapora --dry-run=client -o yaml | kubectl apply -f -
|
|
kubectl label namespace vapora environment=${{ inputs.environment }} --overwrite
|
|
|
|
- name: Create deployment directory
|
|
run: |
|
|
mkdir -p deploy/kubernetes
|
|
cp artifacts/configmap.yaml deploy/kubernetes/
|
|
cp artifacts/deployment.yaml deploy/kubernetes/
|
|
cp artifacts/vapora-${{ inputs.mode || 'multiuser' }}.yaml deploy/kubernetes/config.yaml
|
|
|
|
- name: Validate Kubernetes manifests
|
|
run: |
|
|
kubectl apply --dry-run=client -f deploy/kubernetes/configmap.yaml
|
|
kubectl apply --dry-run=client -f deploy/kubernetes/deployment.yaml
|
|
echo "✓ Kubernetes manifests validated"
|
|
|
|
- name: Show deployment diff (dry-run)
|
|
if: ${{ inputs.dry_run == 'true' }}
|
|
run: |
|
|
echo "🔍 Deployment diff (dry-run):"
|
|
kubectl apply --dry-run=server -f deploy/kubernetes/configmap.yaml -o yaml
|
|
kubectl apply --dry-run=server -f deploy/kubernetes/deployment.yaml -o yaml
|
|
|
|
- name: Deploy ConfigMap
|
|
if: ${{ inputs.dry_run == 'false' }}
|
|
run: |
|
|
echo "📋 Deploying ConfigMap..."
|
|
kubectl apply -f deploy/kubernetes/configmap.yaml
|
|
sleep 5
|
|
kubectl get configmap -n vapora
|
|
|
|
- name: Deploy Deployments
|
|
if: ${{ inputs.dry_run == 'false' }}
|
|
run: |
|
|
echo "🚀 Deploying services..."
|
|
kubectl apply -f deploy/kubernetes/deployment.yaml
|
|
kubectl get deployments -n vapora
|
|
|
|
- name: Wait for rollout (backend)
|
|
if: ${{ inputs.dry_run == 'false' }}
|
|
run: |
|
|
echo "⏳ Waiting for backend deployment..."
|
|
kubectl rollout status deployment/vapora-backend -n vapora --timeout=${{ inputs.rollout_timeout }}s
|
|
|
|
- name: Wait for rollout (agents)
|
|
if: ${{ inputs.dry_run == 'false' }}
|
|
run: |
|
|
echo "⏳ Waiting for agents deployment..."
|
|
kubectl rollout status deployment/vapora-agents -n vapora --timeout=${{ inputs.rollout_timeout }}s
|
|
|
|
- name: Wait for rollout (llm-router)
|
|
if: ${{ inputs.dry_run == 'false' }}
|
|
run: |
|
|
echo "⏳ Waiting for llm-router deployment..."
|
|
kubectl rollout status deployment/vapora-llm-router -n vapora --timeout=${{ inputs.rollout_timeout }}s
|
|
|
|
- name: Verify pod health
|
|
if: ${{ inputs.dry_run == 'false' }}
|
|
run: |
|
|
echo "🏥 Checking pod health..."
|
|
kubectl get pods -n vapora
|
|
echo ""
|
|
echo "Pod details:"
|
|
kubectl describe pods -n vapora | grep -A 5 "Status:"
|
|
|
|
- name: Check deployment status
|
|
if: always()
|
|
run: |
|
|
echo "📊 Deployment Status:"
|
|
kubectl get deployments -n vapora -o wide
|
|
echo ""
|
|
echo "📋 Services:"
|
|
kubectl get services -n vapora
|
|
echo ""
|
|
echo "🔧 ConfigMaps:"
|
|
kubectl get configmaps -n vapora
|
|
|
|
- name: Get service endpoints
|
|
if: ${{ inputs.dry_run == 'false' }}
|
|
run: |
|
|
echo "🌐 Service Endpoints:"
|
|
kubectl get services -n vapora -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.clusterIP}{"\n"}{end}'
|
|
|
|
- name: Save deployment manifest
|
|
if: success()
|
|
run: |
|
|
cat > deploy/kubernetes/DEPLOYMENT.md << 'EOF'
|
|
# Kubernetes Deployment Details
|
|
|
|
**Deployment Time**: $(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
|
**Mode**: ${{ inputs.mode || 'multiuser' }}
|
|
**Environment**: ${{ inputs.environment || 'staging' }}
|
|
**Namespace**: vapora
|
|
**Commit**: ${{ github.sha }}
|
|
**Workflow Run**: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
|
|
## Deployment Status
|
|
|
|
### Deployments
|
|
- **vapora-backend** - REST API server
|
|
- **vapora-agents** - Agent orchestration
|
|
- **vapora-llm-router** - LLM provider routing
|
|
|
|
### Configuration
|
|
- **ConfigMap**: vapora-config (environment data)
|
|
- **Namespace**: vapora (isolated environment)
|
|
|
|
## Kubernetes Commands
|
|
|
|
### View logs
|
|
```bash
|
|
# Backend logs
|
|
kubectl logs -f deployment/vapora-backend -n vapora
|
|
|
|
# Agents logs
|
|
kubectl logs -f deployment/vapora-agents -n vapora
|
|
|
|
# All pod logs
|
|
kubectl logs -f -l app=vapora -n vapora
|
|
```
|
|
|
|
### Check deployment status
|
|
```bash
|
|
kubectl get deployments -n vapora
|
|
kubectl get pods -n vapora
|
|
kubectl describe deployment vapora-backend -n vapora
|
|
```
|
|
|
|
### Rollback if needed
|
|
```bash
|
|
kubectl rollout undo deployment/vapora-backend -n vapora
|
|
kubectl rollout history deployment/vapora-backend -n vapora
|
|
```
|
|
|
|
### Port forwarding
|
|
```bash
|
|
kubectl port-forward -n vapora svc/vapora-backend 8001:8001
|
|
kubectl port-forward -n vapora svc/vapora-frontend 3000:3000
|
|
```
|
|
|
|
### Scale deployment
|
|
```bash
|
|
kubectl scale deployment vapora-backend --replicas=3 -n vapora
|
|
```
|
|
|
|
## Access Services
|
|
|
|
### Internal (ClusterIP)
|
|
- **Backend**: http://vapora-backend.vapora.svc.cluster.local:8001
|
|
- **Agents**: http://vapora-agents.vapora.svc.cluster.local:8002
|
|
- **LLM Router**: http://vapora-llm-router.vapora.svc.cluster.local:8003
|
|
- **Frontend**: http://vapora-frontend.vapora.svc.cluster.local:3000
|
|
|
|
### External (requires Ingress/LoadBalancer)
|
|
- Configure Ingress or LoadBalancer service
|
|
- See production documentation for external access setup
|
|
|
|
EOF
|
|
cat deploy/kubernetes/DEPLOYMENT.md
|
|
|
|
- name: Upload deployment manifests
|
|
if: always()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: k8s-deployment-${{ inputs.environment }}-${{ github.run_id }}
|
|
path: deploy/kubernetes/
|
|
retention-days: 30
|
|
|
|
- name: Create deployment annotation
|
|
if: ${{ inputs.dry_run == 'false' && success() }}
|
|
run: |
|
|
kubectl annotate deployment vapora-backend \
|
|
-n vapora \
|
|
deployment.kubernetes.io/revision=$(date +%s) \
|
|
github.deployment.run=${{ github.run_id }} \
|
|
github.deployment.commit=${{ github.sha }} \
|
|
--overwrite
|
|
|
|
- name: Post deployment summary
|
|
if: always()
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const mode = '${{ inputs.mode || "multiuser" }}';
|
|
const env = '${{ inputs.environment || "staging" }}';
|
|
const isDryRun = '${{ inputs.dry_run }}' === 'true';
|
|
|
|
let message = `${isDryRun ? '🔍' : '✅'} **Kubernetes deployment ${isDryRun ? 'validated' : 'successful'}!**\n\n`;
|
|
message += `**Mode**: ${mode}\n`;
|
|
message += `**Environment**: ${env}\n`;
|
|
message += `**Dry-run**: ${isDryRun ? 'Yes' : 'No'}\n`;
|
|
message += `**Namespace**: vapora\n\n`;
|
|
|
|
message += `**Deployments**:\n`;
|
|
message += `- vapora-backend\n`;
|
|
message += `- vapora-agents\n`;
|
|
message += `- vapora-llm-router\n\n`;
|
|
|
|
message += `**Useful Commands**:\n`;
|
|
message += `\`\`\`bash\n`;
|
|
message += `# View deployment status\n`;
|
|
message += `kubectl get deployments -n vapora\n\n`;
|
|
message += `# View logs\n`;
|
|
message += `kubectl logs -f deployment/vapora-backend -n vapora\n\n`;
|
|
message += `# Port forward\n`;
|
|
message += `kubectl port-forward -n vapora svc/vapora-backend 8001:8001\n`;
|
|
message += `\`\`\`\n`;
|
|
|
|
// Only post to PR if it's a PR event
|
|
if (context.eventName === 'pull_request' || context.payload.pull_request) {
|
|
github.rest.issues.createComment({
|
|
issue_number: context.issue.number,
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
body: message
|
|
});
|
|
}
|
|
|
|
- name: Notify Slack on success
|
|
if: success()
|
|
uses: 8398a7/action-slack@v3
|
|
with:
|
|
status: ${{ job.status }}
|
|
text: |
|
|
VAPORA Kubernetes deployment successful!
|
|
Mode: ${{ inputs.mode || 'multiuser' }}
|
|
Environment: ${{ inputs.environment || 'staging' }}
|
|
Namespace: vapora
|
|
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
|
|
fields: repo,message,commit,author
|
|
continue-on-error: true
|
|
|
|
- name: Notify Slack on failure
|
|
if: failure()
|
|
uses: 8398a7/action-slack@v3
|
|
with:
|
|
status: ${{ job.status }}
|
|
text: |
|
|
VAPORA Kubernetes deployment failed!
|
|
Mode: ${{ inputs.mode || 'multiuser' }}
|
|
Environment: ${{ inputs.environment || 'staging' }}
|
|
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
|
|
fields: repo,message,commit,author
|
|
continue-on-error: true
|