课程目标
- 认识loki,搭建loki服务
- 使用promtail进行日志收集
- 使用grafana进行日志展示
1. Loki
loki是grafana公司的另外一款主打产品,目前是grafana公司继prometheus,grafana,cortex之后的第四款比较知名的产品。这是一款支持垂直扩展,支持高可用,多租户日志聚合的系统。他和我们前面讲的ES就在于降低了成本,操作简单。Loki并没有对日志的内容进行索引,而是对于每个日志流进行打标签的工作。这样,对于小规模的日志存取都非常方便,也就更加适合于我们对于日志监控的要求,让产品定位在了日志监控。而ES在日志监控方面的功能虽然也非常强大,但是操作复杂度和昂贵的价格会让我们望而生畏,因此,轻量且开源的Loki让我们有了另外的选择。
但是Loki的缺点也是非常明显的,这也是大部分开源的云原生产品的共性:没有认证功能。如果想做认证,就需要通过其他的方式来实现,比如nginx,或者k8s ingress的认证,或者其他三方的工具来实现。
2. 架构图
一般来说,我们会通过logagent(比如promtail或者fluentd)挖掘日志,然后发送给Loki服务器,最后通过grafana进行展示。而主要的功能都集中在了loki服务器上
2.1. 组件
- Distributer 分发器,负责接收来自于客户端的日志,通过批处理来构建压缩数据
- Ingester捕获器,负责构建和刷新chunks,满足特定条件在刷新到存储中去
- Querier查询器,根据时间和标签选择器,去索引查找并且匹配到指定的数据,同时去Ingester里面查找尚未刷新到存储中的数据
2.2. 逻辑架构图
Loki同样是使用标签作为索引,一切的数据都是通过筛选标签来得到结果的。实际上,我们真正需要安装的只有grafana,loki和promtail
-
Grafana:用于最终的展示,并且借助于官方网站上的dashboard12559来实现nginx日志的展示与监控
-
Loki:我们上面介绍了Loki的主要组件,但是实际上Loki还包含了query frontend和存储相关的组件,数据的存储方式可以选择本地,也可以选择公有云存储或对象存储
-
promtail:日志的agent,一般安装在需要采集日志的主机上来挖掘日志。
3. 单机安装
为了实现我们上面看到的效果,我们需要借助于nginx+GeoIP。我们选用的是AWS,实例是Amazon Linux2,选用Centos或者RHEL原理基本相同,需要注意的是,nginx需要一个叫做ngx_http_geoip_module.so的模块,如果我们使用的是系统自带的epel源的话,是没办法安装这个模块的,只能用编译的方式来安装这个模块。或者我们可以直接使用nginx官方提供的源。
3.1. 安装Nginx和GEOIP
配置nginx源,/etc/yum.repos.d/nginx.repo,我使用的是amazon linux2,所以baseurl我修改如下
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/7/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
安装相关包
yum install -y nginx # nginx主程序
yum install -y nginx-module-geoip # geoip模块
yum install -y GeoIP-data # geoip的数据文件
配置nginx文件,在全局配置下加载模块
load_module /etc/nginx/modules/ngx_http_geoip_module.so;
load_module /etc/nginx/modules/ngx_stream_geoip_module.so;
在http段中,增加日志格式并且加载地理文件
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#access_log /var/log/nginx/access.log main;
log_format json_analytics escape=json '{'
'"msec": "$msec", ' # request unixtime in seconds with a milliseconds resolution
'"connection": "$connection", ' # connection serial number
'"connection_requests": "$connection_requests", ' # number of requests made in connection
'"pid": "$pid", ' # process pid
'"request_id": "$request_id", ' # the unique request id
'"request_length": "$request_length", ' # request length (including headers and body)
'"remote_addr": "$remote_addr", ' # client IP
'"remote_user": "$remote_user", ' # client HTTP username
'"remote_port": "$remote_port", ' # client port
'"time_local": "$time_local", '
'"time_iso8601": "$time_iso8601", ' # local time in the ISO 8601 standard format
'"request": "$request", ' # full path no arguments if the request
'"request_uri": "$request_uri", ' # full path and arguments if the request
'"args": "$args", ' # args
'"status": "$status", ' # response status code
'"body_bytes_sent": "$body_bytes_sent", ' # the number of body bytes exclude headers sent to a client
'"bytes_sent": "$bytes_sent", ' # the number of bytes sent to a client
'"http_referer": "$http_referer", ' # HTTP referer
'"http_user_agent": "$http_user_agent", ' # user agent
'"http_x_forwarded_for": "$http_x_forwarded_for", ' # http_x_forwarded_for
'"http_host": "$http_host", ' # the request Host: header
'"server_name": "$server_name", ' # the name of the vhost serving the request
'"request_time": "$request_time", ' # request processing time in seconds with msec resolution
'"upstream": "$upstream_addr", ' # upstream backend server for proxied requests
'"upstream_connect_time": "$upstream_connect_time", ' # upstream handshake time incl. TLS
'"upstream_header_time": "$upstream_header_time", ' # time spent receiving upstream headers
'"upstream_response_time": "$upstream_response_time", ' # time spend receiving upstream body
'"upstream_response_length": "$upstream_response_length", ' # upstream response length
'"upstream_cache_status": "$upstream_cache_status", ' # cache HIT/MISS where applicable
'"ssl_protocol": "$ssl_protocol", ' # TLS protocol
'"ssl_cipher": "$ssl_cipher", ' # TLS cipher
'"scheme": "$scheme", ' # http or https
'"request_method": "$request_method", ' # request method
'"server_protocol": "$server_protocol", ' # request protocol, like HTTP/1.1 or HTTP/2.0
'"pipe": "$pipe", ' # "p" if request was pipelined, "." otherwise
'"gzip_ratio": "$gzip_ratio", '
'"http_cf_ray": "$http_cf_ray",'
'"geoip_country_code": "$geoip_country_code"'
'}';
access_log /var/log/nginx/json_access.log json_analytics;
geoip_country /usr/share/GeoIP/GeoIP.dat;
geoip_city /usr/share/GeoIP/GeoIPCity.dat;
...
启动nginx
systemctl start nginx
3.2. 安装grafana
目前版本是7.5,我们选用最新的就可以
yum install -y https://dl.grafana.com/oss/release/grafana-7.5.1-1.x86_64.rpm
记得安装插件
/usr/sbin/grafana-cli plugins install grafana-worldmap-panel
启动即可,访问IP:3000,默认密码是admin/admin
3.3. 安装loki
wget https://github.com/grafana/loki/releases/download/v2.2.0/loki-linux-amd64.zip # 下载
unzip loki-linux-amd64.zip # 解压
mv loki-linux-amd64 /usr/local/sbin/loki # 挪到$PATH下
mkdir /etc/loki # 创建配置文件目录
wget https://raw.githubusercontent.com/grafana/loki/master/cmd/loki/loki-local-config.yaml -O /etc/loki/loki-local-config.yaml # 下载样例
nohup loki -config.file=/etc/loki/loki-local-config.yaml & # 直接启动就好
他会监听在本地的3100端口上,我们这个时候就可以用grafana使用loki作为数据源了
3.4. 安装promtail
wget https://github.com/grafana/loki/releases/download/v2.2.0/promtail-linux-amd64.zip # 下载
unzip loki-linux-amd64.zip # 解压
mv promtail-linux-amd64 /usr/local/sbin/promtail # 挪到$PATH下
mkdir /etc/promtail # 创建配置文件目录
官方提供了配置文件样例
wget https://raw.githubusercontent.com/grafana/loki/master/cmd/promtail/promtail-local-config.yaml
但是这里我们简化一下,为了更好的出图
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://localhost:3100/loki/api/v1/push
scrape_configs:
- job_name: nginx_location_json
pipeline_stages:
- replace:
expression: '(?:[0-9]{1,3}\.){3}([0-9]{1,3})'
replace: '***'
static_configs:
- targets:
- localhost
labels:
job: nginx_access_log
host: appfelstrudel
agent: promtail
__path__: /var/log/nginx/json_access.log
启动
nohup promtail -config.file=/etc/promtail/promtail-local-config.yaml &
3.5. 看图喽
我们这个时候就可以切换到我们的界面上看到效果了
4. 报警
4.1. grafana报警
我们可以在某个指标上直接报警,这个是通过grafana来实现的,但是这个报警严重依赖于grafana,功能非常受限
4.2. alertmanager报警
这个是我们比较推荐的方式
wget https://github.com/prometheus/alertmanager/releases/download/v0.21.0/alertmanager-0.21.0.linux-amd64.tar.gz
tar xf alertmanager-0.21.0.linux-amd64.tar.gz
mv alertmanager-0.21.0.linux-amd64/alertmanager /usr/local/sbin/alertmanager
修改一下配置文件/etc/alertmanager/alertmanager.yml
global:
# global parameter
resolve_timeout: 5m
# smtp parameter
smtp_smarthost: smtpdm.xxx.com:465
smtp_from: monitor@notify.xxx.com
smtp_auth_username: monitor@notify.xxx.com
smtp_auth_password: XXXXXX
smtp_require_tls: false
route:
receiver: default-reciever
group_wait: 10s
group_interval: 10s
repeat_interval: 1h
group_by: ["alertname"]
receivers:
- name: 'default-reciever'
email_configs:
- send_resolved: true
to: 'jormun@xxx.com'
inhibit_rules:
- source_match:
severity: 'critical'
target_match:
severity: 'warning'
equal: ['alertname', 'env', 'instance', 'zone']
启动
nohup alertmanager --config.file=/etc/alertmanager/alertmanager.yml &
配置报警规则,在loki的配置/etc/loki/loki-local-config.yaml文件中有这么一段
ruler:
storage:
type: local
local:
directory: /tmp/loki/rules
rule_path: /tmp/loki/rules-temp
alertmanager_url: http://localhost:9093
ring:
kvstore:
store: inmemory
enable_api: true
这就规定了报警规则的文件需要在/tmp/loki/rules下面,我们需要在/tmp/loki/rules下面创建一个文件夹,然后再写规则才能生效。。。囧。比如:/tmp/loki/rules/demo/example.yml
groups:
- name: sample_alert
rules:
- alert: sample_alert
expr: 'sum by (filename) (count_over_time({filename=~".*json_access.log.*"} |~ "404" [1m]) > 0)'
annotations:
message: "404 alert"
for: 1m
labels:
severity: critical
上面的报警规则是说如果日志中在一分钟内有一个404就来报警(为了好展示效果),我们可以随便访问一个不存在的地址,然后就会收到下面的报警了