Running Three-Tier Architecture App on Virtual Machine with Vagrant

Dalam pengembangan aplikasi modern, arsitektur three-tier (tiga lapis) adalah pendekatan yang umum digunakan karena memisahkan logika aplikasi menjadi tiga komponen utama: frontend, backend, dan database. Namun, seiring bertambahnya kompleksitas sistem, kebutuhan akan pengelolaan infrastruktur yang otomatis dan konsisten menjadi semakin penting.

Melalui artikel ini, kita akan membahas implementasi arsitektur three-tier yang tidak hanya modular, tetapi juga sepenuhnya dapat diotomatisasi menggunakan Vagrant dan VirtualBox. Pendekatan ini sangat cocok untuk tim pengembang yang menginginkan lingkungan lokal yang stabil dan mudah direplikasi.

  • Frontend: Dibangun dengan React.js

  • Backend: Menggunakan Node.js

  • Database: PostgreSQL

  • Load Balancer: Dikelola oleh NGINX

  • Virtualisasi: Semua komponen dijalankan dalam VM yang dikelola oleh Vagrant dan VirtualBox

Arsitektur Sistem

1. User (Client)

Pengguna mengakses aplikasi melalui browser. Semua permintaan dari client diarahkan terlebih dahulu ke server NGINX yang bertindak sebagai load balancer.

2. NGINX sebagai Load Balancer

NGINX berada di garis depan sistem dan berperan sebagai load balancer, yaitu komponen yang bertugas untuk mendistribusikan permintaan dari pengguna ke server tujuan yang sesuai. Dalam arsitektur ini, NGINX memiliki fungsi utama:

  • Menerima request dari user

  • Meneruskan permintaan ke Frontend VM jika permintaan adalah halaman UI

  • Meneruskan permintaan ke Backend VM jika permintaan adalah API

  • Menyediakan SSL termination (opsional, jika HTTPS digunakan)

  • Menyembunyikan VM internal dari akses langsung pengguna

  • Meningkatkan skalabilitas dan keandalan sistem dengan mengatur lalu lintas berdasarkan peran server

3. Frontend VM (React.js)

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.

4. Backend VM (Node.js)

VM ini menjalankan server aplikasi berbasis Node.js. Backend memproses logika bisnis, validasi, autentikasi, dan komunikasi dengan database.

Contoh fungsi backend:

  • Login user

  • Pemrosesan form

  • Pengambilan data produk

  • Penyimpanan data baru ke database

5. Database VM (PostgreSQL)

Menyimpan data aplikasi seperti user, transaksi, atau konten lainnya. Backend akan berkomunikasi langsung dengan VM ini melalui koneksi database.

6. Vagrant + VirtualBox

Seluruh komponen (Frontend, Backend, Database) dijalankan dalam virtual machine yang dikelola oleh Vagrant, dengan VirtualBox sebagai provider VM.

Keuntungan menggunakan Vagrant dengan VirtualBox:

  • Automasi provisioning: Semua VM bisa dibuat dan dikonfigurasi hanya dengan satu perintah (vagrant up)

  • Isolasi lingkungan: Setiap lapisan berjalan di VM terpisah

  • Jaringan virtual: Dapat diatur menggunakan IP statis via private_network atau host-only

  • Lingkungan konsisten: Developer tidak perlu menginstal layanan manual di OS host

  • Portabilitas: Konfigurasi yang sama bisa dijalankan di mesin berbeda

Vagrantfile

Konfigurasi Vagrantfile di bawah adalah bagian dari arsitektur infrastruktur otomatis menggunakan Vagrant dan VirtualBox, yang menetapkan pengaturan umum untuk semua VM dalam proyek.

Vagrant.configure("2") do |config|
  # Configure common settings

  # Base box
  config.vm.box = "bento/ubuntu-22.04"

  # Configure VirtualBox related settings
  config.vm.provider "virtualbox" do |vb|
    # Set CPU and Memory of the VM
    vb.memory = "768"
    vb.cpus = 2
  end

  # I don't want to check for updates
  config.vm.box_check_update = false

  # Disable the default share of the current code directory
  # I don't want the guest to write to make changes to the code
  config.vm.synced_folder ".", "/vagrant", disabled: true

  # Common provisioning script for all VMs
  config.vm.provision "shell", inline: "apt update && apt install -y curl git gnupg net-tools"

  # Common provisioning script to install NodeJS v18
  # It will be used in frontend and backend VMs
  nodejs_install_script = <<-SHELL
    echo "Installing NodeJS v18"
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
    export NVM_DIR="$HOME/.nvm"
    [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
    [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
    nvm install 18
    echo "NodeJS version: $(node -v)"
    echo "NPM version: $(npm -v)"
    sudo npm install -g pm2 npm
  SHELL
.......

Berikut penjelasan tiap bagian:

Vagrant.configure("2") do |config|

Menandakan kita menggunakan Vagrantfile format versi 2, yang merupakan standar saat ini.

config.vm.box = "bento/ubuntu-22.04"

Mengatur sistem operasi dasar semua VM menjadi Ubuntu 22.04 dari provider bento.

config.vm.provider "virtualbox" do |vb|
  vb.memory = "768"
  vb.cpus = 2
end

Mengatur resource untuk setiap VM:

  • RAM: 768 MB

  • CPU: 2 core

Ini memastikan semua VM berjalan ringan, cocok untuk lokal.

config.vm.box_check_update = false

Menonaktifkan pengecekan update box setiap kali vagrant up, agar lebih cepat.

config.vm.synced_folder ".", "/vagrant", disabled: true

Menonaktifkan folder sinkronisasi default antara host (.) dan guest (/vagrant), biasanya untuk mencegah tulisan tak diinginkan ke host, atau menjaga isolasi.

config.vm.provision "shell", inline: "apt update && apt install -y curl git gnupg net-tools"

Script shell ini akan dijalankan di semua VM, menginstal alat bantu dasar:

  • curl, git, gnupg, dan net-tools untuk keperluan umum sistem
nodejs_install_script = <<-SHELL
    echo "Installing NodeJS v18"
    curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
    export NVM_DIR="$HOME/.nvm"
    [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
    [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
    nvm install 18
    echo "NodeJS version: $(node -v)"
    echo "NPM version: $(npm -v)"
    sudo npm install -g pm2 npm
  SHELL

Script ini mendefinisikan instalasi Node.js versi 18 menggunakan NVM (Node Version Manager), dan akan digunakan dalam provisioning Frontend dan Backend VM.

Rincian tugas:

  1. Install NVM

  2. Install Node.js v18

  3. Verifikasi versi node dan npm

  4. Global install:

    • pm2: Untuk menjalankan proses Node.js sebagai background service

    • yarn: Sebagai alternatif npm yang populer dan cepat


Setelah itu file konfigurasi di bawah adalah lanjutan dari Vagrantfile diatas yang mendefinisikan VM untuk Load Balancer dengan peran sebagai NGINX load balancer.

.....
# Define Load Balancer VM
  config.vm.define "lb" do |lb|
    lb.vm.hostname = "lb"
    lb.vm.network "private_network", ip: "192.168.56.5"
    lb.vm.provision "shell", inline: <<-SHELL

    echo "Installing Nginx"
    apt install -y nginx

    echo "Configuring Nginx as a Load Balancer"
cat > /etc/nginx/sites-available/default << EOF
  server {
      listen 80;
      server_name _;

      # Forward requests to /api to the backend
      location ~* /api {
          proxy_pass http://192.168.56.3:3000;
      }

      # Forward other requests to the frontend
      location / {
          proxy_pass http://192.168.56.2;
      }
  }
EOF
    echo "Restart NGINX to apply changes"
    systemctl restart nginx
    SHELL
  end
.....

Menjadikan VM ini sebagai entry point untuk semua request pengguna. NGINX bertindak sebagai router yang mengarahkan request ke frontend atau backend sesuai path URL.

Singkatnya:
Automatisasi setup NGINX
Routing berdasarkan path
Terintegrasi dalam arsitektur 3-tier yang dijalankan via Vagrant

untuk menjalankan VM Load Balancer kita dapat menggunakan perintah

vagrant up lb

jika sudah running kita dapat check menggunakan browser dengan mengakses IP Address VM Load Balancer tersebut. status nya masih 502 Bad Gateway karena VM lain nya belum di aktifkan.

jika kita ingin mengakses VM Load Balancer dengan menggunakan SSH kita dapat menggunakan command

vagrant ssh lb


Selanjutnya kita konfigurasi VM Database masih lanjutan dari Vagrantfile . VM Database menggunakan PostgreSQL versi 16.

......
config.vm.define "db" do |db|
    db.vm.hostname = "db"
    db.vm.network "private_network", ip: "192.168.56.4"
    db.vm.provision "shell", inline: <<-SHELL
      sudo apt install -y postgresql-common
      sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
      sudo apt -y install postgresql-16
      echo "Start PostgreSQL service"
      systemctl start postgresql

      echo "Enable PostgreSQL to start on boot"
      systemctl enable postgresql

      echo "Set postgres user password to postgres, for demonstration purposes"
      sudo -u postgres psql -c "ALTER ROLE postgres WITH password 'postgres'"

      echo "Create the conduit database owned by postgres"
      sudo -u postgres psql -c "CREATE DATABASE conduit OWNER postgres;"

      echo "Configure PostgreSQL to listen on all interfaces"
      sed -i "s/#listen_addresses = 'localhost'/listen_addresses = '*'/g" /etc/postgresql/16/main/postgresql.conf

      echo "Allow all connections in pg_hba.conf"
      echo "host    all             all             0.0.0.0/0               md5" | sudo tee -a /etc/postgresql/16/main/pg_hba.conf

      echo "Restart PostgreSQL to apply changes"
      systemctl restart postgresql
    SHELL
  end
.....

untuk menjalankan VM Database kita dapat menggunakan perintah

vagrant up db

untuk mengakses VM Database seperti biasa kita bisa gunakan command

vagrant ssh db


Selanjutnya kita konfigurasi VM Backend masih lanjutan dari Vagrantfile dengan IP 192.168.56.3 VM Backend ini bertugas menjalankan backend service node.js yang di ambil dari github https://github.com/gothinkster/node-express-realworld-example-app.git .

.....
config.vm.define "backend" do |backend|
    backend.vm.hostname = "backend"
    backend.vm.network "private_network", ip: "192.168.56.3"
    backend.vm.provision "shell", privileged: false, inline: nodejs_install_script
    backend.vm.provision "shell", privileged: false, inline: <<-SHELL
      echo "Begin installing Backend"
      export NVM_DIR="$HOME/.nvm"
      [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
      export DATABASE_URL=postgresql://postgres:postgres@192.168.56.4:5432/conduit
      export JWT_SECRET=6b350c623d150e8a07d36e963e1416079a7a322e8c04952d674fe607914d7b01
      export NODE_ENV=development
      echo "Cloning the backend repository"
      git clone https://github.com/gothinkster/node-express-realworld-example-app.git $HOME/backend

      echo "Writing environment variables to .env"
      cat > $HOME/backend/.env << EOF
DATABASE_URL=postgresql://postgres:postgres@192.168.56.4:5432/conduit
JWT_SECRET=6b350c623d150e8a07d36e963e1416079a7a322e8c04952d674fe607914d7b01
NODE_ENV=development
EOF

      echo "Installing dependencies"
      cd $HOME/backend
      npm install

      echo "Generating Prisma client, running migrations, and seeding the database"
      npx prisma generate
      npx prisma migrate deploy && npx prisma db seed

      echo "Starting the backend server using pm2"
      pm2 start npm --name "backend" -- start
    SHELL
  end
.....

untuk menjalankan VM Backend kita dapat menggunakan perintah

vagrant up backend

untuk mengakses VM Backend seperti biasa kita bisa gunakan command

vagrant ssh backend


Terakhir kita konfigurasi VM Frontend masih lanjutan dari Vagrantfile dengan IP 192.168.56.2. VM Frontend ini bertugas menjalankan frontend service React.js yang di ambil dari github https://github.com/khaledosman/react-redux-realworld-example-app.git.

......
config.vm.define "frontend", autostart: false do |frontend|
    frontend.vm.hostname = "frontend"
    frontend.vm.network "private_network", ip: "192.168.56.2"
    # Copy frontend directory to VM. 
    frontend.vm.provision "shell", inline: "apt install -y nginx"
    frontend.vm.provision "shell", privileged: false, inline: nodejs_install_script
    frontend.vm.provision "shell", privileged: false, inline: <<-SHELL
      echo "Begin installing Frontend"
      export NVM_DIR="$HOME/.nvm"
      [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

      echo "Cloning the frontend repository"
      git clone https://github.com/khaledosman/react-redux-realworld-example-app.git $HOME/frontend

      echo "Installing dependencies"
      cd $HOME/frontend/
      npm install

      echo "Building the frontend"
      REACT_APP_BACKEND_URL="http://192.168.56.5/api" NODE_ENV=production npm run build

      echo "Copying the build to /var/www/html to serve it using Nginx"
      sudo mv $HOME/frontend/build/* /var/www/html/ 
    SHELL
  end
end

untuk menjalankan VM Frontend kita dapat menggunakan perintah

vagrant up frontend

untuk mengakses VM Frontend seperti biasa kita bisa gunakan command

vagrant ssh frontend

setelah semua VM up maka kita akan check kembali ke browser dengan akses http://192.168.56.5/

Bagaimana kita tau bahwa three tier architecture kita berjalan?? cara nya gampang matikan salah satu VM apakah masih berjalan dengan baik, seharusnya tidak bisa di akses kita contoh kan di matikan VM Backend dengan perintah.

vagrant halt backend

terlihat VM Frontend tidak dapat mengakses API VM Backend.
untuk mematikan semua VM kita dapat dengan mudah menggunakan command

vagrant halt

Dengan menerapkan arsitektur three-tier menggunakan NGINX sebagai load balancer, Node.js untuk backend, React sebagai frontend, dan PostgreSQL sebagai database, kita telah membangun lingkungan aplikasi web yang modular, terstruktur, dan mudah dikembangkan.

Penggunaan Vagrant dan VirtualBox memungkinkan kita untuk mengotomasi infrastruktur 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.

Github : https://github.com/mdrdani/three-tier

0
Subscribe to my newsletter

Read articles from Muhamad Dani Ramanda directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Muhamad Dani Ramanda
Muhamad Dani Ramanda

Tukang Infra #root