【实验级】Docker-Compose搭建单服务器ELK伪集群

时间:2021-06-03 14:30:40

本文说明

由于最近在搭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来查看当前集群状态

【实验级】Docker-Compose搭建单服务器ELK伪集群

添加完成后,点击图标,如下图

【实验级】Docker-Compose搭建单服务器ELK伪集群

其中green说明集群健康完全可用。yellow说明有节点挂了,部分可用。red集群不可用

除了直接可以看到的,我们还可以

  • 通过索引来查看当前es索引库中有哪些索引
  • 通过基本查询使用简单查询功能,需要简单查询语法了解
  • 通过复合查询使用复合查询功能,当然使用这个功能需要了解复合查询语法

查询功能可以使用kibana进行查询,那个更方便一些

查看索引数据,点击右侧指定的名称即可查看保存的源数据

【实验级】Docker-Compose搭建单服务器ELK伪集群

Kafka-Manager

使用主机ip:1900,如图添加集群

【实验级】Docker-Compose搭建单服务器ELK伪集群

【实验级】Docker-Compose搭建单服务器ELK伪集群

拖到最下方,点save.如下图添加成功

【实验级】Docker-Compose搭建单服务器ELK伪集群

点击Go to cluster view 立即查看集群

【实验级】Docker-Compose搭建单服务器ELK伪集群

点击左上方Clusters也可以看到集群如下图,以后进来也是这个界面。点击集群名即可查看,如上图示

【实验级】Docker-Compose搭建单服务器ELK伪集群

具体使用kafkaManager请参考官方github: https://github.com/yahoo/kafka-manager

Kibana

访问kibana服务器ip:15601/app/kibana#/discover?_g=()

【实验级】Docker-Compose搭建单服务器ELK伪集群

我们可以在Filters处直接搜索要找的日志,当然也可以使用kql,详情见官网。

添加筛选可以为我们提供更准确的日志搜索,比如当前我们用message就是日志的内容,可以如下操作

【实验级】Docker-Compose搭建单服务器ELK伪集群

我们还可以通过时间来过滤日志

【实验级】Docker-Compose搭建单服务器ELK伪集群

kibana左下角有个隐藏按钮,我们打开看看

【实验级】Docker-Compose搭建单服务器ELK伪集群

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
}
}

结束语

本文只是为了验证方案的可行性,并没有考虑生产环境中的负载情况,所以不适合放到生产环境中使用。

仅用于提供一个思路,正确按照文档操作是不会有问题的。如果有,请在下方评论,我会尽快改正。

声明:原创文章禁止转载