1 - Bin Packing Sumber Daya untuk Sumber Daya Tambahan

FEATURE STATE: Kubernetes 1.16 [alpha]

Kube-scheduler dapat dikonfigurasikan untuk mengaktifkan pembungkusan rapat (bin packing) sumber daya bersama dengan sumber daya tambahan melalui fungsi prioritas RequestedToCapacityRatioResourceAllocation. Fungsi-fungsi prioritas dapat digunakan untuk menyempurnakan kube-scheduler sesuai dengan kebutuhan.

Mengaktifkan Bin Packing menggunakan RequestedToCapacityRatioResourceAllocation

Sebelum Kubernetes 1.15, kube-scheduler digunakan untuk memungkinkan mencetak skor berdasarkan rasio permintaan terhadap kapasitas sumber daya utama seperti CPU dan Memori. Kubernetes 1.16 menambahkan parameter baru ke fungsi prioritas yang memungkinkan pengguna untuk menentukan sumber daya beserta dengan bobot untuk setiap sumber daya untuk memberi nilai dari Node berdasarkan rasio permintaan terhadap kapasitas. Hal ini memungkinkan pengguna untuk bin pack sumber daya tambahan dengan menggunakan parameter yang sesuai untuk meningkatkan pemanfaatan sumber daya yang langka dalam klaster yang besar. Perilaku RequestedToCapacityRatioResourceAllocation dari fungsi prioritas dapat dikontrol melalui pilihan konfigurasi yang disebut RequestToCapacityRatioArguments. Argumen ini terdiri dari dua parameter yaitu shape dan resources. Shape memungkinkan pengguna untuk menyempurnakan fungsi menjadi yang paling tidak diminta atau paling banyak diminta berdasarkan nilai utilization dan score. Sumber daya terdiri dari name yang menentukan sumber daya mana yang dipertimbangkan selama penilaian dan weight yang menentukan bobot masing-masing sumber daya.

Di bawah ini adalah contoh konfigurasi yang menetapkan requestedToCapacityRatioArguments pada perilaku bin packing untuk sumber daya tambahan intel.com/foo dan intel.com/bar

{
    "kind" : "Policy",
    "apiVersion" : "v1",

    ...

    "priorities" : [

       ...

      {
        "name": "RequestedToCapacityRatioPriority",
        "weight": 2,
        "argument": {
          "requestedToCapacityRatioArguments": {
            "shape": [
              {"utilization": 0, "score": 0},
              {"utilization": 100, "score": 10}
            ],
            "resources": [
              {"name": "intel.com/foo", "weight": 3},
              {"name": "intel.com/bar", "weight": 5}
            ]
          }
        }
      }
    ],
  }

Fitur ini dinonaktifkan secara default

Tuning RequestedToCapacityRatioResourceAllocation Priority Function

shape digunakan untuk menentukan perilaku dari fungsi RequestedToCapacityRatioPriority.

 {"utilization": 0, "score": 0},
 {"utilization": 100, "score": 10}

Argumen di atas memberikan Node nilai 0 jika utilisasi 0% dan 10 untuk utilisasi 100%, yang kemudian mengaktfikan perilaku bin packing. Untuk mengaktifkan dari paling yang tidak diminta, nilainya harus dibalik sebagai berikut.

 {"utilization": 0, "score": 100},
 {"utilization": 100, "score": 0}

resources adalah parameter opsional yang secara default diatur ke:

"resources": [
              {"name": "CPU", "weight": 1},
              {"name": "Memory", "weight": 1}
            ]

Ini dapat digunakan untuk menambahkan sumber daya tambahan sebagai berikut:

"resources": [
              {"name": "intel.com/foo", "weight": 5},
              {"name": "CPU", "weight": 3},
              {"name": "Memory", "weight": 1}
            ]

Parameter weight adalah opsional dan diatur ke 1 jika tidak ditentukan. Selain itu, weight tidak dapat diatur ke nilai negatif.

Bagaimana Fungsi Prioritas RequestedToCapacityRatioResourceAllocation Menilai Node

Bagian ini ditujukan bagi kamu yang ingin memahami secara detail internal dari fitur ini. Di bawah ini adalah contoh bagaimana nilai dari Node dihitung untuk satu kumpulan nilai yang diberikan.

Sumber daya yang diminta

intel.com/foo : 2
Memory: 256MB
CPU: 2

Bobot dari sumber daya

intel.com/foo : 5
Memory: 1
CPU: 3

FunctionShapePoint {{0, 0}, {100, 10}}

Spesifikasi dari Node 1

Tersedia:

intel.com/foo : 4
Memory : 1 GB
CPU: 8

Digunakan:

intel.com/foo: 1
Memory: 256MB
CPU: 1


Nilai Node:

intel.com/foo  = resourceScoringFunction((2+1),4)
               =  (100 - ((4-3)*100/4)
               =  (100 - 25)
               =  75
               =  rawScoringFunction(75)
               = 7

Memory         = resourceScoringFunction((256+256),1024)
               = (100 -((1024-512)*100/1024))
               = 50
               = rawScoringFunction(50)
               = 5

CPU            = resourceScoringFunction((2+1),8)
               = (100 -((8-3)*100/8))
               = 37.5
               = rawScoringFunction(37.5)
               = 3

NodeScore   =  (7 * 5) + (5 * 1) + (3 * 3) / (5 + 1 + 3)
            =  5


Spesifikasi dari Node 2

Tersedia:

intel.com/foo: 8
Memory: 1GB
CPU: 8

Digunakan:

intel.com/foo: 2
Memory: 512MB
CPU: 6


Nilai Node:

intel.com/foo  = resourceScoringFunction((2+2),8)
               =  (100 - ((8-4)*100/8)
               =  (100 - 25)
               =  50
               =  rawScoringFunction(50)
               = 5

Memory         = resourceScoringFunction((256+512),1024)
               = (100 -((1024-768)*100/1024))
               = 75
               = rawScoringFunction(75)
               = 7

CPU            = resourceScoringFunction((2+6),8)
               = (100 -((8-8)*100/8))
               = 100
               = rawScoringFunction(100)
               = 10

NodeScore   =  (5 * 5) + (7 * 1) + (10 * 3) / (5 + 1 + 3)
            =  7

2 - Konfigurasi dan Penerapan Konsep

Dokumen ini menyoroti dan memperkuat pemahaman konsep konfigurasi yang dikenalkan di seluruh panduan pengguna, dokumentasi Memulai, dan contoh-contoh.

Dokumentasi ini terbuka. Jika Anda menemukan sesuatu yang tidak ada dalam daftar ini tetapi mungkin bermanfaat bagi orang lain, jangan ragu untuk mengajukan issue atau mengirimkan PR.

Tip konfigurasi secara umum

  • Saat mendefinisikan konfigurasi, tentukan versi API stabil terbaru.

  • File konfigurasi harus disimpan dalam version control sebelum di push ke cluster. Ini memungkinkan Anda untuk dengan cepat mengembalikan perubahan konfigurasi jika perlu. Ini juga membantu penciptaan dan restorasi cluster.

  • Tulis file konfigurasi Anda menggunakan YAML tidak dengan JSON. Meskipun format ini dapat digunakan secara bergantian di hampir semua skenario, YAML cenderung lebih ramah pengguna.

  • Kelompokkan objek terkait ke dalam satu file yang memungkinkan. Satu file seringkali lebih mudah dikelola daripada beberapa file. Lihat pada guestbook-all-in-one.yaml sebagai contoh file sintaks ini.

  • Perhatikan juga bahwa banyak perintah kubectl dapat dipanggil pada direktori. Misalnya, Anda dapat memanggil kubectl apply pada direktori file konfigurasi.

  • Jangan tentukan nilai default yang tidak perlu: sederhana, konfigurasi minimal akan membuat kesalahan lebih kecil.

  • Masukkan deskripsi objek dalam anotasi, untuk memungkinkan introspeksi yang lebih baik.

"Naked" Pods vs ReplicaSets, Deployments, and Jobs

  • Jangan gunakan Pods naked (artinya, Pods tidak terikat dengan a ReplicaSet a Deployment) jika kamu bisa menghindarinya. Pod naked tidak akan dijadwal ulang jika terjadi kegagalan pada node.

    Deployment, yang keduanya menciptakan ReplicaSet untuk memastikan bahwa jumlah Pod yang diinginkan selalu tersedia, dan menentukan strategi untuk mengganti Pods (seperti RollingUpdate), hampir selalu lebih disukai daripada membuat Pods secara langsung, kecuali untuk beberapa yang eksplisit restartPolicy: Never banyak skenario . A Job mungkin juga sesuai.

Services

  • Buat Service sebelum workloads backend terkait (Penyebaran atau ReplicaSets), dan sebelum workloads apa pun yang perlu mengaksesnya. Ketika Kubernetes memulai sebuah container, ia menyediakan environment variabel yang menunjuk ke semua Layanan yang berjalan ketika container itu dimulai. Misalnya, jika Layanan bernama foo ada, semua container akan mendapatkan variabel berikut di environment awalnya:

    FOO_SERVICE_HOST=<the host the Service is running on>
    FOO_SERVICE_PORT=<the port the Service is running on>
    

    *Ini menunjukan persyaratan pemesanan * - Service apa pun yang ingin diakses oleh Pod harus dibuat sebelum Pod itu sendiri, atau environment variabel tidak akan diisi. DNS tidak memiliki batasan ini.

  • Opsional (meskipun sangat disarankan) cluster add-on adalah server DNS. Server DNS melihat API Kubernetes untuk Service baru dan membuat satu set catatan DNS untuk masing-masing. Jika DNS telah diaktifkan di seluruh cluster maka semua Pods harus dapat melakukan resolusi namaService secara otomatis.

  • Jangan tentukan hostPort untuk Pod kecuali jika benar-benar diperlukan. Ketika Anda bind Pod ke hostPort, hal itu membatasi jumlah tempat Pod dapat dijadwalkan, karena setiap kombinasi < hostIP, hostPort, protokol> harus unik. Jika Anda tidak menentukan hostIP dan protokol secara eksplisit, Kubernetes akan menggunakan 0.0.0.0 sebagai hostIP dan TCP sebagai default protokol.

    Jika kamu hanya perlu akses ke port untuk keperluan debugging, Anda bisa menggunakan apiserver proxy atau kubectl port-forward.

    Jika Anda secara eksplisit perlu mengekspos port Pod pada node, pertimbangkan untuk menggunakan NodePort Service sebelum beralih ke hostPort.

  • Hindari menggunakan hostNetwork, untuk alasan yang sama seperti hostPort.

  • Gunakan [headless Services](/id/docs/concepts/services-networking/service/#headless- services) (yang memiliki ClusterIP dari None) untuk Service discovery yang mudah ketika Anda tidak membutuhkan kube-proxy load balancing.

Menggunakan label

  • Deklarasi dan gunakan [labels] (/id/docs/concepts/overview/working-with-objects/labels/) untuk identifikasi semantic attributes aplikasi atau Deployment kamu, seperti { app: myapp, tier: frontend, phase: test, deployment: v3 }. Kamu dapat menggunakan label ini untuk memilih Pod yang sesuai untuk sumber daya lainnya; misalnya, Service yang memilih semua tier: frontend Pods, atau semua komponen phase: test dari app: myapp. Lihat guestbook aplikasi untuk contoh-contoh pendekatan ini.

Service dapat dibuat untuk menjangkau beberapa Penyebaran dengan menghilangkan label khusus rilis dari pemilihnya. Deployments membuatnya mudah untuk memperbarui Service yang sedang berjalan tanpa downtime.

Keadaan objek yang diinginkan dideskripsikan oleh Deployment, dan jika perubahan terhadap spesifikasi tersebut adalah applied, Deployment controller mengubah keadaan aktual ke keadaan yang diinginkan pada tingkat yang terkontrol.

  • Kamu dapat memanipulasi label untuk debugging. Karena Kubernetes controller (seperti ReplicaSet) dan Service Match dengan Pods menggunakan label pemilih, menghapus label yang relevan dari Pod akan menghentikannya dari dianggap oleh Controller atau dari lalu lintas yang dilayani oleh Service. Jika Anda menghapus label dari Pod yang ada, Controller akan membuat Pod baru untuk menggantikannya. Ini adalah cara yang berguna untuk men-debug Pod yang sebelumnya "live" di Environment "quarantine". Untuk menghapus atau menambahkan label secara interaktif, gunakan kubectl label.

Container Images

Ini imagePullPolicy dan tag dari image mempengaruhi ketika kubelet mencoba menarik image yang ditentukan

  • imagePullPolicy: IfNotPresent: image ditarik hanya jika belum ada secara lokal.

  • imagePullPolicy: Always: Image ditarik setiap kali pod dimulai.

  • imagePullPolicy dihilangkan dan tag imagenya adalah :latest atau dihilangkan:always diterapkan.

  • imagePullPolicy dihilangkan dan tag image ada tetapi tidak :latest: IfNotPresent diterapkan.

  • imagePullPolicy: Never: image diasumsikan ada secara lokal. Tidak ada upaya yang dilakukan untuk menarik image.

Catatan: Untuk memastikan container selalu menggunakan versi image yang sama, Anda bisa menentukannya digest, untuk contoh sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2. digest mengidentifikasi secara unik versi image tertentu, sehingga tidak pernah diperbarui oleh Kubernetes kecuali Anda mengubah nilai digest.
Catatan: Anda harus menghindari penggunaan tag : latest saat menempatkan container dalam produksi karena lebih sulit untuk melacak versi image mana yang sedang berjalan dan lebih sulit untuk memutar kembali dengan benar.
Catatan: Semantik caching dari penyedia gambar yang mendasarinya membuat bahkan imagePullPolicy: Always efisien. Dengan Docker, misalnya, jika image sudah ada, upaya pull cepat karena semua lapisan image di-cache dan tidak perlu mengunduh image.

Menggunakan kubectl

  • Gunakan kubectl apply -f <directory>. Ini mencari konfigurasi Kubernetes di semua file .yaml, .yml, dan .json di<directory>dan meneruskannya ke apply.

  • Gunakan label selector untuk operasi get dan delete alih-alih nama objek tertentu. Lihat bagian di label selectors dan using labels effectively.

  • Gunakan kubectl run dan kubectl expose untuk dengan cepat membuat Deployment dan Service single-container. Lihat Use a Service to Access an Application in a Cluster untuk Contoh.

3 - Mengatur Sumber Daya Komputasi untuk Container

Saat kamu membuat spesifikasi sebuah Pod, kamu dapat secara opsional menentukan seberapa banyak CPU dan memori (RAM) yang dibutuhkan oleh setiap Container. Saat Container-Container menentukan request (permintaan) sumber daya, scheduler dapat membuat keputusan yang lebih baik mengenai Node mana yang akan dipilih untuk menaruh Pod-Pod. Dan saat limit (batas) sumber daya Container-Container telah ditentukan, maka kemungkinan rebutan sumber daya pada sebuah Node dapat dihindari. Untuk informasi lebih lanjut mengenai perbedaan request dan limit, lihat QoS Sumber Daya.

Jenis-jenis sumber daya

CPU dan memori masing-masing merupakan jenis sumber daya (resource type). Sebuah jenis sumber daya memiliki satuan dasar. CPU ditentukan dalam satuan jumlah core, dan memori ditentukan dalam satuan bytes. Jika kamu menggunakan Kubernetes v1.14 keatas, kamu dapat menentukan sumber daya huge page. Huge page adalah fitur khusus Linux di mana kernel Node mengalokasikan blok-blok memori yang jauh lebih besar daripada ukuran page bawaannya.

Sebagai contoh, pada sebuah sistem di mana ukuran page bawaannya adalah 4KiB, kamu dapat menentukan sebuah limit, hugepages-2Mi: 80Mi. Jika kontainer mencoba mengalokasikan lebih dari 40 huge page berukuran 20MiB (total 80MiB), maka alokasi tersebut akan gagal.

Catatan: Kamu tidak dapat melakukan overcommit terhadap sumber daya hugepages-*. Hal ini berbeda dari sumber daya memory dan cpu (yang dapat di-overcommit).

CPU dan memori secara kolektif disebut sebagai sumber daya komputasi, atau cukup sumber daya saja. Sumber daya komputasi adalah jumlah yang dapat diminta, dialokasikan, dan dikonsumsi. Mereka berbeda dengan sumber daya API. Sumber daya API, seperti Pod dan Service adalah objek-objek yang dapat dibaca dan diubah melalui Kubernetes API Server.

Request dan Limit Sumber daya dari Pod dan Container

Setiap Container dari sebuah Pod dapat menentukan satu atau lebih dari hal-hal berikut:

  • spec.containers[].resources.limits.cpu
  • spec.containers[].resources.limits.memory
  • spec.containers[].resources.limits.hugepages-<size>
  • spec.containers[].resources.requests.cpu
  • spec.containers[].resources.requests.memory
  • spec.containers[].resources.requests.hugepages-<size>

Walaupun requests dan limits hanya dapat ditentukan pada Container individual, akan lebih mudah untuk membahas tentang request dan limit sumber daya dari Pod. Sebuah request/limit sumber daya Pod untuk jenis sumber daya tertentu adalah jumlah dari request/limit sumber daya pada jenis tersebut untuk semua Container di dalam Pod tersebut.

Arti dari CPU

Limit dan request untuk sumber daya CPU diukur dalam satuan cpu. Satu cpu, dalam Kubernetes, adalah sama dengan:

  • 1 vCPU AWS
  • 1 Core GCP
  • 1 vCore Azure
  • 1 vCPU IBM
  • 1 Hyperthread pada sebuah prosesor Intel bare-metal dengan Hyperthreading

Request dalam bentuk pecahan diizinkan. Sebuah Container dengan spec.containers[].resources.requests.cpu bernilai 0.5 dijamin mendapat setengah CPU dibandingkan dengan yang meminta 1 CPU. Ekspresi nilai 0.1 ekuivalen dengan ekspresi nilai 100m, yang dapat dibaca sebagai "seratus milicpu". Beberapa orang juga membacanya dengan "seratus milicore", dan keduanya ini dimengerti sebagai hal yang sama. Sebuah request dengan angka di belakang koma, seperti 0.1 dikonversi menjadi 100m oleh API, dan presisi yang lebih kecil lagi dari 1m tidak dibolehkan. Untuk alasan ini, bentuk 100m mungkin lebih disukai.

CPU juga selalu diminta dalam jumlah yang mutlak, tidak sebagai jumlah yang relatif; 0.1 adalah jumlah CPU yang sama pada sebuah mesin single-core, dual-core, atau 48-core.

Arti dari Memori

Limit dan request untuk memory diukur dalam satuan bytes. Kamu dapat mengekspresikan memori sebagai plain integer atau sebagai sebuah fixed-point integer menggunakan satu dari sufiks-sufiks berikut: E, P, T, G, M, K. Kamu juga dapat menggunakan bentuk pangkat dua ekuivalennya: Ei, Pi, Ti, Gi, Mi, Ki. Sebagai contoh, nilai-nilai berikut kurang lebih sama:

128974848, 129e6, 129M, 123Mi

Berikut sebuah contoh. Pod berikut memiliki dua Container. Setiap Container memiliki request 0.25 cpu dan 64MiB (226 bytes) memori. Setiap Container memiliki limit 0.5 cpu dan 128MiB memori. Kamu dapat berkata bahwa Pod tersebut memiliki request 0.5 cpu dan 128MiB memori, dan memiliki limit 1 cpu dan 265MiB memori.

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: db
    image: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
  - name: wp
    image: wordpress
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

Bagaimana Pod-Pod dengan request sumber daya dijadwalkan

Saat kamu membuat sebuah Pod, Kubernetes scheduler akan memilih sebuah Node untuk Pod tersebut untuk dijalankan. Setiap Node memiliki kapasitas maksimum untuk setiap jenis sumber daya: jumlah CPU dan memori yang dapat disediakan oleh Node tersebut untuk Pod-Pod. Scheduler memastikan bahwa, untuk setiap jenis sumber daya, jumlah semua request sumber daya dari Container-Container yang dijadwalkan lebih kecil dari kapasitas Node tersebut. Perlu dicatat bahwa walaupun penggunaan sumber daya memori atau CPU aktual/sesungguhnya pada Node-Node sangat rendah, scheduler tetap akan menolak untuk menaruh sebuah Pod pada sebuah Node jika pemeriksaan kapasitasnya gagal. Hal ini adalah untuk menjaga dari kekurangan sumber daya pada sebuah Node saat penggunaan sumber daya meningkat suatu waktu, misalnya pada saat titik puncak traffic harian.

Bagaimana Pod-Pod dengan limit sumber daya dijalankan

Saat Kubelet menjalankan sebuah Container dari sebuah Pod, Kubelet tersebut mengoper limit CPU dan memori ke runtime kontainer.

Ketika menggunakan Docker:

  • spec.containers[].resources.requests.cpu diubah menjadi nilai core-nya, yang mungkin berbentuk angka pecahan, dan dikalikan dengan 1024. Nilai yang lebih besar antara angka ini atau 2 digunakan sebagai nilai dari flag --cpu-shares pada perintah docker run.

  • spec.containers[].resources.limits.cpu diubah menjadi nilai millicore-nya dan dikalikan dengan 100. Nilai hasilnya adalah jumlah waktu CPU yang dapat digunakan oleh sebuah kontainer setiap 100 milidetik. Sebuah kontainer tidak dapat menggunakan lebih dari jatah waktu CPU-nya selama selang waktu ini.

    Catatan: Periode kuota bawaan adalah 100ms. Resolusi minimum dari kuota CPU adalah 1 milidetik.
  • spec.containers[].resources.limits.memory diubah menjadi sebuah bilangan bulat, dan digunakan sebagai nilai dari flag --memory dari perintah docker run.

Jika sebuah Container melebihi batas memorinya, Container tersebut mungkin akan diterminasi. Jika Container tersebut dapat diulang kembali, Kubelet akan mengulangnya kembali, sama seperti jenis kegagalan lainnya.

Jika sebuah Container melebihi request memorinya, kemungkinan Pod-nya akan dipindahkan kapanpun Node tersebut kehabisan memori.

Sebuah Container mungkin atau mungkin tidak diizinkan untuk melebihi limit CPU-nya untuk periode waktu yang lama. Tetapi, Container tersebut tidak akan diterminasi karena penggunaan CPU yang berlebihan.

Untuk menentukan apabila sebuah Container tidak dapat dijadwalkan atau sedang diterminasi karena limit sumber dayanya, lihat bagian Penyelesaian Masalah.

Memantau penggunaan sumber daya komputasi

Penggunaan sumber daya dari sebuah Pod dilaporkan sebagai bagian dari kondisi Pod.

Jika monitoring opsional diaktifkan pada klaster kamu, maka penggunaan sumber daya Pod dapat diambil dari sistem monitoring kamu.

Penyelesaian Masalah

Pod-Pod saya berkondisi Pending (tertunda) dengan event message failedScheduling

Jika scheduler tidak dapat menemukan Node manapun yang muat untuk sebuah Pod, Pod tersebut tidak akan dijadwalkan hingga ditemukannya sebuah tempat yang muat. Sebuah event akan muncul setiap kali scheduler gagal menemukan tempat untuk Pod tersebut, seperti berikut:

kubectl describe pod frontend | grep -A 3 Events
Events:
  FirstSeen LastSeen   Count  From          Subobject   PathReason      Message
  36s   5s     6      {scheduler }              FailedScheduling  Failed for reason PodExceedsFreeCPU and possibly others

Pada contoh di atas, Pod bernama "frontend" gagal dijadwalkan karena kekurangan sumber daya CPU pada Node tersebut. Pesan kesalahan yang serupa dapat juga menunjukkan kegagalan karena kekurangan memori (PodExceedsFreeMemroy). Secara umum, jika sebuah Pod berkondisi Pending (tertunda) dengan sebuah pesan seperti ini, ada beberapa hal yang dapat dicoba:

  • Tambah lebih banyak Node pada klaster.
  • Terminasi Pod-Pod yang tidak dibutuhkan untuk memberikan ruangan untuk Pod-Pod yang tertunda.
  • Periksa jika nilai request Pod tersebut tidak lebih besar dari Node-node yang ada. Contohnya, jika semua Node memiliki kapasitas cpu: 1, maka Pod dengan request cpu: 1.1 tidak akan pernah dijadwalkan.

Kamu dapat memeriksa kapasitas Node-Node dan jumlah-jumlah yang telah dialokasikan dengan perintah kubectl describe nodes. Contohnya:

kubectl describe nodes e2e-test-node-pool-4lw4
Name:            e2e-test-node-pool-4lw4
[ ... lines removed for clarity ...]
Capacity:
 cpu:                               2
 memory:                            7679792Ki
 pods:                              110
Allocatable:
 cpu:                               1800m
 memory:                            7474992Ki
 pods:                              110
[ ... beberapa baris dihapus untuk kejelasan ...]
Non-terminated Pods:        (5 in total)
  Namespace    Name                                  CPU Requests  CPU Limits  Memory Requests  Memory Limits
  ---------    ----                                  ------------  ----------  ---------------  -------------
  kube-system  fluentd-gcp-v1.38-28bv1               100m (5%)     0 (0%)      200Mi (2%)       200Mi (2%)
  kube-system  kube-dns-3297075139-61lj3             260m (13%)    0 (0%)      100Mi (1%)       170Mi (2%)
  kube-system  kube-proxy-e2e-test-...               100m (5%)     0 (0%)      0 (0%)           0 (0%)
  kube-system  monitoring-influxdb-grafana-v4-z1m12  200m (10%)    200m (10%)  600Mi (8%)       600Mi (8%)
  kube-system  node-problem-detector-v0.1-fj7m3      20m (1%)      200m (10%)  20Mi (0%)        100Mi (1%)
Allocated resources:
  (Total limit mungkin melebihi 100 persen, misalnya, karena _overcommit_.)
  CPU Requests    CPU Limits    Memory Requests    Memory Limits
  ------------    ----------    ---------------    -------------
  680m (34%)      400m (20%)    920Mi (12%)        1070Mi (14%)

Pada keluaran di atas, kamu dapat melihat bahwa jika sebuah Pod meminta lebih dari 1120m CPU atau 6.23Gi memori, Pod tersebut tidak akan muat pada Node tersebut.

Dengan melihat pada bagian Pods, kamu dapat melihat Pod-Pod mana saja yang memakan sumber daya pada Node tersebut. Jumlah sumber daya yang tersedia untuk Pod-Pod kurang dari kapasitas Node, karena daemon sistem menggunakan sebagian dari sumber daya yang ada. Kolom allocatable pada NodeStatus memberikan jumlah sumber daya yang tersedia untuk Pod-Pod. Untuk lebih lanjut, lihat Sumber daya Node yang dapat dialokasikan.

Fitur kuota sumber daya dapat disetel untuk membatasi jumlah sumber daya yang dapat digunakan. Jika dipakai bersama dengan Namespace, kuota sumber daya dapat mencegah suatu tim menghabiskan semua sumber daya.

Container saya diterminasi

Container kamu mungkin diterminasi karena Container tersebut melebihi batasnya. Untuk memeriksa jika sebuah Container diterminasi karena ia melebihi batas sumber dayanya, gunakan perintah kubectl describe pod pada Pod yang bersangkutan:

kubectl describe pod simmemleak-hra99
Name:                           simmemleak-hra99
Namespace:                      default
Image(s):                       saadali/simmemleak
Node:                           kubernetes-node-tf0f/10.240.216.66
Labels:                         name=simmemleak
Status:                         Running
Reason:
Message:
IP:                             10.244.2.75
Replication Controllers:        simmemleak (1/1 replicas created)
Containers:
  simmemleak:
    Image:  saadali/simmemleak
    Limits:
      cpu:                      100m
      memory:                   50Mi
    State:                      Running
      Started:                  Tue, 07 Jul 2015 12:54:41 -0700
    Last Termination State:     Terminated
      Exit Code:                1
      Started:                  Fri, 07 Jul 2015 12:54:30 -0700
      Finished:                 Fri, 07 Jul 2015 12:54:33 -0700
    Ready:                      False
    Restart Count:              5
Conditions:
  Type      Status
  Ready     False
Events:
  FirstSeen                         LastSeen                         Count  From                              SubobjectPath                       Reason      Message
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {scheduler }                                                          scheduled   Successfully assigned simmemleak-hra99 to kubernetes-node-tf0f
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   pulled      Pod container image "k8s.gcr.io/pause:0.8.0" already present on machine
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   created     Created with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    implicitly required container POD   started     Started with docker id 6a41280f516d
  Tue, 07 Jul 2015 12:53:51 -0700   Tue, 07 Jul 2015 12:53:51 -0700  1      {kubelet kubernetes-node-tf0f}    spec.containers{simmemleak}         created     Created with docker id 87348f12526a

Pada contoh di atas, Restart Count: 5 menunjukkan bahwa Container simmemleak pada Pod tersebut diterminasi dan diulang kembali sebanyak lima kali.

Kamu dapat menggunakan perintah kubectl get pod dengan opsi -o go-template=... untuk mengambil kondisi dari Container-Container yang sebelumnya diterminasi:

kubectl get pod -o go-template='{{range.status.containerStatuses}}{{"Container Name: "}}{{.name}}{{"\r\nLastState: "}}{{.lastState}}{{end}}'  simmemleak-hra99
Container Name: simmemleak
LastState: map[terminated:map[exitCode:137 reason:OOM Killed startedAt:2015-07-07T20:58:43Z finishedAt:2015-07-07T20:58:43Z containerID:docker://0e4095bba1feccdfe7ef9fb6ebffe972b4b14285d5acdec6f0d3ae8a22fad8b2]]

Kamu dapat lihat bahwa Container tersebut diterminasi karena reason:OOM Killed, di mana OOM merupakan singkatan dari Out Of Memory, atau kehabisan memori.

Penyimpanan lokal sementara

FEATURE STATE: Kubernetes v1.20 [beta]

Kubernetes versi 1.8 memperkenalkan sebuah sumber daya baru, ephemeral-storage untuk mengatur penyimpanan lokal yang bersifat sementara. Pada setiap Node Kubernetes, direktori root dari Kubelet (secara bawaan /var/lib/kubelet) dan direktori log (/var/log) ditaruh pada partisi root dari Node tersebut. Partisi ini juga digunakan bersama oleh Pod-Pod melalui volume emptyDir, log kontainer, lapisan image, dan lapisan kontainer yang dapat ditulis.

Partisi ini bersifat "sementara" dan aplikasi-aplikasi tidak dapat mengharapkan SLA kinerja (misalnya Disk IOPS) dari partisi ini. Pengelolaan penyimpanan lokal sementara hanya berlaku untuk partisi root; partisi opsional untuk lapisan image dan lapisan yang dapat ditulis berada di luar ruang lingkup.

Catatan: Jika sebuah partisi runtime opsional digunakan, partisi root tidak akan menyimpan lapisan image ataupun lapisan yang dapat ditulis manapun.

Menyetel request dan limit dari penyimpanan lokal sementara

Setiap Container dari sebuah Pod dapat menentukan satu atau lebih dari hal-hal berikut:

  • spec.containers[].resources.limits.ephemeral-storage
  • spec.containers[].resources.requests.ephemeral-storage

Limit dan request untuk ephemeral-storage diukur dalam satuan bytes. Kamu dapat menyatakan penyimpanan dalam bilangan bulat biasa, atau sebagai fixed-point integer menggunakan satu dari sufiks-sufiks ini: E, P, T, G, M, K. Kamu jika dapat menggunakan bentuk pangkat dua ekuivalennya: Ei, Pi, Ti, Gi, Mi, Ki. Contohnya, nilai-nilai berikut kurang lebih sama:

128974848, 129e6, 129M, 123Mi

Contohnya, Pod berikut memiliki dua Container. Setiap Container memiliki request 2GiB untuk penyimpanan lokal sementara. Setiap Container memiliki limit 4GiB untuk penyimpanan lokal sementara. Maka, Pod tersebut memiliki jumlah request 4GiB penyimpanan lokal sementara, dan limit 8GiB.

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: db
    image: mysql
    env:
    - name: MYSQL_ROOT_PASSWORD
      value: "password"
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"
  - name: wp
    image: wordpress
    resources:
      requests:
        ephemeral-storage: "2Gi"
      limits:
        ephemeral-storage: "4Gi"

Bagaimana Pod-Pod dengan request ephemeral-storage dijadwalkan

Saat kamu membuat sebuah Pod, Kubernetes scheduler memilih sebuah Node di mana Pod tersebut akan dijalankan. Setiap Node memiliki jumlah maksimum penyimpanan lokal sementara yang dapat disediakan. Untuk lebih lanjut, lihat "Hal-hal yang dapat dialokasikan Node".

Scheduler memastikan bahwa jumlah dari request-request sumber daya dari Container-Container yang dijadwalkan lebih kecil dari kapasitas Node.

Bagaimana Pod-Pod dengan limit ephemeral-storage dijalankan

Untuk isolasi pada tingkat kontainer, jika lapisan yang dapat ditulis dari sebuah Container dan penggunaan log melebihi limit penyimpanannya, maka Pod tersebut akan dipindahkan. Untuk isolasi pada tingkat Pod, jika jumlah dari penyimpanan lokal sementara dari semua Container dan juga volume emptyDir milik Pod melebihi limit, maka Pod teresebut akan dipindahkan.

Memantau penggunaan ephemeral-storage

Saat penyimpanan lokal sementara digunakan, ia dipantau terus-menerus oleh Kubelet. Pemantauan dilakukan dengan cara memindai setiap volume emptyDir, direktori log, dan lapisan yang dapat ditulis secara periodik. Dimulai dari Kubernetes 1.15, volume emptyDir (tetapi tidak direktori log atau lapisan yang dapat ditulis) dapat, sebagai pilihan dari operator klaster, dikelola dengan menggunakan project quotas. Project quotas aslinya diimplementasikan dalam XFS, dan baru-baru ini telah diubah ke ext4fs. Project quotas dapat digunakan baik untuk monitoring dan pemaksaan; sejak Kubernetes 1.16, mereka tersedia sebagai fitur alpha untuk monitoring saja.

Quota lebih cepat dan akurat dibandingkan pemindaian direktori. Saat sebuah direktori ditentukan untuk sebuah proyek, semua berkas yang dibuat pada direktori tersebut dibuat untuk proyek tersebut, dan kernel hanya perlu melacak berapa banyak blok yang digunakan oleh berkas-berkas pada proyek tersebut. Jika sebuah berkas dibuat dan dihapus, tetapi tetap dengan sebuah file descriptor yang terbuka, maka berkas tersebut tetap akan memakan ruangan penyimpanan. Ruangan ini akan dilacak oleh quota tersebut, tetapi tidak akan terlihat oleh sebuah pemindaian direktori.

Kubernetes menggunakan ID proyek yang dimulai dari 1048576. ID-ID yang digunakan akan didaftarkan di dalam /etc/projects dan /etc/projid. Jika ID-ID proyek pada kisaran ini digunakan untuk tujuan lain pada sistem, ID-ID proyek tersebut harus terdaftar di dalam /etc/projects dan /etc/projid untuk mencegah Kubernetes menggunakan ID-ID tersebut.

Untuk mengaktifkan penggunaan project quotas, operator klaster harus melakukan hal-hal berikut:

  • Aktifkan feature gate LocalStorageCapacityIsolationFSQuotaMonitoring=true pada konfigurasi Kubelet. Nilainya secara bawaan false pada Kubernetes 1.16, jadi harus secara eksplisit disetel menjadi true.

  • Pastikan bahwa partisi root (atau partisi opsional runtime) telah dibangun (build) dengan mengaktifkan project quotas. Semua sistem berkas (filesystem) XFS mendukung project quotas, tetapi sistem berkas ext4 harus dibangun secara khusus untuk mendukungnya

  • Pastikan bahwa partisi root (atau partisi opsional runtime) ditambatkan (mount) dengan project quotas yang telah diaktifkan.

Membangun dan menambatkan sistem berkas dengan project quotas yang telah diaktifkan

Sistem berkas XFS tidak membutuhkan tindakan khusus saat dibangun; mereka secara otomatis telah dibangun dengan project quotas yang telah diaktifkan.

Sistem berkas ext4fs harus dibangun dengan mengaktifkan quotas, kemudian mereka harus diaktifkan pada sistem berkas tersebut.

% sudo mkfs.ext4 other_ext4fs_args... -E quotatype=prjquota /dev/block_device
% sudo tune2fs -O project -Q prjquota /dev/block_device

Untuk menambatkan sistem berkasnya, baik ext4fs dan XFS membutuhkan opsi prjquota disetel di dalam /etc/fstab:

/dev/block_device	/var/kubernetes_data	defaults,prjquota	0	0

Sumber daya yang diperluas

Sumber daya yang diperluas (Extended Resource) adalah nama sumber daya di luar domain kubernetes.io. Mereka memungkinkan operator klaster untuk menyatakan dan pengguna untuk menggunakan sumber daya di luar sumber daya bawaan Kubernetes.

Ada dua langkah untuk menggunakan sumber daya yang diperluas. Pertama, operator klaster harus menyatakan sebuah Extended Resource. Kedua, pengguna harus meminta sumber daya yang diperluas tersebut di dalam Pod.

Mengelola sumber daya yang diperluas

Sumber daya yang diperluas pada tingkat Node

Sumber daya yang diperluas pada tingkat Node terikat pada Node.

Sumber daya Device Plugin yang dikelola

Lihat Device Plugin untuk cara menyatakan sumber daya device plugin yang dikelola pada setiap node.

Sumber daya lainnya

Untuk menyatakan sebuah sumber daya yang diperluas tingkat Node, operator klaster dapat mengirimkan permintaan HTTP PATCH ke API server untuk menentukan kuantitas sumber daya yang tersedia pada kolom status.capacity untuk Node pada klaster. Setelah itu, status.capacity pada Node akan memiliki sumber daya baru tersebut. Kolom status.allocatable diperbarui secara otomatis dengan sumber daya baru tersebut secara asynchrounous oleh Kubelet. Perlu dicatat bahwa karena scheduler menggunakan nilai status.allocatable milik Node saat mengevaluasi muat atau tidaknya Pod, mungkin ada waktu jeda pendek antara melakukan PATCH terhadap kapasitas Node dengan sumber daya baru dengan Pod pertama yang meminta sumber daya tersebut untuk dapat dijadwalkan pada Node tersebut.

Contoh:

Berikut sebuah contoh yang menunjukkan bagaimana cara menggunakan curl untuk mengirim permintaan HTTP yang menyatakan lima sumber daya "example.com/foo" pada Node k8s-node-1 yang memiliki master k8s-master.

curl --header "Content-Type: application/json-patch+json" \
--request PATCH \
--data '[{"op": "add", "path": "/status/capacity/example.com~1foo", "value": "5"}]' \
http://k8s-master:8080/api/v1/nodes/k8s-node-1/status
Catatan: Pada permintaan HTTP di atas, ~1 adalah encoding untuk karakter / pada jalur (path) patch. Nilai jalur operasi tersebut di dalam JSON-Patch diinterpretasikan sebagai sebuah JSON-Pointer. Untuk lebih lanjut, lihat IETF RFC 6901, bagian 3.

Sumber daya yang diperluas pada tingkat klaster

Sumber daya yang diperluas pada tingkat klaster tidak terikat pada Node. Mereka biasanya dikelola oleh scheduler extender, yang menangani penggunaan sumber daya dan kuota sumber daya.

Kamu dapat menentukan sumber daya yang diperluas yang ditangani oleh scheduler extender pada konfigurasi kebijakan scheduler.

Contoh:

Konfigurasi untuk sebuah kebijakan scheduler berikut menunjukkan bahwa sumber daya yang diperluas pada tingkat klaster "example.com/foo" ditangani oleh scheduler extender.

  • Scheduler mengirim sebuah Pod ke scheduler extender hanya jika Pod tersebut meminta "example.com/foo".
  • Kolom ignoredByScheduler menentukan bahwa scheduler tidak memeriksa sumber daya "example.com/foo" pada predikat PodFitsResources miliknya.
{
  "kind": "Policy",
  "apiVersion": "v1",
  "extenders": [
    {
      "urlPrefix":"<extender-endpoint>",
      "bindVerb": "bind",
      "managedResources": [
        {
          "name": "example.com/foo",
          "ignoredByScheduler": true
        }
      ]
    }
  ]
}

Menggunakan sumber daya yang diperluas

Pengguna dapat menggunakan sumber daya yang diperluas di dalam spesifikasi Pod seperti CPU dan memori. Scheduler menangani akuntansi sumber daya tersebut agar tidak ada alokasi untuk yang melebihi jumlah yang tersedia.

API server membatasi jumlah sumber daya yang diperluas dalam bentuk bilangan bulat. Contoh jumlah yang valid adalah 3, 3000m, dan 3Ki. Contoh jumlah yang tidak valid adalah 0.5 dan 1500m.

Catatan: Sumber daya yang diperluas menggantikan Opaque Integer Resource. Pengguna dapat menggunakan prefiks nama domain selain kubernetes.io yang sudah dipakai.

Untuk menggunakan sebuah sumber daya yang diperluas di sebuah Pod, masukkan nama sumber daya tersebut sebagai nilai key dari map spec.containers[].resources.limit pada spesifikasi Container.

Catatan: Sumber daya yang diperluas tidak dapat di-overcommit, sehingga request dan limit nilainya harus sama jika keduanya ada di spesifikasi sebuah Container.

Sebuah Pod hanya dijadwalkan jika semua request sumber dayanya terpenuhi, termasuk CPU, memori, dan sumber daya yang diperluas manapun. Pod tersebut akan tetap berada pada kondisi PENDING selama request sumber daya tersebut tidak terpenuhi.

Contoh:

Pod di bawah meminta 2 CPU dan 1 "example.com/foo" (sebuah sumber daya yang diperluas).

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: myimage
    resources:
      requests:
        cpu: 2
        example.com/foo: 1
      limits:
        example.com/foo: 1

Selanjutnya

4 - Menetapkan Pod ke Node

Kamu dapat memaksa sebuah pod untuk hanya dapat berjalan pada node tertentu atau mengajukannya agar berjalan pada node tertentu. Ada beberapa cara untuk melakukan hal tersebut. Semua cara yang direkomendasikan adalah dengan menggunakan selector label untuk menetapkan pilihan yang kamu inginkan. Pada umumnya, pembatasan ini tidak dibutuhkan, sebagaimana scheduler akan melakukan penempatan yang proporsional dengan otomatis (seperti contohnya menyebar pod di node-node, tidak menempatkan pod pada node dengan sumber daya yang tidak memadai, dst.) tetapi ada keadaan-keadaan tertentu yang membuat kamu memiliki kendali lebih terhadap node yang menjadi tempat pod dijalankan, contohnya untuk memastikan pod dijalankan pada mesin yang telah terpasang SSD, atau untuk menempatkan pod-pod dari dua servis yang berbeda yang sering berkomunikasi bersamaan ke dalam zona ketersediaan yang sama.

Kamu dapat menemukan semua berkas untuk contoh-contoh berikut pada dokumentasi yang kami sediakan di sini

nodeSelector

Penggunaan nodeSelector adalah cara pembatasan pemilihan node paling sederhana yang direkomendasikan. nodeSelector adalah sebuah field pada PodSpec. nodeSelector memerinci sebuah map berisi pasangan kunci-nilai. Agar pod dapat dijalankan pada sebuah node yang memenuhi syarat, node tersebut harus memiliki masing-masing dari pasangan kunci-nilai yang dinyatakan sebagai label (namun node juga dapat memiliki label tambahan diluar itu). Penggunaan paling umum adalah satu pasang kunci-nilai.

Mari kita telusuri contoh dari penggunaan nodeSelector.

Langkah Nol: Prasyarat

Contoh ini mengasumsikan bahwa kamu memiliki pemahaman dasar tentang pod Kubernetes dan kamu telah membuat klaster Kubernetes.

Langkah Satu: Menyematkan label pada node

Jalankan kubectl get nodes untuk mendapatkan nama dari node-node yang ada dalam klaster kamu. Temukan node yang akan kamu tambahkan label, kemudian jalankan perintah kubectl label nodes <node-name> <label-key>=<label-value> untuk menambahkan label pada node yang telah kamu pilih. Sebagai contoh, jika nama node yang saya pilih adalah 'kubernetes-foo-node-1.c.a-robinson.internal' dan label yang ingin saya tambahkan adalah 'disktype=ssd', maka saya dapat menjalankan kubectl label nodes kubernetes-foo-node-1.c.a-robinson.internal disktype=ssd.

Jika terjadi kegagalan dengan kesalahan perintah yang tidak valid ("invalid command"), kemungkinan besar kamu menggunakan kubectl dengan versi lebih lama yang tidak memiliki perintah label. Dalam hal ini, lihat [versi sebelumnya] (https://github.com/kubernetes/kubernetes/blob/a053dbc313572ed60d89dae9821ecab8bfd676dc/examples/node-selection/README.md) dari petunjuk ini untuk instruksi tentang cara menetapkan label pada node.

Kamu dapat memastikan perintah telah berhasil dengan menjalankan ulang perintah kubectl get nodes --show-labels and memeriksa bahwa node yang dipilih sekarang sudah memiliki label yang ditambahkan. Kamu juga dapat menggunakan kubectl describe node "nodename" untuk melihat daftar lengkap label yang dimiliki sebuah node.

Langkah Dua: Menambahkan sebuah nodeSelector ke konfigurasi pod kamu

Ambil berkas konfigurasi pod manapun yang akan kamu jalankan, dan tambahkan sebuah bagian nodeSelector pada berkas tersebut, seperti berikut. Sebagai contoh, jika berikut ini adalah konfigurasi pod saya:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx

Kemudian tambahkan sebuah nodeSelector seperti berikut:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

Ketika kamu menjalankan perintah kubectl apply -f https://k8s.io/examples/pods/pod-nginx.yaml, pod tersebut akan dijadwalkan pada node yang memiliki label yang dirinci. Kamu dapat memastikan penambahan nodeSelector berhasil dengan menjalankan kubectl get pods -o wide dan melihat "NODE" tempat Pod ditugaskan.

Selingan: label node built-in

Sebagai tambahan dari label yang kamu sematkan, node sudah terisi dengan satu set label standar. Pada Kubernetes v1.4 label tersebut adalah

  • kubernetes.io/hostname
  • failure-domain.beta.kubernetes.io/zone
  • failure-domain.beta.kubernetes.io/region
  • beta.kubernetes.io/instance-type
  • kubernetes.io/os
  • kubernetes.io/arch
Catatan: Nilai dari label-label tersebut spesifik untuk setiap penyedia layanan cloud dan tidak dijamin reliabilitasnya. Contohnya, nilai dari kubernetes.io/hostname bisa saja sama dengan nama node pada beberapa lingkungan dan berbeda pada lingkungan lain.

Isolasi/pembatasan Node

Menambahkan label pada objek node memungkinkan penargetan pod pada node atau grup node yang spesifik. Penambahan label ini dapat digunakan untuk memastikan pod yang spesifik hanya berjalan pada node dengan isolasi, keamanan, atau pengaturan tertentu. Saat menggunakan label untuk tujuan tersebut, memilih kunci label yang tidak bisa dimodifikasi oleh proses kubelet pada node sangat direkomendasikan. Hal ini mencegah node yang telah diubah untuk menggunakan kredensial kubelet-nya untuk mengatur label-label pada objek nodenya sediri, dan mempengaruhi scheduler untuk menjadwalkan workload ke node yang telah diubah tersebut.

Plugin penerimaan NodeRestriction mencegah kubeletes untuk megatur atau mengubah label dengan awalan node-restriction.kubernetes.io/. Untuk memanfaatkan awalan label untuk isolasi node:

  1. Pastikan kamu menggunakan authorizer node dan mengaktifkan [plugin admission NodeRestriction(/docs/reference/access-authn-authz/admission-controllers/#noderestriction).

  2. Tambah label dengan awalan node-restriction.kubernetes.io/ ke objek node kamu, dan gunakan label tersebut pada node selector kamu. Contohnya, example.com.node-restriction.kubernetes.io/fips=true or example.com.node-restriction.kubernetes.io/pci-dss=true.

Afinitas dan anti-afinitas

_Field_ nodeSelector menyediakan cara yang sangat sederhana untuk membatasi pod ke node dengan label-label tertentu. Fitur afinitas/anti-afinitas saat ini bersifat beta dan memperluas tipe pembatasan yang dapat kamu nyatakan. Peningkatan kunci dari fitur ini adalah

  1. Bahasa yang lebih ekspresif (tidak hanya "AND of exact match")
  2. Kamu dapat memberikan indikasi bahwa aturan yang dinyatakan bersifat rendah/preferensi dibanding dengan persyaratan mutlak sehingga jika scheduler tidak dapat memenuhinya, pod tetap akan dijadwalkan
  3. Kamu dapat membatasi dengan label pada pod-pod lain yang berjalan pada node (atau domain topological lain), daripada dengan label pada node itu sendiri, yang memungkinkan pengaturan tentang pod yang dapat dan tidak dapat dilokasikan bersama.

Fitur afinitas terdiri dari dua tipe afinitas yaitu "node afinitas" dan "inter-pod afinitas/anti-afinitas" Node afinitas adalah seperti nodeSelector yang telah ada (tetapi dengam dua kelebihan pertama yang terdaftar di atas), sementara inter-pod afinitas/anti-afinitas membatasi pada label pod daripada label node, seperti yang dijelaskan pada item ketiga pada daftar di atas, sebagai tambahan dari item pertama dan kedua.

Field nodeSelector tetap berjalan seperti biasa, namun pada akhirnya akan ditinggalkan karena afinitas node dapat menyatakan semua yang nodeSelector dapat nyatakan.

Afinitas node (fitur beta)

Afinitas node diperkenalkan sebagai fitur alfa pada Kubernetes 1.2. Afinitas node secara konseptual mirip dengan nodeSelector yang memungkinkan kamu untuk membatasi node yang memenuhi syarat untuk penjadwalan pod, berdasarkan label pada node.

Saat ini ada dia tipe afinitas node, yaitu requiredDuringSchedulingIgnoredDuringExecution dan preferredDuringSchedulingIgnoredDuringExecution. Kamu dapat menganggap dua tipe ini sebagai "kuat" dan "lemah" secara berurutan, dalam arti tipe pertama menyatakan peraturan yang harus dipenuhi agar pod dapat dijadwalkan pada node (sama seperti nodeSelector tetapi menggunakan sintaksis yang lebih ekpresif), sementara tipe kedua menyatakan preferensi yang akan dicoba dilaksanakan tetapi tidak akan dijamin oleh scheduler. Bagian "IgnoredDuringExecution" dari nama tipe ini berarti, mirip dengan cara kerja nodeSelector, jika label pada node berubah pada runtime yang menyebabkan aturan afinitas pada pod tidak lagi terpenuhi, pod akan tetap berjalan pada node. Pada masa yang akan datang kami berencana menawarkan requiredDuringSchedulingRequiredDuringExecution yang akan berjalan seperti requiredDuringSchedulingIgnoredDuringExecution hanya saja tipe ini akan mengeluarkan pod dari node yang gagal untuk memenuhi persyaratan afinitas node pod.

Dengan denikian, contoh dari requiredDuringSchedulingIgnoredDuringExecution adalah "hanya jalankan pod pada node dengan Intel CPU" dan contoh dari preferredDuringSchedulingIgnoredDuringExecution adalah "coba jalankan set pod ini dalam zona ketersediaan XYZ, tetapi jika tidak memungkinkan, maka biarkan beberapa pod berjalan di tempat lain".

Afinitas node dinyatakan sebagai field nodeAffinity dari field affinity pada PodSpec.

Berikut ini contoh dari pod yang menggunakan afinitas node:

apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/e2e-az-name
            operator: In
            values:
            - e2e-az1
            - e2e-az2
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value
  containers:
  - name: with-node-affinity
    image: k8s.gcr.io/pause:2.0

Aturan afinitas node tersebut menyatakan pod hanya bisa ditugaskan pada node dengan label yang memiliki kunci kubernetes.io/e2e-az-name dan bernilai e2e-az1 atau e2e-az2. Selain itu, dari semua node yang memenuhi kriteria tersebut, mode dengan label dengan kunci another-node-label-key and bernilai another-node-label-value harus lebih diutamakan.

Kamu dapat meilhat operator In digunakan dalam contoh berikut. Sitaksis afinitas node yang baru mendukung operator-operator berikut: In, NotIn, Exists, DoesNotExist, Gt, Lt. Kamu dapat menggunakan NotIn dan DoesNotExist untuk mewujudkan perilaku node anti-afinitas, atau menggunakan node taints untuk menolak pod dari node tertentu.

Jika kamu menyatakan nodeSelector dan nodeAffinity. keduanya harus dipenuhi agar pod dapat dijadwalkan pada node kandidat.

Jika kamu menyatakan beberapa nodeSelectorTerms yang terkait dengan tipe nodeAffinity, maka pod akan dijadwalkan pada node jika salah satu dari nodeSelectorTerms dapat terpenuhi.

Jika kamu menyatakan beberapa matchExpressions yang terkait dengan nodeSelectorTerms, makan pod dapat dijadwalkan pada node hanya jika semua matchExpressions dapat terpenuhi.

Jika kamu menghapus atau mengubah label pada node tempat pod dijadwalkan, pod tidak akan dihapus. Dengan kata lain, pemilihan afinitas hanya bekerja pada saat waktu penjadwalan pod.

Field weight pada preferredDuringSchedulingIgnoredDuringExecution berada pada rentang nilai 1-100. Untuk setiap node yang memenuhi semua persyaratan penjadwalan (permintaan sumber daya, pernyataan afinitas RequiredDuringScheduling, dll.), scheduler akan menghitung nilai jumlah dengan melakukan iterasi pada elemen-elemen dari field ini dan menambah "bobot" pada jumlah jika node cocok dengan MatchExpressions yang sesuai. Nilai ini kemudian digabungkan dengan nilai dari fungsi prioritas lain untuk node. Node dengan nilai tertinggi adalah node lebih diutamakan.

Untuk informasi lebih lanjut tentang afinitas node kamu dapat melihat design doc.

Afinitas and anti-afinitas antar pod (fitur beta)

Afinitas and anti-afinitas antar pod diperkenalkan pada Kubernetes 1.4. Afinitas and anti-afinitas antar pod memungkinkan kamu untuk membatasi node yang memenuhi syarat untuk penjadwalan pod berdasarkan label-label pada pod yang sudah berjalan pada node daripada berdasarkan label-label pada node. Aturan tersebut berbentuk "pod ini harus (atau, dalam kasus anti-afinitas, tidak boleh) berjalan dalam X jika X itu sudah menjalankan satu atau lebih pod yang memenuhi aturan Y". Y dinyatakan sebagai sebuah LabelSelector dengan daftar namespace terkait; tidak seperti node, karena pod are namespaced (maka dari itu label-label pada pod diberi namespace secara implisit), sebuah label selector di atas label-label pod harus menentukan namespace yang akan diterapkan selector. Secara konsep X adalah domain topologi seperti node, rack, zona penyedia cloud, daerah penyedia cloud, dll. Kamu dapat menyatakannya menggunakan topologyKey yang merupakan kunci untuk label node yang digunakan sistem untuk menunjukkan domain topologi tersebut, contohnya lihat kunci label yang terdaftar di atas pada bagian Selingan: label node built-in.

Catatan: Afinitas and anti-afinitas antar pod membutuhkan jumlah pemrosesan yang substansial yang dapat memperlambat penjadwalan pada klaster berukuran besar secara signifikan. Kami tidak merekomendasikan penggunaan mereka pada klaster yang berukuran lebih besar dari beberapa ratus node.
Catatan: Anti-afinitas pod mengharuskan node untuk diberi label secara konsisten, misalnya setiap node dalam klaster harus memiliki label sesuai yang cocok dengan topologyKey. Jika sebagian atau semua node tidak memiliki label topologyKey yang dinyatakan, hal ini dapat menyebabkan perilaku yang tidak diinginkan.

Seperti afinitas node, ada dua tipe afinitas dan anti-afinitas pod, yaitu requiredDuringSchedulingIgnoredDuringExecution dan preferredDuringSchedulingIgnoredDuringExecution yang menunjukan persyaratan "kuat" vs. "lemah". Lihat deskripsi pada bagian afinitas node sebelumnya. Sebuah contoh dari afinitas requiredDuringSchedulingIgnoredDuringExecution adalah "Tempatkan bersamaan pod layanan A dan layanan B di zona yang sama, karena mereka banyak berkomunikasi satu sama lain" dan contoh preferDuringSchedulingIgnoredDuringExecution anti-afinitas akan menjadi "sebarkan pod dari layanan ini di seluruh zona" (persyaratan kuat tidak masuk akal, karena kamu mungkin memiliki lebih banyak pod daripada zona).

Afinitas antar pod dinyatakan sebagai field podAffinity dari field affinity pada PodSpec dan anti-afinitas antar pod dinyatakan sebagai field podAntiAffinity dari field affinity pada PodSpec.

Contoh pod yang menggunakan pod affinity:

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: failure-domain.beta.kubernetes.io/zone
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: failure-domain.beta.kubernetes.io/zone
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

Afinitas pada pod tersebut menetapkan sebuah aturan afinitas pod dan aturan anti-afinitas pod. Pada contoh ini, podAffinity adalah requiredDuringSchedulingIgnoredDuringExecution sementara podAntiAffinity adalah preferredDuringSchedulingIgnoredDuringExecution. Aturan afinitas pod menyatakan bahwa pod dapat dijadwalkan pada node hanya jika node tersebut berada pada zona yang sama dengan minimal satu pod yang sudah berjalan yang memiliki label dengan kunci "security" dan bernilai "S1". (Lebih detail, pod dapat berjalan pada node N jika node N memiliki label dengan kunci failure-domain.beta.kubernetes.io/zonedan nilai V sehingga ada minimal satu node dalam klaster dengan kunci failure-domain.beta.kubernetes.io/zone dan bernilai V yang menjalankan pod yang memiliki label dengan kunci "security" dan bernilai "S1".) Aturan anti-afinitas pod menyatakan bahwa pod memilih untuk tidak dijadwalkan pada sebuah node jika node tersebut sudah menjalankan pod yang memiliki label dengan kunci "security" dan bernilai "S2". (Jika topologyKey adalah failure-domain.beta.kubernetes.io/zone maka dapat diartikan bahwa pod tidak dapat dijadwalkan pada node jika node berada pada zona yang sama dengan pod yang memiliki label dengan kunci "security" dan bernilai "S2".) Lihat design doc untuk lebih banyak contoh afinitas dan anti-afinitas pod, baik requiredDuringSchedulingIgnoredDuringExecution maupun preferredDuringSchedulingIgnoredDuringExecution.

Operator yang sah untuk afinitas dan anti-afinitas pod adalah In, NotIn, Exists, DoesNotExist.

Pada dasarnya, topologyKey dapat berupa label-kunci apapun yang sah. Namun, untuk alasan performa dan keamanan, ada beberapa batasan untuk topologyKey:

  1. Untuk afinitas and anti-afinitas pod requiredDuringSchedulingIgnoredDuringExecution, topologyKey tidak boleh kosong.
  2. Untuk anti-afinitas pod requiredDuringSchedulingIgnoredDuringExecution, pengontrol penerimaan LimitPodHardAntiAffinityTopology diperkenalkan untuk membatasi topologyKey pada kubernetes.io/hostname. Jika kamu menginginkan untuk membuatnya tersedia untuk topologi khusus, kamu dapat memodifikasi pengontrol penerimaan, atau cukup menonaktifkannya saja.
  3. Untuk anti-afinitas pod preferredDuringSchedulingIgnoredDuringExecution, topologyKey yang kosong diinterpretasikan sebagai "semua topologi" ("semua topologi" sekarang dibatasi pada kombinasi dari kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone dan failure-domain.beta.kubernetes.io/region).
  4. Kecuali untuk kasus-kasus di atas, topologyKey dapat berupa label-kunci apapun yang sah.

Sebagai tambahan untuk labelSelector and topologyKey, kamu secara opsional dapat menyatakan daftar namespaces dari namespaces yang akan digunakan untuk mencocokan labelSelector (daftar ini berjalan pada level definisi yang sama dengan labelSelector dan topologyKey)

Jika dihilangkan atau kosong, daftar ini sesuai standar akan merujuk pada namespace dari pod tempat definisi afinitas/anti-afinitas dinyatakan.

Semua matchExpressions berkaitan dengan afinitas and anti-afinitas requiredDuringSchedulingIgnoredDuringExecution harus dipenuhi agar pod dapat dijadwalkan pada node.

Penggunaan yang lebih praktikal

Afinitas and anti-afinitas antar pod dapat menjadi lebih berguna saat digunakan bersamaan dengan koleksi dengan level yang lebih tinggi seperti ReplicaSets, StatefulSets, Deployments, dll. Pengguna dapat dengan mudah mengkonfigurasi bahwa satu set workload harus ditempatkan bersama dalam topologi yang didefinisikan sama, misalnya, node yang sama.

Selalu ditempatkan bersamaan pada node yang sama

Dalam klaster berisi 3 node, sebuah aplikasi web memiliki in-memory cache seperti redis. Kita menginginkan agar web-server dari aplikasi ini sebisa mungkin ditempatkan bersamaan dengan cache.

Berikut ini kutipan yaml dari deployment redis sederhana dengan 3 replika dan label selector app=store, Deployment memiliki konfigurasi PodAntiAffinity untuk memastikan scheduler tidak menempatkan replika bersamaan pada satu node.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

Kutipan yaml dari deployment webserver berikut ini memiliki konfigurasi podAntiAffinity dan podAffinity. Konfigurasi ini menginformasikan scheduler bahwa semua replika harus ditempatkan bersamaan dengan pod yang memiliki label selector app=store. Konfigurasi ini juga memastikan bahwa setiap replika webserver tidak ditempatkan bersamaan pada satu node.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - web-store
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: web-app
        image: nginx:1.12-alpine

Jika kita membuat kedua dployment di atas, klaster berisi 3 node kita seharusnya menjadi seperti berikut.

node-1node-2node-3
webserver-1webserver-2webserver-3
cache-1cache-2cache-3

st Seperti yang kamu lihat, semua 3 replika dari web-server secara otomatis ditempatkan bersama dengan cache seperti yang diharapkan.

$ kubectl get pods -o wide
NAME                           READY     STATUS    RESTARTS   AGE       IP           NODE
redis-cache-1450370735-6dzlj   1/1       Running   0          8m        10.192.4.2   kube-node-3
redis-cache-1450370735-j2j96   1/1       Running   0          8m        10.192.2.2   kube-node-1
redis-cache-1450370735-z73mh   1/1       Running   0          8m        10.192.3.1   kube-node-2
web-server-1287567482-5d4dz    1/1       Running   0          7m        10.192.2.3   kube-node-1
web-server-1287567482-6f7v5    1/1       Running   0          7m        10.192.4.3   kube-node-3
web-server-1287567482-s330j    1/1       Running   0          7m        10.192.3.2   kube-node-2
Tidak akan pernah ditempatkan bersamaan dalam node yang sama

Contoh di atas menggunakan aturan PodAntiAffinity dengan topologyKey: "kubernetes.io/hostname" untuk melakukan deploy klaster redis sehingga tidak ada dua instance terletak pada hos yang sama. Lihat tutorial ZooKeeper untuk contoh dari konfigurasi StatefulSet dengan anti-afinitas untuk ketersediaan tinggi, menggunakan teknik yang sama.

Untuk informasi lebih lanjut tentang afinitas/anti-afinitas antar pod, lihat design doc.

Kamu juga dapat mengecek Taints, yang memungkinkan sebuah node untuk menolak sekumpulan pod.

nodeName

nodeName adalah bentuk paling sederhana dari pembatasan pemilihan node, tetapi karena keterbatasannya biasanya tidak digunakan. nodeName adalah sebuah field dari PodSpec. Jika tidak kosong, scheduler mengabaikan pod dan kubelet yang berjalan pada node tersebut yang mencoba menjalankan pod. Maka, jika nodeName disediakan dalam PodSpec, ia memiliki hak yang lebih tinggi dibanding metode-metode di atas untuk pemilihan node.

Beberapa keterbatasan dari penggunaan nodeName untuk memilih node adalah:

  • Jika node yang disebut tidak ada, maka pod tidak akan dijalankan, dan dalam beberapa kasus akan dihapus secara otomatis.
  • Jika node yang disebut tidak memiliki resource yang cukup untuk mengakomodasi pod, pod akan gagal dan alasannya akan mengindikasikan sebab kegagalan, misalnya OutOfmemory atau OutOfcpu.
  • Nama node pada lingkungan cloud tidak selalu dapat diprediksi atau stabil.

Berikut ini contoh konfigurasi pod menggunakan field nodeName:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx
  nodeName: kube-01

Pod di atas akan berjalan pada node kube-01.

Selanjutnya

5 - Taint dan Toleration

Afinitas Node, seperti yang dideskripsikan di sini, adalah salah satu properti dari Pod yang menyebabkan pod tersebut memiliki preferensi untuk ditempatkan di sekelompok Node tertentu (preferensi ini dapat berupa soft constraints atau hard constraints yang harus dipenuhi). Taint merupakan kebalikan dari afinitas -- properti ini akan menyebabkan Pod memiliki preferensi untuk tidak ditempatkan pada sekelompok Node tertentu.

Taint dan toleration bekerja sama untuk memastikan Pod dijadwalkan pada Node yang sesuai. Satu atau lebih taint akan diterapkan pada suatu node; hal ini akan menyebabkan node tidak akan menerima pod yang tidak mengikuti taint yang sudah diterapkan.

Konsep

Kamu dapat menambahkan taint pada sebuah node dengan menggunakan perintah kubectl taint. Misalnya,

kubectl taint nodes node1 key=value:NoSchedule

akan menerapkan taint pada node node1. Taint tersebut memiliki key key, value value, dan effect taint NoSchedule. Hal ini artinya pod yang ada tidak akan dapat dijadwalkan pada node1 kecuali memiliki taint yang sesuai.

Untuk menghilangkan taint yang ditambahkan dengan perintah di atas, kamu dapat menggunakan perintah di bawah ini:

kubectl taint nodes node1 key:NoSchedule-

Kamu dapat memberikan spesifikasi toleration untuk pod pada bagian PodSpec. Kedua toleration yang diterapkan di bawa ini "sesuai" dengan taint yang taint yang dibuat dengan perintah kubectl taint di atas, sehingga sebuah pod dengan toleration yang sudah didefinisikan akan mampu di-schedule ke node node:

tolerations:
- key: "key"
  operator: "Equal"
  value: "value"
  effect: "NoSchedule"
tolerations:
- key: "key"
  operator: "Exists"
  effect: "NoSchedule"

Sebuah toleration "sesuai" dengan sebuah taint jika key dan efek yang ditimbulkan sama:

  • operator dianggap Exists (pada kasus dimana tidak ada value yang diberikan), atau
  • operator dianggap Equal dan value yang ada sama

Operator bernilai Equal secara default jika tidak diberikan spesifikasi khusus.

Catatan:

Terdapat dua kasus khusus:

  • Sebuah key dengan operator Exists akan sesuai dengan semua key, value, dan effect yang ada. Dengan kata lain, tolaration ini akan menerima semua hal yang diberikan.
tolerations:
- operator: "Exists"
  • Sebuah effect yang kosong akan dianggap sesuai dengan semua effect dengan key key.
tolerations:
- key: "key"
  operator: "Exists"

Contoh yang diberikan di atas menggunakan effect untuk NoSchedule. Alternatif lain yang dapat digunakan adalah effect untuk PreferNoSchedule. PreferNoSchedule merupakan "preferensi" yang lebih fleksibel dari NoSchedule -- sistem akan mencoba untuk tidak menempatkan pod yang tidak menoleransi taint pada node, tapi hal ini bukan merupakan sesuatu yang harus dipenuhi. Jenis ketiga dari effect adalah NoExecute, akan dijelaskan selanjutnya.

Kamu dapat menerapkan beberapa taint sekaligus pada node atau beberapa toleration sekaligus pada sebuah pod. Mekanisme Kubernetes dapat memproses beberapa taint dan toleration sekaligus sama halnya seperti sebuah filter: memulai dengan taint yang ada pada node, kemudian mengabaikan taint yang sesuai pada pod yang memiliki toleration yang sesuai; kemudian taint yang diterapkan pada pod yang sudah disaring tadi akan menghasilkan suatu effect pada pod. Secara khusus:

  • jika terdapat taint yang tidak tersaring dengan effect NoSchedule maka Kubernetes tidak akan menempatkan pod pada node tersebut
  • jika tidak terdapat taint yang tidak tersaring dengan effect NoSchedule tapi terdapat setidaknya satu taint yang tidak tersaring dengan effect PreferNoSchedule maka Kubernetes akan mencoba untuk tidak akan menempatkan pod pada node tersebut
  • jika terdapat taint yang tidak tersaring dengan effect NoExecute maka pod akan berada dalam kondisi evicted dari node (jika pod tersebut sudah terlanjur ditempatkan pada node tersebut), dan tidak akan di-schedule lagi pada node tersebut.

Sebagai contoh, bayangkan kamu memberikan taint pada node sebagai berikut:

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

Dan pod memiliki dua toleration:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

Pada kasus ini, pod tidak akan di-schedule pada node, karena tidak ada toleration yang sesuai dengan taint ketiga. Akan tetapi, pod yang sebelumnya sudah dijalankan di node dimana taint ditambahkan akan tetap jalan, karena taint ketiga merupakan taint yang tidak ditoleransi oleh pod.

Pada umumnya, jika sebuah taint memiliki effect NoExecute ditambahkan pada node, maka semua pod yang tidak menoleransi taint tersebut akan berada dalam state evicted secara langsung, dan semua pod yang menoleransi taint tersebut tidak akan berjalan seperti biasanya (tidak dalam state evicted). Meskipun demikian, toleration dengan effect NoExecute dapat dispesfikasikan sebagai field opsional tolerationSeconds yang memberikan perintah berapa lama suatu pod akan berada pada node apabila sebuah taint ditambahkan. Contohnya:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

ini berarti apabila sebuah pod sedang dalam berada dalam state running, kemudian sebuah taint yang sesuai ditambahkan pada node, maka pod tersebut akan tetap berada di dalam node untuk periode 3600 detik sebelum state-nya berubah menjadi evicted. Jika taint dihapus sebelum periode tersebut, maka pod tetap berjalan sebagaimana mestinya.

Contoh Penggunaan

Taint dan toleration adalah mekanisme fleksibel yang digunakan untuk memaksa pod agar tidak dijadwalkan pada node-node tertentu atau mengubah state pod menjadi evicted. Berikut adalah beberapa contoh penggunaannya:

  • Node-Node yang Sifatnya Dedicated: Jika kamu ingin menggunakan sekumpulan node dengan penggunaan eksklusif dari sekumpulan pengguna, kamu dapat menambahkan taint pada node-node tersebut (misalnya, kubectl taint nodes nodename dedicated=groupName:NoSchedule) dan kemudian menambahkan toleration yang sesuai pada pod-pod yang berada di dalamnya (hal ini dapat dilakukan dengan mudah dengan cara menulis admission controller yang bersifat khusus). Pod-pod dengan toleration nantinya akan diperbolehkannya untuk menggunakan node yang sudah di-taint (atau dengan kata lain didedikasikan penggunaannya) maupun node lain yang ada di dalam klaster. Jika kamu ingin mendedikasikan node khusus yang hanya digunakan oleh pod-pod tadi serta memastikan pod-pod tadi hanya menggunakan node yang didedikasikan, maka kamu harus menambahkan sebuah label yang serupa dengan taint yang diberikan pada sekelompok node (misalnya, dedicated=groupName), dan admission controller sebaiknya menambahkan afininitas node untuk memastikan pod-pod tadi hanya dijadwalkan pada node dengan label dedicated=groupName.

  • Node-Node dengan Perangkat Keras Khusus: Pada suatu klaster dimana sebagian kecuali node memiliki perangkat keras khusus (misalnya GPU), kita ingin memastikan hanya pod-pod yang membutuhkan GPU saja yang dijadwalkan di node dengan GPU. Hal ini dapat dilakukan dengan memberikan taint pada node yang memiliki perangkat keras khusus (misalnya, kubectl taint nodes nodename special=true:NoSchedule atau kubectl taint nodes nodename special=true:PreferNoSchedule) serta menambahkan toleration yang sesuai pada pod yang menggunakan node dengan perangkat keras khusus. Seperti halnya pada kebutuhan dedicated node, hal ini dapat dilakukan dengan mudah dengan cara menulis admission controller yang bersifat khusus. Misalnya, kita dapat menggunakan Extended Resource untuk merepresentasikan perangkat keras khusus, kemudian taint node dengan perangkat keras khusus dengan nama extended resource dan jalankan admission controller ExtendedResourceToleration. Setelah itu, karena node yang ada sudah di-taint, maka tidak akan ada pod yang tidak memiliki toleration yang akan dijadwalkan pada node tersebut_. Meskipun begitu, ketika kamu membuat suatu _pod_ yang membutuhkan _extended resource_, maka _admission controller_ dari ExtendedResourceToleration akan mengoreksi _toleration_ sehingga _pod_ tersebut dapat dijadwalkan pada _node_ dengan perangkat keras khusus. Dengan demikian, kamu tidak perlu menambahkan _toleration_ secara manual pada pod yang ada.

  • Eviction berbasis Taint (fitur beta): Konfigurasi eviction per pod yang terjadi ketika pod mengalami gangguan, hal ini akan dibahas lebih lanjut di bagian selanjutnya.

Eviction berbasis Taint

Sebelumnya, kita sudah pernah membahas soal effect taint NoExecute, yang memengaruhi pod yang sudah dijalankan dengan cara sebagai berikut:

  • pod yang tidak menoleransi taint akan segera diubah state-nya menjadi evicted
  • pod yang menoleransi taint yang tidak menspesifikasikan tolerationSeconds pada spesifikasi toleration yang ada akan tetap berada di dalam node tanpa adanya batas waktu tertentu
  • pod yang menoleransi taint yang menspesifikasikan tolerationSeconds spesifikasi toleration yang ada akan tetap berada di dalam node hingga batas waktu tertentu

Sebagai tambahan, Kubernetes 1.6 memperkenalkan dukungan alfa untuk merepresentasikan node yang bermasalah. Dengan kata lain, node controller akan secara otomatis memberikan taint pada sebuah node apabila node tersebut memenuhi kriteria tertentu. Berikut merupakan taint yang secara default disediakan:

  • node.kubernetes.io/not-ready: Node berada dalam state not ready. Hal ini terjadi apabila value dari NodeCondition Ready adalah "False".
  • node.kubernetes.io/unreachable: Node berada dalam state unreachable dari node controller Hal ini terjadi apabila value dari NodeCondition Ready adalah "Unknown".
  • node.kubernetes.io/out-of-disk: Node kehabisan kapasitas disk.
  • node.kubernetes.io/memory-pressure: Node berada diambang kapasitas memori.
  • node.kubernetes.io/disk-pressure: Node berada diambang kapasitas disk.
  • node.kubernetes.io/network-unavailable: Jaringan pada Node bersifat unavailable.
  • node.kubernetes.io/unschedulable: Node tidak dapat dijadwalkan.
  • node.cloudprovider.kubernetes.io/uninitialized: Ketika kubelet dijalankan dengan penyedia layanan cloud "eksternal", taint ini akan diterapkan pada node untuk menandai node tersebut tidak digunakan. Setelah kontroler dari cloud-controller-manager melakukan inisiasi node tersebut, maka kubelet akan menghapus taint yang ada.

Pada versi 1.13, fitur TaintBasedEvictions diubah menjadi beta dan diaktifkan secara default, dengan demikian taint-taint tersebut secara otomatis ditambahkan oleh NodeController (atau kubelet) dan logika normal untuk melakukan eviction pada pod dari suatu node tertentu berdasarkan value dari Ready yang ada pada NodeCondition dinonaktifkan.

Catatan: Untuk menjaga perilaku rate limiting yang ada pada eviction pod apabila node mengalami masalah, sistem sebenarnya menambahkan taint dalam bentuk rate limiter. Hal ini mencegah eviction besar-besaran pada pod pada skenario dimana master menjadi terpisah dari node lainnya.

Fitur beta ini, bersamaan dengan tolerationSeconds, mengizinkan sebuah pod untuk menspesifikasikan berapa lama pod harus tetap sesuai dengan sebuah node apabila node tersebut bermasalah.

Misalnya, sebuah aplikasi dengan banyak state lokal akan lebih baik untuk tetap berada di suatu node pada saat terjadi partisi jaringan, dengan harapan partisi jaringan tersebut dapat diselesaikan dan mekanisme eviction pod tidak akan dilakukan. Toleration yang ditambahkan akan berbentuk sebagai berikut:

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

Perhatikan bahwa Kubernetes secara otomatis menambahkan toleration untuk node.kubernetes.io/not-ready dengan tolerationSeconds=300 kecuali konfigurasi lain disediakan oleh pengguna. Kubernetes juga secara otomatis menambahkan toleration untuk node.kubernetes.io/unreachable dengan tolerationSeconds=300 kecuali konfigurasi lain disediakan oleh pengguna.

Toleration yang ditambahkan secara otomatis ini menjamin bahwa perilaku default dari suatu pod adalah tetap bertahan selama 5 menit pada node apabila salah satu masalah terdeteksi. Kedua toleration default tadi ditambahkan oleh DefaultTolerationSeconds admission controller.

Pod-pod pada DaemonSet dibuat dengan toleration NoExecute untuk taint tanpa tolerationSeconds:

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

Hal ini menjamin pod-pod yang merupakan bagian dari DaemonSet tidak pernah berada di dalam state evicted apabila terjadi permasalahan pada node.

Taint pada Node berdasarkan Kondisi Tertentu

Pada versi 1.12, fitur TaintNodesByCondition menjadi fitur beta, dengan demikian lifecycle dari kontroler node akan secara otomatis menambahkan taint sesuai dengan kondisi node. Hal yang sama juga terjadi pada scheduler, scheduler tidak bertugas memeriksa kondisi node tetapi kondisi taint. Hal ini memastikan bahwa kondisi node tidak memengaruhi apa yang dijadwalkan di node. Pengguna dapat memilih untuk mengabaikan beberapa permasalahan yang ada pada node (yang direpresentasikan oleh kondisi Node) dengan menambahkan toleration Pod NoSchedule. Sedangkan taint dengan effect NoExecute dikendalikan oleh TaintBasedEviction yang merupakan fitur beta yang diaktifkan secara default oleh Kubernetes sejak versi 1.13.

Sejak Kubernetes versi 1.8, kontroler DaemonSet akan secara otomatis menambahkan toleration NoSchedule pada semua daemon untuk menjaga fungsionalitas DaemonSet.

  • node.kubernetes.io/memory-pressure
  • node.kubernetes.io/disk-pressure
  • node.kubernetes.io/out-of-disk (hanya untuk pod yang bersifat critical)
  • node.kubernetes.io/unschedulable (versi 1.10 atau yang lebih baru)
  • node.kubernetes.io/network-unavailable (hanya untuk jaringan host)

Menambahkan toleration ini menjamin backward compatibility. Kamu juga dapat menambahkan toleration lain pada DaemonSet.

6 - Secret

Objek secret pada Kubernetes mengizinkan kamu menyimpan dan mengatur informasi yang sifatnya sensitif, seperti password, token OAuth, dan ssh keys. Menyimpan informasi yang sifatnya sensitif ini ke dalam secret cenderung lebih aman dan fleksible jika dibandingkan dengan menyimpan informasi tersebut secara apa adanya pada definisi Pod atau di dalam container image. Silahkan lihat Dokumen desain Secret untuk informasi yang sifatnya mendetail.

Ikhtisar Secret

Sebuah Secret merupakan sebuah objek yang mengandung informasi yang sifatnya sensitif, seperti password, token, atau key. Informasi tersebut sebenarnya bisa saja disimpan di dalam spesifikasi Pod atau image; meskipun demikian, melakukan penyimpanan di dalam objek Secret mengizinkan pengguna untuk memiliki kontrol lebih lanjut mengenai bagaimana Secret ini disimpan, serta mencegah tereksposnya informasi sensitif secara tidak disengaja.

Baik pengguna dan sistem memiliki kemampuan untuk membuat objek Secret.

Untuk menggunakan Secret, sebuah Pod haruslah merujuk pada Secret tersebut. Sebuah Secret dapat digunakan di dalam sebuah Pod melalui dua cara: sebagai file yang ada di dalam volume volume yang di-mount pada salah satu container Pod, atau digunakan oleh kubelet ketika menarik image yang digunakan di dalam Pod.

Secret Built-in

Sebuah Service Account akan Secara Otomatis Dibuat dan Meng-attach Secret dengan Kredensial API

Kubernetes secara otomatis membuat secret yang mengandung kredensial yang digunakan untuk mengakses API, serta secara otomatis memerintahkan Pod untuk menggunakan Secret ini.

Mekanisme otomatisasi pembuatan secret dan penggunaan kredensial API dapat di nonaktifkan atau di-override jika kamu menginginkannya. Meskipun begitu, jika apa yang kamu butuhkan hanyalah mengakses apiserver secara aman, maka mekanisme default inilah yang disarankan.

Baca lebih lanjut dokumentasi Service Account untuk informasi lebih lanjut mengenai bagaimana cara kerja Service Account.

Membuat Objek Secret Kamu Sendiri

Membuat Secret dengan Menggunakan kubectl

Misalnya saja, beberapa Pod memerlukan akses ke sebuah basis data. Kemudian username dan password yang harus digunakan oleh Pod-Pod tersebut berada pada mesin lokal kamu dalam bentuk file-file ./username.txt dan ./password.txt.

# Buatlah berkas yang selanjutnya akan digunakan pada contoh-contoh selanjutnya
echo -n 'admin' > ./username.txt
echo -n '1f2d1e2e67df' > ./password.txt

Perintah kubectl create secret akan mengemas file-file ini menjadi Secret dan membuat sebuah objek pada Apiserver.

kubectl create secret generic db-user-pass --from-file=./username.txt --from-file=./password.txt
secret "db-user-pass" created
Catatan: Karakter spesial seperti $, \*, and ! membutuhkan mekanisme escaping. Jika password yang kamu gunakan mengandung karakter spesial, kamu perlu melakukan escape karakter dengan menggunakan karakter \\. Contohnya, apabila password yang kamu miliki adalah S!B\*d$zDsb, maka kamu harus memanggil perintah kubectl dengan cara berikut: kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password=S\!B\\*d\$zDsb Perhatikan bahwa kamu tidak perlu melakukan escape karakter apabila massukan yang kamu berikan merupakan file (--from-file).

Kamu dapat memastikan apakah suatu Secret sudah dibuat atau belum dengan menggunakan perintah:

kubectl get secrets
NAME                  TYPE                                  DATA      AGE
db-user-pass          Opaque                                2         51s
kubectl describe secrets/db-user-pass
Name:            db-user-pass
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

Data
====
password.txt:    12 bytes
username.txt:    5 bytes
Catatan: Perintah-perintah kubectl get dan kubectl describe secara default akan mencegah ditampilkannya informasi yang ada di dalam Secret. Hal ini dilakukan untuk melindungi agar Secret tidak terekspos secara tidak disengaja oleh orang lain, atau tersimpan di dalam log terminal.

Kamu dapat membaca bagaimana cara melakukan decode sebuah secret untuk mengetahui bagaimana cara melihat isi dari Secret.

Membuat Secret Secara Manual

Kamu dapat membuat sebuah Secret dengan terlebih dahulu membuat file yang berisikan informasi yang ingin kamu jadikan Secret dalam bentuk yaml atau json dan kemudian membuat objek dengan menggunakan file tersebut. Secret mengandung dua buah map: data dan stringData. Field data digunakan untuk menyimpan sembarang data, yang di-encode menggunakan base64. Sementara itu stringData disediakan untuk memudahkan kamu untuk menyimpan informasi sensitif dalam format yang tidak di-encode.

Sebagai contoh, untuk menyimpan dua buah string di dalam Secret dengan menggunakan field data, ubahlah informasi tersebut ke dalam base64 dengan menggunakan mekanisme sebagai berikut:

echo -n 'admin' | base64
YWRtaW4=
echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm

Buatlah sebuah Secret yang memiliki bentuk sebagai berikut:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

Kemudian buatlah Secret menggunakan perintah kubectl apply:

kubectl apply -f ./secret.yaml
secret "mysecret" created

Untuk beberapa skenario, kamu bisa saja ingin menggunakan opsi field stringData. Field ini mengizinkan kamu untuk memberikan masukan berupa informasi yang belum di-encode secara langsung pada sebuah Secret, informasi dalam bentuk string ini kemudian akan di-encode ketika Secret dibuat maupun diubah.

Contoh praktikal dari hal ini adalah ketika kamu melakukan proses deploy aplikasi yang menggunakan Secret sebagai penyimpanan file konfigurasi, dan kamu ingin mengisi bagian dari konfigurasi file tersebut ketika aplikasi di_deploy_.

Jika kamu ingin aplikasi kamu menggunakan file konfigurasi berikut:

apiUrl: "https://my.api.com/api/v1"
username: "user"
password: "password"

Kamu dapat menyimpan Secret ini dengan menggunakan cara berikut:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
stringData:
  config.yaml: |-
    apiUrl: "https://my.api.com/api/v1"
    username: {{username}}
    password: {{password}}    

Alat deployment yang kamu gunakan kemudian akan mengubah templat variabel {{username}} dan {{password}} sebelum menjalankan perintah kubectl apply.

stringData merupakan field yang sifatnya write-only untuk alasan kenyamanan pengguna. Field ini tidak pernah ditampilkan ketika Secret dibaca. Sebagai contoh, misalkan saja kamu menjalankan perintah sebagai berikut:

kubectl get secret mysecret -o yaml

Keluaran yang diberikan kurang lebih akan ditampilkan sebagai berikut:

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:40:59Z
  name: mysecret
  namespace: default
  resourceVersion: "7225"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: c280ad2e-e916-11e8-98f2-025000000001
type: Opaque
data:
  config.yaml: YXBpVXJsOiAiaHR0cHM6Ly9teS5hcGkuY29tL2FwaS92MSIKdXNlcm5hbWU6IHt7dXNlcm5hbWV9fQpwYXNzd29yZDoge3twYXNzd29yZH19

Jika sebuah field dispesifikasikan dalam bentuk data maupun stringData, maka nilai dari stringData-lah yang akan digunakan. Sebagai contoh, misalkan saja terdapat definisi Secret sebagai berikut:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
stringData:
  username: administrator

Akan menghasilkan Secret sebagai berikut:

apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: 2018-11-15T20:46:46Z
  name: mysecret
  namespace: default
  resourceVersion: "7579"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: 91460ecb-e917-11e8-98f2-025000000001
type: Opaque
data:
  username: YWRtaW5pc3RyYXRvcg==

Dimana string YWRtaW5pc3RyYXRvcg== akan di-decode sebagai administrator.

Key dari data dan stringData yang boleh tersusun atas karakter alfanumerik, '-', '_' atau '.'.

Catatan Encoding: Value dari JSON dan YAML yang sudah diseriakisasi dari data Secret akan di-encode ke dalam string base64. Newline dianggap tidak valid pada string ini dan harus dihilangkan. Ketika pengguna Darwin/macOS menggunakan alat base64, maka pengguna tersebut harus menghindari opsi -b yang digunakan untuk memecah baris yang terlalu panjang. Sebaliknya pengguna Linux harus menambahkan opsi -w 0 pada perintah base64 atau melakukan mekanisme pipeline base64 | tr -d '\n' jika tidak terdapat opsi -w.

Membuat Secret dengan Menggunakan Generator

Kubectl mendukung mekanisme manajemen objek dengan menggunakan Kustomize sejak versi 1.14. Dengan fitur baru ini, kamu juga dapat membuat sebuah Secret dari sebuah generator dan kemudian mengaplikasikannya untuk membuat sebuah objek pada Apiserver. Generator yang digunakan haruslah dispesifikasikan di dalam sebuah file kustomization.yaml di dalam sebuah direktori.

Sebagai contoh, untuk menghasilan sebuah Secret dari file-file ./username.txt dan ./password.txt

# Membuat sebuah berkas kustomization.yaml dengan SecretGenerator
cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
  files:
  - username.txt
  - password.txt
EOF

Gunakan direktori kustomization untuk membuat objek Secret yang diinginkan.

$ kubectl apply -k .
secret/db-user-pass-96mffmfh4k created

Kamu dapat memastikan Secret tersebut sudah dibuat dengan menggunakan perintah berikut:

$ kubectl get secrets
NAME                             TYPE                                  DATA      AGE
db-user-pass-96mffmfh4k          Opaque                                2         51s

$ kubectl describe secrets/db-user-pass-96mffmfh4k
Name:            db-user-pass
Namespace:       default
Labels:          <none>
Annotations:     <none>

Type:            Opaque

Data
====
password.txt:    12 bytes
username.txt:    5 bytes

Sebagai contoh, untuk membuat sebuah Secret dari literal username=admin dan password=secret, kamu dapat menspesifikasikan generator Secret pada file kustomization.yaml sebagai

# Membuat sebuah berkas kustomization.yaml dengan menggunakan SecretGenerator
$ cat <<EOF >./kustomization.yaml
secretGenerator:
- name: db-user-pass
  literals:
  - username=admin
  - password=secret
EOF

Aplikasikan direktori kustomization untuk membuat objek Secret.

$ kubectl apply -k .
secret/db-user-pass-dddghtt9b5 created
Catatan: Secret yang dihasilkan nantinya akan memiliki tambahan sufix dengan cara melakukan teknik hashing pada isi Secret tersebut. Hal ini dilakukan untuk menjamin dibuatnya sebuah Secret baru setiap kali terjadi perubahan isi dari Secret tersebut.

Melakukan Proses Decode pada Secret

Secret dapat dibaca dengan menggunakan perintah kubectl get secret. Misalnya saja, untuk membaca Secret yang dibuat pada bagian sebelumya:

kubectl get secret mysecret -o yaml
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: 2016-01-22T18:41:56Z
  name: mysecret
  namespace: default
  resourceVersion: "164619"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

Kemudian lakukan mekanisme decode field password:

echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
1f2d1e2e67df

Menggunakan Secret

Secret dapat di-mount sebagai volume data atau dapat diekspos sebagai variabel-variabel environment dapat digunakan di dalam Pod. Secret ini juga dapat digunakan secara langsug oleh bagian lain dari sistem, tanpa secara langsung berkaitan dengan Pod. Sebagai contoh, Secret dapat berisikan kredensial bagian suatu sistem lain yang digunakan untuk berinteraksi dengan sistem eksternal yang kamu butuhkan.

Menggunakan Secret sebagai File melalui Pod

Berikut adalah langkah yang harus kamu penuhi agar kamu dapat menggunakan Secret di dalam volume dalam sebuah Pod:

  1. Buatlah sebuah Secret, atau gunakan sebuah Secret yang sudah kamu buat sebelumnya. Beberapa Pod dapat merujuk pada sebuah Secret yang sama.
  2. Modifikasi definisi Pod kamu dengan cara menambahkan sebuah volume di bawah .spec.volumes[]. Berilah volume tersebut nama, dan pastikan field .spec.volumes[].secret.secretName merujuk pada nama yang sama dengan objek secret.
  3. Tambahkan field .spec.containers[].volumeMounts[] pada setiap container yang membutuhkan Secret. Berikan spesifikasi .spec.containers[].volumeMounts[].readOnly = true dan .spec.containers[].volumeMounts[].mountPath pada direktori dimana Secret tersebut diletakkan.
  4. Modifikasi image dan/atau command line kamu agar program yang kamu miliki merujuk pada file di dalam direktori tersebut. Setiap key pada map data Secret akan menjadi nama dari sebuah file pada mountPath.

Berikut merupakan salah satu contoh dimana sebuah Pod melakukan proses mount Secret pada sebuah volume:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret

Setiap Secret yang ingin kamu gunakan harus dirujuk pada field .spec.volumes.

Jika terdapat lebih dari satu container di dalam Pod, maka setiap container akan membutuhkan blok volumeMounts-nya masing-masing, meskipun demikian hanya sebuah field .spec.volumes yang dibutuhkan untuk setiap Secret.

Kamu dapat menyimpan banyak file ke dalam satu Secret, atau menggunakan banyak Secret, hal ini tentunya bergantung pada preferensi pengguna.

Proyeksi key Secret pada Suatu Path Spesifik

Kita juga dapat mengontrol path di dalam volume di mana sebuah Secret diproyeksikan. Kamu dapat menggunakan field .spec.volumes[].secret.items untuk mengubah path target dari setiap key:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username

Apa yang akan terjadi jika kita menggunakan definisi di atas:

  • Secret username akan disimpan pada file /etc/foo/my-group/my-username dan bukan /etc/foo/username.
  • Secret password tidak akan diproyeksikan.

Jika field .spec.volumes[].secret.items digunakan, hanya key-key yang dispesifikan di dalam items yang diproyeksikan. Untuk mengonsumsi semua key-key yang ada dari Secret, semua key yang ada harus didaftarkan pada field items. Semua key yang didaftarkan juga harus ada di dalam Secret tadi. Jika tidak, volume yang didefinisikan tidak akan dibuat.

Permission File-File Secret

Kamu juga dapat menspesifikasikan mode permission dari file Secret yang kamu inginkan. Jika kamu tidak menspesifikasikan hal tersebut, maka nilai default yang akan diberikan adalah 0644 is used by default. Kamu dapat memberikan mode default untuk semua Secret yang ada serta melakukan mekanisme override permission pada setiap key jika memang diperlukan.

Sebagai contoh, kamu dapat memberikan spesifikasi mode default sebagai berikut:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 256

Kemudian, sebuah Secret akan di-mount pada /etc/foo, selanjutnya semua file yang dibuat pada volume secret tersebut akan memiliki permission 0400.

Perhatikan bahwa spesifikasi JSON tidak mendukung notasi octal, dengan demikian gunakanlah value 256 untuk permission 0400. Jika kamu menggunakan format YAML untuk spesifikasi Pod, kamu dapat menggunakan notasi octal untuk memberikan spesifikasi permission dengan cara yang lebih natural.

Kamu juga dapat melakukan mekanisme pemetaan, seperti yang sudah dilakukan pada contoh sebelumnya, dan kemudian memberikan spesifikasi permission yang berbeda untuk file yang berbeda.

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username
        mode: 511

Pada kasus tersebut, file yang dihasilkan pada /etc/foo/my-group/my-username akan memiliki permission 0777. Karena terdapat batasan pada representasi JSON, maka kamu harus memberikan spesifikasi mode permission dalam bentuk notasi desimal.

Perhatikan bahwa permission ini bida saja ditampilkan dalam bentuk notasi desimal, hal ini akan ditampilkan pada bagian selanjutnya.

Mengonsumsi Value dari Secret melalui Volume

Di dalam sebuah container dimana volume secret di-mount, key dari Secret akan ditampilkan sebagai file dan value dari Secret yang berada dalam bentuk base64 ini akan di-decode dam disimpan pada file-file tadi. Berikut merupakan hasil dari eksekusi perintah di dalam container berdasarkan contoh yang telah dipaparkan di atas:

ls /etc/foo/
username
password
cat /etc/foo/username
admin
cat /etc/foo/password
1f2d1e2e67df

Program di dalam container bertanggung jawab untuk membaca Secret dari file-file yang ada.

Secret yang di-mount Akan Diubah Secara Otomatis

Ketika sebuah Secret yang sedang digunakan di dalam volume diubah, maka key yang ada juga akan diubah. Kubelet akan melakukan mekanisme pengecekan secara periodik apakah terdapat perubahan pada Secret yang telah di-mount. Meskipun demikian, proses pengecekan ini dilakukan dengan menggunakan cache lokal untuk mendapatkan value saat ini dari sebuah Secret. Tipe cache yang ada dapat diatur dengan menggunakan (field ConfigMapAndSecretChangeDetectionStrategy pada struct KubeletConfiguration). Mekanisme ini kemudian dapat diteruskan dengan mekanisme watch(default), ttl, atau melakukan pengalihan semua request secara langsung pada kube-apiserver. Sebagai hasilnya, delay total dari pertama kali Secret diubah hingga dilakukannya mekanisme proyeksi key yang baru pada Pod berlangsung dalam jangka waktu sinkronisasi periodik kubelet + delay propagasi cache, dimana delay propagasi cache bergantung pada jenis cache yang digunakan (ini sama dengan delay propagasi watch, ttl dari cache, atau nol).

Catatan: Sebuah container menggunakan Secret sebagai subPath dari volume yang di-mount tidak akan menerima perubahan Secret.

Menggunakan Secret sebagai Variabel Environment

Berikut merupakan langkah-langkah yang harus kamu terapkan, untuk menggunakan secret sebagai variabel _environment_ pada sebuah Pod:

  1. Buatlah sebuah Secret, atau gunakan sebuah Secret yang sudah kamu buat sebelumnya. Beberapa Pod dapat merujuk pada sebuah Secret yang sama.
  2. Modifikasi definisi Pod pada setiap container dimana kamu menginginkan container tersebut dapat mengonsumsi your Pod definition in each container that you wish to consume the value of a secret key to add an environment variabele for each secret key you wish to consume. The environment variabele that consumes the secret key should populate the secret's name and key in env[].valueFrom.secretKeyRef.
  3. Modifikasi image dan/atau command line kamu agar program yang kamu miliki merujuk pada value yang sudah didefinisikan pada variabel environment.

Berikut merupakan contoh dimana sebuah Pod menggunakan Secret sebagai variabel environment:

apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never

Menggunakan Secret dari Variabel Environment

Di dalam sebuah container yang mengkonsumsi Secret pada sebuah variabel environment, key dari sebuah secret akan ditampilkan sebagai variabel environment pada umumnya dengan value berupa informasi yang telah di-decode ke dalam base64. Berikut merupakan hasil yang didapatkan apabila perintah-perintah di bawah ini dijalankan dari dalam container yang didefinisikan di atas:

echo $SECRET_USERNAME
admin
echo $SECRET_PASSWORD
1f2d1e2e67df

Menggunakan imagePullSecrets

Sebuah imagePullSecret merupakan salah satu cara yang dapat digunakan untuk menempatkan secret yang mengandung password dari registri Docker (atau registri image lainnya) pada Kubelet, sehingga Kubelet dapat mengunduh image dan menempatkannya pada Pod.

Memberikan spesifikasi manual dari sebuah imagePullSecret

Penggunaan imagePullSecrets dideskripsikan di dalam dokumentasi image

Mekanisme yang Dapat Diterapkan agar imagePullSecrets dapat Secara Otomatis Digunakan

Kamu dapat secara manual membuat sebuah imagePullSecret, serta merujuk imagePullSecret yang sudah kamu buat dari sebuah serviceAccount. Semua Pod yang dibuat dengan menggunakan serviceAccount tadi atau serviceAccount default akan menerima field imagePullSecret dari serviceAccount yang digunakan. Bacalah Cara menambahkan ImagePullSecrets pada sebuah service account untuk informasi lebih detail soal proses yang dijalankan.

Mekanisme Mounting Otomatis dari Secret yang Sudah Dibuat

Secret yang dibuat secara manual (misalnya, secret yang mengandung token yang dapat digunakan untuk mengakses akun GitHub) dapat di-mount secara otomatis pada sebuah Pod berdasarkan service account yang digunakan oleh Pod tadi. Baca Bagaimana Penggunaan PodPreset untuk Memasukkan Informasi ke Dalam Pod untuk informasi lebih lanjut.

Detail

Batasan-Batasan

Sumber dari secret volume akan divalidasi untuk menjamin rujukan pada objek yang dispesifikasikan mengarah pada objek dengan type Secret. Oleh karenanya, sebuah secret harus dibuat sebelum Pod yang merujuk pada secret tersebut dibuat.

Sebuah objek API Secret berada di dalam sebuah namespace. Objek-objek ini hanya dapat dirujuk oleh Pod-Pod yang ada pada namespace yang sama.

Secret memiliki batasi dalam hal ukuran maksimalnya yaitu hanya sampai 1MiB per objek. Oleh karena itulah, pembuatan secret dalam ukuran yang sangat besar tidak dianjurkan karena dapat menghabiskan sumber daya apiserver dan memori kubelet. Meskipun demikian, pembuatan banyak secret dengan ukuran kecil juga dapat menhabiskan memori. Pembatasan sumber daya yang diizinkan untuk pembuatan secret merupakan salah satu fitur tambahan yang direncanakan kedepannya.

Kubelet hanya mendukung penggunaan secret oleh Pod apabila Pod tersebut didapatkan melalui apiserver. Hal ini termasuk Pod yang dibuat dengan menggunakan kubectl, atau secara tak langsung melalui replication controller. Hal ini tidak termasuk Pod yang dibuat melalui flag --manifest-url yang ada pada kubelet, maupun REST API yang disediakan (hal ini bukanlah merupakan mekanisme umum yang dilakukan untuk membuat sebuah Pod).

Secret harus dibuat sebelum digunakan oleh Pod sebagai variabel environment, kecuali apabila variabel environment ini dianggap opsional. Rujukan pada Secret yang tidak dapat dipenuhi akan menyebabkan Pod gagal start.

Rujukan melalui secretKeyRef pada key yang tidak ada pada named Secret akan akan menyebabkan Pod gagal start.

Secret yang digunakan untuk memenuhi variabel environment melalui envFrom yang memiliki key yang dianggap memiliki penamaan yang tidak valid akan diabaikan. Hal ini akan akan menyebabkan Pod gagal start. Selanjutnya akan terdapat event dengan alasan InvalidvariabeleNames dan pesan yang berisikan list dari key yang diabaikan akibat penamaan yang tidak valid. Contoh yang ada akan menunjukkan sebuah pod yang merujuk pada secret default/mysecret yang mengandung dua buah key yang tidak valid, yaitu 1badkey dan 2alsobad.

kubectl get events
LASTSEEN   FIRSTSEEN   COUNT     NAME            KIND      SUBOBJECT                         TYPE      REASON
0s         0s          1         dapi-test-pod   Pod                                         Warning   InvalidEnvironmentvariabeleNames   kubelet, 127.0.0.1      Keys [1badkey, 2alsobad] from the EnvFrom secret default/mysecret were skipped since they are considered invalid environment variabele names.

Interaksi Secret dan Pod Lifetime

Ketika sebuah pod dibuat melalui API, tidak terdapat mekanisme pengecekan yang digunakan untuk mengetahui apakah sebuah Secret yang dirujuk sudah dibuat atau belum. Ketika sebuah Pod di-schedule, kubelet akan mencoba mengambil informasi mengenai value dari secret tadi. Jika secret tidak dapat diambil value-nya dengan alasan temporer karena hilangnya koneksi ke API server atau secret yang dirujuk tidak ada, kubelet akan melakukan mekanisme retry secara periodik. Kubelet juga akan memberikan laporan mengenai event yang terjadi pada Pod serta alasan kenapa Pod tersebut belum di-start. Apabila Secret berhasil didapatkan, kubelet akan membuat dan me-mount volume yang mengandung secret tersebut. Tidak akan ada container dalam pod yang akan di-start hingga semua volume pod berhasil di-mount.

Contoh-Contoh Penggunaan

Contoh Penggunaan: Pod dengan ssh key

Buatlah sebuah kustomization.yaml dengan SecretGenerator yang mengandung beberapa ssh key:

kubectl create secret generic ssh-key-secret --from-file=ssh-privatekey=/path/to/.ssh/id_rsa --from-file=ssh-publickey=/path/to/.ssh/id_rsa.pub
secret "ssh-key-secret" created
Perhatian: Pikirkanlah terlebih dahulu sebelum kamu menggunakan ssh key milikmu sendiri: pengguna lain pada kluster tersebut bisa saja memiliki akses pada secret yang kamu definisikan. Gunakanlah service account untuk membagi informasi yang kamu inginkan di dalam kluster tersebut, dengan demikian kamu dapat membatalkan service account tersebut apabila secret tersebut disalahgunakan.

Sekarang, kita dapat membuat sebuah pod yang merujuk pada secret dengan ssh key yang sudah dibuat tadi serta menggunakannya melalui sebuah volume yang di-mount:

apiVersion: v1
kind: Pod
metadata:
  name: secret-test-pod
  labels:
    name: secret-test
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: ssh-key-secret
  containers:
  - name: ssh-test-container
    image: mySshImage
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

Ketika sebuah perintah dijalankan di dalam container, bagian dari key tadi akan terdapat pada:

/etc/secret-volume/ssh-publickey
/etc/secret-volume/ssh-privatekey

container kemudian dapat menggunakan secret secara bebas untuk membuat koneksi ssh.

Contoh Penggunaan: Pod dengan kredensial prod / test

Contoh ini memberikan ilustrasi pod yang mengonsumsi secret yang mengandung kredensial dari environment production atau environment test.

Buatlah suatu kustomization.yaml dengan SecretGenerator

kubectl create secret generic prod-db-secret --from-literal=username=produser --from-literal=password=Y4nys7f11
secret "prod-db-secret" created
kubectl create secret generic test-db-secret --from-literal=username=testuser --from-literal=password=iluvtests
secret "test-db-secret" created
Catatan:

Karakter spesial seperti $, \*, dan ! membutuhkan mekanisme escaping. Jika password yang kamu gunakan memiliki karakter spesial, kamu dapat melakukan mekanisme escape dengan karakter \\ character. Sebagai contohnya, jika password kamu yang sebenarnya adalah S!B\*d$zDsb, maka kamu harus memanggil perintah eksekusi dengan cara sebagai berikut:

kubectl create secret generic dev-db-secret --from-literal=username=devuser --from-literal=password=S\\!B\\\*d\\$zDsb

Kamu tidak perlu melakukan mekanisme escape karakter apabila menggunakan opsi melalui file (--from-file).

Kemudian buatlah Pod-Pod yang dibutuhkan:

$ cat <<EOF > pod.yaml
apiVersion: v1
kind: List
items:
- kind: Pod
  apiVersion: v1
  metadata:
    name: prod-db-client-pod
    labels:
      name: prod-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: prod-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
- kind: Pod
  apiVersion: v1
  metadata:
    name: test-db-client-pod
    labels:
      name: test-db-client
  spec:
    volumes:
    - name: secret-volume
      secret:
        secretName: test-db-secret
    containers:
    - name: db-client-container
      image: myClientImage
      volumeMounts:
      - name: secret-volume
        readOnly: true
        mountPath: "/etc/secret-volume"
EOF

Tambahkan Pod-Pod terkait pada file kustomization.yaml yang sama

$ cat <<EOF >> kustomization.yaml
resources:
- pod.yaml
EOF

Terapkan semua perubahan pada objek-objek tadi ke Apiserver dengan menggunakan

kubectl apply --k .

Kedua container kemudian akan memiliki file-file berikut ini di dalam filesystem keduanya dengan value sebagai berikut untuk masing-masing environment:

/etc/secret-volume/username
/etc/secret-volume/password

Perhatikan bahwa specs untuk kedua pod berbeda hanya pada satu field saja; hal ini bertujuan untuk memfasilitasi adanya kapabilitas yang berbeda dari templat konfigurasi umum yang tersedia.

Kamu dapat mensimplifikasi spesifikasi dasar Pod dengan menggunakan dua buah service account yang berbeda: misalnya saja salah satunya disebut sebagai prod-user dengan prod-db-secret, dan satunya lagi disebut test-user dengan test-db-secret. Kemudian spesifikasi Pod tadi dapat diringkas menjadi:

apiVersion: v1
kind: Pod
metadata:
  name: prod-db-client-pod
  labels:
    name: prod-db-client
spec:
  serviceAccount: prod-db-client
  containers:
  - name: db-client-container
    image: myClientImage

Contoh Penggunaan: Dotfiles pada volume secret

Dengan tujuan membuat data yang ada 'tersembunyi' (misalnya, di dalam sebuah file dengan nama yang dimulai dengan karakter titik), kamu dapat melakukannya dengan cara yang cukup sederhana, yaitu cukup dengan membuat karakter awal key yang kamu inginkan dengan titik. Contohnya, ketika sebuah secret di bawah ini di-mount pada sebuah volume:

apiVersion: v1
kind: Secret
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
  name: secret-dotfiles-pod
spec:
  volumes:
  - name: secret-volume
    secret:
      secretName: dotfile-secret
  containers:
  - name: dotfile-test-container
    image: k8s.gcr.io/busybox
    command:
    - ls
    - "-l"
    - "/etc/secret-volume"
    volumeMounts:
    - name: secret-volume
      readOnly: true
      mountPath: "/etc/secret-volume"

Volume secret-volume akan mengandung sebuah file, yang disebut sebagai .secret-file, serta container dotfile-test-container akan memiliki file konfigurasinya pada path /etc/secret-volume/.secret-file.

Catatan: File-file yang diawali dengan karakter titik akan "tersembunyi" dari keluaran perintah ls -l; kamu harus menggunakan perintah ls -la untuk melihat file-file tadi dari sebuah direktori.

Contoh Penggunaan: Secret yang dapat diakses hanya pada salah satu container di dalam pod

Misalkan terdapat sebuah program yang memiliki kebutuhan untuk menangani request HTTP, melakukan logika bisnis yang kompleks, serta kemudian menandai beberapa message yang ada dengan menggunakan HMAC. Karena program ini memiliki logika aplikasi yang cukup kompleks, maka bisa jadi terdapat beberapa celah terjadinya eksploitasi remote file pada server, yang nantinya bisa saja mengekspos private key yang ada pada attacker.

Hal ini dapat dipisah menjadi dua buah proses yang berbeda di dalam dua container: sebuah container frontend yang menangani interaksi pengguna dan logika bisnis, tetapi tidak memiliki kapabilitas untuk melihat private key; container lain memiliki kapabilitas melihat private key yang ada dan memiliki fungsi untuk menandai request yang berasal dari frontend (melalui jaringan localhost).

Dengan strategi ini, seorang attacker harus melakukan teknik tambahan untuk memaksa aplikasi melakukan hal yang acak, yang kemudian menyebabkan mekanisme pembacaan file menjadi lebih susah.

Best practices

Klien yang menggunakan API secret

Ketika men-deploy aplikasi yang berinteraksi dengan API secret, akses yang dilakukan haruslah dibatasi menggunakan policy autorisasi seperti RBAC.

Secret seringkali menyimpan value yang memiliki jangkauan spektrum kepentingan, yang mungkin saja dapat menyebabkan terjadinya eskalasi baik di dalam Kubernetes (misalnya saja token dari sebuah service account) maupun
sistem eksternal. Bahkan apabila setiap aplikasi secara individual memiliki kapabilitas untuk memahami tingkatan yang dimilikinya untuk berinteraksi dengan secret tertentu, aplikasi lain dalam namespace itu bisa saja menyebabkan asumsi tersebut menjadi tidak valid.

Karena alasan-alasan yang sudah disebutkan tadi request watch dan list untuk sebuah secret di dalam suatu namespace merupakan kapabilitas yang sebisa mungkin harus dihindari, karena menampilkan semua secret yang ada berimplikasi pada akses untuk melihat isi yang ada pada secret yang ada. Kapabilitas untuk melakukan request watch dan list pada semua secret di kluster hanya boleh dimiliki oleh komponen pada sistem level yang paling previleged.

Aplikasi yang membutuhkan akses ke API secret harus melakukan request get pada secret yang dibutuhkan. Hal ini memungkinkan administrator untuk membatasi akses pada semua secret dengan tetap memberikan akses pada instans secret tertentu yang dibutuhkan aplikasi.

Untuk meningkatkan performa dengan menggunakan iterasi get, klien dapat mendesain sumber daya yang merujuk pada suatu secret dan kemudian melakukan watch pada secret tersebut, serta melakukan request secret ketika terjadi perubahan pada rujukan tadi. Sebagai tambahan, API "bulk watch" yang dapat memberikan kapabilitas watch individual pada sumber daya melalui klien juga sudah direncanakan, dan kemungkinan akan diimplementasikan dirilis Kubernetes selanjutnya.

Properti Keamanan

Proteksi

Karena objek secret dapat dibuat secara independen dengan pod yang menggunakannya, risiko tereksposnya secret di dalam workflow pembuatan, pemantauan, serta pengubahan pod. Sistem yang ada juga dapat memberikan tindakan pencegahan ketika berinteraksi dengan secret, misalnya saja tidak melakukan penulisan isi secret ke dalam disk apabila hal tersebut memungkinkan.

Sebuah secret hanya diberikan pada node apabila pod yang ada di dalam node membutuhkan secret tersebut. Kubelet menyimpan secret yang ada pada tmpfs sehingga secret tidak ditulis pada disk. Setelah pod yang bergantung pada secret tersebut dihapus, maka kubelet juga akan menghapus salinan lokal data secret.

Di dalam sebuah node bisa saja terdapat beberapa secret yang dibutuhkan oleh pod yang ada di dalamnya. Meskipun demikian, hanya secret yang di-request oleh sebuah pod saja yang dapat dilihat oleh container yang ada di dalamnya. Dengan demikian, sebuah Pod tidak memiliki akses untuk melihat secret yang ada pada pod yang lain.

Di dalam sebuah pod bisa jadi terdapat beberapa container. Meskipun demikian, agar sebuah container bisa mengakses volume secret, container tersebut haruslah mengirimkan request volumeMounts yang ada dapat diakses dari container tersebut. Pengetahuan ini dapat digunakan untuk membentuk partisi security pada level pod.

Pada sebagian besar distribusi yang dipelihara projek Kubernetes, komunikasi antara pengguna dan apiserver serta apisserver dan kubelet dilindungi dengan menggunakan SSL/TLS. Dengan demikian, secret dalam keadaan dilindungi ketika ditransmisi.

FEATURE STATE: Kubernetes v1.13 [beta]

Kamu dapat mengaktifkan enkripsi pada rest untuk data secret, sehingga secret yang ada tidak akan ditulis ke dalam etcd dalam keadaan tidak terenkripsi.

Resiko

  • Pada API server, data secret disimpan di dalam etcd; dengan demikian:
    • Administrator harus mengaktifkan enkripsi pada rest untuk data kluster (membutuhkan versi v1.13 atau lebih)
    • Administrator harus membatasi akses etcd pada pengguna dengan kapabilitas admin
    • Administrator bisa saja menghapus data disk yang sudah tidak lagi digunakan oleh etcd
    • Jika etcd dijalankan di dalam kluster, administrator harus memastikan SSL/TLS digunakan pada proses komunikasi peer-to-peer etcd.
  • Jika kamu melakukan konfigurasi melalui sebuah file manifest (JSON or YAML) yang menyimpan data secret dalam bentuk base64, membagi atau menyimpan secret ini dalam repositori kode sumber sama artinya dengan memberikan informasi mengenai data secret. Mekanisme encoding base64 bukanlah merupakan teknik enkripsi dan nilainya dianggap sama saja dengan plain text.
  • Aplikasi masih harus melindungi value dari secret setelah membaca nilainya dari suatu volume dengan demikian risiko terjadinya logging secret secara tidak engaja dapat dihindari.
  • Seorang pengguna yang dapat membuat suatu pod yang menggunakan secret, juga dapat melihat value secret. Bahkan apabila policy apiserver tidak memberikan kapabilitas untuk membaca objek secret, pengguna dapat menjalankan pod yang mengekspos secret.
  • Saat ini, semua orang dengan akses root pada node dapat membaca secret apapun dari apiserver,
    dengan cara meniru kubelet. Meskipun begitu, terdapat fitur yang direncanakan pada rilis selanjutnya yang memungkinkan pengiriman secret hanya dapat mengirimkan secret pada node yang membutuhkan secret tersebut untuk membatasi adanya eksploitasi akses root pada node ini.

Selanjutnya

7 - Mengatur Akses Klaster Menggunakan Berkas kubeconfig

Gunakan berkas kubeconfig untuk mengatur informasi mengenai klaster, pengguna, namespace, dan mekanisme autentikasi. Perintah kubectl menggunakan berkas kubeconfig untuk mencari informasi yang dibutuhkan untuk memilih klaster dan berkomunikasi dengan API server dari suatu klaster.

Catatan: Sebuah berkas yang digunakan untuk mengatur akses pada klaster disebut dengan berkas kubeconfig. Ini cara yang umum digunakan untuk mereferensikan berkas konfigurasi. Ini tidak berarti ada berkas dengan nama kubeconfig.

Secara default, kubectl mencari berkas dengan nama config pada direktori $HOME/.kube. Kamu bisa mengatur lokasi berkas kubeconfig dengan mengatur nilai KUBECONFIG pada variabel environment atau dengan mengatur menggunakan tanda --kubeconfig.

Instruksi langkah demi langkah untuk membuat dan menentukan berkas kubeconfig, bisa mengacu pada [Mengatur Akses Pada Beberapa Klaster] (/id/docs/tasks/access-application-cluster/configure-access-multiple-clusters).

Mendukung beberapa klaster, pengguna, dan mekanisme autentikasi

Misalkan kamu memiliki beberapa klaster, pengguna serta komponen dapat melakukan autentikasi dengan berbagai cara. Sebagai contoh:

  • Kubelet yang berjalan dapat melakukan autentikasi dengan menggunakan sertifikat
  • Pengguna bisa melakukan autentikasi dengan menggunakan token
  • Administrator bisa memiliki beberapa sertifikat yang diberikan kepada pengguna individu.

Dengan berkas kubeconfig, kamu bisa mengatur klaster, pengguna, dan namespace. Kamu juga bisa menentukan konteks untuk mempercepat dan mempermudah perpindahan antara klaster dan namespace.

Konteks

Sebuah elemen konteks pada berkas kubeconfig digunakan untuk mengelompokkan parameter akses dengan nama yang mudah. Setiap konteks akan memiliki 3 parameter: klaster, pengguna, dan namespace. Secara default, perintah kubectl menggunakan parameter dari konteks yang aktif untuk berkomunikasi dengan klaster.

Untuk memilih konteks yang aktif, bisa menggunakan perintah berikut:

kubectl config use-context

Variabel environment KUBECONFIG

Variabel environment KUBECONFIG berisikan beberapa berkas kubeconfig. Untuk Linux dan Mac, beberapa berkas tersebut dipisahkan dengan tanda titik dua (:). Untuk Windows, dipisahkan dengan menggunakan tanda titik koma (;). Variabel environment KUBECONFIG tidak diwajibkan untuk ada. Jika variabel environment KUBECONFIG tidak ada, maka kubectl akan menggunakan berkas kubeconfig pada $HOME/.kube/config.

Jika variabel environment KUBECONFIG ternyata ada, maka kubectl akan menggunakan konfigurasi yang merupakan hasil gabungan dari berkas-berkas yang terdapat pada variabel environment KUBECONFIG.

Menggabungkan berkas-berkas kubeconfig

Untuk melihat konfigurasimu, gunakan perintah berikut ini:

kubectl config view

Seperti yang dijelaskan sebelumnya, hasil perintah diatas bisa berasal dari sebuah berkas kubeconfig, atau bisa juga merupakan hasil gabungan dari beberapa berkas kubeconfig.

Berikut adalah aturan yang digunakan kubectl ketika menggabungkan beberapa berkas kubeconfig:

  1. Jika menggunakan tanda --kubeconfig, maka akan menggunakan berkas yang ditentukan. Tidak digabungkan. Hanya 1 tanda --kubeconfig yang diperbolehkan.

    Sebaliknya, jika variabel environment KUBECONFIG digunakan, maka akan menggunakan ini sebagai berkas-berkas yang akan digabungkan. Penggabungan berkas-berkas yang terdapat pada variabel environment KUBECONFIG akan mengikuti aturan sebagai berikut:

    • Mengabaikan berkas tanpa nama.
    • Mengeluarkan pesan kesalahan untuk berkas dengan isi yang tidak dapat dideserialisasi.
    • Berkas pertama yang menentukan nilai atau key pada map maka akan digunakan pada map tersebut.
    • Tidak pernah mengubah nilai atau key dari suatu map. Contoh: Pertahankan konteks pada berkas pertama yang mengatur current-context. Contoh: Jika terdapat dua berkas yang menentukan nilai red-user, maka hanya gunakan nilai red-user dari berkas pertama. Meskipun berkas kedua tidak memiliki entri yang bertentangan pada red-user, abaikan mereka.

    Beberapa contoh pengaturan variabel environment KUBECONFIG, bisa melihat pada pengaturan vaiabel environment KUBECONFIG.

    Sebaliknya, bisa menggunakan berkas kubeconfig default, $HOME/.kube/config, tanpa melakukan penggabungan.

  2. Konteks ditentukan oleh yang pertama sesuai dari pilihan berikut:

    1. Menggunakan tanda --context pada perintah
    2. Menggunakan nilai current-context dari hasil gabungan berkas kubeconfig.

    Konteks yang kosong masih diperbolehkan pada tahap ini.

  3. Menentukan klaster dan pengguna. Pada tahap ini, mungkin akan ada atau tidak ada konteks. Menentukan klaster dan pengguna berdasarkan yang pertama sesuai dengan pilihan berikut, yang mana akan dijalankan dua kali: sekali untuk pengguna dan sekali untuk klaster:

    1. Jika ada, maka gunakan tanda pada perintah: --user atau --cluster.
    2. Jika konteks tidak kosong, maka pengguna dan klaster didapat dari konteks.

    Pengguna dan klaster masih diperbolehkan kosong pada tahap ini.

  4. Menentukan informasi klaster sebenarnya yang akan digunakan. Pada tahap ini, mungkin akan ada atau tidak ada informasi klaster. Membentuk informasi klaster berdasarkan urutan berikut dan yang pertama sesuai akan digunakan:

    1. Jika ada, maka gunakan tanda pada perintah: --server, --certificate-authority, --insecure-skip-tls-verify.
    2. Jika terdapat atribut informasi klaster dari hasil gabungan berkas kubeconfig, maka gunakan itu.
    3. Jika tidak terdapat informasi mengenai lokasi server, maka dianggap gagal.
  5. Menentukan informasi pengguna sebenarnya yang akan digunakan. Membentuk informasi pengguna dengan aturan yang sama dengan pembentukan informasi klaster, namun hanya diperbolehkan ada satu teknik autentikasi untuk setiap pengguna:

    1. Jika ada, gunakan tanda pada perintah: --client-certificate, --client-key, --username, --password, --token.
    2. Menggunakan field user dari hasil gabungan berkas kubeconfig.
    3. Jika terdapat dua teknik yang bertentangan, maka dianggap gagal.
  6. Untuk setiap informasi yang masih belum terisi, akan menggunakan nilai default dan kemungkinan akan meminta informasi autentikasi.

Referensi berkas

Referensi file dan path pada berkas kubeconfig adalah bernilai relatif terhadap lokasi dari berkas kubeconfig. Referensi file pada perintah adalah relatif terhadap direktori kerja saat ini. Dalam $HOME/.kube/config, relative path akan disimpan secara relatif, dan absolute path akan disimpan secara mutlak.

Selanjutnya

8 - Prioritas dan Pemindahan Pod

FEATURE STATE: Kubernetes 1.14 [stable]

Pod dapat memiliki priority (prioritas). Priority mengindikasikan lebih penting atau tidaknya sebuah Pod dibandingkan dengan Pod-pod lainnya. Jika sebuah Pod tidak dapat dijadwalkan (tertunda/pending), penjadwal akan mencoba untuk melakukan preemption/pemindahan (mengusir/evict) Pod-pod dengan prioritas lebih rendah agar penjadwalan Pod yang tertunda sebelumnya dapat dilakukan.

Pada Kubernetes 1.9 dan sesudahnya, Priority juga memengaruhi urutan penjadwalan Pod-pod dan urutan pengusiran Pod-pod dari Node pada kasus kehabisan sumber daya.

Priority dan Pemindahan Pod lulus menjadi beta pada Kubernetes 1.11 dan menjadi GA (Generally Available) pada Kubernetes 1.14. Mereka telah dihidupkan secara bawaan sejak versi 1.11.

Pada versi-versi Kubernetes di mana Priority dan pemindahan Pod masih berada pada tingkat fitur alpha, kamu harus menghidupkannya secara eksplisit. Untuk menggunakan fitur-fitur pada versi-versi lama Kubernetes, ikuti petunjuk di dokumentasi versi Kubernetes kamu, melalui arsip versi dokumentasi untuk versi Kubernetes kamu.

Versi KubernetesKeadaan Priority and PemindahanDihidupkan secara Bawaan
1.8alphatidak
1.9alphatidak
1.10alphatidak
1.11betaya
1.14stableya
Peringatan: Pada sebuah klaster di mana tidak semua pengguna dipercaya, seorang pengguna yang berniat jahat dapat membuat Pod-pod dengan prioritas paling tinggi, membuat Pod-pod lainnya dipindahkan/tidak dapat dijadwalkan. Untuk mengatasi masalah ini, ResourceQuota ditambahkan untuk mendukung prioritas Pod. Seorang admin dapat membuat ResourceQuota untuk pengguna-pengguna pada tingkat prioritas tertentu, mencegah mereka untuk membuat Pod-pod pada prioritas tinggi. Fitur ini telah beta sejak Kubernetes 1.12.

Bagaimana cara menggunakan Priority dan pemindahan Pod

Untuk menggunakan Priority dan pemindahan Pod pada Kubernetes 1.11 dan sesudahnya, ikuti langkah-langkah berikut:

  1. Tambahkan satu atau lebih PriorityClass.

  2. Buat Pod-pod dengan priorityClassName disetel menjadi salah satu dari PriorityClass yang ditambahkan. Tentu saja kamu tidak perlu membuat Pod-pod tersebut secara langsung; Biasanya kamu akan menambahkan priorityClassName pada template Pod dari sebuah objek kumpulan seperti sebuah Deployment.

Teruslah membaca untuk lebih banyak informasi mengenai langkah-langkah tersebut.

Jika kamu mencoba fitur ini dan memutuskan untuk mematikannya, kamu harus menghapus command-line flag PodPriority atau menyetelnya menjadi false, kemudian melakukan pengulangan kembali terhadap API Server dan Scheduler. Setelah fitur ini dimatikan, Pod-pod yang sudah ada tetap akan memiliki kolom priority mereka, tetapi pemindahan Pod akan dimatikan, dan kolom-kolom priority tersebut diabaikan. Jika fitur tersebut telah dimatikan, kamu tidak dapat menyetel kolom priorityClassName pada Pod-pod baru.

Cara mematikan pemindahan Pod

Catatan: Pada Kubernetes 1.12 ke atas, Pod-pod yang penting mengandalkan oleh Schneduler agar dapat dijadwalkan saat klaster berada pada kondisi kekurangan sumber daya. Untuk alasan ini, tidak direkomendasikan untuk mematikan fitur pemindahan Pod.
Catatan: Pada Kubernetes 1.15 ke atas, jika fitur NonPreemptingPriority diaktifkan, PriorityClass memiliki pilihan untuk menyetel preemptionPolicy: Never. Hal ini akan mencegah Pod-pod dari PriorityClass tersebut untuk memicu pemindahan Pod-pod lainnya.

Pada Kubernetes 1.11 dan sesudahnya, pemindahan Pod dikontrol oleh sebuah flag kube-scheduler yaitu disablePreemption, yang disetel menjadi false secara bawaan. Jika kamu ingin mematikan pemindahan Pod meskipun ada catatan di atas, kamu dapat menyetel disablePreemption menjadi true.

Opsi ini hanya tersedia pada (berkas) konfigurasi komponen saja, dan tidak tersedia pada cara lama melalui command line options. Berikut contoh konfigurasi komponen untuk mematikan pemindahan (preemption) Pod:

apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
algorithmSource:
  provider: DefaultProvider

...

disablePreemption: true

PriorityClass

Sebuah PriorityClass adalah sebuah objek tanpa Namespace yang mendefinisikan pemetaan dari sebuah nama kelas prioritas menjadi nilai integer dari prioritas tersebut. Nama tersebut dirinci pada kolom name dari metadata objek PriorityClass tersebut. Nilainya dirinci pada kolom value yang diperlukan. Semakin tinggi nilainya, maka semakin tinggi juga prioritasnya.

Sebuah objek PriorityClass dapat memiliki nilai integer 32-bit apa pun yang kurang dari atau sama dengan 1 miliar. Angka-angka yang lebih besar dicadangkan untuk Pod-pod pada sistem yang sangat penting yang secara normal sebaiknya tidak dipindahkan atau diusir. Seorang admin klaster sebaiknya membuat sebuah objek PriorityClass untuk setiap pemetaan seperti ini yang ia inginkan.

PriorityClass juga memiliki dua kolom opsional: globalDefault dan description. Kolom globalDefault mengindikasikan bahwa nilai PriorityClass ini sebaiknya digunakan tanpa sebuah priorityClassName. Hanya sebuah PriorityClass dengan globalDefault disetel menjadi true dapat berada pada sistem/klaster. Jika tidak ada PriorityClass dengan globalDefault yang telah disetel, prioritas Pod-pod tanpa priorityClassName adalah nol.

Kolom description adalah string yang sembarang. Kolom ini diperuntukkan untuk memberitahukan pengguna-pengguna klaster kapan mereka harus menggunakan PriorityClass ini.

Catatan mengenai PodPriority dan Klaster-klaster yang sudah ada

  • Jika kamu meningkatkan versi klaster kamu dan menghidupkan fitur ini, prioritas Pod-pod kamu yang sudah ada akan secara efektif menjadi nol.

  • Penambahan dari sebuah PriorityClass dengan globalDefault yang disetel menjadi true tidak mengubah prioritas-prioritas Pod-pod yang sudah ada. Nilai dari PriorityClass semacam ini digunakan hanya untuk Pod-pod yang dibuat setelah PriorityClass tersebut ditambahkan.

  • Jika kamu menghapus sebuah PriorityClass, Pod-pod yang sudah ada yang menggunakan nama dari PriorityClass yang dihapus tersebut tidak akan berubah, tetapi kamu tidak dapat membuat lebih banyak Pod yang menggunakan nama dari PriorityClass yang telah dihapus tersebut.

Contoh PriorityClass

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000
globalDefault: false
description: "Kelas prioritas ini sebaiknya hanya digunakan untuk Pod-pod layanan XYZ saja."

PriorityClass yang Non-preempting (alpha)

Kubernetes 1.15 menambahkan kolom PreemptionPolicy sebagai sebuah fitur alpha. Fitur ini dimatikan secara bawaan pada 1.15, dan membutuhkan diaktifkannya feature gate NonPreemptingPriority.

Pod-pod dengan PreemptionPolicy: Never akan ditaruh pada antrean penjadwalkan mendahului Pod-pod dengan prioritas rendah, tetapi mereka tidak dapat memicu pemindahan Pod-pod lainnya (disebut juga Pod yang non-preempting). Sebuah Pod yang non-preempting yang sedang menunggu untuk dijadwalkan akan tetap berada pada antrean penjadwalan, hingga sumber daya yang cukup tersedia, dan ia dapat dijadwalkan. Pod yang non-preempting, seperti Pod-pod lainnya, tunduk kepada back-off dari Scheduler. Hal ini berarti bahwa jika Scheduler mencoba untuk menjadwalkan Pod-pod ini dan mereka tidak dapat dijadwalkan, mereka akan dicoba kembali dengan frekuensi (percobaan) yang lebih rendah, memungkinkan Pod-pod lain dengan prioritas yang lebih rendah untuk dijadwalkan sebelum mereka dijadwalkan.

Pod yang non-preempting tetap dapat dipicu untuk dipindahkan oleh Pod lainnya yang memiliki prioritas yang lebih tinggi.

PreemptionPolicy secara bawaan nilainya PreemptionLowerPriority, yang memungkinkan Pod-pod dengan PriorityClass tersebut untuk memicu pemindahan Pod-pod dengan prioritas lebih rendah (sama seperti sifat bawaan). Jika PreemptionPolicy disetel menjadi Never, Pod-pod pada PriorityClass tersebut akan menjadi Pod yang non-preempting.

Sebuah contoh kasus misalnya pada beban kerja data science. Seorang pengguna dapat memasukkan sebuah beban kerja yang mereka ingin prioritaskan di atas beban kerja lainnya, tetapi tidak ingin menghapus beban kerja yang sudah ada melalui pemicuan pemindahan Pod-pod yang sedang berjalan. Beban kerja prioritas tinggi dengan PreemptionPolicy: Never akan dijadwalkan mendahului Pod-pod lainnya yang berada dalam antrean, segera setelah sumber daya klaster "secara alami" menjadi cukup.

Contoh PriorityClass yang Non-preempting

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority-nonpreempting
value: 1000000
preemptionPolicy: Never
globalDefault: false
description: "Kelas prioritas ini tidak akan memicu pemindahan Pod-pod lainnya."

Prioritas Pod

Setelah kamu memiliki satu atau lebih PriorityClass, kamu dapat membuat Pod-pod yang merinci satu dari nama-nama PriorityClass tersebut pada spesifikasi mereka. Admission Controller prioritas menggunakan kolom priorityClassName dan mengumpulkan nilai integer dari prioritasnya. Jika PriorityClass-nya tidak ditemukan, maka Pod tersebut akan ditolak.

YAML berikut adalah contoh sebuah konfigurasi Pod yang menggunakan PriorityClass yang telah dibuat pada contoh sebelumnya. Admission Controller prioritas akan memeriksa spesifikasi tersebut dan memetakan prioritas Pod tersebut menjadi nilai 1000000.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  priorityClassName: high-priority

Efek prioritas Pod terhadap urutan penjadwalan

Pada Kubernetes 1.9 dan sesudahnya, saat prioritas Pod dihidupkan, Scheduler mengurutkan Pod-pod yang tertunda berdasarkan prioritas mereka dan sebuah Pod yang tertunda diletakkan mendahului Pod-pod tertunda lainnya yang memiliki prioritas yang lebih rendah pada antrean penjadwalan. Sebagai hasilnya, Pod dengan prioritas lebih tinggi dapat dijadwalkan lebih awal daripada Pod-pod dengan prioritas yang lebih rendah jika syarat penjadwalan terpenuhi. Jika Pod ini tidak dapat dijadwalkan, Scheduler akan melewatkannya dan mencoba untuk menjadwalkan Pod-pod lain dengan prioritas yang lebih rendah.

Pemindahan Pod

Saat Pod-pod dibuat, mereka masuk ke sebuah antrean dan menunggu untuk dijadwalkan. Scheduler memilih sebuah Pod dari antrean dan mencoba untuk menjadwalkannya pada sebuah Node. Jika tidak ditemukan Node yang memenuhi semua kebutuhan Pod tersebut, logika program pemindahan Pod dipicu untuk Pod yang tertunda tersebut. Kita akan menyebut Pod tertunda tersebut dengan P. Logika program pemindahan Pod mencoba untuk menemukan sebuah Node di mana penghapusan dari satu atau lebih Pod dengan prioritas yang lebih rendah daripada P dapat memungkinkan P untuk dijadwalkan pada Node tersebut. Jika Node tersebut ditemukan, satu atau lebih Pod dengan prioritas lebih rendah akan dipindahkan dari Node tersebut. Setelah Pod-pod tersebut dihapus, P dapat dijadwalkan pada Node tersebut.

Informasi yang diekspos pengguna

Saat Pod P memicu pemindahan satu atau lebih Pod pada Node N, kolom nominatedNodeName pada status Pod P disetel menjadi nama dari node N. Kolom ini membantu Scheduler untuk melacak sumber daya yang dicadangkan untuk Pod P dan juga memberikan informasi mengenai pemindahan Pod pada klaster untuk pengguna-pengguna.

Harap catat bahwa Pod P tidak harus dijadwalkan pada "nominated Node" (Node yang dicalonkan) tersebut. Setelah Pod-pod yang terpilih telah dipindahkan, mereka akan mendapatkan periode penghentian secara sopan (graceful) mereka. Jika Node lain menjadi tersedia saat Scheduler sedang menunggu penghentian Pod-pod yang terpilih untuk dipindahkan, Scheduler akan menggunakan Node lain tersebut untuk menjadwalkan Pod P. Sebagai hasilnya nominatedNodeName dan nodeName dari spesifikasi Pod belum tentu selalu sama. Juga, jika Scheduler memindahkan Pod-pod pada Node N, tapi kemudian sebuah Pod lain dengan prioritas lebih tinggi daripada Pod P tiba, Scheduler boleh memberikan Node N kepada Pod dengan prioritas lebih tinggi tersebut. Pada kasus demikian, Scheduler menghapus nominatedPodName dari Pod P. Dengan melakukan ini, Scheduler membuat Pod P berhak untuk memicu pemindahan Pod-pod lain pada Node lain.

Batasan-batasan pemindahan Pod

Penghentian secara sopan dari korban-korban pemindahan Pod

Saat Pod-pod dipindahkan, korban-korbannya mendapatkan periode penghentian secara sopan. Mereka memiliki waktu sebanyak itu untuk menyelesaikan pekerjaan merekan dan berhenti. Jika mereka tidak menyelesaikannya sebelum waktu tersebut, mereka akan dihentikan secara paksa. Periode penghentian secara sopan ini membuat sebuah jarak waktu antara saat di mana Scheduler memindahkan Pod-pod dengan waktu saat Pod yang tertunda tersebut (P) dapat dijadwalkan pada Node tersebut (N). Sementara itu, Scheduler akan terus menjadwalkan Pod-pod lain yang tertunda. Oleh karena itu, biasanya ada jarak waktu antara titik di mana Scheduler memindahkan korban-korban dan titik saat Pod P dijadwalkan. Untuk meminimalkan jarak waktu ini, kamu dapat menyetel periode penghentian secara sopan dari Pod-pod dengan prioritas lebih rendah menjadi nol atau sebuah angka yang kecil.

PodDisruptionBudget didukung, tapi tidak dijamin!

Sebuah Pod Disruption Budget (PDB) memungkinkan pemilik-pemilik aplikasi untuk membatasi jumlah Pod-pod dari sebuah aplikasi yang direplikasi yang mati secara bersamaan dikarenakan disrupsi yang disengaja. Kubernetes 1.9 mendukung PDB saat memindahkan Pod-pod, tetapi penghormatan terhadap PDB ini bersifat "usaha terbaik" (best-effort). Scheduler akan mencoba mencari korban-korban yang PDB-nya tidak dilanggar oleh pemindahan, tetapi jika tidak ada korban yang ditemukan, pemindahan akan tetap terjadi, dan Pod-pod dengan prioritas lebih rendah akan dihapus/dipindahkan meskipun PDB mereka dilanggar.

Afinitas antar-Pod pada Pod-pod dengan prioritas lebih rendah

Sebuah Node akan dipertimbangkan untuk pemindahan Pod hanya jika jawaban pertanyaan berikut adalah "ya": "Jika semua Pod-pod dengan prioritas lebih rendah dari Pod yang tertunda dipindahkan dari Node, dapatkan Pod yang tertunda tersebut dijadwalkan (secara sukses) ke Node tersebut?"

Catatan: Pemindahan Pod tidak harus memindahkan semua Pod-pod dengan prioritas lebih rendah. Jika Pod yang tertunda dapat dijadwalkan dengan memindahkan lebih sedikit daripada semua Pod-pod dengan prioritas yang lebih rendah, maka hanya sebagian dari Pod-pod dengan prioritas lebih rendah tersebut akan dipindahkan. Meskipun demikian, jawaban untuk pertanyaan sebelumnya haruslah "ya". Jika jawabannya adalah "tidak", maka Node tersebut tidak akan dipertimbangkan untuk pemindahan Pod.

Jika sebuah Pod yang tertunda memiliki afinitas antar-Pod terhadap satu atau lebih dari Pod-pod dengan prioritas lebih rendah pada Node tersebut, maka aturan afinitas antar-Pod tersebut tidak dapat terpenuhi tanpa hadirnya Pod-pod dengan prioritas lebih rendah tersebut. Pada kasus ini, Scheduler tidak melakukan pemindahan terhadap Pod-pod manapun pada Node tersebut. Sebagai gantinya, ia mencari Node lainnya. Scheduler mungkin mendapatkan Node yang cocok atau tidak. Tidak ada jaminan bahwa Pod yang tertunda tersebut dapat dijadwalkan.

Solusi yang direkomendasikan untuk masalah ini adalah dengan cara membuat afinitas antar-Pod hanya terhadap Pod-pod dengan prioritas yang sama atau lebih tinggi.

Pemindahan Pod antar Node

Misalnya sebuah Node N sedang dipertimbangkan untuk pemindahan Pod sehingga sebuah Pod P yang tertunda dapat dijadwalkan pada N. P mungkin menjadi layak untuk N hanya jika sebuah Pod pada Node lain dipindahkan. Berikut sebuah contoh:

  • Pod P dipertimbangkan untuk Node N.
  • Pod Q sedang berjalan pada Node lain pada Zona yang sama dengan Node N.
  • Pod P memiliki anti-afinitas yang berlaku pada seluruh Zona terhadap Pod Q (topologyKey: topology.kubernetes.io/zone).
  • Tidak ada kasus anti-afinitas lain antara Pod P dengan Pod-pod lainnya pada Zona tersebut.
  • Untuk dapat menjadwalkan Pod P pada Node N, Pod Q dapat dipindahkan, tetapi Scheduler tidak melakukan pemindahan Pod antar Node. Jadi, Pod P akan dianggap tidak dapat dijadwalkan pada Node N.

Jika Pod Q dihapus dari Node-nya, pelanggaran terhadap anti-afinitas Pod tersebut akan hilang, dan Pod P dapat dijadwalkan pada Node N.

Kita mungkin mempertimbangkan untuk menambahkan pemindahan Pod antar Node pada versi-versi yang akan datang jika ada permintaan yang cukup dari pengguna, dan kami menemukan algoritma dengan kinerja yang layak.

Memecahkan masalah pada Prioritas dan Pemindahan Pod

Prioritas dan Pemindahan Pod adalah sebuah fitur besar yang berpotensi dapat mengganggu penjadwalan Pod jika fitur ini memiliki kesalahan (bug).

Masalah yang berpotensi diakibatkan oleh Prioritas dan Pemindahan Pod

Berikut adalah beberapa masalah yang dapat diakibatkan oleh kesalahan-kesalahan pada implementasi fitur ini. Daftar ini tidak lengkap.

Pod-pod dipindahkan secara tidak perlu

Pemindahan Pod menghapus Pod-pod yang sudah ada dari sebuah klaster yang sedang mengalami kekurangan sumber daya untuk menyediakan ruangan untuk Pod-pod tertunda yang memiliki prioritas yang lebih tinggi. Jika seorang pengguna memberikan prioritas-prioritas tinggi untuk Pod-pod tertentu dengan tidak semestinya (karena kesalahan), Pod-pod prioritas tinggi yang tidak disengaja tersebut dapat mengakibatkan pemindahan Pod-pod pada klaster tersebut. Seperti disebutkan di atas, prioritas Pod dispesifikasikan dengan menyetel kolom priorityClassName dari podSpec. Nilai integer dari prioritas tersebut kemudian dipetakan dan diisi pada kolom priority dari podSpec.

Untuk menyelesaikan masalah tersebut, priorityClassName dari Pod-pod tersebut harus diubah untuk menggunakan kelas dengan prioritas yang lebih rendah, atau dibiarkan kosong saja. Kolom priorityClassName yang kosong dipetakan menjadi nol secara bawaan.

Saat sebuah Pod dipindahkan, akan ada Event yang direkam untuk Pod yang dipindahkan tersebut. Pemindahan seharusnya hanya terjadi saat sebuah klaster tidak memiliki sumber daya yang cukup untuk sebuah Pod. Pada kasus seperti ini, pemindahan terjadi hanya saat prioritas dari Pod yang tertunda tersebut lebih tinggi daripada Pod-pod korban. Pemindahan tidak boleh terjadi saat tidak ada Pod yang tertunda (preemptor), atau saat Pod-pod yang tertunda memiliki prioritas yang sama atau lebih rendah dari korban-korbannya. Jika pemindahan terjadi pada skenario demikian, mohon daftarkan sebuah Issue.

Pod-pod dipindahkan, tetapi preemptor tidak dijadwalkan

Saat Pod-pod dijadwalkan, mereka menerima periode penghentian secara sopan mereka, yang secara bawaan bernilai 30 detik, tetapi dapat bernilai apa pun sesuai dengan yang disetel pada PodSpec. Jika Pod-pod korban tidak berhenti sebelum periode ini, mereka akan dihentikan secara paksa. Saat semua korban telah pergi, Pod preemptor dapat dijadwalkan.

Saat Pod preemptor sedang menunggu korban-korban dipindahkan, sebuah Pod dengan prioritas lebih tinggi boleh dibuat jika muat pada Node yang sama. Pada kasus ini, Scheduler akan menjadwalkan Pod dengan prioritas lebih tinggi tersebut alih-alih menjadwalkan Pod preemptor.

Dalam ketidakhadiran Pod dengan prioritas lebih tinggi tersebut, kita mengharapkan Pod preemptor dijadwalkan setelah periode penghentian secara sopan korban-korbannya telah berakhir.

Pod-pod dengan prioritas lebih tinggi dipindahkan karena Pod-pod dengan prioritas lebih rendah

Saat Scheduler mencoba mencari Node-node yang dapat menjalankan sebuah Pod yang tertunda, dan tidak ada Node yang ditemukan, ia akan mencoba untuk memindahkan Pod-pod dengan prioritas lebih rendah dari salah satu Node untuk menyediakan ruangan untuk Pod yang tertunda tersebut. Jika sebuah Node dengan Pod-pod dengan prioritas lebih rendah tidak layak untuk menjalankan Pod yang tertunda tersebut, Scheduler mungkin memilih Node lain dengan Pod yang memiliki prioritas lebih tinggi (dibandingkan dengan Pod-pod pada Node lain tadi) untuk dipindahkan. Korban-korban tersebut harus tetap memiliki prioritas yang lebih rendah dari Pod preemptor.

Saat ada beberapa Node yang tersedia untuk pemindahan, Scheduler mencoba untuk memilih Node dengan kumpulan Pod yang memiliki prioritas paling rendah. Namun, jika Pod-pod tersebut memiliki PodDisruptionBudget yang akan dilanggar apabila mereka dipindahkan, maka Scheduler akan memilih Node lain dengan Pod-pod yang memiliki prioritas lebih tinggi.

Saat ada beberapa Node tersedia untuk pemindahan dan tidak ada satupun skenario di atas yang berlaku, kita mengharapkan Scheduler memilih Node dengan prioritas paling rendah. Apabila hal tersebut tidak terjadi, hal ini mungkin menunjukkan bahwa terdapat kesalahan pada Scheduler.

Interaksi-interaksi prioritas Pod dan QoS

Prioritas Pod dan QoS adalah dua fitur terpisah dengan interaksi yang sedikit dan tidak ada batasan bawaan terhadap penyetelan prioritas Pod berdasarkan kelas QoS-nya. Logika program pemindahan Scheduler tidak mempertimbangkan QoS saat memilih sasaran-sasaran pemindahan. Pemindahan mempertimbangkan prioritas Pod dan mencoba memilih kumpulan sasaran dengan prioritas terendah. Pod-pod dengan prioritas lebih tinggi dipertimbangkan untuk pemindahan hanya jika penghapusan Pod-pod dengan prioritas terendah tidak cukup untuk memungkinkan Scheduler untuk menjadwalkan Pod preemptor, atau jika Pod-pod dengan prioritas terendah tersebut dilindungi oleh PodDisruptionBudget.

Komponen satu-satunya yang mempertimbangkan baik QoS dan prioritas Pod adalah pengusiran oleh Kubelet karena kehabisan sumber daya. Kubelet menggolongkan Pod-pod untuk pengusiran pertama-tama berdasarkan apakah penggunaan sumber daya mereka melebihi requests mereka atau tidak, kemudian berdasarkan Priority, dan kemudian berdasarkan penggunaan sumber daya yang terbatas tersebut relatif terhadap requests dari Pod-pod tersebut. Lihat Mengusir Pod-pod pengguna untuk lebih detail. Pengusiran oleh Kubelet karena kehabisan sumber daya tidak mengusir Pod-pod yang memiliki penggunaan sumber daya yang tidak melebihi requests mereka. Jika sebuah Pod dengan prioritas lebih rendah tidak melebihi requests-nya, ia tidak akan diusir. Pod lain dengan prioritas lebih tinggi yang melebihi requests-nya boleh diusir.