Dalam komentar di artikel Docker saya : saran buruk , ada banyak permintaan untuk menjelaskan mengapa Dockerfile yang dijelaskan di dalamnya sangat mengerikan.
Ringkasan dari seri sebelumnya : dua pengembang dalam tenggat waktu yang sulit membuat Dockerfile. Dalam prosesnya, Ops Igor Ivanovich mendatangi mereka. Dockerfile yang dihasilkan sangat buruk sehingga AI berada di ambang serangan jantung.

Sekarang mari kita cari tahu apa yang salah dengan Dockerfile ini.
Jadi, satu minggu telah berlalu.
Dev Petya bertemu di ruang makan dengan secangkir kopi dengan Ops Igor Ivanovich.
P: Igor Ivanovich, apakah Anda sangat sibuk? Saya ingin mencari tahu di mana kami mengacau.
AI: Ini bagus, Anda tidak sering bertemu pengembang yang tertarik dengan operasi.
Untuk memulai, mari kita sepakati beberapa hal:
- Ideologi buruh pelabuhan: satu wadah - satu proses.
- Semakin kecil wadahnya, semakin baik.
- Semakin banyak cache yang diambil, semakin baik.
P: Dan mengapa harus ada satu proses dalam satu wadah?
AI: Docker, ketika wadah mulai, memonitor status proses dengan pid 1. Jika proses mati, Docker mencoba untuk memulai kembali wadah. Misalkan Anda memiliki beberapa aplikasi yang berjalan di wadah Anda atau aplikasi utama tidak diluncurkan dengan pid 1. Jika prosesnya mati, Docker tidak akan mengetahuinya.
Jika tidak ada pertanyaan lagi, perlihatkan Dockerfile Anda.
Dan Petya menunjukkan:
FROM ubuntu:latest # COPY ./ /app WORKDIR /app # RUN apt-get update # RUN apt-get upgrade # RUN apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor # bundler RUN gem install bundler # nodejs RUN curl -sL https://deb.nodesource.com/setup_9.x | sudo bash - RUN apt-get install -y nodejs # RUN bundle install --without development test --path vendor/bundle # RUN rm -rf /usr/local/bundle/cache/*.gem RUN apt-get clean RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN rake assets:precompile # , , . CMD ["/app/init.sh"]
AI: Oh, mari kita bereskan. Mari kita mulai dengan baris pertama:
FROM ubuntu:latest
Anda mengambil tag latest
. Menggunakan tag latest
menyebabkan konsekuensi yang tidak terduga. Bayangkan bahwa pengelola gambar sedang membangun versi baru dari gambar dengan daftar perangkat lunak yang berbeda, gambar ini mendapatkan tag terbaru. Dan yang terbaik, wadah Anda berhenti mengumpulkan, dan paling buruk, Anda menangkap serangga yang tidak ada sebelumnya.
Anda mengambil gambar dengan OS penuh dengan banyak perangkat lunak yang tidak perlu, yang mengembang wadah. Dan semakin banyak perangkat lunak, semakin banyak lubang dan kerentanan.
Selain itu, semakin besar gambar, semakin banyak ruang yang digunakan pada host dan dalam registri (apakah Anda menyimpan gambar di suatu tempat)?
P: Ya, tentu saja, kami memiliki registri, dan Anda mengaturnya.
II: Jadi, apa yang saya bicarakan? .. Oh ya, volume ... Beban jaringan juga bertambah. Untuk satu gambar, ini tidak terlihat, tetapi ketika ada perakitan terus menerus, tes dan penyebaran, itu bisa diraba. Dan jika Anda tidak memiliki mode Tuhan di AWS, Anda juga akan menerima akun luar angkasa.
Karena itu, Anda perlu memilih gambar yang paling cocok, dengan versi yang tepat dan perangkat lunak minimum. Sebagai contoh, ambil: FROM ruby:2.5.5-stretch
P: Oh, begitu. Dan bagaimana dan di mana melihat gambar yang tersedia? Bagaimana cara memahami yang mana yang saya butuhkan?
AI: Biasanya, gambar diambil dari dockhub , jangan bingung dengan pornhub :). Biasanya ada beberapa rakitan untuk gambar:
Alpine : Gambar dikompilasi pada gambar Linux minimalis, hanya 5 MB. Kekurangannya: ia dibangun dengan implementasi libc sendiri, paket standar di dalamnya tidak berfungsi. Mencari dan menginstal paket yang tepat akan membutuhkan banyak waktu.
Scratch : gambar dasar, tidak digunakan untuk membangun gambar lain. Ini dimaksudkan hanya untuk menjalankan data biner yang disiapkan. Ideal untuk menjalankan aplikasi biner, yang mencakup semua yang Anda butuhkan, seperti aplikasi-go.
Berdasarkan OS apa pun seperti Ubuntu atau Debian. Nah, di sini, saya pikir, tidak perlu dijelaskan.
II: Sekarang kita harus menambahkan semuanya. Paket dan bersihkan cache. Dan segera Anda dapat melakukan upgrade apt-get . Jika tidak, dengan setiap perakitan, meskipun tag tetap dari gambar dasar, gambar yang berbeda akan diperoleh. Memperbarui paket dalam suatu gambar adalah tugas pengelola, disertai dengan perubahan tag.
P: Ya, saya mencoba melakukannya, ternyata seperti ini:
WORKDIR /app COPY ./ /app RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ && apt-get -y install libpq-dev imagemagick gsfonts ruby-full ssh supervisor nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle RUN rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
II: Tidak buruk, tetapi ada juga sesuatu untuk dikerjakan. Lihat, ini perintahnya:
RUN rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
... tidak menghapus data dari gambar akhir, tetapi hanya membuat lapisan tambahan tanpa data ini. Benar sekali:
RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Tapi itu belum semuanya. Apa yang ada di sana, Ruby? Maka tidak perlu menyalin seluruh proyek di awal. Cukup salin Gemfile dan Gemfile.lock.
Dengan pendekatan ini, instalasi bundel tidak akan dijalankan untuk setiap perubahan ke sumber, tetapi hanya jika Gemfile atau Gemfile.lock telah berubah.
Metode yang sama berfungsi untuk bahasa lain dengan manajer dependensi, seperti npm, pip, komposer, dan lainnya berdasarkan file dengan daftar dependensi.
Dan akhirnya, ingat, pada awalnya saya berbicara tentang ideologi Docker "satu wadah - satu proses"? Ini berarti bahwa seorang supervisor tidak diperlukan. Juga, jangan menginstal systemd, karena alasan yang sama. Padahal, Docker sendiri adalah seorang supervisor. Dan ketika Anda mencoba menjalankan beberapa proses di dalamnya, itu seperti meluncurkan beberapa aplikasi dalam satu proses penyelia.
Saat merakit, Anda akan membuat satu gambar, dan kemudian menjalankan jumlah kontainer yang diinginkan sehingga setiap proses memiliki satu proses.
Tetapi lebih lanjut tentang itu nanti.
P: Sepertinya mengerti. Lihat apa yang terjadi:
FROM ruby:2.5.5-stretch WORKDIR /app COPY Gemfile* /app RUN curl -sL https://deb.nodesource.com/setup_9.x | bash - \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY . /app RUN rake assets:precompile CMD ["bundle”, “exec”, “passenger”, “start"]
Apakah daemon akan didefinisikan ulang ketika wadah dimulai?
AI: Ya, benar. Omong-omong, Anda dapat menggunakan CMD dan ENTRYPOINT. Dan untuk memahami apa bedanya, ini adalah pekerjaan rumah Anda. Ada artikel bagus tentang topik ini di Habré.
Jadi ayo. Anda mengunduh file untuk menginstal node, tetapi tidak ada jaminan bahwa itu akan memiliki apa yang Anda butuhkan. Perlu menambahkan validasi. Misalnya, seperti ini:
RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x \ && echo "958c9a95c4974c918dca773edf6d18b1d1a41434 setup_9.x" | sha1sum -c - \ && bash setup_9.x \ && rm -rf setup_9.x \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Dengan menggunakan checksum, Anda dapat memverifikasi bahwa Anda mengunduh file yang benar.
P: Tetapi jika file berubah, maka perakitan tidak akan berfungsi.
II: Ya, dan anehnya, ini juga merupakan nilai tambah. Anda akan menemukan bahwa file telah berubah, dan Anda dapat melihat apa yang diubah di sana. Anda tidak pernah tahu, mereka menambahkan, mengatakan, sebuah skrip yang menghapus semua yang dicapainya, atau melakukan backdoor.
P: Terima kasih. Ternyata Dockerfile terakhir akan terlihat seperti ini:
FROM ruby:2.5.5-stretch WORKDIR /app COPY Gemfile* /app RUN curl -sL https://deb.nodesource.com/setup_9.x > setup_9.x \ && echo "958c9a95c4974c918dca773edf6d18b1d1a41434 setup_9.x" | sha1sum -c - \ && bash setup_9.x \ && rm -rf setup_9.x \ && apt-get -y install libpq-dev imagemagick gsfonts nodejs \ && gem install bundler \ && bundle install --without development test --path vendor/bundle \ && rm -rf /usr/local/bundle/cache/*.gem \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* COPY . /app RUN rake assets:precompile CMD ["bundle”, “exec”, “passenger”, “start"]
P: Igor Ivanovich, terima kasih atas bantuannya. Sudah waktunya bagi saya untuk berlari, saya perlu membuat 10 komitmen lebih untuk hari ini.
Igor Ivanovich, menghentikan pandangan rekannya yang tergesa-gesa, menyesap kopi kental. Setelah berpikir selama beberapa detik tentang 99,9% SLA dan kode tanpa bug, ia mengajukan pertanyaan.
AI: Dan di mana Anda menyimpan log?
P: Tentu saja, di production.log. Ngomong-ngomong, ya, bagaimana kita bisa mengaksesnya tanpa ssh?
II: Jika Anda membiarkannya dalam file, solusi telah ditemukan untuk Anda. Perintah docker exec memungkinkan Anda untuk mengeksekusi perintah apa pun dalam sebuah wadah. Misalnya, Anda dapat membuat cat untuk log. Dan menggunakan -it switch dan menjalankan bash (jika dipasang di wadah), Anda akan mendapatkan akses interaktif ke wadah.
Tetapi Anda tidak harus menyimpan log dalam file. Minimal, ini mengarah pada pertumbuhan kontainer yang tidak terkendali, tetapi tidak ada yang memutar log. Semua log harus dilemparkan ke stdout. Di sana Anda sudah dapat melihat mereka menggunakan perintah log buruh pelabuhan .
P: Igor Ivanovich, tetapi dapat menempatkan log ke direktori yang terpasang, pada simpul fisik, sebagai data pengguna?
AI: Sangat baik bahwa Anda tidak lupa untuk menghapus data yang diunduh ke disk node. Dengan log, ini juga mungkin, hanya ingat untuk mengatur rotasi.
Yang bisa Anda jalankan.
P: Igor Ivanovich, tetapi menyarankan apa yang harus dibaca?
AI: Pertama, baca rekomendasi dari pengembang Docker , hampir tidak ada yang tahu Docker lebih baik dari mereka.
Dan jika Anda ingin menjalani latihan, lakukan secara intensif . Bagaimanapun, sebuah teori tanpa latihan sudah mati.