Lima kesalahan saat menerapkan aplikasi pertama di Kubernetes

Lima kesalahan saat menerapkan aplikasi pertama di KubernetesGagal oleh Aris-Dreamer

Banyak orang yang percaya bahwa memigrasikan aplikasi ke Kubernetes saja (baik menggunakan Helm atau manual) saja sudah cukup dan mereka akan senang. Tapi itu tidak sesederhana itu.

Tim Solusi Cloud Mail.ru menerjemahkan artikel oleh insinyur DevOps Julian Gindi. Dia menceritakan kendala apa saja yang dihadapi perusahaannya selama proses migrasi agar Anda tidak mengalami hal yang sama.

Langkah Pertama: Menyiapkan Permintaan dan Batasan Pod

Mari kita mulai dengan menyiapkan lingkungan bersih di mana pod kita akan berjalan. Kubernetes melakukan tugasnya dengan baik dalam menjadwalkan pod dan menangani kondisi kegagalan. Namun ternyata penjadwal terkadang tidak dapat menempatkan pod jika sulit memperkirakan berapa banyak sumber daya yang dibutuhkan agar dapat bekerja dengan sukses. Di sinilah permintaan sumber daya dan batasan muncul. Ada banyak perdebatan tentang pendekatan terbaik untuk menetapkan permintaan dan batasan. Terkadang rasanya lebih seperti seni daripada sains. Inilah pendekatan kami.

Permintaan pod - Ini adalah nilai utama yang digunakan oleh penjadwal untuk menempatkan pod secara optimal.

Dari Dokumentasi Kubernetes: Langkah pemfilteran menentukan kumpulan node tempat pod dapat dijadwalkan. Misalnya, filter PodFitsResources memeriksa apakah sebuah node memiliki sumber daya yang cukup untuk memenuhi permintaan sumber daya spesifik dari sebuah pod.

Kami menggunakan permintaan aplikasi sehingga dapat digunakan untuk memperkirakan berapa banyak sumber daya sebenarnya Aplikasi membutuhkannya agar berfungsi dengan baik. Dengan cara ini penjadwal dapat menempatkan node secara realistis. Awalnya kami ingin mengatur permintaan dengan margin untuk memastikan bahwa setiap pod memiliki jumlah sumber daya yang cukup besar, namun kami melihat bahwa waktu penjadwalan meningkat secara signifikan dan beberapa pod tidak pernah sepenuhnya dijadwalkan, seolah-olah tidak ada permintaan sumber daya yang diterima untuk pod tersebut.

Dalam kasus ini, penjadwal sering kali mengeluarkan pod dan tidak dapat menjadwal ulang pod tersebut karena bidang kendali tidak tahu berapa banyak sumber daya yang dibutuhkan aplikasi, yang merupakan komponen kunci dari algoritme penjadwalan.

Batasan pod - ini adalah batas yang lebih jelas untuk pod tersebut. Ini mewakili jumlah maksimum sumber daya yang akan dialokasikan klaster ke kontainer.

Sekali lagi, dari dokumentasi resmi: Jika sebuah container mempunyai batas memori sebesar 4 GiB, maka kubelet (dan runtime container) akan menerapkannya. Runtime tidak mengizinkan kontainer menggunakan lebih dari batas sumber daya yang ditentukan. Misalnya, ketika suatu proses dalam wadah mencoba menggunakan lebih dari jumlah memori yang diizinkan, kernel sistem menghentikan proses tersebut dengan kesalahan "kehabisan memori" (OOM).

Sebuah kontainer selalu dapat menggunakan lebih banyak sumber daya daripada yang ditentukan dalam permintaan sumber daya, namun tidak pernah dapat menggunakan lebih dari yang ditentukan dalam batasnya. Nilai ini sulit diatur dengan benar, namun sangat penting.

Idealnya, kita ingin kebutuhan sumber daya sebuah pod berubah selama siklus hidup suatu proses tanpa mengganggu proses lain dalam sistem—itulah tujuan dari penetapan batasan.

Sayangnya, saya tidak bisa memberikan instruksi spesifik tentang nilai-nilai apa yang harus ditetapkan, namun kami sendiri mematuhi aturan berikut:

  1. Dengan menggunakan alat pengujian beban, kami menyimulasikan tingkat lalu lintas dasar dan memantau penggunaan sumber daya pod (memori dan prosesor).
  2. Kami menetapkan permintaan pod ke nilai yang sangat rendah (dengan batas sumber daya sekitar 5 kali nilai permintaan) dan mengamati. Ketika permintaan terlalu rendah, proses tidak dapat dimulai, sering kali menyebabkan kesalahan runtime Go yang misterius.

Perhatikan bahwa batas sumber daya yang lebih tinggi membuat penjadwalan menjadi lebih sulit karena pod memerlukan node target dengan sumber daya yang tersedia cukup.

Bayangkan situasi di mana Anda memiliki server web ringan dengan batas sumber daya yang sangat tinggi, misalnya memori 4 GB. Proses ini kemungkinan besar harus diskalakan secara horizontal, dan setiap modul baru harus dijadwalkan pada node dengan memori tersedia minimal 4 GB. Jika tidak ada node seperti itu, cluster harus memperkenalkan node baru untuk memproses pod tersebut, yang mungkin memerlukan waktu. Penting untuk meminimalkan perbedaan antara permintaan dan batasan sumber daya untuk memastikan penskalaan yang cepat dan lancar.

Langkah kedua: menyiapkan tes Keaktifan dan Kesiapan

Ini adalah topik halus lainnya yang sering dibahas di komunitas Kubernetes. Penting untuk memiliki pemahaman yang baik tentang pengujian Keaktifan dan Kesiapan karena pengujian tersebut menyediakan mekanisme agar perangkat lunak dapat berjalan dengan lancar dan meminimalkan waktu henti. Namun, hal tersebut dapat menyebabkan penurunan kinerja yang serius pada aplikasi Anda jika tidak dikonfigurasi dengan benar. Di bawah ini adalah ringkasan dari kedua sampel tersebut.

Kehidupan menunjukkan apakah container sedang berjalan. Jika gagal, kubelet akan mematikan container tersebut dan kebijakan restart akan diaktifkan untuk container tersebut. Jika wadah tidak dilengkapi dengan probe Liveness, maka status defaultnya adalah sukses - inilah yang tertulis di dalamnya Dokumentasi Kubernetes.

Probe keaktifan harus murah, artinya probe tersebut tidak menghabiskan banyak sumber daya, karena probe ini sering dijalankan dan perlu memberi tahu Kubernetes bahwa aplikasi sedang berjalan.

Jika Anda menyetel opsi untuk berjalan setiap detik, ini akan menambah 1 permintaan per detik, jadi ketahuilah bahwa sumber daya tambahan akan diperlukan untuk menangani lalu lintas ini.

Di perusahaan kami, pengujian Liveness memeriksa komponen inti aplikasi, meskipun data (misalnya, dari database atau cache jarak jauh) tidak dapat diakses sepenuhnya.

Kami telah mengonfigurasi aplikasi dengan titik akhir "kesehatan" yang hanya mengembalikan kode respons 200. Ini merupakan indikasi bahwa proses sedang berjalan dan mampu memproses permintaan (tetapi belum lalu lintas).

Sampel Kesiapan menunjukkan apakah container siap melayani permintaan. Jika pemeriksaan kesiapan gagal, pengontrol titik akhir akan menghapus alamat IP pod dari titik akhir semua layanan yang terkait dengan pod tersebut. Hal ini juga dinyatakan dalam dokumentasi Kubernetes.

Pemeriksaan kesiapan menggunakan lebih banyak sumber daya karena harus dikirim ke backend dengan cara yang menunjukkan bahwa aplikasi siap menerima permintaan.

Ada banyak perdebatan di komunitas tentang apakah akan mengakses database secara langsung. Mengingat overhead (pemeriksaan sering dilakukan, namun dapat disesuaikan), kami memutuskan bahwa untuk beberapa aplikasi, kesiapan untuk melayani lalu lintas hanya dihitung setelah memverifikasi bahwa catatan dikembalikan dari database. Uji coba kesiapan yang dirancang dengan baik memastikan tingkat ketersediaan yang lebih tinggi dan menghilangkan waktu henti selama penerapan.

Jika Anda memutuskan untuk melakukan kueri database guna menguji kesiapan aplikasi Anda, pastikan biayanya semurah mungkin. Mari kita terima permintaan ini:

SELECT small_item FROM table LIMIT 1

Berikut ini contoh cara kami mengonfigurasi kedua nilai ini di Kubernetes:

livenessProbe: 
 httpGet:   
   path: /api/liveness    
   port: http 
readinessProbe:  
 httpGet:    
   path: /api/readiness    
   port: http  periodSeconds: 2

Anda dapat menambahkan beberapa opsi konfigurasi tambahan:

  • initialDelaySeconds — berapa detik yang akan berlalu antara peluncuran kontainer dan dimulainya sampel.
  • periodSeconds — interval tunggu antara pengambilan sampel.
  • timeoutSeconds — jumlah detik setelah unit dianggap darurat. Batas waktu reguler.
  • failureThreshold — jumlah kegagalan pengujian sebelum sinyal restart dikirim ke pod.
  • successThreshold — jumlah probe yang berhasil sebelum pod masuk ke status siap (setelah kegagalan, saat pod dimulai atau dipulihkan).

Langkah ketiga: menyiapkan kebijakan jaringan default untuk pod

Kubernetes memiliki topografi jaringan “datar”; secara default, semua pod berkomunikasi langsung satu sama lain. Dalam beberapa kasus hal ini tidak diinginkan.

Masalah keamanan yang mungkin terjadi adalah penyerang dapat menggunakan satu aplikasi rentan untuk mengirimkan lalu lintas ke semua pod di jaringan. Seperti banyak bidang keamanan lainnya, prinsip hak istimewa paling rendah berlaku di sini. Idealnya, kebijakan jaringan harus secara eksplisit menentukan koneksi antar pod mana yang diperbolehkan dan mana yang tidak.

Misalnya, di bawah ini adalah kebijakan sederhana yang menolak semua lalu lintas masuk untuk namespace tertentu:

---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:  
 name: default-deny-ingress
spec:  
 podSelector: {}  
 policyTypes:  
   - Ingress

Visualisasi konfigurasi ini:

Lima kesalahan saat menerapkan aplikasi pertama di Kubernetes
(https://miro.medium.com/max/875/1*-eiVw43azgzYzyN1th7cZg.gif)
Lebih jelasnya di sini.

Langkah empat: perilaku khusus menggunakan hook dan wadah init

Salah satu tujuan utama kami adalah menyediakan penerapan ke Kubernetes tanpa waktu henti bagi pengembang. Ini sulit karena ada banyak opsi untuk mematikan aplikasi dan mengosongkan sumber daya yang digunakan.

Kesulitan khusus muncul dengan Nginx. Kami memperhatikan bahwa ketika pod ini di-deploy secara berurutan, koneksi aktif terputus sebelum berhasil diselesaikan.

Setelah melakukan penelitian ekstensif secara online, ternyata Kubernetes tidak menunggu koneksi Nginx habis sendiri sebelum menghentikan podnya. Dengan menggunakan hook pre-stop, kami mengimplementasikan fungsi berikut dan sepenuhnya menghilangkan downtime:

lifecycle: 
 preStop:
   exec:
     command: ["/usr/local/bin/nginx-killer.sh"]

Tetapi nginx-killer.sh:

#!/bin/bash
sleep 3
PID=$(cat /run/nginx.pid)
nginx -s quit
while [ -d /proc/$PID ]; do
   echo "Waiting while shutting down nginx..."
   sleep 10
done

Paradigma lain yang sangat berguna adalah penggunaan kontainer init untuk menangani permulaan aplikasi tertentu. Hal ini sangat berguna jika Anda memiliki proses migrasi database intensif sumber daya yang perlu dijalankan sebelum aplikasi dimulai. Anda juga dapat menentukan batas sumber daya yang lebih tinggi untuk proses ini tanpa menetapkan batas tersebut untuk aplikasi utama.

Skema umum lainnya adalah mengakses rahasia dalam wadah init yang memberikan kredensial tersebut ke modul utama, yang mencegah akses tidak sah ke rahasia dari modul aplikasi utama itu sendiri.

Seperti biasa, kutipan dari dokumentasi: Kontainer init dengan aman menjalankan kode atau utilitas khusus yang akan mengurangi keamanan gambar kontainer aplikasi. Dengan memisahkan alat yang tidak diperlukan, Anda membatasi permukaan serangan gambar kontainer aplikasi.

Langkah Lima: Mengonfigurasi Kernel

Terakhir, mari kita bicara tentang teknik yang lebih maju.

Kubernetes adalah platform yang sangat fleksibel yang memungkinkan Anda menjalankan beban kerja sesuai keinginan Anda. Kami memiliki sejumlah aplikasi berkinerja tinggi yang sangat intensif sumber daya. Setelah melakukan pengujian beban ekstensif, kami menemukan bahwa salah satu aplikasi kesulitan menangani beban lalu lintas yang diharapkan ketika pengaturan default Kubernetes diterapkan.

Namun, Kubernetes mengizinkan Anda menjalankan container dengan hak istimewa yang mengubah parameter kernel hanya untuk pod tertentu. Inilah yang kami gunakan untuk mengubah jumlah maksimum koneksi terbuka:

initContainers:
  - name: sysctl
     image: alpine:3.10
     securityContext:
         privileged: true
      command: ['sh', '-c', "sysctl -w net.core.somaxconn=32768"]

Ini adalah teknik yang lebih canggih yang seringkali tidak diperlukan. Namun jika aplikasi Anda kesulitan mengatasi beban berat, Anda dapat mencoba mengubah beberapa pengaturan ini. Detail lebih lanjut tentang proses ini dan pengaturan nilai yang berbeda - seperti biasa dalam dokumentasi resmi.

Sebagai kesimpulan

Meskipun Kubernetes mungkin tampak seperti solusi siap pakai, ada beberapa langkah penting yang perlu Anda ambil agar aplikasi Anda tetap berjalan lancar.

Sepanjang migrasi Kubernetes Anda, penting untuk mengikuti "siklus pengujian beban": meluncurkan aplikasi, menguji beban, mengamati metrik dan perilaku penskalaan, menyesuaikan konfigurasi berdasarkan data tersebut, lalu ulangi siklus tersebut lagi.

Bersikaplah realistis tentang lalu lintas yang Anda harapkan dan cobalah melampauinya untuk melihat komponen mana yang rusak terlebih dahulu. Dengan pendekatan berulang ini, hanya beberapa rekomendasi yang tercantum mungkin cukup untuk mencapai kesuksesan. Atau mungkin memerlukan penyesuaian yang lebih mendalam.

Selalu tanyakan pada diri Anda pertanyaan-pertanyaan ini:

  1. Berapa banyak sumber daya yang dikonsumsi aplikasi dan bagaimana volumenya akan berubah?
  2. Apa saja persyaratan penskalaan yang sebenarnya? Berapa rata-rata lalu lintas yang akan ditangani aplikasi? Bagaimana dengan lalu lintas puncak?
  3. Seberapa sering layanan perlu ditingkatkan skalanya secara horizontal? Seberapa cepat pod baru perlu dihadirkan online untuk menerima traffic?
  4. Seberapa benar cara mematikan pod? Apakah ini perlu? Apakah mungkin untuk mencapai penerapan tanpa downtime?
  5. Bagaimana cara meminimalkan risiko keamanan dan membatasi kerusakan dari pod yang disusupi? Apakah ada layanan yang memiliki izin atau akses yang tidak diperlukan?

Kubernetes menyediakan platform luar biasa yang memungkinkan Anda memanfaatkan praktik terbaik untuk menerapkan ribuan layanan dalam satu cluster. Namun, setiap aplikasi berbeda. Terkadang penerapannya membutuhkan lebih banyak pekerjaan.

Untungnya, Kubernetes menyediakan konfigurasi yang diperlukan untuk mencapai semua tujuan teknis. Dengan menggunakan kombinasi permintaan dan batasan sumber daya, pemeriksaan Keaktifan dan Kesiapan, kontainer init, kebijakan jaringan, dan penyetelan kernel khusus, Anda dapat mencapai kinerja tinggi bersama dengan toleransi kesalahan dan skalabilitas yang cepat.

Apa lagi yang harus dibaca:

  1. Praktik terbaik dan praktik terbaik untuk menjalankan container dan Kubernetes di lingkungan produksi.
  2. 90+ alat berguna untuk Kubernetes: penerapan, pengelolaan, pemantauan, keamanan, dan banyak lagi.
  3. Saluran kami Seputar Kubernetes di Telegram.

Sumber: www.habr.com

Tambah komentar