7 poin oleh GN⁺ 2023-07-17 | 1 komentar | Bagikan ke WhatsApp
  • Layanan pribadi dijalankan langsung di atas VM DigitalOcean $5/bulan dan Debian 10; untuk layanan berskala kecil, memasang perangkat lunak server di VM juga cukup sederhana dan masuk akal
  • Aplikasi server dibuat sebagai binary statis Rust, dan artefak deployment dipertahankan sebagai satu file dengan menyertakan HTML, CSS, konfigurasi, hingga secret
  • Pengelolaan eksekusi ditangani oleh systemd, sementara HTTPS diserahkan ke reverse proxy nginx dan Let’s Encrypt/certbot agar aplikasi tidak perlu menangani TLS secara langsung
  • Layanan yang membutuhkan data memakai satu file SQLite, serta menggunakan backup harian Tarsnap bersama backup streaming Litestream ke DigitalOcean Spaces
  • Saat menaruh beberapa layanan di satu VM, isolasi dilakukan dengan pengguna Unix per layanan; jika membutuhkan keamanan lebih kuat, pilih VM terpisah, systemd-nspawn, atau firejail

Konfigurasi dasar server dan deployment

  • Beberapa layanan seperti thoughts.page, hanabi.site, cgmserver, dan phonebridge dijalankan dengan konfigurasi yang mendekati ini
  • Aplikasi berjalan di VM DigitalOcean, dengan paket $5/bulan dan sistem operasi Debian 10
    • Beberapa layanan berbagi VM yang sama, sementara sebagian lainnya dipisahkan ke VM lain
  • Perangkat lunak server ditulis dengan Rust
    • Ditautkan secara statis
    • HTML, CSS, konfigurasi, secret, dan sebagainya dikompilasi ke dalam binary
    • Menggunakan rust-musl-builder dan rust-embed
  • Dengan cara ini, deployment menjadi sesederhana menyalin satu file ke server
    • Pendekatan serupa juga dianggap bisa digunakan di Go, C++, dan lainnya
    • Untuk bahasa yang tidak mudah melakukan deployment sebagai satu binary, alternatifnya adalah menjadikan container Docker sebagai artefak build

Eksekusi, proxy, dan preservasi data

  • Pengelolaan startup layanan ditangani oleh systemd
    • Binary ikut dijalankan saat server mulai menyala
    • Sebagian besar file unit systemd adalah file sederhana 9 baris
    • systemd sendiri memang kompleks, tetapi untuk tujuan menjalankan server saat boot, sebagian besar kompleksitas itu tidak terasa
  • Deployment ditangani dengan simple deploy script
    • Menyalin binary ke server dan me-restart server
    • Mendukung rollback, dan memastikan versi yang valid selalu berjalan meskipun koneksi terputus saat deployment
  • Program yang membutuhkan database menggunakan SQLite
    • Seluruh state aplikasi berada dalam satu file
    • Setiap hari membuat backup dengan perintah SQLite .backup dan menyimpannya di Tarsnap
    • Skrip backup dijalankan dengan cron
    • Dengan Litestream, salinan database di-streaming setiap beberapa detik ke penyimpanan DigitalOcean Spaces, dan snapshot dibuat setiap 6 jam
  • Semua server berjalan di belakang reverse proxy nginx
    • nginx menangani terminasi TLS, sehingga aplikasi tidak perlu memikirkan HTTPS
    • Sertifikat HTTPS diperoleh dari Let’s Encrypt dan certbot, lalu diperbarui otomatis
    • Contoh konfigurasi nginx untuk hanabi.site tersedia di gist terpisah
    • File statis bisa disajikan dengan nginx, dan cukup disalin ke server menggunakan scp atau rsync

Pemeliharaan dan isolasi layanan

  • Konfigurasi ini mengarah pada jalur operasional yang sederhana dan andal
    • Perangkat lunak di jalur penyajian selain aplikasi itu sendiri adalah komponen yang sudah teruji dan digunakan selama puluhan tahun
    • Selama tagihan DigitalOcean terus dibayar, secara esensial hampir tidak ada pemeliharaan untuk menjalankan situs
    • Satu-satunya masalah yang terdeteksi monitoring hanyalah isu jaringan DigitalOcean yang sementara
  • Sistem operasi dan pembaruan keamanan kadang diperlukan
    • Rilis Debian menyediakan dukungan 5 tahun, jadi sekitar dua setengah tahun lagi perlu upgrade ke Debian 11
    • Jika insiden seperti Heartbleed terjadi lagi, patch akan diperlukan
    • Kejadian seperti ini relatif jarang
  • Biaya memakai VM $5/bulan untuk setiap layanan bisa menjadi beban, tetapi beberapa layanan dapat dijalankan di VM yang sama
    • Isolasi diberikan dengan membuat akun pengguna Unix terpisah untuk tiap layanan
    • Metode isolasi ini sudah ada sejak masa awal Unix, sehingga tampak kokoh
    • Jika membutuhkan isolasi yang lebih kuat, bisa menggunakan systemd-nspawn atau firejail
    • Jika benar-benar penting dari sisi keamanan, bayar tambahan $5/bulan dan jalankan di VM terpisah
  • Prosedur menyiapkan proyek baru singkat
    • Membuat pengguna baru
    • Menambahkan virtual host nginx dan menerbitkan sertifikat HTTPS dengan certbot
    • Menambahkan unit systemd
    • Meng-commit deploy script ke repositori dan menjalankannya
  • Pada awalnya ada banyak hal yang perlu dipelajari, tetapi infrastruktur ini berubah jauh lebih lambat dibanding infrastruktur cloud
    • Format konfigurasi nginx secara esensial hampir tidak berubah selama 10 tahun terakhir
    • Perubahan besar terakhir dalam administrasi sistem Debian adalah transisi ke systemd hampir 10 tahun lalu
    • Lebih sedikit terekspos pada situasi ketika penyedia cloud menghentikan layanan atau diam-diam mengubah perilakunya
    • Dependensinya hanya penyedia VPS, dan server adalah komoditas umum sehingga dapat dipindahkan ke penyedia lain

1 komentar

 
GN⁺ 2023-07-17
Komentar Hacker News
  • Jika menjalankan tiap layanan sebagai pengguna Unix terpisah demi isolasi, fitur DynamicUser di systemd bisa menghemat waktu
    Fitur ini akan menetapkan UID dan membuat direktori log/status dengan izin yang benar
    https://0pointer.net/blog/dynamic-users-with-systemd.html

    • Jika UID harus persisten, membuat pengguna sistem baru dengan systemd-sysusers juga sangat mudah
      Cukup letakkan file teks kecil di /etc/sysusers.d/ yang berisi informasi seperti nama pengguna dan direktori home, lalu jalankan perintah atau layanan sysusers
    • Menggunakan akun layanan terpisah untuk setiap layanan/aplikasi adalah pendekatan yang cukup standar, dan sebaiknya server juga dipisahkan
  • Fungsi cloud dengan trigger HTTP adalah hal yang paling saya sukai belakangan ini
    Jika hati-hati menghindari jebakan khas tiap vendor, ini bisa sangat mengurangi kompleksitas, dan terasa seperti satu-satunya abstraksi cloud-native yang benar-benar tampak sebagai “bentuk final”
    Saya sudah melakukan lebih dari 2000 deployment hanya pada satu aplikasi, dan belum pernah sekalipun lingkungan eksekusinya rusak sampai harus menghubungi tim dukungan atau mengetik perintah konsol yang aneh-aneh
    Dari sisi performa dengan paket App Service terisolasi di Azure juga sangat bagus, dan penolakan ideologis saya terhadap penagihan per permintaan pun sekarang sudah hilang
    Memang bisa lebih murah kalau memiliki sendiri properti tempat server berada, tetapi untuk membangun sendiri atribut seperti kepatuhan dan audit yang diberikan fungsi HTTP sederhana ini, pada praktiknya Anda harus mendirikan perusahaan besar dan merekrut sangat banyak orang
    Logika tentang ketergantungan pada vendor juga sudah tidak terlalu meyakinkan bagi saya. Antarmuka yang menerima permintaan HTTP dan mengembalikan respons HTTP itu alami, dan perbedaan antarvendor paling hanya konteks tambahan seperti signature metode trigger atau klaim OIDC/SAML, jadi justru harus sengaja berusaha kalau ingin membuat struktur yang tidak bisa direfaktor ke vendor lain dalam seminggu
    Untuk blog pribadi, saya paham kalau membeli VM Hetzner lalu merapikannya dengan telaten terasa lebih menyenangkan. Jika industrinya sama sekali tidak diatur, maka argumen untuk menimbang margin dan kompleksitas secara rinci alih-alih sepenuhnya mengalihdayakan server juga bisa lebih kuat

    • Saya memutar mata pada bagian “kalau membangun sendiri harus membuat perusahaan bernilai 1 miliar dolar dan merekrut 1000 orang lagi”
      Saya tidak tahu mereka kira semua orang melakukan apa sebelum pindah ke cloud
      Sekarang saya menangani beberapa tim di perusahaan industri besar untuk memperluas dan menerapkan aplikasi inovasi di Azure, dan biaya cloud sangat tidak masuk akal dibanding jumlah penggunanya
      Dulu kami mengoperasikan aplikasi dengan pengguna 10 kali lebih banyak dengan biaya 1/100 dan kompleksitas yang juga lebih rendah
      Biaya ini dibebankan ke departemen lain yang membuat pilihan meragukan seperti ini, jadi secara pribadi saya tidak terlalu peduli, tetapi saya sulit memahami sikap yang memercayai cloud secara membabi buta
    • Saya bekerja di industri yang regulasinya sangat ketat, tetapi saya tidak mengerti kalimat terakhir itu
      Bagi kami, mengoperasikan hardware sendiri adalah pilihan terbaik, dan di kantor kami bahkan tidak boleh memotret, jadi kemungkinan menyerahkan apa pun ke penyedia cloud adalah 0%
    • Fungsi memang ada di posisi yang lumayan untuk meng-host API sederhana
      Tetapi agar aplikasi benar-benar berguna, tetap butuh elemen lain seperti database, dan bukankah di situ biaya tambahan mulai muncul?
    • Saya tidak yakin apa sebenarnya yang dimaksud dengan fungsi cloud dengan trigger HTTP di sini
      Saya bingung apakah ini handler permintaan pada server HTTP, komputasi cloud berbasis layanan fungsi, atau cuma RPC model lama
    • Yang penting adalah fungsi itu sebenarnya melakukan apa
      Jika ada script kiddie mulai mengirim permintaan 10 kali per menit, berapa lama sampai biaya fungsi menjadi lebih mahal daripada VPS?
  • Saya memakai susunan yang mirip untuk situs pribadi dan situs proyek
    Untuk Linode VM saya memakai paket 5 dolar per bulan, menggunakan Debian GNU/Linux, dan menulis perangkat lunaknya dalam Common Lisp
    Untuk situs web pribadi atau blog, saya menghasilkan situs statis dengan program Common Lisp, dan untuk layanan online atau aplikasi web, saya membuat program Common Lisp yang menangani permintaan HTTP dan memberikan respons dengan Hunchentoot
    Saya menggunakan file unit systemd agar situs/layanan otomatis berjalan saat VM mulai atau dimulai ulang, dan kebanyakan panjangnya sekitar 10–15 baris
    Konfigurasi awal VM saya kodifikasikan dalam skrip shell: https://github.com/susam/dotfiles/blob/main/linode.sh
    Konfigurasi per proyek dan per layanan saya kelola masing-masing dengan Makefile: https://github.com/susam/susam.net/blob/main/Makefile, https://github.com/susam/mathb/blob/main/Makefile
    Saya tidak memakai container. Situs-situs ini sudah berjalan selama bertahun-tahun sejak sebelum container populer, dan sejauh ini skrip inisialisasi serta Makefile sudah cukup
    Saya juga memakai Nginx. Ia menyajikan file statis dan, jika ada layanan backend, berperan sebagai reverse proxy; selain terminasi TLS, ada juga keuntungan seperti pembatasan laju permintaan atau pengaturan allowlist header HTTP untuk melindungi backend
    Di playbook kecil pribadi saya ada beberapa perintah seperti curl LINK -o linode.sh && sh linode.sh, git clone LINK && cd PROJECT && sudo make setup https
    Target make menangani pekerjaan yang diperlukan untuk menjalankan situs, seperti memasang alat Nginx, certbot, sbcl, mengatur konfigurasi Nginx, menyiapkan sertifikat, dan setelah perintah selesai situs web langsung dipublikasikan

    • Saya penasaran implementasi Common Lisp apa yang dipakai
      Dari Makefile tampaknya menggunakan SBCL, dan saya ingin tahu apakah penggunaan memori pernah menjadi masalah
      Saya memakai VPS RAM 512MB, dan satu instance SBCL memakan kira-kira 100MB, jadi hanya beberapa layanan yang bisa dijalankan bersamaan
      Saya sempat mempertimbangkan memindahkan layanan dengan trafik paling rendah ke CLISP, tetapi fitur yang saya gunakan, yaitu alias lokal paket, tidak ada di sana
  • Setelah bertahun-tahun terus menyempurnakan konfigurasinya, sekarang sudah menetap pada pendekatan menjalankan semuanya di container Docker
    Untuk orkestrator memakai docker-compose alih-alih systemd, dan untuk proxy memakai Caddy alih-alih nginx
    Sama seperti tulisan aslinya, ia menulis skrip deployment untuk tiap proyek yang perlu dijalankan, jadi secara keseluruhan sangat mirip
    Salah satu dari banyak kelebihan Docker adalah software pihak ketiga juga bisa dijalankan dengan konfigurasi yang sama
    Sudah beberapa tahun memakai konfigurasi ini dan hasilnya sangat bagus; seperti tulisan aslinya, ia kokoh sekaligus cukup fleksibel untuk diubah sesuai kebutuhan
    Satu-satunya masalah saat ini adalah rolling deployment. Seiring software makin besar, downtime beberapa detik di tiap deployment mulai menjadi masalah, dan meski belum ada solusi sederhana, docker swarm mungkin bisa menjadi arah yang tepat

    • Saya juga menjalankan setup yang mirip dengan Caddy
      Untuk mengurangi downtime, Anda bisa mencoba health_uri /health, lb_try_duration 30s
      Misalnya jika dikonfigurasi seperti reverse_proxy api:8089 { health_uri /health lb_try_duration 30s }, Caddy akan menahan request selama deployment versi baru dan memberi waktu 30 detik sampai service baru naik
      Idealnya, setelah versi baru sudah sehat, Caddy mulai mengarahkannya ke sana lalu baru mematikan container lama
      Saya sudah melihat https://github.com/Wowu/docker-rollout dan https://github.com/lucaslorentz/caddy-docker-proxy, tetapi belum sempat memprioritaskannya
    • Jika ada load balancer seperti Caddy di depan pod, Anda bisa mengaturnya agar menahan request saat pod baru sedang naik: https://twitter.com/bradleyjkemp/status/1486756361845329927
      Memang tidak sempurna, tetapi setidaknya browser hanya akan terus loading beberapa detik alih-alih menerima error koneksi
      https://mrsk.dev/ juga memakai teknik yang sama
    • Saat membangun software stack startup tempat saya bekerja dari nol, kami langsung mengemas aplikasi dengan Docker
      Produksi dimulai dengan compose, lalu belakangan ditingkatkan menjadi pipeline continuous deployment yang meng-upgrade stack secara otomatis
      Seiring perusahaan dan basis pengguna tumbuh, mulai muncul masalah downtime saat restart atau deployment, dan setiap kali ingin menjalankan aplikasi tambahan, kami harus menyiapkan lingkungan deployment baru
      Saya takut hari ketika harus memakai Kubernetes akan datang, karena saya pernah melihat sendiri kompleksitasnya dan tidak ingin menghabiskan sebagian besar hari untuk menenangkan cluster
      Jadi kami beralih ke mode Swarm, dan perjalanannya kadang seperti Jekyll, kadang seperti Hyde
      Ada bug yang tidak ada yang mau perbaiki, bagian dari spesifikasi Docker yang belum diimplementasikan tetapi juga tidak diberi tahu, pilihan implementasi yang bikin ingin mencabuti rambut, dan rasanya seperti pegawai Docker Inc tidak saling bicara atau tidak bisa fokus sampai tuntas
      Meski begitu, ada juga banyak sisi indahnya. Stack compose tetap berjalan apa adanya sambil bisa berkembang di titik yang dibutuhkan, dan jika dikonfigurasi dengan benar, deployment tanpa downtime, upgrade, load balancing, dan rollback berjalan dengan baik
      Raft dapat diandalkan untuk menjaga cluster seperti di tempat lain, dan dengan sedikit usaha Anda bisa membangun platform swalayan yang fleksibel, aman, dan terdistribusi otomatis hanya dengan sebagian kecil dari anggaran pemeliharaan K8s
      Hanya saja, Anda memang harus siap membuat skrip deployment dengan benar. Saya menghabiskan cukup banyak waktu membuat alat Python untuk mengubah file compose spesifikasi Docker yang valid menjadi spesifikasi Swarm, memperbarui dan membersihkan secret, serta mengekspansi environment variable
      Bergantung pada penyedia VPS, Anda juga harus memastikan MTU jaringan diatur dengan benar. Gara-gara ini rasanya umur saya berkurang cukup banyak
    • Jika Anda sudah punya file compose, Swarm memang jalur yang masuk akal, tetapi secara pribadi saya menilai itu tidak sepadan
      Setidaknya sampai jumlah pelanggan atau pengguna benar-benar tumbuh sehingga masalah skala menjadi nyata
    • Saya membuat setup yang mirip, tetapi saya tidak suka cara mendorong image lewat SSH dengan docker save dan docker import
      Saya jadi penasaran apakah Anda menjalankan registry sendiri
  • Di server fisik, saya menaruh database PostgreSQL dan aplikasinya dalam Podman Pod, semuanya berjalan pada port localhost di atas 5000, dan Caddy bertindak sebagai reverse proxy di 443
    Dengan systemd Timer, semua database di-dump ke satu direktori setiap hari pukul 4:55 sore
    Setelah itu, DejaDup [1] otomatis membackup $HOME ke HDD eksternal setiap hari pukul 5 sore; file cache dikecualikan, tetapi dump database tetap disertakan
    OS-nya Debian dengan GNOME Core [2], dan aturan firewalld hanya mengizinkan port 80, 443, serta port SSH kustom
    SSH berbasis key dan autentikasi password dinonaktifkan
    Ini cara yang paling membosankan, tetapi ya memang bekerja dengan baik
    1 - https://flathub.org/apps/org.gnome.DejaDup
    2 - https://packages.debian.org/bookworm/gnome-core

    • Saya penasaran kenapa menjalankan GNOME di server
    • Saya penasaran apakah ada alasan memakai systemd timers alih-alih cron job
  • Akhir-akhir ini saya memakai Dokku di satu server
    Mudah dikelola, dan developer bisa deploy hanya dengan git push seperti gaya Heroku, plus ada banyak plugin sehingga menambahkan database atau mengatur HTTPS pun bisa selesai dalam paling lama 10 detik

    • Saya juga memakai cara yang sama, dan bisa deploy lewat git push itu benar-benar nyaman
      Menambahkan aplikasi, menambahkan database, mengelola environment variable, dan mengelola domain semuanya sangat intuitif
  • Saya suka konfigurasi yang sederhana, jadi pendekatan seperti ini terlihat menyenangkan untuk dikerjakan
    Kemungkinan besar sudah cukup untuk 99% layanan
    Saya rasa saya akan memakai Ansible untuk konfigurasi server dan skrip deployment
    Dengan begitu server bisa terdokumentasi dan skrip deployment juga bisa jadi lebih sederhana
    Namun, saya tetap tidak akan menghindari akses langsung ke server untuk debugging atau pengecekan

    • Saya memang menggunakan Ansible, tetapi sedang mempertimbangkan lagi apakah itu benar-benar bernilai
      Skrip saya hampir tidak berubah selama 7 tahun terakhir, sedangkan Ansible lambat dan cenderung membuat saya harus terus memelihara banyak file
      Untuk debugging, log aplikasi juga bisa diekspos lewat endpoint yang dilindungi kata sandi di nginx
  • Kadang kita seperti lupa bahwa bahkan sebelum pindah ke cloud, CI/CD dan pengelolaan server yang efektif sudah merupakan praktik kerja yang umum

  • Soal menulis perangkat lunak server dalam Rust, lalu melakukan static linking dan mengompilasi HTML, CSS, konfigurasi, secret, dan semuanya ke dalam biner, belakangan ini saya melakukan ini di Go dan saya sangat menyukainya karena ini benar-benar mempermudah penulisan dan deployment perangkat lunak yang bergantung pada file statis

    • Saya masih ragu soal memasukkan secret ke dalam biner yang sudah dikompilasi
      Secret lebih mendekati standar jika memakai environment variable atau konfigurasi; memang menambah satu tahap sebelum dijalankan, tetapi itu mencegah situasi ketika Anda membagikan biner dan lupa bahwa di dalamnya ada secret
      Mengekstrak biner lalu mencari string cukup mudah dilakukan
      Memasukkan aset frontend statis dan konfigurasi default bersama-sama adalah keuntungan besar
    • Saya sebenarnya tidak suka Go, tetapi terus memakainya karena alasan ini
      Dengan Rust, terutama untuk cross-compiling dari Mac ke Linux, prosesnya relatif menyakitkan, sedangkan di Go itu sepele dan sudah terintegrasi ke dalam tool go
      Ini benar-benar memudahkan menghilangkan friksi saat menyelesaikan dan mendeploy side project
    • https://play.clickhouse.com/play?user=play juga berjalan dengan cara yang sama
      Namun ada satu pertanyaan. Di artikel disebutkan, “sertifikat Let’s Encrypt diperoleh dengan certbot dan pembaruan otomatis juga ditangani,” tetapi saya memakai konfigurasi lintas region dengan dua server dan geo-DNS, jadi certbot hanya berjalan di server AS dan saya harus menyalin sertifikat secara manual ke server Eropa
      Apakah ada cara untuk mengatasinya?
      Penjelasan tentang ClickHouse Playground ada di sini: https://ghe.clickhouse.tech/
    • Saya juga memakai pendekatan yang sama
      Saya juga menyajikan aplikasi web dari layanan statically compiled yang sama yang menyediakan API backend
      Dengan menjalankan npm build di CI lalu meng-embed hasilnya, menjadi sangat mudah untuk menjalankan pengujian lokal atau membuat instance demo