Mumpung hari Sabtu, enaknya ngebahas hal-hal yang lumayan kompleks. Mungkin tulisan ini sedikit panjang, semoga cocok untuk menemani satnight mu yang #dirumahaja.
Oke, jadi ceritanya dapet credit gratis sebesar $100 (pengen dapet juga? Daftar Linode sekarang!) untuk nyobain LKE (Linode Kubernetes Engine) yang masih closed beta. Sejauh ini udah nyaman banget sama Docker, tapi karena sedang mempelajari tentang Distributed System, mempelajari Kubernetes sepertinya hal yang menarik.
Tulisan ini menggunakan sudut pandang seorang Fariz, yang tau nya Docker. Jika kamu sudah tau Docker, mungkin tulisan ini sedikit bisa lebih mudah dipahami. Gue gak akan ngebahas tentang apa itu k8s, sejarahnya, dsb karena blog ini bukanlah Wikipedia, jadi mari kita langsung bahas sedikit tentang kenapa pakai k8s.
Container Orchestrator
Di Docker, ada namanya mode Swarm. Yang kita tau, Docker hanyalah Container Runtime aja, sebuah "program" yang berguna untuk menjalankan Container (program/aplikasi) berdasarkan suatu image.

Biasanya kita menggunakan Docker via CLI untuk berkomunikasi dengan Docker ini seperti menjalankan (docker run), mematikan (docker stop), dsb.
Kita membiarkan tangan kita kotor untuk melakukan operasional tersebut, dan meskipun kita menggunakan Docker Compose, kegunaan Compose pada dasarnya hanyalah untuk bisa menjalankan container lebih dari satu secara lebih mudah. Tidak perlu perintah docker run
berulang-ulang, cukup dibungkus dengan berkas yml.
Lalu berkenalan lah dengan Docker Swarm, sebuah container orchestrator. Ketika menggunakan mode Swarm, nantinya ada konsep Manager & Node. Yang intinya, si Manager ini yang bertanggung jawab atas node-node yang berjalan pada suatu jaringan. Jika kamu tidak menggunakan mode Swarm, anggap mesin yang kamu sekarang gunakan tersebut adalah Manager.
Oke sebenarnya apa sih tugas Container Orchestrator ini?
Konsep container membuat proses delivery menjadi lebih cepat. Namun software bukan hanya tentang me-release, namun juga me-maintain. Jika dengan konsep Container kita bisa mengautomasi berbagai tugas dengan jaminan program tersebut akan berjalan secara konsisten (remember "it works on my machine?"), kita juga perlu "sesuatu" untuk bisa mengatur, meng-scale, dan me-maintain container kita tersebut.
Dan "sesuatu" tersebut bernama Container Orchestrator.
Docker Swarm & Kubernetes adalah salah dua contoh dari orchestrator ini, dengan kekurangan & kelebihannya masing-masing.
Mengapa kita butuh orchestrator? Karena mengatur sesuatu itu bukanlah hal yang mudah, terlebih mengatur sesuatu yang saling bersebaran. Apalagi banyak.
Disini, kita akan fokus di Kubernetes, atau k8s agar lebih singkat. Ada satu hal yang membuat k8s terlihat ribet, dan akan kita bahas dibawah!
Kubernetes Engine
Ingat ada konsep Manager & Node di Swarm? Di k8s pun sama, namanya Master & Node (minion). Ketika kita menggunakan mode Swarm di Docker, setiap mesin yang terhubung harus ter-install Docker, bukan? Begitupula dengan k8s.
Mode swarm & k8s adalah tentang komputer yang ter-distribusi, tentang membuat cluster, yang singkatnya adalah kumpulan komputer yang saling terhubung dan bekerja sama.
Jika kamu ingin membuat cluster dengan 3 node, kamu harus melakukan instalasi k8s, konfigurasi, dsb secara manual. Begitupula ketika menambah node lagi.
Disini bagian ribetnya.
Sama seperti ketika kita terbiasa "deployment" menggunakan FTP, lalu dihadapkan dengan Docker. Beruntungya, Docker yang mungkin biasa kita gunakan masih untuk 1 server, jadi gak ribet-ribet banget.
Oke lanjut, k8s terdiri dari beberapa komponen. Kurang lebih begini:

Hahaha ribet, kan? Begitulah kalau kita ingin membuat "cluster" k8s kita sendiri, belum termasuk di biaya usaha pemeliharaan ya.
Bukannya menyelesaikan masalah, malah menambah masalah, ya?
Sekarang begini, bagaimana kalau kita lupakan bagian ribet tersebut (hello abstraction!) melainkan kita fokus ke benefit dari orchestrator: untuk bisa mengatur, meng-scale, dan me-maintain container kita tersebut, secara otomatis.
Itulah mengapa ehm ada sebuah layanan yang bernama "managed" kubernetes yang tugasnya melakukan hal-hal ribet itu semua.
Layanan nya sudah banyak, ada GKE (Google Kubernetes Engine) nya Google, EKS (Amazon Elastic Container Service for Kubernetes), AKS (Azure Kubernetes Service) dan masih banyak termasuk LKE (Linode Kubernetes Engine).
Seperti layanan cloud lainnya, kita harus pintar dalam menghitung harga. Google, Amazon, dan Azure adalah pemain besar di industri cloud, mereka menjual nama & integritas sebagai nilai tambahnya.
Disini kita akan pakai LKE, gue salah satu fans berat nya Linode. Dari penawaran yang ada lumayan menarik, terlebih Linode lumayan terpercaya untuk level developer & di industri cloud.

Tapi kita tidak akan langsung masuk ke bagian praktik, kita akan berkenalan terlebih dahulu dengan objek-objek yang ada di k8s seperti Nodes, Pods, dan Services.
Nodes
Nodes adalah mesin yang ada di cluster, yang bertindak sebagai "worker". Ini sudah jelas, sama seperti konsep Node di Swarm.
Pods
Belum nemu "gambaran di dunia nyata" yang sesuai dengan Pods, tapi kurang lebih Pods ini adalah "sebuah wadah" yang menjalankan container. Docker termasuk container runtime yang digunakan di Pods, yang berarti Pods bisa menjalankan 1 container ataupun lebih

Begitu kira-kira gambaran nya.
Services
Sebelumnya, mari kita bandingkan dahulu bila kita menggunakan Docker.

Biasanya prosesnya client mengirim request ke server lalu di handle sama reverse proxy, dari reverse proxy ini yang meneruskan request tersebut ke service yang bersangkutan.
Biasanya kita membatasi port mana saja yang terbuka ke "dunia luar"—bahkan di server gue cuma port 80 & 443 (oke dan port SSH yang udah diubah)—untuk alasan keamanan.
Container bisa berkomunikasi dengan container lain jika berada di 1 jaringan yang sama, bukan? Anggap seluruh container berada di 1 jaringan bernama network1
.
Setiap container meng-expose port nya masing-masing agar bisa saling berkomunikasi. Disini misal gue pakai Traefik untuk reverse proxy, Traefik tau harus nerusin ke service yang mana (plus ke port berapa) berkat bantuan label.
Sederhana, bukan?
Itu untuk ukuran di 1 server, sedangkan kita sedang berbicara tentang cluster, kan?
Oke intinya service adalah tentang "meng-expose" aplikasi/container kita yang ada di pods. Ada beberapa cara untuk meng-expose nya, bisa via ClusterIP; NodePort, LoadBalancer dan ExternalName.
Bingung?
Mari kita sedikit-sedikit bahas.
- ClusterIP: Ini adalah default nya, hanya bisa diakses oleh cluster itu sendiri. Kecuali melakukan port forwarding.
- NodePort: Ini kita bisa mengakses nya via ip:port yang terlihat familiar bila kita tidak menggunakan reverse proxy. Ip & port tersebut mengarah ke node bila ingin diakses oleh luar cluster (bila IP dari node tersebut 6.6.6.6, berarti bisa diakses dari luar (hp lo misalnya) dengan cara 6.6.6.6:666)
- LoadBalancer: Service di expose via external (bukan dari cluster itu sendiri), biasanya dari Load Balancer yang ditawarkan oleh cloud provider yang kita pakai. Load balancer tersebut akan mengerahkan ke NodePort dan ClusterIP yang mengarah ke aplikasi kita. Ini yang murah, mudah, dan aman.
- ExternalName: Ini kita bisa mengakses nya menggunakan nilai dari
externalName
, belum pernah coba jadi kita skip aja ya hampura.
Berdasarkan dari hasil yang gue baca, untuk pemula bisa dimulai dari menggunakan LoadBalancer. Nanti kita bahas selain via LoadBalancer kalau gue udah gak pemula haha.
Oke selain via Service, untuk bisa meng-expose aplikasi kita bisa via Ingress yang tentunya tidak akan kita bahas sekarang. Sejauh ini mungkin sudah lumayan jelas pusing lah ya konsep Kubernetes ini, waktunya kita praktik!
Membuat Cluster
Kita pakai k3d nya Rancher untuk development. k3d ini versi k3s (kubernetes versi ringan) tapi lebih stabil di berbagai platform. Silahkan install terlebih dahulu k3d nya.
HARUS BANGET PAKAI TUTORIAL INSTALL NIH???
Ok ok, harusnya kalau udah punya Docker di mesin nya udah ada program kubectl
ya untuk bisa berkomunikasi dengan kubernetes manager. Kalau memang ternyata belum ke-install, silahkan install terlebih dahulu.
Oke bos udah nih, cobain jalanin kubectl
di terminal buat mastiin kalau udah bener-bener ter-install.
Sekarang waktunya bikin cluster, k3d ini kita bikin cluster tapi di Docker. Caranya sederhana banget, tinggal eksekusi k3d create
maka cluster sudah jadi (ikutin dulu langkah-langkah yang ada di layar setelah eksekusi perintah tersebut).
Karena k3d (k3s) by default menggunakan Traefik sebagai ingress controller nya, mari kita buat si k3d nge-expose port 80 yang kita pakai buat berkomunikasi dengan Traefik.
$ k3d create --workers 3 --publish="80:80"
Perintah diatas untuk membuat cluster kita plus dengan 3 worker & meng-expose port 80.

Oke sekarang kita sudah punya cluster, lalu apa?
Kita setting PVC terlebih dahulu. Nanti kita akan bahas lebih lanjut seputar PVC/Persistent Volume Controller di tulisan selanjutnya, untuk sekarang kita gunakan manifest default dari rancher aja. Dan untuk testing di development, kita akan pakai local-path
aja.
$ kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml
Verifikasi dengan menjalankan perintah kubectl get storageclass
, jika muncul si local-path
berarti sudah yoi.
Oke, terus apa?
Kita akan deploy sebuah ehm blog yakni Ghost. Kenapa deploy Ghost? Karena udah ada image nya di local gue hahaha. Gue belum tau "best practices" untuk membuat berkas manifest di k8s, jadi mari kita buat berkas nya per-aplikasi dulu. Buat berkas dengan nama ghost.yml
lalu isi dengan nilai berikut:
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog
labels:
app: blog
spec:
replicas: 1
selector:
matchLabels:
app: blog
template:
metadata:
labels:
app: blog
spec:
containers:
- name: blog
image: ghost:3.12-alpine
imagePullPolicy: Always
volumeMounts:
- mountPath: /var/lib/ghost/content
name: content
ports:
- containerPort: 2368
env:
- name: url
value: http://my-awesome-blog.com
volumes:
- name: content
persistentVolumeClaim:
claimName: content
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: content
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
storageClassName: local-path
Sekarang mari kita deploy, jalankan kubectl apply -f ghost.yml
dan tunggu beberapa detik. Manifest diatas intinya kita membuat deployment & membuat volume untuk nyimpen data si blog ini.
Mari kita cek apakah sudah ter-deploy atau belum, bisa cek dengan kubectl get pods
. Jika status sudah Running, berarti sudah yoi. Karena kita belum membuat service untuk meng-expose si blog ini, jadi mari kita pakai port forwarding terlebih dahulu.
$ kubectl port-forward <pod_name> 2368
Jika berhasil, harusnya bisa diakses di localhost:2368

Nice, sekarang mari kita buat service buat nge-expose aplikasi ini via ingress.
apiVersion: v1
kind: Service
metadata:
name: blog
namespace: default
spec:
ports:
- port: 80
targetPort: 2368
protocol: TCP
selector:
app: blog
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: blog
annotations:
kubernetes.io/ingress.class: "traefik"
spec:
rules:
- host: my-awesome-blog.com
http:
paths:
- path: /
backend:
serviceName: blog
servicePort: 80
Ini sebagai contoh aja dulu, nantinya akan kita bahas seputar Ingress & (Cloud) Load Balancer. Sekarang kita apply perubahan tersebut menggunakan kubectl apply -f ghost.yml
, lalu kita coba akses!

Oke sudah berjalan, ya? Biar lebih oke kita buat my-awesome-blog.com arahin ke localhost kita. Tambahkan baris 127.0.0.1 my-awesome-blog.com
ke /etc/hosts
dan jika sudah mari kita lihat di browser!

It is done?
Rekap
Mari kita gambarkan ilustrasinya terlebih dahulu.

Dan untuk berkas lengkapnya dari ghost.yml
tersebut adalah seperti ini:
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog
labels:
app: blog
spec:
replicas: 1
selector:
matchLabels:
app: blog
template:
metadata:
labels:
app: blog
spec:
containers:
- name: blog
image: ghost:3.12-alpine
imagePullPolicy: Always
volumeMounts:
- mountPath: /var/lib/ghost/content
name: content
ports:
- containerPort: 2368
env:
- name: url
value: http://my-awesome-blog.com
volumes:
- name: content
persistentVolumeClaim:
claimName: content
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: content
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
storageClassName: local-path
---
apiVersion: v1
kind: Service
metadata:
name: blog
namespace: default
spec:
ports:
- protocol: TCP
targetPort: 2368
port: 80
selector:
app: blog
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: blog
annotations:
kubernetes.io/ingress.class: "traefik"
spec:
rules:
- host: my-awesome-blog.com
http:
paths:
- path: /
backend:
serviceName: blog
servicePort: 80
Masih banyak hal lain yang belum kita bahas secara detail, khususnya seputar ingress & services, persistensi, dsb. Dan juga kita belum melakukan deployment cluster kita ke "cloud" beneran (via Managed Kubernetes), mungkin akan kita bahas di tulisan selanjutnya.
Satu hal yang paling gue seneng dari menggunakan Managed Kubernetes adalah gue gak perlu melakukan SSH ke mesin gue yang ada di entah dimana untuk melakukan deployment, dan ya, mereka biasanya menawarkan fitur Auto Scaling, jadi, goodbye untuk 503 service unavailable!
Terima kasih sudah mampir.