एका VPS वर Log Management: Loki की OpenSearch?
एका VPS वर log management साठी journald आणि logrotate कधी पुरेसे ठरतात, Loki किंवा OpenSearch साठी किती RAM लागते आणि retention कसे ठरवायचे ते जाणून घ्या.
एका VPS वर self-hosted log management साठी प्रत्यक्षात किती खर्च येतो
एका VPS (virtual private server) वर self-hosted log management करताना मुख्य प्रश्न एकच असतो: तुम्हाला search cluster आवश्यक आहे का, की rotation आणि grep पुरेसे आहेत? अनेक vendor guides एकही log line ship होण्यापूर्वीच तीन nodes आणि 12 GB RAM पासून सुरुवात करतात. एका server वर हा दृष्टिकोन उपयोगाचा नाही. त्यामुळे खालील तुलना प्रत्येक पर्यायाला काहीही साठवण्यापूर्वी छोट्या server कडून किती resources आवश्यक असतात यावर आधारित आहे.
तुम्ही एक किंवा दोन servers चालवत असाल आणि मागील मंगळवारी काय घडले हे जाणून घ्यायचे असेल, तर systemd-journald आणि logrotate हे काम आधीच करतात. अशा वेळी पुढील section नंतर थांबू शकता. अनेक machines चे logs एकाच ठिकाणी जमा करायचे असतील आणि अनेक आठवड्यांतील logs वर search हवे असेल, तर Grafana Loki छोट्या server साठी योग्य ठरते, कारण ते lines च्या text ऐवजी labels index करते. Elasticsearch आणि OpenSearch खरे full text search देतात. त्यासाठी memory लागते, कारण JVM (Java virtual machine) heap साठी आवश्यक किमान मर्यादेपेक्षा कमी जाता येत नाही.
journald पासून सुरुवात करा, कारण बहुतेक जण इथेच थांबतात
सध्याच्या कोणत्याही Ubuntu किंवा Debian सर्व्हरवर systemd-journald आधीपासून चालू असते. ती प्रत्येक service unit चे standard output, kernel messages आणि syslog कडे पाठवलेली कोणतीही माहिती संकलित करते. बहुतेक incidents साठी चार commands पुरेसे असतात.
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-usageशेवटचा command Archived and active journals take up 1.1G in the file system. सारखी ओळ दाखवतो. तुम्हाला आणखी काही आवश्यक आहे का हे ठरवणारी संख्या तीच असते. त्यात काहीशे megabytes दिसत असतील आणि -u आणि --since वापरून आवश्यक माहिती सापडत असेल, तर तुमचे काम पूर्ण झाले.
journal reboot नंतर टिकते की नाही हे Storage= आणि /var/log/journal अस्तित्वात आहे की नाही यावर अवलंबून असते. सामान्य Storage=auto setting वापरली असल्यास, ती directory उपलब्ध असेल तेव्हा journald /var/log/journal मध्ये आणि उपलब्ध नसेल तेव्हा /run/log/journal मध्ये लिहिते. /run हे memory backed आहे. त्यामुळे त्या directory शिवायच्या मशीनवर reboot वेळी सर्व logs पुसले जातात. Logs वाचण्याची आवश्यकता नेमकी त्याच वेळी असते. Ubuntu images मध्ये ही directory असते. Minimal आणि container based images मध्ये ती अनेकदा नसते.
ls -d /var/log/journal
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
journalctl --disk-usageRestart नंतर journalctl --disk-usage ने /run ऐवजी /var/log/journal पेक्षा कमी size दाखवायला हवा. Defaults आधीपासून मर्यादित असतात. त्यामुळे journald हा गंभीर उपाय ठरतो; तो केवळ fallback नाही. journald.conf man page SystemMaxUse= ला file system size च्या 10% वर आणि SystemKeepFree= ला 15% वर सेट करते. तसेच प्रत्येक calculated default ची कमाल मर्यादा 4G ठेवते. SystemMaxFileSize= चे default SystemMaxUse= च्या एक अष्टमांशाइतके असते आणि त्याची कमाल मर्यादा 128M असते. त्यामुळे सामान्यतः सात rotated files ठेवले जातात. MaxRetentionSec= चे default 0 असते. त्यामुळे age based deletion बंद राहते. शेवटचे default पुन्हा लक्षात घ्या: सुरुवातीच्या configuration मध्ये journal फक्त size नुसार मर्यादित असते; age नुसार कधीही नाही.
[Journal]
Storage=persistent
SystemMaxUse=2G
MaxRetentionSec=30dayही setting /etc/systemd/journald.conf.d/99-size.conf मध्ये लिहा, journald restart करा आणि journalctl --disk-usage तुमच्या नवीन ceiling कडे सरकले आहे का ते तपासा. पुढील rotation ची वाट न पाहता आत्ताच जागा मोकळी करायची असल्यास sudo journalctl --vacuum-size=500M किंवा sudo journalctl --vacuum-time=14d चालवा. दोन्ही commands हटवलेल्या प्रत्येक file ची नोंद दाखवतात. त्यामुळे command शांतपणे पूर्ण झाल्यास हटवण्यासाठी काहीही नव्हते.
journal च्या बाहेरील सर्व गोष्टी, जसे /var/log/nginx/access.log, हे logrotate चे काम आहे. ते systemd timer द्वारे दररोज चालते. एक failure माहिती असणे महत्त्वाचे आहे, कारण ते df मधील bug सारखे दिसते. Rotation नंतर जुनी file directory listing मधून गायब होते, पण daemon ती उघडी ठेवतो. त्यामुळे df -h disk full दाखवतो, तर du -sh /var/log खूपच कमी जागा वापरली असल्याचे दाखवतो. Process ने आपली log पुन्हा उघडल्यावरच जागा मोकळी होते. Configuration मधील postrotate reload line याच कारणासाठी असते. sudo lsof -nP +L1 deleted files ची यादी दाखवतो ज्या अजूनही उघड्या आहेत, तसेच प्रत्येक file उघडी ठेवणाऱ्या process चे नाव दाखवतो. काहीही बदल न करता rule ची चाचणी करण्यासाठी sudo logrotate -d /etc/logrotate.d/nginx वापरा.
एका collector कडे अनेक सर्व्हरचे logs पाठवणे
एकापेक्षा जास्त सर्व्हर असतील, तर एकाच वेळी अनेक Linux सर्व्हर व्यवस्थापित करणे त्यांचे logs एकाच ठिकाणी आल्यास सोपे होते. बहुतेक distributions मध्ये rsyslog आधीपासून install केलेले असते. त्यामुळे प्रत्येक sender वर एक file ठेवणे हा सर्वात कमी खर्चाचा central collector पर्याय आहे.
*.* action(type="omfwd" target="logs.example.com" port="514" protocol="tcp")ही configuration /etc/rsyslog.d/50-forward.conf म्हणून जतन करा. sudo rsyslogd -N1 वापरून ती तपासा. हा command configuration validate करतो आणि काहीही सुरू न करता exit होतो. त्यानंतर rsyslog restart करा. Collector वर TCP input enable करा.
module(load="imtcp")
input(type="imtcp" port="514")येथे दोन यांत्रिक स्वरूपाच्या सूचना आहेत. साध्या syslog मध्ये encryption किंवा authentication नसते. त्यामुळे port 514 पर्यंत पोहोचू शकणारी कोणतीही गोष्ट तुमच्या logs सारख्या दिसणाऱ्या log lines inject करू शकते. त्यामुळे ते private network किंवा VPN वर bind करा आणि firewall मध्ये हा port नियंत्रित करा. दुसरे म्हणजे, default action queue memory मध्ये असते. Collector unreachable असल्यास queue भरते आणि messages drop होतात; त्यांची कोणतीही copy जतन केली जात नाही. त्या परिस्थितीसाठी rsyslog च्या reliable forwarding tutorial मध्ये disk assisted queue चे वर्णन केले आहे.
लहान VPS वर ELK stack का बसत नाही
ELK म्हणजे storage आणि search साठी Elasticsearch, ingestion pipeline साठी Logstash आणि interface साठी Kibana. यातील किमान गरज JVM heap ची आहे. कोणतेही log येण्यापूर्वीच ती निश्चित केलेली असते.
Elastic च्या documentation नुसार, प्रत्येक Elasticsearch node साठी उपलब्ध एकूण memory च्या 50% पेक्षा जास्त heap ठेवू नये. कारण process off-heap buffers देखील वापरते आणि index files जलद वाचण्यासाठी operating system च्या file cache वर अवलंबून असते. त्यामुळे 2 GB heap साठी 4 GB मशीन आवश्यक ठरते. यामध्ये Kibana आणि सर्व्हर ज्या मूळ कामासाठी घेतला आहे ते अजून समाविष्ट नाही. Elastic असेही सांगते की Elasticsearch node च्या roles आणि एकूण memory नुसार heap आपोआप ठरवते. त्यामुळे लहान मशीनला लहान heap मिळतो आणि त्याचा बराच वेळ garbage collection मध्ये जातो.
लहान budget साठी सर्वात मोठी अडचण Logstash निर्माण करते. Elastic च्या स्वतःच्या JVM settings पृष्ठावर सामान्य ingestion साठी heap किमान 4GB आणि कमाल 8GB ठेवण्याची शिफारस आहे. 4 GB VPS ची संपूर्ण memory pipeline मधील एका process साठीच खर्च होते.
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
}
]ही मूल्ये प्रत्येक project ने आपल्या documentation मध्ये प्रकाशित केलेली आहेत. ती test box वर घेतलेली measurements नाहीत आणि तुमच्या workload नुसार बदलू शकतात. OpenSearch च्या sample compose file मध्ये demo साठी प्रत्येक node ला 512 MB आणि production example मध्ये 2048 MB सेट केले आहेत, तर Logstash साठी शिफारस केलेली किमान मर्यादा 4096 MB आहे. Loki आणि Alloy साठी heap column मध्ये 0 दिसते, कारण ते JVM heap reserve न करणारे Go programs आहेत. या फरकाचा सारांश एका संख्येत असा आहे: कोणतेही logs आले नाहीत तरी JVM component आपल्यासाठी ठरवलेली memory reserve करते.
तरीही एका लहान सर्व्हरवर Elastic stack वापरायचा असल्यास Logstash काढून टाका आणि light collector द्वारे थेट Elasticsearch मध्ये logs पाठवा. मोठ्या प्रमाणात parsing आणि transformation करण्यासाठी Logstash वापरले जाते. एका सर्व्हरवर हे काम edge वर करता येते किंवा पूर्णपणे वगळता येते.
Elasticsearch आणि OpenSearch दोघांमध्येही vm.max_map_count ची मर्यादा 262144 पर्यंत वाढवावी लागते. कारण ते index files memory map करतात आणि Linux ची default मर्यादा त्यांच्यासाठी खूप कमी असते. नवीन मशीनवर startup नंतर काही seconds मध्ये container बंद होत असेल, तर बहुतेक वेळा कारण हेच असते.
OpenSearch किंवा Elasticsearch: तुम्ही कोणते deploy करू शकता?
तुम्हाला काय चालवण्याची परवानगी आहे हे licence history ठरवते. जानेवारी 2021 मध्ये Elastic ने Elasticsearch आणि Kibana यांना Apache 2.0 वरून dual SSPL (server side public license) आणि Elastic License 2.0 model वर हलवले. AWS ने शेवटचा Apache 2.0 code fork करून OpenSearch तयार केले. OpenSearch अजूनही Apache 2.0 अंतर्गत आहे. सप्टेंबर 2024 मध्ये Elastic ने free source code साठी आणखी एक पर्याय म्हणून AGPLv3 (GNU Affero General Public License version 3) जोडले. एका VPS वर एका व्यक्तीसाठी self hosting करताना, यापैकी प्रत्येक licence तुम्ही करत असलेल्या वापराला परवानगी देतो. तुम्ही हे software इतर लोकांना managed service म्हणून देऊ करता तेव्हा licences लागू होतात.
लहान box वरचा व्यावहारिक फरक या इतिहासावरून वाटतो त्यापेक्षा कमी आहे, कारण दोन्हींच्या अंतर्गत तोच engine आहे. नावे वेगळी आहेत: OpenSearch मध्ये index lifecycle ला ISM (index state management) म्हणतात, तर Elasticsearch मध्ये त्याला ILM (index lifecycle management) म्हणतात. August 2026 पर्यंत, पहिल्यांदा run करताना admin password set केलेला नसेल, तर OpenSearch 2.12 आणि त्यानंतरच्या आवृत्त्या सुरू होण्यास नकार देतात.
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:latestsysctl -w मधील line हे setting आत्ताच लागू करते आणि /etc/sysctl.d/ मधील file reboot नंतरही ते setting टिकवून ठेवते. curl -k -u admin:<password> https://localhost:9200 वापरून container सुरू झाला आहे का ते तपासा. तो demo certificate वापरून https वर response देतो. त्यामुळे -k verification वगळते. Healthy reply मध्ये cluster आणि version ची नावे असलेला छोटा JSON block मिळतो. OpenSearch install page Docker Desktop वापरणाऱ्यांना host साठी किमान 4 GB memory उपलब्ध ठेवण्यासही सांगते. या process ला किती संसाधनांची अपेक्षा आहे, याचा हा योग्य संकेत आहे.
Labels वापरल्यामुळे Loki लहान राहतो: पूर्ण-मजकूर index ऐवजी labels
Loki labels वर एक index ठेवतो आणि log lines संकुचित chunks म्हणून साठवतो. Query प्रथम streams निवडते आणि नंतर text filter करते. {unit="ssh.service"} |= "Failed password" त्याच्या label वरून stream निवडतो आणि त्यानंतर त्या string साठी chunks scan करतो. Line च्या body ला कोणताही index तयार केला जात नाही. त्यामुळे ingestion स्वस्त राहते आणि memory मध्ये inverted index ठेवण्याची गरज नसते. त्याची किंमत query time वर मोजावी लागते. आपण सामान्यतः कोणती service पाहत आहात हे माहीत असल्यास ही योग्य देवाणघेवाण आहे.
Grafana च्या documentation नुसार monolithic mode म्हणजे -target=all सह Loki ची सर्व कार्ये एका process मध्ये चालवणे. हा mode दररोज अंदाजे 20GB पर्यंतच्या कमी read आणि write volumes साठी योग्य आहे. एक VPS या मर्यादेच्या आत सहज राहतो.
धोका label cardinality मध्ये आहे. Label values च्या प्रत्येक वेगळ्या combination साठी एक stream तयार होतो. Stream ची संख्या Loki च्या memory आणि index चा आकार ठरवते. Client IP address किंवा request identifier ठेवणारा label प्रत्येक value साठी स्वतंत्र stream तयार करतो. त्यामुळे व्यस्त web server एका दिवसात दहा हजारो streams तयार करू शकतो आणि kernel process थांबवेपर्यंत तो process वाढत राहतो. कागदावर मोजता येतील अशा values पुरते labels मर्यादित ठेवा: unit, host, job, level. बदलणारा तपशील line मध्येच ठेवा. Query time वर filter expression तो तपशील शोधू शकते.
एका VPS वर Loki आणि Alloy स्थापित करा
हे काम दोन प्रक्रिया करतात. Loki logs साठवतो आणि queries ची उत्तरे देतो. Grafana Alloy logs वाचतो आणि ते push करतो. पूर्वी Promtail shipper म्हणून वापरला जात होता. मात्र 2 March 2026 रोजी त्याचे जीवनचक्र संपले. त्यामुळे नवीन installations मध्ये Alloy वापरला जातो. Loki चे स्वतःचे Docker उदाहरणही आता Alloy configuration release करते.
wget https://raw.githubusercontent.com/grafana/loki/v3.7.0/cmd/loki/loki-local-config.yaml -O loki-config.yamlही file वापरण्यापूर्वी ती वाचा. ती path_prefix: /tmp/loki ला /tmp/loki/chunks अंतर्गत chunks सह सेट करते. Demo साठी हे योग्य आहे, पण server साठी चुकीचे आहे. Container पुन्हा तयार केल्यावर त्याच्या /tmp अंतर्गत असलेली कोणतीही गोष्ट टिकत नाही. त्यामुळे पुढील image update नंतर तुमचा history नाहीसा होतो. ती file तुम्ही mount केलेल्या path कडे निर्देशित करा.
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/readyशेवटच्या command ने 200 छापले पाहिजे. Loki traffic स्वीकारण्यासाठी तयार झाल्यावर /ready HTTP 200 परत करते. याव्यतिरिक्त काही दिसल्यास process अद्याप सुरू होत आहे किंवा configuration नाकारली गेली आहे. कोणते कारण आहे ते docker logs loki सांगते. Run command मधील दोन तपशील जाणीवपूर्वक ठेवले आहेत. Port फक्त 127.0.0.1 वर publish केला आहे. Sample configuration मध्ये auth_enabled: false आहे आणि Loki स्वतःचे user authentication देत नाही. त्यामुळे port 3100 पर्यंत पोहोचू शकणारी कोणतीही व्यक्ती प्रत्येक log वाचू शकते आणि बनावट logs लिहू शकते. तो loopback वर किंवा VPN अथवा authentication करणाऱ्या reverse proxy मागे ठेवा. Named volume महत्त्वाचा आहे. Image loki user म्हणून, UID 10001 सह, चालते. त्यामुळे root च्या मालकीची bind mounted host directory container मध्ये writable नसते.
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 /etc/alloy/config.alloy वाचतो. ही configuration system journal आणि files चा एक संच वाचते आणि दोन्ही local Loki कडे push करते.
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]
}Relabel rule journal मधील __journal__systemd_unit field ची प्रत unit नावाच्या label मध्ये ठेवतो. त्यामुळे पुढे {unit="ssh.service"} कार्य करते. हा rule नसल्यास unit name entry मध्ये राहते, label मध्ये नाही. त्यामुळे त्यावर selection करता येत नाही आणि प्रत्येक query ला सर्व entries scan कराव्या लागतात.
sudo systemctl reload alloy
systemctl show -p User alloy
sudo journalctl -n 5
sudo -u alloy journalctl -n 5बहुतेक setups येथे अडकतात. Alloy स्वतःच्या service account म्हणून चालतो, root म्हणून नाही. System journal वाचण्यासाठी systemd-journal group चे membership आवश्यक आहे. Debian आणि Ubuntu मध्ये /var/log/nginx अंतर्गत असलेल्या files adm group च्या मालकीच्या असतात. शेवटच्या command मध्ये systemctl show ने छापलेला account वापरा. Root म्हणून चालवलेल्या command पेक्षा त्यातून खूपच कमी entries परत आल्यास त्या account ला system journal वाचता येत नाही. तुमची configuration योग्य असली तरी Loki रिकामा राहतो. sudo usermod -aG systemd-journal,adm alloy चालवल्यानंतर sudo systemctl restart alloy वापरून groups जोडा आणि service restart करा.
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'0 पेक्षा मोठी संख्या म्हणजे त्या label सह streams अस्तित्वात आहेत आणि त्यात entries आहेत. 0 म्हणजे त्या label अंतर्गत अद्याप काहीही आलेले नाही. एक default नेहमीचा false alarm निर्माण करतो: loki.source.journal max_age ला 7h वर सेट करते. त्यामुळे नवीन सुरुवात केल्यावर journal मधील मागील सात तास वाचले जातात; त्यापूर्वीचे काहीही वाचले जात नाही. Human interface साठी त्याच box वर Grafana चालवा आणि Loki data source ला http://127.0.0.1:3100. कडे निर्देशित करा. Container logs साठी वेगळा source आवश्यक आहे. Alloy चालू Docker containers शोधतो आणि त्यांचे logs tail करतो. Loki च्या स्वतःच्या getting started उदाहरणात हेच केले आहे. तसेच VPS वरील single node k3s cluster मध्ये हे काम kubelet लिहित असलेल्या pod log directory कडे स्थानांतरित होते.
Retention: logs किती दिवस ठेवायचे ते ठरवा
डिस्क भरून जाईपर्यंत जवळजवळ कोणीही retention period ठरवत नाही. त्यानंतर सेवा बंद असताना पहाटे 3 वाजता तो निर्णय घ्यावा लागतो. पहिल्याच दिवशी दोन प्रश्नांच्या आधारे निर्णय घ्या: तुम्ही प्रत्यक्षात किती मागील कालावधीतील logs पाहता, आणि पुढील महिन्यातील incident review दरम्यान तुमच्याकडे कोणती माहिती उपलब्ध असणे आवश्यक आहे. एका सर्व्हरसाठी 14 ते 30 दिवस हे दोन्ही प्रश्न सोडवतात.
तुम्ही compactor enable करेपर्यंत Loki काहीही delete करत नाही. Retention default ने बंद असते. config मध्ये retention_period निष्क्रिय राहिल्यामुळे volume भरलेले पाहून अनेकांना आश्चर्य वाटते.
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 म्हणजे 31 दिवस. त्या block वर चार documented rules लागू होतात:
- Retention compactor लागू करतो. Grafana च्या documentation नुसार compactor एकाच instance म्हणून चालवावा. एका VPS वर हे आपोआप होते.
- किमान retention period 24h आहे आणि retention फक्त index period 24h असताना कार्य करते. नमुन्यातील
schema_configमध्ये आधीचperiod: 24hवापरले आहे, त्यामुळे ते तसेच ठेवा. retention_enabledtrue असल्यासdelete_request_storeआवश्यक असते. Delete requests साठवणारे store यामध्ये नमूद केले जाते. Filesystem-backed single node मध्ये ते schema मधील आधीच्याobject_store: filesystemशी जुळते.- Chunks प्रथम mark केले जातात आणि
retention_delete_delayनंतर delete केले जातात. येथे ते 2h आहे. त्यामुळे policy मध्ये दर्शवलेल्या वेळेपेक्षा उशिरा free space उपलब्ध होते. Reload केल्यानंतर पाच मिनिटांनीdfपाहून setting चे मूल्यांकन करू नका.
OpenSearch स्वतंत्र lines ऐवजी संपूर्ण indexes delete करते. म्हणून log indexes दररोज तयार केले जातात. ISM policy index ला वेगवेगळ्या states मधून नेते आणि तो पुरेसा जुना झाल्यावर delete करते. ism_template ही policy नवीन indexes ला जोडते, त्यामुळे हे प्रत्येक वेळी manually लक्षात ठेवावे लागत नाही.
14 दिवसांनंतर log indexes delete करणारी ISM policy
{
"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 }
}
}ही policy _plugins/_ism/policies/logs-retention कडे PUT request पाठवून तयार करा. Template policy तयार झाल्यानंतर निर्माण होणाऱ्या indexes वर लागू होते. त्यामुळे डिस्कवर आधीपासून असलेल्या indexes ना policy manually जोडावी लागते.
तुम्ही कोणतीही system चालवत असलात, तरी तिच्यामागील free space तपासणीइतकाच retention number उपयोगी असतो. 14 दिवसांनंतर delete केल्याने उपयोग होत नाही, जर 10 दिवसांचे logs आधीच volume भरत असतील. त्यामुळे policy सोबत VPS वरील disk health monitoring वापरा आणि 80% वापर झाल्यावर alert द्या.
लॉगच्या प्रत्येक GB साठी किती डिस्क जागा
याचे अचूक उत्तर तुमच्या log lines आणि fields वर अवलंबून असते. त्यामुळे प्रकाशित केलेल्या कोणत्याही प्रमाणावर अवलंबून न राहता, तुमच्या स्वतःच्या डेटावर मोजमाप करा. या यंत्रणांमध्ये पुरेसा फरक आहे, त्यामुळे अपेक्षित दिशा स्पष्ट करता येते. OpenSearch आणि Elasticsearch प्रत्येक indexed field साठी stored document सोबत inverted index लिहितात. त्यामुळे डिस्कवर साठणारा डेटा raw text पेक्षा मोठा असतो आणि प्रत्येक replica मुळे तो आणखी वाढतो. Single-node सेटअपमध्ये replica count 0 ठेवा, कारण त्याच node वर असलेला replica shard तो node fail झाल्यास टिकू शकत नाही. तो 1 वर ठेवल्यास डिस्क वापर दुप्पट होतो आणि cluster health कायम yellow राहते. Loki compressed chunks आणि लहान label index लिहितो. त्यामुळे त्याचा disk footprint lines च्या compressed size नुसार वाढतो.
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"सलग दोन दिवस लागू असलेली पद्धत चालवा. दोन्ही मोजमापांमधील फरक म्हणजे तुमची daily growth आहे. ती retention days ने गुणा. त्यानंतर compaction आणि merges साठी सुमारे 30% headroom जोडा आणि ही गरज volume च्या क्षमतेशी तुलना करा. डेटा बसत नसेल, तर disk खरेदी करण्यापूर्वी retention कमी करा. मोठा volume घेतल्याने तीच समस्या फक्त काही आठवडे पुढे ढकलली जाते.
लहान सर्व्हरवर प्रथम काय बिघडते
सर्वप्रथम memory संपते. kernel OOM (out of memory) killer मोठी process निवडतो आणि logging box वरील सर्वात मोठी process म्हणजे JVM असते. journalctl -k | grep -i "killed process" मध्ये brackets मध्ये process name देऊन kill दाखवलेला असतो. बळी नेहमी log stack असेलच असे नाही: त्याऐवजी sshd किंवा तुमचा database निवडला जाऊ शकतो. त्यामुळे logs मिळवण्यासाठी केलेला प्रयोग ज्या application मधून logs घ्यायचे होते तीच बंद पडू शकते. Containers साठी स्पष्ट memory ceilings द्या, जेणेकरून failure तुम्ही ठरवलेल्या ठिकाणी होईल. यासाठीच Docker Compose मधील memory limits वापरले जातात.
त्यानंतर disk भरतो आणि search engines एका विशिष्ट, ओळखण्यासारख्या पद्धतीने fail होतात. Elasticsearch आणि OpenSearch अनेक स्तरांवर disk usage तपासतात. Low watermark 85% वर आणि high watermark 90% वर असतो. Flood stage 95% वर पोहोचल्यास त्या node वर shard असलेल्या प्रत्येक index ला index.blocks.read_only_allow_delete block मिळतो आणि त्यानंतर writes blocked by: [FORBIDDEN/12/index read-only / allow delete (api)] मुळे fail होतात. Usage पुन्हा high watermark पेक्षा खाली गेल्यावर block काढला जातो. आधी free space निर्माण करा. Block तसाच राहिल्यासच तो manually clear करा.
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 अधिक शांतपणे fail होते. त्यात read-only mode उपलब्ध नसल्यामुळे volume पूर्ण भरल्यावर sender कडील failed pushes आणि query results मधील gaps दिसतात. Cardinality ची समस्या error म्हणून दिसत नाही; memory हळूहळू वाढत जाते. chunks directory चा size incident झाल्यानंतर नव्हे, तर ठरावीक schedule नुसार monitor करा.
शेवटचे failure म्हणजे चुकीचा data भरला जाणे. Log system हे metrics system नाही. दर 10 सेकंदांनी sample केलेला CPU load text म्हणून साठवणे महागडे आणि graph करण्यास अवघड असते. हे काम Ubuntu 24.04 वरील Zabbix monitoring server सारख्या साधनाकडे सोपवावे. Application exceptions साठी grouping, deduplication आणि stack trace view आवश्यक असतात. हे काम self-hosted error tracker चे आहे. Site बंद आहे हे फक्त कळणे हा पुन्हा वेगळा मुद्दा आहे. त्यासाठी Uptime Kuma सारखे uptime आणि status page वापरता येते. Log system मध्ये एखादी व्यक्ती वाचेल अशा text linesच ठेवा.
FAQ
माझ्या सर्व्हर logs मध्ये शोध घेण्यासाठी Elasticsearch आवश्यक आहे का?
एक किंवा दोन सर्व्हरसाठी नाही. journalctl आधीच unit, priority, boot आणि time range नुसार filtering करते; तसेच rotated files grep आणि zgrep शी संबंधित शोधांना उत्तर देतात. अनेक मशीन असतील, सर्व मशीनवर एकाच वेळी free text search आवश्यक असेल किंवा अनेक लोकांना shared interface वापरायचा असेल, तेव्हा search cluster साठी लागणारी memory योग्य ठरते. त्यापेक्षा कमी प्रमाणात size limit आणि retention time असलेले journald कोणतीही अतिरिक्त RAM न वापरता तेच काम करते.
self-hosted log management साठी किती RAM आवश्यक आहे?
ढोबळ नियमाऐवजी प्रत्येक project ने प्रकाशित केलेले आकडे वापरा. Loki आणि Alloy हे Go programs आहेत; सुरुवातीलाच heap reserve करण्याची त्यांना आवश्यकता नसते. Grafana च्या documentation नुसार monolithic Loki दररोज सुमारे 20GB पर्यंत वापरू शकतो. OpenSearch च्या sample compose मध्ये demo साठी 512 MB heap आणि production example मध्ये 2 GB heap सेट केलेला आहे. Elastic च्या मते heap एकूण memory च्या 50% पेक्षा जास्त नसावा. त्यामुळे Kibana सुरू करण्यापूर्वी 2 GB heap साठी 4 GB मशीन आवश्यक ठरते. Logstash च्या documentation मध्ये स्वतःसाठी किमान 4GB heap ची शिफारस केली आहे. हे documented settings आहेत, benchmarks नाहीत. त्यामुळे sizing plan निश्चित करण्यापूर्वी तुमच्या स्वतःच्या load चे मोजमाप करा.
logs साठी Loki आणि OpenSearch मधील वास्तविक फरक काय आहे?
Index model. Loki फक्त labels index करते आणि log body compressed chunks म्हणून ठेवते. हे chunks query चालवताना scan केले जातात. त्यामुळे writes स्वस्त असतात; परंतु broad queries साठी queries अधिक खर्चिक ठरतात. OpenSearch fields मधील content index करते. त्यामुळे arbitrary full text search जलद होते; मात्र index साठी memory आणि disk दोन्ही लागतात. कोणती service आणि कोणता time window शोधायचा हे माहीत असेल, तर Loki निवडा. आधीच अंदाज न बांधता येणाऱ्या text मध्ये शोध घ्यायचा असेल, तर OpenSearch निवडा.
VPS वर logs किती काळ ठेवावेत?
Disk स्वतः मर्यादा ठरवण्यापूर्वी ही संख्या ठरवा. प्रत्येक system साठी ती नेमक्या एका ठिकाणी सेट करा: journald साठी MaxRetentionSec= आणि SystemMaxUse=, compactor enabled असलेल्या Loki साठी retention_period, आणि OpenSearch साठी min_index_age असलेली ISM policy. बहुतेक single server setups साठी 14 ते 30 दिवस debugging आणि incident review साठी पुरेसे असतात. यापेक्षा जास्त काळ ठेवणे आवश्यक असलेली माहिती server च्या बाहेर साठवलेल्या copy मध्ये ठेवा. कारण failed server वरच ठेवलेला log हा record राहत नाही.
logs Loki कडे पाठवण्यासाठी Promtail अजूनही योग्य पर्याय आहे का?
नाही. Promtail 2 March 2026 रोजी end of life झाले आणि त्याची जागा Grafana Alloy ने घेतली. Loki च्या स्वतःच्या Docker install example मध्ये आता Alloy configuration दिले आहे. Grafana existing Promtail config चे Alloy syntax मध्ये रूपांतर करणारा converter देते. Existing Promtail install चालू राहते; परंतु त्याला fixes मिळत नाहीत. त्यामुळे migration ला पुढे ढकलता येणारे upgrade न मानता maintenance म्हणून हाताळा.