Had CPU dan pendikitan agresif dalam Kubernetes

Catatan. terjemah: Sejarah membuka mata Omioβ€”agregator perjalanan Eropahβ€”membawa pembaca daripada teori asas kepada selok-belok praktikal konfigurasi Kubernetes yang menarik. Kebiasaan dengan kes sedemikian membantu bukan sahaja meluaskan pandangan anda, tetapi juga menghalang masalah yang tidak remeh.

Had CPU dan pendikitan agresif dalam Kubernetes

Pernahkah anda mempunyai aplikasi tersekat di tempatnya, berhenti bertindak balas terhadap pemeriksaan kesihatan, dan tidak dapat mengetahui sebabnya? Satu penjelasan yang mungkin berkaitan dengan had kuota sumber CPU. Inilah yang akan kita bincangkan dalam artikel ini.

TL; DR:
Kami amat mengesyorkan agar anda melumpuhkan had CPU dalam Kubernetes (atau melumpuhkan kuota CFS dalam Kubelet) jika anda menggunakan versi kernel Linux dengan pepijat kuota CFS. Dalam inti boleh didapati serius dan terkenal pepijat yang membawa kepada pendikitan dan kelewatan yang berlebihan
.

Dalam Omio keseluruhan infrastruktur diuruskan oleh Kubernetes. Semua beban kerja stateful dan stateless kami dijalankan secara eksklusif pada Kubernetes (kami menggunakan Google Kubernetes Engine). Dalam enam bulan yang lalu, kami mula memerhatikan kelembapan rawak. Aplikasi membekukan atau berhenti bertindak balas terhadap pemeriksaan kesihatan, kehilangan sambungan ke rangkaian, dsb. Tingkah laku ini membingungkan kami untuk masa yang lama, dan akhirnya kami memutuskan untuk mengambil serius masalah itu.

Ringkasan artikel:

  • Beberapa perkataan tentang bekas dan Kubernetes;
  • Cara permintaan dan had CPU dilaksanakan;
  • Cara had CPU berfungsi dalam persekitaran berbilang teras;
  • Bagaimana untuk mengesan pendikit CPU;
  • Penyelesaian masalah dan nuansa.

Sedikit perkataan tentang bekas dan Kubernetes

Kubernetes pada asasnya ialah standard moden dalam dunia infrastruktur. Tugas utamanya ialah orkestrasi kontena.

Kontena

Pada masa lalu, kami perlu mencipta artifak seperti JAR/PERANG Java, Telur Python atau boleh laku untuk dijalankan pada pelayan. Walau bagaimanapun, untuk menjadikannya berfungsi, kerja tambahan perlu dilakukan: memasang persekitaran runtime (Java/Python), meletakkan fail yang diperlukan di tempat yang betul, memastikan keserasian dengan versi sistem pengendalian tertentu, dsb. Dalam erti kata lain, perhatian yang teliti perlu diberikan kepada pengurusan konfigurasi (yang selalunya menjadi punca perbalahan antara pembangun dan pentadbir sistem).

Bekas mengubah segala-galanya. Kini artifak itu adalah imej bekas. Ia boleh diwakili sebagai sejenis fail boleh laku lanjutan yang mengandungi bukan sahaja program, tetapi juga persekitaran pelaksanaan sepenuhnya (Java/Python/...), serta fail/pakej yang diperlukan, pra-pasang dan sedia untuk lari. Bekas boleh digunakan dan dijalankan pada pelayan yang berbeza tanpa sebarang langkah tambahan.

Di samping itu, bekas beroperasi dalam persekitaran kotak pasir mereka sendiri. Mereka mempunyai penyesuai rangkaian maya mereka sendiri, sistem fail mereka sendiri dengan akses terhad, hierarki proses mereka sendiri, batasan mereka sendiri pada CPU dan memori, dll. Semua ini dilaksanakan terima kasih kepada subsistem khas kernel Linux - ruang nama.

Kubernetes

Seperti yang dinyatakan sebelum ini, Kubernetes ialah pengatur kontena. Ia berfungsi seperti ini: anda memberikannya sekumpulan mesin, dan kemudian berkata: "Hei, Kubernetes, mari lancarkan sepuluh contoh bekas saya dengan 2 pemproses dan 3 GB memori setiap satu, dan pastikan ia berjalan!" Kubernetes akan menguruskan yang lain. Ia akan mencari kapasiti percuma, melancarkan bekas dan memulakannya semula jika perlu, melancarkan kemas kini apabila menukar versi, dsb. Pada asasnya, Kubernetes membolehkan anda mengabstrak komponen perkakasan dan membuat pelbagai jenis sistem sesuai untuk mengatur dan menjalankan aplikasi.

Had CPU dan pendikitan agresif dalam Kubernetes
Kubernetes dari sudut pandangan orang awam

Apakah permintaan dan had dalam Kubernetes

Okay, kami telah menutup bekas dan Kubernetes. Kami juga tahu bahawa beberapa bekas boleh berada pada mesin yang sama.

Analogi boleh dibuat dengan apartmen komunal. Sebuah premis (mesin/unit) yang luas diambil dan disewakan kepada beberapa penyewa (bekas). Kubernetes bertindak sebagai broker barang. Timbul persoalan, bagaimana untuk menjaga penyewa daripada konflik antara satu sama lain? Bagaimana jika salah seorang daripada mereka, katakan, memutuskan untuk meminjam bilik mandi selama separuh hari?

Di sinilah permintaan dan had dimainkan. CPU Meminta diperlukan semata-mata untuk tujuan perancangan. Ini adalah sesuatu seperti "senarai keinginan" bekas, dan ia digunakan untuk memilih nod yang paling sesuai. Pada masa yang sama CPU Hadkan boleh dibandingkan dengan perjanjian sewa - sebaik sahaja kami memilih unit untuk kontena, yang tak boleh melampaui batas yang ditetapkan. Dan di sinilah masalahnya timbul...

Cara permintaan dan had dilaksanakan dalam Kubernetes

Kubernetes menggunakan mekanisme pendikit (melangkau kitaran jam) yang dibina ke dalam kernel untuk melaksanakan had CPU. Jika aplikasi melebihi had, pendikitan didayakan (iaitu ia menerima lebih sedikit kitaran CPU). Permintaan dan had untuk ingatan disusun secara berbeza, supaya lebih mudah untuk dikesan. Untuk melakukan ini, hanya semak status mula semula terakhir pod: sama ada ia "OOMKilled". Pendikitan CPU tidak begitu mudah, kerana K8 hanya menyediakan metrik mengikut penggunaan, bukan oleh cgroup.

Permintaan CPU

Had CPU dan pendikitan agresif dalam Kubernetes
Cara permintaan CPU dilaksanakan

Untuk kesederhanaan, mari kita lihat proses menggunakan mesin dengan CPU 4-teras sebagai contoh.

K8s menggunakan mekanisme kumpulan kawalan (cgroups) untuk mengawal peruntukan sumber (memori dan pemproses). Model hierarki tersedia untuknya: kanak-kanak mewarisi had kumpulan induk. Butiran pengedaran disimpan dalam sistem fail maya (/sys/fs/cgroup). Dalam kes pemproses ini adalah /sys/fs/cgroup/cpu,cpuacct/*.

K8s menggunakan fail cpu.share untuk memperuntukkan sumber pemproses. Dalam kes kami, cgroup akar mendapat 4096 bahagian sumber CPU - 100% daripada kuasa pemproses yang tersedia (1 teras = 1024; ini adalah nilai tetap). Kumpulan akar mengagihkan sumber secara berkadar bergantung pada bahagian keturunan yang didaftarkan cpu.share, dan mereka, seterusnya, melakukan perkara yang sama dengan keturunan mereka, dsb. Pada nod Kubernetes biasa, cgroup akar mempunyai tiga anak: system.slice, user.slice ΠΈ kubepods. Dua subkumpulan pertama digunakan untuk mengagihkan sumber antara beban sistem kritikal dan program pengguna di luar K8. Yang terakhir - kubepods β€” dicipta oleh Kubernetes untuk mengagihkan sumber antara pod.

Rajah di atas menunjukkan bahawa subkumpulan pertama dan kedua menerima setiap satu 1024 saham, dengan subkumpulan kuberpod diperuntukkan 4096 saham Bagaimanakah ini mungkin: selepas semua, kumpulan akar mempunyai akses kepada sahaja 4096 saham, dan jumlah bahagian keturunannya dengan ketara melebihi jumlah ini (6144)? Intinya ialah nilai itu masuk akal, jadi penjadual Linux (CFS) menggunakannya untuk memperuntukkan sumber CPU secara berkadar. Dalam kes kami, dua kumpulan pertama menerima 680 saham sebenar (16,6% daripada 4096), dan kubepod menerima bakinya 2736 saham Dalam kes masa henti, dua kumpulan pertama tidak akan menggunakan sumber yang diperuntukkan.

Nasib baik, penjadual mempunyai mekanisme untuk mengelakkan pembaziran sumber CPU yang tidak digunakan. Ia memindahkan kapasiti "terbiar" ke kumpulan global, dari mana ia diedarkan kepada kumpulan yang memerlukan kuasa pemproses tambahan (pemindahan berlaku dalam kelompok untuk mengelakkan kerugian pembundaran). Kaedah yang sama digunakan untuk semua keturunan keturunan.

Mekanisme ini memastikan pengagihan kuasa pemproses yang adil dan memastikan tiada sesiapa yang memproses "mencuri" sumber daripada orang lain.

Had CPU

Walaupun fakta bahawa konfigurasi had dan permintaan dalam K8 kelihatan serupa, pelaksanaannya berbeza secara radikal: ini paling mengelirukan dan bahagian yang paling kurang didokumentasikan.

K8s terlibat mekanisme kuota CFS untuk melaksanakan had. Tetapan mereka dinyatakan dalam fail cfs_period_us ΠΈ cfs_quota_us dalam direktori cgroup (fail itu juga terletak di sana cpu.share).

Berbeza cpu.share, kuota adalah berdasarkan Jangka masa, dan bukan pada kuasa pemproses yang tersedia. cfs_period_us menentukan tempoh tempoh (zaman) - ia sentiasa 100000 ΞΌs (100 ms). Terdapat pilihan untuk menukar nilai ini dalam K8, tetapi ia hanya tersedia dalam alfa buat masa ini. Penjadual menggunakan zaman untuk memulakan semula kuota terpakai. Fail kedua cfs_quota_us, menentukan masa yang tersedia (kuota) dalam setiap zaman. Ambil perhatian bahawa ia juga dinyatakan dalam mikrosaat. Kuota mungkin melebihi panjang zaman; dalam erti kata lain, ia mungkin lebih besar daripada 100 ms.

Mari kita lihat dua senario pada mesin 16-teras (jenis komputer paling biasa yang kita ada di Omio):

Had CPU dan pendikitan agresif dalam Kubernetes
Senario 1: 2 utas dan had 200 ms. Tiada pendikit

Had CPU dan pendikitan agresif dalam Kubernetes
Senario 2: 10 utas dan had 200 ms. Pendikitan bermula selepas 20 ms, akses kepada sumber pemproses disambung semula selepas 80 ms lagi

Katakan anda menetapkan had CPU kepada 2 isirong; Kubernetes akan menterjemah nilai ini kepada 200 ms. Ini bermakna bekas boleh menggunakan maksimum 200ms masa CPU tanpa pendikit.

Dan di sinilah keseronokan bermula. Seperti yang dinyatakan di atas, kuota yang tersedia ialah 200 ms. Jika anda bekerja secara selari sepuluh benang pada mesin 12 teras (lihat ilustrasi untuk senario 2), sementara semua pod lain melahu, kuota akan habis dalam masa 20 ms sahaja (sejak 10 * 20 ms = 200 ms), dan semua utas pod ini akan digantung Β» (pendikit) untuk 80 ms seterusnya. Yang telah disebutkan pepijat penjadual, disebabkan pendikitan yang berlebihan berlaku dan bekas itu tidak dapat memenuhi kuota sedia ada.

Bagaimana untuk menilai pendikitan dalam pod?

Hanya log masuk ke pod dan laksanakan cat /sys/fs/cgroup/cpu/cpu.stat.

  • nr_periods β€” jumlah bilangan tempoh penjadual;
  • nr_throttled β€” bilangan tempoh pendikit dalam komposisi nr_periods;
  • throttled_time β€” masa pendikit terkumpul dalam nanosaat.

Had CPU dan pendikitan agresif dalam Kubernetes

Apa sebenarnya yang berlaku?

Akibatnya, kami mendapat pendikitan tinggi dalam semua aplikasi. Kadang-kadang dia masuk satu setengah kali lebih kuat daripada yang dikira!

Ini membawa kepada pelbagai ralat - kegagalan semakan kesediaan, bekas membeku, sambungan rangkaian putus, tamat masa dalam panggilan perkhidmatan. Ini akhirnya mengakibatkan peningkatan kependaman dan kadar ralat yang lebih tinggi.

Keputusan dan akibat

Semuanya mudah di sini. Kami meninggalkan had CPU dan mula mengemas kini kernel OS dalam kelompok kepada versi terkini, di mana pepijat telah diperbaiki. Bilangan ralat (HTTP 5xx) dalam perkhidmatan kami serta-merta menurun dengan ketara:

Ralat HTTP 5xx

Had CPU dan pendikitan agresif dalam Kubernetes
Ralat HTTP 5xx untuk satu perkhidmatan kritikal

Masa tindak balas p95

Had CPU dan pendikitan agresif dalam Kubernetes
Kependaman permintaan perkhidmatan kritikal, persentil ke-95

Kos operasi

Had CPU dan pendikitan agresif dalam Kubernetes
Bilangan jam contoh yang dibelanjakan

Apakah tangkapan?

Seperti yang dinyatakan di awal artikel:

Analogi boleh dibuat dengan apartmen komunal... Kubernetes bertindak sebagai broker barang. Tetapi bagaimana untuk menjaga penyewa daripada konflik antara satu sama lain? Bagaimana jika salah seorang daripada mereka, katakan, memutuskan untuk meminjam bilik mandi selama separuh hari?

Inilah hasil tangkapannya. Satu bekas cuai boleh memakan semua sumber CPU yang ada pada mesin. Jika anda mempunyai timbunan aplikasi pintar (contohnya, JVM, Go, Node VM dikonfigurasikan dengan betul), maka ini bukan masalah: anda boleh bekerja dalam keadaan sedemikian untuk masa yang lama. Tetapi jika aplikasi kurang dioptimumkan atau tidak dioptimumkan sama sekali (FROM java:latest), keadaan mungkin tidak terkawal. Di Omio kami mempunyai fail Docker asas automatik dengan tetapan lalai yang mencukupi untuk timbunan bahasa utama, jadi isu ini tidak wujud.

Kami mengesyorkan memantau metrik Cara Guna (penggunaan, ketepuan dan ralat), kelewatan API dan kadar ralat. Pastikan keputusan memenuhi jangkaan.

rujukan

Ini kisah kami. Bahan-bahan berikut sangat membantu untuk memahami apa yang berlaku:

Laporan pepijat Kubernetes:

Pernahkah anda menghadapi masalah yang sama dalam amalan anda atau mempunyai pengalaman berkaitan pendikitan dalam persekitaran pengeluaran kontena? Kongsi cerita anda dalam komen!

PS daripada penterjemah

Baca juga di blog kami:

Sumber: www.habr.com

Tambah komen