Petua & kiat Kubernetes: ciri penutupan anggun dalam NGINX dan PHP-FPM

Keadaan biasa apabila melaksanakan CI/CD dalam Kubernetes: aplikasi mesti boleh tidak menerima permintaan pelanggan baharu sebelum berhenti sepenuhnya, dan yang paling penting, berjaya menyelesaikan permintaan sedia ada.

Petua & kiat Kubernetes: ciri penutupan anggun dalam NGINX dan PHP-FPM

Pematuhan syarat ini membolehkan anda mencapai masa henti sifar semasa penggunaan. Walau bagaimanapun, walaupun semasa menggunakan berkas yang sangat popular (seperti NGINX dan PHP-FPM), anda boleh menghadapi kesukaran yang akan membawa kepada lonjakan ralat dengan setiap penggunaan...

Teori. Bagaimana pod hidup

Kami telah pun menerbitkan secara terperinci tentang kitaran hayat pod artikel ini. Dalam konteks topik yang sedang dipertimbangkan, kami berminat dengan perkara berikut: pada masa pod memasuki keadaan Mengakhiri, permintaan baharu berhenti dihantar kepadanya (pod dikeluarkan daripada senarai titik akhir untuk perkhidmatan). Oleh itu, untuk mengelakkan masa henti semasa penggunaan, sudah cukup untuk kami menyelesaikan masalah menghentikan aplikasi dengan betul.

Anda juga harus ingat bahawa tempoh tangguh lalai ialah 30 saat: selepas ini, pod akan ditamatkan dan permohonan mesti mempunyai masa untuk memproses semua permintaan sebelum tempoh ini. Nota: walaupun sebarang permintaan yang mengambil masa lebih daripada 5-10 saat sudah bermasalah, dan penutupan anggun tidak lagi membantu...

Untuk lebih memahami perkara yang berlaku apabila pod ditamatkan, lihat sahaja rajah berikut:

Petua & kiat Kubernetes: ciri penutupan anggun dalam NGINX dan PHP-FPM

A1, B1 - Menerima perubahan tentang keadaan perapian
A2 - Berlepas SIGTERM
B2 - Mengalih keluar pod dari titik akhir
B3 - Menerima perubahan (senarai titik akhir telah berubah)
B4 - Kemas kini peraturan iptables

Sila ambil perhatian: memadam pod titik akhir dan menghantar SIGTERM tidak berlaku secara berurutan, tetapi selari. Dan disebabkan fakta bahawa Ingress tidak segera menerima senarai Endpoint yang dikemas kini, permintaan baharu daripada pelanggan akan dihantar ke pod, yang akan menyebabkan ralat 500 semasa penamatan pod (untuk bahan yang lebih terperinci mengenai isu ini, kami diterjemahkan). Masalah ini perlu diselesaikan dengan cara berikut:

  • Hantar Sambungan: tutup dalam pengepala respons (jika ini melibatkan aplikasi HTTP).
  • Jika tidak mungkin untuk membuat perubahan pada kod, maka artikel berikut menerangkan penyelesaian yang akan membolehkan anda memproses permintaan sehingga tamat tempoh tangguh.

Teori. Bagaimana NGINX dan PHP-FPM menamatkan proses mereka

Nginx

Mari kita mulakan dengan NGINX, kerana segala-galanya lebih kurang jelas dengannya. Menyelam ke dalam teori, kami mengetahui bahawa NGINX mempunyai satu proses induk dan beberapa "pekerja" - ini adalah proses kanak-kanak yang memproses permintaan pelanggan. Pilihan yang mudah disediakan: menggunakan arahan nginx -s <SIGNAL> menamatkan proses sama ada dalam mod penutupan pantas atau penutupan anggun. Jelas sekali, pilihan terakhir yang menarik minat kami.

Kemudian semuanya mudah: anda perlu menambah cangkuk praStop arahan yang akan menghantar isyarat penutupan yang anggun. Ini boleh dilakukan dalam Deployment, dalam blok kontena:

       lifecycle:
          preStop:
            exec:
              command:
              - /usr/sbin/nginx
              - -s
              - quit

Sekarang, apabila pod ditutup, kita akan melihat perkara berikut dalam log bekas NGINX:

2018/01/25 13:58:31 [notice] 1#1: signal 3 (SIGQUIT) received, shutting down
2018/01/25 13:58:31 [notice] 11#11: gracefully shutting down

Dan ini bermakna apa yang kita perlukan: NGINX menunggu permintaan untuk diselesaikan, dan kemudian mematikan proses itu. Walau bagaimanapun, di bawah kami juga akan mempertimbangkan masalah biasa yang disebabkan olehnya, walaupun dengan arahan nginx -s quit proses tamat dengan tidak betul.

Dan pada peringkat ini kita selesai dengan NGINX: sekurang-kurangnya dari log anda boleh memahami bahawa semuanya berfungsi sebagaimana mestinya.

Apakah urusan dengan PHP-FPM? Bagaimanakah ia mengendalikan penutupan yang anggun? Mari kita fikirkan.

PHP-FPM

Dalam kes PHP-FPM, terdapat sedikit maklumat yang kurang. Jika anda fokus pada manual rasmi menurut PHP-FPM, ia akan mengatakan bahawa isyarat POSIX berikut diterima:

  1. SIGINT, SIGTERM - penutupan cepat;
  2. SIGQUIT - penutupan anggun (apa yang kita perlukan).

Isyarat yang selebihnya tidak diperlukan dalam tugas ini, jadi kami akan meninggalkan analisisnya. Untuk menamatkan proses dengan betul, anda perlu menulis cangkuk praStop berikut:

        lifecycle:
          preStop:
            exec:
              command:
              - /bin/kill
              - -SIGQUIT
              - "1"

Pada pandangan pertama, ini sahaja yang diperlukan untuk melakukan penutupan yang anggun dalam kedua-dua bekas. Walau bagaimanapun, tugas itu lebih sukar daripada yang kelihatan. Di bawah ialah dua kes di mana penutupan anggun tidak berfungsi dan menyebabkan ketiadaan jangka pendek projek semasa penggunaan.

berlatih. Kemungkinan masalah dengan penutupan yang anggun

Nginx

Pertama sekali, adalah berguna untuk diingat: sebagai tambahan kepada melaksanakan arahan nginx -s quit Ada satu lagi peringkat yang patut diberi perhatian. Kami menghadapi isu di mana NGINX masih akan menghantar SIGTERM dan bukannya isyarat SIGQUIT, menyebabkan permintaan tidak selesai dengan betul. Kes yang serupa boleh didapati, contohnya, di sini. Malangnya, kami tidak dapat menentukan sebab khusus untuk kelakuan ini: terdapat syak wasangka tentang versi NGINX, tetapi ia tidak disahkan. Gejalanya ialah mesej diperhatikan dalam log kontena NGINX: "soket terbuka #10 tinggal di sambungan 5", selepas itu pod berhenti.

Kita boleh melihat masalah sedemikian, sebagai contoh, daripada respons pada Ingress yang kita perlukan:

Petua & kiat Kubernetes: ciri penutupan anggun dalam NGINX dan PHP-FPM
Penunjuk kod status pada masa penggunaan

Dalam kes ini, kami hanya menerima kod ralat 503 daripada Ingress sendiri: ia tidak boleh mengakses bekas NGINX, kerana ia tidak lagi boleh diakses. Jika anda melihat log kontena dengan NGINX, ia mengandungi perkara berikut:

[alert] 13939#0: *154 open socket #3 left in connection 16
[alert] 13939#0: *168 open socket #6 left in connection 13

Selepas menukar isyarat berhenti, bekas mula berhenti dengan betul: ini disahkan oleh fakta bahawa ralat 503 tidak lagi diperhatikan.

Jika anda menghadapi masalah yang sama, adalah wajar untuk mengetahui isyarat henti yang digunakan dalam bekas dan bagaimana rupa cangkuk preStop. Ia agak mungkin bahawa sebabnya terletak tepat pada perkara ini.

PHP-FPM... dan banyak lagi

Masalah dengan PHP-FPM diterangkan dengan cara yang remeh: ia tidak menunggu proses anak selesai, ia menamatkannya, itulah sebabnya ralat 502 berlaku semasa penggunaan dan operasi lain. Terdapat beberapa laporan pepijat pada bugs.php.net sejak 2005 (cth di sini ΠΈ di sini), yang menerangkan masalah ini. Tetapi anda berkemungkinan besar tidak akan melihat apa-apa dalam log: PHP-FPM akan mengumumkan penyiapan prosesnya tanpa sebarang ralat atau pemberitahuan pihak ketiga.

Perlu dijelaskan bahawa masalah itu sendiri mungkin bergantung pada tahap yang lebih kecil atau lebih besar pada aplikasi itu sendiri dan mungkin tidak nyata, sebagai contoh, dalam pemantauan. Jika anda menemuinya, penyelesaian mudah terlintas di fikiran terlebih dahulu: tambahkan cangkuk praStop dengan sleep(30). Ia akan membolehkan anda melengkapkan semua permintaan yang sebelum ini (dan kami tidak menerima yang baharu, kerana pod sudah berkemampuan untuk Mengakhiri), dan selepas 30 saat pod itu sendiri akan berakhir dengan isyarat SIGTERM.

Ia ternyata bahawa lifecycle kerana bekas akan kelihatan seperti ini:

    lifecycle:
      preStop:
        exec:
          command:
          - /bin/sleep
          - "30"

Walau bagaimanapun, disebabkan oleh 30 saat sleep kita kuat kami akan meningkatkan masa penggunaan, kerana setiap pod akan ditamatkan minimum 30 saat, yang tidak baik. Apa yang boleh dilakukan mengenai perkara ini?

Mari kita beralih kepada pihak yang bertanggungjawab untuk pelaksanaan langsung permohonan itu. Dalam kes kami ia adalah PHP-FPMYang secara lalai tidak memantau pelaksanaan proses anaknya: Proses induk ditamatkan serta-merta. Anda boleh menukar tingkah laku ini menggunakan arahan process_control_timeout, yang menentukan had masa untuk proses kanak-kanak menunggu isyarat daripada tuan. Jika anda menetapkan nilai kepada 20 saat, ini akan meliputi kebanyakan pertanyaan yang dijalankan dalam bekas dan akan menghentikan proses induk sebaik sahaja ia selesai.

Dengan pengetahuan ini, mari kita kembali kepada masalah terakhir kita. Seperti yang dinyatakan, Kubernetes bukanlah platform monolitik: komunikasi antara komponennya yang berbeza mengambil sedikit masa. Ini benar terutamanya apabila kita mempertimbangkan operasi Ingresses dan komponen lain yang berkaitan, kerana disebabkan kelewatan sedemikian pada masa penggunaan, adalah mudah untuk mendapatkan lonjakan sebanyak 500 ralat. Sebagai contoh, ralat mungkin berlaku pada peringkat menghantar permintaan ke huluan, tetapi "selang masa" interaksi antara komponen agak singkat - kurang daripada satu saat.

Oleh itu Dalam jumlah dengan arahan yang telah disebutkan process_control_timeout anda boleh menggunakan binaan berikut untuk lifecycle:

lifecycle:
  preStop:
    exec:
      command: ["/bin/bash","-c","/bin/sleep 1; kill -QUIT 1"]

Dalam kes ini, kami akan membayar pampasan untuk kelewatan dengan arahan sleep dan tidak meningkatkan masa penggunaan: adakah terdapat perbezaan yang ketara antara 30 saat dan satu?.. Sebenarnya, ia adalah process_control_timeoutDan lifecycle digunakan hanya sebagai "jaring keselamatan" sekiranya berlaku ketinggalan.

Secara umumnya tingkah laku yang diterangkan dan penyelesaian yang sepadan digunakan bukan sahaja untuk PHP-FPM. Situasi yang sama mungkin satu cara atau yang lain timbul apabila menggunakan bahasa/rangka kerja lain. Jika anda tidak dapat membetulkan penutupan anggun dengan cara lain - contohnya, dengan menulis semula kod supaya aplikasi memproses isyarat penamatan dengan betul - anda boleh menggunakan kaedah yang diterangkan. Ia mungkin bukan yang paling indah, tetapi ia berkesan.

berlatih. Ujian beban untuk memeriksa operasi pod

Ujian beban ialah salah satu cara untuk menyemak cara kontena berfungsi, kerana prosedur ini membawanya lebih dekat kepada keadaan pertempuran sebenar apabila pengguna melawat tapak. Untuk menguji cadangan di atas, anda boleh menggunakan Yandex.Tankom: Ia meliputi semua keperluan kita dengan sempurna. Berikut ialah petua dan cadangan untuk menjalankan ujian dengan contoh yang jelas daripada pengalaman kami terima kasih kepada graf Grafana dan Yandex.Tank itu sendiri.

Perkara yang paling penting di sini ialah semak perubahan langkah demi langkah. Selepas menambah pembetulan baharu, jalankan ujian dan lihat sama ada keputusan telah berubah berbanding dengan larian terakhir. Jika tidak, sukar untuk mengenal pasti penyelesaian yang tidak berkesan, dan dalam jangka panjang ia hanya boleh mendatangkan kemudaratan (contohnya, meningkatkan masa penggunaan).

Satu lagi nuansa ialah melihat log kontena semasa penamatannya. Adakah maklumat tentang penutupan anggun direkodkan di sana? Adakah terdapat sebarang ralat dalam log semasa mengakses sumber lain (contohnya, ke bekas PHP-FPM yang berdekatan)? Ralat dalam aplikasi itu sendiri (seperti dalam kes NGINX yang diterangkan di atas)? Saya berharap maklumat pengenalan daripada artikel ini akan membantu anda memahami dengan lebih baik perkara yang berlaku kepada bekas semasa penamatannya.

Jadi, larian ujian pertama berlaku tanpa lifecycle dan tanpa arahan tambahan untuk pelayan aplikasi (process_control_timeout dalam PHP-FPM). Tujuan ujian ini adalah untuk mengenal pasti anggaran bilangan ralat (dan sama ada terdapat sebarang ralat). Selain itu, daripada maklumat tambahan, anda harus tahu bahawa purata masa penggunaan untuk setiap pod adalah kira-kira 5-10 saat sehingga ia siap sepenuhnya. Hasilnya ialah:

Petua & kiat Kubernetes: ciri penutupan anggun dalam NGINX dan PHP-FPM

Panel maklumat Yandex.Tank menunjukkan peningkatan sebanyak 502 ralat, yang berlaku pada masa penggunaan dan berlangsung secara purata sehingga 5 saat. Mungkin ini adalah kerana permintaan sedia ada untuk pod lama telah ditamatkan apabila ia ditamatkan. Selepas ini, 503 ralat muncul, yang merupakan hasil daripada bekas NGINX yang dihentikan, yang juga menjatuhkan sambungan disebabkan bahagian belakang (yang menghalang Ingress daripada menyambung kepadanya).

Mari kita lihat bagaimana process_control_timeout dalam PHP-FPM akan membantu kami menunggu proses anak selesai, i.e. membetulkan kesilapan tersebut. Gunakan semula menggunakan arahan ini:

Petua & kiat Kubernetes: ciri penutupan anggun dalam NGINX dan PHP-FPM

Tiada lagi ralat semasa penggunaan ke-500! Pengerahan berjaya, penutupan yang anggun berfungsi.

Walau bagaimanapun, adalah wajar untuk mengingati isu dengan bekas Ingress, peratusan kecil ralat yang mungkin kami terima disebabkan ketinggalan masa. Untuk mengelakkannya, yang tinggal hanyalah menambah struktur dengan sleep dan ulangi penggunaan. Walau bagaimanapun, dalam kes tertentu kami, tiada perubahan kelihatan (sekali lagi, tiada ralat).

Kesimpulan

Untuk menamatkan proses dengan anggun, kami mengharapkan gelagat berikut daripada aplikasi:

  1. Tunggu beberapa saat dan kemudian berhenti menerima sambungan baharu.
  2. Tunggu semua permintaan untuk menyelesaikan dan menutup semua sambungan keepalive yang tidak melaksanakan permintaan.
  3. Tamatkan proses anda.

Walau bagaimanapun, tidak semua aplikasi boleh berfungsi dengan cara ini. Satu penyelesaian kepada masalah dalam realiti Kubernetes ialah:

  • menambah cangkuk pra-henti yang akan menunggu beberapa saat;
  • mengkaji fail konfigurasi bahagian belakang kami untuk parameter yang sesuai.

Contoh dengan NGINX menjelaskan bahawa walaupun aplikasi yang pada mulanya harus memproses isyarat penamatan dengan betul mungkin tidak berbuat demikian, jadi adalah penting untuk menyemak 500 ralat semasa penggunaan aplikasi. Ini juga membolehkan anda melihat masalah dengan lebih luas dan tidak menumpukan pada satu pod atau bekas, tetapi melihat keseluruhan infrastruktur secara keseluruhan.

Sebagai alat ujian, anda boleh menggunakan Yandex.Tank bersama-sama dengan mana-mana sistem pemantauan (dalam kes kami, data diambil dari Grafana dengan bahagian belakang Prometheus untuk ujian). Masalah dengan penutupan anggun jelas kelihatan di bawah beban berat yang boleh dijana oleh penanda aras dan pemantauan membantu menganalisis keadaan dengan lebih terperinci semasa atau selepas ujian.

Sebagai tindak balas kepada maklum balas mengenai artikel itu: perlu dinyatakan bahawa masalah dan penyelesaian diterangkan di sini berkaitan dengan NGINX Ingress. Untuk kes lain, terdapat penyelesaian lain, yang mungkin kami pertimbangkan dalam bahan siri berikut.

PS

Lain daripada siri petua & helah K8s:

Sumber: www.habr.com

Tambah komen