Mengapa Tag Container Anda Berbohong

Anda menjalankan docker pull myapp:latest dan mengira Anda tahu persis apa yang akan Anda dapatkan. Tapi sebenarnya tidak. Tag itu bisa menunjuk ke image yang berbeda besok, atau bahkan sudah menunjuk ke image yang berbeda dari satu jam yang lalu. Tag yang sama, perintah yang sama, tetapi perangkat lunak yang berjalan di produksi bisa jadi sangat berbeda.

Ini bukan masalah teoretis. Banyak tim menghabiskan waktu berjam-jam untuk debugging masalah produksi hanya untuk menemukan bahwa image yang mereka kira berjalan ternyata bukan image yang sebenarnya berjalan. Tag mengatakan satu hal, tetapi isinya benar-benar berbeda.

Tempat Image Disimpan

Sebelum kita membahas tag dan masalahnya, kita perlu membahas di mana sebenarnya image container disimpan.

Ketika Anda membangun image Docker di laptop Anda, image itu hanya ada di mesin Anda. Untuk menjalankannya di server, di lingkungan staging, atau di produksi, Anda memerlukan tempat untuk menyimpan dan membagikan image tersebut. Tempat itu disebut registry.

Anggap registry sebagai server file untuk image container, tetapi dengan kemampuan tambahan. Registry mengelola versi, memeriksa integritas, dan mengontrol siapa yang dapat mengakses apa. Ketika Anda menjalankan docker pull nginx:latest, Anda menarik dari Docker Hub, yang merupakan registry publik. Siapa pun bisa menarik dari sana, tetapi siapa pun juga bisa mendorong ke sana, itulah mengapa Anda tidak boleh mengandalkan image publik untuk produksi.

Sebagian besar perusahaan menjalankan registry internal mereka sendiri. Opsi umum termasuk Harbor, Nexus, GitLab Container Registry, atau Amazon ECR. Registry internal memberi Anda tiga hal:

  • Provenans: Anda tahu dari mana setiap image berasal. Tidak ada image acak dari internet.
  • Kecepatan: Mentransfer image melalui jaringan internal Anda jauh lebih cepat daripada menarik dari internet publik.
  • Kontrol: Anda memutuskan siapa yang dapat mendorong image dan siapa yang dapat menariknya.

Tanpa registry, pipeline deployment Anda tidak memiliki tempat untuk menyimpan image yang dibangun. Dengan registry, Anda memiliki satu sumber kebenaran untuk setiap image yang diproduksi tim Anda.

Tag Adalah Label, Bukan Pengidentifikasi

Setiap image di registry memiliki satu atau lebih tag. Tag adalah label yang Anda tempelkan pada image untuk menandai versi atau varian tertentu. Anda melihat tag di mana-mana: myapp:1.0.0, myapp:staging, myapp:latest.

Tag itu praktis. Mereka memberi Anda cara yang mudah dibaca manusia untuk merujuk ke image. Alih-alih mengingat hash yang panjang, Anda mengetik myapp:1.0.0 dan Anda mendapatkan image yang Anda inginkan.

Tetapi tag memiliki kelemahan mendasar: mereka bisa berubah (mutable). Anda dapat mengubah image yang ditunjuk oleh sebuah tag kapan saja. Hari ini, myapp:latest menunjuk ke hash image abc123. Besok, setelah build baru, ia menunjuk ke def456. Tag tetap sama, tetapi image berubah.

Sifat mutable ini menciptakan masalah nyata. Pertimbangkan skenario di mana lingkungan staging Anda menjalankan myapp:staging. Pipeline Anda membangun image baru, menandainya sebagai staging, dan mendorongnya. Sekarang staging menjalankan kode baru. Tetapi bagaimana jika seseorang secara manual menimpa tag staging dengan image yang berbeda? Atau bagaimana jika pipeline Anda secara tidak sengaja menandai build yang salah? Anda tidak punya cara untuk mengetahui image mana yang sebenarnya berjalan di staging kecuali Anda memeriksa digest.

Pola Tag Immutable

Solusinya adalah menggunakan tag immutable - tag yang tidak pernah berubah setelah dibuat. Setelah Anda menetapkan tag ke sebuah image, tag itu akan tetap bersama image itu selamanya.

Tag immutable berisi informasi unik yang mengidentifikasi build tertentu. Pola umum meliputi:

  • Versi semantik: myapp:1.2.3
  • Hash commit Git: myapp:a1b2c3d
  • Timestamp build: myapp:20240515-1430
  • ID run pipeline: myapp:build-456

Dengan tag immutable, Anda dapat melacak kode mana yang berjalan di lingkungan mana pun. Jika seseorang melaporkan bug di produksi, Anda melihat tag, menemukan hash commit, dan tahu persis kode apa yang menghasilkan image itu. Tanpa tebak-tebakan, tanpa "versi latest yang mana ini?"

Beberapa tim menggabungkan pendekatan. Mereka menggunakan versi semantik untuk rilis, hash commit untuk build pengembangan, dan timestamp untuk deployment otomatis. Kuncinya adalah konsistensi: setiap build menghasilkan tag unik yang tidak pernah digunakan kembali.

Digest: Kebenaran Sejati

Tag itu praktis tetapi tidak dapat diandalkan. Jika Anda menginginkan kepastian mutlak tentang image mana yang Anda jalankan, Anda perlu menggunakan digest.

Setiap image container memiliki digest. Digest adalah hash kriptografis dari konten image. Bentuknya seperti sha256:abc123def456.... Digest unik untuk image tertentu. Jika konten image berubah bahkan satu byte saja, digest akan berubah sepenuhnya.

Tidak seperti tag, digest tidak dapat dipindahkan atau ditetapkan ulang. Digest sha256:abc123 akan selalu merujuk ke image yang persis sama, selamanya. Anda tidak bisa mengarahkan digest itu ke image yang berbeda tidak peduli apa pun yang Anda lakukan.

Ketika Anda menarik image berdasarkan digest, Anda dijamin mendapatkan persis image yang Anda harapkan. Tidak ada ambiguitas, tidak ada kemungkinan mendapatkan versi yang berbeda.

# Ini aman - Anda mendapatkan persis apa yang Anda minta
docker pull myapp@sha256:abc123def456

Menggunakan Tag dan Digest Bersama dalam Pipeline

Dalam praktiknya, Anda menggunakan tag dan digest bersama-sama. Tag membuat image mudah dibaca manusia dan mudah dirujuk. Digest memberikan jaminan bahwa Anda menjalankan image yang benar.

Berikut cara pipeline tipikal menangani ini:

  1. Bangun image.
  2. Tag dengan tag immutable (hash commit atau versi).
  3. Dorong image ke registry.
  4. Catat digest dalam metadata deployment Anda.
  5. Saat mempromosikan dari staging ke produksi, verifikasi bahwa digest cocok persis.

Langkah verifikasi sangat penting. Ketika Anda mempromosikan image dari staging ke produksi, Anda tidak boleh hanya memeriksa tag. Anda harus memeriksa bahwa digestnya identik. Ini mencegah situasi di mana seseorang menimpa tag staging dengan image yang berbeda, dan image yang salah itu dipromosikan ke produksi.

Berikut adalah contoh praktis cara mengambil digest setelah mendorong image dan kemudian menggunakannya untuk mengunci deployment:

# Build dan push image dengan tag immutable
docker build -t myapp:build-456 .
docker push myapp:build-456

# Ambil digest dari image yang sudah di-push
digest=$(docker inspect --format='{{index .RepoDigests 0}}' myapp:build-456 | cut -d'@' -f2)
echo "Digest: $digest"

# Gunakan digest di manifest deployment Kubernetes
cat <<EOF > deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp@$digest
EOF

# Apply deployment
kubectl apply -f deployment.yaml

Ini memastikan setiap pod menjalankan image yang persis sama, terlepas dari apa yang terjadi pada tag di registry.

Banyak alat deployment mendukung deployment berbasis digest. Kubernetes, misalnya, memungkinkan Anda menentukan image berdasarkan digest alih-alih tag:

spec:
  containers:
  - name: myapp
    image: myregistry.com/myapp@sha256:abc123def456

Ini memastikan bahwa setiap pod menjalankan image yang persis sama, terlepas dari apa yang terjadi pada tag di registry.

Daftar Periksa Praktis

Sebelum deployment Anda berikutnya, lakukan pemeriksaan ini:

  • Setiap build menghasilkan tag unik dan immutable (hash commit, versi, atau timestamp)
  • Tag latest tidak pernah digunakan dalam deployment produksi
  • Pipeline Anda mencatat digest dari setiap image yang dibangun
  • Promosi image antar lingkungan memverifikasi digest, bukan hanya tag
  • Registry Anda memiliki kontrol akses untuk mencegah push yang tidak sah
  • Image lama dibersihkan secara teratur untuk menghindari pembengkakan penyimpanan

Kesimpulan

Tag untuk manusia. Digest untuk mesin. Gunakan tag untuk mempermudah hidup Anda, tetapi gunakan digest untuk membuat deployment Anda andal. Ketika sesuatu salah di produksi, Anda ingin tahu persis apa yang berjalan. Tag yang mutable tidak akan memberi tahu Anda. Digest akan, setiap saat.