Tidak suka iklan? Pergi Bebas Iklan Hari ini

Pembangunan Multistage Docker Kurangi Ukuran Gambar Tanpa Mengganggu Deploy

Diperbarui pada

Panduan praktis tentang pembangunan Docker berbagai tahap — ukuran gambar sebelum dan sesudah yang nyata, contoh Dockerfile untuk Node.js dan Python, serta kesalahan yang sering terjadi pada setiap tim setidaknya sekali.

Pembangunan Multistage Docker: Kurangi Ukuran Gambar Tanpa Mengganggu Deploy 1
IKLAN · HAPUS?

Gambar Node.js Anda berukuran 1,1 GB. Anda telah menambahkan .dockerignore, memangkas ketergantungan pengembangan, mencoba node:slim — tidak berubah banyak. Solusi sebenarnya adalah pembangunan multistage. Jika Anda belum beralih, Anda mengirimkan kompilator TypeScript ke produksi.

Pembangunan multistage telah ada dalam Docker sejak versi 17.05 (2017). Mereka hanya kurang digunakan. Berikut penjelasan nyata: apa yang berubah, seberapa besar perbedaannya, dan tiga kegagalan yang menyerang tim saat pertama kali melakukan migrasi.

Masalah satu tahap

Sebagian besar Dockerfile dimulai seperti ini:

FROM node:20

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

EXPOSE 3000
CMD ["node", "dist/server.js"]

Bangun dan periksa dengan docker images: ~1,1 GB. Anda mengirimkan gambar Node 20 penuh dengan npm, rantai alat TypeScript Anda, semua ketergantungan pengembangan, dan pohon sumber kode Anda. Tidak satupun dari itu berjalan di produksi — aplikasi hanya membutuhkan output yang dikompilasi dan sejumlah kecil paket runtime. dist/ .

Pembangunan multistage: solusi

Setiap FROM instruksi memulai tahap baru dengan sistem file yang bersih. Beri nama tahap dengan AS, lalu gunakan COPY --from=stagename untuk mengambil file tertentu ke tahap berikutnya. Tahap-tahap antara tidak masuk ke gambar akhir — mereka adalah hasil pembangunan, yang dibuang setelah COPY selesai.

Berikut aplikasi yang sama dalam pembangunan multistage yang benar:

# ---- Build stage ----
FROM node:20 AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build


# ---- Runtime stage ----
FROM node:20-alpine AS runtime

WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist

EXPOSE 3000
CMD ["node", "dist/server.js"]

Baris kritis: COPY --from=builder /app/dist ./dist. Ini mengambil hanya output yang dikompilasi dari tahap pembangun ke gambar runtime berbasis Alpine. Kompilator TypeScript, file sumber, dan ketergantungan pengembangan tidak pernah menyentuh lapisan akhir.

Hasil: ~160 MB sebagai ganti 1,1 GB. Itu merupakan penurunan sekitar 85% untuk aplikasi Node biasa — dan itu adalah hasil bangunan yang sama, hanya tanpa struktur pendukungnya.

Menambahkan tahap uji

Anda dapat menambahkan tahap uji antara pembangunan dan runtime yang menjalankan suite uji Anda. Jika uji gagal, pembangunan berhenti sebelum gambar runtime dibuat. Jika uji berhasil, Anda melewatkan tahap uji saat membangun untuk produksi.

FROM node:20 AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build


FROM builder AS tester

RUN npm test


FROM node:20-alpine AS runtime

WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=builder /app/dist ./dist

EXPOSE 3000
CMD ["node", "dist/server.js"]

Di CI Anda menargetkan tahap tester secara eksplisit: docker build --target tester .. Untuk gambar produksi Anda membangun tanpa target dan Docker menjalankan semua tahap secara berurutan, berhenti pada tahap terakhir FROM. Tahap uji berjalan tetapi sistem file-nya dibuang — uji berfungsi sebagai pintu, bukan sebagai beban.

Python: ide yang sama, eksekusi yang sedikit berbeda

Pembangunan multistage untuk Python mengikuti pola yang sama. Perbedaan utama: instalasi paket pip terjadi di bawah /root/.local ketika Anda menggunakan --user, sehingga Anda menyalin direktori tersebut ke gambar runtime yang ringkas.

FROM python:3.12 AS builder

WORKDIR /app
COPY requirements.txt .
RUN pip install --user -r requirements.txt
COPY . .


FROM python:3.12-slim AS runtime

WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app .

ENV PATH=/root/.local/bin:$PATH

CMD ["python", "main.py"]

python:3.12 basis: ~1 GB. python:3.12-slim dengan hanya paket yang terinstal: ~180–250 MB tergantung pada isi requirements.txt. File yang dikompilasi datang secara gratis karena mereka berada di samping sumber. .pyc .

Tiga kegagalan yang menangkap setiap orang setidaknya sekali

1. Menyalin file yang salah

Kesalahan paling umum: Anda COPY --from=builder /app ./ alih-alih COPY --from=builder /app/dist ./dist. Anda hanya menyalin semuanya — file sumber, fixture uji, node_modules, dan lainnya — ke gambar runtime "minimal" Anda. Sekarang ukurannya lebih besar daripada versi satu tahap.

Jangan ambigu tentang apa yang Anda salin. Salin hanya direktori atau file yang benar-benar dibutuhkan oleh titik masuk produksi. Untuk aplikasi Node kebanyakan: output yang dikompilasi (dist/) dan opsional aset statis. Untuk Python: paket yang terinstal dan kode aplikasi, bukan requirements.txt, bukan uji, bukan notebook.

2. Rahasia pembangunan terbocor ke lapisan

Jika Anda mengirimkan rahasia sebagai argumen pembangunan (misalnya, ARG NPM_TOKEN diikuti dengan penggunaannya dalam perintah RUN ), rahasia itu terlihat di setiap lapisan yang mengikuti — bahkan dalam pembangunan multistage. docker history myimage akan menunjukkan hal itu.

Pendekatan yang benar adalah Docker BuildKit's --mount=type=secret:

RUN --mount=type=secret,id=npm_token     NPM_TOKEN=$(cat /run/secrets/npm_token) npm ci

Rahasia dimount saat runtime lapisan tersebut — tidak pernah disimpan dalam sejarah gambar. Bangun dengan: docker build --secret id=npm_token,src=.npmrc .

Solusi cepat yang digunakan orang — menghapus rahasia di lapisan yang sama RUN — tidak membantu dalam pembangunan multistage, tetapi pendekatan BuildKit lebih bersih terlepas dari itu.

3. Lupa .dockerignore

Pembangunan multistage memperkecil gambar akhir Anda, tetapi COPY . . di tahap pembangunan masih mengirimkan konteks penuh ke daemon Docker. Tanpa .dockerignore, itu termasuk .git/, node_modules/, fixture uji, file lokal .env , dan rahasia yang disimpan dalam file teks biasa. Tahap pembangunan melihat semuanya.

Minimum .dockerignore untuk proyek Node apa pun:

.git
node_modules
dist
.env
*.log
coverage
.nyc_output

Tambahkan .dockerignore hari Anda menambahkan Dockerfile. Ukuran konteks pembangunan muncul di baris pertama dari docker build output (Sending build context to Docker daemon X MB) — jika angka itu terlalu besar, periksa apa yang termasuk.

Alat yang berguna untuk pekerjaan Dockerfile

Jika Anda ingin titik awal sebelum menulis sendiri, Generator Dockerfile di IO Tools akan mengatur Dockerfile multistage untuk tumpukan umum. Setelah Anda memiliki sesuatu yang ditulis, jalankan melalui Pemformat & Pemeriksa Linter Dockerfile untuk menangkap kesalahan umum sebelum mereka mencapai CI — seperti kehilangan WORKDIR, menggunakan latest tag, atau menjalankan sebagai root secara tidak perlu.

Pesan akhir

Pembangunan multistage adalah perubahan dua langkah: tambahkan tahap pembangunan yang dinamakan, salin output yang dikompilasi ke gambar minimal baru. Penurunan ukuran hampir selalu layak — 80–90% adalah nilai biasa untuk aplikasi Node dan Python. Kegagalan utama adalah terlalu luas dengan COPY --from, mengungkapkan rahasia sebagai argumen pembangunan, dan melewatkan .dockerignore. Perbaiki hal ini dan Anda memiliki gambar produksi yang benar-benar disesuaikan untuk produksi.

Ingin bebas iklan? Bebas Iklan Hari Ini

Instal Ekstensi Kami

Tambahkan alat IO ke browser favorit Anda untuk akses instan dan pencarian lebih cepat

Ke Ekstensi Chrome Ke Ekstensi Tepi Ke Ekstensi Firefox Ke Ekstensi Opera

Papan Skor Telah Tiba!

Papan Skor adalah cara yang menyenangkan untuk melacak permainan Anda, semua data disimpan di browser Anda. Lebih banyak fitur akan segera hadir!

IKLAN · HAPUS?
IKLAN · HAPUS?
IKLAN · HAPUS?

Pojok Berita dengan Sorotan Teknologi

Terlibat

Bantu kami untuk terus menyediakan alat gratis yang berharga

Belikan aku kopi
IKLAN · HAPUS?