Build yang gagal sering terasa seperti ditolak tanpa alasan: BuildKit menyembur lusinan baris, sebuah ERROR: failed to solve: yang panjang menutup layar, lalu kita kembali ke prompt tanpa image. Padahal pesan galat BuildKit sebenarnya terstruktur rapi — pesan itu menyebut instruksi Dockerfile mana yang gagal, exit code perintah di dalamnya, dan jenis masalahnya: berkas yang hilang dari build context, perintah yang gagal dieksekusi, atau cache yang menipu kita. Sore ini tim toko mendapat semuanya sekaligus: build image worker:1.0 (basis python:3.12-slim) tumbang di tengah RUN, sementara build web-backend milik Siti berhenti di COPY. Chapter ini mengajari cara membaca bahasa galat tersebut, lalu memberi tiga teknik isolasi yang memperpendek jalan menuju akar masalah: reproduksi manual di base image, segmentasi Dockerfile, dan pembongkaran build cache — ditutup tabel pola kegagalan yang paling sering muncul beserta obatnya.
Anatomi Pesan Galat BuildKit #
Kegagalan worker:1.0 adalah kasus yang paling sering menimpa pemula — dan juga yang paling mudah diurai:
#7 [3/5] RUN apt-get install -y curl
#7 0.412 Reading package lists...
#7 0.638 E: Unable to locate package curl
#7 ERROR: process "/bin/sh -c apt-get install -y curl" did not complete successfully: exit code: 100
------
> [3/5] RUN apt-get install -y curl:
0.412 Reading package lists...
------
ERROR: failed to solve: process "/bin/sh -c apt-get install -y curl" did not complete successfully: exit code: 100Mari kita bedah anatomi pesan tersebut baris demi baris:
#7adalah nomor vertex di graf eksekusi BuildKit — setiap langkah build bernomor, dan seluruh baris log yang diawali#7berasal dari langkah yang sama.[3/5]adalah petunjuk paling berharga: instruksi ke-3 dari 5 instruksi build. Buka Dockerfile, hitung instruksi aktif, dan kita langsung tahu baris mana yang gagal — tanpa menebak.E: Unable to locate package curladalah kata apt sendiri — index paket di dalam image tidak memuat paket yang diminta; baris berawalanE:itulah stderr perintah di dalamRUN.exit code: 100adalah kode keluar apt untuk kegagalan operasi paket — index kosong, GPG kedaluwarsa, konflik lock, atau repository yang sudah tidak tersedia.ERROR: failed to solve:hanyalah pembungkus BuildKit — diagnosa CLI berhenti di situ, tetapi seluruh bukti yang diperlukan sudah tersaji di baris-baris di atasnya.
Penyebab klasik kasus ini: base image berganti ke varian -slim yang sengaja menghapus index paket agar ramping, sementara RUN apt-get install dijalankan tanpa apt-get update terlebih dahulu. Obatnya sudah kita kenal dari chapter Mengurangi Ukuran Image: gabungkan apt-get update && apt-get install -y curl dalam satu instruksi RUN yang sama — atau simpan index-nya di cache mount seperti praktik chapter Cache Mount. Menggabungkannya penting bukan gaya: dua RUN terpisah membuat apt-get update menjadi layer tersendiri yang ter-cache selamanya, sehingga install berikutnya memakai index basi meski Dockerfile sudah diperbaiki.
Path yang Tidak Ditemukan di Build Context #
Kegagalan kedua menyasar instruksi COPY dan ADD. Wujud modernnya, dari Dockerfile web-backend milik Siti:
------
> [4/5] COPY app/server.js /app/server.js:
------
ERROR: failed to solve: failed to compute cache key: "/app/server.js" not foundPesan failed to compute cache key terkesan berputar-putar, tetapi intinya satu: berkas app/server.js tidak ditemukan di build context yang dikirim builder — dan COPY hanya boleh mengambil berkas dari kumpulan tersebut, mekanisme yang kita bedah di chapter Build Context. Kasus Siti: berkasnya ada di akar context bernama server.js, sementara Dockerfile menulis app/server.js. Tiga penyebab menempati urutan frekuensi: salah ketik atau salah folder path, perbedaan huruf besar-kecil (Server.js dan server.js adalah dua berkas berbeda bagi Linux), atau berkas justru dikecualikan oleh .dockerignore.
Versi klasiknya berbunyi lebih langsung namun membingungkan karena menyebut path internal daemon:
COPY failed: stat /var/lib/docker/tmp/docker-builder381749204/serve.js: no such file or directoryPesan warisan builder lama itu bermakna sama persis: berkas tidak ada dalam arsip context yang dikirim builder. Diagnosis cepatnya selalu dua langkah — daftar isi context dengan ls, lalu baca .dockerignore.
Reproduksi Manual di Base Image #
Ketika instruksi RUN yang gagal, cara tercepat menuju akar masalah bukan mengedit Dockerfile dan membangun ulang berkali-kali — setiap build mengulang seluruh layer sebelum baris gagal. Lebih efisien masuk langsung ke base image dan menjalankan perintah yang sama secara manual:
$ docker run -it --rm python:3.12-slim shOutput yang Dihasilkan:
# apt-get install -y curl
Reading package lists...
E: Unable to locate package curlGalat yang sama muncul dalam hitungan detik, tanpa satu layer pun dibangun. Dari shell interaktif tersebut kita bisa mencoba varian perbaikan — menjalankan apt-get update lebih dulu, memeriksa nama paket dengan apt-cache search, menguji argumen perintah — lalu begitu resep yang berhasil ditemukan, tulis kembali ke Dockerfile. Loop reproduksi manual inilah yang mengubah debugging build dari menunggu-menunggu menjadi percakapan bolak-balik yang cepat; pola container sekali pakai --rm yang sama kita pakai sepanjang course.
Melihat Lebih Banyak dengan –progress=plain dan –no-cache #
Output bawaan BuildKit dirancang ringkas untuk terminal interaktif: layer yang diambil dari cache hanya menampilkan CACHED tanpa isinya. Saat penyelidikan butuh mata penuh, dua flag datang menolong — di sini dipakai untuk membangun web-backend yang barusan diperbaiki:
$ docker build --progress=plain --no-cache -t web-backend:1.1 .Output yang Dihasilkan (dipotong):
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 189B done
#2 [internal] load .dockerignore
#2 transferring context: 29B done
#3 [internal] load metadata for docker.io/library/node:22-alpine
#4 [1/5] FROM docker.io/library/node:22-alpine
#4 DONE 0.0s
#5 [2/5] WORKDIR /app
#5 DONE 0.0s
#6 [3/5] COPY package*.json ./
#6 DONE 0.1s
#7 [4/5] RUN npm ci --omit=dev
#7 1.204 npm warn deprecated [email protected]: glob versions before 9 are deprecatedMari kita telaah kedua flag tersebut:
--progress=plainmenampilkan keluaran mentah setiap langkah tanpa animasi TTY — termasuk baris berprefix#7 1.204yang memuat timestamp relatif tiap keluaran perintah, sehingga kita bisa tahu perintah mana yang lambat dan keluaran mana yang muncul kapan.--no-cachememaksa seluruh layer dieksekusi ulang dari nol — perhatikan absennya kataCACHEDpada seluruh langkah. Flag ini adalah pembeda antara kegagalan yang sesungguhnya dan keanehan yang diciptakan cache basi; bila kecurigaan jatuh pada cache, satu build tanpa cache adalah pembuktian tercepat, dengan ongkos waktu build penuh.
Segmentasi Dockerfile untuk Isolasi #
Bila galat tetap samar setelah langkah-langkah di atas, teknik berikutnya memangkas wilayah pencarian dengan segmentasi: komentari separuh instruksi yang dicurigai, bangun ulang, lalu amati apakah kegagalan berpindah atau hilang:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
# USER node
# EXPOSE 3000
# CMD ["node", "server.js"]Bila build berhasil dengan bentuk terpotong ini, kegagalan pasti berada pada salah satu baris yang dikomentari — aktifkan kembali separuhnya, bangun lagi, dan ulangi sampai satu baris tersangka tunggal tersisa. Teknik bagi-dua ini adalah bisect — prinsip yang akan kita formalkan pada chapter Strategi Diagnostik — diterapkan pada ruang instruksi Dockerfile. Lima instruksi aktif pada potongan di atas juga menjelaskan angka [3/5] dan [4/5] yang kita lihat pada anatomi galat di awal chapter: penomoran BuildKit selalu menghitung instruksi aktif saja.
Membongkar Build Cache #
Build cache punya dua wajah: penyelamat waktu di hari baik, dan penyumbang disk nomor satu di hari buruk — pagi ini kita mengukurnya dengan docker system df pada chapter Debugging Storage dan menyapu 6.9GB dengan docker builder prune. Sore ini, tiga build besar sudah mengisinya kembali — cache memang tumbuh cepat itu. Untuk pemeriksaan yang lebih bedah, buildx menyediakan inventaris per rekaman:
$ docker buildx duOutput yang Dihasilkan (dipotong):
ID RECLAIMABLE SIZE LAST ACCESSED
p2qxvzj0ehkjkmwy7e0nfw4nx* true 114.6MB 2 hours ago
8dnq1wkejrqpsgmqzqhn9ik4b* true 89.2MB 2 hours ago
gx7l2e8vk1wzxdqwwah3lxl0mz true 512.4MB 5 hours ago
Total: 2.1GBTanda bintang pada kolom pertama menandai rekaman yang sedang dipakai builder aktif; kolom RECLAIMABLE menegaskan mana yang aman dihapus lewat docker builder prune. Inventaris ini juga memuat rekaman jenis exec.cachemount — cache dependensi npm/pip dari chapter Cache Mount — dan rekaman basi di dalamnya adalah tersangka ketika build menghasilkan keanehan yang hanya hilang setelah --no-cache: paket terkorup atau index kedaluwarsa bisa menetap di cache mount berhari-hari. Obatnya dua pilihan: bersihkan build cache secara menyeluruh, atau ganti id cache mount (misalnya id=npm-cache menjadi id=npm-cache-v2) supaya rekaman lama tidak pernah dijamah lagi.
Tabel Pola Umum Kegagalan Build #
Sepuluh pola berikut menutup hampir semua kegagalan build yang kita jumpai sehari-hari:
| Gejala | Penyebab paling umum | Solusi |
|---|---|---|
failed to compute cache key: "X" not found | Berkas tidak ada di build context — salah ketik atau beda huruf besar-kecil | Periksa path relatif akar context; samakan kapitalisasi |
COPY failed: stat ... no such file or directory | Penyebab sama — pesan varian builder lama | Periksa .dockerignore dan lokasi berkas |
failed to read dockerfile: open Dockerfile: no such file | Dockerfile tidak berada di context atau flag -f salah | Jalankan build dari direktori yang tepat atau set -f jalur/Dockerfile |
exit code: 100 pada RUN apt-get ... | Index paket kosong (base -slim), GPG kedaluwarsa, atau repo EOL | Gabungkan apt-get update && apt-get install dalam satu RUN; ganti base yang masih didukung |
exit code: 1 pada RUN npm ci | Resolusi dependensi gagal atau lock file tak sinkron | Reproduksi manual di base image; commit package-lock.json |
exit code: 127 pada RUN | Perintah tidak ditemukan di dalam image | Pastikan paket penyedianya terpasang di stage yang sama |
pull access denied ... repository does not exist | FROM menarik image privat tanpa login atau tag keliru | docker login; verifikasi nama dan tag image dasar |
no space left on device saat build | Disk host penuh oleh cache dan image | docker buildx du lalu docker builder prune — chapter Debugging Storage |
| Build sukses tetapi container menjalankan kode lama | Service Compose masih memakai image lama | docker compose up -d --build untuk membangun lalu menukar |
Keanehan hilang setelah --no-cache | Cache mount menyimpan artefak basi atau terkorupsi | Bersihkan build cache; ganti id cache mount |
Image akhirnya berhasil dibangun — tetapi ceritanya belum selesai. Container yang lahir dari image sempurna bisa saja mati tiga detik kemudian, dibangkitkan daemon, mati lagi, dan berulang tanpa henti: crash loop. Gejala itu hidup di wilayah pengetahuan yang sudah kita kumpulkan sepanjang section ini — exit code, logs, inspect, restart policy — dan chapter Mengatasi Container Crash merangkainya menjadi resep penanganan yang lengkap.
Referensi #
- Dokumentasi Docker Build
— anatomi keluaran BuildKit dan arti
failed to solve. - Praktik Terbaik Menulis Dockerfile
— pola
apt-get update && installdalam satuRUNdan urutan instruksi. - Referensi Dockerfile
— perilaku
COPY,RUN, dan build context. - Dokumentasi BuildKit
— vertex, cache record, dan
exec.cachemount.