| # CI/CD & DevOps Enhancement Plan |
|
|
| ## Current State |
|
|
| **Existing**: |
| - Basic Go build workflow |
| - Dockerfile for containerization |
| - Docker Compose setup |
| - GoReleaser configuration |
|
|
| **Missing**: |
| - Comprehensive testing in CI |
| - Security scanning |
| - Multi-environment deployment |
| - Infrastructure as Code |
| - Automated rollback |
| - Release automation |
|
|
| ## Phase 1: Enhanced CI Pipeline (Week 1) |
|
|
| ### 1.1 Main CI Workflow |
| **File**: `.github/workflows/ci.yml` |
|
|
| ```yaml |
| name: CI |
| |
| on: |
| push: |
| branches: [main, develop] |
| tags: ['v*'] |
| pull_request: |
| branches: [main, develop] |
| |
| concurrency: |
| group: ${{ github.workflow }}-${{ github.ref }} |
| cancel-in-progress: true |
| |
| jobs: |
| lint: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| |
| - uses: actions/setup-go@v5 |
| with: |
| go-version: '1.24' |
| cache: true |
| |
| - name: golangci-lint |
| uses: golangci/golangci-lint-action@v6 |
| with: |
| version: latest |
| args: --timeout=5m --out-format=colored-line-number |
| |
| - name: Check formatting |
| run: | |
| fmt_files=$(gofmt -l .) |
| if [ -n "$fmt_files" ]; then |
| echo "The following files need formatting:" |
| echo "$fmt_files" |
| exit 1 |
| fi |
| |
| test: |
| runs-on: ubuntu-latest |
| services: |
| postgres: |
| image: postgres:16-alpine |
| env: |
| POSTGRES_PASSWORD: postgres |
| POSTGRES_DB: test |
| options: >- |
| --health-cmd pg_isready |
| --health-interval 10s |
| --health-timeout 5s |
| --health-retries 5 |
| ports: |
| - 5432:5432 |
| |
| redis: |
| image: redis:7-alpine |
| options: >- |
| --health-cmd "redis-cli ping" |
| --health-interval 10s |
| --health-timeout 5s |
| --health-retries 5 |
| ports: |
| - 6379:6379 |
| |
| minio: |
| image: minio/minio:latest |
| env: |
| MINIO_ROOT_USER: minioadmin |
| MINIO_ROOT_PASSWORD: minioadmin |
| ports: |
| - 9000:9000 |
| options: >- |
| server /data |
| |
| steps: |
| - uses: actions/checkout@v4 |
| |
| - uses: actions/setup-go@v5 |
| with: |
| go-version: '1.24' |
| cache: true |
| |
| - name: Download dependencies |
| run: go mod download |
| |
| - name: Run unit tests |
| run: go test -race -coverprofile=coverage.out ./internal/... ./sdk/... |
| env: |
| CGO_ENABLED: 1 |
| |
| - name: Run integration tests |
| run: go test -v -tags=integration ./test/integration/... |
| env: |
| TEST_POSTGRES_URL: postgres://postgres:postgres@localhost:5432/test?sslmode=disable |
| TEST_REDIS_URL: redis://localhost:6379 |
| TEST_MINIO_ENDPOINT: localhost:9000 |
| TEST_MINIO_ACCESS_KEY: minioadmin |
| TEST_MINIO_SECRET_KEY: minioadmin |
| |
| - name: Generate coverage report |
| run: | |
| go tool cover -html=coverage.out -o coverage.html |
| go tool cover -func=coverage.out |
| |
| - name: Upload coverage to Codecov |
| uses: codecov/codecov-action@v3 |
| with: |
| files: ./coverage.out |
| fail_ci_if_error: true |
| verbose: true |
| |
| build: |
| runs-on: ubuntu-latest |
| needs: [lint, test] |
| strategy: |
| matrix: |
| include: |
| - platform: linux/amd64 |
| goos: linux |
| goarch: amd64 |
| - platform: linux/arm64 |
| goos: linux |
| goarch: arm64 |
| - platform: darwin/amd64 |
| goos: darwin |
| goarch: amd64 |
| - platform: darwin/arm64 |
| goos: darwin |
| goarch: arm64 |
| - platform: windows/amd64 |
| goos: windows |
| goarch: amd64 |
| extension: .exe |
| |
| steps: |
| - uses: actions/checkout@v4 |
| |
| - uses: actions/setup-go@v5 |
| with: |
| go-version: '1.24' |
| cache: true |
| |
| - name: Build binary |
| env: |
| GOOS: ${{ matrix.goos }} |
| GOARCH: ${{ matrix.goarch }} |
| CGO_ENABLED: 0 |
| run: | |
| go build -ldflags "-s -w -X main.version=${{ github.ref_name }} -X main.commit=${{ github.sha }} -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ |
| -o dist/cliproxy-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.extension }} \ |
| ./cmd/server |
| |
| - name: Upload artifact |
| uses: actions/upload-artifact@v4 |
| with: |
| name: cliproxy-${{ matrix.goos }}-${{ matrix.goarch }} |
| path: dist/cliproxy-${{ matrix.goos }}-${{ matrix.goarch }}${{ matrix.extension }} |
| retention-days: 7 |
| |
| docker: |
| runs-on: ubuntu-latest |
| needs: [lint, test] |
| steps: |
| - uses: actions/checkout@v4 |
| |
| - name: Set up Docker Buildx |
| uses: docker/setup-buildx-action@v3 |
| |
| - name: Login to Docker Hub |
| if: github.event_name != 'pull_request' |
| uses: docker/login-action@v3 |
| with: |
| username: ${{ secrets.DOCKER_USERNAME }} |
| password: ${{ secrets.DOCKER_PASSWORD }} |
| |
| - name: Login to GHCR |
| if: github.event_name != 'pull_request' |
| uses: docker/login-action@v3 |
| with: |
| registry: ghcr.io |
| username: ${{ github.actor }} |
| password: ${{ secrets.GITHUB_TOKEN }} |
| |
| - name: Docker meta |
| id: meta |
| uses: docker/metadata-action@v5 |
| with: |
| images: | |
| ${{ secrets.DOCKER_USERNAME }}/cliproxy |
| ghcr.io/${{ github.repository }} |
| tags: | |
| type=ref,event=branch |
| type=ref,event=pr |
| type=semver,pattern={{version}} |
| type=semver,pattern={{major}}.{{minor}} |
| type=sha,prefix=,suffix=,format=short |
| |
| - name: Build and push |
| uses: docker/build-push-action@v5 |
| with: |
| context: . |
| platforms: linux/amd64,linux/arm64 |
| push: ${{ github.event_name != 'pull_request' }} |
| tags: ${{ steps.meta.outputs.tags }} |
| labels: ${{ steps.meta.outputs.labels }} |
| cache-from: type=gha |
| cache-to: type=gha,mode=max |
| build-args: | |
| VERSION=${{ github.ref_name }} |
| COMMIT=${{ github.sha }} |
| DATE=${{ github.event.head_commit.timestamp }} |
| |
| - name: Run Trivy vulnerability scanner |
| uses: aquasecurity/trivy-action@master |
| with: |
| image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }} |
| format: 'sarif' |
| output: 'trivy-results.sarif' |
| |
| - name: Upload Trivy results |
| uses: github/codeql-action/upload-sarif@v2 |
| if: always() |
| with: |
| sarif_file: 'trivy-results.sarif' |
| ``` |
|
|
| ### 1.2 PR Automation |
| **File**: `.github/workflows/pr-automation.yml` |
|
|
| ```yaml |
| name: PR Automation |
| |
| on: |
| pull_request: |
| types: [opened, synchronize, reopened, ready_for_review] |
| |
| jobs: |
| assign: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| |
| - name: Auto-assign PR |
| uses: actions/github-script@v7 |
| with: |
| script: | |
| const pr = context.payload.pull_request; |
| if (pr.user.type === 'User') { |
| github.rest.issues.addAssignees({ |
| owner: context.repo.owner, |
| repo: context.repo.repo, |
| issue_number: pr.number, |
| assignees: [pr.user.login] |
| }); |
| } |
| |
| size-label: |
| runs-on: ubuntu-latest |
| steps: |
| - name: Label based on size |
| uses: codelytv/pr-size-labeler@v1 |
| with: |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| xs_label: 'size/xs' |
| xs_max_size: 50 |
| s_label: 'size/s' |
| s_max_size: 200 |
| m_label: 'size/m' |
| m_max_size: 500 |
| l_label: 'size/l' |
| l_max_size: 1000 |
| xl_label: 'size/xl' |
| fail_if_xl: false |
| |
| dependency-review: |
| runs-on: ubuntu-latest |
| if: github.event_name == 'pull_request' |
| steps: |
| - uses: actions/checkout@v4 |
| - uses: actions/dependency-review-action@v3 |
| with: |
| fail-on-severity: high |
| ``` |
|
|
| ## Phase 2: Release Automation (Week 2) |
|
|
| ### 2.1 Release Workflow |
| **File**: `.github/workflows/release.yml` |
|
|
| ```yaml |
| name: Release |
| |
| on: |
| push: |
| tags: |
| - 'v*' |
| |
| permissions: |
| contents: write |
| packages: write |
| |
| jobs: |
| release: |
| runs-on: ubuntu-latest |
| steps: |
| - uses: actions/checkout@v4 |
| with: |
| fetch-depth: 0 |
| |
| - uses: actions/setup-go@v5 |
| with: |
| go-version: '1.24' |
| cache: true |
| |
| - name: Generate changelog |
| id: changelog |
| uses: mikepenz/release-changelog-builder-action@v4 |
| with: |
| configuration: .github/changelog-config.json |
| env: |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| |
| - name: Run GoReleaser |
| uses: goreleaser/goreleaser-action@v5 |
| with: |
| distribution: goreleaser |
| version: latest |
| args: release --clean |
| env: |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} |
| DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} |
| |
| deploy-hf: |
| runs-on: ubuntu-latest |
| needs: release |
| steps: |
| - uses: actions/checkout@v4 |
| |
| - name: Trigger Hugging Face Space update |
| run: | |
| curl -X POST \ |
| -H "Authorization: Bearer ${{ secrets.HF_TOKEN }}" \ |
| https://huggingface.co/api/spaces/${{ secrets.HF_SPACE }}/sync |
| |
| - name: Wait for deployment |
| run: | |
| sleep 60 |
| curl -s https://huggingface.co/api/spaces/${{ secrets.HF_SPACE }} | jq '.runtime.stage' |
| ``` |
|
|
| ### 2.2 GoReleaser Config |
| **File**: `.goreleaser.yml` |
|
|
| ```yaml |
| version: 2 |
| |
| before: |
| hooks: |
| - go mod tidy |
| - go generate ./... |
| |
| builds: |
| - id: cliproxy |
| main: ./cmd/server |
| binary: cliproxy |
| env: |
| - CGO_ENABLED=0 |
| goos: |
| - linux |
| - darwin |
| - windows |
| goarch: |
| - amd64 |
| - arm64 |
| goarm: |
| - '7' |
| ldflags: |
| - -s -w |
| - -X main.version={{.Version}} |
| - -X main.commit={{.Commit}} |
| - -X main.date={{.Date}} |
| |
| archives: |
| - id: cliproxy |
| name_template: >- |
| {{ .ProjectName }}_ |
| {{- title .Os }}_ |
| {{- if eq .Arch "amd64" }}x86_64 |
| {{- else if eq .Arch "386" }}i386 |
| {{- else }}{{ .Arch }}{{ end }} |
| format_overrides: |
| - goos: windows |
| format: zip |
| files: |
| - README.md |
| - LICENSE |
| - config.example.yaml |
| |
| dockers: |
| - image_templates: |
| - "ghcr.io/echyai/cliproxy:{{ .Tag }}-amd64" |
| - "echyai/cliproxy:{{ .Tag }}-amd64" |
| dockerfile: Dockerfile |
| use: buildx |
| build_flag_templates: |
| - --platform=linux/amd64 |
| - --label=org.opencontainers.image.title={{ .ProjectName }} |
| - --label=org.opencontainers.image.description={{ .ProjectName }} |
| - --label=org.opencontainers.image.url=https://github.com/echyai/cliproxy |
| - --label=org.opencontainers.image.source=https://github.com/echyai/cliproxy |
| - --label=org.opencontainers.image.version={{ .Version }} |
| - --label=org.opencontainers.image.created={{ .Date }} |
| - --label=org.opencontainers.image.revision={{ .FullCommit }} |
| - image_templates: |
| - "ghcr.io/echyai/cliproxy:{{ .Tag }}-arm64" |
| - "echyai/cliproxy:{{ .Tag }}-arm64" |
| dockerfile: Dockerfile |
| use: buildx |
| goarch: arm64 |
| build_flag_templates: |
| - --platform=linux/arm64 |
| - --label=org.opencontainers.image.title={{ .ProjectName }} |
| - --label=org.opencontainers.image.description={{ .ProjectName }} |
| - --label=org.opencontainers.image.url=https://github.com/echyai/cliproxy |
| - --label=org.opencontainers.image.source=https://github.com/echyai/cliproxy |
| - --label=org.opencontainers.image.version={{ .Version }} |
| - --label=org.opencontainers.image.created={{ .Date }} |
| - --label=org.opencontainers.image.revision={{ .FullCommit }} |
| |
| docker_manifests: |
| - name_template: "echyai/cliproxy:{{ .Tag }}" |
| image_templates: |
| - "echyai/cliproxy:{{ .Tag }}-amd64" |
| - "echyai/cliproxy:{{ .Tag }}-arm64" |
| - name_template: "echyai/cliproxy:latest" |
| image_templates: |
| - "echyai/cliproxy:{{ .Tag }}-amd64" |
| - "echyai/cliproxy:{{ .Tag }}-arm64" |
| - name_template: "ghcr.io/echyai/cliproxy:{{ .Tag }}" |
| image_templates: |
| - "ghcr.io/echyai/cliproxy:{{ .Tag }}-amd64" |
| - "ghcr.io/echyai/cliproxy:{{ .Tag }}-arm64" |
| - name_template: "ghcr.io/echyai/cliproxy:latest" |
| image_templates: |
| - "ghcr.io/echyai/cliproxy:{{ .Tag }}-amd64" |
| - "ghcr.io/echyai/cliproxy:{{ .Tag }}-arm64" |
| |
| changelog: |
| use: github |
| sort: asc |
| filters: |
| exclude: |
| - '^docs:' |
| - '^test:' |
| - '^chore:' |
| - '^ci:' |
| - Merge pull request |
| - Merge branch |
| |
| release: |
| github: |
| owner: echyai |
| name: cliproxy |
| draft: false |
| prerelease: auto |
| mode: replace |
| header: | |
| ## CLIProxyAPI {{ .Tag }} |
| |
| ### Docker Images |
| ```bash |
| docker pull echyai/cliproxy:{{ .Tag }} |
| docker pull ghcr.io/echyai/cliproxy:{{ .Tag }} |
| ``` |
| |
| footer: | |
| ## Quick Start |
| |
| ```bash |
| # Download and run |
| curl -L https://github.com/echyai/cliproxy/releases/download/{{ .Tag }}/cliproxy_Linux_x86_64.tar.gz | tar xz |
| ./cliproxy --config config.yaml |
| ``` |
| ``` |
| |
| ## Phase 3: Infrastructure as Code (Week 3) |
|
|
| ### 3.1 Terraform for AWS |
| **File**: `infrastructure/terraform/main.tf` |
|
|
| ```hcl |
| terraform { |
| required_version = ">= 1.5" |
| |
| required_providers { |
| aws = { |
| source = "hashicorp/aws" |
| version = "~> 5.0" |
| } |
| } |
| |
| backend "s3" { |
| bucket = "cliproxy-terraform-state" |
| key = "infrastructure/terraform.tfstate" |
| region = "us-east-1" |
| encrypt = true |
| dynamodb_table = "cliproxy-terraform-locks" |
| } |
| } |
| |
| provider "aws" { |
| region = var.aws_region |
| |
| default_tags { |
| tags = { |
| Project = "cliproxy" |
| Environment = var.environment |
| ManagedBy = "terraform" |
| } |
| } |
| } |
| |
| # VPC |
| module "vpc" { |
| source = "terraform-aws-modules/vpc/aws" |
| version = "~> 5.0" |
| |
| name = "${var.project_name}-${var.environment}" |
| cidr = var.vpc_cidr |
| |
| azs = var.availability_zones |
| private_subnets = var.private_subnets |
| public_subnets = var.public_subnets |
| |
| enable_nat_gateway = true |
| single_nat_gateway = var.environment != "production" |
| enable_dns_hostnames = true |
| enable_dns_support = true |
| |
| public_subnet_tags = { |
| "kubernetes.io/role/elb" = "1" |
| } |
| |
| private_subnet_tags = { |
| "kubernetes.io/role/internal-elb" = "1" |
| } |
| } |
| |
| # EKS Cluster |
| module "eks" { |
| source = "terraform-aws-modules/eks/aws" |
| version = "~> 19.0" |
| |
| cluster_name = "${var.project_name}-${var.environment}" |
| cluster_version = "1.29" |
| |
| vpc_id = module.vpc.vpc_id |
| subnet_ids = module.vpc.private_subnets |
| control_plane_subnet_ids = module.vpc.intra_subnets |
| cluster_endpoint_public_access = true |
| |
| eks_managed_node_groups = { |
| general = { |
| desired_size = var.node_desired_size |
| min_size = var.node_min_size |
| max_size = var.node_max_size |
| |
| instance_types = var.node_instance_types |
| capacity_type = var.environment == "production" ? "ON_DEMAND" : "SPOT" |
| |
| labels = { |
| workload = "general" |
| } |
| |
| update_config = { |
| max_unavailable_percentage = 25 |
| } |
| } |
| } |
| |
| # Fargate profiles for serverless workloads |
| fargate_profiles = { |
| default = { |
| name = "default" |
| selectors = [ |
| { namespace = "kube-system" }, |
| { namespace = "default" } |
| ] |
| } |
| } |
| } |
| |
| # RDS PostgreSQL |
| module "rds" { |
| source = "terraform-aws-modules/rds/aws" |
| version = "~> 6.0" |
| |
| identifier = "${var.project_name}-${var.environment}" |
| |
| engine = "postgres" |
| engine_version = "16" |
| family = "postgres16" |
| major_engine_version = "16" |
| instance_class = var.db_instance_class |
| |
| allocated_storage = var.db_allocated_storage |
| max_allocated_storage = var.db_max_allocated_storage |
| |
| db_name = var.db_name |
| username = var.db_username |
| port = 5432 |
| |
| multi_az = var.environment == "production" |
| db_subnet_group_name = module.vpc.database_subnet_group |
| vpc_security_group_ids = [aws_security_group.rds.id] |
| |
| backup_retention_period = var.environment == "production" ? 7 : 1 |
| backup_window = "03:00-04:00" |
| maintenance_window = "Mon:04:00-Mon:05:00" |
| |
| deletion_protection = var.environment == "production" |
| |
| enabled_cloudwatch_logs_exports = ["postgresql"] |
| |
| performance_insights_enabled = var.environment == "production" |
| } |
| |
| # ElastiCache Redis |
| module "redis" { |
| source = "terraform-aws-modules/elasticache/aws" |
| version = "~> 1.0" |
| |
| cluster_id = "${var.project_name}-${var.environment}" |
| |
| engine = "redis" |
| engine_version = "7.1" |
| node_type = var.redis_node_type |
| num_cache_nodes = 1 |
| |
| subnet_group_name = aws_elasticache_subnet_group.redis.name |
| security_group_ids = [aws_security_group.redis.id] |
| |
| at_rest_encryption_enabled = true |
| transit_encryption_enabled = true |
| } |
| |
| # Application Load Balancer |
| module "alb" { |
| source = "terraform-aws-modules/alb/aws" |
| version = "~> 9.0" |
| |
| name = "${var.project_name}-${var.environment}" |
| |
| load_balancer_type = "application" |
| |
| vpc_id = module.vpc.vpc_id |
| subnets = module.vpc.public_subnets |
| |
| security_groups = [aws_security_group.alb.id] |
| |
| listeners = { |
| https = { |
| port = 443 |
| protocol = "HTTPS" |
| certificate_arn = aws_acm_certificate.main.arn |
| |
| fixed_response = { |
| content_type = "text/plain" |
| message_body = "OK" |
| status_code = "200" |
| } |
| } |
| } |
| } |
| |
| # Route53 |
| resource "aws_route53_record" "main" { |
| zone_id = var.route53_zone_id |
| name = var.domain_name |
| type = "A" |
| |
| alias { |
| name = module.alb.dns_name |
| zone_id = module.alb.zone_id |
| evaluate_target_health = true |
| } |
| } |
| ``` |
|
|
| ### 3.2 Kubernetes Manifests |
| **File**: `infrastructure/k8s/base/deployment.yaml` |
|
|
| ```yaml |
| apiVersion: apps/v1 |
| kind: Deployment |
| metadata: |
| name: cliproxy |
| labels: |
| app: cliproxy |
| spec: |
| replicas: 3 |
| strategy: |
| type: RollingUpdate |
| rollingUpdate: |
| maxSurge: 25% |
| maxUnavailable: 10% |
| selector: |
| matchLabels: |
| app: cliproxy |
| template: |
| metadata: |
| labels: |
| app: cliproxy |
| annotations: |
| prometheus.io/scrape: "true" |
| prometheus.io/port: "8080" |
| prometheus.io/path: "/metrics" |
| spec: |
| serviceAccountName: cliproxy |
| securityContext: |
| runAsNonRoot: true |
| runAsUser: 1000 |
| fsGroup: 1000 |
| containers: |
| - name: cliproxy |
| image: echyai/cliproxy:latest |
| imagePullPolicy: Always |
| ports: |
| - name: http |
| containerPort: 8080 |
| protocol: TCP |
| securityContext: |
| allowPrivilegeEscalation: false |
| readOnlyRootFilesystem: true |
| capabilities: |
| drop: |
| - ALL |
| resources: |
| requests: |
| memory: "256Mi" |
| cpu: "250m" |
| limits: |
| memory: "1Gi" |
| cpu: "1000m" |
| livenessProbe: |
| httpGet: |
| path: /health |
| port: http |
| initialDelaySeconds: 30 |
| periodSeconds: 10 |
| timeoutSeconds: 5 |
| failureThreshold: 3 |
| readinessProbe: |
| httpGet: |
| path: /ready |
| port: http |
| initialDelaySeconds: 5 |
| periodSeconds: 5 |
| timeoutSeconds: 3 |
| failureThreshold: 3 |
| env: |
| - name: CONFIG_TYPE |
| value: "postgres" |
| - name: POSTGRES_HOST |
| valueFrom: |
| secretKeyRef: |
| name: cliproxy-db |
| key: host |
| - name: POSTGRES_PORT |
| valueFrom: |
| secretKeyRef: |
| name: cliproxy-db |
| key: port |
| - name: POSTGRES_DATABASE |
| valueFrom: |
| secretKeyRef: |
| name: cliproxy-db |
| key: database |
| - name: POSTGRES_USERNAME |
| valueFrom: |
| secretKeyRef: |
| name: cliproxy-db |
| key: username |
| - name: POSTGRES_PASSWORD |
| valueFrom: |
| secretKeyRef: |
| name: cliproxy-db |
| key: password |
| volumeMounts: |
| - name: tmp |
| mountPath: /tmp |
| - name: cache |
| mountPath: /cache |
| volumes: |
| - name: tmp |
| emptyDir: {} |
| - name: cache |
| emptyDir: |
| sizeLimit: 500Mi |
| --- |
| apiVersion: v1 |
| kind: Service |
| metadata: |
| name: cliproxy |
| labels: |
| app: cliproxy |
| spec: |
| type: ClusterIP |
| ports: |
| - port: 80 |
| targetPort: http |
| protocol: TCP |
| name: http |
| selector: |
| app: cliproxy |
| --- |
| apiVersion: networking.k8s.io/v1 |
| kind: Ingress |
| metadata: |
| name: cliproxy |
| annotations: |
| kubernetes.io/ingress.class: alb |
| alb.ingress.kubernetes.io/scheme: internet-facing |
| alb.ingress.kubernetes.io/target-type: ip |
| alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS":443}]' |
| alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:us-east-1:123456789012:certificate/... |
| spec: |
| rules: |
| - host: api.cliproxy.example.com |
| http: |
| paths: |
| - path: / |
| pathType: Prefix |
| backend: |
| service: |
| name: cliproxy |
| port: |
| number: 80 |
| ``` |
|
|
| ### 3.3 Kustomize Overlays |
| **File**: `infrastructure/k8s/overlays/production/kustomization.yaml` |
|
|
| ```yaml |
| apiVersion: kustomize.config.k8s.io/v1beta1 |
| kind: Kustomization |
| |
| namespace: cliproxy-production |
| |
| resources: |
| - ../../base |
| - hpa.yaml |
| - pdb.yaml |
| |
| replicas: |
| - name: cliproxy |
| count: 5 |
| |
| images: |
| - name: echyai/cliproxy |
| newTag: v1.2.3 |
| |
| patchesStrategicMerge: |
| - deployment-patch.yaml |
| |
| configMapGenerator: |
| - name: cliproxy-config |
| literals: |
| - LOG_LEVEL=info |
| - METRICS_ENABLED=true |
| |
| secretGenerator: |
| - name: cliproxy-db |
| envs: |
| - .env.db |
| |
| commonLabels: |
| environment: production |
| ``` |
|
|
| ## Phase 4: Monitoring Stack (Week 4) |
|
|
| ### 4.1 Prometheus Rules |
| **File**: `infrastructure/monitoring/prometheus-rules.yaml` |
|
|
| ```yaml |
| apiVersion: monitoring.coreos.com/v1 |
| kind: PrometheusRule |
| metadata: |
| name: cliproxy-alerts |
| spec: |
| groups: |
| - name: cliproxy |
| rules: |
| - alert: CLProxyHighErrorRate |
| expr: | |
| ( |
| sum(rate(cliproxy_http_requests_total{status=~"5.."}[5m])) |
| / |
| sum(rate(cliproxy_http_requests_total[5m])) |
| ) > 0.05 |
| for: 5m |
| labels: |
| severity: critical |
| annotations: |
| summary: "High error rate detected" |
| description: "Error rate is above 5% for 5 minutes" |
| |
| - alert: CLProxyHighLatency |
| expr: | |
| histogram_quantile(0.99, |
| sum(rate(cliproxy_http_request_duration_seconds_bucket[5m])) by (le) |
| ) > 2 |
| for: 5m |
| labels: |
| severity: warning |
| annotations: |
| summary: "High latency detected" |
| description: "P99 latency is above 2 seconds" |
| |
| - alert: CLProxyProviderErrors |
| expr: | |
| sum(rate(cliproxy_provider_requests_total{status="error"}[5m])) by (provider) > 10 |
| for: 5m |
| labels: |
| severity: warning |
| annotations: |
| summary: "Provider {{ $labels.provider }} has high error rate" |
| |
| - alert: CLProxyCircuitBreakerOpen |
| expr: cliproxy_circuit_breaker_state == 2 |
| for: 1m |
| labels: |
| severity: warning |
| annotations: |
| summary: "Circuit breaker is open" |
| description: "Circuit breaker {{ $labels.name }} has opened" |
| |
| - alert: CLProxyRateLimitHits |
| expr: | |
| sum(rate(cliproxy_rate_limit_hits_total[5m])) > 100 |
| for: 5m |
| labels: |
| severity: info |
| annotations: |
| summary: "High rate limit hits" |
| ``` |
|
|
| ### 4.2 Grafana Dashboard |
| **File**: `infrastructure/monitoring/dashboard.json` (simplified) |
|
|
| ```json |
| { |
| "dashboard": { |
| "title": "CLIProxyAPI", |
| "tags": ["cliproxy", "api"], |
| "timezone": "UTC", |
| "panels": [ |
| { |
| "title": "Request Rate", |
| "type": "graph", |
| "targets": [ |
| { |
| "expr": "sum(rate(cliproxy_http_requests_total[5m])) by (status)" |
| } |
| ] |
| }, |
| { |
| "title": "Latency", |
| "type": "graph", |
| "targets": [ |
| { |
| "expr": "histogram_quantile(0.99, sum(rate(cliproxy_http_request_duration_seconds_bucket[5m])) by (le))" |
| } |
| ] |
| }, |
| { |
| "title": "Provider Usage", |
| "type": "graph", |
| "targets": [ |
| { |
| "expr": "sum(rate(cliproxy_provider_requests_total[5m])) by (provider)" |
| } |
| ] |
| }, |
| { |
| "title": "Active Connections", |
| "type": "singlestat", |
| "targets": [ |
| { |
| "expr": "cliproxy_active_connections" |
| } |
| ] |
| } |
| ] |
| } |
| } |
| ``` |
|
|
| ## Success Metrics |
|
|
| - [ ] Build time < 5 minutes |
| - [ ] Test coverage > 70% |
| - [ ] Zero-downtime deployments |
| - [ ] Automated rollback capability |
| - [ ] Full observability (logs, metrics, traces) |
| - [ ] Infrastructure as Code coverage 100% |
| - [ ] Security scanning in CI |
| - [ ] Multi-arch container images |
|
|
| ## Deployment Flow |
|
|
| ``` |
| 1. Developer pushes code |
| β |
| 2. CI: Lint, Test, Build |
| β |
| 3. Docker image built (multi-arch) |
| β |
| 4. Security scan (Trivy) |
| β |
| 5. Push to registry |
| β |
| 6. Deploy to staging (auto) |
| β |
| 7. Integration tests |
| β |
| 8. Deploy to production (manual approval) |
| β |
| 9. Smoke tests |
| β |
| 10. Monitor and alert |
| ``` |
|
|