日志系统是企业IT基础设施的重要组成部分,为了能够更好地管理和分析系统运行时产生的大量数据,从而帮助提升系统的稳定性、安全性、性能以及用户体验,遂进行日志系统搭建。主要满足以下几方面的能力。
企业通常面临两种选择:使用付费(商用)解决方案或自研(自行开发)。
这里选择自研方案
一个完整的日志架构包含以下几个方案
结合企业当前现状,人员配置,技术栈等因素,在对比分析Graylog、ELK(Elasticsearch、Logstash、Kibana)等主流日志审计框架后,我们最终选择了Progmtail收集数据,Loki作为日志系统,MinIO用于存储,AlertManager负责告警监控,Grafana用于看板展示的组合方案。
框架 | 优点 | 缺点 |
---|---|---|
Graylog | 集成度高,一站式日志管理解决方案。 强大的搜索与分析功能,支持全文检索。 用户友好的Web界面,易于操作。 | 对硬件资源要求较高,尤其在大规模日志处理场景下。 扩展性相对有限,对复杂日志结构的处理能力稍逊。 |
ELK Stack | Elasticsearch作为强大的搜索引擎,提供出色的日志查询与分析能力。 Logstash提供丰富的数据处理插件,支持复杂日志预处理。 Kibana提供丰富的可视化界面,支持定制化看板。 | 整体资源消耗较大,运维复杂度较高。 在大规模分布式环境下,需要额外关注集群稳定性与数据一致性问题。 |
Loki | 专为微服务架构设计,轻量、高效,资源占用低。 基于标签的索引机制,查询速度快,存储成本低。 与Prometheus生态深度集成,便于监控与告警联动。 | 功能相对简洁,对于极复杂日志处理场景可能需要额外定制。 |
由于日志审计系统,数据为企业核心敏感数据,以及企业对数据本地化要求,暂时先不考虑云存储(如阿里oss,腾讯云cos,华为云obs……)
存储服务 | 优点 | 缺点 |
---|---|---|
NFS | 成熟的技术,广泛支持。 配置简单,易于部署和管理。 适合文件共享和协作。 | 性能受限于网络和单个服务器的性能。 对于大量小文件的性能不佳,可能影响日志检索速度。 没有内建的高可用性解决方案。 |
Ceph | 高度可扩展,适合大规模存储需求。 CRUSH算法提供了优秀的数据分布和容错能力。 开源和社区支持强大。 | 整个系统非常复杂需要大量的人力进行维护 对于日志数据这类非结构化数据,可能存在一定的性能和成本问题。 |
MinIO | 专为对象存储设计,适合存储大量小文件,如日志数据。 高性能,支持高并发访问。 与S3 API兼容,方便云原生应用集成。 提供了企业级的安全性和数据保护特性。 | 可能需要更多时间来评估其长期稳定性。 需要对S3 API有一定的了解和适配。 |
下面重点介绍最终选型系统在企业内实现
数据收集,这里可以原生配套promtail(此文以promtail, version 2.9.4 (branch: HEAD, revision: f599ebc535)展开说明)其余第三方收集器如下https://grafana.com/docs/loki/latest/send-data/
apiVersion: v1
kind: ConfigMap
metadata:
namespace: loki
labels:
app: loki
name: loki-local-config
data:
local-config.yaml: |-
auth_enabled: false
server:
http_listen_port: 3100 # 配置HTTP监听端口号为3100。
http_listen_address: 0.0.0.0
graceful_shutdown_timeout: 60s # 配置优雅停机的超时时间为60秒。
http_server_read_timeout: 60s # 配置HTTP服务器读取超时时间为60秒。
http_server_write_timeout: 60s # 配置HTTP服务器写入超时时间为60秒。
grpc_listen_port: 9096
grpc_listen_address: 0.0.0.0
grpc_server_max_recv_msg_size: 10000000
grpc_server_max_send_msg_size: 10000000
grpc_server_max_concurrent_streams: 0
memberlist:
join_members: ["loki-0.loki.loki.svc.cluster.local", "loki-1.loki.loki.svc.cluster.local", "loki-2.loki.loki.svc.cluster.local"]
dead_node_reclaim_time: 30s
gossip_to_dead_nodes_time: 15s
left_ingesters_timeout: 30s
bind_addr: ['0.0.0.0']
bind_port: 7946
ingester: # 配置Loki的ingester部分,用于接收和处理日志数据。
lifecycler: # 配置生命周期管理器,用于管理日志数据的生命周期。
# address: 127.0.0.1 # 配置生命周期管理器的地址
ring: # 配置哈希环,用于将日志数据分配给不同的Loki节点
kvstore: # 配置键值存储,用于存储哈希环的节点信息。 支持consul、etcd或者memroy,三者选其一
store: memberlist # 配置存储引擎为inmemory,即内存中存储
replication_factor: 1 # 配置复制因子为1,即每个节点只存储一份数据。
final_sleep: 0s # 配置最终休眠时间为0秒,即关闭时立即停止。
chunk_idle_period: 3m # 配置日志块的空闲时间为1小时。如果一个日志块在这段时间内没有收到新的日志数据,则会被刷新。
max_chunk_age: 5m # 配置日志块的最大年龄为1小时。当一个日志块达到这个年龄时,所有的日志数据都会被刷新。
chunk_target_size: 1048576 # 配置日志块的目标大小为2048576字节(约为1.5MB)。如果日志块的空闲时间或最大年龄先达到,Loki会首先尝试将日志块刷新到目标大小。
chunk_retain_period: 30s # 配置日志块的保留时间为30秒。这个时间必须大于索引读取缓存的TTL(默认为5分钟)。
max_transfer_retries: 0 # 配置日志块传输的最大重试次数为0,即禁用日志块传输。
wal:
enabled: true
dir: /tmp/loki/wal
query_scheduler:
max_outstanding_requests_per_tenant: 10000
chunk_store_config: # 配置如何缓存块,以及在将它们保存到存储之前等待多长时间
max_look_back_period: 4320h #限制查询数据的时间,默认是禁用的,为0,为避免查询超过保留期的数据,必须小于或等于下方的table_manager.retention_period时间值
table_manager:
retention_deletes_enabled: true # 日志保留周期开关,用于表保留删除
retention_period: 4320h # 日志保留周期,保留期必须是索引/块的倍数
limits_config:
enforce_metric_name: false
reject_old_samples: true # 旧样品是否会被拒绝
reject_old_samples_max_age: 168h # 拒绝旧样本的最大时限
ingestion_rate_mb: 32 # 配置日志数据的最大摄入速率为32MB/s
ingestion_burst_size_mb: 64 # 配置日志数据的最大摄入突发大小为64MB。
max_streams_matchers_per_query: 100000 # 配置每个查询的最大流匹配器数量为100000。
max_entries_limit_per_query: 50000 # 配置每个查询的最大条目限制为50000。
max_query_series: 100000
storage_config:
aws:
# prod env
s3: s3://__S3_SECRET_KEY__:S3_SECRET_KEY@S3_HOST/loki
s3forcepathstyle: true
insecure: true
boltdb_shipper:
active_index_directory: /tmp/loki/boltdb-shipper-active
cache_location: /tmp/loki/boltdb-shipper-cache
cache_ttl: 24
# Can be increased for faster performance over longer query periods, uses more disk space
shared_store: aws
compactor: # 配置日志压缩器的信息。
working_directory: /tmp/loki/boltdb-shipper-compactor # 配置工作目录为/tmp/loki/boltdb-shipper-compactor。
shared_store: aws #配置共享存储引擎为filesystem,即使用文件系统存储
schema_config:
configs:
- from: 2024-04-09
store: boltdb-shipper
object_store: aws
schema: v11
index:
prefix: index_
period: 24h
chunks:
prefix: chunks_
period: 24h
ruler:
storage:
type: local
local:
directory: /tmp/loki/rules # Loki告警规则存储路径
rule_path: /tmp/loki/rules-temp
evaluation_interval: 1m # 设置评估间隔为1分钟
alertmanager_url: ALERTMANAGER_URL # alertmanager地址和端口
ring:
kvstore:
store: inmemory
enable_api: true
enable_alertmanager_v2: true
loki.yaml
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: loki
namespace: loki
labels:
app: loki
spec:
podManagementPolicy: OrderedReady
replicas: 3
serviceName: loki
selector:
matchLabels:
app: loki
template:
metadata:
labels:
app: loki
spec:
affinity: {}
containers:
- name: loki
image: registry.opsman.top/grafana/loki:2.9.4
args:
- "-config.file=/etc/loki/local-config.yaml"
- "-target=all,table-manager"
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3100
name: http-metrics
protocol: TCP
- containerPort: 9095
name: grpc-9095
protocol: TCP
- containerPort: 7946
name: tcp-7946
protocol: TCP
resources:
limits:
cpu: "4"
memory: 6Gi
requests:
cpu: "1"
memory: "2Gi"
volumeMounts:
- name: loki-config
mountPath: /etc/loki/
readOnly: true
- name: timezone
mountPath: /etc/localtime # 挂载到容器的目录
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 120
periodSeconds: 30
successThreshold: 1
tcpSocket:
port: 3100
timeoutSeconds: 5
securityContext:
capabilities:
add: []
drop:
- ALL
readOnlyRootFilesystem: false
#runAsNonRoot: true
runAsUser: 0
securityContext:
fsGroup: 100
volumes:
- name: loki-config
configMap:
name: loki-local-config
- name: timezone
hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai # 宿主机的目录
service.yaml
---
apiVersion: v1 # 资源版本
kind: Service # 资源类型
metadata: # 元数据
name: loki # 资源名称
namespace: loki # 资源所属命名空间
labels:
app: loki # 标签
spec:
type: ClusterIP # 虚拟服务的ip地址
ports:
- port: 3100 # service端口
protocol: TCP
name: http-metrics
targetPort: http-metrics # pod端口
selector: # 标签选择器,用于确定当前service代理哪些pod
app: loki
---
apiVersion: v1
kind: Service
metadata:
name: loki-headless
namespace: loki
annotations:
labels:
app: loki
spec:
type: NodePort
externalTrafficPolicy: Cluster
ports:
- name: loki
nodePort: 32537 # 主机端口,将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务。(在每个Node上分配一个端口作为外部访问入口)
port: 3100 # service端口
protocol: TCP
targetPort: 3100 # pod端口
selector:
app: loki
sessionAffinity: None # session亲和性,支持ClientIP、None两个选项
kubectl apply -f loki-sys/configmap.yaml
kubectl apply -f loki-sys/service.yaml
kubectl apply -f loki-sys/loki.yaml
更多请最新参考https://gitee.com/ceagle/loki-sys.git
(这里以部署在/loki-sys目录举例)
cd loki-sys
wget https://github.com/grafana/loki/releases/download/v2.9.4/promtail-linux-amd64.zip
unzip promtail-linux-amd64.zip
ln -s promtail-linux-amd64 promtail
(这里以部署在/loki-sys目录举例)
$ tree loki-sys/promtail
loki-sys/promtail
├── bin
│ ├── promtail -> promtail-linux-amd64 # promtail 二进制文件
│ ├── promtail-linux-amd64
│ ├── start.sh
│ └── stop.sh
├── config
│ ├── config.yml
├── docker
│ ├── config
│ │ └── config.yml
│ ├── docker-compose
│ └── docker-compose-promtail.yaml
└── logs
└── start_promtail.log
1) promtail配置文件(config/config.yaml)
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push # 这里替换为部署ip地址+端口
tenant_id: 1
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
#- hostname
labels:
instance: ${local_ip}
job: secure
env: extranet
__path__: /var/log/{messages*,secure*,wtmp*} #日志的位置,支持正则表达式
2) 启动脚本
#!/bin/bash
# bin/start.sh
workdir=$(cd $(dirname $0); pwd)
cd ${workdir}
host_name=`hostname`
export local_ip=$(hostname -I |hostname -I|grep -o -e '[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}' |head -n 1)
#declare -x local_ip=${local_ip}
process_num=$(ps aux | grep 'promtail --config.file' | grep -v grep|wc -l)
function start(){
if [ ${process_num} -ge 1 ]
then
echo "$(date "+%Y-%m-%d %H:%M:%S") promtail|pid ${pid} exist,start kill it..."
pid=$(ps aux | grep 'promtail --config.file' | grep -v grep|awk '{print $2}')
kill -SIGTERM ${pid} && echo "$(date "+%Y-%m-%d %H:%M:%S") Stop promtail|pid ${pid} succ"
fi
nohup ${workdir}/promtail --config.file=../config/config.yml -config.expand-env=true > ../logs/start_promtail.log 2>&1 &
process_num=$(ps aux | grep 'promtail --config.file' | grep -v grep|wc -l)
if [ ${process_num} -eq 1 ]
then
pid=$(ps aux | grep 'promtail --config.file' | grep -v grep|awk '{print $2}')
echo "$(date "+%Y-%m-%d %H:%M:%S") Start promtail|new pid ${pid} succ"
else
echo "$(date "+%Y-%m-%d %H:%M:%S") Start promtail|new pid ${pid} fail"
fi
}
start
3) 停止脚本
#!/bin/bash
# bin/stop.sh
workdir=$(cd $(dirname $0); pwd)
cd ${workdir}
host_name=`hostname`
export local_ip=$(hostname -I |hostname -I|grep -o -e '[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]\{1,3\}' |head -n 1)
#declare -x local_ip=${local_ip}
process_num=$(ps aux | grep 'promtail --config.file' | grep -v grep|wc -l)
function stop(){
if [ ${process_num} -ge 1 ]
then
echo "$(date "+%Y-%m-%d %H:%M:%S") promtail|pid ${pid} exist,start kill it..."
pid=$(ps aux | grep 'promtail --config.file' | grep -v grep|awk '{print $2}')
kill -SIGTERM ${pid} && echo "$(date "+%Y-%m-%d %H:%M:%S") Stop promtail|pid ${pid} succ"
fi
sleep 1
process_num=$(ps aux | grep 'promtail --config.file' | grep -v grep|wc -l)
if [ ${process_num} -eq 1 ]
then
pid=$(ps aux | grep 'promtail --config.file' | grep -v grep|awk '{print $2}')
echo "$(date "+%Y-%m-%d %H:%M:%S") Stop promtail|pid ${pid} fail"
fi
}
# start main
stop
4) 启停服务
# 启动服务
cd /data/promtail;./bin/start.sh
# 停止服务
cd /data/promtail;./bin/start.sh
config.yml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push # 这里替换为部署ip地址+端口
scrape_configs:
- job_name: system
static_configs:
- targets:
- localhost
#- hostname
labels:
instance: 192.168.1.15 # 替换为自己部署的宿主机或者容器
job: secure
env: extranet
__path__: /var/log/{messages*,secure*,wtmp*} #日志的位置,支持正则表达式
docker-compose.yaml
version: "3"
services:
promtail:
image:grafana/promtail:2.9.4 # 如果不能联网,替换为本地镜像仓库地址
ports:
- "9080:9080"
volumes:
- /var/log:/var/log
- ./config/config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
启停
# 启动
docker-compose up -d
# 停止
docker-compose down
替换掉s3信息,这里为minio的bucket loki有权限的配置
S3_HOST="1.1.1.1:9000"
S3_ACCESS_KEY="5372OS9A1ZR5UD6WWE6O"
S3_SECRET_KEY="xP1I3XBjqMnt62dZA9c2wXXCmLVPaMUOmMBt1M63"
sed -i "s#__S3_HOST__#${S3_HOST}#g" loki-configmap.yaml
sed -i "s#__S3_ACCESS_KEY__#${S3_ACCESS_KEY}#g" loki-configmap.yaml
sed -i "s#__S3_SECRET_KEY__#${S3_SECRET_KEY}#g" loki-configmap.yaml
configmap.yaml
# configmap.yaml
...
storage_config:
aws:
# prod env
s3: s3://__S3_ACCESS_KEY__:__S3_SECRET_KEY__@__S3_HOST__/loki
s3forcepathstyle: true
insecure: true
boltdb_shipper:
active_index_directory: /tmp/loki/boltdb-shipper-active
cache_location: /tmp/loki/boltdb-shipper-cache
cache_ttl: 24
# Can be increased for faster performance over longer query periods, uses more disk space
shared_store: aws
配置 Alertmanager 以实现告警的集中管理和通知。
# loki-configmap.yaml
...
ruler:
storage:
type: local
local:
directory: /tmp/loki/rules # Loki告警规则存储路径
rule_path: /tmp/loki/rules-temp
evaluation_interval: 1m # 设置评估间隔为1分钟
alertmanager_url: http://<alertmanager:9093> # alertmanager地址和端口
ring:
kvstore:
store: inmemory
enable_api: true
enable_alertmanager_v2: true
将 DingTalk 集成到告警通知流程中,实现快速响应。
alertmanager.yml
data:
alertmanager.yml: |
global:
resolve_timeout: 5m
receivers:
- name: 'default'
webhook_configs:
- url: 'http://dingtalk:8060/dingtalk/webhook/send'
send_resolved: true
route:
group_interval: 1m
group_wait: 10s
receiver: default
repeat_interval: 1m
告警示例
[RESOLVED] TooManySshFailed
Alerts Resolved
[CRITICAL] 服务器疑似SSH暴力破解
Description: 10分钟内ssh失败 8次 大于3次
Details:
alertname: TooManySshFailed
env: extranet
filename: /var/log/secure
instance: 192.168.1.1
job: secure
后面有时间再补上
先可以参考下列资料进行配置
https://zhuanlan.zhihu.com/p/599699193
https://www.cnblogs.com/xgsh/p/17458149.html
third-party clients also enable sending logs to Loki:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。