Dari Dev ke Production

Yang membuat lingkungan production, lingkungan production

Dari Dev ke Production

Semua berawal dari direktori kosong, terlepas akan menggunakan pendekatan monorepo ataupun polyrepo. Direktori tersebut mulai diisi dengan beberapa kode, yang ditulis menggunakan bahasa pemrograman tertentu, yang berada di mesin pengembang.

Si pengembang memilih bahasa pemrograman JavaScript yang akan dijalankan di server dan juga menggunakan pnpm sebagai manajer paketnya. Dia ingin membuat sebuah aplikasi web, yang berarti dia harus membuat sesuatu yang disebut web server.

Fastify dipilih karena selain tidak ingin membuat web server dengan vanilla JavaScript, juga si pengembang sudah cukup familiar dengan tools tersebut. Si pengembang menjalankan pnpm add fastify untuk bisa menggunakan tools tersebut di kode nya, lalu mulai membuat kode bernama server.js.

const fastify = require('fastify')

const app = fastify({ logger: true })

const PORT = process.env.PORT || 3000

app.get('/', async (req, res) => {
  return { hello: 'world' }
})

const start = async () => {
  try {
    await app.listen({ port: PORT })
  } catch (err) {
    app.log.error(err)
    process.exit(1)
  }
}

start()

Lalu menjalankannya dengan node server.js. Aplikasi berjalan sebagaimana mestinya, dan ini menjadi awal yang menjanjikan.

Sebagaimana pengembang pada umumnya, dia ingin menulis kode test untuk "kode production" yang sudah dia tulis. Node TAP pun dipilih sekali lagi karena faktor familiaritas. Memasang Node TAP sesederhana menjalankan pnpm add tap --save-dev, dan parameter --save-dev disini penting karena penggunaan Node TAP hanya relevan di lingkungan development.

Untuk mempermudah pengujian, pengembang melakukan refactoring dengan memisahkan "perhatian" yang ada menggunakan pendekatan MVC. Pertama, dia membuat "controller" pertama untuk aplikasinya tersebut:

// index.controller.js

async function Homepage(req, res) {
  return { hello: 'world' }
}

module.exports = {
  Homepage: Homepage
}

Setelah itu dia membuat "router" untuk mengatur si controller:

// index.router.js

const IndexController = require('./index.controller')

async function routes(fastify, opts) {
  fastify.get('/', IndexController.Homepage)
}

module.exports = routes

Membuat berkas app.js untuk membungkus segalanya:

// app.js

const fastify = require('fastify')

const IndexRoute = require('./index.router')

function boot(opts) {
  const app = fastify(opts)

  app.register(IndexRoute)

  return app
}

module.exports = boot

Dan mengganti isi server.js menjadi lebih relevan.

// server.js

const app = require('./app')

const server = app({ logger: true })
const PORT = process.env.PORT || 3000

server.listen({ port: PORT }, err => {
  if (err) {
    server.log.error(err)
    process.exit(1)
  }
})

Lalu mulai membuat kode test pertamanya:

// index.spec.js

const { test } = require('tap')

const server = require('./app')

test('requests "index" page', async t => {
  const app = server()

  const response = await app.inject({
    method: 'GET',
    url: '/'
  })

  t.equal(response.statusCode, 200, 'returns 200 status code')
})

Dan menjalankannya:

Dengan begitu dia bisa melakukan refactoring lebih lanjut seperti mengelompokkan kode-kode kedalam direktori tanpa khawatir dapat merusak fungsionalitas yang sudah diharapkan.

Shortcuts

Siapa yang ingin menjalankan node server.js dan atau pnpm tap ... untuk melakukan sesuatu? Di JavaScript, kita bisa membuat alias untuk menjalankan perintah-perintah sesuai dengan fungsinya. Disini kita akan membuat 3 alias:

  • start untuk menjalankan aplikasi di lingkungan production
  • dev untuk menjalankan aplikasi di lingkungan development
  • test untuk menjalankan pengujian pada aplikasi

Sehingga menjalankan pnpm start dapat mempersingkat waktu alih-alih menulis NODE_ENV=production node server.js, misalnya.

Lalu kita buat alias tersebut di package.json melalui keys script:

"scripts": {
  "start": "NODE_ENV=production node server.js",
  "dev": "NODE_ENV=development node server.js",
  "test": "pnpm tap"
}

Dan menyesuaikan sedikit kode:

// server.js

const NODE_ENV = process.env.NODE_ENV || 'test'

const LOGGER_CONFIG = {
  development: {
    level: 'debug'
  },
  production: true,
  test: false
}

const server = app({ logger: LOGGER_CONFIG[NODE_ENV] })

server.listen({ port: PORT }, err => {
  server.log.debug("App ready")

  if (err) {
    server.log.error(err)
    process.exit(1)
  }
})

Dan menjalankannya seperti biasa:

Dari sini alur kerja menjadi semakin menyenangkan!

Requirements

Agar pengembang lain dapat menjalankan aplikasi ini sesuai harapan, setidaknya mereka harus memiliki lingkungan yang cukup mirip. Misal, dari versi Node.js yang dipasang sampai ke sistem operasi yang digunakan, yang mana diharapkan sama.

Pengembang lain disarankan untuk menggunakan NVM (Node Version Manager) untuk mempermudah manajemen versi. Lalu pengembang membuat berkas bernama .nvmrc dengan isi 18.13.0 untuk memastikan siapapun menggunakan versi Node.js yang sama dan juga memastikan untuk menggunakan pnpm alih-alih manajer paket bawaan, karena suatu alasan.

Dan yang terakhir, pengembang membuat repositori git untuk project ini sehingga siapapun dapat berkontribusi untuk menulis kode dari aplikasi yang sedang dikembangkan.

Kode tersebut disimpan di remote repository sentral, dan menjadi "source of truth" dari sumber kode utama yang ada.

Production

Kita mulai masuk ke inti. Mari kita mulai dengan apa yang membedakan antara lingkungan "development" dan "production".

Pertama, mesin, tentu saja. Mungkin pengembang menggunakan Macbook Air M1 dengan 8 core CPU + 8 GB memori dan pengembang lain menggunakan Lenovo Thinkpad X1 Carbon dengan 2 core CPU + 16 GB memori. Apapun jenis mesin yang digunakan pengembang, yang pasti, bukan mesin tersebut yang akan digunakan oleh pengguna akhir.

Kedua, aksesibilitas. Pengguna akhir harus bisa mengakses aplikasi tersebut, yang dewasa ini, umumnya melalui jaringan publik bernama internet dan menggunakan sebuah sistem bernama DNS. Di jaringan internet, setiap komputer berkomunikasi dengan komputer lainnya melalui alamat IP publik, dan tentu saja kita tidak membiarkan pengguna akhir mengakses aplikasi melalui alamat IP pribadi seperti localhost (127.0.0.1) sebagaimana yang biasa kita lakukan di lingkungan pengembang.

Terakhir, kondisi. Kondisi di lingkungan development tentu saja berbeda dengan di production karena suatu alasan. Misal, di lingkungan production kita tidak perlu Display Server, di lingkungan development kita tidak perlu mengakses aplikasi melalui HTTPS, dsb, dsb.

Pada dasarnya, mesin apapun dapat dianggap sebagai "lingkungan production" jika ingin. Misal, Macbook Air yang digunakan untuk menulis tulisan ini. Selama dapat diakses pengguna akhir, maka... dapat diakses pengguna akhir.

Yang berarti, harus:

  • Memiliki alamat IP yang dapat diakses melalui jaringan internet
  • Terus berjalan selama 24x7
  • Bisa mengurus permintaan masuk/keluar dari/ke aplikasi yang ada
  • Siap dengan serangan-serangan yang ada di jaringan internet

Sayangnya, tidak sesederhana itu.

Pertama, harus memastikan bahwa listrik tidak padam. Mungkin ini bisa diatasi dengan memasang genset, atau panel solar, atau baterai, literally whatever works.

Tapi tetap, itu ada biayanya.

Kedua, harus memastikan bahwa alamat IP yang diberikan oleh ISP tidak berubah-ubah (statis). Mungkin ini bisa diatasi, dari meminta ISP untuk mengalokasikan alamat IP statis sampai ke menggunakan "tunnel" dari siapapun yang menyediakan layanan tersebut.

Ketiga, bandwidth. Misal, jika memiliki video berdurasi 1 menit dengan total ukuran 60 MB yang ingin diberikan kepada pengguna akhir. Agar mereka dapat menontonnya secara optimal, setidaknya jaringan harus dapat mengirim video tersebut 1 MB/s alias 8 Mbps. Tentu perhitungannya tidak sesederhana itu, yang pasti, setidaknya jaringan yang ada pun harus dapat sanggup mengatur lalu lintas untuk 1 MB/s (8 Mbps) tersebut.

Terakhir, prasarana lainnya yang bersifat "pembantu" namun sangat penting. Dari router, switch, kabel, firewall, hardware, dsb.

Gue pribadi menjalankan server di jaringan rumah, dan hanya dengan menggunakan 1 komputer berjenis Intel NUC yang memiliki 2 CPU dan 8 GB memory. Untuk skala gue, itu sudah cukup. Ada 20 containers yang berjalan terus menerus, dan uptime per tulisan ini dibuat mencapai 79 days, yang setiap 3 bulan sekali gue melakukan restart untuk kepentingan pemeliharaan.

Jika merujuk ke laporan di Cloudflare, ada 873.2k permintaan di last 30 days. Pada dasarnya itu angka yang relatif kecil, karena setara dengan 29,107/hari alias 1,212/jam alias 20/s.

Yang maksudnya, menjalankan "lingkungan production" di jaringan rumah sekalipun adalah sesuatu yang dapat dilakukan, alias bukan sesuatu yang mustahil.

Tapi kita tidak akan senekat itu, khususnya untuk sesuatu yang business-critical™.

Ada SLA yang harus dijaga untuk menumbuhkan kepercayaan.

Umumnya kita akan menggunakan jasa dari pihak ketiga, yang benar-benar bisnis utamanya adalah menyediakan dan menawarkan infrastruktur, dari mesin; jaringan internet, sampai ke listrik.

Ada perdebatan antara Colo vs X, tapi itu akan kita bahas di lain waktu.

Penyedia jasa tersebut umumnya disebut dengan Cloud Provider. Cloud gampangnya adalah infrastruktur yang dapat diakses dan dikontrol melalui jaringan internet.

Favorit gue adalah Linode, selain karena memiliki lokasi data center di Singapore yang mana cukup dekat dengan Indonesia juga karena harganya yang kompetitif serta layanannya yang cukup terpercaya. Mereka sudah berada di industri ini selama 20 tahun sejak 2003, dan bergabung dengan Akamai pada tahun 2022 kemarin, yang mana terlihat sangat menjanjikan.

Dengan $40/bulan, gue bisa mendapatkan mesin dengan 4 CPU + 8 GB memori + penyimpanan 160 GB (SSD). Juga, bandwidth dengan I/O 40/5 Gbps yang sebenarnya lebih dari cukup.

600rb per bulan sekilas terdengar relatif mahal. Dalam setahun, setidaknya sudah mengeluarkan 7.2jt. Tapi tidak ada listrik dan jaringan internet yang harus dijaga, tidak ada mesin yang harus dirawat, dan tidak ada overhead lain yang sebenarnya tidak terlalu penting tapi tidak bisa dihindari.

Tidak ada investasi lain yang harus dibayar di awal.

Mari kita ambil paket yang paling murah: $5/bulan.

Meskipun gue tidak terlalu pede dengan apa yang bisa dilakukan oleh mesin berkapasitas 1 GB memori dan 1 CPU.

Linux at somewhere

Jika sistem operasi yang di lingkungan pengembang yang digunakan oleh pengembang A adalah Mac OS dan pengembang B adalah Windows, apa sistem operasi yang akan kita gunakan untuk di lingkungan production?

GNU/Linux! Benar sekali.

Dengan menggunakan GNU/Linux (yang nantinya akan gue persingkat dengan sebutan Linux), tidak perlu ada biaya lisensi yang harus dibayar sekian waktu. Dan juga, tidak perlu ada biaya yang relatif mahal untuk memiliki perangkat keras hanya untuk menjalankan sebuah sistem operasi.

Sumber kode Linux adalah terbuka dan memiliki lisensi yang cukup bebas. Yang gampangnya, siapapun dapat menjalankan Linux untuk keperluan apapun.

Linux berjalan dimana-mana. Mungkin "kulkas pintar" di tempatmu menjalankan sistem operasi yang berbasis Linux. Linux pada dasarnya hanyalah sebuah kernel: sesuatu yang menghubungkan antara perangkat keras dan perangkat lunak. Dan varian dari Linux tersebut yang umumnya digunakan, dari Ubuntu nya Canonical; openSUSE nya SUSE, RHEL nya Red Hat, dsb.

Anggap kita menggunakan Ubuntu, karena, mengapa tidak.

Hal utama yang harus kita lakukan adalah menyamakan mesin tersebut dengan apa yang ada di lingkungan development. Di banyak kasus, justru seharusnya sebaliknya, tapi mari kita biarkan sebagaimana adanya agar lebih sederhana.

Pertama, kita perlu memasang NVM, dan pastikan menggunakan versi yang sama.

Kedua, kita perlu mengatur repositori git. Karena pada akhirnya, sumber kebenaran yang akan digunakan di kode kita adalah apa yang ada di remote repositori.

Ketiga, kita perlu memasang dependensi yang digunakan untuk membuat aplikasi tersebut berjalan.

Apakah cukup sampai situ?

Tentu saja tidak! Kita perlu memastikan hal lain dari penggunaan sumber daya dan keamanan.

Tapi mari kita permudah untuk mempersingkat.

First deployment – Running for production

Kita tahu bahwa "cabang" yang digunakan untuk lingkungan production adalah cabang utama, yang anggap bernama mastah. Kita perlu memastikan bahwa kode yang ada di "mesin production" kita sudah "terkini" dengan apa yang ada di repositori git.

Ini cukup sederhana, menjalankan git fetch dan git rebase origin/mastah seharusnya cukup.

Kedua, kita perlu memastikan bahwa dependensi yang terpasang sudah terkini juga. Ini pun relatif sederhana, menjalankan pnpm install seharusnya cukup.

Ketiga, kita perlu memastikan bahwa aplikasi dapat jalan terus menerus. Ini cukup tricky, umumnya, aplikasi harus berjalan sebagai "daemon" alias aplikasi harus berjalan sebagai background process (berjalan di belakang).

Terkadang pengembang (ataupun administrator) akan menggunakan "process manager" seperti supervisord atau pm2. Tapi karena kita menggunakan Linux dan pada dasarnya kebanyakan varian Linux menawarkan fitur serupa (via systemd), menggunakan systemd sendiri pun gue rasa sudah lebih dari cukup meskipun beberapa orang ada yang anti dengan systemd.

Untuk cara pertama—membuat program berjalan di "belakang"—bisa dengan menggunakan ampersand (&) setelah nama program. Misal, seperti ini:

$ pnpm start &

Program akan berjalan di latar belakang, dan akan tetap berjalan sekalipun pengguna yang menjalankan program tersebut sudah tidak terhubung ke mesin tersebut.

Dengan kode kita yang sebelumnya, seharusnya ada "log" yang dicatat. Yang dalam kasus ini, berarti ke "layar" alias ke standard output (stdout). Untuk log yang bersifat error, harusnya ke standard error (stderr). Jika kita ingin menyimpan log alias pesan-pesan tersebut ke sebuah berkas, kita bisa menambahkan perintah berikut:

$ pnpm start >> app.log 2>&1 &

Yang gampangnya "semua pesan baik itu error ataupun bukan, simpen ke berkas app.log", dan jalankan aplikasi tersebut di background btw.

Log akan tercatat dan aplikasi akan terus berjalan sampai sudah tidak berjalan lagi.

Jika aplikasi pada suatu waktu mati karena suatu hal (misal crash), jalankan kembali program tersebut, dan profit.

Second deployment — Preparing for the worse

Tadi kita sempat menyinggung jika aplikasi mati, maka bisa diselesaikan dengan cara menyalakannya kembali. Masalahnya, kita tidak tahu tepatnya kapan dia mati berikut dengan seberapa lama "waktu pemulihan" dari kejadian tersebut.

Yang mana berpotensi mempengaruhi bisnis.

Jika ada sesuatu yang bertugas "restart aplikasi jika mati", maka process manager adalah sesuatu tersebut. Hal utama yang harus dilakukan adalah dengan membuat "services" yang umumnya berada di direktori/etc/systemd/system.

Sintaks nya sederhananya seperti ini:

[Unit]
Description=my very app
ConditionFileIsExecutable=/home/ubuntu/.local/bin/pnpm
After=syslog.target network-online.target
StartLimitBurst=5
StartLimitIntervalSec=0

[Service]
Restart=always
RestartSec=1

User=ubuntu
Group=ubuntu

WorkingDirectory=/home/ubuntu/app
ExecStart=/home/ubuntu/.local/bin/pnpm start

StandardOutput=/home/ubuntu/app/log/app.log
StandardError=/home/ubuntu/app/log/error.log

[Install]
WantedBy=multi-user.target

Bagian pentingnya adalah di bagian Restart=always dan di baris selanjutnya hanya menjelaskan bahwasannya "Restart aplikasi 5x setiap 1 detik jika masih terus tidak bisa dijalankan... sampai kiamat".

Juga, kita tidak perlu "hack" >> app.log 2>&1 & lagi berkat Systemd.

Untuk mulai menggunakan service tersebut, pertama kita perlu memberitahu systemd untuk menjalankan service tersebut setiap reboot:

$ systemctl enable my-very-app.service

Dan jika dirasa sudah yoi, bisa jalankan:

$ systemctl start my-very-app.service

Lalu cek status nya dengan:

$ systemctl status my-very-app.service

Jika terdapat error, pastikan executables yang digunakan dapat digunakan dan ditemukan oleh si pengguna yang menjalankan; atau permissionnya sudah benar, atau tidak ada typo yang terjadi.

Third deployment — Versioning

Umumnya, sebelum deployment terdapat satu upacara yang biasa disebut dengan rilis. Rilis ini pada dasarnya adalah: Mengambil perubahan apa saja yang diinginkan & menandakan kumpulan perubahan tersebut dengan sesuatu.

Sesuatu tersebut biasa disebut dengan versi.

Di kasus yang lebih nyata, tidak jarang kita akan berurusan dengan "upacara" sebelum "mempromosikan" perubahan terbaru yang ada ke pengguna akhir. Entah itu UAT, security analysis, backup DB, database migration, dsb, dsb.

Umumnya lagi, upacara tersebut harusnya berjalan di fase pre-deployment alias di lingkungan Staging. Lingkungan ini adalah lingkungan yang hampir mirip dengan di lingkungan production dan aman disebut sebagai "cermin" dari lingkungan production. Bedanya dengan staging, di lingkungan production, perubahan berdampak ke... data "sungguhan" yang ada.

Kasusnya adalah seperti ini. Misal, di production menjalankan versi 1.2.0, dan kita akan mempromosikan versi 1.3.0. Di lingkungan staging, ini sudah teruji dan tidak ada major regression yang terjadi. Perbedaan antara data di staging dengan data yang di production terlambat 5 jam, dan kita bertaruh bahwa di perbedaan 5 jam ini, seharusnya tidak ada hal-hal yang membuat versi terbaru gagal dipromosikan.

DAN, JUST IN CASE, IF YOU HAVE A BAD DAY AND SUCH CONDITIONS HAPPEN, setidaknya ada 3 hal yang bisa dipilih salah satunya:

  • "Rollback" ke versi sebelumnya
  • Perbaiki apapun yang bisa diperbaiki di versi terbaru tersebut tanpa melakukan deployment
  • Deploy "hotfix" jika masih hot

Setidaknya gue sempat merasakan 3 hal diatas.

Dengan melakukan versioning, setidaknya kita bisa melakukan sesuatu just in case something bad happened. Misal rollback sesederhana berpindah dari v1.3.0 ke v1.2.0. Atau sebelum deployment, tag 1.2.0 diubah jadi rollback dan 1.3.0 menjadi current. Whateva.

Di kasus contoh ini, kita bisa menggunakan bantuan git-tag(1) dan menggunakan git-checkout(1) untuk berpindah sebuah ke tag yang sudah dibuat. Umumnya, proses rilisnya seperti ini:

  • Buat branch rilis
  • Ambil perubahan-perubahan yang nantinya akan dimasukkan ke branch mastah
  • Tag branch tersebut
  • Gunakan branch tersebut ketika deployment
  • Jika everything's ok, rebase branch tersebut dengan branch utama (mastah)

Everything's ok diatas bervariasi, tapi biasanya, sampai aplikasi setidaknya dapat berjalan.

Tapi disini ada satu hal, yang entah expected ataupun unexpected tergantung keputusan organisasi: downtime.

Di aplikasi ataupun layanan yang mungkin setiap detiknya berharga, mereka hampir tidak memiliki downtime ketika meluncurkan versi baru. Jika 1 menit down time akan merugikan bisnis mereka sebesar 10jt misalnya, setiap deployment selama 5 menit gampangnya akan merugikan mereka 50jt.

Zero downtime deployment ini sebenarnya tricky, tapi secara prinsip cukup sederhana. Strategi yang relatif lebih mudah dilakukan adalah Rolling deployment, yang sederhananya, adalah dengan menjalankan 2 versi, dan mematikan versi yang lama secara perlahan.

Ini kita perlu bantuan dari sesuatu bernama "reverse proxy" yang akan kita bahas di bagian terakhir, meskipun rolling deployment tidak selalu tentang aplikasi yang diakses langsung oleh pengguna akhir.

Cara yang relatif sederhana adalah dengan bantuan DNS. Gambarannya adalah seperti ini:

Ketika si reverse proxy melakukan query ke "my_app", apa alamat IP yang akan diterima oleh si reverse proxy tersebut? Dan apa faktor yang menyebabkan alamat IP tersebut berubah?

Misal kita memiliki script yang pseudocode nya seperti ini:

function deploy (oldVersion, newVersion) {
  const checkNewVersion = setInterval(() => {
    const check = curl(`http://${newVersion}:3000/healthz`)
    
    if (check.statusCode === 200) {
      updateDNS('my_app', newVersion)
        .then(teardownService(oldVersion))
        .then(clearInterval(checkNewVersion))
    }
  }, 1000)
}

Dari kode tersebut bisa diasumsikan faktor yang menyebabkan perubahan tersebut adalah ketika endpoint dari newVersion mengembalikkan status code 200.

Pada dasarnya ini tentang scheduling dan akan gue bahas khusus tentang hal ini di lain hari!

Si reverse proxy seharusnya dapat mengetahui mana upstream yang up dan mana yang down, tapi itu cerita yang berbeda untuk di kasus ini.

Untuk penggunaan "alamat IP internal", di subnet 172.16.0.0/12 sendiri terdapat 1,048,574 alamat IP yang dapat digunakan (172.16.0.1-172.31.255.254). Atau 65,534 alamat IP di subnet 192.168.0.0/16 alias 192.168.0.1-192.168.255.254.

Tinggal pilih, I guess?

The final — Reverse proxy

Ini bagian yang cukup krusial.

Komputer berkomunikasi dengan komputer lainnya melalui alamat IP. Di aplikasi kita yang sebelumnya, dia menerima permintaan masuk melalui port 3000. Jika aplikasi tersebut berjalan di komputer dengan alamat IP publik 107.155.21.186, gampangnya, aplikasi tersebut dapat diakses melalui 107.155.21.186:3000.

Tapi tentu saja realita tidak semudah itu.

Untuk dapat mengakses blog ini, pengunjung dapat mengetik chaosplane.com di peramban favoritnya alih-alih mengetik 107.155.21.186:2368 (alamat IP nya Bunny, yang secara teknis tidak ada bedanya) yang mana lebih sulit dihafal.

Tapi bukan hanya tentang itu.

Pertama, permintaan dari pengguna/pengunjung harus menggunakan enkripsi, sekalipun, mungkin, tidak ada informasi "rahasia" yang ditukar. Aplikasi kita tidak memiliki fitur untuk melakukan TLS Termination, karena, itu bukan fitur utama dari aplikasi kita.

TLS termination gampangnya adalah proses untuk mendekripsi permintaan yang menggunakan protokol HTTPS. Ketika menerapkan protocol HTTPS, singkatnya setiap permintaan masuk/keluar akan di enkripsi/dekripsi berdasarkan sebuah sertifikat. Sertifikat tersebut diterbitkan oleh penerbit yang terpercaya dan hanya berlaku untuk "hostname" tertentu.

Hostname gampangnya adalah sebuah alamat domain. Konsep hostname (atau virtual host, lebih tepatnya) ini yang membuat "melayani 3 domain dalam 1 mesin yang sama" dapat terjadi.

Selain itu, reverse proxy pun kerap disebut sebagai Load Balancer. Ini gampangnya adalah untuk membuat "beban" ke origin server menjadi lebih ringan karena permintaan masuk akan diteruskan ke proses atau ke mesin yang berbeda ataupun karena sudah di cache oleh si Load Balancer.

Dan terakhir, reverse proxy pun dapat mengatur failover. Yang gampangnya, jika terdapat 2 replika dari aplikasi yang berjalan dan salah satunya misalnya mati, reverse proxy akan meneruskan permintaan masuk tersebut hanya ke aplikasi yang menyala sampai si aplikasi yang mati tersebut kembali menyala.

Ada kondisi dimana reverse proxy tidak diperlukan. Yang gampangnya, jika sudah menggunakan reverse proxy yang tidak di kontrol oleh kita. Misal, menggunakan layanan CDN. Atau, menggunakan layanan Load Balancer yang ditawarkan oleh layanan pihak ketiga. Enkripsi mungkin masih diperlukan namun menggunakan pendekatan perbedaan seperti menggunakan mTLS, tapi itu cerita lain.

Blog ini berada di belakang reverse proxy yang dijalankan oleh Bunny dan langsung meneruskan permintaan masuk ke proses si Ghost berjalan.

Di blog gue yang lain, si Cloudflare meneruskan permintaan masuk ke reverse proxy (Nginx) yang berjalan di jaringan rumah gue, lalu si Nginx tersebut meneruskan permintaan yang ada berdasarkan hostname tersebut.

Overhead yang ada ketika menambahkan reverse proxy didepan sesuatu seharusnya relatif kecil, tapi pertimbangan untuk menambahkan proxy tersebut kembali lagi ke kebutuhan masing-masing.

Ketika sudah menjalankan dan mengkonfigurasi reverse proxy, pengguna akhir seharusnya sudah siap menggunakan aplikasi kita, menggunakan cara yang mereka umum lakukan (dengan mengetik alamat domain tanpa secara eksplisit memberikan target port), di lingkungan yang umum digunakan (production).

Dan reverse proxy favorit gue sampai saat ini adalah Traefik.

Penutup

Semoga tulisan ini cukup menjelaskan tentang bagaimana "going from development to production" terjadi.

...dalam kasus yang sederhana.

Tulisan ini adalah awal untuk topik-topik yang mungkin tingkat lanjut yang semoga cukup seru dibahas.

Dan, hey, apakah kita melakukan deployment menggunakan SSH atau RCE?