- Ketika kita me-running sebuah Kubernetes, artinya kita me-running sebuah Cluster (Kubernetes Cluster).
- Cluster terdiri dari dua bagian besar, yakni Kubernetes Master dan Kubernetes Worker/Node
- Kubernetes Master adalah orang/pihak yang melakukan management Kubernetes.
- kubectl adalah CLI untuk berinteraksi dg Kubernetes Cluster
- Node adalah worker machine di Kubernetes, sebelumnya ada yang menyebutnya Minion.
- Aplikasi yang dideploy ke Kubernetes akan jalan di Node/Worker, initinya inilah yang akan mengeksekusi aplikasi kita.
- Node bisa saja dalam bentuk VM atau mesin fisik.
- Di dalam Node selalu terdapat kubelet, kube-proxy dan container manager.
- Unit terkecil yang bisa dideploy di Kubernetes Cluster.
- Pod berisi satu atau lebih container.
- Secara sederhana, Pod adalah aplikasi kita yang running di Kubernetes Cluster.
// Get list pods and nodes
kubectl get nodes / kubectl get node
kubectl get pods / kubectl get pod
kubectl describe node <nod_name>
kubectl describe pod <pod_name>
kubectl get pod -o wide
kubectl get pod --show-labels
// Menghapus pod
kubectl delete pod <pod_name>
kubectl delete pod <pod_name1> <pod_name2> <pod_name3>
// Menghapus pod berdasarkan label tertentu
kubectl delete pod -l key=value
// Menghapus semua pod pada namespace tertentu
kubectl delete pod --all -n <namespace_name>
// Menghapus semua resources
kubectl delete all --all
// Menghapus semua resources pada namespace tertentu
kubectl delete all --all --namespace <namespace_name>
// Melihat log dari pod
kubectl logs <pod_name>
// Submit configurasi ke Kubernetes Cluster
kubectl create -f <config_file>
// Mengakses pod/mengecek apakah pod running
kubectl port-forward <pod_name> <port_akses>:<port_pod>
// Menambah atau merubah label di Pod/Node melalui command line
kubectl label pod/node <resource_name> key=value
kubectl label pod/node <resource_name> key=value --overwrite
// Mencari Pods dengan Label
kubectl get pods -l key
kubectl get pods -l key=value
kubectl get pods -l '!key'
kubectl get pods -l key!=value
kubectl get pods -l 'key in (value1,value2)'
kubectl get pods -l 'key notin (value1,value2)'
// Mencari Pods dengan beberapa label
kubectl get pods -l key,key2=value
// Menambah atau merubah annotation di Pod melalui command line
kubectl annotate pod <pod_name> key=value
kubectl annotate pod <pod_name> key=value --overwrite
// Cara melihat daftar namespace
kubectl get namespaces
kubectl get namespace
kubectl get ns
// Melihat daftar pod pada namespace
kubectl get pod --namespace <namespace_name>
kubectl get pod -n <namespace_name>
// Membuat pod pada namespace tertentu
kubectl create -f <config_file> -n <namespace_name>
// Menghapus namespace
kubectl delete namespace <namespace_name>
// Melihat replication controller
kubectl get replicationcontrollers
kubectl get replicationcontroller
kubectl get rc
// Menghapus replication controller
kubectl delete rc <rc_name>
// Menghapus replication controller tanpa menghapus Pod didalamnya
kubectl delete rc <rc_name> --cascade=false
// Melihat replica set
kubectl get replicasets
kubectl get replicaset
kubectl get rs
// Menghapus replica set
kubectl delete rs <rs_name>
// Menghapus replica set tanpa menghapus Pod didalamnya
kubectl delete rs <rs_name> --cascade=false
// Melihat daemon set
kubectl get daemonsets
kubectl get daemonset
kubectl get ds
// Menghapus daemon set
kubectl delete ds <ds_name>
// Melihat daftar job
kubectl get jobs
// Melihat daftar cronjob
kubectl get cronjobs
// Melihat semua daftar resources
kubectl get all
// Melihat service
kubectl get services
// Menghapus service
kubectl delete services <service_name>
// Mengakses service dari dalam cluster
kubectl exec <pod_name> -it -- /bin/sh
curl http://cluster-ip:port/
// Melihat environment variable pada Pod
kubectl exec <pod_name> -- env
// Melihat service endpoint
kubectl describe service <service_name>
kubectl get endpoints <service_name>
// Melihat ingress
kubectl get ingress
// Menghapus ingress
kubectl delete ingress <ingress_name>
// Melihat resource yang lain
kubectl get configmaps
kubectl get secrets
- Untuk memberi tanda pada Pod.
- Untuk mengorganisir Pod.
- Memberi informasi tambahan pada Pod.
- Tidak hanya digunakan pada Pod, tapi juga untuk semua resource di Kubernetes, seperti Replication Controller, Replica Set, Service, dll.
- Annotation mirip seperti label tapi tidak dapat difilter.
- Digunakan untuk memberikan informasi tambahan dalam ukuran besar.
- Namespace digunakan ketika resource Kubernetes sudah terlalu banyak.
- Atau ketika butuh memisahkan resources untuk multi-tenant, team atau environment.
- Nama resource bisa sama di namespace yang berbeda.
- Namespace tidak bisa digunakan untuk mengisolasi pod, pod-pod yang berada pada namespace yang beda, tetap dapat saling berkomunikasi.
- Jika ada kebutuhan untuk mengisolasi pod, buat pada cluster yang berbeda.
- Jika kita menghapus sebuah namespace, maka semua pod dalam namaspace tersebut bakal terhapus.
- Kubelet menggunakan Liveness untuk mengecek kapan harus me-restart Pod.
- Kalau aplikasi kita tidak sehat, misalnya saat Pod tidak merespon kubelet, maka akan me-restrart secara otomatis.
- Kubelet menggunakan Readiness untuk mengecek apakah Pod siap menerima traffic.
- Readiness akan men-stop traffic pada aplikasi yg tidak sehat (buka me-restart), setelah itu akan mengecek lagi sampai aplikasi berjalan dg baik. Lalu dia akan menerima traffic lagi.
- Kubelet menggunakan Startup Probe untuk apakah Pod sudah berjalan, jika belum, maka kubelet tidak akan melakukan pengecekan Liveness & Readiness.
- Startup Probe cocok untuk Pod yang membutuhkan proses start yang lama, untuk memastikan Pod tidak mati sebelum berjalan dengan sempurna.
- HTTP Get
- TCP Socket
- Command Exec
containers:
...
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5 # Waktu delay sebelum pengecekan (default 0)
periodSeconds: 5 # Berapa detik sekali akan dilakukan pengecekan (default 10)
timeoutSeconds: 1 # Maksimal waktu timeout untuk dianggap gagal (default 1)
successThreshold: 1 # Berapa kali success checking agar dianggap sehat (default 1)
failureThreshold: 3 # Berapa kali failure checking agar dianggap gagal (default 3)
- Replication controller bertugas untuk memastikan bahwa Pod selalu berjalan
- Jika Pod tiba-tiba mati atau hilang, maka replication controller akan otomatis membuat Pod baru
- Replication controller akan memastikan jumlah Pod yang berjalan sejumlah yang telah ditentukan. Jika kurang, maka akan membuat buat Pod baru, jika lebih mana akan menghapus Pod yang sudah ada.
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx-rc
spec:
replicas: 3
selector:
app: nginx # selector.app ini mengarah ke template.metadata.labels.app
template:
metadata:
name: nginx # nantinya, nama pod dari hasil replika akan secara otomatis ditambahkan code unik
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
- Saat kita menghapus replication controller, semua Pod yang berada pada label selectornya akan ikut terhapus
- Jika ingin menghapus replication controller, tanpa menghapus Podnya, kita bisa menambahkan
--cascade=false
- Replica Set adalah generasi baru sebagai pengganti Replication Controller
- Replication Controller sendiri saat ini sudah tidak direkomendasikan
- Replica Set memiliki label selector yang lebih expressive dibandingkan dengan Replication controller yang hanya memiliki fitur label selector secara match
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-rc
spec:
replicas: 3
selector:
matchLabels: # selector query matchLabels
app: nginx # selector.app ini mengarah ke template.metadata.labels.app
template:
metadata:
name: nginx # nantinya, nama pod dari hasil replika akan secara otomatis ditambahkan code unik
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
Operator pada match expressions:
- In, value label harus ada di value in
- NotIn, value label tidak boleh ada di value in
- Exists, label harus ada
- NotExists, label tidak boleh ada
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx-rs
spec:
replicas: 3
selector:
matchExpressions: # match expression
- key: app
operator: In
values:
- nginx
- key: env
operator: In
values:
- prod
- qa
- dev
template:
metadata:
name: nginx # nantinya, nama pod dari hasil replika akan secara otomatis ditambahkan code unik
labels:
app: nginx
env: prod
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
- Saat menggunakan Replica Set atau Replication Controller, Pod akan dijalankan di Node secara random oleh K8s
- Jika ingin menjalankan Pod di setiap Node, dan tiap Pod hanya boleh jalan 1 di Node, maka bisa menggunakan Daemon Set
- Secara default, Daemon Set akan menjalankan Pod di setiap Node, kecuali jika kita meminta hanya jalan di Node tertentu
- Aplikasi untuk monitoring Node
- Aplikasi untuk mengambil log di Node
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: daemon-nginx
labels:
name: daemon-nginx
spec:
selector:
matchLabels:
name: daemon-nginx
template:
metadata:
name: daemon-nginx
labels:
name: daemon-nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
- Job adalah resource di Kubernetes yang digunakan untuk menjalankan Pod yang hanya butuh berjalan sekali, lalu berhenti.
- Pada Replication Controller, Replica Set dan Daemon Set, jika Pod mati, maka akan secara otomatis Pod akan dijalankan ulang.
- Berbeda dengan Job, yang justru Pod akan mati jika pekerjaannya selesai dilakukan.
apiVersion: batch/v1
kind: Job
metadata:
name: nodejs-job
spec:
completions: 4 # berapa banyak job yg harus dijalankan sampai dianggap complete, default 1.
parallelism: 2 # berapa banyak job yg berjalan pada satu waktu, default 1.
template:
spec:
restartPolicy: Never # ini wajib Never
containers:
- name: nodejs-job
image: khannedy/nodejs-job
$ kubectl describe job nodejs-job
...
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 7m39s job-controller Created pod: nodejs-job-6gmrd
Normal SuccessfulCreate 7m39s job-controller Created pod: nodejs-job-wpfgd
Normal SuccessfulCreate 7m3s job-controller Created pod: nodejs-job-mrvp5
Normal SuccessfulCreate 7m1s job-controller Created pod: nodejs-job-742kn
Normal Completed 6m53s job-controller Job completed
- Cron Job sendiri adalah aplikasi untuk penjadwalan yang ada di sistem operasi unix.
- Cron Job pada Kubernetes cara kerjanya mirip dengan Job, hanya saja kalau Job berjalan sekali, tapi Cron Job bisa berjalan berulang kali sesuai dengan jadwal yang kita inginkan.
- Cron Job juga memungkinkan kita untuk menjalankan aplikasi dengan waktu yang telah ditentukan.
- Aplikasi untuk membuat laporan harian
- Aplikasi untuk membackup data secara berkala
- Aplikasi untuk mengirim data tagihan tiap bulan ke pihak lain
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: nodejs-cronjob
labels:
name: nodejs-cronjob
spec:
schedule: "* * * * *" # kapan cronjob akan dieksekusi
jobTemplate:
spec:
template:
metadata:
name: nodejs-cronjob # nama Pod
labels:
name: nodejs-cronjob
spec:
restartPolicy: Never
containers:
- name: nodejs-cronjob
image: khannedy/nodejs-job
$ ku get all
NAME READY STATUS RESTARTS AGE
pod/nodejs-cronjob-1607403060-sq9q7 0/1 Completed 0 2m57s
pod/nodejs-cronjob-1607403120-f8crb 0/1 Completed 0 2m7s
pod/nodejs-cronjob-1607403180-gw2n6 0/1 Completed 0 67s
pod/nodejs-cronjob-1607403240-55c5r 0/1 Completed 0 6s
NAME COMPLETIONS DURATION AGE
job.batch/nodejs-cronjob-1607403060 1/1 7s 2m59s
job.batch/nodejs-cronjob-1607403120 1/1 6s 2m8s
job.batch/nodejs-cronjob-1607403180 1/1 7s 68s
job.batch/nodejs-cronjob-1607403240 1/1 6s 8s
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob.batch/nodejs-cronjob * * * * * False 1 8s 10m
- Kadang kita membuat Node dengan spesifikasi berbeda dari Node biasanya.
- Misal Node yang memiliki GPU, atau dengan hard disk SSD.
- Dengan Node Selector, kita bisa meminta Kubernetes untuk menjalankan Pod pada Node tertentu.
Pertama kita harus menambahkan label terlebih dahulu pada Node
kubectl label node <node_name> gpu=true
Kemudian tinggal menambahkan field nodeSelector
pada yaml.
apiVersion: v1
kind: Pod
metadata:
name: pod-name
spec:
nodeSelector: # sode selector
gpu: "true"
containers:
- name: container-name
image: image-name
ports:
- containerPort: 80
- Service digunakan untuk membuat satu gerbang untuk mengakses satu atau lebih Pod.
- Service memiliki IP Address dan Port yang tidak berubah selama service itu ada.
- Client bisa mengakses service tersebut, dan secara otomatis akan meneruskan ke Pod yang ada di belakang service tersebut.
- Dengan begini client tidak perlu tahu kolasi tiap Pod, dan Pod bisa bertambah, berkurang atau berpindah, tanpa harus menggangu client.
- Service akan mendistibusikan trafik ke Pod yang ada di belakangnya secara seimbang.
- Service menggunakan label selector untuk menentukan Pod mana yang ada di belakang service tersebut.
kubectl exec <pod_name> -it -- /bin/sh
curl http://cluster-ip:port/
apiVersion: v1
kind: Service
metadata:
name: service-name
spec:
selector:
label-key1: label-value1
ports:
- port: 8080
targetPort: 80
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
name: nginx
template:
metadata:
name: nginx
labels:
name: nginx # Label Pod
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
name: nginx # Menentukan Pod dengan label "nginx" yang akan diteruskan jika ada request dari client.
ports:
- port: 8080
targetPort: 80
---
# Ini hanya sebagai contoh, nanti digunakan untuk mencoba mengakses IP address dari dalam Cluster.
# kubectl exec curl -it -- /bin/sh
# curl http://cluster-ip:port
apiVersion: v1
kind: Pod
metadata:
name: curl
labels:
name: curl
spec:
containers:
- name: curl
image: khannedy/nginx-curl
- Untuk mengakses service, ada 2 cara, yang pertama cara manual, yakni dengan melihat env variable terlebih dahulu
$ kubectl exec -it <pod_name> -- /bin/sh
# env // setelah masuk ke dalam Pod
...
- Cara yang kedua, dengan menggunakan DNS, cara ini lebih direkomendasikan karena kita tidak perlu tahu IP addressnya, hanya perlu nama service dan namespace.
nama-service.nama-namespace.svc.cluster.local:port
// misalnya
nginx-service.default.svc.cluster.local:8080
- Biasanya Service digunakan sebagai gateway untuk internal Pod.
- Tapi Service juga bisa digunakan sebagai gateway untuk aplikasi yang berada di luar Kubernetes Cluster.
- External service berperan sebagai gerbang untuk dapat mengakses aplikasi dari luar.
kubectl describe service <service_name>
kubectl get endpoints <service_name>
apiVersion: v1
kind: Service
metadata:
name: example-service
labels:
name: example-service
spec:
type: ExternalName
externalName: example.com
ports:
- port: 80
---
# Pod ini hanya digunakan untuk mengakses external service
apiVersion: v1
kind: Pod
metadata:
name: curl
labels:
name: curl
spec:
containers:
- name: curl
image: khannedy/nginx-curl
Kemudian, kita coba akses external service tersebut.
$ ku exec curl -it -- /bin/sh
/ # curl example-service.default.svc.cluster.local
- Kadang ada kebutuhan untuk mengekspos service keluar
- Tujuannya adalah agar aplikasi dari luar dapat mengakses Pod yang berada di belakang service tersebut.
- ClusterIP: Mengekspos Service di dalam internal kubernetes cluster.
- ExternalName: Memetakan Service ke ExternalName, misalnya example.com.
- NodePort: Mengekspos Service pada setiap IP node dan port yang sama. Kita dapat mengakses Service dengan tipe ini dari luar cluster melalui
<NodeIP>:<NodePort>
. - LoadBalancer: Mengekspos Service secara eksternal dengan menggunakan LoadBalancer yang disediakan oleh penyedia layanan cloud.
- Menggunakan NodePort, sehingga Node akan membuka port yang akan meneruskan request ke Service yang dituju.
- Menggunakan LoadBalancer, sehingga Service bisa diakses via LoadBalancer, dan LoadBalancer akan meneruskan request ke NodePort dan dilanjutkan ke Service.
- Menggunakan Ingress, dimana Ingress adalah resource yang memang ditujukan untuk mengekspos Service. Namun Ingress hanya beroperasi di level HTTP.
- Saat kita membuat Service dengan tipe NodePort, maka Node yang ada di Kubernetes akan membuka port.
- Saat client mengakses
<NodeIP>:<NodePort>
pada Node 1, maka Node tsb akan meneruskan ke Service kemudian diteruskan lagi ke Pod.
$ minikube service kubernetes
|-----------|------------|-------------|--------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------------|-------------|--------------|
| default | kubernetes | | No node port |
|-----------|------------|-------------|--------------|
😿 service default/kubernetes has no node port
🏃 Starting tunnel for service kubernetes.
|-----------|------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|------------|-------------|------------------------|
| default | kubernetes | | http://127.0.0.1:52900 |
|-----------|------------|-------------|------------------------|
🎉 Opening service default/kubernetes in default browser...
❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
name: nginx
template:
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort # default, type: ClusterIP
selector:
name: nginx
ports:
- port: 80
targetPort: 80
nodePort: 30001
- Kemudian jalankan
minikube service nginx-service
$ ku get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3h8m
nginx-service NodePort 10.109.193.143 <none> 80:30001/TCP 13m
$ minikube service nginx-service
|-----------|---------------|-------------|---------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|---------------|-------------|---------------------------|
| default | nginx-service | 80 | http://192.168.49.2:30001 |
|-----------|---------------|-------------|---------------------------|
🏃 Starting tunnel for service nginx-service.
|-----------|---------------|-------------|------------------------|
| NAMESPACE | NAME | TARGET PORT | URL |
|-----------|---------------|-------------|------------------------|
| default | nginx-service | | http://127.0.0.1:53204 |
|-----------|---------------|-------------|------------------------|
🎉 Opening service default/nginx-service in default browser...
❗ Because you are using a Docker driver on darwin, the terminal needs to be open to run it.
- And open http://127.0.0.1:53204 in browser
- Cloud provider seperti Google Cloud atau Amazon Web Service biasanya memiliki Cloud Load Balancer
- Kubernetes bisa menggunakan Load Balancer bawaan dari Cloud Provider sebagai cara untuk mengekspos Service
- Load Balancer akan melakukan load balance request ke NodePort
- Sayangnya Service Load Balancer tidak bisa ditest di local seperti menggunakan Minikube
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
name: nginx
template:
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: LoadBalancer # type LoadBalancer
selector:
name: nginx
ports:
- port: 80
targetPort: 80
Ada beberapa masalah saat mengekspos service:
- Jika menggunakan NodePort
- Maka semua Node harus terekspos ke public
- Client harus tahu semua IP Address Node
- Jika menggunakan LoadBalancer
- Maka semua LoadBalancer harus terekspos ke public
- Client harus tahu semua IP Address LoadBalancer
- Ingress adalah salah satu cara yang bisa digunakan untuk mengekspos Service.
- Berbeda dengan LoadBalancer atau NodePort, jika menggunakan Ingress, client hanya butuh tahu satu lokasi IP Address Ingress
- Ketika client melakukan request ke Ingress, pemilihan servicenya ditentukan menggunakan hostname dari request (mis. service1.example.com)
- Ingress hanya mendukung protocol HTTP
minikube addons list
minikube addons enable ingress
kubectl get pods --namespace kube-system
If there is trouble when enabling ingress, it should run:
minikube delete
minikube start --vm=true
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
name: nginx
template:
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
name: nginx
ports:
- port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: nginx-ingress
labels:
name: nginx-ingress
spec:
rules:
- host: nginx.khannedy.local # contoh domain
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
Setelah kita create, kemudian kita lihat ingressnya
$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
nginx-ingress <none> nginx.khannedy.local 192.168.64.2 80 5m55s
Kemudian kita cek apakah Pods sudah running
$ ku get all
NAME READY STATUS RESTARTS AGE
pod/nginx-mhj8c 1/1 Running 0 4m25s
pod/nginx-rwkkj 1/1 Running 0 4m25s
pod/nginx-z48x8 1/1 Running 0 4m25s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24m
service/nginx-service ClusterIP 10.107.124.171 <none> 80/TCP 4m25s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx 3 3 3 4m25s
Kalau sudah, kita coba setup host di local, caranya kita lihat dulu IP address ingressnya
$ minikube ip
192.168.64.2
Kemudian kita setup di /etc/hosts/
$ code /etc/hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
# Added by Docker Desktop
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section
# Tambahkan ini
192.168.64.2 nginx.khannedy.local
- Saat menggunakan Docker, kita selalu diajarkan bahwa 1 aplikasi adalah 1 Container.
- Di Kubernetes agak sedikit berbeda, saat kita deploy aplikasi kita, maka akan disimpan dalam 1 Pod. Kenapa Pod? tidak Container, karena sebenarnya di dalam Pod, kita bisa menambahkan banyak Container.
- Ini cocok sekali jika memang kita butuh aplikasi yang berjalan di beberapa Container, dan jika ingin scale, harus semuanya ikut scale.
- Saat running multiple container, IP dan Port nya sharing dalam 1 Pod, jadi kalau kita buat lebih dari satu container dalam Pod, Port tidak boleh sama.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
name: nginx
template:
metadata:
name: nginx
labels:
name: nginx
spec:
containers: # Multiple Container
- name: nginx
image: nginx
ports:
- containerPort: 80
- name: nodejs-web
image: khannedy/nodejs-web
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
name: nginx
ports:
- port: 8080
targetPort: 80
name: nginx
- port: 3000
targetPort: 3000
name: nodejs-web
---
apiVersion: v1
kind: Pod
metadata:
name: curl
labels:
name: curl
spec:
containers:
- name: curl
image: khannedy/nginx-curl
Tes akses service di dalam Kubernetes Cluster.
$ kubect exec -it curl -- /bin/sh
# curl http://custer-ip-service:port-container-1
#curl http://custer-ip-service:port-container-2
- Agar data-data di dalam container tidak ikut terhapus saat Pod atau Container dihapus, maka kita perlu membuat Volume.
- Volume secara sederhana adalah sebuah direktori yang bisa diakses oleh container-container di Pod.
- emptyDir, direktori sederhana yang kosong
- hostPath, digunakan untuk sharing direktori di Node ke Pod
- gitRepo, direktori yang dibuat pertama kali dengan meng-clone git repository
- nfs, sharing network file system
- dan lain-lain: https://kubernetes.io/id/docs/concepts/storage/volumes/#jenis-jenis-volume
Kita buat aplikasi sederhana dengan Node.js
const fs = require("fs");
const process = require("process");
let location = process.env.HTML_LOCATION;
if (!location) {
location = "/app/html"
}
setInterval(() => {
const date = new Date();
const html = `<html><body>${date}</body></html>`;
fs.writeFile(location + "/index.html", html, err => {
if (err) {
console.log("Failed write file")
} else {
console.log("Success write file")
}
})
}, 5000);
Kemudian buat Dockerfile, kemudian build seperti biasa.
FROM node:12-alpine
RUN apk --no-cache add curl
RUN mkdir -p /app/html
ADD app.js app.js
CMD ["node", "app.js"]
Kemudian kita buat template Kubernetesnya, create seperti biasa.
apiVersion: v1
kind: Pod
metadata:
name: nodejs-writer
labels:
name: nodejs-writer
spec:
volumes:
- name: html
emptyDir: {}
containers:
- name: nodejs-writer
image: khannedy/nodejs-writer
volumeMounts:
- mountPath: /app/html
name: html
Cek volumenya
$ kubectl exec nodejs-writer -it -- /bin/sh
# cd /app/html
# ls
index.html
# cat index.html
- Sharing Volume antar Container dalam 1 Pod.
- Ini sangat cocok ketika kita butuh sharing direktori antar Container, misal container pertama membuat file, container kedua memproses file.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
name: nginx
template:
metadata:
name: nginx
labels:
name: nginx
spec:
volumes:
- name: html
emptyDir: {}
containers:
- name: nodejs-writer
image: khannedy/nodejs-writer
volumeMounts:
- mountPath: /app/html
name: html
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: html # container nginx mengambil data dari volume yang sama
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
selector:
name: nginx
ports:
- port: 8080
targetPort: 80
nodePort: 30001
Gunakan minikube service nginx-service
untuk mengakses public IP address.
apiVersion: v1
kind: Pod
metadata:
name: nodejs-writer
labels:
name: nodejs-writer
spec:
volumes:
- name: html
emptyDir: {}
containers:
- name: nodejs-writer
image: khannedy/nodejs-writer
volumeMounts:
- mountPath: /app/folder-from-env
name: html
env:
- name: HTML_LOCATION # Environment variable
value: /app/folder-from-env
- ConfigMap digunakan untuk memisahkan environment variable dalam object yang berbeda dengan Pod. Hal ini bertujuan agar penulisan environment lebih konsisten.
apiVersion: v1
kind: ConfigMap
metadata:
name: nodejs-env-config
data:
APPLICATION: My Cool Application
VERSION: 1.0.0
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nodejs-env
spec:
replicas: 3
selector:
matchLabels:
name: nodejs-env
template:
metadata:
name: nodejs-env
labels:
name: nodejs-env
spec:
containers:
- name: nodejs-env
image: khannedy/nodejs-env
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: nodejs-env-config
---
apiVersion: v1
kind: Service
metadata:
name: nodejs-env-service
spec:
type: NodePort
selector:
name: nodejs-env
ports:
- port: 3000
targetPort: 3000
nodePort: 30001
Execute this command to run app on browser.
minikube service nodejs-env-service
- Secret prinsipnya sama seperti ConfigMap, tapi Secret dikhususkan untuk menyimpan data yang sifatnya sensitif.
apiVersion: v1
kind: Secret
metadata:
name: configmap-name
data:
ENV: base64(VALUE) # jika menggunakan ini, maka harus diencode ke base64 secara manual
stringData:
ENV: VALUE # kalau tidak, bisa langsung menggunakan stringData tanpa harus harus encode ke base64
apiVersion: v1
kind: ConfigMap
metadata:
name: nodejs-env-config
data:
APPLICATION: My Cool Application
---
apiVersion: v1
kind: Secret
metadata:
name: nodejs-env-secret
stringData:
VERSION: 1.0.0
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nodejs-env
spec:
replicas: 3
selector:
matchLabels:
name: nodejs-env
template:
metadata:
name: nodejs-env
labels:
name: nodejs-env
spec:
containers:
- name: nodejs-env
image: khannedy/nodejs-env
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: nodejs-env-config
- secretRef:
name: nodejs-env-secret
---
apiVersion: v1
kind: Service
metadata:
name: nodejs-env-service
spec:
type: NodePort
selector:
name: nodejs-env
ports:
- port: 3000
targetPort: 3000
nodePort: 30001
Perbedaan antara ConfigMap dan Secret adalah, ketika kita melihat describe ConfigMap, maka environment variable akan dieskpos.
$ kubectl describe configmap <configmap_name>
Data
====
APPLICATION:
----
My Cool Application
Sedangkan untuk Secret tidak (hanya size datanya saja)
$ kubectl describe secret <secret_name>
Data
====
VERSION: 5 bytes
- Downward API memungkinkan kita mengambil informasi seputar Pod dan Node melalui environment variable.
- Jadi Downward API adalah environment bawaan dari Kubernetes yang bisa kita akses untuk keperluan deployment.
- Jangan bingung dengan kata API, Downward API sendiri bukanlah RESTful API.
apiVersion: v1
kind: ConfigMap
metadata:
name: nodejs-env-config
data:
APPLICATION: My Cool Application
VERSION: 1.0.0
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nodejs-env
spec:
replicas: 3
selector:
matchLabels:
name: nodejs-env
template:
metadata:
name: nodejs-env
labels:
name: nodejs-env
spec:
containers:
- name: nodejs-env
image: khannedy/nodejs-env
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: nodejs-env-config
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: POD_NODE
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: POD_NODE_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
---
apiVersion: v1
kind: Service
metadata:
name: nodejs-env-service
spec:
type: NodePort
selector:
name: nodejs-env
ports:
- port: 3000
targetPort: 3000
nodePort: 30001
Note:
kubectl get -f file.yaml
sama artinya dengan kita menjalankankubectl get pod nama-pod
. Artinya Pod harus di-create terlebih dahulu.kubectl replace
hanya bisa me-replace sebagian kecil saja, tidak semua object.kubectl delete -f file.yaml
sama artinya dengan kita menjalankankubectl get pod nama-pod
.
- Perbedaan antara
apply
dancreate
adalah jika menggunakanapply
(declarative management), file konfigurasi akan disimpan di dalam annotations. - Ini sangat bermanfaat saat menggunakan object Deployment.
- Rata-rata saat ini declarative management lebih banyak digunakan dibandingkan Imperative Management.
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
Coba menggunakan apply
untuk meng-create Pod.
$ kubectl apply -f nginx.yaml
Kemudian kita get.
$ kubectl get -f nginx -o json
// Atau kita buka di VS Editor
$ kubectl get -f nginx -o json | code -
- Deployment digunakan untuk melakukan deployment aplikasi dan update aplikasi secara deklaratif menggunakan file konfigurasi.
- Saat kita membuat deployment, secara otomatis Kubernetes akan membuat ReplicaSet, yg mana ReplicaSet akan secara otomatis membuat Pod.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-web
labels:
name: nodejs-web
spec:
replicas: 3
selector:
matchLabels:
name: nodejs-web
template:
metadata:
name: nodejs-web
labels:
name: nodejs-web
spec:
containers:
- name: nodejs-web
image: khannedy/nodejs-web:1
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: nodejs-web-service
spec:
type: NodePort
selector:
name: nodejs-web
ports:
- port: 3000
targetPort: 3000
nodePort: 30001
Create deployment dengan menggunakan kubectl apply -f deployment.yaml
.
Jika kita lihat hasilnya, ReplicaSet secara otomatis telah dibuat.
Untuk menajalankan aplikasinya, tinggal execute minikube service nama-service
.
- Untuk update deployment caranya sangat mudah, tinggal jalankan perintah
kubectl apply -f new-deployment.yaml
. - Saat deployment terbaru dieksekusi, secara otomatis deployment akan membuat ReplicaSet baru, lalu menjalankan Pod baru, setelah Pod siap, deployment akan menghapus Pod lama secara otomatis.
- Ini membuat proses update berjalan mulus, dan tidak terjadi downtime.
- Pod yang lama tidak serta merta langsung dihapus, tapi akan tetap disimpan terlebih dahulu untuk kebutuhan roll-back.
- Maksimal kapasitas penyimpanan Pod lama, secara default sebanyak 10. Untuk mengubahnya tinggal tambahkan
revisionHistoryLimit
.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-web
labels:
name: nodejs-web
spec:
revisionHistoryLimit: 5 # here
replicas: 3
selector:
matchLabels:
name: nodejs-web
template:
metadata:
name: nodejs-web
labels:
name: nodejs-web
spec:
containers:
- name: nodejs-web
image: khannedy/nodejs-web:1
ports:
- containerPort: 3000
Jadi untuk me-rollback bisa menggunakan perintah kubectl rollout undo deployment nama-deployment
.
- Persistent Volume sebenarnya mirip dengan Volume biasa, hanya saja cara kerjanya sedikit berbeda.
- Cara membuat Persistent Volume sedikit lebih ribet dibanding Volume, namun ada benefit yg bisa didapat jika menggunakan Persistent Volume.
- HostPath, berkas disimpan di Node, tidak direkomendasikan di production, hanya untuk testing
- GCEPersistentDisk, Google Cloud Persistence Disk
- Dll, lihat https://kubernetes.io/docs/concepts/storage/persistent-volumes/
- Membuat Persistent Volume
- Membuat Persistent Volume Claim
- Menambahkan Persistent Volume Clain ke Pod
- Persistent Volume Claim digunakan untuk menentukan berapa kapasitas storage untuk sebuat Pod. Ditentukan berdasarkan kapasitas Persistent Volume dibagi total Pod. Misalnya total storage Persistent Volume adalah 10, nanti akan dipakai untuk 3 Pod, maka tiga Pod itu bisa menentukan Persistent Volume Claim sebesar masing-masing 2 atau 3 (bebas).
kubectl get pv
kubectl describe pv nama-persistent-volume
kubectl get pvc
kubectl describe pvc nama-persistent-volume-claim
kubectl delete pv nama-persistent-volume
kubectl delete pvc nama-persistent-volume-claim
apiVersion: v1
kind: PersistentVolume
metadata:
name: nodejs-writer-volume
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
hostPath:
path: /data/location
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nodejs-writer-volume-claim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Pod
metadata:
name: nodejs-writer
labels:
name: nodejs-writer
spec:
volumes:
- name: html
persistentVolumeClaim:
claimName: nodejs-writer-volume-claim
containers:
- name: nodejs-writer
image: khannedy/nodejs-writer
volumeMounts:
- mountPath: /app/html
name: html
- Aplikasi stateful adalah aplikasi yang menyimpan data atau memiliki database, aplikasi jenis ini tidak bisa dihapus sembarangan.
- Berbeda bengan aplikasi stateless, yang tidak menyimpan data dapat dihapus dan dibuat ulang, tidak menjadi masalah.
- PersistentVolume tidak tidak bisa membuat aplikasi statefull, karena semua Pod men-claim semua PersistentVolume yang sama dan direktori yang sama.
- Sedangkan jika aplikasi stateful, umumnya memiliki data yang independen untuk tiap Pod, walaupun jenis Podnya sama.
- StatefulSet adalah object di Kubernetes yang digunakan untuk memanage aplikasi jenis stateful.
- StatefulSet memastikan nama Pod yang konsisten, identitas network yang stabil, dan persisten volume yang independen untuk tiap Pod.
- Jika Pod mati, maka StatefulSet akan membuat Pod baru dengan informasi yang sama dengan Pod yang mati.
kubectl get statefulset
kubectl describe statefulset nama-statefulset
kubectl delete statefulset nama-statefulset
apiVersion: v1
kind: PersistentVolume
metadata:
name: nodejs-stateful-volume
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
hostPath:
path: /data/location
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nodejs-stateful
labels:
name: nodejs-stateful
spec:
# https://github.com/kubernetes/kubernetes/issues/69608
serviceName: nodejs-stateful-service # bug
replicas: 3
selector:
matchLabels:
name: nodejs-stateful
volumeClaimTemplates:
- metadata:
name: nodejs-stateful-volume-claim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
template:
metadata:
name: nodejs-stateful
labels:
name: nodejs-stateful
spec:
containers:
- name: nodejs-stateful
image: khannedy/nodejs-stateful
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- mountPath: /app/data
name: nodejs-stateful-volume-claim
Coba hapus Pod, maka StatefulSet akan membuat Pod baru lagi dengan nama yang sama.
- Kubenetes Dashboard adalah GUI untuk memanage kubernetes berbasis web.
- Biasanya kalau kita menggunakan Cloud Provider, sudah disediakan web user interface untuk k8s.
- Tapi jika ingin menginstall Kubernetes data center sendiri, bisa menggunakan GUI dari bawaan k8s, namanya Kubernetes Dashboard: https://github.com/kubernetes/dashboard
minikube addons enable dashboard
kubectl get all --namespace nama-namespace
minikube dashboard
Note: di dalam Kubernetes Dashboard kita juga bisa membuat object/resource kubernetes.
- Adalah mekanisme membatasi jumlah resource yang digunakan oleh Pod agar tidak terjadi perebutan resource antar Pod.
- Misalnya sebuah Node memiliki memori 10 gb dan cpu 4 core, maka itu akan digunakan oleh Pod-pod yang ada di dalamnya.
- Pod-pod dalam sebuah Node, akan menggunakan resource secara sharing. Jadi, jika ada 1 Pod yang sibuk, akan membuat Pod lain menjadi lambat, karena menggunakan resource yang besar.
- Request dan Limit adalah mekanisme Kubernetes untuk mengontrol penggunaan memory dan CPU.
- Request adalah menetapkan permintaan jumlah resource untuk Pod. Kubernetes hanya akan menjalankan di Node yang bisa memenuhi jumlah resource tersebut.
- Limit adalah batas penggunaan resource pada sebuah container. Misalnya sebuah Pod memiliki limit 5 GB untuk memori, maka Pod tersebut tidak bisa menggunakaan lebih dari itu.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-web
labels:
name: nodejs-web
spec:
replicas: 3
selector:
matchLabels:
name: nodejs-web
template:
metadata:
name: nodejs-web
labels:
name: nodejs-web
spec:
containers:
- name: nodejs-web
image: khannedy/nodejs-web
ports:
- containerPort: 3000
resources: # resources
requests:
cpu: 1000m # milli core
memory: 1000Mi # mebibyte
limits:
cpu: 1000m
memory: 1000Mi
---
apiVersion: v1
kind: Service
metadata:
name: nodejs-web-service
spec:
type: NodePort
selector:
name: nodejs-web
ports:
- port: 3000
targetPort: 3000
nodePort: 30001
- Saat aplikasi sedang sibuk, maka konsumsi memory atau CPU akan tinggi, maka ada kemungkinan performa aplikasi kita akan turun.
- Saat hal ini terjadi, application scaling sangat dibutuhkan.
- Secara garis besar, ada 2 jenis application scaling, yakni Vertical Scaling dan Horizontal Scaling.
- Vertical Scaling adalah cara application scaling dengan mengupgrade computational resource di aplikasi kita.
- Misal dari 1 CPU menjadi 2 CPU, dari 1 GB memory menjadi 2 GB.
- Namun permasalahannya Vertical Scaling akan ada batasnya. Pod di Kubernetes tidak bisa menggunakan resource melebihi yang telah tersedia di Node.
- Horizontal Scaling adalah application scaling dengan cara membuat Pod baru, agar beban pekerjaan bisa didistribusikan ke Pod baru tersebut.
- Scalability terbaik harusnya dicapai oleh Horizontal Scaling, karena tidak butuh upgrade Node dengan resource yang lebih tinggi.
- Adalah kemampuan secara otomatis application scaling secara vertical dengan cara mengupgrade resource Pod dan menurunkan secara otomatis jika diperlukan.
- https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler
- Adalah kemampuan secara otomatis application scaling secara horizontal dengan cara menambah Pod baru dan mengurangi secara otomatis bila diperlukan.
- HPA adalah object di Kubernetes.
- HPA bekerja dengan cara melihat data metrics dari setiap Pod, dan jika sudah mencapai batas tertentu, HPA akan melakukan auto scaling (baik itu menaikan jumlah Pod atau menurunkan).
minikube addons enable metrics-server
kubectl get pods --namespace kube-system
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-web
labels:
name: nodejs-web
spec:
selector:
matchLabels:
name: nodejs-web
template:
metadata:
name: nodejs-web
labels:
name: nodejs-web
spec:
containers:
- name: nodejs-web
image: khannedy/nodejs-web:1
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: nodejs-web-service
spec:
type: NodePort
selector:
name: nodejs-web
ports:
- port: 3000
targetPort: 3000
nodePort: 30001
---
# Horizontal Pod Autoscaler
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: hpa-name
spec:
minReplicas: 3 # Jumlah minimal replica pada Pod, jika Pod hanya awalnya hanya 1 replica, maka akan dibuat menjadi 3 replica.
maxReplicas: 5 # Jumlah maksimal replica jika resource menyentuh angkat tertentu
scaleTargetRef: # Target object
apiVersion: apps/v1
kind: Deployment
name: nodejs-web
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # Rata-rata penggunaan CPU 70%
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 70 # Rata-rata penggunaan memory 70%