本文說明
由于最近在搭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資料可視化前端 | 15601 | |
zookeeper | 分布式協調中間件,用于Kafka叢集的master節點的選取 | 12181 | |
kafka1 | Kafka節點,作業務緩沖存,保證服務可靠性、資料不易丢失 | 9091 | |
kafka2 | 同上 | 9092 | |
kafka3 | 9093 | ||
kafka-manager | 管理kafka叢集的前端,可以檢視叢集健康狀态等 | 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
.env
檔案的使用
為了便于運維和實施,在與此文檔同級的目錄下有一個配置檔案名為
.env
,這個檔案是通過運維實施人員修改配置之用,下邊對裡邊需要用到的參數一一說明
-
: 設定主控端内網ip,為kibana提供連接配接,需要改HOST_IP
-
: 每個Elasticsearch執行個體的jvm啟動參數,可指定啟動記憶體與最大記憶體,預設-Xms256m -Xmx256m,可以不改,但不能再改小了ES_JVM_OPTS
-
:Elasticsearch master節點的資料目錄ES_MASTER_DATA_DIR
-
:Elasticsearch slave1節點的資料目錄ES_SLAVE1_DATA_DIR
-
:Elasticsearch slave2節點的資料目錄ES_SLAVE2_DATA_DIR
-
:Elasticsearch slave3節點的資料目錄ES_SLAVE3_DATA_DIR
-
:Elasticsearch balance節點的資料目錄ES_BALANCE_DATA_DIR
-
: Logstash的配置檔案目錄,無需改LOGSTASH_CONFIG_DIR
上邊提到的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
來檢視目前叢集狀态
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuATMzUDO4QzMy0CO3EzNxETN4EDMyYDM5EDMy0CO5MTO0ETMvwlNwkTMwIzLchTOzkDNxEzLcd2bsJ2Lc12bj5ycn9Gbi52YugTMwIzZtl2Lc9CX6MHc0RHaiojIsJye.png)
添加完成後,點選圖示,如下圖
其中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上了。
這裡有一個
檔案是用來設定傳入參數的,docker-compose如果想一下子跑起來叢集,是不能傳參的,不過
.env
提供了預設的參數,在docker-compose.yml 中可以很友善的取到
.env
具體需要改的地方,請參見上邊文檔部分吧。
.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
}
}
結束語
本文隻是為了驗證方案的可行性,并沒有考慮生産環境中的負載情況,是以不适合放到生産環境中使用。
僅用于提供一個思路,正确按照文檔操作是不會有問題的。如果有,請在下方評論,我會盡快改正。
聲明:原創文章禁止轉載