DevOps & Cloud

GitLab CI/CD Advanced Pipeline

GRATIS

Kuasai GitLab CI/CD tingkat lanjut โ€” parent-child pipeline, DAG, dynamic environments, review apps, secrets management, dan optimasi pipeline untuk project skala besar

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.

Arsitektur GitLab CI/CD Advanced
๐Ÿ“
Repository
.gitlab-ci.yml
Includes & Templates
Parent-child configs
โ†’ Trigger โ†’
๐Ÿ”„
GitLab CI Engine
Pipeline scheduling
DAG resolution
Variable expansion
โ†’ Execute โ†’
๐Ÿƒ
GitLab Runner
Shared / Group / Specific
Docker, Shell, K8s
Autoscaling
โ†’ Deploy โ†’
๐Ÿš€
Environments
Staging, Production
Review Apps
Canary deployments

1.1 Konfigurasi Dasar Pipeline Lanjut

.gitlab-ci.yml โ€” Struktur Advanced
# 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'
๐Ÿ’ก Tips: Include Patterns

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

.gitlab-ci.yml โ€” Parent Pipeline
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

services/api/.gitlab-ci.yml โ€” Child Pipeline
# 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

DAG configuration โ€” .gitlab-ci.yml
# 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

DAG: Jobs Berjalan Paralel Berdasarkan Dependencies
๐Ÿ”จ
build-frontend
โ†“
๐Ÿงช
test-frontend
โ†“
๐Ÿš€
deploy-frontend
๐Ÿ”จ
build-backend
โ†“
๐Ÿงช
test-backend
โ†“
๐Ÿš€
deploy-backend
๐Ÿ”จ
build-docs
โ†“
๐Ÿ“–
deploy-docs
๐Ÿ’ก DAG Menghemat Waktu

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.

Environments & Dynamic 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.

.gitlab-ci.yml โ€” Review App Setup
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

Secrets management patterns
# 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

GitLab + Mozilla 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
DAGHemat 30-50% waktuGunakan needs antar jobs
CacheHemat 40-60% install timecache: key: paths:
RulesSkip job yang tidak relevanrules: changes:
ArtifactsHindari build ulangPass artifacts antar jobs
Image yang tepatStartup lebih cepatGunakan alpine images
Parallel testTest lebih cepatparallel: 5
Optimization techniques
# 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

.gitlab/ci/templates/build.yml โ€” Shared template
# 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"
โš ๏ธ Pipeline Size Limit

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:

Pertanyaan 1: Apa fungsi utama parent-child pipeline di GitLab CI/CD?

a) Menghubungkan GitLab dengan Jenkins
b) Memecah pipeline besar menjadi beberapa pipeline anak yang berjalan paralel, cocok untuk monorepo
c) Menggabungkan beberapa repo menjadi satu pipeline
d) Membuat pipeline yang berjalan di parent directory

Pertanyaan 2: Apa perbedaan execution model DAG dibandingkan stage-based di GitLab CI?

a) DAG membuat semua job berjalan secara serial
b) DAG memungkinkan jobs berjalan segera setelah dependency (needs) terpenuhi, tanpa menunggu seluruh stage selesai
c) DAG hanya bisa digunakan untuk deploy
d) Tidak ada perbedaan, keduanya identik

Pertanyaan 3: Apa fungsi auto_stop_in pada environment di GitLab CI?

a) Menghentikan pipeline jika terlalu lama
b) Mengatur timeout untuk setiap job
c) Menghentikan environment secara otomatis setelah periode waktu tertentu untuk menghemat resource
d) Menghapus repository setelah tidak aktif

Pertanyaan 4: Mengapa menggunakan resource_group di GitLab CI?

a) Untuk mengelompokkan resource Kubernetes
b) Untuk membatasi penggunaan CPU dan memory runner
c) Untuk memastikan hanya satu deployment yang berjalan pada satu waktu di environment yang sama, mencegah race condition
d) Untuk mendistribusikan jobs ke beberapa runner

Pertanyaan 5: Apa keuntungan menggunakan GitLab CI/CD Components dibandingkan include template biasa?

a) Components lebih cepat dieksekusi
b) Components mendukung input parameters dan versioning, sehingga lebih fleksibel dan terstruktur
c) Components hanya bisa digunakan di GitLab.com
d) Components menghilangkan kebutuhan untuk .gitlab-ci.yml
โ† Sebelumnya GitHub Actions Advanced Workflows Selanjutnya โ†’ ArgoCD: GitOps for Kubernetes
๐Ÿ” Zoom
100%
๐ŸŽจ Tema