Memahami lapisan Docker

27

Kami memiliki blok berikut di Dockerfile:

RUN yum -y update
RUN yum -y install epel-release
RUN yum -y groupinstall "Development Tools"
RUN yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

Saya telah diberitahu bahwa kita harus menyatukan RUNperintah - perintah ini untuk mengurangi lapisan buruh pelabuhan yang dibuat:

RUN yum -y update \
    && yum -y install epel-release \
    && yum -y groupinstall "Development Tools" \
    && yum -y install python-pip git mysql-devel libxml2-devel libxslt-devel python-devel openldap-devel libffi-devel openssl-devel

Saya sangat baru untuk buruh pelabuhan dan tidak yakin saya sepenuhnya memahami perbedaan antara dua versi ini menentukan beberapa perintah RUN. Kapan seseorang akan menyatukan RUNperintah menjadi satu dan ketika masuk akal untuk memiliki beberapa RUNperintah?

alecxe
sumber

Jawaban:

35

Gambar buruh pelabuhan sebenarnya adalah daftar yang terhubung dari lapisan sistem file. Setiap instruksi dalam Dockerfile membuat lapisan filesystem yang menjelaskan perbedaan dalam filesystem sebelum dan sesudah eksekusi instruksi yang sesuai. docker inspectSub - perintah ini dapat digunakan pada gambar buruh pelabuhan untuk mengungkapkan sifatnya sebagai daftar tautan dari lapisan sistem file.

Jumlah lapisan yang digunakan dalam suatu gambar adalah penting

  • saat mendorong atau menarik gambar, karena hal itu mempengaruhi jumlah unggahan atau unduhan yang terjadi secara bersamaan.
  • ketika memulai sebuah wadah, karena lapisan-lapisannya digabungkan bersama untuk menghasilkan sistem file yang digunakan dalam wadah tersebut; semakin banyak layer yang terlibat, semakin buruk kinerjanya, tetapi backend filesystem yang berbeda dipengaruhi secara berbeda oleh ini.

Ini memiliki beberapa konsekuensi untuk bagaimana gambar harus dibangun. Saran pertama dan paling penting yang bisa saya berikan adalah:

Saran # 1 Pastikan langkah-langkah pembuatan di mana kode sumber Anda terlibat datang selambat mungkin di Dockerfile dan tidak terikat dengan perintah sebelumnya menggunakan a &&atau a ;.

Alasan untuk ini, adalah bahwa semua langkah sebelumnya akan di-cache dan lapisan yang sesuai tidak perlu diunduh berulang kali. Ini berarti versi yang lebih cepat dan rilis yang lebih cepat, yang mungkin Anda inginkan. Cukup menarik, ternyata sulit untuk memanfaatkan cache buruh pelabuhan secara optimal.

Saran kedua saya kurang penting tetapi saya merasa sangat berguna dari sudut pandang pemeliharaan:

Nasihat # 2 Jangan menulis perintah kompleks di Dockerfile tetapi gunakan skrip yang akan disalin dan dieksekusi.

Sebuah Dockerfile mengikuti saran ini akan terlihat seperti

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh
COPY install_pacakges.sh /root/
RUN sh -x /root/install_packages.sh

dan seterusnya. Saran mengikat beberapa perintah dengan &&hanya memiliki ruang lingkup terbatas. Jauh lebih mudah untuk menulis dengan skrip, di mana Anda dapat menggunakan fungsi, dll untuk menghindari redundansi atau untuk keperluan dokumentasi.

Orang-orang tertarik dengan pra-prosesor dan bersedia menghindari overhead kecil yang disebabkan oleh COPYlangkah - langkah dan benar-benar menghasilkan Dockerfile di mana

COPY apt_setup.sh /root/
RUN sh -x /root/apt_setup.sh

urutan digantikan oleh

RUN base64 --decode … | sh -x

di mana adalah versi base64-encoded dari apt_setup.sh.

Saran ketiga saya adalah untuk orang-orang yang ingin membatasi ukuran dan jumlah lapisan dengan biaya pembuatan yang lebih lama.

Saran # 3 Gunakan with-idiom untuk menghindari file yang ada di lapisan perantara tetapi tidak di sistem file yang dihasilkan.

File yang ditambahkan oleh beberapa instruksi buruh pelabuhan dan dihapus oleh beberapa instruksi kemudian tidak ada dalam sistem berkas yang dihasilkan tetapi disebutkan dua kali dalam lapisan buruh pelabuhan yang merupakan gambar buruh pelabuhan dalam konstruksi. Sekali, dengan nama dan konten lengkap di lapisan yang dihasilkan dari instruksi menambahkannya, dan sekali sebagai pemberitahuan penghapusan di lapisan yang dihasilkan dari instruksi menghapusnya.

Sebagai contoh, anggap kita untuk sementara membutuhkan kompiler C dan beberapa gambar dan pertimbangkan

# !!! THIS DISPLAYS SOME PROBLEM --- DO NOT USE !!!
RUN apt-get install -y gcc
RUN gcc --version
RUN apt-get --purge autoremove -y gcc

(Contoh yang lebih realistis akan membangun beberapa perangkat lunak dengan kompiler alih-alih hanya menyatakan kehadirannya dengan --versionbendera.)

Cuplikan Dockerfile membuat tiga lapisan, yang pertama berisi suite gcc lengkap sehingga meskipun tidak ada dalam sistem file akhir, data yang sesuai masih merupakan bagian dari gambar dengan cara yang sama dan perlu diunduh, diunggah dan dibongkar setiap kali gambar akhir adalah.

The with-idiom adalah bentuk umum dalam pemrograman fungsional dengan kepemilikan sumber daya isolat dan sumber daya melepaskan dari logika menggunakannya. Sangat mudah untuk memindahkan idiom ini menjadi shell-scripting, dan kita dapat menguraikan kembali perintah sebelumnya sebagai skrip berikut, untuk digunakan dengan COPY & RUNseperti pada Saran # 2.

# with_c_compiler SIMPLE-COMMAND
#  Execute SIMPLE-COMMAND in a sub-shell with gcc being available.

with_c_compiler()
(
    set -e
    apt-get install -y gcc
    "$@"
    trap 'apt-get --purge autoremove -y gcc' EXIT
)

with_c_compiler\
    gcc --version

Perintah kompleks dapat diubah menjadi fungsi sehingga dapat diumpankan ke with_c_compiler. Dimungkinkan juga untuk melakukan panggilan ke beberapa with_whateverfungsi, tetapi mungkin tidak terlalu diinginkan. (Menggunakan lebih banyak fitur esoteris dari shell, tentu saja memungkinkan untuk membuat with_c_compilerperintah kompleks, tetapi dalam semua aspek lebih baik untuk membungkus perintah kompleks ini ke dalam fungsi.)

Jika kita ingin mengabaikan Saran # 2, cuplikan Dockerfile yang dihasilkan akan menjadi

RUN apt-get install -y gcc\
 && gcc --version\
 && apt-get --purge autoremove -y gcc

yang tidak begitu mudah dibaca dan dipelihara karena kebingungan. Lihat bagaimana varian skrip shell gcc --versionmengeluarkan penekanan pada bagian penting sementara &&varian berantai mengubur bagian itu di tengah kebisingan.

Michael Le Barbier Grünewald
sumber
1
Bisakah Anda menyertakan hasil ukuran kotak setelah membangun menggunakan skrip dan menggunakan beberapa perintah dalam satu pernyataan RUN?
030
1
Sepertinya ide yang buruk bagi saya untuk mencampur konfigurasi basis gambar (yaitu hal-hal OS) dan bahkan lib dengan set up dari sumber yang Anda tulis. Anda mengatakan "Pastikan bahwa langkah-langkah pembangunan di mana kode sumber Anda terlibat selambat mungkin". Apakah ada masalah dalam menjadikan bagian itu artefak yang sepenuhnya independen?
JimmyJames
1
@ 030 Apa maksudmu dengan ukuran "kotak"? Saya tidak tahu kotak mana yang Anda maksud.
Michael Le Barbier Grünewald
1
Maksud saya ukuran gambar buruh pelabuhan
030
1
@ JimmyJames Ini sangat tergantung pada skenario penempatan Anda. Jika kita mengasumsikan program terkompilasi, "hal yang benar untuk dilakukan" adalah mengemasnya dan menginstal dependensi paket itu dan paket itu sendiri sebagai dua langkah mendekati-akhir yang berbeda. Ini untuk memaksimalkan kegunaan cache buruh pelabuhan dan untuk menghindari mengunduh berulang-ulang lapisan dengan file yang sama. Saya merasa lebih mudah untuk berbagi resep membangun untuk membangun gambar buruh pelabuhan daripada membangun rantai ketergantungan gambar yang lama, karena yang terakhir membuat pembangunan kembali menjadi lebih sulit.
Michael Le Barbier Grünewald
13

Setiap instruksi yang Anda buat di Dockerfile Anda menghasilkan lapisan gambar baru yang dibuat. Setiap lapisan membawa data tambahan yang tidak selalu merupakan bagian dari gambar yang dihasilkan. Misalnya, jika Anda menambahkan file dalam satu lapisan, tetapi menghapusnya di lapisan lain nanti, ukuran gambar akhir akan mencakup ukuran file yang ditambahkan dalam bentuk file "putih" khusus meskipun Anda menghapusnya.

Katakanlah Anda memiliki Dockerfile berikut:

FROM centos:6

RUN yum -y update 
RUN yum -y install epel-release

Ukuran gambar yang dihasilkan akan

bigimage     latest        3c5cbfbb4116        2 minutes ago    407MB

Sebagai kebalikan, dengan Dockerfile "mirip":

FROM centos:6

RUN yum -y update  && yum -y install epel-release

Ukuran gambar yang dihasilkan akan

smallimage     latest        7edeafc01ffe        3 minutes ago    384MB

Anda akan mendapatkan ukuran yang lebih kecil, jika Anda membersihkan cache yum dalam satu pernyataan RUN.

Jadi Anda ingin menjaga keseimbangan antara keterbacaan / perawatan mudah dan jumlah lapisan / ukuran gambar.

oryades
sumber
4

The RUNpernyataan mewakili masing-masing satu lapisan. Bayangkan seseorang mengunduh sebuah paket, menginstalnya dan ingin menghapusnya. Jika seseorang menggunakan tiga RUNpernyataan maka ukuran gambar tidak akan menyusut karena ada lapisan yang terpisah. Jika seseorang menjalankan semua perintah menggunakan satu RUNpernyataan, ukuran gambar disk dapat dikurangi.

030
sumber