Pikirkan baik-baik sebelum menggunakan Docker-in-Docker untuk CI atau lingkungan pengujian.



Docker-in-Docker adalah daemon Docker tervirtualisasi yang berjalan dalam wadah itu sendiri untuk membuat gambar wadah. Tujuan utama pembuatan Docker-in-Docker adalah untuk membantu mengembangkan Docker itu sendiri. Banyak orang menggunakannya untuk menjalankan Jenkins CI. Pada awalnya ini tampak normal, tetapi kemudian ada masalah yang dapat dihindari dengan menginstal Docker di wadah Jenkins CI. Artikel ini menjelaskan cara melakukan ini. Jika Anda tertarik pada solusi akhir tanpa detail, cukup baca bagian terakhir artikel "Memecahkan masalah."



Docker-in-Docker: Bagus


Lebih dari dua tahun lalu, saya memasukkan flag –privileged di Docker dan menulis versi pertama dari dind . Tujuannya adalah untuk membantu tim inti mengembangkan Docker lebih cepat. Sebelum Docker-in-Docker, siklus pengembangan khas adalah seperti ini:

  • hackity hack;
  • majelis
  • menghentikan daemon docker yang sedang berjalan;
  • meluncurkan daemon buruh pelabuhan baru;
  • pengujian;
  • ulangi lingkaran.

Jika Anda ingin membuat perakitan yang indah dan dapat direproduksi (yaitu, dalam wadah), maka itu menjadi lebih rumit:

  • hackity hack;
  • pastikan versi Docker yang berfungsi berjalan;
  • membangun buruh pelabuhan baru dengan buruh pelabuhan lama;
  • menghentikan daemon buruh pelabuhan;
  • meluncurkan daemon buruh pelabuhan baru;
  • untuk mengetes;
  • menghentikan daemon Docker baru;
  • ulang.

Dengan munculnya Docker-in-Docker, prosesnya telah disederhanakan:

  • hackity hack;
  • perakitan + peluncuran dalam satu langkah;
  • ulangi lingkaran.

Bukankah itu jauh lebih baik?



Docker-in-Docker: Buruk


Namun, bertentangan dengan kepercayaan populer, Docker-in-Docker tidak 100% terdiri dari bintang, kuda, dan unicorn. Maksud saya, ada beberapa masalah yang perlu diketahui pengembang.

Salah satunya menyangkut LSM (modul keamanan Linux), seperti AppArmor dan SELinux: ketika wadah mulai, "Docker internal" dapat mencoba menerapkan profil keamanan yang akan bertentangan atau membingungkan "Docker eksternal". Ini adalah masalah paling sulit yang perlu dipecahkan ketika mencoba untuk menggabungkan implementasi asli dari flag –privileged. Perubahan saya berhasil, dan semua tes akan melewati mesin Debian saya dan Ubuntu menguji mesin virtual juga, tetapi mereka akan crash dan terbakar pada mesin Michael Crosby (sejauh yang saya ingat, dia punya Fedora). Saya tidak dapat mengingat penyebab pasti masalah itu, tetapi itu mungkin terjadi karena Mike adalah orang bijak yang bekerja dengan SELINUX = menegakkan (saya menggunakan AppArmor) dan perubahan saya tidak memperhitungkan profil SELinux.

Docker-in-Docker: Marah


Masalah kedua terkait dengan driver penyimpanan Docker. Ketika Anda meluncurkan Docker-in-Docker, Docker eksternal berjalan di atas sistem file biasa (EXT4, BTRFS, atau apa pun yang Anda miliki), dan Docker internal berjalan di atas sistem salin dan tulis (AUFS, BTRFS, Device Mapper, dll.) , tergantung pada apa yang dikonfigurasikan untuk menggunakan Docker eksternal). Dalam hal ini, ada banyak kombinasi yang tidak akan berfungsi. Misalnya, Anda tidak dapat menjalankan AUFS di atas AUFS.

Jika Anda menjalankan BTRFS di atas BTRFS, ini harus berfungsi terlebih dahulu, tetapi begitu subkunci muncul, subvolume induk tidak dapat dihapus. Modul Device Mapper tidak memiliki namespace, jadi jika beberapa instance Docker menggunakannya pada mesin yang sama, mereka semua dapat melihat (dan memengaruhi) gambar satu sama lain dan pada perangkat cadangan kontainer. Ini buruk.

Ada solusi untuk memecahkan banyak masalah ini. Misalnya, jika Anda ingin menggunakan AUFS di Docker internal, cukup ubah folder / var / lib / docker menjadi satu dan semuanya akan baik-baik saja. Docker menambahkan beberapa ruang nama dasar ke nama target Device Mapper, sehingga jika beberapa panggilan Docker dilakukan pada mesin yang sama, mereka tidak akan "melangkah" satu sama lain.

Namun, pengaturan ini jauh dari sederhana, seperti yang Anda lihat dari artikel ini di repositori dind di GitHub.

Docker-in-Docker: semakin buruk


Bagaimana dengan cache build? Ini juga bisa sangat sulit. Orang-orang sering bertanya kepada saya “jika saya menjalankan Docker-in-Docker, bagaimana saya bisa menggunakan gambar yang terletak di host saya, alih-alih menarik semuanya kembali ke Docker internal saya”?

Beberapa orang giat mencoba untuk mengikat / var / lib / docker dari host ke wadah Docker-in-Docker. Terkadang mereka berbagi / var / lib / buruh pelabuhan dengan beberapa kontainer.


Ingin merusak data? Karena inilah yang akan merusak data Anda!

Daemon buruh pelabuhan jelas dirancang untuk memiliki akses eksklusif ke / var / lib / docker. Tidak ada lagi yang harus "menyentuh, menyodok atau menyentuh" ​​file Docker apa pun di folder ini.

Kenapa begitu? Karena itu adalah hasil dari salah satu pelajaran paling sulit yang dipelajari dari pengembangan dotCloud. Mesin wadah dotCloud bekerja dengan beberapa proses mengakses / var / lib / dotcloud secara bersamaan. Trik rumit, seperti penggantian file atom (alih-alih mengedit di tempat), "bertengger" kode dengan kunci penasehat dan wajib, dan percobaan lain dengan sistem yang aman seperti SQLite dan BDB, tidak selalu berhasil. Ketika kami mendesain ulang mesin peti kemas kami, yang akhirnya berubah menjadi Docker, salah satu keputusan desain utama adalah mengumpulkan semua operasi peti kemas di bawah satu daemon untuk menghilangkan semua omong kosong dari akses simultan ini.

Jangan salah paham: sangat mungkin untuk melakukan sesuatu yang baik, dapat diandalkan dan cepat, yang akan mencakup beberapa proses dan kontrol paralel modern. Namun kami pikir lebih mudah dan lebih mudah untuk menulis dan memelihara kode menggunakan Docker sebagai satu-satunya pemain.

Ini berarti bahwa jika Anda membagikan direktori / var / lib / docker di antara beberapa instance Docker, Anda akan mengalami masalah. Tentu saja, ini mungkin berhasil, terutama pada tahap awal pengujian. "Dengar, Bu, aku bisa menjalankan ubuntu sebagai buruh pelabuhan!" Tetapi coba lakukan sesuatu yang lebih kompleks, misalnya, tarik gambar yang sama dari dua contoh berbeda, dan Anda akan melihat bagaimana dunia terbakar.

Ini berarti bahwa jika sistem CI Anda melakukan assemblies dan reassemblies, maka setiap kali Anda me-restart wadah Docker-in-Docker, Anda berisiko menjatuhkan bom nuklir di cache. Ini sama sekali tidak keren!

Solusi untuk masalah tersebut


Mari kita mundur selangkah. Apakah Anda benar-benar membutuhkan Docker-in-Docker atau hanya ingin dapat menjalankan Docker, yaitu membuat dan menjalankan wadah dan gambar dari sistem CI Anda, sementara sistem CI ini sendiri ada di dalam wadah?

Saya yakin sebagian besar orang membutuhkan opsi yang terakhir, yaitu, mereka menginginkan sistem CI seperti Jenkins untuk menjalankan kontainer. Dan cara termudah untuk melakukan ini adalah cukup memasukkan soket Docker ke wadah CI Anda, mengaitkannya dengan flag -v.

Sederhananya, ketika Anda meluncurkan wadah CI Anda (Jenkins atau yang lain), alih-alih meretas sesuatu dengan Docker-in-Docker, mulailah dari baris:

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

Sekarang kontainer ini akan memiliki akses ke soket Docker dan, karenanya, akan dapat meluncurkan kontainer. Kecuali bahwa alih-alih meluncurkan wadah "anak", itu akan menjalankan wadah "terkait".

Coba ini menggunakan gambar buruh pelabuhan resmi (yang berisi biner Docker):

docker run -v /var/run/docker.sock:/var/run/docker.sock \
           -ti docker

Itu terlihat dan berfungsi seperti Docker-in-Docker, tetapi itu bukan Docker-in-Docker: ketika wadah ini membuat kontainer tambahan, mereka akan dibuat di Docker tingkat tertinggi. Anda tidak akan mengalami efek samping dari bersarang, dan cache build akan dibagi di beberapa panggilan.

Catatan: versi sebelumnya dari artikel ini menyarankan untuk mengikat biner Docker dari host ke wadah. Ini sekarang menjadi tidak dapat diandalkan, karena mekanisme Docker tidak lagi meluas ke perpustakaan statis atau hampir statis.

Jadi, jika Anda ingin menggunakan Docker dari Jenkins CI, Anda memiliki 2 opsi:
menginstal Docker CLI menggunakan sistem pengemasan gambar dasar (mis. Jika gambar Anda didasarkan pada Debian, gunakan paket .deb), menggunakan Docker API.

Sedikit iklan :)


Terima kasih untuk tetap bersama kami. Apakah Anda suka artikel kami? Ingin melihat materi yang lebih menarik? Dukung kami dengan melakukan pemesanan atau merekomendasikan kepada teman Anda, cloud VPS untuk pengembang dari $ 4,99 , analog unik dari server entry-level yang diciptakan oleh kami untuk Anda: Seluruh kebenaran tentang VPS (KVM) E5-2697 v3 (6 Cores) 10GB DDR4 480GB SSD 1Gbps mulai dari $ 19 atau cara membagi server? (opsi tersedia dengan RAID1 dan RAID10, hingga 24 core dan hingga 40GB DDR4).

Dell R730xd 2 kali lebih murah di pusat data Equinix Tier IV di Amsterdam? Hanya kami yang memiliki 2 x Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100 TV dari $ 199 di Belanda!Dell R420 - 2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB - mulai dari $ 99! Baca tentang Cara Membangun Infrastruktur Bldg. kelas c menggunakan server Dell R730xd E5-2650 v4 seharga 9.000 euro untuk satu sen?

All Articles