GitLab CI #
GitLab CI menawarkan beberapa keunggulan dibanding GitHub Actions untuk tim yang sudah di ekosistem GitLab: pipeline yang lebih fleksibel dengan include dan extends, environments dengan deployment tracking terintegrasi, dan kemampuan menjalankan pipeline yang sepenuhnya self-hosted tanpa bergantung pada infrastruktur eksternal. Artikel ini membahas pola integrasi Ansible yang komprehensif dengan GitLab CI, dan jadi pelengkap dari artikel GitHub Actions sebelumnya — keduanya mengaplikasikan prinsip desain yang dibahas di Pipeline Design.
Anatomi GitLab CI untuk Ansible #
Berbeda dengan GitHub Actions yang berorientasi pada event-trigger, GitLab CI berorientasi pada stages yang berjalan berurutan. Konsepnya lebih dekat ke pipeline tradisional Makefile atau Jenkins: setiap stage punya daftar job, semua job dalam satu stage berjalan paralel, dan stage berikutnya baru mulai setelah stage sebelumnya selesai.
flowchart TB
subgraph S1["Stage: validate"]
V1["lint"]
V2["syntax-check"]
V3["check-production --check --diff"]
end
subgraph S2["Stage: test"]
T1["molecule-common"]
T2["molecule-nginx"]
T3["molecule-postgresql"]
T4["molecule-docker"]
end
subgraph S3["Stage: build"]
B1["build-image"]
end
subgraph S4["Stage: deploy-staging"]
D1["deploy-staging"]
end
subgraph S5["Stage: verify-staging"]
V4["verify-staging smoke test"]
end
subgraph S6["Stage: deploy-production"]
D2["deploy-production manual"]
end
V1 --> S2
V2 --> S2
V3 --> S2
S2 --> B1
B1 --> D1
D1 --> V4
V4 --> D2
Tiga konsep penting di GitLab CI yang tidak ada di GitHub Actions dengan nama yang sama: stages mendefinisikan urutan eksekusi (validate → test → build → deploy), extends me-reuse konfigurasi antar job (mirip inheritance di OOP), dan !reference memungkinkan komposisi before_script dari beberapa template sekaligus. Ketiganya membuat pipeline untuk fleet Ansible yang besar tetap manageable.
Saat pertama kali desain pipeline GitLab CI, tulis dulu stages: dengan nama-nama yang bermakna, lalu tambahkan job ke masing-masing stage. Jangan mulai dari job — mulai dari urutan lifecycle. Ini mencegah kita lupa memisahkan job build dari job deploy di stage yang sama.
Struktur Pipeline dengan Stages #
File .gitlab-ci.yml adalah single source of truth untuk seluruh pipeline. Tidak seperti GitHub Actions yang bisa punya banyak file workflow, GitLab CI terpusat di satu file (atau di-include dari banyak file lewat include:).
# .gitlab-ci.yml
stages:
- validate # Lint dan syntax check
- test # Unit test dan molecule
- build # Build Docker image
- deploy-staging
- verify-staging
- deploy-production
variables:
ANSIBLE_FORCE_COLOR: "true"
ANSIBLE_HOST_KEY_CHECKING: "false"
PY_COLORS: "1"
IMAGE_TAG: "${CI_COMMIT_SHORT_SHA}"
REGISTRY: "${CI_REGISTRY}"
IMAGE_NAME: "${CI_REGISTRY_IMAGE}"
Perhatikan dua variabel khusus GitLab: ${CI_COMMIT_SHORT_SHA} adalah short git SHA (8 karakter pertama) yang jadi image tag — immutable dan traceable ke commit tertentu. ${CI_REGISTRY_IMAGE} adalah path default container registry yang sudah dikonfigurasi untuk project GitLab kita. Tidak perlu setup credential tambahan; GitLab Runner otomatis me-mount token ke runner saat job berjalan.
ANSIBLE_HOST_KEY_CHECKING: "false" di-set di pipeline untuk mencegah job deploy macet di prompt SSH “Are you sure you want to continue connecting?”. Di production dengan managed node yang sering rebuild, prompt ini muncul setiap ada host baru dan akan membuat job CI hang. Set ke false di pipeline; di Ansible config lokal development, biarkan true untuk keamanan.
Template dengan extends dan before_script
#
Salah satu kekuatan terbesar GitLab CI dibanding platform lain: sistem template-nya sangat fleksibel. Dua job dengan setup yang mirip tidak perlu duplikasi konfigurasi.
# Template untuk semua job Ansible
.ansible_base:
image: python:3.11-slim
before_script:
- pip install ansible --quiet
- ansible-galaxy install -r requirements.yml --force
cache:
key: ansible-$CI_COMMIT_REF_SLUG
paths:
- ~/.ansible/roles
- .cache/pip
# Template untuk deployment (butuh SSH)
.deploy_base:
extends: .ansible_base
before_script:
- !reference [.ansible_base, before_script]
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- echo "$VAULT_PASSWORD" > /tmp/.vault_pass
- chmod 600 /tmp/.vault_pass
after_script:
- rm -f /tmp/.vault_pass
Template dimulai dengan titik (.ansible_base) — ini menandakan dia adalah template, bukan job. Job yang extend template akan inherit semua field-nya, kecuali yang di-override di job itu sendiri. !reference adalah cara untuk me-reuse bagian spesifik dari template lain (dalam hal ini before_script dari .ansible_base) sambil menambahkan langkah baru di before_script itu sendiri.
tr -d '\r' di echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - itu kecil tapi penting — variable SSH key di GitLab CI kadang punya trailing carriage return (terutama kalau di-paste dari Windows), dan SSH agent langsung menolak key dengan format aneh. Strip karakter \r dulu sebelum di-add.
Stage Validate dan Test #
Setelah template didefinisikan, job di setiap stage jadi sangat ringkas — semuanya extend dari template yang sesuai:
# Stage: validate
lint:
extends: .ansible_base
stage: validate
script:
- pip install ansible-lint --quiet
- ansible-lint --profile production
- |
for playbook in playbooks/*.yml; do
ansible-playbook "$playbook" --syntax-check -i inventory/staging/
done
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
check-production:
extends: .deploy_base
stage: validate
variables:
SSH_PRIVATE_KEY: $PROD_SSH_KEY
VAULT_PASSWORD: $VAULT_PASS_PROD
script:
- ansible-playbook -i inventory/production/ site.yml
--check --diff
--vault-password-file /tmp/.vault_pass
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Stage: test — Molecule parallel
.molecule_base:
extends: .ansible_base
stage: test
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- pip install ansible molecule molecule-plugins[docker] --quiet
- ansible-galaxy install -r requirements.yml --force
molecule-common:
extends: .molecule_base
script: cd roles/common && molecule test
molecule-nginx:
extends: .molecule_base
script: cd roles/nginx && molecule test
molecule-postgresql:
extends: .molecule_base
script: cd roles/postgresql && molecule test
Job check-production menarik: ia menjalankan playbook dengan --check --diff — Ansible mensimulasikan eksekusi tanpa benar-benar mengubah apapun, lalu menampilkan diff dari perubahan yang akan terjadi. Ini adalah dry-run paling akurat untuk Ansible: tidak hanya cek syntax, tapi juga verifikasi semua kondisi, dependencies, dan variabel. Kombinasikan dengan lint dan syntax-check di stage yang sama, kita memiliki tiga lapis validasi sebelum kode sampai ke runtime.
--checkmode di Ansible tidak sempurna. Task yang butuh koneksi ke remote service (misalnyacommandataushelldi managed node) akan gagal karena Ansible tidak benar-benar konek saat mode check. Untuk verifikasi penuh, jalankan molecule test di stagetest— itu yang paling mendekati eksekusi nyata.
Build dan Push ke GitLab Container Registry #
GitLab punya container registry built-in yang sudah terintegrasi dengan project. Tidak perlu setup Docker Hub atau registry eksternal untuk kebanyakan kasus:
build-image:
stage: build
image: docker:24
services:
- docker:dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_NAME:$IMAGE_TAG .
- docker tag $IMAGE_NAME:$IMAGE_TAG $IMAGE_NAME:latest
- docker push $IMAGE_NAME:$IMAGE_TAG
- docker push $IMAGE_NAME:latest
- echo "IMAGE_FULL_TAG=$IMAGE_NAME:$IMAGE_TAG" > build.env
artifacts:
reports:
dotenv: build.env # Teruskan variabel ke job berikutnya
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
artifacts.reports.dotenv adalah cara resmi GitLab untuk share variabel antar job. File build.env yang ditulis di akhir script di-parse dan di-inject sebagai environment variable di job-job berikutnya. Jadi IMAGE_FULL_TAG yang ditulis di build job akan tersedia otomatis di deploy job tanpa perlu pass manual atau download artifact. Untuk konteks: cara di GitHub Actions adalah outputs: di level job; di GitLab CI adalah file dotenv di artifacts. Pola pikirnya sama, implementasinya beda.
Deployment dengan GitLab Environments #
GitLab Environments adalah fitur yang sering di-underestimate: setiap deployment ke environment di-track otomatis, dengan history, status, dan link ke commit/job yang melakukannya. Sangat berguna untuk audit dan rollback.
deploy-staging:
extends: .deploy_base
stage: deploy-staging
variables:
SSH_PRIVATE_KEY: $STAGING_SSH_KEY
VAULT_PASSWORD: $VAULT_PASS_STAGING
script:
- ansible-playbook -i inventory/staging/ playbooks/deploy.yml
-e "app_version=$IMAGE_TAG"
--vault-password-file /tmp/.vault_pass
environment:
name: staging
url: https://staging.company.com
deployment_tier: staging
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
verify-staging:
stage: verify-staging
image: curlimages/curl:latest
script:
- sleep 15
- curl -f https://staging.company.com/health
- |
VERSION=$(curl -s https://staging.company.com/api/version | python3 -c "import json,sys; print(json.load(sys.stdin)['version'])")
[ "$VERSION" = "$IMAGE_TAG" ] || (echo "Version mismatch: expected $IMAGE_TAG, got $VERSION" && exit 1)
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
deploy-production:
extends: .deploy_base
stage: deploy-production
variables:
SSH_PRIVATE_KEY: $PROD_SSH_KEY
VAULT_PASSWORD: $VAULT_PASS_PROD
script:
- ansible-playbook -i inventory/production/ playbooks/deploy.yml
-e "app_version=$IMAGE_TAG"
--vault-password-file /tmp/.vault_pass
environment:
name: production
url: https://app.company.com
deployment_tier: production
when: manual # Harus dipicu manual — tidak otomatis
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
allow_failure: false
environment: block punya tiga peran: (1) ngasih label ke deployment sehingga tertampil di menu Environments GitLab, (2) track history deployment per environment dengan timestamp dan job ref, (3) enable fitur “re-deploy” dari UI yang akan re-trigger job dengan commit yang sama. when: manual di production deployment adalah guard terpenting — pipeline tidak akan otomatis promote ke production. Seseorang harus klik tombol “play” di UI GitLab untuk job deploy-production jalan.
verify-staging melakukan dua hal: smoke test endpoint untuk memastikan container hidup, dan version check untuk memastikan container yang jalan benar-benar image yang baru di-deploy. Tanpa version check, ada risiko smoke test lulus tapi sebenarnya container yang merespons adalah instance lama yang masih jalan dari deployment sebelumnya.
Dynamic Child Pipeline #
Untuk monorepo dengan banyak service, hardcode semua job di .gitlab-ci.yml cepat jadi tidak scalable. Dynamic child pipeline memungkinkan kita generate pipeline berdasarkan service yang berubah di commit tertentu.
# .gitlab-ci.yml (parent pipeline)
generate-child-pipeline:
stage: validate
image: python:3.11-slim
script:
- python3 scripts/generate-pipeline.py > generated-pipeline.yml
artifacts:
paths:
- generated-pipeline.yml
trigger-child:
stage: deploy-staging
trigger:
include:
- artifact: generated-pipeline.yml
job: generate-child-pipeline
strategy: depend
# scripts/generate-pipeline.py
# Generate pipeline berdasarkan service yang berubah
import subprocess
import yaml
import sys
# Deteksi service yang berubah
changed = subprocess.run(
['git', 'diff', '--name-only', 'HEAD~1'],
capture_output=True, text=True
).stdout.split()
services = set()
for path in changed:
if path.startswith('services/'):
service = path.split('/')[1]
services.add(service)
# Generate job untuk setiap service yang berubah
pipeline = {'stages': ['deploy'], 'jobs': {}}
for service in services:
pipeline['jobs'][f'deploy-{service}'] = {
'stage': 'deploy',
'script': [
f'ansible-playbook -i inventory/staging/ playbooks/deploy-{service}.yml'
]
}
print(yaml.dump(pipeline))
Pola ini powerful tapi butuh kehati-hatian: script generate-pipeline.py adalah kode yang menghasilkan kode, dan kalau bug, bisa saja skip deployment service yang seharusnya di-deploy. Selalu validate output script ini (misalnya: kalau services kosong, gagal eksplisit; jangan generate pipeline kosong yang auto-success).
strategy: depend di trigger: membuat parent pipeline menunggu child pipeline selesai. Tanpa depend, parent dianggap sukses begitu child di-trigger, dan kalau child gagal, parent tetap hijau. Ini berbahaya — kita akan mendapatkan false positive di pipeline status.
Masked Variables untuk Secret #
GitLab punya tiga fitur keamanan untuk variable yang harus kita gunakan secara konsisten:
# Di GitLab Settings → CI/CD → Variables
# Gunakan opsi:
# - Masked: nilai tidak muncul di log pipeline
# - Protected: hanya tersedia di protected branch
# - File: nilai disimpan sebagai file, bukan environment variable
# Contoh variabel yang harus di-mask:
# VAULT_PASS_PROD → masked + protected
# PROD_SSH_KEY → masked + protected + file type
# REGISTRY_PASSWORD → masked
Perbedaan penting di sini: Masked hanya menyembunyikan dari log pipeline UI, tapi variable masih bisa diakses oleh job sebagai environment variable biasa. Untuk SSH key atau file credential lain, opsi File lebih aman — variable ditulis sebagai file di runner, dan kita bisa langsung mereferensikan path-nya di command. Ini mengurangi risiko echo iseng yang tidak sengaja print secret ke log.
Protected memastikan variable hanya tersedia di branch/tag yang masuk daftar protected (biasanya main dan tag release). Pull request dari fork tidak akan punya akses ke protected variable, jadi PR dari kontributor eksternal tidak akan bisa pakai production credential untuk exploit.
GitLab CI vs Jenkins vs GitHub Actions #
Memilih CI/CD platform adalah keputusan arsitektur yang sulit di-revert. Tabel dan decision tree di bawah ini membantu kita melihat trade-off-nya:
| Aspek | GitLab CI | Jenkins | GitHub Actions |
|---|---|---|---|
| Konfigurasi | YAML di-repo, versi sama dengan kode | Groovy/UI, versi terpisah dari kode | YAML di-repo, versi sama dengan kode |
| Runner | Built-in (shared) atau self-hosted | Self-hosted only (default) | GitHub-hosted atau self-hosted |
| Learning curve | moderate — stages + extends + rules | steep — plugin ecosystem + Jenkinsfile DSL | moderate — jobs + steps + matrix |
| Ecosystem | Terintegrasi dengan GitLab (issues, MR, registry) | Plugin marketplace terbesar | Marketplace besar, integrasi GitHub native |
| Self-host overhead | rendah — GitLab Runner ringan | tinggi — Jenkins master + agent | tidak perlu untuk hosted, ringan untuk self-hosted |
| Pipeline as code | Ya (.gitlab-ci.yml) |
Ya (Jenkinsfile) tapi juga bisa UI-only | Ya (.github/workflows/) |
| Cocok untuk | Tim yang ingin all-in-one DevOps platform | Tim dengan kebutuhan plugin sangat custom | Tim yang repository-nya di GitHub |
| Licensing | Community Edition free, self-hostable | Open source, fully self-hostable | Free untuk public, berbayar untuk private di atas quota |
flowchart TD
A{"Di mana repository disimpan?"} -- GitHub --> B["GitHub Actions"]
A -- GitLab --> C["GitLab CI"]
A -- Bitbucket/Other --> D{"Apakah membutuhkan plugin<br/>yang sangat kustom?"}
D -- Ya --> E["Jenkins"]
D -- Tidak --> F["Evaluasi migrasi<br/>ke GitLab/GitHub"]
B --> G{"Apakah memerlukan kontrol<br/>penuh atas runner?"}
C --> G
E --> G
G -- Ya --> H["Self-hosted runner/agent"]
G -- Tidak --> I["Hosted runner"]
Untuk Ansible, ketiganya sama-sama mampu. Pilihan biasanya ditentukan oleh ekosistem yang sudah ada: kalau repo-mu di GitHub, GitHub Actions adalah path of least resistance. Kalau kita sudah menggunakan GitLab untuk issues, MR, dan registry, GitLab CI mengurangi jumlah tool yang harus kita pelihara. Jenkins masih masuk akal untuk organisasi yang punya requirement sangat spesifik (misalnya build server farm dengan OS lama, atau integrasi dengan tool internal yang cuma ada plugin Jenkins).
Perbandingan Langsung: GitLab CI dan GitHub Actions #
Berikut perbandingan side-by-side untuk fitur yang paling sering dipakai di Ansible pipeline. Ini bukan untuk menunjukkan “siapa yang lebih baik”, tapi untuk membantu kita menerjemahkan workflow antar platform:
| Konsep | GitHub Actions | GitLab CI |
|---|---|---|
| File konfigurasi | .github/workflows/*.yml |
.gitlab-ci.yml |
| Urutan eksekusi | jobs.<id>.needs: [jobs lain] |
stages: block di top-level |
| Reuse konfigurasi | workflow_call (reusable workflow) |
extends: dan !reference |
| Paralel matrix | strategy.matrix |
parallel: matrix atau trigger manual per kombinasi |
| Pass variabel antar job | outputs: di level job |
artifacts.reports.dotenv |
| Approval manual | environment dengan required reviewers |
when: manual di job |
| Environment tracking | environment: di job (limited) |
environment: dengan full deployment history |
| Container registry | Harus setup sendiri atau pakai ghcr.io | Built-in per project ($CI_REGISTRY_IMAGE) |
| Secret management | Repository/org/environment secrets | Group/project/environment variables + masked + file type |
| Trigger dari event eksternal | repository_dispatch |
trigger: dengan include artifact |
| Dynamic pipeline | Composite action + matrix | trigger: dengan include artifact (child pipeline) |
| Self-hosted runner | Daftar di repo/org settings | Daftar di project/group/instance settings |
Perbedaan paling kontras: GitHub Actions pakai pendekatan event-driven (workflow trigger → jobs run in parallel dengan needs: dependency), GitLab CI pakai pendekatan stage-driven (stages run sequentially → jobs in each stage run in parallel). Jika kita sering memikirkan “job mana yang harus jalan duluan”, GitLab CI lebih cocok. Jika kita lebih sering memikirkan “event apa yang trigger ini”, GitHub Actions lebih cocok.
Migrasi workflow antar platform itu nyata. Banyak tim pindah dari Jenkins ke GitLab CI, dari GitLab CI ke GitHub Actions, dan sebaliknya. Saat migrasi, jangan terjebak menerjemahkan pola 1:1 — ambil kesempatan untuk refactor pipeline berdasarkan best practice platform baru. Yang tadinya “freestyle job” di Jenkins bisa jadi “reusable workflow” di GitHub Actions; yang tadinya extends: di GitLab CI bisa jadi “composite action” di GitHub Actions.
Anti-Pattern yang Harus Dihindari #
Tiga anti-pattern yang paling umum di GitLab CI untuk Ansible:
1. Secret di-Print di Log Pipeline #
# ANTI-PATTERN: secret di-set sebagai env var dan di-print
deploy-production:
script:
- export VAULT_PASS="$VAULT_PASS_PROD"
- export SSH_KEY="$PROD_SSH_KEY"
- echo "Deploying with vault password: $VAULT_PASS"
- echo "Using SSH key: $SSH_KEY"
- ansible-playbook -i inventory/prod/ deploy.yml
# ANTI-PATTERN: meskipun GitLab mask "sebagian" nilai di log,
# step `echo` di atas akan menampilkan nilai lengkap karena
# GitLab cuma mask kalau format exact sama dengan pattern
# yang sudah di-mask. Print bebas tidak akan ke-mask.
# BENAR: secret langsung tulis ke file, no echo
deploy-production:
variables:
VAULT_PASS_FILE: $VAULT_PASS_PROD # File type variable
script:
- ansible-playbook -i inventory/prod/ deploy.yml
--vault-password-file "$VAULT_PASS_FILE"
# BENAR: VAULT_PASS_PROD di-set sebagai File type variable di
# GitLab Settings → CI/CD → Variables. GitLab otomatis tulis
# nilai ke file dan expose path-nya. Tidak ada env var berisi
# secret, tidak ada echo yang bisa bocor.
2. Long-Running Pipeline dengan Stage Campur Aduk #
# ANTI-PATTERN: stage validation dan deployment di stage yang sama
stages:
- build
- deploy
build-and-deploy-staging:
stage: deploy
script:
- ansible-lint
- ansible-playbook -i inventory/staging/ site.yml
- ./run-integration-tests.sh
- ./run-security-scan.sh
# ANTI-PATTERN: satu job raksasa yang punya 4 concern berbeda
# (lint, deploy, test, scan). Kalau integration test gagal,
# kita tidak tahu apakah masalahnya di deploy, di test
# script, atau di environment. Dan kita tidak bisa melakukan paralelisasi
# molecule test untuk 10 role kalau semua di satu job.
# BENAR: stage terpisah untuk setiap concern
stages:
- validate
- test
- build
- deploy
lint:
stage: validate
extends: .ansible_base
script: ansible-lint
molecule-common:
stage: test
extends: .molecule_base
script: cd roles/common && molecule test
molecule-nginx:
stage: test
extends: .molecule_base
script: cd roles/nginx && molecule test
deploy-staging:
stage: deploy
extends: .deploy_base
script: ansible-playbook -i inventory/staging/ deploy.yml
# BENAR: empat concern terpisah, paralel di dalam stage masing-
# masing, fail-fast antar stage. Kalau molecule-nginx gagal,
# deploy-staging tidak jalan. Stage yang sudah selesai tidak
# perlu diulang kalau stage berikutnya gagal.
3. Hardcode Image Tag di Multiple Jobs #
# ANTI-PATTERN: tag image di-hardcode di setiap deploy job
deploy-staging:
stage: deploy
script:
- ansible-playbook -i inventory/staging/ deploy.yml
-e "app_version=2.1.5-abc1234" # Hardcoded!
deploy-production:
stage: deploy
script:
- ansible-playbook -i inventory/production/ deploy.yml
-e "app_version=2.1.5-abc1234" # Hardcoded juga!
# ANTI-PATTERN: setiap deploy job harus update tag secara
# manual, atau ada script terpisah yang update semua file.
# Risiko: lupa update salah satu job, deploy environment
# yang salah, atau tag drift antara staging dan production.
# BENAR: image tag ditulis sekali di build, diteruskan via dotenv
build-image:
stage: build
script:
- docker build -t $IMAGE_NAME:$IMAGE_TAG .
- docker push $IMAGE_NAME:$IMAGE_TAG
- echo "DEPLOY_VERSION=$IMAGE_TAG" > build.env # IMAGE_TAG = CI_COMMIT_SHORT_SHA
artifacts:
reports:
dotenv: build.env
deploy-staging:
stage: deploy
script:
- ansible-playbook -i inventory/staging/ deploy.yml
-e "app_version=$DEPLOY_VERSION" # Ambil dari build.env
deploy-production:
stage: deploy
script:
- ansible-playbook -i inventory/production/ deploy.yml
-e "app_version=$DEPLOY_VERSION" # Pasti sama dengan staging
# BENAR: satu sumber kebenaran untuk image tag. Staging dan
# production selalu dapat versi yang sama (yang sudah di-test
# di staging). Tidak ada kesempatan untuk typo atau lupa update.
Ringkasan #
- Gunakan
.template_name:denganextends:untuk menghindari duplikasi konfigurasi antar job — perubahan di template langsung berlaku di semua job yang meng-extend-nya.!referenceuntuk me-reusebefore_scriptdari template lain — memungkinkan komposisi yang lebih fleksibel dariextendssaja.- GitLab Environments memberikan deployment tracking otomatis — history semua deployment ke setiap environment tersedia di UI GitLab.
artifacts.reports.dotenvuntuk meneruskan variabel dari satu job ke job berikutnya — cara yang tepat untuk berbagi image tag antara build job dan deploy job.when: manualdi deployment production — memerlukan klik manual di UI GitLab, pipeline tidak otomatis melanjutkan ke production.- Masked + Protected + File type variables untuk semua secret — masked mencegah nilai muncul di log, protected memastikan hanya branch yang dilindungi yang bisa mengaksesnya, file type menghindari env var berisi secret.
- Pisahkan stage untuk setiap concern: validate, test, build, deploy — bukan satu stage multi-tujuan. Ini memungkinkan paralelisme di dalam stage dan fail-fast antar stage.
- Dynamic child pipeline untuk monorepo dengan banyak service — generate pipeline berdasarkan service yang berubah di commit, deploy hanya yang perlu di-deploy.
← Sebelumnya: GitHub Actions Berikutnya: Environment Management →