Ebook Syafi
Koleksi
Admin
← DevOps Dari Homelab ke Production
Edit Bab
💾 Simpan
Batal
Syarat
Mukadimah
Bab
Penutup
B
I
H2
H3
List
1.
Quote
Code
Link
Img
Table
Edit
Split
Preview
0 perkataan
# 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: ```javascript // 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}`); }); ``` ```json // 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** ```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** ```yaml # .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 production ``` **4. Buat Kubernetes manifests** ```yaml # 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: ClusterIP ``` > **Nota 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** ```bash # 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:443 ``` **2. Install ArgoCD CLI dan login** ```bash # Install CLI brew install argocd # Login argocd login localhost:8080 --username admin --password
--insecure ``` **3. 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 ``` ```yaml # apps/devops-demo/base/kustomization.yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - deployment.yaml - service.yaml ``` ```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 ``` ```yaml # 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-demo ``` **4. Buat ArgoCD Application** ```yaml # 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=true ``` ```bash kubectl apply -f argocd-app.yaml ``` Sekarang, 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** ```hcl # 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 } ``` ```hcl # 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** ```yaml # 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 ``` ```yaml # 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-agent ``` **3. Jalankan** ```bash # Provision VMs cd terraform/ terraform init terraform plan terraform apply # Configure K3s cd ../ansible/ ansible-playbook -i inventory/hosts.yml playbooks/setup-k3s.yml ``` > **Nota 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** ```bash # Add Helm repo helm repo add prometheus-community \ https://prometheus-community.github.io/helm-charts helm repo update # Buat namespace kubectl create namespace monitoring ``` Buat values file untuk customize installation: ```yaml # 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 }} ``` ```bash # Install stack helm install monitoring prometheus-community/kube-prometheus-stack \ -n monitoring \ -f monitoring-values.yaml ``` **2. Install Loki untuk logs** ```bash helm repo add grafana https://grafana.github.io/helm-charts # Buat Loki values cat <
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.yaml ``` **3. Install Promtail untuk collect logs** ```yaml # promtail-values.yaml config: clients: - url: http://loki-gateway.monitoring.svc.cluster.local/loki/api/v1/push ``` ```bash helm install promtail grafana/promtail -n monitoring -f promtail-values.yaml ``` **4. Buat custom alert rules** ```yaml # 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" ``` ```bash kubectl apply -f custom-alerts.yaml ``` Selepas 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** ```yaml # 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** ```yaml # 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" ``` ```yaml # 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 ``` ```yaml # 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.internal ``` **3. CI/CD pipeline dengan environment promotion** ```yaml # .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=webapp ``` Perhatikan 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. \newpage