本次实践过程全部在Docker环境下完成。
操作系统环境为centos7,dockerce,docker-compose,xshell、xftp 教育版等。
前提
需要提前准备三台独立服务器(虚拟机),每台服务器的内存至少需要4G,实践过程会将ELK三大件分别部署在这三台服务器上,以最接近生产环境的部署方式进行学习实践。
docker-compose
docker-compose环境变量
文件命名为 .env
ES_VERSION=8.2.2
ZOO=172.17.30.158:12181
KAFKA_CFG_MAX_REQUEST_SIZE=11534336
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1
KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS=36000
KAFKA1_HOST=172.17.30.158
KAFKA1_PORT=19092
KAFKA2_HOST=172.17.30.160
KAFKA2_PORT=19093
KAFKA3_HOST=172.17.30.160
KAFKA3_PORT=19094
elasticsearch.yml
version: '3.4'
services:
elasticsearch:
image: "elasticsearch:${ES_VERSION}"
container_name: es
restart: always
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms3g -Xmx3g
volumes:
- /etc/localtime:/etc/localtime
- /srv/elk/data:/usr/share/elasticsearch/data
- /root/elk/esconfig/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
ports:
- "19200:9200"
- "19300:9300"
deploy:
resources:
limits:
memory: 3G
networks:
- app-network
networks:
app-network:
driver: bridge
elasticsearch 主体配置文件
映射路径为:
elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
配置内容为:
cluster.name: "docker-cluster"
network.host: 0.0.0.0
xpack.security.enabled: false
xpack.security.authc.api_key.enabled: false
#discovery.type: single-node
logger.level: error
ingest.geoip.downloader.enabled: false
#index.number_of_replicas: 0
xpack.security.enrollment.enabled: true
kibana & logstash
version: '3.4'
services:
logstash:
image: "logstash:${ES_VERSION}"
container_name: logstash
restart: always
environment:
#LOG_LEVEL: error
ES_JAVA_OPTS: -Xms1g -Xmx1g
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
- ./logstash.yml:/usr/share/logstash/config/logstash.yml
- /srv/logstash/logs:/usr/share/logstash/logs
ports:
- "5044:5044"
deploy:
resources:
limits:
memory: 1G
networks:
- app-network
kibana:
image: "kibana:${ES_VERSION}"
container_name: kibana
restart: always
environment:
- ELASTICSEARCH_URL=http://172.17.30.193:19200
volumes:
- /etc/localtime:/etc/localtime
- ./kibana.yml:/usr/share/kibana/config/kibana.yml
ports:
- "5601:5601"
networks:
- app-network
metricbeat:
image: "elastic/metricbeat:${ES_VERSION}"
container_name: mb
restart: always
environment:
- setup.kibana.host=kibana:5601
- output.elasticsearch.hosts=["172.17.30.193:19200"]
depends_on:
- kibana
- logstash
volumes:
- /etc/localtime:/etc/localtime
- /root/elk/mbconfig/logstash.yml:/usr/share/metricbeat/modules.d/logstash.yml
- /root/elk/mbconfig/metricbeat.yml:/usr/share/metricbeat/metricbeat.yml
links:
- kibana
- logstash
networks:
- app-network
networks:
app-network:
driver: bridge
kibana 配置文件 kibana.yml
映射路径为:
./kibana.yml:/usr/share/kibana/config/kibana.yml
配置文件内容为:
#
# ** THIS IS AN AUTO-GENERATED FILE **
#
# Default Kibana configuration for docker target
server.host: "0.0.0.0"
server.shutdownTimeout: "5s"
elasticsearch.hosts: [ "http://172.17.30.193:19200" ]
monitoring.ui.container.elasticsearch.enabled: true
server.publicBaseUrl: "https://kibana.eo.demo.ehi.com.cn"
i18n.locale: "zh-CN"
xpack.reporting.encryptionKey: f7df822ad927a287fb4579831c16350f
xpack.security.encryptionKey: 7c98394b79e706d8f48deab2635d0c3f
xpack.encryptedSavedObjects:
encryptionKey: "ae0c2bce5918f313ba192162e944d48b"
keyRotation:
decryptionOnlyKeys: ["wy80Kj77iJZlFg65nz4jolf2IKoKlymo", "6oAOsRhu86PVgdaMILLNvjh4oWulx8EG"]
xpack.reporting.kibanaServer.hostname: localhost
xpack.reporting.roles.enabled: false
logstash 配置文件 logstash.yml
映射路径为:
- ./logstash.yml:/usr/share/logstash/config/logstash.yml
配置内容为:
http.host: 0.0.0.0
log.level: warn
monitoring.enabled: false
xpack.monitoring.elasticsearch.hosts:
- http://172.17.30.193:19200
logstash.conf
映射路径为:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
配置文件为:
input {
kafka {
bootstrap_servers => "172.17.30.158:19092,172.17.30.160:19093,172.17.30.160:19094"
consumer_threads => 10
group_id => "eolog"
auto_commit_interval_ms => 1000
topics => ["csenterprisecarrentalapilog","csenterprisecarrentalapiaudit","csenterprisecarrentalapirequest"]
decorate_events => "basic"
# auto_offset_reset => "latest"
auto_offset_reset => "earliest"
#auto_offset_reset => "none"
max_partition_fetch_bytes => 104857600
}
}
filter {
json {
source => "message" #将message字段进行解析
skip_on_invalid_json => true #无效的json则跳过
remove_field => ["message"] #删除message字段
}
# json {
# source => "[message][responseData]" #将responseData字段进行解析
# target => "[message][responseData]"
# skip_on_invalid_json => true #无效的json则跳过
# # remove_field => ["responseData"] #删除message字段
# }
#增加一个字段,计算 timestamp 8小时
ruby {
code => "event.set('timestamp', event.get('@timestamp').time.utc+8*60*60)"
}
#用 mutate 插件先转换为 string 类型,gsub只处理string类型的数据
#再用正则匹配,最终得到想要的日期
mutate {
convert => ["timestamp", "string"]
gsub => ["timestamp", "T([\S\s]*?)Z", ""]
gsub => ["timestamp", "-", "."]
}
}
output {
if [@metadata][kafka][topic] == "csenterprisecarrentalapirequest" {
elasticsearch {
hosts => ["172.17.30.193:19200"]
index => "csenterprisecarrentalapirequest-%{timestamp}"
}
}
else if [@metadata][kafka][topic] == "csenterprisecarrentalapiaudit" {
elasticsearch {
hosts => ["172.17.30.193:19200"]
index => "csenterprisecarrentalapiaudit-%{timestamp}"
}
}
else if [@metadata][kafka][topic] == "csenterprisecarrentalapilog" {
elasticsearch {
hosts => ["172.17.30.193:19200"]
index => "csenterprisecarrentalapilog-%{timestamp}"
}
}
#stdout { codec => rubydebug }
}
metricbeat 监控
配置文件名为:metricbeat.yml
映射路径为:
metricbeat.yml:/usr/share/metricbeat/metricbeat.yml
配置内容为:
metricbeat.config.modules:
path: ${path.config}/modules.d/*.yml
reload.enabled: false
processors:
- add_cloud_metadata: ~
- add_docker_metadata: ~
output.elasticsearch:
hosts: ["https://172.17.30.193:19200"]
username: '${ELASTICSEARCH_USERNAME:}'
password: '${ELASTICSEARCH_PASSWORD:}'
kafka-master
version: '3.4'
services:
# kafka-ui
kafka-ui:
container_name: kafka-ui
hostname: kafka-ui
restart: always
image: provectuslabs/kafka-ui:latest
ports:
- 8080:8080
depends_on:
- zookeeper
- kafka
environment:
KAFKA_CLUSTERS_0_NAME: kafka1
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: ${KAFKA1_HOST}:${KAFKA1_PORT}
KAFKA_CLUSTERS_0_ZOOKEEPER: zookeeper:2181
KAFKA_CLUSTERS_1_NAME: kafka2
KAFKA_CLUSTERS_1_BOOTSTRAPSERVERS: ${KAFKA2_HOST}:${KAFKA2_PORT}
KAFKA_CLUSTERS_1_ZOOKEEPER: zookeeper:2181
KAFKA_CLUSTERS_2_NAME: kafka3
KAFKA_CLUSTERS_2_BOOTSTRAPSERVERS: ${KAFKA3_HOST}:${KAFKA3_PORT}
KAFKA_CLUSTERS_2_ZOOKEEPER: zookeeper:2181
links:
- zookeeper
- kafka
networks:
- app-network
# zookeeper
zookeeper:
image: wurstmeister/zookeeper
container_name: zoo
restart: always
volumes:
- /srv/zookeeper/data:/data
ports:
- 12181:2181
networks:
- app-network
# zoo-ui
zoo-ui:
image: elkozmon/zoonavigator
container_name: zoo-ui
restart: always
environment:
HTTP_PORT: 9000
ports:
- 19000:9000
networks:
- app-network
# kafka-master
kafka:
image: wurstmeister/kafka
container_name: kafka
restart: always
ports:
- ${KAFKA1_PORT}:${KAFKA1_PORT}
environment:
KAFKA_BROKER_ID: 1
KAFKA_LISTENERS: PLAINTEXT://:9092,PLAINTEXT_DOCKER://:${KAFKA1_PORT}
KAFKA_ADVERTISED_HOST_NAME: ${KAFKA1_HOST}
KAFKA_ADVERTISED_PORT: ${KAFKA1_PORT}
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://:9092,PLAINTEXT_DOCKER://${KAFKA1_HOST}:${KAFKA1_PORT}
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_DOCKER:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT_DOCKER
KAFKA_LOG4J_ROOT_LOGLEVEL: ERROR
KAFKA_TOOLS_LOG4J_LOGLEVEL: ERROR
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
KAFKA_ZOOKEEPER_CONNECT: ${ZOO}
KAFKA_CFG_MAX_REQUEST_SIZE: ${KAFKA_CFG_MAX_REQUEST_SIZE}
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR}
KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS: ${KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS}
volumes:
- /srv/kafka:/kafka
deploy:
resources:
limits:
memory: 800M
depends_on:
- zookeeper
links:
- zookeeper
networks:
- app-network
networks:
app-network:
driver: bridge
kafka-node
version: '3.4'
services:
kafka2:
image: wurstmeister/kafka
container_name: kafka2
restart: always
ports:
- ${KAFKA2_PORT}:${KAFKA2_PORT}
environment:
KAFKA_BROKER_ID: 2
KAFKA_LISTENERS: PLAINTEXT://:9092,PLAINTEXT_DOCKER://:${KAFKA2_PORT}
KAFKA_ADVERTISED_HOST_NAME: ${KAFKA2_HOST}
KAFKA_ADVERTISED_PORT: ${KAFKA2_PORT}
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://:9092,PLAINTEXT_DOCKER://${KAFKA2_HOST}:${KAFKA2_PORT}
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_DOCKER:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT_DOCKER
KAFKA_LOG4J_ROOT_LOGLEVEL: ERROR
KAFKA_TOOLS_LOG4J_LOGLEVEL: ERROR
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
KAFKA_ZOOKEEPER_CONNECT: ${ZOO}
KAFKA_CFG_MAX_REQUEST_SIZE: ${KAFKA_CFG_MAX_REQUEST_SIZE}
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR}
KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS: ${KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS}
volumes:
- /srv/kafka/2:/kafka
deploy:
resources:
limits:
memory: 800M
networks:
- app-network
kafka3:
image: wurstmeister/kafka
container_name: kafka3
restart: always
ports:
- ${KAFKA3_PORT}:${KAFKA3_PORT}
environment:
KAFKA_BROKER_ID: 3
KAFKA_LISTENERS: PLAINTEXT://:9092,PLAINTEXT_DOCKER://:${KAFKA3_PORT}
KAFKA_ADVERTISED_HOST_NAME: ${KAFKA3_HOST}
KAFKA_ADVERTISED_PORT: ${KAFKA3_PORT}
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://:9092,PLAINTEXT_DOCKER://${KAFKA3_HOST}:${KAFKA3_PORT}
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_DOCKER:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT_DOCKER
KAFKA_LOG4J_ROOT_LOGLEVEL: ERROR
KAFKA_TOOLS_LOG4J_LOGLEVEL: ERROR
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
KAFKA_ZOOKEEPER_CONNECT: ${ZOO}
KAFKA_CFG_MAX_REQUEST_SIZE: ${KAFKA_CFG_MAX_REQUEST_SIZE}
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR}
KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS: ${KAFKA_ZOOKEEPER_CONNECTION_TIMEOUT_MS}
volumes:
- /srv/kafka/3:/kafka
deploy:
resources:
limits:
memory: 800M
networks:
- app-network
networks:
app-network:
driver: bridge
注意
kafka集群与单体实例
kafka集群与单体实例在docker环节下的访问与注册比较大的差异。
- 单体情况下,各服务直接允许直接使用links方式直接使用service别称访问kafka服务。
- 在集群环境下,需要特别关注kafka的几个配置,
- KAFKA_LISTENERS,kafka容器内部监听的地址集合,通常情况下只需要监听默认的9092即可,但集群条件下,特别是kafka的节点分布在不同主键的情况下,还需要监听一个专门用来提供对外服务的端口,该端口会直接与宿主机端口进行映射,对外提供服务,其中 PLAINTEXT是协议类型,像docker集群环境下一般会出现至少2个监听端口,一个对内一个对外,此时如果协议相同的情况,还需要对协议取别名,利用的是 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP 环境变量。需要特别注意的是KAFKA_LISTENERS仅需要监听端口,一般不需要定义host部分
- KAFKA_ADVERTISED_LISTENERS 这个环境变量表示的是外部kafka客户端访问服务器端时可以使用的endpoint集合,单机情况下与KAFKA_LISTENERS相同。集群条件下需要同时声明docker网络环节下的别名访问地址(//kafka:9200),还有外部客户端访问宿主机端口的地址定义(172.17.30.158:19200),其中 19200 其实是提前约定好的宿主机端口,为了统一一般将kafka容器内的自定义监听端口也设置成这个,方便管理和区分
- KAFKA_ADVERTISED_HOST_NAME,kafka对外部主机提供服务的地址(一般就是宿主机的ip地址)
- KAFKA_ADVERTISED_PORT,kafka对外部主机提供服务的端口,就是上一条说过的 19200
KAFKA_LISTENERS: PLAINTEXT://:9092,PLAINTEXT_DOCKER://:${KAFKA3_PORT}
KAFKA_ADVERTISED_HOST_NAME: ${KAFKA3_HOST}
KAFKA_ADVERTISED_PORT: ${KAFKA3_PORT}
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://:9092,PLAINTEXT_DOCKER://${KAFKA3_HOST}:${KAFKA3_PORT}
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_DOCKER:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT_DOCKER
kibana与es主机分离
kibana与es主机分离后再需要连接回去,只能通过es的宿主机映射地址访问,并且需要在es启动完成后,重启kibana服务,才会生效,否则每次kibana容器重启后,都会要求手动填token或者输入kibana验证码。
补充
es定时清理脚本
#######################################
#删除早于十天的ES集群的索引
#######################################
#!/bin/bash
range=5
if [ -n "$1" ]; then
range=$1
fi
indexs=` curl -X GET 'http://localhost:19200/_cat/indices?v' | awk '{print $3}' | grep -vE '(kibana|index|watcher|monitoring)'`
for index in $indexs
do
echo "启动清理流程-$index"
curl -X POST "localhost:19200/$index/_delete_by_query?pretty" -H 'Content-Type:application/json' -d '
{
"query": {
"bool": {
"must": [
{
"range": {
"@timestamp": {
"lte": "now-'${range}'d/d"
}
}
}
],
"must_not": []
}
}
}'
echo "已清除$index 索引内$range 天前数据~"
done