Cara Saya Mengoperasikan Server (2022)
(blog.wesleyac.com)- 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
.backupdan 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
scpataursync
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
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
Cukup letakkan file teks kecil di
/etc/sysusers.d/yang berisi informasi seperti nama pengguna dan direktori home, lalu jalankan perintah atau layanansysusersFungsi 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 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
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%
Tetapi agar aplikasi benar-benar berguna, tetap butuh elemen lain seperti database, dan bukankah di situ biaya tambahan mulai muncul?
Saya bingung apakah ini handler permintaan pada server HTTP, komputasi cloud berbasis layanan fungsi, atau cuma RPC model lama
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 httpsTarget
makemenangani pekerjaan yang diperlukan untuk menjalankan situs, seperti memasang alat Nginx, certbot, sbcl, mengatur konfigurasi Nginx, menyiapkan sertifikat, dan setelah perintah selesai situs web langsung dipublikasikanDari 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
Untuk mengurangi downtime, Anda bisa mencoba
health_uri /health,lb_try_duration 30sMisalnya 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 naikIdealnya, 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
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
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
Setidaknya sampai jumlah pelanggan atau pengguna benar-benar tumbuh sehingga masalah skala menjadi nyata
docker savedandocker importSaya 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
$HOMEke HDD eksternal setiap hari pukul 5 sore; file cache dikecualikan, tetapi dump database tetap disertakanOS-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
Akhir-akhir ini saya memakai Dokku di satu server
Mudah dikelola, dan developer bisa deploy hanya dengan
git pushseperti gaya Heroku, plus ada banyak plugin sehingga menambahkan database atau mengatur HTTPS pun bisa selesai dalam paling lama 10 detikgit pushitu benar-benar nyamanMenambahkan 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
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
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
Dengan Rust, terutama untuk cross-compiling dari Mac ke Linux, prosesnya relatif menyakitkan, sedangkan di Go itu sepele dan sudah terintegrasi ke dalam tool
goIni benar-benar memudahkan menghilangkan friksi saat menyelesaikan dan mendeploy side project
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 menyajikan aplikasi web dari layanan statically compiled yang sama yang menyediakan API backend
Dengan menjalankan
npm builddi CI lalu meng-embed hasilnya, menjadi sangat mudah untuk menjalankan pengujian lokal atau membuat instance demo