Skip to main content
DevOps dari nol · Pelajaran 3 dari 8 · 11 menit

Belajar Docker untuk Pemula: Image, Container, dan Dockerfile

Belajar Docker dari Dockerfile nyata: apa itu image dan container, cara kerja layer dan build cache, serta urutan instruksi agar build tetap cepat.

Oleh Matthews WongDitinjau pada

Poin utama

Docker image adalah tumpukan layer read-only yang dibangun dari instruksi di Dockerfile; container adalah salinan image yang sedang berjalan dengan satu layer tipis yang bisa ditulisi di atasnya. Docker menyimpan cache tiap layer, jadi urutan instruksi dari yang paling jarang ke paling sering berubah itulah yang membuat rebuild tetap cepat.

Setelah pelajaran ini, Anda bisa

  • Menjelaskan perbedaan image, layer, dan container.
  • Memperkirakan langkah build Docker mana yang diambil dari cache setelah Anda mengubah file.
  • Menyusun Dockerfile dan .dockerignore agar build cepat dan secret tidak ikut masuk.
Lihat cara kerjanya

Ikuti diagramnya langkah demi langkah

Tekan Putar, atau melangkah dengan panah. Setiap langkah juga ditulis di bawah diagram.

Bagaimana Docker image dibangun, layer demi layer

Build context dikirim ke docker build, tiap instruksi Dockerfile menambah satu layer ke image, perubahan file hanya membangun ulang layer sesudahnya, dan menjalankan image menambah satu layer yang bisa ditulisi.

Langkah 1 dari 7. Dockerfile adalah daftar instruksi. Sebagian besar instruksi menambah satu layer read-only ke image, dan layer-layer itu bertumpuk dari bawah ke atas.

Tombol: ← → langkah · Home / End · K putar atau jeda

  1. Dockerfile adalah daftar instruksi. Sebagian besar instruksi menambah satu layer read-only ke image, dan layer-layer itu bertumpuk dari bawah ke atas.

  2. docker build mengirim build context lebih dulu. .dockerignore menahan node_modules, .git, dan .env, jadi semuanya tidak mungkin masuk ke image.

  3. FROM memulai dari image yang sudah ada. Layer-layernya dipakai ulang apa adanya; tidak ada yang disalin atau diubah.

    FROM node:20-alpine AS deps
  4. Salin file dependensinya saja, lalu install. Layer ini hanya berubah kalau package-lock.json berubah, dan cache mount menyimpan paket yang sudah diunduh antar build.

    COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm npm ci
  5. Sekarang salin source code lalu build. Source sering berubah, jadi instruksi ini diletakkan paling akhir.

    COPY . . RUN npm run buildTebak langkah berikutnya Anda mengubah satu file source lalu build lagi. Apa yang dijalankan ulang? (Semua instruksi, mulai dari FROM · COPY . . dan semua sesudahnya · Hanya RUN npm ci)
  6. Ubah satu file lalu build lagi. Layer base dan install diambil dari cache; COPY . . dan semua layer sesudahnya dijalankan ulang.

    Tebak langkah berikutnya Apa yang ditambahkan saat image dijalankan? (Satu layer tipis yang bisa ditulisi · Salinan tulis dari setiap layer)
  7. Saat image dijalankan, satu layer tipis yang bisa ditulisi ditambahkan di atas. Layer image tetap utuh, jadi banyak container bisa memakainya bersama.

Apa perbedaan Docker image dan container?

Image adalah template read-only: tumpukan layer, di mana setiap layer berisi perubahan filesystem dari satu instruksi Dockerfile. Setelah dibuat, layer tidak pernah berubah. Container adalah hasil saat image dijalankan: Docker menaruh satu layer tipis yang bisa ditulisi di atasnya, dan semua yang ditulis proses yang berjalan masuk ke sana.

Baca selengkapnya (1)

Pembagian itulah yang membuat sepuluh container dari satu image hampir tidak menambah ruang disk: semuanya berbagi layer read-only yang sama, dan masing-masing hanya menyimpan layer tulisnya sendiri. Itu juga alasan semua yang ditulis container hilang saat container dihapus, kecuali Anda memasang volume.

Kenapa urutan Dockerfile menentukan cepat lambatnya rebuild?

Docker menyimpan cache setiap layer. Pada build berikutnya, layer dipakai ulang jika instruksi dan input-nya tidak berubah. Tetapi invalidasi hanya mengalir ke bawah: begitu satu layer berubah, semua layer sesudahnya ikut dibangun ulang, walaupun instruksinya sama persis.

Baca selengkapnya (1)

Jadi aturannya: urutkan instruksi dari yang paling jarang ke yang paling sering berubah. Dependensi jarang berubah dan paling lama diinstall, jadi salin package.json dan package-lock.json saja lalu jalankan install sebelum menyalin sisa source. Perubahan satu baris kode kemudian memakai ulang layer install dan selesai dalam hitungan detik.

Apa yang dilindungi oleh .dockerignore?

Sebelum build, docker build mengirim build context: kumpulan file yang bisa diakses build, biasanya folder proyek. Apa pun di dalamnya bisa tersalin ke image oleh instruksi COPY . . yang ceroboh. File .dockerignore mengeluarkan path dari context, jadi node_modules, .git, dan file .env tidak pernah sampai ke builder.

Baca selengkapnya (1)

File yang sama punya jebakan. Pola seperti *.md hanya cocok dengan file Markdown di root context; file di subfolder tetap ikut. Pengecualian diawali tanda !, dan baris terakhir yang cocok yang menang, jadi pengecualian yang ditulis sebelum aturan yang ingin ditimpanya tidak berpengaruh apa-apa.

Kenapa npm ci, dan apa gunanya cache mount?

npm ci menginstall persis seperti yang tertulis di package-lock.json, jadi setiap build mendapat pohon dependensi yang sama. npm install bisa memperbarui lockfile dan membuat hasil build bergeser. Di Dockerfile, yang Anda butuhkan adalah yang bisa direproduksi.

Baca selengkapnya (1)

Cache mount, ditulis RUN --mount=type=cache, memberi satu instruksi itu folder persisten yang berada di luar layer image. Arahkan ke cache unduhan npm, maka perubahan lockfile tetap menjalankan ulang npm ci, tetapi npm memakai ulang paket yang sudah diunduh, bukan mengambil semua tarball dari awal.

Cara situs ini melakukannya

Konfigurasi nyata di balik pelajaran ini

Kutipan dari repositori yang membangun dan men-deploy situs ini, apa adanya.

Dockerfile:1-11
# syntax=docker/dockerfile:1
# The syntax directive is what enables `--mount=type=cache` below.

FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
# Cache npm's download directory, not node_modules. This survives within a build
# even when the layer above is invalidated, so a lockfile change re-links from
# cache instead of re-downloading every tarball. Still `npm ci`, so the lockfile
# stays authoritative.
RUN --mount=type=cache,target=/root/.npm npm ci

Stage pertama image situs ini. Hanya dua file dependensi yang disalin sebelum install, jadi mengubah satu halaman tidak pernah menjalankan ulang npm ci. Cache mount diarahkan ke folder unduhan npm, bukan node_modules: perubahan lockfile tetap menginstall dengan bersih, hanya tidak mengunduh ulang semuanya. Baris 1 yang mengaktifkan sintaks --mount.

.dockerignore:4-10
node_modules
.git
.env
.env.*
.DS_Store
npm-debug.log*
*.log

Baris-baris pertama .dockerignore situs ini. Dependensi lokal, riwayat git, dan semua file .env dikeluarkan dari build context, jadi COPY . . di Dockerfile tidak akan pernah menyalin secret ke dalam image.

.dockerignore:37-41
# NOT excluded, deliberately — each of these looks like dead weight and is not:
#   public/    78 MB, but the runner stage copies it into the final image
#   messages/  12 MB, compiled into the server bundle by next-intl
#   scripts/   10 MB, because `prebuild` runs generate-blog-sources.mjs --check,
#              check-placements.mjs and check-imports.mjs

Sisi lain dari aturannya. Folder-folder ini terlihat berat, dan mengeluarkannya justru membuat build gagal atau situs rusak. Sebelum mengabaikan sesuatu yang besar, periksa dulu apakah build membacanya.

Yang sering rusak

Kesalahan yang benar-benar membuang waktu

  • COPY . . sebelum install

    Menyalin seluruh proyek sebelum npm ci mengikat layer install ke setiap file source, jadi perubahan satu baris menginstall ulang semua dependensi. Salin manifest, install, baru salin sisanya.

  • Secret ikut terpanggang ke dalam layer

    Tanpa .dockerignore, file .env ikut masuk lewat COPY . . dan tetap tersimpan di layer itu selamanya. Menghapusnya di instruksi berikutnya tidak menolong: layer sebelumnya masih menyimpannya. Keluarkan secret dari context dan berikan saat runtime.

  • Mengira cache mount bertahan di CI

    Di runner CI yang baru, cache mount mulai kosong, dan cache GitHub Actions tidak menyimpannya secara default. Situs ini mengakalinya untuk cache kompilasi Next dengan pengecualian di .dockerignore, !.next/cache, sehingga cache yang dipulihkan ikut terkirim bersama build context.

  • Yang akan saya ubah di sini: base image

    Situs ini masih dibangun di atas node:20-alpine, dan Node 20 sudah end of life sejak 30 April 2026. Base image berhenti mendapat perbaikan keamanan saat runtime-nya berhenti, jadi perubahan nyata berikutnya untuk Dockerfile ini adalah pindah ke versi Node yang masih didukung.

Image atau container: apa bedanya
Apa ituTumpukan layer read-onlySalinan image yang sedang berjalan
Bisa berubah?Tidak: Anda membangun image baruYa, di layer tulisnya
Dibuat dengandocker builddocker run
Saat dihapusLayer hilang jika tidak ada image yang memakainyaLayer tulisnya ikut hilang
Coba sendiri

Lakukan di komputer Anda sendiri

Anda hanya butuh Docker dan Node terpasang. Buat folder berisi server kecil, build dua kali, lalu perhatikan langkah mana yang diambil dari cache.

  1. Buat package.json dan src/server.js, lalu jalankan npm install sekali untuk membuat package-lock.json.

    { "name": "devops-lab", "version": "1.0.0", "private": true }
    
    // src/server.js
    require("node:http")
      .createServer((req, res) => res.end("hello\n"))
      .listen(3000, "0.0.0.0");
  2. Tulis Dockerfile yang menginstall dependensi sebelum menyalin source, dan .dockerignore yang mengeluarkan node_modules.

    # syntax=docker/dockerfile:1
    FROM node:22-alpine
    WORKDIR /app
    COPY package.json package-lock.json ./
    RUN --mount=type=cache,target=/root/.npm npm ci
    COPY . .
    CMD ["node", "src/server.js"]
    
    # .dockerignore
    node_modules
    .git
  3. Build, ubah server-nya, lalu build lagi. Langkah npm ci menampilkan CACHED; COPY . . dijalankan ulang.

    docker build -t devops-lab:v1 .
    echo "// tweak" >> src/server.js
    docker build -t devops-lab:v1 .
  4. Tampilkan daftar layer beserta ukurannya, lalu jalankan container dan buka http://localhost:8080.

    docker history devops-lab:v1
    docker run --rm -p 8080:3000 devops-lab:v1
Kuis singkat

Tiga pertanyaan sebelum lanjut

Pilih jawaban untuk melihat kenapa benar atau salah. Tidak ada yang disimpan atau dinilai, jadi tebak saja dengan bebas.

  1. 1.Sebuah Dockerfile menjalankan COPY . . lalu RUN npm ci. Anda memperbaiki salah ketik di satu komponen lalu build ulang. Apa yang terjadi pada langkah install?
  2. 2.Tidak ada .dockerignore, dan sebuah file .env terselip di proyek. Dockerfile menjalankan COPY . . lalu RUN rm .env. Apakah secret itu sudah keluar dari image?
  3. 3.Di lokal, perubahan lockfile tetap cepat di-build karena cache mount menyimpan unduhan npm. Di runner GitHub Actions yang baru, setiap build mengunduh ulang semua paket. Kenapa?
Pikirkan dulu

Jawab dalam hati, lalu cek

  1. 1.Anda mengubah satu baris di sebuah komponen. Instruksi Dockerfile mana yang dijalankan ulang?Lihat jawabannya

    Jawaban:COPY . . dan semua instruksi sesudahnya. Invalidasi hanya mengalir ke bawah, dan layer install hanya bergantung pada package.json dan package-lock.json.

  2. 2.Kenapa package.json dan package-lock.json disalin sebelum COPY . .?Lihat jawabannya

    Jawaban:Agar layer install yang lambat hanya bergantung pada file yang paling jarang berubah. Panduan Docker sendiri menyarankan langkah yang sering berubah diletakkan di akhir Dockerfile.

  3. 3..dockerignore situs ini berisi *.md. Apakah itu mengecualikan docs/guide.md?Lihat jawabannya

    Jawaban:Tidak. *.md hanya cocok dengan file Markdown di root context; file di subfolder tetap ikut. Situs ini mengecualikan folder docs di barisnya sendiri.

Pelajari lebih dalam

Bahasan mendalam terkait di situs ini