Skip to content

Membuat Docker Image sendiri

Sampai sini kamu menjalankan image yang dibuat orang lain. Pada praktiknya di pekerjaan, kamu sering membuat image sendiri supaya app tim bisa dijalankan dengan satu command.

Instruksi untuk membuat image ditulis di file bernama Dockerfile (tanpa ekstensi), biasanya di root project. Root project adalah folder utama yang berisi source code dan file konfigurasi project.

Instruksi yang perlu kamu tahu

Buat folder latihan agar semua file contoh tersimpan di satu tempat:

sh
mkdir docker-essentials-demo
cd docker-essentials-demo

index.html:

html
<!DOCTYPE html>
<html lang="id">
  <body>
    <h1>Hello dari container</h1>
  </body>
</html>

Dockerfile:

dockerfile
FROM nginx:1.27-alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80

Arti baris per baris:

InstruksiFungsi
FROMMenentukan base image. Semua Dockerfile mulai dari sini.
COPYMenyalin file dari laptop ke dalam image.
EXPOSEMendokumentasikan port yang dipakai app. Tidak otomatis publish ke laptop. Port tetap perlu di-map dengan -p saat run.

Untuk app yang benar-benar kamu tulis, Dockerfile pada praktiknya biasanya lebih lengkap:

dockerfile
FROM node:22-alpine
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
InstruksiFungsi
WORKDIRFolder kerja di dalam image. Command COPY dan RUN berikutnya akan mengarah relatif ke folder ini.
RUNMenjalankan perintah saat build (install dependency, compile). Hasilnya menjadi layer image.
CMDPerintah default saat container start.

Perbedaan RUN dan CMD

RUN dan CMD sama-sama terlihat seperti command di dalam Dockerfile, tetapi waktunya berbeda:

  • RUN dijalankan saat image di-build. Contohnya RUN npm install dan RUN npm ci. Hasilnya disimpan di dalam layer image.
  • CMD dijalankan saat container di-start. Contohnya CMD ["npm", "start"]. Perintah ini menjadi default command untuk container.

Pada contoh tersebut, npm ci dijalankan saat build. npm start baru dijalankan setiap kali container di-start.

Catatan tentang CMD

  • Jika Dockerfile tidak memiliki CMD, Docker akan menggunakan CMD dari base image, jika base image tersebut memilikinya.
  • Jika Dockerfile tidak memiliki CMD dan base image juga tidak memiliki default command, container tidak dapat dijalankan karena Docker tidak tahu command yang harus dijalankan.

CMD vs ENTRYPOINT

ENTRYPOINT berguna untuk image yang berfungsi sebagai CLI. ENTRYPOINT adalah program utama yang dijalankan, sedangkan CMD dapat menjadi argumen default untuk program tersebut.

Contoh Dockerfile dengan ENTRYPOINT:

dockerfile
FROM alpine:3.20
ENTRYPOINT ["echo"]
CMD ["Hello dari container"]

Untuk membuktikan perbedaan ENTRYPOINT dan CMD, build image lalu jalankan beberapa container:

sh
docker build -t echo-demo:1.0 .
docker run --rm echo-demo:1.0
docker run --rm echo-demo:1.0 "Pesan lain"

Output command pertama adalah Hello dari container. Pada command kedua, argumen dari CMD diganti sehingga output-nya menjadi Pesan lain.

ENTRYPOINT dan CMD bisa ditulis dalam bentuk exec seperti ["program", "argument"]. Bentuk ini disarankan karena command dan argument dipisahkan dengan jelas.

.dockerignore

File ini mirip .gitignore. Isinya daftar file yang tidak perlu dikirim ke Docker Engine sebagai build context.

gitignore
.git
node_modules
*.md
.env

Tanpa file ini, COPY . . bisa menyertakan node_modules dari host, file rahasia, atau folder Git. Build menjadi lebih lambat dan image lebih besar.

Layer dan cache

Image disusun dari beberapa layer yang menumpuk. Setiap instruksi tertentu di Dockerfile biasanya menambah satu layer.

Bayangkan image seperti sandwich. Layer paling bawah adalah base image, lalu layer lain ditambahkan di atasnya. Jika beberapa image memakai base image yang sama, Docker Engine bisa memakai layer yang sudah ada tanpa men-download ulang.

Cache membuat proses build ulang menjadi lebih cepat. Jika sebuah layer tidak berubah, Docker Engine bisa menggunakan layer tersebut dari cache dan tidak memprosesnya dari awal. Build hanya mengulang layer yang berubah dan layer setelahnya.

Urutan instruksi di Dockerfile memengaruhi seberapa banyak cache yang bisa digunakan. Letakkan langkah yang jarang berubah, seperti install dependency, di bagian atas. Letakkan source code yang sering berubah di bagian bawah.

Pada contoh Node di atas, package.json di-copy lebih dulu, lalu npm ci, baru source code di-copy. Jika kamu hanya mengubah index.js, layer npm ci bisa memakai dari cache.

Jika COPY . . diletakkan sebelum npm ci, setiap perubahan pada source code akan memaksa proses install berjalan ulang. Build menjadi lebih lambat.

Build dan jalankan

Dari folder yang berisi Dockerfile, gunakan format command berikut untuk membuat image:

sh
docker build -t <nama-image>:<tag> <build-context>

Contoh:

sh
docker build -t hello-web:1.0 .
  • -t hello-web:1.0 = nama dan tag image hasil build.
  • . = build context, yaitu folder yang dikirim ke Docker Engine untuk dipakai oleh COPY.

Untuk menjalankan container dari image yang baru dibuat, gunakan format berikut:

sh
docker run -d --name <nama-container> -p <port-local>:<port-di-container> <nama-image>:<tag>

Contoh:

sh
docker run -d --name hello -p 8080:80 hello-web:1.0

Buka http://localhost:8080 di browser.

Push image ke Docker Hub

Image yang ada di laptop bisa dibagikan melalui registry. Pada contoh ini, kamu akan menggunakan Docker Hub.

Sebelum push, buat account Docker Hub dan buat satu repository baru, misalnya hello-web. Gunakan Docker Hub username kamu sebagai bagian dari nama image. Repository adalah tempat di Docker Hub untuk menyimpan image dengan nama dan tag tertentu.

Login ke Docker Hub melalui Docker CLI agar Docker Engine memiliki izin untuk mengirim image:

Format command:

sh
docker login -u <dockerhub-username>

Contoh:

sh
docker login -u bagus

Docker akan meminta password. Untuk account Docker Hub yang menggunakan two-factor authentication, gunakan personal access token sebagai password. Jangan menulis password atau token langsung di command dan jangan commit credential ke Git.

Setelah login, tambahkan nama Docker Hub ke image lokal. Docker Hub membutuhkan format <username>/<repository>:<tag> agar image dikirim ke repository yang benar:

Format command:

sh
docker tag <image-lokal>:<tag-lokal> <dockerhub-username>/<repository>:<tag>

Contoh:

sh
docker tag hello-web:1.0 bagus/hello-web:1.0

Push image ke Docker Hub:

Format command:

sh
docker push <dockerhub-username>/<repository>:<tag>

Contoh:

sh
docker push bagus/hello-web:1.0

Setelah push selesai, image bisa diambil dari mesin lain menggunakan command berikut:

sh
docker pull <dockerhub-username>/<repository>:<tag>

Contoh:

sh
docker pull bagus/hello-web:1.0

Ganti bagus dengan username Docker Hub kamu. Nama repository dan tag pada command harus sama dengan image yang kamu push.