概述
应用日志的收集,在docker未出现前,常用做法是将日志写到主机的某个文件,当需要查看日志时先登录到主机然后去查看相关的日志文件,主机上的日志只要不特意删除会一直存在;随着docker和容器编排技术的出现,容器的销毁和重新创建被看成是一种常态,如果用原来的方式将应用日志写到容器的文件系统里,那么当容器销毁时日志就会丢失,这种情况下,一种有效的解决方法是引入集中式的日志管理系统,将容器的日志通过agent发送给日志系统进行统一存储。
目前主流的分布式日志系统有ELK,flume,fluentd,splunk等,本文利用腾讯云容器服务搭建ELK系统收集nginx的访问日志 ,搭建过程中共搭建如下4个服务,每个服务的功能如下:
- ElasticSearch服务:该服务提供日志集中存储和查询
- Kibana服务:以web的形式提供日志的可视化展现和查询界面
- Kibana登录鉴权服务:Kibana如果直接放到公网上,因为kibana本身没有鉴权机制,如果被端口扫描工具扫出来,很容易出现他人查看操作日志的行为,为避免该情况,在kibana前面加个nginx服务,利用nginx的basic认证来做鉴权。
- nginx应用服务:创建该服务的目的是为了验证是否可以将应用日志采集到ElasticSearch里。该服务用nginx的访问日志做日志源,浏览器每访问一次nginx服务,就会生成一条access日志,和该服务在一起的logstash容器会将新产生的日志发到ElasticSearch里。
在开始搭建日志系统之前,了解容器日志的输出方式和ELK的相关配置是非常必要的,因此下文先简单介绍这两个方面的内容,然后再进行具体的搭建步骤说明,最后对搭建过程中碰到的问题进行总结。
下面的搭建方法采用的方式是一个应用容器绑定一个采集日志容器的做法,如果一台主机上有很多服务的日志需要采集,建议先提前规划好日志目录,用一台主机放一个日志采集容器的方法。
容器日志输出方式
容器日志常见输出形式主要有以下两种:
- 标准输出,当docker 的log driver使用默认设置时,用docker logs 命令看到的日志输出采用的就是这种方式。如果要收集标准输出的日志,常用方式是设置docker 的log driver,目前docker 支持的log driver 如下表所示,但是kubernetes目前的pod 定义中还不包括log driver的字段,一种解决方式是修改docker daemon的log driver默认配置。
日志驱动名称 | 描述 |
---|---|
none | 不输出任何内容 |
json-file | 日志以json的格式保存成文件,这是默认的docker日志输出驱动 |
syslog | 将log日志输出到syslog |
journald | 将日志输出到journald |
gelf | 将日志输出到支持GaryLog Extended log Fromat(GELF)格式的日志采集系统如Graylog和Logstash |
fluentd | 将日志输出到fluentd |
awslogs | 将日志输出到AWS cloudwatch logs |
splunk | 将日志输出到Splunk |
etwlogs | 将日志输出到Windows的事件系统,这个驱动只有对windows下的docker生效 |
gcplogs | 将日志输出到GCP的日志系统中 |
-
日志文件输出,像nginx、apache,tomcat等应用都是将日志写到文件。这种形式的日志输出常用的收集方式有两种:
- 每个应用容器单独再配一个日志采集容器如logstash或filebeat等,在k8s中应用容器和日志采集容器可以做成一个pod,然后他们之间共享一个volume。这种方式的缺点是应用容器越多,日志采集容器也会变多,会增加系统的资源消耗。
- 在主机上放一个日志采集容器,将某个主机目录映射到容器内,同时将该主机目录映射到每个应用容器的日志输出目录,这种方式的优点是每台主机上只有一个日志采集容器,缺点是要对每个应用的日志输出目录做本地磁盘映射,同时要提前规划好各个应用的日志文件名称和目录,避免出现不同应用生成相同的日志文件或日志目录。
ELK简介和配置简介
ELK是由ElasticSearch、logstash和kibana三个项目共同组成的日志系统,其中:
- ElasticSearch(简称ES)是日志搜索引擎,java语言开发,提供日志存储、聚合、搜索和查询等功能,支持插件
- logstash是日志采集模块,java语言开发,支持的日志输入源包括syslog、http、tcp、file等,日志输出端支持kafka、elasticsearch、file和mogodb等,有丰富的插件。
- kibana是日志分析和展现模块,可以通过配置和ElasticSearch对接,支持自定义界面展示。
在ELK系统中采集端如果用logstash,logstash因为是java写的,对系统资源占用相对较多,因此ELK生态中出现了轻量级的采集端beat家族,目前beat家族的成员如下:
- Filebeat:采集文件中的日志到Elasticsearch。
- Metricbeat:采集主机的CPU、内存、文件系统、磁盘IO和网络IO等信息到ElasticSearch
- Packetbeat:对网络包进行采集,将采集数据发到ElasticSearch
- winlogbeat:采集windows系统的event logs到Elasticsearch
- Heartbat:对服务进行健康检查,将检查结果发给Elasticsearch
Elasticsearch配置
elaticsearch的配置文件采用yaml文件格式
配置项名称 | 含义 |
---|---|
cluster.name | ES集群名称 |
node.name | ES节点名称 |
node.master | 是否允许该节点成为master |
node.data | 是否允许该节点成为数据节点 |
path.logs | 存储ES日志的目录 |
path.data | 存储ES数据的目录 |
transport.host | bind的主机地址 |
discovery.zen.ping.unicast.hosts | 集群中主机之间相互发现时使用该字段,指定集群内的所有主机IP,格式如discovery.zen.ping.unicast.hosts: ["host1", "host2"] |
discovery.zen.minimum_master_nodes | 成为master的时候,集群中至少需要多少个节点 |
logstash配置
logstash的配置文件包含三部分:
1、input部分,指定数据源,可以同时指定多个数据源,可以使syslog,标准输出,kafka等
2、filter部分,数据处理部分,可以对输入的数据做格式解析,提取新字段等。
3、output部分, 指定数据输出到哪里,可以同时指定多个输出源,可以是标准输出,kafka,redis和elasticsearch等。
kibana配置
kibana的配置文件也是采用yaml格式
配置项名称 | 含义 |
---|---|
server.port | web的服务端口,默认为5601 |
elasticsearch.url | 指定elasticsearch的url |
server.ssl.enabled | 是否启用https |
server.ssl.certificate | ssl证书 |
logging.dest | 日志输出的地址 |
logging.silent | 控制是否输出日志 |
使用腾讯云容器服务搭建ELK
下面的搭建步骤基于腾讯云容器服务来搭建,容器服务集群中包括两台机器,配置是2核4G的ubuntu16.04系统。
搭建步骤如下:
- 创建 elasticseach服务,该服务包含一个ES容器
- 创建kibana服务,该服务包含一个kibana容器,kibana和Elasticsearch 之间以服务的方式连接。
- 搭建kibana的鉴权服务,该服务使用nginx容器,放在kibana前面,避免kibaban在公网上被随机登录使用。
- 创建nginx应用服务,该服务包括两个容器,一个nginx还有一个是logstash,logstash和nginx容器之间共享数据目录/var/log/nginx,logstash采集nginx的访问日志文件并将新生成的日志发给ElasticSearch。
搭建过程中使用的镜像是dockerhub上的offical的elasticsearch、kibana和logstash的镜像,版本为5.4
在搭建ELK之前,参考Elasticsearch的安装文档,需要对容器主机做相关设置:
- 使用free命令确保主机至少有3G的可用内存,因为elastic search的镜像中的java配置参数是2G.
- 在每台容器主机上执行sysctl -w vm.max_map_count=262144。ES默认使用 hybrid mmapfs / niofs 目录来存储索引。 默认操作系统对mmap计数的限制太低,可能引发内存不足的异常。
创建elasticsearch服务
启动elastcisearch的docker run命令如下:
docker run -d --name elas -p 9200:9200 -v "$PWD/esdata":/usr/share/elasticsearch/data elasticsearch -Etransport.host=0.0.0.0 -Ediscovery.zen.minimum_master_nodes=1
对应腾讯容器服务控制台创建elasticsearch的参数配置如下:
配置项名称 | 配置值 |
---|---|
服务名称 | elasticsearch |
CPU限制 | 1核 |
内存限制 | 3072M |
镜像 | elasticsearch |
镜像版本 | 5.4 |
实例个数 | 1 |
运行命令 | /docker-entrypoint.sh |
运行参数 | -Etransport.host=0.0.0.0 -Ediscovery.zen.minimum_master_nodes=1 |
网络参数 | 网络方式选仅在集群内访问,协议选TCP,容器端口9200,服务端口9200 |
数据卷 | 选择本地磁盘,选择一个主机目录,取名为esdata |
挂载点 | 选择数据卷esdata,映射到容器目录为/usr/share/elasticsearch/data |
另外为了避免容器还在运行状态但是es进程异常导致服务不可用,可以考虑给elasticsearch服务配置健康检查参数。
创建kibana服务
启动kibana的docker run 命令如下:
docker run --name some-kibana --link elas:elasticsearch -p 5601:5601 -d kibana
对应腾讯容器服务控制台创建该服务的参数如下:
配置项名称 | 配置值 |
---|---|
服务名称 | kibana |
核数 | 1核 |
内存 | 512M |
实例个数 | 1 |
镜像 | kibana |
镜像版本 | 5.4 |
网络方式 | 提供集群内访问方式,不在公网上直接暴露5601的端口 |
创建kibana认证服务
第一步,在主机上用htpasswd生成包含用户名和密码的文件,具体的使用方式如下:
htpasswd /etc/nginx/.htpasswd administrator
在两台主机上将生成的帐号密码文件.htpasswd放到该目录放到/home/ubuntu/conf目录下
第二步,在两台主机的/home/ubuntu/conf目录下创建nginx的配置文件内容如下:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
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;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main;
location / {
auth_basic "kibana ui";
auth_basic_user_file /conf/.htpasswd;
autoindex on;
proxy_pass http://kibana:5601;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
第三步,创建nginx认证服务,配置参数如下:
配置项名称 | 配置值 |
---|---|
服务名称 | nginx-basicauth |
镜像 | nginx |
数据卷1 | 使用本地硬盘,volume名称用nginxconf,对应的主机目录 /home/ubuntu/conf/nginx.conf |
数据卷2 | 使用本地磁盘,volume名称用passwd,对应的主机目录/home/ubuntu/conf/.htpasswd |
网络方式 | 提供公网访问,容器端口用80,服务端口用9098 |
挂载点1 | 使用volume nginxconf,容器内目录/etc/nginx/nginx.conf,放nginx的配置文件 |
挂载点2 | 使用volume passwd,容器内目录/conf/.htpasswd,放basic auth的用户密码文件 |
第四步,在外网访问kibana服务,输入正确的用户名和密码后,可以看到kibana的界面,表示这一步配置成功。
输入正确的用户名和密码后,如果可以看到kibana的如下界面,表示ELK搭建成功:
创建nginx应用服务
第一步,先在两台主机上放好logstash的配置文件,配置文件的内容如下,配置文件放到主机的/home/ubuntu/conf/logstash.conf,需要映射到logstash容器的目录为/conf/logstash.conf。该配置文件同时抓取access.log和error.log两个日志文件到elasticsearch。
input {
file {
path => ["/var/log/nginx/access.log"]
type => "nginx_access_log"
start_position => "beginning"
}
file {
path => ["/var/log/nginx/error.log"]
type => "nginx_error_log"
start_position => "beginning"
}
}
output {
elasticsearch { hosts => ["elasticsearch:9200"] }
}
第二步,创建两个数据卷
数据卷名称 | 对应的本机目录 | 用途 |
---|---|---|
log | 不填主机上临时分配 | nginx在该目录下产生日志,logstash读取该目录的日志 |
conf | /home/ubuntu/conf | 存放nginx的启动配置文件 |
第三步, 创建logstash容器
配置项名称 | 配置值 |
---|---|
实例个数 | 1 |
镜像 | logstash |
镜像版本 | 5.4 |
核数 | 1 |
内存 | 1650M |
运行命令 | /docker-entrypoint.sh |
运行参数 | -f /conf/logstash.conf,注意在界面上填的时候,-f 和/conf/logstash.conf要放两行 |
挂载点1 | 挂载卷 log,容器目录/var/log/nginx,存放nginx生成的日志文件 |
挂载点2 | 挂载卷conf,容器目录/conf,存放logstash的配置文件 |
第四步, 创建nginx容器
配置项名称 | 配置值 |
---|---|
实例个数 | 1 |
镜像 | nginx |
内存 | 128M |
核数 | 0.2 |
挂载点1 | 挂载卷 log,容器目录/var/log/nginx,存放nginx生成的日志文件 |
第五步,将nginx的端口80映射到外部8088端口
第六步:等服务创建完成,通过网页访问nginx服务,检查kibana界面上是否可以看到nginx的访问日志,正常的访问日志如下:
搭建过程问题总结
在使用腾讯云容器服务搭建ELK系统的过程中碰到如下问题:
- 容器主机上用docker run运行ElasticSearch没有跑起来,原因是elasticSearch对内存要求较高,申请的主机如果只有1G的内存,容器会自动退出(原因为配置的javaoption都是2G的内存,配置文件在/etc/elasticsearch目录),建议使用4G以上的主机;另外需要对主机执行sysctl -w vm.max_map_count=262144。运行ES之前需要做的系统配置在https://www.elastic.co/guide/en/elasticsearch/reference/current/system-config.html
- 对于docker run命令可以正常运行的elastic search,相同的参数使用容器服务运行失败。原因是容器服务默认做了内存限制,大小为128M,因为镜像中的默认配置是2G,先通过free看可用内存是否有2G,如果没有的2G的话就用 echo 3 > /proc/sys/vm/drop_caches 释放一部分内存出来,如果该参数设置为刚刚好2G还是会报错killed,建议用3G。
- logstash用默认设置128M启动不了,原因是镜像中配置的jvm option选项最小堆栈是256M,最大为1g;通过将logstash的内存调到1G。
- 数据采集端logstash如果采集的是主机某个目录的日志,碰上这种情况需要容器服务支持daemonset的功能,指定对应的采集pod在某些label的主机上运行,还有如果可以支持自定义配置文件的定义和下发,让容器可以引用到下发的配置文件,就不用预先在所有的主机上预先放置配置文件了。这两个需求点待容器服务推出daemonset和配置管理功能后就能很好地解决。
- 如果需要重新搭建一套elk环境,通过目前界面上的操作比较繁琐,希望能够支持从已经部署完的服务中导出yaml配置文件,支持从yaml文件导入创建服务。这个需求点,目前容器服务团队已经在做应用模版的开发工作,推出应用模版后可以解决该问题。
- 本文档的部署方式只用一个elaticsearch容器,当elasticsearch容器所在的主机异常时,如果elasticsearch在另外一台机器上启动了,原先的数据就看不到了,针对这种情况,就需要容器服务支持网络存储如nfs volume等。olume等。