3 tahun dengan Kubernetes dalam produksi: inilah yang kami dapatkan

Approx. terjemahan. : di artikel lain dari kategori "pelajaran yang didapat", insinyur DevOps dari perusahaan Australia membagikan kesimpulan utama dari penggunaan jangka panjang Kubernetes dalam produksi untuk layanan yang dimuat. Penulis mencakup Java, CI / CD, jaringan, dan kompleksitas K8 secara umum.



Kami mulai membuat cluster Kubernetes pertama kami pada tahun 2017 (dari versi K8s 1.9.4). Kami memiliki dua kelompok. Satu bekerja pada bare metal, pada mesin virtual RHEL, yang lainnya pada cloud AWS EC2.



Saat ini infrastruktur kami mencakup lebih dari 400 mesin virtual yang tersebar di beberapa pusat data. Platform ini berfungsi sebagai dasar untuk aplikasi dan sistem penting yang sangat tersedia yang menggerakkan jaringan besar yang terdiri dari hampir 4 juta perangkat aktif.



Pada akhirnya, Kubernetes membuat hidup kami lebih mudah, tetapi jalan menuju ini sulit dan membutuhkan perubahan paradigma yang menyeluruh. Telah terjadi transformasi total tidak hanya dalam seperangkat keterampilan dan alat, tetapi juga pendekatan desain dan pemikiran. Kami harus menguasai banyak teknologi baru dan berinvestasi besar-besaran dalam pengembangan infrastruktur dan pengembangan tim.



Berikut adalah pelajaran utama yang kami pelajari dari penggunaan Kubernetes dalam produksi selama tiga tahun.



1. Cerita menghibur dengan aplikasi Java



Dalam hal layanan mikro dan containerization, para insinyur cenderung menghindari Java, terutama karena manajemen memorinya yang terkenal tidak sempurna. Namun, saat ini situasinya berbeda dan kompatibilitas Java dengan container telah meningkat dalam beberapa tahun terakhir. Lagi pula, bahkan sistem populer seperti Apache Kafka dan Elasticsearch berjalan di Java.



Pada 2017-2018, beberapa aplikasi kami berjalan di Java versi 8. Mereka sering menolak untuk berfungsi di lingkungan dalam peti kemas seperti Docker dan mengalami error karena masalah dengan memori heap dan pekerjaan pengumpul sampah yang tidak memadai. Ternyata, masalah ini disebabkan oleh ketidakmampuan JVM menjalankan mekanisme containerization Linux ( cgroupsdan namespaces).



Sejak itu, Oracle telah melakukan upaya signifikan untuk meningkatkan kompatibilitas Java dengan dunia container. Pada Java versi 8, flag JVM eksperimental muncul untuk mengatasi masalah ini: XX:+UnlockExperimentalVMOptionsdan XX:+UseCGroupMemoryLimitForHeap.



Terlepas dari semua peningkatan, tidak ada yang akan membantah bahwa Java masih memiliki reputasi buruk karena konsumsi memori yang berlebihan dan startup yang lambat dibandingkan dengan Python atau pergi. Hal ini terutama disebabkan oleh spesifikasi manajemen memori di JVM dan ClassLoader.



Hari ini, jika kita harus bekerja dengan Java, setidaknya kita mencoba menggunakan versi 11 atau lebih tinggi. Dan batas memori kami di Kubernetes adalah 1 GB lebih tinggi dari batas memori heap maksimum di JVM (-Xmx) (untuk berjaga-jaga). Artinya, jika JVM menggunakan 8 GB untuk memori heap, batas memori Kubernetes untuk aplikasi tersebut akan disetel ke 9 GB. Berkat langkah-langkah dan peningkatan ini, hidup menjadi sedikit lebih mudah.



2. Pembaruan terkait dengan siklus hidup Kubernetes



Manajemen siklus hidup Kubernetes (pembaruan, penambahan) adalah hal yang rumit dan sulit, terutama jika cluster didasarkan pada bare metal atau mesin virtual . Ternyata untuk mengupgrade ke versi baru, jauh lebih mudah untuk meningkatkan cluster baru dan kemudian mentransfer beban kerja ke cluster tersebut. Memutakhirkan situs yang ada tidak praktis karena melibatkan banyak upaya dan perencanaan yang cermat.



Ini karena Kubernetes memiliki terlalu banyak bagian "bergerak" untuk dipertimbangkan saat melakukan upgrade. Agar cluster berfungsi, Anda harus mengumpulkan semua komponen ini - dari Docker ke plugin CNI seperti Calico atau Flannel. Proyek-proyek seperti Kubespray, KubeOne, kops, dan kube-aws agak menyederhanakan prosesnya, tetapi mereka bukannya tanpa kekurangan.



Kami menerapkan cluster kami di mesin virtual RHEL menggunakan Kubespray. Dia telah membuktikan dirinya luar biasa. Kubespray memiliki skrip untuk membuat, menambah atau menghapus node, memperbarui versi, dan hampir semua yang Anda butuhkan untuk bekerja dengan Kubernetes dalam produksi. Meskipun demikian, skrip peningkatan disertai dengan peringatan bahwa bahkan versi kecil tidak boleh dilewati. Dengan kata lain, untuk mendapatkan versi yang diinginkan, pengguna harus menginstal semua versi perantara.



Kesimpulan utama di sini adalah jika Anda berencana untuk menggunakan atau sudah menggunakan Kubernetes, pikirkan langkah-langkah siklus hidup K8 Anda dan bagaimana hal itu cocok dengan solusi Anda. Seringkali lebih mudah untuk membuat dan menjalankan cluster daripada memperbaruinya.



3. Bangun dan terapkan



Bersiaplah untuk fakta bahwa Anda harus merevisi pipeline build dan deployment. Dengan transisi ke Kubernetes, kami telah mengalami transformasi radikal dari proses-proses ini. Kami tidak hanya merestrukturisasi pipeline Jenkins, tetapi dengan bantuan alat seperti Helm, kami mengembangkan strategi baru untuk membangun dan bekerja dengan Git, menandai gambar Docker dan membuat versi bagan Helm.



Anda akan membutuhkan satu strategi untuk mempertahankan kode Anda, file penerapan Kubernetes, Dockerfiles, gambar Docker, diagram Helm, dan cara untuk mengikat semuanya.



Setelah beberapa iterasi, kami menyelesaikan diagram berikut:



  • Kode aplikasi dan bagan Helmnya terletak di repositori yang berbeda. Ini memungkinkan kita untuk membuat versi secara independen satu sama lain ( pembuatan versi semantik ).
  • , , . , , app-1.2.0 charts-1.1.0. (values) Helm, patch- (, 1.1.0 1.1.1). (RELEASE.txt) .
  • , Apache Kafka Redis ( ), . , Docker- Helm-. Docker- , .


(. .: Open Source- Kubernetes โ€” werf โ€” , .)



4. Liveness Readiness ( )



Pemeriksaan keaktifan dan kesiapan Kubernetes sangat bagus untuk menangani masalah sistem secara mandiri. Mereka dapat memulai ulang penampung jika terjadi kegagalan dan mengalihkan lalu lintas dari instance "tidak sehat". Namun dalam beberapa keadaan, pemeriksaan ini dapat berubah menjadi pedang bermata dua dan memengaruhi startup dan pemulihan aplikasi (ini terutama berlaku untuk aplikasi yang bersifat stateful seperti platform perpesanan atau database).



Kafka kami menjadi korban mereka. Kami memiliki set stateful dari 3 Brokerdan 3 Zookeeperdengan replicationFactor= 3 danminInSyncReplica= 2. Masalah terjadi saat memulai ulang Kafka setelah crash atau crash acak. Saat memulai, Kafka menjalankan skrip tambahan untuk memperbaiki indeks yang rusak, yang membutuhkan waktu 10 hingga 30 menit, bergantung pada tingkat keparahan masalahnya. Penundaan ini menyebabkan uji ketahanan terus gagal, menyebabkan Kubernetes "mematikan" dan memulai ulang Kafka. Akibatnya, Kafka tidak hanya bisa memperbaiki indeks, tetapi bahkan memulai.



Satu-satunya solusi pada saat itu adalah menyesuaikan parameter initialDelaySecondsdalam pengaturan uji keaktifan sehingga pemeriksaan dilakukan hanya setelah wadah diluncurkan. Tantangan terbesar, tentu saja, adalah memutuskan penundaan mana yang harus ditetapkan. Individu memulai setelah kegagalan dapat memakan waktu hingga satu jam, dan ini harus diperhitungkan. Di sisi lain, lebih banyakinitialDelaySeconds, Kubernetes yang lebih lambat akan merespons kegagalan selama penyalaan container.



Dalam hal ini, sweet spot adalah nilai initialDelaySecondsyang paling sesuai dengan persyaratan ketahanan Anda sambil tetap memberikan aplikasi cukup waktu untuk berhasil diluncurkan dalam semua situasi kegagalan (kegagalan disk, masalah jaringan, sistem crash, dll.)



Pembaruan : di versi terbaru Kubernetes, jenis pengujian ketiga telah muncul yang disebut probe startup. Ini tersedia sebagai versi alfa sejak rilis 1.16 , dan sebagai versi beta sejak 1.18.



Startup Probe memecahkan masalah di atas dengan menonaktifkan pemeriksaan kesiapan dan keaktifan hingga penampung mulai, sehingga memungkinkan aplikasi untuk memulai secara normal.


5. Bekerja dengan IP eksternal



Ternyata, menggunakan IP eksternal statis untuk mengakses layanan memberikan tekanan yang signifikan pada mekanisme pelacakan koneksi kernel. Jika Anda tidak memikirkannya dengan hati-hati, itu bisa "rusak".



Di cluster kami, kami menggunakan CalicoCNI dan BGPsebagai protokol perutean, serta untuk berinteraksi dengan router perbatasan. Mode Kube-proxy diaktifkan iptables. Kami membuka akses ke layanan kami yang sangat sibuk di Kubernetes (memproses jutaan koneksi setiap hari) melalui IP eksternal. Karena SNAT dan masking yang berasal dari jaringan yang ditentukan perangkat lunak, Kubernetes memerlukan mekanisme untuk melacak semua alur logis ini. Untuk K8 ini menggunakan alat inti ini sebagai onntrackdannetfilter... Dengan bantuan mereka, ia mengelola koneksi eksternal ke IP statis, yang kemudian diubah menjadi IP internal layanan dan terakhir ke alamat IP pod. Dan semua ini dilakukan dengan menggunakan tabel conntrackdan iptables.



Namun, kemungkinan tabel tidak conntrackterbatas. Ketika batas tercapai, cluster Kubernetes (lebih tepatnya, kernel OS pada intinya) tidak akan dapat lagi menerima koneksi baru. Di RHEL, batas ini dapat diperiksa sebagai berikut:



$  sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_maxnet.netfilter.nf_conntrack_count = 167012
net.netfilter.nf_conntrack_max = 262144


Salah satu cara untuk mengatasi batasan ini adalah dengan menggabungkan beberapa node dengan router edge sehingga koneksi yang masuk ke IP statis didistribusikan ke seluruh cluster. Jika Anda memiliki banyak armada mesin di cluster Anda, pendekatan ini dapat secara signifikan meningkatkan ukuran tabel conntrackuntuk menangani koneksi masuk dalam jumlah yang sangat besar.



Ini benar-benar membingungkan kami ketika kami pertama kali memulai pada tahun 2017. Namun, relatif baru-baru ini (pada April 2019) proyek Calico menerbitkan studi mendetail dengan judul yang tepat " Why conntrack bukan lagi teman Anda " (ada terjemahan semacam itu ke dalam bahasa Rusia - kira-kira. Terjemahan) .



Apakah Anda benar-benar membutuhkan Kubernetes?



Tiga tahun telah berlalu, namun kami masih terus menemukan / mempelajari sesuatu yang baru setiap hari. Kubernetes adalah platform yang kompleks dengan serangkaian tantangannya sendiri, terutama di bidang memulai lingkungan dan menjaganya tetap aktif dan berjalan. Ini akan mengubah pemikiran, arsitektur, sikap Anda terhadap desain. Anda harus berurusan dengan peningkatan dan peningkatan tim.



Di sisi lain, bekerja di cloud dan dapat menggunakan Kubernetes sebagai layanan akan menghemat sebagian besar kekhawatiran yang terkait dengan pemeliharaan platform (seperti memperluas CIDR jaringan internal dan memperbarui Kubernetes).



Hari ini kita telah memahami bahwa pertanyaan utama untuk ditanyakan pada diri kita sendiri sebenarnyaapakah Anda membutuhkan Kubernetes? Ini akan membantu Anda menilai seberapa global masalahnya dan apakah Kubernetes akan membantu Anda mengatasinya.



Masalahnya, pindah ke Kubernetes itu mahal. Jadi keuntungan dari kasus penggunaan Anda (dan seberapa banyak dan bagaimana hal itu memanfaatkan platform) harus membenarkan harga yang Anda bayarkan. Jika demikian, Kubernetes dapat meningkatkan produktivitas Anda secara signifikan.



Ingatlah bahwa teknologi demi teknologi tidak ada artinya.



PS dari penerjemah



Baca juga di blog kami:






All Articles