Mencuri: yang mencuri waktu prosesor dari mesin virtual

Mencuri: yang mencuri waktu prosesor dari mesin virtual

Halo! Saya ingin memberi tahu Anda secara sederhana tentang mekanisme pencurian di dalam mesin virtual dan tentang beberapa artefak tidak jelas yang berhasil kami temukan selama penelitiannya, yang harus saya selami sebagai direktur teknis platform cloud. Solusi Cloud Mail.ru. Platform ini berjalan pada KVM.

Waktu pencurian CPU adalah waktu di mana mesin virtual tidak menerima sumber daya prosesor untuk pelaksanaannya. Kali ini hanya dihitung dalam sistem operasi tamu di lingkungan virtualisasi. Alasan mengapa sebagian besar sumber daya yang dialokasikan ini digunakan, seperti dalam kehidupan, sangat tidak jelas. Namun kami memutuskan untuk mencari tahu, dan bahkan melakukan sejumlah eksperimen. Bukannya sekarang kami tahu segalanya tentang mencuri, tapi kami akan memberi tahu Anda sesuatu yang menarik sekarang.

1. Apa yang mencuri

Jadi, steal adalah metrik yang menunjukkan kurangnya waktu prosesor untuk proses di dalam mesin virtual. Seperti yang dijelaskan di patch kernel KVMStealth adalah waktu di mana hypervisor menjalankan proses lain di OS host meskipun hypervisor telah mengantri proses mesin virtual untuk dieksekusi. Artinya, mencuri dihitung sebagai perbedaan antara waktu ketika proses siap untuk dieksekusi dan waktu ketika proses tersebut dialokasikan waktu prosesor.

Kernel mesin virtual menerima metrik pencurian dari hypervisor. Pada saat yang sama, hypervisor tidak menentukan secara pasti proses lain apa yang sedang dijalankannya, ia hanya mengatakan β€œselagi saya sibuk, saya tidak bisa memberi Anda waktu.” Di KVM, dukungan untuk penghitungan pencurian telah ditambahkan tambalan. Ada dua poin penting di sini:

  • Mesin virtual mempelajari tentang pencurian dari hypervisor. Artinya, dari sudut pandang kerugian, untuk proses pada mesin virtual itu sendiri, ini merupakan pengukuran tidak langsung yang dapat mengalami berbagai distorsi.
  • Hypervisor tidak berbagi informasi dengan mesin virtual tentang apa lagi yang dilakukannya - yang utama adalah ia tidak mencurahkan waktu untuk itu. Oleh karena itu, mesin virtual itu sendiri tidak dapat mendeteksi distorsi pada indikator pencurian, yang dapat dinilai berdasarkan sifat proses yang bersaing.

2. Apa yang mempengaruhi pencurian

2.1. Mencuri perhitungan

Pada dasarnya, pencurian dihitung kira-kira sama dengan waktu penggunaan CPU normal. Tidak banyak informasi tentang bagaimana daur ulang dipertimbangkan. Mungkin karena kebanyakan orang menganggap pertanyaan ini sudah jelas. Namun ada juga kendala di sini. Untuk membiasakan diri dengan proses ini, Anda dapat membaca artikel oleh Brendan Gregg: Anda akan belajar banyak perbedaan saat menghitung pemanfaatan dan situasi di mana perhitungan ini salah karena alasan berikut:

  • Prosesor terlalu panas, menyebabkan siklus terlewati.
  • Mengaktifkan/menonaktifkan turbo boost, yang mengubah kecepatan jam prosesor.
  • Perubahan lamanya irisan waktu yang terjadi saat menggunakan teknologi hemat daya prosesor seperti SpeedStep.
  • Masalah dalam menghitung rata-rata: memperkirakan pemanfaatan satu menit sebesar 80% dapat menyembunyikan ledakan jangka pendek sebesar 100%.
  • Spin lock menyebabkan prosesor diambil kembali, namun proses pengguna tidak melihat kemajuan apa pun dalam eksekusinya. Hasilnya, pemanfaatan prosesor yang dihitung oleh proses tersebut akan menjadi seratus persen, meskipun proses tersebut tidak akan menghabiskan waktu prosesor secara fisik.

Saya belum menemukan artikel yang menjelaskan perhitungan serupa untuk mencuri (jika Anda tahu, bagikan di komentar). Tapi kalau dilihat dari source codenya, mekanisme perhitungannya sama dengan daur ulang. Sederhananya, penghitung lain ditambahkan di kernel, langsung untuk proses KVM (proses mesin virtual), yang menghitung durasi proses KVM menunggu waktu CPU. Penghitung mengambil informasi tentang prosesor dari spesifikasinya dan memeriksa apakah semua kutu digunakan oleh proses mesin virtual. Jika hanya itu, maka kita asumsikan prosesor hanya ditempati oleh proses mesin virtual. Jika tidak, kami informasikan bahwa prosesor sedang melakukan sesuatu yang lain, pencurian muncul.

Proses penghitungan pencurian mempunyai masalah yang sama seperti penghitungan daur ulang biasa. Bukan berarti masalah seperti itu sering muncul, namun terlihat mengecewakan.

2.2. Jenis virtualisasi pada KVM

Secara garis besar ada tiga jenis virtualisasi yang semuanya didukung oleh KVM. Mekanisme terjadinya pencurian mungkin bergantung pada jenis virtualisasi.

Siaran. Dalam hal ini, pengoperasian sistem operasi mesin virtual dengan perangkat hypervisor fisik terjadi seperti ini:

  1. Sistem operasi tamu mengirimkan perintah ke perangkat tamunya.
  2. Driver perangkat tamu menerima perintah, membuat permintaan untuk BIOS perangkat dan mengirimkannya ke hypervisor.
  3. Proses hypervisor menerjemahkan perintah ke perintah untuk perangkat fisik, menjadikannya lebih aman.
  4. Driver perangkat fisik menerima perintah yang dimodifikasi dan mengirimkannya ke perangkat fisik itu sendiri.
  5. Hasil dari menjalankan perintah kembali ke jalur yang sama.

Keuntungan terjemahan adalah memungkinkan Anda meniru perangkat apa pun dan tidak memerlukan persiapan khusus dari kernel sistem operasi. Namun Anda harus membayarnya, pertama-tama, dalam hal kecepatan.

Virtualisasi perangkat keras. Dalam hal ini, perangkat di tingkat perangkat keras memahami perintah dari sistem operasi. Ini adalah cara tercepat dan terbaik. Namun sayangnya, ini tidak didukung oleh semua perangkat fisik, hypervisor, dan sistem operasi tamu. Saat ini perangkat utama yang mendukung virtualisasi perangkat keras adalah prosesor.

Paravirtualisasi. Opsi paling umum untuk virtualisasi perangkat di KVM dan umumnya mode virtualisasi paling umum untuk sistem operasi tamu. Keunikannya adalah bekerja dengan beberapa subsistem hypervisor (misalnya, dengan jaringan atau tumpukan disk) atau alokasi halaman memori terjadi menggunakan API hypervisor, tanpa menerjemahkan perintah tingkat rendah. Kekurangan dari metode virtualisasi ini adalah kernel sistem operasi tamu harus dimodifikasi agar dapat berkomunikasi dengan hypervisor menggunakan API ini. Namun hal ini biasanya diatasi dengan menginstal driver khusus pada sistem operasi tamu. Di KVM API ini disebut API virtio.

Dengan paravirtualisasi, dibandingkan dengan penyiaran, jalur ke perangkat fisik dikurangi secara signifikan dengan mengirimkan perintah langsung dari mesin virtual ke proses hypervisor di host. Hal ini memungkinkan Anda untuk mempercepat eksekusi semua instruksi di dalam mesin virtual. Di KVM, hal ini dilakukan oleh virtio API, yang hanya berfungsi untuk perangkat tertentu, seperti adaptor jaringan atau disk. Inilah sebabnya mengapa driver virtio diinstal di dalam mesin virtual.

Kelemahan dari akselerasi ini adalah tidak semua proses yang berjalan di dalam mesin virtual tetap berada di dalamnya. Hal ini menciptakan beberapa efek khusus yang dapat mengakibatkan pemijahan saat mencuri. Saya sarankan memulai studi mendetail tentang masalah ini dengan API untuk I/O virtual: virtio.

2.3. Penjadwalan yang "adil".

Mesin virtual pada hypervisor sebenarnya adalah proses biasa yang mematuhi hukum penjadwalan (distribusi sumber daya antar proses) di kernel Linux, jadi mari kita lihat lebih dekat.

Linux menggunakan apa yang disebut CFS, Completely Fair Scheduler, yang telah menjadi penjadwal default sejak kernel 2.6.23. Untuk memahami algoritma ini, Anda dapat membaca Arsitektur Kernel Linux atau kode sumbernya. Inti dari CFS adalah mendistribusikan waktu prosesor antar proses tergantung pada durasi eksekusinya. Semakin banyak waktu CPU yang dibutuhkan suatu proses, semakin sedikit waktu CPU yang diterimanya. Hal ini memastikan bahwa semua proses dijalankan secara "adil" - sehingga satu proses tidak terus-menerus menempati semua prosesor, dan proses lain juga dapat dijalankan.

Terkadang paradigma ini menghasilkan artefak yang menarik. Pengguna lama Linux mungkin ingat pembekuan editor teks biasa di desktop saat menjalankan aplikasi intensif sumber daya seperti kompiler. Hal ini terjadi karena tugas-tugas non-intensif sumber daya dalam aplikasi desktop bersaing dengan tugas-tugas intensif sumber daya, seperti compiler. CFS menganggap ini tidak adil, sehingga secara berkala menghentikan editor teks dan membiarkan prosesor menangani tugas kompiler. Ini diperbaiki menggunakan mekanisme jadwal_autogroup, tetapi banyak fitur lain dari distribusi waktu prosesor antar tugas tetap ada. Sebenarnya, ini bukan cerita tentang betapa buruknya segala sesuatu di CFS, tetapi upaya untuk menarik perhatian pada fakta bahwa distribusi waktu prosesor yang β€œadil” bukanlah tugas yang paling sepele.

Poin penting lainnya dalam penjadwal adalah preemption. Hal ini diperlukan untuk menghilangkan proses tertawa-tawa dari prosesor dan membiarkan orang lain bekerja. Proses ejeksi disebut peralihan konteks. Dalam hal ini, seluruh konteks tugas dipertahankan: status tumpukan, register, dll., setelah itu proses dikirim untuk menunggu, dan proses lain menggantikannya. Ini adalah operasi yang mahal untuk OS dan jarang digunakan, tetapi tidak ada yang salah dengan itu. Peralihan konteks yang sering mungkin mengindikasikan adanya masalah pada OS, namun biasanya terjadi terus-menerus dan tidak menunjukkan sesuatu yang khusus.

Cerita yang begitu panjang diperlukan untuk menjelaskan satu fakta: semakin banyak sumber daya prosesor yang coba digunakan suatu proses dalam penjadwal Linux yang jujur, semakin cepat proses tersebut akan dihentikan sehingga proses lain juga dapat bekerja. Apakah ini benar atau tidak adalah pertanyaan kompleks yang dapat diselesaikan secara berbeda pada beban yang berbeda. Di Windows, hingga saat ini, penjadwal difokuskan pada pemrosesan prioritas aplikasi desktop, yang dapat menyebabkan terhentinya proses latar belakang. Sun Solaris memiliki lima kelas penjadwal yang berbeda. Saat kami meluncurkan virtualisasi, kami menambahkan yang keenam, Penjadwal pembagian yang adil, karena lima sebelumnya tidak bekerja secara memadai dengan virtualisasi Solaris Zones. Saya sarankan memulai studi mendetail tentang masalah ini dengan buku-buku seperti Solaris Internal: Arsitektur Kernel Solaris 10 dan OpenSolaris ΠΈΠ»ΠΈ Memahami Kernel Linux.

2.4. Bagaimana cara memantau pencurian?

Memantau pencurian di dalam mesin virtual, seperti metrik prosesor lainnya, sangatlah sederhana: Anda dapat menggunakan alat metrik prosesor apa pun. Yang utama adalah mesin virtualnya ada di Linux. Untuk beberapa alasan Windows tidak memberikan informasi ini kepada penggunanya. πŸ™

Mencuri: yang mencuri waktu prosesor dari mesin virtual
Output dari perintah teratas: rincian beban prosesor, di kolom paling kanan - mencuri

Kesulitan muncul ketika mencoba memperoleh informasi ini dari hypervisor. Anda dapat mencoba memprediksi pencurian pada mesin host, misalnya menggunakan parameter Load Average (LA) - nilai rata-rata jumlah proses yang menunggu dalam antrian eksekusi. Cara menghitung parameter ini tidak sederhana, namun secara umum jika LA dinormalisasi dengan jumlah thread prosesor lebih dari 1, ini menandakan bahwa server Linux kelebihan beban dengan sesuatu.

Apa yang menunggu dari semua proses ini? Jawaban yang jelas adalah prosesornya. Namun jawabannya tidak sepenuhnya benar, karena terkadang prosesornya gratis, tetapi LA keluar skala. Ingat bagaimana NFS menurun dan bagaimana LA tumbuh. Hal yang sama dapat terjadi pada disk dan perangkat input/output lainnya. Namun pada kenyataannya, proses dapat menunggu akhir dari kunci apa pun, baik fisik, yang terkait dengan perangkat I/O, atau logis, seperti mutex. Ini juga termasuk penguncian pada tingkat perangkat keras (respon yang sama dari disk), atau logika (yang disebut penguncian primitif, yang mencakup sekumpulan entitas, mutex adaptif dan putaran, semafor, variabel kondisi, kunci rw, kunci ipc ...).

Fitur lain dari LA adalah dianggap sebagai sistem operasi rata-rata. Misalnya, 100 proses bersaing untuk satu file, dan kemudian LA=50. Nilai yang begitu besar sepertinya menandakan bahwa sistem operasi tersebut buruk. Tetapi untuk kode lain yang ditulis tidak benar, ini mungkin merupakan keadaan normal, meskipun faktanya hanya itu yang buruk, dan proses lain dalam sistem operasi tidak terganggu.

Karena rata-rata ini (dan dalam waktu tidak kurang dari satu menit), menentukan apa pun dengan indikator LA bukanlah tugas yang paling bermanfaat, dengan hasil yang sangat tidak pasti dalam kasus-kasus tertentu. Jika Anda mencoba mencari tahu, Anda akan menemukan bahwa artikel di Wikipedia dan sumber lain yang tersedia hanya menjelaskan kasus paling sederhana, tanpa penjelasan mendalam tentang prosesnya. Saya mengirim semua orang yang tertarik, sekali lagi, di sini untuk Brendan Gregg  - ikuti tautan di bawah ini. Siapa yang terlalu malas untuk berbicara bahasa Inggris - terjemahan artikel populernya tentang LA.

3. Efek khusus

Sekarang mari kita lihat kasus-kasus utama pencurian yang kita temui. Saya akan memberi tahu Anda bagaimana hal tersebut mengikuti semua hal di atas dan bagaimana kaitannya dengan indikator di hypervisor.

Mendaur ulang. Yang paling sederhana dan umum: hypervisor telah digunakan kembali. Memang banyak sekali mesin virtual yang berjalan, konsumsi prosesor yang tinggi di dalamnya, banyak persaingan, pemanfaatan LA lebih dari 1 (dinormalisasi dengan thread prosesor). Segala sesuatu di dalam semua mesin virtual melambat. Pencurian yang ditransmisikan dari hypervisor juga semakin meningkat, maka perlu mendistribusikan ulang beban atau mematikan seseorang. Secara umum, semuanya logis dan dapat dimengerti.

Paravirtualisasi vs. Instans Tunggal. Hanya ada satu mesin virtual pada hypervisor; ia menggunakan sebagian kecil mesin tersebut, namun menghasilkan beban I/O yang besar, misalnya pada disk. Dan entah dari mana, sedikit pencurian muncul di dalamnya, hingga 10% (seperti yang ditunjukkan oleh beberapa percobaan).

Kasus ini menarik. Pencurian muncul di sini justru karena pemblokiran pada tingkat pengemudi yang diparavirtualisasi. Interupsi dibuat di dalam mesin virtual, diproses oleh driver dan dikirim ke hypervisor. Karena adanya penanganan interupsi pada hypervisor, untuk mesin virtual terlihat seperti permintaan terkirim, siap dieksekusi dan menunggu prosesor, namun tidak diberikan waktu prosesor. Gadis virtual itu mengira kali ini telah dicuri.

Ini terjadi pada saat buffer dikirim, ia masuk ke ruang kernel hypervisor, dan kami mulai menunggunya. Meskipun, dari sudut pandang mesin virtual, ia harus segera kembali. Oleh karena itu, menurut algoritma perhitungan mencuri, kali ini dianggap dicuri. Kemungkinan besar, dalam situasi ini mungkin terdapat mekanisme lain (misalnya, memproses beberapa panggilan sistem lainnya), namun mekanisme tersebut tidak akan jauh berbeda.

Penjadwal versus mesin virtual dengan muatan tinggi. Ketika satu mesin virtual mengalami pencurian lebih sering daripada yang lain, hal ini disebabkan oleh penjadwal. Semakin banyak suatu proses memuat prosesor, semakin cepat penjadwal akan mengeluarkannya sehingga proses lain juga dapat bekerja. Jika mesin virtual mengkonsumsi sedikit, ia tidak akan terlihat mencuri: prosesnya sejujurnya hanya menunggu dan menunggu, kita perlu memberinya lebih banyak waktu. Jika mesin virtual menghasilkan beban maksimum pada semua intinya, sering kali mesin tersebut dikeluarkan dari prosesor dan mereka berusaha untuk tidak memberikan banyak waktu.

Lebih buruk lagi ketika proses di dalam mesin virtual mencoba untuk mendapatkan lebih banyak prosesor karena mereka tidak dapat mengatasi pemrosesan data. Kemudian sistem operasi pada hypervisor, karena optimasi yang jujur, akan memberikan waktu prosesor yang semakin sedikit. Proses ini terjadi seperti longsoran salju, dan mencuri lompatan ke langit, meskipun mesin virtual lain mungkin tidak menyadarinya. Dan semakin banyak core, semakin buruk mesin yang terkena dampaknya. Singkatnya, mesin virtual dengan muatan tinggi dengan banyak inti paling menderita.

LA rendah, tapi ada yang mencuri. Jika LA kira-kira 0,7 (artinya, hypervisor tampaknya kekurangan muatan), tetapi pencurian terjadi di dalam masing-masing mesin virtual:

  • Opsi dengan paravirtualisasi sudah dijelaskan di atas. Mesin virtual dapat menerima metrik yang mengindikasikan pencurian, meskipun hypervisornya baik-baik saja. Berdasarkan hasil percobaan kami, opsi pencurian ini tidak melebihi 10% dan seharusnya tidak berdampak signifikan terhadap kinerja aplikasi di dalam mesin virtual.
  • Parameter LA dihitung secara tidak benar. Lebih tepatnya, pada setiap momen tertentu dihitung dengan benar, tetapi jika dirata-ratakan selama satu menit ternyata diremehkan. Misalnya, jika satu mesin virtual per sepertiga hypervisor menggunakan semua prosesornya tepat setengah menit, maka LA per menit pada hypervisor akan menjadi 0,15; empat mesin virtual yang bekerja secara bersamaan akan menghasilkan 0,6. Dan fakta bahwa selama setengah menit pada masing-masingnya terjadi pencurian liar sebesar 25% menurut indikator LA tidak dapat lagi ditarik keluar.
  • Sekali lagi, karena penjadwal yang memutuskan bahwa seseorang makan terlalu banyak dan membiarkan seseorang menunggu. Sementara itu, saya akan mengubah konteksnya, menangani interupsi, dan mengurus hal-hal penting lainnya dalam sistem. Akibatnya, beberapa mesin virtual tidak mengalami masalah apa pun, sementara yang lain mengalami penurunan kinerja yang serius.

4. Distorsi lainnya

Ada sejuta alasan lain yang mendistorsi pengembalian waktu prosesor yang adil pada mesin virtual. Misalnya, hyperthreading dan NUMA menimbulkan kesulitan dalam penghitungan. Mereka benar-benar membingungkan pilihan kernel untuk menjalankan proses, karena penjadwal menggunakan koefisien - bobot, yang membuat penghitungan menjadi lebih sulit saat mengganti konteks.

Ada distorsi karena teknologi seperti turbo boost atau, sebaliknya, mode hemat energi, yang, ketika menghitung pemanfaatan, dapat secara artifisial menambah atau mengurangi frekuensi atau bahkan pembagian waktu di server. Mengaktifkan turbo boost akan mengurangi kinerja satu thread prosesor karena peningkatan kinerja thread lainnya. Pada saat ini, informasi tentang frekuensi prosesor saat ini tidak dikirimkan ke mesin virtual, dan diyakini bahwa seseorang mencuri waktunya (misalnya, ia meminta 2 GHz, tetapi menerima setengahnya).

Secara umum, ada banyak penyebab terjadinya distorsi. Anda mungkin menemukan sesuatu yang lain pada sistem tertentu. Lebih baik memulai dengan buku-buku yang saya berikan tautannya di atas, dan mengambil statistik dari hypervisor menggunakan utilitas seperti perf, sysdig, systemtap, di antaranya puluhan.

5. Kesimpulan

  1. Pencurian dalam jumlah tertentu mungkin terjadi karena paravirtualisasi, dan ini dapat dianggap normal. Mereka menulis di Internet bahwa nilainya bisa 5-10%. Tergantung pada aplikasi di dalam mesin virtual dan beban yang diberikan pada perangkat fisiknya. Di sini penting untuk memperhatikan bagaimana aplikasi terasa di dalam mesin virtual.
  2. Rasio beban pada hypervisor dan pencurian di dalam mesin virtual tidak selalu saling terkait secara jelas; kedua perkiraan pencurian bisa salah dalam situasi tertentu dengan beban berbeda.
  3. Penjadwal memiliki sikap buruk terhadap proses yang banyak meminta. Dia mencoba memberi lebih sedikit kepada mereka yang meminta lebih. Mesin virtual besar itu jahat.
  4. Sedikit pencurian bisa menjadi hal yang biasa bahkan tanpa paravirtualisasi (dengan mempertimbangkan beban di dalam mesin virtual, karakteristik beban tetangga, distribusi beban di seluruh thread, dan faktor lainnya).
  5. Jika Anda ingin mengetahui pencurian dalam sistem tertentu, Anda harus menjelajahi berbagai opsi, mengumpulkan metrik, menganalisisnya dengan cermat, dan memikirkan cara mendistribusikan beban secara merata. Penyimpangan dari kasus apa pun mungkin terjadi, yang harus dikonfirmasi secara eksperimental atau dilihat di debugger kernel.

Sumber: www.habr.com

Tambah komentar