目录
- kubernetes集群
- 集群拓扑图
- 新节点加入集群
- 本地 docker registry与开发环境
- ELK 开发与部署
- ELK架构拓扑图
- Elasticsearch 开发、调试与部署
- Kibana 部署
- Logstash+filebeat 开发、调试与部署
注:本文仅涉及使ELK+k8s能够正确搜集并分析ZStack manager log所做的相关开发工作,关于HA(Hign available)、HC(High Concurrency)、日志保存等更多问题将在后期研究中给出相关解决方案。后续会进行如下优化或研究:
一、kubernetes集群
- 集群结构拓扑图(位于172.20.0.10上的4台vm搭建的kubernetes集群)
- kubeadm version:1.14.2-0
- kubelet version: 1.14.2-0
- kubernetes-cni version: 0.7.5-0
- docker server version: 1.13.1
- 新woker node加入集群方法
- 新节点加入cluster命令:
kubeadm join 10.0.159.230:6443 --token lqrxg1.j8nlsaz7kdhjkaro \ |
- 注意其中token的生命周期为24 hours,在master.node上采用以下命令获得新token:
kubeadm token create |
- 随后即可观察到新加入的节点
二、本地 docker registry与开发环境
- 目的
- 在172.20.0.10上的一台vm,并在其上使用dokcer快速构建出一个docker image registry,挂载vlomue /opt/data/registry:/var/lib/registry,可将镜像保存到本地的/opt/data/registry中,访问地址为:172.20.11.210:5000。
注意:selinux运行条件下,docker daemon进程不具备读写host普通文件的能力,此时可关闭selinux;或者正确设置selinux,将普通文件加入到docker daemon能够读取的类型之中。 - elastic官方的镜像仓库访问慢,本地的image registry有利于自行构建镜像的分发
- 要使用该镜像仓库,需要对docker daemon进行以下配置
- 在172.20.0.10上的一台vm,并在其上使用dokcer快速构建出一个docker image registry,挂载vlomue /opt/data/registry:/var/lib/registry,可将镜像保存到本地的/opt/data/registry中,访问地址为:172.20.11.210:5000。
docker client 1.13版本后默认使用https与image registry进行通信,需要配置docker daemon使用http与172.20.11.210:5000进行通信
# vim /usr/lib/systemd/system/docker.service .... ExecStart=/usr/bin/dockerd-current \ ... |
2.开发环境与镜像的构建、分发与获取
-
- 172.20.0.10上的一台vm包含了本次需要的所有镜像,并已经提交到image registry,(vm ip:10.0.191.182,EIP:172.20.11.210),建议在该机器上进行开发。
- 所有代码已提交到git lab,位于zstack-elk-in-kubernetes,账号:密码为kaihang.zhang:password
- 接下来以创建一个logstash_zstack镜像为例(除构建镜像外,在开发调试中,利用docker -v将配置文件挂载到容器中,进行快速调试;或者开发部署中,利用k8s GitRepo来提高效率):
Dev/Logstash/config_for_logstash_docker_image下为构建logstash_zstack 镜像相关文件,文件内容将在下一节进行说明.
├── Dockerfile
└── pipeline
├── logstash.conf
└── patterns
└── zstack
在Dev/Logstash/config_for_logstash_docker_image内构建镜像
docker build -t 10.0.191.182:5000/logstash_zstack . |
然后可以看到构建的镜像,并将其推送到image registry
[root@10-0-191-182 config_for_logstash_docker_image]# docker images [root@10-0-191-182 config_for_logstash_docker_image]# docker push 10.0.191.182:5000/logstash_zstack |
在同一私网下可获取该镜像10.0.191.182:5000/logstash_zstack
docker pull 10.0.191.182:5000/logstash_zstack |
三、ELK开发与部署
1.ELK架构拓扑图
2.Elasticsearch 开发、调试与部署
2.1 开发
-
- 在Dev/Elasticsearch/config_for_elasticsearch_docker_image内,目录文件如下:
.
├── Dockerfile
└── entrypoint_scripts
├── create-cluster-info.sh
└── docker-entrypoint.sh
-
- Dockerfile
构建时尽可能减少RUN、COPY、ADD指令数量,这些指令会增加镜像层数,最终可能导致docker文件系统读写效率降低;
将COPY放在RUN之后,entrypoint_scripts中文件内容改变时,仍可利用前面指令的cache快速构建镜像。
- Dockerfile
FROM docker.elastic.co/elasticsearch/elasticsearch:7.2.0
RUN yum install -y epel-release && \
yum install -y jq && \
yum clean all
COPY entrypoint_scripts/ /usr/local/bin/
-
- docker-entrypoint.sh为elasticsearch官方镜像中默认的ENTRYPOINT指令。
creat-cluster-info.sh 该脚本用于构建elasticsearch cluster的相关信息。
export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt curl -H "Authorization: Bearer $TOKEN" "https://kubernetes/api/v1/namespaces/default/pods?labelSelector=pod-for=elasticsearch" \ #设置pod访问k8s api的证书与token,通过pod的label来检索目前集群中所有的elasticsearch节点,并将相应信息存储到 /usr/share/elasticsearch/elasticsearch-pod.json中 #如果k8s使用了RBAC,则pod无权限访问k8s api,此时需要给pod相应权限,在内部环境中,可简单使用命令给予每个pod访问api server的权限 #$ kubectl create clusterrolebinding permissive-binding \
elasticsearch_pod_json=/usr/share/elasticsearch/elasticsearch-pod.json elasticsearch_config_create(){ for elasticsearch_arg in ${elasticsearch_args} # Set discovery.seed_hosts in elasticsearch.yml # Set cluster.initial_master_nodes in elasticsearch.yml #配置elasticsearch cluster的启动配置文件/usr/share/elasticsearch/config/elasticsearch.yml,即将目前集群中的elasticsearch的ip和name写入到该配置文件中 |
2.2 调试与部署
一个重要的设置 vm.max_map_count=262144
- 不要在构建镜像时,在/etc/sysctl.conf 中设置这一参数,容器启动的1号进程不再是init,而是用户指定的指令,所以不会加载/etc/sysctl.conf中的配置
- 不要在容器中执行 sysctl -w vm.max_map_count=262144 来设置elasticsearch jvm执行所需的max virtual memory,因为默认情况下,容器启动后不具备执行该指令相应的capability(root 权力划分的一种机制),尽管可以指定参数privileged来使容器具备所有的capability,但不建议这样做。
- 在k8s cluster所在的host上,执行 sysctl -w vm.max_map_count=262144,在未设置cgroup或systemd来限制容器内存容量的情况下(默认情况),这样运行在其上容器会沿用这一设置,因为容器中进程本质是运行在host上的相应进程。
在elasticsearch中,有以下两个yaml文件
- replicset_elasticsearch.yaml 该yaml用于在k8s中创建replicaset,用于管理elasticsearch cluster中的elasticsearch节点。
- service_elasticserch.yaml
该yaml用于创建名为elasticsearch的service,通过该service向k8s中其他pod(logstash、kibana)提供服务。 - elasticsearch cluster架构图
如何调试?
- 观察日志:kubectl logs pod xxxx
- 在entrypoint 所涉及的可执行文档中设置“断点”,输出任何想要的内容,如echo,cat某文件,然后观察日志即可进行调试除错。
如何观察集群信息?
找到elasticsearch(svc)
[root@master ~]# kubectl get service |
查看集群健康状况、节点、index
# curl 10.101.66.130:9200/_cat/health?v # curl 10.101.66.130:9200/_cat/nodes?v # curl 10.101.66.130:9200/_cat/indices?v |
2.3 后期优化
现有方案已经实现elasticsearch cluster的高可用,能够忍受任一节点失效,但是还需要进行如下优化。
- 日志数据持久化,可采用volume将日志数据保存到本地,或者ceph、iscsi中。
- liveness检测,当pod仍在运行,但其中的elasticsearch失败时,此时pod不会被清理并重新创建,需要添加liveness检测,当elasticsearch失败时立即重新创建pod。
目前实现方法为httpGet访问elasticsearch在9200端口提供的服务,initialDelaySeconds为66秒。 - readiness检测,当pod启动时,其上的应用正在进行初始化,还不能工作,需要在service中添加readiness检测,当应用初始化完成才将相应的pod加入service的endpoint当中,由service将流量发送给该pod。
- more......
3.Kibana部署
建议先完成logstash+filebeat的部署再来使用kibana。
3.1 kibana架构图
用于构建replicaset-for-kibana(rs)和kibana (svc)的yaml文件在kibana中。
3.2 如何使用kibana?
在 Management → Index management中即可观察到elasticsearch中已有的indices。
在 Management → Index patterns中即可创建 index pattern用于查看日志信息。
在后面加入了logstash+filebeat,并收集了一定数据后可在kibana中查看日志。
3.3 怎样解决Kibana server is not ready yet?
有些时候,特别是使用nfs作为elasticsearch的后端存储时,Kibana启动后会无法工作,显示Kibana server is not ready yet,可尝试如下方法。
- 首先检查elasticsearch集群的健康状态。
- 当status为green时,查看elasticsearch集群中的indices.
发现".kibana_1"中的docs.count为0,此时则会出现kibana server is not ready yet的状况。
如果".kibana_task_manager"的docs.count为0,此时kinaba ui可正常访问,但其中某些功能无法正常使用,比如:在kibana中添加index pattern时会发现无法添加成功。(将.kibana_task_manager删除然后重启kibana即可) - 此时删除.kibana_1,然后重启kibana。
[root@master kibana]# curl -X DELETE 10.107.126.51:9200/.kibana_1
[root@master kibana]# kubectl delete pod replicaset-for-kibana-xxxx # replicaset-for-kibana会重新创建一个kibana
replicaset.extensions "replicaset-for-kibana" deleted
- 若未成功,尝试将两个indices删除后再重启kibana。
可以通过命令"kubectl logs xxxxx"查看pod的日志信息,有时错误也可能在elasticsearch上。
4.Logstash+filebeat 开发、调试与部署
4.1 filebeat 快速部署与运行
- 在Dev/FileBeat 中有安装包(filebeat-7.2.0-x86_64.rpm)和配置文件(filebeat.yml),在zstack管理节点上安装filebeat,并用filebeat.yml文件替换掉默认的配置文件/etc/filebeat/filebeat.yml
- 可用使用命令 “filebeat &”,让其工作在后台。
- filebeat工作方式与“tail -f”类似,且使用offset来记录seek值以避免重复传输相同内容,只要文件没有更名或者inode没有改变,filebeat重启仍然不会造成内容的重复传输。反之,任何改变文件inode的行为(如使用vim对文件进行编辑操作)都将导致filebeat重头开始读取文件。
4.2 filebeat 开发与调试
- filebeat架构图
filebeat.yml配置文件
- filebeat.inputs:
- type: log
paths:
# change the path to yours
- /usr/local/zstacktest/apache/logs/management-server.log
multiline.pattern: '^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}'
multiline.negate: true
multiline.match: after
processors:
- include_fields:
fields: ["host","message"]
output.logstash:
# add the real hosts url
hosts: ["172.20.13.244:30000"]
# put the event to console for dev
# output.console:
# pretty: true
filebeat.inputs
-
-
- 定义filebeat的输入
- 在paths中指定log文件path,可包含多个路径
- 关于multiline。
- filebeat搜集日志文件按行构成event进行传输
- 但是某些异常行为在日志中被分成了多行,比如
-
2019-07-15 18:41:06,267 WARN [Defer] {} (Thread-125) a unhandled exception happened
java.lang.NoClassDefFoundError: org/zstack/portal/managementnode/ManagementNodeManagerImpl$21
at org.zstack.portal.managementnode.ManagementNodeManagerImpl.lambda$1(ManagementNodeManagerImpl.java:929) ~[portal-3.5.0.jar:?]
at org.zstack.core.defer.Defer.runDefer(Defer.java:62) ~[core-3.5.0.jar:?]
at org.zstack.core.defer.DeferAspect.ajc$after$org_zstack_core_defer_DeferAspect$4$e86ef6d0(DeferAspect.aj:82) ~[core-3.5.0.jar:?]
at org.zstack.portal.managementnode.ManagementNodeManagerImpl.stop(ManagementNodeManagerImpl.java:1013) ~[portal-3.5.0.jar:?]
at org.zstack.portal.managementnode.ManagementNodeManagerImpl.lambda$0(ManagementNodeManagerImpl.java:267) ~[portal-3.5.0.jar:?]
at java.lang.Thread.run(Thread.java:748) [?:1.8.0_151]
-
- 此时需要将后面的异常信息追加到第一行的message当中
multiline.pattern为正则表达式,用于匹配第一行开头的日期信息,negate和match的使用如下
multiline.negate |
multiline.match |
result |
true |
after |
将未匹配到的行追加到之前匹配到的行中 |
true |
before |
将未匹配到的行追加到之后匹配到的行中 |
false |
after |
将匹配到的行追加到之前未匹配到的行中 |
false |
before |
将匹配到的行追加到之后未匹配到的行中 |
-
- multiline也可以在logstash中实现,但强烈建议不要这样操作,否则不但可能会造成数据损坏或丢失,还会加重集群中唯一的logstash的负担。
- processsors
用于定义需要传给logstash的相关fields - output.
output可以定义多种不同的数据接收端
-
- output.logstash。将数据输出到logstash实例监听的地址与端口。
- output.console。将数据输出到终端,这一功能对开发调试非常有用,你可以观察到filebeat本身的输出内容,从而决定在processors中向logstash输出的fields。
4.3 logstash快速部署与运行
-
- 在logstash中存在两个文件 replicaset_for_logstash.yaml(rs)和 service_logstash.yaml (svc)用于构建名为logstash的replicaset和名为logstash的service。
- 可用上面两个文件在k8s集群上将logstash快速搭建起来,结合之前的基础,鄙人掐指一算,你已经搭建了一个可用的ELK环境,赶快开始你愉快的ELK之旅吧!!!
4.4 logstash开发调试
- logstash架构图
- logstash.conf (Dev/Logstash/config_for_logstash_docker_image/pipeline)中定义了logstash的input、filter、output内容
input { |
- input
使用input插件beats,并监听在5044端口上,接收来自filebeat的数据。 - filter
使用插件grok对message进行匹配,并将匹配到的内容形成相应的fields,然后在remove_field中去除不需要向elasticsearch发送的feilds。其中自定义pattern内容如下。
ZSTACK_ACTION [A-Z]* |
现在结合一条样本信息做介绍,样本信息如下.
2019-07-12 06:10:08,927 DEBUG [TimeoutRestTemplate] {} (zs-thread-96) Created POST request for "http://172.24.249.29:7070/localstorage/check/initializedfile"(carezkh test)
-
- 匹配信息看match中相关内容,以下仅强调两点
- 为什么 ZSTACK_ACTOR不采用简单的 \(.*\)?“.*”是贪婪模式,匹配结果将是(zs-thread-96........carezkh test),会将ZSTACK_MESSAGE的内容一并进行匹配
- 空格的匹配,为什么要匹配空格*次?通常各field之间仅有一个空格,但有的开发人员输出的日志却有两个空格(DEBUG和[TimeoutRestTemplate])
怎么调试匹配规则?在kibana中有调试工具(Dev tools → Grok debugger),可在其中输入样本、匹配规则、自定义pattern来测试正则是否有效。
- output
可采用多种插件
-
-
- elasticsearch {.....}.调用elasticsearch的restful api,将数据传输到elasticsearch中
- stdout {}.将数据直接输出到终端,此插件在开发调试中非常有用!可根据输出判断正则是否正确以及确定需要过滤的feilds。
-
4.5 利用docker volume来调试logstash配置文件
在一些简单的场景下,为了快速验证对logstash配置文件修改是否有效,可将配置文件作为命名volume挂载到docker内,进行调试。
在Dev/Logstash/config_for_logstash_docker_image中,采用命令将本地的配置文件挂载到容器中,进行调试。
docker run -it --rm -v `pwd`/pipeline:/usr/share/logstash/pipeline 10.0.191.182:5000/logstash_zstack |
- 需要做的是将logstash的output设置为stdout,便于观察搜集样本log(数量较少,用于测试的log)的输出。
4.6 一些tips和优化
- 在实际使用中,无法保证logstash匹配100%成功,如果filter中grok match失败,则后续remove_fields也不会执行,那么你将在kibana中看到message等字段(被成功匹配的行,message字段将为空,而没有被匹配到的行,则message字段存在内容),你可以对该message进行分析并最终解决问题,对filter进行完善。此时不要采用logstash的output{ stdout {} },毕竟百万行数据不是肉眼可以观察的。以下将举例说明,并在该例中阐述了从开发、调试、部署的全过程。
- 进入到目录
[root@10-0-191-182 config_for_logstash_docker_image]# pwd
/root/ELK-Gitlab/Dev/Logstash/config_for_logstash_docker_image
[root@10-0-191-182 config_for_logstash_docker_image]# tree
.
├── Dockerfile
└── pipeline
├── logstash.conf
└── patterns
└── zstack
2.修改zstack中的内容,将ZSTACK_PLAIN改为仅匹配空格一次(这会造成错误,后续将逐步分析并解决该错误)
ZSTACK_ACTION [A-Z]* |
-
- 选择一台zstack管理节点,安装配置filebeat。
将GitLab中的Dev/Filebeat中安装包和配置文件copy到该管理节点中,(如172.24.247.142)。
rpm -i filebeat-7.2.0-x86_64.rpm #安装filebeat。
用配置文件替换掉/etc/filebeat/filebeat.yml默认配置文件,并正确配置相关内容 - 在k8s中删除pod(replicaset-for-logstash-xxxx),此时replicaset-for-logstash为了维持replicas=1,将创建新的pod。重新创建pod中的docker时,如果image registry中相关镜像已发生改变,则会pull新的image来创建docker。
[root@master ELK-Gitlab]# kubectl delete pod replicaset-for-logstash-x268h
[root@master ELK-Gitlab]# kubectl get pod -o wide #查看新的pod并观察其所在的node,ip等信息。 - 构建docker image并推送镜像(留意build最后的一个".",用于指定build的context)
[root@10-0-191-182 config_for_logstash_docker_image]# docker build -t 10.0.191.182:5000/logstash_zstack .
[root@10-0-191-182 config_for_logstash_docker_image]# docker push 10.0.191.182:5000/logstash_zstack
- 选择一台zstack管理节点,安装配置filebeat。
filebeat.inputs:
- type: log
paths:
- /usr/local/zstacktest/apache-tomcat/logs/management-server.log
multiline.pattern: '^[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}'
multiline.negate: true
multiline.match: after
processors:
- include_fields:
fields: ["host","message"]
output.logstash:
hosts: ["172.20.13.244:30000"]
#output.console:
# pretty: true
-
-
- 在管理节点上运行filebeat
filebeat & #如需在终端看到filebeat的日志,可采用filebeat -e -d "public" - 在k8s master上查看service信息
-
[root@master ELK-Gitlab]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
elasticsearch ClusterIP 10.101.66.130 <none> 9200/TCP 12d
kibana NodePort 10.103.182.220 <none> 5601:30001/TCP 11d
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 63d
logstash NodePort 10.96.109.82 <none> 5044:30000/TCP 11d
-
- 查看elasticsearch中的index,可观察到不断增长的docs.count
[root@master ELK-Gitlab]# curl 10.101.66.130:9200/_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open .kibana EqD1rU-dSty_2K84pSjVtQ 1 1 3 0 29kb 14.5kb
green open 172-24-247-142 SRBujJ0CQw2iy_meyVEmBA 1 1 47875 0 155.8mb 77.8mb
-
- 在kibana中查看elasticsearch中该index的信息
- 在index中创建index pattern,并按照指示进行后续操作
- 在Discover中即可观察到相关index
- 接下来看看问题出在哪里,将message添加到selected fields,即可看到未被match的message
- copy一份message到dev tools中的grok debugger,并添加match规则和custom pattern,(注意message只有一行,将复制时的多行添加到第一行末尾)
- 尝试匹配但未匹配成功
- 此时修改ZSTACK_PLAIN匹配空格任意次,再进行匹配,匹配成功。
- 此时要做的就是修改pattern,重复第一到这一步,直到获得想要的结果。可能需要将elasticsearch的数据删除(包括kibana的元数据),或者在Kibana management中删除elasticsearch 中的index
- 在index中创建index pattern,并按照指示进行后续操作
- 在kibana中查看elasticsearch中该index的信息
[root@master ELK-Gitlab]# curl 10.101.66.130:9200/_cat/indices?v
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
green open .kibana_task_manager jqvzvJpzSF2m580lZVKOsg 1 1 2 0 61.2kb 45.5kb
green open 172-24-247-142 SRBujJ0CQw2iy_meyVEmBA 1 1 499658 0 221.9mb 111.4mb
green open .kibana_1 GawNWlaiQt6zq3SRTv8J-Q 1 1 5 0 86.9kb 43.4kb
[root@master ELK-Gitlab]# curl -X DELETE 10.101.66.130:9200/.kibana_task_manager
[root@master ELK-Gitlab]# curl -X DELETE 10.101.66.130:9200/.kibana_1
[root@master ELK-Gitlab]# curl -X DELETE 10.101.66.130:9200/172-24-247-142
[root@master ELK-Gitlab]# kubectl delete pod replicaset-for-kibana-xxxx #删除原来的kibana,replicaset-for-kibana会创建新的kibana
-
-
- 现在可以成功查看到被正确匹配分段的日志
- 尝试检索message_action含有org字段的log信息
-
- 高并发下,logstash的负载问题。filebeat不仅会向logstash传输数据,同时还具有探测logstash负载的能力,当logstash负载较重时,filebeat会减慢向logstash传输数据的速率。我们仍可以在filebeat和logstash之间建立缓冲,采用redis或kafaka集群方式来尝试优化此问题。
logstash同样存在如elasticsearch的liveness和readiness问题。
存在liveness问题,目前还没有较好的解决方法。readiness通过filebeat+logstash本身机制已经解决,filebeat和logstash需要建立连接,才能进行数据传输,所以只有在logstash初始化完成时filebeat才会发送日志数据到logstash。- more......
参考文献