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

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

Docker-in-Docker adalah lingkungan daemon Docker tervirtualisasi yang berjalan di dalam container itu sendiri untuk membuat image container. Tujuan utama pembuatan Docker-in-Docker adalah untuk membantu mengembangkan Docker itu sendiri. Banyak orang menggunakannya untuk menjalankan Jenkins CI. Ini tampak normal pada awalnya, tetapi kemudian muncul masalah yang dapat dihindari dengan menginstal Docker di container Jenkins CI. Artikel ini memberi tahu Anda cara melakukan ini. Jika Anda tertarik dengan solusi akhir tanpa rincian, baca saja bagian terakhir artikel, “Memecahkan Masalah.”

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

Docker-in-Docker: "Bagus"

Lebih dari dua tahun yang lalu saya memasukkan Docker bendera –memiliki hak istimewa dan menulis versi pertama dind. Tujuannya adalah membantu tim inti mengembangkan Docker lebih cepat. Sebelum Docker-in-Docker, siklus pengembangan umumnya terlihat seperti ini:

  • peretasan peretasan;
  • membangun;
  • menghentikan daemon Docker yang sedang berjalan;
  • meluncurkan daemon Docker baru;
  • pengujian;
  • ulangi siklusnya.

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

  • peretasan peretasan;
  • pastikan versi Docker yang berfungsi sedang berjalan;
  • membangun Docker baru dengan Docker lama;
  • hentikan daemon Docker;
  • memulai daemon Docker baru;
  • tes;
  • hentikan daemon Docker baru;
  • mengulang.

Dengan hadirnya Docker-in-Docker, prosesnya menjadi lebih sederhana:

  • peretasan peretasan;
  • perakitan + peluncuran dalam satu tahap;
  • ulangi siklusnya.

Bukankah lebih baik begini?

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

Docker-in-Docker: "Buruk"

Namun, bertentangan dengan kepercayaan populer, Docker-in-Docker tidak 100% berupa bintang, kuda poni, dan unicorn. Yang saya maksud adalah ada beberapa masalah yang perlu diwaspadai oleh seorang pengembang.

Salah satunya menyangkut LSM (modul keamanan Linux) seperti AppArmor dan SELinux: saat menjalankan sebuah container, "Docker internal" mungkin mencoba menerapkan profil keamanan yang akan menimbulkan konflik atau membingungkan "Docker eksternal". Ini adalah masalah yang paling sulit dipecahkan ketika mencoba menggabungkan implementasi asli dari –privileged flag. Perubahan saya berhasil dan semua tes akan lulus pada mesin Debian saya dan VM pengujian Ubuntu, tetapi mereka akan crash dan terbakar pada mesin Michael Crosby (seingat saya dia punya Fedora). Saya tidak ingat persis penyebab masalahnya, tapi mungkin karena Mike adalah orang bijak yang bekerja dengan SELINUX=enforce (saya menggunakan AppArmor) dan perubahan saya tidak memperhitungkan profil SELinux.

Docker-in-Docker: "Jahat"

Masalah kedua adalah dengan driver penyimpanan Docker. Saat Anda menjalankan 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 copy-on-write (AUFS, BTRFS, Device Mapper , dll.). , tergantung pada apa yang dikonfigurasi untuk menggunakan Docker eksternal). Hal ini menciptakan banyak kombinasi yang tidak akan berhasil. Misalnya, Anda tidak akan dapat menjalankan AUFS di atas AUFS.

Jika Anda menjalankan BTRFS di atas BTRFS, ini akan berfungsi pada awalnya, tetapi setelah ada subvolume yang disarangkan, penghapusan subvolume induk akan gagal. Modul Device Mapper tidak memiliki namespace, jadi jika beberapa instance Docker menjalankannya di mesin yang sama, semuanya akan dapat melihat (dan memengaruhi) image satu sama lain dan pada perangkat cadangan container. Ini buruk.

Ada solusi untuk mengatasi banyak masalah ini. Misalnya, jika Anda ingin menggunakan AUFS di Docker internal, cukup ubah folder /var/lib/docker menjadi volume dan Anda akan baik-baik saja. Docker telah menambahkan beberapa namespace dasar ke nama target Device Mapper sehingga jika beberapa panggilan Docker berjalan di mesin yang sama, panggilan tersebut tidak akan saling menginjak.

Namun, pengaturan seperti itu sama sekali tidak sederhana, seperti terlihat pada gambar di bawah ini artikel di repositori dind di GitHub.

Docker-in-Docker: Ini menjadi lebih buruk

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

Beberapa orang yang giat telah mencoba mengikat /var/lib/docker dari host ke container Docker-in-Docker. Terkadang mereka berbagi /var/lib/docker dengan banyak container.

Pikirkan baik-baik sebelum menggunakan Docker-in-Docker untuk CI atau lingkungan pengujian
Apakah Anda ingin merusak data Anda? Karena justru hal inilah yang akan merusak data Anda!

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

Mengapa demikian? Karena ini adalah hasil dari salah satu pelajaran tersulit yang didapat selama mengembangkan dotCloud. Mesin kontainer dotCloud dijalankan dengan memiliki beberapa proses yang mengakses /var/lib/dotcloud secara bersamaan. Trik licik seperti penggantian file atom (alih-alih mengedit di tempat), menambahkan kode dengan kunci penasehat dan wajib, dan eksperimen lain dengan sistem aman seperti SQLite dan BDB tidak selalu berhasil. Saat kami mendesain ulang mesin container kami, yang akhirnya menjadi Docker, salah satu keputusan desain besar adalah mengkonsolidasikan semua operasi container di bawah satu daemon untuk menghilangkan semua omong kosong konkurensi.

Jangan salah paham: sangat mungkin membuat sesuatu yang baik, andal, dan cepat yang melibatkan banyak proses dan kontrol paralel modern. Namun menurut kami lebih sederhana dan mudah untuk menulis dan memelihara kode menggunakan Docker sebagai satu-satunya pemain.

Artinya, jika Anda berbagi direktori /var/lib/docker di antara beberapa instance Docker, Anda akan mengalami masalah. Tentu saja ini bisa berhasil, terutama pada pengujian tahap awal. “Dengar, Bu, saya bisa menjalankan ubuntu sebagai buruh pelabuhan!” Namun cobalah sesuatu yang lebih kompleks, seperti mengambil gambar yang sama dari dua kejadian berbeda, dan Anda akan melihat dunia terbakar.

Artinya, jika sistem CI Anda melakukan pembangunan dan pembangunan kembali, setiap kali Anda memulai ulang kontainer Docker-in-Docker, Anda berisiko memasukkan nuklir ke dalam cache-nya. Ini sama sekali tidak keren!

Solusinya

Mari kita mundur selangkah. Apakah Anda benar-benar membutuhkan Docker-in-Docker atau Anda hanya ingin dapat menjalankan Docker dan membangun serta menjalankan container dan image dari sistem CI Anda saat sistem CI itu sendiri berada di dalam container?

Saya yakin kebanyakan orang menginginkan opsi terakhir, artinya mereka menginginkan sistem CI seperti Jenkins yang dapat menjalankan container. Dan cara termudah untuk melakukannya adalah dengan memasukkan soket Docker ke dalam wadah CI Anda dan mengaitkannya dengan flag -v.

Sederhananya, saat Anda menjalankan container CI (Jenkins atau lainnya), alih-alih meretas sesuatu bersama Docker-in-Docker, mulailah dengan baris:

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

Kontainer ini sekarang akan memiliki akses ke soket Docker dan karenanya dapat menjalankan kontainer. Kecuali bahwa alih-alih menjalankan container “anak”, ia akan meluncurkan container “saudara”.

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

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

Ini terlihat dan berfungsi seperti Docker-in-Docker, tetapi bukan Docker-in-Docker: ketika kontainer ini membuat kontainer tambahan, kontainer tersebut akan dibuat di Docker tingkat atas. Anda tidak akan mengalami efek samping dari penyarangan dan cache perakitan akan dibagikan ke beberapa panggilan.

Catatan: Versi sebelumnya dari artikel ini menyarankan untuk menautkan biner Docker dari host ke container. Ini sekarang menjadi tidak dapat diandalkan karena mesin Docker tidak lagi mencakup perpustakaan statis atau hampir statis.

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

Beberapa iklan 🙂

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

Dell R730xd 2x lebih murah di pusat data Equinix Tier IV di Amsterdam? Hanya disini 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 $99! Membaca tentang Bagaimana membangun infrastruktur corp. kelas dengan penggunaan server Dell R730xd E5-2650 v4 senilai 9000 euro untuk satu sen?

Sumber: www.habr.com

Tambah komentar