Artifact Management #
Artifact adalah output dari proses build yang siap di-deploy — Docker image, package Python, binary yang dikompilasi, atau archive tarball. Mengelola artifact dengan baik berarti: artifact bisa ditelusuri ke commit yang membuatnya, tersimpan di tempat yang persisten dan bisa diakses oleh semua environment, versinya immutable (tidak bisa diubah setelah dibuat), dan yang sudah tidak digunakan dibersihkan dari storage. Ansible bisa mengotomasi seluruh lifecycle ini. Artikel ini membahas strategi artifact management yang komprehensif — dari immutable labeling, registry selection, distribusi dengan checksum verification, atomic symlink deployment, hingga signing dan cleanup otomatis.
Prinsip Artifact yang Baik #
Sebelum masuk ke implementasi, pahami empat prinsip yang membedakan artifact management profesional dari sekadar “taruh file di server”:
1. Immutable
Setelah artifact bertag v2.1.0 dibuat, isinya tidak pernah berubah.
Jika ada bugfix, buat artifact baru dengan tag v2.1.1.
2. Traceable
Dari artifact, bisa ditelusuri: kode commit mana yang membuatnya,
siapa yang menjalankan build, kapan, dan dari branch mana.
3. Verified
Setiap artifact punya checksum (SHA256) yang bisa diverifikasi
sebelum deployment — memastikan artifact tidak rusak atau dimanipulasi.
4. Lifecycle-Managed
Artifact lama yang tidak diperlukan dibersihkan secara otomatis
untuk menghemat storage.
Keempat prinsip ini saling terkait. Immutable memungkinkan verified (pengecekan SHA256 dilakukan terhadap sesuatu yang tidak berubah). Traceable mendukung audit dan debugging saat deployment gagal. Lifecycle-managed mencegah biaya storage membengkak dan memastikan artifact lama tidak disalahgunakan. Tanpa keempat prinsip ini, kita akan segera menemukan kebingungan seperti: “artifact mana yang sedang berjalan di production?”, “siapa yang men-deploy versi ini?”, “kapan release ini dibuat?”
Artifact Lifecycle Flow #
Artifact memiliki lifecycle yang jelas dari kode sumber hingga cleanup. Visualisasi dengan flowchart membantu kita melihat di titik mana masing-masing tools dan tim berperan:
flowchart LR
A["Source Code"] -->|"git push"| B["CI Build Job"]
B -->|"compile & test"| C["Artifact Production"]
C -->|"tag v2.1.0 + SHA"| D["Registry / Object Storage"]
D -->|"pull by tag"| E["Deployment Job"]
E -->|"health check pass"| F["Production Running"]
D -->|"garbage collect"| G["Cleanup: hapus v2.0.x lama"]
F -. "bug ditemukan" .-> H["Trigger Rollback"]
H -->|"deploy ulang v2.0.5"| F
Perhatikan: ada dua jalur “keluar” dari D (Registry). Yang pertama adalah distribusi ke production melalui deployment job. Yang kedua adalah cleanup untuk artifact lama. Jalur ketiga (rollback) bisa jadi pull ulang dari registry untuk kembali ke versi sebelumnya. Ini sebabnya registry harus persisten — tidak boleh hilang hanya karena deployment sudah selesai.
Image Docker di registry adalah “single source of truth” untuk deployment. Jangan pernah melakukan re-build image saat melakukan deploy — selalu lakukan pull image yang sudah di-build dan di-tag di CI. Image yang sama harus dipakai di staging, production, dan rollback. Jika kita melakukan re-build di setiap environment, kita tidak bisa menjamin staging merepresentasikan production secara akurat.
Docker Image sebagai Artifact #
Docker registry adalah cara paling umum untuk mendistribusikan artifact berbasis container. Ansible, melalui modul community.docker, bisa mengotomasi build, tag, dan push image ke registry seperti Docker Hub, GitHub Container Registry (GHCR), AWS ECR, atau self-hosted seperti Harbor.
# playbooks/build-artifact.yml
---
- name: Build dan push Docker image artifact
hosts: localhost
vars:
registry: registry.company.com
image_name: myapp
version: "{{ version | mandatory }}"
git_sha: "{{ lookup('pipe', 'git rev-parse --short HEAD') }}"
build_date: "{{ ansible_date_time.iso8601 }}"
tasks:
- name: Login ke registry
community.docker.docker_login:
registry_url: "{{ registry }}"
username: "{{ registry_username }}"
password: "{{ vault_registry_password }}"
no_log: true
- name: Build image dengan label untuk traceability
community.docker.docker_image:
name: "{{ registry }}/{{ image_name }}"
tag: "{{ version }}"
source: build
build:
path: "{{ playbook_dir }}/.."
labels:
version: "{{ version }}"
git.sha: "{{ git_sha }}"
build.date: "{{ build_date }}"
build.pipeline: "{{ lookup('env', 'CI_PIPELINE_ID') | default('local') }}"
args:
APP_VERSION: "{{ version }}"
state: present
- name: Push image ke registry
community.docker.docker_image:
name: "{{ registry }}/{{ image_name }}"
tag: "{{ version }}"
push: true
source: local
- name: Generate checksum manifest
command: >
docker inspect
--format="{{ '{{' }}index .RepoDigests 0{{ '}}' }}"
{{ registry }}/{{ image_name }}:{{ version }}
register: image_digest
changed_when: false
- name: Simpan artifact manifest
copy:
content: |
image={{ registry }}/{{ image_name }}:{{ version }}
digest={{ image_digest.stdout }}
version={{ version }}
git_sha={{ git_sha }}
build_date={{ build_date }}
dest: "{{ playbook_dir }}/artifact-manifest.txt"
delegate_to: localhost
Label Docker (org.opencontainers.image.version, org.opencontainers.image.revision, org.opencontainers.image.created) adalah standar industri untuk traceability. Tools seperti dive, crane, atau skopeo bisa membaca label ini tanpa harus melakukan inspect image secara mendalam. Dengan label yang konsisten, kita bisa melakukan query seperti: “image mana yang dibuat dari commit abc123?” hanya dengan menjalankan docker images --filter "label=org.opencontainers.image.revision=abc123".
ANTI-PATTERN: Commit Binary ke Git vs Artifact Registry #
Salah satu kesalahan paling umum di awal mula project adalah menyimpan binary hasil build langsung di repository Git. Cara ini kelihatan pragmatis (“tinggal clone sudah mendapat binary-nya!”), tetapi membawa masalah besar.
# ANTI-PATTERN: commit binary ke Git
git add dist/myapp-2.1.0.tar.gz # 50MB binary
git add build/myapp-2.1.0.jar # 80MB binary
git add target/release/myapp # 120MB binary
git commit -m "build: add v2.1.0 binaries"
git push origin main
Masalahnya langsung terasa: repository Git membengkak dengan cepat. Setiap clone dari awal akan mengunduh seluruh history, termasuk semua binary yang sudah tidak relevan. CI/CD menjadi lambat karena harus melakukan checkout semua binary hanya untuk sampai ke source code terbaru. Lebih buruk lagi, binary yang di-commit ke Git tidak memiliki metadata build (siapa yang melakukan build, dari commit mana, dengan dependency versi apa), tidak bersifat immutable (siapa saja bisa melakukan amend force-push), dan sulit dibersihkan (kita harus menggunakan filter-branch atau BFG Repo-Cleaner untuk membersihkannya).
# BENAR: artifact di registry, Git hanya berisi source code
# .gitignore
dist/
build/
target/
*.jar
*.tar.gz
*.zip
*.exe
# CI/CD pipeline: build → push ke registry, tidak pernah ke Git
# .github/workflows/build.yml
- name: Build artifact
run: |
docker build -t registry.company.com/myapp:${{ github.sha }} .
docker push registry.company.com/myapp:${{ github.sha }}
Repository Git hanya berisi source code, konfigurasi build, dan test. Artifact fisik (binary, image, package) berada di registry. Hasilnya: clone repo menjadi cepat, Git history bersih, artifact bisa di-purge secara independen saat sudah tidak dipakai, dan traceability menjadi jelas — di Git kita melihat kode, di registry kita melihat artifact yang dihasilkan oleh kode tersebut.
Repository Git yang menyimpan binary bisa membengkak sampai puluhan GB dalam hitungan bulan. Migrasi ke registry setelah itu sangat menyakitkan — kita harus melakukan re-clone pada repositori semua orang, GitHub/GitLab akan mengenakan biaya storage tambahan, dan kita harus menulis ulang history (rewrite history) untuk menghapus binary. Pencegahan jauh lebih mudah: berlakukan.gitignoredan.dockerignoresejak awal, dan berikan edukasi kepada tim bahwa binary bukan bagian dari source code.
Distribusi Artifact ke Managed Node #
Setelah build, artifact harus didistribusikan ke server target. Untuk Docker, proses ini berarti melakukan pull image dari registry. Untuk binary (tarball, package), kita melakukan download dari object storage. Pola yang digunakan tetap sama: ambil artifact, verifikasi checksum, lakukan extract, dan lakukan switch symlink.
# playbooks/distribute-artifact.yml
---
- name: Distribusikan artifact ke managed node
hosts: appservers
vars:
artifact_url: "https://artifacts.company.com/releases/{{ app_name }}/{{ version }}/{{ app_name }}-{{ version }}.tar.gz"
artifact_checksum: "sha256:{{ artifact_sha256 }}"
tasks:
- name: Buat direktori artifact
file:
path: /opt/releases/{{ version }}
state: directory
owner: deployer
mode: '0755'
- name: Download artifact dengan verifikasi checksum
get_url:
url: "{{ artifact_url }}"
dest: "/opt/releases/{{ version }}/app.tar.gz"
checksum: "{{ artifact_checksum }}" # Ansible otomatis verifikasi setelah download
owner: deployer
mode: '0644'
register: artifact_download
- name: Extract artifact
unarchive:
src: "/opt/releases/{{ version }}/app.tar.gz"
dest: "/opt/releases/{{ version }}/"
remote_src: true
when: artifact_download.changed
- name: Atomic symlink switch ke versi baru
file:
src: "/opt/releases/{{ version }}"
dest: /opt/app/current
state: link
force: true # Overwrite symlink yang ada
- name: Bersihkan release lama (simpan 3 terakhir)
shell: |
ls -dt /opt/releases/*/ | tail -n +4 | xargs rm -rf
args:
warn: false
changed_when: false
Parameter checksum: di modul get_url adalah fitur Ansible yang secara otomatis mendownload SHA256SUM, memverifikasinya, dan menggagalkan task jika checksum tidak cocok. Ini merupakan pertahanan pertama terhadap file download yang corrupt atau artifact yang disusupi di tengah jalan (man-in-the-middle attack). Tanpa verifikasi checksum, kita tidak memiliki jaminan bahwa binary di server adalah binary yang kita upload.
Pola atomic symlink adalah cara yang elegan untuk deployment tanpa downtime. Symlink /opt/app/current diperbarui secara atomic setelah semua file berhasil diekstrak. Service yang berjalan dan membaca dari /opt/app/current akan selalu merujuk ke direktori release yang lengkap dan konsisten. Tidak ada celah waktu di mana service membaca direktori yang baru setengah diekstrak.
Atomic Symlink untuk Zero-Downtime Deployment #
sequenceDiagram
participant CI as "CI Pipeline"
participant FS as "Filesystem"
participant App as "Aplikasi (running)"
CI->>FS: "extract ke /opt/releases/v2.1.0/"
CI->>FS: "symlink /opt/app/current → /opt/releases/v2.1.0/"
Note over FS,App: "Switch symlink adalah atomic di level filesystem"
App->>FS: "baca /opt/app/current/file.txt"
FS-->>App: "file dari v2.1.0"
CI->>App: "SIGHUP reload config"
App->>FS: "buka file baru"
CI->>FS: "hapus /opt/releases/v2.0.9/ (cleanup)"
Sequence diagram ini memperlihatkan betapa elegannya atomic symlink: ekstrak direktori baru di luar path yang sedang dibaca aplikasi, lalu lakukan switch symlink yang merupakan operasi atomic di tingkat filesystem. Aplikasi yang sedang berjalan tidak pernah membaca direktori yang setengah jadi — saat symlink berganti, semua request berikutnya langsung membaca dari direktori baru.
Di Linux, symlink adalah atomic rename. Perintah ln -sfn target linkname adalah satu syscall yang mengganti target symlink secara instan. Tidak ada celah di mana symlink merujuk ke target yang tidak valid atau setengah jadi. Inilah yang membuat pola atomic symlink sangat aman digunakan untuk deployment tanpa downtime.
Artifact di Object Storage (S3/MinIO) #
Untuk artifact non-container seperti binary atau package, object storage adalah pilihan yang tepat. S3 (atau yang kompatibel: MinIO, Wasabi, Backblaze B2) menawarkan durability 99.999999999% dengan biaya per-GB yang jauh lebih murah daripada block storage.
# Upload artifact ke S3 setelah build
- name: Upload artifact ke S3
amazon.aws.s3_object:
bucket: company-artifacts
object: "releases/{{ app_name }}/{{ version }}/{{ app_name }}-{{ version }}.tar.gz"
src: "/tmp/build/{{ app_name }}-{{ version }}.tar.gz"
mode: put
metadata:
version: "{{ version }}"
git_sha: "{{ git_sha }}"
build_date: "{{ ansible_date_time.iso8601 }}"
region: ap-southeast-1
# Upload checksum file
- name: Upload SHA256 checksum
amazon.aws.s3_object:
bucket: company-artifacts
object: "releases/{{ app_name }}/{{ version }}/{{ app_name }}-{{ version }}.tar.gz.sha256"
content: "{{ artifact_sha256 }} {{ app_name }}-{{ version }}.tar.gz"
mode: put
region: ap-southeast-1
S3 metadata (x-amz-meta-version, x-amz-meta-git-sha) membuat artifact bersifat self-describing. Saat kita melakukan list object di S3, metadata ini bisa dilihat tanpa harus men-download artifact. Ini membantu operasi seperti “cari artifact untuk commit abc123” tanpa harus memindai isi artifact secara manual.
ANTI-PATTERN: No Versioning vs Semver + Digest #
Tanpa versioning yang konsisten, kita akan segera kehilangan jejak mengenai: “artifact mana yang sedang berjalan di production?”, “kapan bug ini muncul?”, atau “rollback ke versi mana?”.
# ANTI-PATTERN: tag latest, tidak ada versioning
docker tag myapp:latest registry.company.com/myapp:latest
docker push registry.company.com/myapp:latest
# Di server, jalankan:
docker run -d registry.company.com/myapp:latest
Masalah fatal: tag latest selalu berubah. Hari ini latest adalah v2.0.5, besok bisa menjadi v2.0.6 tanpa kita sadari. Saat deployment dilakukan dengan tag latest, kita tidak mengetahui secara pasti versi mana yang sedang berjalan. Saat bug muncul, kita tidak bisa melakukan “freeze” pada production untuk melakukan investigasi karena target latest terus bergerak.
# BENAR: tag dengan semver + digest SHA256
VERSION="2.1.0"
GIT_SHA=$(git rev-parse --short HEAD)
TAG="${VERSION}-${GIT_SHA}"
DIGEST=$(docker build -t registry.company.com/myapp:${TAG} . | tee /dev/stderr | grep -oP 'sha256:[a-f0-9]{64}')
# Push dengan multiple tags
docker push registry.company.com/myapp:${TAG}
docker push registry.company.com/myapp:${VERSION} # Major.minor pointer
# Di server, jalankan dengan tag eksplisit + digest
- name: Deploy dengan tag eksplisit
community.docker.docker_container:
name: myapp
image: "registry.company.com/myapp:2.1.0-abc123@{{ image_digest }}"
state: started
Dengan pola ini, deployment ke production selalu memiliki tiga identifier: tag eksplisit (2.1.0-abc123), digest SHA256, dan metadata di manifest. Tidak ada ambiguitas. Rollback ke v2.0.5 cukup dilakukan dengan mengganti tag, tanpa ada guesswork.
Selalu sertakan digest SHA256 saat melakukan deploy ke production, bukan hanya sekadar tag. Tag bisa dipindahkan (meskipun tidak seharusnya), tetapi digest SHA256 adalah identifier cryptographic dari isi image. Saat kita men-deploy dengan image: myapp@sha256:abc..., kita dijamin mendapatkan image yang sama persis dengan yang kita verifikasi sebelumnya. Ini merupakan jaminan terkuat (strongest guarantee) dalam supply chain artifact.
Perbandingan Artifact Storage #
Pemilihan artifact storage mempengaruhi biaya, fitur, dan kompleksitas operasional. Tabel ini membandingkan opsi-opsi yang populer:
| Aspek | Docker Hub | GHCR | AWS ECR | JFrog Artifactory | Sonatype Nexus | S3/MinIO |
|---|---|---|---|---|---|---|
| Biaya (free tier) | 100 pulls/6 jam | 500MB storage | 500MB/bulan | Open source: gratis | Open source: gratis | ~$23/TB/bulan |
| Native Docker | Ya | Ya | Ya | Ya | Ya | Tidak (perlu config) |
| Native npm/PyPI/Maven | Tidak | Tidak | Tidak | Ya | Ya | Tidak |
| Replication multi-region | Ya (private) | Tidak | Ya (cross-region) | Ya (Enterprise) | Ya (Pro) | Ya (cross-region replication) |
| Vulnerability scanning | Terbatas | Ya (GitHub) | Ya (Inspector) | Ya (Xray) | Ya (Pro) | Tidak (perlu tool terpisah) |
| Pull-through cache | Tidak | Ya | Tidak | Ya | Ya | Tidak |
| Self-hostable | Tidak | Tidak | Tidak | Ya | Ya | Ya (MinIO) |
| Cocok untuk | Public image | Project di GitHub | AWS-native shop | Enterprise multi-format | Java/Python shop | Custom large artifact |
| Akses kontrol | Token | GitHub IAM | IAM | LDAP/SAML | LDAP | IAM policy |
| Audit trail | Terbatas | GitHub audit | CloudTrail | Lengkap | Lengkap | CloudTrail/S3 access log |
| Cleanup otomatis | Tidak | Tidak | Ya (lifecycle policy) | Ya | Ya | Ya (lifecycle rule) |
Untuk tim yang menggunakan full-Docker dan sudah berada di AWS, ECR adalah pilihan paling sederhana — integrasi IAM, lifecycle policy built-in, dan tidak ada biaya egress dalam region. Untuk tim yang bersifat polyglot (Docker + npm + Maven + PyPI), Artifactory OSS atau Nexus OSS memberikan single registry untuk semua format. Untuk tim yang fokus pada biaya rendah dan kontrol penuh, MinIO self-hosted adalah pilihan yang solid.
Decision Tree Storage Backend #
Memilih storage backend yang tepat bergantung pada format artifact, skala, dan ekosistem kita. Decision tree ini membantu menentukan pilihan:
flowchart TD
A["Format artifact?"] -->|"Container image"| B["Container Registry"]
A -->|"Tarball / binary"| C["Object Storage"]
A -->|"Polyglot packages"| D["Universal Repo Manager"]
B --> E{"Cloud atau Self-host?"}
E -->|"Cloud-native AWS"| F["ECR"]
E -->|"Cloud-native GCP"| G["GAR"]
E -->|"Cloud-native Azure"| H["ACR"]
E -->|"Project di GitHub"| I["GHCR"]
E -->|"Self-host"| J["Harbor / Quay"]
D --> K{"Skala?"}
K -->|"Besar enterprise"| L["Artifactory Pro"]
K -->|"Tim kecil-menengah"| M["Nexus OSS"]
C -->|"AWS"| N["S3"]
C -->|"Self-host"| O["MinIO"]
C -->|"Multi-cloud"| P["Cloudflare R2 / Wasabi"]
Decision tree ini bukan aturan kaku — ini adalah heuristik. Untuk project kecil, kita bisa menggunakan GHCR untuk semua hal termasuk tarball. Untuk enterprise dengan compliance ketat, Artifactory Pro mungkin menjadi satu-satunya pilihan yang memenuhi syarat audit. Yang penting: pilih berdasarkan kebutuhan riil, bukan berdasarkan “best practice” yang kita baca tanpa memahami konteksnya.
Artifact Lifecycle Management #
Artifact lama yang menumpuk akan menghabiskan storage yang mahal. Bersihkan secara berkala dengan retention policy yang jelas.
# playbooks/cleanup-artifacts.yml
---
- name: Bersihkan artifact Docker yang sudah tidak dipakai
hosts: localhost
vars:
registry: registry.company.com
image_name: myapp
keep_versions: 10 # Simpan 10 versi terakhir
tasks:
- name: Ambil semua tag image dari registry
uri:
url: "https://{{ registry }}/v2/{{ image_name }}/tags/list"
headers:
Authorization: "Bearer {{ vault_registry_token }}"
return_content: true
register: image_tags
no_log: true
- name: Sortir tag dan ambil yang akan dihapus
set_fact:
tags_to_delete: >-
{{ image_tags.json.tags
| sort
| list
| difference(['latest'])
| list
| reverse
| list
| skip(keep_versions) }}
- name: Hapus tag lama dari registry
uri:
url: "https://{{ registry }}/v2/{{ image_name }}/manifests/{{ item }}"
method: DELETE
headers:
Authorization: "Bearer {{ vault_registry_token }}"
status_code: [202, 404]
loop: "{{ tags_to_delete }}"
loop_control:
label: "Menghapus tag: {{ item }}"
Aturan praktis: simpan N versi terakhir di registry (N=10 adalah default yang baik), simpan semua yang sedang berjalan di production, dan jangan simpan yang sudah tidak dipakai lebih dari 90 hari. Untuk container registry cloud seperti ECR, gunakan lifecycle policy built-in yang lebih reliable daripada playbook custom.
Artifact Signing dengan Cosign #
Di era supply chain attack (CI yang terkompromi, dependency yang berbahaya), verifikasi bahwa artifact benar-benar dibuat oleh tim kita — bukan pihak luar yang menyamar — menjadi sangat penting. Cosign (bagian dari Sigstore) memungkinkan cryptographic signing untuk image container.
# Sign image setelah build
COSIGN_EXPERIMENTAL=1 cosign sign registry.company.com/myapp:v2.1.0-abc123
# Verify signature saat deployment
cosign verify registry.company.com/myapp:v2.1.0-abc123 \
--certificate-identity-regexp "https://github.com/company/myapp" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
# playbooks/deploy-with-verification.yml
- name: Verify artifact signature sebelum deploy
hosts: appservers
tasks:
- name: Pull image
community.docker.docker_image:
name: "registry.company.com/myapp:{{ deploy_version }}"
source: pull
- name: Verifikasi signature dengan cosign
command: >
cosign verify registry.company.com/myapp:{{ deploy_version }}
--certificate-identity-regexp "https://github.com/company/myapp"
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
register: signature_check
failed_when: signature_check.rc != 0
changed_when: false
- name: Deploy hanya jika signature valid
community.docker.docker_container:
name: myapp
image: "registry.company.com/myapp:{{ deploy_version }}"
state: started
when: signature_check.rc == 0
Signing menambahkan lapisan keamanan (security layer): bahkan jika registry terkompromi dan penyerang menukar image, signature verification akan gagal dan deployment otomatis ditolak. Ini adalah defense-in-depth yang murah untuk deployment bernilai tinggi.
Cosign gratis dan open source, bagian dari proyek Sigstore yang disponsori oleh Linux Foundation. Bagi sebagian besar tim, setup awal signing + verification memakan waktu kurang dari 1 hari kerja, tetapi nilainya dalam hal supply chain security sangat besar. Mulailah dari tim yang memiliki compliance requirement (finance, healthcare), atau yang merilis aplikasi ke publik.
Ringkasan #
- Artifact harus immutable — setelah dibuat dengan tag tertentu, isinya tidak pernah berubah. Bugfix = artifact baru dengan tag baru.
- Tambahkan label/metadata ke artifact saat build: versi, git SHA, tanggal build, pipeline ID — memungkinkan traceability penuh dari artifact ke kode sumber.
- Gunakan
checksum:diget_urluntuk memverifikasi integritas artifact setelah download — mendeteksi download yang rusak atau artifact yang dimanipulasi.- Atomic symlink (
/opt/app/current → /opt/releases/v2.1.0) adalah pola deployment yang elegan — switch versi terjadi secara atomic, rollback semudah mengganti symlink.- Simpan checksum artifact di tempat yang terpisah dari artifact itu sendiri — idealnya di object storage yang berbeda atau signed secara kriptografis.
- Lifecycle management: bersihkan artifact lama secara otomatis — simpan hanya N versi terakhir untuk mengontrol biaya storage.
- Jangan commit binary ke Git — binary membengkakkan repository dan tidak punya build metadata. Pakai registry, bukan Git, untuk artifact.
- Tag dengan semver + git SHA, plus digest SHA256 saat deploy ke production. Hindari tag
latestdi production.- Pilih registry berdasarkan ekosistem: ECR untuk AWS-native, GHCR untuk GitHub projects, Artifactory/Nexus untuk polyglot, S3/MinIO untuk tarball besar.
- Sign artifact dengan cosign untuk supply chain security — verifikasi signature di deployment job sebelum start container.
- S3 metadata (
x-amz-meta-version,x-amz-meta-git-sha) membuat artifact self-describing untuk query tanpa download.- Retention policy harus eksplisit: simpan N versi terakhir, simpan semua yang running di production, cleanup lebih dari 90 hari.
← Sebelumnya: Rollback Strategy Berikutnya: Notification & Reporting →