Masalah pembersihan "pintar" dari gambar kontainer dan solusinya di werf





Artikel ini membahas masalah pembersihan image yang terkumpul di container registry (Docker Registry dan analognya) dalam realitas pipeline CI / CD modern untuk aplikasi cloud native yang dikirim ke Kubernetes. Kriteria utama untuk relevansi gambar dan kesulitan yang dihasilkan dalam otomatisasi pembersihan, penghematan ruang, dan pemenuhan kebutuhan tim diberikan. Terakhir, dengan menggunakan contoh proyek Open Source tertentu, kami akan memberi tahu Anda bagaimana kesulitan ini dapat diatasi.



pengantar



Jumlah gambar di registri penampung dapat bertambah dengan cepat, menghabiskan lebih banyak ruang penyimpanan dan, karenanya, meningkatkan biayanya secara signifikan. Untuk mengontrol, membatasi, atau mempertahankan pertumbuhan yang dapat diterima dari ruang yang ditempati dalam registri, itu diterima:



  1. gunakan sejumlah tag untuk gambar;
  2. bersihkan gambar dengan cara apa pun.


Batasan pertama terkadang berlaku untuk tim kecil. Jika pengembang memiliki tag permanen yang cukup ( latest, main, test, borisdll), registry tidak akan membengkak dalam ukuran dan dapat menjadi waktu yang lama untuk tidak berpikir tentang pembersihan. Lagi pula, semua gambar yang tidak relevan sudah usang, dan tidak ada pekerjaan yang tersisa untuk dibersihkan (semuanya dilakukan oleh pemulung biasa).



Namun, pendekatan ini sangat membatasi pengembangan dan jarang dapat diterapkan pada proyek CI / CD saat ini. Otomasi telah menjadi bagian integral dari pengembanganyang memungkinkan Anda menguji, menerapkan, dan memberikan fungsionalitas baru kepada pengguna dengan lebih cepat. Misalnya, di semua proyek kami, pipeline CI secara otomatis dibuat di setiap commit. Itu membangun gambar, mengujinya, meluncurkannya ke berbagai sirkuit Kubernetes untuk debugging dan pemeriksaan yang tersisa, dan jika semuanya berjalan dengan baik, perubahan akan mencapai pengguna akhir. Dan ini bukan ilmu roket untuk waktu yang lama, tetapi kehidupan sehari-hari bagi banyak orang - kemungkinan besar bagi Anda, karena Anda membaca artikel ini.



Karena bug dihilangkan dan fungsionalitas baru dikembangkan secara paralel, dan rilis dapat dilakukan beberapa kali sehari, jelaslah bahwa proses pengembangan disertai dengan sejumlah besar komit, yang berarti sejumlah besar gambar di registri... Akibatnya, masalah pengorganisasian pembersihan registri yang efektif menjadi akut, mis. penghapusan gambar yang tidak relevan.



Tapi bagaimana Anda bisa menentukan apakah sebuah gambar relevan?



Kriteria relevansi gambar



Pada sebagian besar kasus, kriteria utamanya adalah sebagai berikut:



1. Yang pertama (yang paling jelas dan paling kritis dari semuanya) adalah image yang saat ini digunakan di Kubernetes . Menghapus gambar ini dapat menyebabkan biaya waktu henti yang serius untuk produksi (misalnya, gambar mungkin diperlukan selama replikasi) atau meniadakan upaya tim yang terlibat dalam debugging pada sirkuit mana pun. (Untuk alasan ini, kami bahkan membuat eksportir Prometheus khusus yang memantau tidak adanya gambar seperti itu di cluster Kubernetes mana pun.)



2. Yang kedua (kurang jelas, tetapi juga sangat penting dan sekali lagi terkait dengan operasi) - gambar yang diperlukan untuk rollback jika terjadi serius masalahdi versi saat ini. Misalnya, dalam kasus Helm, ini adalah gambar yang digunakan dalam versi rilis yang disimpan. (By the way, batas default di Helm adalah 256 revisi, tetapi hampir tidak ada orang yang benar-benar memiliki kebutuhan untuk menyimpan seperti sejumlah besar versi? ..) Setelah semua, untuk ini, khususnya, kita menyimpan versi sehingga nanti bisa gunakan, yaitu "Kembalikan" ke mereka jika perlu.



3. Ketiga - kebutuhan pengembang : semua gambar yang terkait dengan pekerjaan mereka saat ini. Misalnya, jika kita mempertimbangkan PR, maka masuk akal untuk membiarkan gambar sesuai dengan komit terakhir dan, katakanlah, komit sebelumnya: dengan cara ini pengembang dapat dengan cepat kembali ke tugas apa pun dan bekerja dengan perubahan terbaru.



4. Keempat - gambar itusesuai dengan versi aplikasi kami , yaitu adalah produk akhir: v1.0.0, 20.04.01, sierra, dll.



NB: Kriteria yang ditentukan di sini dirumuskan berdasarkan pengalaman berinteraksi dengan lusinan tim pengembang dari berbagai perusahaan. Namun, tentu saja, bergantung pada proses pengembangan dan infrastruktur yang digunakan (misalnya, Kubernetes tidak digunakan), kriteria ini mungkin berbeda.



Kelayakan dan solusi yang ada



Layanan populer dengan registri kontainer, sebagai suatu peraturan, menawarkan kebijakan pembersihan gambarnya sendiri: di dalamnya Anda dapat menentukan kondisi saat tag dihapus dari registri. Namun, kondisi ini dibatasi oleh parameter seperti nama, waktu pembuatan, dan jumlah tag *.



* Bergantung pada implementasi registri kontainer tertentu. Kami melihat solusi berikut: Azure CR, Docker Hub, ECR, GCR, Paket GitHub, GitLab Container Registry, Harbor Registry, JFrog Artifactory, Quay.io - per September 2020.



Kumpulan parameter ini cukup untuk memenuhi kriteria keempat - yaitu, memilih gambar yang cocok dengan versi. Namun, untuk semua kriteria lainnya, seseorang harus memilih beberapa jenis solusi kompromi (yang lebih keras atau, sebaliknya, kebijakan hemat) - tergantung pada ekspektasi dan kemampuan finansial.



Misalnya, kriteria ketiga - terkait dengan kebutuhan pengembang - dapat diselesaikan dengan mengatur proses dalam tim: penamaan gambar tertentu, mempertahankan daftar izin khusus, dan perjanjian internal. Namun pada akhirnya masih perlu diotomatisasi. Dan jika kemungkinan solusi siap pakai tidak cukup, Anda harus melakukan sesuatu sendiri.



Situasinya serupa dengan dua kriteria pertama: mereka tidak dapat dipenuhi tanpa menerima data dari sistem eksternal - di mana aplikasi di-deploy (dalam kasus kami, ini adalah Kubernetes).



Ilustrasi alur kerja di Git



Misalkan Anda mengerjakan sesuatu seperti ini di Git:







Ikon dengan kepala di diagram menandai gambar kontainer yang saat ini diterapkan ke Kubernetes untuk semua pengguna (pengguna akhir, penguji, manajer, dll.) Atau digunakan oleh pengembang untuk debugging dan tujuan serupa.



Apa yang terjadi jika kebijakan pembersihan memungkinkan Anda menyimpan (bukan menghapus) gambar hanya untuk nama tag yang ditentukan ?







Jelas, skenario ini tidak akan menyenangkan siapa pun.



Apa yang akan berubah jika kebijakan mengizinkan Anda untuk tidak menghapus gambar untuk interval waktu tertentu / jumlah komit terbaru ?







Hasilnya memang jauh lebih baik, tapi masih jauh dari ideal. Bagaimanapun, kami masih memiliki pengembang yang membutuhkan gambar di registri (atau bahkan disebarkan di K8s) untuk men-debug bug ... Meringkas



situasi saat ini di pasar: fungsi yang tersedia di registri kontainer tidak menawarkan fleksibilitas yang cukup untuk pembersihan, dan alasan utamanya adalah tidak ada kemungkinan berinteraksi dengan dunia luar . Ternyata tim yang membutuhkan fleksibilitas ini terpaksa mengimplementasikan penghapusan image "di luar" sendiri menggunakan Docker Registry API (atau API asli dari implementasi yang sesuai).



Namun, kami sedang mencari solusi universal yang akan mengotomatiskan pembersihan gambar untuk tim berbeda menggunakan registri berbeda ...



Jalan kami menuju pembersihan citra universal



Dari manakah kebutuhan ini berasal? Faktanya adalah kami bukan grup pengembang yang terpisah, tetapi tim yang melayani banyak dari mereka sekaligus, membantu memecahkan masalah CI / CD secara komprehensif. Dan alat teknis utama untuk ini adalah utilitas werf open source . Keunikannya adalah tidak melakukan satu fungsi, tetapi menyertai proses pengiriman berkelanjutan di semua tahap: dari perakitan hingga penerapan.



Menerbitkan gambar ke registri * (segera setelah dibuat) adalah fungsi yang jelas dari utilitas semacam itu. Dan karena gambar ditempatkan di sana untuk penyimpanan, maka - jika penyimpanan Anda tidak terbatas - Anda harus bertanggung jawab untuk pembersihan selanjutnya. Bagaimana kami mencapai kesuksesan dalam hal ini, memenuhi semua kriteria yang ditentukan, akan dibahas lebih lanjut.



* Meskipun registri itu sendiri mungkin berbeda (Docker Registry, GitLab Container Registry, Harbour, dll.), Penggunanya menghadapi masalah yang sama. Solusi universal dalam kasus kami tidak bergantung pada implementasi registri, karena berjalan di luar registri itu sendiri dan menawarkan perilaku yang sama untuk semua orang.



Terlepas dari kenyataan bahwa kami menggunakan werf sebagai contoh implementasi, kami berharap pendekatan yang digunakan akan bermanfaat bagi tim lain yang menghadapi kesulitan serupa.



Jadi, kami mengambil yang eksternalimplementasi mekanisme untuk membersihkan gambar - alih-alih kemampuan yang sudah ada di dalam register untuk container. Langkah pertama adalah menggunakan Docker Registry API untuk membuat semua kebijakan primitif yang sama berdasarkan jumlah tag dan waktu pembuatannya (disebutkan di atas). Daftar yang diizinkan telah ditambahkan ke ini berdasarkan gambar yang digunakan dalam infrastruktur yang diterapkan , yaitu. Kubernetes. Untuk yang terakhir, cukup melalui semua sumber daya yang diterapkan melalui Kubernetes API dan mendapatkan daftar nilai image.



Solusi sepele ini menutup masalah yang paling kritis (kriteria # 1), tetapi ini baru permulaan dari perjalanan kami untuk meningkatkan mekanisme pembersihan. Langkah berikutnya - dan yang lebih menarik - adalah keputusan untuk mengaitkan gambar yang dipublikasikan dengan sejarah Git .



Skema pemberian tag



Untuk memulainya, kami memilih pendekatan di mana gambar akhir harus menyimpan informasi yang diperlukan untuk pembersihan, dan membangun proses pada skema penandaan. Saat memublikasikan gambar, pengguna memilih opsi pemberian tag tertentu ( git-branch, git-commitatau git-tag) dan menggunakan nilai yang sesuai. Dalam sistem CI, nilai-nilai ini ditetapkan secara otomatis berdasarkan variabel lingkungan. Pada dasarnya, gambar akhir dikaitkan dengan primitif Git tertentu , menyimpan data yang diperlukan untuk pembersihan dalam label.



Pendekatan ini menghasilkan serangkaian kebijakan yang memungkinkan Git digunakan sebagai satu-satunya sumber kebenaran:



  • Saat menghapus cabang / tag di Git, gambar terkait di registri juga otomatis dihapus.
  • Jumlah gambar yang diasosiasikan dengan tag dan komit Git dapat dikontrol oleh jumlah tag yang digunakan dalam skema yang dipilih dan waktu komit terkait dibuat.


Secara umum, implementasi yang dihasilkan memenuhi kebutuhan kami, tetapi segera tantangan baru menanti kami. Faktanya adalah bahwa selama penggunaan skema penandaan untuk primitif Git, kami menemukan sejumlah kekurangan. (Karena deskripsi mereka berada di luar cakupan artikel ini, siapa pun dapat membaca detailnya di sini .) Jadi, setelah memutuskan untuk beralih ke pendekatan penandaan yang lebih efisien (penandaan berbasis konten), kami harus merevisi penerapan pembersihan gambar.



Algoritme baru



Mengapa? Ketika ditandai sebagai berbasis konten, setiap tag dapat menampung banyak komit di Git. Saat membersihkan gambar, Anda tidak lagi hanya dapat mengandalkan komit di mana tag baru telah ditambahkan ke registri.



Untuk algoritme pembersihan baru, diputuskan untuk beralih dari skema penandaan dan membangun proses pada meta-gambar , yang masing-masing menyimpan banyak:



  • komit tempat publikasi dilakukan (tidak masalah jika gambar ditambahkan, diubah, atau tetap sama di registri penampung);
  • dan pengenal internal kami yang sesuai dengan gambar yang dibangun.


Dengan kata lain, tag yang diterbitkan ditautkan ke komit di Git .



Konfigurasi akhir dan algoritma umum



Saat mengonfigurasi pembersihan, pengguna sekarang memiliki akses ke kebijakan yang dengannya pemilihan gambar aktual dilakukan. Setiap kebijakan tersebut ditentukan:



  • banyak referensi, yaitu Tag Git atau cabang Git yang digunakan selama perayapan;
  • dan batas gambar yang diperlukan untuk setiap referensi dari set.


Sebagai ilustrasi, berikut adalah bagaimana konfigurasi kebijakan default mulai terlihat:



cleanup:
  keepPolicies:
  - references:
      tag: /.*/
      limit:
        last: 10
  - references:
      branch: /.*/
      limit:
        last: 10
        in: 168h
        operator: And
    imagesPerReference:
      last: 2
      in: 168h
      operator: And
  - references:  
      branch: /^(main|staging|production)$/
    imagesPerReference:
      last: 10


Konfigurasi ini berisi tiga kebijakan yang mematuhi aturan berikut:



  1. Simpan gambar untuk 10 tag Git terakhir (berdasarkan tanggal pembuatan tag).
  2. Simpan tidak lebih dari 2 gambar yang diterbitkan dalam seminggu terakhir, untuk tidak lebih dari 10 cabang dengan aktivitas selama seminggu terakhir.
  3. Simpan 10 gambar untuk setiap cabang main, stagingdan production.


Algoritma terakhir direduksi menjadi langkah-langkah berikut:



  • Mendapatkan manifes dari container registry.
  • Mengecualikan gambar yang digunakan di Kubernetes karena kami telah memilih mereka sebelumnya dengan melakukan polling pada K8s API.
  • Memindai riwayat Git dan mengecualikan gambar untuk kebijakan tertentu.
  • Menghapus gambar yang tersisa.


Kembali ke ilustrasi kami, inilah yang terjadi dengan werf:







Namun, meskipun Anda tidak menggunakan werf, pendekatan serupa untuk pembersihan gambar tingkat lanjut - dalam satu penerapan atau lainnya (sesuai dengan pendekatan yang disukai untuk menandai gambar) - dapat diterapkan di sistem lain juga. / keperluan. Untuk melakukan ini, cukup mengingat tentang masalah yang muncul dan menemukan peluang tersebut di tumpukan Anda yang memungkinkan Anda membangun solusi mereka dengan cara yang paling mulus. Kami berharap jalur yang telah kami tempuh akan membantu untuk melihat kasus khusus Anda dengan detail dan pemikiran baru.



Kesimpulan



  • Cepat atau lambat, sebagian besar tim menghadapi masalah kelebihan registri.
  • Saat mencari solusi, pertama-tama, perlu ditentukan kriteria relevansi gambar.
  • Alat yang ditawarkan oleh layanan registri kontainer populer memungkinkan pembersihan yang sangat sederhana yang tidak memperhitungkan "dunia luar": gambar yang digunakan di Kubernetes dan spesifikasi alur kerja tim.
  • Algoritme yang fleksibel dan efisien harus memiliki pemahaman tentang proses CI / CD, beroperasi tidak hanya dengan data gambar Docker.


PS



Baca juga di blog kami:






All Articles