고객사 설명:
"Logging like daily? Approximately [one] 150 gigabyte to...
a maximum 250 gigabytes... for the audit logging."
현재 상태:
벤더 확인:
"event log and Audit Log... they're monitoring is bucket level
[operations]"
로그 종류:
1. Audit Log: 모든 API 호출 (GET, PUT, DELETE)
2. Event Log: Bucket 이벤트 (특정 작업)
고객사 우려:
"They found it too much... it's too chatty."
문제:
구조:
MinIO Cluster (54 nodes)
↓ (Audit Log)
Fluentd (Log aggregator)
↓ (Webhook/HTTP)
OpenSearch (Log storage)
↓
Kibana (Visualization)
현재 설정:
# fluentd.conf (추정)
<source>
@type http
port 8888
bind 0.0.0.0
</source>
<match minio.audit.**>
@type opensearch
host opensearch.example.com
port 9200
index_name minio-audit-%Y%m%d
# 문제: Batch size 미설정 또는 작음
# flush_interval 1s
# chunk_limit_size 1M
</match>
문제:
벤더 제안:
"So if you do batch size 100 fluent[d]"
Fluentd 최적화:
<match minio.audit.**>
@type opensearch
host opensearch.example.com
port 9200
index_name minio-audit-%Y%m%d
# 배치 설정
<buffer>
@type file
path /var/log/fluentd/buffer/opensearch
# 핵심: Batch size
chunk_limit_size 10M
flush_interval 10s
flush_at_shutdown true
# 재시도
retry_type exponential_backoff
retry_wait 1s
retry_max_interval 60s
retry_timeout 1h
</buffer>
# Bulk insert
bulk_message_request_threshold 100
# 압축
compression gzip
</match>
벤더 설명:
"it will just make [OpenSearch] less chatty, because right now,
less traffic"
Before (개별 전송):
100M logs/day ÷ 86400 seconds ≈ 1,157 requests/second
After (Batch 100):
100M logs/day ÷ 100 per batch ÷ 86400 seconds ≈ 12 requests/second
개선:
벤더 확인:
"less traffic, but... the size of logs will be the same."
주의:
비유:
Before: 100만 개 소포 (개별 배송)
After: 1만 개 컨테이너 (100개씩 묶음)
총 화물 무게: 동일
배송 횟수: 99% 감소
설정:
POST /_bulk
{ "index": { "_index": "minio-audit-20260202" } }
{ "timestamp": "2026-02-02T10:00:00Z", "user": "admin", "action": "GET" }
{ "index": { "_index": "minio-audit-20260202" } }
{ "timestamp": "2026-02-02T10:00:01Z", "user": "user1", "action": "PUT" }
...
(100개 batch)
장점:
PUT /minio-audit-template
{
"index_patterns": ["minio-audit-*"],
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "30s",
"codec": "best_compression"
}
}
최적화:
벤더 제안:
"You could just keep a day of how it looks in [OpenSearch], or
a month, and then everything else, take it out and put it into
[like] a compressed [file]."
계층:
Hot (OpenSearch):
Warm (S3/MinIO):
Cold (S3/MinIO):
Curator 설정:
# curator_actions.yml
actions:
1:
action: delete_indices
description: Delete indices older than 30 days
options:
ignore_empty_list: True
filters:
- filtertype: pattern
kind: prefix
value: minio-audit-
- filtertype: age
source: name
direction: older
timestring: '%Y%m%d'
unit: days
unit_count: 30
또는 ILM (Index Lifecycle Management):
PUT /_ilm/policy/minio-audit-policy
{
"policy": {
"phases": {
"hot": {
"actions": {}
},
"warm": {
"min_age": "7d",
"actions": {
"forcemerge": { "max_num_segments": 1 },
"shrink": { "number_of_shards": 1 }
}
},
"delete": {
"min_age": "30d",
"actions": {
"delete": {}
}
}
}
}
}
Lambda/Cron 작업:
#!/usr/bin/env python3
import boto3
from datetime import datetime, timedelta
from elasticsearch import Elasticsearch
es = Elasticsearch(['http://opensearch:9200'])
s3 = boto3.client('s3', endpoint_url='http://minio:9000')
# 7일 이상 지난 로그
cutoff = datetime.now() - timedelta(days=7)
index = f"minio-audit-{cutoff.strftime('%Y%m%d')}"
# OpenSearch에서 추출
query = {"query": {"match_all": {}}}
result = es.search(index=index, body=query, scroll='5m', size=10000)
# S3에 저장 (Parquet)
logs = [hit['_source'] for hit in result['hits']['hits']]
df = pd.DataFrame(logs)
df.to_parquet(f's3://audit-archive/{index}.parquet', compression='snappy')
# OpenSearch에서 삭제
es.indices.delete(index=index)
벤더 설명:
"Audit log has the same data. It has more data... As long as
you have access to audit log, you can basically do everything"
비교:
| 항목 | Audit Log | Event Log |
|---|---|---|
| 범위 | 모든 API 호출 | 특정 Bucket 이벤트 |
| 상세도 | 매우 상세 | 선택적 |
| 용도 | 컴플라이언스, 추적 | 알림, 워크플로우 |
| 크기 | 매우 큼 | 작음 |
포함 정보:
예시:
{
"timestamp": "2026-02-02T10:15:30Z",
"user": "ldap:john.doe@company.com",
"action": "s3:GetObject",
"bucket": "data-warehouse",
"object": "2026/02/sales.parquet",
"ip": "10.0.1.100",
"user_agent": "Spark/3.5.0",
"status": 200,
"bytes_sent": 134217728
}
벤더 설명:
"Bucket events... you need to send them on a bucket level."
설정:
# Bucket event 설정
mc event add minio/data-warehouse \
arn:minio:sqs::primary:webhook:audit \
--event put,delete
# Webhook endpoint
# POST http://event-processor:8080/webhook
사용 사례:
벤더 설명:
"if I do every time somebody does a put, and I can track that
based on their LDAP name... Do you want to track it to one bucket
or lots of buckets?"
시나리오:
OpenSearch Query:
GET /minio-audit-*/_search
{
"query": {
"bool": {
"must": [
{ "match": { "user": "ldap:john.doe@company.com" } },
{ "match": { "action": "s3:PutObject" } },
{ "match": { "bucket": "shared-data" } }
],
"range": {
"timestamp": {
"gte": "2026-02-01",
"lte": "2026-02-28"
}
}
}
},
"aggs": {
"total_bytes": {
"sum": { "field": "bytes_sent" }
},
"count": {
"value_count": { "field": "action" }
}
}
}
결과:
{
"hits": { "total": 15234 },
"aggregations": {
"total_bytes": { "value": 524288000000 },
"count": { "value": 15234 }
}
}
해석:
벤더 설명:
"you have to track their gets. You have to track their puts and
their deletes. Gets usually don't [matter] unless you're tracking
API costs"
추적 항목:
Storage Cost:
API Cost:
SQL 예시:
-- 사용자별 월별 사용량
SELECT
user,
action,
COUNT(*) as request_count,
SUM(bytes_sent) / 1024 / 1024 / 1024 as total_gb,
DATE_FORMAT(timestamp, '%Y-%m') as month
FROM minio_audit_logs
WHERE timestamp >= '2026-02-01'
AND timestamp < '2026-03-01'
GROUP BY user, action, month
ORDER BY total_gb DESC;
주요 지표:
Overview:
Performance:
Security:
예시 Query:
// Failed requests
GET /minio-audit-*/_search
{
"query": {
"bool": {
"must": [
{ "range": { "status": { "gte": 400 } } },
{ "range": { "timestamp": { "gte": "now-1h" } } }
]
}
},
"aggs": {
"by_user": {
"terms": { "field": "user.keyword", "size": 10 }
}
}
}
OpenSearch Alerting:
{
"name": "High Error Rate",
"type": "monitor",
"schedule": {
"period": {
"interval": 5,
"unit": "MINUTES"
}
},
"inputs": [{
"search": {
"indices": ["minio-audit-*"],
"query": {
"bool": {
"filter": [
{ "range": { "timestamp": { "gte": "now-5m" } } },
{ "range": { "status": { "gte": 400 } } }
]
}
}
}
}],
"triggers": [{
"name": "Error rate > 5%",
"severity": "1",
"condition": {
"script": {
"source": "ctx.results[0].hits.total.value > 100"
}
},
"actions": [{
"name": "Slack notification",
"destination_id": "slack-webhook",
"message_template": {
"source": "High error rate detected: {{ctx.results[0].hits.total.value}} errors in last 5 minutes"
}
}]
}]
}
Fluentd 최적화 가이드
OpenSearch 통합 문서
보관 전략 문서
Fluentd 설정 최적화
모니터링
OpenSearch 최적화
보관 정책 구현
Dashboard 구축
Request 수:
OpenSearch 부하:
네트워크:
저장 공간:
운영:
문서 버전: 1.0
최종 수정일: 2026년 2월 2일
다음 리뷰: 최적화 적용 후 (2026년 2월 중순)
"Logging like daily? Approximately [one] 150 gigabyte to...
a maximum 250 gigabytes... for the audit logging."
"They found it too much... it's too chatty."
"So if you do batch size 100 fluent[d]... it will just make
[OpenSearch] less chatty"
"less traffic, but... the size of logs will be the same."
"You could just keep a day... in [OpenSearch], or a month, and
then everything else, take it out and put it into [like] a
compressed [file]."
"Audit log has the same data. It has more data... As long as
you have access to audit log, you can basically do everything"