Pendahuluan
Ada banyak artikel tentang menjalankan wadah dan menulis
docker-compose.yml . Tetapi bagi saya untuk waktu yang lama, pertanyaannya tidak jelas bagaimana melanjutkan dengan benar jika beberapa wadah tidak boleh diluncurkan sampai wadah lain siap untuk memproses permintaannya atau melakukan sejumlah pekerjaan.
Pertanyaan ini menjadi relevan setelah kami mulai aktif menggunakan
docker-compose , alih-alih meluncurkan masing-masing docker.
Untuk apa ini?
Memang, biarkan aplikasi dalam wadah B tergantung pada ketersediaan layanan dalam wadah A. Dan pada saat startup, aplikasi dalam wadah B tidak menerima layanan ini. Apa yang harus dilakukan?
Ada dua opsi:
- yang pertama adalah mati (lebih disukai dengan kode kesalahan)
- yang kedua adalah menunggu, dan kemudian mati pula, jika aplikasi dalam wadah B tidak merespons untuk batas waktu yang diberikan
Setelah kontainer B mati,
komposisi buruh pelabuhan (tergantung pada konfigurasi tentu saja) akan memulai kembali dan aplikasi dalam wadah B akan mencoba lagi untuk mencapai layanan dalam wadah A.
Ini akan berlanjut sampai layanan dalam wadah A siap untuk menanggapi permintaan, atau sampai kami melihat bahwa kontainer terus kelebihan muatan.
Dan sebenarnya, ini adalah cara normal untuk arsitektur multi-container.
Namun, khususnya, kami dihadapkan pada situasi di mana wadah A memulai dan menyiapkan data untuk wadah B. Aplikasi dalam wadah B tidak dapat memeriksa apakah data siap atau tidak, ia segera mulai bekerja dengan mereka. Karena itu, kita harus menerima dan memproses sinyal tentang kesiapan data sendiri.
Saya pikir Anda masih bisa memberikan beberapa use case. Tetapi yang paling penting, Anda perlu memahami persis mengapa Anda melakukan ini. Kalau tidak, lebih baik menggunakan alat pembuat
docker standar.
Sedikit ideologi
Jika Anda membaca dokumentasi dengan cermat, maka semuanya tertulis di sana. Yaitu masing-masing
unit independen dan harus menjaga semua layanan dengan
dengan mana ia akan bekerja, tersedia baginya.
Oleh karena itu, pertanyaannya bukan untuk memulai atau tidak memulai wadah, tetapi untuk memulai
di dalam wadah, periksa kesiapan semua layanan yang diperlukan dan hanya
kemudian transfer kontrol ke aplikasi kontainer.
Bagaimana penerapannya
Untuk mengatasi masalah ini, deskripsi
buruh pelabuhan-menulis banyak membantu saya,
ini bagian dari itu
dan
sebuah artikel tentang penggunaan
entrypoint dan
cmd yang tepat .
Jadi apa yang kita butuhkan:
- ada lampiran A yang kami bungkus dalam wadah A
- itu dimulai dan mulai merespons OK pada port 8000
- dan juga, ada aplikasi B, yang kita mulai dari wadah B, tetapi itu harus mulai bekerja tidak lebih awal dari aplikasi A akan mulai menanggapi permintaan pada port 8000
Dokumentasi resmi menawarkan dua cara untuk mengatasi masalah ini.
Yang pertama adalah menulis titik masuk Anda sendiri dalam wadah, yang akan melakukan semua pemeriksaan, dan kemudian memulai aplikasi yang berfungsi.
Yang kedua adalah menggunakan file batch yang sudah ditulis
wait-for-it.sh .
Kami mencoba keduanya.
Menulis Entrypoint Anda Sendiri
Apa itu
entrypoint ?
Ini hanya file yang dapat dieksekusi yang Anda tentukan saat membuat wadah di
Dockerfile di bidang
ENTRYPOINT . File ini, sebagaimana telah disebutkan, melakukan pemeriksaan, dan kemudian meluncurkan aplikasi utama wadah.
Jadi apa yang kita dapatkan:
Buat folder
Entrypoint .
Ini memiliki dua subfolder -
container_A dan
container_B . Kami akan membuat wadah kami di dalamnya.
Untuk penampung A, mari kita ambil http server sederhana dengan python. Dia, setelah memulai, mulai merespons untuk mendapatkan permintaan pada port 8000.
Untuk menjadikan percobaan kami lebih eksplisit, kami menetapkan penundaan 15 detik sebelum memulai server.
Ternyata
file buruh pelabuhan berikut
untuk wadah A :
FROM python:3 EXPOSE 8000 CMD sleep 15 && python3 -m http.server --cgi
Untuk kontainer B, buat
file buruh pelabuhan berikut
untuk wadah B :
FROM ubuntu:18.04 RUN apt-get update RUN apt-get install -y curl COPY ./entrypoint.sh /usr/bin/entrypoint.sh ENTRYPOINT [ "entrypoint.sh" ] CMD ["echo", "!!!!!!!! Container_A is available now !!!!!!!!"]
Dan menempatkan entrypoint.sh kita dapat dieksekusi di folder yang sama. Kami akan memilikinya seperti ini
Apa yang terjadi dalam wadah B:
- Saat dimulai, ia mulai ENTRYPOINT , mis. meluncurkan entrypoint.sh
- entrypoint.sh , menggunakan curl , mulai polling port 8000 untuk container A. Ia melakukan ini hingga menerima respons 200 (mis. curl dalam hal ini akan berakhir dengan hasil nol dan loop akan berakhir)
- Ketika 200 diterima, loop berakhir dan kontrol beralih ke perintah yang ditentukan dalam variabel $ cmd . Dan itu menunjukkan apa yang kami tunjukkan dalam file buruh pelabuhan di bidang CMD , mis. echo "!!! Container_A tersedia sekarang !!!!!!!!" Mengapa demikian, dijelaskan dalam artikel di atas
- Kami mencetak - !!! Container_A tersedia sekarang !!! dan menyimpulkan.
Kami akan memulai semuanya dengan
menulis buruh pelabuhan .
docker-compose.yml di sini kita memiliki ini:
version: '3' networks: waiting_for_conteiner: services: conteiner_a: build: ./conteiner_A container_name: conteiner_a image: conteiner_a restart: unless-stopped networks: - waiting_for_conteiner ports: - 8000:8000 conteiner_b: build: ./conteiner_B container_name: conteiner_b image: waiting_for_conteiner.entrypoint.conteiner_b restart: "no" networks: - waiting_for_conteiner
Di sini, di
conteiner_a, tidak perlu menentukan
port: 8000: 8000 . Ini dilakukan untuk dapat memverifikasi operasi server http yang berjalan di dalamnya dari luar.
Juga, wadah B tidak memulai kembali setelah dimatikan.
Kami meluncurkan:
docker-compose up —-build
Kami melihat bahwa selama 15 detik ada pesan tentang tidak tersedianya wadah A, dan kemudian
conteiner_b | Conteiner_A is unavailable - sleeping conteiner_b | % Total % Received % Xferd Average Speed Time Time Time Current conteiner_b | Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> conteiner_b | <html> conteiner_b | <head> conteiner_b | <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> conteiner_b | <title>Directory listing for /</title> conteiner_b | </head> conteiner_b | <body> conteiner_b | <h1>Directory listing for /</h1> conteiner_b | <hr> conteiner_b | <ul> conteiner_b | <li><a href=".dockerenv">.dockerenv</a></li> conteiner_b | <li><a href="bin/">bin/</a></li> conteiner_b | <li><a href="boot/">boot/</a></li> conteiner_b | <li><a href="dev/">dev/</a></li> conteiner_b | <li><a href="etc/">etc/</a></li> conteiner_b | <li><a href="home/">home/</a></li> conteiner_b | <li><a href="lib/">lib/</a></li> conteiner_b | <li><a href="lib64/">lib64/</a></li> conteiner_b | <li><a href="media/">media/</a></li> conteiner_b | <li><a href="mnt/">mnt/</a></li> conteiner_b | <li><a href="opt/">opt/</a></li> conteiner_b | <li><a href="proc/">proc/</a></li> conteiner_b | <li><a href="root/">root/</a></li> conteiner_b | <li><a href="run/">run/</a></li> conteiner_b | <li><a href="sbin/">sbin/</a></li> conteiner_b | <li><a href="srv/">srv/</a></li> conteiner_b | <li><a href="sys/">sys/</a></li> conteiner_b | <li><a href="tmp/">tmp/</a></li> conteiner_b | <li><a href="usr/">usr/</a></li> conteiner_b | <li><a href="var/">var/</a></li> conteiner_b | </ul> conteiner_b | <hr> conteiner_b | </body> conteiner_b | </html> 100 987 100 987 0 0 98700 0 --:--:-- --:--:-- --:--:-- 107k conteiner_b | Conteiner_A is up - executing command conteiner_b | !!!!!!!! Container_A is available now !!!!!!!!
Kami mendapatkan jawaban atas permintaan Anda, cetak
!!! Container_A tersedia sekarang !!!!!!!! dan menyimpulkan.
Menggunakan wait-for-it.sh
Perlu segera mengatakan bahwa jalur ini tidak berfungsi untuk kami seperti yang dijelaskan dalam dokumentasi.
Yaitu, diketahui bahwa jika
ENTRYPOINT dan
CMD ditulis dalam
Dockerfile , maka ketika wadah dimulai, perintah dari
ENTRYPOINT akan dieksekusi, dan konten
CMD akan diteruskan ke parameter tersebut.
Diketahui juga bahwa
ENTRYPOINT dan
CMD yang ditentukan dalam
Dockerfile dapat didefinisikan ulang di
docker-compose.ymlFormat startup
wait-for-it.sh adalah sebagai berikut:
wait-for-it.sh __ -- ___
Kemudian, seperti yang ditunjukkan dalam
artikel , kita dapat mendefinisikan
ENTRYPOINT baru di
docker-compose.yml , dan
CMD akan diganti dari
Dockerfile .
Jadi, kita dapat:
File Docker untuk kontainer A tetap tidak berubah:
FROM python:3 EXPOSE 8000 CMD sleep 15 && python3 -m http.server --cgi
File docker untuk kontainer B FROM ubuntu:18.04 COPY ./wait-for-it.sh /usr/bin/wait-for-it.sh CMD ["echo", "!!!!!!!! Container_A is available now !!!!!!!!"]
Docker-compose.yml terlihat seperti ini:
version: '3' networks: waiting_for_conteiner: services: conteiner_a: build: ./conteiner_A container_name: conteiner_a image: conteiner_a restart: unless-stopped networks: - waiting_for_conteiner ports: - 8000:8000 conteiner_b: build: ./conteiner_B container_name: conteiner_b image: waiting_for_conteiner.wait_for_it.conteiner_b restart: "no" networks: - waiting_for_conteiner entrypoint: ["wait-for-it.sh", "-s" , "-t", "20", "conteiner_a:8000", "--"]
Kami menjalankan perintah
wait-for-it , suruh menunggu 20 detik hingga container A hidup kembali, dan tentukan parameter lain
“-” , yang akan memisahkan parameter
wait-for-it dari program yang akan diluncurkan setelah selesai.
Kami mencoba!
Dan sayangnya, kami tidak mendapatkan apa-apa.
Jika kita memeriksa dengan argumen apa kita menjalankan wait-for-it, maka kita akan melihat bahwa hanya apa yang kita tentukan di
entry point yang dilewatkan ke sana ,
CMD dari wadah tidak terpasang.
Opsi kerja
Lalu, hanya ada satu opsi. Apa yang kami tentukan dalam
CMD di
Dockerfile , kami harus mentransfer ke
perintah di
docker-compose.yml .
Kemudian,
biarkan Dockerfile dari wadah B tidak berubah, dan
docker-compose.yml akan terlihat seperti ini:
version: '3' networks: waiting_for_conteiner: services: conteiner_a: build: ./conteiner_A container_name: conteiner_a image: conteiner_a restart: unless-stopped networks: - waiting_for_conteiner ports: - 8000:8000 conteiner_b: build: ./conteiner_B container_name: conteiner_b image: waiting_for_conteiner.wait_for_it.conteiner_b restart: "no" networks: - waiting_for_conteiner entrypoint: ["wait-for-it.sh", "-s" ,"-t", "20", "conteiner_a:8000", "--"] command: ["echo", "!!!!!!!! Container_A is available now !!!!!!!!"]
Dan dalam versi ini, ini berfungsi.
Kesimpulannya, harus dikatakan bahwa menurut pendapat kami, cara yang benar adalah yang pertama. Ini adalah yang paling universal dan memungkinkan Anda untuk melakukan pemeriksaan kesiapan dengan cara apa pun yang memungkinkan.
Tunggu-itu- hanya utilitas bermanfaat yang dapat Anda gunakan baik secara terpisah atau dengan menanamkan di
entrypoint Anda.