🎯 MỤC TIÊU BÀI HỌC
- ✅ Thiết kế CI/CD pipeline cho microservices + GitOps
- ✅ GitHub Actions: build, test, lint, scan
- ✅ Container image build với multi-stage Dockerfile
- ✅ Image vulnerability scanning (Trivy)
- ✅ GitOps trigger: auto-update image tag in Git
- ✅ Environment promotion workflow (dev → staging → prod)
PHẦN 1: CI/CD + GITOPS PIPELINE DESIGN
Complete CI/CD + GitOps Pipeline:
┌──────────────────────────────────────────────────────────┐
│ CI PIPELINE (GitHub Actions) │
│ │
│ Developer ──► Git Push ──► Build ──► Test ──► Scan │
│ │ │
│ ┌─────▼─────┐ │
│ │ Build & │ │
│ │ Push Image │ │
│ └─────┬─────┘ │
└──────────────────────────────────────────────────┼────────┘
│
┌────────▼────────┐
│ Update image │
│ tag in GitOps │
│ repo (PR/commit)│
└────────┬────────┘
│
┌──────────────────────────────────────────────────┼────────┐
│ CD PIPELINE (ArgoCD) │ │
│ ▼ │
│ GitOps Repo ◄── ArgoCD watches ──► K8s Cluster │
│ (manifests) (auto-sync) (deploy) │
└───────────────────────────────────────────────────────────┘
Two Repositories:
1. App Repo: source code, Dockerfile, CI pipeline
2. GitOps Repo: K8s manifests, Helm values, ArgoCD config
PHẦN 2: MULTI-STAGE DOCKERFILE
# Dockerfile (Go microservice example):
# Stage 1: Build
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server
# Stage 2: Runtime
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /app/server /server
COPY --from=builder /app/configs /configs
USER nonroot:nonroot
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=5s CMD ["/server", "healthcheck"]
ENTRYPOINT ["/server"]
# Dockerfile (Node.js microservice):
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Stage 2: Runtime
FROM node:20-alpine
RUN addgroup -g 1001 -S nodejs && adduser -S appuser -u 1001
WORKDIR /app
COPY --from=builder --chown=appuser:nodejs /app/dist ./dist
COPY --from=builder --chown=appuser:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:nodejs /app/package.json ./
USER appuser
EXPOSE 8080
CMD ["node", "dist/index.js"]
PHẦN 3: GITHUB ACTIONS CI PIPELINE
# .github/workflows/ci.yaml:
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: registry.myapp.com
IMAGE: order-service
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Run Tests
run: |
go test -v -race -coverprofile=coverage.out ./...
go tool cover -func=coverage.out
- name: Lint
uses: golangci/golangci-lint-action@v6
with:
version: latest
build-and-push:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
outputs:
image-tag: ${{ steps.meta.outputs.version }}
steps:
- uses: actions/checkout@v4
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE }}
tags: |
type=sha,prefix=
type=semver,pattern={{version}}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
scan:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: "${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ needs.build-and-push.outputs.image-tag }}"
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'
update-gitops:
needs: [build-and-push, scan]
runs-on: ubuntu-latest
steps:
- name: Checkout GitOps repo
uses: actions/checkout@v4
with:
repository: myorg/k8s-manifests
token: ${{ secrets.GITOPS_TOKEN }}
path: gitops
- name: Update image tag
run: |
cd gitops
NEW_TAG="${{ needs.build-and-push.outputs.image-tag }}"
# Update Helm values:
yq eval ".image.tag = \"$NEW_TAG\"" -i \
apps/order-service/values-staging.yaml
# Commit and push:
git config user.name "github-actions"
git config user.email "[email protected]"
git add .
git commit -m "chore: update order-service to $NEW_TAG"
git push
PHẦN 4: ENVIRONMENT PROMOTION
Promotion Workflow:
main branch push
│
▼
┌────────────┐
│ CI: Build │
│ Test, Scan │
└─────┬──────┘
│ Auto-update staging values
▼
┌────────────┐ ArgoCD
│ STAGING │ ◄── auto-sync
│ (auto) │
└─────┬──────┘
│ Manual PR: promote to production
▼
┌────────────┐ ArgoCD
│ PRODUCTION │ ◄── auto-sync (after PR merge)
│ (approval) │
└────────────┘
# Promotion PR workflow:
# .github/workflows/promote.yaml:
name: Promote to Production
on:
workflow_dispatch:
inputs:
service:
description: 'Service to promote'
required: true
type: choice
options:
- order-service
- payment-service
- user-service
tag:
description: 'Image tag to promote'
required: true
jobs:
promote:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Update production values
run: |
yq eval ".image.tag = \"${{ inputs.tag }}\"" -i \
apps/${{ inputs.service }}/values-production.yaml
- name: Create PR
uses: peter-evans/create-pull-request@v6
with:
title: "🚀 Promote ${{ inputs.service }} ${{ inputs.tag }} to production"
body: |
Promoting **${{ inputs.service }}** version `${{ inputs.tag }}` to production.
## Checklist
- [ ] Staging tests passed
- [ ] Performance acceptable
- [ ] Rollback plan ready
branch: "promote/${{ inputs.service }}-${{ inputs.tag }}"
reviewers: "platform-team"
PHẦN 5: PRIVATE REGISTRY (HARBOR)
# Install Harbor:
helm repo add harbor https://helm.goharbor.io
helm repo update
helm install harbor harbor/harbor \
--namespace harbor \
--create-namespace \
--set expose.type=ingress \
--set expose.ingress.hosts.core=registry.myapp.com \
--set externalURL=https://registry.myapp.com \
--set persistence.persistentVolumeClaim.registry.storageClass=ceph-block \
--set persistence.persistentVolumeClaim.registry.size=100Gi \
--set trivy.enabled=true
💡 KEY TAKEAWAYS
- Two repos: App repo (CI) + GitOps repo (CD) separation
- CI pipeline: Test → Build → Scan → Push image
- GitOps trigger: CI updates image tag in GitOps repo
- ArgoCD auto-sync: Detects tag change → deploys to K8s
- Promotion: Auto to staging, PR-based to production
- Harbor: Private registry with built-in vulnerability scanning
🎯 BÀI TẬP
Bài tập 1: Complete Pipeline
- Setup GitHub Actions CI for sample Go service
- Build + push to Harbor, scan with Trivy
- Auto-update GitOps repo, ArgoCD deploys
Bài tập 2: Promotion Workflow
- Implement staging → production promotion PR
- Add required reviewers and checks
- Test full cycle: code change → production deploy
📚 BÀI TIẾP THEO
Trong Bài 32: Prometheus Stack — Monitoring Infrastructure, chúng ta sẽ setup observability stack với Prometheus, Grafana, và Alertmanager.