The Twelve-Factor App pada tahun 2011
(12factor.net)- The Twelve-Factor App adalah metodologi untuk menjalankan dan menskalakan aplikasi web·SaaS dalam jangka panjang, yang mencakup otomatisasi konfigurasi·portabilitas·deployment cloud·continuous deployment
- Tidak terikat pada kombinasi bahasa pemrograman tertentu atau backing service seperti database·queue·cache memori, sehingga dapat diterapkan pada berbagai aplikasi berbasis layanan
- Disusun berdasarkan pengalaman terlibat langsung dalam pengembangan·deployment ratusan aplikasi di platform Heroku, serta pengamatan tidak langsung atas pengembangan·operasi·skalabilitas ratusan ribu aplikasi
- Kesadaran masalah utamanya adalah menyediakan kosakata bersama untuk mengurangi biaya kolaborasi dan erosi perangkat lunak yang muncul saat aplikasi bertumbuh secara organik
- Dapat digunakan sebagai standar praktis tidak hanya oleh pengembang yang membuat aplikasi layanan, tetapi juga oleh engineer operasi yang melakukan deployment dan pengelolaannya
12 prinsip operasional untuk aplikasi SaaS
- Perangkat lunak modern sering disediakan dalam bentuk aplikasi web atau SaaS, dan Twelve-Factor App adalah metodologi untuk membangun aplikasi semacam ini
- Tujuannya adalah membuat proses pengembangan·deployment·operasi aplikasi menjadi lebih dapat diprediksi
- Menggunakan otomatisasi konfigurasi deklaratif untuk mengurangi waktu dan biaya saat pengembang baru bergabung ke proyek
- Menetapkan kontrak yang jelas dengan sistem operasi dasar untuk meningkatkan portabilitas antar lingkungan eksekusi
- Dirancang agar sesuai dengan deployment platform cloud modern sambil mengurangi beban pengelolaan server dan sistem
- Mengurangi perbedaan antara lingkungan pengembangan dan produksi sehingga memungkinkan continuous deployment
- Memungkinkan skalabilitas tanpa perlu banyak mengubah tool, arsitektur, atau praktik pengembangan
- Cakupan penerapannya tidak terbatas pada stack teknologi tertentu
- Dapat diterapkan pada aplikasi yang ditulis dengan bahasa pemrograman apa pun
- Backing service mencakup database, queue, cache memori, dan sebagainya
Latar belakang yang dirangkum dari pengalaman Heroku
- Para kontributor terlibat langsung dalam pengembangan·deployment ratusan aplikasi di platform Heroku, dan mengamati secara tidak langsung pengembangan·operasi·skalabilitas ratusan ribu aplikasi
- Berdasarkan pengalaman dan pengamatan dari aplikasi SaaS nyata, mereka merangkum praktik ideal dalam pengembangan aplikasi
- Memperhatikan alur ketika aplikasi bertumbuh secara organik seiring waktu
- Membahas cara banyak pengembang berkolaborasi pada codebase yang sama
- Memandang menghindari biaya erosi perangkat lunak sebagai tujuan penting
- Bentuk penyajiannya terinspirasi oleh Patterns of Enterprise Application Architecture dan Refactoring karya Martin Fowler
12 faktor
- I. Codebase: memiliki satu codebase yang dikelola dengan version control dan banyak deployment
- II. Dependencies: mendeklarasikan dan mengisolasi dependensi secara eksplisit
- III. Config: menyimpan konfigurasi di environment
- IV. Backing services: memperlakukan backing service sebagai resource yang terhubung
- V. Build, release, run: memisahkan tahap build dan tahap run secara ketat
- VI. Processes: menjalankan aplikasi sebagai satu atau lebih proses stateless
- VII. Port binding: menyediakan layanan ke luar melalui port binding
- VIII. Concurrency: melakukan scale-out dengan model proses
- IX. Disposability: meningkatkan ketangguhan dengan startup cepat dan shutdown yang elegan
- X. Dev/prod parity: menjaga development, staging, dan production semirip mungkin
- XI. Logs: memperlakukan log sebagai aliran event
- XII. Admin processes: menjalankan tugas administratif sebagai proses sekali jalan
1 komentar
Komentar Hacker News
12-Factor App adalah rekomendasi yang dibuat pada 2011 dengan lebih bertumpu pada Heroku dan keterbatasan infrastruktur berbasis kontainer saat itu, bukan dokumen yang tampak berlandaskan prinsip rekayasa secara mendalam.
Misalnya, klaim bahwa konfigurasi harus dimasukkan ke variabel lingkungan muncul karena para penulisnya bekerja di Heroku, dan Heroku mengisi variabel lingkungan melalui kolom input aplikasi web.
Jika ingin melacak riwayat konfigurasi dengan version control, memakai GitOps, memakai ConfigMap k8s, atau menaruh file konfigurasi di volume yang di-mount, semuanya pada umumnya adalah pilihan yang baik. Karena semua itu memisahkan status konfigurasi dari status deployment aplikasi.
Dokumen ini mencampuradukkan gambaran besar dan detailnya, serta memberi rekomendasi yang lebih disesuaikan dengan fitur produk perusahaan penulisnya daripada prinsip rekayasa nyata, jadi saya melihatnya sebagai pedoman yang merugikan.
Jika ingin penyimpanan konfigurasi yang aman di Kubernetes, pada akhirnya Anda akan memakai Secrets, dan itu berbentuk key-value seperti variabel lingkungan. Untuk mendapatkan keamanan serupa di Git, diperlukan lapisan enkripsi, dan itu merusak diff serta membutuhkan alat tambahan.
Pada akhirnya kita kembali ke alasan mengapa alat deployment level tinggi seperti Heroku dibuat.
Prinsip bahwa log diperlakukan sebagai stream masih benar. Tulis log ke STDOUT, bukan ke file, lalu biarkan orchestrator membaca dan menyimpannya.
Konfigurasi diambil dari lingkungan. Aplikasi cenderung membaca konfigurasi dari sumber yang berbeda sesuai cara deployment, seperti .env secara lokal dan secret store di production.
Port binding juga sama: aplikasi membuka port, lalu menaruh sesuatu seperti nginx di depan untuk membentuk reverse proxy. Service dan ingress K8S menjalankan peran itu.
Kritik terbesar terhadap 12-Factor saat ini adalah bahwa dokumennya sebenarnya tidak ditulis dengan baik, dan mengasumsikan pembaca sudah tahu persis apa yang sedang dibicarakan.
Namun saya mengakui jasa Heroku dalam memopulerkan konsep ini dan memperluas penggunaannya.
Bukan dengan menambahkan settings.json ke mesin sebelum menjalankan aplikasi, melainkan jika source yang sama di-deploy ke klaster AKS Azure EU north, ia memakai nilai yang dikonfigurasi di klaster itu; jika di-deploy ke klaster RPi Zero Docker Swarm di dalam bingkai Ikea, ia memakai konfigurasi klaster tersebut.
Nama klaster itu disebut Gibson.
Saya merasa masing-masing poin ini bisa dibantah dengan cukup masuk akal.
Pertama, prinsip satu aplikasi satu repositori tidak secara fundamental salah. Tidak masalah mengembangkan beberapa aplikasi dalam satu repositori jika mereka terikat kuat secara fungsional dan berbagi siklus rilis, tetapi perlu di-deploy terpisah demi mendapatkan keuntungan proses terpisah dan penskalaan independen. Yang terlintas adalah pemisahan API publik dan proses worker, misalnya Sidekiq di Ruby, Celery di Python, atau konsumen Kafka pada umumnya.
Kedua, pernyataan “aplikasi 12-Factor tidak bergantung pada keberadaan implisit paket sistem global” sangat sulit dicapai dalam praktik kecuali memakai sesuatu seperti Nix. Dependensi pada API system call kernel pun bocor, dan sebagian besar aplikasi Rust, kecuali musl, bergantung secara implisit pada glibc. Itu ada di distribusi Linux utama selain distro slim seperti Alpine. Saya melihat Docker sebagai penopang yang dibutuhkan untuk meredakan masalah ini.
Ketiga, menyimpan konfigurasi di variabel lingkungan tampak lebih rapuh. Itu bisa menurunkan keamanan karena memaksa secret dimasukkan ke lingkungan, dan membuat kita melepas konfigurasi file terstruktur yang memberi type safety, autocomplete IDE, dan parsing otomatis. Di variabel lingkungan, kita harus mengimplementasikan sendiri parser untuk nilai konfigurasi kompleks yang bukan string. Dalam praktiknya, konfigurasi sering juga disimpan sebagai file .env dan di-commit ke repositori, sehingga argumen soal keamanan commit menjadi tidak berarti.
Maksudnya bukan jangan bergantung pada sistem operasi termasuk glibc, melainkan jangan membuat aplikasi hanya bisa berjalan jika paket dari package manager bahasa tertentu sudah terpasang di mesin.
Saya setuju dengan kritik lainnya. Khususnya soal variabel lingkungan, itu lebih mirip para penulis menganggap praktik yang umum di pengembangan Ruby saat itu sebagai praktik terbaik, bukan saran yang benar-benar punya dasar.
Karena itu kita harus bersiap untuk situasi seperti itu, dan untuk memudahkan pengujian berbagai kombinasi versi, saya rasa repositori terpisah lebih baik.
Sebagian besar bahasa tingkat tinggi tidak bergantung pada glibc tertentu. Jika runtime bahasa tersebut berjalan dengan benar, aplikasi juga berjalan di atasnya. Tentu dalam beberapa kasus kita akan memakai sesuatu seperti Docker. Hanya karena sulit bukan berarti tidak bernilai.
Bahasa dinamis atau bahasa terkelola sering kali bisa mengabaikan sebagian besar kerepotan terkait paket sistem.
Secara umum saya menyukainya, tetapi sudah terlalu sering orang nonteknis atau setengah teknis mengeluarkan “12 factor” seperti kartu kuning serbaguna untuk memperlambat rilis, sampai-sampai saya hampir sepenuhnya mengabaikannya
Sebenarnya “agile” juga mirip. Saya memahami maksud panduan semacam ini, tetapi nilai praktisnya tampak jauh lebih berguna bagi orang-orang yang hanya bisa menawarkan kepemimpinan teknis ala menara gading
Saya pernah menghadapi engineer junior yang terlalu bersemangat atau calon arsitek yang memakai tulisan 12-Factor seolah-olah itu syarat wajib untuk setiap peluncuran
Hal-hal seperti ini adalah tujuan yang baik untuk dikejar, tetapi di dunia nyata kita harus berkompromi demi peluncuran dan perlu menjelaskan dengan tegas serta konsisten bagian mana yang akan diperlambat atau ditunda
Penyimpangan kecil tidak akan membuat rilis diblokir, tetapi jika secara keseluruhan tidak selaras, setidaknya itu harus diperlakukan sebagai utang teknis. Jika suatu rilis mundur besar pada salah satu poin, adil saja untuk memblokirnya, atau setidaknya memaksa peninjauan yang lebih rinci tentang mengapa trade-off itu dianggap layak
Jika organisasi mengadopsi 12FA sebagai praktik terbaik, itu memang harus dipenuhi, tetapi tidak boleh memblokir deployment
12FA bukan satu kotak tunggal yang harus dicentang. Seiring produk matang, tiap poin bisa—dan umumnya seharusnya—diimplementasikan dengan menambahkannya satu per satu ke produk, bahkan dipecah lebih kecil bila perlu
Jika sejak awal engineering-nya baik, yaitu ada abstraksi dan interface yang tepat serta tidak semuanya di-hardcode, ini seharusnya tidak menjadi masalah
YAGNI juga disalahgunakan sebanyak 12FA. Hal terbesar yang kurang dari 12FA adalah contoh konkret yang bisa dijadikan acuan oleh engineer junior
Saya belajar untuk menanggapi masalah seperti ini dengan itikad baik. Perlu dilihat mengapa masalah diajukan, apakah proses yang ada tidak jelas, cacat, atau kurang dapat dipercaya
Jika ditemukan kekhawatiran yang masuk akal, proses bisa diubah dan dokumentasinya diperbarui
Twelve-Factor App mengatakan gunakan environment untuk konfigurasi, sedangkan Docker mengatakan jangan gunakan environment untuk konfigurasi, karena itu tidak aman
Saya menyukai dan memakai banyak pola dari 12-Factor, tetapi sebagian di antaranya ditulis dalam konteks VPS. Saat itu environment stabil, aman, dan lebih tetap, tetapi pada container environment bisa saja terbawa di layer mana pun
Di era container, poin spesifik ini cukup menjadi bahan pertimbangan. Docker secrets juga tidak selalu cocok, sehingga perlu berbagai akrobat agar bisa berjalan
Menyuntikkan secret ke aplikasi sebagai environment variable tidak berarti seluruh penanganan di luarnya juga tidak aman. Misalnya, container AWS ECS memiliki dukungan bawaan untuk mengambil secret dari Secret Manager saat startup dan meneruskannya sebagai environment variable: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/...
Secret diambil saat container mulai, menggunakan kredensial IAM milik aplikasi yang sedang berjalan untuk mengambilnya dari Secret Manager. Jadi harus ada izin atas secret tersebut
Manfaat memakai environment variable tampaknya sepenuhnya bergantung pada bagaimana variabel itu akhirnya disetel. Dengan mekanisme seperti ini, saya tidak melihat kelemahan besar
Kelemahan utama yang terlihat adalah malware umum yang mencoba men-dump environment variable bisa menangkap nilainya, tetapi jika secret tidak dihindari dari penyimpanan permanen di memori, upaya mencegah ancaman itu pada umumnya lebih mirip obfuscation
Private key yang terekspos seperti itu benar-benar harus dihindari
Saya tidak punya masalah berarti dengan cara k8s me-mount secret ke file system. Tentu saja semua ini bergantung pada lingkungan deployment
Kadang environment variable adalah pilihan yang lebih tidak buruk. Karena secret memang selalu sulit
Jika kita memercayai input secara membabi buta hanya karena single-tenant, nanti ketika kondisi berubah akibat pivot bisnis yang sewenang-wenang, kita akan menghadapi bug yang sulit dilacak dan malam-malam panjang
Selama beberapa tahun terakhir, saya banyak membahas aplikasi 12-Factor dan juga melihat banyak kebingungan. Situs 12-Factor memang bagus, tetapi terutama berguna bagi orang yang sudah memahami mengapa poin-poin itu penting.
Bagi orang yang tidak tahu alasan di balik aturannya, perlu penjelasan yang lebih mendalam. Karena itu saya membuat video “What are 12 Factor Apps and Why Should You Care?”[1], dan saya dengar beberapa perusahaan menggunakannya untuk pelatihan karyawan baru engineer/DevOps dan sangat membantu.
Dari mana pun Anda mempelajarinya, aplikasi 12-Factor layak dipelajari selama satu atau dua jam. Sebagian besar “aturan” itu adalah hal-hal yang perlu disadari, dan tidak langsung jelas sebelum Anda sendiri melakukan kesalahan dan merasakan sakitnya.
[1] https://youtu.be/REbM4BDeua0
Konsep “unit test” mulai mendapat traction sekitar akhir 90-an, dan saya lega karena ada seseorang yang berwibawa yang mendukungnya.
Alasan saya tidak melakukannya sendiri sampai Kent Beck merilis JUnit adalah karena kode yang saya kerjakan tidak memiliki struktur yang mudah dikendalikan oleh kode lain. Karena variabel global, state yang tersebar luas, ketergantungan pada sistem eksternal dan layout sistem berkas tertentu, serta pengabaian modularitas, tidak ada apa pun yang bisa dijalankan di luar konteks yang memang dirancang untuknya.
Semua itu adalah “desain buruk”, tetapi deadline terpenuhi, jadi semua orang melakukannya. Saya berharap ketika unit test mendapat traction, programmer akan keluar dari desain monolitik.
Setelah 25 tahun melihat unit test untuk getter/setter, dan satu unit test raksasa yang membuat database in-memory karena setiap fungsi dalam aplikasi membutuhkan live database hanya untuk mencoba berjalan, lalu akhirnya gagal dan dikomentari, saya kehilangan keyakinan bahwa unit test akan menjadi lebih dari sekadar checkbox yang tidak bermakna. Semua orang mengisinya karena itu “praktik terbaik”, tetapi tidak berhenti untuk memikirkan mengapa mereka melakukannya.
Saya selalu paling tidak setuju dengan saran soal konfigurasi. Konfigurasi akhirnya didefinisikan oleh banyak pihak, dan sering juga oleh developer, sehingga sering kali yang terbaik adalah menyertakan default yang masuk akal bersama aplikasi lalu menimpanya dengan file per lingkungan dan variabel lingkungan.
Untuk sebagian besar aplikasi sisi server, cara ini paling fleksibel. Sering kali kita sudah tahu konfigurasi apa yang seharusnya dipakai, dan itu sebaiknya dikelola dalam source control, tetapi nilai rahasia harus disuntikkan saat runtime.
Agar tidak menghabiskan waktu konfigurasi yang sangat besar di development, test, dan production, konfigurasi membutuhkan penimpaan hierarkis.
Jika untuk development, pada akhirnya itu akan merusak production, dan default production mungkin sama sekali tidak bermakna di development.
Salah satu cara memikirkan bagian konfigurasi adalah “apakah strategi konfigurasi ini cocok dengan container?” Jika Anda membangun image, Anda mendapatkan state disk statis yang perubahannya tidak bertahan kecuali membuat image baru.
Jika konfigurasi hanya berbasis file, Anda harus membangun image yang benar-benar baru untuk beralih antara perilaku test dan production.
Membuat konfigurasi bisa diubah terlepas dari disk dasar membantu mengisolasi perubahan. Anda perlu membedakan apakah aplikasi rusak karena deployment, yaitu pembuatan image, yang rusak, atau karena konfigurasinya salah.
Jika pembuatan image dan perubahan konfigurasi dipisahkan, pertanyaan itu sendiri hilang.
Misalnya, jangan sampai memungkinkan kesalahan manusia yang cukup mudah diprediksi, seperti QA mengirim 100 ribu percobaan ulang penolakan autentikasi ke antrean submit job production.
Namun banyak konfigurasi bukan soal infrastruktur, melainkan sering kali soal, misalnya, bean ResolverStrategy mana yang akan dihubungkan di tiap lingkungan.
Konfigurasi harus dikelola dengan version control, tetapi harus dikelola terpisah dari source code. Sebab konfigurasi menjelaskan deployment itu sendiri, bukan image yang dipakai untuk deployment.
Jelas ini adalah norma engineering yang berpengaruh. Sekarang ada banyak abstraksi hosting yang mudah seperti Render atau Vercel, tetapi rasanya agak aneh jika mengingat dokumen ini ditulis pada 2012 dan saat itu web app masih jauh lebih seperti Wild West dalam hal praktik umum yang diterima.
Hal besar yang hilang dari dokumen ini adalah justifikasi untuk aturan-aturannya. Hampir semuanya hanya berupa aturan.
Sulit menilai apakah aturan itu baik, dan dokumen ini tidak membantu untuk mengetahuinya.
Dari judulnya saja saya kira ini komentar tentang autentikasi dua faktor. Maksudnya aplikasi yang untuk sekali login meminta semuanya: foto paspor, pemindaian wajah, SIM, SMS, Google Authenticator, tautan email, kata sandi, dan sidik jari.
Pada masa awal Docker, saya melakukan cukup banyak pekerjaan untuk membuat WordPress berperilaku seperti Twelve-Factor App.
Secara tradisional WordPress tidak berperilaku begitu, dan sampai batas tertentu itu wajar. WordPress tumbuh di dunia tempat server berumur panjang dengan disk lokal yang bisa ditulis dan persisten adalah hal umum.
Sejak itu situasinya mungkin sudah banyak berubah. Ini cerita sekitar 2016, tetapi tantangannya benar-benar menarik.
Saya terkejut melihat betapa sederhananya berbagai hal menjadi, dan bisa melakukan load balancing ke beberapa node tanpa memikirkan session stickiness.
Tentu saja, dalam cara lain ini menjadi lebih sulit, seperti membutuhkan DB terpisah untuk sesi.