1. Arsitektur Pipeline GitLab CI/CD
GitLab CI/CD adalah platform CI/CD bawaan GitLab yang menyediakan automasi build, test, dan deployment langsung dari repository. Pipeline didefinisikan dalam file .gitlab-ci.yml dan dijalankan oleh GitLab Runner.
Includes & Templates
Parent-child configs
DAG resolution
Variable expansion
Docker, Shell, K8s
Autoscaling
Review Apps
Canary deployments
1.1 Konfigurasi Dasar Pipeline Lanjut
# Definisikan stages urutan eksekusi
stages:
- validate
- build
- test
- security
- deploy-staging
- integration-test
- deploy-production
- notify
# Default configuration untuk semua jobs
default:
image: node:20-alpine
tags:
- docker
- linux
before_script:
- npm ci --cache .npm --prefer-offline
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
- node_modules/
retry:
max: 2
when:
- runner_system_failure
- stuck_or_timeout_failure
# Workflow rules โ kontrol kapan pipeline dijalankan
workflow:
rules:
# Pipeline untuk merge request
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
# Pipeline untuk push ke main/develop
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
- if: $CI_COMMIT_BRANCH == "develop"
# Pipeline untuk tag
- if: $CI_COMMIT_TAG
# Jangan jalankan pipeline untuk branch lain
- when: never
# Global variables
variables:
NODE_ENV: "production"
DOCKER_TLS_CERTDIR: "/certs"
FF_USE_FASTZIP: "true" # Faster artifact compression
# Include shared templates
include:
- local: '.gitlab/ci/lint.yml'
- local: '.gitlab/ci/test.yml'
- local: '.gitlab/ci/deploy.yml'
- project: 'my-org/ci-templates'
ref: main
file: '/templates/security-scan.yml'
- template: 'Security/SAST.gitlab-ci.yml'
- template: 'Security/Dependency-Scanning.gitlab-ci.yml'
GitLab CI mendukung berbagai sumber include: local (file di repo), project (repo lain di GitLab), template (built-in GitLab), dan remote (URL publik). Ini memungkinkan Anda membangun library template CI yang shared di seluruh organisasi.
2. Parent-Child Pipeline
Parent-child pipeline memungkinkan Anda memecah pipeline besar menjadi beberapa pipeline anak yang berjalan secara paralel. Ini sangat berguna untuk monorepo di mana setiap service atau module memiliki pipeline sendiri.
2.1 Parent Pipeline yang Memicu Child
stages:
- trigger
- deploy
# Deteksi perubahan dan trigger child pipeline
generate-child-pipeline:
stage: trigger
image: alpine:latest
script:
- apk add --no-cache git jq
# Analisis file yang berubah
- CHANGED=$(git diff --name-only HEAD~1 HEAD || echo "")
- echo "Changed files: $CHANGED"
# Generate child pipeline config dinamis
- |
cat > child-pipeline.yml <<'EOF'
stages:
- build
- test
- push
include:
- local: 'services/api/.gitlab-ci.yml'
- local: 'services/web/.gitlab-ci.yml'
- local: 'services/worker/.gitlab-ci.yml'
EOF
# Tambahkan hanya service yang berubah
- |
for service in api web worker; do
if echo "$CHANGED" | grep -q "^services/$service/"; then
echo "Service $service has changes"
fi
done
artifacts:
paths:
- child-pipeline.yml
# Trigger child pipeline
trigger-child:
stage: trigger
needs: [generate-child-pipeline]
trigger:
include:
- artifact: child-pipeline.yml
job: generate-child-pipeline
strategy: depend # Tunggu child pipeline selesai
# Static child pipeline (tidak perlu generate dinamis)
trigger-api-pipeline:
stage: trigger
trigger:
include:
- local: 'services/api/.gitlab-ci.yml'
strategy: depend
rules:
- changes:
- services/api/**/*
trigger-web-pipeline:
stage: trigger
trigger:
include:
- local: 'services/web/.gitlab-ci.yml'
strategy: depend
rules:
- changes:
- services/web/**/*
deploy-all:
stage: deploy
needs:
- job: trigger-api-pipeline
artifacts: false
- job: trigger-web-pipeline
artifacts: false
script:
- echo "All child pipelines passed, deploying..."
environment:
name: production
url: https://app.example.com
2.2 Child Pipeline Config
# Child pipeline untuk API service
stages:
- build
- test
- push
- deploy
variables:
IMAGE_NAME: $CI_REGISTRY_IMAGE/api
build-api:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $IMAGE_NAME:$CI_COMMIT_SHA .
artifacts:
paths:
- services/api/dist/
expire_in: 1 hour
test-api:
stage: test
image: node:20
script:
- cd services/api
- npm ci
- npm test -- --coverage
- npm run test:integration
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
artifacts:
reports:
junit: services/api/test-results.xml
coverage_report:
coverage_format: cobertura
path: services/api/coverage/cobertura-coverage.xml
push-api:
stage: push
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker tag $IMAGE_NAME:$CI_COMMIT_SHA $IMAGE_NAME:latest
- docker push $IMAGE_NAME:$CI_COMMIT_SHA
- docker push $IMAGE_NAME:latest
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy-api-staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl set image deployment/api api=$IMAGE_NAME:$CI_COMMIT_SHA -n staging
- kubectl rollout status deployment/api -n staging --timeout=120s
environment:
name: staging/api
url: https://api.staging.example.com
rules:
- if: $CI_COMMIT_BRANCH == "develop"
3. DAG (Directed Acyclic Graph)
DAG memungkinkan Anda mendefinisikan dependency antar jobs secara eksplisit menggunakan needs, sehingga jobs bisa berjalan segera setelah dependency terpenuhi โ tanpa harus menunggu seluruh stage selesai.
3.1 DAG vs Stage-based Execution
# DEFAULT: Stage-based โ semua job di stage harus selesai
# sebelum stage berikutnya dimulai
stages:
- build
- test
- deploy
# DENGAN DAG: Jobs berjalan segera setelah needs terpenuhi
# Aktifkan DAG di project level
variables:
FF_USE_DAG: "true"
# Build jobs
build-frontend:
stage: build
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/frontend/
build-backend:
stage: build
image: golang:1.22
script:
- go build -o bin/server ./cmd/server
artifacts:
paths:
- bin/
build-docs:
stage: build
image: python:3.12
script:
- pip install mkdocs
- mkdocs build
artifacts:
paths:
- site/
# Test jobs โ hanya butuh build yang relevan
test-frontend:
stage: test
needs: [build-frontend] # Hanya tunggu frontend build
script:
- npm run test:unit
- npm run test:e2e
test-backend:
stage: test
needs: [build-backend] # Hanya tunggu backend build
image: golang:1.22
script:
- go test ./... -v -race -coverprofile=coverage.out
test-integration:
stage: test
needs:
- job: build-frontend
artifacts: true
- job: build-backend
artifacts: true
services:
- postgres:15
- redis:7
script:
- ./bin/server &
- sleep 5
- npm run test:integration
# Deploy bisa dimulai segera setelah test relevant selesai
deploy-frontend:
stage: deploy
needs: [test-frontend] # Tidak perlu tunggu test-backend
script:
- aws s3 sync dist/frontend/ s3://frontend-bucket
environment:
name: production/frontend
deploy-backend:
stage: deploy
needs: [test-backend] # Tidak perlu tunggu test-frontend
script:
- kubectl set image deployment/backend backend=$IMAGE
environment:
name: production/backend
3.2 DAG Visualization
Dengan DAG, deploy-frontend tidak perlu menunggu test-backend selesai. Jika frontend build & test selesai dalam 3 menit tetapi backend membutuhkan 8 menit, frontend bisa langsung di-deploy tanpa menunggu. Ini bisa menghemat 30-50% total pipeline time.
4. Environments & Deployments
GitLab Environments menyediakan tracking deployment yang terstruktur, termasuk deployment history, rollback, dan protected environments.
# Static environment
deploy-staging:
stage: deploy
script:
- ./deploy.sh staging
environment:
name: staging
url: https://staging.example.com
# Auto-stop setelah 2 hari
auto_stop_in: 2 days
# Kubernetes namespace mapping
kubernetes:
namespace: staging
# Deployment untuk stop environment
on_stop: stop-staging
rules:
- if: $CI_COMMIT_BRANCH == "develop"
# Stop environment job
stop-staging:
stage: deploy
script:
- ./teardown.sh staging
environment:
name: staging
action: stop
when: manual
allow_failure: true
# Dynamic environment (per-branch review apps)
deploy-review:
stage: deploy
script:
- export NAMESPACE="review-$CI_COMMIT_REF_SLUG"
- ./deploy.sh $NAMESPACE
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_COMMIT_REF_SLUG.review.example.com
auto_stop_in: 1 week
on_stop: stop-review
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
stop-review:
stage: deploy
script:
- export NAMESPACE="review-$CI_COMMIT_REF_SLUG"
- kubectl delete namespace $NAMESPACE --ignore-not-found
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
# Canary deployment
deploy-canary:
stage: deploy
script:
- kubectl set image deployment/app app=$IMAGE -n production
- kubectl rollout status deployment/app -n production --timeout=120s
environment:
name: production
url: https://canary.example.com
deployment_tier: production
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
resource_group: production # Hanya satu deployment di production
5. Review Apps
Review Apps adalah environment sementara yang dibuat otomatis untuk setiap merge request, memungkinkan reviewer melihat perubahan secara langsung sebelum merge.
variables:
REVIEW_DOMAIN: "review.example.com"
deploy-review-app:
stage: deploy
image: bitnami/kubectl:latest
script:
- NAMESPACE="review-${CI_COMMIT_REF_SLUG}"
- APP_URL="${CI_COMMIT_REF_SLUG}.${REVIEW_DOMAIN}"
# Buat namespace jika belum ada
- kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
# Deploy menggunakan Helm
- |
helm upgrade --install $NAMESPACE ./charts/app \
--namespace $NAMESPACE \
--set image.tag=$CI_COMMIT_SHA \
--set ingress.host=$APP_URL \
--set ingress.tls.host=$APP_URL \
--set replicaCount=1 \
--set resources.limits.cpu=200m \
--set resources.limits.memory=256Mi \
--wait --timeout=300s
# Comment di MR dengan URL review app
- |
echo "๐ Review app deployed!"
echo "URL: https://$APP_URL"
environment:
name: review/$CI_COMMIT_REF_SLUG
url: https://$CI_COMMIT_REF_SLUG.review.example.com
auto_stop_in: 3 days
on_stop: cleanup-review-app
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
resource_group: review/$CI_COMMIT_REF_SLUG
cleanup-review-app:
stage: deploy
image: bitnami/kubectl:latest
script:
- NAMESPACE="review-${CI_COMMIT_REF_SLUG}"
- helm uninstall $NAMESPACE --namespace $NAMESPACE || true
- kubectl delete namespace $NAMESPACE --ignore-not-found
environment:
name: review/$CI_COMMIT_REF_SLUG
action: stop
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: manual
6. Secrets Management
GitLab menyediakan beberapa mekanisme untuk mengelola secrets โ dari CI/CD Variables hingga integrasi dengan external secrets manager seperti HashiCorp Vault.
6.1 CI/CD Variables
# Gunakan masked & protected variables di GitLab UI
# Settings โ CI/CD โ Variables
# Contoh penggunaan di pipeline
deploy-production:
stage: deploy
script:
# Variabel yang di-masking tidak akan muncul di log
- echo "Deploying with token..."
- curl -H "Authorization: Bearer $DEPLOY_TOKEN" \
-X POST https://api.example.com/deploy
variables:
# Variable scope terbatas ke environment tertentu
# Dikonfigurasi di GitLab UI dengan environment scope
AWS_ACCESS_KEY_ID: $PROD_AWS_ACCESS_KEY
AWS_SECRET_ACCESS_KEY: $PROD_AWS_SECRET_KEY
environment:
name: production
# HashiCorp Vault integration
deploy-with-vault:
stage: deploy
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.example.com
secrets:
DATABASE_URL:
vault: secret/data/production/database#url
token: $VAULT_ID_TOKEN
file: false
API_KEY:
vault: secret/data/production/api#key
token: $VAULT_ID_TOKEN
file: false
script:
- echo "Deploying with secrets from Vault..."
# $DATABASE_URL dan $API_KEY tersedia sebagai env vars
- echo "DB URL is configured (masked)"
- ./deploy.sh
# File-based secrets
deploy-with-file-secret:
stage: deploy
secrets:
GCP_SA_KEY:
vault: secret/data/production/gcp#service-account-json
token: $VAULT_ID_TOKEN
file: true # Secret disimpan sebagai file
script:
# $GCP_SA_KEY adalah path ke file temporary
- gcloud auth activate-service-account --key-file=$GCP_SA_KEY
- gcloud run deploy my-app --image=$IMAGE
6.2 External Secrets dengan SOPS
# Install SOPS di pipeline
decrypt-secrets:
stage: .pre
image: mozilla/sops:latest
script:
# Decrypt secrets file yang di-commit (encrypted)
- sops --decrypt --in-place secrets.enc.yaml
# Export sebagai environment variables
- |
eval $(sops --decrypt secrets.enc.yaml | \
yq -o shell '.secrets')
artifacts:
paths:
- secrets.enc.yaml
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
7. Pipeline Optimization
7.1 Speed Optimization Techniques
| Teknik | Dampak | Cara Implementasi |
|---|---|---|
| DAG | Hemat 30-50% waktu | Gunakan needs antar jobs |
| Cache | Hemat 40-60% install time | cache: key: paths: |
| Rules | Skip job yang tidak relevan | rules: changes: |
| Artifacts | Hindari build ulang | Pass artifacts antar jobs |
| Image yang tepat | Startup lebih cepat | Gunakan alpine images |
| Parallel test | Test lebih cepat | parallel: 5 |
# Parallel testing
test-unit:
stage: test
parallel: 5 # Jalankan 5 instance paralel
script:
- |
# CI_NODE_INDEX = 0..4, CI_NODE_TOTAL = 5
npm test -- --shard=$CI_NODE_INDEX/$CI_NODE_TOTAL
# Conditional execution dengan rules
lint:
stage: validate
rules:
# Hanya jalankan jika ada perubahan file source
- changes:
- "src/**/*"
- "*.ts"
- "*.json"
when: always
# Selalu jalankan untuk main branch
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: always
# Manual trigger untuk branch lain
- when: manual
allow_failure: true
script:
- npm run lint
# Efficient caching dengan hashFiles equivalent
cache:
key:
files:
- package-lock.json
- tsconfig.json
paths:
- node_modules/
- .cache/
policy: pull-push # atau 'pull' untuk read-only cache
8. CI/CD Templates & Components
# CI/CD Components โ reusable pipeline building blocks
# https://docs.gitlab.com/ee/ci/components/
# component: build-node-app
# Versi: 1.0.0
spec:
inputs:
node_version:
default: "20"
build_script:
default: "npm run build"
test_script:
default: "npm test"
---
build:
stage: build
image: node:$[[ inputs.node_version ]]-alpine
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
script:
- npm ci --prefer-offline
- $[[ inputs.build_script ]]
artifacts:
paths:
- dist/
test:
stage: test
image: node:$[[ inputs.node_version ]]-alpine
needs: [build]
script:
- npm ci --prefer-offline
- $[[ inputs.test_script ]]
artifacts:
reports:
junit: test-results.xml
# Usage di repository lain:
# include:
# - component: gitlab.com/my-org/components/build-node-app@1.0.0
# inputs:
# node_version: "22"
# build_script: "pnpm build"
GitLab memiliki limit maksimum 500 jobs per pipeline dan ukuran file .gitlab-ci.yml yang di-expand maksimal 5 MB. Untuk project besar, gunakan parent-child pipeline untuk memecah pipeline menjadi beberapa bagian.
9. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang GitLab CI/CD Advanced: