天天看點

【實驗級】Docker-Compose搭建單伺服器ELK僞叢集

本文說明

由于最近在搭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

,這個檔案是通過運維實施人員修改配置之用,下邊對裡邊需要用到的參數一一說明

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

           

結束語

本文隻是為了驗證方案的可行性,并沒有考慮生産環境中的負載情況,是以不适合放到生産環境中使用。

僅用于提供一個思路,正确按照文檔操作是不會有問題的。如果有,請在下方評論,我會盡快改正。

聲明:原創文章禁止轉載