1. 背景
日志主要包括系统日志、应用程序日志和安全日志。系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因。经常分析日志可以了解服务器的负荷,性能安全性,从而及时采取措施纠正错误。(3种日志作用)
通常,日志被分散的储存不同的设备上。如果你管理数十上百台服务器,你还在使用依次登录每台机器的传统方法查阅日志。这样是不是感觉很繁琐和效率低下。当务之急我们使用集中化的日志管理,例如:开源的 syslog ,将所有服务器上的日志收集汇总。
集中化管理日志后,日志的统计和检索又成为一件比较麻烦的事情,一般我们使用 grep 、 awk 和 wc 等 Linux命令能实现检索和统计,但是对于要求更高的查询、排序和统计等要求和庞大的机器数量依然使用这样的方法难免有点力不从心。
开源实时日志分析 ELK 平台能够完美的解决我们上述的问题, ELK 由 ElasticSearch 、 Logstash 和 Kiabana 三个开源工具组成。
官方网站: https://www.elastic.co/products
另一篇入门:分布式实时日志分析解决方案ELK部署架构
组件说明
日志收集(Beats, Logstash),日志存储搜索(Elasticsearch),展示查询(Kibana),更多见官网简介
组件组合&扩展
- ELK: Logstash -> Elasticsearch -> Kibana
- EFK: Fluentd -> Elasticsearch -> Kibana
- ELKStack: Filebeat -> Logstash -> Elasticsearch -> Kibana
- 扩展: Logstash -> 队列(redis, kafaka) -> Logstash -> Elasticsearch -> Kibana
组件简介
-
Elasticsearch:开源基于JSON分布式搜索引擎,它的特点有:分布式,实时,基于Lucence全文搜索,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等;《ELK在广告系统监控中的应用 及 Elasticsearch简介》
-
kibana: 开源和免费的工具, Kibana 可以为 Logstash 和 ElasticSearch 提供日志分析友好的 Web 界面,可以帮助您汇总、分析和搜索重要数据日志;
-
Logstash:完全开源的工具,他可以对你的日志进行收集、分析,并将其存储供以后使用。可以定制多种输入、输出及转换规则(过滤,分析,格式化)。Logstash 是动态数据收集管道,拥有可扩展的插件生态系统,能够与 Elasticsearch 产生强大的协同作用;
-
Beats:轻量型采集器的平台,从边缘机器向 Logstash 和 Elasticsearch 发送数据;
工作流程
在需要收集日志的所有服务上部署 logstash ,作为 logstash agent(logstash shipper)用于监控并过滤收集日志,将过滤后的内容发送到 logstash indexer , logstash indexer 将日志收集在一起交给全文搜索服务 ElasticSearch ,可以用 ElasticSearch 进行自定义搜索通过 Kibana 来结合自定义搜索进行页面展示。
目录 Directory Layout
4个组件的目录基本统一:
类型 | 描述 | 配置项 | ElasticSearch | Kibana | Logstash | Filebeat |
---|---|---|---|---|---|---|
home | home目录 | $ES_HOME | $KIBANA_HOME | {extract.path} | path.home | |
bin | 二进制文件 | $ES_HOME/bin<br>elasticsearch<br>elasticsearch-plugin | $KIBANA_HOME\bin<br>kibana<br>kibana-plugin | {extract.path}/bin<br>logstash<br>logstash-plugin | {path.home}<br>filebeat | |
conf | 配置文件 | $ES_HOME/conf<br>elasticsearch.yml<br>jvm.options<br>log4j2.properties | $KIBANA_HOME/config<br>kibana.yml | {path.home}<br>filebeat.yml | ||
settings | 配置文件 | path.settings | {extract.path}/config<br>logstash.yml<br>jvm.options<br>log4j2.properties<br>startup.options | |||
data | 数据文件 | path.data | $ES_HOME/data<br>可配置多个 | $KIBANA_HOME/data | {extract.path}/data | {path.home}/data |
logs | 日志文件 | path.logs | $ES_HOME/logs | {extract.path}/logs | {path.home}/logs | |
plugins | 插件目录 | $ES_HOME/plugins | $KIBANA_HOME/plugins | {extract.path}/plugins | ||
script | 脚本目录 | path.scripts | $ES_HOME/scripts | |||
repo | path.repo | |||||
optimize | $KIBANA_HOME/optimize |
ELK Docker 文档
以下镜像dockerfile参考k8s中EFK的写法,Dockerfile注意点:
- 公用的或不经常变化的命令写在前面,可以镜像间层可以复用,制作镜像时也可以复用不用重新构建
- 使用add或copy,再tar会导致文件会有2倍的大小,即使镜像压缩后可以变小,但是公用层不能复用
- 使用curl不使用wget,curl支持管道操作,管道后接tar命令,构建出来的镜像不会出现2倍体积
- 如果需要另外的用户启动服务,可以再Dockerfile先用root用户,再最后的启动脚本中使用gosu切换用户启动应用,并再此之前更改文件的所有者
- 可以在启动脚本中设置环境变量的默认值,在配置文件中使用环境变量注入配置。软件的根目录使用环境变量替代,还有设置PATH
- -x84_64.tar.gz后缀的包貌似不兼容alpine系统,需要FROM其他小的系统如ubuntu-slim
ES
1.镜像制作
目录
ES
├──Dockerfile-es
├──config/
| ├── elasticsearch.yml //ES配置文件
| ├── jvm.options //ES jvm 调优配置,使用官方默认配置
| └── log4j2.properties //ES 日志配置文件,使用官方默认配置
└──shell
├── env-es //公共的环境变量配置属性键值对
├── env-es-master //master节点的环境变量配置属性键值对
├── env-es-slave //slave节点的环境变量配置属性键值对
├── run.sh //启动脚本
└── sysConfig.sh //es宿主机优化脚本
Dockerfile-es
FROM java:openjdk-8-jre-alpine
ENV LANG='zh_CN.UTF-8'
RUN apk update \
&& apk --no-cache add \
--repository https://dl-3.alpinelinux.org/alpine/edge/main \
--repository https://dl-3.alpinelinux.org/alpine/edge/testing \
--repository https://dl-3.alpinelinux.org/alpine/edge/community \
shadow curl tar gosu
ENV ELASTICSEARCH_VERSION=5.6.4 \
ES_HOME=/ES \
PATH=${ES_HOME}/bin:$PATH
WORKDIR ${ES_HOME}
RUN curl -fL https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-$ELASTICSEARCH_VERSION.tar.gz \
| tar --strip-components=1 -zx
RUN mkdir data logs \
&& useradd --no-create-home --user-group elasticsearch \
&& apk del curl tar
COPY config config
COPY shell/run.sh /
RUN chmod 775 /run.sh
CMD ["/run.sh"]
run.sh
#!/bin/sh
export NODE_NAME=${NODE_NAME:-${HOSTNAME}}
export NODE_DATA=${NODE_DATA:-true}
export NODE_PORT_HTTP=${NODE_PORT_HTTP:-9200}
export NODE_PORT_TCP=${NODE_PORT_TCP:-9300}
chown -R elasticsearch:elasticsearch ${ES_HOME}
exec gosu elasticsearch sh ${ES_HOME}/bin/elasticsearch
elasticsearch.yml
#节点&集群
#集群名称,节点通过集群名称加入集群
cluster.name: es-logging
#节点名称
node.name: ${NODE_NAME}
#是否为master节点,默认为true
node.master: ${NODE_MASTER}
#是否为数据节点,默认为true
node.data: true
discovery.zen.minimum_master_nodes: 1
discovery.zen.ping.unicast.hosts:
- ${DISC_NODE_1}
- ${DISC_NODE_2}
#网络
transport.tcp.port: ${NODE_PORT_TCP}
http.port: ${NODE_PORT_HTTP}
#节点绑定的IP地址
network.host: 0.0.0.0
network.publish_host: ${NODE_IP}
#路径配置:数据和日志路径
path.data: ${ES_HOME}/data
path.logs: ${ES_HOME}/logs
#验证关闭系统swapping
#bootstrap.memory_lock: true
sysConfig.sh
#设置MMap
sysctl -w vm.max_map_count=262144
#打开文件的最大数目
ulimit -n 65536
#shell所能建立的最大文件
ulimit -f unlimited
#最大内存大小
ulimit -m unlimited
# 虚拟内存
ulimit -v unlimited
#最大进程
ulimit -u 2048
#要使 limits.conf 文件配置生效,必须要确保 pam_limits.so 文件被加入到启动文件中。
#查看 /etc/pam.d/login 文件中有: session required /lib/security/pam_limits.so
echo "session required /lib/security/pam_limits.so" >> /etc/pam.d/login
# vi /etc/security/limits.conf
#打开文件的最大数目
echo "root - nofile 65536" >> /etc/security/limits.conf
echo "elasticsearch - nofile 65536" >> /etc/security/limits.conf
#最大锁定内存地址空间
echo "root - memlock unlimited" >> /etc/security/limits.conf
echo "elasticsearch - memlock unlimited" >> /etc/security/limits.conf
#地址空间限制
echo "root - as unlimited" >> /etc/security/limits.conf
echo "elasticsearch - as unlimited" >> /etc/security/limits.conf
#最大文件大小
echo "root - fsize unlimited" >> /etc/security/limits.conf
echo "elasticsearch - fsize unlimited" >> /etc/security/limits.conf
#最大进程
echo "root - noproc 2048" >> /etc/security/limits.conf
echo "elasticsearch - noproc 2048" >> /etc/security/limits.conf
# vi /etc/security/limits.d/20-nproc.conf #打开进程数
echo "root - nproc 65536 ">> /etc/security/limits.d/20-nproc.conf
echo "elasticsearch - nproc 65536">> /etc/security/limits.d/20-nproc.conf
2.启动示例
docker build -t es:5.6.4 -f Dockerfile-es .
mkdir -p /data/ELK/ES/master/data
mkdir -p /data/ELK/ES/master/logs
mkdir -p /data/ELK/ES/slave1/data
mkdir -p /data/ELK/ES/slave1/logs
### master
docker run -d -p 9200:9200 -p 9300:9300 \
--env-file /data/ELK/ES/shell/env-es-master \
--env-file /data/ELK/ES/shell/env-es \
-v /data/ELK/ES/master/data:/ES/data \
-v /data/ELK/ES/master/logs:/ES/logs \
--name es-master \
es:5.6.4
### slave1
docker run -d -p 9201:9201 -p 9301:9301 \
--env-file /data/ELK/ES/shell/env-es-slave \
--env-file /data/ELK/ES/shell/env-es \
-v /data/ELK/ES/slave1/data:/ES/data \
-v /data/ELK/ES/slave1/logs:/ES/logs \
--name es-slave1 \
es:5.6.4
Kibana
1.镜像制作
目录
Kibana
├──Dockerfile-kibana
└──shell
└── run.sh //启动脚本
Dockerfile-kibana
FROM docker.io/zshiki/ubuntu-slim
ENV LANG='zh_CN.UTF-8'
RUN apt-get update \
&& apt-get install -y curl \
&& apt-get clean
ENV KIBANA_VERSION=5.6.4 \
KIBANA_HOME=/kibana \
PATH=${KIBANA_HOME}/bin:$PATH
WORKDIR ${KIBANA_HOME}
RUN curl -fL https://artifacts.elastic.co/downloads/kibana/kibana-$KIBANA_VERSION-linux-x86_64.tar.gz \
| tar --strip-components=1 -zx
COPY shell/run.sh /
RUN chmod 775 /run.sh
CMD ["/run.sh"]
run.sh
#!/bin/sh
export PORT=${PORT:-5601}
export ES_URL=${ES_URL:-"http://localhost:9200"}
echo "server.host: \"0\""
echo "server.host: \"0\"" >> ${KIBANA_HOME}/config/kibana.yml
echo "server.name: kibana-logging"
echo "server.name: kibana-logging" >> ${KIBANA_HOME}/config/kibana.yml
echo "server.port: ${PORT}"
echo "server.port: ${PORT}" >> ${KIBANA_HOME}/config/kibana.yml
echo "elasticsearch.url: ${ES_URL}"
echo "elasticsearch.url: ${ES_URL}" >> ${KIBANA_HOME}/config/kibana.yml
sh ${KIBANA_HOME}/bin/kibana
2.启动示例
docker build -t kibana:5.6.4 -f Dockerfile-kibana .
docker run -d -p 5601:5601 \
-e ES_URL=http://192.168.52.7:9200 \
--name kibana-logging \
kibana:5.6.4
Logstash
1.镜像制作
目录
Logstash
├──Dockerfile-logstash
├──config/
| ├── logstash.yml //logstash配置文件
| ├── jvm.options //logstash jvm 调优配置,使用官方默认配置
| └── log4j2.properties //logstash 日志配置文件,使用官方默认配置
└──shell
└── run.sh //启动脚本
Dockerfile-logstash
FROM java:openjdk-8-jre-alpine
ENV LANG='zh_CN.UTF-8'
RUN apk update \
&& apk --no-cache add \
--repository https://dl-3.alpinelinux.org/alpine/edge/main \
--repository https://dl-3.alpinelinux.org/alpine/edge/testing \
--repository https://dl-3.alpinelinux.org/alpine/edge/community \
shadow curl tar fontconfig freetype gosu bash
ENV LOGSTASH_VERSION=5.6.4 \
LOGSTASH_HOME=/logstash \
PATH=${LOGSTASH_HOME}/bin:$PATH
WORKDIR ${LOGSTASH_HOME}
RUN curl -Lf https://artifacts.elastic.co/downloads/logstash/logstash-$LOGSTASH_VERSION.tar.gz \
| tar --strip-components=1 -xz
RUN mkdir pipeline logs \
&& useradd --no-create-home --user-group logstash \
&& apk del curl tar
COPY config config
COPY shell/run.sh /
RUN chmod 775 /run.sh
CMD ["/run.sh"]
run.sh
#!/bin/sh
export LOG_LEVEL=${LOG_LEVEL:-info}
export PIPELINE_WORKERS=${PIPELINE_WORKERS:-2}
chown -R logstash:logstash ${LOGSTASH_HOME}
exec gosu logstash sh ${LOGSTASH_HOME}/bin/logstash
logstash.yml
pipeline.workers: ${PIPELINE_WORKERS}
log.level: ${LOG_LEVEL}
http.host: "0.0.0.0"
path.config: ${LOGSTASH_HOME}/pipeline
config.reload.automatic: true
2.启动示例
pipeline/logstash.conf
input {
beats {
port => 5044
}
}
filter {
if [fields][app_id] == "portal" {
grok {
match => { "message" => "%{HOUR:hour}:%{MINUTE:minute}:%{SECOND:second} \[%{DATA:thread}\] %{LOGLEVEL:log_level} %{DATA}" }
}
}
if [fields][source_type] == "docker_log" {
mutate {
rename => { "log" => "message" }
}
grok {
match => { "message" => "\[%{DATA}%{HOUR:hour}:%{MINUTE:minute}:%{SECOND:second}\] %{DATA:thread} %{LOGLEVEL:log_level} %{DATA}" }
}
}
}
output {
elasticsearch {
hosts => [ "192.168.0.7:9200" ]
}
}
启动示例
docker build -t logstash:5.6.4 -f Dockerfile-logstash .
docker run -d -p 5044:5044 \
-v /data/ELK/Logstash/pipeline:/logstash/pipeline \
--name logstash \
logstash:5.6.4
Filebeat
1.镜像制作
目录
Filebeat
└──Dockerfile-filebeat
Dockerfile-filebeat
FROM docker.io/zshiki/ubuntu-slim
ENV LANG='zh_CN.UTF-8'
RUN apt-get update \
&& apt-get install -y curl \
&& apt-get clean
ENV BEAT_VERSION=5.6.4 \
FILEBEAT_HOME=/filebeat \
PATH=${FILEBEAT_HOME}:$PATH
WORKDIR ${FILEBEAT_HOME}
RUN curl -Lf https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-${BEAT_VERSION}-linux-x86_64.tar.gz \
| tar --strip-components=1 -xz
RUN mkdir data logs \
&& apt-get purge -y curl
RUN ls -l ${FILEBEAT_HOME}
CMD ["filebeat, "-e"]
2.启动示例
config/filebeat.yml
filebeat.prospectors:
- type: log
scan_frequency: 10s
json.keys_under_root: true
json.add_error_key: true
json.message_key: log
multiline.pattern: '^\['
multiline.negate: true
multiline.match: after
fields:
source_type: docker_log
paths:
- /data/docker/containers/*/*-json.log
output.logstash:
hosts: ["192.168.0.7:5044"]
worker: 2
loadbalance: true
index: filebeat
启动示例
docker build -t filebeat:5.6.4 -f Dockerfile-filebeat .
docker run -d \
-v /opt/filebeat/config/filebeat.yml:/filebeat/filebeat.yml \
-v /data/docker/containers:/data/docker/containers \
--name filebeat-docker \
filebeat:5.6.4