本文说明
由于最近在搭ELK的日志系统,为了演示方案搭了个单台服务器的日志系统,就是前一篇文章中所记,其实这些笔记已经整理好久了,一直在解决各种问题就没有发出来。在演示过程中我提到了两个方案,其中之一来自于【原创】分布式之elk日志架构的演进一文中的中级版,另一自然是使用日志直接输出到Kafka.
为了让这个看起来还不错的方案落地,领导决定把这些中间件,服务等都虚拟化,做成docker容器,现在仍在试验阶段,所以做了有点不合理的设计,把这些容器都布署在同一台服务器上!为了能实现这种假想式的设计,做了此次尝试,也为了了解这个集群是否可行性,后期拆分参考。大家如果自己电脑内存有16G以上可用的,可以尝试下这个脚本,话不多说,把之前准备的脚本和文档发出来,给大家个参考。
本文不包含kafka之前的日志采集部分,这部分可以参考使用本人的LogDemo https://github.com/HellxZ/LogDemo.git
声明
如果有人擅自使用本文内容放到自己公司生产环境
中,出现的所有问题,后果自负,一率与本人无关!
环境准备
- Debain Stretch 9.9 16G内存
- Docker 18.09.6
- Docker-Compose 1.17.1
- Java 8 及以上
文档部分
服务器环境准备
1.确认端口号是否有冲突
为了保证环境一致,请仔细查看如下设置是否与当前服务器端口占用有冲突、映射是否有问题
服务名 | 服务功能说明 | 容器名 | 端口号 |
---|---|---|---|
es-master | Elasticsearch主节点,不存数据:防止脑裂问题 | es-cluster-master | 19200 |
es-slave1 | Elasticsearch从节点1,存数据 | es-cluster-slave1 | 19201 |
es-slave2 | Elasticsearch从节点2,存数据 | es-cluster-slave2 | 19202 |
es-slave3 | Elasticsearch从节点3,存数据 | es-cluster-slave3 | 19203 |
es-balance | Elasticsearch连接节点,不存数据,供Kibana与Logstash连接 | es-cluster-balance | 19204 |
kibana | Elasticsearch数据可视化前端 | kibana | 15601 |
zookeeper | 分布式协调中间件,用于Kafka集群的master节点的选取 | zookeeper | 12181 |
kafka1 | Kafka节点,作业务缓冲存,保证服务可靠性、数据不易丢失 | kafka1 | 9091 |
kafka2 | 同上 | kafka2 | 9092 |
kafka3 | 同上 | kafka3 | 9093 |
kafka-manager | 管理kafka集群的前端,可以查看集群健康状态等 | kafka-manager | 19000 |
logstash1 | Logstash节点,日志数据消费、日志分词、转储到Elasticsearch集群 | logstash-1 | 9601 |
logstash2 | 同上 | logstash-2 | 9602 |
logstash3 | 同上 | logstash-3 | 9603 |
这里边占用的端口号均为宿主机的,为了防止出现冲突,前边加1都做了处理,如果仍有冲突,请联系我
2.确定docker与docker-compose安装
安装docker与配置国内镜像请参阅CentOS安装Docker-ce并配置国内镜像
sudo docker -v #查看docker版本,如果正常说明docker已经安装,可以参考把当前登录用户加入docker组,来去除sudo
Docker默认使用root用户权限才能执行,如果想使用普通用户执行docker还不想用sudo,可以使用如下命令
sudo usermod -aG docker 要使用的用户名 && newgrp docker
安装docker-compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose #赋执行权限
查看docker-compose是否安装
docker-compose -v #查看docker-compose版本
3./etc/hosts
修改
由于Kafka需要进行域名映射,而我们使用服务的时候很有可能是内网,所以,这里我们通过修改hosts配置文件,来达到局域网内其它服务的访问
sudo vim /etc/hosts
#追加如下内容
#kafka hosts
本机ip kafka1
本机ip kafka2
本机ip kafka3
其中本机ip是当前主机的内网ip,如果微服务与其它服务不在同一台服务器,我们还需要配置nginx使用stream监听指定端口, IP也变成了外网的ip,此条待测
4.Centos7修改systemd的配置文件
vim /etc/systemd/system.conf
#添加以下内容
DefaultLimitNOFILE=65536
DefaultLimitNPROC=32000
DefaultLimitMEMLOCK=infinity
修改此处的目的在于需要对Elasticsearch内存进行限制,在systemd管理下如果不设置elasticsearch的限制,有很大可能出现内存溢出与服务器宕机
5.减少swap分区的使用与生产环境es配置
sudo echo "vm.swappiness=0" >> /etc/sysctl.conf #追加尽少使用swap配置
grep vm.max_map_count /etc/sysctl.conf #查看内存权限大小,如果低于262144,需要设置为最小262144
sudo sysctl -p #使配置文件立即生效
减少swap分区使用,而不是禁用,此方式可以使elasticsearch与kafka的性能保持稳定
配置文件准备
配置文件这些放在文章末尾的脚本部分里了,参考目录结构,创建相同结构的相同内容的配置文件,按下方配置文件的修改即可。
1.复制elk-docker配置文件夹到当前用户有读写权限的目录中
复制elk-docker文件夹到启动docker用户可访问的目录
sudo chown -R 当前用户:当前用户组 elk-docker #赋权访问
2.修改文件夹下的logstash/config下的两个文件内容
如你所见,此文件夹下有logstash.conf 与logstash.yml两个文件,下边分别进行修改
修改logstash.yml文件
修改xpack.monitoring.elasticsearch.hosts中的ip为当前内网ip
修改logstash.conf文件
找到output/elasticsearch下的hosts,把这个ip地址也改成当前内网ip
3..env
文件的使用
为了便于运维和实施,在与此文档同级的目录下有一个配置文件名为.env
,这个文件是通过运维实施人员修改配置之用,下边对里边需要用到的参数一一说明
-
HOST_IP
: 设置宿主机内网ip,为kibana提供连接,需要改 -
ES_JVM_OPTS
: 每个Elasticsearch实例的jvm启动参数,可指定启动内存与最大内存,默认-Xms256m -Xmx256m,可以不改,但不能再改小了 -
ES_MASTER_DATA_DIR
:Elasticsearch master节点的数据目录 -
ES_SLAVE1_DATA_DIR
:Elasticsearch slave1节点的数据目录 -
ES_SLAVE2_DATA_DIR
:Elasticsearch slave2节点的数据目录 -
ES_SLAVE3_DATA_DIR
:Elasticsearch slave3节点的数据目录 -
ES_BALANCE_DATA_DIR
:Elasticsearch balance节点的数据目录 -
LOGSTASH_CONFIG_DIR
: Logstash的配置文件目录,无需改
上边提到的ES开头变量的指向的是宿主机目录,如果自行指定,虽然启动集群时自动创建目录,但是无法正确访问的,原因是创建这些文件夹的是root用户,而es使用的是启动docker的账户,当然了,如果都是在root用户下执行自然没问题,可以使用
ls -alh
查看,不推荐在正式环境中直接使用root用户
启动集群服务
启动前请确保es的数据目录可以被docker用户访问,正确创建
sudo docker-compose up -d #项目后台启动,没有报错,正确输出多个服务
docker&docker-compose常用命令
#查看各服务日志
docker logs -f 容器名或容器id
#查看当前运行中的容器
docker ps
#关闭docker-compose所有服务
cd docker-compose.yml所在的目录
docker-compose down #关闭该文件中定义的所有服务 并 移除集群的虚拟网卡
#强制删除所有容器,无论是开着的还是已经关闭的
docker rm -f $(docker ps -qa)
#进入正在运行的容器中
docker exec -it 容器名或容器id bash #使用bash打开
集群管理与功能使用
Elasticsearch-head插件
安装chrome插件elasticsearch-head,连接到主机的19200端口,进行查看,除些之外,可以通过当前ip:19200/_cat/health
来查看当前集群状态
添加完成后,点击图标,如下图
其中green说明集群健康完全可用。yellow说明有节点挂了,部分可用。red集群不可用
除了直接可以看到的,我们还可以
- 通过
索引
来查看当前es索引库中有哪些索引 - 通过
基本查询
使用简单查询功能,需要简单查询语法了解 - 通过
复合查询
使用复合查询功能,当然使用这个功能需要了解复合查询语法
查询功能可以使用kibana进行查询,那个更方便一些
查看索引数据,点击右侧指定的名称即可查看保存的源数据
Kafka-Manager
使用主机ip:1900,如图添加集群
拖到最下方,点save.如下图添加成功
点击Go to cluster view 立即查看集群
点击左上方Clusters也可以看到集群如下图,以后进来也是这个界面。点击集群名即可查看,如上图示
具体使用kafkaManager请参考官方github: https://github.com/yahoo/kafka-manager
Kibana
访问kibana服务器ip:15601/app/kibana#/discover?_g=()
我们可以在Filters
处直接搜索要找的日志,当然也可以使用kql,详情见官网。
添加筛选
可以为我们提供更准确的日志搜索,比如当前我们用message就是日志的内容,可以如下操作
我们还可以通过时间来过滤日志
kibana左下角有个隐藏按钮,我们打开看看
ps: 暂时我们用到的也就是开始的时候创建索引,以及后续用的Discover与开发工具
脚本部分
这里先把脚本与文件结构先列出来
elk-docker/
├── docker-compose.yml
├── .env
├── es-data
│ ├── es-balance-data
│ ├── es-master-data
│ ├── es-slave1-data
│ ├── es-slave2-data
│ └── es-slave3-data
├── logstash
│ └── config
│ ├── logstash.conf
│ └── logstash.yml
└── ReadMeFirst.md
8 directories, 5 files
这里es-data下的所有都是文件夹,logstash文件夹为配置文件夹(官网没给Docker设置的部分),docker-compose.yml就是我们的脚本,ReadMeFirst.md就是上边的文档部分,分别贴出来,好像git不会提交空文件夹,这里就不放github上了。
这里有一个
.env
文件是用来设置传入参数的,docker-compose如果想一下子跑起来集群,是不能传参的,不过.env
提供了默认的参数,在docker-compose.yml 中可以很方便的取到
具体需要改的地方,请参见上边文档部分吧。
.env
# .env file for docker-compose default. please be careful.
# kibana use HOST_IP to connect to elasticsearch, need to change it to host machine intranet ip
HOST_IP=10.2.114.110
# elasticsearch's JVM args setting, for every elasticsearch instance.
# -Xms is es boostrap occupied memory size
# -Xmx is the biggest memory es can use.
ES_JVM_OPTS=-Xms256m -Xmx256m
# WARNINGS: after you set elasticsearch data dirs, you must mkdirs with login user.
# because the es-cluster need to save data to dirs. if you use root to create folders, they can not save data then cluster down.
# the es-master data mount dir set
ES_MASTER_DATA_DIR=./es-data/es-master-data
# the es-slave1 data mount dir set
ES_SLAVE1_DATA_DIR=./es-data/es-slave1-data
# the es-slave2 data mount dir set
ES_SLAVE2_DATA_DIR=./es-data/es-slave2-data
# the es-slave3 data mount dir set
ES_SLAVE3_DATA_DIR=./es-data/es-slave3-data
# the es-balance data mount dir set, ofcourse this node have no data but node info.
ES_BALANCE_DATA_DIR=./es-data/es-balance-data
# logstash config dir mount set. change inside dir config file to change logstash cluster settings.
# default use relation path. don't change if you don't know what means.
LOGSTASH_CONFIG_DIR=./logstash/config
# kafka bootstrap create topic name
# by default is TOPIC_NAME=all_logs:1:1, "all_logs" is topic's name, first "1" means have one partition, last 1 means have one replicas.
# it's also can set TOPIC_NAME=topicName:patitionSum:replicasNum:cleanupPolicy, e.g. "Topic1:1:3:compact", and that can also set multiple topics,such as "Topic1:1:3,Topic2:2:4:compact"
# the multiple topics separator default is "," you can set new separator by "KAFKA_CREATE_TOPICS_SEPARATOR".
KAFKA_BOOTSTRAP_CREATE_TOPICS=all_logs:1:1
docker-compose.yml
version: "3"
services:
es-master: # use to be Tribe Node & Master Node, with no data save.
image: elasticsearch:7.1.0
container_name: es-cluster-master
environment: # setting container env
- cluster.name=es-cluster
- node.name=es-master
- cluster.initial_master_nodes=es-master
- node.master=true # specific this node is master node.
- node.data=false # master node don't save data to prevent cluster down.
- http.cors.enabled=true # solve cross origin
- http.cors.allow-origin=*
- bootstrap.memory_lock=true # lock bootstrap memory.
- ES_JAVA_OPTS=${ES_JVM_OPTS} # set es bootstrap jvm args
ports:
- "19200:9200"
expose: # expose ports let other containers link it
- "9200" # let kibana connect this port.
- "9300" # use this port let cluster other nodes to discovery.
restart: always
volumes: # mount host's dirs
- ${ES_MASTER_DATA_DIR}:/usr/share/elasticsearch/data:rw
networks:
- elk-network
es-slave1:
image: elasticsearch:7.1.0
container_name: es-cluster-slave1
depends_on:
- es-master
environment: # setting container env
- cluster.name=es-cluster
- node.name=slave1
- node.master=false # no master and saving data
- node.data=true
- cluster.initial_master_nodes=es-master # point to init master node name.
- discovery.zen.ping.unicast.hosts=es-master # unique master
- bootstrap.memory_lock=true # lock bootstrap memory.
- ES_JAVA_OPTS=${ES_JVM_OPTS}
ports:
- "19201:9200"
expose: # expose ports let other containers link it
- "9300"
restart: always
volumes: # mount host's dirs to save data, has read and write privilege
- ${ES_SLAVE1_DATA_DIR}:/usr/share/elasticsearch/data:rw
networks:
- elk-network
es-slave2:
image: elasticsearch:7.1.0
container_name: es-cluster-slave2
depends_on:
- es-master
environment: # setting container env
- cluster.name=es-cluster
- node.name=slave2
- node.master=false
- node.data=true
- cluster.initial_master_nodes=es-master
- discovery.zen.ping.unicast.hosts=es-master
- bootstrap.memory_lock=true # lock bootstrap memory.
- ES_JAVA_OPTS=${ES_JVM_OPTS}
ports:
- "19202:9200"
expose: # expose ports let other containers link it
- "9300"
labels: # add description
com.jiuqi.description: "elk-cluster-slave2 on docker"
restart: always
volumes: # mount host's dirs
- ${ES_SLAVE2_DATA_DIR}:/usr/share/elasticsearch/data:rw
networks:
- elk-network
es-slave3:
image: elasticsearch:7.1.0
container_name: es-cluster-slave3
depends_on:
- es-master
environment: # setting container env
- cluster.name=es-cluster
- node.name=slave3
- node.master=false
- node.data=true
- cluster.initial_master_nodes=es-master
- discovery.zen.ping.unicast.hosts=es-master
- bootstrap.memory_lock=true # lock bootstrap memory.
- ES_JAVA_OPTS=${ES_JVM_OPTS}
ports:
- "19203:9200"
expose: # expose ports let other containers link it
- "9300"
restart: always
volumes: # mount host's dirs
- ${ES_SLAVE3_DATA_DIR}:/usr/share/elasticsearch/data:rw
networks:
- elk-network
es-balance:
image: elasticsearch:7.1.0
container_name: es-cluster-balance
depends_on:
- es-master
environment: # setting container env
- cluster.name=es-cluster
- node.name=balance
- node.master=false # the balance is no master and no data save.
- node.data=false
- cluster.initial_master_nodes=es-master
- discovery.zen.ping.unicast.hosts=es-master
- bootstrap.memory_lock=true # lock bootstrap memory.
- ES_JAVA_OPTS=${ES_JVM_OPTS}
ports:
- "19204:9200"
expose: # expose ports let other containers link it
- "9300"
restart: always
volumes: # mount host's dirs
- ${ES_BALANCE_DATA_DIR}:/usr/share/elasticsearch/data:rw
networks:
- elk-network
kibana:
image: kibana:7.1.0
container_name: kibana
depends_on:
- es-master
environment:
- ELASTICSEARCH_HOSTS=http://${HOST_IP}:19204 # connect the es-balance node
- I18N_LOCALE=zh-CN
ports:
- "15601:5601"
networks:
- elk-network
zookeeper:
image: zookeeper:3.5.5
restart: always
container_name: zookeeper
ports:
- "12181:2181"
expose:
- "2181" #let kafka manager connect.
networks:
- elk-network
kafka1:
image: wurstmeister/kafka:2.12-2.2.1
restart: always
container_name: kafka1
ports:
- "9091:9091"
environment:
- KAFKA_BROKER_ID=1
- KAFKA_LISTENERS=PLAINTEXT://kafka1:9091
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka1:9091
- KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
- KAFKA_MESSAGE_MAX_BYTES=2000000
- KAFKA_CREATE_TOPICS=${KAFKA_BOOTSTRAP_CREATE_TOPICS}
expose:
- "9091"
depends_on:
- zookeeper
networks:
- elk-network
kafka2:
image: wurstmeister/kafka:2.12-2.2.1
restart: always
container_name: kafka2
ports:
- "9092:9092"
environment:
- KAFKA_BROKER_ID=2
- KAFKA_LISTENERS=PLAINTEXT://kafka2:9092
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka2:9092
- KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
- KAFKA_MESSAGE_MAX_BYTES=2000000
- KAFKA_CREATE_TOPICS=${KAFKA_BOOTSTRAP_CREATE_TOPICS}
expose:
- "9092"
depends_on:
- zookeeper
networks:
- elk-network
kafka3:
image: wurstmeister/kafka:2.12-2.2.1
restart: always
container_name: kafka3
ports:
- "9093:9093"
environment:
- KAFKA_BROKER_ID=3
- KAFKA_LISTENERS=PLAINTEXT://kafka3:9093
- KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://kafka3:9093
- KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
- KAFKA_MESSAGE_MAX_BYTES=2000000
- KAFKA_CREATE_TOPICS=${KAFKA_BOOTSTRAP_CREATE_TOPICS}
expose:
- "9093"
depends_on:
- zookeeper
networks:
- elk-network
kafka-manager:
image: sheepkiller/kafka-manager
container_name: kafka-manager
ports:
- "19000:9000"
environment:
ZK_HOSTS: zookeeper:2181
APPLICATION_SECRET: "admin"
depends_on:
- zookeeper
networks:
- elk-network
logstash1:
image: logstash:7.1.0
container_name: logstash-1
ports:
- "9601:9600"
environment:
- XPACK_MONITORING_ENABLED=true
volumes:
- ${LOGSTASH_CONFIG_DIR}/logstash.conf:/usr/share/logstash/pipeline/logstash.conf:rw
- ${LOGSTASH_CONFIG_DIR}/logstash.yml:/usr/share/logstash/config/logstash.yml:rw
depends_on:
- kafka1
- kafka2
- kafka3
- es-master
- es-slave1
- es-slave2
- es-slave3
networks:
- elk-network
logstash2:
image: logstash:7.1.0
container_name: logstash-2
ports:
- "9602:9600"
environment:
- XPACK_MONITORING_ENABLED=true
volumes:
- ${LOGSTASH_CONFIG_DIR}/logstash.conf:/usr/share/logstash/pipeline/logstash.conf:rw
- ${LOGSTASH_CONFIG_DIR}/logstash.yml:/usr/share/logstash/config/logstash.yml:rw
depends_on:
- kafka1
- kafka2
- kafka3
- es-master
- es-slave1
- es-slave2
- es-slave3
networks:
- elk-network
logstash3:
image: logstash:7.1.0
container_name: logstash-3
ports:
- "9603:9600"
environment:
- XPACK_MONITORING_ENABLED=true
volumes:
- ${LOGSTASH_CONFIG_DIR}/logstash.conf:/usr/share/logstash/pipeline/logstash.conf:rw
- ${LOGSTASH_CONFIG_DIR}/logstash.yml:/usr/share/logstash/config/logstash.yml:rw
depends_on:
- kafka1
- kafka2
- kafka3
- es-master
- es-slave1
- es-slave2
- es-slave3
networks:
- elk-network
networks:
elk-network:
driver: bridge
logstash/config/logstash.yml
http.host: "0.0.0.0"
xpack.monitoring.elasticsearch.hosts: [ "http://10.2.114.110:19204" ]
logstash/config/logstash.conf
input {
kafka {
bootstrap_servers => "kafka1:9091,kafka2:9092,kafka3:9093"
topics => ["all_logs"]
group_id => "logstash"
codec => json
}
}
filter {
}
output {
elasticsearch {
hosts => ["10.2.114.110:19204"]
index => "all-logs-%{+YYYY.MM.dd}"
#user => "elastic"
#password => "changeme"
}
stdout {
codec => rubydebug
}
}
结束语
本文只是为了验证方案的可行性,并没有考虑生产环境中的负载情况,所以不适合放到生产环境中使用。
仅用于提供一个思路,正确按照文档操作是不会有问题的。如果有,请在下方评论,我会尽快改正。
声明:原创文章禁止转载