1 - Pengenalan Pod

Halaman ini menyajikan ikhtisar dari Pod, objek terkecil yang dapat di deploy di dalam objek model Kubernetes.

Memahami Pod

Sebuah Pod adalah unit dasar di Kubernetes--unit terkecil dan paling sederhana di dalam objek model Kubernetes yang dapat dibuat dan di deploy. Sebuah Pod merepresentasikan suatu proses yang berjalan di dalam klaster.

Pod membungkus sebuah kontainer (atau, di beberapa kasus, beberapa kontainer), sumber penyimpanan, alamat jaringan IP yang unik, dan opsi yang mengatur bagaimana kontainer harus dijalankan. Pod merupakan representasi dari unit deployment: sebuah instance aplikasi di dalam Kubernetes, yang mungkin terdiri dari satu kontainer atau sekumpulan kontainer yang berbagi resource.

Docker adalah salah satu kontainer runtime yang paling umum digunakan di Kubernetes Pod, tetapi Pod mendukung kontainer runtime lainnya.

Pod di Kubernetes klaster dapat digunakan dengan dua cara:

  • Pod menjalankan satu kontainer. Model satu kontainer per Pod adalah model yang umum digunakan di Kubernetes; kamu dapat membayangkan sebuah Pod sebagai pembungkus kontainer tersebut, dan Kubernetes tidak mengelola kontainer secara langsung tetapi mengelola Pod tersebut.
  • Pod menjalankan beberapa kontainer yang perlu berjalan bersamaan. Sebuah Pod dapat membungkus sebuah aplikasi yang terdiri dari beberapa kontainer yang perlu berbagi resource. Kontainer yang ditempatkan di dalam satu Pod ini membentuk sebuah layanan. Sebuah kontainer menyajikan berkas dari sumber penyimpanan ke publik, sedangkan kontainer sidecar yang lain melakukan pembaharuan terhadap berkas tersebut. Pod membungkus semua kontainer dan resource penyimpanan sebagai satu kesatuan yang dapat dikelola.

Kubernetes Blog menyediakan beberapa informasi tambahan terkait penggunaan Pod. Informasi selengkapnya, kunjungi:

Setiap Pod dimaksudkan untuk menjalankan satu instance aplikasi. Jika kamu ingin mengembangkan aplikasi secara horizontal (contoh, banyak instance sekaligus), kamu dapat menggunakan banyak Pod, satu untuk setiap instance. Di Kubernetes, konsep ini umumnya disebut dengan replikasi. Pod yang direplikasi biasanya dibuat dan dikelola sebagai grup oleh objek abstraksi yang disebut kontroler. Lihat Pod dan Kontroler untuk informasi selengkapnya.

Bagaimana Pod mengelola beberapa Kontainer

Pod didesain untuk mendukung banyak proses (sebagai kontainer) yang membentuk sebuah layanan. Kontainer di dalam sebuah Pod akan otomatis ditempatkan bersama di dalam satu mesin fisik atau mesin virtual di dalam klaster. Kontainer tersebut dapat berbagi resource dan dependensi, berkomunikasi satu sama lain, dan berkoordinasi kapan dan bagaimana mereka diterminasi.

Perhatikan bahwa mengelompokan kontainer di dalam satu Pod merupakan kasus lanjutan. Kamu dapat menggunakan pola ini hanya dalam kasus tertentu. Sebagai contoh, kamu memiliki kontainer yang bertindak sebagai web server yang menyajikan berkas dari resource penyimpanan bersama, dan kontainer sidecar melakukan pembaharuan terhadap berkas tersebut dari sumber lain, seperti dalam diagram Pod berikut:

Pod diagram

Pod menyediakan dua jenis resource sebagai penyusun dari kontainer: jaringan dan penyimpanan.

Jaringan

Setiap Pod diberikan sebuah alamat IP unik. Setiap kontainer di dalam Pod berbagi network namespace, termasuk alamat IP dan port jaringan. Setiap kontainer di dalam Pod dapat berkomunikasi satu sama lain menggunakan localhost. Saat para kontainer di dalam Pod berkomunikasi dengan entitas lain di luar Pod, mereka harus berkoordinasi satu sama lain bagaimana mereka menggunakan resource jaringan (seperti Port).

Penyimpanan

Pod dapat menentukan penyimpanan bersama yaitu volumes. Semua kontainer di dalam Pod dapat mengakses volumes ini, mengizinkan kontainer untuk berbagi data. Volumes juga memungkinkan data di Pod untuk bertahan jika salah satu kontainer perlu melakukan proses restart. Lihat Volumes untuk informasi lebih lanjut bagaimana Kubernetes mengimplementasikan penyimpanan di dalam Pod.

Bekerja dengan Pod

Kamu akan jarang membuat Pod secara langsung di Kubernetes. Ini karena Pod dirancang sebagai entitas sesaat. Saat Pod dibuat (baik oleh kamu, atau secara tidak langsung oleh kontroler), Pod ditempatkan dan dijalankan di sebuah Node di dalam klaster. Pod akan tetap di Node tersebut sampai proses dihentikan, Objek Pod dihapus, Pod dihentikan karena kekurangan resource, atau Node tersebut berhenti berjalan.

Pod tidak melakukan mekanisme penyembuhan diri sendiri. Jika Pod ditempatkan disebuah Node yang gagal, atau proses penempatan Pod itu sendiri gagal, Pod akan dihapus; demikian juga, Pod tidak akan bertahan jika Node tersebut kehabisan resource atau sedang dalam tahap pemeliharaan. Kubernetes menggunakan abstraksi yang disebut kontroler, yang menangani dan mengelola Pod. Jadi, meskipun Pod dapat dipakai secara langsung di Kubernetes, kontroler merupakan cara umum yang digunakan untuk mengelola Pod. Lihat Pod dan kontroler untuk informasi lebih lanjut bagaimana Kubernetes menggunakan kontroler untuk mengimpelentasikan mekanisme penyembuhan diri sendiri dan replikasi pada Pod.

Pod dan Kontroler

Kontroler dapat membuat dan mengelola banyak Pod untuk kamu, menangani replikasi dan menyediakan kemampuan penyembuhan diri sendiri pada lingkup klaster. Sebagai contoh, jika sebuah Node gagal, kontroler akan otomatis mengganti Pod tersebut dengan menempatkan Pod yang identik di Node yang lain.

Beberapa contoh kontroler yang berisi satu atau lebih Pod meliputi:

Secara umum, kontroler menggunakan templat Pod yang kamu sediakan untuk membuat Pod.

Templat Pod

Templat Pod adalah spesifikasi dari Pod yang termasuk di dalam objek lain seperti Replication Controllers, Jobs, dan DaemonSets. Kontroler menggunakan templat Pod untuk membuat Pod.

Contoh di bawah merupakan manifestasi sederhana untuk Pod yang berisi kontainer yang membuat sebuah pesan.

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600']

Perubahan yang terjadi pada templat atau berganti ke templat yang baru tidak memiliki efek langsung pada Pod yang sudah dibuat. Pod yang dibuat oleh replication controller dapat diperbarui secara langsung.

Selanjutnya

2 - Pod

Pod adalah unit komputasi terkecil yang bisa di-deploy dan dibuat serta dikelola dalam Kubernetes.

Apa Itu Pod?

Sebuah Pod (seperti pod pada paus atau kacang polong) adalah sebuah kelompok yang terdiri dari satu atau lebih kontainer (misalnya kontainer Docker), dengan ruang penyimpanan ataupun jaringan yang dipakai bersama, dan sebuah spesifikasi mengenai bagaimana menjalankan kontainer. Isi dari Pod akan selalu diletakkan dan dijadwalkan bersama, serta berjalan dalam konteks yang sama. Sebuah Pod memodelkan "logical host" yang spesifik terhadap aplikasi. Ini mengandung lebih dari satu kontainer aplikasi yang secara relatif saling terhubung erat. Sebelum masa kontainer, menjalankan aplikasi dalam mesin fisik atau virtual berarti menjalankan dalam logical host yang sama.

Walaupun Kubernetes mendukung lebih banyak runtime kontainer selain Docker, namun Docker adalah yang paling umum diketahui dan ini membantu dalam menjelaskan Pod dengan istilah pada Docker.

Konteks bersama dalam sebuah Pod adalah kumpulan Linux namespace, cgroup dan kemungkinan segi isolasi lain, hal yang sama yang mengisolasi kontainer Docker. Dalam sebuah konteks pada Pod, setiap aplikasi bisa menerapkan sub-isolasi lebih lanjut.

Semua kontainer dalam suatu Pod akan berbagi alamat IP dan port yang sama, dan bisa saling berkomunikasi melalui localhost. Komunikasi tersebut mengunakan standar inter-process communications (IPC) seperti SystemV semaphores atau POSIX shared memory. Kontainer pada Pod yang berbeda memiliki alamat IP yang berbeda dan tidak dapat berkomunikasi menggunakan IPC tanpa pengaturan khusus. Kontainer ini biasa berkomunikasi dengan yang lain menggunakan alamat IP setiap Pod.

Aplikasi dalam suatu Pod juga memiliki akses ke ruang penyimpanan bersama, yang didefinisikan sebagai bagian dari Pod dan dibuat bisa diikatkan ke masing-masing filesystem pada aplikasi.

Dalam istilah konsep Docker, sebuah Pod dimodelkan sebagai gabungan dari kontainer Docker yang berbagi namespace dan ruang penyimpanan filesystem.

Layaknya aplikasi dengan kontainer, Pod dianggap sebagai entitas yang relatif tidak kekal (tidak bertahan lama). Seperti yang didiskusikan dalam siklus hidup Pod, Pod dibuat, diberikan ID unik (UID), dan dijadwalkan pada suatu mesin dan akan tetap disana hingga dihentikan (bergantung pada aturan restart) atau dihapus. Jika mesin mati, maka semua Pod pada mesin tersebut akan dijadwalkan untuk dihapus, namun setelah suatu batas waktu. Suatu Pod tertentu (sesuai dengan ID unik) tidak akan dijadwalkan ulang ke mesin baru, namun akan digantikan oleh Pod yang identik, bahkan jika dibutuhkan bisa dengan nama yang sama, tapi dengan ID unik yang baru (baca replication controller untuk info lebih lanjut)

Ketika sesuatu dikatakan memiliki umur yang sama dengan Pod, misalnya saja ruang penyimpanan, maka itu berarti akan tetap ada selama Pod tersebut masih ada. Jika Pod dihapus dengan alasan apapun, sekalipun Pod pengganti yang identik telah dibuat, semua yang berhubungan (misalnya ruang penyimpanan) akan dihapus dan dibuat ulang.

Pod diagram

Sebuah Pod dengan banyak kontainer, yaitu File Puller dan Web Server yang menggunakan ruang penyimpanan persisten untuk berbagi ruang penyimpanan bersama antara kontainer.

Motivasi suatu Pods

Pengelolaan

Pod adalah suatu model dari pola beberapa proses yang bekerja sama dan membentuk suatu unit layanan yang kohesif. Menyederhanakan proses melakukan deploy dan pengelolaan aplikasi dengan menyediakan abstraksi tingkat yang lebih tinggi daripada konstituen aplikasinya. Pod melayani sebagai unit dari deployment, penskalaan horizontal, dan replikasi. Colocation (co-scheduling), berbagi nasib (misalnya dimatikan), replikasi terkoordinasi, berbagi sumber daya dan pengelolaan ketergantungan akan ditangani otomatis untuk kontainer dalam suatu Pod.

Berbagi sumber daya dan komunikasi

Pod memungkinkan berbagi data dan komunikasi diantara konstituennya.

Semua aplikasi dalam suatu Pod menggunakan namespace jaringan yang sama (alamat IP dan port yang sama), dan menjadikan bisa saling mencari dan berkomunikasi dengan menggunakan localhost. Oleh karena itu, aplikasi dalam Pod harus berkoordinasi mengenai penggunaan port. Setiap Pod memiliki alamat IP dalam satu jaringan bersama yang bisa berkomunikasi dengan komputer lain dan Pod lain dalam jaringan yang sama.

Kontainer dalam suatu Pod melihat hostname sistem sebagai sesuatu yang sama dengan konfigurasi name pada Pod. Informasi lebih lanjut terdapat dibagian jaringan.

Sebagai tambahan dalam mendefinisikan kontainer aplikasi yang berjalan dalam Pod, Pod memberikan sepaket sistem penyimpanan bersama. Sistem penyimpanan memungkinkan data untuk bertahan saat kontainer dijalankan ulang dan dibagikan kepada semua aplikasi dalam Pod tersebut.

Penggunaan Pod

Pod dapat digunakan untuk menjalankan beberapa aplikasi yang terintegrasi secara vertikal (misalnya LAMP), namun motivasi utamanya adalah untuk mendukung berlokasi bersama, mengelola program pembantu, diantaranya adalah:

  • sistem pengelolaan konten, pemuat berkas dan data, manajer cache lokal, dll.
  • catatan dan checkpoint cadangan, kompresi, rotasi, dll.
  • pengamat perubahan data, pengintip catatan, adapter pencatatan dan pemantauan, penerbit peristiwa, dll.
  • proksi, jembatan dan adaptor.
  • pengontrol, manajer, konfigurasi dan pembaharu.

Secara umum, masing-masing Pod tidak dimaksudkan untuk menjalankan beberapa aplikasi yang sama.

Penjelasan lebih lengkap bisa melihat The Distributed System ToolKit: Patterns for Composite Containers.

Alternatif pertimbangan

Kenapa tidak menjalankan banyak program dalam satu kontainer (Docker)?

  1. Transparansi. Membuat kontainer dalam suatu Pod menjadi terlihat dari infrastruktur, memungkinkan infrastruktur menyediakan servis ke kontainer tersebut, misalnya saja pengelolaan proses dan pemantauan sumber daya. Ini memfasilitasi sejumlah kenyamanan untuk pengguna.
  2. Pemisahan ketergantungan perangkat lunak. Setiap kontainer mungkin memiliki versi, dibuat dan dijalankan ulang secara independen. Kubernetes mungkin mendukung pembaharuan secara langsung terhadap suatu kontainer, suatu saat nanti.
  3. Mudah digunakan. Penguna tidak diharuskan menjalankan manajer prosesnya sendiri, khawatir dengan sinyal dan propagasi exit-code, dan lain sebagainya.
  4. Efisiensi. Karena infrastruktur memegang lebih banyak tanggung jawab, kontainer bisa lebih ringan.

Kenapa tidak mendukung penjadwalan kontainer berdasarkan affinity?

Cara itu bisa menyediakan lokasi yang sama, namun tidak memberikan banyak keuntungan dari Pod, misalnya saja berbagi sumber daya, IPC, jaminan berbagi nasib dan kemudahan manajemen.

Ketahanan suatu Pod (atau kekurangan)

Pod tidak dimaksudkan untuk diperlakukan sebagai entitas yang tahan lama. Mereka tidak akan bertahan dengan kegagalan penjadwalan, kegagalan mesin, atau eviction (pengusiran), misalnya karena kurangnya sumber daya atau dalam suatu kasus mesin sedang dalam pemeliharaan.

Secara umum, pengguna tidak seharusnya butuh membuat Pod secara langsung. Mereka seharusnya selalu menggunakan pengontrol, sekalipun untuk yang tunggal, misalnya, Deployment. Pengontrol menyediakan penyembuhan diri dengan ruang lingkup kelompok, begitu juga dengan pengelolaan replikasi dan penluncuran. Pengontrol seperti StatefulSet bisa memberikan dukungan terhadap Pod yang stateful.

Penggunaan API kolektif sebagai user-facing primitive utama adalah hal yang relatif umum diantara sistem penjadwalan kluster, seperti

Borg, Marathon, Aurora, dan Tupperware.

Pod diekspose sebagai primitive untuk memfasilitasi hal berikut:

  • penjadwalan dan pengontrol sifat pluggability
  • mendukung operasi pada level Pod tanpa perlu melakukan proksi melalui API pengontrol
  • pemisahan antara umur suatu Pod dan pengontrol, seperti misalnya bootstrapping.
  • pemisahan antara pengontrol dan servis, pengontrol endpoint hanya memperhatikan Pod
  • komposisi yang bersih antara fungsionalitas dilevel Kubelet dan klaster. Kubelet secara efektif adalah pengontrol Pod.
  • aplikasi dengan ketersediaan tinggi, yang akan mengharapkan Pod akan digantikan sebelum dihentikan dan tentu saja sebelum dihapus, seperti dalam kasus penggusuran yang direncanakan atau pengambilan gambar.

Penghentian Pod

Karena Pod merepresentasikan proses yang berjalan pada mesin didalam klaster, sangat penting untuk memperbolehkan proses ini berhenti secara normal ketika sudah tidak dibutuhkan (dibandingkan dengan dihentikan paksa dengan sinyal KILL dan tidak memiliki waktu untuk dibersihkan). Pengguna seharusnya dapat meminta untuk menghapus dan tahu proses penghentiannya, serta dapat memastikan penghentian berjalan sempurna. Ketika pengguna meminta menghapus Pod, sistem akan mencatat masa tenggang untuk penghentian secara normal sebelum Pod dipaksa untuk dihentikan, dan sinyal TERM akan dikirim ke proses utama dalam setiap kontainer. Setelah masa tenggang terlewati, sinyal KILL akan dikirim ke setiap proses dan Pod akan dihapus dari API server. Jika Kubelet atau kontainer manajer dijalankan ulang ketika menunggu suatu proses dihentikan, penghentian tersebut akan diulang dengan mengembalikan masa tenggang senilai semula.

Contohnya sebagai berikut:

  1. Pengguna mengirim perintah untuk menghapus Pod, dengan masa tenggang (30 detik)
  2. Pod dalam API server akan diperbarui dengan waktu dimana Pod dianggap "mati" bersama dengan masa tenggang.
  3. Pod ditampilkan dalam status "Terminating" ketika tercantum dalam perintah klien
  4. (bersamaan dengan poin 3) Ketika Kubelet melihat Pod sudah ditandai sebagai "Terminating" karena waktu pada poin 2 sudah diatur, ini memulai proses penghentian Pod
    1. Jika salah satu kontainer pada Pod memiliki preStop hook, maka akan dipanggil di dalam kontainer. Jika preStop hook masih berjalan setelah masa tenggang habis, langkah 2 akan dipanggil dengan tambahan masa tenggang yang sedikit, 2 detik.
    2. Semua kontainer akan diberikan sinyal TERM. Sebagai catatan, tidak semua kontainer akan menerima sinyal TERM dalam waktu yang sama dan mungkin butuh waktu untuk menjalankan preStop hook jika bergantung pada urutan penghentiannya.
  5. (bersamaan dengan poin 3) Pod akan dihapus dari daftar endpoint untuk servis dan tidak lagi dianggap sebagai bagian dari Pod yang berjalan dalam replication controllers. Pod yang dihentikan, secara perlahan tidak akan melayani permintaan karena load balancer (seperti servis proksi) menghapus mereka dari daftar rotasi.
  6. Ketika masa tenggang sudah lewat, semua proses yang masih berjalan dalam Pod akan dihentikan dengan sinyal SIGKILL.
  7. Kubelet akan selesai menghapus Pod dalam API server dengan mengatur masa tenggang menjadi 0 (langsung menghapus). Pod akan menghilang dari API dan tidak lagi terlihat oleh klien.

Secara default, semua penghapusan akan berjalan normal selama 30 detik. Perintah kubectl delete mendukung opsi --grace-period=<waktu dalam detik> yang akan memperbolehkan pengguna untuk menimpa nilai awal dan memberikan nilai sesuai keinginan pengguna. Nilai 0 akan membuat Pod dihapus paksa. Kamu harus memberikan opsi tambahan --force bersamaan dengan --grace-period=0 untuk melakukan penghapusan paksa.

Penghapusan paksa sebuah Pod

Penghapusan paksa dari sebuah Pod didefinisikan sebagai penghapusan Pod dari state klaster dan etcd secara langsung. Ketika penghapusan paksa dilakukan, API server tidak akan menunggu konfirmasi dari kubelet bahwa Pod sudah dihentikan pada mesin ia berjalan. Ini menghapus Pod secara langsung dari API, sehingga Pod baru bisa dibuat dengan nama yang sama. Dalam mesin, Pod yang dihentikan paksa akan tetap diberikan sedikit masa tenggang sebelum dihentikan paksa.

Penghentian paksa dapat menyebabkan hal berbahaya pada beberapa Pod dan seharusnya dilakukan dengan perhatian lebih. Dalam kasus StatefulSet Pods, silakan melihat dokumentasi untuk penghentian Pod dari StatefulSet.

Hak istimewa untuk kontainer pada Pod

Setiap kontainer dalam Pod dapat mengaktifkan hak istimewa (mode privileged), dengan menggunakan tanda privileged pada konteks keamanan pada spesifikasi kontainer. Ini akan berguna untuk kontainer yang ingin menggunakan kapabilitas Linux seperti memanipulasi jaringan dan mengakses perangkat. Proses dalam kontainer mendapatkan hak istimewa yang hampir sama dengan proses di luar kontainer. Dengan hak istimerwa, seharusnya lebih mudah untuk menulis pada jaringan dan plugin ruang penyimpanan sebagai Pod berbeda yang tidak perlu dikompilasi ke dalam kubelet.

API Object

Pod adalah sumber daya tingkat tinggi dalam Kubernetes REST API. Definisi Objek Pod API menjelaskan mengenai objek secara lengkap.

3 - Siklus Hidup Pod

Halaman ini menjelaskan siklus hidup sebuah Pod

Fase Pod

Field status dari sebuah Pod merupakan sebuah objek PodStatus, yang memiliki sebuah field phase.

Fase dari sebuah Pod adalah sesuatu yang sederhana, ringkasan yang lebih tinggi tentang Pod dalam siklus hidupnya. Fase ini tidak ditujukan sebagai sebuah kesimpulan yang luas dari observasi suatu kontainer atau state suatu Pod, serta tidak ditujukan sebagai state machine yang luas.

Jumlah dan arti dari nilai-nilai fase Pod dijaga ketat. Selain yang ada dalam dokumentasi ini, tidak perlu berasumsi mengenai Pod telah diberikan nilai phase.

Berikut adalah nilai yang mungkin diberikan untuk suatu phase:

NilaiDeskripsi
PendingPod telah disetujui oleh sistem Kubernetes, tapi ada satu atau lebih image kontainer yang belum terbuat. Ini termasuk saat sebelum dijadwalkan dan juga saat mengunduh image melalui jaringan, yang mungkin butuh beberapa waktu.
RunningPod telah terikat ke suatu node, dan semua kontainer telah terbuat. Setidaknya ada 1 kontainer yang masih berjalan, atau dalam proses memulai atau restart.
SucceededSemua kontainer di dalam Pod sudah berhasil dihentikan, dan tidak akan dilakukan restart.
FailedSemua kontainer dalan suatu Pod telah dihentikan, dan setidaknya ada satu kontainer yang terhenti karena kegagalan. Itu merupakan kontainer yang keluar dengan kode status bukan 0 atau dihentikan oleh sistem.
UnknownState suatu Pod tidak dapat diperoleh karena suatu alasan, biasanya karena kesalahan dalam komunikasi dengan host yang digunakan Pod tersebut.

Kondisi Pod

Suatu Pod memiliki sebuah PodStatus, yang merupakan array dari PodConditions yang telah atau belum dilewati oleh Pod. Setiap elemen dari array PodConditions mungkin memiliki enam field berikut:

  • Field lastProbeTime memberikan nilai timestamp yang menandakan kapan terakhir kali kondisi kondisi Pod diperiksa.

  • Field lastTransitionTime memberikan nilai timestamp yang menandakan kapan terakhir kali Pod berubah status ke status lain.

  • Field message adalah pesan yang bisa dibaca manusia yang mengidikasikan detail dari suatu transisi.

  • Field reason adalah suatu alasan yang unik, satu kata, ditulis secara CamelCase untuk kondisi transisi terakhir.

  • Field status adalah sebuah kata dengan kemungkinan nilainya berupa "True", "False", dan "Unknown".

  • Field type adalah sebuah kata yang memiliki kemungkinan nilai sebagai berikut:

    • PodScheduled: Pod telah dijadwalkan masuk ke node;
    • Ready: Pod sudah mampu menerima request masuk dan seharusnya sudah ditambahkan ke daftar pembagian beban kerja untuk servis yang sama;
    • Initialized: Semua init containers telah berjalan sempurna.
    • Unschedulable: scheduler belum dapat menjadwalkan Pod saat ini, sebagai contoh karena kekurangan resources atau ada batasan-batasan lain.
    • ContainersReady: Semua kontainer di dalam Pod telah siap.

Pemeriksaan Kontainer

Sebuah Probe adalah sebuah diagnosa yang dilakukan secara berkala oleh kubelet dalam suatu kontainer. Untuk melakukan diagnosa, kubelet memanggil sebuah Handler yang diimplementasikan oleh kontainer. Ada 3 tipe Handler yang tersedia, yaitu:

  • ExecAction: Mengeksekusi perintah tertentu di dalam kontainer. Diagnosa dikatakan berhasil jika perintah selesai dengan kode status 0.

  • TCPSocketAction: Melakukan pengecekan TCP terhadap alamat IP kontainer dengan port tertentu. Diagnosa dikatakan berhasil jika port tersebut terbuka.

  • HTTPGetAction: Melakukan sebuah request HTTP Get terhadap alamat IP kontainer dengan port dan path tertentu. Diagnosa dikatakan berhasil jika responnya memiliki kode status lebih besar atau sama dengan 200 dan kurang dari 400.

Setiap pemeriksaan akan menghasilkan salah satu dari tiga hasil berikut:

  • Success: Kontainer berhasil melakukan diagnosa.
  • Failure: Kontainer gagal melakukan diagnosa.
  • Unknown: Gagal melakukan diagnosa, sehingga tidak ada aksi yang harus dilakukan.

Kubelet dapat secara optimal melakukan dan bereaksi terhadap dua jenis pemeriksaan yang sedang berjalan pada kontainer, yaitu:

  • livenessProbe: Ini menunjukkan apakah kontainer sedang berjalan. Jika tidak berhasil melakukan pemeriksaan terhadap liveness dari kontainer, maka kubelet akan mematikan kontainer, dan kontainer akan mengikuti aturan dari restart policy. Jika kontainer tidak menyediakan pemeriksaan terhadap liveness, maka nilai dari state adalah Success.

  • readinessProbe: Ini menunjukan apakah kontainer sudah siap melayani request. Jika tidak berhasil melakukan pemeriksaan terhadap kesiapan dari kontainer, maka endpoints controller akan menghapus alamat IP Pod dari daftar semua endpoint untuk servis yang sama dengan Pod. Nilai awal state sebelum jeda awal adalah Failure. Jika kontainer tidak menyediakan pemeriksaan terhadap readiness, maka nilai awal state adalah Success.

Kapan sebaiknya menggunakan pemeriksaan terhadap liveness atau readiness?

Jika proses dalam kontainer mungkin gagal yang dikarenakan menghadapi suatu masalah atau menjadi tidak sehat, maka pemeriksaan terhadap liveness tidak diperlukan. Kubelet akan secara otomatis melakukan aksi yang tepat mengikuti restartPolicy dari Pod.

Jika kamu ingin kontainer bisa dimatikan dan dijalankan ulang ketika gagal melakukan pemeriksaan, maka tentukan pemeriksaan liveness dan tentukan nilai restartPolicy sebagai Always atau OnFailure.

Jika kamu ingin mulai mengirim traffic ke Pod hanya ketika pemeriksaan berhasil, maka tentukan pemeriksaan readiness. Dalam kasus ini, pemeriksaan readiness mungkin akan sama dengan pemeriksaan liveness, tapi keberadaan pemeriksaan readiness dalam spec berarti Pod akan tetap dijalankan tanpa menerima traffic apapun dan akan mulai menerima traffic ketika pemeriksaan yang dilakukan mulai berhasil. Jika kontainermu dibutuhkan untuk tetap berjalan ketika loading data yang besar, file konfigurasi, atau melakukan migrasi ketika startup, maka tentukanlah pemeriksaan readiness.

Jika kamu ingin kontainermu dalam mematikan dirinya sendiri, kamu dapat menentukan suatu pemeriksaan readiness yang melakukan pengecekan terhadap endpoint untuk readiness. endpoint tersebut berbeda dengan endpoint untuk pengecekan liveness.

Perlu dicatat, jika kamu hanya ingin bisa menutup request ketika Pod sedang dihapus maka kamu tidak perlu menggunakan pemeriksaan readiness. Dalam penghapusan, Pod akan secara otomatis mengubah state dirinya menjadi unready tanpa peduli apakah terdapat pemeriksaan readiness atau tidak. Pod tetap ada pada state unready selama menunggu kontainer dalam Pod berhenti.

Untuk informasi lebih lanjut mengenai pengaturan pemeriksaan liveness atau readiness, lihat bagian Konfigurasi Liveness dan Readiness Probe.

Status Pod dan Kontainer

Untuk informasi lebih mendalam mengenai status Pod dan kontainer, silakan lihat PodStatus dan ContainerStatus. Mohon diperhatikan, informasi tentang status Pod bergantung pada ContainerState.

State Kontainer

Ketika Pod sudah ditempatkan pada suatu node oleh scheduler, kubelet mulai membuat kontainer menggunakan runtime kontainer. Ada tiga kemungkinan state untuk suatu kontainer, yaitu Waiting, Running, dan Terminated. Untuk mengecek state suatu kontainer, kamu bisa menggunakan perintah kubectl describe pod [NAMA_POD]. State akan ditampilkan untuk masing-masing kontainer dalam Pod tersebut.

  • Waiting: Merupakan state default dari kontainer. Jika state kontainer bukan Running atau Terminated, berarti dalam Wating state. Suatu kontainer dalam Waiting state akan tetap menjalan operasi-operasi yang dibutuhkan, misalnya mengunduh images, mengaplikasikan Secrets, dsb. Bersamaan dengan state ini, sebuah pesan dan alasan tentang state akan ditampilkan untuk memberi informasi lebih.

    ...
      State:          Waiting
       Reason:       ErrImagePull
      ...
    
  • Running: Menandakan kontainer telah berjalan tanpa masalah. Setelah kontainer masuk ke state Running, jika terdapat hook postStart maka akan dijalankan. State ini juga menampilkan waktu ketika kontainer masuk ke state Running.

    ...
       State:          Running
        Started:      Wed, 30 Jan 2019 16:46:38 +0530
    ...
    
  • Terminated: Menandakan kontainer telah menyelesaikan "tugasnya". Kontainer akan menjadi state ini ketika telah menyelesaikan eksekusi atau terjadi kesalahan. Terlepas dari itu, sebuah alasan dan exit code akan ditampilkan, bersama dengan waktu kontainer mulai dijalankan dan waktu berhenti. Sebelum kontainer masuk ke state Terminated, jika terdapat preStop hook maka akan dijalankan.

    ...
       State:          Terminated
         Reason:       Completed
         Exit Code:    0
         Started:      Wed, 30 Jan 2019 11:45:26 +0530
         Finished:     Wed, 30 Jan 2019 11:45:26 +0530
     ...
    

Pod readiness gate

FEATURE STATE: Kubernetes v1.14 [stable]

Dalam rangka menambahkan ekstensibilitas terhadap kesiapan Pod dengan menggunakan injeksi umpan balik tambahan atau sinyal ke dalam PodStatus, Kubernetes 1.11 memperkenalkan sebuah fitur bernama Pod ready++. Kamu dapat menggunakan field baru ReadinessGate dalam sebuah PodSpec untuk menunjukan kondisi tambahan yang akan dievaluasi untuk kesiapan Pod. Jika Kubernetes tidak dapat menemukan kondisi pada field status.conditions dalam suatu Pod, maka statusnya akan secara otomatis menjadi False. Berikut adalah contoh pemakaiannya:

Kind: Pod
...
spec:
  readinessGates:
    - conditionType: "www.example.com/feature-1"
status:
  conditions:
    - type: Ready  # ini adalah PodCondition yang telah tersedia
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
    - type: "www.example.com/feature-1"   # sebuah PodCondition tambahan
      status: "False"
      lastProbeTime: null
      lastTransitionTime: 2018-01-01T00:00:00Z
  containerStatuses:
    - containerID: docker://abcd...
      ready: true
...

Kondisi Pod yang baru harus memenuhi format label pada Kubernetes. Sejak perintah kubectl patch belum mendukung perubahan status objek, kondisi Pod yang baru harus mengubah melalui aksi PATCH dengan menggunakan salah satu dari KubeClient libraries.

Dengan diperkenalkannya kondisi Pod yang baru, sebuah Pod akan dianggap siap hanya jika memenuhi dua syarat berikut:

  • Semua kontainer dalam Pod telah siap.
  • Semua kontainer yang diatur dalam ReadinessGates bernilai "True".

Untuk memfasilitasi perubahan tersebut terhadap evaluasi kesiapan Pod, dibuatkan sebuah kondisi Pod baru yaitu ContainerReady, untuk dapat menangani kondisi Pod Ready yang sudah ada.

Dalam K8s 1.11, sebagai fitur alpha, fitur "Pod Ready++" harus diaktifkan melalui pengaturan fitur gate pada PodReadinessGates.

Dalam K8s 1.12, fitur tersebut sudah diaktifkan dari awal.

Aturan Menjalankan Ulang

Sebuah PodSpec memiliki field restartPolicy dengan kemungkinan nilai berupa Always, OnFailure, dan Never. Nilai awalnya berupa Always. restartPolicy akan berlaku untuk semua kontainer dalam Pod. Kontainer yang mati dan dijalankan ulang oleh kubelet akan dijalankan ulang dengan jeda waktu yang ekponensial (10s, 20s, 40s, ...) dengan batas atas senilai lima menit. Jeda waktu ini akan diatur ulang setelah sukses berjalan selama 10 menit. Sesuai dengan diskusi pada dokumen Pod, setelah masuk ke suatu node, sebuah Pod tidak akan pindah ke node lain.

Umur Pod

Secara umum, Pod tidak hilang sampai ada yang menghapusnya. Ini mungkin dihapus oleh orang atau pengontrol. Satu pengecualian untuk aturan ini adalah Pod dengan phase bernilai Succeeded atau Failed untuk waktu beberapa lama yang akan berakhir dan secara otomatis akan dihapus. (diatur dalam terminated-pod-gc-threshold pada master)

Tiga tipe pengontrol yang tersedia yaitu:

  • Menggunakan sebuah Job untuk Pod yang diharapkan akan berakhir, sebagai contoh, penghitungan dalam jumlah banyak. Jobs hanyak cocok untuk Pod dengan restartPolicy yang bernilai OnFailure atau Never.

  • Menggunakan sebuah ReplicationController, ReplicaSet, atau Deployment untuk Pod yang tidak diharapkan untuk berakhir, sebagai contoh, web servers. ReplicationControllers hanya cocok digunakan pada Pod dengan restartPolicy yang bernilai Always.

  • Menggunakan sebuah DaemonSet untuk Pod yang akan berjalan hanya satu untuk setiap mesin, karena menyediakan servis yang spesifik untuk suatu mesin.

Ketiga tipe pengontrol ini memiliki sebuah PodTemplate. Direkomdasikan untuk membuat pengontrol yang sesuai dan membiarkan ini membuat Pod, daripada membuat Pod sendiri secara langsung. Karena Pod itu sendiri tidak tahan terhadap gagalnya suatu mesin, namun pengontrol tahan.

Jika node mati atau sambungannya terputus dari klaster, Kubernetes mengatur phase dari semua Pod pada node yang mati untuk menjadi Failed.

Contoh

Contoh Liveness Probe tingkat lanjut

Liveness probe dieksekusi oleh kubelet, jadi semua permintaan akan dilakukan di dalam namespace jaringan kubelet.

apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-http
spec:
  containers:
  - args:
    - liveness
    image: registry.k8s.io/e2e-test-images/agnhost:2.40
    livenessProbe:
      httpGet:
        # ketika "host" tidak ditentukan, "PodIP" akan digunakan
        # host: my-host
        # ketika "scheme" tidak ditentukan, _scheme_ "HTTP" akan digunakan. Hanya "HTTP" and "HTTPS" yang diperbolehkan
        # scheme: HTTPS
        path: /healthz
        port: 8080
        httpHeaders:
        - name: X-Custom-Header
          value: Awesome
      initialDelaySeconds: 15
      timeoutSeconds: 1
    name: liveness

Contoh State

  • Pod sedang berjalan dan memiliki sebuah kontainer. Kontainer berhenti dengan sukses.

    • Mencatat event penyelesaian.
    • Jika nilai restartPolicy adalah:
      • Always: Jalankan ulang kontainer; nilai phase Pod akan tetap Running.
      • OnFailure: nilai phase Pod akan berubah menjadi Succeeded.
      • Never: nilai phase Pod akan berubah menjadi Succeeded.
  • Pod sedang berjalan dan memiliki sebuah kontainer. Kontainer berhenti dengan kegagalan.

    • Mencatat event kegagalan.
    • Jika nilai restartPolicy adalah:
      • Always: Jalankan ulang kontainer, nilai phase Pod akan tetap Running.
      • OnFailure: Jalankan ulang kontainer, nilai phase Pod akan tetap Running.
      • Never: nilai phase Pod akan menjadi Failed.
  • Pod sedang berjalan dan memiliki dua kontainer. Kontainer pertama berhenti dengan kegagalan.

    • Mencatat event kegagalan.
    • Jika nilai restartPolicy adalah:
      • Always: Jalankan ulang kontainer, nilai phase Pod akan tetap Running.
      • OnFailure: Jalankan ulang kontainer, nilai phase Pod akan tetap Running.
      • Never: Tidak akan menjalankan ulang kontainer, nilai phase Pod akan tetap Running.
    • Jika kontainer pertama tidak berjalan dan kontainer kedua berhenti:
      • Mencatat event kegagalan.
      • Jika nilai restartPolicy adalah:
        • Always: Jalankan ulang kontainer, nilai phase Pod akan tetap Running.
        • OnFailure: Jalankan ulang kontainer, nilai phase Pod akan tetap Running.
        • Never: nilai phase Pod akan menjadi Failed.
  • Pod sedang berjalan dan memiliki satu kontainer. Kontainer berhenti karena kehabisan memory.

    • Kontainer diberhentikan dengan kegagalan.
    • Mencatat kejadian kehabisan memory (OOM)
    • Jika nilai restartPolicy adalah:
      • Always: Jalankan ulang kontainer, nilai phase Pod akan tetap Running.
      • OnFailure: Jalankan ulang kontainer, nilai phase Pod akan tetap Running.
      • Never: Mencatat kejadian kegagalan, nilai phase Pod akan menjadi Failed.
  • Pod sedang berjalan dan sebuah disk mati.

    • Menghentikan semua kontainer.
    • Mencatat kejadian yang sesuai.
    • Nilai phase Pod menjadi Failed.
    • Jika berjalan menggunakan pengontrol, maka Pod akan dibuat ulang di tempat lain.
  • Pod sedang berjalan, dan node mengalami segmented out.

    • Node pengontrol menunggu sampai suatu batas waktu.
    • Node pengontrol mengisi nilai phase Pod menjadi Failed.
    • Jika berjalan menggunakan pengontrol, maka Pod akan dibuat ulang di tempat lain.

Selanjutnya

4 - Init Container

Halaman ini menyediakan ikhtisar untuk Init Container, yaitu Container khusus yang dijalankan sebelum Container aplikasi dan berisi skrip peralatan atau setup yang tidak tersedia di dalam image dari Container aplikasi.

Fitur ini telah keluar dari trek Beta sejak versi 1.6. Init Container dapat dispesifikasikan di dalam PodSpec bersama dengan array containers aplikasi. Nilai anotasi beta akan tetap diperhitungkan dan akan menimpa nilai pada PodSpec, tetapi telah ditandai sebagai kedaluarsa pada versi 1.6 dan 1.7. Pada versi 1.8, anotasi beta tidak didukung lagi dan harus diganti menjadi nilai pada PodSpec.

Memahami Init Container

Sebuah Pod dapat memiliki beberapa Container yang berjalan di dalamnya, dan dapat juga memiliki satu atau lebih Init Container, yang akan berjalan sebelum Container aplikasi dijalankan.

Init Container sama saja seperti Container biasa, kecuali:

  • Mereka selalu berjalan hingga selesai.
  • Setiap Init Container harus selesai secara sukses sebelum Init Container berikutnya dijalankan.

Jika sebuah Init Container tidak selesai secara sukses untuk sebuah Pod, Kubernetes akan mengulang kembali Pod tersebut secara terus menerus hingga Init Container selesai secara sukses. Tetapi, jika Pod tersebut memiliki nilai restartPolicy berupa Never, Pod tersebut tidak akan diulang kembali.

Untuk menspesifikasikan sebuah Container sebagai Init Container, tambahkan kolom initContainers pada PodSpec sebagai sebuah array JSON yang berisi objek dengan tipe Container, berdampingan dengan array containers aplikasi. Status-status dari Init Container dikembalikan di kolom .status.initContainerStatuses sebagai sebuah array dari status-status Container (mirip seperti kolom status.containerStatuses)

Perbedaan dengan Container biasa

Init Container mendukung semua kolom dan fitur dari Container aplikasi, termasuk konfigurasi limit sumber daya, volume, dan keamanan. Tetapi, request dan limit sumber daya dari sebuah Init Container ditangani dengan cara yang sedikit berbeda, yang didokumentasikan di bagian Sumber Daya di bawah. Juga, Init Container tidak mendukung readiness probe karena mereka harus berjalan hingga selesai sebelum Pod dapat siap.

Jika beberapa Init Container dispesifikasikan untuk sebuah Pod, Container-container tersebut akan dijalankan satu per satu secara berurutan. Setiap Init Container harus selesai secara sukses sebelum yang berikutnya dapat berjalan. Saat semua Init Container telah berjalan hingga selesai, Kubernetes akan menginisialisasi Pod dan menjalankan Container aplikasi seperti biasa.

Apa kegunaan Init Container?

Karena Init Container memiliki image yang berbeda dengan Container aplikasi, mereka memiliki beberapa kelebihan untuk kode yang berhubungan dengan dimulainya Init Container:

  • Mereka dapat berisi dan menjalankan skrip peralatan yang tidak diinginkan untuk berada di dalam image Container aplikasi karena alasan keamanan.
  • Mereka dapat berisi skrip peralatan atau setup yang tidak tersedia di dalam image aplikasi. Misalnya, kita tidak perlu membuat image dengan instruksi FROM dari image lainnya hanya untuk menggunakan peralatan seperti sed, awk, python, atau dig pada saat setup.
  • Peran builder atau deployer dari image dapat bekerja secara independen tanpa harus digabung untuk membuat satu image aplikasi.
  • Mereka menggunakan namespace Linux, sehingga mereka dapat memiliki sudut pandang filesystem yang berbeda dengan Container aplikasi. Oleh karenanya, mereka dapat diberikan akses terhadap Secret yang tidak boleh diakses oleh Container aplikasi.
  • Mereka berjalan hingga selesai sebelum Container aplikasi manapun dimulai, sedangkan Container aplikasi dijalankan secara paralel, sehingga Init Container menyediakan cara yang mudah untuk menunda dijalankannya Container aplikasi hingga ketentuan-ketentuan yang diinginkan dipenuhi.

Contoh-contoh

Berikut beberapa contoh kasus penggunaan Init Container:

  • Menunggu sebuah Service untuk dibuat dengan perintah shell seperti:

    for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
    
  • Mendaftarkan suatu Pod ke sebuah peladen terpisah dari downward API dengan perintah seperti:

    `curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'`
    
  • Menunggu beberapa waktu sebelum menjalankan Container aplikasi dengan perintah seperti sleep 60.

  • Mengklon sebuah git repository ke dalam sebuah volume.

  • Menaruh nilai-nilai tertentu ke dalam sebuah file konfigurasi dan menjalankan peralatan template untuk membuat file konfigurasi secara dinamis untuk Container aplikasi utama. Misalnya, untuk menaruh nilai POD_IP ke dalam sebuah konfigurasi dan membuat konfigurasi aplikasi utama menggunakan Jinja.

Contoh-contoh penggunaan yang lebih detail dapat dilihat pada dokumentasi StatefulSet dan petunjuk Produksi Pod.

Menggunakan Init Container

File YAML untuk Kubernetes 1.5 berikut menguraikan sebuah Pod sederhana yang memiliki dua buah Init Container. Pod pertama menunggu myservice dan yang kedua menunggu mydb. Saat kedua Init Container tersebut sudah selesai, Podnya akan dijalankan.

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
  annotations:
    pod.beta.kubernetes.io/init-containers: '[
        {
            "name": "init-myservice",
            "image": "busybox:1.28",
            "command": ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
        },
        {
            "name": "init-mydb",
            "image": "busybox:1.28",
            "command": ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
        }
    ]'
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']

Ada sintaksis baru pada Kubernetes 1.6, walaupun sintaksis anotasi yang lama tetap akan bekerja untuk versi 1.6 dan 1.7. Sintaksis yang baru harus digunakan untuk versi 1.8 ke atas. Deklarasi Init Container dipindahkan ke dalam spec:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox:1.28
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

Sintaksis versi 1.5 tetap akan bekerja pada versi 1.6 dan 1.7, tetapi kami menyarankan untuk menggunakan sintaksis versi 1.6. Pada Kubernetes 1.6, Init Container dijadikan sebagai sebuah kolom di dalam API Kubernetes. Anotasi beta tetap akan diperhitungkan pada versi 1.6 dan 1.7, tetapi tidak didukung lagi pada versi 1.8 ke atas.

File YAML di bawah menguraikan Service mydb dan myservice.

apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9377

Pod ini dapat dijalankan dan di-debug dengan menggunakan perintah berikut:

kubectl apply -f myapp.yaml
pod/myapp-pod created
kubectl get -f myapp.yaml
NAME        READY     STATUS     RESTARTS   AGE
myapp-pod   0/1       Init:0/2   0          6m
kubectl describe -f myapp.yaml
Name:          myapp-pod
Namespace:     default
[...]
Labels:        app=myapp
Status:        Pending
[...]
Init Containers:
  init-myservice:
[...]
    State:         Running
[...]
  init-mydb:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Containers:
  myapp-container:
[...]
    State:         Waiting
      Reason:      PodInitializing
    Ready:         False
[...]
Events:
  FirstSeen    LastSeen    Count    From                      SubObjectPath                           Type          Reason        Message
  ---------    --------    -----    ----                      -------------                           --------      ------        -------
  16s          16s         1        {default-scheduler }                                              Normal        Scheduled     Successfully assigned myapp-pod to 172.17.4.201
  16s          16s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulling       pulling image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Pulled        Successfully pulled image "busybox"
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Created       Created container with docker id 5ced34a04634; Security:[seccomp=unconfined]
  13s          13s         1        {kubelet 172.17.4.201}    spec.initContainers{init-myservice}     Normal        Started       Started container with docker id 5ced34a04634
kubectl logs myapp-pod -c init-myservice # Memeriksa Init Container pertama
kubectl logs myapp-pod -c init-mydb      # Memeriksa Init Container kedua

Saat kita menjalankan Service mydb dan myservice, kita dapat melihat Init Container telah selesai dan myapp-pod pun dibuat:

kubectl apply -f services.yaml
service/myservice created
service/mydb created
kubectl get -f myapp.yaml
NAME        READY     STATUS    RESTARTS   AGE
myapp-pod   1/1       Running   0          9m

Contoh ini sangat sederhana, tetapi dapat memberikan sedikit petunjuk bagi kamu untuk membuat Init Container sendiri.

Perilaku mendetail

Saat dimulainya sebuah Pod, Init Container dijalankan secara berurutan, setelah jaringan dan volume telah diinisialisasi. Setiap Init Container harus selesai dan keluar secara berhasil sebelum yang berikutnya dijalankan. Jika ada Init Container yang gagal dijalankan atau keluar secara gagal, dia akan diulang kembali sesuai dengan restartPolicy yang dimiliki Pod. Tetapi, jika restartPolicy Pod disetel dengan nilai Always, Init Container akan menggunakan strategi RestartPolicy OnFailure.

Sebuah Pod tidak dapat masuk ke status Ready hingga semua Init Container berhasil selesai. Port di sebuah Init Container tidak diagregasikan di dalam sebuah Service. Sebuah Pod yang sedang diinisalisasikan akan masuk ke dalam status Pending, tetapi akan memiliki kondisi Initialized yang disetel menjadi true.

Jika sebuah Pod diulang kembali, semua Init Container harus dijalankan kembali.

Perubahan pada spesifikasi Init Container dibatasi hanya pada kolom image pada Init Container. Mengganti kolom image sebuah Init Container sama dengan mengulang kembali Pod tersebut.

Karena Init Container dapat diulang kembali, dicoba ulang, atau dijalankan ulang, Init Container sebaiknya bersifat idempotent. Khususnya, kode yang menulis ke dalam file pada EmptyDir sebaiknya dipersiapkan untuk menangani kemungkinan jika file keluaran yang diharapkan sudah ada di dalam EmptyDir tersebut.

Init Container memiliki semua kolom yang dimiliki oleh Container aplikasi. Tetapi, Kubernetes melarang penggunaan readinessProbe karena Init Container tidak dapat mendefinisikan/menggunakan readiness probe setelah selesai/keluar secara berhasil. Hal ini dipaksakan saat proses validasi.

Gunakan activeDeadlineSeconds pada Pod dan livenessProbe pada Container untuk mencegah Init Container gagal terus menerus. Nilai activeDeadlineSeconds berlaku juga terhadap Init Container.

Nama setiap Container aplikasi dan Init Container pada sebuah Pod haruslah unik; Kesalahan validasi akan terjadi jika ada Container atau Init Container yang memiliki nama yang sama.

API untuk sidecar containers

FEATURE STATE: Kubernetes v1.28 [alpha]

Mulai dari Kubernetes 1.28 dalam mode alpha, terdapat fitur yang disebut SidecarContainers yang memungkinkan Anda untuk menentukan restartPolicy untuk kontainer init yang independen dari Pod dan kontainer init lainnya. [Probes] (/docs/concepts/workloads/pods/pod-lifecycle/#types-of-probe) juga dapat ditambahkan untuk mengendalikan siklus hidup mereka.

Jika sebuah kontainer init dibuat dengan restartPolicy yang diatur sebagai Always, maka kontainer ini akan mulai dan tetap berjalan selama seluruh masa hidup Pod, yang berguna untuk menjalankan layanan pendukung yang terpisah dari kontainer aplikasi utama.

Jika sebuah readinessProbe ditentukan untuk kontainer init ini, hasilnya akan digunakan untuk menentukan status siap dari Pod.

Karena kontainer-kontainer ini didefinisikan sebagai kontainer init, mereka mendapatkan manfaat dari urutan dan jaminan berurutan yang sama seperti kontainer init lainnya, yang memungkinkan mereka dicampur dengan kontainer init lainnya dalam aliran inisialisasi Pod yang kompleks.

Dibandingkan dengan kontainer init reguler, kontainer init tipe sidecar terus berjalan, dan kontainer init berikutnya dapat mulai menjalankan saat kubelet telah menetapkan status kontainer started menjadi benar untuk kontainer init tipe sidecar. Status tersebut menjadi benar karena ada proses yang berjalan dalam kontainer dan tidak ada probe awal yang ditentukan, atau sebagai hasil dari keberhasilan startupProbe.

Fitur ini dapat digunakan untuk mengimplementasikan pola kontainer sidecar dengan lebih tangguh, karena kubelet selalu akan me-restart kontainer sidecar jika kontainer tersebut gagal.

Berikut adalah contoh Deployment dengan dua kontainer, salah satunya adalah sidecar:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: alpine:latest
          command: ['sh', '-c', 'while true; do echo "logging" >> /opt/logs.txt; sleep 1; done']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          restartPolicy: Always
          command: ['sh', '-c', 'tail -F /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      volumes:
        - name: data
          emptyDir: {}

Fitur ini juga berguna untuk menjalankan Job dengan sidecar, karena kontainer sidecar tidak akan mencegah Job untuk menyelesaikan tugasnya setelah kontainer utama selesai.

Berikut adalah contoh sebuah Job dengan dua kontainer, salah satunya adalah sidecar:

apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  template:
    spec:
      containers:
        - name: myjob
          image: alpine:latest
          command: ['sh', '-c', 'echo "logging" > /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          restartPolicy: Always
          command: ['sh', '-c', 'tail -F /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      restartPolicy: Never
      volumes:
        - name: data
          emptyDir: {}

Sumber Daya

Karena eksekusi Init Container yang berurutan, aturan-aturan untuk sumber daya berlaku sebagai berikut:

  • Yang tertinggi antara request atau limit sumber daya yang didefinisikan pada semua Init Container adalah request/limit inisialisasi yang berlaku.
  • request/limit sumber daya Pod yang berlaku adalah yang paling besar diantara:
    • Jumah request/limit semua Container aplikasi untuk suatu sumber daya.
    • request/limit inisialisasi yang berlaku untuk suatu sumber daya.
  • Penjadwalan dilakukan berdasarkan request/limit (Pod) yang berlaku, yang berarti bahwa Init Container dapat mengambil sumber daya inisialisasi yang tidak digunakan selama umur Pod tersebut.
  • Tingkat QoS yang berlaku milik Pod adalah sama dengan tingkat QoS untuk Init Container dan Container aplikasi.

ResourceQuota dan limitedResources diberlakukan berdasarkan request dan limit Pod yang berlaku.

Cgroup pada tingat Pod didasarkan pada request dan limit Pod yang berlaku, sama dengan scheduler.

Alasan Pod diulang kembali

Pod dapat diulang kembali, yang berakibat pada diulangnya eksekusi Init Container, diakibatkan oleh beberapa alasan berikut:

  • Seorang pengguna memperbarui PodSpec, mengakibatkan image Init Container berubah. Perubahan apapun pada image Init Container akan mengulang kembali Pod tersebut. Perubahan pada image Container aplikasi hanya mengulang kembali Container aplikasi yang bersangkutan.
  • Infrastruktur Container Pod diulang kembali. Hal ini jarang terjadi, dan hanya dapat dilakukan oleh seseorang yang memiliki akses root pada node yang bersangkutan.
  • Semua Container di dalam Pod diterminasi, dengan nilai restartPolicy yang disetel sebagai Always, memaksa pengulangan kembali, dan catatan selesainya Init Container telah hilang karena garbage collection.

Dukungan dan kompatibilitas

Sebuah klaster dengan versi Apiserver 1.6.0 ke atas mendukung Init Container melalui kolom .spec.initContainers. Versi-versi sebelumnya mendukung Init Container melalui anotasi alpha atau beta. Kolom .spec.initContainers juga diduplikasikan dalam bentuk anotasi alpha dan beta agar Kubelet versi 1.3.0 ke atas dapat menjalankan Init Container, dan agar Apiserver versi 1.6 dapat dengan aman dikembalikan ke versi 1.5.x tanpa kehilangan fungsionalitas Pod-pod yang telah dibuat sebelumnya.

Pada Apiserver dan Kubelet versi 1.8.0 ke atas, dukungan untuk anotasi alpha dan beta telah dihapus, sehingga dibutuhkan konversi (manual) dari anotasi yang telah kedaluwarsa tersebut ke dalam bentuk kolom .spec.initContainers.

Selanjutnya

5 - Batasan Persebaran Topologi Pod

FEATURE STATE: Kubernetes v1.18 [beta]

Kamu dapat menggunakan batasan perseberan topologi (topology spread constraints) untuk mengatur bagaimana Pod akan disebarkan pada klaster yang ditetapkan sebagai failure-domains, seperti wilayah, zona, Node dan domain topologi yang ditentukan oleh pengguna. Ini akan membantu untuk mencapai ketersediaan yang tinggi dan juga penggunaan sumber daya yang efisien.

Persyaratan

Mengaktifkan Gerbang Fitur

Gerbang fitur (feature gate) EvenPodsSpread harus diaktifkan untuk API Server dan penjadwal (_scheduler_).

Label Node

Batasan persebaran topologi bergantung dengan label pada Node untuk menentukan domain topologi yang memenuhi untuk semua Node. Misalnya saja, sebuah Node bisa memiliki label sebagai berikut: node=node1,zone=us-east-1a,region=us-east-1

Misalkan kamu memiliki klaster dengan 4 Node dengan label sebagai berikut:

NAME    STATUS   ROLES    AGE     VERSION   LABELS
node1   Ready    <none>   4m26s   v1.16.0   node=node1,zone=zoneA
node2   Ready    <none>   3m58s   v1.16.0   node=node2,zone=zoneA
node3   Ready    <none>   3m17s   v1.16.0   node=node3,zone=zoneB
node4   Ready    <none>   2m43s   v1.16.0   node=node4,zone=zoneB

Maka klaster tersebut secara logika akan dilihat sebagai berikut:

+---------------+---------------+
|     zoneA     |     zoneB     |
+-------+-------+-------+-------+
| node1 | node2 | node3 | node4 |
+-------+-------+-------+-------+

Tanpa harus memberi label secara manual, kamu dapat menggunakan [label ternama] (/docs/reference/kubernetes-api/labels-annotations-taints/) yang terbuat dan terkumpulkan secara otomatis pada kebanyakan klaster.

Batasan Persebaran untuk Pod

API

Field pod.spec.topologySpreadConstraints diperkenalkan pada versi 1.16 sebagai berikut:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  topologySpreadConstraints:
  - maxSkew: <integer>
    minDomains: <integer>
    topologyKey: <string>
    whenUnsatisfiable: <string>
    labelSelector: <object>

Kamu dapat mendefinisikan satu atau lebih topologySpreadConstraint untuk menginstruksikan kube-scheduler mengenai cara peletakan tiap Pod baru dengan menggunakan kondisi Pod yang sudah ada dalam klaster kamu. Field yang ada adalah:

  • maxSkew menentukan batasan yang menandakan Pod tidak tersebar secara merata. Ini merupakan nilai maksimal dari selisih jumlah Pod yang sama untuk setiap 2 domain topologi yang sama. Nilai ini harus lebih dari 0.
  • topologyKey adalah kunci dari label Node. Jika terdapat dua Node memiliki label dengan kunci ini dan memiliki nilai yang identik untuk label tersebut, maka penjadwal akan menganggap kedua Noode dalam topologi yang sama. Penjadwal akan mencoba untuk menyeimbangkan jumlah Pod dalam setiap domain topologi.
  • whenUnsatisfiable mengindikasikan cara menangani Pod yang tidak memenuhi batasan persebaran:
    • DoNotSchedule (default) memberitahukan penjadwal untuk tidak menjadwalkan Pod tersebut.
    • ScheduleAnyway memberitahukan penjadwal untuk tetap menjadwalkan Pod namun tetap menjaga ketidakseimbangan Node sekecil mungkin.
  • labelSelector digunakan untuk mencari Pod yang sesuai. Pod dengan label yang sama dengan ini akan dihitung untuk menentukan jumlah Pod dalam domain topologi yang sesuai. Silakan baca Label dan Selector untuk lebih detailnya.

Kamu juga bisa membaca lebih detail mengenai field ini dengan menjalankan perintah kubectl explain Pod.spec.topologySpreadConstraints.

Contoh: Satu TopologySpreadConstraint

Misalkan kamu memiliki klaster dengan 4 Node dimana 3 Pod berlabel foo:bar terdapat pada node1, node2 dan node3 (P merepresentasikan Pod):

+---------------+---------------+
|     zoneA     |     zoneB     |
+-------+-------+-------+-------+
| node1 | node2 | node3 | node4 |
+-------+-------+-------+-------+
|   P   |   P   |   P   |       |
+-------+-------+-------+-------+

Jika kita ingin Pod baru akan disebar secara merata berdasarkan Pod yang telah ada pada semua zona, maka spec bernilai sebagai berikut:

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

topologyKey: zone berarti persebaran merata hanya akan digunakan pada Node dengan pasangan label "zone: ". whenUnsatisfiable: DoNotSchedule memberitahukan penjadwal untuk membiarkan tetap ditunda jika Pod yang baru tidak memenuhi batasan yang diterapkan.

Jika penjadwal menempatkan Pod baru pada "zoneA", persebaran Pod akan menjadi [3, 1], menjadikan ketidakseimbangan menjadi bernilai 2 (3 - 1), yang mana akan melanggar batasan maxSkew: 1. Dalam contoh ini, Pod baru hanya dapat ditempatkan pada "zoneB":

+---------------+---------------+      +---------------+---------------+
|     zoneA     |     zoneB     |      |     zoneA     |     zoneB     |
+-------+-------+-------+-------+      +-------+-------+-------+-------+
| node1 | node2 | node3 | node4 |  OR  | node1 | node2 | node3 | node4 |
+-------+-------+-------+-------+      +-------+-------+-------+-------+
|   P   |   P   |   P   |   P   |      |   P   |   P   |  P P  |       |
+-------+-------+-------+-------+      +-------+-------+-------+-------+

Kamu dapat mengatur spesifikasi Pod untuk memenuhi beberapa persyaratan berikut:

  • Ubah nilai maxSkew menjadi lebih besar, misal "2", sehingga Pod baru dapat ditempatkan pada "zoneA".
  • Ubah nilai topologyKey menjadi "node" agar Pod disebarkan secara merata pada semua Node, bukan zona. Pada contoh di atas, jika maxSkew tetap bernilai "1", maka Pod baru hanya akan ditempatkan pada "node4".
  • Ubah nilai whenUnsatisfiable: DoNotSchedule menjadi whenUnsatisfiable: ScheduleAnyway untuk menjamin agar semua Pod baru akan tetap dijadwalkan (misalkan saja API penjadwalan lain tetap terpenuhi). Namun, ini lebih suka ditempatkan pada domain topologi yang memiliki lebih sedikit Pod yang sesuai. (Harap diperhatikan bahwa preferensi ini digabungkan bersama dengan prioritas penjadwalan internal yang lain, seperti rasio penggunaan sumber daya, dan lain sebagainya.)

Contoh: Beberapa TopologySpreadConstraint

Ini dibuat berdasarkan contoh sebelumnya. Misalkan kamu memiliki klaster dengan 4 Node dengan 3 Pod berlabel foo:bar yang ditempatkan pada node1, node2 dan node3. (P merepresentasikan Pod):

+---------------+---------------+
|     zoneA     |     zoneB     |
+-------+-------+-------+-------+
| node1 | node2 | node3 | node4 |
+-------+-------+-------+-------+
|   P   |   P   |   P   |       |
+-------+-------+-------+-------+

Kamu dapat menggunakan 2 TopologySpreadConstraint untuk mengatur persebaran Pod pada zona dan Node:

kind: Pod
apiVersion: v1
metadata:
  name: mypod
  labels:
    foo: bar
spec:
  topologySpreadConstraints:
  - maxSkew: 1
    topologyKey: zone
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  - maxSkew: 1
    topologyKey: node
    whenUnsatisfiable: DoNotSchedule
    labelSelector:
      matchLabels:
        foo: bar
  containers:
  - name: pause
    image: registry.k8s.io/pause:3.1

Dalam contoh ini, untuk memenuhi batasan pertama, Pod yang baru hanya akan ditempatkan pada "zoneB", sedangkan untuk batasan kedua, Pod yang baru hanya akan ditempatkan pada "node4". Maka hasil dari 2 batasan ini akan digunakan (AND), sehingga opsi untuk menempatkan Pod hanya pada "node4".

Beberapa batasan dapat berujung pada konflik. Misalnya saja kamu memiliki klaster dengan 3 Node pada 2 zona berbeda:

+---------------+-------+
|     zoneA     | zoneB |
+-------+-------+-------+
| node1 | node2 | node3 |
+-------+-------+-------+
|  P P  |   P   |  P P  |
+-------+-------+-------+

Jika kamu menerapkan "two-constraints.yaml" pada klaster ini, kamu akan mendapatkan "mypod" tetap dalam kondisi Pending. Ini dikarenakan oleh: untuk memenuhi batasan pertama, "mypod" hanya dapat ditempatkan pada "zoneB", sedangkan untuk batasan kedua, "mypod" hanya dapat ditempatkan pada "node2". Tidak ada hasil penggabungan dari "zoneB" dan "node2".

Untuk mengatasi situasi ini, kamu bisa menambahkan nilai maxSkew atau mengubah salah satu dari batasan untuk menggunakan whenUnsatisfiable: ScheduleAnyway.

Konvensi

Ada beberapa konvensi implisit yang perlu diperhatikan di sini:

  • Hanya Pod dengan Namespace yang sama dengan Pod baru yang bisa menjadi kandidat yang cocok.

  • Node tanpa memiliki topologySpreadConstraints[*].topologyKey akan dilewatkan. Ini berarti:

    1. Pod yang ditempatkan pada Node tersebut tidak berpengaruh pada perhitungan maxSkew. Dalam contoh di atas, misalkan "node1" tidak memiliki label "zone", maka kedua Pod tidak diperhitungkan dan menyebabkan Pod yang baru akan dijadwalkan masuk ke "zoneA".
    2. Pod yang baru tidak memiliki kesempatan untuk dijadwalkan ke Node tersebut, pada contoh di atas, misalkan terdapat "node5" dengan label {zone-typo: zoneC} bergabung dalam klaster, Node ini akan dilewatkan karena tidak memiliki label dengan kunci "zone".
  • Harap diperhatikan mengenai hal yang terjadi jika nilai topologySpreadConstraints[*].labelSelector pada Pod yang baru tidak sesuai dengan labelnya. Pada contoh di atas, jika kita menghapus label pada Pod yang baru, maka Pod akan tetap ditempatkan pada "zoneB" karena batasan yang ada masih terpenuhi. Namun, setelah ditempatkan, nilai ketidakseimbangan pada klaster masih tetap tidak berubah, zoneA tetap memiliki 2 Pod dengan label {foo:bar} dan zoneB memiliki 1 Pod dengan label {foo:bar}. Jadi jika ini tidak yang kamu harapkan, kami menyarankan nilai dari topologySpreadConstraints[*].labelSelector disamakan dengan labelnya.

  • Jika Pod yang baru memiliki spec.nodeSelector atau spec.affinity.nodeAffinity, Node yang tidak sesuai dengan nilai tersebut akan dilewatkan.

    Misalkan kamu memiliki klaster dengan 5 Node dari zoneA sampai zoneC:

    +---------------+---------------+-------+
    |     zoneA     |     zoneB     | zoneC |
    +-------+-------+-------+-------+-------+
    | node1 | node2 | node3 | node4 | node5 |
    +-------+-------+-------+-------+-------+
    |   P   |   P   |   P   |       |       |
    +-------+-------+-------+-------+-------+
    

    dan kamu mengetahui bahwa "zoneC" harus tidak diperhitungkan. Dalam kasus ini, kamu dapat membuat berkas yaml seperti di bawah, jadi "mypod" akan ditempatkan pada "zoneB", bukan "zoneC". Demikian juga spec.nodeSelector akan digunakan.

    kind: Pod
      apiVersion: v1
      metadata:
        name: mypod
        labels:
          foo: bar
      spec:
        topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              foo: bar
        affinity:
          nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
              nodeSelectorTerms:
              - matchExpressions:
                - key: zone
                  operator: NotIn
                  values:
                  - zoneC
        containers:
        - name: pause
          image: registry.k8s.io/pause:3.1

Batasan default pada tingkat klaster

FEATURE STATE: Kubernetes v1.18 [alpha]

Ini memungkinkan untuk mengatur batasan persebaran topologi bawaan untuk klaster. Batasan persebaran topologi bawaan akan digunakan pada Pod jika dan hanya jika:

  • Hal ini tidak mendefinisikan batasan apapun pada .spec.topologySpreadConstraints.
  • Hal ini milik sebuah Service, ReplicationController, ReplicaSet atau StatefulSet.

Batasan bawaan akan diatur sebagai bagian dari argumen pada plugin PodTopologySpread di dalam sebuah profil penjadwalan. Batasan dispesifikasikan dengan API yang sama dengan di atas, kecuali bagian labelSelector harus kosong. selector akan dihitung dari Service, ReplicationController, ReplicaSet atau StatefulSet yang dimiliki oleh Pod tersebut.

Sebuah contoh konfigurasi sebagai berikut:

apiVersion: kubescheduler.config.k8s.io/v1alpha2
kind: KubeSchedulerConfiguration

profiles:
  - pluginConfig:
      - name: PodTopologySpread
        args:
          defaultConstraints:
            - maxSkew: 1
              topologyKey: topology.kubernetes.io/zone
              whenUnsatisfiable: ScheduleAnyway

Perbandingan dengan PodAffinity/PodAntiAffinity

Di Kubernetes, arahan yang terkait dengan "Afinitas" mengontrol bagaimana Pod dijadwalkan - lebih terkumpul atau lebih tersebar.

  • Untuk PodAffinity, kamu dapat mencoba mengumpulkan beberapa Pod ke dalam suatu domain topologi yang memenuhi syarat.
  • Untuk PodAntiAffinity, hanya satu Pod yang dalam dijadwalkan pada sebuah domain topologi.

Fitur "EvenPodsSpread" memberikan opsi fleksibilas untuk mendistribusikan Pod secara merata pada domain topologi yang berbeda, untuk meraih ketersediaan yang tinggi atau menghemat biaya. Ini juga dapat membantu saat perbaruan bergilir dan menaikan jumlah replika dengan lancar. Silakan baca motivasi untuk lebih detail.

Limitasi yang diketahui

Pada versi 1.18, dimana fitur ini masih Beta, beberapa limitasi yang sudah diketahui:

  • Pengurangan jumlah Deployment akan membuat ketidakseimbangan pada persebaran Pod.
  • Pod yang cocok pada tainted Node akan dihargai. Lihat Issue 80921

6 - Pod Preset

Halaman ini menyajikan gambaran umum tentang PodPreset, yang merupakan objek untuk memasukkan informasi tertentu ke dalam Pod pada saat waktu penciptaan. Informasi dapat berupa secret, volume, volume mount, dan variabel environment.

Memahami Pod Preset


Sebuah Pod Preset adalah sebuah resource API untuk memasukkan kebutuhan runtime tambahan ke dalam sebuah Pod pada saat waktu penciptaan. Kamu akan menggunakan label selector untuk menunjuk Pod dimana Pod Preset diterapkan.

Menggunakan sebuah Pod Preset memungkinkan pembuat templat pod untuk tidak menyediakan secara eksplisit semua informasi untuk setiap pod. Dengan demikian, pembuat templat pod yang mengkonsumsi sebuah service spesifik tidak perlu tahu semua detail-detail tentang service tersebut.

Untuk informasi lebih lanjut mengenai latar belakang lihat proposal desain untuk PodPreset.

Bagaimana Cara Kerja Pod Preset


Kubernetes menyediakan sebuah admission controller (PodPreset) dimana, ketika diaktifkan, PodPreset diterapkan kepada permintaan penciptaan Pod yang akan datang. Ketika sebuah penciptaan Pod terjadi, sistem melakukan hal-hal berikut:

  1. Mengambil semua PodPreset yang tersedia untuk digunakan.
  2. Cek jika label selector dari salah satu PodPreset cocok dengan label pada pod yang sedang diciptakan.
  3. Usaha untuk menggabungkan berbagai resource didefinisikan oleh PodPreset ke dalam Pod yang sedang diciptakan.
  4. Ketika terjadi galat, lempar sebuah event yang mendokumentasikan galat penggabungan dalam pod, dan membuat pod tanpa salah satu resource dari PodPreset.
  5. Anotasikan hasil spesifikasi Pod yang telah dimodifikasi untuk menunjukkan bahwa Pod telah dimodifikasi oleh sebuah PodPreset. Anotasi berupa podpreset.admission.kubernetes.io/podpreset-<nama pod-preset>: "<versi resource>".

Tiap Pod akan bisa dipasangkan oleh nol atau lebih PodPreset; dan tiap PodPreset bisa diterapkan ke nol atau lebih Pod. Ketika sebuah PodPreset diterapkan ke satu atau lebih Pod, Kubernetes memodifikasi Pod Spec. Untuk perubahan terhadap Env,EnvFrom, dan VolumeMount, Kubernetes memodifikasi spesifikasi kontainer untuk semua kontainer di dalam Pod; Untuk perubahan terhadap Volume, Kubernetes memodifikasi Pod Spec.

Menonaktifkan Pod Preset untuk sebuah Pod Spesifik

Mungkin akan ada keadaan dimana kamu menginginkan sebuah Pod tidak bisa diubah oleh sebuah mutasi PodPreset. Pada kasus ini, kamu bisa menambahkan sebuah anotasi pada Pod Spec dalam bentuk: podpreset.admission.kubernetes.io/exclude: "true".

Mengaktifkan Pod Preset


Dalam rangka untuk menggunakan Pod Preset di dalam klaster kamu, kamu harus memastikan hal berikut:

  1. Kamu telah mengaktifkan tipe API settings.k8s.io/v1alpha1/podpreset. Sebagai contoh, ini bisa dilakukan dengan menambahkan settings.k8s.io/v1alpha1=true di dalam opsi --runtime-config untuk API server. Dalam minikube tambahkan argumen berikut --extra-config=apiserver.runtime-config=settings.k8s.io/v1alpha1=true saat menginisialisasi klaster.

  2. Kamu telah mengaktifkan admission controller dari PodPreset. Salah satu cara untuk melakukannya adalah dengan menambahkan PodPreset di dalam nilai opsi --enable-admission-plugins yang dispesifikasikan untuk API server. Dalam minikube tambahkan argumen berikut

    --extra-config=apiserver.enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,PodPreset
    

    saat menginisialisasi klaster.

  3. Kamu telah membuat objek PodPreset pada namespace yang kamu gunakan dengan cara mendefinisikan Pod Preset.

Selanjutnya

7 - Disrupsi

Petunjuk ini ditujukan pada pemilik aplikasi yang meninginkan aplikasinya memiliki ketersediaan yang tinggi, sehingga butuh untuk mengerti jenis-jenis Disrupsi yang dapat terjadi pada Pod-pod.

Petunjuk ini juga ditujukan pada administrator klaster yang ingin melakukan berbagai tindakan otomasi pada klaster, seperti pembaruan dan autoscaling klaster.

Disrupsi yang Disengaja dan Tidak Disengaja

Pod-pod tidak akan terhapus sampai sesuatu (orang ataupun pengendali) menghancurkan mereka atau ada kesalahan perangkat keras maupun perangkat lunak yang tidak dapat dihindari.

Kita menyebut kasus-kasus yang tidak dapat dihindari sebagai disrupsi yang tidak disengaja terhadap aplikasi. Beberapa contohnya adalah sebagai berikut:

  • Kesalahan perangkat keras pada mesin yang menjalankan Node
  • Administrator klaster menghapus virtual machine secara tidak sengaja
  • Kesalahan pada penyedia layanan cloud yang mengakibatkan terhapusnya virtual machine
  • Sebuah kernel panic
  • Node menghilang dari klaster karena partisi jaringan klaster
  • Pod mengalami eviction karena Node kehabisan sumber daya

Dengan pengecualian pada kondisi kehabisan sumber daya, kondisi-kondisi tersebut pada umumnya diketahui oleh kebanyakan pengguna karena kondisi-kondisi tersebut tidak spesifik pada Kubernetes saja.

Kita menyebut kasus-kasus lainnya sebagai disrupsi yang disengaja. Hal ini termasuk tindakan yang dilakukan oleh pemilik aplikasi atau yang dilakukan oleh administrator klaster. Pemilik aplikasi umumnya melakukan hal-hal berikut:

  • Menghapus Deployment atau pengendali yang mengatur Pod
  • Memperbarui templat Pod yang menyebabkan pengulangan kembali/restart
  • Menghapus Pod secara langsung

Administrator klaster umumnya melakukan hal-hal berikut:

  • Melakukan drain terhadap Node untuk perbaikan atau pembaruan.
  • Melakukan drain terhadap sebuah node dari klaster untuk memperkecil ukuran klaster (untuk lebih lanjutnya, pelajari Autoscaling klaster).
  • Menghapus sebuah Pod dari node untuk memuat Pod lain ke node tersebut.

Tindakan-tindakan tersebut dapat dilakukan secara langsung oleh administrator klaster, atau oleh alat otomasi yang dijalankan oleh administrator klaster, atau oleh penyedia layanan Kubernetes kamu.

Tanyakan administrator klaster atau penyedia layanan cloud kamu, atau lihatlah dokumentasi penyedia layanan Kubernetes kamu untuk mengetahui bila ada sumber-sumber yang berpotensi mengakibatkan disrupsi yang disengaja yang ada pada klastermu. Jika tidak ada, kamu bisa melewatkan pembuatan PodDisruptionBudget

Mengatasi Disrupsi

Berikut beberapa cara untuk mengatasi disrupsi yang tidak disengaja:

Frekuensi disrupsi yang disengaja dapat berubah-ubah. Pada klaster Kubernetes yang dasar, tidak ada disrupsi yang disengaja sama sekali. Tetapi, administrator klaster atau penyedia layanan Kubernetes kamu mungkin saja menjalankan beberapa servis tambahan yang dapat mengakibatkan disrupsi yang disengaja. Misalnya, memperbarui perangkat lunak pada node yang dapat mengakibatkan disrupsi yang disengaja. Selain itu, beberapa implementasi autoscaling klaster (atau node) dapat mengakibatkan disrupsi yang disengaja untuk merapikan dan memadatkan node-node pada klaster. Administrator klaster atau penyedia layanan Kubernetes kamu perlu mendokumentasikan tingkatan disrupsi yang disengaja, jika ada disrupsi yang telah diperkirakan.

Kubernetes menawarkan fitur-fitur untuk membantu menjalankan aplikasi-aplikasi dengan ketersediaan tinggi bersamaan dengan seringnya disrupsi yang disengaja, fitur-fitur tersebut dinamai Disruption Budget.

Bagaimana cara kerja Disruption Budget

Pemilik aplikasi dapat membuat objek PodDisruptionBudget (PDB) untuk setiap aplikasi. Sebuah PDB membatasi jumlah Pod yang boleh mati secara bersamaan pada aplikasi yang direplikasi dikarenakan disrupsi yang disengaja. Misalnya, sebuah aplikasi yang bekerja secara quorum mau memastikan bahwa jumlah replika yang berjalan tidak jatuh ke bawah yang dibutuhkan untuk membentuk sebuah quorum. Contoh lainnya, sebuah front-end web mungkin perlu memastikan bahwa jumlah replika yang melayani trafik tidak pernah turun ke total persentase yang telah ditentukan.

Administrator klaster dan penyedia layanan Kubernetes sebaiknya menggunakan alat-alat yang menghormati PDB dengan cara berkomunikasi dengan Eviction API dari pada menghapus Pod atau Deployment secara langsung. Contohnya adalah perintah kubectl drain dan skrip pembaruan Kubernetes-on-GCE (cluster/gce/upgrade.sh)

Saat seorang administrator klaster ingin melakukan drain terhadap sebuah node, ia akan menggunakan perintah kubectl drain. Alat tersebut mencoba untuk "mengusir" semua Pod di node tersebut. Permintaan untuk mengusir Pod tersebut mungkin ditolak untuk sementara, dan alat tersebut akan mencoba ulang permintaannya secara periodik hingga semua Pod dihapus, atau hingga batas waktu yang ditentukan telah dicapai.

Sebua PDB merinci jumlah replika yang dapat ditoleransi oleh sebuah aplikasi, relatif terhadap berapa banyak yang seharusnya dimiliki oleh aplikasi tersebut. Sebagai contoh, sebuah Deployment yang memiliki rincian .spec.replicas :5 diharapkan memiliki 5 Pod pada satu waktu. Jika PDB aplikasi tersebut mengizinkan ada 4 replika pada satu waktu, maka Eviction API akan mengizinkan disrupsi yag disengaja sebanyak satu, tapi tidak mengizinkan dua, pada satu waktu.

Sebuah kelompok Pod yang mewakili aplikasi dispesifikasikan menggunakan sebuah label selector yang sama dengan yang digunakan oleh pengatur aplikasi tersebut (Deployment, StatefulSet, dsb.)

Jumlah Pod yang "diharapkan" dihitung dari .spec.replicas dari pengendali Pod tersebut. Pengendali dari sebuah Pod dapat ditemukan di spesifikasi .metadata.ownerReferences objek Pod yang bersangkutan.

PDB tidak dapat mencegah disrupsi yang tidak disengaja, tapi disrupsi ini akan dihitung terhadap bujet PDB.

Pod yang dihapus atau tidak tersetia dikarenakan pembaruan bertahap juga dihitung terhadap bujet PDB, tetapi pengendali (seperti Deployment dan StatefulSet) tidak dibatasi oleh PDB ketika melakukan pembaruan bertahap; Penanganan kerusakan saat pembaruan aplikasi dikonfigurasikan pada spesifikasi pengendali. (Pelajari tentang memperbarui sebuah Deployment.)

Saat sebuah Pod diusir menggunakan eviction API, Pod tersebut akan dihapus secara graceful (lihat terminationGracePeriodSeconds pada PodSpec.))

Contoh PDB

Kita ambil contoh sebuah klaster dengan 3 node, node-1 hingga node-3. Klaster tersebut menjalankan beberapa aplikasi. Salah satu dari aplikasi tersebut awalnya memiliki 3 replika, yang akan kita namai Pod-a, Pod-b, dan Pod-c. Sebuah Pod lain yang tidak bersangkutan dan tidak memiliki PDB, dinamai Pod-x juga terlihat. Awalnya, Pod-pod tersebut berada pada node-node sebagai berikut:

node-1node-2node-3
Pod-a availablePod-b availablePod-c available
Pod-x available

3 Pod Pod-a hingga Pod-c adalah bagian dari sebuah Deployment, dan mereka secara kolektif memiliki sebuah PDB yang mengharuskan ada setidaknya 2 dari 3 Pod untuk tersedia sepanjang waktu.

Sebagai contoh, asumsikan administrator klaster ingin me-reboot ke dalam versi kernel baru untuk memperbaiki kesalahan di dalam kernel lama. Administator klaster pertama-tama mencoba untuk melakukan drain terhadap node-1 menggunakan perintah kubectl drain. Perintah tersebut mencoba untuk mengusir Pod-a dan Pod-x. Hal ini langsung berhasil. Kedua Pod tersebut masuk ke dalam kondisi terminating secara bersamaan. Hal ini mengubah kondisi klaster menjadi sebagai berikut:

node-1 drainingnode-2node-3
Pod-a terminatingPod-b availablePod-c available
Pod-x terminating

Deployment tersebut melihat bahwa salah satu Pod berada dalam kondisi terminating, sehingga Deployment mencoba untuk membuat penggantinya, Pod-d. Sejak node-1 ditutup (karena perintah kubectl-drain), Pod-d masuk ke node lainnya. Sesuatu juga membuat Pod-y sebagai pengganti Pod-x

(Catatan: untuk sebuah StatefulSet, Pod-a, akan dinamai dengan Pod-1, harus diterminasi hingga selesai sebelum penggantinya, yang juga dinamai Pod-1 tetapi memiliki UID yang berbeda, akan dibuat. Selain hal ini, seluruh contoh ini juga berlaku untuk StatefulSet.)

Sekarang, klaster berada pada kondisi berikut:

node-1 drainingnode-2node-3
Pod-a terminatingPod-b availablePod-c available
Pod-x terminatingPod-d startingPod-y

Pada satu waktu, Pod-pod yang diusir pun selesai diterminasi, dan kondisi klaster menjadi seperti berikut:

node-1 drainednode-2node-3
Pod-b availablePod-c available
Pod-d startingPod-y

Pada titik ini, jika seorang administrator klaster yang tidak sabar mencoba untuk melakukan drain terhadap node-2 atau node-3, perintah untuk melakukan drain terhadap node tersebut akan terhalang, karena hanya ada 2 Pod yang tersedia, dan PDB-nya membutuhkan setidaknya ada 2 Pod tersedia. Setelah beberapa waktu, Pod-d menjadi tersedia.

Kondisi klaster menjadi seperti berikut:

node-1 drainednode-2node-3
Pod-b availablePod-c available
Pod-d availablePod-y

Sekarang, administrator klaster mencoba untuk melakukan drain terhadap node-2. Perintah drain tersebut akan mencoba mengusir Pod-pod tersebut secara berurutan (tidak bersamaan), misalnya Pod-b yang pertama dan diikuti dengan Pod-d. Perintah tersebut akan berhasil mengusir Pod-b. Tetapi, pada saat ia mencoba untuk mengusir Pod-d, hal tersebut akan ditolak karena hal tersebut akan mengakibatkan hanya satu Pod yang tersedia untuk Deployment yang bersangkutan.

Deployment tersebut membuat pengganti Pod-b yang dinamai Pod-e. Karena tidak ada sumber daya klaster yang cukup untuk mengalokasikan Pod-e, proses drain akan kembali terhalang. Klaster mungkin berada pada kondisi berikut:

node-1 drainednode-2node-3no node
Pod-b availablePod-c availablePod-e pending
Pod-d availablePod-y

Pada titik ini, administrator klaster mesti menambah sebuah node untuk klaster agar bisa melanjutkan pembaruan klaster.

Kamu dapat melihat bagaimana frekuensi disrupsi dapat berubah-ubah pada Kubernetes, tergantung pada:

  • Berapa banyak replika yang dibutuhkan sebuah aplikasi
  • Berapa lama waktu yang dibutuhkan untuk mematikan sebuah Pod secara graceful
  • Berapa lama waktu yang dibutuhkan untuk memulai sebuah Pod
  • Tipe pengendali
  • Kapasitas sumber daya klaster

Memisahkan Peran Pemilik Klaster dan Pemilik Aplikasi

Seringkali akan bermanfaat untuk berpikir Administrator Klaster dan Pemilik Aplikasi sebagai peran yang terpisah dan dengan pengetahuan yang terbatas satu sama lainnya. Pemisahan ini dapat dimengerti dalam beberapa skenario berikut:

  • Saat ada banyak tim aplikasi yang berbagi pakai sebuah klaster Kubernetes, dan ada pembagian peran yang spesifik
  • Saat alat atau servis pihak ketiga digunakan untuk melakukan otomasi manajemen klaster.

PDB mendukung pemisahan peran ini dengan cara menyediakan antarmuka bagi peran-peran tersebut.

Jika kamu tidak memiliki pemisahan peran seperti ini pada organisasimu, kamu mungkin tidak membutuhkan PDB.

Bagaimana cara melakukan Tindakan Disruptif terhadap Klaster

Jika kamu adalah Administrator Klaster, maka kamu mesti melakukan tindakan disruptif pada setiap node di klastermu, seperti melakukan pembaruan perangkat lunak pada node, berikut beberapa opsinya:

  • Menerima downtime pada saat pembaruan node
  • Melakukan failover ke replika lengkap klaster lain.
    • Tanpa downtime, tetapi mungkin lebih mahal, baik ongkos duplikasi node-node dan tenaga yang dibutuhkan untuk melakukan failover.
  • Membuat aplikasi yang toleran terhadap disrupsi, dan gunakan PDB.
    • Tanpa downtime.
    • Duplikasi sumber daya yang minimal.
    • Mengizinkan lebih banyak otomasi administrasi klaster.
    • Membuat aplikasi yang toleran terhadap disrupsi agak rumit, tetapi usaha yang dilakukan untuk menoleransi disrupsi yang disengaja kebanyakan beririsan dengan usaha untuk mendukung autoscaling dan menoleransi disrupsi yang tidak disengaja.

Selanjutnya

8 - Kontainer Sementara (Ephemeral)

FEATURE STATE: Kubernetes v1.16 [alpha]

Halaman ini memberikan gambaran umum tentang kontainer sementara: satu jenis kontainer khusus yang berjalan sementara pada Pod yang sudah ada untuk melakukan tindakan yang diinisiasi oleh pengguna seperti dalam pemecahan masalah. Kamu menggunakan kontainer sementara untuk memeriksa layanan bukan untuk membangun aplikasi.

Memahami Kontainer Sementara

Pod adalah blok pembangun fundamental dalam aplikasi Kubernetes. Karena Pod diharapkan digunakan hanya sekali dan dapat diganti, sehingga kamu tidak dapat menambahkan kontainer ke dalam Pod setelah Pod tersebut dibuat. Sebaliknya, kamu biasanya menghapus dan mengganti beberapa Pod dengan cara yang terkontrol melalui Deployment.

Namun, kadang-kadang perlu juga untuk memeriksa keadaan Pod yang telah ada, sebagai contoh untuk memecahkan masalah bug yang sulit direproduksi. Dalam kasus ini, kamu dapat menjalankan sebuah kontainer sementara di dalam suatu Pod yang sudah ada untuk memeriksa statusnya dan menjalankannya segala macam perintah.

Apa itu Kontainer Sementara?

Kontainer sementara berbeda dengan kontainer lainnya karena tidak memiliki jaminan sumber daya maupun akan eksekusi, dan mereka tidak akan pernah secara otomatis melakukan restart, jadi mereka tidak sesuai untuk membangun aplikasi. Kontainer sementara dideskripsikan dengan menggunakan ContainerSpec yang sama dengan kontainer biasa, tetapi banyak bagian yang tidak kompatibel dan tidak diperbolehkan untuk kontainer sementara.

  • Kontainer sementara mungkin tidak memiliki port, sehingga bagian seperti port, livenessProbe, readinessProbe tidak diperbolehkan.
  • Alokasi sumber daya untuk Pod tidak dapat diubah, sehingga pengaturan sumber daya tidak diperbolehkan.
  • Untuk daftar lengkap bagian yang diperbolehkan, dapat di lihat referensi dokumentasi Kontainer Sementara.

Kontainer sementara dibuat dengan menggunakan handler khusus EphemeralContainers dalam API tanpa menambahkannya langsung ke pod.spec, sehingga tidak memungkinan untuk menambahkan kontainer sementara dengan menggunakan kubectl edit.

Seperti dengan kontainer biasa, kamu tidak dapat mengubah atau menghapus kontainer sementara setelah kamu memasukkannya ke dalam sebuah Pod.

Penggunaan Kontainer Sementara

Kontainer sementara berguna untuk pemecahan masalah secara interaktif pada saat kubectl exec tidak mencukupi karena sebuah kontainer telah hancur atau kontainer image tidak memiliki utilitas untuk debugging.

Khususnya, untuk images_distroless memungkinkan kamu untuk menyebarkan kontainer image minimal yang mengurangi surface attack dan paparan bug dan vulnerability. Karena image distroless tidak mempunyai sebuah shell atau utilitas debugging apa pun, sehingga sulit untuk memecahkan masalah image distroless dengan menggunakan kubectl exec saja.

Saat menggunakan kontainer sementara, akan sangat membantu untuk mengaktifkan process namespace sharing sehingga kamu dapat melihat proses pada kontainer lain.

Contoh

Contoh-contoh pada bagian ini menunjukkan bagaimana kontainer sementara muncul dalam API. Kamu biasanya dapat menggunakan plugin kubectl untuk mengatasi masalah untuk mengotomatiskan langkah-langkah ini.

Kontainer sementara dibuat menggunakan subresource ephemeralcontainers Pod, yang dapat didemonstrasikan menggunakan kubectl --raw. Pertama-tama deskripsikan kontainer sementara untuk ditambahkan dalam daftar EphemeralContainers:

{
    "apiVersion": "v1",
    "kind": "EphemeralContainers",
    "metadata": {
        "name": "example-pod"
    },
    "ephemeralContainers": [{
        "command": [
            "sh"
        ],
        "image": "busybox",
        "imagePullPolicy": "IfNotPresent",
        "name": "debugger",
        "stdin": true,
        "tty": true,
        "terminationMessagePolicy": "File"
    }]
}

Untuk memperbarui kontainer yang sudah berjalan dalam example-pod:

kubectl replace --raw /api/v1/namespaces/default/pods/example-pod/ephemeralcontainers  -f ec.json

Ini akan menampilkan daftar baru dari seluruh kontainer sementara:

{
   "kind":"EphemeralContainers",
   "apiVersion":"v1",
   "metadata":{
      "name":"example-pod",
      "namespace":"default",
      "selfLink":"/api/v1/namespaces/default/pods/example-pod/ephemeralcontainers",
      "uid":"a14a6d9b-62f2-4119-9d8e-e2ed6bc3a47c",
      "resourceVersion":"15886",
      "creationTimestamp":"2019-08-29T06:41:42Z"
   },
   "ephemeralContainers":[
      {
         "name":"debugger",
         "image":"busybox",
         "command":[
            "sh"
         ],
         "resources":{

         },
         "terminationMessagePolicy":"File",
         "imagePullPolicy":"IfNotPresent",
         "stdin":true,
         "tty":true
      }
   ]
}

Kamu dapat melihat kondisi kontainer sementara yang baru dibuat dengan menggunakan kubectl describe:

kubectl describe pod example-pod
...
Ephemeral Containers:
  debugger:
    Container ID:  docker://cf81908f149e7e9213d3c3644eda55c72efaff67652a2685c1146f0ce151e80f
    Image:         busybox
    Image ID:      docker-pullable://busybox@sha256:9f1003c480699be56815db0f8146ad2e22efea85129b5b5983d0e0fb52d9ab70
    Port:          <none>
    Host Port:     <none>
    Command:
      sh
    State:          Running
      Started:      Thu, 29 Aug 2019 06:42:21 +0000
    Ready:          False
    Restart Count:  0
    Environment:    <none>
    Mounts:         <none>
...

Kamu dapat mengakses kontainer sementara yang baru menggunakan kubectl attach:

kubectl attach -it example-pod -c debugger

Jika proses berbagi namespace diaktifkan, kamu dapat melihat proses dari semua kontainer dalam Pod tersebut. Misalnya, setelah mengakses, kamu jalankan ps di kontainer debugger:

# Jalankan ini pada _shell_ dalam _debugger_ dari kontainer sementara
ps auxww

Hasilnya akan seperti ini:

PID   USER     TIME  COMMAND
    1 root      0:00 /pause
    6 root      0:00 nginx: master process nginx -g daemon off;
   11 101       0:00 nginx: worker process
   12 101       0:00 nginx: worker process
   13 101       0:00 nginx: worker process
   14 101       0:00 nginx: worker process
   15 101       0:00 nginx: worker process
   16 101       0:00 nginx: worker process
   17 101       0:00 nginx: worker process
   18 101       0:00 nginx: worker process
   19 root      0:00 /pause
   24 root      0:00 sh
   29 root      0:00 ps auxww