Ada 20 devbox yang berjalan di tempat kerja gue sekarang per tulisan ini dibuat. Mesin paling minimum yang digunakan adalah e2-standard-2
yang seharga 840/bulan. Tidak semua menggunakan e2-standard-2
, beberapa ada yang e2-standard-4
dan juga ada VM Windows, tapi itu cerita lain.
Untuk block storage, paling minimum adalah 30 GB. Ini masih relatif murah, sekitar 112/bulan. Beberapa ada yang menggunakan 50-60 GB, tapi gue ambil angka yang paling kecilnya aja.
Terakhir, static IP. Ini relatif murah juga, 0,08/bulan.
Total biaya untuk 20 devbox tersebut setidaknya sekitar 31,74/hari.
Dengan 709, hemat ~8/hari untuk hitungan 1 bulan = 30 hari. Penghematan 3,7jt IDR/bulan adalah angka yang relatif kecil, namun topik utama dari tulisan ini bukanlah tentang penghematan.
Utilization trend
Beberapa devbox ada yang normal, ada yang under dan ada yang over utilization.
Jika mengambil threshold 75% untuk status βnormalβ, gampangnya, dengan e2-standard-2
, itu berarti rata-rata hanya menggunakan 1500 mCPU dan 9 GiB memory (plus swap). Untuk under/over berarti dibawah/diatas itu.
Beberapa kali GCP (Google Cloud Provider) menyarankan untuk melakukan something yang bisa menghemat cost untuk devbox tertentu. Di lain hari, devbox tersebut malah menjadi over utilization atau bahkan menjadi normal.
Dalam sebulan, setidaknya devbox efektif digunakan selama 186 jam (31/424), yang umumnya, jika work-life balance (LOL) seharusnya hanya 62 jam (31/48). Jika ambil nilai tengah, safe to say sekitar 78 jam jika sehari mendedikasikan waktu selama 10 jam.
Problem pertama yang ingin gue coba solve adalah mencari jalan tengah untuk tidak over/under utilization, dan opsi pertama gue adalah dengan cara menjadikannya satu.
Dengan mesin e2-highmem-16
, setidaknya ada 16000 mCPU dan 192 GiB memory (plus swap) pool yang bisa digunakan. Ambil threshold 75%, berarti 12000 mCPU dan 144 GiB memory. Untuk 20 developers (1 devbox=1 developer), minimum resource yang bisa dialokasi adalah 600 mCPU dan 7,2 GiB memory. Jika rata-rata penggunaan resource **setiap developer **adalah sebagaimana yang disebutkan, seharusnya dalam 30 hari, mesin e2-highmem-16
tidak akan over/under utilization.
Maintenance nightmare
Disamping itu gue rasa tidak semua 20 developers peduli dengan maintenance VM. I mean, upaya untuk memelihara VM mereka selain melakukan penghematan sumber daya seperti CPU/memory/block storage. Ada kernel yang harus dipelihara, keamanan, dependensi, apapun. Rata-rata devbox sudah berjalan selama 4752 jam (198 hari), untuk gue pribadi yang agak sedikit rajin memelihara VM, per tulisan ini dibuat saja ada 77 updates yang harus dilakukan dan kernel gue menggunakan versi 5.15 (LTS).
Untuk keamanan, setup firewall di level VPC (Virtual Private Cloud) sudah agak membantu. Hanya inbound connection ke 22/tcp, 80/tcp, 443/tcp yang diperbolehkan, tapi untuk outbound, ya semua jenis. Ada beberapa kasus yang bisa membuat setup ini menjadi rentan, tapi kita bahas di lain waktu saja.
Selain itu, tidak ada rotation terkait SSH key.
Berdoa saja tidak ada private key yang tercuri.
RFC 1: gigantic devbox
Ambil contoh mesin e2-highmem-16
, setiap developer secara teori akan mendapatkan maksimal 800 mCPU dan 9,6 GiB memory. Untuk workload gue rasa paling banyak di I/O bound, dan maksimal CPU yang bisa digunakan dalam satu cycle anggap 75% dari total yakni 12 vCPU.
Dengan 1 gigantic devbox, berarti hanya 1 mesin dan 1 static IP. Mungkin bisa menggunakan 1 block storage juga.
Gampangnya, gue bisa hanya memelihara dan *hardening *1 mesin dan 1 IP.
Dan tentunya bisa lebih mudah untuk beriterasi mencari angka yang tepat untuk tidak over/under utilization.
Problemnya, GCP melakukan charge menggunakan model pay per use (which is good). Dilemanya, jika 1 mesin raksasa tersebut tidak pernah mati, kita perlu membayar full per bulannya. Jika mesin tersebut dimatikan secara periodic, mungkin akan mengganggu alur kerja beberapa developer.
Penghematan ~3jt tetaplah penghematan, tapi at what cost?
Challenge lain, gue perlu membuat abstraksi khususnya untuk melakukan port forwarding. Rencanannya setiap developer akan mendapatkan βcontainer khususβ, dan gue perlu melakukan port forwarding di level host. Seperti, jika container B publish port 80, gue harus make sure port 80 tersebut tidak di publish di host, tapi bisa diakses via container-b-80.box.dev
misalnya.
RFC 2: Kubernetes (uh)
Alih-alih menggunakan mesin raksasa, mungkin bisa menggunakan 25% dari mesin tersebut. Misal, mesin e2-highmem-8
yang memiliki 8 vCPU dan 64 GiB memory. Untuk 20 developer, berarti setidaknya ada 4 node yang berjalan.
Biaya untuk e2-highmem-8
adalah 708, lebih murah $1 dari yang sebelumnya lol.
Problem di Kubernetes kita tidak bisa membuat swap, tapi setidaknya kita bisa mengalokasikan 14-15 GB memory dan ~1800 mCPU per container.
Tantangannya tentu ada. Pertama, gue perlu make sureβharus bener-bener yakinβapakah container tersebut memang benar-benar sedang tidak digunakan. Misal, jika tidak ada aktivitas selama 2 jam (waktu yang relatif pas) berarti container tersebut akan dimatikan. Tantangannya, gue perlu cari tahu untuk mengetahui ada/tidak adanya aktivitas tersebut.
Kedua, akan lebih mudah bila menggunakan node pool yang spesifikasinya sama dengan devbox mereka sebelumnya, misal e2-standard-2
, alias, 1 container = 1 devbox. Ini secara teori tidak akan menganggu workload lain, dan hal-hal lain seperti port forwarding relatif lebih mudah dilakukan. Problemnya adalah jika resource yang digunakan melebihi jumlah resource yang dialokasikan, yang seharusnya hanya masalah memory (OOM thing). Mungkin dengan mesin e2-highmem-2
masalah tersebut bisa diselesaikan, tapi perbedaan antara 4 e2-highmem-8
dengan 20 e2-highmem-2
adalah ~$1000, jika hitung-hitungan gue benar.
Yang bahkan lebih besar dari biaya 1 vm = 1 developer, jika container tersebut somehow tidak pernah mati.
Tertantang jadi orang DevOps?
Different pools for different beasts
Since gue punya kontrol terhadap apa yang akan berjalan, memiliki node pool yang bervariasi mungkin bisa menjadi solusi. Misal, untuk warga frontend dan PM bisa menggunakan e2-highmem-2
dan/atau e2-highmem-4
dan sisanya menggunakan e2-standard-2
, misalnya. Ini secara teori bisa menyelesaian masalah perbedaan ~$1000 tersebut, problemnya, adalah, what if, si pool e2-standard-2
pada suatu waktu membutuhkan lebih dari 8 GiB memory?
Pertanyaan what if memang selalu menyebalkan.
Stick with Kubernetes?
Anggap gue memilih Kubernetes, dan berikut pool yang ada:
- frontend: e2-highmem-4
- pm: e2-highmem-2
- backend: e2-standard-2
Konsumsi memori backend berdasarkan yang gue lakukan tidak terlalu besar karena tidak menjalankan ehm Node.js dan/atau Cypress, but YMMV.
Setiap βdevboxβ menggunakan container yang gue maintain, dan setiap reboot, doi akan update tanpa menyentuh persistent storage yang ada. Ini gue rasa bisa menghemat space, seperti, bayangkan setiap βdevboxβ memiliki /var/lib/docker/image
dan mungkin /var/lib/docker/buildkit
dan atau /var/lib/docker/overlay2
yang sama? Problemnya perlu make sure masalah permission (e.g docker system/image prune
), tapi itu cerita lain.
Dan yang paling penting, hanya memiliki 1 IP dan tidak perlu maintain apapun yang berkaitan dengan OS dari kernel dan versi docker.
Mungkin gambarannya seperti ini:
βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ
β Ingress βββββββββββββΊβ Service βββββββββββΊβ Workload β
βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ
*.box.dev username-port.box.dev username.svc.cluster.local:port
Jadi gue harus buat proxy ketika developer:
- ssh user@username.box.dev
- Handle request dari
fariz-8080.box.dev
kefariz.svc.cluster.local:8080
Seems promising, meskipun sudah ada solusi lain dari Coder dan Gitpod misalnya.
Scheduling
Jika belum terlalu familiar dengan container orchestration seperti Kubernetes dan Nomad, pada dasarnya yang mereka lakukan adalah scheduling. Seperti, setiap 5s cek apakah perlu melakukan replica, setiap 3m apakah perlu menurunkan replica, atau bahkan sampai 0.
Dalam kasus devbox ini, ini agak menantang. Metriks nya mungkin βkapan terakhir command dijalankan oleh developer?β jika ternyata 2 jam yang lalu, maka matikan si container.
Yang paling menantang adalah untuk PM. Mungkin mereka akan ada mengubah something seperti berkas docker-compose.yml
, masalahnya, jika di kemudian waktu tidak ada yang diubah dan container dinyalakan karena ada traffic masuk, bagaimana cara mematikannya di kemudian hari? Of course bisa melihat ke traffic, misal, jika dalam 2 jam hanya menerima 120 traffic, matikan container.
Iterasi berlanjut perlu dilakukan, seperti biasa. Hadeh.
Persistent storage
Ini agak tricky juga.
Tapi yang paling aman adalah dengan menggunakan 1 container = 1 PV.
Dengan block storage 30 GB sebelumnya, setidaknya ~2.5 GB digunakan untuk OS itu sendiri (Ubuntu) dan ~500 MB untuk Docker. 10% overhead berasal dari OS.
Yang paling banyak berkontribusi adalah Docker image. Dan bila Docker image tersimpan di penyimpanan khusus, gue rasa ini bisa membantu. Sumber kode dari satu repositori per tulisan ini dibuat gue rasa adalah ~3GB, sudah termasuk .next
dan node_modules
, you know lah yang mana. Yang berarti, 30 GB per container gue rasa cukup untuk menyimpan semuanya termasuk data-data di βsub containerβ yang dijalankan. Bahkan mungkin 10-20 GB saja cukup.
Objektif & key results
Tentu saja yang ingin gue capai adalah mengurangi maintenance overhead. Setiap developer tidak perlu melakukan maintenance terhadap VM yang digunakan.
Selain itu, cost, tentu saja. Jika asumsinya sebelumnya menghabiskan $840/bulan untuk mesin (spoiler: aslinya lebih dari itu), dengan setup:
- e2-highmem-4: 4
- e2-highmem-2: 3
- e2-standard-2: 13
Setidaknya berubah menjadi $1729/bulanβlah kok makin besar?
Jika selama sebulan hanya berjalan 200 jam (10 jam/hari alias 50 jam/minggu), berarti harusnya menjadi 840/bulan khusus untuk VM. Turun sekitar 43.3% alias hemat 5,6jt yang mana mungkin bisa dialokasikan ke hal lain yang lebih produktif seperti ke salary gue misalnya hehehe (hehe he).
Selain itu bisa berkurang juga biaya lain dari persistent storage dan static IP. Concern gue sebenarnya hanya di static IP, dan ini bukan masalah cost. Memiliki lebih sedikit yang harus di maintenance adalah ultimate goals yang ingin dicapai.
Dan yang paling penting adalah produktifitas developer. Tentu langkah ini tidak menambah produktivitas developer, namun setidaknya jangan membuatnya menjadi turun.
Per hari ini, upaya gue dalam membuat developer melupakan adanya server somewhat works. Atau setidaknya, tidak ada yang resign hanya karena hal yang gue lakukan lol. Developer sudah tidak lagi perlu memikirkan terkait server di lingkungan production dan staging, atau gampangnya, tidak ada akses SSH kesana. Segala hal yang ingin dilakukan harus eksplisit, jika sebelumnya melakukan SSH hanya untuk melihat log dari container, sekarang ada cara lain untuk melakukan hal serupa.
Dari perspektif keamanan, ini mengurangi attack surface. Dengan akses SSH, bukan hanya siapapun yang memiliki akses dapat melihat log container, tapi bisa shutdown server & menghapus data production juga. Who knows.
Selain itu, ini memudahkan untuk memelihara kepercayaan untuk setiap pihak. Setiap developer yang ingin mengakses lingkungan production, harus memiliki tujuan jelas, yang kemungkinan besar hanya untuk keperluan bisnis. Setiap akses ke layanan internal yang berhubungan langsung dengan lingkungan production akan direkam dan dilindungi SSO, ini membantu untuk menjaga data pengguna karena dari perspektif keamanan, ancaman yang paling besar adalah dari internal.
Dan kita menerapkan Zero Trustβ’ untuk selalu trust but verify.
Kembali ke pembahasan, jika ini berhasil diterapkan, artinya, devbox sudah tidak perlu lagi diperlakukan sebagai VM. Yang mana seharusnya hanya do one thing and do it well: menyelesaikan pekerjaan.
Terakhir, tujuan paling penting yang ingin dicapai adalah membuat semua orang happy. Developer happy, manajemen happy, DevOps happy.
Mungkin ini akan mempersulit gue dalam membuat laporan tren utilitas mengingat sistem βon-demandβ yang diterapkan, jika RFC 1 tidak gue pilih.
Tapi tidak masalah, selagi tidak ada laporan βoverutilizedβ yang terjadi.