Best Practices CI/CD
Pengenalan
Dokumen ini berisi kumpulan best practices untuk implementasi CI/CD dengan Gitea, Gitea Runner, dan Kubernetes. Mengikuti panduan ini akan membantu memastikan pipeline CI/CD yang reliable, secure, dan maintainable.
1. Repository Structure
Organized Directory Layout
project-root/
├── .gitea/
│ └── workflows/
│ ├── ci.yml # Continuous Integration
│ ├── cd.yml # Continuous Deployment
│ ├── release.yml # Release management
│ └── cleanup.yml # Maintenance tasks
├── k8s/
│ ├── base/ # Base configurations
│ │ ├── deployment.yaml
│ │ ├── service.yaml
│ │ └── kustomization.yaml
│ ├── overlays/
│ │ ├── dev/ # Development environment
│ │ ├── staging/ # Staging environment
│ │ └── production/ # Production environment
│ └── README.md
├── scripts/
│ ├── build.sh
│ ├── test.sh
│ └── deploy.sh
├── docs/ # Documentation
├── src/ # Source code
├── tests/ # Test files
├── .dockerignore
├── .gitignore
├── Dockerfile
├── Dockerfile.dev
└── README.md
Keuntungan Struktur Ini
- Separation of concerns: Workflow, Kubernetes manifests, dan scripts terpisah
- Environment management: Overlay untuk multiple environments
- Maintainability: Mudah di-navigate dan di-update
- Documentation: Dedicated folder untuk docs
2. Workflow Design Patterns
Pattern 1: Single Pipeline
Satu workflow untuk semua stages.
name: Complete Pipeline
on:
push:
branches: [main, develop]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run build
test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm test
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: ./scripts/deploy.sh
Kapan digunakan:
- Small to medium projects
- Simple deployment process
- Quick feedback needed
Pattern 2: Separate Workflows
Multiple workflows untuk different concerns.
# .gitea/workflows/ci.yml
name: CI
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: npm test
---
# .gitea/workflows/cd.yml
name: CD
on:
workflow_run:
workflows: ["CI"]
types: [completed]
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: ./scripts/deploy.sh
Kapan digunakan:
- Large projects
- Complex deployment process
- Different triggers untuk CI dan CD
Pattern 3: Reusable Workflows
# .gitea/workflows/reusable-build.yml
name: Reusable Build
on:
workflow_call:
inputs:
node-version:
required: true
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ inputs.node-version }}
- run: npm ci
- run: npm run build
---
# .gitea/workflows/main.yml
name: Main Pipeline
on: [push]
jobs:
build-node-18:
uses: ./.gitea/workflows/reusable-build.yml
with:
node-version: '18'
build-node-20:
uses: ./.gitea/workflows/reusable-build.yml
with:
node-version: '20'
3. Secrets Management
Hierarchy of Secrets
Repository Secrets
├── Development
│ ├── DEV_API_KEY
│ └── DEV_DB_PASSWORD
├── Staging
│ ├── STAGING_API_KEY
│ └── STAGING_DB_PASSWORD
└── Production
├── PROD_API_KEY
└── PROD_DB_PASSWORD
Best Practices
1. Environment-specific Secrets
jobs:
deploy-staging:
environment: staging
steps:
- name: Deploy
env:
API_KEY: ${{ secrets.STAGING_API_KEY }}
2. Never Log Secrets
- name: Use secret
run: |
# BAD - akan log secret
echo "API_KEY=${{ secrets.API_KEY }}"
# GOOD - tidak log secret
export API_KEY="${{ secrets.API_KEY }}"
./script.sh
3. Rotate Regularly
# Script untuk rotate secrets
#!/bin/bash
NEW_TOKEN=$(generate_token)
kubectl create secret generic app-secret \
--from-literal=token=$NEW_TOKEN \
--dry-run=client -o yaml | kubectl apply -f -
4. Use External Secret Managers
# Menggunakan external-secrets operator
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: gitea-docs-secrets
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: gitea-docs-secrets
data:
- secretKey: api-key
remoteRef:
key: secret/data/app
property: api-key
4. Docker Best Practices
Multi-stage Builds
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production=false
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine AS production
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Image Tagging Strategy
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
Contoh hasil:
main→latest,main-abc1234develop→develop,develop-def5678v1.2.3→1.2.3,1.2,v1.2.3
Security Scanning
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: myimage:latest
format: 'table'
exit-code: '1'
ignore-unfixed: true
severity: 'CRITICAL,HIGH'
Image Optimization
# Use specific versions
FROM node:20.10.0-alpine3.18
# Combine RUN commands
RUN apk add --no-cache git curl && \
npm ci && \
npm cache clean --force
# Use .dockerignore
# .dockerignore file:
node_modules
.git
.gitignore
*.md
tests/
5. Testing Strategy
Test Pyramid
/\
/ \
/ E2E \ E2E Tests (Few)
/------\
/ \
/Integration\ Integration Tests (Some)
/------------\
/ \
/ Unit Tests \ Unit Tests (Many)
------------------
Implement in CI/CD
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- run: npm run test:unit
- uses: codecov/codecov-action@v3
integration-tests:
needs: unit-tests
runs-on: ubuntu-latest
steps:
- run: npm run test:integration
e2e-tests:
needs: integration-tests
runs-on: ubuntu-latest
steps:
- run: npm run test:e2e
Parallel Testing
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
test-group: [unit, integration, e2e]
node-version: [18, 20, 22]
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm run test:${{ matrix.test-group }}
6. Deployment Strategies
Blue-Green Deployment
# Deployment blue
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-blue
spec:
replicas: 3
template:
metadata:
labels:
app: myapp
version: blue
---
# Deployment green
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-green
spec:
replicas: 3
template:
metadata:
labels:
app: myapp
version: green
---
# Service switch
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
version: green # Switch to green
Workflow:
jobs:
deploy:
steps:
- name: Deploy to green
run: kubectl apply -f k8s/deployment-green.yaml
- name: Wait for green to be ready
run: kubectl rollout status deployment/app-green
- name: Run smoke tests
run: ./scripts/smoke-test.sh green
- name: Switch traffic to green
run: kubectl patch service myapp -p '{"spec":{"selector":{"version":"green"}}}'
- name: Verify production
run: ./scripts/verify.sh
- name: Scale down blue
run: kubectl scale deployment/app-blue --replicas=0
Canary Deployment
# Main deployment (90%)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-stable
spec:
replicas: 9
template:
metadata:
labels:
app: myapp
track: stable
---
# Canary deployment (10%)
apiVersion: apps/v1
kind: Deployment
metadata:
name: app-canary
spec:
replicas: 1
template:
metadata:
labels:
app: myapp
track: canary
spec:
containers:
- name: app
image: myapp:new-version
Rolling Update (Recommended)
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
replicas: 3
Workflow:
jobs:
deploy:
steps:
- name: Update image
run: |
kubectl set image deployment/myapp \
myapp=myapp:${{ github.sha }}
- name: Monitor rollout
run: |
kubectl rollout status deployment/myapp --timeout=5m
- name: Verify deployment
run: ./scripts/verify.sh
- name: Rollback on failure
if: failure()
run: kubectl rollout undo deployment/myapp
7. Monitoring dan Observability
Logging Best Practices
# Structured logging
- name: Log structured data
run: |
echo "::group::Build Information"
echo "Commit: ${{ github.sha }}"
echo "Branch: ${{ github.ref }}"
echo "Author: ${{ github.actor }}"
echo "::endgroup::"
Metrics Collection
# Prometheus metrics
apiVersion: v1
kind: Service
metadata:
name: myapp
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
Alerting
# AlertManager configuration
groups:
- name: deployment_alerts
rules:
- alert: DeploymentFailed
expr: kube_deployment_status_replicas_unavailable > 0
for: 5m
labels:
severity: critical
annotations:
summary: "Deployment {{ $labels.deployment }} has unavailable replicas"
Dashboard
# Grafana dashboard JSON
{
"dashboard": {
"title": "CI/CD Pipeline",
"panels": [
{
"title": "Build Duration",
"targets": [{
"expr": "workflow_run_duration_seconds"
}]
},
{
"title": "Deployment Frequency",
"targets": [{
"expr": "rate(deployments_total[1h])"
}]
}
]
}
}
8. Performance Optimization
Caching Strategy
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
~/.npm
~/.cache
node_modules
key: ${{ runner.os }}-deps-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-deps-
- name: Cache build output
uses: actions/cache@v3
with:
path: build/
key: build-${{ github.sha }}
Docker Layer Caching
- name: Build with cache
uses: docker/build-push-action@v4
with:
context: .
cache-from: type=registry,ref=myimage:buildcache
cache-to: type=registry,ref=myimage:buildcache,mode=max
Artifact Management
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: build-artifacts
path: build/
retention-days: 7 # Auto-cleanup after 7 days
- name: Download artifacts
uses: actions/download-artifact@v3
with:
name: build-artifacts
9. Security Best Practices
1. Scan Dependencies
- name: Audit dependencies
run: npm audit --audit-level=moderate
- name: Check for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
2. Image Scanning
- name: Scan Docker image
uses: aquasecurity/trivy-action@master
with:
image-ref: myimage:latest
severity: CRITICAL,HIGH
exit-code: 1
3. SBOM Generation
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: myimage:latest
format: spdx-json
output-file: sbom.spdx.json
4. Sign Images
- name: Install Cosign
uses: sigstore/cosign-installer@main
- name: Sign image
run: |
cosign sign --key cosign.key myimage:latest
5. Policy Enforcement
# OPA Policy
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
not input.request.object.spec.securityContext.runAsNonRoot
msg = "Containers must not run as root"
}
10. Error Handling
Retry Logic
- name: Deploy with retry
uses: nick-invision/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
retry_wait_seconds: 30
command: kubectl apply -f k8s/
Graceful Degradation
- name: Deploy to primary
id: primary
run: ./deploy.sh primary
continue-on-error: true
- name: Deploy to backup
if: steps.primary.outcome == 'failure'
run: ./deploy.sh backup
Notification on Failure
- name: Notify on failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Deployment failed! 🔴'
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
11. Documentation
Workflow Documentation
name: Build and Deploy
# Purpose: Build Docker image and deploy to Kubernetes
# Triggers: Push to main branch
# Requirements:
# - DOCKER_USERNAME secret
# - DOCKER_PASSWORD secret
# - KUBE_CONFIG secret
on:
push:
branches: [main]
README Template
# Project CI/CD
## Pipeline Overview
- **CI**: Runs on every push and PR
- **CD**: Deploys to staging on merge to develop
- **Production**: Manual approval required
## Required Secrets
| Secret | Description | Where to get |
|--------|-------------|--------------|
| DOCKER_USERNAME | Docker Hub username | Docker Hub account |
| DOCKER_PASSWORD | Docker Hub token | Docker Hub → Security |
| KUBE_CONFIG | Kubernetes config | Base64 encoded kubeconfig |
## Deployment Process
1. Code merged to main
2. CI pipeline runs tests
3. Docker image built and pushed
4. Deployed to staging automatically
5. Manual approval for production
6. Deployed to production
12. Cost Optimization
Efficient Runner Usage
# Use smaller runners for simple tasks
jobs:
lint:
runs-on: ubuntu-latest # Sufficient for linting
build:
runs-on: ubuntu-latest-4core # Heavy build tasks
Conditional Execution
jobs:
deploy:
# Only deploy on main branch
if: github.ref == 'refs/heads/main'
expensive-tests:
# Only on tags
if: startsWith(github.ref, 'refs/tags/')
Resource Limits
spec:
containers:
- name: app
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
13. Compliance dan Audit
Audit Logging
- name: Log deployment
run: |
echo "Deployment initiated" >> audit.log
echo "User: ${{ github.actor }}" >> audit.log
echo "Time: $(date -u)" >> audit.log
echo "Version: ${{ github.sha }}" >> audit.log
Change Approval
jobs:
deploy-production:
environment:
name: production
# Requires manual approval from protected reviewers
steps:
- run: ./deploy.sh
Compliance Checks
- name: Check compliance
run: |
# Verify all required labels exist
kubectl get pods -n production -o json | \
jq '.items[] | select(.metadata.labels.owner == null)'
14. Troubleshooting Guide
Common Issues
Issue 1: Pipeline Timeout
# Increase timeout
jobs:
build:
timeout-minutes: 30 # Default is 360
Issue 2: Intermittent Failures
# Add retry logic
- name: Flaky test
uses: nick-invision/retry@v2
with:
max_attempts: 3
command: npm test
Issue 3: Resource Constraints
# Optimize resource usage
- name: Build
run: |
export NODE_OPTIONS="--max-old-space-size=4096"
npm run build
Debug Mode
- name: Debug information
run: |
echo "::debug::Current directory: $(pwd)"
echo "::debug::Environment: $(env | sort)"
echo "::debug::Disk usage: $(df -h)"
15. Checklist untuk Production
Pre-deployment Checklist
- All tests passing
- Security scans completed
- Documentation updated
- Secrets configured
- Monitoring setup
- Backup strategy in place
- Rollback plan tested
- Team notified
Post-deployment Checklist
- Verify application health
- Check logs for errors
- Monitor metrics
- Verify database migrations
- Test critical user paths
- Update status page
- Document changes
Kesimpulan
Mengikuti best practices ini akan membantu:
- ✅ Reliability: Pipeline yang stable dan predictable
- ✅ Security: Protection terhadap vulnerabilities
- ✅ Performance: Faster build dan deployment
- ✅ Maintainability: Easy to understand dan update
- ✅ Scalability: Handle increased workload
- ✅ Cost efficiency: Optimal resource utilization
- ✅ Compliance: Meet regulatory requirements
Terus iterate dan improve pipeline sesuai kebutuhan project dan team.