OOMkiller di Docker lebih sulit daripada yang Anda pikirkan

Halo lagi. Sebagai antisipasi dimulainya kursus, "Pengembang Java" menyiapkan terjemahan materi kecil lainnya.




Baru-baru ini, salah satu pengguna APM Plumbr memiliki masalah aneh dengan menabrak kontainer buruh pelabuhan dengan kode 137. Konfigurasi sederhana dengan beberapa wadah bersarang dan mesin virtual, mirip dengan boneka bersarang:

  • server besi sendiri dengan Ubuntu;
  • banyak wadah buruh pelabuhan dengan Ubuntu di dalamnya;
  • Java Virtual Machine di dalam wadah buruh pelabuhan.

Selama penyelidikan masalah, kami menemukan dokumentasi Docker tentang topik ini. Menjadi jelas bahwa alasannya adalah penghentian manual wadah atau kurangnya memori dan intervensi oomkiller (Pembunuh Memori Keluar).

Kami melihat syslog dan melihat bahwa, memang, oomkiller dipanggil :

[138805.608851] java invoked oom-killer: gfp_mask=0xd0, order=0, oom_score_adj=0
[138805.608887] [<ffffffff8116d20e>] oom_kill_process+0x24e/0x3b0
[138805.608916] Task in /docker/264b771811d88f4dbd3249a54261f224a69ebffed6c7f76c7aa3bc83b3aaaf71 killed as a result of limit of /docker/264b771811d88f4dbd3249a54261f224a69ebffed6c7f76c7aa3bc83b3aaaf71
[138805.608902] [<ffffffff8116da84>] pagefault_out_of_memory+0x14/0x90
[138805.608918] memory: usage 3140120kB, limit 3145728kB, failcnt 616038
[138805.608940] memory+swap: usage 6291456kB, limit 6291456kB, failcnt 2837
[138805.609043] Memory cgroup out of memory: Kill process 20611 (java) score 1068 or sacrifice child

Seperti yang Anda lihat, proses java mencapai batas 3145728 KB (ini sekitar 3 GB), yang menyebabkan wadah berhenti. Ini agak aneh, karena buruh pelabuhan itu sendiri diluncurkan dengan batas 4 GB (dalam file docker-compose).

Anda mungkin tahu bahwa JVM juga memaksakan batasannya pada penggunaan memori. Meskipun buruh pelabuhan itu sendiri diatur ke batas 4 GB, JVM diluncurkan dengan opsi Xmx=3GB. Ini bisa jadi lebih membingungkan, tetapi perlu diingat bahwa JVM dapat menggunakan lebih banyak memori daripada yang ditunjukkan -Xmx (lihat artikel tentang menganalisis penggunaan memori di JVM ).

Sampai kita mengerti apa yang terjadi. Docker harus memungkinkan penggunaan 4 GB. Jadi mengapa OOMkiller bekerja pada 3 GB? Pencarian lebih lanjut untuk informasi membawa kami pada fakta bahwa ada batasan lain memori di OS, yang digunakan pada perangkat keras.

Ucapkan terima kasih kepada cgroups (kelompok kontrol). cgroups adalah mesin kernel Linux untuk membatasi, mengendalikan, dan menghitung penggunaan sumber daya oleh kelompok proses. Dibandingkan dengan solusi lain (tim niceatau /etc/security/limits.conf), cgroup menawarkan lebih banyak fleksibilitas karena mereka dapat bekerja dengan (di bawah) serangkaian proses.

Dalam situasi kami, cgroup membatasi penggunaan memori hingga 3 GB (via memory.limit_in_bytes). Kami memiliki beberapa kemajuan!

Sebuah studi tentang memori GC dan peristiwa menggunakan Plumbr menunjukkan bahwa sebagian besar waktu JVM digunakan sekitar 700 MB. Pengecualian hanya tepat sebelum berhenti, ketika ada lonjakan alokasi memori. Dia diikuti oleh jeda panjang GC. Jadi sepertinya yang berikut terjadi:

  • Kode Java yang berjalan di dalam JVM berusaha mendapatkan banyak memori.
  • JVM, setelah memverifikasi bahwa batas Xmx 3 GB masih jauh, meminta untuk mengalokasikan memori untuk sistem operasi.
  • Docker juga memeriksa dan melihat bahwa batas 4 GB-nya belum tercapai.
  • Kernel OS memeriksa batas cgroup 3 GB dan membunuh kontainer.
  • JVM berhenti dengan wadah sebelum dapat memprosesnya sendiri OutOfMemoryError.

Memahami hal ini, kami telah mengonfigurasi semua batasan 2,5 GB untuk buruh pelabuhan dan 1,5 untuk java. Setelah itu, JVM dapat menangani OutOfMemoryErrordan melempar pengecualian OutOfMemoryError. Ini memungkinkan Plumbr untuk melakukan keajaibannya - untuk mendapatkan snapshot memori dengan dump dump yang sesuai dan menunjukkan bahwa ada satu permintaan ke database, yang dalam situasi tertentu mencoba memuat hampir seluruh database.

temuan


Bahkan dalam situasi yang begitu sederhana, ada tiga keterbatasan memori:

  • JVM via parameter -Xmx
  • Docker melalui opsi dalam file docker-compose
  • Sistem operasi melalui parameter memory.limit_in_bytes cgroups

Jadi, ketika Anda menemukan pembunuh OOM, Anda harus memperhatikan semua keterbatasan memori yang terlibat.

Kesimpulan lain untuk pengembang buruh pelabuhan. Tampaknya tidak masuk akal untuk membiarkan "boneka bersarang" seperti itu berjalan di mana batas memori wadah tertutup lebih tinggi dari batas kelompok cgroup. Pemeriksaan sederhana ini ketika memulai wadah dengan peringatan yang tepat dapat menghemat ratusan jam debugging untuk pengguna Anda.

Itu saja. Kami menunggu Anda di jalur .

All Articles