evilfactorylabs

Cover image for Mengenal dan mengapa menggunakan Function as a Service (FaaS)
Rizaldy
Rizaldy

Posted on

Mengenal dan mengapa menggunakan Function as a Service (FaaS)

Ini adalah bagian dari topik cloud, jika kamu belum terlalu familiar dengan konsep komputasi awan, mungkin bisa mempertimbangkan untuk membaca tulisan berjudul Mengenal "Cloud" untuk Software Developer terlebih dahulu.

Dewasa ini (anjay dewasa) proses deployment sebuah layanan ataupun aplikasi sudah semakin beragam khususnya untuk platform web yang melakukan komputasi dibagian server. Dari yang sebelumnya mungkin mendistribusikannya sebagai CGI script, dilanjutkan dengan sebuah berkas be-ekstensi php, lalu sampai ke sebuah berkas arsip yang biasa disebut dengan docker image yang sekarang umum digunakan.

Apapun pendekatan yang dipilih, tujuan utamanya adalah satu: agar dapat memproses sesuatu, dibagian server. Entah untuk alasan keamanan, privasi, ataupun karena pengguna akhir tidak layak untuk melakukan proses tersebut.

Salah satu keunggulan komputer yang disebut dengan server adalah umumnya dia berjalan secara terus menerus, mungkin sebagaimana arti secara harfiahnya. Beda dengan personal computer (PC) yang memang tugasnya untuk melakukan kegiatan-kegiatan pribadi seperti menonton film ataupun memainkan permainan.

Server pada dasarnya adalah sebuah komputer, yang berarti memiliki perangkat keras (pastinya) dan perangkat lunak termasuk sistem operasi. Dan jika ingin membuat program untuk pengguna internet, pastinya ada sebuah program berjenis web server yang bertugas untuk melayani permintaan masuk dan keluar.

Tidak sampai situ, ada satu lapisan lagi yang harus dimiliki oleh server: runtime program. Jika kamu ingin menjalankan aplikasi/layanan yang ditulis menggunakan PHP, mungkin kamu membutuhkan php-fpm. Jika itu JavaScript, mungkin itu Node.js. Jika itu C, mungkin libc ataupun musl.

Apakah sudah cukup sampai situ? Tentu tidak. Kamu pasti butuh Reverse proxy karena seharusnya pekerjaan-pekerjaan seperti SSL termination, compression, caching, dsb bukanlah tanggung jawab aplikasi/layanan yang kamu kembangkan.

Sebagai rekap, untuk membuat sebuah program yang menampilkan Hello World menggunakan JavaScript (Node.js), setidaknya komputer (server) kamu harus memiliki komponen berikut:

  • Sistem operasi (e.g: Ubuntu)
  • Reverse proxy (e.g: Caddy)
  • Runtime program (e.g: Node.js)

Tidak menutup kemungkinan kamu harus tau juga tentang supervisor sehingga program-program yang kamu butuhkan bisa berjalan kembali secara otomatis baik setelah reboot ataupun setelah crash.

Baiklah, mari kita coba menggunakan pendekatan tradisional terlebih dahulu. Anggap gue menggunakan Ubuntu dan sudah memasang Caddy dan Node.js di server gue, ya.

Pertama kita buat program sederhananya dulu menggunakan JavaScript, berikut kodenya:

// server.js
const http = require('http')

const server = http.createServer(({ headers, socket }, res) => {
  const ip = headers['x-forwarded-for'] || socket['remoteAddress']

  res.statusCode = 200
  res.setHeader('Content-Type', 'text/plain')
  res.end(`Hello ${ip}\n`)
})

server.listen(8080, '127.0.0.1', () => {
  console.log('server run at http://127.0.0.1:8080')
})
Enter fullscreen mode Exit fullscreen mode

Dengan menjalankan node server.js, program sederhana kita sudah bisa diakses melalui localhost:8080 yang mana setiap kali ada permintaan masuk ke path / dengan method GET (ataupun POST, DELETE, dsb) akan memberikan Hello {alamat ip} sebagai respon.

Kita ingin membuat program tersebut dapat diakses oleh pengguna internet, dan tidak lucu bila program kita tersebut diakses menggunakan kombinasi alamat ip + port secara langsung.

Disini kita butuh Caddy sebagai Reverse Proxy. Dan sebagaimana reverse proxy pada umumnya, kita menggunakan SNI (think like virtual host) untuk melakukan service discovery atau menentukan upstream backend sederhananya.

# Caddyfile

fake.evilfactorylabs.org {
  reverse_proxy * 127.0.0.1:8080
}
Enter fullscreen mode Exit fullscreen mode

Reverse proxy umumnya berjalan di port 80 (HTTP) dan 443 (HTTPS), setiap ada permintaan masuk ke fake.evilfactorylabs.org yang mana mengarah ke ip server tersebut, Caddy akan meneruskan permintaannya ke 127.0.0.1:8080 tempat dimana aplikasi tersebut bejalan.

Aktivitas diatas adalah effort minimum yang setidaknya harus dilakukan ketika mengembangkan aplikasi yang ditulis menggunakan JavaScript.

FaaS way

Jika melihat secara garis besar, berdasarkan contoh aplikasi diatas, satu hal yang benar-benar kita butuhkan hanyalah Node.js sebagai runtime. Harusnya kita tidak perlu peduli sistem operasi dan reverse proxy apa yang digunakan, khususnya jika aplikasi kita tidak berurusan dengan sistem operasi secara langsung (misal membuat tun device) ataupun ingin melakukan hal yang biasa dilakukan oleh reverse proxy (misal melakukan kompresi ke brotli).

Dan gue rasa, kebanyakan aplikasi web pun memiliki karakteristik yang sama.

Oke kita kembali ke konteks. Perlu diketahui bahwa tidak semua aplikasi cocok menggunakan pola FaaS, atau Serverless jika lebih familiar dengan sebutan tersebut.

FaaS lebih cocok untuk melakukan hal-hal yang bersifat on-demand yang tidak perlu berjalan selamanya.

Misal situs ini secara keseluruhan, meskipun secara teori bisa saja dibuat menggunakan pendekatan FaaS, namun tidak efektif, seperti, bayangkan harus menjalankan dan mematikan aplikasi yang dibuat menggunakan Ruby On Rails ini setiap kali ada permintaan masuk agar mungkin dapat menghemat biaya sekian rupiah perbulan.

Untuk aplikasi Ruby fucking On Rails.

Mungkin bisa saja gue pisahkan si fungsi komentar yang ada di aplikasi ini, membuat function untuk mengatur event mengambil data komentar dan memproses komentar baru, lalu revalidate cache, tapi nanaonan (kalau kata orang Sunda mah).

Contoh sederhana yang cocok untuk menggunakan pendekatan FaaS adalah sistem notifikasi. Umumnya sistem notifikasi berjalan sebagai worker untuk memisahkannya dari thread utama agar aplikasi utama tidak memakan terlalu banyak memori. Karena ada pemisahan ini, umumnya juga ada komponen tambahan seperti queue system yang secara garis besar bertugas sebagai "waiting room" untuk si worker ini untuk mengatur setiap kali ada "event" masuk ataupun keluar.

Bayangkan ketika pengguna melakukan registrasi ke situs ini dan harus menunggu loading karena aplikasi ini sedang mengirim surel yang dituju ke SMTP server yang digunakan untuk mengirim surel verifikasi, yang entah berhasil ataupun gagal.

Nah itu jika 1 pengguna yang dalam waktu yang sama, ya.

Dengan menerapkan queue system, kita bisa melakukan misalnya hanya mengirim 10 email per-detik setiap ada event registrasi, misal untuk menghindari rate-limit dari SMTP server.

Kuncinya ada di event. Hal yang bisa dilakukan di worker, besar kemungkinan bisa dijadikan sebagai serverless function.

Disini gue akan menggunakan contoh untuk Webhook dari layanan Saweria yang gue rasa cocok untuk contoh ini.

Setiap kali ada saweran masuk, sistem Saweria akan mengirim permintaan POST beserta payloadnya ke sebuah endpoint yang sudah gue tentukan.

Dan setiap kali ada permintaan POST masuk di function gue, gue mengirim permintaan POST juga ke sebuah endpoint yang sudah sistem Discord tentukan.

Jadi disini ada 3 webhook:

Gue gak tau kapan si Saweria ngirim POST ke gue, yang berarti layanan gue harus terus berjalan, kan? Gue bisa spin up VM (atau menggunakan VM yang sama) tapi gue gak mau berurusan dengan Node.js (dan OS) ataupun Docker hanya untuk menjalankan layanan ini.

Jadi gue menggunakan layanan Cloudflare Workers terlebih karena situs ini juga menggunakan layanan Cloudflare.

Kodenya relatif sederhana, misal jika tidak menggunakan KV storage nya Cloudflare seperti ini:

const DISCORD_WEBHOOK = 'https://discord.com/api/webhooks/xxx'

async function handlePost(request) {
  try {
    const requestPayload = await request.json()

    const { donator_name, message, amount_raw } = requestPayload

    const duit = new Intl.NumberFormat('id', {
      style: 'currency',
      currency: 'IDR'
    })

    const body = {
      content: `**${donator_name}** baru saja menyawer **${duit.format(
        amount_raw
      )}** dengan pesan **_${message}_**`
    }

    const payload = {
      body: JSON.stringify(body),
      method: 'POST',
      headers: {
        'content-type': 'application/json'
      }
    }

    const response = await fetch(DISCORD_WEBHOOK, payload)

    return new Response(JSON.stringify({ success: true }), payload)
  } catch {
    return new Response('<pre>noted</pre>', {
      headers: {
        'content-type': 'text/html'
      }
    })
  }
}

async function handleGet() {
  return new Response('<pre>ok</pre>', {
    headers: {
      'content-type': 'text/html'
    }
  })
}

addEventListener('fetch', event => {
  const { request } = event

  if (request.method === 'POST') {
    return event.respondWith(handlePost(request))
  }

  return event.respondWith(handleGet())
})
Enter fullscreen mode Exit fullscreen mode

Gue tidak membuat validasi lebih lanjut karena kalau ada error yang disebabkan oleh orang iseng pun gue rasa gue tidak rugi apa-apa, jadi, yaudalaya.

Anyways, jika melihat kode diatas, kunci dari function ini adalah event fetch yang pada dasarnya POST request dari sistem Saweria adalah fetch request, kan?

Dan jika melihat di dashboard, function diatas rata-rata berjalan selama ~4ms dengan penggunaan CPU rata-rata 72.8 MB per-detiknya.

The nice thing is, gue tidak perlu ribet-ribet ngatur reverse proxy dan web server untuk si webhook ini, cukup fokus menulis kodenya aja sudah mendapat endpoint (HTTPS) yang siap untuk digunakan.

Mungkin buat yang lebih serius, si Cloudflare Workers ini nawarin event cron juga misal kalau somehow si workers ini gagal mengirim POST request ke Discord, dengan bantuan KV Storage, mungkin bisa melakukan re-request per-sekian waktu untuk membuat queue system ala ala.

Jika ingin mencoba, silahkan kunjungi saweria.co/evilfactorylabs dan lihat di channel #wall-of-love di server Discord evilfactorylabs.

...nah bikin URL shortener ala ala juga cocok sih pakai serverless function ini

Penutup

Perlu diketahui bahwa model biaya dari FaaS biasanya adalah:

  • execution/CPU time
  • egress bandwidth
  • x req/mo

Yang gue rasa harganya relatif affordable.

Perlu diketahui juga untuk cloud provider yang menawarkan FaaS biasanya menggunakan proprietary runtime dan juga tidak (atau belum) memiliki/memenuhi standar. Yang maksudnya, di Cloudflare Workers menggunakan semantik add.eventListener dengan properti event blablabla, sedangkan, misalnya, di AWS Lambda menggunakan exports.handler = async function(event, context) yang secara teknis mungkin melakukan hal yang sama namun menggunakan pendekatan yang berbeda.

PR dari migrasi ke cloud sekali lagi adalah kepercayaan, yang jika mengambil contoh FaaS disini, kita harus mempercayakan cloud provider yang kita pilih terlebih karena kita tidak berurusan dengan sistem operasi; reverse proxy, dan web server yang ingin digunakan.

Salah satu tujuan dari ekosistem cloud menurut gue adalah untuk semaksimal mungkin menghindari pengembang (dan operator) berurusan dengan server yang menurunkan penghalang untuk siapapun yang ingin membuat sesuatu di internet.

Sebagai penutup, kelemahan dari serverless function ini adalah di maksimum CPU time. Misal, bila kamu membuat sebuah layanan untuk mentransformasi webm ke mp4 yang mungkin memakan waktu 20s untuk video berdurasi 30 menit dan sedangkan maksimum execution time nya adalah 10s, besar kemungkinan proses tersebut akan gagal.

Lalu bagaimana solusi untuk masalah diatas? Akan kita bahas di tulisan selanjutnya!

Top comments (0)