Mukadimah
Assalamualaikum dan salam sejahtera.
Terima kasih kerana memilih untuk membaca buku ini. Kalau anda sedang pegang buku ini, kemungkinan besar anda sudah pun berjaya membina homelab sendiri. Anda sudah tahu pasang Proxmox, deploy container dengan Docker, dan setup rangkaian asas. Tahniah, itu bukan pencapaian kecil.
Tapi sekarang, anda mungkin tertanya-tanya, “Lepas ni apa?”
Itulah soalan yang saya sendiri pernah tanya bertahun-tahun lalu.
Perjalanan Saya
Saya bermula sebagai seorang yang suka “utak-atik” server di rumah. Satu PC lama jadi hypervisor, pasang pfSense untuk firewall, buat NAS dengan TrueNAS, dan main-main dengan Docker container. Semuanya berjalan dalam homelab kecil di bilik tidur.
Dari pengalaman saya, homelab itu sebenarnya sekolah terbaik. Di situ saya belajar tentang Linux, networking, virtualization, dan containerization tanpa perlu risau kalau server crash. Kalau rosak, format dan mula semula. Tiada siapa marah, tiada downtime yang menjejaskan pelanggan.
Tapi apabila saya mula bekerja dalam industri, saya sedar ada satu jurang yang besar. Skill teknikal homelab memang berguna, tetapi dunia production jauh lebih kompleks. Bukan sekadar “benda jalan”, tetapi bagaimana nak pastikan benda itu jalan secara konsisten, boleh dipercayai, dan boleh diskala.
Di sinilah DevOps masuk.
Saya mula belajar tentang CI/CD pipeline, Infrastructure as Code, monitoring, dan automation. Saya sedar bahawa banyak konsep DevOps sebenarnya sudah saya praktikkan dalam homelab, cuma saya tak tahu namanya dan tak tahu cara buat secara profesional.
Kenapa Buku Ini Wujud
Buku ini adalah sambungan kepada panduan homelab yang saya tulis sebelum ini. Kalau buku pertama mengajar anda cara membina homelab, buku ini akan membawa anda ke langkah seterusnya, iaitu menggunakan kemahiran homelab anda untuk memulakan kerjaya dalam DevOps.
Saya tulis buku ini kerana saya percaya ramai di luar sana yang berada dalam situasi yang sama. Anda sudah ada asas yang kukuh, tetapi perlukan panduan untuk menjembatani jurang antara homelab dan dunia profesional.
Siapa Patut Baca Buku Ini
Buku ini ditulis khas untuk:
- IT professional yang sudah ada pengalaman dengan homelab dan ingin upgrade ke DevOps
- System administrator yang ingin beralih ke peranan yang lebih moden
- Developer yang ingin memahami sisi operations dan deployment
- Pelajar IT yang sudah bermain dengan homelab dan ingin tahu arah kerjaya DevOps
Saya andaikan anda sudah selesa dengan:
- Linux command line
- Docker dan konsep containerization
- Asas networking (IP, DNS, firewall)
- Virtualization (Proxmox, VirtualBox, atau seumpamanya)
Kalau anda belum selesa dengan perkara di atas, saya cadangkan baca dulu buku homelab sebelum ini.
Bagaimana Membaca Buku Ini
Setiap bab dalam buku ini dibina atas bab sebelumnya. Saya cadangkan anda membaca secara berurutan, terutamanya jika ini kali pertama anda berjinak dengan DevOps.
Setiap bab akan bermula dengan pengenalan ringkas dan senarai perkara yang anda akan belajar. Di penghujung setiap bab, ada ringkasan untuk membantu anda mengingat perkara penting.
Anda akan jumpa banyak contoh praktikal, kod, dan konfigurasi yang boleh terus digunakan. Saya juga sertakan “Nota Beginner” untuk menerangkan konsep yang mungkin baru bagi sesetengah pembaca.
Saya harap buku ini dapat membantu anda dalam perjalanan DevOps anda. Ingat, semua pakar pernah bermula sebagai beginner. Yang penting, terus belajar dan terus cuba.
Semoga berjaya.
Syafiyullah Yahya www.notainfra.com
Syarat dan Penafian
DevOps: Dari Homelab ke Production
Hak Cipta (c) 2026 Syafiyullah Yahya. Hak cipta terpelihara.
Laman web: www.notainfra.com
Tiada mana-mana bahagian dalam buku ini boleh diterbitkan semula, disimpan dalam sistem simpanan data, atau dipindahkan dalam apa jua bentuk atau cara, sama ada secara elektronik, mekanikal, fotokopi, rakaman, atau sebaliknya, tanpa kebenaran bertulis terlebih dahulu daripada penulis.
Penafian:
Buku ini ditulis untuk tujuan pendidikan dan panduan sahaja. Penulis dan penerbit tidak bertanggungjawab ke atas sebarang kerosakan, kehilangan data, atau masalah teknikal yang mungkin timbul daripada penggunaan maklumat dalam buku ini.
Semua contoh kod, konfigurasi, dan arahan dalam buku ini disediakan “seadanya” tanpa jaminan dalam apa jua bentuk. Pembaca bertanggungjawab sepenuhnya untuk menguji dan mengesahkan semua arahan sebelum menggunakannya dalam persekitaran production.
Nama produk, jenama, dan tanda dagangan yang disebut dalam buku ini adalah hak milik pemilik masing-masing dan digunakan untuk tujuan pengenalan sahaja.
Versi: 1.0 Tarikh Terbit: 2026
Ditulis dengan penuh dedikasi oleh Syafiyullah Yahya.
Untuk maklum balas, pertanyaan, atau pembetulan, sila hubungi melalui www.notainfra.com.
Bab 1: Apa Itu DevOps?
Anda mungkin pernah dengar perkataan “DevOps” berpuluh kali. Mungkin dalam iklan kerja, dalam artikel teknologi, atau dalam perbualan dengan rakan sekerja. Tapi apa sebenarnya DevOps? Adakah ia satu jawatan? Satu perisian? Satu budaya?
Jawapan ringkasnya: semua sekali. Dan dalam bab ini, kita akan kupas satu persatu supaya anda betul-betul faham apa yang anda sedang masuki.
Apa yang anda akan belajar:
- Definisi DevOps dan kenapa ia wujud
- Perbezaan antara DevOps dan IT tradisional
- Tiang-tiang utama budaya DevOps
- Peranan dan laluan kerjaya dalam DevOps
- Bagaimana kemahiran homelab anda boleh diterjemahkan ke DevOps
1.1 Definisi DevOps
DevOps adalah gabungan dua perkataan: Development dan Operations. Pada asasnya, ia adalah satu set amalan, budaya, dan alatan yang menyatukan pasukan pembangunan perisian (Dev) dan pasukan operasi IT (Ops) supaya mereka boleh bekerja bersama dengan lebih cekap.
Dalam model tradisional, developer menulis kod dan kemudian “lempar” kepada pasukan operations untuk di-deploy. Kalau ada masalah, kedua-dua pihak saling menuding jari. Developer kata “Di mesin saya jalan,” dan Ops kata “Itu bukan masalah server.”
Nota Beginner: Istilah “Dev” merujuk kepada sesiapa yang menulis kod atau membina aplikasi. “Ops” merujuk kepada sesiapa yang menguruskan server, rangkaian, dan infrastruktur. DevOps menyatukan kedua-dua dunia ini.
DevOps menyelesaikan masalah ini dengan menghapuskan “tembok” antara kedua-dua pasukan. Dalam persekitaran DevOps, semua orang berkongsi tanggungjawab dari menulis kod sehingga memastikan ia berjalan dengan baik dalam production.
Secara praktikal, DevOps melibatkan:
- Automation untuk proses berulang seperti testing dan deployment
- CI/CD pipeline untuk menghantar kod dari development ke production secara konsisten
- Infrastructure as Code (IaC) untuk menguruskan infrastruktur seperti menulis perisian
- Monitoring dan observability untuk mengesan masalah sebelum pengguna merasakannya
- Collaboration antara semua pihak yang terlibat dalam pembangunan perisian
1.2 DevOps vs IT Tradisional
Untuk benar-benar memahami DevOps, kita perlu bandingkan dengan cara kerja tradisional. Saya akan gunakan analogi yang mudah difahami.
Model Tradisional (Waterfall)
Bayangkan anda sedang membina rumah. Dalam model tradisional, prosesnya seperti ini:
- Arkitek lukis pelan (Planning)
- Tukang bina, bina rumah (Development)
- Inspektor periksa rumah (Testing)
- Pemilik terima kunci (Deployment)
Setiap langkah mesti selesai sebelum langkah seterusnya bermula. Kalau inspektor jumpa masalah struktur, tukang bina kena buat semula. Proses ini lambat dan mahal.
Dalam IT tradisional, developer menulis kod selama berbulan-bulan, kemudian serahkan kepada QA team untuk diuji, dan akhirnya Ops team deploy ke production. Keseluruhan proses boleh mengambil masa berminggu-minggu atau berbulan-bulan.
Model DevOps
Sekarang bayangkan pendekatan berbeza. Anda bina rumah satu bilik dulu, periksa, dan biarkan orang duduk. Kemudian tambah bilik lagi, periksa, dan bukakan untuk penghuni. Setiap penambahan kecil tetapi kerap.
Inilah intipati DevOps. Kod dihantar dalam bahagian kecil (small batches), diuji secara automatik, dan di-deploy dengan cepat. Kalau ada masalah, ia dikesan awal dan diperbaiki segera.
Berikut perbandingan ringkas:
| Aspek | IT Tradisional | DevOps |
|---|---|---|
| Deployment | Sebulan sekali atau kurang | Berkali-kali sehari |
| Testing | Manual, di akhir proses | Automatik, berterusan |
| Infrastruktur | Setup manual, snowflake servers | Infrastructure as Code, immutable |
| Pasukan | Dev dan Ops berasingan | Pasukan bersepadu |
| Kegagalan | Besar dan jarang | Kecil dan mudah dipulihkan |
| Maklum balas | Lambat (minggu/bulan) | Pantas (minit/jam) |
Nota Beginner: “Snowflake server” bermaksud server yang dikonfigurasi secara manual sehingga setiap satu unik seperti kepingan salji. Ini menyukarkan penyelenggaraan. DevOps menggantikan ini dengan server yang boleh dibina semula secara automatik menggunakan kod.
1.3 Budaya DevOps
DevOps bukan sekadar alatan. Saya ulang: DevOps bukan sekadar alatan. Ramai orang tersalah faham bahawa dengan memasang Jenkins atau menggunakan Kubernetes, mereka sudah “buat DevOps.” Itu tidak benar.
DevOps pada terasnya adalah satu budaya. Alatan hanyalah pemudah cara. Tanpa budaya yang betul, alatan terbaik dunia pun tidak akan membantu.
Mari kita lihat tiang-tiang utama budaya DevOps.
Collaboration (Kerjasama)
Dalam budaya DevOps, tembok antara pasukan diruntuhkan. Developer, Ops, QA, Security, semuanya bekerja bersama dari awal projek. Bukan lagi “saya buat bahagian saya, anda buat bahagian anda.”
Dari pengalaman saya, perubahan budaya ini selalunya yang paling susah. Teknikal boleh dipelajari dalam beberapa bulan, tetapi mengubah cara orang bekerja bersama boleh mengambil masa bertahun-tahun.
Automation (Automasi)
Kalau sesuatu tugas perlu dilakukan lebih dari sekali, ia patut diautomasi. Ini adalah prinsip asas DevOps.
Anda mungkin sudah mengamalkan ini dalam homelab anda. Mungkin anda tulis script Bash untuk backup, atau guna Docker Compose supaya tak perlu taip arahan Docker yang panjang setiap kali. Itu sudah automation.
Dalam dunia DevOps profesional, automation dibawa ke tahap seterusnya:
- Build automation: Kod dikompil secara automatik setiap kali ada perubahan
- Test automation: Ujian dijalankan secara automatik tanpa campur tangan manusia
- Deployment automation: Aplikasi di-deploy ke production secara automatik selepas lulus ujian
- Infrastructure automation: Server dan rangkaian dibina menggunakan kod
CI/CD (Continuous Integration / Continuous Delivery)
CI/CD adalah nadi DevOps. Ia adalah amalan menghantar perubahan kod secara kerap dan automatik.
Continuous Integration (CI) bermaksud setiap developer menggabungkan (merge) kod mereka ke repository utama secara kerap, biasanya beberapa kali sehari. Setiap penggabungan mencetuskan automated build dan test untuk mengesan masalah awal.
Continuous Delivery (CD) bermaksud kod yang telah lulus semua ujian sentiasa dalam keadaan sedia untuk di-deploy ke production. Deployment boleh dilakukan pada bila-bila masa dengan satu klik butang.
Continuous Deployment (juga CD) pergi satu langkah lebih jauh. Setiap perubahan yang lulus ujian akan secara automatik di-deploy ke production tanpa campur tangan manusia.
Nota Beginner: Jangan keliru antara Continuous Delivery dan Continuous Deployment. Delivery bermaksud kod sedia untuk di-deploy (tetapi manusia masih klik butang). Deployment bermaksud ia secara automatik di-deploy. Kebanyakan organisasi bermula dengan Delivery sebelum beralih ke Deployment.
Monitoring dan Observability
Dalam DevOps, kerja anda tidak tamat selepas deploy. Anda perlu sentiasa memantau aplikasi dan infrastruktur untuk memastikan semuanya berjalan lancar.
Monitoring bermaksud mengumpul data seperti penggunaan CPU, memori, masa tindak balas (response time), dan kadar ralat (error rate). Observability pergi lebih jauh dengan memberikan anda keupayaan untuk memahami keadaan dalaman sistem berdasarkan data luaran.
Kalau anda pernah guna Grafana atau Prometheus dalam homelab, anda sudah ada asas yang baik. Dalam production, monitoring menjadi lebih kritikal kerana downtime boleh menjejaskan ribuan pengguna dan menyebabkan kerugian wang.
Infrastructure as Code (IaC)
IaC bermaksud menguruskan infrastruktur (server, rangkaian, firewall) menggunakan fail konfigurasi yang boleh disimpan dalam version control. Ini bermaksud anda boleh membina semula seluruh infrastruktur anda dari kosong hanya dengan menjalankan satu arahan.
Alatan popular untuk IaC termasuk Terraform, Ansible, dan Pulumi. Kalau anda pernah guna Docker Compose atau menulis Ansible playbook untuk homelab, anda sudah mula dengan IaC tanpa menyedarinya.
1.4 Peranan dan Laluan Kerjaya DevOps
Salah satu soalan yang sering ditanya ialah, “Apa sebenarnya kerja seorang DevOps engineer?” Jawapannya bergantung kepada organisasi, tetapi secara umum, terdapat beberapa peranan utama dalam ekosistem DevOps.
DevOps Engineer
Ini adalah peranan yang paling umum. DevOps engineer bertanggungjawab membina dan menyelenggara CI/CD pipeline, mengurus infrastruktur menggunakan IaC, dan memastikan proses deployment berjalan lancar.
Kemahiran utama yang diperlukan:
- Linux administration
- Scripting (Bash, Python)
- CI/CD tools (Jenkins, GitLab CI, GitHub Actions)
- Containerization (Docker, Podman)
- Container orchestration (Kubernetes)
- IaC tools (Terraform, Ansible)
- Cloud platforms (AWS, Azure, GCP)
Site Reliability Engineer (SRE)
SRE adalah pendekatan Google terhadap DevOps. SRE memberi tumpuan kepada kebolehpercayaan (reliability) sistem. Mereka menetapkan Service Level Objectives (SLO) dan menggunakan error budget untuk mengimbangi antara kelajuan pembangunan dan kestabilan sistem.
Platform Engineer
Platform engineer membina dan menyelenggara platform dalaman yang memudahkan developer melakukan deployment sendiri. Mereka membina “jalan raya” supaya developer boleh fokus memandu tanpa perlu risau tentang infrastruktur.
Cloud Engineer
Cloud engineer fokus kepada pengurusan infrastruktur dalam persekitaran cloud. Mereka mereka bentuk, membina, dan mengoptimumkan sumber cloud untuk prestasi dan kos.
Security Engineer (DevSecOps)
Dalam DevSecOps, keselamatan diintegrasikan ke dalam setiap peringkat pipeline DevOps. Security engineer memastikan kod dan infrastruktur mematuhi piawaian keselamatan secara automatik.
Nota Beginner: Anda tidak perlu kuasai semua peranan ini. Pilih satu yang menarik minat anda dan fokus di situ. Kebanyakan profesional bermula sebagai DevOps engineer sebelum mengkhusus dalam bidang tertentu.
1.5 Kemahiran Homelab Anda Sudah Relevan
Ini bahagian yang saya paling suka. Kalau anda sudah ada pengalaman homelab, anda sebenarnya sudah ada banyak kemahiran yang diperlukan dalam DevOps. Anda cuma perlu tahu cara menterjemahkannya.
Mari kita lihat:
| Kemahiran Homelab | Terjemahan DevOps |
|---|---|
| Pasang Proxmox, buat VM | Virtualization, cloud computing |
| Guna Docker, Docker Compose | Containerization, container orchestration |
| Setup pfSense/OPNsense | Network engineering, firewall management |
| Tulis script Bash untuk automation | Scripting, automation |
| Pasang Grafana + Prometheus | Monitoring, observability |
| Setup Nginx reverse proxy | Load balancing, ingress management |
| Backup dan restore | Disaster recovery, business continuity |
| Troubleshoot masalah rangkaian | Incident response, debugging |
| Guna SSH untuk remote access | Remote administration, security |
| Self-host Gitea | Version control, Git workflows |
Nampak tak? Anda sudah ada asas yang kukuh. Yang anda perlukan sekarang adalah untuk memformalkan kemahiran ini dan belajar cara menggunakannya dalam konteks profesional.
Dari pengalaman saya, orang yang datang dari latar belakang homelab selalunya lebih cepat memahami konsep DevOps berbanding orang yang belajar teori sahaja. Ini kerana anda sudah “kotor tangan” dan tahu perasaan bila server crash tengah malam.
Perbezaan utama antara homelab dan production ialah skala, kebolehpercayaan, dan kerjasama pasukan. Dalam homelab, kalau server mati, anda sahaja yang terjejas. Dalam production, ribuan pengguna mungkin terkesan. Dalam homelab, anda bekerja seorang diri. Dalam production, anda bekerja dalam pasukan.
Tetapi jangan risau. Buku ini akan membimbing anda melalui peralihan ini langkah demi langkah. Kita akan bermula dengan asas yang paling penting dalam DevOps, iaitu version control dengan Git, dan kemudian bergerak ke CI/CD, containerization, IaC, dan seterusnya.
1.6 Peralatan Yang Anda Perlukan
Sebelum kita mula bab seterusnya, pastikan anda mempunyai perkara berikut:
- Komputer dengan Linux (atau WSL2 pada Windows). Homelab anda boleh digunakan.
- Git dipasang pada mesin anda.
- Docker dipasang dan berjalan.
- Akaun GitHub atau GitLab (atau Gitea dalam homelab anda).
- Text editor yang anda selesa, sama ada VS Code, Vim, atau Nano.
Untuk memasang Git dan Docker pada Ubuntu/Debian:
# Pasang Git
sudo apt update
sudo apt install git -y
# Sahkan pemasangan
git --version
# Pasang Docker
sudo apt install docker.io -y
sudo systemctl enable docker
sudo systemctl start docker
# Tambah user anda ke docker group
sudo usermod -aG docker $USERNota Beginner: Kalau anda guna homelab, anda boleh buat satu VM khas untuk belajar DevOps. Saya cadangkan Ubuntu Server 22.04 LTS atau lebih baru. Beri sekurang-kurangnya 2 CPU, 4GB RAM, dan 40GB disk.
Selepas pasang, konfigurasikan Git dengan identiti anda:
git config --global user.name "Nama Anda"
git config --global user.email "email@anda.com"Senarai DevOps Tools dan Fungsinya
Sebelum kita masuk ke bab seterusnya, saya nak bagi gambaran besar tentang tools yang akan kita guna sepanjang ebook ini. Tak perlu hafal semua sekarang. Anggap ini sebagai rujukan pantas yang boleh anda kembali bila-bila masa.
Source Control (Urus Kod)
| Tool | Fungsi | Nota |
|---|---|---|
| Git | Version control system untuk track perubahan kod | Wajib tahu. Asas segala-galanya dalam DevOps. |
| GitHub | Platform hosting Git dengan CI/CD, issues, PR | Paling popular. Free untuk projek peribadi. |
| GitLab | Alternatif GitHub dengan CI/CD terbina dalam | Boleh self-host. Sesuai untuk organisasi. |
| Gitea | Self-hosted Git server yang ringan | Sesuai untuk homelab. Kita dah setup dalam ebook pertama. |
CI/CD (Automate Build, Test, Deploy)
| Tool | Fungsi | Nota |
|---|---|---|
| GitHub Actions | CI/CD terbina dalam GitHub | Percuma untuk repo public. Mudah bermula. |
| GitLab CI | CI/CD terbina dalam GitLab | Konfigurasi via .gitlab-ci.yml. |
| Jenkins | CI/CD server yang paling matang | Self-hosted. Sangat fleksibel tapi agak kompleks. |
| ArgoCD | GitOps continuous delivery untuk Kubernetes | Auto-sync dari Git ke K8s cluster. |
| Drone CI | CI/CD ringan berasaskan container | Setiap step jalan dalam container sendiri. |
Containerization (Pakej Aplikasi)
| Tool | Fungsi | Nota |
|---|---|---|
| Docker | Bina dan jalankan container | Standard industri. Anda dah biasa dari homelab. |
| Docker Compose | Urus multi-container dengan YAML | Sesuai untuk development dan homelab. |
| Podman | Alternatif Docker tanpa daemon | Rootless by default. Serasi dengan Docker CLI. |
| containerd | Container runtime untuk production | Digunakan oleh Kubernetes secara dalaman. |
Orchestration (Urus Container Berskala)
| Tool | Fungsi | Nota |
|---|---|---|
| Kubernetes (K8s) | Orkestrasi container untuk production | Standard industri. Kita guna K3s untuk belajar. |
| K3s | Kubernetes ringan untuk edge dan homelab | Kurang dari 100MB. Sesuai untuk bermula. |
| Helm | Package manager untuk Kubernetes | Macam apt tapi untuk K8s deployments. |
| Docker Swarm | Orkestrasi Docker yang lebih simple | Lebih mudah dari K8s tapi kurang ciri. |
Infrastructure as Code (Provision dan Configure)
| Tool | Fungsi | Nota |
|---|---|---|
| Terraform | Provision infrastruktur secara deklaratif | Cipta VM, network, cloud resources dari kod. |
| Ansible | Configuration management dan automasi | Agentless. Guna SSH. Mudah dipelajari. |
| Pulumi | IaC menggunakan bahasa pengaturcaraan sebenar | Alternatif Terraform untuk developer. |
| Packer | Bina machine image secara automatik | Cipta template VM atau AMI. |
Monitoring (Pantau Kesihatan Sistem)
| Tool | Fungsi | Nota |
|---|---|---|
| Prometheus | Kumpul dan simpan metrik masa nyata | Standard untuk monitoring dalam K8s. |
| Grafana | Visualisasi data dalam dashboard cantik | Boleh connect dengan pelbagai data source. |
| Uptime Kuma | Pantau uptime perkhidmatan | Ringan. Kita dah guna dalam homelab. |
| Datadog | Platform monitoring all-in-one (SaaS) | Mahal tapi sangat berkuasa. |
| Zabbix | Monitoring enterprise, self-hosted | Percuma. Agak kompleks untuk bermula. |
Logging (Kumpul dan Analisa Log)
| Tool | Fungsi | Nota |
|---|---|---|
| Loki | Sistem agregasi log oleh Grafana Labs | Ringan. Integrates baik dengan Grafana. |
| Promtail | Pengumpul log untuk Loki | Hantar log dari server ke Loki. |
| ELK Stack | Elasticsearch + Logstash + Kibana | Berkuasa tapi berat. Perlukan banyak RAM. |
| Fluentd | Pengumpul log universal | Sokongan banyak output destinations. |
Security (Keselamatan Pipeline)
| Tool | Fungsi | Nota |
|---|---|---|
| Trivy | Imbas kerentanan container image | Percuma, cepat, mudah integrate dalam CI/CD. |
| SonarQube | Analisis kualiti dan keselamatan kod (SAST) | Self-hosted. Sokong banyak bahasa. |
| OWASP ZAP | Imbas kerentanan aplikasi web (DAST) | Percuma. Boleh automate dalam pipeline. |
| HashiCorp Vault | Urus secrets dan credentials | Simpan API keys, password dengan selamat. |
| Snyk | Imbas kerentanan dalam dependencies | Integrate dengan GitHub. Free tier tersedia. |
Cloud Providers
| Provider | Kelebihan | Sesuai Untuk |
|---|---|---|
| AWS | Paling banyak servis, paling besar pasaran | Enterprise, persijilan, kerjaya |
| Google Cloud | Kuat dalam data dan Kubernetes (GKE) | K8s, machine learning |
| Azure | Integrasi Microsoft, hybrid cloud | Organisasi guna Microsoft stack |
| DigitalOcean | Simple, murah, developer-friendly | Projek kecil, belajar cloud |
| Hetzner | Sangat murah, prestasi baik | VPS bajet, dedicated servers |
Testing (Uji Kualiti)
| Tool | Fungsi | Nota |
|---|---|---|
| Jest | Testing framework untuk JavaScript | Paling popular untuk Node.js. |
| PyTest | Testing framework untuk Python | Simple dan berkuasa. |
| Selenium / Playwright | End-to-end browser testing | Automate ujian UI web. |
| k6 | Load testing dan performance testing | Tulis test dalam JavaScript. |
| Postman / Newman | API testing | Newman untuk automate dalam CI/CD. |
Nota Beginner: Jangan rasa overwhelmed tengok senarai ni. Anda tak perlu belajar semua sekarang. Dalam ebook ini, kita akan fokus kepada tools yang paling penting dan praktikal. Mulakan dengan Git, Docker, dan GitHub Actions, kemudian tambah satu persatu mengikut keperluan anda.
Tools Mana Nak Mula Dulu?
Kalau anda baru nak bermula, ini susunan yang saya cadangkan:
- Git + GitHub (Bab 2). Ini asas. Tanpa ini, semua yang lain tak boleh berfungsi.
- Docker (Bab 4). Anda dah biasa dari homelab, sekarang bawa ke level production.
- GitHub Actions (Bab 3). Automate deployment pertama anda.
- Ansible (Bab 6). Automate server configuration.
- Prometheus + Grafana (Bab 7). Pantau apa yang anda deploy.
- Kubernetes (Bab 5). Bila anda dah selesa dengan semua di atas.
Selebihnya, tambah mengikut keperluan projek anda.
Ringkasan
Dalam bab ini, kita telah belajar:
- DevOps ialah gabungan budaya, amalan, dan alatan yang menyatukan development dan operations
- DevOps bukan sekadar alatan, ia adalah budaya yang menekankan kerjasama, automasi, dan penambahbaikan berterusan
- Tiang utama DevOps termasuk CI/CD, automation, monitoring, dan Infrastructure as Code
- Terdapat pelbagai laluan kerjaya dalam DevOps termasuk DevOps engineer, SRE, platform engineer, dan lain-lain
- Kemahiran homelab anda sudah relevan dan boleh diterjemahkan terus ke dunia DevOps profesional
Anda sudah ada asas yang kukuh dari pengalaman homelab anda. Sekarang tiba masanya untuk membina di atas asas itu. Dalam bab seterusnya, kita akan mula dengan kemahiran yang paling fundamental dalam DevOps: Git dan version control.
Jom teruskan perjalanan ini bersama.
Bab 2: Git dan Version Control
Kalau DevOps ada satu kemahiran yang anda wajib kuasai sebelum yang lain, ia adalah Git. Serius, tanpa Git, anda tidak boleh melangkah lebih jauh dalam dunia DevOps. Setiap CI/CD pipeline bermula dengan Git. Setiap Infrastructure as Code disimpan dalam Git. Setiap kerjasama pasukan bergantung kepada Git.
Berita baiknya, Git tidak susah. Ia cuma perlu masa untuk menjadi selesa. Dan kalau anda pernah self-host Gitea dalam homelab anda, anda sudah pun berjinak dengannya.
Apa yang anda akan belajar:
- Kenapa Git penting dalam DevOps
- Arahan asas Git yang anda perlukan setiap hari
- Strategi branching untuk pasukan (GitFlow dan trunk-based)
- Pull request dan code review
- Platform Git hosting (GitHub, GitLab, Gitea)
- Penggunaan
.gitignoredan Git hooks - Workflow praktikal yang boleh terus digunakan
2.1 Kenapa Git Penting
Sebelum Git, developer menyimpan kod dengan cara yang mengerikan. Ada yang guna folder bernama projek_v1, projek_v2, projek_v2_final, projek_v2_final_BETUL. Ada yang email fail zip kepada rakan sekerja. Ada yang guna shared drive dan berdoa tiada siapa tulis ganti fail mereka.
Git menyelesaikan semua masalah ini. Ia adalah sistem version control yang menjejaki setiap perubahan pada kod anda. Setiap perubahan dicatat dengan siapa yang buat, bila, dan kenapa. Anda boleh kembali ke mana-mana versi terdahulu pada bila-bila masa.
Dalam konteks DevOps, Git bukan sekadar tempat simpan kod. Ia adalah:
- Sumber kebenaran tunggal (single source of truth) untuk semua kod dan konfigurasi
- Pencetus (trigger) untuk CI/CD pipeline. Bila anda push kod, pipeline bermula
- Alat kerjasama yang membolehkan ramai orang bekerja pada kod yang sama tanpa konflik
- Audit trail yang merekodkan siapa ubah apa dan bila
Nota Beginner: Version control bukan hanya untuk developer. Sebagai DevOps engineer, anda akan simpan Terraform files, Ansible playbooks, Dockerfile, Kubernetes manifests, dan pelbagai konfigurasi dalam Git. Kalau ia boleh ditulis sebagai teks, ia patut disimpan dalam Git.
2.2 Arahan Asas Git
Mari kita mula dengan arahan yang anda akan guna setiap hari. Saya andaikan anda sudah pasang Git seperti yang ditunjukkan dalam bab sebelumnya.
Mencipta Repository Baru
# Buat direktori baru untuk projek
mkdir projek-devops
cd projek-devops
# Inisialisasi Git repository
git init
# Anda akan nampak mesej:
# Initialized empty Git repository in /home/user/projek-devops/.git/Arahan git init mencipta folder .git tersembunyi yang menyimpan semua sejarah dan metadata repository anda. Jangan sesekali padam folder ini secara manual.
Aliran Kerja Asas: Add, Commit, Push
Inilah aliran kerja Git yang paling asas. Anda akan ulang proses ini berpuluh kali sehari.
# 1. Buat atau ubah fail
echo "# Projek DevOps Pertama" > README.md
# 2. Semak status - lihat apa yang berubah
git status
# Output akan tunjukkan README.md sebagai "untracked file"
# 3. Tambah fail ke staging area
git add README.md
# 4. Commit perubahan dengan mesej yang bermakna
git commit -m "Tambah README untuk projek"
# 5. (Selepas setup remote) Push ke server
git push origin mainMari kita fahamkan setiap langkah:
git status menunjukkan keadaan semasa working directory anda. Fail boleh berada dalam tiga keadaan: untracked (Git belum jejak), modified (sudah diubah tetapi belum staged), atau staged (sedia untuk commit).
git add memindahkan fail dari working directory ke staging area. Staging area adalah seperti “ruang menunggu” sebelum perubahan disimpan secara rasmi.
git commit menyimpan snapshot semua perubahan dalam staging area ke dalam sejarah Git. Setiap commit mempunyai ID unik (hash), mesej, penulis, dan cap masa.
git push menghantar commit tempatan anda ke repository remote (seperti GitHub atau GitLab).
Nota Beginner: Fikirkan Git seperti sistem “save game.”
git addialah memilih apa yang nak disimpan.git commitialah menekan butang save.git pushialah upload save file ke cloud supaya tidak hilang.
Melihat Sejarah
# Lihat senarai commit
git log
# Lihat log dalam format ringkas (satu baris per commit)
git log --oneline
# Lihat log dengan graf visual untuk branches
git log --oneline --graph --all
# Lihat perubahan yang belum di-commit
git diff
# Lihat perubahan yang sudah di-stage
git diff --stagedClone Repository Sedia Ada
Kebanyakan masa, anda tidak akan mula dari kosong. Anda akan clone repository yang sudah wujud.
# Clone dari GitHub
git clone https://github.com/username/repo-name.git
# Clone dari Gitea homelab anda
git clone https://gitea.homelab.local/username/repo-name.git
# Clone dan beri nama direktori berbeza
git clone https://github.com/username/repo-name.git nama-baruPull Perubahan Terkini
Sebelum mula bekerja setiap hari, pastikan anda pull perubahan terkini dari remote.
# Pull perubahan terkini
git pull origin main
# Atau kalau anda sudah set upstream
git pullSaya cadangkan jadikan ini tabiat pertama setiap pagi sebelum mula menulis kod. Ini mengelakkan konflik yang tidak perlu.
2.3 Branching: Bekerja Secara Selari
Branching adalah salah satu ciri paling berkuasa dalam Git. Ia membolehkan anda bekerja pada ciri baru atau pembetulan pepijat tanpa mengganggu kod utama.
Bayangkan kod utama anda seperti jalan raya utama. Branch adalah jalan keluar sementara. Anda keluar dari jalan utama, buat kerja anda, dan apabila siap, anda masuk semula ke jalan utama.
# Lihat semua branch
git branch
# Buat branch baru
git branch feature/tambah-login
# Tukar ke branch baru
git checkout feature/tambah-login
# Atau buat dan tukar sekaligus (cara lebih ringkas)
git checkout -b feature/tambah-login
# Selepas siap, kembali ke main
git checkout main
# Gabungkan branch ke main
git merge feature/tambah-login
# Padam branch yang sudah digabung
git branch -d feature/tambah-loginNota Beginner: Penamaan branch yang baik sangat penting. Gunakan format seperti
feature/nama-ciri,bugfix/nama-pepijat, atauhotfix/nama-pembetulan. Ini memudahkan semua orang dalam pasukan memahami tujuan setiap branch.
2.4 Strategi Branching
Apabila anda bekerja dalam pasukan, anda perlukan strategi branching yang jelas supaya semua orang tahu cara bekerja bersama. Dua strategi yang paling popular ialah GitFlow dan trunk-based development.
GitFlow
GitFlow adalah strategi yang lebih berstruktur. Ia menggunakan beberapa jenis branch:
main(ataumaster): Kod production yang stabildevelop: Branch integrasi untuk ciri-ciri barufeature/*: Branch untuk setiap ciri barurelease/*: Branch untuk menyediakan release baruhotfix/*: Branch untuk pembetulan segera di production
main ──────●──────────────────●──────────── (production)
\ /
develop ─────●───●───●───●──● ───────────── (integration)
\ / \ /
feature/a ─────●───● \ /
feature/b ──●───●
Aliran kerja GitFlow:
# Mula ciri baru dari develop
git checkout develop
git checkout -b feature/user-auth
# Buat kerja, commit...
git add .
git commit -m "Tambah fungsi login"
# Selesai, gabung balik ke develop
git checkout develop
git merge feature/user-auth
# Sedia untuk release
git checkout -b release/1.0.0
# Buat testing, fix bugs...
# Deploy ke production
git checkout main
git merge release/1.0.0
git tag v1.0.0GitFlow sesuai untuk projek yang mempunyai jadual release yang tetap, contohnya release sebulan sekali atau setiap sprint.
Trunk-Based Development
Trunk-based development adalah pendekatan yang lebih ringkas dan digemari dalam budaya DevOps moden. Semua developer bekerja pada satu branch utama (trunk), biasanya main.
main ──●──●──●──●──●──●──●──●──●── (semua kerja di sini)
\ / \ /
short-lived short-lived
branch branch
Prinsip utama:
- Branch hidup tidak lebih dari satu atau dua hari
- Perubahan dibuat dalam bahagian kecil
- Feature flags digunakan untuk menyembunyikan ciri yang belum siap
- CI/CD pipeline berjalan pada setiap commit ke main
# Buat branch pendek untuk satu tugas kecil
git checkout -b fix/typo-readme
# Buat perubahan kecil
git add .
git commit -m "Betulkan typo dalam README"
# Push dan buat pull request
git push origin fix/typo-readme
# Selepas di-review dan di-merge, padam branch
git branch -d fix/typo-readmeNota Beginner: Kalau anda baru bermula, saya cadangkan trunk-based development. Ia lebih mudah difahami dan lebih sesuai dengan amalan CI/CD. GitFlow cenderung menjadi terlalu kompleks untuk pasukan kecil.
Dari pengalaman saya, kebanyakan pasukan DevOps yang matang menggunakan trunk-based development kerana ia menggalakkan perubahan kecil dan kerap, yang sememangnya nadi DevOps.
2.5 Pull Request dan Code Review
Pull request (PR) atau merge request (MR) adalah mekanisme untuk meminta rakan sepasukan menyemak kod anda sebelum ia digabungkan ke branch utama. Ini adalah amalan penting dalam DevOps kerana ia:
- Menangkap pepijat awal sebelum masuk ke production
- Berkongsi pengetahuan dalam pasukan
- Mengekalkan kualiti kod yang konsisten
- Mencipta dokumentasi tentang kenapa perubahan dibuat
Aliran Kerja Pull Request
# 1. Buat branch untuk tugasan anda
git checkout -b feature/tambah-health-check
# 2. Buat perubahan dan commit
# Katakan anda tambah health check endpoint
git add .
git commit -m "Tambah /health endpoint untuk monitoring"
# 3. Push branch ke remote
git push origin feature/tambah-health-check
# 4. Buat pull request melalui GitHub/GitLab/Gitea
# Biasanya melalui web interface, atau guna CLI:
# Untuk GitHub CLI
gh pr create --title "Tambah health check endpoint" \
--body "Menambah /health endpoint supaya monitoring tools boleh periksa status aplikasi"
# Untuk GitLab CLI
glab mr create --title "Tambah health check endpoint" \
--description "Menambah /health endpoint untuk monitoring"Tips Code Review Yang Baik
Sebagai reviewer, fokus pada perkara berikut:
- Logik kod: Adakah kod ini betul? Ada edge case yang terlepas?
- Keselamatan: Ada credential yang terdedah? Input yang tidak disahkan?
- Kebolehbacaan: Bolehkah orang lain faham kod ini tanpa penjelasan?
- Testing: Ada ujian untuk perubahan ini?
- Dokumentasi: Perlu ke kemaskini dokumentasi?
Sebagai penulis PR, bantu reviewer anda:
- Tulis deskripsi yang jelas tentang apa dan kenapa
- Pastikan PR bersaiz kecil (kurang 400 baris kalau boleh)
- Sertakan screenshot kalau ada perubahan UI
- Jalankan semua ujian sebelum minta review
2.6 Platform Git Hosting
Anda perlukan tempat untuk menyimpan repository Git anda secara remote. Berikut adalah pilihan utama.
GitHub
Platform paling popular. Percuma untuk repository awam dan peribadi. Dilengkapi dengan GitHub Actions untuk CI/CD.
# Setup SSH key untuk GitHub
ssh-keygen -t ed25519 -C "email@anda.com"
cat ~/.ssh/id_ed25519.pub
# Salin dan tampal ke GitHub Settings > SSH Keys
# Uji sambungan
ssh -T git@github.comGitLab
Platform lengkap dengan CI/CD terbina dalam. Boleh di-self-host. Sangat popular dalam organisasi enterprise.
Gitea (Dari Homelab Anda)
Kalau anda sudah self-host Gitea dalam homelab, tahniah. Anda sudah ada platform Git hosting sendiri. Ini sangat bagus untuk belajar kerana anda mempunyai kawalan penuh.
# Clone dari Gitea homelab
git clone git@gitea.homelab.local:username/projek.git
# Tambah remote untuk GitHub sebagai backup
git remote add github git@github.com:username/projek.git
# Push ke kedua-dua remote
git push origin main
git push github mainNota Beginner: Saya cadangkan anda guna GitHub untuk projek awam dan portfolio, dan Gitea dalam homelab untuk eksperimen peribadi. Mempunyai profil GitHub yang aktif adalah aset besar apabila memohon kerja DevOps.
2.7 Fail .gitignore
Fail .gitignore memberitahu Git fail mana yang tidak perlu dijejaki. Ini sangat penting kerana anda tidak mahu commit perkara seperti password, API key, atau fail yang dijana secara automatik.
Buat fail .gitignore di root repository anda:
# Buat .gitignore
cat > .gitignore << 'EOF'
# Fail persekitaran dan rahsia
.env
.env.local
.env.production
*.pem
*.key
credentials.json
# Fail sistem operasi
.DS_Store
Thumbs.db
# Dependencies
node_modules/
vendor/
__pycache__/
*.pyc
# Fail yang dijana
*.log
*.tmp
dist/
build/
*.o
*.class
# Fail IDE
.vscode/
.idea/
*.swp
*.swo
# Fail Docker yang tidak perlu
docker-compose.override.yml
# Terraform state (mengandungi maklumat sensitif)
*.tfstate
*.tfstate.backup
.terraform/
EOFIni adalah antara kesilapan yang paling biasa saya nampak. Developer baru sering terlupa untuk setup .gitignore dan akhirnya commit fail .env yang mengandungi password database ke GitHub. Kalau ini berlaku, anggap password itu sudah terdedah dan tukar segera, walaupun anda padam fail itu kemudian. Sejarah Git menyimpan segala-galanya.
Nota Beginner: Kalau anda sudah tersilap commit fail sensitif, gunakan
git filter-branchatau alat seperti BFG Repo Cleaner untuk membuangnya dari sejarah. Tetapi langkah pertama tetap menukar semua credential yang terdedah.
2.8 Git Hooks
Git hooks adalah script yang berjalan secara automatik pada titik-titik tertentu dalam aliran kerja Git. Ia adalah bentuk automation yang paling asas dan sangat berguna.
Hooks disimpan dalam folder .git/hooks/. Berikut adalah beberapa hooks yang berguna.
Pre-commit Hook
Berjalan sebelum commit dibuat. Sesuai untuk memeriksa kualiti kod.
# Buat pre-commit hook
cat > .git/hooks/pre-commit << 'HOOK'
#!/bin/bash
echo "Menjalankan pemeriksaan sebelum commit..."
# Semak kalau ada fail .env yang cuba di-commit
if git diff --cached --name-only | grep -q '\.env'; then
echo "AMARAN: Anda cuba commit fail .env!"
echo "Sila keluarkan fail .env dari staging area."
exit 1
fi
# Semak kalau ada TODO yang tertinggal
if git diff --cached | grep -q 'TODO'; then
echo "AMARAN: Terdapat TODO dalam kod anda."
echo "Pastikan ini disengajakan."
# Tidak block commit, hanya amaran
fi
echo "Pemeriksaan selesai. Meneruskan commit."
HOOK
# Jadikan boleh dilaksanakan
chmod +x .git/hooks/pre-commitCommit-msg Hook
Berjalan selepas mesej commit ditulis. Sesuai untuk memastikan format mesej commit konsisten.
# Buat commit-msg hook
cat > .git/hooks/commit-msg << 'HOOK'
#!/bin/bash
commit_msg=$(cat "$1")
# Pastikan mesej commit bermula dengan huruf besar
if ! echo "$commit_msg" | head -1 | grep -qE '^[A-Z]'; then
echo "RALAT: Mesej commit mesti bermula dengan huruf besar."
echo "Contoh: 'Tambah fungsi login' bukan 'tambah fungsi login'"
exit 1
fi
# Pastikan mesej commit sekurang-kurangnya 10 aksara
if [ ${#commit_msg} -lt 10 ]; then
echo "RALAT: Mesej commit terlalu pendek (minimum 10 aksara)."
exit 1
fi
HOOK
chmod +x .git/hooks/commit-msgNota Beginner: Hooks dalam folder
.git/hooks/tidak dikongsi melalui Git. Untuk berkongsi hooks dengan pasukan, simpan mereka dalam folder sepertiscripts/hooks/dan buat setup script yang mencipta symlink. Atau lebih baik lagi, gunakan alat sepertipre-commitframework.
Berkongsi Hooks Dengan Pasukan
Cara yang lebih praktikal untuk menguruskan hooks dalam pasukan:
# Simpan hooks dalam repo
mkdir -p scripts/hooks
# Pindahkan hooks ke folder yang dijejak Git
cp .git/hooks/pre-commit scripts/hooks/pre-commit
cp .git/hooks/commit-msg scripts/hooks/commit-msg
# Buat setup script
cat > scripts/setup-hooks.sh << 'SETUP'
#!/bin/bash
echo "Memasang Git hooks..."
HOOK_DIR=".git/hooks"
SCRIPT_DIR="scripts/hooks"
for hook in "$SCRIPT_DIR"/*; do
hook_name=$(basename "$hook")
ln -sf "../../$SCRIPT_DIR/$hook_name" "$HOOK_DIR/$hook_name"
echo " Dipasang: $hook_name"
done
echo "Semua hooks telah dipasang."
SETUP
chmod +x scripts/setup-hooks.sh
# Setiap ahli pasukan hanya perlu jalankan:
# ./scripts/setup-hooks.sh2.9 Workflow Praktikal: Dari Mula Hingga Akhir
Mari kita gabungkan semua yang telah kita belajar dalam satu contoh workflow yang lengkap. Katakan anda bekerja dalam pasukan dan perlu menambah ciri health check pada aplikasi.
# 1. Pastikan anda berada di branch main dan up to date
git checkout main
git pull origin main
# 2. Buat branch baru untuk tugasan
git checkout -b feature/health-check
# 3. Buat perubahan
cat > healthcheck.py << 'EOF'
from flask import Flask, jsonify
import datetime
app = Flask(__name__)
@app.route('/health')
def health():
return jsonify({
"status": "healthy",
"timestamp": datetime.datetime.now().isoformat(),
"version": "1.0.0"
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
EOF
# 4. Semak apa yang berubah
git status
git diff
# 5. Stage dan commit
git add healthcheck.py
git commit -m "Tambah health check endpoint untuk monitoring"
# 6. Push ke remote
git push origin feature/health-check
# 7. Buat pull request (guna GitHub CLI)
gh pr create \
--title "Tambah health check endpoint" \
--body "## Perubahan
- Tambah /health endpoint yang mengembalikan status, timestamp, dan versi
- Endpoint ini akan digunakan oleh monitoring system
## Cara Uji
1. Jalankan: python healthcheck.py
2. Buka: http://localhost:5000/health
3. Pastikan response JSON dikembalikan"
# 8. Selepas PR di-approve dan di-merge, kemas kini local
git checkout main
git pull origin main
# 9. Padam branch yang sudah selesai
git branch -d feature/health-check2.10 Situasi Biasa dan Penyelesaian
Berikut adalah beberapa situasi yang anda pasti akan hadapi dan cara menanganinya.
Merge Conflict
Merge conflict berlaku apabila dua orang mengubah bahagian yang sama dalam fail yang sama.
# Apabila pull atau merge menyebabkan conflict
git pull origin main
# CONFLICT (content): Merge conflict in config.yml
# Buka fail yang bermasalah
# Anda akan nampak penanda seperti ini:
# <<<<<<< HEAD
# port: 8080
# =======
# port: 3000
# >>>>>>> origin/main
# Edit fail, pilih versi yang betul, buang penanda
# Kemudian:
git add config.yml
git commit -m "Selesaikan merge conflict pada config.yml"Undo Perubahan
# Buang perubahan pada fail yang belum di-stage
git checkout -- nama-fail.txt
# Buang fail dari staging area (tetapi kekalkan perubahan)
git reset HEAD nama-fail.txt
# Kembali ke commit sebelumnya (buat commit baru yang membatalkan)
git revert HEAD
# Lihat fail pada commit tertentu
git show abc1234:nama-fail.txtStash: Simpan Kerja Sementara
# Anda tengah coding, tapi perlu tukar branch segera
git stash
# Tukar branch, buat kerja lain
git checkout main
# ... buat sesuatu ...
# Kembali dan ambil semula kerja yang di-stash
git checkout feature/my-feature
git stash popNota Beginner:
git stashsangat berguna apabila anda perlu tukar konteks dengan cepat. Fikirkan ia seperti meletakkan kerja anda dalam laci sementara.git stash popmengeluarkan semula dari laci.
Ringkasan
Dalam bab ini, kita telah belajar:
- Git adalah asas DevOps. Setiap pipeline, setiap IaC, setiap kerjasama pasukan bergantung kepada Git.
- Arahan asas (
init,add,commit,push,pull,clone) adalah rutin harian anda. - Branching membolehkan kerja selari tanpa mengganggu kod utama.
- GitFlow sesuai untuk release berjadual. Trunk-based development sesuai untuk CI/CD yang kerap.
- Pull request dan code review mengekalkan kualiti kod dalam pasukan.
.gitignoremelindungi fail sensitif daripada masuk ke repository.- Git hooks menjalankan pemeriksaan secara automatik untuk mengekalkan standard.
Git mungkin kelihatan mudah pada permulaan, tetapi ia mempunyai kedalaman yang luar biasa. Jangan risau kalau anda belum ingat semua arahan. Dengan masa dan amalan, ia akan menjadi sebahagian daripada memori otot anda.
Saya cadangkan anda luangkan masa beberapa hari untuk bermain dengan Git. Buat repository dalam homelab Gitea anda, cuba buat branch, simulate merge conflict, dan praktik workflow yang kita bincangkan. Lebih banyak anda praktik, lebih selesa anda akan jadi.
Dalam bab seterusnya, kita akan melihat bagaimana Git menjadi pencetus kepada CI/CD pipeline, iaitu jantung DevOps automation.
Teruskan belajar, anda sedang berada di landasan yang betul.
Bab 3: CI/CD Pipeline
Bayangkan situasi ini. Anda baru sahaja siapkan satu feature baru untuk aplikasi web anda. Anda test di local, semua berjalan lancar. Kemudian anda SSH ke server, pull code terbaru, restart service, dan harap semuanya okay. Dua jam kemudian, ada bug. Rupanya anda terlupa run test sebelum deploy. Pernah tak jadi macam ni?
Kalau pernah, anda bukan seorang. Ini adalah masalah klasik yang hampir setiap developer pernah hadapi. Dan inilah sebab CI/CD wujud.
Dalam bab ini, kita akan belajar bagaimana automate keseluruhan proses dari code commit hingga deployment. Tak perlu lagi SSH manual ke server. Tak perlu lagi harap dan doa setiap kali deploy. Semuanya automatic, consistent, dan boleh dipercayai.
Apa yang anda akan belajar:
- Konsep CI/CD dan kenapa ia penting untuk production
- Menulis GitHub Actions workflow dari awal untuk Node.js, Python, dan Docker
- Asas GitLab CI sebagai alternatif
- Pipeline stages: build, test, dan deploy
- Menguruskan environment variables dan secrets dengan selamat
- Deployment strategies: blue-green, rolling, dan canary
- Hands-on: set up CI/CD untuk projek sebenar
Apa Itu CI/CD?
CI/CD adalah singkatan untuk Continuous Integration dan Continuous Delivery (atau Continuous Deployment). Mari kita pecahkan satu per satu.
Continuous Integration (CI) bermaksud setiap kali developer push code ke repository, code tersebut akan di-build dan di-test secara automatik. Kalau ada masalah, anda akan tahu dalam beberapa minit, bukan beberapa hari kemudian bila ada orang lain complain.
Continuous Delivery (CD) pula bermaksud code yang sudah lulus semua test sedia untuk di-deploy pada bila-bila masa. Anda cuma perlu klik satu butang (atau dalam kes Continuous Deployment, ia terus deploy tanpa perlu klik apa-apa).
Nota Beginner: Fikirkan CI/CD macam assembly line di kilang kereta. Setiap bahagian kereta melalui inspection station yang berbeza sebelum sampai ke customer. Kalau satu bahagian tak pass inspection, kereta tak akan keluar dari kilang. Sama juga dengan code anda.
Kenapa Perlu Automate?
Mungkin anda fikir, “Saya seorang je yang kerja kat projek ni. Perlu ke CI/CD?” Jawapan pendek: ya. Sebab manusia buat silap. Kita lupa run test. Kita lupa build Docker image. Kita lupa update environment variable. Machine tak lupa.
Berikut beberapa sebab utama kenapa CI/CD penting:
- Consistency - Setiap deployment melalui proses yang sama. Tak ada variasi “oh hari tu saya deploy cara lain.”
- Kelajuan - Deployment yang dulu ambil 30 minit manual boleh jadi 5 minit automatic.
- Keyakinan - Anda tahu code yang di-deploy sudah lulus semua test.
- Audit trail - Setiap deployment ada rekod. Siapa deploy, bila, apa yang berubah.
- Rollback mudah - Kalau ada masalah, boleh revert ke version sebelumnya dengan cepat.
GitHub Actions: Bermula dengan CI/CD
GitHub Actions adalah pilihan paling popular untuk CI/CD sekarang, terutama kalau code anda sudah ada di GitHub. Ia percuma untuk public repository dan ada free tier yang generous untuk private repository.
Struktur Asas
GitHub Actions menggunakan YAML files yang disimpan dalam folder .github/workflows/ di repository anda. Setiap file adalah satu workflow.
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup environment
run: echo "Kita bermula di sini!"Mari fahamkan setiap bahagian:
- name - Nama workflow anda. Akan dipaparkan di GitHub UI.
- on - Bila workflow ini perlu run. Di sini, setiap kali ada push ke branch
mainataudevelop, dan setiap kali ada pull request kemain. - jobs - Senarai kerja yang perlu dilakukan.
- runs-on - Machine apa yang digunakan.
ubuntu-latestadalah pilihan paling common. - steps - Langkah demi langkah apa yang perlu dilakukan.
Workflow untuk Node.js
Ini contoh workflow lengkap untuk aplikasi Node.js:
# .github/workflows/node-ci.yml
name: Node.js CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Run build
run: npm run build
security-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
- run: npm ci
- name: Security audit
run: npm audit --audit-level=highPerhatikan penggunaan strategy.matrix. Ini bermaksud workflow akan run pada tiga versi Node.js secara selari. Kalau aplikasi anda perlu support multiple versions, ini cara terbaik untuk pastikan semuanya berjalan lancar.
Nota Beginner:
npm ciberbeza daripadanpm install. Ia lebih strict dan sesuai untuk CI environment kerana ia install dependencies berdasarkanpackage-lock.jsonsahaja, tanpa modify apa-apa.
Workflow untuk Python
# .github/workflows/python-ci.yml
name: Python CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12']
steps:
- uses: actions/checkout@v4
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Lint with flake8
run: |
flake8 . --count --select=E9,F63,F7,F82 --show-source
flake8 . --count --max-line-length=120 --statistics
- name: Run tests with coverage
run: |
pytest --cov=app --cov-report=xml --cov-report=html
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report-${{ matrix.python-version }}
path: htmlcov/Workflow untuk Docker Build dan Push
Ini adalah workflow yang paling anda perlukan untuk production. Ia build Docker image, tag dengan betul, dan push ke container registry.
# .github/workflows/docker-publish.yml
name: Docker Build and Publish
on:
push:
branches: [main]
tags: ['v*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix=
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=maxWorkflow ini akan automatically tag image anda berdasarkan Git tag. Kalau anda push tag v1.2.3, image akan di-tag sebagai 1.2.3 dan 1.2. Sangat kemas untuk version management.
GitLab CI: Alternatif yang Mantap
Kalau anda menggunakan GitLab, ia mempunyai CI/CD yang built-in. Anda cuma perlu create file .gitlab-ci.yml di root repository.
# .gitlab-ci.yml
stages:
- build
- test
- deploy
variables:
NODE_VERSION: "20"
cache:
paths:
- node_modules/
build:
stage: build
image: node:${NODE_VERSION}
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
test:
stage: test
image: node:${NODE_VERSION}
script:
- npm ci
- npm test
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
deploy-staging:
stage: deploy
image: alpine:latest
script:
- echo "Deploying to staging..."
- ./deploy.sh staging
environment:
name: staging
url: https://staging.myapp.com
only:
- develop
deploy-production:
stage: deploy
image: alpine:latest
script:
- echo "Deploying to production..."
- ./deploy.sh production
environment:
name: production
url: https://myapp.com
only:
- main
when: manualPerbezaan utama GitLab CI dengan GitHub Actions ialah GitLab menggunakan konsep stages dengan lebih explicit. Setiap stage mesti selesai sebelum stage seterusnya bermula. Perhatikan juga when: manual pada deploy-production. Ini bermaksud production deployment memerlukan seseorang untuk klik butang deploy secara manual. Ini adalah good practice untuk mengelakkan accidental deployment.
Pipeline Stages: Build, Test, Deploy
Sebuah CI/CD pipeline yang baik biasanya mempunyai beberapa stage yang jelas.
Stage 1: Build
Pada stage ini, code anda di-compile atau di-bundle. Untuk aplikasi Node.js, ini mungkin npm run build. Untuk Go, go build. Untuk Docker, docker build.
Tujuan utama stage ini adalah untuk pastikan code anda boleh di-build tanpa error. Kalau ada syntax error atau missing dependency, ia akan gagal di sini.
Stage 2: Test
Stage test biasanya terbahagi kepada beberapa jenis:
- Unit tests - Test fungsi individual. Paling cepat.
- Integration tests - Test bagaimana komponen berbeza berinteraksi.
- End-to-end tests - Test keseluruhan flow dari perspektif pengguna.
- Security scanning - Scan untuk known vulnerabilities.
- Linting - Pastikan code style consistent.
# Contoh test stage yang comprehensive
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Run unit tests
run: npm run test:unit
- name: Run integration tests
run: npm run test:integration
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
- name: Run e2e tests
run: npm run test:e2ePerhatikan bagaimana kita boleh spin up PostgreSQL sebagai service dalam GitHub Actions. Ini bermaksud integration test anda boleh test dengan database sebenar, bukan mock.
Stage 3: Deploy
Stage deploy adalah tempat code anda sampai ke server. Bergantung pada setup anda, ini boleh jadi SSH ke server, push ke Kubernetes cluster, atau update cloud service.
Environment Variables dan Secrets
Ini bahagian yang ramai orang buat silap. Jangan sesekali hardcode passwords, API keys, atau credentials dalam code anda. Pernah nampak tak orang push AWS keys ke GitHub? Dalam beberapa minit, bots sudah guna keys tersebut untuk spin up crypto miners.
GitHub Actions Secrets
GitHub menyediakan cara yang selamat untuk menyimpan secrets:
- Pergi ke repository Settings > Secrets and variables > Actions
- Klik “New repository secret”
- Masukkan nama dan value
Kemudian dalam workflow:
steps:
- name: Deploy to production
run: |
ssh -i key.pem ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
"cd /app && docker compose pull && docker compose up -d"
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}Environment-Specific Variables
Untuk projek yang mempunyai multiple environments (staging, production), gunakan GitHub Environments:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy
run: ./deploy.sh
env:
APP_URL: ${{ vars.APP_URL }}
API_KEY: ${{ secrets.API_KEY }}
deploy-production:
runs-on: ubuntu-latest
environment: production
needs: [deploy-staging]
steps:
- name: Deploy
run: ./deploy.sh
env:
APP_URL: ${{ vars.APP_URL }}
API_KEY: ${{ secrets.API_KEY }}Setiap environment boleh mempunyai set secrets dan variables yang berbeza. Staging guna staging database, production guna production database. Simple dan selamat.
Nota Beginner: Jangan sesekali log atau print secrets dalam pipeline output. Walaupun GitHub akan cuba mask secrets secara automatik, lebih baik berjaga-jaga.
Deployment Strategies
Okay, code anda sudah lulus semua test. Sekarang, macam mana nak deploy? Ada beberapa strategi yang biasa digunakan.
Rolling Deployment
Ini adalah strategi paling simple. Server anda di-update satu per satu. Kalau anda ada 4 server, server pertama akan di-update dulu. Selepas ia siap dan healthy, server kedua pula. Dan seterusnya.
Kelebihan: Mudah difahami, tiada downtime. Kekurangan: Untuk seketika, ada dua versi aplikasi running serentak.
# Contoh rolling deployment dengan Docker Compose
deploy:
runs-on: ubuntu-latest
steps:
- name: Rolling deploy
run: |
for server in ${{ secrets.SERVER_LIST }}; do
echo "Deploying to $server..."
ssh deploy@$server "
cd /app &&
docker compose pull &&
docker compose up -d --no-deps --build web &&
sleep 10 &&
docker compose exec web curl -f http://localhost:3000/health
"
echo "$server deployed successfully"
doneBlue-Green Deployment
Bayangkan anda ada dua set server yang identical. Satu dipanggil “blue” (version semasa), satu lagi “green” (version baru). Anda deploy version baru ke green. Test. Kalau okay, tukar traffic dari blue ke green. Kalau ada masalah, tukar balik ke blue. Senang.
# Contoh blue-green dengan Nginx
deploy-blue-green:
runs-on: ubuntu-latest
steps:
- name: Deploy to green environment
run: |
ssh deploy@${{ secrets.DEPLOY_HOST }} "
cd /app &&
docker compose -f docker-compose.green.yml pull &&
docker compose -f docker-compose.green.yml up -d &&
sleep 15 &&
curl -f http://localhost:3001/health
"
- name: Switch traffic to green
run: |
ssh deploy@${{ secrets.DEPLOY_HOST }} "
cp /etc/nginx/conf.d/green.conf /etc/nginx/conf.d/active.conf &&
nginx -s reload
"
- name: Stop blue environment
run: |
ssh deploy@${{ secrets.DEPLOY_HOST }} "
docker compose -f docker-compose.blue.yml down
"Canary Deployment
Canary deployment adalah strategi di mana anda deploy version baru kepada sebahagian kecil users dulu. Mungkin 5% dulu. Monitor. Kalau tak ada masalah, naikkan ke 25%, 50%, dan akhirnya 100%.
Nama “canary” datang dari amalan pelombong yang bawa burung kenari ke dalam lombong. Kalau burung mati, bermaksud ada gas berbahaya. Sama juga dengan canary deployment. Kalau 5% users mengalami masalah, anda tahu ada issue sebelum ia menjejaskan semua users.
Strategi ini lebih sesuai untuk aplikasi berskala besar dan biasanya memerlukan load balancer yang menyokong traffic splitting seperti Nginx, HAProxy, atau Kubernetes Ingress.
Perbandingan Deployment Strategies
| Strategi | Downtime | Rollback | Complexity | Sesuai Untuk |
|---|---|---|---|---|
| Rolling | Tiada | Sederhana | Rendah | Kebanyakan aplikasi |
| Blue-Green | Tiada | Sangat cepat | Sederhana | Aplikasi yang perlu instant rollback |
| Canary | Tiada | Cepat | Tinggi | Aplikasi berskala besar |
Untuk kebanyakan projek homelab-to-production, rolling deployment sudah memadai. Ia simple, berkesan, dan tidak memerlukan infrastructure tambahan. Anda boleh upgrade ke blue-green atau canary bila keperluan anda bertambah.
Nota Beginner: Jangan terus jump ke canary deployment hanya kerana Netflix atau Google gunakannya. Mulakan dengan rolling deployment. Bila anda faham betul-betul bagaimana ia berfungsi, barulah pertimbangkan strategi yang lebih advanced.
Praktikal: Set Up CI/CD untuk Projek Sebenar
Mari kita buat end-to-end. Katakan anda ada sebuah Node.js API yang menggunakan Docker. Ini complete setup.
Langkah 1: Struktur Projek
my-api/
src/
index.js
routes/
middleware/
tests/
unit/
integration/
Dockerfile
docker-compose.yml
docker-compose.prod.yml
.github/
workflows/
ci.yml
deploy.yml
package.json
Langkah 2: CI Workflow
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
needs: [lint]
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Run tests
run: npm test
env:
DATABASE_URL: postgresql://testuser:testpass@localhost:5432/testdb
REDIS_URL: redis://localhost:6379
build-image:
runs-on: ubuntu-latest
needs: [test]
if: github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=maxLangkah 3: Deploy Workflow
# .github/workflows/deploy.yml
name: Deploy
on:
workflow_run:
workflows: [CI]
types: [completed]
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
environment: production
steps:
- name: Deploy to production server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_KEY }}
script: |
cd /opt/my-api
export IMAGE_TAG=${{ github.sha }}
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml up -d
sleep 10
curl -f http://localhost:3000/health || exit 1
echo "Deployment successful!"
- name: Notify on success
if: success()
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-H 'Content-Type: application/json' \
-d '{"text":"Deployment successful! Version: ${{ github.sha }}"}'
- name: Rollback on failure
if: failure()
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.DEPLOY_HOST }}
username: ${{ secrets.DEPLOY_USER }}
key: ${{ secrets.DEPLOY_KEY }}
script: |
cd /opt/my-api
docker compose -f docker-compose.prod.yml down
export IMAGE_TAG=$(docker images --format '{{.Tag}}' | head -2 | tail -1)
docker compose -f docker-compose.prod.yml up -d
echo "Rolled back to previous version"Perhatikan beberapa perkara penting di sini. Pertama, deploy workflow hanya run selepas CI workflow berjaya. Kedua, ia menggunakan environment production yang boleh dikonfigurasi untuk memerlukan approval. Ketiga, ada step untuk rollback kalau deployment gagal. Dan keempat, ada notification ke Slack supaya team tahu status deployment.
Nota Beginner: Untuk permulaan, anda mungkin tak perlukan semua ini. Mulakan dengan CI sahaja dulu (lint + test). Selepas selesa, tambah Docker build. Kemudian baru tambah automated deployment. Jangan cuba buat semua sekaligus.
Langkah 4: Dockerfile untuk Projek
Untuk melengkapkan setup, ini Dockerfile yang sesuai untuk production:
# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN npm prune --production
FROM node:20-alpine
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]Langkah 5: Docker Compose untuk Production
# docker-compose.prod.yml
services:
web:
image: ghcr.io/username/my-api:${IMAGE_TAG:-latest}
restart: always
ports:
- "3000:3000"
env_file:
- .env.production
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
db:
image: postgres:16-alpine
restart: always
volumes:
- pgdata:/var/lib/postgresql/data
env_file:
- .env.db
redis:
image: redis:7-alpine
restart: always
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
pgdata:Langkah 6: Test Keseluruhan Flow
Sekarang, mari test keseluruhan flow ini.
- Buat satu perubahan kecil dalam code anda.
- Push ke branch
developdan create pull request kemain. - Perhatikan CI workflow bermula secara automatik di GitHub Actions tab.
- Tunggu lint dan test selesai. Kalau ada error, fix dan push lagi.
- Merge pull request ke
main. - Perhatikan CI workflow run lagi, kali ini termasuk Docker build dan push.
- Selepas CI selesai, deploy workflow akan bermula secara automatik.
- Check server anda untuk pastikan deployment berjaya.
Proses ini mungkin nampak banyak langkah, tapi sebenarnya selepas setup awal, anda hanya perlu buat langkah 1 hingga 5. Selebihnya berlaku secara automatik. Itulah keajaiban CI/CD.
Tips dan Best Practices
Sebelum kita tutup bab ini, beberapa tips yang saya pelajari dari pengalaman.
Cache dependencies. Setiap kali pipeline run, ia perlu download semua dependencies. Gunakan caching untuk speed up proses ini. GitHub Actions dan GitLab CI kedua-duanya menyokong caching.
Keep pipelines fast. Kalau pipeline ambil 30 minit untuk complete, developer akan mula skip CI. Idealnya, pipeline sepatutnya selesai dalam 5 hingga 10 minit. Gunakan parallel jobs dan caching untuk achieve ini.
Protect main branch. Set up branch protection rules supaya code tak boleh di-merge ke main tanpa CI passing. Ini adalah safety net yang paling penting.
Version your artifacts. Setiap Docker image, setiap build artifact, perlu ada unique version. Gunakan Git SHA atau semantic versioning. Jangan guna latest tag sahaja untuk production.
Monitor your pipelines. Set up notifications untuk pipeline failures. Kalau pipeline gagal dan tak ada siapa perasan, ia sama sahaja macam tak ada pipeline.
Don’t test in production. Gunakan staging environment yang mirror production. Deploy ke staging dulu, test, baru deploy ke production. Ini nampak macam common sense, tapi anda akan terkejut berapa ramai orang skip langkah ini.
Use branch naming conventions. Standardize nama branch dalam team anda. Contohnya, feature/add-login, fix/broken-auth, hotfix/critical-bug. Ini memudahkan pipeline untuk trigger berdasarkan jenis perubahan. Anda juga boleh configure workflow untuk run different stages bergantung pada branch pattern.
Fail fast. Letakkan checks yang paling cepat di awal pipeline. Linting biasanya selesai dalam beberapa saat, jadi letakkan ia sebelum tests yang mungkin ambil beberapa minit. Kalau linting gagal, developer tahu dalam masa yang singkat tanpa perlu tunggu semua test selesai.
Pipeline sebagai dokumentasi. Pipeline anda sebenarnya adalah dokumentasi yang hidup tentang bagaimana projek anda di-build, di-test, dan di-deploy. Developer baru boleh baca workflow files untuk faham keseluruhan proses. Pastikan ia jelas dan well-commented.
Troubleshooting CI/CD yang Common
Sebelum kita tutup, beberapa masalah yang sering saya jumpa dan cara mengatasinya.
Pipeline pass locally tapi fail di CI. Ini biasanya disebabkan oleh perbezaan environment. Pastikan Node.js/Python version yang sama digunakan. Check juga sama ada ada dependencies yang depend pada OS-specific packages. Satu tip: gunakan Docker dalam CI supaya environment sentiasa consistent.
Pipeline terlalu lambat. Audit setiap step dan tengok mana yang paling lama. Biasanya install dependencies dan Docker build yang paling lambat. Gunakan caching aggressively. Untuk Docker, gunakan BuildKit cache. Untuk npm, cache node_modules. Untuk pip, cache pip download directory.
Flaky tests. Tests yang kadang pass, kadang fail tanpa code change. Ini biasanya disebabkan oleh race conditions, external API dependencies, atau timezone issues. Fix root cause, jangan cuma retry. Tapi kalau memang perlu, kebanyakan CI platforms ada retry mechanism.
# GitHub Actions: retry step
- name: Run flaky test
uses: nick-invision/retry@v2
with:
timeout_minutes: 5
max_attempts: 3
command: npm run test:e2eSecrets not available. Kalau anda nampak error berkaitan secrets kosong, pastikan anda sudah add secrets di Settings. Untuk pull requests dari forked repositories, secrets tidak available secara default kerana security reasons. Ini adalah behavior yang dijangkakan.
Ringkasan
Dalam bab ini, kita telah belajar bahawa CI/CD bukan sekadar tool. Ia adalah satu amalan yang mengubah cara kita deliver software. Dengan CI, setiap code change di-validate secara automatik. Dengan CD, deployment menjadi proses yang boleh dipercayai dan repeatable.
Kita telah explore GitHub Actions dan GitLab CI sebagai platform CI/CD. Kedua-duanya mempunyai kelebihan masing-masing, tetapi konsep asasnya sama. Kita juga telah belajar tentang pipeline stages (build, test, deploy), cara menguruskan secrets dengan selamat, dan pelbagai deployment strategies.
Yang paling penting, kita telah set up satu CI/CD pipeline yang complete untuk projek sebenar. Dari lint dan test, hingga Docker build dan automated deployment dengan rollback.
Dalam bab seterusnya, kita akan mendalami Docker untuk production. Bukan basics lagi, tetapi teknik-teknik advanced seperti multi-stage builds, image optimization, dan security hardening yang anda perlukan untuk run containers dengan yakin di production.
Bab 4: Docker untuk Production
Anda sudah tahu Docker. Anda boleh tulis Dockerfile, run docker compose up, dan deploy aplikasi di homelab anda. Itu bagus. Tapi production adalah binatang yang berbeza sama sekali.
Di homelab, kalau container crash tengah malam, anda bangun esok pagi dan restart. Di production, kalau container crash tengah malam, customer call support, support escalate ke manager, manager call anda. Tidak fun.
Bab ini bukan tentang belajar Docker dari awal. Anda sudah lepas tahap itu. Bab ini tentang bagaimana run Docker dengan yakin di production. Bagaimana buat image yang kecil dan selamat. Bagaimana monitor containers. Bagaimana pastikan semuanya berjalan walaupun anda sedang tidur.
Apa yang anda akan belajar:
- Multi-stage builds untuk image yang optimized
- Teknik optimization Docker image (saiz, layers, security)
- Docker registry: Harbor dan GitHub Container Registry
- Docker Compose untuk production vs development
- Docker networking dalam konteks production
- Container health checks yang berkesan
- Strategi logging untuk containers
- Docker security best practices
- Resource limits dan kenapa ia penting
Multi-Stage Builds
Ini adalah teknik pertama yang anda perlu kuasai untuk production. Multi-stage builds membolehkan anda gunakan satu Dockerfile untuk build dan hasilkan image yang kecil.
Masalah dengan Dockerfile biasa: anda install build tools (compiler, dev dependencies) untuk build aplikasi, tetapi tools ini tak diperlukan untuk run aplikasi. Hasilnya? Image yang besar tanpa sebab.
Contoh: Node.js
# Stage 1: Build
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
RUN npm prune --production
# Stage 2: Production
FROM node:20-alpine AS production
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]Perhatikan apa yang berlaku di sini. Stage pertama install semua dependencies (termasuk devDependencies), build aplikasi, kemudian prune devDependencies. Stage kedua hanya copy files yang diperlukan. Hasilnya? Image yang jauh lebih kecil.
Contoh: Go
Go adalah contoh terbaik untuk multi-stage builds kerana Go binary adalah standalone. Anda boleh compile di satu stage dan copy binary ke image yang sangat minimal.
# Stage 1: Build
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server
# Stage 2: Production
FROM scratch
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]Image dari scratch hanya mengandungi binary anda dan SSL certificates. Saiznya boleh jadi serendah 10 hingga 15 MB berbanding ratusan MB kalau guna image biasa.
Nota Beginner:
scratchadalah “empty” image. Ia betul-betul kosong, tiada shell, tiada tools, tiada apa-apa. Sesuai untuk Go, Rust, atau mana-mana compiled language yang menghasilkan static binary. Tapi kalau anda perlu debug, gunakanalpinesebagai base image.
Contoh: Python
# Stage 1: Build
FROM python:3.12-slim AS builder
WORKDIR /app
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Stage 2: Production
FROM python:3.12-slim AS production
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
WORKDIR /app
COPY --chown=appuser:appgroup . .
USER appuser
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "app:create_app()"]Docker Image Optimization
Image size bukan sekadar soal disk space. Image yang kecil bermaksud pull lebih cepat, deploy lebih cepat, dan attack surface yang lebih kecil.
Pilih Base Image yang Sesuai
node:20 ~ 1.1 GB
node:20-slim ~ 200 MB
node:20-alpine ~ 130 MB
Perbezaan ini significant. Kalau anda deploy 10 kali sehari, bayangkan berapa bandwidth yang anda jimat dengan alpine.
Namun, alpine menggunakan musl dan bukannya glibc. Sesetengah native dependencies mungkin tidak serasi. Kalau anda menghadapi masalah compatibility, slim adalah pilihan yang baik sebagai jalan tengah.
Optimumkan Layer Caching
Docker build setiap instruction dalam Dockerfile sebagai satu layer. Kalau satu layer berubah, semua layer selepasnya perlu rebuild. Jadi, susun instructions anda supaya yang jarang berubah berada di atas.
# BAIK: Dependencies dulu, source code kemudian
COPY package*.json ./
RUN npm ci
COPY . .
# KURANG BAIK: Semua sekali
COPY . .
RUN npm ciDalam contoh pertama, kalau anda hanya ubah source code tanpa ubah dependencies, Docker boleh guna cached layer untuk npm ci. Ini boleh jimat beberapa minit dalam setiap build.
Gunakan .dockerignore
Sama macam .gitignore, tapi untuk Docker. Pastikan anda tak copy file yang tak perlu ke dalam image.
# .dockerignore
node_modules
.git
.gitignore
*.md
docker-compose*.yml
.env*
tests/
coverage/
.github/
Docker Registry
Di homelab, mungkin anda cuma buat docker build dan terus run. Di production, anda perlukan registry untuk simpan dan distribute images.
GitHub Container Registry (GHCR)
Kalau code anda di GitHub, GHCR adalah pilihan paling mudah. Ia integrate terus dengan GitHub Actions dan menggunakan GITHUB_TOKEN yang sedia ada.
# Login
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Tag dan push
docker tag my-app:latest ghcr.io/username/my-app:v1.0.0
docker push ghcr.io/username/my-app:v1.0.0Harbor: Self-Hosted Registry
Untuk organisasi yang memerlukan lebih kawalan, Harbor adalah pilihan yang excellent. Ia open source, mempunyai vulnerability scanning built-in, dan boleh di-host sendiri.
# docker-compose untuk Harbor (simplified)
# Untuk production, gunakan installer rasmi dari Harbor
version: '3'
services:
registry:
image: goharbor/harbor-core:v2.10.0
ports:
- "443:8443"
volumes:
- harbor-data:/dataNota Beginner: Untuk permulaan, GHCR sudah lebih dari cukup. Anda cuma perlukan self-hosted registry bila ada keperluan specific seperti compliance, air-gapped environment, atau anda perlu vulnerability scanning yang lebih mendalam.
Image Tagging Strategy
Bagaimana anda tag images sangat penting untuk production. Ini beberapa pendekatan yang biasa digunakan.
Semantic Versioning menggunakan format v1.2.3. Major version untuk breaking changes, minor untuk features baru, patch untuk bug fixes. Ini paling mudah difahami oleh manusia.
Git SHA menggunakan commit hash sebagai tag, contohnya abc123f. Setiap image boleh di-trace balik ke exact commit. Sangat berguna untuk debugging.
Gabungan kedua-duanya adalah pendekatan terbaik. Tag image anda dengan kedua-dua semantic version dan Git SHA.
# Tag dengan multiple tags
docker tag my-app:latest ghcr.io/username/my-app:v1.2.3
docker tag my-app:latest ghcr.io/username/my-app:abc123f
docker tag my-app:latest ghcr.io/username/my-app:latest
# Push semua tags
docker push ghcr.io/username/my-app:v1.2.3
docker push ghcr.io/username/my-app:abc123f
docker push ghcr.io/username/my-app:latestUntuk production deployment, selalu guna specific version tag. Gunakan latest hanya untuk development atau testing.
Cleanup Old Images
Registry boleh membesar dengan cepat kalau anda tidak cleanup. Set up retention policy untuk automatically delete old images.
# Untuk GHCR, boleh guna GitHub Actions
# Delete images yang lebih dari 30 hari dan bukan tagged version
- uses: snok/container-retention-policy@v2
with:
image-names: my-app
cut-off: 30 days ago UTC
keep-at-least: 5
account-type: personal
token: ${{ secrets.GITHUB_TOKEN }}Docker Compose: Production vs Development
Anda mungkin sudah biasa dengan satu docker-compose.yml. Untuk production, anda patut ada setup yang berbeza.
Development
# docker-compose.yml (development)
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- DEBUG=app:*
command: npm run dev
db:
image: postgres:16
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=devpassword
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:Production
# docker-compose.prod.yml
services:
app:
image: ghcr.io/username/my-app:${IMAGE_TAG:-latest}
restart: always
ports:
- "3000:3000"
environment:
- NODE_ENV=production
env_file:
- .env.production
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
db:
image: postgres:16-alpine
restart: always
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
pgdata:
driver: localNampak perbezaan? Production version tidak mount source code (guna pre-built image), tidak expose database port ke host, mempunyai resource limits, health checks, proper logging configuration, dan menggunakan secrets untuk sensitive data. Ini semua perkara kecil yang membezakan homelab setup dengan production setup.
Docker Networking dalam Production
Di homelab, semua container biasanya dalam satu network yang sama. Di production, anda perlu lebih berhati-hati.
services:
nginx:
image: nginx:alpine
networks:
- frontend
ports:
- "80:80"
- "443:443"
app:
image: my-app:latest
networks:
- frontend
- backend
db:
image: postgres:16
networks:
- backend
redis:
image: redis:7-alpine
networks:
- backend
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: truePerhatikan internal: true pada network backend. Ini bermaksud containers dalam network ini tidak boleh access internet secara langsung. Database dan Redis hanya boleh diakses oleh app container melalui internal network. Nginx boleh access app melalui frontend network, tapi tidak boleh access database secara langsung. Ini adalah prinsip least privilege yang penting untuk security.
Container Health Checks
Health checks adalah cara Docker (atau orchestrator) tahu sama ada container anda sihat. Tanpa health check, Docker hanya tahu container running atau tidak. Ia tak tahu kalau aplikasi anda stuck dalam infinite loop atau database connection putus.
# Dalam Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1Dan endpoint health check anda perlu bermakna:
// Node.js health check endpoint
app.get('/health', async (req, res) => {
try {
// Check database connection
await db.query('SELECT 1');
// Check Redis connection
await redis.ping();
res.status(200).json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
checks: {
database: 'connected',
redis: 'connected'
}
});
} catch (error) {
res.status(503).json({
status: 'unhealthy',
error: error.message
});
}
});Nota Beginner: Parameter
--start-periodpenting untuk aplikasi yang mengambil masa untuk start up. Selama tempoh ini, health check failures tidak dikira. Tanpa ini, Docker mungkin restart container sebelum ia sempat start.
Strategi Logging
Di homelab, anda mungkin cuma docker logs dan scroll. Di production, anda perlukan logging yang lebih terstruktur.
Structured Logging
Tulis log dalam format JSON supaya mudah di-parse oleh tools seperti ELK Stack atau Loki.
// Daripada ini:
console.log('User logged in: john@example.com');
// Gunakan ini:
logger.info({
event: 'user_login',
email: 'john@example.com',
ip: req.ip,
timestamp: new Date().toISOString()
});Docker Logging Drivers
# docker-compose.prod.yml
services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
labels: "app"
tag: "{{.Name}}"Untuk setup yang lebih advanced, anda boleh forward logs ke centralized logging system:
services:
app:
logging:
driver: "fluentd"
options:
fluentd-address: "localhost:24224"
tag: "app.{{.Name}}"Penting untuk set max-size dan max-file. Tanpa limit ini, log files boleh memenuhi disk anda. Saya pernah lihat server yang disk penuh kerana log files. Semua services down hanya kerana tak ada disk space. Jangan jadi macam tu.
Log Levels
Pastikan anda gunakan log levels yang betul. Ini memudahkan filtering di production.
// Gunakan library seperti winston atau pino
const logger = require('pino')({
level: process.env.LOG_LEVEL || 'info'
});
logger.debug('Detailed debugging info'); // Hanya untuk development
logger.info('User created successfully'); // Normal operations
logger.warn('Disk usage above 80%'); // Perlu perhatian
logger.error('Database connection failed'); // Ada masalah
logger.fatal('Application crashed'); // KritikalDi production, set log level ke info atau warn. Jangan gunakan debug di production kerana ia akan menghasilkan terlalu banyak log dan boleh menjejaskan performance.
Centralized Logging dengan Loki
Untuk setup yang lebih lengkap, anda boleh gunakan Grafana Loki untuk centralized logging. Ia lebih lightweight berbanding ELK Stack dan integrate dengan baik bersama Grafana untuk visualization.
services:
loki:
image: grafana/loki:2.9.0
ports:
- "3100:3100"
volumes:
- loki-data:/loki
promtail:
image: grafana/promtail:2.9.0
volumes:
- /var/log:/var/log
- /var/lib/docker/containers:/var/lib/docker/containers:ro
command: -config.file=/etc/promtail/config.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=adminNota Beginner: Kalau anda baru bermula,
json-filelogging driver denganmax-sizelimit sudah memadai. Anda boleh upgrade ke centralized logging bila anda ada multiple servers atau bila anda rasa sukar untuk troubleshoot issues dengandocker logssahaja.
Docker Security Best Practices
Security di production bukan optional. Ini checklist yang anda perlu ikut.
1. Jangan Run sebagai Root
# Buat user baru
RUN addgroup -g 1001 -S appgroup && \
adduser -S appuser -u 1001 -G appgroup
# Tukar kepada user tersebut
USER appuser2. Gunakan Read-Only Filesystem
services:
app:
read_only: true
tmpfs:
- /tmp
- /app/tempIni memastikan tiada siapa (termasuk attacker) boleh write ke filesystem container. Kalau aplikasi anda perlu write ke temporary files, gunakan tmpfs.
3. Drop Unnecessary Capabilities
services:
app:
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true4. Scan Images untuk Vulnerabilities
# Gunakan Docker Scout
docker scout cves my-app:latest
# Atau Trivy (open source)
trivy image my-app:latestIntegrate scanning ini dalam CI/CD pipeline supaya setiap image di-scan sebelum deploy:
# Dalam GitHub Actions
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: ghcr.io/${{ github.repository }}:${{ github.sha }}
exit-code: '1'
severity: 'CRITICAL,HIGH'5. Gunakan Specific Tags, Bukan Latest
# KURANG BAIK
FROM node:latest
# BAIK
FROM node:20.11-alpinelatest boleh berubah pada bila-bila masa. Satu hari semuanya berjalan lancar, hari berikutnya image berubah dan aplikasi anda rosak. Pin your versions.
6. Jangan Simpan Secrets dalam Image
Ini kesilapan yang kerap berlaku. Jangan copy .env file atau secrets ke dalam Docker image. Walaupun anda delete file tersebut dalam layer seterusnya, ia masih boleh diakses melalui layer sebelumnya.
# SALAH - secret masih ada dalam image layers
COPY .env .
RUN source .env && npm run build
RUN rm .env
# BETUL - guna build arguments untuk build-time secrets
ARG API_ENDPOINT
ENV API_ENDPOINT=$API_ENDPOINT
RUN npm run build
# LEBIH BETUL - guna Docker BuildKit secrets
RUN --mount=type=secret,id=env,target=/app/.env npm run buildUntuk runtime secrets, gunakan environment variables atau Docker secrets, bukan files yang di-copy ke dalam image.
7. Keep Images Updated
Set up automated process untuk rebuild images bila base image ada security update. Tools seperti Dependabot atau Renovate boleh bantu automate ini. Anda juga boleh schedule weekly rebuilds dalam CI/CD pipeline untuk pastikan base images sentiasa up to date.
Resource Limits
Ini adalah satu perkara yang sering diabaikan tetapi sangat kritikal. Tanpa resource limits, satu container yang leak memory boleh menjatuhkan keseluruhan server.
services:
app:
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128Mlimits bermaksud maximum resources yang container boleh guna. Kalau container cuba guna lebih dari 512MB memory, Docker akan kill container tersebut (OOM kill).
reservations bermaksud minimum resources yang dijamin untuk container. Docker akan pastikan sekurang-kurangnya resources ini tersedia.
Untuk menentukan resource limits yang sesuai, monitor aplikasi anda terlebih dahulu. Jalankan ia tanpa limits di staging environment, perhatikan berapa banyak CPU dan memory ia guna dalam keadaan normal dan di bawah load. Kemudian set limits dengan buffer yang munasabah, mungkin 1.5 hingga 2 kali ganda penggunaan normal.
Nota Beginner: Kalau anda nampak container anda kerap di-OOM kill, jangan terus naikkan memory limit. Mungkin ada memory leak dalam aplikasi anda. Check dan fix root cause dulu.
Container Restart Policies
Di homelab, kalau container crash, anda mungkin manually restart. Di production, anda perlukan restart policy yang automatik.
services:
app:
restart: always # Sentiasa restart, kecuali manually stopped
worker:
restart: on-failure # Restart hanya kalau exit code bukan 0
migration:
restart: "no" # Jangan restart (untuk one-off tasks)Bila guna yang mana?
- always sesuai untuk long-running services seperti web server dan API. Ia akan restart walaupun selepas server reboot.
- on-failure sesuai untuk background workers yang mungkin crash sesekali. Ia akan restart kalau process exit dengan error, tapi tidak kalau ia exit secara normal.
- no sesuai untuk tasks yang hanya perlu run sekali, seperti database migration atau data seeding.
Ada juga unless-stopped yang sama macam always, tapi tidak restart selepas anda manually stop container. Ini berguna kalau anda perlu stop container untuk maintenance tanpa risau ia restart sendiri.
Docker Image Layers dan Build Cache
Memahami bagaimana Docker layers berfungsi boleh menjimatkan banyak masa build. Setiap instruction dalam Dockerfile menghasilkan satu layer. Docker cache setiap layer. Kalau instruction dan semua layers sebelumnya tidak berubah, Docker guna cached version.
Ini bermaksud susun atur instructions anda sangat penting:
# Optimized layer ordering
FROM node:20-alpine
WORKDIR /app
# Layer 1: Jarang berubah
COPY package.json package-lock.json ./
# Layer 2: Hanya rebuild bila dependencies berubah
RUN npm ci --production
# Layer 3: Berubah setiap kali ada code change
COPY . .
# Layer 4: Build step
RUN npm run buildKalau anda cuma ubah source code tanpa ubah dependencies, Docker hanya perlu rebuild Layer 3 dan 4. Layer 1 dan 2 guna cache. Ini boleh jimat beberapa minit dalam setiap build, terutama kalau projek anda ada banyak dependencies.
Satu lagi tip: gunakan .dockerignore yang comprehensive. Setiap file yang di-COPY ke build context boleh invalidate cache. Kalau anda accidentally copy node_modules atau .git folder, cache akan invalidate setiap kali walaupun dependencies tidak berubah.
Backup dan Disaster Recovery
Ini satu topik yang ramai orang abaikan sehingga terlambat. Di production, anda perlu plan untuk worst case scenario.
Backup Database Volumes
# Backup PostgreSQL data
docker compose exec db pg_dump -U postgres mydb > backup_$(date +%Y%m%d).sql
# Atau backup volume secara langsung
docker run --rm \
-v pgdata:/data \
-v $(pwd)/backups:/backup \
alpine tar czf /backup/pgdata_$(date +%Y%m%d).tar.gz -C /data .Automated Backup dengan Cron
services:
backup:
image: postgres:16-alpine
entrypoint: /bin/sh
command: >
-c "while true; do
PGPASSWORD=$$POSTGRES_PASSWORD pg_dump -h db -U postgres mydb
> /backups/backup_$$(date +%Y%m%d_%H%M%S).sql;
find /backups -name '*.sql' -mtime +7 -delete;
sleep 86400;
done"
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- ./backups:/backups
secrets:
- db_password
depends_on:
- dbScript ini akan backup database setiap hari dan delete backups yang lebih dari 7 hari. Simple tapi berkesan. Untuk production yang lebih serius, pertimbangkan untuk upload backups ke cloud storage seperti S3 atau MinIO supaya backups anda tidak hilang kalau server mati.
Nota Beginner: Test restore process anda secara berkala. Backup yang tidak boleh di-restore bukan backup. Set up schedule untuk test restore sekurang-kurangnya sebulan sekali.
Production Checklist
Sebelum deploy sebarang container ke production, pastikan anda sudah check perkara berikut:
[ ] Multi-stage build digunakan
[ ] Base image menggunakan specific version tag
[ ] Container tidak run sebagai root
[ ] Health check dikonfigurasi
[ ] Resource limits ditetapkan
[ ] Logging dikonfigurasi dengan size limits
[ ] .dockerignore wujud dan lengkap
[ ] Secrets tidak di-hardcode dalam image
[ ] Image sudah di-scan untuk vulnerabilities
[ ] Network segmentation ditetapkan
[ ] Restart policy dikonfigurasi
[ ] Volumes untuk persistent data dikonfigurasi
Ringkasan
Dalam bab ini, kita telah melangkah jauh dari Docker basics ke Docker production. Multi-stage builds membantu kita buat image yang kecil dan efficient. Proper networking dan security practices memastikan containers kita selamat. Health checks dan resource limits memastikan containers kita reliable.
Perkara yang paling penting untuk diingat adalah ini: production Docker bukan sekadar tentang membuat sesuatu berfungsi. Ia tentang membuat sesuatu berfungsi dengan selamat, efficient, dan boleh dipercayai walaupun pada jam 3 pagi.
Kita telah melihat perbezaan antara Docker Compose untuk development dan production. Kita telah belajar tentang container registries, logging strategies, dan security hardening. Semua ini adalah building blocks yang anda perlukan sebelum kita masuk ke topik seterusnya: Kubernetes.
Bab 5: Kubernetes Asas
Ada satu titik dalam perjalanan DevOps anda di mana Docker Compose mula terasa tidak cukup. Mungkin anda perlu deploy ke multiple servers. Mungkin anda perlu auto-scaling. Mungkin anda perlu zero-downtime deployment tanpa perlu tulis skrip yang rumit. Di situlah Kubernetes masuk.
Kubernetes (atau K8s, kerana ada 8 huruf antara K dan s) pada mulanya mungkin nampak menakutkan. Banyak terminologi baru, banyak YAML files, banyak moving parts. Tapi kalau anda sudah faham Docker, anda sebenarnya sudah mempunyai 60% daripada pengetahuan yang diperlukan. Bab ini akan bawa anda melalui 40% yang tinggal.
Kita tak akan cover semuanya. Kubernetes adalah ecosystem yang sangat luas. Tapi selepas habis bab ini, anda akan mempunyai pemahaman yang kukuh tentang core concepts dan boleh deploy aplikasi pertama anda ke K8s cluster.
Apa yang anda akan belajar:
- Kenapa Kubernetes wujud dan masalah apa yang ia selesaikan
- Arsitektur K8s: control plane, worker nodes, pods, services
- Setup K3s di homelab anda (step by step)
- Kubectl commands yang anda perlu tahu
- Deploy aplikasi pertama ke K8s
- Services dan Ingress untuk expose aplikasi
- ConfigMaps dan Secrets
- Persistent storage
- Scaling aplikasi
- Asas Helm
- Bila guna K8s vs Docker Compose
Kenapa Kubernetes?
Sebelum kita terjun ke technical details, mari faham masalah yang Kubernetes selesaikan. Bayangkan anda ada 5 aplikasi microservices, setiap satu memerlukan 2 replicas, dan anda ada 3 servers.
Dengan Docker Compose, anda perlu manually decide: service A dan B di server 1, service C di server 2, dan seterusnya. Kalau server 2 mati, anda perlu manually pindahkan services ke server lain. Kalau traffic meningkat, anda perlu manually tambah replicas.
Kubernetes automate semua ini. Anda cuma perlu cakap: “Saya nak 2 replicas untuk service A.” Kubernetes akan decide di mana nak letak, dan kalau satu replica mati, ia akan automatically create yang baru.
Ini dipanggil declarative configuration. Anda declare apa yang anda nak, bukan bagaimana nak achieve ia.
Selain itu, Kubernetes memberikan anda beberapa superpower yang Docker Compose tidak ada:
- Self-healing: Kalau container crash, K8s automatically restart. Kalau node mati, K8s pindahkan workload ke node lain.
- Auto-scaling: K8s boleh tambah atau kurangkan replicas berdasarkan CPU/memory usage secara automatik.
- Rolling updates: Deploy version baru tanpa downtime. K8s akan slowly replace pods lama dengan pods baru.
- Service discovery: Setiap service dapat DNS name secara automatik. Tak perlu hardcode IP addresses.
- Load balancing: Traffic diagihkan secara automatik antara semua replicas.
Nota Beginner: K8s bukan penyelesaian untuk semua masalah. Ia menambah complexity yang significant. Pastikan anda benar-benar perlukan features di atas sebelum adopt K8s. Banyak aplikasi berjaya dengan Docker Compose sahaja.
Arsitektur Kubernetes
Untuk faham K8s, anda perlu faham beberapa komponen utama.
Control Plane
Control plane adalah “otak” K8s. Ia terdiri daripada beberapa komponen.
API Server adalah pintu masuk utama. Semua arahan kepada K8s melalui API server, termasuk arahan dari kubectl.
etcd adalah database yang simpan semua state cluster. Ia tahu berapa replicas setiap service ada, di mana ia running, dan apa configuration mereka.
Scheduler bertanggungjawab untuk decide di mana nak run pods baru. Ia mempertimbangkan resource availability, constraints, dan affinity rules.
Controller Manager menjalankan pelbagai controllers yang memastikan actual state cluster sepadan dengan desired state. Kalau anda kata nak 3 replicas tapi hanya 2 yang running, controller manager akan buat satu lagi.
Worker Nodes
Worker nodes adalah machines yang actually run containers anda. Setiap worker node mempunyai beberapa komponen penting.
kubelet adalah agent yang run di setiap node. Ia menerima arahan dari control plane dan memastikan containers running seperti yang dikehendaki.
kube-proxy menguruskan network rules supaya traffic boleh sampai ke pods yang betul.
Container Runtime (biasanya containerd) adalah software yang actually run containers.
Pods
Pod adalah unit terkecil dalam Kubernetes. Satu pod boleh mengandungi satu atau lebih containers. Dalam kebanyakan kes, satu pod mengandungi satu container sahaja.
Nota Beginner: Fikirkan pod macam wrapper di sekeliling container anda. Kenapa perlu wrapper? Kerana K8s perlu tempat untuk letak metadata, health check info, restart policy, dan networking config. Pod menyediakan semua ini.
Services
Service dalam K8s bukan service dalam Docker Compose. Di sini, service adalah abstraksi yang menyediakan stable network endpoint untuk satu set pods. Pods boleh datang dan pergi (dihapus, dicipta semula), tetapi service address kekal sama.
Setting Up K3s di Homelab
K3s adalah lightweight Kubernetes distribution dari Rancher. Ia sesuai untuk homelab kerana ia menggunakan kurang resources berbanding full K8s, tetapi masih production-ready.
Keperluan
- Satu atau lebih machines (boleh VMs) dengan minimum 2GB RAM dan 2 CPU cores
- Ubuntu 22.04 atau mana-mana Linux distribution yang supported
- Network connectivity antara semua nodes
Install K3s Server (Control Plane)
# Di server utama (control plane)
curl -sfL https://get.k3s.io | sh -
# Check status
sudo systemctl status k3s
# Dapatkan kubeconfig
sudo cat /etc/rancher/k3s/k3s.yamlItu sahaja. Serius. K3s boleh di-install dalam satu command. Selepas install, anda sudah ada functional Kubernetes cluster dengan satu node.
Tambah Worker Nodes
# Di server utama, dapatkan token
sudo cat /var/lib/rancher/k3s/server/node-token
# Di worker node, join cluster
curl -sfL https://get.k3s.io | K3S_URL=https://CONTROL_PLANE_IP:6443 \
K3S_TOKEN=TOKEN_DARI_SERVER sh -Setup kubectl di Local Machine
# Copy kubeconfig dari server
scp user@server:/etc/rancher/k3s/k3s.yaml ~/.kube/config
# Edit server address (tukar 127.0.0.1 ke server IP)
# Dalam ~/.kube/config, cari line "server:" dan tukar ke IP server
# Test connection
kubectl get nodesOutput yang anda sepatutnya nampak:
NAME STATUS ROLES AGE VERSION
server-01 Ready control-plane,master 10m v1.28.5+k3s1
worker-01 Ready <none> 5m v1.28.5+k3s1
worker-02 Ready <none> 3m v1.28.5+k3s1
Nota Beginner: Kalau anda cuma ada satu machine, tak perlu risau. K3s boleh run semua workloads di control plane node juga. Untuk belajar, satu node sudah cukup.
Kubectl Commands yang Anda Perlu Tahu
kubectl adalah CLI tool untuk interact dengan Kubernetes cluster. Ini commands yang paling kerap digunakan.
# Lihat semua resources
kubectl get pods # Senarai pods
kubectl get services # Senarai services
kubectl get deployments # Senarai deployments
kubectl get all # Senarai semua resources
kubectl get all -n kube-system # Resources dalam namespace tertentu
# Maklumat terperinci
kubectl describe pod POD_NAME # Detail sesuatu pod
kubectl describe service SVC_NAME # Detail sesuatu service
# Logs
kubectl logs POD_NAME # Lihat logs
kubectl logs -f POD_NAME # Follow logs (live)
kubectl logs POD_NAME -c CONTAINER # Logs dari specific container
# Debug
kubectl exec -it POD_NAME -- /bin/sh # Shell ke dalam pod
kubectl port-forward POD_NAME 8080:80 # Forward port ke local
# Apply configuration
kubectl apply -f manifest.yaml # Create atau update resource
kubectl delete -f manifest.yaml # Delete resourceTips berguna: set alias supaya hidup lebih mudah.
# Tambah dalam ~/.bashrc atau ~/.zshrc
alias k='kubectl'
alias kgp='kubectl get pods'
alias kgs='kubectl get services'
alias kgd='kubectl get deployments'
alias kd='kubectl describe'
alias kl='kubectl logs'
alias ka='kubectl apply -f'Deploy Aplikasi Pertama
Mari deploy satu aplikasi web yang simple. Dalam K8s, anda define apa yang anda nak dalam YAML files yang dipanggil manifests.
Deployment
# app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-web-app
labels:
app: my-web-app
spec:
replicas: 3
selector:
matchLabels:
app: my-web-app
template:
metadata:
labels:
app: my-web-app
spec:
containers:
- name: web
image: ghcr.io/username/my-app:v1.0.0
ports:
- containerPort: 3000
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 15
periodSeconds: 20YAML ini memberitahu K8s: “Saya nak 3 replicas aplikasi saya, setiap satu guna image ini, expose port 3000, dan ini resource limits yang saya nak.”
# Apply deployment
kubectl apply -f app-deployment.yaml
# Check status
kubectl get deployments
kubectl get pods
# Tunggu sehingga semua pods Ready
kubectl rollout status deployment/my-web-appPerhatikan dua jenis probes. readinessProbe menentukan sama ada pod sedia menerima traffic. Kalau fail, pod akan dikeluarkan dari service (tak terima traffic baru, tapi tidak di-kill). livenessProbe menentukan sama ada pod masih hidup. Kalau fail beberapa kali berturut-turut, K8s akan restart pod tersebut.
Services dan Ingress
Pods sudah running, tapi bagaimana nak access mereka? Di sinilah Services dan Ingress masuk.
ClusterIP Service
# app-service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-web-app
spec:
selector:
app: my-web-app
ports:
- port: 80
targetPort: 3000
type: ClusterIPClusterIP service hanya boleh diakses dari dalam cluster. Ia menyediakan stable IP address dan DNS name (my-web-app.default.svc.cluster.local). Sesuai untuk internal services seperti API yang diakses oleh services lain.
NodePort Service
apiVersion: v1
kind: Service
metadata:
name: my-web-app-nodeport
spec:
selector:
app: my-web-app
ports:
- port: 80
targetPort: 3000
nodePort: 30080
type: NodePortNodePort expose service pada setiap node di port tertentu (30080 dalam contoh ini). Anda boleh access aplikasi melalui http://NODE_IP:30080. Sesuai untuk testing, tetapi bukan untuk production.
Ingress
Untuk production, gunakan Ingress. Ia menyediakan HTTP routing, SSL termination, dan virtual hosting.
# app-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-web-app
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-web-app
port:
number: 80K3s datang dengan Traefik sebagai Ingress controller secara default. Anda cuma perlu create Ingress resource dan point DNS ke cluster anda.
# Apply semua resources
kubectl apply -f app-deployment.yaml
kubectl apply -f app-service.yaml
kubectl apply -f app-ingress.yaml
# Check Ingress status
kubectl get ingressMultiple Services dengan Ingress
Salah satu kelebihan Ingress ialah anda boleh route traffic ke different services berdasarkan path atau hostname.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-app-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- api.example.com
- dashboard.example.com
secretName: multi-app-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- host: dashboard.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dashboard-service
port:
number: 80Dengan setup ini, satu Ingress controller boleh handle traffic untuk multiple applications. Setiap hostname diarahkan ke service yang berbeza. Ini jauh lebih kemas berbanding run multiple reverse proxies.
ConfigMaps dan Secrets
Jangan hardcode configuration dalam Docker image. Gunakan ConfigMaps untuk non-sensitive config dan Secrets untuk sensitive data.
ConfigMap
# app-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-web-app-config
data:
APP_ENV: "production"
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
CACHE_TTL: "3600"Secret
# Buat secret via kubectl
kubectl create secret generic my-web-app-secrets \
--from-literal=DATABASE_URL='postgresql://user:pass@db:5432/mydb' \
--from-literal=API_KEY='sk-supersecretkey123'Atau dalam YAML (values mesti base64 encoded):
# app-secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: my-web-app-secrets
type: Opaque
data:
DATABASE_URL: cG9zdGdyZXNxbDovL3VzZXI6cGFzc0BkYjo1NDMyL215ZGI=
API_KEY: c2stc3VwZXJzZWNyZXRrZXkxMjM=Nota Beginner: Base64 bukan encryption. Sesiapa yang ada access ke cluster boleh decode secrets. Untuk security yang lebih baik, gunakan tools seperti Sealed Secrets atau external secret management seperti HashiCorp Vault.
Gunakan dalam Deployment
spec:
containers:
- name: web
image: ghcr.io/username/my-app:v1.0.0
envFrom:
- configMapRef:
name: my-web-app-config
- secretRef:
name: my-web-app-secrets
# Atau specific keys sahaja:
env:
- name: DATABASE_HOST
valueFrom:
configMapKeyRef:
name: my-web-app-config
key: DATABASE_HOST
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: my-web-app-secrets
key: DATABASE_PASSWORDPersistent Storage
Pods adalah ephemeral. Kalau pod mati, data di dalamnya hilang. Untuk data yang perlu kekal (database, uploaded files), gunakan Persistent Volumes.
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: local-path# postgres-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secrets
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: '1'
memory: 1Gi
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-dataK3s menyediakan local-path storage class secara default. Ia menyimpan data di node tempat pod running. Untuk production yang lebih robust, anda mungkin perlu NFS, Longhorn, atau cloud storage (EBS, GCE PD).
Longhorn: Distributed Storage untuk K3s
Kalau anda run K3s dengan multiple nodes, Longhorn adalah pilihan yang excellent untuk storage. Ia replicate data across nodes, jadi kalau satu node mati, data anda masih selamat.
# Install Longhorn via Helm
helm repo add longhorn https://charts.longhorn.io
helm repo update
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultReplicaCount=2Selepas install, anda boleh gunakan Longhorn sebagai storage class:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 10GiNota Beginner: Untuk homelab dengan satu node,
local-pathsudah memadai. Anda hanya perlukan Longhorn atau distributed storage bila anda ada multiple nodes dan perlu data redundancy.
Scaling
Salah satu kelebihan utama K8s adalah scaling yang mudah.
Manual Scaling
# Scale up
kubectl scale deployment my-web-app --replicas=5
# Scale down
kubectl scale deployment my-web-app --replicas=2Horizontal Pod Autoscaler (HPA)
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: my-web-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-web-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80Dengan HPA, K8s akan automatically tambah pods kalau CPU usage melebihi 70% atau memory usage melebihi 80%. Dan ia akan kurangkan pods kalau usage rendah. Anda set minimum 2 replicas (untuk high availability) dan maximum 10 replicas (untuk control cost).
# Apply HPA
kubectl apply -f hpa.yaml
# Monitor HPA
kubectl get hpa
kubectl describe hpa my-web-app-hpaNamespaces
Sebelum kita pergi ke Helm, satu concept yang penting ialah namespaces. Namespace adalah cara K8s untuk memisahkan resources dalam satu cluster.
# Lihat semua namespaces
kubectl get namespaces
# Buat namespace baru
kubectl create namespace staging
kubectl create namespace production
# Deploy ke namespace tertentu
kubectl apply -f app-deployment.yaml -n staging
kubectl apply -f app-deployment.yaml -n production
# Lihat pods dalam namespace tertentu
kubectl get pods -n staging
kubectl get pods -n productionDengan namespaces, anda boleh run staging dan production dalam satu cluster (walaupun untuk production sebenar, cluster berasingan adalah lebih selamat). Anda juga boleh set resource quotas per namespace supaya satu environment tidak menggunakan semua resources.
# resource-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: staging-quota
namespace: staging
spec:
hard:
pods: "20"
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16GiNota Beginner: Untuk homelab, anda mungkin cuma guna
defaultnamespace dan itu sudah okay. Tapi bila anda mula deploy multiple aplikasi atau environments, namespaces akan menjadi sangat berguna untuk organisasi.
Troubleshooting K8s
Sebelum kita pergi ke Helm, ini beberapa teknik troubleshooting yang sangat berguna. Anda pasti akan perlukan ini.
# Pod stuck dalam "Pending" state? Check events
kubectl describe pod POD_NAME
# Biasanya sebab: insufficient resources, PVC not bound, atau image pull error
# Pod dalam "CrashLoopBackOff"? Check logs
kubectl logs POD_NAME --previous
# "--previous" tunjuk logs dari container yang crash sebelumnya
# Pod running tapi app tak respond? Exec ke dalam pod
kubectl exec -it POD_NAME -- /bin/sh
# Check process, network, files dari dalam pod
# Service tak boleh diakses? Check endpoints
kubectl get endpoints SERVICE_NAME
# Kalau endpoints kosong, selector mungkin tidak match pod labels
# Check semua events dalam cluster
kubectl get events --sort-by='.lastTimestamp'Debugging Kubernetes boleh jadi frustrating pada awalnya. Tapi selepas anda biasa, pattern yang sama akan muncul berulang kali. Kebanyakan masalah berpunca daripada image pull errors, resource limits yang terlalu ketat, atau selector labels yang tidak match.
Asas Helm
Bila aplikasi anda mempunyai banyak Kubernetes manifests (deployment, service, ingress, configmap, secret, pvc, hpa), managing semua YAML files ini boleh jadi rumit. Helm menyelesaikan masalah ini.
Helm adalah package manager untuk Kubernetes. Fikirkan ia macam apt untuk Ubuntu atau brew untuk macOS, tapi untuk K8s.
Install Helm
# macOS
brew install helm
# Linux
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bashGunakan Helm Charts yang Sedia Ada
# Tambah repository
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
# Search charts
helm search repo postgresql
# Install PostgreSQL
helm install my-postgres bitnami/postgresql \
--set auth.postgresPassword=mypassword \
--set primary.persistence.size=10Gi
# Senarai installations
helm list
# Uninstall
helm uninstall my-postgresBuat Helm Chart Sendiri
# Scaffold chart baru
helm create my-app
# Struktur yang dihasilkan:
# my-app/
# Chart.yaml - Metadata
# values.yaml - Default values
# templates/ - K8s manifest templates
# deployment.yaml
# service.yaml
# ingress.yaml
# hpa.yamlContoh values.yaml:
# values.yaml
replicaCount: 3
image:
repository: ghcr.io/username/my-app
tag: "v1.0.0"
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 80
targetPort: 3000
ingress:
enabled: true
host: myapp.example.com
tls: true
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 10
targetCPUUtilization: 70Kemudian dalam templates, anda gunakan values ini:
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app: {{ .Release.Name }}
template:
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
resources:
{{- toYaml .Values.resources | nindent 12 }}Deploy menggunakan Helm:
# Install
helm install my-release ./my-app
# Upgrade dengan values baru
helm upgrade my-release ./my-app --set image.tag=v1.1.0
# Upgrade dengan custom values file
helm upgrade my-release ./my-app -f production-values.yaml
# Rollback kalau ada masalah
helm rollback my-release 1Kelebihan Helm yang paling jelas ialah anda boleh deploy aplikasi yang sama ke multiple environments dengan hanya menukar values file. Staging guna staging-values.yaml, production guna production-values.yaml. Sama templates, values yang berbeza.
Bila Guna K8s vs Docker Compose?
Ini soalan yang ramai orang tanya. Dan jawapannya bukan “K8s selalu lebih baik.” Ada masa untuk Docker Compose dan ada masa untuk Kubernetes.
Gunakan Docker Compose bila:
- Anda deploy ke satu server sahaja
- Aplikasi anda ada 2 hingga 5 services
- Team anda kecil (1 hingga 3 orang)
- Anda tak perlu auto-scaling
- Budget terhad (K8s memerlukan lebih resources untuk cluster overhead)
- Aplikasi anda bukan mission-critical (sedikit downtime boleh diterima)
Gunakan Kubernetes bila:
- Anda deploy ke multiple servers
- Anda perlukan high availability (zero downtime)
- Anda perlukan auto-scaling
- Anda ada banyak microservices (10+)
- Team anda cukup besar untuk manage K8s
- Anda sudah ada CI/CD pipeline yang mature
Nota Beginner: Jangan gunakan Kubernetes semata-mata kerana ia popular. K8s menambah complexity yang significant. Kalau Docker Compose sudah memenuhi keperluan anda, teruskan gunakannya. Ramai startup berjaya yang run on bare Docker Compose. Gunakan K8s hanya bila anda benar-benar memerlukannya.
Satu pendekatan yang bagus ialah mulakan dengan Docker Compose. Bila anda mula rasa limitasinya (perlu multi-server, perlu auto-scaling, perlu self-healing), barulah migrate ke Kubernetes. Pengalaman anda dengan Docker Compose akan memudahkan transition kerana concepts seperti services, volumes, dan networking wujud dalam kedua-duanya.
Migration Path: Docker Compose ke K8s
Kalau anda decide nak migrate, ini rough steps yang boleh anda ikut:
- Pastikan semua services anda sudah containerized dengan proper Dockerfiles dan images di registry.
- Setup K3s cluster di homelab dulu. Test dengan satu node.
- Convert Docker Compose services ke K8s manifests satu per satu. Mulakan dengan stateless services (web apps, APIs) kerana ia paling mudah.
- Migrate stateful services (databases) terakhir. Ini paling tricky kerana perlu handle persistent storage.
- Update CI/CD pipeline untuk deploy ke K8s cluster.
- Run kedua-dua environments secara selari untuk seketika. Route sebahagian traffic ke K8s untuk validate.
- Fully cutover ke K8s bila anda yakin semuanya stable.
Proses ini mungkin ambil beberapa minggu atau bulan bergantung pada complexity aplikasi anda. Jangan terburu-buru. Lebih baik migrate dengan yakin daripada cepat tapi penuh masalah.
Ada juga tools seperti kompose yang boleh convert Docker Compose files ke K8s manifests secara automatik. Tapi hasilnya biasanya perlu banyak tweaking. Saya recommend tulis K8s manifests sendiri supaya anda benar-benar faham apa yang berlaku.
# Kalau nak cuba kompose
kompose convert -f docker-compose.yml
# Ia akan generate K8s manifests, tapi expect banyak manual adjustmentRingkasan
Dalam bab ini, kita telah bermula dari soalan “Kenapa Kubernetes?” dan sampai ke tahap boleh deploy, expose, scale, dan manage aplikasi di K8s cluster.
Kita telah belajar arsitektur K8s dengan control plane dan worker nodes. Kita telah setup K3s di homelab dan deploy aplikasi pertama lengkap dengan Deployment, Service, Ingress, ConfigMaps, Secrets, persistent storage, dan autoscaling.
Kita juga telah sentuh Helm sebagai cara untuk manage Kubernetes manifests dengan lebih terstruktur. Dan yang paling penting, kita telah bincangkan bila sebenarnya anda perlu K8s dan bila Docker Compose sudah memadai.
Kubernetes adalah perjalanan yang panjang. Apa yang kita cover di sini adalah asas yang kukuh. Dari sini, anda boleh explore topik yang lebih advanced seperti namespaces, network policies, RBAC, service mesh, dan monitoring. Tapi dengan apa yang anda belajar dalam bab ini, anda sudah boleh mula deploy real applications ke K8s cluster anda sendiri.
Bab 6: Infrastructure as Code
Bayangkan anda perlu setup 10 server baru untuk projek baru. Anda SSH satu per satu, install packages, configure firewall, setup users. Lepas dua jam, server ketiga ada masalah sebab anda terlupa satu langkah. Sound familiar?
Inilah masalah yang Infrastructure as Code (IaC) selesaikan. Daripada klik-klik manual atau taip command satu-satu, kita tulis code yang describe infrastructure kita. Code ini boleh di-version, di-review, dan di-reuse. Sama macam anda tulis code untuk application, kita tulis code untuk infrastructure.
Kalau anda pernah setup Proxmox VM secara manual dan rasa frustrated bila kena buat benda yang sama berulang kali, bab ini untuk anda.
Apa yang anda akan belajar:
- Konsep Infrastructure as Code dan kenapa ia penting
- Ansible untuk configuration management (inventory, playbooks, roles)
- Terraform untuk provisioning infrastructure
- Bila guna Ansible, bila guna Terraform
- GitOps workflow untuk manage infrastructure code
- Practical examples yang boleh terus digunakan
Apa Itu Infrastructure as Code?
Infrastructure as Code bermaksud kita define dan manage infrastructure menggunakan code files, bukan manual process. Daripada login ke server dan run commands, kita tulis semua dalam files yang boleh disimpan dalam Git.
Kenapa ini penting? Sebab manual setup ada banyak masalah.
Masalah manual setup:
- Tidak consistent. Server A mungkin berbeza daripada Server B walaupun sepatutnya sama.
- Tidak reproducible. Kalau server crash, boleh ke anda setup semula exactly sama?
- Tidak scalable. Setup 3 server okay, macam mana 30? 300?
- Tidak auditable. Siapa yang buat perubahan itu? Bila? Kenapa?
Kelebihan IaC:
- Consistency. Setiap server di-setup exactly sama.
- Version control. Semua perubahan di-track dalam Git.
- Reusability. Tulis sekali, guna banyak kali.
- Self-documenting. Code itu sendiri adalah dokumentasi.
- Testable. Boleh test infrastructure changes sebelum apply.
Nota Beginner: Kalau anda pernah tulis script Bash untuk automate server setup, anda sebenarnya sudah buat IaC dalam bentuk paling basic. Tools macam Ansible dan Terraform bawa konsep ini ke level yang lebih structured dan powerful.
Declarative vs Imperative
Ada dua pendekatan dalam IaC yang anda perlu faham.
Imperative bermaksud anda tulis step-by-step instructions. “Install Nginx, then copy config file, then restart service.” Bash scripts adalah imperative. Anda beritahu sistem apa nak buat, langkah demi langkah.
Declarative bermaksud anda describe end state yang anda mahukan. “Saya mahu Nginx installed, config file ini di tempat ini, dan service running.” Tools akan figure out sendiri macam mana nak sampai ke state tersebut.
Terraform adalah fully declarative. Anda describe infrastructure yang anda mahu, dan Terraform determine langkah-langkah untuk capai state tersebut. Ansible berada di tengah-tengah. Playbooks ditulis secara imperative (step by step), tapi banyak modules bersifat declarative (mereka check current state sebelum buat changes).
Kelebihan declarative approach adalah idempotency. Anda boleh run code yang sama banyak kali dan hasilnya akan sentiasa sama. Kalau Nginx sudah installed, Ansible tidak akan install lagi. Kalau VM sudah exist, Terraform tidak akan create duplicate.
Mutable vs Immutable Infrastructure
Satu lagi konsep penting. Mutable infrastructure bermaksud anda update server yang sedia ada. SSH masuk, upgrade packages, tukar config. Server itu terus hidup dan berubah over time.
Immutable infrastructure bermaksud anda tidak pernah update server. Bila ada changes, anda create server baru dengan config terbaru dan destroy yang lama. Container-based deployments biasanya mengikut pendekatan ini.
Dalam practice, kebanyakan team guna combination kedua-dua pendekatan. Homelab biasanya mutable (anda SSH masuk dan tweak). Production yang mature biasanya move towards immutable (deploy new, destroy old).
Ansible: Configuration Management
Ansible adalah tool untuk automate configuration dan management server. Ia agentless, bermaksud anda tidak perlu install apa-apa pada target server. Ansible guna SSH untuk connect dan jalankan tasks.
Ini menjadikan Ansible sangat sesuai untuk homelab sebab anda tidak perlu setup agent pada setiap VM atau container. Berbanding tools lain macam Puppet atau Chef yang require agent pada setiap node, Ansible hanya perlukan SSH access dan Python pada target. Kebanyakan Linux distributions sudah ada kedua-duanya secara default.
Install Ansible
Pada Ubuntu atau Debian:
sudo apt update
sudo apt install ansible -yPada macOS:
brew install ansibleVerify installation:
ansible --versionInventory: Senarai Server Anda
Inventory adalah file yang senaraikan semua server yang Ansible akan manage. Paling simple, ia adalah text file dengan IP addresses.
Buat file inventory.ini:
[webservers]
web1 ansible_host=192.168.1.101 ansible_user=syafi
web2 ansible_host=192.168.1.102 ansible_user=syafi
[databases]
db1 ansible_host=192.168.1.110 ansible_user=syafi
[monitoring]
monitor1 ansible_host=192.168.1.120 ansible_user=syafi
[homelab:children]
webservers
databases
monitoring
[all:vars]
ansible_python_interpreter=/usr/bin/python3Anda boleh group server mengikut fungsi. [homelab:children] adalah group yang mengandungi group lain. [all:vars] set variables untuk semua hosts.
Test connection ke semua server:
ansible all -i inventory.ini -m pingKalau semuanya okay, anda akan nampak output SUCCESS untuk setiap host.
Nota Beginner: Pastikan SSH key anda sudah di-copy ke semua target server menggunakan
ssh-copy-id. Ansible bergantung pada SSH untuk connect, jadi passwordless SSH sangat disyorkan.
Playbooks: Resipi Automation Anda
Playbook adalah YAML file yang describe tasks yang Ansible perlu jalankan. Ia macam resipi. Anda tulis langkah-langkah, dan Ansible akan ikut satu per satu.
Contoh playbook setup-webserver.yml untuk setup Nginx web server:
---
- name: Setup Nginx Web Server
hosts: webservers
become: yes
vars:
domain_name: "myapp.homelab.local"
app_port: 3000
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install required packages
apt:
name:
- nginx
- certbot
- python3-certbot-nginx
- ufw
state: present
- name: Configure UFW firewall
ufw:
rule: allow
port: "{{ item }}"
proto: tcp
loop:
- "22"
- "80"
- "443"
- name: Enable UFW
ufw:
state: enabled
default: deny
- name: Copy Nginx config
template:
src: templates/nginx.conf.j2
dest: "/etc/nginx/sites-available/{{ domain_name }}"
notify: Reload Nginx
- name: Enable site
file:
src: "/etc/nginx/sites-available/{{ domain_name }}"
dest: "/etc/nginx/sites-enabled/{{ domain_name }}"
state: link
notify: Reload Nginx
- name: Remove default site
file:
path: /etc/nginx/sites-enabled/default
state: absent
notify: Reload Nginx
handlers:
- name: Reload Nginx
service:
name: nginx
state: reloadedBuat template file templates/nginx.conf.j2:
server {
listen 80;
server_name {{ domain_name }};
location / {
proxy_pass http://127.0.0.1:{{ app_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Jalankan playbook:
ansible-playbook -i inventory.ini setup-webserver.ymlBeberapa perkara penting dalam playbook di atas:
become: yesbermaksud run sebagai root (sudo).varsdefine variables yang boleh digunakan dalam tasks.templatemodule guna Jinja2 templating. Variables dalam{{ }}akan diganti dengan nilai sebenar.handlershanya run bila di-trigger olehnotify. Ini elakkan restart service yang tidak perlu.loopmembolehkan anda repeat task untuk multiple items.
Roles: Organisasi Yang Lebih Baik
Bila playbook anda makin besar, ia jadi susah nak maintain. Roles membantu anda organise tasks, templates, files, dan variables dalam structure yang standard.
Buat role structure:
mkdir -p roles/common/{tasks,handlers,templates,files,vars,defaults}
mkdir -p roles/docker/{tasks,handlers,templates,vars,defaults}File roles/common/tasks/main.yml:
---
- name: Update and upgrade packages
apt:
update_cache: yes
upgrade: safe
cache_valid_time: 3600
- name: Install common packages
apt:
name:
- curl
- wget
- vim
- htop
- git
- ufw
- fail2ban
- unattended-upgrades
state: present
- name: Set timezone
timezone:
name: "{{ timezone }}"
- name: Configure fail2ban
template:
src: jail.local.j2
dest: /etc/fail2ban/jail.local
notify: Restart fail2ban
- name: Enable and start fail2ban
service:
name: fail2ban
state: started
enabled: yesFile roles/common/defaults/main.yml:
---
timezone: "Asia/Kuala_Lumpur"File roles/docker/tasks/main.yml:
---
- name: Install Docker prerequisites
apt:
name:
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
- name: Add Docker GPG key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
apt_repository:
repo: "deb https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Install Docker
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
update_cache: yes
- name: Add user to docker group
user:
name: "{{ ansible_user }}"
groups: docker
append: yes
- name: Start and enable Docker
service:
name: docker
state: started
enabled: yesGuna roles dalam playbook site.yml:
---
- name: Setup all servers
hosts: all
become: yes
roles:
- common
- name: Setup Docker hosts
hosts: webservers
become: yes
roles:
- dockerJalankan:
ansible-playbook -i inventory.ini site.ymlNota Beginner: Saya sangat recommend guna roles dari awal walaupun projek kecil. Bila projek membesar nanti, anda akan berterima kasih pada diri sendiri.
Ansible Galaxy: Roles Dari Community
Anda tidak perlu tulis semua roles dari scratch. Ansible Galaxy menyediakan ribuan roles yang sedia untuk digunakan.
# Install role dari Galaxy
ansible-galaxy install geerlingguy.docker
ansible-galaxy install geerlingguy.nodejs
# Atau guna requirements.ymlBuat file requirements.yml:
---
roles:
- name: geerlingguy.docker
version: "7.1.0"
- name: geerlingguy.nodejs
version: "6.2.0"Install semua roles sekaligus:
ansible-galaxy install -r requirements.ymlAnsible Vault: Manage Secrets
Dalam real-world usage, anda pasti ada passwords, API keys, dan secrets lain yang perlu digunakan dalam playbooks. Jangan hardcode secrets dalam playbook files. Guna Ansible Vault untuk encrypt sensitive data.
# Create encrypted file
ansible-vault create secrets.yml
# Edit encrypted file
ansible-vault edit secrets.yml
# Encrypt existing file
ansible-vault encrypt vars/production.yml
# Run playbook dengan vault
ansible-playbook -i inventory.ini site.yml --ask-vault-passContoh encrypted variables file secrets.yml:
db_password: "super-secret-password-123"
api_key: "sk-1234567890abcdef"
registry_token: "ghp_xxxxxxxxxxxx"Guna dalam playbook:
---
- name: Deploy with secrets
hosts: webservers
become: yes
vars_files:
- secrets.yml
tasks:
- name: Set database password
template:
src: templates/env.j2
dest: /opt/myapp/.env
mode: "0600"Anda boleh commit encrypted files ke Git dengan selamat. Tanpa vault password, tiada siapa boleh baca content sebenar. Ini membolehkan anda version control secrets bersama code tanpa risiko security.
Nota Beginner: Untuk team, consider guna vault password file instead of typing password setiap kali. Simpan password file di tempat selamat dan tambahkan dalam
.gitignore. Jangan sesekali commit vault password file ke Git.
Terraform: Infrastructure Provisioning
Kalau Ansible adalah untuk configure server yang sudah ada, Terraform adalah untuk create infrastructure itu sendiri. Terraform boleh provision VMs, networks, DNS records, cloud resources, dan banyak lagi.
Terraform guna pendekatan declarative. Anda describe apa yang anda mahu, dan Terraform akan figure out macam mana nak capai state tersebut.
Install Terraform
# Ubuntu/Debian
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
# macOS
brew tap hashicorp/tap
brew install hashicorp/tap/terraformKonsep Asas Terraform
Providers adalah plugins yang membolehkan Terraform interact dengan platform tertentu. Ada providers untuk AWS, GCP, Azure, DigitalOcean, Proxmox, dan banyak lagi.
Resources adalah infrastructure objects yang anda mahu create. Contohnya VM, network, firewall rule.
State adalah record tentang infrastructure yang Terraform manage. Terraform simpan state dalam file terraform.tfstate. File ini sangat penting. Jangan delete atau edit secara manual.
Plan adalah preview tentang apa yang Terraform akan buat sebelum ia buat. Ini membolehkan anda review changes sebelum apply.
Variables membolehkan anda parameterize configuration. Daripada hardcode values, guna variables supaya code lebih flexible dan reusable.
Outputs adalah values yang Terraform display selepas apply. Contohnya IP address VM yang baru dibuat. Outputs juga boleh digunakan oleh modules lain.
Data Sources membolehkan anda query information daripada provider tanpa create resources. Contohnya, query AMI ID terbaru atau existing VPC ID.
Terraform workflow sangat straightforward. Anda tulis code, run terraform init untuk download providers, run terraform plan untuk preview, dan run terraform apply untuk execute. Kalau anda mahu destroy semua resources, run terraform destroy. Setiap langkah memberikan anda kawalan penuh terhadap apa yang berlaku kepada infrastructure anda.
Practical: Provision VM di Proxmox
Ini contoh yang sangat relevan untuk homelab. Kita akan guna Terraform untuk create VM di Proxmox.
Buat file main.tf:
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "3.0.1-rc4"
}
}
}
provider "proxmox" {
pm_api_url = "https://192.168.1.10:8006/api2/json"
pm_api_token_id = "terraform@pam!terraform-token"
pm_api_token_secret = var.proxmox_api_token
pm_tls_insecure = true
}
variable "proxmox_api_token" {
description = "Proxmox API token secret"
type = string
sensitive = true
}
variable "vm_count" {
description = "Number of VMs to create"
type = number
default = 2
}
resource "proxmox_vm_qemu" "web_server" {
count = var.vm_count
name = "web-${count.index + 1}"
target_node = "pve"
clone = "ubuntu-template"
cores = 2
memory = 2048
sockets = 1
disk {
storage = "local-lvm"
size = "20G"
type = "scsi"
}
network {
model = "virtio"
bridge = "vmbr0"
}
os_type = "cloud-init"
ipconfig0 = "ip=192.168.1.${110 + count.index}/24,gw=192.168.1.1"
ciuser = "syafi"
sshkeys = file("~/.ssh/id_rsa.pub")
lifecycle {
ignore_changes = [
network,
]
}
}
output "vm_ips" {
value = [for vm in proxmox_vm_qemu.web_server : vm.default_ipv4_address]
}
Buat file terraform.tfvars (jangan commit file ini ke Git):
proxmox_api_token = "your-api-token-secret-here"
vm_count = 3
Jalankan Terraform:
# Initialize - download providers
terraform init
# Preview changes
terraform plan
# Apply changes - create the VMs
terraform apply
# Bila nak destroy semua
terraform destroyterraform plan akan tunjukkan apa yang akan dibuat. Review dengan teliti sebelum run terraform apply. Ini salah satu kekuatan Terraform. Anda boleh nampak exactly apa yang akan berubah sebelum ia berlaku.
Nota Beginner: Sentiasa run
terraform plansebelumterraform apply. Jadikan ia habit. Dalam production, ini boleh selamatkan anda daripada accidentally delete database atau resources penting.
Practical: Provision di DigitalOcean
Untuk cloud, prosesnya sangat serupa. Cuma provider yang berbeza.
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {
token = var.do_token
}
variable "do_token" {
description = "DigitalOcean API token"
type = string
sensitive = true
}
resource "digitalocean_droplet" "web" {
image = "ubuntu-24-04-x64"
name = "web-production"
region = "sgp1"
size = "s-1vcpu-1gb"
ssh_keys = [digitalocean_ssh_key.default.fingerprint]
tags = ["web", "production"]
}
resource "digitalocean_ssh_key" "default" {
name = "my-ssh-key"
public_key = file("~/.ssh/id_rsa.pub")
}
resource "digitalocean_firewall" "web" {
name = "web-firewall"
droplet_ids = [digitalocean_droplet.web.id]
inbound_rule {
protocol = "tcp"
port_range = "22"
source_addresses = ["0.0.0.0/0"]
}
inbound_rule {
protocol = "tcp"
port_range = "80"
source_addresses = ["0.0.0.0/0"]
}
inbound_rule {
protocol = "tcp"
port_range = "443"
source_addresses = ["0.0.0.0/0"]
}
outbound_rule {
protocol = "tcp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0"]
}
}
output "web_ip" {
value = digitalocean_droplet.web.ipv4_address
}
Perhatikan pattern yang sama. Define provider, declare resources, run plan dan apply. Ini keindahan Terraform. Workflow yang sama untuk mana-mana platform.
Nota Beginner: Satu kesilapan yang biasa pada permulaan ialah meletakkan semua resources dalam satu file
main.tfyang panjang. Walaupun Terraform membenarkan ini, practice yang lebih baik ialah pecahkan kepada beberapa files. Contohnyaproviders.tfuntuk provider configuration,variables.tfuntuk variable declarations,main.tfuntuk resources utama,outputs.tfuntuk output values, danfirewall.tfuntuk security rules. Terraform akan automatically load semua.tffiles dalam satu directory.
Terraform Modules: Reusable Components
Modules membolehkan anda package Terraform code untuk digunakan semula. Bayangkan anda selalu create VM dengan pattern yang sama. Jadikan ia module.
Structure:
terraform/
modules/
web-server/
main.tf
variables.tf
outputs.tf
environments/
staging/
main.tf
production/
main.tf
File modules/web-server/variables.tf:
variable "name" {
description = "Server name"
type = string
}
variable "size" {
description = "Droplet size"
type = string
default = "s-1vcpu-1gb"
}
variable "region" {
description = "Droplet region"
type = string
default = "sgp1"
}
File modules/web-server/main.tf:
resource "digitalocean_droplet" "this" {
image = "ubuntu-24-04-x64"
name = var.name
region = var.region
size = var.size
}
File modules/web-server/outputs.tf:
output "ip_address" {
value = digitalocean_droplet.this.ipv4_address
}
output "id" {
value = digitalocean_droplet.this.id
}
Guna module dalam environments/production/main.tf:
module "web_app" {
source = "../../modules/web-server"
name = "production-web"
size = "s-2vcpu-4gb"
region = "sgp1"
}
module "web_staging" {
source = "../../modules/web-server"
name = "staging-web"
size = "s-1vcpu-1gb"
region = "sgp1"
}
output "production_ip" {
value = module.web_app.ip_address
}
Terraform State Management
State file adalah jantung Terraform. Ia track mapping antara code anda dan real infrastructure. Untuk projek solo atau homelab, local state file okay. Tetapi untuk team, anda perlu remote state.
Contoh remote state menggunakan S3 (atau DigitalOcean Spaces):
terraform {
backend "s3" {
bucket = "my-terraform-state"
key = "production/terraform.tfstate"
region = "ap-southeast-1"
endpoint = "https://sgp1.digitaloceanspaces.com"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_requesting_account_id = true
skip_s3_checksum = true
}
}
Nota Beginner: Jangan sekali-kali commit
terraform.tfstateke Git. File ini mungkin mengandungi sensitive data seperti passwords dan API keys. Tambahkan*.tfstatedan*.tfstate.backupdalam.gitignoreanda.
Terraform Commands Yang Perlu Anda Tahu
Selain init, plan, apply, dan destroy, ada beberapa commands lain yang berguna dalam daily workflow.
# Format semua Terraform files mengikut standard style
terraform fmt
# Validate configuration tanpa access provider APIs
terraform validate
# Tunjuk current state
terraform show
# List semua resources dalam state
terraform state list
# Import existing resource ke Terraform state
terraform import aws_instance.web i-1234567890abcdef0
# Remove resource dari state tanpa destroy
terraform state rm aws_instance.old_server
# Refresh state untuk match actual infrastructure
terraform refresh
# Output values
terraform outputterraform import sangat berguna bila anda ada existing infrastructure yang anda mahu start manage dengan Terraform. Daripada recreate, anda boleh import resource tersebut ke state file.
terraform fmt pastikan semua files anda mengikut standard formatting. Saya recommend run command ini sebelum setiap commit. Anda boleh setup pre-commit hook untuk automate ini.
Ansible vs Terraform: Bila Guna Yang Mana?
Soalan ini sering ditanya. Jawapan ringkas: guna kedua-duanya, untuk tujuan yang berbeza.
Terraform untuk provisioning. Create VMs, networks, DNS records, cloud resources. Terraform tahu macam mana nak create, update, dan destroy infrastructure.
Ansible untuk configuration. Install software, configure services, deploy applications, manage users. Ansible tahu macam mana nak configure server yang sudah wujud.
Workflow yang biasa:
- Terraform create VM baru.
- Terraform output IP address VM tersebut.
- Ansible ambil IP address itu dan configure VM. Install Docker, setup firewall, deploy app.
Contoh integration. Terraform create VM, kemudian trigger Ansible:
resource "proxmox_vm_qemu" "app_server" {
name = "app-server"
target_node = "pve"
clone = "ubuntu-template"
cores = 4
memory = 4096
provisioner "local-exec" {
command = "sleep 30 && ansible-playbook -i '${self.default_ipv4_address},' setup.yml"
}
}
Ini adalah pattern yang sangat powerful. Infrastructure provisioning dan configuration management dalam satu workflow.
Satu lagi scenario biasa. Anda guna Terraform untuk create cloud resources (VMs, databases, load balancers), kemudian Ansible untuk deploy dan configure applications pada VMs tersebut. Terraform output boleh dijadikan Ansible inventory secara dynamic.
Contoh generate Ansible inventory daripada Terraform output:
# Dapatkan IP dari Terraform output dan run Ansible
terraform output -json vm_ips | jq -r '.[]' > /tmp/inventory.txt
ansible-playbook -i /tmp/inventory.txt deploy.ymlIni membolehkan anda automate keseluruhan flow dari infrastructure creation sampai application deployment dalam satu pipeline.
GitOps: Manage Infrastructure Dengan Git
GitOps bermaksud Git menjadi single source of truth untuk infrastructure anda. Semua changes melalui Git. Tiada manual changes.
Structure Repository
infra/
ansible/
inventory/
homelab.ini
production.ini
playbooks/
setup-webserver.yml
deploy-app.yml
roles/
common/
docker/
nginx/
ansible.cfg
requirements.yml
terraform/
modules/
web-server/
database/
environments/
homelab/
main.tf
terraform.tfvars
production/
main.tf
terraform.tfvars
.gitignore
README.md
.gitignore Untuk IaC
# Terraform
*.tfstate
*.tfstate.backup
.terraform/
*.tfvars
!*.tfvars.example
crash.log
# Ansible
*.retry
# Secrets
.env
*secret*
*credential*
Nota Beginner: Buat file
terraform.tfvars.exampledengan dummy values sebagai reference. Ini membantu team members baru tahu variables apa yang diperlukan tanpa expose actual secrets.
Workflow GitOps
Workflow yang saya recommend:
- Buat branch baru untuk setiap infrastructure change.
- Tulis code Terraform atau Ansible.
- Run plan/check untuk preview changes.
- Buat Pull Request untuk review.
- Merge selepas approved.
- Apply changes (boleh automate dengan CI/CD).
# Buat branch untuk infrastructure change
git checkout -b feat/add-monitoring-server
# Buat perubahan pada Terraform files
# Edit main.tf
# Run terraform plan untuk verify
terraform plan -out=plan.out
# Commit dan push
git add .
git commit -m "feat: add monitoring server"
git push origin feat/add-monitoring-server
# Buat PR, review, merge, then applyDalam production environment yang mature, terraform apply dijalankan oleh CI/CD pipeline selepas PR di-merge. Ini bermakna tiada siapa yang run Terraform dari laptop mereka. Semua melalui pipeline yang controlled dan auditable.
Ringkasan
Infrastructure as Code mengubah cara kita manage infrastructure daripada manual, error-prone process kepada automated, repeatable, dan version-controlled workflow.
Perkara penting yang anda belajar:
- IaC menyelesaikan masalah consistency, reproducibility, dan scalability.
- Ansible sesuai untuk configuration management. Guna inventories untuk senarai server, playbooks untuk tasks, dan roles untuk organisasi.
- Terraform sesuai untuk infrastructure provisioning. Guna providers untuk connect ke platform, resources untuk define infrastructure, dan modules untuk reusability.
- Guna Terraform untuk create infrastructure, Ansible untuk configure. Kedua-dua tools saling melengkapi.
- GitOps menjadikan Git sebagai single source of truth. Semua changes melalui Git dan boleh di-track, di-review, dan di-audit.
- Jangan commit secrets dan state files ke Git.
Pada bab seterusnya, kita akan belajar tentang monitoring dan observability. Sebab infrastructure yang automated tanpa monitoring adalah macam kereta tanpa dashboard. Anda tidak tahu apa yang sedang berlaku.
Bab 7: Monitoring dan Observability
Anda sudah belajar automate infrastructure dengan Ansible dan Terraform. Server sudah up, apps sudah deploy. Tapi macam mana anda tahu semuanya berjalan dengan baik? Macam mana anda tahu disk hampir penuh sebelum ia benar-benar penuh? Macam mana anda tahu response time API anda makin perlahan?
Ini di mana monitoring masuk. Monitoring bukan sekadar “nice to have”. Dalam production, ia adalah keperluan mutlak. Saya pernah belajar perkara ini secara hard way. Server homelab saya down selama 2 hari dan saya tidak perasan sebab tiada monitoring. Jangan jadi macam saya.
Observability pula adalah konsep yang lebih luas. Ia bukan sekadar tahu “server up atau down”, tetapi memahami kenapa sesuatu berlaku dalam sistem anda.
Apa yang anda akan belajar:
- Tiga pillars of observability: metrics, logs, traces
- Setup Prometheus dan Grafana untuk metrics dan dashboards
- Loki untuk centralized logging
- Alerting dengan Alertmanager, PagerDuty, dan Telegram
- Uptime monitoring dengan Uptime Kuma
- Konsep SLI, SLO, dan SLA
- Asas incident response
- Practical: setup full monitoring stack untuk web app
Tiga Pillars of Observability
Observability dibina atas tiga komponen utama.
1. Metrics
Metrics adalah numerical data yang menunjukkan status sistem pada sesuatu masa. Contohnya CPU usage 45%, memory usage 2.1GB, request count 1500 per minute, response time 120ms.
Metrics sangat baik untuk dashboards dan alerting sebab ia lightweight dan mudah di-aggregate.
2. Logs
Logs adalah event records yang describe apa yang berlaku dalam sistem. Contohnya “User john logged in at 14:23”, “Database connection failed: timeout after 30s”. Logs memberi context yang metrics tidak boleh bagi.
3. Traces
Traces track perjalanan sesuatu request melalui pelbagai services. Dalam microservices architecture, satu user request mungkin melalui 5 atau 6 services. Traces membantu anda faham di mana bottleneck berlaku.
Untuk homelab dan early production, fokus pada metrics dan logs dulu. Traces menjadi penting bila anda mula guna microservices architecture.
Monitoring vs Observability
Ramai orang guna istilah ini secara interchangeable, tapi sebenarnya ada perbezaan.
Monitoring bermaksud anda tahu apa yang nak dipantau. Anda setup dashboard untuk CPU usage, memory usage, response time. Anda tahu soalan yang nak ditanya.
Observability bermaksud sistem anda boleh memberitahu anda apa yang berlaku walaupun anda tidak tahu soalan yang nak ditanya. Bila ada masalah baru yang anda tidak pernah jangka, observability membolehkan anda investigate dan faham root cause tanpa perlu deploy new instrumentation.
Dalam practice, anda mula dengan monitoring (setup dashboards dan alerts untuk perkara yang anda tahu penting) dan gradually move towards observability (add more instrumentation, structured logging, tracing) seiring sistem anda menjadi lebih complex.
Pull vs Push Model
Prometheus guna pull model. Ia secara aktif scrape metrics daripada targets pada interval yang ditetapkan. Ini berbeza daripada push model (macam StatsD atau InfluxDB Telegraf) di mana applications push metrics ke central server.
Kelebihan pull model: Prometheus tahu bila sesuatu target down (sebab ia gagal scrape). Ia juga mudah untuk debug sebab anda boleh manually access metrics endpoint di browser.
Kekurangan pull model: tidak ideal untuk short-lived jobs atau serverless functions. Untuk kes macam ini, Prometheus ada Pushgateway.
Prometheus + Grafana: The Power Duo
Prometheus adalah monitoring system yang collect dan store metrics. Grafana adalah visualization tool yang buat dashboards cantik daripada data Prometheus. Bersama, mereka adalah stack monitoring paling popular dalam dunia DevOps.
Setup Prometheus dan Grafana Dengan Docker Compose
Buat file docker-compose.monitoring.yml:
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus/alert-rules.yml:/etc/prometheus/alert-rules.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=30d'
- '--web.enable-lifecycle'
ports:
- "9090:9090"
restart: unless-stopped
networks:
- monitoring
grafana:
image: grafana/grafana:latest
container_name: grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD}
- GF_INSTALL_PLUGINS=grafana-clock-panel
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
ports:
- "3000:3000"
restart: unless-stopped
depends_on:
- prometheus
networks:
- monitoring
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.rootfs=/rootfs'
- '--path.sysfs=/host/sys'
ports:
- "9100:9100"
restart: unless-stopped
networks:
- monitoring
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
container_name: cadvisor
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- "8080:8080"
restart: unless-stopped
networks:
- monitoring
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
volumes:
- ./alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml
ports:
- "9093:9093"
restart: unless-stopped
networks:
- monitoring
volumes:
prometheus_data:
grafana_data:
networks:
monitoring:
driver: bridgeNode Exporter collect metrics daripada host machine (CPU, memory, disk, network). cAdvisor collect metrics daripada Docker containers. Kedua-duanya feed data ke Prometheus.
Konfigurasi Prometheus
Buat file prometheus/prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
- "alert-rules.yml"
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
scrape_configs:
- job_name: "prometheus"
static_configs:
- targets: ["localhost:9090"]
- job_name: "node-exporter"
static_configs:
- targets: ["node-exporter:9100"]
- job_name: "cadvisor"
static_configs:
- targets: ["cadvisor:8080"]
- job_name: "web-app"
static_configs:
- targets: ["web-app:8000"]
metrics_path: /metrics
scrape_interval: 10sscrape_interval bermaksud Prometheus akan collect metrics setiap 15 saat. Untuk web app, kita set 10 saat sebab kita mahu data yang lebih granular.
PromQL: Query Language Untuk Metrics
PromQL adalah bahasa query untuk Prometheus. Anda perlu tahu basics untuk buat dashboards dan alerts yang berguna.
Beberapa contoh PromQL yang sering digunakan:
# CPU usage percentage
100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
# Memory usage percentage
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
# Disk usage percentage
(1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100
# HTTP request rate (requests per second)
rate(http_requests_total[5m])
# HTTP error rate (5xx responses)
rate(http_requests_total{status=~"5.."}[5m])
# 95th percentile response time
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# Container CPU usage
rate(container_cpu_usage_seconds_total{name!=""}[5m]) * 100
# Container memory usage
container_memory_usage_bytes{name!=""} / 1024 / 1024
Nota Beginner: Jangan takut dengan PromQL. Mulakan dengan query simple macam
up(tunjuk mana targets yang up) dannode_memory_MemAvailable_bytes(memory available). Lepas tu explore dari situ.
Grafana Dashboards
Selepas Prometheus dan Grafana running, buka Grafana di http://localhost:3000. Login dengan admin credentials.
Langkah pertama, tambah Prometheus sebagai data source:
- Pergi ke Settings > Data Sources > Add data source.
- Pilih Prometheus.
- URL:
http://prometheus:9090. - Klik Save & Test.
Untuk dashboards, anda tidak perlu buat dari scratch. Import dashboards yang sedia ada dari Grafana community.
Dashboard IDs yang saya recommend:
- 1860 untuk Node Exporter Full. Tunjuk semua metrics host.
- 893 untuk Docker dan system monitoring.
- 14282 untuk cAdvisor.
Pergi ke Dashboards > Import > masukkan dashboard ID > pilih Prometheus data source > Import.
Untuk custom dashboard, contoh panel configuration:
{
"title": "Request Rate",
"type": "timeseries",
"datasource": "Prometheus",
"targets": [
{
"expr": "rate(http_requests_total[5m])",
"legendFormat": "{{method}} {{path}}"
}
]
}Loki: Centralized Logging
Grafana Loki adalah log aggregation system yang designed untuk work dengan Grafana. Ia macam Prometheus, tapi untuk logs. Loki tidak index full log text, ia hanya index labels. Ini menjadikan ia sangat efficient dari segi storage.
Tambah Loki dan Promtail ke Docker Compose:
loki:
image: grafana/loki:latest
container_name: loki
volumes:
- loki_data:/loki
- ./loki/loki-config.yml:/etc/loki/config.yml
command: -config.file=/etc/loki/config.yml
ports:
- "3100:3100"
restart: unless-stopped
networks:
- monitoring
promtail:
image: grafana/promtail:latest
container_name: promtail
volumes:
- /var/log:/var/log:ro
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail/promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
restart: unless-stopped
depends_on:
- loki
networks:
- monitoringFile promtail/promtail-config.yml:
server:
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
labels:
job: varlogs
__path__: /var/log/*.log
- job_name: docker
static_configs:
- targets:
- localhost
labels:
job: docker
__path__: /var/lib/docker/containers/*/*-json.log
pipeline_stages:
- json:
expressions:
log: log
stream: stream
time: time
- output:
source: logPromtail collect logs dan hantar ke Loki. Tambah Loki sebagai data source dalam Grafana sama macam anda tambah Prometheus. URL: http://loki:3100.
Dalam Grafana, anda boleh query logs dengan LogQL:
# Semua logs dari docker job
{job="docker"}
# Filter logs yang contain "error"
{job="docker"} |= "error"
# Logs dari specific container
{job="docker", container="web-app"} |= "500"
# Count errors per minute
count_over_time({job="docker"} |= "error" [1m])
Alerting: Jangan Tunggu User Complain
Monitoring tanpa alerting adalah macam pasang CCTV tapi tiada siapa yang tengok. Anda perlu alerts yang memberitahu anda bila ada masalah.
Alert Rules Dalam Prometheus
Buat file prometheus/alert-rules.yml:
groups:
- name: system-alerts
rules:
- alert: HighCPUUsage
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
for: 5m
labels:
severity: warning
annotations:
summary: "High CPU usage on {{ $labels.instance }}"
description: "CPU usage is above 80% for 5 minutes. Current value: {{ $value }}%"
- alert: HighMemoryUsage
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
for: 5m
labels:
severity: warning
annotations:
summary: "High memory usage on {{ $labels.instance }}"
description: "Memory usage is above 85%. Current value: {{ $value }}%"
- alert: DiskSpaceLow
expr: (1 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"})) * 100 > 85
for: 10m
labels:
severity: critical
annotations:
summary: "Low disk space on {{ $labels.instance }}"
description: "Disk usage is above 85%. Current value: {{ $value }}%"
- alert: ServiceDown
expr: up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Service {{ $labels.job }} is down"
description: "{{ $labels.instance }} has been down for more than 1 minute."
- name: app-alerts
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is above 5%. Current value: {{ $value }}"
- alert: SlowResponseTime
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: "Slow response time detected"
description: "95th percentile response time is above 2 seconds."Field for bermaksud condition mesti bertahan selama tempoh tersebut sebelum alert di-fire. Ini mengelakkan false alarms daripada spike yang sementara.
Alertmanager Configuration
Alertmanager handle routing dan deduplication alerts. Ia boleh hantar notifications ke pelbagai channels.
File alertmanager/alertmanager.yml:
global:
resolve_timeout: 5m
route:
receiver: "telegram"
group_by: ["alertname", "severity"]
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- receiver: "telegram-critical"
match:
severity: critical
repeat_interval: 1h
receivers:
- name: "telegram"
telegram_configs:
- bot_token: "${TELEGRAM_BOT_TOKEN}"
chat_id: ${TELEGRAM_CHAT_ID}
message: |
{{ range .Alerts }}
{{ .Labels.severity | toUpper }}: {{ .Annotations.summary }}
{{ .Annotations.description }}
{{ end }}
- name: "telegram-critical"
telegram_configs:
- bot_token: "${TELEGRAM_BOT_TOKEN}"
chat_id: ${TELEGRAM_CHAT_ID}
message: |
CRITICAL ALERT
{{ range .Alerts }}
{{ .Annotations.summary }}
{{ .Annotations.description }}
{{ end }}Nota Beginner: Saya recommend guna Telegram bot untuk alerting dalam homelab. Ia free, mudah setup, dan anda akan terima notifications terus pada phone. Untuk production, consider PagerDuty atau Opsgenie yang ada on-call rotation features.
Untuk setup Telegram bot: 1. Chat dengan @BotFather di Telegram. 2. Buat bot baru dan dapatkan bot token. 3. Buat group atau channel dan dapatkan chat ID. 4. Masukkan token dan chat ID dalam Alertmanager config.
Uptime Monitoring Dengan Uptime Kuma
Uptime Kuma adalah self-hosted uptime monitoring tool. Ia check sama ada services anda accessible dari luar. Ia sangat mudah untuk setup dan mempunyai interface yang cantik.
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
volumes:
- uptime_kuma_data:/app/data
ports:
- "3001:3001"
restart: unless-stopped
networks:
- monitoringSelepas deploy, buka http://localhost:3001 dan configure monitors:
- HTTP(s) monitor untuk web apps.
- TCP monitor untuk databases dan services.
- Ping monitor untuk network devices.
- DNS monitor untuk domain resolution.
Uptime Kuma boleh hantar notifications ke Telegram, Discord, Slack, dan banyak lagi. Ia juga boleh generate status page yang boleh anda share dengan users.
Saya guna Uptime Kuma untuk monitor semua services dalam homelab saya. Setiap kali sesuatu down, saya dapat notification di Telegram dalam masa 60 saat.
Best Practices Untuk Alerting
Alerting yang baik adalah tentang balance. Terlalu banyak alerts menyebabkan alert fatigue. Anda mula ignore alerts, dan bila ada real problem, anda miss it. Terlalu sedikit alerts bermaksud anda tidak tahu bila ada masalah.
Beberapa guidelines yang saya ikut:
Alert on symptoms, not causes. Alert bila response time tinggi (symptom), bukan bila CPU tinggi (cause). CPU tinggi mungkin normal semasa batch processing. Response time tinggi sentiasa bermaksud users affected.
Setiap alert perlu action. Kalau anda receive alert dan response anda adalah “okay, noted” tanpa buat apa-apa, alert tersebut perlu dibuang atau ditukar threshold. Setiap alert sepatutnya require seseorang investigate atau take action.
Group dan deduplicate alerts. Alertmanager handle ini dengan group_by. Kalau 10 servers semuanya report CPU tinggi pada masa yang sama, anda mahu satu alert yang summarize situasi, bukan 10 separate notifications.
Classify severity levels. Saya guna tiga levels. Warning bermaksud investigate bila sempat, biasanya dalam beberapa jam. Critical bermaksud perlu perhatian segera, service mungkin affected. Emergency bermaksud outage sedang berlaku, users affected right now.
SLI, SLO, dan SLA
Konsep ini penting bila anda mula deal dengan production systems dan users sebenar.
SLI (Service Level Indicator) adalah measurement sebenar performance service anda. Contoh: 99.5% requests berjaya, 95th percentile response time 200ms.
SLO (Service Level Objective) adalah target yang anda tetapkan. Contoh: “99.9% uptime setiap bulan”, “95th percentile response time mestilah di bawah 500ms”.
SLA (Service Level Agreement) adalah agreement formal dengan customer. Kalau anda gagal meet SLA, biasanya ada consequences seperti credit atau refund.
Untuk homelab, anda tidak perlu SLA. Tapi menetapkan SLOs membantu anda decide berapa banyak effort nak letak pada reliability.
Contoh SLO untuk web app:
- Availability: 99.9% (bermaksud maximum 43.8 minit downtime sebulan)
- Response time: p95 di bawah 500ms
- Error rate: kurang daripada 0.1%
Buat Grafana dashboard yang track SLIs anda supaya anda sentiasa tahu sama ada anda meet SLOs atau tidak.
Asas Incident Response
Bila alert berbunyi, anda perlu tahu apa nak buat. Incident response yang baik bermakna masalah diselesaikan dengan cepat dan systematic.
Langkah-langkah basic:
- Acknowledge. Terima alert dan maklumkan team bahawa anda sedang investigate.
- Assess. Tentukan severity. Adakah ia affect users? Berapa ramai?
- Mitigate. Selesaikan masalah secepat mungkin. Kadang-kadang ini bermaksud rollback, kadang-kadang restart service.
- Communicate. Update stakeholders tentang status.
- Resolve. Fix the root cause.
- Post-mortem. Selepas incident selesai, tulis post-mortem. Apa yang berlaku, kenapa, dan macam mana nak prevent pada masa depan.
Nota Beginner: Post-mortem bukan untuk blame orang. Ia adalah blameless exercise untuk belajar daripada masalah. Fokus pada sistem dan proses, bukan individu. Dalam DevOps culture, kesilapan adalah peluang untuk improve.
Contoh Post-Mortem Template
Untuk reference, ini template post-mortem yang simple tapi effective:
# Incident Post-Mortem: [Tajuk Incident]
**Tarikh:** 2026-03-15
**Severity:** Critical
**Duration:** 45 minit (14:00 - 14:45 MYT)
**Author:** Syafi
## Summary
Web app down selama 45 minit disebabkan disk penuh pada database server.
## Timeline
- 14:00 - Alert "DiskSpaceLow" triggered pada db1
- 14:05 - On-call engineer acknowledge alert
- 14:10 - Investigate dan confirm disk 100% full
- 14:20 - Clear old log files, free up 5GB space
- 14:30 - Database service recovered
- 14:45 - All services confirmed healthy
## Root Cause
Database logs tidak di-rotate dan mengisi disk selama 3 bulan.
## Action Items
- [ ] Setup log rotation pada semua database servers
- [ ] Tambah alert untuk disk usage > 70% (early warning)
- [ ] Automate log cleanup dengan cron jobBiasakan diri tulis post-mortem walaupun untuk incident kecil di homelab. Ini membina habit yang sangat berharga untuk production environment.
Practical: Full Monitoring Stack Untuk Web App
Jom combine semuanya. Katakan anda ada Node.js web app yang anda mahu monitor sepenuhnya.
Pertama, app anda perlu expose metrics. Guna library prom-client:
const express = require('express');
const promClient = require('prom-client');
const app = express();
// Create metrics
const httpRequestsTotal = new promClient.Counter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'path', 'status']
});
const httpRequestDuration = new promClient.Histogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration in seconds',
labelNames: ['method', 'path'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5]
});
// Collect default metrics (CPU, memory, etc.)
promClient.collectDefaultMetrics();
// Middleware to track metrics
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer({ method: req.method, path: req.path });
res.on('finish', () => {
httpRequestsTotal.inc({ method: req.method, path: req.path, status: res.statusCode });
end();
});
next();
});
// Metrics endpoint for Prometheus
app.get('/metrics', async (req, res) => {
res.set('Content-Type', promClient.register.contentType);
res.end(await promClient.register.metrics());
});
app.get('/', (req, res) => {
res.json({ status: 'ok' });
});
app.listen(8000, () => {
console.log('App running on port 8000');
});Untuk Python Flask app pula, anda boleh guna prometheus_flask_instrumentator:
from flask import Flask
from prometheus_flask_instrumentator import FlaskInstrumentator
app = Flask(__name__)
FlaskInstrumentator().instrument(app)
@app.route('/')
def hello():
return {'status': 'ok'}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)Idea utama sama untuk mana-mana language. Expose /metrics endpoint, dan Prometheus akan scrape data dari situ. Kebanyakan frameworks mempunyai library yang memudahkan proses ini.
Kemudian, jalankan full monitoring stack:
docker compose -f docker-compose.monitoring.yml up -dVerify semua containers running:
docker compose -f docker-compose.monitoring.yml psSekarang buka Prometheus di http://localhost:9090/targets untuk confirm semua scrape targets berstatus UP. Kalau ada yang DOWN, check network connectivity dan pastikan port betul.
Sekarang anda mempunyai:
- Prometheus (port 9090) yang collect metrics daripada app, node exporter, dan cAdvisor.
- Grafana (port 3000) untuk dashboards.
- Loki (port 3100) untuk centralized logs.
- Alertmanager (port 9093) untuk routing alerts ke Telegram.
- Uptime Kuma (port 3001) untuk uptime monitoring.
Ini adalah setup monitoring yang solid untuk homelab dan boleh di-scale untuk production.
Monitoring Checklist
Sebelum anda consider monitoring stack anda “complete”, pastikan anda ada perkara berikut:
Infrastructure level: - CPU, memory, dan disk usage untuk setiap server. - Network traffic in dan out. - Docker container health dan resource usage.
Application level: - Request rate (requests per second). - Error rate (percentage of 5xx responses). - Response time (p50, p95, p99). - Active connections atau concurrent users.
Business level: - Signup rate, login rate, atau transaction rate. - Key business metrics yang relevant.
Alerting: - Alert untuk service down. - Alert untuk high error rate. - Alert untuk disk space low. - Alert untuk SSL certificate expiry. - Notification channel yang anda actually check (Telegram, email).
Mulakan dengan yang basic dan tambah secara gradual. Anda tidak perlu setup semuanya dalam satu hari. Yang penting adalah anda mula dan terus improve monitoring coverage anda over time.
Ringkasan
Monitoring dan observability bukan luxury. Ia adalah keperluan untuk mana-mana sistem yang anda care tentang uptime dan performance.
Perkara penting yang anda belajar:
- Tiga pillars of observability: metrics (numerical data), logs (event records), dan traces (request journeys).
- Prometheus collect dan store metrics. Grafana visualize data. Bersama, mereka adalah monitoring stack yang powerful.
- PromQL adalah query language untuk Prometheus. Belajar basics untuk buat dashboards dan alerts yang berguna.
- Loki adalah Prometheus untuk logs. Ia lightweight dan integrate baik dengan Grafana.
- Alerting penting supaya anda tahu ada masalah sebelum users complain. Guna Telegram untuk homelab, PagerDuty untuk production.
- Uptime Kuma adalah tool simple tapi powerful untuk monitor availability services anda.
- SLI mengukur performance, SLO menetapkan target, SLA adalah agreement formal dengan customer.
- Incident response yang baik adalah systematic dan blameless. Sentiasa buat post-mortem untuk belajar daripada masalah.
Pada bab seterusnya, kita akan belajar tentang perjalanan dari homelab ke cloud. Bila homelab tidak cukup lagi dan anda perlu scale ke cloud providers.
Bab 8: Dari Homelab ke Cloud
Ada satu titik dalam perjalanan homelab di mana anda mula terfikir, “Okay, macam mana kalau saya nak serve users sebenar?” Mungkin anda sudah buat app yang kawan-kawan guna, atau anda mahu host side project yang perlu uptime 24/7 tanpa bergantung pada power supply rumah anda.
Homelab adalah tempat terbaik untuk belajar. Tapi production workload ada requirements yang berbeza. Reliability, scalability, global accessibility. Ini bukan bermaksud homelab tidak berguna. Sebaliknya, skill yang anda belajar di homelab adalah foundation yang sangat solid untuk cloud.
Bab ini akan guide anda dari homelab ke cloud. Kita akan explore kenapa dan bila perlu cloud, bandingkan providers, dan deploy app sebenar ke cloud step by step.
Apa yang anda akan belajar:
- Bila homelab tidak mencukupi dan kenapa cloud diperlukan
- Perbandingan cloud providers (AWS, GCP, Azure, DigitalOcean)
- Konsep cloud yang penting (VPC, IAM, load balancer, managed services)
- Cost management supaya bil cloud tidak mengejutkan anda
- Pendekatan hybrid: homelab dan cloud bersama
- Practical: deploy app ke DigitalOcean dan AWS
- Cloud certifications yang berbaloi untuk kerjaya
Kenapa Cloud? Bila Homelab Tidak Cukup
Homelab ada limitasi yang cloud boleh selesaikan.
Reliability. Rumah anda ada power outage, internet down, atau hardware failure. Cloud providers ada multiple data centers dengan redundancy yang anda tidak mampu replicate di rumah.
Scalability. Anda boleh tambah RAM pada server homelab, tapi ada limit fizikal. Dalam cloud, anda boleh scale dari 1 server ke 100 server dalam masa beberapa minit.
Global reach. Kalau users anda di US dan Europe, serve dari server di rumah anda di Malaysia bermakna latency yang tinggi. Cloud providers ada data centers di seluruh dunia.
Compliance. Sesetengah industries require data disimpan di certified data centers. Homelab tidak memenuhi requirements ini.
Bandwidth. Home internet biasanya ada upload speed yang rendah dan ISP mungkin block certain ports.
Tapi ingat, cloud bukan jawapan untuk semua benda. Untuk development, testing, learning, dan personal projects, homelab masih sangat relevan dan jauh lebih murah.
Nota Beginner: Jangan terus migrate semua ke cloud. Start dengan identify workloads yang benar-benar perlu cloud (public-facing apps, production APIs) dan keep yang lain di homelab (development, CI runners, media server, monitoring).
Perbandingan Cloud Providers
Amazon Web Services (AWS)
AWS adalah cloud provider paling besar dan paling mature. Ia mempunyai lebih daripada 200 services.
Kelebihan: - Services paling lengkap. Apa sahaja yang anda perlukan, AWS mungkin ada. - Community paling besar. Banyak tutorials, documentation, dan Stack Overflow answers. - Free tier yang generous. 12 bulan free tier untuk banyak services. - Paling banyak job opportunities.
Kekurangan: - Learning curve yang steep. Terlalu banyak services boleh jadi overwhelming. - Pricing yang complex. Bil boleh jadi mengejutkan kalau tidak monitor. - Console UI yang kurang intuitive.
Sesuai untuk: Production workloads, enterprise, kalau anda target kerjaya cloud engineering.
Google Cloud Platform (GCP)
GCP adalah cloud provider dari Google. Strong dalam data analytics, machine learning, dan Kubernetes.
Kelebihan: - Google Kubernetes Engine (GKE) adalah managed Kubernetes terbaik. - BigQuery untuk data analytics sangat powerful. - Pricing lebih transparent dan predictable. - UI yang lebih clean daripada AWS.
Kekurangan: - Kurang services berbanding AWS. - Community lebih kecil. - Google ada track record shutdown products.
Sesuai untuk: Data-heavy applications, Kubernetes workloads, machine learning.
Microsoft Azure
Azure adalah cloud provider dari Microsoft. Strong dalam enterprise dan integration dengan Microsoft ecosystem.
Kelebihan: - Integration sempurna dengan Active Directory, Office 365, dan Windows ecosystem. - Hybrid cloud capabilities yang baik. - Enterprise compliance dan security features.
Kekurangan: - Naming convention yang confusing (mereka rename services kerap). - Documentation kadang-kadang tidak up to date. - Pricing boleh jadi complex.
Sesuai untuk: Enterprise environments, organisations yang sudah guna Microsoft stack.
DigitalOcean
DigitalOcean bukan “big three” tapi ia sangat popular di kalangan developers dan small teams.
Kelebihan: - Paling simple dan straightforward. - Pricing yang transparent dan predictable. - Documentation yang sangat baik dan tutorials yang banyak. - Perfect untuk small to medium projects.
Kekurangan: - Kurang services advanced berbanding big three. - Tidak suitable untuk enterprise-scale workloads. - Kurang regions berbanding AWS atau GCP.
Sesuai untuk: Side projects, startups, small businesses, belajar cloud basics.
Recommendation saya untuk orang yang baru mula? DigitalOcean untuk hands-on learning dan side projects. AWS untuk kerjaya dan production workloads. Sebabnya simple. DigitalOcean tidak overwhelming, dan AWS adalah standard industri.
Perbandingan Ringkas
| Feature | AWS | GCP | Azure | DigitalOcean |
|---|---|---|---|---|
| Jumlah services | 200+ | 100+ | 200+ | 20+ |
| Free tier | 12 bulan | 12 bulan + always free | 12 bulan | $200 credit 60 hari |
| Pricing complexity | Tinggi | Sederhana | Tinggi | Rendah |
| Learning curve | Steep | Moderate | Steep | Easy |
| Job market | Terbesar | Growing | Enterprise | Niche |
| Region terdekat | Singapore | Singapore, Jakarta | Singapore | Singapore |
Satu tip penting. Jangan terlalu worry tentang memilih provider yang “betul”. Skill cloud anda transferable antara providers. Kalau anda faham konsep VPC di AWS, anda boleh setup VPC di GCP atau Azure juga. Yang berbeza cuma interface dan naming.
Konsep Cloud Yang Penting
Sebelum deploy apa-apa ke cloud, anda perlu faham beberapa konsep asas.
VPC (Virtual Private Cloud)
VPC adalah virtual network anda dalam cloud. Bayangkan ia macam network rumah anda, tapi di cloud. Anda control IP ranges, subnets, routing, dan firewall rules.
VPC: 10.0.0.0/16
├── Public Subnet: 10.0.1.0/24 (web servers, load balancers)
├── Private Subnet: 10.0.2.0/24 (app servers)
└── Database Subnet: 10.0.3.0/24 (databases, caches)
Best practice: letak web servers dalam public subnet (boleh diakses dari internet), app servers dan databases dalam private subnet (hanya boleh diakses dari dalam VPC).
IAM (Identity and Access Management)
IAM control siapa boleh buat apa dalam cloud account anda. Ini critical untuk security.
Principles: - Principle of least privilege. Beri hanya permission yang diperlukan, jangan lebih. - Jangan guna root account untuk daily tasks. - Enable MFA (Multi-Factor Authentication) untuk semua users. - Guna roles dan policies, bukan hardcode credentials.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-app-bucket/*"
}
]
}Policy di atas hanya allow read dan write ke specific S3 bucket. Tidak boleh delete, tidak boleh access bucket lain. Ini contoh least privilege.
Load Balancer
Load balancer distribute traffic ke multiple servers. Kalau satu server down, traffic automatically pergi ke server yang lain.
Jenis load balancer: - Application Load Balancer (ALB). Layer 7, faham HTTP. Boleh route berdasarkan URL path atau hostname. - Network Load Balancer (NLB). Layer 4, handle millions of requests per second. Untuk TCP/UDP traffic.
Managed Services
Managed services bermaksud cloud provider handle maintenance, patching, backups, dan scaling untuk anda.
Contoh: - Managed Database. RDS (AWS), Cloud SQL (GCP). Anda tidak perlu worry tentang database backups, patching, atau replication. - Managed Kubernetes. EKS (AWS), GKE (GCP). Control plane di-manage oleh provider. - Managed Cache. ElastiCache (AWS). Redis atau Memcached yang di-manage.
Managed services kos lebih mahal daripada self-hosted, tapi ia save masa dan reduce operational burden. Untuk team kecil, managed services biasanya berbaloi.
Nota Beginner: Mulakan dengan managed services dan hanya self-host kalau anda ada sebab yang kukuh (cost, compliance, specific requirements). Masa engineer lebih mahal daripada kos managed services.
Cost Management
Bil cloud boleh jadi sangat mahal kalau anda tidak careful. Berikut beberapa tips.
Set budget alerts. Semua cloud providers ada billing alerts. Set alert pada 50%, 80%, dan 100% budget anda. Jangan tunggu sampai hujung bulan untuk check bil.
Guna right-sizing. Jangan guna instance besar kalau workload anda kecil. Monitor actual usage dan downsize kalau perlu.
Shutdown apa yang tidak digunakan. Development servers tidak perlu running 24/7. Automate start/stop schedule.
Guna reserved instances atau savings plans. Kalau anda tahu workload akan consistent selama setahun atau lebih, reserved pricing boleh save 30% hingga 60%.
Monitor daily. Install tools macam Infracost untuk Terraform. Ia tunjuk cost estimate sebelum anda provision resources.
# Install Infracost
brew install infracost
# Generate cost estimate dari Terraform code
infracost breakdown --path .Output:
Name Monthly Qty Unit Monthly Cost
aws_instance.web
├─ Instance usage (t3.small) 730 hours $18.98
└─ root_block_device
└─ Storage (gp3, 20 GB) 20 GB $1.60
aws_db_instance.main
├─ Database instance (db.t3.micro) 730 hours $14.98
└─ Storage (gp2, 20 GB) 20 GB $2.30
OVERALL TOTAL $37.86
Nota Beginner: Rule of thumb saya: kalau anda belajar, gunakan free tier. Kalau anda test, gunakan smallest instance. Kalau anda production, size berdasarkan actual load testing, bukan guessing.
Contoh Monthly Cost Estimation
Untuk memberi gambaran, ini anggaran kos bulanan untuk setup web app yang typical:
Setup minimum (side project): - 1x DigitalOcean Droplet (s-1vcpu-1gb): $6/bulan - Managed database (basic): $15/bulan - Domain name: ~$1/bulan - Total: sekitar $22/bulan
Setup production (small business): - 2x AWS EC2 t3.small: ~$38/bulan - 1x RDS db.t3.micro: ~$15/bulan - Application Load Balancer: ~$18/bulan - S3 storage (10GB): ~$0.25/bulan - CloudFront CDN: ~$5/bulan - Total: sekitar $76/bulan
Bandingkan dengan homelab di mana anda mungkin bayar RM50 hingga RM100 lebihan bil elektrik sahaja. Untuk learning dan development, homelab jauh lebih jimat. Tapi untuk serve real users dengan reliability, cloud berbaloi dengan kosnya.
Tagging Resources
Satu practice yang ramai orang abaikan tapi sangat penting untuk cost management. Tag semua cloud resources anda.
resource "aws_instance" "web" {
# ... config ...
tags = {
Name = "web-production"
Environment = "production"
Project = "mywebapp"
Owner = "syafi"
CostCenter = "engineering"
}
}
Dengan tags, anda boleh filter bil mengikut project, environment, atau team. Ini sangat membantu bila anda ada multiple projects dan mahu tahu project mana yang paling mahal.
Pendekatan Hybrid: Homelab + Cloud
Anda tidak perlu pilih satu sahaja. Ramai DevOps engineers guna pendekatan hybrid.
Homelab untuk: - Development dan testing. - CI/CD runners (save cloud compute cost). - Internal tools (wiki, password manager, file sharing). - Learning dan experimentation. - Media server dan personal apps.
Cloud untuk: - Production-facing applications. - APIs yang perlu low latency globally. - Services yang perlu high availability. - Workloads yang perlu elastic scaling.
Architecture contoh hybrid:
Internet
|
[Cloud - DO/AWS]
├── Load Balancer
├── Web App (Production)
├── Database (Managed)
└── CDN
|
VPN Tunnel (WireGuard)
|
[Homelab]
├── GitLab / Gitea
├── CI/CD Runners
├── Dev/Staging Environment
├── Monitoring (Grafana)
└── Backup Storage
WireGuard VPN connect homelab dan cloud securely. CI/CD runners di homelab buat builds dan push ke cloud deployment. Monitoring di homelab observe kedua-dua environments.
Practical: Deploy App ke DigitalOcean
Jom deploy web app ke DigitalOcean step by step.
Step 1: Setup DigitalOcean Account
- Daftar di digitalocean.com.
- Buat API token di Settings > API > Generate New Token.
- Install
doctlCLI.
# macOS
brew install doctl
# Login
doctl auth init
# Paste your API tokenStep 2: Provision Dengan Terraform
Buat file main.tf:
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {
token = var.do_token
}
variable "do_token" {
type = string
sensitive = true
}
variable "ssh_key_fingerprint" {
type = string
}
resource "digitalocean_droplet" "web" {
image = "ubuntu-24-04-x64"
name = "web-production"
region = "sgp1"
size = "s-1vcpu-1gb"
ssh_keys = [var.ssh_key_fingerprint]
tags = ["web", "production"]
user_data = <<-EOF
#!/bin/bash
apt-get update
apt-get install -y docker.io docker-compose-plugin
systemctl enable docker
systemctl start docker
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
EOF
}
resource "digitalocean_firewall" "web" {
name = "web-firewall"
droplet_ids = [digitalocean_droplet.web.id]
inbound_rule {
protocol = "tcp"
port_range = "22"
source_addresses = ["0.0.0.0/0"]
}
inbound_rule {
protocol = "tcp"
port_range = "80"
source_addresses = ["0.0.0.0/0"]
}
inbound_rule {
protocol = "tcp"
port_range = "443"
source_addresses = ["0.0.0.0/0"]
}
outbound_rule {
protocol = "tcp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0"]
}
outbound_rule {
protocol = "udp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0"]
}
}
resource "digitalocean_domain" "default" {
name = "myapp.com"
}
resource "digitalocean_record" "web" {
domain = digitalocean_domain.default.id
type = "A"
name = "@"
value = digitalocean_droplet.web.ipv4_address
}
output "web_ip" {
value = digitalocean_droplet.web.ipv4_address
}
terraform init
terraform plan
terraform applyStep 3: Deploy App Dengan Ansible
Selepas Terraform provision server, guna Ansible untuk deploy app.
Buat deploy-playbook.yml:
---
- name: Deploy Web App
hosts: production
become: yes
vars:
app_name: "mywebapp"
app_image: "ghcr.io/myuser/mywebapp:latest"
app_port: 3000
domain: "myapp.com"
tasks:
- name: Create app directory
file:
path: "/opt/{{ app_name }}"
state: directory
mode: "0755"
- name: Copy docker-compose file
template:
src: templates/docker-compose.prod.yml.j2
dest: "/opt/{{ app_name }}/docker-compose.yml"
- name: Copy Nginx config
template:
src: templates/nginx.prod.conf.j2
dest: "/opt/{{ app_name }}/nginx.conf"
- name: Login to container registry
docker_login:
registry: ghcr.io
username: "{{ registry_user }}"
password: "{{ registry_token }}"
- name: Pull latest image
docker_image:
name: "{{ app_image }}"
source: pull
force_source: yes
- name: Start services
community.docker.docker_compose_v2:
project_src: "/opt/{{ app_name }}"
state: present
pull: "always"
- name: Install Certbot and get SSL certificate
shell: |
snap install --classic certbot
certbot --nginx -d {{ domain }} --non-interactive --agree-tos -m admin@{{ domain }}
args:
creates: "/etc/letsencrypt/live/{{ domain }}"Template templates/docker-compose.prod.yml.j2:
services:
app:
image: {{ app_image }}
container_name: {{ app_name }}
restart: unless-stopped
environment:
- NODE_ENV=production
- PORT={{ app_port }}
ports:
- "{{ app_port }}:{{ app_port }}"
nginx:
image: nginx:alpine
container_name: nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- /etc/letsencrypt:/etc/letsencrypt:ro
depends_on:
- appDeploy:
ansible-playbook -i "DROPLET_IP," deploy-playbook.ymlPractical: Deploy ke AWS
Untuk AWS, prosesnya similar tapi dengan lebih services involved.
Setup AWS CLI
# Install
brew install awscli
# Configure
aws configure
# Masukkan Access Key ID, Secret Access Key, region (ap-southeast-1)Terraform Untuk AWS
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-southeast-1"
}
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "production-vpc"
}
}
resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-southeast-1a"
map_public_ip_on_launch = true
tags = {
Name = "public-subnet"
}
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
}
resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}
# Security Group
resource "aws_security_group" "web" {
name = "web-sg"
vpc_id = aws_vpc.main.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# EC2 Instance
resource "aws_instance" "web" {
ami = "ami-0f935a2ecd3a7bd5c"
instance_type = "t3.small"
key_name = "my-key-pair"
subnet_id = aws_subnet.public.id
vpc_security_group_ids = [aws_security_group.web.id]
root_block_device {
volume_size = 20
volume_type = "gp3"
}
tags = {
Name = "web-production"
Environment = "production"
}
}
output "public_ip" {
value = aws_instance.web.public_ip
}
Ini adalah setup basic AWS. Dalam real production, anda biasanya akan tambah Application Load Balancer, Auto Scaling Group, RDS untuk database, dan S3 untuk static assets. Tapi untuk start, ini sudah mencukupi.
terraform init
terraform plan
terraform applySelepas instance running, guna Ansible untuk configure dan deploy app sama macam contoh DigitalOcean di atas.
Cloud Certifications Yang Berbaloi
Certifications membantu validate knowledge anda dan membuka peluang kerjaya. Berikut certifications yang saya recommend.
AWS Solutions Architect Associate (SAA-C03)
Ini adalah certification cloud paling popular. Ia cover architectural best practices, core AWS services, security, dan cost optimization. Sangat berbaloi untuk sesiapa yang mahu masuk cloud engineering.
Tips preparation: - Belajar 2 hingga 3 bulan secara konsisten. - Guna kursus dari Adrian Cantrill atau Stephane Maarek. - Buat hands-on labs, bukan sekadar baca. - Practice exams dari Tutorial Dojo sangat membantu.
Certified Kubernetes Administrator (CKA)
Kalau anda target DevOps atau platform engineering role, CKA sangat valuable. Ia adalah hands-on exam di mana anda perlu solve real Kubernetes problems.
Tips preparation: - Setup Kubernetes cluster di homelab anda untuk practice. - Kursus dari Mumshad Mannambeth di KodeKloud sangat bagus. - Practice dengan killer.sh (percuma bila register exam). - Belajar kubectl shortcuts. Masa sangat terhad dalam exam.
Certification Path Yang Saya Recommend
- AWS Cloud Practitioner kalau anda benar-benar baru dengan cloud. Ia basic tapi foundation yang baik.
- AWS Solutions Architect Associate sebagai certification utama.
- CKA kalau anda banyak kerja dengan containers dan Kubernetes.
- Terraform Associate kalau anda banyak guna Terraform (ini relatively easy).
Nota Beginner: Certification bukan pengganti pengalaman. Ia melengkapi. Kalau anda ada homelab experience dan certifications, anda sudah ahead of banyak candidates. Employers value combination hands-on skills dan validated knowledge.
Ringkasan
Perjalanan dari homelab ke cloud bukan lompatan yang besar kalau anda sudah ada foundation yang betul. Konsep networking, Linux administration, containers, dan IaC yang anda belajar di homelab semua applicable di cloud.
Perkara penting yang anda belajar:
- Cloud diperlukan untuk reliability, scalability, dan global reach yang homelab tidak boleh provide.
- DigitalOcean paling sesuai untuk belajar dan small projects. AWS paling sesuai untuk kerjaya dan production.
- Faham konsep VPC, IAM, load balancer, dan managed services sebelum deploy ke cloud.
- Cost management sangat penting. Set budget alerts, right-size instances, dan shutdown apa yang tidak digunakan.
- Pendekatan hybrid (homelab + cloud) adalah pragmatic. Guna homelab untuk development, cloud untuk production.
- Terraform dan Ansible yang anda belajar di Bab 6 boleh digunakan untuk provision dan configure cloud resources.
- Cloud certifications (AWS SAA, CKA) membantu validate knowledge dan buka peluang kerjaya. Combine dengan hands-on homelab experience untuk impact maximum.
Anda tidak perlu tinggalkan homelab anda. Jadikan ia lab R&D peribadi dan gunakan cloud untuk serve dunia.
Satu nasihat terakhir. Jangan tunggu sampai anda rasa “ready” untuk mula dengan cloud. Buat akaun free tier, deploy satu app kecil, dan belajar dari situ. Pengalaman hands-on lebih berharga daripada membaca 10 buku tentang cloud architecture. Homelab anda sudah beri anda foundation yang kukuh. Sekarang masanya untuk expand horizon anda ke cloud.
Bab 9: DevSecOps - Security Dalam Pipeline DevOps
Bila kita bercakap tentang DevOps, ramai yang fokus pada speed. Deploy laju, iterate cepat, automate semua benda. Tapi ada satu perkara yang selalu jadi mangsa bila kita terlalu ghairah dengan kelajuan: security.
Saya pernah buat kesilapan ini. Satu projek kecil, saya push code terus ke production tanpa scan apa-apa. Dua minggu kemudian, saya perasan ada API key yang ter-commit dalam repo. Nasib baik projek tu kecil dan API key tu boleh di-revoke dengan cepat. Tapi bayangkan kalau itu berlaku dalam production system yang handle data pelanggan.
DevSecOps bukan sekadar buzzword. Ia adalah pendekatan di mana security menjadi tanggungjawab semua orang dalam team, bukan hanya security team sahaja. Dan yang paling penting, security dimasukkan dari awal proses development, bukan ditambah di hujung sebagai afterthought.
Apa yang anda akan belajar:
- Konsep shift-left security dan kenapa ia penting
- SAST tools untuk scan source code
- DAST tools untuk scan running applications
- Container image scanning dengan Trivy dan Grype
- Secrets management yang betul
- Supply chain security dan SBOM
- Compliance as code
- Hands-on: Tambah security scanning dalam CI/CD pipeline
Shift-Left Security: Tangkap Masalah Lebih Awal
Dalam model tradisional, security testing berlaku di hujung sekali. Developer tulis code, QA test, kemudian baru security team buat assessment. Masalahnya? Kalau jumpa vulnerability di hujung, kos untuk fix adalah sangat tinggi. Code dah deploy, dependencies dah banyak, dan kadang-kadang kena redesign seluruh architecture.
Shift-left bermaksud kita gerakkan security testing ke kiri dalam timeline development. Lebih awal kita tangkap masalah, lebih murah dan mudah untuk fix.
Bayangkan macam ini: lebih senang repair foundation rumah sebelum rumah siap dibina, berbanding selepas rumah dah siap dan orang dah duduk di dalamnya.
Nota Beginner: Shift-left bukan bermaksud security team tak diperlukan lagi. Ia bermaksud developer juga ambil tanggungjawab untuk security, dan tools automatik membantu tangkap masalah common sebelum code sampai ke production.
Dalam konteks CI/CD pipeline, shift-left security bermaksud kita tambah security checks pada setiap stage:
- Code commit - Pre-commit hooks untuk scan secrets
- Build stage - SAST scanning pada source code
- Test stage - DAST scanning pada running application
- Package stage - Container image scanning
- Deploy stage - Infrastructure security checks
SAST: Static Application Security Testing
SAST tools menganalisis source code tanpa menjalankan aplikasi. Ia scan code anda untuk mencari patterns yang dikenali sebagai vulnerability. Contohnya SQL injection, cross-site scripting (XSS), atau penggunaan cryptographic functions yang lemah.
SonarQube
SonarQube adalah salah satu SAST tool yang paling popular. Ia bukan sahaja scan untuk security issues, tetapi juga code quality, bugs, dan code smells.
Untuk setup SonarQube dalam homelab, anda boleh gunakan Docker:
# docker-compose.yml
version: '3'
services:
sonarqube:
image: sonarqube:community
ports:
- "9000:9000"
environment:
- SONAR_JDBC_URL=jdbc:postgresql://db:5432/sonar
- SONAR_JDBC_USERNAME=sonar
- SONAR_JDBC_PASSWORD=sonar
volumes:
- sonarqube_data:/opt/sonarqube/data
- sonarqube_extensions:/opt/sonarqube/extensions
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_USER=sonar
- POSTGRES_PASSWORD=sonar
- POSTGRES_DB=sonar
volumes:
- postgresql_data:/var/lib/postgresql/data
volumes:
sonarqube_data:
sonarqube_extensions:
postgresql_data:Selepas SonarQube running, anda boleh integrate dengan CI/CD pipeline. Dalam GitHub Actions, tambahkan step ini:
- name: SonarQube Scan
uses: sonarqube-quality-gate-action@v1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}Semgrep
Semgrep adalah SAST tool yang lebih ringan dan mudah digunakan. Ia menggunakan pattern-based approach dan mempunyai banyak community rules. Saya suka Semgrep kerana ia cepat dan boleh run secara local tanpa perlu server.
# Install Semgrep
pip install semgrep
# Scan project dengan default rules
semgrep --config auto .
# Scan dengan specific ruleset
semgrep --config p/owasp-top-ten .
semgrep --config p/secrets .Dalam CI/CD pipeline:
- name: Semgrep Scan
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/secrets
p/owasp-top-tenNota Beginner: Mula dengan Semgrep kalau anda baru dalam SAST. Ia lebih mudah di-setup, cepat, dan free. SonarQube bagus untuk team yang lebih besar dan perlukan dashboard serta tracking over time.
DAST: Dynamic Application Security Testing
Kalau SAST scan source code, DAST pula scan running application. Ia bertindak seperti attacker sebenar, menghantar pelbagai jenis request ke application anda untuk mencari vulnerability.
OWASP ZAP
OWASP ZAP (Zed Attack Proxy) adalah DAST tool open-source yang paling popular. Ia boleh digunakan secara manual melalui GUI, atau secara automatik dalam CI/CD pipeline.
# Dalam GitHub Actions
- name: OWASP ZAP Scan
uses: zaproxy/action-full-scan@v0.7.0
with:
target: 'https://staging.myapp.com'
rules_file_name: '.zap/rules.tsv'
cmd_options: '-a'Untuk baseline scan yang lebih cepat:
- name: OWASP ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.7.0
with:
target: 'https://staging.myapp.com'Satu tip penting: jalankan DAST scan terhadap staging environment, bukan production. DAST scan boleh menjadi agresif dan mungkin menyebabkan side effects pada application anda.
Container Image Scanning
Bila anda menggunakan containers, anda bukan sahaja perlu scan application code, tetapi juga container images. Base images mungkin mengandungi vulnerability yang sudah diketahui. Libraries yang di-install mungkin sudah outdated.
Trivy
Trivy daripada Aqua Security adalah container scanner yang saya paling suka. Ia cepat, comprehensive, dan mudah digunakan.
# Scan container image
trivy image myapp:latest
# Scan dengan severity filter
trivy image --severity HIGH,CRITICAL myapp:latest
# Scan filesystem
trivy fs --security-checks vuln,secret,config .
# Scan Kubernetes cluster
trivy k8s --report summary clusterDalam CI/CD pipeline:
- name: Build Image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'Setting exit-code: '1' bermaksud pipeline akan gagal jika jumpa vulnerability dengan severity HIGH atau CRITICAL. Ini penting untuk memastikan images yang vulnerable tidak sampai ke production.
Grype
Grype daripada Anchore adalah alternatif kepada Trivy. Ia juga cepat dan mempunyai database vulnerability yang comprehensive.
# Install Grype
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s
# Scan image
grype myapp:latest
# Scan dengan output format
grype myapp:latest -o json > grype-results.json
# Fail jika jumpa severity tertentu
grype myapp:latest --fail-on highNota Beginner: Pilih satu sahaja untuk mula. Trivy atau Grype, kedua-duanya bagus. Anda boleh cuba kedua-dua dan pilih yang mana anda lebih selesa. Yang penting ialah anda ada scanning dalam pipeline.
Secrets Management
Ini adalah area di mana saya pernah buat kesilapan, dan saya pasti ramai developer lain juga pernah. Hardcode password dalam code, commit API keys ke Git, atau simpan credentials dalam plain text config files.
HashiCorp Vault
Vault adalah gold standard untuk secrets management. Ia menyimpan secrets secara encrypted, boleh generate dynamic secrets, dan mempunyai audit log yang detailed.
# Start Vault server (dev mode untuk belajar)
vault server -dev
# Simpan secret
vault kv put secret/myapp/database \
username="dbuser" \
password="supersecretpassword"
# Baca secret
vault kv get secret/myapp/databaseDalam Kubernetes, anda boleh gunakan Vault Agent Injector untuk automatically inject secrets ke dalam pods:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-db-creds: "secret/data/myapp/database"
spec:
serviceAccountName: myapp
containers:
- name: myapp
image: myapp:latestSealed Secrets
Kalau anda menggunakan GitOps (dan anda patut!), ada satu masalah. Macam mana nak simpan secrets dalam Git repo kalau secrets tu sensitive? Sealed Secrets daripada Bitnami menyelesaikan masalah ini.
Sealed Secrets encrypt secrets menggunakan public key, dan hanya controller dalam cluster yang ada private key untuk decrypt.
# Install kubeseal CLI
brew install kubeseal
# Create sealed secret
kubectl create secret generic myapp-secret \
--from-literal=password=supersecret \
--dry-run=client -o yaml | \
kubeseal --format yaml > sealed-secret.yamlFile sealed-secret.yaml yang dihasilkan boleh di-commit ke Git dengan selamat kerana ia encrypted. Hanya Sealed Secrets controller dalam cluster anda yang boleh decrypt ia.
Supply Chain Security
Supply chain attacks semakin menjadi ancaman besar. Ingat insiden SolarWinds? Atau log4j? Anda perlu memastikan bahawa semua components dalam supply chain anda boleh dipercayai.
Container Image Signing
Gunakan Cosign untuk sign dan verify container images:
# Install Cosign
brew install cosign
# Generate key pair
cosign generate-key-pair
# Sign image
cosign sign --key cosign.key myregistry/myapp:v1.0
# Verify image
cosign verify --key cosign.pub myregistry/myapp:v1.0SBOM (Software Bill of Materials)
SBOM adalah senarai lengkap semua components dalam software anda. Ia seperti senarai ramuan pada produk makanan. Jika ada vulnerability baru ditemui dalam sesuatu library, anda boleh dengan cepat check sama ada software anda terjejas.
# Generate SBOM dengan Syft
syft myapp:latest -o spdx-json > sbom.json
# Scan SBOM untuk vulnerabilities
grype sbom:sbom.jsonCompliance as Code
Dalam banyak industri, ada compliance requirements yang perlu dipatuhi. Daripada manually check compliance, anda boleh automate ia menggunakan tools seperti Open Policy Agent (OPA).
# policy.rego - Pastikan containers tidak run sebagai root
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
container := input.request.object.spec.containers[_]
not container.securityContext.runAsNonRoot
msg := sprintf("Container '%v' mesti run sebagai non-root", [container.name])
}
deny[msg] {
input.request.kind.kind == "Pod"
container := input.request.object.spec.containers[_]
not container.resources.limits
msg := sprintf("Container '%v' mesti ada resource limits", [container.name])
}
Nota Beginner: Compliance as code nampak advanced, tetapi konsepnya simple. Anda tulis rules dalam code, dan system akan automatically check sama ada infrastructure anda comply dengan rules tersebut. Mula dengan rules yang simple dulu.
Hands-On: Security Scanning Dalam CI/CD Pipeline
Sekarang mari kita gabungkan semua yang kita belajar. Berikut adalah contoh complete CI/CD pipeline dengan security scanning di setiap stage:
name: Secure CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
secret-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Scan for secrets
uses: trufflesecurity/trufflehog@main
with:
extra_args: --only-verified
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Semgrep SAST Scan
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/secrets
p/owasp-top-ten
build-and-scan:
needs: [secret-scan, sast]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker Image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy Image Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
format: 'table'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: myapp:${{ github.sha }}
format: spdx-json
output-file: sbom.json
- name: Push Image
if: github.ref == 'refs/heads/main'
run: |
docker tag myapp:${{ github.sha }} myregistry/myapp:${{ github.sha }}
docker push myregistry/myapp:${{ github.sha }}
dast:
needs: [build-and-scan]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to Staging
run: echo "Deploy to staging environment"
- name: OWASP ZAP Baseline Scan
uses: zaproxy/action-baseline@v0.7.0
with:
target: 'https://staging.myapp.com'
deploy:
needs: [dast]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to Production
run: echo "Deploy to production"Pipeline ini menjalankan: 1. Secret scanning menggunakan TruffleHog untuk pastikan tiada secrets yang ter-commit 2. SAST scanning menggunakan Semgrep untuk scan source code 3. Container image scanning menggunakan Trivy 4. SBOM generation untuk supply chain visibility 5. DAST scanning menggunakan OWASP ZAP terhadap staging
Setiap stage bertindak sebagai quality gate. Jika mana-mana scan gagal, pipeline berhenti dan code tidak sampai ke production.
Ringkasan
DevSecOps bukan tentang menambah kerja. Ia tentang mengautomasikan security supaya ia berlaku secara konsisten tanpa memperlahankan development process secara ketara.
Perkara utama yang perlu diingat:
- Shift-left security bermaksud tangkap masalah lebih awal. Lebih awal anda tangkap, lebih murah untuk fix.
- SAST tools seperti SonarQube dan Semgrep scan source code untuk vulnerability.
- DAST tools seperti OWASP ZAP scan running applications seperti seorang attacker.
- Container scanning dengan Trivy atau Grype pastikan images anda tidak ada known vulnerabilities.
- Secrets management menggunakan Vault atau Sealed Secrets, bukan hardcoded credentials.
- Supply chain security melalui image signing dan SBOM memberikan visibility terhadap semua components.
- Compliance as code mengautomasikan security policies.
Anda tidak perlu implement semua ini sekaligus. Mulakan dengan satu atau dua tools, dan tambahkan secara berperingkat. Yang penting ialah anda mula memikirkan security sebagai bahagian integral dalam DevOps workflow anda, bukan sesuatu yang ditambah kemudian.
Security bukan destination, ia adalah journey. Dan dengan DevSecOps, journey itu menjadi sebahagian daripada setiap commit, setiap build, dan setiap deployment.
Bab 10: Projek Praktikal - Bina Sendiri, Belajar Sendiri
Teori tanpa praktikal ibarat membaca resipi tanpa pernah memasak. Anda mungkin faham konsepnya, tapi tangan anda belum pernah rasa prosesnya. Bab ini adalah di mana anda benar-benar “masak” sendiri.
Saya sediakan 5 projek praktikal yang boleh anda bina dalam homelab. Setiap projek direka untuk mengukuhkan kemahiran yang anda telah pelajari sepanjang buku ini. Anda boleh buat projek-projek ini secara berurutan, atau pilih mana-mana yang paling menarik minat anda.
Satu nasihat: jangan hanya copy-paste. Cuba fahamkan setiap baris configuration. Bila ada error, baca error message tu dengan teliti. Proses troubleshooting itu sendiri adalah guru yang paling berkesan.
Apa yang anda akan belajar:
- Membina full CI/CD pipeline untuk web application
- Setup GitOps dengan ArgoCD
- Infrastructure provisioning dengan Terraform dan Ansible
- Complete monitoring stack
- Multi-environment setup untuk dev, staging, dan production
Projek 1: Full CI/CD Pipeline untuk Web App
Apa yang projek ini ajar
Projek ini menggabungkan hampir semua yang anda belajar: version control, containerization, CI/CD, dan deployment ke Kubernetes. Anda akan bina satu pipeline yang lengkap dari code push hingga deployment.
Architecture
Developer Push Code
|
v
GitHub Repository
|
v
GitHub Actions (CI)
- Run tests
- Build Docker image
- Push to registry
- Security scan
|
v
Docker Registry
(GitHub Container Registry)
|
v
K3s Cluster (CD)
- Pull image
- Deploy application
- Health check
Langkah-langkah
1. Sediakan sample web application
Kita akan guna satu Node.js app yang simple. Buat repository baru dan tambah files berikut:
// app.js
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.get('/', (req, res) => {
res.json({
message: 'Hello from DevOps Pipeline!',
version: process.env.APP_VERSION || '1.0.0',
environment: process.env.NODE_ENV || 'development'
});
});
app.get('/health', (req, res) => {
res.status(200).json({ status: 'healthy' });
});
app.listen(port, () => {
console.log(`App running on port ${port}`);
});// package.json
{
"name": "devops-demo-app",
"version": "1.0.0",
"scripts": {
"start": "node app.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.2"
},
"devDependencies": {
"jest": "^29.7.0"
}
}2. Buat Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine
WORKDIR /app
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder /app/node_modules ./node_modules
COPY app.js .
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "app.js"]3. Buat GitHub Actions workflow
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
build-and-push:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}'
severity: 'CRITICAL,HIGH'
deploy:
needs: build-and-push
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to K3s
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.K3S_HOST }}
username: ${{ secrets.K3S_USER }}
key: ${{ secrets.K3S_SSH_KEY }}
script: |
kubectl set image deployment/devops-demo \
app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-n production
kubectl rollout status deployment/devops-demo -n production4. Buat Kubernetes manifests
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: devops-demo
namespace: production
spec:
replicas: 2
selector:
matchLabels:
app: devops-demo
template:
metadata:
labels:
app: devops-demo
spec:
containers:
- name: app
image: ghcr.io/yourusername/devops-demo:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 10
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"
---
apiVersion: v1
kind: Service
metadata:
name: devops-demo
namespace: production
spec:
selector:
app: devops-demo
ports:
- port: 80
targetPort: 3000
type: ClusterIPNota Beginner: Projek ini nampak banyak files, tapi setiap satu ada tujuan yang jelas. Mulakan dengan buat app dan Dockerfile dulu. Test secara local. Kemudian baru tambah CI/CD dan Kubernetes deployment. Jangan cuba buat semua sekali gus.
Projek 2: GitOps dengan ArgoCD
Apa yang projek ini ajar
GitOps adalah pendekatan di mana Git repository menjadi single source of truth untuk infrastructure dan application deployment. ArgoCD akan memantau repo anda dan automatically sync perubahan ke cluster.
Architecture
Git Repository (Config Repo)
|
| ArgoCD monitors
v
ArgoCD Server
|
| Syncs desired state
v
K3s Cluster
- Namespace: dev
- Namespace: staging
- Namespace: production
Langkah-langkah
1. Install ArgoCD dalam K3s
# Buat namespace
kubectl create namespace argocd
# Install ArgoCD
kubectl apply -n argocd \
-f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Tunggu semua pods ready
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=argocd-server \
-n argocd --timeout=300s
# Dapatkan initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d
# Port forward untuk akses UI
kubectl port-forward svc/argocd-server -n argocd 8080:4432. Install ArgoCD CLI dan login
# Install CLI
brew install argocd
# Login
argocd login localhost:8080 --username admin --password <password> --insecure3. Buat GitOps config repository
Buat repository baru yang khusus untuk Kubernetes manifests. Strukturnya:
gitops-config/
apps/
devops-demo/
base/
deployment.yaml
service.yaml
kustomization.yaml
overlays/
dev/
kustomization.yaml
staging/
kustomization.yaml
production/
kustomization.yaml
# apps/devops-demo/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml# apps/devops-demo/overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev
bases:
- ../../base
patches:
- patch: |-
- op: replace
path: /spec/replicas
value: 1
target:
kind: Deployment
name: devops-demo# apps/devops-demo/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
bases:
- ../../base
patches:
- patch: |-
- op: replace
path: /spec/replicas
value: 3
target:
kind: Deployment
name: devops-demo4. Buat ArgoCD Application
# argocd-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: devops-demo-production
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/yourusername/gitops-config.git
targetRevision: main
path: apps/devops-demo/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=truekubectl apply -f argocd-app.yamlSekarang, setiap kali anda push perubahan ke gitops-config repository, ArgoCD akan automatically detect dan sync perubahan tersebut ke cluster. Cuba update image tag dalam deployment.yaml dan push. Perhatikan ArgoCD UI untuk melihat sync process berlaku secara automatik.
Nota Beginner: GitOps mungkin terasa over-engineered untuk projek kecil. Tapi bila anda bekerja dengan team dan multiple environments, ia menyelamatkan banyak masa dan mengurangkan human error. Setiap deployment ada audit trail dalam Git history.
Projek 3: Infrastructure Provisioning dengan Terraform + Ansible
Apa yang projek ini ajar
Terraform untuk provision infrastructure (create VMs, networks, storage) dan Ansible untuk configure infrastructure tersebut (install software, configure services). Kedua-duanya bersama adalah kombinasi yang sangat powerful.
Architecture
Terraform Ansible
(Infrastructure) (Configuration)
| |
v v
Create VMs on Proxmox ---> Install K3s
Create Networks ---> Configure firewall
Create Storage ---> Deploy monitoring
| |
v v
Infrastructure Ready
Langkah-langkah
1. Terraform untuk Proxmox VMs
# main.tf
terraform {
required_providers {
proxmox = {
source = "telmate/proxmox"
version = "3.0.1rc1"
}
}
}
provider "proxmox" {
pm_api_url = var.proxmox_api_url
pm_api_token_id = var.proxmox_token_id
pm_api_token_secret = var.proxmox_token_secret
pm_tls_insecure = true
}
resource "proxmox_vm_qemu" "k3s_master" {
name = "k3s-master"
target_node = "pve"
clone = "ubuntu-cloud-template"
cores = 2
memory = 4096
scsihw = "virtio-scsi-pci"
disk {
storage = "local-lvm"
size = "30G"
type = "scsi"
}
network {
model = "virtio"
bridge = "vmbr0"
}
ipconfig0 = "ip=192.168.1.100/24,gw=192.168.1.1"
sshkeys = file("~/.ssh/id_rsa.pub")
}
resource "proxmox_vm_qemu" "k3s_worker" {
count = 2
name = "k3s-worker-${count.index + 1}"
target_node = "pve"
clone = "ubuntu-cloud-template"
cores = 2
memory = 4096
scsihw = "virtio-scsi-pci"
disk {
storage = "local-lvm"
size = "30G"
type = "scsi"
}
network {
model = "virtio"
bridge = "vmbr0"
}
ipconfig0 = "ip=192.168.1.10${count.index + 1}/24,gw=192.168.1.1"
sshkeys = file("~/.ssh/id_rsa.pub")
}
# Output IP addresses untuk Ansible inventory
output "master_ip" {
value = proxmox_vm_qemu.k3s_master.default_ipv4_address
}
output "worker_ips" {
value = proxmox_vm_qemu.k3s_worker[*].default_ipv4_address
}
# variables.tf
variable "proxmox_api_url" {
description = "Proxmox API URL"
type = string
}
variable "proxmox_token_id" {
description = "Proxmox API token ID"
type = string
sensitive = true
}
variable "proxmox_token_secret" {
description = "Proxmox API token secret"
type = string
sensitive = true
}
2. Ansible untuk configure K3s
# inventory/hosts.yml
all:
children:
masters:
hosts:
k3s-master:
ansible_host: 192.168.1.100
workers:
hosts:
k3s-worker-1:
ansible_host: 192.168.1.101
k3s-worker-2:
ansible_host: 192.168.1.102
vars:
ansible_user: ubuntu
ansible_ssh_private_key_file: ~/.ssh/id_rsa# playbooks/setup-k3s.yml
---
- name: Setup K3s Master
hosts: masters
become: yes
tasks:
- name: Install K3s master
shell: |
curl -sfL https://get.k3s.io | sh -s - \
--write-kubeconfig-mode 644 \
--disable traefik \
--tls-san {{ ansible_host }}
args:
creates: /usr/local/bin/k3s
- name: Get node token
slurp:
src: /var/lib/rancher/k3s/server/node-token
register: node_token
- name: Store token as fact
set_fact:
k3s_token: "{{ node_token.content | b64decode | trim }}"
- name: Setup K3s Workers
hosts: workers
become: yes
vars:
master_ip: "{{ hostvars['k3s-master']['ansible_host'] }}"
k3s_token: "{{ hostvars['k3s-master']['k3s_token'] }}"
tasks:
- name: Install K3s worker
shell: |
curl -sfL https://get.k3s.io | K3S_URL=https://{{ master_ip }}:6443 \
K3S_TOKEN={{ k3s_token }} sh -
args:
creates: /usr/local/bin/k3s-agent3. Jalankan
# Provision VMs
cd terraform/
terraform init
terraform plan
terraform apply
# Configure K3s
cd ../ansible/
ansible-playbook -i inventory/hosts.yml playbooks/setup-k3s.ymlNota Beginner: Projek ini memerlukan Proxmox server. Kalau anda belum ada, anda boleh adapt Terraform config untuk provider lain seperti DigitalOcean atau AWS. Konsep yang sama, cuma provider yang berbeza.
Projek 4: Complete Monitoring Stack
Apa yang projek ini ajar
Monitoring adalah mata dan telinga anda dalam production. Projek ini setup full observability stack: metrics dengan Prometheus, visualization dengan Grafana, logs dengan Loki, dan alerting dengan Alertmanager.
Architecture
Applications & Infrastructure
| | |
v v v
Prometheus Loki Node Exporter
(Metrics) (Logs) (System Metrics)
| | |
+-----+-----+-----------+
|
v
Grafana
(Dashboards)
|
v
Alertmanager
(Notifications)
| |
v v
Slack Email
Langkah-langkah
1. Install kube-prometheus-stack menggunakan Helm
# Add Helm repo
helm repo add prometheus-community \
https://prometheus-community.github.io/helm-charts
helm repo update
# Buat namespace
kubectl create namespace monitoringBuat values file untuk customize installation:
# monitoring-values.yaml
grafana:
adminPassword: "YourSecurePassword123"
ingress:
enabled: true
hosts:
- grafana.homelab.local
additionalDataSources:
- name: Loki
type: loki
url: http://loki-gateway.monitoring.svc.cluster.local
access: proxy
prometheus:
prometheusSpec:
retention: 30d
storageSpec:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 50Gi
alertmanager:
config:
global:
resolve_timeout: 5m
route:
group_by: ['alertname', 'namespace']
group_wait: 10s
group_interval: 5m
repeat_interval: 4h
receiver: 'slack'
routes:
- match:
severity: critical
receiver: 'slack'
repeat_interval: 1h
receivers:
- name: 'slack'
slack_configs:
- api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
channel: '#alerts'
title: '{{ .GroupLabels.alertname }}'
text: >-
{{ range .Alerts }}
*Alert:* {{ .Annotations.summary }}
*Severity:* {{ .Labels.severity }}
*Description:* {{ .Annotations.description }}
{{ end }}# Install stack
helm install monitoring prometheus-community/kube-prometheus-stack \
-n monitoring \
-f monitoring-values.yaml2. Install Loki untuk logs
helm repo add grafana https://grafana.github.io/helm-charts
# Buat Loki values
cat <<EOF > loki-values.yaml
loki:
auth_enabled: false
commonConfig:
replication_factor: 1
storage:
type: filesystem
singleBinary:
replicas: 1
persistence:
size: 20Gi
EOF
helm install loki grafana/loki -n monitoring -f loki-values.yaml3. Install Promtail untuk collect logs
# promtail-values.yaml
config:
clients:
- url: http://loki-gateway.monitoring.svc.cluster.local/loki/api/v1/pushhelm install promtail grafana/promtail -n monitoring -f promtail-values.yaml4. Buat custom alert rules
# custom-alerts.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: custom-alerts
namespace: monitoring
labels:
release: monitoring
spec:
groups:
- name: application-alerts
rules:
- alert: HighErrorRate
expr: |
rate(http_requests_total{status=~"5.."}[5m])
/ rate(http_requests_total[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "More than 5% of requests are failing"
- alert: PodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[15m]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Pod is crash looping"
description: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is restarting frequently"
- alert: HighMemoryUsage
expr: |
container_memory_working_set_bytes{container!=""}
/ container_spec_memory_limit_bytes{container!=""} > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "Container memory usage is high"
description: "Container {{ $labels.container }} in pod {{ $labels.pod }} using more than 85% memory"kubectl apply -f custom-alerts.yamlSelepas semua installed, akses Grafana di grafana.homelab.local. Import dashboard ID 1860 untuk Node Exporter dashboard dan 15760 untuk Kubernetes overview. Anda akan dapat visibility yang lengkap terhadap cluster anda.
Nota Beginner: Monitoring stack ini mungkin memerlukan resources yang agak banyak. Pastikan cluster anda mempunyai sekurang-kurangnya 8GB RAM tersedia. Kalau resource terhad, mulakan dengan Prometheus dan Grafana sahaja, dan tambah Loki kemudian.
Projek 5: Multi-Environment Setup
Apa yang projek ini ajar
Dalam production sebenar, anda tidak deploy terus ke production. Biasanya ada dev, staging, dan production environments. Projek ini mengajar cara setup dan manage multiple environments menggunakan Kubernetes namespaces dan Kustomize.
Architecture
Git Repository
|
v
CI/CD Pipeline
|
+---> Dev Namespace
| (auto-deploy on push)
|
+---> Staging Namespace
| (deploy on PR merge)
|
+---> Production Namespace
(manual approval required)
Langkah-langkah
1. Setup namespaces dan resource quotas
# namespaces.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev
labels:
environment: dev
---
apiVersion: v1
kind: Namespace
metadata:
name: staging
labels:
environment: staging
---
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
environment: production
---
# Resource quota untuk dev (limit resources)
apiVersion: v1
kind: ResourceQuota
metadata:
name: dev-quota
namespace: dev
spec:
hard:
requests.cpu: "2"
requests.memory: 4Gi
limits.cpu: "4"
limits.memory: 8Gi
pods: "20"
---
# Production gets more resources
apiVersion: v1
kind: ResourceQuota
metadata:
name: production-quota
namespace: production
spec:
hard:
requests.cpu: "8"
requests.memory: 16Gi
limits.cpu: "16"
limits.memory: 32Gi
pods: "50"2. Kustomize overlays untuk setiap environment
# base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 1
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: webapp:latest
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: webapp-config
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"# overlays/dev/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: dev
bases:
- ../../base
patches:
- patch: |-
- op: replace
path: /spec/replicas
value: 1
target:
kind: Deployment
configMapGenerator:
- name: webapp-config
literals:
- NODE_ENV=development
- LOG_LEVEL=debug
- DB_HOST=dev-db.internal# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
bases:
- ../../base
patches:
- patch: |-
- op: replace
path: /spec/replicas
value: 3
- op: replace
path: /spec/template/spec/containers/0/resources/requests/memory
value: "256Mi"
- op: replace
path: /spec/template/spec/containers/0/resources/limits/memory
value: "512Mi"
target:
kind: Deployment
configMapGenerator:
- name: webapp-config
literals:
- NODE_ENV=production
- LOG_LEVEL=warn
- DB_HOST=prod-db.internal3. CI/CD pipeline dengan environment promotion
# .github/workflows/multi-env.yml
name: Multi-Environment Deploy
on:
push:
branches: [main, develop]
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy'
required: true
type: choice
options:
- staging
- production
jobs:
build:
runs-on: ubuntu-latest
outputs:
image_tag: ${{ github.sha }}
steps:
- uses: actions/checkout@v4
- name: Build and Push Image
run: |
docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
docker push ghcr.io/${{ github.repository }}:${{ github.sha }}
deploy-dev:
needs: build
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
environment: dev
steps:
- uses: actions/checkout@v4
- name: Deploy to Dev
run: |
cd overlays/dev
kustomize edit set image webapp=ghcr.io/${{ github.repository }}:${{ github.sha }}
kustomize build . | kubectl apply -f -
deploy-staging:
needs: build
if: github.ref == 'refs/heads/main' || github.event.inputs.environment == 'staging'
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- name: Deploy to Staging
run: |
cd overlays/staging
kustomize edit set image webapp=ghcr.io/${{ github.repository }}:${{ github.sha }}
kustomize build . | kubectl apply -f -
deploy-production:
needs: deploy-staging
if: github.event.inputs.environment == 'production'
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.com
steps:
- uses: actions/checkout@v4
- name: Deploy to Production
run: |
cd overlays/production
kustomize edit set image webapp=ghcr.io/${{ github.repository }}:${{ github.sha }}
kustomize build . | kubectl apply -f -
- name: Verify Deployment
run: |
kubectl rollout status deployment/webapp -n production --timeout=300s
kubectl get pods -n production -l app=webappPerhatikan bahawa production deployment menggunakan GitHub Environments dengan protection rules. Anda boleh configure require manual approval sebelum deploy ke production melalui GitHub repository settings.
Nota Beginner: Dalam homelab, ketiga-tiga environments mungkin berada dalam satu cluster yang sama, dibezakan oleh namespaces. Dalam production sebenar, staging dan production biasanya berada dalam clusters yang berbeza untuk isolation yang lebih baik.
Ringkasan
Lima projek ini memberikan anda pengalaman hands-on yang sebenar. Setiap projek membina di atas kemahiran yang anda pelajari dalam bab-bab sebelumnya.
Beberapa tips untuk mendapat manfaat maksimum daripada projek-projek ini:
- Buat satu projek pada satu masa. Jangan rush. Fahami setiap component sebelum bergerak ke projek seterusnya.
- Dokumentasikan pengalaman anda. Tulis blog post atau buat notes tentang apa yang anda belajar dan masalah yang anda hadapi. Ini sangat berguna untuk portfolio anda.
- Experiment dan break things. Homelab adalah tempat yang selamat untuk buat kesilapan. Cuba modify configurations, lihat apa yang berlaku. Belajar dari kesilapan adalah cara paling berkesan.
- Share dengan komuniti. Push projek anda ke GitHub. Ini bukan sahaja membantu orang lain, tetapi juga menunjukkan kemahiran anda kepada potential employers.
Yang paling penting, ingat bahawa setiap DevOps engineer yang berpengalaman pun bermula dari sini. Setiap projek yang anda siapkan membawa anda satu langkah lebih dekat ke matlamat anda.
Bab 11: Kerjaya DevOps - Dari Homelab ke Dunia Sebenar
Sepanjang buku ini, anda telah belajar tentang tools, technologies, dan practices dalam DevOps. Tapi ada satu soalan besar yang mungkin bermain di fikiran anda: “Macam mana nak jadikan semua ini sebagai kerjaya?”
Saya faham perasaan itu. Saya sendiri pernah berada di tempat yang sama. Belajar sendiri, experiment dalam homelab, dan tertanya-tanya sama ada kemahiran yang saya ada cukup untuk dunia sebenar. Jawapan ringkasnya: ya, cukup. Tapi ada beberapa perkara yang anda perlu tahu untuk memaksimumkan peluang anda.
Apa yang anda akan belajar:
- Landscape kerjaya DevOps di Malaysia dan global
- Julat gaji dan apa yang mempengaruhinya
- Kemahiran yang dicari oleh employers
- Certifications yang bernilai
- Cara membina portfolio yang menarik
- Tips resume dan interview
- Freelancing vs full-time
- Learning roadmap untuk jangka panjang
Landscape Kerjaya DevOps
Di Malaysia
DevOps di Malaysia sedang berkembang dengan pesat. Banyak syarikat, terutama yang berkaitan dengan fintech, e-commerce, dan cloud services, sedang aktif mencari DevOps engineers. Bandar-bandar utama seperti Kuala Lumpur, Cyberjaya, dan Penang mempunyai banyak peluang.
Beberapa trend yang saya perhatikan:
- Bank dan institusi kewangan sedang melalui digital transformation dan memerlukan ramai DevOps engineers untuk modernize infrastructure mereka.
- Startups biasanya mencari “full-stack DevOps” yang boleh handle everything dari CI/CD hingga infrastructure.
- Multinational companies (MNCs) seperti yang beroperasi di Malaysia menawarkan exposure kepada large-scale systems dan global practices.
- Government agencies juga mula adopt cloud dan DevOps practices, walaupun pada kadar yang lebih perlahan.
Global (Remote Work)
Salah satu kelebihan besar kerjaya DevOps ialah banyak posisi yang boleh dilakukan secara remote. Ini membuka peluang untuk bekerja dengan syarikat dari seluruh dunia sambil tinggal di Malaysia. Gaji remote biasanya lebih tinggi daripada gaji tempatan, dan dengan kos sara hidup di Malaysia, ini adalah situasi yang sangat menguntungkan.
Platform seperti LinkedIn, Indeed, dan Remote.co mempunyai banyak listing untuk remote DevOps positions. Ada juga platform khusus seperti We Work Remotely dan Remote OK yang fokus pada remote jobs.
Julat Gaji
Mari kita bercakap tentang nombor. Ini adalah anggaran berdasarkan pemerhatian saya terhadap pasaran semasa. Angka sebenar bergantung pada banyak faktor termasuk syarikat, lokasi, dan kemahiran spesifik anda.
Malaysia (bulanan, MYR): - Junior DevOps Engineer (0-2 tahun): RM 4,000 - RM 7,000 - Mid-level DevOps Engineer (2-5 tahun): RM 7,000 - RM 12,000 - Senior DevOps Engineer (5+ tahun): RM 12,000 - RM 20,000 - DevOps Lead/Manager: RM 18,000 - RM 30,000+
Remote/Global (bulanan, USD): - Junior: USD 3,000 - USD 5,000 - Mid-level: USD 5,000 - USD 8,000 - Senior: USD 8,000 - USD 15,000+
Nota Beginner: Jangan terlalu fokus pada gaji di peringkat awal. Fokus pada belajar dan kumpul pengalaman. Gaji akan datang apabila kemahiran anda meningkat. Saya pernah ambil role dengan gaji yang lebih rendah kerana peluang untuk belajar technologies baru, dan keputusan itu terbayar dalam jangka panjang.
Kemahiran yang Dicari oleh Employers
Berdasarkan job listings yang saya sering lihat, ini adalah kemahiran utama yang dicari:
Kemahiran Teknikal (Hard Skills): - Linux administration adalah asas. Hampir semua servers dalam production menggunakan Linux. - Containerization dengan Docker dan orchestration dengan Kubernetes. - CI/CD tools seperti GitHub Actions, GitLab CI, Jenkins, atau ArgoCD. - Infrastructure as Code menggunakan Terraform, Ansible, atau Pulumi. - Cloud platforms terutama AWS, Azure, atau GCP. Sekurang-kurangnya satu. - Scripting dalam Bash, Python, atau Go. - Monitoring dan observability dengan Prometheus, Grafana, ELK stack, atau Datadog. - Networking fundamentals termasuk DNS, load balancing, dan firewalls.
Kemahiran Insaniah (Soft Skills): - Communication adalah yang paling penting. DevOps engineers bekerja di antara development dan operations teams. Anda perlu boleh berkomunikasi dengan kedua-dua pihak. - Problem-solving kerana anda akan menghadapi masalah yang unik setiap hari. - Documentation kerana infrastructure yang tidak didokumentasikan adalah infrastructure yang suatu hari nanti akan menyebabkan masalah besar. - Collaboration kerana DevOps pada dasarnya adalah tentang breaking down silos.
Certifications yang Bernilai
Certifications bukan mandatory, tetapi ia boleh membantu terutama di peringkat awal kerjaya. Ia menunjukkan kepada employers bahawa anda mempunyai pengetahuan yang tervalidasi.
Berikut adalah certifications yang saya recommend, disusun mengikut keutamaan:
Tier 1 (Mula di sini): - AWS Certified Solutions Architect - Associate atau AWS Certified Cloud Practitioner untuk mereka yang baru mula. - Certified Kubernetes Administrator (CKA) adalah sangat bernilai kerana Kubernetes semakin menjadi standard. - Linux Foundation Certified System Administrator (LFCS) untuk membuktikan kemahiran Linux anda.
Tier 2 (Selepas ada pengalaman): - HashiCorp Certified: Terraform Associate untuk Infrastructure as Code. - AWS Certified DevOps Engineer - Professional untuk yang lebih advanced. - Certified Kubernetes Security Specialist (CKS) untuk specialization dalam security.
Tier 3 (Specialization): - Google Cloud Professional DevOps Engineer - Azure DevOps Engineer Expert
Nota Beginner: Jangan collect certifications tanpa tujuan. Pilih satu atau dua yang paling relevan dengan kerjaya path anda. Lebih baik ada satu certification yang anda benar-benar faham daripada lima yang anda hanya hafal untuk exam.
Satu lagi tip: banyak certification exams boleh didapatkan dengan diskaun melalui program seperti Linux Foundation sales events. Pantau untuk offers ini dan jimatkan kos anda.
Membina Portfolio dengan Homelab
Ini adalah kelebihan terbesar anda. Semua projek yang anda bina dalam homelab sepanjang buku ini boleh dijadikan portfolio yang sangat menarik.
Apa yang perlu ada dalam portfolio anda:
GitHub repository yang terurus. Push semua configurations, scripts, dan documentation ke GitHub. Pastikan setiap repo mempunyai README yang jelas menerangkan apa projek itu, kenapa anda buat, dan macam mana nak guna.
Blog atau documentation site. Tulis tentang pengalaman anda setup homelab, masalah yang anda hadapi, dan macam mana anda selesaikan. Ini menunjukkan kemahiran communication dan documentation anda. Anda boleh gunakan platform seperti Hashnode, Dev.to, atau buat blog sendiri.
Architecture diagrams. Lukis architecture homelab anda. Tunjukkan macam mana semua components bersambung. Ini menunjukkan anda faham big picture, bukan hanya individual tools.
Contributions kepada open-source projects. Walaupun kecil, contributions menunjukkan anda boleh bekerja dengan codebase yang besar dan collaborate dengan orang lain.
Saya pernah interview seorang candidate yang tiada pengalaman kerja dalam DevOps, tapi dia ada homelab yang documented dengan sangat baik di GitHub. Dia dapat kerja itu kerana portfolio homelabnya menunjukkan passion, kemahiran teknikal, dan initiative.
Tips Resume
Resume anda adalah first impression. Berikut beberapa tips:
- Letak GitHub profile link di bahagian atas resume. Banyak hiring managers akan check GitHub anda sebelum interview.
- Gunakan keywords yang specific. Bukan hanya “cloud experience”, tetapi “deployed and managed Kubernetes clusters on AWS EKS with Terraform”. Specificity menunjukkan pengalaman sebenar.
- Highlight projek homelab anda. Kalau anda belum ada pengalaman kerja dalam DevOps, projek homelab adalah bukti kemahiran anda. Tulis ia sebagai “Projects” section dalam resume.
- Quantify achievements. “Reduced deployment time from 2 hours to 15 minutes using CI/CD automation” lebih impactful daripada “implemented CI/CD pipeline.”
- Tailor resume untuk setiap application. Baca job description dengan teliti dan pastikan resume anda address keperluan specific jawatan tersebut.
Persediaan Interview
Interview untuk DevOps positions biasanya terdiri daripada beberapa komponen:
Soalan teknikal yang common: - “Terangkan CI/CD pipeline yang anda pernah bina.” - “Macam mana anda handle secrets management?” - “Apa yang anda buat bila production server down?” - “Terangkan perbezaan antara Docker dan virtual machines.” - “Macam mana anda implement Infrastructure as Code?” - “Terangkan GitOps dan kenapa ia berguna.”
Scenario-based questions: - “Application anda tiba-tiba slow. Apa steps troubleshooting anda?” - “Macam mana anda design deployment strategy untuk zero-downtime?” - “Team anda nak migrate dari on-premise ke cloud. Macam mana anda approach projek ini?”
Hands-on/practical test: Sesetengah syarikat akan beri anda hands-on test. Contohnya, setup CI/CD pipeline dalam masa tertentu, atau troubleshoot failing Kubernetes deployment. Pengalaman homelab anda akan sangat membantu di sini.
Tips interview: - Jujur tentang apa yang anda tahu dan tidak tahu. Lebih baik kata “saya belum pernah guna tool itu, tapi saya tahu konsepnya dan boleh belajar” daripada berpura-pura tahu. - Ceritakan pengalaman troubleshooting. Employers suka dengar tentang masalah yang anda hadapi dan macam mana anda selesaikan. - Tunjukkan curiosity. Tanya soalan tentang tech stack syarikat, challenges yang mereka hadapi, dan bagaimana team mereka beroperasi.
Freelancing vs Full-Time
Kedua-dua path mempunyai kelebihan dan kekurangan:
Full-time: - Pendapatan yang stabil dan consistent - Benefits seperti medical, EPF, dan annual leave - Peluang untuk belajar dari team members yang berpengalaman - Exposure kepada large-scale systems - Career progression yang lebih jelas
Freelancing/Consulting: - Flexibility dalam jadual dan pemilihan projek - Potensi pendapatan yang lebih tinggi - Variety dalam jenis projek dan industries - Kebebasan untuk bekerja dari mana-mana - Perlu uruskan sendiri taxes, invoicing, dan client management
Nota Beginner: Kalau anda baru bermula, saya sangat recommend full-time dulu. Kumpulkan pengalaman selama 2 hingga 3 tahun, bina network, dan fahami macam mana production systems beroperasi. Selepas itu, anda boleh consider freelancing kalau ia sesuai dengan gaya hidup anda.
Learning Roadmap
DevOps adalah bidang yang sentiasa berubah. Anda perlu terus belajar. Berikut adalah roadmap yang saya cadangkan:
Tahun 1: Foundation - Linux administration dan scripting (Bash, Python) - Docker dan containerization - Git dan version control - Basic networking - Satu CI/CD tool (GitHub Actions) - Satu cloud provider (AWS)
Tahun 2: Intermediate - Kubernetes dan container orchestration - Infrastructure as Code (Terraform) - Configuration management (Ansible) - Monitoring dan observability - Security fundamentals (DevSecOps)
Tahun 3: Advanced - Service mesh (Istio, Linkerd) - Advanced Kubernetes (operators, CRDs) - Multi-cloud strategies - Platform engineering - Chaos engineering - Leadership dan mentoring
Berterusan: - Ikuti blog dan podcast tentang DevOps - Sertai komuniti seperti DevOps Malaysia, CNCF meetups - Contribute kepada open-source - Attend conferences (KubeCon, DevOpsDays) - Baca post-mortems dari syarikat besar untuk belajar dari insiden sebenar
Ringkasan
Kerjaya DevOps adalah salah satu kerjaya yang paling menarik dalam teknologi sekarang. Demand tinggi, gaji kompetitif, dan peluang untuk remote work menjadikan ia pilihan yang sangat baik.
Perkara yang paling penting:
- Kemahiran praktikal lebih penting daripada certifications. Homelab anda adalah bukti terbaik kemahiran anda.
- Bina portfolio yang solid. GitHub repos, blog posts, dan documentation menunjukkan kemahiran sebenar anda.
- Jangan tunggu sampai “ready”. Tiada siapa yang pernah 100% ready. Mula apply, mula interview, dan belajar dari setiap pengalaman.
- Network dengan komuniti. Ramai peluang datang melalui connections, bukan job portals.
- Terus belajar. Bidang ini berubah dengan cepat, dan mereka yang terus belajar akan sentiasa relevan.
Anda dah ada pengetahuan. Anda dah ada kemahiran. Sekarang, langkah seterusnya adalah milik anda.
Penutup
Terima kasih kerana bersama saya sepanjang perjalanan ini.
Bila saya mula menulis buku ini, saya teringat pada diri saya sendiri beberapa tahun yang lalu. Seorang yang curious tentang DevOps, tapi keliru dengan banyaknya tools, terminologi, dan “best practices” yang seolah-olah berubah setiap minggu. Saya tahu betapa overwhelming rasanya, dan itulah sebabnya saya tulis buku ini dengan cara yang saya harap saya ada waktu saya baru bermula.
Kita telah melalui banyak perkara bersama. Daripada memahami asas Linux dan containerization, hingga membina CI/CD pipelines, mengurus Kubernetes clusters, dan mengamalkan DevSecOps. Daripada homelab yang sederhana, kita belajar konsep-konsep yang digunakan oleh syarikat besar di seluruh dunia.
Tapi inilah rahsianya: buku ini hanyalah permulaan.
Teknologi akan terus berubah. Tools baru akan muncul. Best practices hari ini mungkin akan menjadi legacy esok. Tapi prinsip-prinsip asas yang anda pelajari di sini, iaitu automation, monitoring, security, collaboration, dan continuous improvement, akan kekal relevan tidak kira apa tools yang popular pada masa hadapan.
Yang paling penting, anda sudah buktikan sesuatu kepada diri sendiri. Anda mampu belajar perkara yang kompleks. Anda mampu setup systems yang berfungsi. Anda mampu troubleshoot masalah dan cari penyelesaian. Itu adalah kemahiran yang tiada tools atau certification boleh ganti.
Beberapa pesanan terakhir saya:
Jangan berhenti experiment. Homelab anda adalah playground yang tak terhad. Cuba technologies baru, break things, dan belajar dari setiap kesilapan. Setiap kali sesuatu rosak dan anda fix, anda menjadi lebih baik.
Ajar orang lain. Cara terbaik untuk memahami sesuatu dengan mendalam ialah dengan mengajarkannya. Tulis blog, buat video, atau sekadar tolong rakan sekerja. Bila anda mengajar, anda juga belajar.
Bersabar dengan diri sendiri. Anda tidak perlu tahu semua benda. Tidak ada seorang pun yang tahu semuanya dalam DevOps. Yang penting ialah anda terus belajar dan terus mencuba.
Sertai komuniti. DevOps bukan perjalanan solo. Sertai meetups, online communities, dan forums. Berkongsi pengalaman dan belajar dari orang lain. Anda akan terkejut betapa ramainya orang yang willing untuk membantu.
Jika anda belum membaca buku companion saya tentang Homelab, saya sangat recommend anda check ia juga. Ia memberikan foundation yang lebih mendalam tentang setup homelab dari segi hardware, networking, dan virtualization, yang menjadi asas kepada semua yang kita bincangkan dalam buku ini.
Untuk resources tambahan, updates, dan artikel-artikel baru, lawati website saya di www.notainfra.com. Saya juga berkongsi tips dan pengalaman di sana secara berkala.
Akhir kata, terima kasih kerana mempercayai saya sebagai guide dalam perjalanan DevOps anda. Saya harap buku ini telah memberikan anda keyakinan dan kemahiran untuk melangkah ke hadapan dengan yakin.
Selamat maju jaya. Jumpa di production.
Syafiyullah Yahya www.notainfra.com