Pengelolaan Log di Satu VPS: Loki atau OpenSearch?
Kelola log di satu VPS mulai dari journald dan logrotate. Bandingkan kebutuhan RAM Loki dan OpenSearch, serta aturan retensi sebelum memilih.
Biaya nyata pengelolaan log yang di-host sendiri pada satu VPS
Pengelolaan log yang di-host sendiri pada satu VPS (virtual private server) bermuara pada satu pertanyaan: apakah Anda memerlukan cluster pencarian, atau cukup rotasi dan grep? Sebagian besar panduan vendor memulai dari tiga node dan RAM 12 GB sebelum satu baris log pun dikirim. Pada satu server, jawaban itu tidak berguna. Karena itu, perbandingan di bawah ini didasarkan pada kebutuhan setiap opsi pada server kecil sebelum menyimpan data apa pun.
Jika Anda menjalankan satu atau dua server dan ingin mengetahui apa yang terjadi Selasa lalu, systemd-journald dan logrotate sudah menangani pekerjaan tersebut. Anda dapat berhenti setelah bagian berikutnya. Jika log dari beberapa mesin harus dikirim ke satu tempat dan dapat dicari selama beberapa minggu, Grafana Loki cocok untuk server kecil karena yang diindeks adalah label, bukan teks baris log. Elasticsearch dan OpenSearch menyediakan pencarian teks lengkap yang sebenarnya. Keduanya memerlukan lebih banyak memori karena heap JVM (Java virtual machine) memiliki batas minimum yang tidak dapat Anda kurangi.
Mulai dengan journald karena biasanya masalah selesai di sini
systemd-journald sudah berjalan pada setiap server Ubuntu atau Debian saat ini. Layanan ini menangkap output standar dari setiap unit service, pesan kernel, dan semua yang dikirim ke syslog. Empat perintah mencakup sebagian besar insiden.
journalctl -u nginx.service --since "2026-08-14 09:00" --until "2026-08-14 10:00"
journalctl -p err -b
journalctl -f -u ssh.service
journalctl --disk-usagePerintah terakhir mencetak baris seperti Archived and active journals take up 1.1G in the file system.. Nilai itulah yang menentukan apakah Anda memerlukan langkah lain. Jika nilainya hanya beberapa ratus megabyte dan Anda dapat menemukan informasi yang diperlukan dengan -u dan --since, masalah sudah selesai.
Apakah journal tetap tersedia setelah reboot bergantung pada Storage= dan keberadaan /var/log/journal. Dengan pengaturan Storage=auto yang umum, journald menulis ke /var/log/journal jika direktori tersebut tersedia, dan ke /run/log/journal jika tidak tersedia. /run menggunakan memori sebagai media penyimpanan. Jadi, pada sistem yang tidak memiliki direktori tersebut, semua log terhapus saat reboot, tepat ketika Anda ingin membacanya. Image Ubuntu menyertakan direktori tersebut. Image minimal dan image berbasis container sering kali tidak menyediakannya.
ls -d /var/log/journal
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
journalctl --disk-usageSetelah restart, journalctl --disk-usage seharusnya melaporkan ukuran di bawah /var/log/journal, bukan /run. Nilai default sudah memiliki batas, dan itulah alasan utama journald merupakan solusi yang tepat, bukan sekadar alternatif. Halaman manual journald.conf menetapkan SystemMaxUse= sebesar 10% dari ukuran file system dan SystemKeepFree= sebesar 15%, serta membatasi setiap nilai default yang dihitung hingga 4G. SystemMaxFileSize= secara default bernilai seperdelapan dari SystemMaxUse=, dengan batas 128M, sehingga biasanya Anda menyimpan tujuh file hasil rotasi. MaxRetentionSec= secara default bernilai 0, yang menonaktifkan penghapusan berdasarkan usia. Perhatikan kembali nilai default terakhir tersebut: secara bawaan, journal dibatasi hanya berdasarkan ukuran, bukan usia.
[Journal]
Storage=persistent
SystemMaxUse=2G
MaxRetentionSec=30dayTulis konfigurasi tersebut ke /etc/systemd/journald.conf.d/99-size.conf, restart journald, lalu periksa apakah journalctl --disk-usage bergerak mendekati batas baru Anda. Untuk segera mengosongkan ruang, tanpa menunggu rotasi berikutnya, jalankan sudo journalctl --vacuum-size=500M atau sudo journalctl --vacuum-time=14d. Keduanya mencetak setiap file yang dihapus, sehingga eksekusi tanpa output berarti tidak ada yang perlu dihapus.
Semua log di luar journal, seperti /var/log/nginx/access.log, merupakan tugas logrotate, dan prosesnya berjalan setiap hari melalui systemd timer. Ada satu masalah yang perlu diketahui karena tampak seperti bug pada df. Setelah rotasi, file lama sudah tidak muncul dalam daftar direktori, tetapi daemon masih membukanya. Akibatnya, df -h melaporkan disk penuh, sedangkan du -sh /var/log melaporkan penggunaan yang jauh lebih kecil. Ruang tersebut baru tersedia kembali ketika proses membuka ulang log-nya. Itulah fungsi baris reload postrotate dalam konfigurasi. sudo lsof -nP +L1 menampilkan file yang sudah dihapus tetapi masih terbuka, serta nama proses yang masih membukanya. Uji sebuah aturan tanpa mengubah apa pun menggunakan sudo logrotate -d /etc/logrotate.d/nginx.
Mengirim log dari beberapa server ke satu collector
Setelah jumlah server lebih dari satu, mengelola beberapa server Linux sekaligus menjadi lebih mudah jika log dikirim ke satu lokasi. Sebagian besar distribusi sudah menyertakan rsyslog, sehingga collector terpusat paling sederhana adalah satu file pada setiap pengirim.
*.* action(type="omfwd" target="logs.example.com" port="514" protocol="tcp")Simpan konfigurasi tersebut sebagai /etc/rsyslog.d/50-forward.conf, lalu periksa dengan sudo rsyslogd -N1. Perintah ini memvalidasi konfigurasi dan keluar tanpa menjalankan apa pun. Setelah itu, restart rsyslog. Pada collector, aktifkan input TCP.
module(load="imtcp")
input(type="imtcp" port="514")Ada dua peringatan yang perlu diperhatikan. Syslog biasa tidak menyediakan enkripsi maupun autentikasi. Karena itu, apa pun yang dapat menjangkau port 514 dapat menyisipkan baris log yang terlihat persis seperti log Anda. Ikat layanan tersebut ke jaringan privat atau VPN, lalu batasi port itu dengan firewall. Kedua, antrean tindakan default disimpan di memori. Jika collector tidak dapat dijangkau, antrean akan penuh dan pesan akan dibuang tanpa salinan yang disimpan. rsyslog mendokumentasikan antrean berbantuan disk untuk kasus tersebut dalam tutorial penerusan yang andal.
Mengapa stack ELK tidak cocok untuk VPS kecil
ELK terdiri atas Elasticsearch untuk penyimpanan dan pencarian, Logstash untuk pipeline ingestion, serta Kibana untuk antarmuka. Batas minimumnya adalah heap JVM, dan heap ini ditetapkan sebelum log apa pun masuk.
Dokumentasi Elastic menyarankan agar heap ditetapkan tidak lebih dari 50% total memori yang tersedia pada setiap node Elasticsearch, karena proses tersebut juga menggunakan buffer off-heap dan bergantung pada cache file sistem operasi untuk membaca file indeks dengan cepat. Jadi, heap 2 GB memerlukan mesin 4 GB, sebelum memperhitungkan Kibana dan fungsi utama server tersebut. Elastic juga menyatakan bahwa Elasticsearch menghitung ukuran heap secara otomatis berdasarkan peran node dan total memori. Akibatnya, mesin kecil mendapatkan heap kecil, lalu terus menghabiskan waktunya untuk garbage collection.
Logstash adalah komponen yang langsung melampaui anggaran kecil. Halaman pengaturan JVM milik Elastic sendiri menyarankan heap minimal 4GB dan maksimal 8GB untuk ingestion biasa. Itu sudah menghabiskan seluruh VPS 4 GB hanya untuk satu proses di tengah pipeline.
The data behind this chart
[
{
"label": "Loki plus Alloy (no JVM)",
"documented_heap_mb": 0
},
{
"label": "OpenSearch demo compose",
"documented_heap_mb": 512
},
{
"label": "OpenSearch production example",
"documented_heap_mb": 2048
},
{
"label": "Logstash recommended minimum",
"documented_heap_mb": 4096
}
]Nilai tersebut adalah angka yang dipublikasikan setiap proyek dalam dokumentasinya sendiri. Angka itu bukan hasil pengukuran pada mesin pengujian, dan beban kerja Anda dapat mengubahnya. File compose contoh OpenSearch menetapkan 512 MB per node untuk demo dan 2048 MB dalam contoh produksi, sedangkan batas bawah yang direkomendasikan untuk Logstash adalah 4096 MB. Kolom heap menampilkan 0 untuk Loki dan Alloy karena keduanya adalah program Go yang tidak memerlukan heap JVM untuk dicadangkan. Seluruh perbedaannya terlihat dalam satu angka: komponen JVM tetap mengambil alokasi tersebut, terlepas dari ada atau tidaknya log yang masuk.
Jika Anda tetap ingin menjalankan stack Elastic pada satu server kecil, hilangkan Logstash dan kirim log langsung ke Elasticsearch dengan collector ringan. Logstash digunakan untuk mengurai dan mengubah data dalam volume besar. Pada satu mesin, pekerjaan tersebut dapat dilakukan di edge atau dilewati.
Elasticsearch dan OpenSearch juga memerlukan vm.max_map_count dinaikkan menjadi 262144, karena keduanya memetakan file indeks ke memori dan batas default Linux terlalu rendah. Container yang berhenti beberapa detik setelah startup pada mesin baru biasanya disebabkan oleh hal ini, bukan masalah lain.
OpenSearch atau Elasticsearch: mana yang dapat Anda deploy?
Sejarah lisensinya singkat, tetapi hal ini menentukan apa yang boleh Anda jalankan. Pada Januari 2021, Elastic memindahkan Elasticsearch dan Kibana dari Apache 2.0 ke model dual SSPL (server side public license) dan Elastic License 2.0. AWS melakukan fork terhadap kode Apache 2.0 terakhir sebagai OpenSearch, yang tetap menggunakan Apache 2.0. Pada September 2024, Elastic menambahkan AGPLv3 (GNU Affero General Public License version 3) sebagai opsi lain untuk kode sumber gratis. Untuk satu orang yang melakukan self-hosting pada satu VPS, semua lisensi tersebut mengizinkan penggunaan yang Anda lakukan. Ketentuan lisensi mulai berdampak ketika Anda menawarkan software tersebut kepada orang lain sebagai managed service.
Perbedaan praktis pada server kecil lebih kecil daripada yang terlihat dari sejarahnya, karena keduanya menggunakan engine yang sama di bawahnya. Namanya berbeda: index lifecycle disebut ISM (index state management) di OpenSearch dan ILM (index lifecycle management) di Elasticsearch. Per Agustus 2026, OpenSearch 2.12 dan versi setelahnya tidak mau start jika password admin belum ditetapkan saat pertama kali dijalankan.
sudo sysctl -w vm.max_map_count=262144
printf 'vm.max_map_count = 262144\n' | sudo tee /etc/sysctl.d/99-opensearch.conf
docker run -d -p 9200:9200 -p 9600:9600 -e "discovery.type=single-node" \
-e "OPENSEARCH_INITIAL_ADMIN_PASSWORD=<custom-admin-password>" \
opensearchproject/opensearch:latestBaris sysctl -w menerapkan pengaturan tersebut sekarang, sedangkan file di /etc/sysctl.d/ adalah bagian yang tetap berlaku setelah reboot. Periksa apakah container sudah berjalan dengan curl -k -u admin:<password> https://localhost:9200. Container tersebut merespons melalui https menggunakan sertifikat demo, sehingga -k melewati verifikasi. Respons yang sehat berupa blok JSON kecil yang mencantumkan cluster dan versinya. Halaman instalasi OpenSearch juga memberi tahu pengguna Docker Desktop agar menyediakan memori minimal 4 GB untuk host. Ini menjadi indikasi yang wajar tentang resource yang diharapkan tersedia untuk proses tersebut.
Cara Loki tetap ringan: label, bukan indeks teks lengkap
Loki menyimpan satu indeks untuk label dan menyimpan baris log sebagai chunk terkompresi. Kueri terlebih dahulu memilih stream, lalu memfilter teks. {unit="ssh.service"} |= "Failed password" memilih stream berdasarkan labelnya, kemudian memindai chunk tersebut untuk mencari string. Tidak ada indeks untuk isi baris, sehingga proses ingestion tetap murah dan Loki tidak perlu menyimpan inverted index di memori. Beban dipindahkan ke waktu kueri. Ini merupakan pertukaran yang baik jika biasanya Anda sudah mengetahui service yang sedang diperiksa.
Dokumentasi Grafana menempatkan mode monolitik, yaitu seluruh Loki berjalan dalam satu proses dengan -target=all, pada volume baca dan tulis kecil hingga sekitar 20GB per hari. Satu VPS masih jauh di bawah batas tersebut.
Masalahnya adalah cardinality label. Setiap kombinasi nilai label yang berbeda menjadi satu stream, dan jumlah stream menentukan penggunaan memori serta ukuran indeks Loki. Label yang berisi alamat IP client atau identifier request membuat satu stream untuk setiap nilai. Akibatnya, web server yang sibuk dapat menghasilkan puluhan ribu stream dalam sehari, lalu proses terus membesar hingga dihentikan oleh kernel. Batasi label pada nilai yang dapat Anda hitung di atas kertas: unit, host, job, level. Simpan detail yang berubah-ubah di dalam baris log itu sendiri. Filter expression dapat menemukannya saat kueri dijalankan.
Instal Loki dan Alloy pada satu VPS
Dua proses menjalankan tugas ini. Loki menyimpan log dan menjawab kueri. Grafana Alloy membaca log lalu mengirimkannya. Promtail sebelumnya digunakan sebagai pengirim log, tetapi mencapai akhir masa pakainya pada 2 March 2026. Karena itu, instalasi baru menggunakan Alloy, dan contoh Docker milik Loki sekarang menyertakan konfigurasi Alloy.
wget https://raw.githubusercontent.com/grafana/loki/v3.7.0/cmd/loki/loki-local-config.yaml -O loki-config.yamlBaca file tersebut sebelum menggunakannya. File itu menetapkan path_prefix: /tmp/loki dengan chunk di bawah /tmp/loki/chunks. Konfigurasi ini benar untuk demo, tetapi tidak sesuai untuk server. Tidak ada data di bawah /tmp milik container yang bertahan setelah container dibuat ulang. Akibatnya, riwayat Anda hilang saat image diperbarui. Arahkan konfigurasi tersebut ke path yang Anda mount.
common:
instance_addr: 127.0.0.1
path_prefix: /loki
storage:
filesystem:
chunks_directory: /loki/chunks
rules_directory: /loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemorydocker volume create loki-data
docker run --name loki -d \
-v $(pwd):/mnt/config -v loki-data:/loki \
-p 127.0.0.1:3100:3100 \
grafana/loki:3.7.0 -config.file=/mnt/config/loki-config.yaml
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3100/readyPerintah terakhir seharusnya mencetak 200, karena /ready mengembalikan HTTP 200 setelah Loki siap menerima trafik. Hasil lain berarti proses masih berjalan atau konfigurasi ditolak. docker logs loki menunjukkan penyebabnya. Ada dua detail yang disengaja dalam perintah run tersebut. Port hanya dipublikasikan pada 127.0.0.1 karena konfigurasi contoh menyertakan auth_enabled: false dan Loki tidak menyediakan autentikasi pengguna bawaan. Karena itu, apa pun yang dapat menjangkau port 3100 dapat membaca semua log dan menulis log palsu. Pertahankan port tersebut di loopback, atau letakkan Loki di belakang VPN atau reverse proxy yang menyediakan autentikasi. Named volume penting karena image berjalan sebagai user loki dengan UID 10001. Dengan demikian, direktori host yang di-bind mount dan dimiliki oleh root tidak dapat ditulisi oleh container.
sudo apt-get update && sudo apt-get install -y gpg wget
sudo mkdir -p /etc/apt/keyrings/
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" \
| sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get install -y alloyAlloy membaca /etc/alloy/config.alloy. Konfigurasi ini mengambil journal sistem dan satu kumpulan file, lalu mengirimkan keduanya ke Loki lokal.
loki.write "local" {
endpoint {
url = "http://127.0.0.1:3100/loki/api/v1/push"
}
}
loki.relabel "journal" {
forward_to = []
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
}
loki.source.journal "read" {
forward_to = [loki.write.local.receiver]
relabel_rules = loki.relabel.journal.rules
labels = {job = "systemd-journal", host = "app-01"}
}
local.file_match "nginx" {
path_targets = [{"__path__" = "/var/log/nginx/*.log", "job" = "nginx", "host" = "app-01"}]
}
loki.source.file "nginx" {
targets = local.file_match.nginx.targets
forward_to = [loki.write.local.receiver]
}Aturan relabel menyalin field journal __journal__systemd_unit ke label bernama unit. Label inilah yang membuat {unit="ssh.service"} berfungsi nanti. Tanpa aturan tersebut, nama unit berada di dalam entry, bukan sebagai label. Anda tidak dapat memilih berdasarkan nama unit, sehingga setiap kueri harus memindai semua data.
sudo systemctl reload alloy
systemctl show -p User alloy
sudo journalctl -n 5
sudo -u alloy journalctl -n 5Sebagian besar setup berhenti pada tahap ini. Alloy berjalan sebagai service account sendiri, bukan sebagai root. Pembacaan journal sistem memerlukan keanggotaan dalam grup systemd-journal. File di bawah /var/log/nginx juga dimiliki oleh grup adm pada Debian dan Ubuntu. Ganti account pada perintah terakhir dengan account yang dicetak oleh systemctl show. Jika hasilnya jauh lebih sedikit daripada saat dijalankan sebagai root, account tersebut tidak dapat membaca journal sistem. Dalam kondisi itu, Loki tetap kosong, meskipun konfigurasi Anda benar. Tambahkan grup tersebut, lalu restart dengan sudo usermod -aG systemd-journal,adm alloy diikuti sudo systemctl restart alloy.
curl -G -s "http://127.0.0.1:3100/loki/api/v1/query_range" \
--data-urlencode 'query={job="systemd-journal"}' \
--data-urlencode 'limit=5' | jq '.data.result | length'Angka di atas 0 berarti ada stream dengan label tersebut dan stream itu berisi entry. Angka 0 berarti belum ada data yang masuk dengan label tersebut. Satu default sering menimbulkan peringatan yang keliru: loki.source.journal menetapkan max_age ke 7h. Karena itu, proses yang baru dimulai membaca journal selama tujuh jam terakhir dan tidak membaca data yang lebih lama. Untuk antarmuka pengguna, jalankan Grafana pada server yang sama dan arahkan data source Loki ke http://127.0.0.1:3100.. Log container memerlukan data source yang berbeda. Alloy menemukan container Docker yang sedang berjalan dan memantau lognya. Itulah yang dilakukan contoh getting started milik Loki. Pada cluster k3s satu node pada VPS, tugas tersebut beralih ke direktori log pod yang ditulis oleh kubelet.
Retensi: tentukan kapan log dihapus
Hampir tidak ada orang yang menentukan periode retensi sebelum disk penuh. Akibatnya, keputusan itu dibuat pada pukul 3 pagi ketika service sedang down. Tentukan sejak hari pertama berdasarkan 2 pertanyaan: seberapa jauh ke belakang Anda benar-benar perlu melihat log, dan data apa yang masih harus tersedia saat meninjau insiden bulan depan. Untuk satu server, periode 14 hingga 30 hari biasanya memenuhi kedua kebutuhan tersebut.
Loki tidak menghapus apa pun sampai Anda mengaktifkan compactor. Retensi secara default nonaktif. Hal ini sering mengejutkan pengguna ketika volume penuh karena retention_period hanya ada di konfigurasi tanpa melakukan apa pun.
limits_config:
retention_period: 744h
compactor:
working_directory: /loki/retention
compaction_interval: 10m
retention_enabled: true
retention_delete_delay: 2h
retention_delete_worker_count: 150
delete_request_store: filesystem744h berarti 31 hari. Ada 4 aturan terdokumentasi yang mengatur blok tersebut:
- Retensi diterapkan oleh compactor. Dokumentasi Grafana menyarankan agar compactor dijalankan sebagai satu instance. Pada satu VPS, kondisi ini terpenuhi dengan sendirinya.
- Periode retensi minimum adalah 24h. Retensi hanya berfungsi jika periode index adalah 24h. Contoh
schema_configsudah menggunakanperiod: 24h, jadi jangan mengubahnya. delete_request_storediperlukan setelahretention_enabledbernilai true. Parameter ini menentukan store yang menyimpan permintaan penghapusan. Pada node tunggal berbasis filesystem, nilainya sama denganobject_store: filesystemyang sudah ada dalam schema.- Chunk terlebih dahulu ditandai, lalu dihapus setelah
retention_delete_delay, yaitu 2h dalam konfigurasi ini. Karena itu, ruang kosong baru tersedia setelah jeda yang lebih lama daripada yang tersirat oleh kebijakan. Jangan menilai pengaturan berdasarkandf5 menit setelah reload.
OpenSearch menghapus seluruh index, bukan baris individual. Karena itu, index log dibuat per hari. Kebijakan ISM memindahkan index melalui beberapa state, lalu menghapusnya setelah mencapai usia tertentu. Sementara itu, ism_template menerapkan kebijakan tersebut ke index baru sehingga Anda tidak perlu mengingatnya secara manual.
Kebijakan ISM yang menghapus index log setelah 14 hari
{
"policy": {
"description": "delete log indexes after 14 days",
"default_state": "hot",
"states": [
{
"name": "hot",
"actions": [],
"transitions": [
{ "state_name": "delete", "conditions": { "min_index_age": "14d" } }
]
},
{
"name": "delete",
"actions": [ { "delete": {} } ],
"transitions": []
}
],
"ism_template": { "index_patterns": ["logs-*"], "priority": 100 }
}
}Buat kebijakan tersebut dengan PUT ke _plugins/_ism/policies/logs-retention. Template berlaku untuk index yang dibuat setelah kebijakan tersedia. Oleh karena itu, kebijakan harus diterapkan secara manual ke data yang sudah ada di disk.
Apa pun sistem yang Anda gunakan, angka retensi hanya efektif jika disertai pemeriksaan ruang kosong. Menghapus log setelah 14 hari tidak akan membantu jika log selama 10 hari sudah memenuhi volume. Karena itu, terapkan kebijakan tersebut bersama pemantauan kesehatan disk pada VPS dan alert ketika penggunaan mencapai 80%.
Berapa kebutuhan disk per GB log
Jawaban yang tepat bergantung pada jumlah baris dan field Anda. Karena itu, ukur pada data Anda sendiri dan jangan mengandalkan rasio yang dipublikasikan. Mekanismenya cukup berbeda sehingga arah perbedaannya dapat diperkirakan. OpenSearch dan Elasticsearch menulis inverted index untuk setiap field yang diindeks, selain menyimpan dokumen. Karena itu, data yang tersimpan di disk lebih besar daripada teks mentahnya, dan setiap replica menggandakannya. Pada satu node, atur jumlah replica ke 0. Replica shard pada node yang sama tidak dapat bertahan jika node tersebut gagal. Jika nilainya tetap 1, penggunaan disk menjadi dua kali lipat dan status kesehatan cluster akan terus berada pada kondisi yellow. Loki menulis chunk terkompresi serta index label berukuran kecil. Karena itu, penggunaan disk-nya mengikuti ukuran baris yang telah dikompresi.
sudo du -sh /var/lib/docker/volumes/loki-data/_data
curl -k -u admin:<password> "https://localhost:9200/_cat/indices?v&h=index,docs.count,store.size"Jalankan metode yang sesuai selama 2 hari berturut-turut. Selisihnya adalah pertumbuhan harian. Kalikan dengan jumlah hari retensi, lalu tambahkan sekitar 30% ruang cadangan untuk compaction dan merge. Setelah itu, bandingkan hasilnya dengan kapasitas volume. Jika tidak mencukupi, kurangi retensi sebelum membeli disk. Volume yang lebih besar hanya menunda masalah yang sama selama beberapa minggu.
Apa yang pertama kali rusak pada server kecil
Memori habis lebih dahulu. OOM (out of memory) killer pada kernel memilih proses berukuran besar, dan proses terbesar pada server logging adalah JVM. journalctl -k | grep -i "killed process" menampilkan proses yang dihentikan, dengan nama proses di dalam tanda kurung siku. Proses yang dihentikan tidak selalu merupakan log stack: sshd atau database Anda juga dapat terpilih. Inilah cara eksperimen logging dapat menghentikan aplikasi yang ingin Anda ambil lognya. Tetapkan batas maksimum secara eksplisit untuk container agar kegagalan terjadi pada komponen yang Anda tentukan. Itulah fungsi batas memori di Docker Compose.
Disk menjadi masalah berikutnya, dan search engine gagal dengan cara yang spesifik serta mudah dikenali. Elasticsearch dan OpenSearch memantau penggunaan disk pada beberapa tingkat. Low watermark berada pada 85% dan high watermark pada 90%. Pada flood stage sebesar 95%, setiap index yang memiliki shard pada node tersebut menerima block index.blocks.read_only_allow_delete, lalu operasi tulis gagal dengan blocked by: [FORBIDDEN/12/index read-only / allow delete (api)]. Block dilepas setelah penggunaan disk kembali di bawah high watermark. Kosongkan ruang terlebih dahulu, lalu hapus block secara manual hanya jika block tersebut tetap ada.
curl -k -u admin:<password> -X PUT "https://localhost:9200/_all/_settings" \
-H 'Content-Type: application/json' \
-d '{"index.blocks.read_only_allow_delete": null}'Loki gagal dengan lebih senyap. Loki tidak memiliki mode read-only, sehingga volume yang penuh terlihat sebagai push yang gagal pada pengirim dan celah pada hasil query. Masalah cardinality muncul sebagai peningkatan penggunaan memori secara perlahan, bukan sebagai error. Pantau ukuran direktori chunks secara berkala, bukan setelah insiden.
Kegagalan terakhir terjadi saat data yang dimasukkan tidak sesuai. Sistem log bukan sistem metrik: beban CPU yang diambil setiap 10 detik dan disimpan sebagai teks membutuhkan banyak ruang serta sulit dibuat grafiknya. Pekerjaan itu lebih sesuai untuk sesuatu seperti server monitoring Zabbix pada Ubuntu 24.04. Exception aplikasi memerlukan pengelompokan, deduplikasi, dan tampilan stack trace. Pekerjaan itu sesuai untuk pelacak error yang di-host sendiri. Mengetahui bahwa situs sedang tidak aktif merupakan pekerjaan yang berbeda, dan dapat ditangani oleh uptime dan status page seperti Uptime Kuma. Gunakan sistem log untuk baris teks yang akan dibaca manusia.
FAQ
Apakah saya memerlukan Elasticsearch untuk mencari log server?
Tidak untuk satu atau dua server. journalctl sudah memfilter berdasarkan unit, prioritas, boot, dan rentang waktu, sedangkan file yang dirotasi menjawab kebutuhan grep dan zgrep. Search cluster bermanfaat ketika Anda memiliki banyak mesin, perlu melakukan pencarian teks bebas di semua mesin sekaligus, atau beberapa orang memerlukan antarmuka bersama. Untuk skala di bawah itu, journald dengan batas ukuran dan waktu retensi dapat menjalankan fungsi yang sama tanpa memerlukan RAM tambahan.
Berapa banyak RAM yang saya perlukan untuk pengelolaan log self-hosted?
Gunakan angka yang dipublikasikan oleh setiap proyek, bukan aturan umum. Loki dan Alloy adalah program Go yang tidak perlu mencadangkan heap sejak awal, dan Grafana mendokumentasikan Loki monolitik hingga sekitar 20GB per hari. Compose contoh OpenSearch menetapkan heap 512 MB untuk demo dan 2 GB dalam contoh production, sedangkan Elastic menyatakan bahwa heap harus tetap pada atau di bawah 50% dari total memori. Jadi, heap 2 GB memerlukan mesin 4 GB sebelum Kibana dijalankan. Dokumentasi Logstash sendiri merekomendasikan heap minimal 4GB. Angka tersebut adalah pengaturan yang didokumentasikan, bukan hasil benchmark. Ukur beban Anda sendiri sebelum menentukan ukuran deployment.
Apa perbedaan sebenarnya antara Loki dan OpenSearch untuk log?
Model indeksnya. Loki hanya mengindeks label dan menyimpan isi log sebagai chunk terkompresi yang dipindai saat query dijalankan. Karena itu, penulisan lebih ringan, tetapi query yang luas memerlukan biaya lebih besar. OpenSearch mengindeks isi field, sehingga pencarian teks penuh yang bebas berlangsung cepat. Namun, indeks tersebut menggunakan memori dan disk. Pilih Loki jika Anda mengetahui service dan rentang waktu yang ingin dicari. Pilih OpenSearch jika Anda perlu mencari teks yang tidak dapat diprediksi sebelumnya.
Berapa lama log sebaiknya disimpan di VPS?
Tentukan jumlah harinya sebelum disk menentukannya untuk Anda. Tetapkan retensi di tepat satu tempat untuk setiap sistem: MaxRetentionSec= dan SystemMaxUse= untuk journald, retention_period dengan compactor yang diaktifkan untuk Loki, dan kebijakan ISM dengan min_index_age untuk OpenSearch. Untuk sebagian besar setup satu server, 14 hingga 30 hari cukup untuk debugging dan peninjauan insiden. Data yang harus disimpan lebih lama perlu ditempatkan dalam salinan di luar server, karena log yang hanya disimpan pada server yang gagal bukan merupakan catatan yang andal.
Apakah Promtail masih digunakan untuk mengirim log ke Loki?
Tidak. Promtail mencapai akhir masa dukungan pada 2 March 2026, dan Grafana Alloy menggantikannya. Contoh instalasi Docker milik Loki kini menyertakan konfigurasi Alloy, dan Grafana menyediakan converter yang mengubah konfigurasi Promtail yang ada menjadi sintaks Alloy. Instalasi Promtail yang sudah ada tetap berjalan, tetapi tidak lagi menerima perbaikan. Karena itu, perlakukan migrasi sebagai pemeliharaan, bukan upgrade yang dapat ditunda tanpa batas.