Menulis Dockerfile dan Entrypoint Shell yang Ringkas
Buat skrip build multi-tahap dan shim entrypoint yang tangguh dengan penanganan sinyal serta templating konfigurasi.
Menulis Dockerfile dan Entrypoint Shell yang Ringkas adalah pelajaran DevOps Bootcamp gratis di CoddyKit. Ini adalah pelajaran 1 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar DevOps Bootcamp, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus DevOps Bootcamp mencakup 4 pelajaran total.
Mengapa Dockerfile Ringkas Penting bagi DevOps
Di lingkungan produksi, setiap megabita dalam image Docker memiliki biaya: pengambilan image menjadi lebih lambat, permukaan serangan lebih luas, dan penyimpanan registry terbuang. Dockerfile yang ringkas, dipadukan dengan entrypoint shell yang tangguh, merupakan ciri praktik DevOps yang matang.
- Build multi-tahap memisahkan alat saat build dari image runtime akhir sehingga ukurannya berkurang secara drastis.
- Shim entrypoint adalah skrip shell kecil yang menyiapkan container: membuat template berkas konfigurasi, memvalidasi variabel lingkungan, menangani sinyal, lalu menjalankan proses utama dengan exec.
- Keduanya menjadi tulang punggung beban kerja container yang andal dan portabel di Kubernetes, ECS, serta lingkungan bare-metal.
Pelajaran ini membahas kedua disiplin tersebut secara menyeluruh, dengan pola siap produksi yang dapat langsung Anda terapkan dalam pipeline Anda.
Anatomi Dockerfile Multi-Tahap
Dockerfile multi-tahap menggunakan beberapa instruksi FROM. Setiap tahap merupakan kumpulan lapisan yang terisolasi; Anda hanya menyalin artefak yang diperlukan ke tahap berikutnya.
- Tahap 0 (builder): memasang kompiler, pelaksana pengujian, dan dependensi build.
- Tahap 1 (runtime): dimulai dari basis minimal (misalnya
alpine,distroless) dan hanya menyalin biner terkompilasi atau bundel aplikasi. - Image akhir tidak pernah berisi
gcc,make, atau kode sumber kecuali Anda menyalinnya secara eksplisit.
Gunakan --from=<stage> dalam COPY untuk mengambil berkas lintas batas tahap. Beri nama tahap dengan AS <name> agar mudah dibaca dan dapat dipilih secara selektif menggunakan docker build --target.
# ---- Stage 0: builder ----
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags='-s -w' -o /app/server ./cmd/server
# ---- Stage 1: runtime ----
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]Meminimalkan Lapisan dan Membatalkan Cache
Setiap instruksi RUN, COPY, dan ADD membuat lapisan baru. Urutan instruksi yang buruk akan membatalkan cache build secara tidak perlu sehingga pipeline CI menjadi lambat.
- Salin manifes dependensi (
package.json,go.mod,requirements.txt) sebelum menyalin kode sumber agar pemasangan dependensi di-cache secara terpisah. - Gabungkan perintah yang berkaitan dengan
&&dan lakukan pembersihan pada lapisanRUNyang sama agar cache paket tidak tertinggal di lapisan perantara. - Gunakan
--no-cachepada pengelola paket dan hapus berkas daftar setelah pemasangan.
FROM python:3.12-slim AS builder
WORKDIR /app
# 1. Install deps first (cached until requirements change)
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# 2. Copy source (cache busted only when code changes)
COPY src/ ./src/
FROM python:3.12-slim
COPY --from=builder /install /usr/local
COPY --from=builder /app/src /app/src
WORKDIR /app
CMD ["python", "-m", "src.main"]Rahasia BuildKit dan Penerusan SSH
Registry privat, kunci SSH, dan token API tidak boleh muncul di lapisan image. Docker BuildKit menyediakan dua mekanisme yang aman:
--secret: memasang berkas rahasia di dalam satu langkahRUNtanpa memasukkannya ke dalam lapisan. Akses melalui/run/secrets/<id>.--ssh: meneruskan soket agen SSH dari host ke dalam proses build sehinggagit clonedapat melakukan autentikasi tanpa menyematkan kunci privat.
Aktifkan BuildKit dengan DOCKER_BUILDKIT=1 atau melalui docker buildx build. Direktif # syntax=docker/dockerfile:1 membuka fitur-fitur ini.
# syntax=docker/dockerfile:1
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
# Mount NPM token as a secret — never stored in the image
RUN --mount=type=secret,id=npm_token \
NPM_TOKEN=$(cat /run/secrets/npm_token) \
npm ci --prefer-offline
# Usage at build time:
# DOCKER_BUILDKIT=1 docker build \
# --secret id=npm_token,src=~/.npmrc-token \
# -t myapp:latest .Menulis Shim Entrypoint yang Tangguh
Shim entrypoint adalah skrip shell yang ditetapkan sebagai ENTRYPOINT dalam Dockerfile. Tugasnya adalah menyiapkan lingkungan runtime sebelum menyerahkan kendali kepada proses utama.
Shim yang terstruktur dengan baik mengikuti urutan berikut:
- Langkah 1: Tetapkan
set -euo pipefailagar setiap kegagalan segera menghentikan proses. - Langkah 2: Validasi variabel lingkungan yang wajib diisi dan hentikan proses dengan cepat disertai pesan yang membantu.
- Langkah 3: Buat template berkas konfigurasi dari variabel lingkungan.
- Langkah 4: Daftarkan penangan sinyal untuk penghentian yang tertib.
- Langkah 5:
exec "$@"— ganti shell dengan proses utama agar PID 1 adalah aplikasi, bukan shim.
exec terakhir sangat penting: tanpa itu, sinyal yang dikirim oleh Kubernetes atau runtime Docker tidak akan diteruskan ke proses anak.
#!/usr/bin/env bash
set -euo pipefail
# Step 2: validate required env vars
REQUIRED_VARS=(DATABASE_URL APP_SECRET PORT)
for var in "${REQUIRED_VARS[@]}"; do
if [[ -z "${!var:-}" ]]; then
echo "[entrypoint] ERROR: required env var '$var' is not set" >&2
exit 1
fi
done
# Step 3: template config (see next scene)
# Step 4: signal handling (see scene after that)
# Step 5: hand off to CMD
exec "$@"Membuat Template Konfigurasi dengan envsubst
envsubst (dari paket GNU gettext, tersedia di Alpine sebagai gettext) mengganti placeholder ${VAR} dalam berkas template dengan nilai variabel lingkungan saat ini.
- Sertakan template konfigurasi
*.tmpldalam image; entrypoint akan merendernya saat startup. - Berikan daftar variabel secara eksplisit kepada
envsubstagar tanda dolar yang tidak berkaitan (misalnya dalam regex nginx) tidak ikut diperluas. - Tulis berkas hasil render ke jalur yang dapat ditulisi seperti
/tmpatau volume konfigurasi khusus.
#!/usr/bin/env bash
# Template: /etc/nginx/conf.d/app.conf.tmpl contains:
# server { listen ${NGINX_PORT}; server_name ${SERVER_NAME}; ... }
export NGINX_PORT=${NGINX_PORT:-8080}
export SERVER_NAME=${SERVER_NAME:-localhost}
envsubst '${NGINX_PORT} ${SERVER_NAME}' \
< /etc/nginx/conf.d/app.conf.tmpl \
> /etc/nginx/conf.d/app.conf
echo "[entrypoint] nginx config rendered:"
grep -E 'listen|server_name' /etc/nginx/conf.d/app.conf
exec "$@"Menangani Sinyal dan Penghentian yang Tertib
Container menerima SIGTERM saat dihentikan oleh Kubernetes, ECS, atau docker stop. Jika shim entrypoint Anda adalah PID 1 dan tidak meneruskan sinyal, proses utama akan dihentikan dengan SIGKILL setelah masa tenggang — sehingga permintaan dapat terputus atau data rusak.
- Gunakan
trapuntuk menangkapSIGTERMdanSIGINTdalam shim. - Teruskan sinyal ke PID anak menggunakan
kill -TERM "$child". - Gunakan
wait "$child"untuk menunggu hingga proses anak keluar, lalu teruskan kode keluarnya. - Atau, gunakan
execuntuk mengganti shell sepenuhnya — dengan begitu, OS mengirimkan sinyal langsung ke proses anak dan trap tidak diperlukan. Ini adalah pola yang disarankan untuk kasus sederhana.
#!/usr/bin/env bash
set -euo pipefail
# Start main process in background
"$@" &
child=$!
# Forward SIGTERM and SIGINT to the child
trap 'echo "[entrypoint] caught SIGTERM, forwarding..."; kill -TERM "$child"' TERM
trap 'echo "[entrypoint] caught SIGINT, forwarding..."; kill -INT "$child"' INT
# Wait for child to exit and capture its exit code
wait "$child"
exit $?Menggunakan tini sebagai Proses Init Minimal
Saat container Anda membuat proses anak (misalnya shell yang menggandakan proses pekerja), Anda memerlukan init sungguhan untuk membersihkan proses zombie. tini adalah biner init kecil yang dirancang khusus untuk container.
- Tambahkan
tinike image Anda dan tetapkan sebagai pembungkus entrypoint. - Proses ini membersihkan anak zombie, meneruskan sinyal dengan benar, dan keluar dengan kode status anak.
- Docker menyediakan tini bawaan yang diaktifkan dengan
docker run --init, tetapi menyematkannya dalam image memastikan perilakunya konsisten di berbagai runtime (Kubernetes, ECS, dan sebagainya).
FROM node:20-alpine
# Install tini for proper signal handling and zombie reaping
RUN apk add --no-cache tini
WORKDIR /app
COPY --chown=node:node . .
RUN npm ci --omit=dev
USER node
# tini wraps CMD; forwards SIGTERM and reaps zombies
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "server.js"]Menjalankan Proses sebagai Pengguna Non-Root
Container yang berjalan sebagai root (UID 0) merupakan risiko keamanan besar: pelolosan dari container memberikan akses penuh ke host. Selalu beralih ke pengguna tanpa hak istimewa sebelum menjalankan proses utama.
- Buat pengguna dan grup sistem khusus dalam Dockerfile dengan
addgroup/adduser(Alpine) ataugroupadd/useradd(Debian). - Ubah kepemilikan berkas aplikasi dengan
COPY --chown=appuser:appgroup— lebih efisien daripada lapisanRUN chownterpisah. - Beralih ke pengguna tersebut dengan instruksi
USER. Entrypoint dan CMD mewarisi pengguna ini. - Kubernetes
securityContext.runAsNonRoot: trueakan menolak memulai image yang masih berjalan sebagai root.
FROM python:3.12-slim
# Create non-root user
RUN groupadd --gid 1001 appgroup && \
useradd --uid 1001 --gid appgroup --shell /bin/bash --create-home appuser
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy app files with correct ownership in a single layer
COPY --chown=appuser:appgroup src/ ./src/
COPY --chown=appuser:appgroup entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
USER appuser
ENTRYPOINT ["/entrypoint.sh"]
CMD ["python", "-m", "src.main"]Pemeriksaan Kesehatan dan Probe Kesiapan dalam Image
Probe liveness dan readiness Kubernetes ditentukan dalam manifes, tetapi Anda juga dapat menyertakan HEALTHCHECK dalam Dockerfile untuk lingkungan mandiri docker run dan Docker Compose.
HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 CMD ...- Gunakan
curl --failatauwget -qO-untuk layanan HTTP; untuk daemon non-HTTP, periksa soket dengan/dev/tcp/localhost/PORT. - Pasang hanya yang diperlukan: dalam image distroless, hindari menambahkan
curlhanya untuk pemeriksaan kesehatan — gunakan biner probe khusus atau biner kesehatan milik aplikasi.
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY dist/ /usr/share/nginx/html/
# Lightweight health check using bash TCP pseudo-device
# (no curl needed — works on any image with bash)
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 \
CMD bash -c 'exec 3<>/dev/tcp/localhost/80 && echo -e "GET /health HTTP/1.0\r\n" >&3 && cat <&3 | grep -q "200 OK"' || exit 1
EXPOSE 80Menyatukan Semuanya: Entrypoint Produksi
Berikut adalah shim entrypoint lengkap siap produksi yang menggabungkan semua pola dari pelajaran ini: validasi lingkungan, pembuatan template konfigurasi, penerusan sinyal, dan serah-terima dengan exec. Pola ini digunakan dalam layanan mikro Node.js, Python, dan Go di dunia nyata yang diterapkan pada Kubernetes.
- Setiap bagian diberi komentar sehingga berfungsi sebagai template yang mendokumentasikan dirinya sendiri.
- Shim ini dijaga agar kurang dari 50 baris — entrypoint harus sederhana dan mudah diaudit.
- Perhatikan
exec "$@"terakhir: setelah semua persiapan selesai, shell digantikan oleh proses aplikasi sehingga menjadi PID 1 dan menerima semua sinyal OS secara langsung.
#!/usr/bin/env bash
# entrypoint.sh — production-grade container entrypoint shim
set -euo pipefail
# ── 1. Validate required environment variables ──────────────────
REQUIRED=(DATABASE_URL APP_SECRET PORT LOG_LEVEL)
for var in "${REQUIRED[@]}"; do
[[ -n "${!var:-}" ]] || { echo "[entrypoint] FATAL: $var is not set" >&2; exit 1; }
done
# ── 2. Set safe defaults for optional variables ─────────────────
export HOST=${HOST:-0.0.0.0}
export WORKERS=${WORKERS:-2}
# ── 3. Render config template ───────────────────────────────────
if [[ -f /etc/app/app.conf.tmpl ]]; then
envsubst '${DATABASE_URL} ${PORT} ${LOG_LEVEL} ${HOST}' \
< /etc/app/app.conf.tmpl \
> /etc/app/app.conf
echo "[entrypoint] config rendered at /etc/app/app.conf"
fi
# ── 4. Wait for dependent services (optional, fast) ─────────────
if [[ -n "${WAIT_FOR_HOST:-}" ]]; then
echo "[entrypoint] waiting for ${WAIT_FOR_HOST}:${WAIT_FOR_PORT:-5432}..."
until bash -c "exec 3<>/dev/tcp/${WAIT_FOR_HOST}/${WAIT_FOR_PORT:-5432}" 2>/dev/null; do
sleep 1
done
echo "[entrypoint] dependency ready"
fi
# ── 5. Hand off to CMD (PID 1 becomes the application) ──────────
echo "[entrypoint] starting: $*"
exec "$@"Uji Pemahaman: Penanganan Sinyal dalam Entrypoint
Uji pemahaman Anda tentang penanganan sinyal dalam skrip entrypoint container.
Ringkasan: Dockerfile Ringkas dan Entrypoint Shell
Anda telah membahas seluruh tumpukan penulisan container produksi:
- Build multi-tahap menggunakan beberapa instruksi
FROMuntuk menjaga kompiler dan alat build tetap berada di luar image akhir, sehingga menghasilkan lapisan runtime yang ringkas dan minimal. - Pengurutan lapisan — salin manifes dependensi sebelum kode sumber — memaksimalkan cache hit dan mempercepat pipeline CI.
- Rahasia BuildKit dan pemasangan SSH menjaga kredensial tetap berada di luar riwayat image tanpa mengganggu build terautentikasi.
- Shim entrypoint memvalidasi variabel lingkungan, membuat template berkas konfigurasi dengan
envsubst, dan menyerahkan kendali kepada aplikasi melaluiexec "$@". - Penanganan sinyal memerlukan
exec(agar aplikasi menjadi PID 1) atau pola eksplisittrap+kill+waitsaat pekerjaan latar belakang digunakan. - tini menambahkan pembersihan zombie dan penerusan sinyal yang benar saat container membuat beberapa proses.
- Pengguna non-root dan instruksi
HEALTHCHECKmelengkapi image yang aman dan mudah dipantau, siap untuk beban kerja produksi Kubernetes.
Gabungkan pola-pola ini secara konsisten agar image Anda lebih kecil, lebih cepat diterapkan, dan jauh lebih tangguh dalam kondisi produksi.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Menulis Dockerfile dan Entrypoint Shell yang Ringkas” gratis?
Ya — teks lengkap “Menulis Dockerfile dan Entrypoint Shell yang Ringkas” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus DevOps Bootcamp, upgrade ke CoddyKit PRO. Kursus DevOps Bootcamp mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Menulis Dockerfile dan Entrypoint Shell yang Ringkas”?
Buat skrip build multi-tahap dan shim entrypoint yang tangguh dengan penanganan sinyal serta templating konfigurasi. Kamu berlatih DevOps Bootcamp dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai DevOps Bootcamp?
Tidak diperlukan pengalaman sebelumnya. DevOps Bootcamp di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 1 dari 4.
Berapa lama pelajaran “Menulis Dockerfile dan Entrypoint Shell yang Ringkas” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran DevOps Bootcamp ini?
Ya. Setiap pelajaran DevOps Bootcamp menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Menulis Dockerfile dan Entrypoint Shell yang Ringkas
- Membuat Template Konfigurasi dengan envsubst dan heredoc
- Membuat Skrip Resource Cloud dengan CLI dan jq
- Probe Kesehatan, Gerbang Kesiapan, dan Loop Tunggu