<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Mdr{dani} Notes]]></title><description><![CDATA[Mdr{dani} Notes]]></description><link>https://notes.mdrdani.my.id</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1771431527844/94fc0d31-301f-4d6f-b845-8133c48052af.png</url><title>Mdr{dani} Notes</title><link>https://notes.mdrdani.my.id</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 11 Mar 2026 04:21:12 GMT</lastBuildDate><atom:link href="https://notes.mdrdani.my.id/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><atom:link rel="first" href="https://notes.mdrdani.my.id/rss.xml"/><atom:link rel="next" href="https://notes.mdrdani.my.id/rss.xml?after=Njk4MTQ2ODIxZDAwNjhkZWM2MGM1Y2M2XzIwMjYtMDItMDNUMDA6NDk6MjUuMTA2Wg=="/><item><title><![CDATA[Docker Swarm Lab: Maintenance & Scaling — Ketika User Meledak]]></title><description><![CDATA[<p>Aplikasi sudah dideploy, fitur sudah lengkap, tapi tiba-tiba user kita melonjak dari ratusan menjadi puluhan ribu. Apa yang harus dilakukan? Sebagai DevOps, kita tidak boleh panik. Kita harus punya strategi untuk memperbesar kapasitas tanpa membuat user mengalami <em>loading</em> lama atau bahkan <em>error</em>.</p>
<h2>1. Identifikasi Scaling: Up atau Out?</h2>
<p>Banyak orang bingung kapan harus menambah kekuatan server yang ada (<em>Scaling Up</em>) dan kapan harus menambah jumlah server (<em>Scaling Out</em>). Inilah cara identifikasinya:</p>
<ul>
<li><p><strong>Scaling Up (Vertical):</strong> Menambah CPU atau RAM pada server yang sudah ada. Lakukan ini jika satu kontainer kita membutuhkan <em>resource</em> besar untuk satu proses berat (misal: pengolahan video atau kalkulasi rumit).</p>
</li>
<li><p><strong>Scaling Out (Horizontal):</strong> Menambah jumlah replika kontainer atau menambah server Worker baru. Lakukan ini untuk menangani jumlah user yang banyak secara bersamaan (<em>concurrent</em>).</p>
</li>
<li><p><strong>Kapan Tambah Replika?</strong> Jika penggunaan CPU server masih di bawah 60%, tapi aplikasi mulai terasa lambat merespon <em>request</em>, tambahkan replika: <code>docker service scale godocapi_backend=5</code>.</p>
</li>
<li><p><strong>Kapan Tambah Worker?</strong> Jika total penggunaan CPU/RAM di seluruh server sudah menyentuh 70-80%, itulah saatnya Anda membeli server baru dan melakukan <code>docker swarm join</code>.</p>
</li>
</ul>
<h2>2. Zero Downtime Deployment: Update Tanpa Drama</h2>
<p>Sebagai profesional, kita tidak boleh mematikan layanan hanya untuk update versi aplikasi. Kita akan menggunakan strategi <strong>Rolling Update</strong> dengan konfigurasi <code>start-first</code>.</p>
<p>Artinya: Swarm akan menyalakan kontainer versi baru <em>terlebih dahulu</em> sampai sehat, baru mematikan kontainer versi lama. User tidak akan pernah merasakan koneksi terputus.</p>
<p>Tambahkan ini di file <code>docker-stack.yml</code> :</p>
<pre><code class="language-shell">services:
  backend:
    image: cehamot/godocapi:latest
    deploy:
      update_config:
        order: start-first
        parallelism: 1
        delay: 10s
      rollback_config:
        parallelism: 1
        order: stop-first
</code></pre>
<blockquote>
<p><strong>Pro Tip:</strong> Jika update versi baru ternyata <em>buggy</em>, Anda cukup jalankan <code>docker service rollback godocapi_backend</code> untuk kembali ke versi stabil sebelumnya secara otomatis.</p>
</blockquote>
<img src="https://cdn.hashnode.com/uploads/covers/6981458ee7d0f2f0857e2e6b/5ea022b9-47dc-4e1c-8049-40401e674f53.png" alt="" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/uploads/covers/6981458ee7d0f2f0857e2e6b/10675632-28b7-403e-8cae-f68d8b5c65ab.png" alt="" style="display:block;margin:0 auto" />

<h2>3. Placement Constraint: Menjaga Docker Manager Tetap Dingin</h2>
<p>Sesuai arsitektur yang Anda bangun, <strong>Manager (Server A)</strong> harus tetap fokus mengelola kluster dan tidak boleh terbebani oleh aplikasi berat. Kita akan "mengusir" backend agar hanya berjalan di Worker (Server B atau C).</p>
<p>Tambahkan ini di file <code>docker-stack.yml</code></p>
<pre><code class="language-shell">services:
  backend:
    deploy:
      placement:
        constraints:
          - node.role == worker
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/6981458ee7d0f2f0857e2e6b/572cdd47-da8f-4b16-9d85-67ae63d1135d.png" alt="" style="display:block;margin:0 auto" />

<p>pastikan kembali backend berjalan di node worker</p>
<pre><code class="language-shell">docker service ps godocapi_backend
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/6981458ee7d0f2f0857e2e6b/60cfb73b-59d8-47ee-8323-9f1bf3f255b4.png" alt="" style="display:block;margin:0 auto" />

<p>terlihat untuk backend sudah pindah ke node docker worker dengan ini, Manager Anda akan selalu sehat untuk mengawasi jalannya kluster meskipun trafik sedang tinggi.</p>
<h2>4. Integrasi HAProxy: The Front Gate</h2>
<p>Di depan kluster Swarm, kita butuh <strong>HAProxy</strong> sebagai <em>Reverse Proxy</em> sekaligus penyeimbang beban (<em>Load Balancer</em>). HAProxy akan menerima trafik dari internet dan membaginya ke IP Server A, B, atau C.</p>
<p>Gunakan referensi artikel <a href="https://notes.mdrdani.my.id/beyond-load-balancing-arsitektur-high-availability-yang-sebenarnya-part-2#heading-install-haproxy">berikut ini</a> untuk membuat server HAProxy.</p>
<p><strong>Contoh Konfigurasi</strong> <code>haproxy.cfg</code> <strong>:</strong></p>
<pre><code class="language-plaintext">frontend http_front
   bind *:80
   default_backend swarm_cluster

backend swarm_cluster
   balance roundrobin
   option httpchk GET /health
   server manager_a 192.168.20.101:3000 check
   server worker_b 192.168.20.102:3000 check
</code></pre>
<p>Berkat teknologi <strong>Ingress Mesh</strong>, HAProxy tidak perlu pusing mencari di mana kontainer berada. Cukup tembak ke IP server mana pun di dalam kluster, Swarm yang akan mengurus sisanya.</p>
<h2>5. Kesimpulan</h2>
<ul>
<li><p>Ketika pengguna meledak, jangan panik  identifikasi dulu apakah masalahnya butuh <em>scaling up</em> (vertical) atau <em>scaling out</em> (horizontal).</p>
</li>
<li><p>Gunakan <em>scaling up</em> untuk kebutuhan proses tunggal yang berat (CPU/RAM per kontainer), dan <em>scaling out</em> untuk menangani banyak koneksi sekaligus (menambah replika atau worker).</p>
</li>
<li><p>Praktik sederhana: jika CPU per server masih &lt;~60% tapi respon melambat, tambah replika (mis. <code>docker service scale godocapi_backend=5</code>); jika total penggunaan cluster sudah ~7080%, tambahkan node worker dan <code>docker swarm join</code>.</p>
</li>
<li><p>Pastikan monitoring dan alerting (CPU, RAM, latency, error rate) aktif supaya keputusan scaling didasarkan data, bukan tebak-tebakan.</p>
</li>
<li><p>Siapkan proses deployment tanpa downtime (rolling updates, health checks, cukup replika saat update) agar skala naik/turun dan pembaruan versi tidak mengganggu user.</p>
</li>
<li><p>Automasi (script/CI/CD, autoscaler, infrastruktur as code) dan perencanaan kapasitas membantu menghindari krisis saat lonjakan traffic berikutnya.</p>
</li>
</ul>
<p>Intinya: ukur dulu, pilih strategi yang sesuai (up vs out), otomatisasi tindakan yang rutin, dan jaga pengalaman pengguna tetap mulus selama scaling atau update.</p>
]]></description><link>https://notes.mdrdani.my.id/docker-swarm-lab-maintenance-scaling-ketika-user-meledak</link><guid isPermaLink="true">https://notes.mdrdani.my.id/docker-swarm-lab-maintenance-scaling-ketika-user-meledak</guid><category><![CDATA[docker swarm]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[Next.js]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Docker Swarm Lab: Hands-On Guide Membangun Kluster High Availability]]></title><description><![CDATA[<p>Setelah di artikel sebelumnya kita membahas filosofi "kenapa harus Docker Swarm", sekarang saatnya kita berhenti berteori dan mulai <em>get our hands dirty</em>. Di artikel ini, kita akan melakukan <strong>Step-by-Step Deployment</strong> untuk merakit kluster yang tangguh, dari nol sampai aplikasi Anda berjalan di banyak server sekaligus.</p>
<h2><strong>1. Persiapan Environment</strong></h2>
<p>Sebelum kita masuk ke konfigurasi masing-masing server, pastikan kamu sudah menyiapkan "bahan-bahan" berikut:</p>
<ol>
<li><p><strong>Virtualization Tool:</strong> Saya menggunakan <strong>Oracle VM VirtualBox</strong>.</p>
</li>
<li><p><strong>Sistem Operasi:</strong> Image <strong>Ubuntu 24.04 LTS (Noble Numbat)</strong> untuk semua node server.</p>
</li>
<li><p><strong>Spesifikasi Server:</strong></p>
<p>Docker Manager 4 GB RAM dan 15 GB Hardisk.</p>
<p>Docker Worker 2 GB RAM dan 15 GB Hardisk.</p>
</li>
<li><p><strong>Networking:</strong> Setiap VM dikonfigurasi dengan <strong>2 Network Adapter</strong>:</p>
<ul>
<li><p><strong>Adapter 1 (NAT):</strong> Untuk akses internet (update package, install HAProxy, dll).</p>
</li>
<li><p><strong>Adapter 2 (Host-Only):</strong> Digunakan untuk jaringan LAN antar VM (komunikasi internal agar IP <code>192.168.20.x</code> bisa saling ping).</p>
</li>
</ul>
</li>
<li><p><strong>Source Code:</strong> Kita akan menggunakan API sederhana berbasis Go sebagai backend dan Frontend berbasis Nextjs. Kamu bisa melakukan <em>clone</em> dari repository ini:</p>
<p>Backend  <a href="https://github.com/mdrdani/GoDocAPI">https://github.com/mdrdani/GoDocAPI</a></p>
<p>frontend  <a href="https://github.com/mdrdani/frontend-GoDocAPI">https://github.com/mdrdani/frontend-GoDocAPI</a>.</p>
</li>
<li><p><strong>Docker:</strong> Dengan Docker, kita dapat dengan mudah mengatur dan mengelola environment yang diperlukan untuk menguji arsitektur yang telah dirancang.</p>
</li>
<li><p><strong>DBeaver</strong>: Tambahkan DBeaver untuk akses dan manajemen database.</p>
</li>
<li><p><strong>Koneksi Internet:</strong> Dibutuhkan selama proses instalasi <em>dependency</em>.</p>
</li>
</ol>
<h2>2. Persiapan Infrastruktur: The Blueprint</h2>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771335633386/8ea9055b-0b93-418c-a09f-28cd331a9105.png" alt="" style="display:block;margin:0 auto" />

<p>Sebelum mengetik perintah, kita butuh fondasi yang jelas.</p>
<ul>
<li><p><strong>Server A (Manager):</strong> Otak dari operasi kita.</p>
</li>
<li><p><strong>Server B (Worker):</strong> Pasukan yang siap menjalankan beban kerja.</p>
</li>
<li><p><strong>Server C (Database &amp; Storage):</strong> Rumah bagi data kita agar tetap aman jika kluster aplikasi di-reset.</p>
</li>
</ul>
<blockquote>
<p><strong>Catatan :</strong> Jangan pernah mencampur Database di dalam kluster Swarm aplikasi jika Anda masih awam dengan <em>shared storage</em>. Biarkan DB berdiri di server sendiri agar data Anda tidak "hilang" saat kontainer berpindah-pindah.</p>
</blockquote>
<h2><strong>3. Install Docker</strong></h2>
<p>Langkah pertama adalah memastikan Docker terpasang di semua <strong>server (A, B, dan C)</strong>. Gunakan skrip otomatis agar standar versinya sama di setiap mesin:</p>
<pre><code class="language-bash">sudo apt install apt-transport-https ca-certificates curl software-properties-common -y 

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc 

echo "deb [arch=\((dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \)(. /etc/os-release &amp;&amp; echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null

sudo apt update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

sudo usermod -aG docker $user
</code></pre>
<h2><strong>4.</strong> Setup Server C  PostgreSQL Database &amp; RustFS Object Storage</h2>
<p>Pada tahap ini, kita akan menyiapkan <strong>Server C (192.168.20.104)</strong> sebagai pusat penyimpanan data untuk kluster.<br />Server ini berperan sebagai:</p>
<ul>
<li><p><strong>Database server</strong> menggunakan PostgreSQL</p>
</li>
<li><p><strong>Object storage</strong> menggunakan RustFS untuk menyimpan file aplikasi (assets, upload, dll)</p>
</li>
</ul>
<p>Pastikan Anda sudah dapat mengakses server melalui SSH:</p>
<pre><code class="language-bash">ssh user@192.168.20.104
</code></pre>
<h3>Docker Compose</h3>
<p>Selanjutnya, buat file <code>docker-compose.yaml</code> untuk menjalankan PostgreSQL dan RustFS dalam satu host.</p>
<pre><code class="language-bash">services:
  postgres:
    image: postgres:16
    container_name: postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: app_user
      POSTGRES_PASSWORD: app_password
      POSTGRES_DB: app_db
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app_user -d app_db"]
      interval: 10s
      timeout: 5s
      retries: 5

  rustfs:
    image: rustfs/rustfs:latest
    container_name: rustfs
    restart: unless-stopped
    ports:
      - "9000:9000"
      - "9001:9001"
    environment:
      RUSTFS_VOLUMES: /data/rustfs{0..3}
      RUSTFS_ADDRESS: 0.0.0.0:9000
      RUSTFS_CONSOLE_ADDRESS: 0.0.0.0:9001
      RUSTFS_CONSOLE_ENABLE: "true"
      RUSTFS_ACCESS_KEY: rustfsadmin
      RUSTFS_SECRET_KEY: rustfsadmin
    volumes:
      - rustfs_data:/data
      - rustfs_logs:/logs
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 20s

volumes:
  postgres_data:
  rustfs_data:
  rustfs_logs:
</code></pre>
<h3>Jalankan service</h3>
<p>Setelah file dibuat:</p>
<pre><code class="language-bash">mkdir -p data/rustfs0 data/rustfs1 data/rustfs2 data/rustfs3 logs
docker compose up -d
</code></pre>
<p>Cek container:</p>
<pre><code class="language-bash">docker ps
</code></pre>
<p>Akses service:</p>
<ul>
<li><p>PostgreSQL  <code>192.168.20.104:5432</code></p>
</li>
<li><p>RustFS API  <code>http://192.168.20.104:9000</code></p>
</li>
<li><p>RustFS Console  <code>http://192.168.20.104:9001</code></p>
</li>
</ul>
<p>Server C kini siap menjadi <strong>storage layer</strong> untuk cluster Docker Swarm pada tahap berikutnya.</p>
<h3><strong>Test Akses PostgreSQL</strong></h3>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770602513110/df630af1-8b41-4f60-a1dc-e36fbb4e8466.png?auto=compress,format&amp;format=webp" alt="" />

<p>jangan lupa untuk inisialisasi Database schema dahulu:</p>
<pre><code class="language-sql">CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE IF NOT EXISTS documents (
  id           UUID        PRIMARY KEY DEFAULT uuid_generate_v4(),
  filename     TEXT        NOT NULL,
  storage_path TEXT        NOT NULL UNIQUE,
  size         BIGINT      NOT NULL CHECK (size &gt;= 0),
  content_type TEXT        NOT NULL,
  created_at   TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_documents_filename ON documents (filename);
</code></pre>
<h3><strong>Test Akses RustFS</strong></h3>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770602403515/452990d3-9dc4-434f-aaaa-ef66b999a479.png?auto=compress,format&amp;format=webp" alt="" />

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770602589862/05f8e5c6-2a3d-461d-8c19-ffc21047043d.png?auto=compress,format&amp;format=webp" alt="" />

<p>jika ingin melihat logs dari docker container yang telah kita jalankan jalankan perintah</p>
<pre><code class="language-bash">docker compose logs -f
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770601404424/48b615d9-6856-4f2a-814e-5bc3dba54015.png?auto=compress,format&amp;format=webp" alt="" />

<h2>5.Setup Server A &amp; B  Docker Swarm Manager dan Worker Node</h2>
<p>Pada bagian ini kita akan menyiapkan <strong>Server A</strong> dan <strong>Server B</strong> sebagai node utama dalam kluster Docker Swarm. Pastikan kedua server sudah terinstall Docker Engine versi terbaru.</p>
<h3>Inisialisasi Docker Swarm (Server A  Manager)</h3>
<p>Masuk ke <strong>Server A</strong> lalu jalankan:</p>
<pre><code class="language-shell">docker swarm init --advertise-addr 192.168.20.101
</code></pre>
<p>Output akan menampilkan <strong>join token</strong> worker, contoh:</p>
<pre><code class="language-shell">docker swarm join \
--token SWMTKN-1-xxxxx \
192.168.20.101:2377
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771487272627/0e46acb6-6cd1-474d-a5b8-c576126f620a.png" alt="" style="display:block;margin:0 auto" />

<p>Simpan token tersebut untuk Server B.</p>
<p>Cek status swarm:</p>
<pre><code class="language-bash">docker node ls
</code></pre>
<p>Server A akan berstatus:</p>
<pre><code class="language-bash">Leader
</code></pre>
<h3>Join Worker ke Cluster (Server B)</h3>
<p>Masuk ke <strong>Server B</strong>, lalu jalankan token join dari Server A:</p>
<pre><code class="language-shell">docker swarm join \
--token SWMTKN-1-xxxxx \
192.168.20.101:2377
</code></pre>
<p>Jika berhasil:</p>
<pre><code class="language-plaintext">This node joined a swarm as a worker.
</code></pre>
<p>Verifikasi dari Server A:</p>
<pre><code class="language-plaintext">docker node ls
</code></pre>
<p>Output seharusnya:</p>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/aa6603b2-80e7-4be4-af2f-2a504fae236f.png" alt="" style="display:block;margin:0 auto" />

<pre><code class="language-plaintext">Manager   Server A
Worker    Server B
</code></pre>
<h2>6. Deploy Aplikasi GoDocAPI &amp; Frontend ke Docker Swarm</h2>
<p>Setelah <strong>Server B</strong> berhasil join ke <strong>Server A (Manager)</strong>, tahap berikutnya adalah build image aplikasi dan melakukan deployment menggunakan <code>docker stack</code>.</p>
<blockquote>
<p>Semua proses build dan deployment dilakukan dari Server A (Manager). Alternatifnya, build dapat dilakukan melalui Github Actions atau Jenkins.</p>
</blockquote>
<h3>Clone Repository</h3>
<p>Masuk ke Server A:</p>
<pre><code class="language-plaintext">ssh user@192.168.20.101
</code></pre>
<p>Clone kedua repository:</p>
<pre><code class="language-plaintext">git clone https://github.com/mdrdani/GoDocAPI.git
git clone https://github.com/mdrdani/frontend-GoDocAPI.git
</code></pre>
<h3>Konfigurasi Environment Backend (GoDocAPI)</h3>
<p>Masuk ke folder backend:</p>
<pre><code class="language-plaintext">cd GoDocAPI
</code></pre>
<p>Copy file <code>.env.example</code> Edit file <code>.env</code> dan sesuaikan dengan <strong>Server C (192.168.20.104)</strong>:</p>
<pre><code class="language-plaintext">SERVER_PORT=:8080
DB_URL=postgres://swarmuser:swarmpassword@192.168.20.104:5432/swarmdb?sslmode=disable
RUSTFS_ENDPOINT=http://192.168.20.104:9000
RUSTFS_ACCESS_KEY=admin
RUSTFS_SECRET_KEY=password123
RUSTFS_BUCKET=godoc-bucket
RUSTFS_REGION=us-east-1
</code></pre>
<blockquote>
<p> Penting:</p>
<ul>
<li><p>Jangan gunakan <code>localhost</code>, karena backend berjalan di node berbeda.</p>
</li>
<li><p>Gunakan IP Server C atau domain internal.</p>
</li>
</ul>
</blockquote>
<h3>Build Image Backend (Golang)</h3>
<p>Masih di folder <code>GoDocAPI</code>:</p>
<pre><code class="language-plaintext">docker build -t godocapi:latest .
</code></pre>
<p>Verifikasi:</p>
<pre><code class="language-plaintext">docker images | grep godocapi
</code></pre>
<h3>Konfigurasi Environment Frontend (Next.js)</h3>
<p>Masuk ke folder frontend:</p>
<pre><code class="language-plaintext">cd ../frontend-GoDocAPI
</code></pre>
<p>Edit file <code>.env</code>:</p>
<pre><code class="language-plaintext">API_URL=http://backend:8080
</code></pre>
<blockquote>
<p>Penjelasan:</p>
<ul>
<li>Karena menggunakan Docker Swarm overlay network, service backend dapat diakses melalui <strong>service name</strong> (<code>backend</code>), bukan IP.</li>
</ul>
</blockquote>
<h3>Build Image Frontend (Next.js)</h3>
<pre><code class="language-plaintext">docker build -t frontendgodocapi:latest .
</code></pre>
<p>Cek image:</p>
<pre><code class="language-plaintext">docker images | grep frontendgodocapi
</code></pre>
<h3>Login ke Docker Hub</h3>
<pre><code class="language-plaintext">docker login
</code></pre>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/42be84e4-4f66-48d4-8adf-ce5f3dbe0202.png" alt="" style="display:block;margin:0 auto" />

<h3>Tag Image</h3>
<pre><code class="language-plaintext">docker tag godocapi:latest cehamot/godocapi:latest
docker tag fegodocapi:latest cehamot/fegodocapi:latest
</code></pre>
<h3>Push ke Docker Hub</h3>
<pre><code class="language-plaintext">docker push cehamot/godocapi:latest
docker push cehamot/fegodocapi:latest
</code></pre>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/de1b9dd9-ea02-4453-8f02-5af5867b86fd.png" alt="" style="display:block;margin:0 auto" />

<h3>Buat File docker-stack.yaml</h3>
<p>Kembali ke direktori utama (misalnya buat folder khusus deploy):</p>
<pre><code class="language-plaintext">mkdir swarm-deploy
cd swarm-deploy
</code></pre>
<p>Buat file <code>docker-stack.yaml</code>:</p>
<pre><code class="language-shell">version: '3.8'

services:
  frontend:
    image: cehamot/fegodocapi:latest
    ports:
        # Publish port menggunakan routing mesh (akses dari semua node)
      - "3000:3000"
    environment:
        # Mengakses backend via service name (internal DNS swarm)
      - API_URL=http://backend:8080
    deploy:
      # Menjalankan 2 instance container untuk high availability
      replicas: 2
      # Restart hanya jika container error
      restart_policy:
        condition: on-failure
      update_config:
        # Rolling update 1 container per batch
        parallelism: 1
        # Delay 10 detik antar update container
        delay: 10s
    networks:
        # Menggunakan overlay network antar node
      - app-net

  backend:
    image: cehamot/godocapi:latest
    ports:
        # Publish API via routing mesh swarm
      - "8080:8080"
    env_file:
        # File environment berisi DB &amp; RustFS config (Server C)
      - ../GoDocAPI/.env
    deploy:
      # 2 instance backend untuk redundancy
      replicas: 2
      restart_policy:
        condition: on-failure
      update_config:
        # Rolling update satu per satu
        parallelism: 1
        delay: 10s
    networks:
        # Terhubung ke overlay network yang sama
      - app-net

networks:
  app-net:
    # Network multi-host (wajib untuk komunikasi antar node swarm)
    driver: overlay
</code></pre>
<h3>Deploy ke Docker Swarm</h3>
<p>Jalankan dari Manager:</p>
<pre><code class="language-plaintext">docker stack deploy -c docker-stack.yaml godocapi
</code></pre>
<p>Cek service:</p>
<pre><code class="language-plaintext">docker service ls
</code></pre>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/bed1abca-84aa-4175-a794-d0ce89dadfd6.png" alt="" style="display:block;margin:0 auto" />

<p>Cek detail task:</p>
<pre><code class="language-plaintext">docker service ps godocapi_backend
docker service ps godocapi_frontend
</code></pre>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/07e33b08-782f-461f-8b9e-704eef1010b3.png" alt="" style="display:block;margin:0 auto" />

<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/829aafd1-cc9e-4ca6-a737-c8782ba065a9.png" alt="" style="display:block;margin:0 auto" />

<p>cek detail logs:</p>
<pre><code class="language-shell">docker service logs -f godocapi_backend
docker service logs -f godocapi_frontend
</code></pre>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/085afd09-6619-4f92-a0d0-061683c1ed13.png" alt="" style="display:block;margin:0 auto" />

<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/0cbe7541-0eca-40f8-b6d7-74662fb3577a.png" alt="" style="display:block;margin:0 auto" />

<h3>Verifikasi High Availability</h3>
<p>Akses aplikasi melalui:</p>
<pre><code class="language-plaintext">http://192.168.20.101:3000
atau
http://192.168.20.102:3000
</code></pre>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/c526a486-79a0-4f9a-9fcc-26c375e4d75b.png" alt="" style="display:block;margin:0 auto" />

<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/6981458ee7d0f2f0857e2e6b/c0a02744-56fb-41f3-b65c-44c24b2144c0.png" alt="" style="display:block;margin:0 auto" />

<p>Karena menggunakan:</p>
<ul>
<li><p>Replicas: 2</p>
</li>
<li><p>Overlay network</p>
</li>
<li><p>Routing mesh</p>
</li>
</ul>
<p>Swarm akan mendistribusikan container ke Manager dan Worker secara otomatis.</p>
<h2>7. Kesimpulan</h2>
<p>Secara keseluruhan, pada Part 1 ini kita telah berhasil membangun fondasi arsitektur High Availability menggunakan Docker Swarm dengan pendekatan yang menyerupai lingkungan produksi nyata. Dimulai dari inisialisasi cluster dengan pemisahan peran Manager dan Worker, kemudian memisahkan application layer (frontend dan backend) dari data layer (PostgreSQL dan RustFS di Server C), hingga melakukan deployment menggunakan <code>docker-stack.yaml</code> dengan konfigurasi replication, overlay network, dan rolling update. Aplikasi yang bersumber dari repository <a href="https://github.com/mdrdani/GoDocAPI"><strong>GoDocAPI</strong></a> dan <a href="https://github.com/mdrdani/frontend-GoDocAPI"><strong>frontend-GoDocAPI</strong></a> kini berjalan secara terdistribusi, memiliki kemampuan self-healing, load balancing internal melalui routing mesh, serta siap untuk scaling horizontal. Dengan fondasi ini, cluster sudah memenuhi prinsip dasar sistem terdistribusi yang resilien dan siap dikembangkan lebih lanjut pada tahap optimasi dan hardening di bagian berikutnya.</p>
<p>selanjutnya kita akan membahas "Maintenance &amp; Scaling: Ketika User Meledak"<br />1. Scaling Up vs Scaling Out: Kapan harus tambah replika, kapan harus tambah server Worker. 2. Zero Downtime Deployment: Cara update image aplikasi tanpa memutus koneksi user.<br />3. Integrasi HAProxy sebagai reverse proxy layer</p>
]]></description><link>https://notes.mdrdani.my.id/docker-swarm-lab-hands-on-guide-membangun-kluster-high-availability</link><guid isPermaLink="true">https://notes.mdrdani.my.id/docker-swarm-lab-hands-on-guide-membangun-kluster-high-availability</guid><category><![CDATA[docker swarm]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA["The Why and How" - Perkenalan & Filosofi Docker Swarm]]></title><description><![CDATA[<h2>1. Kenapa Docker Swarm Tercipta? (The "Why")</h2>
<p>Dulu, Docker hanya bisa berjalan di satu komputer. Jika trafik membludak atau server mati, selesailah riwayat aplikasi kita. Docker Swarm diciptakan sebagai jawaban atas kerumitan orkestrasi kontainer.</p>
<iframe></iframe>

<h3>Swarm vs Kubernetes: Sebuah Perbandingan Jujur</h3>
<p>Jika kita bandingkan keduanya, perbedaannya sangat mencolok:</p>
<ul>
<li><p><strong>Kubernetes (K8s):</strong> Ibarat sebuah kota metropolitan yang canggih. Anda punya polisi (monitoring), pemadam kebakaran (auto-healing), dan sistem transportasi yang sangat rumit. Tapi, Anda butuh tim ahli untuk mengurus administrasinya setiap hari.</p>
</li>
<li><p><strong>Docker Swarm:</strong> Ibarat sebuah perumahan yang tertata rapi. Semua fitur penting sudah ada (Load Balancing, Scaling, High Availability) tanpa perlu pusing dengan konfigurasi yang berlembar-lembar. Jika kita sudah mengerti <strong>Docker Compose</strong>, kita sudah 90% mengerti Swarm.</p>
</li>
</ul>
<h2>2. Teknologi di Docker Swarm: Bagaimana Mereka Saling Bicara?</h2>
<p>Mungkin kita bertanya, "Bagaimana server-server ini bisa kompak?" Ada dua teknologi utama yang menjadi "nyawa" di balik Docker Swarm:</p>
<h3><strong>A. Raft Consensus Algorithm</strong></h3>
<p>Bayangkan para Manager sedang duduk di meja bundar. Agar kluster tetap stabil, mereka harus punya satu pemimpin (<em>Leader</em>). Raft adalah protokol yang mereka gunakan untuk melakukan "voting". Jika <em>Leader</em> pingsan, Manager yang lain akan langsung berdiskusi dan memilih pemimpin baru dalam hitungan milidetik agar sistem tidak lumpuh.</p>
<h3><strong>B. Gossip Protocol</strong></h3>
<p>Seperti namanya (Gosip), ini adalah cara para Worker menyebarkan informasi. Mereka saling membisikkan status kesehatan satu sama lain secara cepat. Jadi, jika ada satu kontainer yang mati di pojok sana, kabar tersebut akan menyebar secepat kilat ke seluruh kluster sehingga Manager bisa segera mengambil tindakan.</p>
<img src="https://docs.docker.com/engine/swarm/images/swarm-diagram.webp" alt="" />

<h3><strong>C. Overlay Network &amp; VXLAN</strong></h3>
<p>Secara fisik, server kita mungkin terpisah-pisah, namun Swarm menggunakan teknologi <strong>VXLAN</strong> untuk menciptakan <strong>Overlay Network</strong>.</p>
<ul>
<li><p><strong>Analoginya:</strong> Seperti membangun terowongan rahasia di bawah tanah yang menghubungkan gedung-gedung berbeda.</p>
</li>
<li><p>Kontainer di Server A bisa mengobrol dengan kontainer di Server B seolah-olah mereka berada di dalam satu kabel yang sama, tanpa Anda perlu repot mengatur IP publik atau rute jaringan yang rumit.</p>
</li>
</ul>
<h3><strong>D. Ingress Routing Mesh &amp; Internal DNS</strong></h3>
<p>Inilah teknologi yang membuat pengelolaan trafik menjadi sangat mudah:</p>
<ul>
<li><p><strong>Ingress Mesh:</strong> Membuat Anda bisa mengakses aplikasi melalui IP server mana pun di dalam kluster. Jika aplikasi ada di Server B tapi Anda mengakses IP Server A, Swarm akan otomatis meneruskannya ke tempat yang benar.</p>
</li>
<li><p><strong>Service Discovery (Internal DNS):</strong> Anda tidak perlu menghafal IP kontainer yang sering berubah. Cukup panggil nama layanannya (misal: <code>http://backend</code>), dan DNS internal Swarm akan mencarikan kontainer mana yang sedang aktif dan sehat untuk Anda.</p>
</li>
</ul>
<h2>3. Glosarium Docker Swarm:</h2>
<p>Agar tidak bingung, mari kita pelajari bahasa "keren" Docker Swarm:</p>
<ul>
<li><p><strong>Manager Node:</strong> "Otak" dari kluster. Ia yang menerima perintah Anda dan membagi tugas ke server lain.</p>
</li>
<li><p><strong>Worker Node:</strong> "Buruh" pelaksana. Tugasnya hanya menjalankan kontainer aplikasi Anda tanpa ikut campur urusan administrasi.</p>
</li>
</ul>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771331722117/ee952e89-f871-4a7b-8f81-1b9fd9513f68.png" alt="" style="display:block;margin:0 auto" />

<ul>
<li><p><strong>Service:</strong> Definisi dari aplikasi kita. Misal: "Saya mau menjalankan backend dengan 3 replika". Definisi inilah yang disebut Service.</p>
</li>
<li><p><strong>Task:</strong> Unit terkecil dalam Swarm. Jika Service minta 3 replika, maka akan ada 3 Task yang berjalan.</p>
</li>
<li><p><strong>Ingress Mesh:</strong> Fitur sakti yang membuat kita bisa mengakses aplikasi dari IP server mana saja di dalam kluster, dan trafiknya akan otomatis diarahkan ke server yang benar.</p>
</li>
</ul>
<h2>4. Perintah Docker Swarm</h2>
<p>Berikut adalah daftar perintah Docker Swarm wajib bagi kita ketahui:</p>
<h3>1. Pengelolaan Kluster (Node Management)</h3>
<p>Digunakan untuk mengatur server-server (Manager &amp; Worker) yang tergabung dalam kluster.</p>
<ul>
<li><p><code>docker swarm init --advertise-addr &lt;IP&gt;</code> : Inisialisasi Manager pertama.</p>
</li>
<li><p><code>docker swarm join-token worker</code> : Menampilkan perintah/token agar server lain bisa bergabung sebagai Worker.</p>
</li>
<li><p><code>docker swarm join-token manager</code> : Menampilkan perintah/token agar server lain bisa bergabung sebagai Manager baru.</p>
</li>
<li><p><code>docker node ls</code> : Melihat daftar semua server di kluster beserta statusnya (Leader, Reachable, atau Ready).</p>
</li>
<li><p><code>docker node update --availability drain &lt;NODE_ID&gt;</code> : Menghentikan kontainer di node tertentu (berguna saat mau <em>maintenance</em> server tanpa mematikan layanan).</p>
</li>
<li><p><code>docker node rm &lt;NODE_ID&gt;</code> : Menghapus node dari kluster (pastikan node sudah <em>down</em> atau sudah di-<em>drain</em>).</p>
</li>
</ul>
<hr />
<h3>2. Pengelolaan Stack (High-Level Deployment)</h3>
<p>Ini adalah cara kita men-deploy aplikasi secara utuh menggunakan file <code>docker-stack.yml</code>.</p>
<p>%%[jagohosting2-mdrdani]</p>
<ul>
<li><p><code>docker stack deploy -c &lt;FILE.yaml&gt; &lt;NAMA_STACK&gt;</code> : Deploy atau update aplikasi ke kluster.</p>
</li>
<li><p><code>docker stack ls</code> : Melihat daftar semua aplikasi (stack) yang sedang berjalan.</p>
</li>
<li><p><code>docker stack services &lt;NAMA_STACK&gt;</code> : Melihat status service di dalam stack (jumlah replika yang jalan vs diminta).</p>
</li>
<li><p><code>docker stack ps &lt;NAMA_STACK&gt;</code> : Melihat secara detail kontainer mana jalan di server mana.</p>
</li>
<li><p><code>docker stack rm &lt;NAMA_STACK&gt;</code> : Menghapus seluruh stack (mematikan semua layanan terkait).</p>
</li>
</ul>
<hr />
<h3>3. Pengelolaan Service (Granular Control)</h3>
<p>Digunakan jika kita ingin mengatur satu layanan spesifik tanpa menyentuh seluruh stack.</p>
<ul>
<li><p><code>docker service ls</code> : Melihat semua layanan yang berjalan di seluruh kluster.</p>
</li>
<li><p><code>docker service ps &lt;NAMA_SERVICE&gt;</code> : Cek status spesifik satu layanan (misal <code>godocapi_backend</code>).</p>
</li>
<li><p><code>docker service logs -f &lt;NAMA_SERVICE&gt;</code> : Melihat log aplikasi secara <em>real-time</em> dari seluruh node sekaligus.</p>
</li>
<li><p><code>docker service scale &lt;NAMA_SERVICE&gt;=&lt;JUMLAH&gt;</code> : Menambah atau mengurangi jumlah kontainer secara instan tanpa edit file YAML.</p>
</li>
<li><p><code>docker service update --image &lt;IMAGE:TAG&gt; &lt;NAMA_SERVICE&gt;</code> : Mengupdate versi aplikasi (image) secara manual.</p>
</li>
<li><p><code>docker service inspect --pretty &lt;NAMA_SERVICE&gt;</code> : Melihat detail konfigurasi layanan (port, environment, dll) dalam format yang mudah dibaca.</p>
</li>
</ul>
<hr />
<h3>4. Troubleshooting &amp; Pemeliharaan</h3>
<p>Perintah penyelamat saat aplikasi kita mulai bertingkah aneh.</p>
<ul>
<li><p><code>docker system df</code> : Melihat penggunaan disk (penting karena Docker sering menyisakan image sampah).</p>
</li>
<li><p><code>docker system prune -a</code> : Menghapus container, network, dan image yang tidak terpakai (gunakan dengan hati-hati!).</p>
</li>
<li><p><code>docker network ls</code> : Melihat daftar network, cari yang bertipe <code>overlay</code> untuk komunikasi antar server.</p>
</li>
<li><p><code>docker stats</code> : Memantau penggunaan CPU dan RAM kontainer di server lokal secara <em>real-time</em>.</p>
</li>
</ul>
<h2>Kesimpulan</h2>
<p>Docker Swarm adalah pilihan paling "waras" untuk tim kecil yang ingin mengejar target <strong>High Availability</strong> tanpa harus pusing dengan kurva belajar Kubernetes yang curam. Ia ringan, cepat, dan sudah ada di dalam instalasi Docker kita.</p>
<p>Di artikel berikutnya, kita akan langsung praktek membangun kluster pertama. Siapkan minimal dua server (Manager &amp; Worker) dan mari kita mulai petualangan ini!</p>
<p>%%[nihbuatjajan2-mdrdani]</p>
]]></description><link>https://notes.mdrdani.my.id/the-why-and-how-perkenalan-and-filosofi-docker-swarm</link><guid isPermaLink="true">https://notes.mdrdani.my.id/the-why-and-how-perkenalan-and-filosofi-docker-swarm</guid><category><![CDATA[docker swarm]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Beyond Load Balancing: Arsitektur High Availability yang Sebenarnya (Part 3)]]></title><description><![CDATA[<p>Setelah berhasil melakukan pengujian di <a href="https://notes.mdrdani.my.id/beyond-load-balancing-arsitektur-high-availability-yang-sebenarnya-part-2">artikel sebelumnya</a>, kita sekarang memiliki sistem yang mampu menangani kegagalan di level aplikasi. Namun, mari kita jujur: jika satu-satunya server HAProxy kita (192.168.87.6) mengalami kendala <em>hardware</em> atau <em>crash</em>, maka seluruh redundansi di level <em>backend</em> tadi menjadi tidak berartisistem akan tetap mati total.</p>
<p>Inilah yang disebut sebagai <strong>Single Point of Failure</strong>. Untuk mengatasinya, kita akan melangkah ke tahap yang lebih tinggi: <strong>High Availability di level Load Balancer</strong>.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770623337822/d2f9bd9d-bc79-47ce-9425-aba8d5d7a3b6.png" alt="" style="display:block;margin:0 auto" />

<h2>Apa yang Akan Kita Bangun?</h2>
<p>Pada tahap ini, kita akan menyiapkan server HAProxy kedua (<code>192.168.87.7</code>) yang akan bertindak sebagai cadangan (<em>Standby</em>). Keduanya akan diikat menggunakan protokol <strong>VRRP (Virtual Router Redundancy Protocol)</strong> melalui layanan bernama <strong>Keepalived</strong>.</p>
<p>Hasil akhirnya adalah sebuah <strong>Virtual IP (VIP)</strong> atau <strong>Floating IP</strong>. User tidak lagi mengakses IP fisik server (192.168.87.6 atau 192.168.87.7), melainkan mengakses satu IP "keramat" yang akan selalu aktif selama salah satu dari kedua Load Balancer tersebut hidup. Jika Load Balancer utama mati, IP tersebut akan "berpindah" secara otomatis ke server cadangan dalam hitungan detik.</p>
<h2><strong>VM 2 Load Balancer Setup</strong></h2>
<h3><strong>Install HAProxy</strong></h3>
<p>Setelah berhasil menguji node pertama, langkah selanjutnya adalah menyiapkan VM kedua dengan alamat IP <strong>192.168.87.7</strong>. VM ini akan berperan sebagai node cadangan yang siap mengambil alih trafik jika node utama bermasalah.</p>
<p>Agar performa dan fiturnya konsisten, kita akan menginstal versi HAProxy yang sama dengan node sebelumnya. Silakan SSH ke VM kedua dan jalankan rangkaian perintah berikut:</p>
<pre><code class="language-bash">sudo apt update

sudo apt-get install --no-install-recommends software-properties-common

sudo add-apt-repository ppa:vbernat/haproxy-3.2

sudo apt-get install haproxy=3.2.\*
</code></pre>
<p>Pastikan versi yang terinstal sudah sesuai dengan menjalankan perintah:</p>
<pre><code class="language-bash">haproxy -v
</code></pre>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770624839511/edc1edba-71dc-414a-8b3a-8a044ea1b9d4.png" alt="" style="display:block;margin:0 auto" />

<p>untuk memastikan HAProxy sudah berjalan dengan baik kita check dengan perintah</p>
<pre><code class="language-bash">sudo systemctl status haproxy
</code></pre>
<h3><strong>Enable Dashboard</strong></h3>
<p>Modifikasi Konfigurasi HAProxy Buka file <code>/etc/haproxy/haproxy.cfg</code> dan tambahkan blok konfigurasi berikut untuk menentukan pada port dan alamat mana dashboard bisa diakses:</p>
<pre><code class="language-bash">frontend stats
        mode http
        bind :8404
        stats enable
        stats refresh 10s
        stats uri /stats
        stats show-modules
</code></pre>
<blockquote>
<p><em><strong>Catatan: Di sini kita menggunakan port</strong></em> <code>8404</code>. Pastikan port ini terbuka dan tidak terpakai oleh layanan lain.</p>
</blockquote>
<p>Terapkan Perubahan Agar konfigurasi baru terbaca oleh sistem, lakukan <em>restart</em> pada layanan HAProxy dengan perintah berikut:</p>
<pre><code class="language-bash">sudo systemctl restart haproxy.service
</code></pre>
<p>Akses Dashboard Sekarang, kita bisa melihat kondisi infrastruktur secara langsung melalui browser dengan mengakses alamat: <code>http://192.168.87.7:8404/stats</code></p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770624978001/7ebd711d-2f14-45ed-a38d-d845f3f60c3f.png" alt="" style="display:block;margin:0 auto" />

<h3><strong>Load Balancer Config</strong></h3>
<p>Setelah dashboard aktif, langkah selanjutnya adalah inti dari skenario kita: <strong>mengatur pembagian trafik</strong>. Di sini, HAProxy akan bertindak sebagai gerbang utama (port 80) yang akan meneruskan permintaan user ke dua server aplikasi (<code>backend-1</code> dan <code>backend-2</code>) secara bergantian.</p>
<iframe></iframe>

<p>Silakan buka kembali file <code>/etc/haproxy/haproxy.cfg</code> dan tambahkan blok konfigurasi berikut di bagian bawah:</p>
<pre><code class="language-bash">frontend docapi_front
        bind :80
        default_backend docapi_backend

backend docapi_backend
        balance roundrobin
        option httpchk GET /api/v1/health

        # Definisi server aplikasi dengan parameter health check yang presisi
        server backend-1 192.168.87.12:8080 check inter 5s fall 3 rise 2
        server backend-2 192.168.87.13:8080 check inter 5s fall 3 rise 2
</code></pre>
<p>Agar sistem benar-benar <em>High Availability</em>, kita tidak hanya sekadar mendaftarkan IP, tapi juga mengatur bagaimana HAProxy memantau "kesehatan" server tersebut:</p>
<ul>
<li><p><code>balance roundrobin</code>: Trafik akan dibagi rata secara bergantian ke setiap server.</p>
</li>
<li><p><code>option httpchk</code>: HAProxy akan melakukan pengecekan ke <em>endpoint</em> <code>/api/v1/health</code>. Jika <em>endpoint</em> ini merespon dengan status 200 OK, maka server dianggap sehat.</p>
</li>
<li><p><code>inter 5s</code>: Pengecekan dilakukan setiap 5 detik sekali.</p>
</li>
<li><p><code>fall 3</code>: Jika server gagal merespon sebanyak 3 kali berturut-turut, HAProxy akan menandai server tersebut sebagai <strong>DOWN</strong> dan berhenti mengirim trafik ke sana.</p>
</li>
<li><p><code>rise 2</code>: Jika server yang sebelumnya mati kembali sehat dan berhasil merespon sebanyak 2 kali berturut-turut, HAProxy akan memasukkannya kembali ke dalam antrean trafik (<strong>UP</strong>).</p>
</li>
</ul>
<p>Terakhir, simpan file tersebut dan jalankan perintah berikut untuk menerapkan konfigurasi baru:</p>
<pre><code class="language-bash">sudo systemctl restart haproxy.service
</code></pre>
<h2>Keepalived</h2>
<p>Setelah memiliki dua node HAProxy yang identik, kita membutuhkan mekanisme untuk mengatur siapa yang menjadi "pemimpin" (Active) dan siapa yang menjadi "cadangan" (Standby). Di sinilah <strong>Keepalived</strong> berperan.</p>
<p><strong>Keepalived</strong> adalah layanan berbasis Linux yang dirancang untuk menyediakan fitur High Availability melalui protokol <strong>VRRP (Virtual Router Redundancy Protocol)</strong>. Tugas utamanya adalah mengelola <strong>Virtual IP (VIP)</strong>sebuah alamat IP "melayang" yang akan berpindah secara otomatis dari node utama ke node cadangan jika terdeteksi adanya kegagalan sistem. Dengan Keepalived, proses <em>failover</em> terjadi secara instan tanpa perlu campur tangan manual.</p>
<p><a href="https://client.jetorbit.com/aff.php?aff=945"><img src="https://www.jetorbit.com/images/banner/banner-square-300x-250x.png" alt="" style="display:block;margin:0 auto" /></a></p>
<h3>Install KeepAlived</h3>
<p>Lakukan installasi keepalived dikedua VM Load Balancer (HAProxy-1 dan HAProxy-2) dengan perintah berikut.</p>
<pre><code class="language-bash">sudo apt update

sudo apt install keepalived -y
</code></pre>
<p>Setelah proses instalasi selesai, kita perlu memastikan bahwa layanan Keepalived langsung berjalan dan otomatis aktif setiap kali server melakukan <em>reboot</em> (booting). Jalankan perintah berikut:</p>
<pre><code class="language-bash">sudo systemctl enable keepalived --now
</code></pre>
<p>Untuk memastikan layanan sudah berjalan dengan benar di latar belakang, kamu bisa mengecek statusnya menggunakan perintah:</p>
<pre><code class="language-bash">sudo systemctl status keepalived.service
</code></pre>
<h3>Keepalived Configuration</h3>
<p>Sekarang kita masuk ke tahap paling krusial, yaitu menentukan peran masing-masing node HAProxy. Kita akan membuat file konfigurasi <code>/etc/keepalived/keepalived.conf</code> pada kedua VM.</p>
<p>Meskipun isinya terlihat mirip, ada perbedaan parameter kunci antara <strong>HAProxy-1 (Master)</strong> dan <strong>HAProxy-2 (Backup)</strong> yang menentukan siapa yang berhak memegang Virtual IP pertama kali.</p>
<h4>1. Konfigurasi pada HAProxy-1 (Node Master)</h4>
<p>Node ini akan menjadi pemegang utama Virtual IP selama layanannya berjalan normal. Buat filenya dengan perintah <code>sudo nano /etc/keepalived/keepalived.conf</code> dan masukkan kode berikut:</p>
<pre><code class="language-bash">vrrp_script chk_haproxy {
    # Mengecek apakah proses haproxy sedang berjalan
    script "pkill -0 haproxy"
    # Cek setiap 2 detik
    interval 2
    # Jika gagal 2x, anggap service mati
    fall 2
    # Jika sukses 1x, anggap service hidup kembali
    rise 1
    # Kurangi prioritas sebanyak 20 jika haproxy mati
    weight -20
}

vrrp_instance VI_1 {
    # Peran utama sebagai Master
    state MASTER
    # SESUAIKAN dengan nama interface jaringan VM kamu
    interface enp0s8
    # Harus sama di kedua node
    virtual_router_id 51
    # Prioritas lebih tinggi dari Backup
    priority 100
    # Interval pengiriman sinyal 'Heartbeat' (1 detik)
    advert_int 1

    authentication {
        auth_type PASS
        # Password sinkronisasi antar node
        auth_pass 1111
    }

    virtual_ipaddress {
        # Virtual IP (VIP) yang akan diakses user
        192.168.87.10
    }

    track_script {
        # Hubungkan dengan script monitoring di atas
        chk_haproxy
    }
}
</code></pre>
<h4>2. Konfigurasi pada HAProxy-2 (Node Backup)</h4>
<p>Node ini akan tetap dalam posisi <em>standby</em> dan hanya akan mengambil alih Virtual IP jika node Master mengalami gangguan. Gunakan konfigurasi yang sama, namun ubah bagian yang ditandai:</p>
<pre><code class="language-bash">vrrp_script chk_haproxy {
    # Mengecek apakah proses haproxy sedang berjalan
    script "pkill -0 haproxy"
    # Cek setiap 2 detik
    interval 2
    # Jika gagal 2x, anggap service mati
    fall 2
    # Jika sukses 1x, anggap service hidup kembali
    rise 1
    # Kurangi prioritas sebanyak 20 jika haproxy mati
    weight -20
}

vrrp_instance VI_1 {
    # Peran sebagai cadangan (Backup)
    state BACKUP
    # SESUAIKAN dengan nama interface jaringan VM kamu
    interface enp0s8
    # Harus sama di kedua node
    virtual_router_id 51
    # Prioritas lebih rendah (90) dibanding Master (100)
    priority 90
    # Interval pengiriman sinyal 'Heartbeat' (1 detik)
    advert_int 1

    authentication {
        auth_type PASS
        # Password sinkronisasi antar node
        auth_pass 1111
    }

    virtual_ipaddress {
        # Virtual IP (VIP) yang akan diakses user
        192.168.87.10
    }

    track_script {
        # Hubungkan dengan script monitoring di atas
        chk_haproxy
    }
}
</code></pre>
<h4>3. Terapkan Konfigurasi</h4>
<p>Setelah file tersimpan di kedua VM, segera lakukan <em>restart</em> pada layanan Keepalived agar pengaturan baru tersebut aktif:</p>
<pre><code class="language-bash">sudo systemctl restart keepalived.service
</code></pre>
<h2>Pengujian Akhir: Uji Ketangguhan Infrastruktur (High Availability Test)</h2>
<p>Setelah seluruh komponen terpasang, saatnya kita melakukan pengujian menyeluruh. Pada tahap ini, kita tidak lagi mengakses IP fisik server, melainkan menggunakan <strong>Virtual IP (VIP)</strong> yang telah kita konfigurasi. Silakan buka browser dan akses:</p>
<p><code>http://192.168.87.10/swagger/index.html</code></p>
<iframe></iframe>

<h3>Skenario 1: Kegagalan di Level Aplikasi (Backend Failure)</h3>
<p>Matikan salah satu server aplikasi (Backend 1 atau Backend 2).</p>
<ul>
<li><p><strong>Cara Cek:</strong> Pantau melalui dashboard stats di <code>http://192.168.87.10:8404/stats</code>.</p>
</li>
<li><p><strong>Hasil:</strong> Kamu akan melihat salah satu baris berubah menjadi merah. Namun, saat kamu me-<em>refresh</em> halaman Swagger, aplikasi tetap berjalan normal karena HAProxy mengalihkan trafik ke node yang masih sehat.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770625546819/944d33eb-650a-4539-88ac-7b77dbfe3477.png" alt="" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770625467045/1b5a37e5-02a8-4b41-9cde-b06f85678392.png" alt="" style="display:block;margin:0 auto" />

<h3>Skenario 2: Kegagalan di Level Load Balancer (Failover Test)</h3>
<p>Ini adalah pengujian paling krusial. Kita akan melihat bagaimana Virtual IP berpindah antar server Load Balancer.</p>
<ol>
<li><p>Buka terminal di <strong>VM 2 (Backup)</strong> dan jalankan perintah untuk memantau log secara <em>real-time</em>:</p>
<pre><code class="language-bash">sudo journalctl -u keepalived -f
</code></pre>
</li>
<li><p>Saat ini, status VM 2 seharusnya masih berada pada <strong>BACKUP STATE</strong>.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770625974592/f16ff968-289b-4e84-9af5-74c6e1ef4c95.png" alt="" style="display:block;margin:0 auto" />
</li>
<li><p>Sekarang, matikan <strong>VM 1 (Master)</strong> atau hentikan layanan HAProxy-nya.</p>
</li>
<li><p><strong>Perhatikan Log di VM 2:</strong> Kamu akan melihat log yang menyatakan bahwa VM 2 mendeteksi kegagalan Master dan segera berubah status menjadi <strong>MASTER STATE</strong>.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770626217725/04145000-20e4-49fc-bcd3-ccea968db9e4.png" alt="" style="display:block;margin:0 auto" />
</li>
<li><p><strong>Uji Akses:</strong> Akses kembali <code>http://192.168.87.10/swagger/index.html</code>.</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770626277648/55c03e16-da96-41bd-a9a9-34efc8c1fc3d.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Hasil Akhir:</strong> Meskipun Load Balancer utama mati total, aplikasi tetap dapat diakses seolah-olah tidak terjadi apa-apa. Virtual IP telah berpindah ke VM 2 dalam hitungan detik. Inilah arsitektur <strong>High Availability yang Sebenarnya!</strong></p>
<h2>Kesimpulan</h2>
</li>
<li><p>Membangun infrastruktur yang <strong>High Availability (HA)</strong> bukan berarti kita menciptakan sistem yang mustahil untuk rusak. Sebaliknya, HA adalah pengakuan bahwa kegagalanbaik itu <em>hardware failure</em>, <em>network issue</em>, maupun <em>human error</em>adalah hal yang pasti akan terjadi suatu saat nanti.</p>
<p>Melalui seri artikel ini, kita telah belajar bahwa arsitektur yang tangguh dibangun di atas tiga pilar utama:</p>
<ul>
<li><p><strong>Redundansi di Setiap Layer</strong>: Memastikan tidak ada satu pun komponen yang berdiri sendiri tanpa cadangan, mulai dari Load Balancer hingga server aplikasi.</p>
</li>
<li><p><strong>Mekanisme Failover Otomatis</strong>: Menggunakan protokol seperti <strong>VRRP</strong> melalui <strong>Keepalived</strong> untuk memastikan perpindahan trafik terjadi dalam hitungan detik tanpa perlu campur tangan manual.</p>
</li>
<li><p><strong>Pemisahan Data &amp; State</strong>: Menjaga agar server aplikasi tetap <em>stateless</em> dengan memisahkan penyimpanan data ke <strong>PostgreSQL</strong> dan <strong>Rustfs</strong>, sehingga konsistensi data tetap terjaga meskipun node aplikasi berpindah-pindah.</p>
</li>
</ul>
<p>Dengan mengimplementasikan <strong>Beyond Load Balancing</strong>, kita telah berhasil mengeliminasi <strong>Single Point of Failure (SPOF)</strong>. Kini, kamu bisa tidur lebih nyenyak karena tahu bahwa jika salah satu server atau load balancer kamu "pingsan" di jam 3 pagi, sistem kamu cukup cerdas untuk menyembuhkan dirinya sendiri dan tetap melayani pengguna dengan normal.</p>
<p>Infrastruktur yang baik adalah infrastruktur yang tidak terlihat oleh pengguna karena ia selalu ada dan selalu bisa diandalkan.</p>
<p><a href="https://www.nihbuatjajan.com/mdrdani"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770683387569/016985e6-e845-42a4-9a17-d23aceaef753.png" alt="https://www.nihbuatjajan.com/mdrdani" style="display:block;margin:0 auto" /></a></p>
</li>
</ul>
]]></description><link>https://notes.mdrdani.my.id/beyond-load-balancing-arsitektur-high-availability-yang-sebenarnya-part-3</link><guid isPermaLink="true">https://notes.mdrdani.my.id/beyond-load-balancing-arsitektur-high-availability-yang-sebenarnya-part-3</guid><category><![CDATA[Load Balancing]]></category><category><![CDATA[Haproxy]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Ubuntu]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Beyond Load Balancing: Arsitektur High Availability yang Sebenarnya (Part 2)]]></title><description><![CDATA[<p>Teori tanpa praktik itu seperti sayur tanpa garam. Untuk membuktikan arsitektur pada <a target="_blank" href="https://mdrdani.hashnode.dev/beyond-load-balancing-arsitektur-high-availability-yang-sebenarnya-part-1">artikel Part 1</a> bekerja, saya akan membagikan langkah-langkah implementasinya menggunakan lingkungan virtual yang bisa kamu replikasi sendiri di laptop atau PC.</p>
<h2 id="heading-persiapan-environment">Persiapan Environment</h2>
<p>Sebelum kita masuk ke konfigurasi masing-masing server, pastikan kamu sudah menyiapkan "bahan-bahan" berikut:</p>
<ol>
<li><p><strong>Virtualization Tool:</strong> Saya menggunakan <strong>Oracle VM VirtualBox</strong>.</p>
</li>
<li><p><strong>Sistem Operasi:</strong> Image <strong>Ubuntu 24.04 LTS (Noble Numbat)</strong> untuk semua node server.</p>
<p> <strong>Spesifikasi Server:</strong> 2 GB RAM dan 15 GB Hardisk.</p>
</li>
<li><p><strong>Networking:</strong> Setiap VM dikonfigurasi dengan <strong>2 Network Adapter</strong>:</p>
<ul>
<li><p><strong>Adapter 1 (NAT):</strong> Untuk akses internet (update package, install HAProxy, dll).</p>
</li>
<li><p><strong>Adapter 2 (Host-Only):</strong> Digunakan untuk jaringan LAN antar VM (komunikasi internal agar IP <code>192.168.87.x</code> bisa saling ping).</p>
</li>
</ul>
</li>
<li><p><strong>Source Code:</strong> Kita akan menggunakan API sederhana berbasis Go sebagai bahan uji coba. Kamu bisa melakukan <em>clone</em> dari repository ini: <a target="_blank" href="https://github.com/mdrdani/GoDocAPI"><code>https://github.com/mdrdani/GoDocAPI</code></a>.</p>
</li>
<li><p><strong>Docker:</strong> Dengan Docker, kita dapat dengan mudah mengatur dan mengelola environment yang diperlukan untuk menguji arsitektur yang telah dirancang.</p>
</li>
<li><p><strong>DBeaver</strong>: Tambahkan DBeaver untuk akses dan manajemen database.</p>
</li>
<li><p><strong>Koneksi Internet:</strong> Dibutuhkan selama proses instalasi <em>dependency</em>.</p>
</li>
</ol>
<p><a target="_blank" href="https://client.jetorbit.com/aff.php?aff=945"><img src="https://www.jetorbit.com/images/banner/banner-square-300x-250x.png" alt="Rekomendasi hosting untuk praktek tutorial ini" class="image--center mx-auto" /></a></p>
<h2 id="heading-topologi-lab-virtual">Topologi Lab Virtual</h2>
<p>Berdasarkan diagram yang sudah kita bahas, kita akan membagi peran setiap VM sebagai berikut:</p>
<ul>
<li><p><strong>Load Balancer 1 &amp; 2:</strong> Menginstal <code>haproxy</code> dan <code>keepalived</code>.</p>
</li>
<li><p><strong>App Backend 1 &amp; 2:</strong> Menjalankan <code>GoDocAPI</code>.</p>
</li>
<li><p><strong>Storage &amp; DB:</strong> Menginstal <code>postgresql</code> dan setup <code>Rustfs</code>.</p>
</li>
</ul>
<p>Dengan <em>set-up</em> ini, kita benar-benar mensimulasikan lingkungan pusat data (<em>data center</em>) nyata di dalam satu mesin lokal. Yuk, kita mulai dengan konfigurasi pertama!</p>
<h2 id="heading-konfigurasi-single-haproxy-basic-load-balancing">Konfigurasi Single HAProxy (Basic Load Balancing)</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770600892028/c245015b-8bf5-4234-b5d4-1eaa4f6aeb0a.png" alt class="image--center mx-auto" /></p>
<p>Tujuan utama kita di tahap ini adalah memastikan bahwa layanan aplikasi tetap <em>online</em> selama setidaknya ada satu server <em>backend</em> yang hidup. HAProxy akan bertindak sebagai "polisi lalu lintas" yang memantau kesehatan setiap node aplikasi.</p>
<hr />
<h2 id="heading-vm-database-postgres-sql-dan-rustfs-object-storage-setup">VM Database Postgres SQL dan RustFS Object Storage Setup</h2>
<p>SSH Server IP Address <strong>192.168.87.11</strong></p>
<h3 id="heading-install-docker"><strong>Install Docker</strong></h3>
<p>Jalankan perintah berikut untuk melakukan installasi Docker di VM.</p>
<pre><code class="lang-bash">sudo apt install apt-transport-https ca-certificates curl software-properties-common -y 

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc 

<span class="hljs-built_in">echo</span> <span class="hljs-string">"deb [arch=<span class="hljs-subst">$(dpkg --print-architecture)</span> signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu <span class="hljs-subst">$(. /etc/os-release &amp;&amp; echo <span class="hljs-string">"<span class="hljs-variable">$VERSION_CODENAME</span>"</span>)</span> stable"</span> | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null

sudo apt update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

sudo usermod -aG docker <span class="hljs-variable">$user</span>
</code></pre>
<p>silakan buatkan file <code>docker-compose.yaml</code> dengan konfigurasi sebagai berikut.</p>
<pre><code class="lang-dockerfile">services:
  postgres:
    image: postgres:<span class="hljs-number">16</span>
    container_name: postgres
    restart: unless-stopped
    environment:
      POSTGRES_USER: app_user
      POSTGRES_PASSWORD: app_password
      POSTGRES_DB: app_db
    ports:
      - <span class="hljs-string">"5432:5432"</span>
    volumes:
      - postgres_data:/var/lib/postgresql/data
    <span class="hljs-keyword">healthcheck</span><span class="bash">:</span>
      test: [<span class="hljs-string">"CMD-SHELL"</span>, <span class="hljs-string">"pg_isready -U app_user -d app_db"</span>]
      interval: <span class="hljs-number">10</span>s
      timeout: <span class="hljs-number">5</span>s
      retries: <span class="hljs-number">5</span>

  rustfs:
    image: rustfs/rustfs:latest
    container_name: rustfs
    restart: unless-stopped
    ports:
      - <span class="hljs-string">"9000:9000"</span>
      - <span class="hljs-string">"9001:9001"</span>
    environment:
      RUSTFS_VOLUMES: /data/rustfs{<span class="hljs-number">0</span>..<span class="hljs-number">3</span>}
      RUSTFS_ADDRESS: <span class="hljs-number">0.0</span>.<span class="hljs-number">0.0</span>:<span class="hljs-number">9000</span>
      RUSTFS_CONSOLE_ADDRESS: <span class="hljs-number">0.0</span>.<span class="hljs-number">0.0</span>:<span class="hljs-number">9001</span>
      RUSTFS_CONSOLE_ENABLE: <span class="hljs-string">"true"</span>
      RUSTFS_ACCESS_KEY: rustfsadmin
      RUSTFS_SECRET_KEY: rustfsadmin
    volumes:
      - rustfs_data:/data
      - rustfs_logs:/logs
    <span class="hljs-keyword">healthcheck</span><span class="bash">:</span>
      test: [<span class="hljs-string">"CMD"</span>, <span class="hljs-string">"curl"</span>, <span class="hljs-string">"-f"</span>, <span class="hljs-string">"http://localhost:9000/"</span>]
      interval: <span class="hljs-number">15</span>s
      timeout: <span class="hljs-number">5</span>s
      retries: <span class="hljs-number">5</span>
      start_period: <span class="hljs-number">20</span>s

volumes:
  postgres_data:
  rustfs_data:
  rustfs_logs:
</code></pre>
<h3 id="heading-jalankan-service">Jalankan service</h3>
<p>Setelah file dibuat:</p>
<pre><code class="lang-bash">mkdir -p data/rustfs0 data/rustfs1 data/rustfs2 data/rustfs3 logs
docker compose up -d
</code></pre>
<p>Cek container:</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p>Akses service:</p>
<ul>
<li><p>PostgreSQL  <code>192.168.20.104:5432</code></p>
</li>
<li><p>RustFS API  <a target="_blank" href="http://192.168.20.104:9000"><code>http://192.168.20.104:9000</code></a></p>
</li>
<li><p>RustFS Console  <a target="_blank" href="http://192.168.20.104:9001"><code>http://192.168.20.104:9001</code></a></p>
</li>
</ul>
<p>Server C kini siap menjadi <strong>storage layer</strong> untuk cluster Docker Swarm pada tahap berikutnya.</p>
<h3 id="heading-test-akses-postgresql">Test Akses PostgreSQL</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770602513110/df630af1-8b41-4f60-a1dc-e36fbb4e8466.png" alt class="image--center mx-auto" /></p>
<p>jangan lupa untuk inisialisasi Database schema dahulu:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> EXTENSION <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> <span class="hljs-string">"uuid-ossp"</span>;

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> documents (
  <span class="hljs-keyword">id</span>           <span class="hljs-keyword">UUID</span>        PRIMARY <span class="hljs-keyword">KEY</span> <span class="hljs-keyword">DEFAULT</span> uuid_generate_v4(),
  filename     <span class="hljs-built_in">TEXT</span>        <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  storage_path <span class="hljs-built_in">TEXT</span>        <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">UNIQUE</span>,
  <span class="hljs-keyword">size</span>         <span class="hljs-built_in">BIGINT</span>      <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">CHECK</span> (<span class="hljs-keyword">size</span> &gt;= <span class="hljs-number">0</span>),
  content_type <span class="hljs-built_in">TEXT</span>        <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  created_at   TIMESTAMPTZ <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">now</span>()
);

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> idx_documents_filename <span class="hljs-keyword">ON</span> documents (filename);
</code></pre>
<h3 id="heading-test-akses-rustfs"><strong>Test Akses RustFS</strong></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770602403515/452990d3-9dc4-434f-aaaa-ef66b999a479.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770602589862/05f8e5c6-2a3d-461d-8c19-ffc21047043d.png" alt class="image--center mx-auto" /></p>
<p>jika ingin melihat logs dari docker container yang telah kita jalankan jalankan perintah</p>
<pre><code class="lang-bash">docker compose logs -f
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770601404424/48b615d9-6856-4f2a-814e-5bc3dba54015.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-vm-backend-1-dan-2-setup">VM Backend 1 dan 2 Setup</h2>
<p>SSH Server IP Address <strong>192.168.87.12</strong></p>
<h3 id="heading-install-docker-1"><strong>Install Docker</strong></h3>
<p>Jalankan perintah berikut untuk melakukan installasi Docker di VM.</p>
<pre><code class="lang-bash">sudo apt install apt-transport-https ca-certificates curl software-properties-common -y 

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc 

<span class="hljs-built_in">echo</span> <span class="hljs-string">"deb [arch=<span class="hljs-subst">$(dpkg --print-architecture)</span> signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu <span class="hljs-subst">$(. /etc/os-release &amp;&amp; echo <span class="hljs-string">"<span class="hljs-variable">$VERSION_CODENAME</span>"</span>)</span> stable"</span> | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null

sudo apt update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y

sudo usermod -aG docker <span class="hljs-variable">$user</span>
</code></pre>
<h3 id="heading-clone-repository"><strong>Clone Repository</strong></h3>
<p>Lakukan clone repository dengna perintah berikut.</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/mdrdani/GoDocAPI
</code></pre>
<h3 id="heading-build-docker-image"><strong>Build Docker Image</strong></h3>
<p>Lakukan build image pada repository GoDocAPI.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> GoDocAPI/

docker build -t godocapi .
</code></pre>
<h3 id="heading-create-docker-compose"><strong>Create Docker Compose</strong></h3>
<p>buatkan file docker-compose.yaml dengan konfigurasi sebagai berikut</p>
<pre><code class="lang-dockerfile">services:
  app:
    image: godocapi:latest
    container_name: godocapi-app
    restart: unless-stopped
    ports:
      - <span class="hljs-number">8080</span>:<span class="hljs-number">8080</span>
    env_file:
      - .<span class="hljs-keyword">env</span>
</code></pre>
<h3 id="heading-create-env"><strong>Create .Env</strong></h3>
<p>buatkan satu buah file .env untuk menyimpan konfigurasi aplikasi seperti koneksi database dan object storage</p>
<pre><code class="lang-dockerfile">SERVER_PORT=:<span class="hljs-number">8080</span>
DB_URL=postgres://app_user:app_password@<span class="hljs-number">192.168</span>.<span class="hljs-number">87.11</span>:<span class="hljs-number">5432</span>/app_db?sslmode=disable
RUSTFS_ENDPOINT=http://<span class="hljs-number">192.168</span>.<span class="hljs-number">87.11</span>:<span class="hljs-number">9000</span>
RUSTFS_ACCESS_KEY=rustfsadmin
RUSTFS_SECRET_KEY=rustfsadmin
RUSTFS_BUCKET=docapi
RUSTFS_REGION=us-east-<span class="hljs-number">1</span>
</code></pre>
<h3 id="heading-running-docker-compose"><strong>Running Docker Compose</strong></h3>
<p>jalankan Docker compose dengan perintah berikut</p>
<pre><code class="lang-bash">docker compose up -d
</code></pre>
<p>jika ingin melihat logs dari docker container yang telah kita jalankan jalankan perintah</p>
<pre><code class="lang-bash">docker compose logs -f
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770602678112/8e489e33-b0b6-490f-a08b-0731d9bb0ec9.png" alt class="image--center mx-auto" /></p>
<p>Ulangi di VM kedua menggunakan SSH ke IP address <strong>192.168.87.13.</strong></p>
<hr />
<div class="hn-embed-widget" id="jagohosting-mdrdani"></div><p> </p>
<h2 id="heading-load-balancer-setup">Load Balancer Setup</h2>
<p>Pada tahap ini, kita akan menggunakan <strong>HAProxy Community Edition</strong> sebagai solusi <em>load balancer</em>. Saya menyiapkan satu Virtual Machine khusus dengan sistem operasi <strong>Ubuntu 24.04 LTS (Noble Numbat)</strong> untuk menjalankan tugas ini. Berikut adalah langkah-langkah instalasi dan konfigurasinya.</p>
<h3 id="heading-install-haproxy"><strong>Install HAProxy</strong></h3>
<p>SSH ke VM IP Address 192.168.87.6, kemudian jalankan perintah berikut</p>
<pre><code class="lang-bash">sudo apt update

sudo apt-get install --no-install-recommends software-properties-common

sudo add-apt-repository ppa:vbernat/haproxy-3.2

sudo apt-get install haproxy=3.2.\*
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770603999708/9eec05f9-4ce3-44f1-8382-a4cf56fe4a9f.png" alt class="image--center mx-auto" /></p>
<p>untuk memastikan HAProxy sudah berjalan dengan baik kita check dengan perintah</p>
<pre><code class="lang-bash">sudo systemctl status haproxy
</code></pre>
<h3 id="heading-enable-dashboard"><strong>Enable Dashboard</strong></h3>
<p>Secara <em>default</em>, HAProxy bekerja di balik layar tanpa tampilan visual. Namun, HAProxy memiliki fitur dashboard bawaan yang sangat berguna untuk memantau status server <em>backend</em>, jumlah trafik, hingga mendeteksi secara visual jika salah satu node aplikasi sedang <em>down</em>.</p>
<p>Untuk mengaktifkan fitur ini, silakan ikuti langkah-langkah berikut:</p>
<p>Modifikasi Konfigurasi HAProxy Buka file <code>/etc/haproxy/haproxy.cfg</code> dan tambahkan blok konfigurasi berikut untuk menentukan pada port dan alamat mana dashboard bisa diakses:</p>
<pre><code class="lang-bash">frontend stats
        mode http
        <span class="hljs-built_in">bind</span> :8404
        stats <span class="hljs-built_in">enable</span>
        stats refresh 10s
        stats uri /stats
        stats show-modules
</code></pre>
<blockquote>
<p><strong>Catatan:</strong> Di sini kita menggunakan port <code>8404</code>. Pastikan port ini terbuka dan tidak terpakai oleh layanan lain.</p>
</blockquote>
<p>Terapkan Perubahan Agar konfigurasi baru terbaca oleh sistem, lakukan <em>restart</em> pada layanan HAProxy dengan perintah berikut:</p>
<pre><code class="lang-bash">sudo systemctl restart haproxy.service
</code></pre>
<p>Akses Dashboard Sekarang, kita bisa melihat kondisi infrastruktur secara langsung melalui browser dengan mengakses alamat: <a target="_blank" href="http://192.168.87.6:8404/stats"><code>http://192.168.87.6:8404/stats</code></a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770604336060/d668c4dd-ff7a-433f-bde6-e839139b6699.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-load-balancer-config"><strong>Load Balancer Config</strong></h3>
<p>Setelah dashboard aktif, langkah selanjutnya adalah inti dari skenario kita: <strong>mengatur pembagian trafik</strong>. Di sini, HAProxy akan bertindak sebagai gerbang utama (port 80) yang akan meneruskan permintaan user ke dua server aplikasi (<code>backend-1</code> dan <code>backend-2</code>) secara bergantian.</p>
<p>Silakan buka kembali file <code>/etc/haproxy/haproxy.cfg</code> dan tambahkan blok konfigurasi berikut di bagian bawah:</p>
<pre><code class="lang-bash">frontend docapi_front
        <span class="hljs-built_in">bind</span> :80
        default_backend docapi_backend

backend docapi_backend
        balance roundrobin
        option httpchk GET /api/v1/health

        <span class="hljs-comment"># Definisi server aplikasi dengan parameter health check yang presisi</span>
        server backend-1 192.168.87.12:8080 check inter 5s fall 3 rise 2
        server backend-2 192.168.87.13:8080 check inter 5s fall 3 rise 2
</code></pre>
<p>Agar sistem benar-benar <em>High Availability</em>, kita tidak hanya sekadar mendaftarkan IP, tapi juga mengatur bagaimana HAProxy memantau "kesehatan" server tersebut:</p>
<ul>
<li><p><code>balance roundrobin</code>: Trafik akan dibagi rata secara bergantian ke setiap server.</p>
</li>
<li><p><code>option httpchk</code>: HAProxy akan melakukan pengecekan ke <em>endpoint</em> <code>/api/v1/health</code>. Jika <em>endpoint</em> ini merespon dengan status 200 OK, maka server dianggap sehat.</p>
</li>
<li><p><code>inter 5s</code>: Pengecekan dilakukan setiap 5 detik sekali.</p>
</li>
<li><p><code>fall 3</code>: Jika server gagal merespon sebanyak 3 kali berturut-turut, HAProxy akan menandai server tersebut sebagai <strong>DOWN</strong> dan berhenti mengirim trafik ke sana.</p>
</li>
<li><p><code>rise 2</code>: Jika server yang sebelumnya mati kembali sehat dan berhasil merespon sebanyak 2 kali berturut-turut, HAProxy akan memasukkannya kembali ke dalam antrean trafik (<strong>UP</strong>).</p>
</li>
</ul>
<p>Terakhir, simpan file tersebut dan jalankan perintah berikut untuk menerapkan konfigurasi baru:</p>
<pre><code class="lang-bash">sudo systemctl restart haproxy.service
</code></pre>
<h3 id="heading-testing-load-balancer"><strong>Testing Load Balancer</strong></h3>
<p>Setelah semua konfigurasi selesai, saatnya kita melakukan pembuktian. Tujuan pengujian ini adalah memastikan bahwa Load Balancer benar-benar mampu menjaga ketersediaan layanan meskipun salah satu server di belakangnya tumbang.</p>
<div class="hn-embed-widget" id="jetorbit-mdrdani"></div><p> </p>
<p>Pertama, pastikan aplikasi dapat diakses secara normal melalui alamat IP Load Balancer. Karena kita menggunakan <strong>GoDocAPI</strong>, silakan buka browser dan akses: <a target="_blank" href="http://192.168.87.6/swagger/index.html">http://192.168.87.6/swagger/index.html</a></p>
<p>Jika muncul tampilan dokumentasi Swagger, berarti HAProxy telah berhasil meneruskan trafik ke salah satu backend yang tersedia.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770606165200/c4343967-b445-4bfd-8ef8-25d531a41090.png" alt class="image--center mx-auto" /></p>
<p>Sekarang, mari kita simulasikan kondisi <em>error</em> yang sebenarnya. Ikuti langkah berikut:</p>
<ul>
<li><p><strong>Matikan Aplikasi:</strong> Masuk ke VM <code>App Backend</code> 2 (192.168.87.13) dan hentikan layanan aplikasinya (atau matikan VM tersebut).</p>
</li>
<li><p><strong>Pantau Dashboard:</strong> Jika kamu membuka dashboard stats di port <code>8404</code>, kamu akan melihat baris <code>backend-1</code> berubah menjadi warna merah (Status: <strong>DOWN</strong>).</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770606334842/702cca25-0923-4eb6-991d-2d78a60a2513.png" alt class="image--center mx-auto" /></p>
</li>
<li><p><strong>Uji Kembali:</strong> Coba <em>refresh</em> halaman Swagger tadi.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770606358160/52abbddd-25b0-4d42-ab8a-131ea0e70da6.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<p><strong>Hasilnya?</strong> Aplikasi tetap dapat diakses dan berfungsi dengan normal. Hal ini terjadi karena HAProxy secara otomatis mendeteksi kegagalan pada <code>backend-1</code> dan mengalihkan seluruh permintaan user ke <code>backend-2</code>. Inilah esensi dari redundansi di level aplikasi.</p>
<p>Next Part 3 kita akan implementasikan 2 HAProxy.</p>
<p><a target="_blank" href="https://www.nihbuatjajan.com/mdrdani"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770683509345/908c9629-e163-44d4-a0eb-fa80c3b1bd78.png" alt class="image--center mx-auto" /></a></p>
]]></description><link>https://notes.mdrdani.my.id/beyond-load-balancing-arsitektur-high-availability-yang-sebenarnya-part-2</link><guid isPermaLink="true">https://notes.mdrdani.my.id/beyond-load-balancing-arsitektur-high-availability-yang-sebenarnya-part-2</guid><category><![CDATA[Ubuntu]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Haproxy]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Beyond Load Balancing: Arsitektur High Availability yang Sebenarnya (Part 1)]]></title><description><![CDATA[<h2 id="heading-mitos-load-balancer-saja-cukup">Mitos "Load Balancer Saja Cukup"</h2>
<p>Banyak dari kita mungkin merasa sudah aman ketika berhasil menerapkan <em>load balancer</em> di depan beberapa server <em>backend</em>. Rasanya seperti sudah punya asuransi; kalau satu server mati, yang lain masih bisa menopang. Tapi, pernahkah kamu bertanya-tanya: <strong>Bagaimana jika si pengatur lalu lintas itu sendiri yang pingsan?</strong></p>
<p>Inilah jebakan <em>Single Point of Failure</em> (SPOF) yang sering terlupakan. Sebagus apapun <em>backend</em> yang kita bangun, jika pintu gerbang utamanya hanya satu dan ia tumbang, maka seluruh sistem kita akan ikut 'gelap'. Dalam artikel kali ini, saya ingin mengajak teman-teman untuk melihat lebih jauh melampaui konsep distribusi beban biasa. Kita akan membedah bagaimana membangun arsitektur <em>High Availability</em> yang sebenarnyasebuah sistem yang tidak hanya tangguh di level aplikasi, tapi juga memiliki mekanisme pertahanan diri saat infrastrukturnya mengalami kegagalan</p>
<div class="hn-embed-widget" id="jagohosting2-mdrdani"></div><p> </p>
<h2 id="heading-redundansi-di-level-aplikasi">Redundansi di Level Aplikasi</h2>
<p>Pada tahap awal pengembangan, kita sering kali memulai dengan satu server tunggal. Namun, seiring bertambahnya trafik, kita mulai mengenal konsep redundansi. Mari kita perhatikan ilustrasi pertama di bawah ini:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770262121222/987eece1-4df2-4258-9bbe-c5ad95f5af01.png" alt class="image--center mx-auto" /></p>
<p>Di sini, kita menempatkan <strong>HAProxy</strong> sebagai garda terdepan. Tugasnya sederhana namun krusial: menerima permintaan dari user dan membaginya ke <strong>App Backend 1</strong> dan <strong>App Backend 2</strong>. Jika Backend 1 sedang sibuk atau mengalami <em>crash</em>, HAProxy secara cerdas akan mengalihkan trafik ke Backend 2. Inilah bentuk awal dari ketahanan sistem.</p>
<h3 id="heading-mengapa-database-dan-storage-harus-terpisah">Mengapa Database dan Storage Harus Terpisah?</h3>
<p>Satu hal yang menarik dari diagram di atas adalah pemisahan antara server aplikasi dengan <strong>PostgreSQL</strong> dan <strong>Rustfs (Object Storage)</strong>. Mungkin ada yang bertanya, <em>"Kenapa tidak dijadikan satu saja di dalam server aplikasi agar lebih hemat?"</em> Ada dua alasan utama mengapa pemisahan ini wajib dilakukan dalam arsitektur High Availability:</p>
<ul>
<li><p><strong>Data Consistency (Konsistensi Data):</strong> Jika setiap backend memiliki database lokal sendiri, maka data antara Backend 1 dan Backend 2 tidak akan pernah sinkron. Dengan memisahkan database ke server tersendiri, semua node aplikasi akan melihat dan menulis ke satu sumber kebenaran (<em>single source of truth</em>) yang sama.</p>
</li>
<li><p><strong>Stateless Application:</strong> Agar kita bisa menambah atau menghapus server aplikasi sesuka hati (<em>scaling</em>), server aplikasi tersebut tidak boleh menyimpan data permanen secara lokal. Semua file gambar atau dokumen harus dilempar ke <strong>Rustfs</strong>, dan semua data relasional disimpan di <strong>PostgreSQL</strong>. Dengan begini, server aplikasi menjadi <em>stateless</em>artinya, ia bisa diganti kapan saja tanpa takut kehilangan data user.</p>
</li>
</ul>
<p>Arsitektur ini sudah cukup bagus, tapi masih menyimpan satu titik lemah yang fatal: <strong>Bagaimana jika HAProxy yang ada di tengah itu mati?</strong> Seluruh sistem akan lumpuh seketika. Masalah inilah yang akan kita pecahkan di bagian selanjutnya.</p>
<div class="hn-embed-widget" id="jetorbit-mdrdani"></div><p> </p>
<h2 id="heading-high-availability-yang-sebenarnya">High Availability yang Sebenarnya</h2>
<p>Jika pada diagram pertama kita merasa sudah aman dengan dua backend, sekarang mari kita hadapi kenyataan pahit: <strong>Bagaimana jika server HAProxy dengan IP</strong> 192.168.87.6 mati?</p>
<p>Meskipun kamu punya 100 backend yang sehat di belakangnya, user tetap tidak akan bisa mengakses aplikasi karena pintu masuknya terkunci. Di sinilah kita masuk ke level High Availability yang sebenarnya dengan memperkenalkan konsep <strong>Load Balancer Redundancy</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770262132815/253032fd-58b5-41d9-bf77-7bb18723c2de.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-mengenal-mekanisme-failover-amp-health-check">Mengenal Mekanisme Failover &amp; Health Check</h3>
<p>Pada diagram kedua, kita tidak lagi hanya mengandalkan satu HAProxy. Kita menambahkan satu lagi "penjaga" dengan IP 192.168.87.7. Perhatikan tanda panah dua arah bertuliskan <strong>"Health Check"</strong> di antara keduanya. Inilah rahasianya:</p>
<ol>
<li><p><strong>Saling Mengawasi (Heartbeat):</strong> Kedua node HAProxy ini saling mengirimkan sinyal "detak jantung" secara terus-menerus. Selama node utama (192.168.87.6) memberikan respon, node cadangan (192.168.87.7) akan tetap dalam posisi <em>standby</em>.</p>
</li>
<li><p><strong>Deteksi Kegagalan:</strong> Begitu node utama berhenti merespon (mungkin karena <em>hardware failure</em> atau <em>kernel panic</em>), node cadangan akan langsung mendeteksinya dalam hitungan milidetik.</p>
</li>
<li><p><strong>Pengambilalihan (Failover):</strong> Node cadangan akan segera mengambil alih tugas node utama. Di sinilah biasanya kita menggunakan teknologi seperti <strong>Keepalived</strong> atau <strong>VRRP</strong>.</p>
</li>
</ol>
<h3 id="heading-keajaiban-floating-ip-virtual-ip">Keajaiban Floating IP (Virtual IP)</h3>
<p>Mungkin kamu bingung: <em>"Kalau IP-nya ada dua (192.168.87.6 dan 192.168.87.7), lalu user tembak ke IP yang mana?"</em></p>
<p>Dalam arsitektur HA yang matang, kita menggunakan <strong>Floating IP</strong> atau <strong>Virtual IP (VIP)</strong>. User tidak menembak langsung ke IP asli server, melainkan ke satu IP bayangan. IP bayangan ini secara dinamis akan "menempel" pada server HAProxy yang sedang aktif. Jika server A mati, IP bayangan itu akan "berpindah" secara otomatis ke server B tanpa user sadari.</p>
<h3 id="heading-menghilangkan-single-point-of-failure">Menghilangkan Single Point of Failure</h3>
<p>Dengan konfigurasi ini, kita telah berhasil mengeliminasi titik lemah di setiap lapisan:</p>
<ul>
<li><p><strong>Layer Traffic:</strong> Jika satu LB mati, ada LB cadangan.</p>
</li>
<li><p><strong>Layer Application:</strong> Jika satu backend mati, ada node lain yang siap sedia.</p>
</li>
<li><p><strong>Layer Data:</strong> Database dan Storage dipisahkan agar tetap <em>persistent</em> dan bisa diakses secara kolektif.</p>
</li>
</ul>
<p>Inilah yang kita sebut sebagai infrastruktur yang memiliki daya tahan tinggi atau <em>Resilient Infrastructure</em>.</p>
<h2 id="heading-kesimpulan">Kesimpulan</h2>
<p>Setelah membedah diagram-diagram di atas, satu hal yang harus kita sadari adalah: <strong>High Availability (HA) bukanlah sebuah barang yang bisa kita beli jadi, lalu selesai.</strong> HA adalah sebuah perjalanan untuk terus mengidentifikasi dan mengeliminasi setiap kemungkinan titik kegagalan atau <em>Single Point of Failure</em> (SPOF).</p>
<p>Membangun infrastruktur seperti ini memang membutuhkan usaha ekstrakita harus mengonfigurasi dua HAProxy, mengatur protokol VRRP, hingga memastikan aplikasi kita bersifat <em>stateless</em> agar bisa berkomunikasi dengan PostgreSQL dan Rustfs secara lancar. Namun, imbalannya sebanding: sebuah sistem yang memiliki daya tahan tinggi (<em>resilience</em>) dan kepercayaan dari pengguna.</p>
<p>Ringkasnya, prinsip utama dalam membangun arsitektur HA adalah:</p>
<ul>
<li><p><strong>Redundansi:</strong> Selalu miliki cadangan di setiap lapisan.</p>
</li>
<li><p><strong>Failover otomatis:</strong> Jangan biarkan proses perpindahan bergantung pada campur tangan manusia.</p>
</li>
<li><p><strong>Separation of Concerns:</strong> Pisahkan logika aplikasi dari penyimpanan data agar sistem lebih fleksibel.</p>
</li>
</ul>
<p>Infrastruktur yang kuat bukan berarti infrastruktur yang tidak pernah mengalami kendala. Justru sebaliknya, infrastruktur yang hebat adalah yang sudah siap menghadapi kerusakan di bagian mana pun, namun tetap mampu melayani pengguna seolah-olah tidak terjadi apa-apa.</p>
<p>Next Part 2 kita implementasikan.</p>
<p><a target="_blank" href="https://client.jetorbit.com/aff.php?aff=945"><img src="https://www.jetorbit.com/images/banner/banner-wide-728-v2.png" alt class="image--center mx-auto" /></a></p>
<p><a target="_blank" href="https://www.nihbuatjajan.com/mdrdani"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770683578719/3eceb5d2-6e40-4fd5-894e-762e237201c0.png" alt class="image--center mx-auto" /></a></p>
]]></description><link>https://notes.mdrdani.my.id/beyond-load-balancing-arsitektur-high-availability-yang-sebenarnya-part-1</link><guid isPermaLink="true">https://notes.mdrdani.my.id/beyond-load-balancing-arsitektur-high-availability-yang-sebenarnya-part-1</guid><category><![CDATA[Load Balancing]]></category><category><![CDATA[high availability]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Go Language]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Rust]]></category><category><![CDATA[Haproxy]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Running Three-Tier Architecture App on Virtual Machine with Vagrant]]></title><description><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747582464576/6fa968e5-9263-444c-a36c-bb596ab186ff.png" alt class="image--center mx-auto" /></p>
<p>Dalam pengembangan aplikasi modern, arsitektur <strong>three-tier</strong> (tiga lapis) adalah pendekatan yang umum digunakan karena memisahkan logika aplikasi menjadi tiga komponen utama: <strong>frontend</strong>, <strong>backend</strong>, dan <strong>database</strong>. Namun, seiring bertambahnya kompleksitas sistem, kebutuhan akan pengelolaan <strong>infrastruktur yang otomatis dan konsisten</strong> menjadi semakin penting.</p>
<p>Melalui artikel ini, kita akan membahas implementasi arsitektur three-tier yang tidak hanya modular, tetapi juga <strong>sepenuhnya dapat diotomatisasi</strong> menggunakan <strong>Vagrant</strong> dan <strong>VirtualBox</strong>. Pendekatan ini sangat cocok untuk tim pengembang yang menginginkan lingkungan lokal yang stabil dan mudah direplikasi.</p>
<ul>
<li><p><strong>Frontend</strong>: Dibangun dengan React.js</p>
</li>
<li><p><strong>Backend</strong>: Menggunakan Node.js</p>
</li>
<li><p><strong>Database</strong>: PostgreSQL</p>
</li>
<li><p><strong>Load Balancer</strong>: Dikelola oleh NGINX</p>
</li>
<li><p><strong>Virtualisasi</strong>: Semua komponen dijalankan dalam <strong>VM</strong> yang dikelola oleh <strong>Vagrant</strong> dan <strong>VirtualBox</strong></p>
</li>
</ul>
<h2 id="heading-arsitektur-sistem">Arsitektur Sistem</h2>
<h3 id="heading-1-user-client">1. <strong>User (Client)</strong></h3>
<p>Pengguna mengakses aplikasi melalui browser. Semua permintaan dari client diarahkan terlebih dahulu ke server NGINX yang bertindak sebagai <strong>load balancer</strong>.</p>
<h3 id="heading-2-nginx-sebagai-load-balancer">2. NGINX sebagai Load Balancer</h3>
<p>NGINX berada di garis depan sistem dan berperan sebagai <strong>load balancer</strong>, yaitu komponen yang bertugas untuk <strong>mendistribusikan permintaan dari pengguna ke server tujuan yang sesuai</strong>. Dalam arsitektur ini, NGINX memiliki fungsi utama:</p>
<ul>
<li><p><strong>Menerima request dari user</strong></p>
</li>
<li><p><strong>Meneruskan permintaan ke Frontend VM</strong> jika permintaan adalah halaman UI</p>
</li>
<li><p><strong>Meneruskan permintaan ke Backend VM</strong> jika permintaan adalah API</p>
</li>
<li><p><strong>Menyediakan SSL termination</strong> <em>(opsional, jika HTTPS digunakan)</em></p>
</li>
<li><p><strong>Menyembunyikan VM internal dari akses langsung pengguna</strong></p>
</li>
<li><p><strong>Meningkatkan skalabilitas dan keandalan sistem</strong> dengan mengatur lalu lintas berdasarkan peran server</p>
</li>
</ul>
<div class="hn-embed-widget" id="jagohosting-mdrdani"></div><p> </p>
<h3 id="heading-3-frontend-vm-reactjs">3. <strong>Frontend VM (React.js)</strong></h3>
<p>VM ini menjalankan aplikasi React yang merupakan antarmuka pengguna (UI). Semua aset statis seperti HTML, CSS, dan JavaScript di-serve dari sini. React akan mengirimkan permintaan API ke backend menggunakan endpoint yang sudah ditentukan.</p>
<h3 id="heading-4-backend-vm-nodejs">4. <strong>Backend VM (Node.js)</strong></h3>
<p>VM ini menjalankan server aplikasi berbasis <strong>Node.js</strong>. Backend memproses logika bisnis, validasi, autentikasi, dan komunikasi dengan database.</p>
<p>Contoh fungsi backend:</p>
<ul>
<li><p>Login user</p>
</li>
<li><p>Pemrosesan form</p>
</li>
<li><p>Pengambilan data produk</p>
</li>
<li><p>Penyimpanan data baru ke database</p>
</li>
</ul>
<h3 id="heading-5-database-vm-postgresql">5. <strong>Database VM (PostgreSQL)</strong></h3>
<p>Menyimpan data aplikasi seperti user, transaksi, atau konten lainnya. Backend akan berkomunikasi langsung dengan VM ini melalui koneksi database.</p>
<h3 id="heading-6-vagrant-virtualbox">6. <strong>Vagrant + VirtualBox</strong></h3>
<p>Seluruh komponen (Frontend, Backend, Database) dijalankan dalam virtual machine yang dikelola oleh <strong>Vagrant</strong>, dengan <strong>VirtualBox</strong> sebagai provider VM.</p>
<p>Keuntungan menggunakan Vagrant dengan VirtualBox:</p>
<ul>
<li><p><strong>Automasi provisioning</strong>: Semua VM bisa dibuat dan dikonfigurasi hanya dengan satu perintah (<code>vagrant up</code>)</p>
</li>
<li><p><strong>Isolasi lingkungan</strong>: Setiap lapisan berjalan di VM terpisah</p>
</li>
<li><p><strong>Jaringan virtual</strong>: Dapat diatur menggunakan IP statis via <code>private_network</code> atau <code>host-only</code></p>
</li>
<li><p><strong>Lingkungan konsisten</strong>: Developer tidak perlu menginstal layanan manual di OS host</p>
</li>
<li><p><strong>Portabilitas</strong>: Konfigurasi yang sama bisa dijalankan di mesin berbeda</p>
</li>
</ul>
<h3 id="heading-vagrantfile">Vagrantfile</h3>
<p>Konfigurasi <code>Vagrantfile</code> di bawah adalah bagian dari arsitektur infrastruktur otomatis menggunakan <strong>Vagrant</strong> dan <strong>VirtualBox</strong>, yang menetapkan <strong>pengaturan umum</strong> untuk semua VM dalam proyek.</p>
<pre><code class="lang-bash">Vagrant.configure(<span class="hljs-string">"2"</span>) <span class="hljs-keyword">do</span> |config|
  <span class="hljs-comment"># Configure common settings</span>

  <span class="hljs-comment"># Base box</span>
  config.vm.box = <span class="hljs-string">"bento/ubuntu-22.04"</span>

  <span class="hljs-comment"># Configure VirtualBox related settings</span>
  config.vm.provider <span class="hljs-string">"virtualbox"</span> <span class="hljs-keyword">do</span> |vb|
    <span class="hljs-comment"># Set CPU and Memory of the VM</span>
    vb.memory = <span class="hljs-string">"768"</span>
    vb.cpus = 2
  end

  <span class="hljs-comment"># I don't want to check for updates</span>
  config.vm.box_check_update = <span class="hljs-literal">false</span>

  <span class="hljs-comment"># Disable the default share of the current code directory</span>
  <span class="hljs-comment"># I don't want the guest to write to make changes to the code</span>
  config.vm.synced_folder <span class="hljs-string">"."</span>, <span class="hljs-string">"/vagrant"</span>, disabled: <span class="hljs-literal">true</span>

  <span class="hljs-comment"># Common provisioning script for all VMs</span>
  config.vm.provision <span class="hljs-string">"shell"</span>, inline: <span class="hljs-string">"apt update &amp;&amp; apt install -y curl git gnupg net-tools"</span>

  <span class="hljs-comment"># Common provisioning script to install NodeJS v18</span>
  <span class="hljs-comment"># It will be used in frontend and backend VMs</span>
  nodejs_install_script = &lt;&lt;-SHELL
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Installing NodeJS v18"</span>
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
    <span class="hljs-built_in">export</span> NVM_DIR=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.nvm"</span>
    [ -s <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span> ] &amp;&amp; \. <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span>
    [ -s <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/bash_completion"</span> ] &amp;&amp; \. <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/bash_completion"</span>
    nvm install 18
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"NodeJS version: <span class="hljs-subst">$(node -v)</span>"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"NPM version: <span class="hljs-subst">$(npm -v)</span>"</span>
    sudo npm install -g pm2 npm
  SHELL
.......
</code></pre>
<p>Berikut penjelasan tiap bagian:</p>
<pre><code class="lang-bash">Vagrant.configure(<span class="hljs-string">"2"</span>) <span class="hljs-keyword">do</span> |config|
</code></pre>
<p>Menandakan kita menggunakan <strong>Vagrantfile format versi 2</strong>, yang merupakan standar saat ini.</p>
<pre><code class="lang-bash">config.vm.box = <span class="hljs-string">"bento/ubuntu-22.04"</span>
</code></pre>
<p>Mengatur <strong>sistem operasi dasar</strong> semua VM menjadi <strong>Ubuntu 22.04</strong> dari provider bento.</p>
<pre><code class="lang-bash">config.vm.provider <span class="hljs-string">"virtualbox"</span> <span class="hljs-keyword">do</span> |vb|
  vb.memory = <span class="hljs-string">"768"</span>
  vb.cpus = 2
end
</code></pre>
<p>Mengatur resource untuk setiap VM:</p>
<ul>
<li><p>RAM: 768 MB</p>
</li>
<li><p>CPU: 2 core</p>
</li>
</ul>
<p>Ini memastikan semua VM berjalan ringan, cocok untuk lokal.</p>
<pre><code class="lang-bash">config.vm.box_check_update = <span class="hljs-literal">false</span>
</code></pre>
<p>Menonaktifkan pengecekan update box setiap kali <code>vagrant up</code>, agar lebih cepat.</p>
<pre><code class="lang-bash">config.vm.synced_folder <span class="hljs-string">"."</span>, <span class="hljs-string">"/vagrant"</span>, disabled: <span class="hljs-literal">true</span>
</code></pre>
<p>Menonaktifkan <strong>folder sinkronisasi default</strong> antara host (<code>.</code>) dan guest (<code>/vagrant</code>), biasanya untuk mencegah <strong>tulisan tak diinginkan ke host</strong>, atau menjaga isolasi.</p>
<pre><code class="lang-bash">config.vm.provision <span class="hljs-string">"shell"</span>, inline: <span class="hljs-string">"apt update &amp;&amp; apt install -y curl git gnupg net-tools"</span>
</code></pre>
<p>Script shell ini akan dijalankan <strong>di semua VM</strong>, menginstal alat bantu dasar:</p>
<ul>
<li><code>curl</code>, <code>git</code>, <code>gnupg</code>, dan <code>net-tools</code> untuk keperluan umum sistem</li>
</ul>
<pre><code class="lang-bash">nodejs_install_script = &lt;&lt;-SHELL
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Installing NodeJS v18"</span>
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
    <span class="hljs-built_in">export</span> NVM_DIR=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.nvm"</span>
    [ -s <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span> ] &amp;&amp; \. <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span>
    [ -s <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/bash_completion"</span> ] &amp;&amp; \. <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/bash_completion"</span>
    nvm install 18
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"NodeJS version: <span class="hljs-subst">$(node -v)</span>"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"NPM version: <span class="hljs-subst">$(npm -v)</span>"</span>
    sudo npm install -g pm2 npm
  SHELL
</code></pre>
<p>Script ini mendefinisikan <strong>instalasi Node.js versi 18</strong> menggunakan <strong>NVM (Node Version Manager)</strong>, dan akan digunakan dalam provisioning <strong>Frontend</strong> dan <strong>Backend VM</strong>.</p>
<p>Rincian tugas:</p>
<ol>
<li><p><strong>Install NVM</strong></p>
</li>
<li><p><strong>Install Node.js v18</strong></p>
</li>
<li><p><strong>Verifikasi versi node dan npm</strong></p>
</li>
<li><p><strong>Global install:</strong></p>
<ul>
<li><p><code>pm2</code>: Untuk menjalankan proses Node.js sebagai background service</p>
</li>
<li><p><code>yarn</code>: Sebagai alternatif <code>npm</code> yang populer dan cepat</p>
</li>
</ul>
</li>
</ol>
<div class="hn-embed-widget" id="jagohosting2-mdrdani"></div><p> </p>
<hr />
<p>Setelah itu file konfigurasi di bawah adalah lanjutan dari <code>Vagrantfile</code> diatas yang mendefinisikan <strong>VM untuk Load Balancer</strong> dengan peran sebagai <strong>NGINX load balancer</strong>.</p>
<pre><code class="lang-bash">.....
<span class="hljs-comment"># Define Load Balancer VM</span>
  config.vm.define <span class="hljs-string">"lb"</span> <span class="hljs-keyword">do</span> |lb|
    lb.vm.hostname = <span class="hljs-string">"lb"</span>
    lb.vm.network <span class="hljs-string">"private_network"</span>, ip: <span class="hljs-string">"192.168.56.5"</span>
    lb.vm.provision <span class="hljs-string">"shell"</span>, inline: &lt;&lt;-SHELL

    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Installing Nginx"</span>
    apt install -y nginx

    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Configuring Nginx as a Load Balancer"</span>
cat &gt; /etc/nginx/sites-available/default &lt;&lt; EOF
  server {
      listen 80;
      server_name _;

      <span class="hljs-comment"># Forward requests to /api to the backend</span>
      location ~* /api {
          proxy_pass http://192.168.56.3:3000;
      }

      <span class="hljs-comment"># Forward other requests to the frontend</span>
      location / {
          proxy_pass http://192.168.56.2;
      }
  }
EOF
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Restart NGINX to apply changes"</span>
    systemctl restart nginx
    SHELL
  end
.....
</code></pre>
<p>Menjadikan VM ini sebagai <strong>entry point</strong> untuk semua request pengguna. NGINX bertindak sebagai <strong>router</strong> yang mengarahkan request ke frontend atau backend sesuai path URL.</p>
<p>Singkatnya:<br /> <strong>Automatisasi setup NGINX</strong><br /> <strong>Routing berdasarkan path</strong><br /> <strong>Terintegrasi dalam arsitektur 3-tier yang dijalankan via Vagrant</strong></p>
<p>untuk menjalankan <strong>VM Load Balancer</strong> kita dapat menggunakan perintah</p>
<pre><code class="lang-bash">vagrant up lb
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747578806625/156bc048-5c3d-4cea-b263-25b0752251af.png" alt class="image--center mx-auto" /></p>
<p>jika sudah <strong>running</strong> kita dapat check menggunakan browser dengan mengakses IP Address <strong>VM Load Balancer</strong> tersebut. status nya masih <strong>502 Bad Gateway</strong> karena VM lain nya belum di aktifkan.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747578917683/856f553f-fd39-483a-aba3-08fb643443fb.png" alt class="image--center mx-auto" /></p>
<p>jika kita ingin mengakses <strong>VM Load Balancer</strong> dengan menggunakan <strong>SSH</strong> kita dapat menggunakan command</p>
<pre><code class="lang-bash">vagrant ssh lb
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747579115077/0925ec64-5fe1-417b-a752-8a0d7b4f9662.png" alt class="image--center mx-auto" /></p>
<hr />
<p>Selanjutnya kita konfigurasi <strong>VM Database</strong> masih lanjutan dari <code>Vagrantfile</code> . <strong>VM Database</strong> menggunakan <strong>PostgreSQL versi 16.</strong></p>
<pre><code class="lang-bash">......
config.vm.define <span class="hljs-string">"db"</span> <span class="hljs-keyword">do</span> |db|
    db.vm.hostname = <span class="hljs-string">"db"</span>
    db.vm.network <span class="hljs-string">"private_network"</span>, ip: <span class="hljs-string">"192.168.56.4"</span>
    db.vm.provision <span class="hljs-string">"shell"</span>, inline: &lt;&lt;-SHELL
      sudo apt install -y postgresql-common
      sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
      sudo apt -y install postgresql-16
      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Start PostgreSQL service"</span>
      systemctl start postgresql

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Enable PostgreSQL to start on boot"</span>
      systemctl <span class="hljs-built_in">enable</span> postgresql

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Set postgres user password to postgres, for demonstration purposes"</span>
      sudo -u postgres psql -c <span class="hljs-string">"ALTER ROLE postgres WITH password 'postgres'"</span>

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Create the conduit database owned by postgres"</span>
      sudo -u postgres psql -c <span class="hljs-string">"CREATE DATABASE conduit OWNER postgres;"</span>

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Configure PostgreSQL to listen on all interfaces"</span>
      sed -i <span class="hljs-string">"s/#listen_addresses = 'localhost'/listen_addresses = '*'/g"</span> /etc/postgresql/16/main/postgresql.conf

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Allow all connections in pg_hba.conf"</span>
      <span class="hljs-built_in">echo</span> <span class="hljs-string">"host    all             all             0.0.0.0/0               md5"</span> | sudo tee -a /etc/postgresql/16/main/pg_hba.conf

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Restart PostgreSQL to apply changes"</span>
      systemctl restart postgresql
    SHELL
  end
.....
</code></pre>
<p>untuk menjalankan <strong>VM Database</strong> kita dapat menggunakan perintah</p>
<pre><code class="lang-bash">vagrant up db
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747580179723/25cf7a7f-b5ee-4ef2-9b3f-8d2130839328.png" alt class="image--center mx-auto" /></p>
<p>untuk mengakses <strong>VM Database</strong> seperti biasa kita bisa gunakan command</p>
<pre><code class="lang-bash">vagrant ssh db
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747580368141/681e2acb-8711-4f52-ad04-3b6004941795.png" alt class="image--center mx-auto" /></p>
<hr />
<p>Selanjutnya kita konfigurasi <strong>VM Backend</strong> masih lanjutan dari <code>Vagrantfile</code> dengan IP <code>192.168.56.3</code> <strong>VM Backend</strong> ini bertugas menjalankan <strong>backend service node.js</strong> yang di ambil dari github <a target="_blank" href="https://github.com/gothinkster/node-express-realworld-example-app.git">https://github.com/gothinkster/node-express-realworld-example-app.git</a> <strong>.</strong></p>
<div class="hn-embed-widget" id="jetorbit-mdrdani"></div><p> </p>
<pre><code class="lang-bash">.....
config.vm.define <span class="hljs-string">"backend"</span> <span class="hljs-keyword">do</span> |backend|
    backend.vm.hostname = <span class="hljs-string">"backend"</span>
    backend.vm.network <span class="hljs-string">"private_network"</span>, ip: <span class="hljs-string">"192.168.56.3"</span>
    backend.vm.provision <span class="hljs-string">"shell"</span>, privileged: <span class="hljs-literal">false</span>, inline: nodejs_install_script
    backend.vm.provision <span class="hljs-string">"shell"</span>, privileged: <span class="hljs-literal">false</span>, inline: &lt;&lt;-SHELL
      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Begin installing Backend"</span>
      <span class="hljs-built_in">export</span> NVM_DIR=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.nvm"</span>
      [ -s <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span> ] &amp;&amp; \. <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span>
      <span class="hljs-built_in">export</span> DATABASE_URL=postgresql://postgres:postgres@192.168.56.4:5432/conduit
      <span class="hljs-built_in">export</span> JWT_SECRET=6b350c623d150e8a07d36e963e1416079a7a322e8c04952d674fe607914d7b01
      <span class="hljs-built_in">export</span> NODE_ENV=development
      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Cloning the backend repository"</span>
      git <span class="hljs-built_in">clone</span> https://github.com/gothinkster/node-express-realworld-example-app.git <span class="hljs-variable">$HOME</span>/backend

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Writing environment variables to .env"</span>
      cat &gt; <span class="hljs-variable">$HOME</span>/backend/.env &lt;&lt; EOF
DATABASE_URL=postgresql://postgres:postgres@192.168.56.4:5432/conduit
JWT_SECRET=6b350c623d150e8a07d36e963e1416079a7a322e8c04952d674fe607914d7b01
NODE_ENV=development
EOF

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Installing dependencies"</span>
      <span class="hljs-built_in">cd</span> <span class="hljs-variable">$HOME</span>/backend
      npm install

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Generating Prisma client, running migrations, and seeding the database"</span>
      npx prisma generate
      npx prisma migrate deploy &amp;&amp; npx prisma db seed

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Starting the backend server using pm2"</span>
      pm2 start npm --name <span class="hljs-string">"backend"</span> -- start
    SHELL
  end
.....
</code></pre>
<p>untuk menjalankan <strong>VM Backend</strong> kita dapat menggunakan perintah</p>
<pre><code class="lang-bash">vagrant up backend
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747581508970/875bcb8e-5df8-4aa5-81fa-30a18926c784.png" alt class="image--center mx-auto" /></p>
<p>untuk mengakses <strong>VM Backend</strong> seperti biasa kita bisa gunakan command</p>
<pre><code class="lang-bash">vagrant ssh backend
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747581646499/bb8ffee7-0782-4f45-8655-83c8a10bf311.png" alt class="image--center mx-auto" /></p>
<hr />
<p>Terakhir kita konfigurasi <strong>VM Frontend</strong> masih lanjutan dari <code>Vagrantfile</code> dengan IP <code>192.168.56.</code>2. <strong>VM Frontend</strong> ini bertugas menjalankan <strong>frontend service React.js</strong> yang di ambil dari github <a target="_blank" href="https://github.com/khaledosman/react-redux-realworld-example-app.githttps://github.com/khaledosman/react-redux-realworld-example-app.git">https://github.com/khaledosman/react-redux-realworld-example-app.git</a>.</p>
<pre><code class="lang-bash">......
config.vm.define <span class="hljs-string">"frontend"</span>, autostart: <span class="hljs-literal">false</span> <span class="hljs-keyword">do</span> |frontend|
    frontend.vm.hostname = <span class="hljs-string">"frontend"</span>
    frontend.vm.network <span class="hljs-string">"private_network"</span>, ip: <span class="hljs-string">"192.168.56.2"</span>
    <span class="hljs-comment"># Copy frontend directory to VM. </span>
    frontend.vm.provision <span class="hljs-string">"shell"</span>, inline: <span class="hljs-string">"apt install -y nginx"</span>
    frontend.vm.provision <span class="hljs-string">"shell"</span>, privileged: <span class="hljs-literal">false</span>, inline: nodejs_install_script
    frontend.vm.provision <span class="hljs-string">"shell"</span>, privileged: <span class="hljs-literal">false</span>, inline: &lt;&lt;-SHELL
      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Begin installing Frontend"</span>
      <span class="hljs-built_in">export</span> NVM_DIR=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.nvm"</span>
      [ -s <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span> ] &amp;&amp; \. <span class="hljs-string">"<span class="hljs-variable">$NVM_DIR</span>/nvm.sh"</span>

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Cloning the frontend repository"</span>
      git <span class="hljs-built_in">clone</span> https://github.com/khaledosman/react-redux-realworld-example-app.git <span class="hljs-variable">$HOME</span>/frontend

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Installing dependencies"</span>
      <span class="hljs-built_in">cd</span> <span class="hljs-variable">$HOME</span>/frontend/
      npm install

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Building the frontend"</span>
      REACT_APP_BACKEND_URL=<span class="hljs-string">"http://192.168.56.5/api"</span> NODE_ENV=production npm run build

      <span class="hljs-built_in">echo</span> <span class="hljs-string">"Copying the build to /var/www/html to serve it using Nginx"</span>
      sudo mv <span class="hljs-variable">$HOME</span>/frontend/build/* /var/www/html/ 
    SHELL
  end
end
</code></pre>
<p>untuk menjalankan <strong>VM Frontend</strong> kita dapat menggunakan perintah</p>
<pre><code class="lang-bash">vagrant up frontend
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747582535842/41a220ef-b906-495b-9523-0ddee04b79c3.png" alt class="image--center mx-auto" /></p>
<p>untuk mengakses <strong>VM Frontend</strong> seperti biasa kita bisa gunakan command</p>
<pre><code class="lang-bash">vagrant ssh frontend
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747582669255/4e528f6e-8c5a-4ae8-bec6-b00cd5a69f40.png" alt class="image--center mx-auto" /></p>
<p>setelah semua VM up maka kita akan check kembali ke browser dengan akses <a target="_blank" href="http://192.168.56.5/">http://192.168.56.5/</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747622413860/557e239b-55d6-4047-a49b-f5938637b1e4.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747622802658/6a0401ae-2681-44b2-ace0-c161c74f2e8e.gif" alt class="image--center mx-auto" /></p>
<p>Bagaimana kita tau bahwa <strong>three tier architecture</strong> kita berjalan?? cara nya gampang matikan salah satu VM apakah masih berjalan dengan baik, seharusnya tidak bisa di akses kita contoh kan di matikan <strong>VM Backend</strong> dengan perintah.</p>
<div class="hn-embed-widget" id="jetorbit2-mdrdani"></div><p> </p>
<pre><code class="lang-bash">vagrant halt backend
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747622954433/ed06e43d-b478-4947-bcd4-18f5292ffac0.png" alt class="image--center mx-auto" /></p>
<p>terlihat <strong>VM Frontend</strong> tidak dapat mengakses <strong>API</strong> <strong>VM Backend.</strong><br />untuk mematikan semua VM kita dapat dengan mudah menggunakan command</p>
<pre><code class="lang-bash">vagrant halt
</code></pre>
<hr />
<p>Dengan menerapkan arsitektur <strong>three-tier</strong> menggunakan <strong>NGINX sebagai load balancer</strong>, <strong>Node.js untuk backend</strong>, <strong>React sebagai frontend</strong>, dan <strong>PostgreSQL sebagai database</strong>, kita telah membangun lingkungan aplikasi web yang <strong>modular, terstruktur, dan mudah dikembangkan</strong>.</p>
<p>Penggunaan <strong>Vagrant</strong> dan <strong>VirtualBox</strong> memungkinkan kita untuk <strong>mengotomasi infrastruktur</strong> secara konsisten, sehingga setiap anggota tim dapat mereplikasi lingkungan pengembangan dengan satu perintah. Hal ini sangat penting dalam pengembangan perangkat lunak modern yang menuntut efisiensi, konsistensi, dan skalabilitas.</p>
<p>Github : <a target="_blank" href="https://github.com/mdrdani/three-tier">https://github.com/mdrdani/three-tier</a></p>
<p><a target="_blank" href="https://www.nihbuatjajan.com/mdrdani"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770683661317/8d35f269-fa57-4d1a-9f2c-91798fbc129f.png" alt class="image--center mx-auto" /></a></p>
]]></description><link>https://notes.mdrdani.my.id/running-three-tier-architecture-app-on-virtual-machine-with-vagrant</link><guid isPermaLink="true">https://notes.mdrdani.my.id/running-three-tier-architecture-app-on-virtual-machine-with-vagrant</guid><category><![CDATA[nginx]]></category><category><![CDATA[vagrant]]></category><category><![CDATA[React]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Challenge 2 : Configure Resource Limits Linux]]></title><description><![CDATA[<p>Pernahkah Anda bertanya-tanya bagaimana cara mencegah satu grup atau pengguna menghabiskan seluruh sumber daya server Linux Anda? Nah, di postingan kali ini, kita akan memecahkan masalah yang di berikan <a target="_blank" href="https://adinusa.id">adinusa.id</a> dengan mengkonfigurasi <em>resource limits.</em> Mengelola sumber daya sistem secara efisien adalah kunci untuk menjaga performa server Linux yang optimal.</p>
<h2 id="heading-intructions">Intructions</h2>
<ol>
<li><p>Configure resource limits for local group <strong>student</strong>, so that a maximum of <strong>100 processes</strong> can be start.</p>
</li>
<li><p>Configure resource limits for local user <strong>student</strong>, so that a maximum open file is <strong>3000</strong>, and configure current limit is <strong>2000.</strong></p>
</li>
<li><p>The above process limit should run when the student user logs in for the first time, ex: after rebooting</p>
</li>
</ol>
<hr />
<h3 id="heading-configure-resource-limits-for-local-group-student-so-that-a-maximum-of-100-processes-can-be-start">Configure resource limits for local group <strong>student</strong>, so that a maximum of <strong>100 processes</strong> can be start.</h3>
<p>Untuk konfigurasi resource limit group student langkah pertama kita check terlebih dahulu apakah group student sudah ada menggunakan command linux</p>
<pre><code class="lang-bash">getent group student
</code></pre>
<p>Jika sudah ada group <strong>student,</strong> maka selanjutnya edit file <strong>/etc/security/limits.conf</strong> dan tambahkan baris ini di dalamnya</p>
<pre><code class="lang-bash"><span class="hljs-comment">#@faculty        hard    nproc           50</span>
<span class="hljs-comment">#ftp             hard    nproc           0</span>
<span class="hljs-comment">#ftp             -       chroot          /ftp</span>
<span class="hljs-comment">#@student        -       maxlogins       4</span>

@student        hard    nproc           100
</code></pre>
<p>Keterangan:</p>
<ul>
<li><p><code>@student</code>  Menunjukkan bahwa ini berlaku untuk grup <code>student</code>.</p>
</li>
<li><p><code>hard nproc 100</code>  Batas maksimal proses yang bisa dijalankan</p>
</li>
</ul>
<hr />
<h3 id="heading-configure-resource-limits-for-local-user-student-so-that-a-maximum-open-file-is-3000-and-configure-current-limit-is-2000">Configure resource limits for local user <strong>student</strong>, so that a maximum open file is <strong>3000</strong>, and configure current limit is <strong>2000.</strong></h3>
<p>jika tadi kita konfigurasi untuk group nya maka selanjutnya kita konfigurasi untuk user nya. Langkah Pertama kita check apakah ada user <strong>student</strong> dan adakah user di dalam group <strong>student</strong>.<strong>.</strong></p>
<pre><code class="lang-bash">id student
</code></pre>
<pre><code class="lang-bash">uid=1000(student) gid=1000(student) groups=1000(student),100(users),117(admin)
</code></pre>
<p>jika tidak ada di dalam group student, kita bisa gunakan</p>
<pre><code class="lang-bash">sudo usermod -aG student student
</code></pre>
<p>selanjutnya untuk melimit akun student ini kita edit file <strong>/etc/security/limits.conf</strong> kembali dengan menambahkan baris berikut</p>
<pre><code class="lang-bash"><span class="hljs-comment">#ftp             hard    nproc           0</span>
<span class="hljs-comment">#ftp             -       chroot          /ftp</span>
<span class="hljs-comment">#@student        -       maxlogins       4</span>

@student        hard    nproc           100

student         soft    nofile          2000
student         hard    nofile          3000
</code></pre>
<p>Keterangan :</p>
<ul>
<li><p><code>soft nofile 2000</code>  Batas awal (soft limit) untuk file terbuka.</p>
</li>
<li><p><code>hard nofile 3000</code>  Batas maksimal (hard limit) untuk file terbuka.</p>
</li>
</ul>
<hr />
<h3 id="heading-the-above-process-limit-should-run-when-the-student-user-logs-in-for-the-first-time-ex-after-rebooting">The above process limit should run when the student user logs in for the first time, ex: after rebooting</h3>
<p>untuk memproses limit ketika user login pertama kali maka kita kita perlu mengedit file <strong>/etc/pam.d/common-session</strong></p>
<pre><code class="lang-bash">session required pam_limits.so
</code></pre>
<pre><code class="lang-bash"><span class="hljs-comment"># See "man pam_umask".</span>
session optional                        pam_umask.so
<span class="hljs-comment"># and here are more per-package modules (the "Additional" block)</span>
session required        pam_unix.so
session optional        pam_systemd.so
session required        pam_limits.so
<span class="hljs-comment"># end of pam-auth-update config</span>
</code></pre>
<p>dan edit juga file <strong>/etc/pam.d/common-session-noninteractive</strong></p>
<pre><code class="lang-bash"><span class="hljs-comment"># See "man pam_umask".</span>
session optional                        pam_umask.so
<span class="hljs-comment"># and here are more per-package modules (the "Additional" block)</span>
session required        pam_unix.so
session required        pam_limits.so
<span class="hljs-comment"># end of pam-auth-update config</span>
</code></pre>
<p>Ini memastikan bahwa batasan dari <code>/etc/security/limits.conf</code> diterapkan saat login.</p>
<hr />
<p>Jika ingin mengatur <strong>nice value</strong> untuk pengguna student, dapat menambahkan baris</p>
<pre><code class="lang-bash">student hard priority 10
student soft priority 10
</code></pre>
<h3 id="heading-apa-itu-nice-value"><strong>Apa Itu Nice Value?</strong></h3>
<p><code>nice</code> value adalah tingkat prioritas suatu proses di sistem Linux. Nilai ini menentukan seberapa banyak CPU time yang diberikan ke proses tertentu dibandingkan dengan proses lain.</p>
<ul>
<li><p>Nilai <strong>rendah (misal -20)</strong> berarti proses memiliki <strong>prioritas tinggi</strong> (lebih diutamakan oleh CPU).</p>
</li>
<li><p>Nilai <strong>tinggi (misal 19)</strong> berarti proses memiliki <strong>prioritas rendah</strong> (kurang diutamakan oleh CPU).</p>
</li>
</ul>
<p>Secara default, <strong>nice value</strong> untuk proses yang berjalan adalah <strong>0</strong>.</p>
<pre><code class="lang-bash"><span class="hljs-comment">#@student        -       maxlogins       4</span>

@student        hard    nproc           100

student         soft    nofile          2000
student         hard    nofile          3000

student         hard    priority        10
student         soft    priority        10

<span class="hljs-comment"># End of file</span>
</code></pre>
<h3 id="heading-mengapa-mengatur-nice-value"><strong>Mengapa Mengatur Nice Value?</strong></h3>
<p>Mengatur <strong>nice value</strong> untuk pengguna bertujuan untuk:</p>
<ul>
<li><p><strong>Mengontrol alokasi CPU</strong> untuk proses yang dibuat oleh user.</p>
</li>
<li><p><strong>Mencegah pengguna</strong> mengambil terlalu banyak CPU, sehingga proses penting lain tetap berjalan optimal.</p>
</li>
<li><p><strong>Membantu manajemen sumber daya dalam sistem multi-user</strong>.</p>
</li>
</ul>
<p><strong>Cek Nice Value Default</strong><br /><strong>Jalankan perintah berikut sebagai</strong> <code>student</code><strong>:</strong></p>
<pre><code class="lang-bash">nice
</code></pre>
<p>Jika konfigurasi benar, outputnya harus:</p>
<pre><code class="lang-bash">10
</code></pre>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/challenge-2-configure-resource-limits-linux</link><guid isPermaLink="true">https://notes.mdrdani.my.id/challenge-2-configure-resource-limits-linux</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Challenge 1 : How to list and Count Linux Files]]></title><description><![CDATA[<p>Di awal pelatihan Linux System Administrator di <a href="http://adinusa.id">adinusa.id</a>, saya langsung dihadapkan pada tantangan menarik: menghitung jumlah file di direktori <code>lab2</code>. Proses ini tidak hanya menguji kemampuan teknis, tetapi juga melatih ketelitian dan pemahaman saya terhadap struktur direktori Linux. kurang lebih intruksi yang di berikan seperti ini :</p>
<p><strong>Instructions</strong></p>
<ol>
<li><p>Enter the lab2 directory, see the contents in the directory.</p>
</li>
<li><p>Count the files by name and write to <code>/home/student/lab2/lab2.txt</code><br /><strong>example</strong>: client = 20</p>
</li>
</ol>
<p>Hasilnya, saya berhasil membuat file <code>lab2.txt</code> yang berisi informasi jumlah file seperti yang diminta dengan menggunakan command linux :</p>
<pre><code class="language-bash">echo "client = $(ls lab2 | grep '^client' | wc -l)" &gt; lab2.txt &amp;&amp; \
echo "manager = $(ls lab2 | grep '^manager' | wc -l)" &gt;&gt; lab2.txt &amp;&amp; \
echo "ops = $(ls lab2 | grep '^ops' | wc -l)" &gt;&gt; lab2.txt
</code></pre>
<p><strong>Penjelasan Perintah Secara Detail</strong></p>
<ol>
<li><p><code>$(...)</code></p>
<ul>
<li><p>Ini adalah <strong>command substitution</strong>, yang menjalankan perintah di dalamnya dan menggantinya dengan hasilnya.</p>
</li>
<li><p>Contoh: Jika ada 4 file <code>client</code>, maka <code>$(ls lab2 | grep '^client' | wc -l)</code> akan menjadi <code>4</code>.</p>
</li>
</ul>
</li>
<li><p><code>ls lab2</code></p>
<ul>
<li>Menampilkan daftar semua file dalam folder <code>lab2</code>.</li>
</ul>
</li>
<li><p><code>grep '^client'</code></p>
<ul>
<li><p>Menyaring hanya file yang namanya diawali dengan <code>"client"</code>.</p>
</li>
<li><p><code>^</code> adalah simbol regex yang berarti "diawali dengan".</p>
</li>
<li><p>Contoh: Jika ada <code>client1</code>, <code>client2</code>, <code>client3</code>, ini akan memilih ketiga file tersebut.</p>
</li>
</ul>
</li>
<li><p><code>wc -l</code></p>
<ul>
<li><p>Menghitung jumlah baris dari output <code>grep</code>.</p>
</li>
<li><p>Karena setiap file ditampilkan sebagai satu baris, ini berarti jumlah file yang cocok.</p>
</li>
</ul>
</li>
<li><p><code>echo "client = ..." &gt; lab2.txt</code></p>
<ul>
<li>Menulis hasil ke dalam file <code>lab2.txt</code>, mengganti isi file jika sudah ada.</li>
</ul>
</li>
<li><p><code>&gt;&gt; lab2.txt</code></p>
<ul>
<li>Menambahkan hasil berikutnya ke file <code>lab2.txt</code> tanpa menghapus isi sebelumnya.</li>
</ul>
</li>
<li><p><code>&amp;&amp;</code></p>
<ul>
<li><p>Memastikan bahwa perintah berikutnya hanya dijalankan jika perintah sebelumnya sukses.</p>
</li>
<li><p>Jika <code>ls lab2</code> gagal (misalnya folder tidak ada), maka perintah selanjutnya tidak akan</p>
</li>
</ul>
</li>
</ol>
<p>Nanti expetasi yang akan terlihat di file <strong>lab2.txt</strong> akan seperti ini</p>
<pre><code class="language-plaintext">client = 4
manager = 3
ops = 3
</code></pre>
]]></description><link>https://notes.mdrdani.my.id/challenge-1-how-to-list-and-count-linux-files</link><guid isPermaLink="true">https://notes.mdrdani.my.id/challenge-1-how-to-list-and-count-linux-files</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Linux Hands-on 4 (Networking Tools)]]></title><description><![CDATA[<p>Di dunia Linux yang serba bisa ini, jaringan adalah urat nadi yang menghubungkan semuanya. Baik kamu seorang <em>sysadmin</em>, <em>developer</em>, atau sekadar pengguna yang penasaran, pemahaman tentang <em>networking tools</em> adalah kunci untuk menjelajahi dan mengendalikan jaringanmu.</p>
<h2 id="heading-task-41-use-ping-to-check-connectivity-to-googlecom">Task 4.1: Use ping to check connectivity to google.com</h2>
<pre><code class="lang-bash">ping google.com &gt; ping_output.txt
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739174040307/62feed14-9fc0-41ed-9dab-c3513824dd83.png" alt class="image--center mx-auto" /></p>
<p>Komando ini adalah kombinasi dari dua perintah Linux yang digunakan untuk menguji koneksi jaringan ke <a target="_blank" href="http://google.com"><code>google.com</code></a> dan menyimpan hasilnya ke dalam file bernama <code>ping_output.txt</code>.</p>
<ul>
<li><p><code>ping</code> <a target="_blank" href="http://google.com"><code>google.com</code></a>: Perintah <code>ping</code> digunakan untuk menguji konektivitas jaringan dengan mengirimkan paket data ke alamat IP tertentu (dalam hal ini, alamat IP dari <a target="_blank" href="http://google.com"><code>google.com</code></a>) dan menunggu balasan. Tujuannya adalah untuk mengetahui apakah komputer kita dapat terhubung ke alamat IP tersebut dan berapa lama waktu yang dibutuhkan untuk menerima balasan.</p>
</li>
<li><p><code>&gt;</code>: Tanda ini adalah <em>redirection operator</em> yang digunakan untuk mengalihkan output dari suatu perintah ke file. Dalam hal ini, output dari perintah <code>ping</code> <a target="_blank" href="http://google.com"><code>google.com</code></a> akan dialihkan ke file <code>ping_output.txt</code></p>
</li>
</ul>
<p><strong>Kenapa pakai perintah ini?</strong></p>
<p>Perintah ini sangat berguna untuk:</p>
<ul>
<li><p><strong>Memeriksa Konektivitas</strong>: Kita bisa tahu apakah komputer kita terhubung ke internet atau tidak.</p>
</li>
<li><p><strong>Menguji Kecepatan Jaringan</strong>: Kita bisa melihat seberapa cepat koneksi internet kita berdasarkan waktu tempuh bolak-balik.</p>
</li>
<li><p><strong>Mencari Masalah Jaringan</strong>: Kalau ada masalah koneksi, kita bisa lihat dari output <code>ping</code>, misalnya apakah ada paket data yang hilang atau waktu tempuh bolak-balik yang terlalu lama.</p>
</li>
</ul>
<h2 id="heading-task-42-use-ping-with-10-packets">Task 4.2: Use ping with 10 packets</h2>
<pre><code class="lang-bash">ping -c 10 google.com &gt; ping_10_packets.txt
</code></pre>
<p><code>-c 10</code>: Opsi ini adalah yang bikin beda. <code>-c</code> adalah singkatan dari "count". Angka 10 di belakangnya berarti kita hanya ingin mengirimkan 10 paket ping saja. Jadi, ping tidak akan berjalan terus-menerus, tapi hanya 10 kali saja.</p>
<h2 id="heading-task-43-use-ping-with-a-packet-size-of-1000-bytes">Task 4.3: Use ping with a packet size of 1000 Bytes</h2>
<pre><code class="lang-bash">ping -s 1000 google.com &gt; ping_large_packet.txt
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739174409187/77922a6d-d882-4f4c-a680-fa152768c8e9.png" alt class="image--center mx-auto" /></p>
<p><code>-s 1000</code>: Opsi ini adalah yang bikin beda. <code>-s</code> adalah singkatan dari "size". Angka 1000 di belakangnya berarti kita ingin mengirim paket ping dengan ukuran 1000 byte. Ukuran default paket ping biasanya lebih kecil dari ini.</p>
<p><strong>Kenapa pakai perintah ini?</strong></p>
<ul>
<li><p><strong>Tes Ukuran Paket:</strong> Berguna untuk menguji kemampuan jaringan dalam menangani paket data berukuran besar.</p>
</li>
<li><p><strong>Masalah MTU:</strong> Bisa membantu mendeteksi masalah terkait MTU pada jaringan. Jika paket terlalu besar, router mungkin perlu memecahnya, yang bisa mempengaruhi performa.</p>
</li>
</ul>
<h2 id="heading-task-44-use-telnet-to-test-port-80-on-discordcom">Task 4.4: Use telnet to test port 80 on discord.com</h2>
<pre><code class="lang-bash">sudo apt install telnet
telnet discord.com 80
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739174777931/57df0189-27d0-416f-9ea3-b20e38703b28.png" alt class="image--center mx-auto" /></p>
<p><code>telnet</code>: Ini adalah nama perintahnya, yaitu program <code>telnet</code> itu sendiri. <code>telnet</code> adalah <em>tool</em> kuno, tapi masih berguna untuk beberapa tujuan.</p>
<p>Perlu diingat bahwa <code>telnet</code> adalah <em>tool</em> yang sangat sederhana. Ia hanya membuat koneksi TCP, tanpa memahami protokol yang lebih tinggi seperti HTTP.</p>
<h2 id="heading-task-45-use-telnet-to-connect-to-the-smtp-server-on-port-25">Task 4.5: Use telnet to connect to the SMTP server on port 25</h2>
<pre><code class="lang-bash">telnet smtp.gmail.com 25
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739174935241/06090b0d-9659-463a-b0ba-9551e8845081.png" alt class="image--center mx-auto" /></p>
<p>Komando ini mencoba membuat koneksi ke server SMTP (Simple Mail Transfer Protocol) Gmail di <a target="_blank" href="http://smtp.gmail.com"><code>smtp.gmail.com</code></a> melalui <em>port</em> 25 menggunakan <em>tool</em> <code>telnet</code>.</p>
<h2 id="heading-task-46-use-curl-to-fetch-content-of-httpsdiscordcom">Task 4.6: Use curl to fetch content of https://discord.com</h2>
<pre><code class="lang-bash">curl https://discord.com -o discord.html
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739175045138/fdb45a2d-6190-4dbe-b472-10d2bf394795.png" alt class="image--center mx-auto" /></p>
<p><code>curl</code>: Ini adalah nama perintahnya, yaitu program <code>curl</code> itu sendiri. <code>curl</code> adalah <em>tool</em> serbaguna yang digunakan untuk mentransfer data dari atau ke <em>server</em> menggunakan berbagai protokol, termasuk HTTP</p>
<p>Perintah ini sangat berguna untuk:</p>
<ul>
<li><p><strong>Mengunduh Kode Sumber</strong>: Kita bisa melihat bagaimana sebuah halaman web dibuat dengan mengunduh kode sumbernya.</p>
</li>
<li><p><strong><em>Web Scraping</em></strong>: Kita bisa mengambil data tertentu dari sebuah halaman web secara otomatis.</p>
</li>
<li><p><strong><em>Debugging</em></strong>: Kita bisa melihat <em>error</em> atau masalah yang mungkin terjadi saat <em>load</em> halaman web.</p>
</li>
</ul>
<h2 id="heading-task-47-use-curl-with-a-custom-user-agent">Task 4.7: Use curl with a custom User-Agent</h2>
<pre><code class="lang-bash">curl -H <span class="hljs-string">"User-Agent: MyBrowser"</span> https://google.com
<span class="hljs-comment">#or</span>
curl -L -H <span class="hljs-string">"User-Agent: MyBrowser"</span> https://google.com
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739175283342/694600a3-77e4-4e1a-aa10-6e4def4aa535.png" alt class="image--center mx-auto" /></p>
<p><code>-H "User-Agent: MyBrowser"</code>: Opsi <code>-H</code> digunakan untuk menambahkan atau memodifikasi <em>header</em> HTTP yang dikirim. Dalam hal ini, kita mengubah <em>header</em> <code>User-Agent</code> menjadi "MyBrowser". <em>User-Agent</em> adalah <em>string</em> yang diidentifikasi <em>browser</em> atau aplikasi yang digunakan untuk mengakses <em>website</em>. Dengan mengubahnya, kita "berpura-pura" menjadi <em>browser</em> lain</p>
<p><code>-L</code>: Opsi ini adalah yang membedakan kedua perintah ini. <code>-L</code> adalah singkatan dari "location". Opsi ini akan membuat <code>curl</code> mengikuti <em>redirect</em> HTTP jika <em>server</em> mengembalikannya</p>
<h2 id="heading-task-48-use-netstat-to-list-active-tcp-connections">Task 4.8: Use netstat to list active TCP connections</h2>
<pre><code class="lang-bash">sudo apt install net-tools
netstat -at
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739175519575/8619982e-dcbd-4680-beab-12dbd2a517de.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>netstat</code>: Ini adalah nama perintahnya, yaitu program <code>netstat</code> itu sendiri. <code>netstat</code> adalah <em>tool</em> klasik untuk melihat statistik jaringan.</p>
</li>
<li><p><code>-a</code>: Opsi ini berarti "all", yang akan menampilkan semua koneksi yang sedang aktif, baik yang berstatus <em>listening</em> (menunggu koneksi masuk) maupun <em>established</em> (sudah terhubung).</p>
</li>
<li><p><code>-t</code>: Opsi ini berarti "TCP", yang akan menampilkan koneksi yang menggunakan protokol TCP. TCP adalah protokol yang umum digunakan untuk komunikasi data di internet.</p>
</li>
</ul>
<p><strong>Kenapa pakai perintah ini?</strong></p>
<p>Perintah <code>netstat -at</code> sangat berguna untuk:</p>
<ul>
<li><p><strong>Memantau Koneksi</strong>: Kita bisa melihat koneksi apa saja yang sedang aktif di komputer kita, dan ke mana saja komputer kita terhubung.</p>
</li>
<li><p><strong>Mencari Masalah</strong>: Kalau ada masalah dengan jaringan, kita bisa cek dengan perintah ini, mungkin ada koneksi yang mencurigakan atau koneksi yang bermasalah.</p>
</li>
<li><p><strong>Keamanan</strong>: Kita bisa melihat apakah ada koneksi yang tidak dikenal atau mencurigakan, yang mungkin menandakan adanya aktivitas yang tidak diinginkan.</p>
</li>
</ul>
<h2 id="heading-task-49-use-netstat-to-display-all-ports-that-are-in-the-listening-state-on-your-system">Task 4.9: Use netstat to display all ports that are in the listening state on your system.</h2>
<pre><code class="lang-bash">netstat -l
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739175616380/6726d4f0-e183-42a9-94f7-cc7b285601c2.png" alt class="image--center mx-auto" /></p>
<p><code>-l</code>: Opsi ini berarti "listening", yang akan menampilkan <em>socket</em> yang sedang dalam status <em>listening</em>. Artinya, <em>socket</em> ini sedang menunggu koneksi dari komputer lain.</p>
<p><strong>Kenapa pakai perintah ini?</strong></p>
<p>Perintah <code>netstat -l</code> sangat berguna untuk:</p>
<ul>
<li><p><strong>Mencari Layanan yang Aktif</strong>: Kita bisa melihat layanan atau program apa saja yang sedang berjalan di komputer kita dan menunggu koneksi masuk.</p>
</li>
<li><p><strong>Keamanan</strong>: Kita bisa melihat apakah ada <em>socket</em> yang <em>listening</em> yang tidak kita kenal atau mencurigakan, yang mungkin menandakan adanya aktivitas yang tidak diinginkan.</p>
</li>
<li><p><strong><em>Troubleshooting</em></strong>: Kalau ada masalah dengan layanan jaringan, kita bisa cek dengan perintah ini, mungkin ada <em>socket</em> yang tidak seharusnya <em>listening</em> atau ada <em>socket</em> yang bermasalah.</p>
</li>
</ul>
<h2 id="heading-task-410-use-iperf-in-client-mode-to-test-the-network-bandwidth-to-a-remote-server-iperf-servergooglecomhttpiperf-servergooglecom"><strong>Task 4.10: Use iperf in client mode to test the network bandwidth to a remote server (</strong><a target="_blank" href="http://iperf-server.google.com"><strong>iperf-server.google.com</strong></a><strong>).</strong></h2>
<pre><code class="lang-bash">sudo apt install iperf
iperf -c iperf.he.net
</code></pre>
<ul>
<li><p><code>iperf</code>: Ini adalah nama perintahnya, yaitu program <code>iperf</code> itu sendiri. <code>iperf</code> adalah <em>tool</em> yang sangat berguna untuk mengukur <em>bandwidth</em> atau kecepatan transfer data dalam jaringan.</p>
</li>
<li><p><code>-c</code>: Opsi ini adalah singkatan dari "client". Artinya, kita menjalankan <code>iperf</code> dalam mode <em>client</em>, yaitu sebagai pengirim atau penerima data.</p>
</li>
<li><p><a target="_blank" href="http://iperf.he.net"><code>iperf.he.net</code></a>: Ini adalah alamat <em>domain</em> atau <em>hostname</em> dari server <code>iperf</code>. Dalam hal ini, kita menggunakan server <code>iperf</code> yang disediakan oleh Hurricane Electric (HE), sebuah penyedia layanan internet.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739175870835/b9e39959-4c7a-4786-9623-d12b503eb528.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-task-411-use-iperf-in-server-mode-to-listen-for-incoming-connections-on-a-remote-machine-iperf-serverdiscordcomhttpiperf-serverdiscordcom"><strong>Task 4.11: Use iperf in server mode to listen for incoming connections on a remote machine (</strong><a target="_blank" href="http://iperf-server.discord.com"><strong>iperf-server.discord.com</strong></a><strong>).</strong></h2>
<pre><code class="lang-bash">sudo apt install iperf3
iperf3 -s -p 8791
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739176844177/64d31e24-4d52-4349-9afd-db2919d586ac.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>-s</code>: Opsi ini adalah singkatan dari "server". Artinya, kita menjalankan <code>iperf3</code> dalam mode <em>server</em>, yaitu sebagai penerima data. Komputer yang menjalankan perintah ini akan menunggu koneksi dari <em>client</em>.</p>
</li>
<li><p><code>-p 8791</code>: Opsi ini digunakan untuk menentukan <em>port</em> yang akan digunakan oleh <em>server</em>. Secara <em>default</em>, <code>iperf3</code> menggunakan <em>port</em> 5201, tapi di sini kita mengubahnya menjadi 8791. Ini berguna jika <em>port</em> <em>default</em> sedang digunakan oleh aplikasi lain, atau jika kita ingin menjalankan beberapa <em>instance iperf3</em> secara bersamaan.</p>
</li>
</ul>
<p><strong>and client</strong></p>
<pre><code class="lang-bash">iperf3 -c 192.168.1.14 -p 8791
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739176878877/df2ee921-e7c7-408f-b9ab-ffed2d859632.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>-c</code>: Opsi ini adalah singkatan dari "client". Artinya, kita menjalankan <code>iperf3</code> dalam mode <em>client</em>, yaitu sebagai pengirim data. Komputer yang menjalankan perintah ini akan terhubung ke <em>server</em>.</p>
</li>
<li><p><code>192.168.1.14</code>: Ini adalah alamat IP dari komputer yang menjalankan <em>server iperf3</em>. <em>Client</em> perlu mengetahui alamat IP <em>server</em> agar bisa terhubung.</p>
</li>
<li><p><code>-p 8791</code>: Opsi ini harus sama dengan <em>port</em> yang digunakan oleh <em>server</em>. Karena <em>server</em> menggunakan <em>port</em> 8791, maka <em>client</em> juga harus menggunakan <em>port</em> 8791.</p>
</li>
</ul>
<h2 id="heading-task-412-use-nc-netcat-to-check-if-port-443-is-open-on-discordcomhttpdiscordcom"><strong>Task 4.12: Use nc (Netcat) to check if port 443 is open on</strong> <a target="_blank" href="http://discord.com"><strong>discord.com</strong></a><strong>.</strong></h2>
<pre><code class="lang-bash">nc -zv discord.com 443
</code></pre>
<ul>
<li><p><code>nc</code>: Ini adalah nama perintahnya, yaitu program <code>nc</code> atau netcat itu sendiri. <code>nc</code> adalah <em>tool</em> serbaguna yang bisa digunakan untuk berbagai macam tugas terkait jaringan, termasuk membuat koneksi TCP atau UDP, <em>listening</em> di <em>port</em> tertentu, dan lain-lain.</p>
</li>
<li><p><code>-z</code>: Opsi ini berarti "zero-I/O mode". Artinya, <code>nc</code> tidak akan mengirim atau menerima data apa pun. Kita hanya ingin tahu apakah koneksi bisa dibuat atau tidak.</p>
</li>
<li><p><code>-v</code>: Opsi ini berarti "verbose". Artinya, <code>nc</code> akan menampilkan informasi yang lebih detail tentang proses koneksi.</p>
</li>
<li><p><a target="_blank" href="http://discord.com"><code>discord.com</code></a>: Ini adalah alamat <em>domain</em> atau <em>hostname</em> yang ingin kita tuju. Dalam hal ini, kita mau coba koneksi ke Discord.</p>
</li>
<li><p><code>443</code>: Ini adalah nomor <em>port</em>. <em>Port</em> 443 biasanya digunakan untuk HTTPS, yaitu protokol yang aman untuk <em>browsing</em> web.</p>
</li>
</ul>
<h2 id="heading-task-413-use-nc-to-send-the-message-hello-server-to-googlecomhttpgooglecom-on-port-12345"><strong>Task 4.13: Use nc to send the message Hello, Server! to</strong> <a target="_blank" href="http://google.com"><strong>google.com</strong></a> <strong>on port 12345.</strong></h2>
<pre><code class="lang-bash"><span class="hljs-comment">#listening</span>
nc -l 12345
<span class="hljs-comment">#send message</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Hello, Server!"</span> | nc localhost 12345
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177163497/c5118e14-52c1-4781-aa11-638eeb99a129.png" alt class="image--center mx-auto" /></p>
<p>Kedua perintah ini menunjukkan cara menggunakan <code>nc</code> (netcat) untuk komunikasi sederhana antar dua proses di Linux, mirip <em>client-server</em>. Perintah pertama menjalankan <code>nc</code> sebagai <em>server</em> yang "mendengarkan" di <em>port</em> 12345, dan perintah kedua menjalankan <code>nc</code> sebagai <em>client</em> yang mengirim pesan "Hello, Server!" ke <em>server</em> tersebut.</p>
<h2 id="heading-task-414-use-nc-to-start-a-simple-http-server-on-port-8080-and-serve-a-static-html-file"><strong>Task 4.14: Use nc to start a simple HTTP server on port 8080 and serve a static HTML file.</strong></h2>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"&lt;html&gt;&lt;body&gt;&lt;h1&gt;Hi, mdrdani&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;"</span> &gt; index.html
</code></pre>
<p><code>echo "&lt;html&gt;&lt;body&gt;&lt;h1&gt;Hi, mdrdani&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;" &gt; index.html</code>: Perintah ini membuat file <code>index.html</code> dan menuliskan kode HTML sederhana di dalamnya. Tanda <code>&gt;</code> mengalihkan <em>output</em> dari <code>echo</code> ke file <code>index.html</code>.</p>
<pre><code class="lang-bash"><span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>; <span class="hljs-keyword">do</span> (<span class="hljs-built_in">echo</span> -e <span class="hljs-string">"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"</span>; cat index.html) | nc -l 8791; <span class="hljs-keyword">done</span>
</code></pre>
<ul>
<li><p><code>while true</code>: Ini adalah <em>loop</em> tak terbatas. Artinya, <em>web server</em> ini akan terus berjalan sampai kamu hentikan secara manual (biasanya dengan <code>Ctrl+C</code>).</p>
</li>
<li><p><code>do</code>: Ini menandai awal dari blok perintah yang akan diulang dalam <em>loop</em>.</p>
</li>
<li><p><code>(echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"; cat index.html)</code>: Bagian ini membuat <em>response</em> HTTP yang akan dikirim ke <em>client</em>.</p>
<ul>
<li><p><code>echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"</code>: Perintah <code>echo</code> dengan opsi <code>-e</code> digunakan untuk menampilkan teks dengan interpretasi <em>escape sequence</em>. Ini membuat <em>header</em> HTTP. <code>HTTP/1.1 200 OK</code> menandakan <em>response</em> sukses. <code>Content-Type: text/html</code> memberitahu <em>browser</em> bahwa kontennya adalah HTML. <code>\r\n</code> adalah <em>carriage return</em> dan <em>line feed</em>, yang menandai akhir baris dalam <em>header</em> HTTP. Dua baris kosong setelah <em>Content-Type</em> menandai akhir <em>header</em>.</p>
</li>
<li><p><code>;</code>: Tanda titik koma memisahkan dua perintah dalam satu baris.</p>
</li>
<li><p><code>cat index.html</code>: Perintah <code>cat</code> digunakan untuk membaca isi file <code>index.html</code>.</p>
</li>
</ul>
</li>
<li><p><code>|</code>: Tanda pipa ini "menyalurkan" <em>output</em> dari perintah di dalam kurung ke perintah selanjutnya.</p>
</li>
<li><p><code>nc -l 8791</code>: Perintah <code>nc</code> (netcat) dijalankan dalam mode <em>listening</em> (<code>-l</code>) pada <em>port</em> 8791. Ini akan menerima koneksi dari <em>client</em> (misalnya, <em>browser</em>).</p>
</li>
<li><p><code>done</code>: Ini menandai akhir dari blok perintah yang diulang dalam <em>loop</em>.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177497780/b96369a8-0766-45fa-8470-1f6fd24158fc.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-task-415-manually-query-for-the-domain-discordcomhttpdiscordcom"><strong>Task 4.15: Manually query for the domain</strong> <a target="_blank" href="http://discord.com"><strong>discord.com</strong></a><strong>.</strong></h2>
<pre><code class="lang-bash">sudo apt install bind9-dnsutils
dig @8.8.8.8 discord.com
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177727895/0946ae70-b173-4688-8aa2-f06417bba36b.png" alt class="image--center mx-auto" /></p>
<p><code>dig</code> (domain information groper) untuk mencari informasi DNS (Domain Name System) tentang <a target="_blank" href="http://discord.com"><code>discord.com</code></a> dengan bantuan <em>DNS server</em> publik Google di <code>8.8.8.8</code>.</p>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/linux-hands-on-4-networking-tools</link><guid isPermaLink="true">https://notes.mdrdani.my.id/linux-hands-on-4-networking-tools</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Linux Hands-on 3 (Process Management)]]></title><description><![CDATA[<p>Sebagai pengguna Linux, Anda mungkin sudah familiar dengan perintah-perintah seperti <code>ps</code>, <code>top</code>, atau <code>kill</code>. Namun, tahukah Anda bagaimana cara kerja perintah-perintah tersebut dan bagaimana memanfaatkannya secara maksimal? Mari kita telusuri lebih dalam tentang <em>Linux Process Management</em></p>
<h2 id="heading-task-31-start-a-long-running-process">Task 3.1: Start a Long-Running Process</h2>
<pre><code class="lang-bash">sleep 500 &amp;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739149764046/62ca8dee-8911-41f0-ae49-917daf006f9f.png" alt class="image--center mx-auto" /></p>
<p>Jadi, gini, di dunia Linux, kadang kita perlu "menunda" sesuatu. Nah, perintah <code>sleep</code> ini gunanya buat itu, semacam ngasih "jeda" sementara. Angka 500 di sini artinya kita mau nunda selama 500 detik.</p>
<p>Terus, tanda <code>&amp;</code> di belakangnya itu apa? Nah, ini yang bikin seru. Tanda ini artinya kita mau perintah <code>sleep</code> ini jalan di "latar belakang" atau <em>background</em>. Jadi, sementara dia lagi "tidur" selama 500 detik, kita masih bisa tetep ngapa-ngapain di terminal, nggak perlu nungguin dia selesai.</p>
<h2 id="heading-task-32-simulate-cpu-and-memory-usage">Task 3.2: Simulate CPU and memory usage</h2>
<pre><code class="lang-bash">stress --cpu 1 --vm 1 --vm-bytes 512M --timeout 30s
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739154729632/fa91c25d-6aa2-44fa-98c5-26213f02da26.png" alt class="image--center mx-auto" /></p>
<p>Komando ini adalah cara buat "menyiksa" komputer Linux kita, alias memberikan tekanan atau <em>stress test</em> biar kita tahu seberapa kuat dia. <code>stress</code> adalah alat yang populer untuk melakukan pengujian semacam ini.</p>
<ul>
<li><p><code>stress</code>: Ini adalah nama perintahnya, yaitu program <code>stress</code> itu sendiri.</p>
</li>
<li><p><code>--cpu 1</code>: Artinya, kita mau membebani CPU dengan 1 prosesor (atau core). Kalau komputer kamu punya banyak core, kamu bisa ganti angka 1 jadi angka lain sesuai kebutuhan.</p>
</li>
<li><p><code>--vm 1</code>: Artinya, kita mau membebani memori virtual (swap) dengan 1 proses.</p>
</li>
<li><p><code>--vm-bytes 512M</code>: Nah, ini yang menarik. Kita mau alokasikan 512 Megabyte (M) memori virtual untuk pengujian ini. Jadi, <code>stress</code> akan "pura-pura" menggunakan memori sebanyak ini.</p>
</li>
<li><p><code>--timeout 30s</code>: Artinya, kita mau batasi waktu pengujian hanya selama 30 detik. Jadi, setelah 30 detik, <code>stress</code> akan berhenti otomatis.</p>
</li>
</ul>
<p><strong>Kenapa pakai perintah ini?</strong></p>
<ul>
<li><p><strong>Uji Ketahanan:</strong> Buat lihat seberapa kuat komputer kita, apakah dia stabil kalau dipaksa kerja keras.</p>
</li>
<li><p><strong>Cari Masalah:</strong> Kadang, kalau ada masalah hardware atau software, baru ketahuan pas komputer lagi "stress".</p>
</li>
<li><p><strong>Benchmark:</strong> Buat membandingkan performa komputer yang berbeda.</p>
</li>
</ul>
<h2 id="heading-task-33-monitor-system-usage">Task 3.3: Monitor System Usage</h2>
<pre><code class="lang-bash">top
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739154839321/eb3acb9e-d0b7-4ef7-bc26-af3266f9a6a5.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash">sudo apt install htop
htop
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739154886608/b5015e9f-f558-444d-a96c-7d424cb97d58.png" alt class="image--center mx-auto" /></p>
<p>Singkatnya, <code>top</code> dan <code>htop</code> adalah alat yang sangat berguna di Linux untuk memantau aktivitas sistem secara <em>real-time</em>. Keduanya menampilkan daftar proses yang sedang berjalan, penggunaan CPU, memori, dan informasi penting lainnya. Anggap saja mereka seperti "jendela" yang menunjukkan apa yang sedang terjadi di dalam komputer Linux kita.</p>
<p>Keduanya menampilkan informasi yang mirip, antara lain:</p>
<ul>
<li><p><strong>Daftar Proses</strong>: Menampilkan semua proses yang sedang berjalan di sistem, lengkap dengan ID proses (PID), penggunaan CPU, memori, dan lain-lain.</p>
</li>
<li><p><strong>Penggunaan CPU</strong>: Menunjukkan berapa persen CPU yang sedang digunakan.</p>
</li>
<li><p><strong>Penggunaan Memori</strong>: Menunjukkan berapa banyak memori (RAM) yang sedang digunakan.</p>
</li>
<li><p><strong>Waktu Sistem</strong>: Menunjukkan sudah berapa lama sistem berjalan.</p>
</li>
</ul>
<h2 id="heading-task-34-verify-the-sleep-process">Task 3.4: Verify the sleep process</h2>
<pre><code class="lang-bash">ps -aux | grep sleep
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739154991237/73c7e842-bba5-473b-8dfd-2f5a33edd7d9.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>ps -aux</code>: Perintah <code>ps</code> digunakan untuk menampilkan informasi tentang proses yang sedang berjalan. Opsi <code>-aux</code> memberikan informasi yang lebih lengkap dan detail, termasuk semua proses (baik yang dimiliki oleh pengguna maupun sistem), dalam format yang mudah dibaca.</p>
</li>
<li><p><code>grep sleep</code>: Perintah <code>grep</code> digunakan untuk mencari pola teks tertentu dalam output dari perintah lain. Dalam hal ini, kita mencari kata "sleep".</p>
</li>
<li><p>Tanda <code>|</code> (pipa) di antara kedua perintah tersebut berfungsi untuk "menyalurkan" output dari perintah <code>ps -aux</code> ke perintah <code>grep sleep</code>. Jadi, <code>grep sleep</code> akan mencari baris yang mengandung kata "sleep" dalam output dari <code>ps -aux</code>.</p>
</li>
</ul>
<p><strong>Kenapa pakai perintah ini?</strong></p>
<p>Perintah ini sangat berguna untuk mencari proses yang berkaitan dengan perintah <code>sleep</code>. Misalnya, kamu ingin melihat apakah ada proses <code>sleep</code> yang sedang berjalan, atau kamu ingin mencari tahu berapa lama proses <code>sleep</code> tersebut sudah berjalan.</p>
<h2 id="heading-task-35-check-disk-space-usage">Task 3.5: Check disk space usage</h2>
<pre><code class="lang-bash">df -h
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739155089294/863413af-d51c-48c8-a69b-5534a0265095.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash">du -h /home/sdb/ | sort -hr | head -n 10
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739155224053/2406c055-d482-49ea-9424-419f7cfde1eb.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>df -h</code>: Perintah <code>df</code> digunakan untuk menampilkan informasi tentang penggunaan ruang disk pada sistem. Opsi <code>-h</code> membuat ukuran ditampilkan dalam format yang mudah dibaca (misalnya, KB, MB, GB). Namun, perintah ini tidak akan membantu kita melihat ukuran per folder.</p>
</li>
<li><p><code>du -h /home/sdb/</code>: Perintah <code>du</code> digunakan untuk menampilkan penggunaan ruang disk per file dan direktori. Opsi <code>-h</code> sama seperti di <code>df</code>, membuat ukuran ditampilkan dalam format yang mudah dibaca. <code>/home/sdb/</code> adalah jalur folder yang ingin kita analisis.</p>
</li>
<li><p><code>|</code>: Tanda pipa ini berfungsi untuk "menyalurkan" output dari perintah sebelumnya ke perintah selanjutnya.</p>
</li>
<li><p><code>sort -hr</code>: Perintah <code>sort</code> digunakan untuk mengurutkan baris teks. Opsi <code>-h</code> mengurutkan berdasarkan ukuran yang mudah dibaca (misalnya, 10M lebih besar dari 2K), dan <code>-r</code> membalik urutan (dari terbesar ke terkecil).</p>
</li>
<li><p><code>head -n 10</code>: Perintah <code>head</code> digunakan untuk menampilkan beberapa baris pertama dari input. Opsi <code>-n 10</code> berarti kita hanya ingin menampilkan 10 baris pertama.</p>
</li>
</ul>
<p><strong>Kenapa pakai perintah ini?</strong></p>
<p>Perintah ini sangat berguna untuk mencari folder atau file mana yang memakan ruang disk paling banyak. Dengan begitu, kita bisa tahu folder atau file mana yang bisa dihapus atau dipindahkan untuk mengosongkan ruang disk.</p>
<h2 id="heading-task-36-monitor-memory-usage">Task 3.6: Monitor Memory Usage</h2>
<pre><code class="lang-bash">free -h
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739155418060/b232709c-e69f-42cf-898a-ce1e8c45bf57.png" alt class="image--center mx-auto" /></p>
<p>Komando <code>free -h</code> adalah perintah sederhana di Linux yang digunakan untuk menampilkan informasi tentang penggunaan memori sistem, baik memori fisik (RAM) maupun memori swap. Opsi <code>-h</code> membuat outputnya lebih mudah dibaca oleh manusia.</p>
<h2 id="heading-task-37-log-high-cpu-usage-processes">Task 3.7: Log High CPU Usage Processes</h2>
<pre><code class="lang-bash"><span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>; <span class="hljs-keyword">do</span> ps -aux | awk <span class="hljs-string">'$3 &gt; 10 {print $0}'</span> &gt;&gt; high_cpu.log; sleep 5; <span class="hljs-keyword">done</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739155765888/50ba06c2-68f2-4de8-9fa1-f6b50ccb2994.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>while true</code>: Ini adalah awal dari <em>loop</em> tak terbatas. Artinya, perintah-perintah di dalam <em>loop</em> akan terus diulang sampai kita menghentikannya secara manual (biasanya dengan <code>Ctrl+C</code>).</p>
</li>
<li><p><code>do</code>: Ini menandai awal dari blok perintah yang akan diulang.</p>
</li>
<li><p><code>ps -aux</code>: Seperti yang sudah dibahas sebelumnya, perintah ini menampilkan informasi lengkap tentang semua proses yang sedang berjalan.</p>
</li>
<li><p><code>|</code>: Tanda pipa ini "menyalurkan" output dari <code>ps -aux</code> ke perintah selanjutnya.</p>
</li>
<li><p><code>awk '$3 &gt; 10 {print $0}'</code>: Perintah <code>awk</code> digunakan untuk memfilter dan memproses teks. Dalam hal ini, kita memfilter baris-baris dari output <code>ps -aux</code> yang kolom ketiganya (<code>$3</code>, yang biasanya menunjukkan persentase penggunaan CPU) lebih besar dari 10. <code>{print $0}</code> mencetak seluruh baris jika kondisinya terpenuhi.</p>
</li>
<li><p><code>&gt;&gt; high_cpu.log</code>: Tanda <code>&gt;&gt;</code> digunakan untuk <em>append</em> (menambahkan) output dari perintah <code>awk</code> ke file <code>high_cpu.log</code>. Jadi, setiap kali ada proses yang CPU-nya di atas 10%, informasinya akan ditambahkan ke file ini.</p>
</li>
<li><p><code>sleep 5</code>: Perintah ini akan menunda eksekusi selama 5 detik. Jadi, <em>loop</em> akan berjalan setiap 5 detik.</p>
</li>
<li><p><code>done</code>: Ini menandai akhir dari blok perintah yang akan diulang.</p>
</li>
</ul>
<p><strong>Kenapa pakai perintah ini?</strong></p>
<p>Perintah ini berguna untuk memantau proses-proses yang "rakus" CPU. Kita bisa melihat proses mana saja yang menggunakan CPU di atas ambang batas tertentu (dalam hal ini 10%) dan menganalisisnya.</p>
<h2 id="heading-task-38-kill-the-long-running-process">Task 3.8: Kill the long-running process</h2>
<pre><code class="lang-bash"><span class="hljs-built_in">kill</span> &lt;PID&gt;
</code></pre>
<p>Komando <code>kill</code> di Linux digunakan untuk mengakhiri atau "membunuh" sebuah proses yang sedang berjalan. <code>&lt;PID&gt;</code> adalah <em>Process ID</em>, yaitu nomor unik yang diberikan oleh sistem ke setiap proses yang sedang berjalan.</p>
<ul>
<li><p><code>kill</code>: Ini adalah nama perintahnya, yaitu program <code>kill</code> itu sendiri.</p>
</li>
<li><p><code>&lt;PID&gt;</code>: Ini adalah <em>Process ID</em> dari proses yang ingin kita akhiri. Kita perlu mengganti <code>&lt;PID&gt;</code> dengan nomor PID yang sebenarnya.</p>
</li>
</ul>
<p><strong>Cara Mendapatkan PID</strong></p>
<p>Sebelum menggunakan perintah <code>kill</code>, kita perlu tahu dulu PID dari proses yang ingin kita akhiri. Ada beberapa cara untuk mendapatkan PID, antara lain:</p>
<ul>
<li><p>Menggunakan perintah <code>top</code> atau <code>htop</code>: Kedua perintah ini menampilkan daftar proses yang sedang berjalan, lengkap dengan PID-nya.</p>
</li>
<li><p>Menggunakan perintah <code>ps aux</code>: Perintah ini juga menampilkan daftar proses, dan kita bisa mencari PID dari proses yang ingin kita akhiri.</p>
</li>
</ul>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/linux-hands-on-3-process-management</link><guid isPermaLink="true">https://notes.mdrdani.my.id/linux-hands-on-3-process-management</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Linux Hands-on 2 (Output and Text Manipulation)]]></title><description><![CDATA[<p>Nah, di artikel ini kita lanjut ke Tugas 2, nih! Fokusnya kali ini soal ngolah teks dan <em>output</em>. Kita bakal pakai macem-macem perintah dan bikin <em>output</em>-nya dalam bentuk <em>file</em> teks.</p>
<h2 id="heading-task-21-generate-a-fake-log-file">Task 2.1: Generate a Fake Log File</h2>
<pre><code class="lang-bash">go install github.com/mingrammer/flog@latest
</code></pre>
<p><code>flog</code>: Ini adalah nama aplikasi yang mau kita instal. <code>flog</code> adalah <em>tool</em> atau alat yang berguna untuk membuat <em>fake log</em>, alias <em>log</em> palsu. Biasanya dipakai untuk <em>testing</em> atau <em>simulasi</em>. Karena <code>flog</code> ini dibuat menggunakan bahasa pemrograman Go, maka kita butuh <em>tool</em> "Go" untuk memasangnya.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> go/bin
./flog -s 10s -n 200 &gt; /home/sdb/access.log
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738803429221/08a891a1-6c40-4870-b6b3-e5762adc43eb.png" alt class="image--center mx-auto" /></p>
<p><strong>Bagian 1:</strong> <code>cd go/bin</code></p>
<ul>
<li><p><code>cd</code>: Ini singkatan dari "change directory" alias "pindah folder". Jadi, perintah ini digunakan untuk berpindah dari folder tempat kita berada sekarang ke folder lain.</p>
</li>
<li><p><code>go/bin</code>: Ini adalah "alamat" atau lokasi folder yang ingin kita tuju. Dalam hal ini, kita mau pindah ke folder <code>bin</code> yang ada di dalam folder <code>go</code>. Biasanya, di folder <code>bin</code> ini terdapat <em>file-file</em> program yang bisa dijalankan.</p>
</li>
</ul>
<p><strong>Jadi, bagian ini intinya adalah:</strong> Berpindah ke folder <code>go/bin</code>.</p>
<p><strong>Bagian 2:</strong> <code>./flog -s 10s -n 200 &gt; /home/sdb/access.log</code></p>
<ul>
<li><p><code>./flog</code>: Ini adalah perintah untuk menjalankan program <code>flog</code>. Tanda <code>./</code> di depan <code>flog</code> artinya program <code>flog</code> tersebut ada di folder tempat kita berada sekarang (yaitu folder <code>go/bin</code>).</p>
</li>
<li><p><code>-s 10s</code>: Opsi <code>-s</code> ini artinya "seconds" alias detik. Jadi, kita mau <code>flog</code> menghasilkan <em>log</em> setiap 10 detik.</p>
</li>
<li><p><code>-n 200</code>: Opsi <code>-n</code> ini artinya "number" alias jumlah. Jadi, kita mau <code>flog</code> menghasilkan 200 baris <em>log</em>.</p>
</li>
<li><p><code>&gt;</code>: Nah, ini nih yang penting. Tanda <code>&gt;</code> ini artinya kita mau "mengarahkan" <em>output</em> dari perintah <code>flog</code> ke sebuah file. Jadi, <em>log</em> yang dihasilkan tidak hanya ditampilkan di layar, tetapi juga disimpan di dalam sebuah file.</p>
</li>
<li><p><code>/home/sdb/access.log</code>: Ini adalah "alamat" lengkap file tempat kita menyimpan <em>log</em> yang dihasilkan oleh <code>flog</code>. Dalam hal ini, kita menyimpannya di dalam file <code>access.log</code> yang ada di folder <code>sdb</code> di dalam folder <code>home</code>.</p>
</li>
</ul>
<h2 id="heading-task-22-extract-unique-ip-address">Task 2.2: Extract unique IP Address</h2>
<pre><code class="lang-bash">awk <span class="hljs-string">'{print $1}'</span> access.log | sort | uniq
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738803856991/e69ef76c-c66c-4562-a33b-f78471763f2a.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>awk</code>: Ini adalah program yang sangat berguna untuk memproses teks. <code>awk</code> bekerja dengan membaca file baris per baris, dan kita bisa menentukan apa yang ingin kita lakukan untuk setiap barisnya.</p>
</li>
<li><p><code>'{print $1}'</code>: Ini adalah "instruksi" untuk <code>awk</code>. <code>print $1</code> artinya kita mau menampilkan kata pertama dari setiap baris. Dalam file <code>access.log</code>, biasanya kata pertama ini berisi <em>IP address</em> atau alamat <em>server</em> yang mengakses.</p>
</li>
<li><p><code>access.log</code>: Ini adalah nama file yang berisi <em>log</em> akses.</p>
</li>
<li><p><code>sort</code>: Ini adalah perintah untuk mengurutkan teks. Secara default, <code>sort</code> akan mengurutkan teks secara alfabetis.</p>
</li>
<li><p><code>uniq</code>: Ini adalah perintah untuk menampilkan baris-baris unik. Jadi, kalau ada <em>IP address</em> yang muncul berkali-kali, <code>uniq</code> hanya akan menampilkannya sekali.</p>
</li>
</ul>
<h2 id="heading-task-23-count-accurrence-of-each-ip-address">Task 2.3: Count Accurrence of each IP Address</h2>
<pre><code class="lang-bash">awk <span class="hljs-string">'{print $1}'</span> access.log | sort | uniq -c | sort -nr &gt; ip_counts.txt
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738804015454/720cc3d5-fca8-4a53-a8b3-b3f2b5230d6f.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>uniq</code>: Ini adalah perintah untuk menampilkan baris-baris unik. Jadi, kalau ada <em>IP address</em> yang muncul berkali-kali, <code>uniq</code> hanya akan menampilkannya sekali.</p>
</li>
<li><p><code>-c</code>: Opsi <code>-c</code> ini artinya "count" alias hitung. Jadi, selain menampilkan <em>IP address</em> yang unik, <code>uniq</code> juga akan menampilkan berapa kali <em>IP address</em> tersebut muncul.</p>
</li>
<li><p><code>sort</code>: Sama seperti sebelumnya, ini adalah perintah untuk mengurutkan teks.</p>
</li>
<li><p><code>-n</code>: Opsi <code>-n</code> ini artinya "numeric" alias angka. Jadi, kita mau mengurutkan berdasarkan angka, bukan alfabet.</p>
</li>
<li><p><code>-r</code>: Opsi <code>-r</code> ini artinya "reverse" alias terbalik. Jadi, kita mau mengurutkan dari angka terbesar ke angka terkecil.</p>
</li>
</ul>
<h2 id="heading-task-24-filter-log-entries-with-status-code-404">Task 2.4: Filter Log Entries with status Code 404</h2>
<pre><code class="lang-bash">grep <span class="hljs-string">" 404 "</span> access.log &gt; errors.log
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738826135064/d7d29605-d544-4ae1-8779-0f6a74a66605.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>grep</code>: Nah, kalau <code>grep</code> ini kayak detektif yang lagi nyari sesuatu di dalam teks. Dia bakal nyari baris-baris yang mengandung kata atau pola tertentu.</p>
</li>
<li><p><code>" 404 "</code>: Ini adalah "target" yang mau dicari oleh <code>grep</code>. Dalam hal ini, kita mau mencari baris yang mengandung kode "404". Kode "404" ini biasanya muncul di <em>log</em> akses <em>website</em> dan menandakan bahwa halaman yang diminta tidak ditemukan alias "error".</p>
</li>
<li><p><code>access.log</code>: Ini adalah nama file yang akan "diperiksa" oleh <code>grep</code>. Jadi, <code>grep</code> akan mencari kode "404" di dalam file <code>access.log</code>.</p>
</li>
<li><p><code>&gt;</code>: Nah, ini nih yang penting. Tanda <code>&gt;</code> ini artinya kita mau "mengarahkan" <em>output</em> dari perintah <code>grep</code> ke sebuah file. Jadi, baris-baris yang mengandung kode "404" tidak hanya ditampilkan di layar, tetapi juga disimpan di dalam sebuah file.</p>
</li>
<li><p><code>errors.log</code>: Ini adalah nama file tempat kita menyimpan baris-baris yang mengandung kode "404".</p>
</li>
</ul>
<h2 id="heading-task-25-replace-mozilla-with-browserx">Task 2.5: Replace Mozilla with BrowserX</h2>
<pre><code class="lang-bash">sed <span class="hljs-string">'s/Mozilla/BrowserX/g'</span> access.log &gt; access_modified.log
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738826602466/3a1fc1d7-3a20-4bfa-a1ec-b0ebd96b48ca.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>sed</code>: Nah, kalau <code>sed</code> ini kayak editor teks otomatis. Dia bisa mencari dan mengganti teks di dalam file secara otomatis, tanpa perlu kita buka filenya satu per satu.</p>
</li>
<li><p><code>'s/Mozilla/BrowserX/g'</code>: Ini adalah "instruksi" untuk <code>sed</code>. Mari kita pecah lagi:</p>
<ul>
<li><p><code>s</code>: Ini singkatan dari "substitute" alias "ganti". Jadi, kita mau mengganti teks.</p>
</li>
<li><p><code>/Mozilla/</code>: Ini adalah teks yang mau kita cari atau ganti. Dalam hal ini, kita mau mencari kata "Mozilla".</p>
</li>
<li><p><code>/BrowserX/</code>: Ini adalah teks penggantinya. Jadi, kata "Mozilla" akan diganti dengan "BrowserX".</p>
</li>
<li><p><code>g</code>: Ini artinya "global". Jadi, semua kemunculan kata "Mozilla" di setiap baris akan diganti, bukan hanya yang pertama saja.</p>
</li>
</ul>
</li>
<li><p><code>access.log</code>: Ini adalah nama file yang akan "diedit" oleh <code>sed</code>. Jadi, <code>sed</code> akan mencari dan mengganti teks di dalam file <code>access.log</code>.</p>
</li>
<li><p><code>&gt;</code>: Nah, ini nih yang penting. Tanda <code>&gt;</code> ini artinya kita mau "mengarahkan" <em>output</em> dari perintah <code>sed</code> ke sebuah file baru. Jadi, hasil editan tidak langsung mengubah file <code>access.log</code> aslinya, tetapi disimpan di file baru.</p>
</li>
<li><p><code>access_modified.log</code>: Ini adalah nama file baru yang akan berisi teks yang sudah dimodifikasi.</p>
</li>
</ul>
<h2 id="heading-task-26-extract-log-entries-froom-a-spesific-date">Task 2.6: Extract Log Entries froom a Spesific Date</h2>
<pre><code class="lang-bash"> grep <span class="hljs-string">"06/Feb/2025"</span> access.log &gt; specific_date.log
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738826932921/864686ae-b995-430f-b443-f04c718cb873.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>grep</code>: Perintah untuk mencari pola teks dalam file.</p>
</li>
<li><p><code>"06/Feb/2025"</code>: Pola teks yang dicari. Dalam hal ini, kita mencari baris yang mengandung tanggal "06/Feb/2025". Perhatikan bahwa format tanggal ini harus sama persis dengan yang ada di file <code>access.log</code> Anda.</p>
</li>
<li><p><code>access.log</code>: Nama file yang akan dicari.</p>
</li>
<li><p><code>&gt;</code>: Operator <em>redirection</em> yang mengarahkan <em>output</em> dari perintah <code>grep</code> ke file lain.</p>
</li>
<li><p><code>specific_date.log</code>: Nama file baru yang akan berisi baris-baris yang cocok dengan pola yang dicari.</p>
</li>
</ul>
<h2 id="heading-task-27-calculate-total-bytes-transferred">Task 2.7: Calculate Total Bytes Transferred</h2>
<pre><code class="lang-bash">awk <span class="hljs-string">'{sum += $10} END {print sum}'</span> access.log
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738827068930/74afac29-3011-498a-a3e6-d3d043673336.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>sum += $10</code>: Ini artinya kita membuat sebuah variabel bernama <code>sum</code> dan menambahkan nilai dari kolom ke-10 (<code>$10</code>) ke variabel <code>sum</code> tersebut. Jadi, <code>sum</code> akan menyimpan total dari nilai-nilai di kolom ke-10.</p>
</li>
<li><p>Kolom ke-10 di file <code>access.log</code> Anda kemungkinan besar berisi ukuran <em>response</em> dalam <em>byte</em>.</p>
</li>
<li><p><code>'END {print sum}'</code>: Ini juga "instruksi" untuk <code>awk</code>.</p>
<ul>
<li><p><code>END</code>: Ini artinya perintah di dalam kurung kurawal akan dieksekusi setelah <code>awk</code> selesai membaca seluruh file.</p>
</li>
<li><p><code>print sum</code>: Ini artinya kita mau menampilkan nilai dari variabel <code>sum</code>, yaitu total dari ukuran <em>response</em>.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-task-28-find-ip-with-the-most-404-errors">Task 2.8: Find IP with the Most 404 Errors</h2>
<pre><code class="lang-bash">grep <span class="hljs-string">' 404 '</span> access.log | awk <span class="hljs-string">'{print $1}'</span> | sort | uniq -c | sort -nr | head -n 1 &gt; top_404_ip.txt
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738827337691/c4664728-6bf4-48a7-ba2d-42a9ae52b4fa.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>grep ' 404 ' access.log</code>:</p>
<ul>
<li><p><code>grep</code>: Ini adalah perintah untuk mencari pola teks dalam file.</p>
</li>
<li><p><code>' 404 '</code>: Pola teks yang dicari. Kita mencari baris yang mengandung kode "404". Kode ini biasanya menunjukkan error "Not Found" di log akses web.</p>
</li>
<li><p><code>access.log</code>: Nama file log yang akan dicari.</p>
</li>
<li><p><strong>Intinya:</strong> Mencari semua baris di <code>access.log</code> yang mengandung "404".</p>
</li>
</ul>
</li>
<li><p><code>awk '{print $1}'</code>:</p>
<ul>
<li><p><code>awk</code>: Program untuk memproses teks per baris.</p>
</li>
<li><p><code>'{print $1}'</code>: Instruksi untuk <code>awk</code>. <code>$1</code> artinya kolom pertama dari setiap baris. Biasanya, di log akses web, kolom pertama ini berisi alamat IP.</p>
</li>
<li><p><strong>Intinya:</strong> Mengambil alamat IP dari setiap baris yang dihasilkan oleh <code>grep</code>.</p>
</li>
</ul>
</li>
<li><p><code>sort</code>:</p>
<ul>
<li><p><code>sort</code>: Perintah untuk mengurutkan teks. Secara default, ia mengurutkan berdasarkan abjad.</p>
</li>
<li><p><strong>Intinya:</strong> Mengurutkan daftar alamat IP.</p>
</li>
</ul>
</li>
<li><p><code>uniq -c</code>:</p>
<ul>
<li><p><code>uniq</code>: Perintah untuk menampilkan baris-baris unik.</p>
</li>
<li><p><code>-c</code>: Opsi untuk menghitung berapa kali setiap baris muncul.</p>
</li>
<li><p><strong>Intinya:</strong> Menghitung berapa kali setiap alamat IP muncul (berapa kali setiap IP menghasilkan error 404).</p>
</li>
</ul>
</li>
<li><p><code>sort -nr</code>:</p>
<ul>
<li><p><code>sort</code>: Perintah untuk mengurutkan teks.</p>
</li>
<li><p><code>-n</code>: Opsi untuk mengurutkan secara numerik (bukan abjad).</p>
</li>
<li><p><code>-r</code>: Opsi untuk membalik urutan (dari terbesar ke terkecil).</p>
</li>
<li><p><strong>Intinya:</strong> Mengurutkan alamat IP berdasarkan jumlah kemunculannya, dari yang paling sering muncul.</p>
</li>
</ul>
</li>
<li><p><code>head -n 1</code>:</p>
<ul>
<li><p><code>head</code>: Perintah untuk menampilkan beberapa baris pertama dari sebuah file atau output.</p>
</li>
<li><p><code>-n 1</code>: Opsi untuk menampilkan hanya 1 baris.</p>
</li>
<li><p><strong>Intinya:</strong> Mengambil alamat IP yang paling sering menghasilkan error 404.</p>
</li>
</ul>
</li>
<li><p><code>&gt;</code>:</p>
<ul>
<li><p><code>&gt;</code>: Operator <em>redirection</em> untuk mengarahkan output ke sebuah file.</p>
</li>
<li><p><code>top_404_ip.txt</code>: Nama file yang akan menyimpan output.</p>
</li>
<li><p><strong>Intinya:</strong> Menyimpan alamat IP yang paling sering menghasilkan error 404 ke dalam file <code>top_404_ip.txt</code>.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-task-29-anonymize-all-ip-address">Task 2.9: Anonymize All IP Address</h2>
<pre><code class="lang-bash">sed -E <span class="hljs-string">'s/^[^ ]+/ANONYMIZED_IP/'</span> access.log &gt; access_anonymized.log
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738827669009/9414ffe2-f50d-4d2a-967d-9b23e16be89c.png" alt class="image--center mx-auto" /></p>
<p><code>'s/^[^ ]+/ANONYMIZED_IP/'</code>: Ini adalah "instruksi" untuk <code>sed</code>. Mari kita bedah lagi:</p>
<ul>
<li><p><code>s</code>: Ini singkatan dari "substitute" alias "ganti". Jadi, kita mau mengganti teks.</p>
</li>
<li><p><code>^</code>: Ini artinya "awal baris". Jadi, kita mau mencari teks yang ada di awal baris.</p>
</li>
<li><p><code>[^ ]+</code>: Ini adalah pola teks yang mau kita cari. <code>[^ ]</code> artinya "semua karakter kecuali spasi". <code>+</code> artinya "satu atau lebih kemunculan". Jadi, <code>[^ ]+</code> artinya satu atau lebih karakter yang bukan spasi. Karena ada <code>^</code> di depannya, berarti kita mencari satu atau lebih karakter bukan spasi yang ada di awal baris. Biasanya, di <em>log</em> akses web, bagian ini adalah alamat IP.</p>
</li>
<li><p><code>/ANONYMIZED_IP/</code>: Ini adalah teks penggantinya. Jadi, teks yang cocok dengan pola <code>^[^ ]+</code> (yaitu alamat IP di awal baris) akan diganti dengan "ANONYMIZED_IP".</p>
</li>
</ul>
<h2 id="heading-task-210-count-log-entries-with-500-status-code">Task 2.10: Count Log Entries with 500 status code</h2>
<pre><code class="lang-bash">grep <span class="hljs-string">" 500 "</span> access.log | wc -l
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738827923027/443cd1ac-3cd0-46e2-a529-68e0f8ac76d6.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>wc</code>: Ini adalah perintah untuk menghitung jumlah kata, baris, dan karakter dalam sebuah file atau input.</p>
</li>
<li><p><code>-l</code>: Opsi untuk menghitung jumlah baris.</p>
</li>
<li><p><strong>Intinya:</strong> Menghitung jumlah baris yang diberikan sebagai input. Dalam hal ini, inputnya adalah output dari perintah <code>grep</code>.</p>
</li>
</ul>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/linux-hands-on-2-output-and-text-manipulation</link><guid isPermaLink="true">https://notes.mdrdani.my.id/linux-hands-on-2-output-and-text-manipulation</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Linux Hands-on 1 (File Management and Directory Operations)]]></title><description><![CDATA[<p>Artikel ini akan menceritakan pengalaman saya menyelesaikan tugas <a target="_blank" href="https://discord.gg/8bUq4aNT"><em>Community DevOps Focus</em></a> <em>Group</em>. Tugas ini terdiri dari 4 bagian, dan saya akan memisahkannya ke dalam beberapa artikel. Pada artikel pertama ini, saya akan berfokus pada tugas-tugas yang berkaitan dengan file management dan directory operations.</p>
<p>Sebelum menjalankan perintah pertama, kita perlu memasang <code>flog</code> untuk menghasilkan <em>log</em> palsu dan <code>stress</code> untuk mensimulasikan beban CPU dan memori. Saya juga memasang <code>tree</code> untuk mempermudah visualisasi struktur direktori.</p>
<pre><code class="lang-plaintext">https://github.com/mingrammer/flog
</code></pre>
<pre><code class="lang-bash">sudo apt install stress
sudo apt install tree
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738744387921/42a21dd2-a4fa-4c0f-a1f3-938da81a584f.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-task-11-create-the-following-directory-structure">Task 1.1: Create the following directory structure:</h2>
<pre><code class="lang-bash">mkdir -p project/reports/2024 project/reports/2025 project/scripts project/data
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738744540754/abb51287-24d2-4d16-b397-e0b72f67b1e3.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-task-12-create-empty-files-in-reports2024">Task 1.2: Create empty files in reports/2024</h2>
<pre><code class="lang-bash">touch project/reports/2024/q1.txt
touch project/reports/2024/q2.txt
touch project/reports/2024/q3.txt
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738744803618/07d26f06-5dba-4e5f-9362-e8661a6f04bc.png" alt class="image--center mx-auto" /></p>
<p>Kita bisa pakai perintah <code>touch</code> buat bikin file. Perintah <code>ls</code> digunakan untuk menampilkan daftar berkas dan direktori. Berikut penjelasan opsi yang digunakan:</p>
<ul>
<li><p><code>-l</code>: Menampilkan daftar dalam format <em>long listing</em> (panjang), yang memberikan informasi detail seperti izin berkas, pemilik, grup, ukuran, tanggal modifikasi, dan nama berkas.</p>
</li>
<li><p><code>-h</code>: Menampilkan ukuran berkas dalam format <em>human-readable</em> (mudah dibaca manusia), misalnya KB, MB, atau GB, bukan dalam <em>byte</em>.</p>
</li>
<li><p><code>-t</code>: Mengurutkan daftar berdasarkan waktu modifikasi terakhir.</p>
</li>
</ul>
<p>Jadi, <code>ls -lht</code> akan menampilkan daftar berkas dan direktori dalam format panjang, dengan ukuran yang mudah dibaca, dan diurutkan berdasarkan waktu modifikasi terakhir.</p>
<p><strong>Penjelasan <em>File Permission</em> (Izin Berkas) di Linux:</strong></p>
<p>Izin berkas di Linux mengatur siapa yang boleh melakukan apa terhadap suatu berkas. Izin ini direpresentasikan dalam sepuluh karakter, contohnya <code>-rw-rw-r--</code>.</p>
<ul>
<li><p>Karakter pertama (<code>-</code>): Menunjukkan jenis berkas. <code>-</code> berarti berkas biasa, <code>d</code> berarti direktori, <code>l</code> berarti <em>symbolic link</em>, dan lainnya.</p>
</li>
<li><p>Sembilan karakter berikutnya dibagi menjadi tiga kelompok, masing-masing untuk <em>owner</em> (pemilik), <em>group</em> (kelompok), dan <em>others</em> (lainnya).</p>
</li>
<li><p>Setiap kelompok memiliki tiga bit:</p>
<ul>
<li><p><code>r</code>: <em>Read</em> (baca) - Izin untuk membaca isi berkas.</p>
</li>
<li><p><code>w</code>: <em>Write</em> (tulis) - Izin untuk mengubah atau menghapus isi berkas.</p>
</li>
<li><p><code>x</code>: <em>Execute</em> (eksekusi) - Izin untuk menjalankan berkas (jika berkas tersebut adalah program yang dapat dieksekusi).</p>
</li>
</ul>
</li>
<li><p>Tanda <code>-</code> berarti izin tersebut tidak diberikan.</p>
</li>
</ul>
<p>Dalam contoh <code>-rw-rw-r--</code>:</p>
<ul>
<li><p><em>Owner</em>: Memiliki izin baca (<code>r</code>) dan tulis (<code>w</code>), tetapi tidak memiliki izin eksekusi (<code>-</code>).</p>
</li>
<li><p><em>Group</em>: Memiliki izin baca (<code>r</code>) dan tulis (<code>w</code>), tetapi tidak memiliki izin eksekusi (<code>-</code>).</p>
</li>
<li><p><em>Others</em>: Memiliki izin baca (<code>r</code>), tetapi tidak memiliki izin tulis (<code>-</code>) dan eksekusi (<code>-</code>).</p>
</li>
</ul>
<h2 id="heading-task-13-move-q1txt-to-2025">Task 1.3: Move q1.txt to 2025</h2>
<pre><code class="lang-bash">mv project/reports/2024/q1.txt project/reports/2025/
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738745074985/4cd1ff47-5397-4acc-adb7-49d7bbd5b700.png" alt class="image--center mx-auto" /></p>
<p><code>mv</code> itu artinya "move" alias mindahin. Nah, biar nggak nyasar, mending tulis perintah lengkap sama alamat lengkap filenya, jangan cuma <code>q1.txt</code> doang. Kalau tiba-tiba muncul pesan "No such file or directory", itu biasanya gara-gara alamat filenya salah.</p>
<h2 id="heading-task-14-copy-q2txt-to-scripts-dan-rename-menjadi-analysistxt">Task 1.4: Copy q2.txt to scripts dan rename menjadi analysis.txt</h2>
<pre><code class="lang-bash">cp project/reports/2024/q1.txt project/scripts/analysis.txt
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738745416076/da5c1ad1-06e2-4f7e-98a4-4a41b9eb811f.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-task-15-list-files-in-2025-and-save-output-to-file-filelistlog">Task 1.5: list files in 2025 and save output to file file_list.log</h2>
<pre><code class="lang-bash">ls project/reports/2025 &gt; file_list.log
</code></pre>
<h2 id="heading-task-16-append-task-completed-to-q3txt">Task 1.6: Append Task Completed to q3.txt</h2>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"Task Completed"</span> &gt;&gt; project/reports/2024/q3.txt
cat project/reports/2024/q3.txt
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738745656456/46e18c6e-82c7-4598-89bf-c7732a25ff2d.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>echo "Task Completed"</code>: Nah, bagian ini artinya kita mau "ngomong" atau "nulis" kalimat "Task Completed". <code>echo</code> itu kayak perintah buat nampilin sesuatu di layar, tapi di sini kita mau tampilinnya di dalam file.</p>
</li>
<li><p><code>&gt;&gt;</code>: Tanda ini nih yang penting. Ini artinya kita mau "nambahin" tulisan "Task Completed" tadi ke dalam file. Jadi, kalau di file udah ada tulisan lain, tulisan "Task Completed" ini bakal ditaruh di baris baru, di bawah tulisan yang lama. Beda kalau kita pakai tanda <code>&gt;</code>, kalau pakai ini, tulisan yang lama di file akan dihapus dan diganti dengan tulisan "Task Completed".</p>
</li>
<li><p><code>cat</code>: Nah, kalau <code>cat</code> ini kayak baca buku. Dia bakal nampilin isi file ke layar.</p>
</li>
</ul>
<h2 id="heading-task-17-display-directory-structure-and-save-to-structurelog">Task 1.7: Display directory structure and save to structure.log</h2>
<pre><code class="lang-bash">tree project/ &gt; structure.log
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738745885088/1a403379-0c80-44be-9ee0-5796e28495e2.png" alt class="image--center mx-auto" /></p>
<p><code>tree</code>: Nah, perintah <code>tree</code> ini kayak tukang kebun yang rapi banget. Dia bakal nunjukin struktur folder dan file kita dalam bentuk pohon, lengkap dengan cabang-cabangnya. Jadi, kita bisa lihat dengan jelas, file ini ada di dalam folder apa, folder itu ada di dalam folder apa lagi, dan seterusnya.</p>
<h2 id="heading-task-18-delete-analysistxt-and-confirm">Task 1.8: Delete analysis.txt and confirm</h2>
<pre><code class="lang-bash"> rm project/scripts/analysis.txt
</code></pre>
<p><code>rm</code>: Nah, kalau <code>rm</code> ini artinya "remove" alias hapus. Jadi, perintah ini digunakan untuk menghapus file atau folder.</p>
<h2 id="heading-task-19-copy-the-entire-project-directory">Task 1.9: Copy the entire project directory</h2>
<pre><code class="lang-bash"> cp -r project/ project-old
</code></pre>
<ul>
<li><p><code>cp</code>: Nah, kalau <code>cp</code> ini artinya "copy" alias salin. Jadi, perintah ini digunakan untuk menyalin file atau folder.</p>
</li>
<li><p><code>-r</code>: Opsi <code>-r</code> ini singkatan dari "recursive". Artinya, kalau yang disalin itu folder, maka semua isinya (termasuk subfolder dan file-file di dalamnya) juga ikut disalin.</p>
</li>
<li><p><code>project/</code>: Ini adalah lokasi folder yang mau kita salin. Dalam contoh ini, kita mau menyalin folder <code>project/</code> beserta seluruh isinya.</p>
</li>
<li><p><code>project-old</code>: Ini adalah nama folder baru hasil salinan. Jadi, kita akan membuat folder baru bernama <code>project-old</code> yang isinya sama persis dengan folder <code>project/</code>.</p>
</li>
</ul>
<h2 id="heading-task-110-delete-original-project">Task 1.10: Delete Original project</h2>
<pre><code class="lang-bash">rm -ri project
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738746475666/80f418d5-ad39-4797-a92f-1acea7fd0a33.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>rm</code>: Nah, kalau <code>rm</code> ini artinya "remove" alias hapus. Jadi, perintah ini digunakan untuk menghapus file atau folder.</p>
</li>
<li><p><code>-r</code>: Opsi <code>-r</code> ini singkatan dari "recursive". Artinya, kalau yang dihapus itu folder, maka semua isinya (termasuk subfolder dan file-file di dalamnya) juga ikut dihapus.</p>
</li>
<li><p><code>-i</code>: Opsi <code>-i</code> ini singkatan dari "interactive". Artinya, sebelum menghapus setiap file atau folder, perintah ini akan bertanya dulu ke kita, "Yakin mau dihapus nggak nih?". Jadi, kita punya kesempatan untuk berpikir ulang sebelum file atau folder benar-benar hilang.</p>
</li>
<li><p><code>project</code>: Ini adalah nama folder yang mau kita hapus beserta seluruh isinya.</p>
</li>
</ul>
<h2 id="heading-task-111-create-a-5gb-file">Task 1.11: Create a 5GB file</h2>
<pre><code class="lang-bash">truncate -s 5G large_file.dat
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738746631800/436c3f97-2318-4580-a976-35b81e3f61a0.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>truncate</code>: Nah, kalau <code>truncate</code> ini artinya "memotong" atau "memangkas". Perintah ini digunakan untuk mengubah ukuran file, bisa memperbesar atau memperkecil.</p>
</li>
<li><p><code>-s 5G</code>: Opsi <code>-s</code> ini digunakan untuk menentukan ukuran file yang baru. Dalam contoh ini, kita mau membuat ukuran file menjadi 5 Gigabyte (5G). Kamu bisa menggunakan satuan lain seperti K (Kilobyte), M (Megabyte), atau G (Gigabyte).</p>
</li>
<li><p><code>large_file.dat</code>: Ini adalah nama file yang ukurannya mau kita ubah.</p>
</li>
</ul>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/linux-hands-on-1-file-management-and-directory-operations</link><guid isPermaLink="true">https://notes.mdrdani.my.id/linux-hands-on-1-file-management-and-directory-operations</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Traefik as Reverse Proxy & Load Balancer Locally]]></title><description><![CDATA[<p><img src="https://doc.traefik.io/traefik/assets/img/traefik-architecture.png" alt="Architecture" /></p>
<h2 id="heading-tentang-traefik">Tentang Traefik</h2>
<p><strong>Traefik</strong> adalah <em>reverse proxy</em> dan <em>load balancer</em> modern yang dirancang untuk mengelola lalu lintas web di dalam infrastruktur berbasis <em>microservices</em>. Traefik mendukung integrasi langsung dengan berbagai sistem orkestrasi seperti Docker, Kubernetes, dan lainnya, serta menyediakan fitur-fitur canggih untuk memudahkan pengelolaan lalu lintas aplikasi.</p>
<h2 id="heading-apa-itu-reverse-proxy">Apa itu Reverse Proxy</h2>
<p><em>Reverse proxy</em> adalah server yang duduk di depan satu atau lebih server backend. Tugasnya adalah menerima permintaan dari klien (misalnya, browser pengguna), meneruskannya ke server backend yang sesuai, dan mengirimkan respons kembali ke klien. Dengan <em>reverse proxy</em>, kita bisa:</p>
<ul>
<li><p>Menyembunyikan detail server backend.</p>
</li>
<li><p>Membagi beban lalu lintas ke beberapa server backend.</p>
</li>
<li><p>Menambahkan fitur keamanan seperti TLS/SSL.</p>
</li>
</ul>
<p>Traefik bertindak sebagai <em>reverse proxy</em> untuk layanan-layanan Anda.</p>
<p><a target="_blank" href="https://client.jetorbit.com/aff.php?aff=945"><img src="https://www.jetorbit.com/images/banner/banner-wide-728-v2.png" alt class="image--center mx-auto" /></a></p>
<h2 id="heading-fitur-utama-traefik">Fitur Utama Traefik</h2>
<h3 id="heading-a-integrasi-otomatis-dengan-orkestrasi-kontainer">a. <strong>Integrasi Otomatis dengan Orkestrasi Kontainer</strong></h3>
<p>Traefik dapat secara otomatis mendeteksi dan mengatur rute untuk layanan yang berjalan di dalam sistem orkestrasi seperti Docker, Kubernetes, Nomad, atau Swarm. Anda tidak perlu secara manual mendefinisikan rute atau memuat ulang konfigurasinya.</p>
<h3 id="heading-b-tls-termination-https">b. <strong>TLS Termination (HTTPS)</strong></h3>
<p>Traefik mendukung <em>TLS termination</em>, yaitu proses mendekripsi lalu lintas HTTPS di sisi Traefik sebelum diteruskan ke server backend. Ini memungkinkan backend Anda tetap berjalan di HTTP biasa. Traefik juga mendukung integrasi dengan Let's Encrypt untuk mengelola sertifikat TLS otomatis.</p>
<h3 id="heading-c-routing-dinamis">c. <strong>Routing Dinamis</strong></h3>
<p>Traefik memungkinkan Anda untuk mendefinisikan rute secara dinamis menggunakan berbagai aturan:</p>
<ul>
<li><p>Berdasarkan host (contoh: <code>Host(</code><a target="_blank" href="http://app.example.com"><code>app.example.com</code></a><code>)</code>).</p>
</li>
<li><p>Berdasarkan jalur (contoh: <code>Path(/api)</code>).</p>
</li>
<li><p>Kombinasi aturan lainnya.</p>
</li>
</ul>
<h3 id="heading-d-middlewares">d. <strong>Middlewares</strong></h3>
<p>Middlewares memungkinkan Anda untuk memodifikasi lalu lintas sebelum diteruskan ke server backend. Contoh middleware:</p>
<ul>
<li><p><strong>Redirect Scheme</strong>: Mengarahkan HTTP ke HTTPS.</p>
</li>
<li><p><strong>Rate Limiting</strong>: Membatasi jumlah permintaan.</p>
</li>
<li><p><strong>Authentication</strong>: Menambahkan otentikasi dasar untuk layanan tertentu.</p>
</li>
</ul>
<h3 id="heading-e-load-balancing">e. <strong>Load Balancing</strong></h3>
<p>Traefik mendukung berbagai algoritma <em>load balancing</em> (round-robin, <em>least connections</em>, dll.) untuk mendistribusikan lalu lintas ke beberapa server backend.</p>
<h3 id="heading-f-dashboard">f. <strong>Dashboard</strong></h3>
<p>Traefik menyediakan dashboard berbasis web yang memungkinkan Anda memantau status rute, middleware, dan layanan.</p>
<h3 id="heading-g-plug-and-play">g. Plug-and-Play</h3>
<p>Traefik mendukung banyak protokol seperti HTTP, HTTPS, TCP, dan UDP secara native, sehingga sangat fleksibel.</p>
<h2 id="heading-setup-traefik-in-docker-compose">Setup Traefik in docker-compose</h2>
<p>docker-compose.yaml</p>
<pre><code class="lang-yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">'3.9'</span>

<span class="hljs-attr">services:</span>
  <span class="hljs-attr">traefik:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">traefik:v2.10</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">traefik</span>
    <span class="hljs-attr">command:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--api.insecure=true'</span> <span class="hljs-comment"># Enables the Traefik dashboard</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--providers.docker=true'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--providers.file.directory=/etc/traefik/dynamic'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--entrypoints.web.address=:80'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--entrypoints.websecure.address=:443'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'--entrypoints.websecure.http.tls=true'</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'80:80'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'443:443'</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">/var/run/docker.sock:/var/run/docker.sock</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./certs:/certs</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./dynamic:/etc/traefik/dynamic</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.enable=true'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.routers.traefik.rule=Host(`traefik.docker.localhost`)'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.routers.traefik.service=api@internal'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.routers.traefik.tls=true'</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">frontend</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>

  <span class="hljs-attr">nginx:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx:latest</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">nginx</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.enable=true'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.routers.nginx.rule=Host(`app.docker.localhost`)'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.routers.nginx.entrypoints=websecure'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.routers.nginx.tls=true'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.services.nginx.loadbalancer.server.port=80'</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">frontend</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>

  <span class="hljs-attr">nginxtwo:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx:latest</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">nginxtwo</span>
    <span class="hljs-attr">labels:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.enable=true'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.routers.nginxtwo.rule=Host(`apptwo.docker.localhost`)'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.routers.nginxtwo.entrypoints=websecure'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.routers.nginxtwo.tls=true'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'traefik.http.services.nginxtwo.loadbalancer.server.port=80'</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">frontend</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>

<span class="hljs-attr">networks:</span>
  <span class="hljs-attr">frontend:</span>
    <span class="hljs-attr">external:</span> <span class="hljs-literal">true</span>
</code></pre>
<p>buat networks agar semua aplikasi menjadi satu network</p>
<pre><code class="lang-bash">$ docker network create frontend
</code></pre>
<p>selanjutnya kita setting domain localhost</p>
<pre><code class="lang-bash">$ sudo nano /etc/hosts
</code></pre>
<p>tambahkan domain local</p>
<ul>
<li><p><strong>traefik.docker.localhost</strong></p>
</li>
<li><p><strong>app.docker.localhost</strong></p>
</li>
<li><p><strong>apptwo.docker.localhost</strong></p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732086296948/23d468a5-900d-4a3d-8c00-08ccf10b7623.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-adding-ssl-certificates-for-localhosthttplocalhost">Adding SSL certificates for <a target="_blank" href="http://localhost">localhost</a></h2>
<p>Jika sudah setting domain local, selanjutnya generate local SSL dengan <strong>mkcert.</strong></p>
<p>untuk ubuntu dapat menginstall dengan</p>
<pre><code class="lang-bash">$ sudo apt install mkcert libnss3-tools
</code></pre>
<pre><code class="lang-bash">$ mkdir cert
$ <span class="hljs-built_in">cd</span> cert
$ mkcert -install
$ mkcert traefik.docker.localhost
$ mkcert app.docker.localhost
$ mkcert apptwo.docker.localhost
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732086712920/84c24d76-55b4-4c7a-8ed1-04038fbe0ddc.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-basic-traefik-configuration-walkthrough">Basic Traefik configuration walkthrough</h2>
<p>selanjutnya kita buat dynamic configuration file.</p>
<pre><code class="lang-bash">$ mkdir dynamic
$ touch tls.yml
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-comment"># dynamic/tls.yml</span>
<span class="hljs-attr">tls:</span>
  <span class="hljs-attr">certificates:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">certFile:</span> <span class="hljs-string">/certs/traefik.docker.localhost.pem</span>
      <span class="hljs-attr">keyFile:</span> <span class="hljs-string">/certs/traefik.docker.localhost-key.pem</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">certFile:</span> <span class="hljs-string">/certs/app.docker.localhost.pem</span>
      <span class="hljs-attr">keyFile:</span> <span class="hljs-string">/certs/app.docker.localhost-key.pem</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">certFile:</span> <span class="hljs-string">/certs/apptwo.docker.localhost.pem</span>
      <span class="hljs-attr">keyFile:</span> <span class="hljs-string">/certs/apptwo.docker.localhost-key.pem</span>
</code></pre>
<h3 id="heading-run-traefik">Run Traefik</h3>
<pre><code class="lang-bash">//jika ingin melihat <span class="hljs-built_in">log</span> tidak berjalan di background
$ docker compose up
//jika ingin lgsung berjalan di background
$ docker compose up -d
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732087440285/79db5588-4d07-477d-8eec-dad7ce709337.png" alt class="image--center mx-auto" /></p>
<p>kita check di browser</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732087828139/cb4f3d74-ca1a-4621-bd2e-514afc16256e.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732087874026/a5350f15-1ba2-4ffa-9259-30c3796cf627.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732087888272/0fda6508-f91e-4d3b-b08c-0d261f4697b4.png" alt class="image--center mx-auto" /></p>
<p>thanks</p>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/traefik-as-reverse-proxy-load-balancer-locally</link><guid isPermaLink="true">https://notes.mdrdani.my.id/traefik-as-reverse-proxy-load-balancer-locally</guid><category><![CDATA[Traefik]]></category><category><![CDATA[Reverse Proxy]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Ansible Fundamental - Sesi 2]]></title><description><![CDATA[<h3 id="heading-ansible-playbook">Ansible Playbook</h3>
<p>Dalam praktek lapangan kita akan banyak menjalankan banyak module/tugas sekaligus, di sini untuk mempermudah masalah ini kita bisa menggunakan <a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_intro.html#ansible-playbooks"><strong>Ansible Playbook</strong></a> dimana kita mendefinisikan banyak module dalam satu file <strong>YAML.</strong></p>
<p>Oke selanjutnya kita coba buat file <strong>Ansible Playbook</strong> yang tugas awal ini kita menginstall Web server Nginx dan menjalankan file index.html,buatlah file dengan nama <strong>playbook-webserver.yaml.</strong></p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Playbook</span> <span class="hljs-string">setup</span> <span class="hljs-string">web</span> <span class="hljs-string">server</span>
  <span class="hljs-attr">hosts:</span> <span class="hljs-string">server_test</span>
  <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">become_method:</span> <span class="hljs-string">sudo</span>
  <span class="hljs-attr">tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Update</span> <span class="hljs-string">repository</span>
      <span class="hljs-attr">ansible.builtin.apt:</span>
        <span class="hljs-attr">update_cache:</span> <span class="hljs-literal">true</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">nginx</span>
      <span class="hljs-attr">ansible.builtin.apt:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Start</span> <span class="hljs-string">Nginx</span>
      <span class="hljs-attr">ansible.builtin.service:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">started</span>
        <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">file</span> <span class="hljs-string">html</span>
      <span class="hljs-attr">ansible.builtin.copy:</span>
        <span class="hljs-attr">src:</span> <span class="hljs-string">./web/</span>
        <span class="hljs-attr">dest:</span> <span class="hljs-string">/var/www/html/</span>
        <span class="hljs-attr">mode:</span> <span class="hljs-string">'604'</span>
</code></pre>
<p>jika sudah di buat jalankan dengan perintah</p>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-playbook</span> <span class="hljs-string">playbook-webserver.yaml</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713857683392/0408d9b4-5044-45d4-be0c-e8e4e0ab38ed.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714228503871/954a497b-5c20-4c7f-a1ca-fcf8b887de95.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-ansible-playbook-variables">Ansible Playbook Variables</h3>
<p>Terkadang kita memerlukan konfigurasi kepada hal yang sama, contoh kita membuat nama user pada **task 1,**pada <strong>task ke 2</strong> kita membuat grub dengan nama user yang sama kita dapat membuat nya sebagai <a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html">variables</a> untuk mendefinisikan sekali saja.</p>
<p><strong>playbook-webserver-vars.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Playbook</span> <span class="hljs-string">setup</span> <span class="hljs-string">web</span> <span class="hljs-string">server</span>
  <span class="hljs-attr">hosts:</span> <span class="hljs-string">server_test</span>
  <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">vars:</span> <span class="hljs-comment"># mendefinisikan variable</span>
    <span class="hljs-attr">user_app:</span> <span class="hljs-string">ansibleweb</span>
  <span class="hljs-attr">tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">nginx</span>
      <span class="hljs-attr">ansible.builtin.apt:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Buat</span> <span class="hljs-string">User</span> {{ <span class="hljs-string">user_app</span> }}
      <span class="hljs-attr">ansible.builtin.user:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
        <span class="hljs-attr">password:</span> <span class="hljs-string">belajaransible</span>
        <span class="hljs-attr">shell:</span> <span class="hljs-string">/bin/bash</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">file</span> <span class="hljs-string">html</span>
      <span class="hljs-attr">ansible.builtin.copy:</span>
        <span class="hljs-attr">src:</span> <span class="hljs-string">./web/</span>
        <span class="hljs-attr">dest:</span> <span class="hljs-string">/var/www/html/</span>
        <span class="hljs-attr">mode:</span> <span class="hljs-string">'604'</span>
        <span class="hljs-attr">owner:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
        <span class="hljs-attr">group:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-playbook</span> <span class="hljs-string">playbook-webserver-vars.yaml</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714231825315/07626647-1da9-4a61-978d-c483328bfe88.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714232462930/d884f401-5641-4b0b-bf37-cf27a1898321.png" alt class="image--center mx-auto" /></p>
<p>terlihat file <strong>index.html</strong> sudah kita rubah owner menggunakan <strong>user ansibleweb dan group ansibleweb.</strong></p>
<p>karena playbook menggunakan format <strong>YAML</strong> sebenarnya kita bisa menggunakan variable dalam bentuk <a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#boolean-variables">boolean</a>, <a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#list-variables">list</a> dan <a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#dictionary-variables">dictionary</a> jika dibutuhkan.</p>
<h3 id="heading-ansible-conditional">Ansible Conditional</h3>
<p>Terkadang pada kondisi tertentu kita menemui task yang bisa menyesuaikan keadaan yang kita tuliskan di playbook. Contoh menginstall Nginx di distro Ubuntu/debian menggunakan <strong>APT,</strong> namun jika di distro centos/redhat meggunakan <strong>YUM.</strong></p>
<p>Ansible memiliki kemampuan untuk membuat semacam "if-else" di pemrograman yang bernama <a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_conditionals.html#conditionals">Conditional</a>.</p>
<p>Sama seperti if conditionals pada umumnya kita dapat menggunakan operasi logical operator seperti <em>and, or, not</em> dan comparision operator seperti &gt;=, &lt;=,==</p>
<p><strong>playbook-webserver-when.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Playbook</span> <span class="hljs-string">setup</span> <span class="hljs-string">web</span> <span class="hljs-string">server</span>
  <span class="hljs-attr">hosts:</span> <span class="hljs-string">server_test</span>
  <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">gather_facts:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># defaultnya memang true</span>
  <span class="hljs-attr">vars:</span> <span class="hljs-comment"># mendefinisikan variable</span>
    <span class="hljs-attr">user_app:</span> <span class="hljs-string">ansibleweb</span>
  <span class="hljs-attr">tasks:</span>

    <span class="hljs-comment">## Install nginx</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">nginx</span> <span class="hljs-string">(Debian)</span>
      <span class="hljs-attr">ansible.builtin.apt:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
      <span class="hljs-attr">when:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ansible_os_family</span> <span class="hljs-string">==</span> <span class="hljs-string">"Debian"</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ansible_processor_cores</span> <span class="hljs-string">&gt;=</span> <span class="hljs-number">0.25</span> <span class="hljs-string">or</span> <span class="hljs-string">ansible_memory_mb.real.total</span> <span class="hljs-string">&gt;=</span> <span class="hljs-number">512</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">nginx</span> <span class="hljs-string">(Alpine)</span>
      <span class="hljs-attr">community.general.apk:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">nginx</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
      <span class="hljs-attr">when:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ansible_os_family</span> <span class="hljs-string">==</span> <span class="hljs-string">"Alpine"</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ansible_processor_cores</span> <span class="hljs-string">&gt;=</span> <span class="hljs-number">0.25</span> <span class="hljs-string">or</span> <span class="hljs-string">ansible_memory_mb.real.total</span> <span class="hljs-string">&gt;=</span> <span class="hljs-number">512</span>

    <span class="hljs-comment">## Buat user</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Buat</span> <span class="hljs-string">user</span> <span class="hljs-string">(Debian)</span> {{ <span class="hljs-string">user_app</span> }}
      <span class="hljs-attr">ansible.builtin.user:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
        <span class="hljs-attr">password:</span> <span class="hljs-string">belajaransible</span>
        <span class="hljs-attr">shell:</span> <span class="hljs-string">/bin/bash</span>
      <span class="hljs-attr">when:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ansible_os_family</span> <span class="hljs-string">==</span> <span class="hljs-string">"Debian"</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Buat</span> <span class="hljs-string">user</span> <span class="hljs-string">(Alpine)</span> {{ <span class="hljs-string">user_app</span> }}
      <span class="hljs-attr">ansible.builtin.user:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
        <span class="hljs-attr">password:</span> <span class="hljs-string">belajaransible</span>
        <span class="hljs-attr">shell:</span> <span class="hljs-string">/bin/sh</span>
      <span class="hljs-attr">when:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ansible_os_family</span> <span class="hljs-string">==</span> <span class="hljs-string">"Alpine"</span>

    <span class="hljs-comment">## Copy file html</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">file</span> <span class="hljs-string">html</span> <span class="hljs-string">(Debian)</span>
      <span class="hljs-attr">ansible.builtin.copy:</span>
        <span class="hljs-attr">src:</span> <span class="hljs-string">./web/</span>
        <span class="hljs-attr">dest:</span> <span class="hljs-string">/var/www/html/</span>
        <span class="hljs-attr">mode:</span> <span class="hljs-string">'604'</span>
        <span class="hljs-attr">owner:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
        <span class="hljs-attr">group:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
      <span class="hljs-attr">when:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ansible_os_family</span> <span class="hljs-string">==</span> <span class="hljs-string">"Debian"</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">file</span> <span class="hljs-string">html</span> <span class="hljs-string">(Alpine)</span>
      <span class="hljs-attr">ansible.builtin.copy:</span>
        <span class="hljs-attr">src:</span> <span class="hljs-string">./web/</span>
        <span class="hljs-attr">dest:</span> <span class="hljs-string">/usr/share/nginx/html</span>
        <span class="hljs-attr">mode:</span> <span class="hljs-string">'604'</span>
        <span class="hljs-attr">owner:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
        <span class="hljs-attr">group:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
      <span class="hljs-attr">when:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ansible_os_family</span> <span class="hljs-string">==</span> <span class="hljs-string">"Alpine"</span>
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-playbook</span> <span class="hljs-string">playbook-webserver-when.yaml</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714374231691/eb47b895-7f09-4b9f-ba8e-009c8a50ff0f.png" alt class="image--center mx-auto" /></p>
<p>Secara default saat menjalankan playbook, Ansible akan mengambil informasi dari server tujuan terelebih dahulu sebelum mejalankan task yang akan dikerjakan.</p>
<p>Informasi tersebut diambil dan di simpan dalam sebuah variable yang dinamakan <a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_vars_facts.html#ansible-facts">Facts</a>. Jika ingin mengececek <strong>Facts</strong> secara manual untuk melihat informasi bisa menggunakan perintah <em>ansible &lt;pattern/host&gt; -m ansible.builtin.setup.</em></p>
<pre><code class="lang-yaml"><span class="hljs-string">ansible</span> <span class="hljs-string">server_test</span> <span class="hljs-string">-m</span> <span class="hljs-string">ansible.builtin.setup</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714373127699/d71b2dc6-906c-42bc-b956-d72a68e8f3b4.png" alt class="image--center mx-auto" /></p>
<p>Terkadang kita hanya ingin menjalankan task task tersentu yang ada di suatu playbook. Misal kita sudah menginstall web server nginx, jadi kita hanya ingin menjalankan task copy file html untuk menghemat waktu.</p>
<p>Untuk mengatasi ini kita dapat menggunakan <a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_tags.html#tags">Tags</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714378974992/72bd4767-2f32-4000-8ca2-a5d62335e943.png" alt class="image--center mx-auto" /></p>
<p>untuk menjalankan nya kita dapat menggunakan command</p>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-playbook</span> <span class="hljs-string">playbook-webserver-when.yaml</span> <span class="hljs-string">--check</span> <span class="hljs-string">--tags</span> <span class="hljs-string">copy_file</span>
</code></pre>
<p>atau bisa skip task selain yang di tentukan</p>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-playbook</span> <span class="hljs-string">playbook-webserver-when.yaml</span> <span class="hljs-string">--check</span> <span class="hljs-string">--skip-tags</span> <span class="hljs-string">copy_file</span>
</code></pre>
<h3 id="heading-ansible-loops">Ansible Loops</h3>
<p>Pada ansible ada yang nama nya <a target="_blank" href="https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_loops.html"><strong>Loops</strong></a><strong>,</strong> yang guna nya untuk menghindari duplikasi atau redundan dari konfigurasi YAML kita.</p>
<p>Contoh kita install php8.2, php8.2-cli, dll, nah ini kita membuat 3 task untuk menginstall 3 package tersebut.</p>
<p><strong>playbook-php-loop.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Playbook</span> <span class="hljs-string">setup</span> <span class="hljs-string">PHP</span>
  <span class="hljs-attr">hosts:</span> <span class="hljs-string">server_belajar</span>
  <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">gather_facts:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># defaultnya memang true</span>
  <span class="hljs-attr">vars:</span>
    <span class="hljs-attr">taget_php_version:</span> <span class="hljs-number">8.2</span>
  <span class="hljs-attr">tasks:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Add</span> <span class="hljs-string">repository</span> <span class="hljs-string">for</span> <span class="hljs-string">PHP</span>
      <span class="hljs-attr">ansible.builtin.apt_repository:</span>
        <span class="hljs-attr">repo:</span> <span class="hljs-string">'ppa:ondrej/php'</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
      <span class="hljs-attr">tags:</span> <span class="hljs-string">prepare</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Update</span> <span class="hljs-string">repo</span>
      <span class="hljs-attr">ansible.builtin.apt:</span>
        <span class="hljs-attr">update_cache:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">tags:</span> <span class="hljs-string">prepare</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">php</span> {{ <span class="hljs-string">taget_php_version</span> }}
      <span class="hljs-attr">ansible.builtin.apt:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ item }}</span>"</span>
        <span class="hljs-attr">state:</span> <span class="hljs-string">present</span>
      <span class="hljs-attr">with_items:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-cli</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-common</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-imap</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-redis</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-xml</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-zip</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-mbstring</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-curl</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-gd</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-bcmath</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-gmp</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">php{{</span> <span class="hljs-string">taget_php_version</span> <span class="hljs-string">}}-mysqli</span>
      <span class="hljs-attr">tags:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">install</span>
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-playbook</span> <span class="hljs-string">playbook-php-loop.yaml</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714380524455/302606cf-ccdd-4131-8f9e-8fd7de735e25.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-ansible-vault">Ansible Vault</h3>
<p>Pada Playbook sebelumnya kita membuat user beserta <strong>password</strong> secara transparan, menulis informasi yang sensitif tidak di izinkan apalagi jika di production.</p>
<p>Untuk memproteksi hal yang sifatnya sensitif sebaiknya kita menggunakan <a target="_blank" href="https://docs.ansible.com/ansible/latest/vault_guide/index.html#protecting-sensitive-data-with-ansible-vault">Vault</a> di ansible. Dengan menggunakan <strong>Vault</strong> data2 yang senstif tersebut akan di enkripsi sehingga jauh lebih aman.</p>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-vault</span> <span class="hljs-string">create</span> <span class="hljs-string">secret-user.yaml</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714381100612/6570e7bd-2e1f-4177-beee-0c93cf0ab5bf.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714381089380/592b9ffa-f0f0-4cca-96ef-56be5632ccc9.png" alt class="image--center mx-auto" /></p>
<p>nanti akan terbentuk file baru dengan nama <strong>secret-user.yaml</strong> yang isinya akan angka acak seperti ini.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714381154386/6cb1631c-8f47-4029-bf7b-c571d93f6de5.png" alt class="image--center mx-auto" /></p>
<p>Setelah kita mendefinisikan varibale di encrypted file selanjutnya bagaimana kita memanggil variable tersebut di file playbook kita</p>
<p>Agar variable tersebut dapat digunakan di playbook kita perlu melakukan parsing variable tersebut dengan module <strong>ansible.builtin.include_vars.</strong></p>
<p><strong>playbook-vault.yaml</strong></p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Playbook</span> <span class="hljs-string">buat</span> <span class="hljs-string">user</span> <span class="hljs-string">baru</span>
  <span class="hljs-attr">hosts:</span> <span class="hljs-string">server_test</span>
  <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">gather_facts:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># defaultnya memang true</span>
  <span class="hljs-attr">vars:</span> <span class="hljs-comment"># mendefinisikan variable</span>
    <span class="hljs-attr">user_app:</span> <span class="hljs-string">userbaru</span>
  <span class="hljs-attr">tasks:</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Parsing</span> <span class="hljs-string">variable</span> <span class="hljs-string">dari</span> <span class="hljs-string">secret</span> <span class="hljs-string">file</span>
      <span class="hljs-attr">ansible.builtin.include_vars:</span>
        <span class="hljs-attr">file:</span> <span class="hljs-string">secret-user.yaml</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Add</span> <span class="hljs-string">new</span> <span class="hljs-string">user</span>
      <span class="hljs-attr">ansible.builtin.user:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_app }}</span>"</span>
        <span class="hljs-comment"># password: belajaransible # gak secure kita ganti pake Ansible Vault</span>

        <span class="hljs-attr">password:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ user_pass | password_hash('sha512') }}</span>"</span> <span class="hljs-comment"># ambil value dari variable lalu lakukan hash</span>
        <span class="hljs-attr">shell:</span> <span class="hljs-string">/bin/bash</span>
      <span class="hljs-attr">when:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">ansible_os_family</span> <span class="hljs-string">==</span> <span class="hljs-string">"Debian"</span>
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-playbook</span> <span class="hljs-string">playbook-vault.yaml</span> <span class="hljs-string">--ask-vault-pass</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1714381579501/56aa2081-aed1-47d1-93cb-51a3293b123b.png" alt class="image--center mx-auto" /></p>
<p>jika kita perlu mengedit isi dari file yang enkripsi bisa dengan command</p>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-vault</span> <span class="hljs-string">edit</span> <span class="hljs-string">secret-user.yaml</span>
</code></pre>
<p>atau melihat isi dari file</p>
<pre><code class="lang-yaml"><span class="hljs-string">ansible-vault</span> <span class="hljs-string">view</span> <span class="hljs-string">secret-user.yaml</span>
</code></pre>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/ansible-fundamental-sesi-2</link><guid isPermaLink="true">https://notes.mdrdani.my.id/ansible-fundamental-sesi-2</guid><category><![CDATA[ansible]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Ansible Fundamental - Sesi 1]]></title><description><![CDATA[<h3 id="heading-pengenalan-ansible">Pengenalan Ansible</h3>
<p>Berangkat dari permasalahan kenapa Ansible di ciptakan dan kita butuh memakai nya? Ketika kita konfigurasi server Linux maka kita akan melakukan remote SSH lalu melakukan task yang ingin kita konfigurasi.</p>
<p>Bagaimana kalau server linux yang kita miliki berjumlah puluhan,dengan konfigurasi yang sama? ada beberapa permasalahan yang mungkin kita temui.</p>
<ul>
<li><p>Capek / Pegel</p>
</li>
<li><p>Ada perintah command yang terlewat</p>
</li>
<li><p>Perintah yang di jalankan tidak terdokumentasi dengan baik.</p>
</li>
</ul>
<p>Ansible merupakan tools untuk mengotomatisasi tugas-tugas seperti membuat dan mengkonfigurasi resource linux, windows, database dll.</p>
<p><img src="https://res.cloudinary.com/lwgatsby/f_auto/www/uploads/2023/05/image01-How-Ansible-Works-Rich-Media-min.jpg" alt="What is Ansible and What Does it Do? | Liquid Web" /></p>
<p>Kelebihan ansible adalah sifatnya <em>agent-less,</em> maksudnya itu tidak perlu menginsal <em>agent</em> di setiap server yang ingin di otomasti. cukup install ansible di laptop/komputer admin saja.</p>
<p>Cara kerja Ansible yaitu tugas tugas yang akan di jalankan dibungkus dalam sebuah <strong>module,</strong> setiap <strong>module</strong> mempunyai tugas kecil secara spesifik seperti <strong>copy file, install web server,</strong> atau <strong>create user.</strong></p>
<p>Di Ansible juga ada nama nya konsep <strong>Inventory,</strong> yang dimana kita dapat mendefinisikan IP/Domain dalam satu file yang nanti nya Ansible akan membaca file <strong>Inventory</strong> tersebut sebelum membaca <strong>module.</strong></p>
<h3 id="heading-menginstall-ansible">Menginstall Ansible</h3>
<p>Ansible berjalan diatas bahasa pemrograman Python. Khusus sistem operasi windows, Ansible menyarankan menggunakan <a target="_blank" href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#control-node-requirements">WSL</a>.</p>
<p>Untuk menginstall Ansible cukup ikuti dokumentasi <a target="_blank" href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-and-upgrading-ansible-with-pip">ini</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713540977860/c6edb346-7707-4e71-b72f-4aa31a869946.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-prepare-linux-server">Prepare Linux Server</h3>
<p>untuk Server kita coba create melalui <strong>Terraform</strong> di Google Cloud Platform.</p>
<p>Jika sudah terbuat selanjutnya lakukan <strong>SSH</strong> ke server yang sudah dibuat.</p>
<p>Check apakah server sudah terinstall <strong>Python</strong></p>
<pre><code class="lang-bash">$ python3 --version
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713627856386/5e6f8c93-6167-4c98-bf90-9c44a97303c3.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-ansible-inventory">Ansible Inventory</h3>
<p>Seperti yang sudah di bahas sebelumnya ansible mengetahui server mana yang akan kita manage melalui file <a target="_blank" href="https://docs.ansible.com/ansible/latest/plugins/inventory.html#enabling-inventory-plugins"><strong>Inventory</strong></a><strong>.</strong></p>
<p>Ansible mendukung banyak sekali format penulisan file <strong>Inventory,</strong> yaitu format <strong>INI</strong> dan <strong>YAML.</strong></p>
<p>Selanjutnya kita coba buat file dengan nama **Inventory,**dan isikan IP Public dari server yang akan di manage</p>
<p><strong>inventory</strong></p>
<pre><code class="lang-bash">//ip server dan private key
34.128.82.28 
ansible_user=muhamaddani3004 
ansible_ssh_private_key_file=./ssh-key/cehamot
</code></pre>
<p>lalu kita coba ping untuk memastikan koneksi ke server.</p>
<pre><code class="lang-bash">$ ansible 34.128.82.28 -i ./inventory -m ping
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713631552544/1a6de9f7-36de-4cb2-b68f-5e004f48731a.png" alt class="image--center mx-auto" /></p>
<p>response yang di dapat adalah <strong>Success</strong>,berarti kita telah berhasil terhubung ke server melalui <strong>ansible.</strong></p>
<p>Biasanya satu file inventory memiliki list ip/domain server, agar file inventory terlihat rapih kita dapat kelompokan dalam sebuah <a target="_blank" href="https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html#inventory-basics-formats-hosts-and-groups"><strong>group</strong></a><strong>.</strong></p>
<p>Bahkan parameter yang sudah kita buat sebelumnya "sangat tidak clean" didefinisikan berulang-ulang, bagaimana kalau kita buat <a target="_blank" href="https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html#assigning-a-variable-to-many-machines-group-variables"><strong>group variables</strong></a><strong>.</strong></p>
<pre><code class="lang-bash">[server_test]
34.128.82.28

[server_test:vars]
ansible_user=muhamaddani3004
ansible_ssh_private_key_file=./ssh-key/cehamot
</code></pre>
<p>dan command ping tetap</p>
<pre><code class="lang-bash">$ ansible 34.128.82.28 -i ./inventory -m ping
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713632572634/5530012a-aa32-4e52-bca5-e7e5997972fd.png" alt class="image--center mx-auto" /></p>
<p>command sebelumnya menggunakan pattern <strong><em>ansible &lt;pattern&gt; &lt;options&gt;</em></strong></p>
<p><strong>-m atau -i</strong> itu adalah options</p>
<p><a target="_blank" href="https://docs.ansible.com/ansible/latest/inventory_guide/intro_patterns.html#patterns-targeting-hosts-and-groups"><strong><em>Pattern</em></strong></a> merupakan target host/group yang diinginkan.</p>
<p>ada beberapa cara penulisan pattern yang dapat di lihat <a target="_blank" href="https://docs.ansible.com/ansible/latest/inventory_guide/intro_patterns.html#common-patterns">disini</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713632806244/fd2bdc25-1e20-494b-bdf7-90bc13a06d64.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-ansible-config">Ansible Config</h3>
<p>Ansible config digunakan untuk mengkonfigurasi behavior ansible saat kita menjalankan perintah-perintah ansible.</p>
<p>Ansible config di tulis dengan format <strong>INI</strong> dengan nama ansible.cfg,dan ansible config ini memiliki hirarki dalam pembacaan nya. <a target="_blank" href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html#the-configuration-file"><strong>Disini</strong></a></p>
<p>Penulisan konfigurasi di ansible config di kelompokan dalam sebuah <strong>Section.</strong></p>
<p>jadi contoh nya seperti ini,</p>
<pre><code class="lang-bash">$ ansible server_test -i inventory -m ping
</code></pre>
<p>pada command sebelumnya kita selalu mendefinisikan <strong>option -i &lt;file-inventory&gt;</strong> saat memanggil file inventory, dengan menggunakan <strong>ansible.cfg</strong> kita bisa menyederhanakan dengan menambahkan <strong>key inventory</strong> di section defaults.</p>
<pre><code class="lang-bash">[default]
inventory=./inventory
host_key_checking=False
</code></pre>
<p>satu hal lagi yang cukup penting menambahkan line <strong>host_key_checking</strong> yang fungsinya untuk menghindari host key checking saat pertama kali konek ke server baru.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713836417592/41ae49c6-9d6b-493d-bea1-020ed657402f.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-ansible-module">Ansible Module</h3>
<p>Pada materi sebelumnya kita selalu menggunakan option <strong><em>-m ping,</em></strong> option yang kita gunakan tersebut nama nya <strong>module.</strong></p>
<p><a target="_blank" href="https://docs.ansible.com/ansible/latest/module_plugin_guide/modules_intro.html#introduction-to-modules"><strong>Module</strong></a> merupakan unit code untuk menjalankan suatu tugas tertentu.</p>
<p>Ansible punya banyak sekali module yang dapat kita gunakan <a target="_blank" href="https://docs.ansible.com/ansible/latest/collections/index_module.html#index-of-all-modules">dokumentasi</a>.</p>
<p>Jika module di eksekusi langsung di sebut <a target="_blank" href="https://docs.ansible.com/ansible/latest/command_guide/intro_adhoc.html#introduction-to-ad-hoc-commands"><strong><em>adhoc command</em></strong></a><strong><em>.</em></strong></p>
<blockquote>
<ul>
<li><p><a target="_blank" href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/add_host_module.html#ansible-collections-ansible-builtin-add-host-module">ansible.builtin.add_host</a>  Add a host (and alternatively a group) to the ansible-playbook in-memory inventory</p>
</li>
<li><p><a target="_blank" href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html#ansible-collections-ansible-builtin-apt-module">ansible.builtin.apt</a>  Manages apt-packages</p>
</li>
<li><p><a target="_blank" href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_key_module.html#ansible-collections-ansible-builtin-apt-key-module">ansible.builtin.apt_key</a>  Add or remove an apt key</p>
</li>
<li><p><a target="_blank" href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_repository_module.html#ansible-collections-ansible-builtin-apt-repository-module">ansible.builtin.apt_repository</a>  Add and remove APT repositories</p>
</li>
<li><p><a target="_blank" href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/assemble_module.html#ansible-collections-ansible-builtin-assemble-module">ansible.builtin.assemble</a>  Assemble configuration files from fragments</p>
</li>
<li><p><a target="_blank" href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/assert_module.html#ansible-collections-ansible-builtin-assert-module">ansible.builtin.assert</a>  Asserts given expressions are true</p>
</li>
</ul>
</blockquote>
<p>Setiap <strong>module</strong> dapat memiliki argument untuk kebutuhan <strong>module</strong> itu sendiri, argument ini juga sering di sebut sebagai <strong>parameter.</strong></p>
<p>salah satu contoh seperti ini:</p>
<pre><code class="lang-bash">$ ansible server_test -m <span class="hljs-built_in">command</span> <span class="hljs-string">"date"</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713841890039/4f6fb069-8bcd-47a1-93cc-1ab257dc0c5d.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-bash">$ ansible server_test -m copy -a <span class="hljs-string">"src=./file.txt dest=/tmp/"</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713842034740/01ef1f80-a2eb-4e15-9da4-0642923a8bf9.png" alt class="image--center mx-auto" /></p>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/ansible-fundamental-sesi-1</link><guid isPermaLink="true">https://notes.mdrdani.my.id/ansible-fundamental-sesi-1</guid><category><![CDATA[ansible]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Terraform Fundamental - Sesi 2 (GCP)]]></title><description><![CDATA[<p>kita lanjutkan ....</p>
<h3 id="heading-input-variables">Input Variables</h3>
<p>untuk mendefinisikan value dari konfigurasi secara terpisah kita memerlukan <strong>Input Variables.</strong> Default dari <strong>Variables</strong> berupa string, namun ada jenis lainnya seperti list, map, dan boolean.</p>
<p>create file dengan nama <strong>variables.tf</strong></p>
<pre><code class="lang-bash">variable <span class="hljs-string">"compute_zone"</span> {
  <span class="hljs-built_in">type</span>    = string
  default = <span class="hljs-string">"asia-southeast2-a"</span>
}
</code></pre>
<p>pada file <strong>main.tf</strong> di rubah pada bagian <strong>Zone</strong></p>
<pre><code class="lang-bash">resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Satu"</span> {
  name                      = <span class="hljs-string">"vm-satu"</span>
  machine_type              = <span class="hljs-string">"e2-micro"</span>
  zone                      = var.compute_zone
  allow_stopping_for_update = <span class="hljs-literal">true</span>

......
}
</code></pre>
<p>Selain variables berupa string,kita akan coba juga menggunakan variable berupa <strong>list</strong> ini mirip dengan array. Value pada <strong>list</strong> variable memiliki index dan index di mulai dari angka 0.</p>
<p>Contoh di sini jika kita ingin mengumpulkan beberapa zone yang ada di GCP.</p>
<p>pada file <strong>variables.tf</strong> kita bisa rubah seperti ini.</p>
<pre><code class="lang-bash">variable <span class="hljs-string">"compute_zone"</span> {
  <span class="hljs-built_in">type</span> = list(any)
  default = [
    <span class="hljs-string">"asia-southeast2-a"</span>,
    <span class="hljs-string">"asia-southeast2-b"</span>,
  <span class="hljs-string">"asia-southeast2-c"</span>]
}
</code></pre>
<p>dan digunakan di <strong>main.tf</strong> akan seperti ini,</p>
<pre><code class="lang-bash">resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Satu"</span> {
  name                      = <span class="hljs-string">"vm-satu"</span>
  machine_type              = <span class="hljs-string">"e2-micro"</span>
  zone                      = var.compute_zone[0]
  allow_stopping_for_update = <span class="hljs-literal">true</span>

.....
}
</code></pre>
<p>Selain variables berupa list,kita akan coba juga menggunakan variable berupa <strong>map</strong> yang merupakan collection dari key dan string. untuk memanggil valuenya, kita dapat menggunakan key yang ada.</p>
<p>masih menggunakan contoh zone pada bagian <strong>variables.tf</strong> di rubah menjadi seperti ini</p>
<pre><code class="lang-bash">variable <span class="hljs-string">"compute_zone"</span> {
  <span class="hljs-built_in">type</span> = map(any)
  default = {
    <span class="hljs-string">"zone-1"</span> = <span class="hljs-string">"asia-southeast2-a"</span>
    <span class="hljs-string">"zone-2"</span> = <span class="hljs-string">"asia-southeast2-b"</span>
    <span class="hljs-string">"zone-3"</span> = <span class="hljs-string">"asia-southeast2-c"</span>
  }
}
</code></pre>
<p>pada bagian <strong>main.tf</strong> kita menggunakan nya seperti ini</p>
<pre><code class="lang-bash">resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Satu"</span> {
  name                      = <span class="hljs-string">"vm-satu"</span>
  machine_type              = <span class="hljs-string">"e2-micro"</span>
  zone                      = var.compute_zone[<span class="hljs-string">"zone-1"</span>]
  allow_stopping_for_update = <span class="hljs-literal">true</span>

.....
}
</code></pre>
<p>Selain variables berupa map,kita akan coba juga menggunakan variable berupa <strong>boolean</strong> yang kita definisikan true/false value.</p>
<p>contoh di sini pada file <strong>variables.tf</strong></p>
<pre><code class="lang-bash">variable <span class="hljs-string">"allow_stop_vm"</span> {
  <span class="hljs-built_in">type</span>    = bool
  default = <span class="hljs-literal">false</span>
}
</code></pre>
<p>dan cara menggunakan nya di <strong>main.tf</strong> seperti ini</p>
<pre><code class="lang-bash">resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Satu"</span> {
  name                      = <span class="hljs-string">"vm-satu"</span>
  machine_type              = <span class="hljs-string">"e2-micro"</span>
  zone                      = var.compute_zone[<span class="hljs-string">"zone-1"</span>]
  allow_stopping_for_update = var.allow_stop_vm

.....
}
</code></pre>
<blockquote>
<p>Tips: untuk melakukan override value variable pada saat <strong>terraform plan &amp; apply</strong> , kita dapat passing options -var</p>
<p>ex: terraform plan -var allow_stop_vm=false</p>
</blockquote>
<p>Jika ingin memasukan <strong>ssh-key</strong> langsung ke instance saat instance tersebut di create pertama kali kita bisa masukan <strong>ssh-key</strong> ke file <strong>variables.tf</strong></p>
<p>contoh di sini pada file <strong>variables.tf</strong></p>
<pre><code class="lang-bash">variable <span class="hljs-string">"ssh_keys"</span> {
  <span class="hljs-built_in">type</span>    = string
  default = <span class="hljs-string">"muhamaddani3004:ssh-rsa fdsfsf....."</span>
}
</code></pre>
<p>dan cara menggunakan nya di <strong>main.tf</strong> seperti ini</p>
<pre><code class="lang-bash">resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Satu"</span> {
  name                      = <span class="hljs-string">"vm-satu"</span>
  machine_type              = <span class="hljs-string">"e2-micro"</span>
  zone                      = var.compute_zone[<span class="hljs-string">"zone-1"</span>]
  allow_stopping_for_update = var.allow_stop_vm

  metadata = {
    ssh-keys = var.ssh_keys
  }
.....
}
</code></pre>
<p>jika nanti kita akan lakukan <strong>ssh</strong> kita lgsung sudah bisa masuk ke instance.</p>
<h3 id="heading-output-value">Output Value</h3>
<p>kita dapat menampilkan suatu argument dari sebuah Resource dari command line, kurang lebih contoh seperti menampilkan VM kita dapat IP Publik berapa agar kita tidak perlu lagi check console.</p>
<p>kita tempatkan output value di file <strong>outputs.tf</strong></p>
<pre><code class="lang-bash">output <span class="hljs-string">"public_ip_vm_satu"</span> {
  value       = google_compute_instance.VM-Satu.network_interface[0].access_config[0].nat_ip
  description = <span class="hljs-string">"IP Public VM Satu"</span>
}

output <span class="hljs-string">"public_ip_vm_dua"</span> {
  value       = google_compute_instance.VM-Dua.network_interface[0].access_config[0].nat_ip
  description = <span class="hljs-string">"IP Public VM Dua"</span>
}
</code></pre>
<p>dan hasil output nya di dapat.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713327826692/0e244aba-34f1-4752-9d75-23a5b18ed1ae.png" alt class="image--center mx-auto" /></p>
<p>atau bisa dengan command</p>
<pre><code class="lang-bash">$ terraform output public_ip_vm_dua
$ terraform output public_ip_vm_satu
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713327895924/92ab76b8-4fba-4e8e-ad2c-f9ef9e269472.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-backend">Backend</h3>
<p>Sebelumnya kita sudah bahas apa itu <a target="_blank" href="https://developer.hashicorp.com/terraform/language/state">State File</a>. file tersebut secara default di letakan di folder konfigurasi Terraform dengan nama <strong>terraform.tfstate.</strong></p>
<p>Dalam best practice file tersebut tidak di push ke VCS seperti git. Namun repot kalau jika file state tersebut hilang ya salah satunya broken deployment.</p>
<p>Pada umumnya kita letakan file tersebut di Object Storage seperti Google Cloud Storage atau S3 AWS.</p>
<p>Best practicenya kita tulis konfigurasi backend di file <strong>backend.tf</strong></p>
<pre><code class="lang-bash">terraform {
  backend <span class="hljs-string">"gcs"</span> {
    bucket = <span class="hljs-string">"mdrdani-tfstate"</span>
  }
}
</code></pre>
<p>sebelumnya kita create terlbih dahulu Object Storage nya, masuk console Google Cloud.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713329086106/926662e6-539c-4aed-906b-0ee5dff12fff.png" alt class="image--center mx-auto" /></p>
<p>karena konfigurasi <strong>Backend</strong> ini letaknya di **Terraform Blocks,**maka kita harus menjalankan ulang perintah <strong>terraform init.</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713329246004/44329e11-386d-4dd6-824d-180e8ab592c0.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713329280150/57630540-fa6d-444f-a13a-073bde3556ba.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-terraform-state-command">Terraform State Command</h3>
<p>State Command digunakan untuk melakukan manajemen state yang suda ada,yang umumnya di gunakan untuk melihat list, detail atau menghapus state resource.</p>
<pre><code class="lang-bash">$ terraform state list

$ terraform state show {state_name}

$ terraform state rm {state_name}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713329488325/10069136-05a1-4acd-837b-6d934047f993.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713329819042/9050327e-07ca-4713-879e-e2f3aa0ef00c.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-terraform-destroy-command">Terraform Destroy Command</h3>
<p>jika resource tidak terpakai lagi seharusnya kan kita hapus agar tidak menambah beban cost yang dikeluarkan. Untuk menghapus resource dapat menggunakan destroy command.</p>
<pre><code class="lang-bash">//menghapus spesifik resource
$ terraform destroy -target &lt;<span class="hljs-built_in">type</span>&gt;.&lt;name&gt;

//menghapus semua resource
$ terraform destroy
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713330007182/7385c23e-dce2-49c2-9896-a048ce975ec9.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713330137915/37c7a1da-e092-45e1-a127-3117a85817f2.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Tips: sebelumnya kita dapat menghapus secara spesifik resource mana yang ingin di hapus, ini juga berlaku untuk terraform plan &amp; apply</p>
</blockquote>
<pre><code class="lang-bash">//plan spesifik resource
$ terraform plan -target &lt;<span class="hljs-built_in">type</span>&gt;.&lt;name&gt;

// apply spesifik resource
$ terraform apply -target &lt;<span class="hljs-built_in">type</span>&gt;.&lt;name&gt;
</code></pre>
<h3 id="heading-resource-lifecycle">Resource Lifecycle</h3>
<p>Digunakan untuk mengontrol behavior dari sebuah resource saat dibuat,di update atau dihapus. Contoh kita bisa mencegah suatu resource terhapus secara tidak sengaja.</p>
<pre><code class="lang-bash">resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Dua"</span> {
  name                      = <span class="hljs-string">"vm-dua"</span>
  machine_type              = <span class="hljs-string">"e2-micro"</span>
  zone                      = var.compute_zone[<span class="hljs-string">"zone-1"</span>]
  allow_stopping_for_update = var.allow_stop_vm

 lifecycle {
    prevent_destroy = <span class="hljs-literal">true</span>
  }
  ....
}
</code></pre>
<p>Oke thanks yang sudah membaca materi nya.</p>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/terraform-fundamental-sesi-2-gcp</link><guid isPermaLink="true">https://notes.mdrdani.my.id/terraform-fundamental-sesi-2-gcp</guid><category><![CDATA[Terraform]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Terraform Fundamental - Sesi 1 (GCP)]]></title><description><![CDATA[<p>Pada kalangan DevOps / SRE tidak asing lagi dengan tools yang satu ini Terraform merupakan tools Infrastructure as Code untuk mempercepat dalam mengelola cloud seperti AWS, GCP, atau Azure namun tidak hanya cloud kita juga dapat menggunakan terraform untuk memanage Virtualisasi seperti Proxmox ataupun Container Docker. Terraform adalah tool open source yang dibuat oleh Hashicorp. Dengan terraform kita dapat membuat, mengubah, menghapus dan menduplikasikan infrastructure.</p>
<h3 id="heading-core-terraform-workflow"><strong>Core Terraform Workflow</strong></h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674393759006/c98f1aa3-d68a-4870-9f3d-5fea8368c03d.avif" alt class="image--center mx-auto" /></p>
<p><strong>Write</strong> : menulis terraform configuration dan initialize</p>
<p>mendefinisikan infrastructure pada file konfigurasi</p>
<p><strong>Plan :</strong> Preview</p>
<p>Sebelum dieksekusi ke infrastuk kita bisa melakukan preview terlebih dahulu apa yang akan di buat, ubah atau hapus sehingga bisa lebih aman saat di eksekusi ke infrastuktur.</p>
<p><strong>Apply :</strong> Execution</p>
<p>Setelah tahap plan (preview) maka kita bisa eksekusi ke infrastucture yang sebenarnya.</p>
<h3 id="heading-terraform-command"><strong>Terraform Command</strong></h3>
<p>cukup banyak command yang dimiliki oleh terraform yang bisa digunakan, namun untuk awal kita hanya cukup mengetahui beberapa command yang biasa di pakai. Berikut ini beberapa command dasar :</p>
<ul>
<li><p><em>terraform init =&gt; inisialisasi</em></p>
</li>
<li><p><em>terraform fmt=&gt; merapihkan konfigurasi code file</em></p>
</li>
<li><p><em>terraform validate =&gt; check apakah konfigurasi code sudah sesuai</em></p>
</li>
<li><p><em>terraform plan =&gt; check apa saja yang akan di rubah</em></p>
</li>
<li><p><em>terraform apply =&gt; menerapkan perubahan</em></p>
</li>
<li><p><em>terraform destroy=&gt; menghapus</em></p>
</li>
</ul>
<h3 id="heading-install-terraform"><strong>Install Terraform</strong></h3>
<p>Download Terafform di situs ini <a target="_blank" href="https://developer.hashicorp.com/terraform/downloads"><strong>https://developer.hashicorp.com/terraform/downloads</strong></a>. Pilih operating system menggunakan apa, di sini saya menggunakan windows.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674393925368/5e29778c-76b2-465e-86b7-e977fdc404f0.jpeg?auto=compress,format&amp;format=webp" alt /></p>
<p>Buka file zip nya copy <strong>terraform.exe</strong> ke drive C: dengan membuat folder terraform.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674393944477/066779bd-3180-47b7-98ff-2878d6d8dd8c.jpeg?auto=compress,format&amp;format=webp" alt /></p>
<p>Update system global path pada windows di bagian <strong>Control Panel -&gt; System -&gt; System Settings-&gt; Environment Variable</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674393965767/aed79b41-12bb-4110-b4c5-760a01e54925.jpeg?auto=compress,format&amp;format=webp" alt /></p>
<p><strong>Oke Save,</strong> dan test buka CMD kembali.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674393994650/dd114a92-48d0-4be6-a132-340d548f5c65.jpeg?auto=compress,format&amp;format=webp" alt /></p>
<p>Sukses terraform sudah terinstall di windows.</p>
<h3 id="heading-setup-gcloud-cli">Setup gcloud CLI</h3>
<p>Rekomendasi jika menggunakan PC/Laptop pribadi adalah dengan menggunakan <strong>gcloud.</strong> Kita akan menggunakan <strong>Application Default Credentials (ADC)</strong> dari google. install dahulu gcloud cli dari link ini <a target="_blank" href="https://cloud.google.com/sdk/docs/install">https://cloud.google.com/sdk/docs/install</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713192670055/a4a424eb-1f49-49d3-a0d2-0edf00f3396e.png" alt class="image--center mx-auto" /></p>
<p>biasa nya instalasi memakan waktu sekitar 5 menitan.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># gcloud auth login</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713193612315/7f81c637-9461-4dc9-9da2-203ab8addb2f.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713193663026/c07d87fe-2e74-42cc-a7e1-344ad1982e05.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713193691025/36d21ed7-60f7-48ec-8757-c99f234fed94.png" alt class="image--center mx-auto" /></p>
<p>sukses authentikasi menggunakan account google cloud. selanjutnya login ADC agar terraform dapat berkomunikasi dengan Google Cloud.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># gcloud auth application-default login</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713194335434/1a68e8b6-57c6-4857-b66a-ba892255ecb2.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-terraform-provider">Terraform Provider</h3>
<p>Terraform provider digunakan sebagai penjembatan antara Terraform dengan remote system (Cloud / On-Premise). Didalam <a target="_blank" href="https://developer.hashicorp.com/terraform/language/settings">Terraform Block</a> kita harus menentukan provider yang akan digunakan. Minimal kita definisikan source &amp; version yang kita gunakan.</p>
<p>Dalam Visual studio code, create file dengan nama <strong>main.tf</strong></p>
<pre><code class="lang-bash">terraform {
  required_providers {
    google = {
      <span class="hljs-built_in">source</span> = <span class="hljs-string">"hashicorp/google"</span>
      version = <span class="hljs-string">"5.24.0"</span>
    }
  }
}
</code></pre>
<p>setelah itu pada bagian provider block kita dapat mendefinisikan konfigurasi dari provider namun ini optional di biarkan blank juga tidak apa, contoh</p>
<pre><code class="lang-bash">...
provider <span class="hljs-string">"google"</span> {
  <span class="hljs-comment"># Configuration options</span>
  project = <span class="hljs-string">"development-m-dani"</span>
  region = <span class="hljs-string">"asia-southeast2"</span> //jakarta
}
</code></pre>
<blockquote>
<p><em>tips :</em></p>
<ul>
<li><p><em>jika ingin merapihkan reformat konfigurasi file main.tf cukup ketikan perintah<strong>**terraform fmt.</strong></em></p>
</li>
<li><p>jika ingin melakukan pengecekan konfigurasi dapat memvalidasi menggunakan perintah <strong>terraform validate.</strong></p>
</li>
<li><p>jika pertama kali mendefinisikan Terraform Block atau ada perubahan gunakan perintah <strong>terraform init.</strong></p>
</li>
</ul>
</blockquote>
<h3 id="heading-resources-blocks">Resources Blocks</h3>
<p>Resource blocks merupakan tempat kita mendefinisikan object dari infrastruktur. Contoh kita akan membuat VM atau istilah nya di GCP yaitu Google Compute Instance. <a target="_blank" href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance">Dokumentasi</a>.</p>
<p>Tambahkan di <strong>main.tf</strong></p>
<pre><code class="lang-bash">...
resource <span class="hljs-string">"google_service_account"</span> <span class="hljs-string">"my_service_account"</span> {
  account_id   = <span class="hljs-string">"mdrdani-sa"</span>
  display_name = <span class="hljs-string">"mdrdani Service Account"</span>
}

resource <span class="hljs-string">"google_compute_firewall"</span> <span class="hljs-string">"http"</span> {
  name    = <span class="hljs-string">"allow-http"</span>
  network = <span class="hljs-string">"default"</span>

  allow {
    protocol = <span class="hljs-string">"tcp"</span>
    ports    = [<span class="hljs-string">"80"</span>]
  }

  source_ranges = [<span class="hljs-string">"0.0.0.0/0"</span>]
}

resource <span class="hljs-string">"google_compute_firewall"</span> <span class="hljs-string">"https"</span> {
  name    = <span class="hljs-string">"allow-https"</span>
  network = <span class="hljs-string">"default"</span>

  allow {
    protocol = <span class="hljs-string">"tcp"</span>
    ports    = [<span class="hljs-string">"443"</span>]
  }

  source_ranges = [<span class="hljs-string">"0.0.0.0/0"</span>]
}

resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Satu"</span> {
  name         = <span class="hljs-string">"vm-satu"</span>
  machine_type = <span class="hljs-string">"e2-micro"</span>
  zone         = <span class="hljs-string">"asia-southeast2-a"</span> //jakarta
  tags         = [<span class="hljs-string">"http-server"</span>, <span class="hljs-string">"https-server"</span>]

  boot_disk {
    initialize_params {
      image = <span class="hljs-string">"debian-cloud/debian-11"</span>
    }
  }

  network_interface {
    network = <span class="hljs-string">"default"</span>

    access_config {
      // Ephemeral public IP
    }
  }

  <span class="hljs-comment">#   metadata_startup_script = "echo hi &gt; /test.txt"</span>

  service_account {
    <span class="hljs-comment"># Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles.</span>
    <span class="hljs-comment"># email  = google_service_account.default.email</span>
    scopes = [<span class="hljs-string">"cloud-platform"</span>]
  }
}
</code></pre>
<p>jika sudah kita coba ketikan perintah <strong>terrafom plan</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713199694457/93557fd9-4ace-40da-802d-adc184bde22b.png" alt class="image--center mx-auto" /></p>
<p>terlihat di bagian <strong>Plan</strong> ada 1 to add yang artinya kita akan membuat 1 VM. Jika sudah sesuai maka selanjutnya kita ketikan perintah <strong>terraform apply.</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713199873340/c7184eb8-75af-4af6-8590-92c6af3e272f.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713199937726/71ce61c8-4df0-4596-9398-5f5b1398df46.png" alt class="image--center mx-auto" /></p>
<p><strong>apply complete !!</strong> kita sudah sukses create VM Google CLoud lewat Terraform.</p>
<blockquote>
<p>tips: jika kita auto melewati pertanyaan yes, maka bisa menggunakan perintah <strong>terraform apply -auto-approve</strong></p>
</blockquote>
<h3 id="heading-data-sources">Data Sources</h3>
<p>Untuk mendefinisikan informasi yang nanti nya dapat dipanggil dari Blocks lain kita gunakan <strong>Data Sources</strong>, biasa nya berdampingan dengan <strong>Resources Blocks.</strong> Contoh di sini kita definisikan image booting untuk VM. <a target="_blank" href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_image">Dokumentasi</a></p>
<p>tambahkan di <strong>main.tf</strong></p>
<pre><code class="lang-bash">....

data <span class="hljs-string">"google_compute_image"</span> <span class="hljs-string">"my_image"</span> {
  family  = <span class="hljs-string">"debian-11"</span>
  project = <span class="hljs-string">"debian-cloud"</span>
}

resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Satu"</span> {
  .....

  boot_disk {
    initialize_params {
      image = data.google_compute_image.my_image.self_link
    }
  }

  .....
  }

...
</code></pre>
<p>di sini ada kata <strong>self_link</strong> yang artinya merupakan link referensi unik suatu resource.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713280901921/38f2523b-d22e-4575-af95-06822ad6398f.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Tips: untuk mendapatkan list public image Google Cloud kita bisa mengetikan perintah <strong>gcloud compute images list</strong></p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713281628070/ec354125-39ce-4b25-9234-087a3ecdf297.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-accessing-resource-attributes">Accessing Resource Attributes</h3>
<p>kita terkadang akan membutuhkan memanggil sebuah attribute suatu resource dari tempat lain, untuk memanggil nya kita dapat menggunakan format <strong>&lt;TYPE&gt;.&lt;NAME&gt;.&lt;ATTRIBUTE&gt;.</strong></p>
<p><a target="_blank" href="https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/google_service_account"><strong>Dokumentasi</strong></a><strong>1</strong></p>
<p><a target="_blank" href="https://cloud.google.com/compute/docs/access/service-accounts"><strong>Dokumentasi2</strong></a></p>
<p>contoh kita akan membuat <strong>Service Account GCP</strong> dan menggunakan nya di VM yang telah kita buat.</p>
<p>tambahkan pada bagian <strong>main.tf</strong></p>
<pre><code class="lang-bash">....

resource <span class="hljs-string">"google_service_account"</span> <span class="hljs-string">"service_account"</span> {
  account_id   = <span class="hljs-string">"service-account-id"</span>
  display_name = <span class="hljs-string">"Service Account"</span>
}

resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Satu"</span> {
....
service_account {
    <span class="hljs-comment"># Google recommends custom service accounts that have cloud-platform scope and permissions granted via IAM Roles.</span>
    email  = google_service_account.my_service_account.email
    scopes = [<span class="hljs-string">"cloud-platform"</span>]
  }
....
}
...
</code></pre>
<p>jika terjadi error VM tidak dapat restart pada saat update <strong>Service Account</strong> kita dapat menambahkan <strong>allow_stopping_for_update = true</strong></p>
<pre><code class="lang-bash">....

resource <span class="hljs-string">"google_compute_instance"</span> <span class="hljs-string">"VM-Satu"</span> {
    name                      = <span class="hljs-string">"vm-dua"</span>
    machine_type              = <span class="hljs-string">"e2-micro"</span>
    zone                      = <span class="hljs-string">"asia-southeast2-a"</span> //jakarta
    allow_stopping_for_update = <span class="hljs-literal">true</span>
....
}

...
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713283547688/254db535-2174-4bca-85c1-eb940817d6d4.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713283712999/f8a949b6-796b-477a-84e7-a08e86a68fb9.png" alt class="image--center mx-auto" /></p>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/terraform-fundamental-sesi-1-gcp</link><guid isPermaLink="true">https://notes.mdrdani.my.id/terraform-fundamental-sesi-1-gcp</guid><category><![CDATA[Terraform]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Containerize your #nodejs application]]></title><description><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693037937586/de027d38-0524-468a-9272-35efdbed39f6.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-set-the-base-image">Set the base image</h3>
<blockquote>
<p>FROM node:20-alpine</p>
</blockquote>
<p>Use the official Node.js container image in 𝘢𝘭𝘱𝘪𝘯𝘦 variant</p>
<p>Advantages of using minimal base images:</p>
<p> reduced attack surface thanks to fewer unnecessary packages and security vulnerabilities,<br /> improved performance thanks to efficient disk space, memory and network utilization.</p>
<h3 id="heading-set-the-working-directory">Set the working directory</h3>
<blockquote>
<p>WORKDIR /app</p>
</blockquote>
<p>Set the working directory which defines the target location inside the container for all the subsequent instructions.</p>
<h3 id="heading-copy-and-install-depedencies">Copy and install depedencies</h3>
<blockquote>
<p>COPY package*.json /app</p>
<p>RUN npm install</p>
</blockquote>
<p>Use 𝘯𝘱𝘮 to install dependencies. By separating the dependencies from application source code you can take advantage of container layer caching. As long as you do not change the versions of libraries used by your application, the cached version of previously built layers will be reused, which will significantly shorten the build time.</p>
<h3 id="heading-copy-the-application-code">Copy the application code</h3>
<blockquote>
<p>COPY --chown=node:node app.js /app</p>
</blockquote>
<p>Copy the source code of your application to the working directory configured in the second step.</p>
<h3 id="heading-run-as-non-privileged-user">Run as non-privileged user</h3>
<blockquote>
<p>USER node</p>
</blockquote>
<p>Ensure that processes running in your container will be executed in non-privileged mode. Since a non-privileged user called 𝘯𝘰𝘥𝘦 is already registered in the base Node.js image, just use it.</p>
<h3 id="heading-set-the-entry-command">Set the entry command</h3>
<blockquote>
<p>CMD ["node", "app.js"]</p>
</blockquote>
<p>Finally, specify which application should be executed when the container starts.</p>
<p>complete Dockerfile</p>
<pre><code class="lang-bash">FROM node:20-alpine

WORKDIR /app

COPY package*.json /app
RUN npm install

COPY --chown=node:node app.js /app

USER node

CMD [<span class="hljs-string">"node"</span>, <span class="hljs-string">"app.js"</span>]
</code></pre>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/containerize-your-nodejs-application</link><guid isPermaLink="true">https://notes.mdrdani.my.id/containerize-your-nodejs-application</guid><category><![CDATA[containers]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item><item><title><![CDATA[Containerize your #go application]]></title><description><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1693036899789/50672595-82e3-4cc5-a3a1-bb31fadd1873.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-set-the-base-image">Set the base image</h3>
<blockquote>
<p>FROM golang:1.21</p>
</blockquote>
<p>use the official Go Docker image.</p>
<h3 id="heading-set-the-working-directory">Set the working directory</h3>
<blockquote>
<p><em>WORKDIR /app</em></p>
</blockquote>
<p>set the working directory which defines the target location inside the container for all the subsequent instructions.</p>
<h3 id="heading-copy-and-install-dependencies">Copy and install dependencies</h3>
<blockquote>
<p><em>COPY go.mod go.sum ./</em></p>
<p>RUN go mod download</p>
</blockquote>
<p>Use 𝘨𝘰.𝘮𝘰𝘥 to manage dependencies. By separating the dependencies from application source code you can take advantage of container layer caching. As long as you do not change the versions of libraries used by your application, the cached version of previously built layers will be reused, which will significantly shorten the build time. Additionally, including 𝘨𝘰.𝘴𝘶𝘮 file with checksums of all direct and indirect dependencies ensures that their content has not been modified.</p>
<h3 id="heading-copy-the-application-code">Copy the application code</h3>
<blockquote>
<p>COPY *.go ./</p>
</blockquote>
<p>Copy the source code of your application to the working directory created in the second step.</p>
<h3 id="heading-compile-the-application">Compile the application</h3>
<blockquote>
<p>RUN go build -o /myapp</p>
</blockquote>
<p>Compile your application from the source code and</p>
<h3 id="heading-run-as-non-privileged-user">Run as non-privileged user</h3>
<blockquote>
<p>USER 1000</p>
</blockquote>
<p>Ensure that processes running in your container will be executed in non-privileged mode. Since a non-privileged user with id 1000 is already registered in the base Go image, just use it.</p>
<h3 id="heading-set-the-entry-command">Set the entry command</h3>
<blockquote>
<p>CMD ["/myapp"]</p>
</blockquote>
<p>this complete Dockerfile</p>
<pre><code class="lang-bash">FROM golang:1.21

WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download

COPY *.go ./

RUN go build -o /myapp

USER 1000

CMD [<span class="hljs-string">"/myapp"</span>]
</code></pre>
<div class="hn-embed-widget" id="nihbuatjajan2-mdrdani"></div>]]></description><link>https://notes.mdrdani.my.id/containerize-your-go-application</link><guid isPermaLink="true">https://notes.mdrdani.my.id/containerize-your-go-application</guid><category><![CDATA[Go Language]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Muhamad Dani Ramanda]]></dc:creator></item></channel></rss>