1.服务版本号
服务名 | 版本号 | 端口 | 描述 |
---|---|---|---|
Grafana | latest | 3000 | 日志查看 |
Fluent-bit | 2.1.8或latest | 2020 | 日志收集 |
Elasticsearch | 7.17.12或latest | 9200,9300 | 存储日志数据 |
Nginx | latest | 80,443 | 网关 |
Kafka | latest | 9092 | 日志队列 |
2.服务器环境
服务器IP | 服务 | 描述 |
---|---|---|
192.168.52.171 | Nginx | |
192.168.52.172 | Kafka | |
192.168.52.173 | Grafana | |
192.168.52.174 | Fluent-bit | |
192.168.52.175 | Elasticsearch |
3.安装
nginx安装
yum安装方式
root@localhost:~# sudo yum install epel-release -y
root@localhost:~# sudo yum install nginx -y
配置nginx日志输出格式
root@localhost:~# vim /etc/nginx/nginx.conf
http {
# Logging Settings
log_format main '{"@timestamp":"$time_iso8601",'
'"@source":"$server_addr",'
'"hostname":"$hostname",'
'"ip":"$http_x_forwarded_for",'
'"client":"$remote_addr",'
'"request_method":"$request_method",'
'"scheme":"$scheme",'
'"domain":"$server_name",'
'"referer":"$http_referer",'
'"request":"$request_uri",'
'"args":"$args",'
'"size":$body_bytes_sent,'
'"status": $status,'
'"responsetime":$request_time,'
'"upstreamtime":"$upstream_response_time",'
'"upstreamaddr":"$upstream_addr",'
'"http_user_agent":"$http_user_agent",'
'"https":"$https"'
'}';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
}
Elasticsearch安装
生产环境建议调整Xms和Xmx参数
// 调整内核参数
sysctl -w vm.max_map_count=262144
// 创建持久化存储文件夹
mkdir -p /home/es/data
mkdir -p /home/es/plugins
// 设置文件夹权限
chmod 777 /home/es/data
chmod 777 /home/es/plugins
// 拉取镜像和安装
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.17.12
docker run \
--name es \
--restart always \
--publish 9200:9200 -p 9300:9300 \
--env "discovery.type=single-node" \
--env "ES_JAVA_OPTS=-Xms1024m -Xmx1024m" \
--volume /home/es/data:/usr/share/elasticsearch/data \
--volume /home/es/plugins:/usr/share/elasticsearch/plugins \
-d docker.elastic.co/elasticsearch/elasticsearch:7.17.12
Grafana 安装
// 创建持久化存储文件夹
mkdir -p /home/grafana/data
// 设置文件夹权限
chmod 777 /home/grafana/data
// 拉取镜像和安装
docker pull grafana/grafana-oss
docker run -d -p 3000:3000 --name=grafana \
--volume /home/grafana/data:/var/lib/grafana \
grafana/grafana-oss
kafka 安装
注意 --env “KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.52.172:9092” 改为自己服务器ip
mkdir -p /home/kafka/data
chmod 777 /home/kafka/data
docker pull bitnami/kafka:3.5
docker run -d -p 9092:9092 --name=kafka \
--volume /home/kafka/data:/bitnami/kafka/data \
--env "TZ=Aisa/Shanghai" \
--env "KAFKA_CFG_NODE_ID=0" \
--env "KAFKA_CFG_PROCESS_ROLES=controller,broker" \
--env "KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@localhost:9093" \
--env "KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093" \
--env "KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://192.168.52.172:9092" \
--env "KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" \
--env "KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER" \
--env "KAFKA_CFG_INTER_BROKER_LISTENER_NAME=PLAINTEXT" \
bitnami/kafka:3.5
4.两种方案
优势对比
类型 | 优点 | 缺点 |
---|---|---|
第一方案 | 速度快,简单 | 定位非常不准 |
第二方案 | 定位准确,可定制开发 | 复杂 |
4.1第一方案(使用fluent-bit内置geoip2定位ip坐标)
架构图
下载Geoip2离线数据库
访问 https://www.maxmind.com/ 网站
点击 MY ACCOUNT
点击 Download Files
选择 GeoLite2 City 点击Download GZIP(离线数据库每次都会有更新)
fluent-bit安装配置
// 下载好哦上传 GeoLite2-City.mmdb
root@localhost:~# mv GeoLite2-City.mmdb /home/fluent-bit
// 创建fluent-bit目录
root@localhost:~# mkdir -p /home/fluent-bit
root@localhost:~# cd /home/fluent-bit
root@localhost:~# vim /home/fluent-bit/fluent-bit.conf
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
[INPUT]
name tail
path /opt/nginx/*.log
DB /fluent-bit/etc/fluent-bit.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
Parser json
[OUTPUT]
Name es
Match *
Index syslog
Type journal
Host 192.168.52.173
Port 9200
Generate_ID On
Logstash_Format On
Time_Key_Nanos On
Current_Time_Index On
[FILTER]
Name geoip2
Match *
Database /fluent-bit/etc/GeoLite2-City.mmdb
Lookup_key client
Record country client %{country.names.zh-CN}
Record isocode client %{country.iso_code}
Record city client %{city.names.zh-CN}
Record latitude client %{location.latitude}
Record longitude client %{location.longitude}
[OUTPUT]
Name stdout
Match *
root@localhost:~# vim /home/fluent-bit/parsers.conf
[PARSER]
Name apache2
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>.*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache_error
Format regex
Regex ^\[[^ ]* (?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])?( \[client (?<client>[^\]]*)\])? (?<message>.*)$
[PARSER]
Name nginx
Format regex
Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
# https://rubular.com/r/IhIbCAIs7ImOkc
Name k8s-nginx-ingress
Format regex
Regex ^(?<host>[^ ]*) - (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*) "(?<referer>[^\"]*)" "(?<agent>[^\"]*)" (?<request_length>[^ ]*) (?<request_time>[^ ]*) \[(?<proxy_upstream_name>[^ ]*)\] (\[(?<proxy_alternative_upstream_name>[^ ]*)\] )?(?<upstream_addr>[^ ]*) (?<upstream_response_length>[^ ]*) (?<upstream_response_time>[^ ]*) (?<upstream_status>[^ ]*) (?<reg_id>[^ ]*).*$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name json
Format json
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
# --
# Since Fluent Bit v1.2, if you are parsing Docker logs and using
# the Kubernetes filter, it's not longer required to decode the
# 'log' key.
#
# Command | Decoder | Field | Optional Action
# =============|==================|=================
#Decode_Field_As json log
[PARSER]
Name docker-daemon
Format regex
Regex time="(?<time>[^ ]*)" level=(?<level>[^ ]*) msg="(?<msg>[^ ].*)"
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
[PARSER]
Name syslog-rfc5424
Format regex
Regex ^\<(?<pri>[0-9]{1,5})\>1 (?<time>[^ ]+) (?<host>[^ ]+) (?<ident>[^ ]+) (?<pid>[-0-9]+) (?<msgid>[^ ]+) (?<extradata>(\[(.*?)\]|-)) (?<message>.+)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
[PARSER]
Name syslog-rfc3164-local
Format regex
Regex ^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
Time_Key time
Time_Format %b %d %H:%M:%S
Time_Keep On
[PARSER]
Name syslog-rfc3164
Format regex
Regex /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
Time_Key time
Time_Format %b %d %H:%M:%S
Time_Keep On
[PARSER]
Name mongodb
Format regex
Regex ^(?<time>[^ ]*)\s+(?<severity>\w)\s+(?<component>[^ ]+)\s+\[(?<context>[^\]]+)]\s+(?<message>.*?) *(?<ms>(\d+))?(:?ms)?$
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
Time_Key time
[PARSER]
# https://rubular.com/r/0VZmcYcLWMGAp1
Name envoy
Format regex
Regex ^\[(?<start_time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)? (?<protocol>\S+)" (?<code>[^ ]*) (?<response_flags>[^ ]*) (?<bytes_received>[^ ]*) (?<bytes_sent>[^ ]*) (?<duration>[^ ]*) (?<x_envoy_upstream_service_time>[^ ]*) "(?<x_forwarded_for>[^ ]*)" "(?<user_agent>[^\"]*)" "(?<request_id>[^\"]*)" "(?<authority>[^ ]*)" "(?<upstream_host>[^ ]*)"
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
Time_Key start_time
[PARSER]
# https://rubular.com/r/17KGEdDClwiuDG
Name istio-envoy-proxy
Format regex
Regex ^\[(?<start_time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)? (?<protocol>\S+)" (?<response_code>[^ ]*) (?<response_flags>[^ ]*) (?<response_code_details>[^ ]*) (?<connection_termination_details>[^ ]*) (?<upstream_transport_failure_reason>[^ ]*) (?<bytes_received>[^ ]*) (?<bytes_sent>[^ ]*) (?<duration>[^ ]*) (?<x_envoy_upstream_service_time>[^ ]*) "(?<x_forwarded_for>[^ ]*)" "(?<user_agent>[^\"]*)" "(?<x_request_id>[^\"]*)" (?<authority>[^ ]*)" "(?<upstream_host>[^ ]*)" (?<upstream_cluster>[^ ]*) (?<upstream_local_address>[^ ]*) (?<downstream_local_address>[^ ]*) (?<downstream_remote_address>[^ ]*) (?<requested_server_name>[^ ]*) (?<route_name>[^ ]*)
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
Time_Key start_time
[PARSER]
# http://rubular.com/r/tjUt3Awgg4
Name cri
Format regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
[PARSER]
Name kube-custom
Format regex
Regex (?<tag>[^.]+)?\.?(?<pod_name>[a-z0-9](?:[-a-z0-9]*[a-z0-9])?(?:\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)_(?<namespace_name>[^_]+)_(?<container_name>.+)-(?<docker_id>[a-z0-9]{64})\.log$
// 拉取镜像
docker pull fluent/fluent-bit:2.1.8
docker run -d --name fluent-bit \
--restart always \
-v /var/log/nginx/:/opt/nginx \
-v /home/fluent-bit:/fluent-bit/etc/ \
fluent/fluent-bit:2.1.8
访问 http://192.168.52.173:3000,点击 Connections
搜索Elasticsearch
添加Elasticsearch服务器,选择Pattern为Daily
增加Dashboard Geomap
Layer type 选择Heatmap为热力图
4.2第二方案 (使用外部程序定位ip坐标)
架构图
由于第一方案采用fluent-bit内置geoip2,官方geoip2离线数据库定位效果很差,通过go程序对数据进行二次过滤。
fluent-bit 安装配置
// 创建fluent-bit目录
root@localhost:~# mkdir -p /home/fluent-bit
root@localhost:~# cd /home/fluent-bit
root@localhost:~# vim /home/fluent-bit/fluent-bit.conf
[SERVICE]
Flush 1
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
[INPUT]
name tail
path /opt/nginx/*.log
DB /fluent-bit/etc/fluent-bit.db
Mem_Buf_Limit 5MB
Skip_Long_Lines On
Refresh_Interval 10
Parser json
[OUTPUT]
Name kafka
Match *
Brokers 192.168.52.172:9092
Topics nginx-log
[OUTPUT]
Name stdout
Match *
root@localhost:~# vim /home/fluent-bit/parsers.conf
[PARSER]
Name apache2
Format regex
Regex ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>.*)")?$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name apache_error
Format regex
Regex ^\[[^ ]* (?<time>[^\]]*)\] \[(?<level>[^\]]*)\](?: \[pid (?<pid>[^\]]*)\])?( \[client (?<client>[^\]]*)\])? (?<message>.*)$
[PARSER]
Name nginx
Format regex
Regex ^(?<remote>[^ ]*) (?<host>[^ ]*) (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
# https://rubular.com/r/IhIbCAIs7ImOkc
Name k8s-nginx-ingress
Format regex
Regex ^(?<host>[^ ]*) - (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*) "(?<referer>[^\"]*)" "(?<agent>[^\"]*)" (?<request_length>[^ ]*) (?<request_time>[^ ]*) \[(?<proxy_upstream_name>[^ ]*)\] (\[(?<proxy_alternative_upstream_name>[^ ]*)\] )?(?<upstream_addr>[^ ]*) (?<upstream_response_length>[^ ]*) (?<upstream_response_time>[^ ]*) (?<upstream_status>[^ ]*) (?<reg_id>[^ ]*).*$
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name json
Format json
Time_Key time
Time_Format %d/%b/%Y:%H:%M:%S %z
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
# --
# Since Fluent Bit v1.2, if you are parsing Docker logs and using
# the Kubernetes filter, it's not longer required to decode the
# 'log' key.
#
# Command | Decoder | Field | Optional Action
# =============|==================|=================
#Decode_Field_As json log
[PARSER]
Name docker-daemon
Format regex
Regex time="(?<time>[^ ]*)" level=(?<level>[^ ]*) msg="(?<msg>[^ ].*)"
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
[PARSER]
Name syslog-rfc5424
Format regex
Regex ^\<(?<pri>[0-9]{1,5})\>1 (?<time>[^ ]+) (?<host>[^ ]+) (?<ident>[^ ]+) (?<pid>[-0-9]+) (?<msgid>[^ ]+) (?<extradata>(\[(.*?)\]|-)) (?<message>.+)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
[PARSER]
Name syslog-rfc3164-local
Format regex
Regex ^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$
Time_Key time
Time_Format %b %d %H:%M:%S
Time_Keep On
[PARSER]
Name syslog-rfc3164
Format regex
Regex /^\<(?<pri>[0-9]+)\>(?<time>[^ ]* {1,2}[^ ]* [^ ]*) (?<host>[^ ]*) (?<ident>[a-zA-Z0-9_\/\.\-]*)(?:\[(?<pid>[0-9]+)\])?(?:[^\:]*\:)? *(?<message>.*)$/
Time_Key time
Time_Format %b %d %H:%M:%S
Time_Keep On
[PARSER]
Name mongodb
Format regex
Regex ^(?<time>[^ ]*)\s+(?<severity>\w)\s+(?<component>[^ ]+)\s+\[(?<context>[^\]]+)]\s+(?<message>.*?) *(?<ms>(\d+))?(:?ms)?$
Time_Format %Y-%m-%dT%H:%M:%S.%L
Time_Keep On
Time_Key time
[PARSER]
# https://rubular.com/r/0VZmcYcLWMGAp1
Name envoy
Format regex
Regex ^\[(?<start_time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)? (?<protocol>\S+)" (?<code>[^ ]*) (?<response_flags>[^ ]*) (?<bytes_received>[^ ]*) (?<bytes_sent>[^ ]*) (?<duration>[^ ]*) (?<x_envoy_upstream_service_time>[^ ]*) "(?<x_forwarded_for>[^ ]*)" "(?<user_agent>[^\"]*)" "(?<request_id>[^\"]*)" "(?<authority>[^ ]*)" "(?<upstream_host>[^ ]*)"
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
Time_Key start_time
[PARSER]
# https://rubular.com/r/17KGEdDClwiuDG
Name istio-envoy-proxy
Format regex
Regex ^\[(?<start_time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)? (?<protocol>\S+)" (?<response_code>[^ ]*) (?<response_flags>[^ ]*) (?<response_code_details>[^ ]*) (?<connection_termination_details>[^ ]*) (?<upstream_transport_failure_reason>[^ ]*) (?<bytes_received>[^ ]*) (?<bytes_sent>[^ ]*) (?<duration>[^ ]*) (?<x_envoy_upstream_service_time>[^ ]*) "(?<x_forwarded_for>[^ ]*)" "(?<user_agent>[^\"]*)" "(?<x_request_id>[^\"]*)" (?<authority>[^ ]*)" "(?<upstream_host>[^ ]*)" (?<upstream_cluster>[^ ]*) (?<upstream_local_address>[^ ]*) (?<downstream_local_address>[^ ]*) (?<downstream_remote_address>[^ ]*) (?<requested_server_name>[^ ]*) (?<route_name>[^ ]*)
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
Time_Key start_time
[PARSER]
# http://rubular.com/r/tjUt3Awgg4
Name cri
Format regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
Time_Keep On
[PARSER]
Name kube-custom
Format regex
Regex (?<tag>[^.]+)?\.?(?<pod_name>[a-z0-9](?:[-a-z0-9]*[a-z0-9])?(?:\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)_(?<namespace_name>[^_]+)_(?<container_name>.+)-(?<docker_id>[a-z0-9]{64})\.log$
安装geo程序
// Linux
wget https://github.com/jidancong/geo/releases/download/v1.0.0/geo_v1.0.0_Linux_amd64.tar.gz
// Windows
wget https://github.com/jidancong/geo/releases/download/v1.0.0/geo_v1.0.0_Win-64bit.zip
配置config.yaml文件
kafka:
host: "localhost:9092"
topic: "nginx-log"
groupId: "1"
numPartitions: 3
replication: -1
es:
host: http://localhost:9200
preName: logstash-
chunzhen:
path: config/qqwry.dat
ip2location:
path: config/IP2LOCATION-LITE-DB11.BIN