Dockerfile.1
mengeksekusi banyak RUN
:
FROM busybox
RUN echo This is the A > a
RUN echo This is the B > b
RUN echo This is the C > c
Dockerfile.2
bergabung dengan mereka:
FROM busybox
RUN echo This is the A > a &&\
echo This is the B > b &&\
echo This is the C > c
Masing-masing RUN
membuat layer, jadi saya selalu berasumsi bahwa lebih sedikit layer yang lebih baik dan karenanya Dockerfile.2
lebih baik.
Ini jelas benar ketika sebuah RUN
menghapus sesuatu yang ditambahkan oleh sebelumnya RUN
(yaitu yum install nano && yum clean all
), tetapi dalam kasus di mana setiap RUN
menambahkan sesuatu, ada beberapa poin yang perlu kita pertimbangkan:
Layers seharusnya hanya menambahkan diff di atas yang sebelumnya, jadi jika lapisan kemudian tidak menghapus sesuatu yang ditambahkan sebelumnya, seharusnya tidak ada banyak ruang menghemat ruang antara kedua metode ...
Lapisan ditarik secara paralel dari Docker Hub, jadi
Dockerfile.1
, meskipun mungkin sedikit lebih besar, secara teoritis akan diunduh lebih cepat.Jika menambahkan kalimat ke-4 (yaitu
echo This is the D > d
) dan pembangunan kembali secara lokal,Dockerfile.1
akan membangun lebih cepat berkat cache, tetapiDockerfile.2
harus menjalankan semua 4 perintah lagi.
Jadi, pertanyaannya: Mana cara yang lebih baik untuk melakukan Dockerfile?
sumber
Jawaban:
Jika memungkinkan, saya selalu menggabungkan perintah yang membuat file dengan perintah yang menghapus file yang sama menjadi satu
RUN
baris. Ini karena setiapRUN
baris menambahkan lapisan pada gambar, hasilnya secara harfiah perubahan sistem file yang dapat Anda lihatdocker diff
pada wadah sementara yang dibuatnya. Jika Anda menghapus file yang dibuat di lapisan yang berbeda, yang dilakukan oleh sistem file gabungan adalah mendaftarkan perubahan sistem file di lapisan baru, file tersebut masih ada di lapisan sebelumnya dan dikirimkan melalui jaringan dan disimpan dalam disk. Jadi jika Anda mengunduh kode sumber, mengekstraknya, mengompilasinya menjadi biner, dan kemudian menghapus file tgz dan sumber pada akhirnya, Anda benar-benar ingin semua ini dilakukan dalam satu lapisan untuk mengurangi ukuran gambar.Selanjutnya, saya pribadi membagi lapisan berdasarkan potensi mereka untuk digunakan kembali dalam gambar lain dan penggunaan caching yang diharapkan. Jika saya memiliki 4 gambar, semua dengan gambar dasar yang sama (misalnya debian), saya dapat menarik kumpulan utilitas umum ke sebagian besar gambar tersebut ke dalam perintah jalankan pertama sehingga gambar lain mendapat manfaat dari caching.
Urutan di Dockerfile penting ketika melihat penggunaan kembali cache gambar. Saya melihat setiap komponen yang akan memperbarui sangat jarang, mungkin hanya ketika gambar dasar diperbarui dan menempatkan mereka di Dockerfile. Menjelang akhir Dockerfile, saya menyertakan perintah yang akan berjalan cepat dan dapat sering berubah, misalnya menambahkan pengguna dengan host spesifik UID atau membuat folder dan mengubah izin. Jika wadah menyertakan kode yang ditafsirkan (misalnya JavaScript) yang sedang dikembangkan secara aktif, yang akan ditambahkan selambat mungkin sehingga pembangunan kembali hanya menjalankan perubahan tunggal itu.
Di masing-masing kelompok perubahan ini, saya melakukan konsolidasi sebaik mungkin untuk meminimalkan lapisan. Jadi jika ada 4 folder kode sumber yang berbeda, mereka ditempatkan di dalam satu folder sehingga dapat ditambahkan dengan satu perintah. Setiap paket yang diinstal dari sesuatu seperti apt-get digabung ke dalam RUN tunggal jika mungkin untuk meminimalkan jumlah overhead paket manajer (memperbarui dan membersihkan).
Pembaruan untuk bangunan multi-tahap:
Saya kurang khawatir tentang mengurangi ukuran gambar pada tahap non-final dari bangunan multi-tahap. Ketika tahap-tahap ini tidak ditandai dan dikirim ke node lain, Anda dapat memaksimalkan kemungkinan penggunaan kembali cache dengan memisahkan setiap perintah ke
RUN
baris yang terpisah .Namun, ini bukan solusi sempurna untuk menekan lapisan karena semua yang Anda salin di antara tahapan adalah file, dan bukan seluruh meta-data gambar seperti pengaturan variabel lingkungan, titik masuk, dan perintah. Dan ketika Anda menginstal paket dalam distribusi linux, perpustakaan dan dependensi lainnya dapat tersebar di seluruh sistem file, membuat salinan semua dependensi menjadi sulit.
Karena itu, saya menggunakan multi-stage builds sebagai pengganti untuk membangun binari pada server CI / CD, sehingga server CI / CD saya hanya perlu menjalankan tooling
docker build
, dan tidak memiliki jdk, nodejs, go, dan alat kompilasi lain yang diinstal.sumber
Jawaban resmi tercantum dalam praktik terbaik mereka (gambar resmi HARUS mematuhi ini)
Sejak docker 1.10
COPY
,ADD
danRUN
pernyataan menambahkan layer baru ke gambar Anda. Berhati-hatilah saat menggunakan pernyataan ini. Cobalah untuk menggabungkan perintah menjadi satuRUN
pernyataan. Pisahkan ini hanya jika diperlukan agar mudah dibaca.Info lebih lanjut: https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#/minimize-the-number-of-layers
Pembaruan: Multi stage di buruh pelabuhan> 17.05
Dengan pembuatan multi-tahap Anda dapat menggunakan beberapa
FROM
pernyataan di Dockerfile Anda. SetiapFROM
pernyataan adalah sebuah panggung dan dapat memiliki gambar dasarnya sendiri. Pada tahap akhir Anda menggunakan gambar dasar minimal seperti alpine, salin artefak bangunan dari tahap sebelumnya dan instal persyaratan runtime. Hasil akhir dari tahap ini adalah gambar Anda. Jadi di sinilah Anda khawatir tentang lapisan seperti yang dijelaskan sebelumnya.Seperti biasa, buruh pelabuhan memiliki dokumen hebat tentang pembuatan multi-tahap. Berikut kutipan singkatnya:
Posting blog yang hebat tentang ini dapat ditemukan di sini: https://blog.alexellis.io/mutli-stage-docker-builds/
Untuk menjawab poin Anda:
Ya, lapisan adalah semacam diff. Saya tidak berpikir ada lapisan yang ditambahkan jika tidak ada perubahan sama sekali. Masalahnya adalah bahwa sekali Anda menginstal / mengunduh sesuatu di lapisan # 2, Anda tidak dapat menghapusnya di lapisan # 3. Jadi begitu sesuatu dituliskan dalam layer, ukuran gambar tidak dapat dikurangi lagi dengan menghapusnya.
Meskipun lapisan dapat ditarik secara paralel, sehingga berpotensi lebih cepat, setiap lapisan tidak diragukan lagi meningkatkan ukuran gambar, bahkan jika mereka menghapus file.
Ya, caching berguna jika Anda memperbarui file buruh pelabuhan Anda. Tapi itu bekerja dalam satu arah. Jika Anda memiliki 10 layer, dan Anda mengubah layer # 6, Anda masih harus membangun kembali semuanya dari layer # 6- # 10. Jadi tidak terlalu sering mempercepat proses build, tetapi dijamin tidak perlu meningkatkan ukuran gambar Anda.
Terima kasih kepada @Mohan yang telah mengingatkan saya untuk memperbarui jawaban ini.
sumber
Sepertinya jawaban di atas sudah ketinggalan zaman. Catatan dokumen:
https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/#minimize-the-number-of-layers
dan
https://docs.docker.com/engine/userguide/eng-image/multistage-build/
Praktik terbaik tampaknya telah berubah menggunakan multistage build dan menjaga agar tetap
Dockerfile
dapat dibaca.sumber
docker image build --squash
opsi berjalan di luar eksperimental.squash
mendapatkan hasil eksperimen. Ini memiliki banyak trik dan hanya masuk akal sebelum multi-stage dibangun. Dengan multi stage build, Anda hanya perlu mengoptimalkan tahap akhir yang sangat mudah.Itu tergantung pada apa yang Anda masukkan dalam lapisan gambar Anda.
Poin kuncinya adalah membagikan sebanyak mungkin layer:
Contoh Buruk:
Dockerfile.1
Dockerfile.2
Contoh yang baik:
Dockerfile.1
Dockerfile.2
Saran lain adalah menghapus tidak begitu berguna hanya jika itu terjadi pada lapisan yang sama dengan tindakan menambahkan / menginstal.
sumber
RUN yum install big-package
dari cache?