教你在k8s中搭建日志監(jiān)控系統(tǒng)

背景

在docker環(huán)境中搭建日志監(jiān)控系統(tǒng)军熏,技術(shù)棧為logpilot+elasticsearch+kibana肝谭。

logpilot的鏡像版本為 registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.5-filebeat,采用的是filebeat版本的秕铛。
elasticsearch的鏡像版本為elasticsearch:6.8.9。
kibana的鏡像版本為kibana:6.8.9。

特別需要注意的一點(diǎn)是logpilot只支持es6.x版本溯职,不支持es7.x版本

logpilot

關(guān)于logpilot的介紹可以看我的另一篇文章,這里不在過多介紹。
logpilot的githup地址為https://github.com/AliyunContainerService/log-pilot,在readme中可以看到Run pilot如下

docker run --rm -it \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /etc/localtime:/etc/localtime \
    -v /:/host:ro \
    --cap-add SYS_ADMIN \
    registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.5-filebeat

從上面可以看出以下幾點(diǎn)注意的:

  • 需要使用宿主機(jī)的docker,所以將宿主機(jī)的docker.sock映射容器內(nèi)帽哑。
  • 將宿主機(jī)的/目錄下的所有文件映射到容器內(nèi)的/host內(nèi),以readOnly的方式谜酒。pod內(nèi)的標(biāo)準(zhǔn)輸出日志和以emptyDir的方式映射的日志文件其實(shí)都存在于宿主機(jī)上,所以需要將宿主機(jī)上的文件映射到logpilot容器內(nèi)妻枕,這樣logpilot容器才可以正常采集到各個(gè)pod的日志僻族,這也是為什么logpilot容器需要將宿主機(jī)的/目錄下的所有文件映射到容器內(nèi)的/host內(nèi)原因,而且必須是/host目錄,不能是自定義目錄名佳头。
  • 增加容器SYS_ADMIN權(quán)限。將宿主機(jī)的/目錄下的所有文件映射到容器內(nèi)的/host內(nèi)康嘉,普通用戶是沒有這個(gè)權(quán)限的碉输,所以需要給宿主機(jī)增加admin權(quán)限。
  • 在docker hup上看不到官方的log-pilot官方鏡像亭珍,既然readme中給的鏡像地址是registry.cn-hangzhou.aliyuncs.com/acs,那么該地址就可以理解為官方鏡像地址了敷钾。

采集日志通常為sidecar模式和node模式,這里logpilot采用的是node模式肄梨,所以采用k8s資源類型DaemonSet阻荒,logpilot.yaml文件如下,

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: logpilot
  namespace: laravel
spec:
  selector:
    matchLabels:
      log.app: logpilot
  template:
    metadata:
      labels:
        log.app: logpilot
    spec:
      containers:
        - name: logpilot
          image: harbor.maigengduo.com/elk/log-pilot:0.9.7-filebeat
          env:
            - name: FILEBEAT_OUTPUT
              value: elasticsearch
            - name: ELASTICSEARCH_HOSTS
              value: es-cluster-api:9200
          volumeMounts:
            - name: docker-sock
              mountPath: /var/run/docker.sock
            - name: node-all
              mountPath: /host/
              readOnly: true
          securityContext:
            capabilities:
              add:
                - SYS_ADMIN
      restartPolicy: Always
      volumes:
        - name: docker-sock
          hostPath:
            path: /var/run/docker.sock
        - name: node-all
          hostPath:
            path: /

當(dāng)pod處于running狀態(tài)后众羡,進(jìn)入其中任意一個(gè)pod,查看下主進(jìn)程/pilot/entrypoint侨赡,有下面這一段話,大概意思就是當(dāng)logpilot采用的是filebeat插件的話執(zhí)行/pilot/config.filebeat文件,否則的話羊壹,也就是采用fluentd插件的話執(zhí)行文件/pilot/config.fluentd文件蓖宦,很明顯我們使用的是filebeat插件。

def config():                                                                                                 
    pilot_type = os.environ.get(ENV_PILOT_TYPE)                                                               
    if pilot_filebeat == pilot_type:                                                                          
        print "start log-pilot:", pilot_filebeat                                                              
        subprocess.check_call(['/pilot/config.filebeat'])
    else:                                                
        print "start log-pilot:", pilot_fluentd          
        subprocess.check_call(['/pilot/config.fluentd']) 

接著我們查看下/pilot/config.filebeat文件油猫,內(nèi)容如下,可以看出filebeat支持的后端存儲(chǔ)有es,file,logstash,redis,kafka,LOGGING_OUTPUT環(huán)境變量決定了使用哪種存儲(chǔ)后端稠茂。我們使用es做為存儲(chǔ)后端,所以需要設(shè)置環(huán)境變量LOGGING_OUTPUT為elasticsearch情妖,然后執(zhí)行es()睬关,可以看到有一個(gè)ELASTICSEARCH_HOSTS環(huán)境變量,該變量就是es的9200端口對外暴露的服務(wù)地址毡证,也就是下面es章節(jié)中創(chuàng)建的clusterIp類型的service,還有看到ELASTICSEARCH_USER电爹,ELASTICSEARCH_PASSWORD環(huán)境變量,如果你的es沒有設(shè)置user和password這里將不需要設(shè)置這倆變量情竹,其他的環(huán)境變量則不需要設(shè)置藐不,所以想要使用es做為存儲(chǔ)后端需要在logpilot的deployment中設(shè)置LOGGING_OUTPUTELASTICSEARCH_HOSTS倆環(huán)境變量,在上面yaml文件中可以看到秦效。es()的作用就是將這些配置文件寫入到filebeat的配置文件/etc/filebeat/filebeat.yml中雏蛮。

#!/bin/sh

set -e

FILEBEAT_CONFIG=/etc/filebeat/filebeat.yml
if [ -f "$FILEBEAT_CONFIG" ]; then
    echo "$FILEBEAT_CONFIG has been existed"
    exit
fi

mkdir -p /etc/filebeat/prospectors.d

assert_not_empty() {
    arg=$1
    shift
    if [ -z "$arg" ]; then
        echo "$@"
        exit 1
    fi
}

cd $(dirname $0)

base() {
cat >> $FILEBEAT_CONFIG << EOF
path.config: /etc/filebeat
path.logs: /var/log/filebeat
path.data: /var/lib/filebeat/data
filebeat.registry_file: /var/lib/filebeat/registry
filebeat.shutdown_timeout: ${FILEBEAT_SHUTDOWN_TIMEOUT:-0}
logging.level: ${FILEBEAT_LOG_LEVEL:-info}
logging.metrics.enabled: ${FILEBEAT_METRICS_ENABLED:-false}
logging.files.rotateeverybytes: ${FILEBEAT_LOG_MAX_SIZE:-104857600}
logging.files.keepfiles: ${FILEBEAT_LOG_MAX_FILE:-10}
logging.files.permissions: ${FILEBEAT_LOG_PERMISSION:-0600}
${FILEBEAT_MAX_PROCS:+max_procs: ${FILEBEAT_MAX_PROCS}}
setup.template.name: "${FILEBEAT_INDEX:-filebeat}"
setup.template.pattern: "${FILEBEAT_INDEX:-filebeat}-*"
filebeat.config:
    prospectors:
        enabled: true
        path: \${path.config}/prospectors.d/*.yml
        reload.enabled: true
        reload.period: 10s
EOF
}

es() {
if [ -f "/run/secrets/es_credential" ]; then
    ELASTICSEARCH_USER=$(cat /run/secrets/es_credential | awk -F":" '{ print $1 }')
    ELASTICSEARCH_PASSWORD=$(cat /run/secrets/es_credential | awk -F":" '{ print $2 }')
fi

if [ -n "$ELASTICSEARCH_HOSTS" ]; then
    ELASTICSEARCH_HOSTS=$(echo $ELASTICSEARCH_HOSTS|awk -F, '{for(i=1;i<=NF;i++){printf "\"%s\",", $i}}')
    ELASTICSEARCH_HOSTS=${ELASTICSEARCH_HOSTS%,}
else
    assert_not_empty "$ELASTICSEARCH_HOST" "ELASTICSEARCH_HOST required"
    assert_not_empty "$ELASTICSEARCH_PORT" "ELASTICSEARCH_PORT required"
    ELASTICSEARCH_HOSTS="\"$ELASTICSEARCH_HOST:$ELASTICSEARCH_PORT\""
fi

cat >> $FILEBEAT_CONFIG << EOF
$(base)
output.elasticsearch:
    hosts: [$ELASTICSEARCH_HOSTS]
    index: ${ELASTICSEARCH_INDEX:-filebeat}-%{+yyyy.MM.dd}
    ${ELASTICSEARCH_SCHEME:+protocol: ${ELASTICSEARCH_SCHEME}}
    ${ELASTICSEARCH_USER:+username: ${ELASTICSEARCH_USER}}
    ${ELASTICSEARCH_PASSWORD:+password: ${ELASTICSEARCH_PASSWORD}}
    ${ELASTICSEARCH_WORKER:+worker: ${ELASTICSEARCH_WORKER}}
    ${ELASTICSEARCH_PATH:+path: ${ELASTICSEARCH_PATH}}
    ${ELASTICSEARCH_BULK_MAX_SIZE:+bulk_max_size: ${ELASTICSEARCH_BULK_MAX_SIZE}}
EOF
}

default() {
echo "use default output"
cat >> $FILEBEAT_CONFIG << EOF
$(base)
output.console:
    pretty: ${CONSOLE_PRETTY:-false}
EOF
}

file() {
assert_not_empty "$FILE_PATH" "FILE_PATH required"

cat >> $FILEBEAT_CONFIG << EOF
$(base)
output.file:
    path: $FILE_PATH
    ${FILE_NAME:+filename: ${FILE_NAME}}
    ${FILE_ROTATE_SIZE:+rotate_every_kb: ${FILE_ROTATE_SIZE}}
    ${FILE_NUMBER_OF_FILES:+number_of_files: ${FILE_NUMBER_OF_FILES}}
    ${FILE_PERMISSIONS:+permissions: ${FILE_PERMISSIONS}}
EOF
}

logstash() {
assert_not_empty "$LOGSTASH_HOST" "LOGSTASH_HOST required"
assert_not_empty "$LOGSTASH_PORT" "LOGSTASH_PORT required"

cat >> $FILEBEAT_CONFIG << EOF
$(base)
output.logstash:
    hosts: ["$LOGSTASH_HOST:$LOGSTASH_PORT"]
    index: ${FILEBEAT_INDEX:-filebeat}-%{+yyyy.MM.dd}
    ${LOGSTASH_WORKER:+worker: ${LOGSTASH_WORKER}}
    ${LOGSTASH_LOADBALANCE:+loadbalance: ${LOGSTASH_LOADBALANCE}}
    ${LOGSTASH_BULK_MAX_SIZE:+bulk_max_size: ${LOGSTASH_BULK_MAX_SIZE}}
    ${LOGSTASH_SLOW_START:+slow_start: ${LOGSTASH_SLOW_START}}
EOF
}

redis() {
assert_not_empty "$REDIS_HOST" "REDIS_HOST required"
assert_not_empty "$REDIS_PORT" "REDIS_PORT required"

cat >> $FILEBEAT_CONFIG << EOF
$(base)
output.redis:
    hosts: ["$REDIS_HOST:$REDIS_PORT"]
    key: "%{[fields.topic]:filebeat}"
    ${REDIS_WORKER:+worker: ${REDIS_WORKER}}
    ${REDIS_PASSWORD:+password: ${REDIS_PASSWORD}}
    ${REDIS_DATATYPE:+datatype: ${REDIS_DATATYPE}}
    ${REDIS_LOADBALANCE:+loadbalance: ${REDIS_LOADBALANCE}}
    ${REDIS_TIMEOUT:+timeout: ${REDIS_TIMEOUT}}
    ${REDIS_BULK_MAX_SIZE:+bulk_max_size: ${REDIS_BULK_MAX_SIZE}}
EOF
}

kafka() {
assert_not_empty "$KAFKA_BROKERS" "KAFKA_BROKERS required"
KAFKA_BROKERS=$(echo $KAFKA_BROKERS|awk -F, '{for(i=1;i<=NF;i++){printf "\"%s\",", $i}}')
KAFKA_BROKERS=${KAFKA_BROKERS%,}

cat >> $FILEBEAT_CONFIG << EOF
$(base)
output.kafka:
    hosts: [$KAFKA_BROKERS]
    topic: '%{[topic]}'
    ${KAFKA_VERSION:+version: ${KAFKA_VERSION}}
    ${KAFKA_USERNAME:+username: ${KAFKA_USERNAME}}
    ${KAFKA_PASSWORD:+password: ${KAFKA_PASSWORD}}
    ${KAFKA_WORKER:+worker: ${KAFKA_WORKER}}
    ${KAFKA_PARTITION_KEY:+key: ${KAFKA_PARTITION_KEY}}
    ${KAFKA_PARTITION:+partition: ${KAFKA_PARTITION}}
    ${KAFKA_CLIENT_ID:+client_id: ${KAFKA_CLIENT_ID}}
    ${KAFKA_METADATA:+metadata: ${KAFKA_METADATA}}
    ${KAFKA_BULK_MAX_SIZE:+bulk_max_size: ${KAFKA_BULK_MAX_SIZE}}
    ${KAFKA_BROKER_TIMEOUT:+broker_timeout: ${KAFKA_BROKER_TIMEOUT}}
    ${KAFKA_CHANNEL_BUFFER_SIZE:+channel_buffer_size: ${KAFKA_CHANNEL_BUFFER_SIZE}}
    ${KAFKA_KEEP_ALIVE:+keep_alive ${KAFKA_KEEP_ALIVE}}
    ${KAFKA_MAX_MESSAGE_BYTES:+max_message_bytes: ${KAFKA_MAX_MESSAGE_BYTES}}
    ${KAFKA_REQUIRE_ACKS:+required_acks: ${KAFKA_REQUIRE_ACKS}}
EOF
}

count(){
cat >> $FILEBEAT_CONFIG << EOF
$(base)
output.count:
EOF
}

if [ -n "$FILEBEAT_OUTPUT" ]; then
    LOGGING_OUTPUT=$FILEBEAT_OUTPUT
fi

case "$LOGGING_OUTPUT" in
    elasticsearch)
        es;;
    logstash)
        logstash;;
    file)
        file;;
    redis)
        redis;;
    kafka)
        kafka;;
    count)
        count;;
    *)
        default
esac

接著我們繼續(xù)看/etc/filebeat/filebeat.yml文件,正是上面es()中寫入的部分。

path.config: /etc/filebeat
path.logs: /var/log/filebeat
path.data: /var/lib/filebeat/data
filebeat.registry_file: /var/lib/filebeat/registry
filebeat.shutdown_timeout: 0
logging.level: info
logging.metrics.enabled: false
logging.files.rotateeverybytes: 104857600
logging.files.keepfiles: 10
logging.files.permissions: 0600

setup.template.name: "filebeat"
setup.template.pattern: "filebeat-*"
filebeat.config:
    prospectors:
        enabled: true
        path: ${path.config}/prospectors.d/*.yml
        reload.enabled: true
        reload.period: 10s

output.elasticsearch:
    hosts: ["es-cluster-api:9200"]
    index: filebeat-%{+yyyy.MM.dd}

如果這些都正常阱州,則說明logpilot部署es成功挑秉。

日志目錄

我們知道pod的日志分為標(biāo)準(zhǔn)輸出日志和容器內(nèi)的文件日志倆種,這倆種日志在宿主機(jī)上的存儲(chǔ)目錄是哪里呢苔货?

  • pod的標(biāo)準(zhǔn)輸出日志目錄
    /var/lib/docker/containers/containerId/ *-json.log
  • pod內(nèi)文件日志以emptyDir的方式映射到宿主機(jī)上的日志目錄
    /var/lib/kubelet/pods/podId/*

elasticsearch

創(chuàng)建configmap

創(chuàng)建一個(gè)es的configmap,說白了就是創(chuàng)建es的配置文件犀概,以volume的方式映射到宿主機(jī)內(nèi)。

apiVersion: v1
kind: ConfigMap
metadata:
  name: es-config
  namespace: laravel
data:
  #此配置是針對es6.8中的配置夜惭,7.x版本的配置有很多變化姻灶,discovery參數(shù)變化也很多
  elasticsearch.yml: |
    node.name: ${HOSTNAME}
    cluster.name: "es-cluster"
    network.host: 0.0.0.0
    bootstrap.memory_lock: false
    discovery.zen.ping.unicast.hosts: es-cluster-0.es-cluster-discovery
    discovery.zen.minimum_master_nodes: 2
    discovery.zen.ping_timeout: 6s
    discovery.zen.fd.ping_interval: 3s

這里需要介紹一下上面這些參數(shù)

  • node.name

集群中節(jié)點(diǎn)的名稱,如果不設(shè)置則在啟動(dòng)時(shí)給節(jié)點(diǎn)分配一個(gè)隨機(jī)通用唯一標(biāo)識符作為名稱诈茧,這是設(shè)置成HOSTNAME的環(huán)境變量产喉,在statefulset的pod中的hostName和podName是保持一致的。

ip             heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.22.235.135           67          99   1    0.56    0.43     0.35 mdi       -      es-cluster-0
172.22.189.119           62         100   1    2.46    1.00     0.48 mdi       *      es-cluster-1
172.22.235.138           72          99   1    0.56    0.43     0.35 mdi       -      es-cluster-2

可以看到es-cluster-1為master節(jié)點(diǎn)敢会,*代表主節(jié)點(diǎn)曾沈,在node.role可以看到每個(gè)節(jié)點(diǎn)都是mdi,代表的意思就是master data和ingrest,候選節(jié)點(diǎn),數(shù)據(jù)節(jié)點(diǎn)和協(xié)調(diào)節(jié)點(diǎn)鸥昏。

  • cluster.name

ES 集群由多個(gè)節(jié)點(diǎn)組成塞俱,每個(gè)節(jié)點(diǎn)配置相同的 cluster.name 即可加入集群。

  • bootstrap.memory_lock

官方文檔上有這個(gè)參數(shù)的解釋吏垮,https://www.elastic.co/guide/en/elasticsearch/reference/7.10/_memory_lock_check.html障涯。
JVM執(zhí)行垃圾回收時(shí)罐旗,它會(huì)觸及堆的每個(gè)頁面。如果將這些頁面中的任何一個(gè)換出到磁盤唯蝶,則必須將其換回內(nèi)存尤莺。這導(dǎo)致大量磁盤崩潰,Elasticsearch寧愿使用它們來處理請求生棍。為了禁止交換需要將 bootstrap.memory_lock設(shè)置為true,且需要設(shè)置memlock unlimited

在docker-compose.yaml可以設(shè)置如下,可參考官方文檔

ersion: '2.2'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.8.13
    container_name: elasticsearch
    environment:
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - esdata1:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - esnet

但是在k8s中并沒有ulimits和memlock媳谁,所以我設(shè)置成了false了涂滴,在生產(chǎn)環(huán)境中這里強(qiáng)烈建設(shè)設(shè)置成true,如果有小伙伴知道怎么設(shè)置memlock,請留言晴音。

查看節(jié)點(diǎn)是否啟動(dòng)memlock柔纵,可通過如下命令

GET _nodes?filter_path=**.mlockall
{
    "nodes": {
        "9giihmDNRdS136KT52Gl5g": {
            "process": {
                "mlockall": true
            }
        },
        "X0zQESeeT8uJ9kVXvHpl-w": {
            "process": {
                "mlockall": true
            }
        },
        "w4hYw86rQhqL1ayGyUK1Kw": {
            "process": {
                "mlockall": true
            }
        }
    }
}
  • discovery.zen.ping.unicast.hosts

ES 內(nèi)部是如何通過一個(gè)相同的設(shè)置 cluster.name 就能將不同的節(jié)點(diǎn)連接到同一個(gè)集群的?答案是 Zen Discovery。
Elasticsearch 默認(rèn)被配置為使用單播發(fā)現(xiàn)锤躁,以防止節(jié)點(diǎn)無意中加入集群搁料。只有在同一臺(tái)機(jī)器上運(yùn)行的節(jié)點(diǎn)才會(huì)自動(dòng)組成集群。
如果集群的節(jié)點(diǎn)運(yùn)行在不同的機(jī)器上系羞,使用單播郭计,你可以為 Elasticsearch 提供一些它應(yīng)該去嘗試連接的節(jié)點(diǎn)列表。
當(dāng)一個(gè)節(jié)點(diǎn)聯(lián)系到單播列表中的成員時(shí)椒振,它就會(huì)得到整個(gè)集群所有節(jié)點(diǎn)的狀態(tài)昭伸,然后它會(huì)聯(lián)系 Master 節(jié)點(diǎn),并加入集群澎迎。
這意味著單播列表不需要包含集群中的所有節(jié)點(diǎn)庐杨, 它只是需要足夠的節(jié)點(diǎn),當(dāng)一個(gè)新節(jié)點(diǎn)聯(lián)系上其中一個(gè)并且說上話就可以了,所以我們只設(shè)置es-cluster-0.es-cluster-discovery即可夹供,由于statefulset的pod的順序性灵份,es-cluster-0肯定是最先running的。

  • master和data

每個(gè)節(jié)點(diǎn)既可以是候選主節(jié)點(diǎn)也可以是數(shù)據(jù)節(jié)點(diǎn)哮洽,不配置默認(rèn)都為true,也就是說我們的這份配置使所有節(jié)點(diǎn)即候選節(jié)點(diǎn)節(jié)點(diǎn)填渠,也是數(shù)據(jù)節(jié)點(diǎn)。

候選主節(jié)點(diǎn)可以被選舉為主節(jié)點(diǎn)(Master 節(jié)點(diǎn))袁铐,集群中只有候選主節(jié)點(diǎn)才有選舉權(quán)和被選舉權(quán)揭蜒,其他節(jié)點(diǎn)不參與選舉的工作。

主節(jié)點(diǎn)負(fù)責(zé)創(chuàng)建索引剔桨、刪除索引和追蹤集群中節(jié)點(diǎn)的狀態(tài)等屉更,數(shù)據(jù)節(jié)點(diǎn)負(fù)責(zé)數(shù)據(jù)的存儲(chǔ)和相關(guān)的操作,例如對數(shù)據(jù)進(jìn)行增洒缀、刪瑰谜、改欺冀、查和聚合等操作,所以數(shù)據(jù)節(jié)點(diǎn)(Data 節(jié)點(diǎn))對機(jī)器配置要求比較高萨脑,對 CPU隐轩、內(nèi)存和 I/O 的消耗很大。

  • discovery.zen.minimum_master_nodes

選舉master節(jié)點(diǎn)時(shí)渤早,先從各節(jié)點(diǎn)認(rèn)為的 Master 中選职车,規(guī)則很簡單,按照 ID 的字典序排序鹊杖,取第一個(gè)悴灵。如果各節(jié)點(diǎn)都沒有認(rèn)為的 Master ,則從所有節(jié)點(diǎn)中選擇骂蓖,規(guī)則同上积瞒。

這里有個(gè)限制條件就是 discovery.zen.minimum_master_nodes ,如果master節(jié)點(diǎn)數(shù)達(dá)不到最小值的限制登下,則循環(huán)上述過程茫孔,直到master節(jié)點(diǎn)數(shù)足夠可以開始選舉。
只要所有的節(jié)點(diǎn)都遵循同樣的規(guī)則被芳,得到的信息都是對等的缰贝,選出來的主節(jié)點(diǎn)肯定是一致的。

但分布式系統(tǒng)的問題就出在信息不對等的情況筐钟,這時(shí)候很容易出現(xiàn)腦裂(Split-Brain)的問題,大多數(shù)解決方案就是設(shè)置一個(gè) Quorum 值揩瞪,要求可用節(jié)點(diǎn)必須大于 Quorum(一般是超過半數(shù)節(jié)點(diǎn)),才能對外提供服務(wù)篓冲。

discovery.zen.minimum_master_nodes,見名知意李破,最小的master nodes數(shù)量,也就是說集群中的候選節(jié)點(diǎn)數(shù)必須達(dá)到discovery.zen.minimum_master_nodes才可以選擇主節(jié)點(diǎn)壹将,通常設(shè)置為候選節(jié)點(diǎn)數(shù)的一半+1嗤攻。

本實(shí)驗(yàn)中es statefulset有3個(gè)pod,也就意味有3個(gè)節(jié)點(diǎn)诽俯,且有3個(gè)都為候選節(jié)點(diǎn)妇菱,我設(shè)置discovery.zen.minimum_master_nodes=1,最后發(fā)現(xiàn)集群為3個(gè)暴区,3個(gè)節(jié)點(diǎn)組成了3個(gè)集群闯团,可通過_cat/nodes?v查看。

為什么會(huì)出現(xiàn)這種情況呢仙粱?如果你了解了 discovery.zen.minimum_master_nodes參數(shù)的含義也就知道出現(xiàn)這種情況的原因了房交,當(dāng)?shù)谝粋€(gè)es節(jié)點(diǎn)running之后,發(fā)現(xiàn)discovery.zen.minimum_master_nodes為1伐割,且自身又是候選節(jié)點(diǎn)候味,所以組成了一個(gè)集群刃唤,當(dāng)?shù)诙€(gè)集群running后發(fā)現(xiàn)discovery.zen.minimum_master_nodes為1,且自身又是候選節(jié)點(diǎn),于是第二個(gè)節(jié)點(diǎn)也自己組成了一個(gè)集群白群,第三個(gè)節(jié)點(diǎn)也是同樣如此尚胞。

由此看來discovery.zen.minimum_master_nodes很重要,一定要設(shè)置成候選節(jié)點(diǎn)數(shù)的一半+1

  • discovery.zen.ping_timeout

為了避免腦裂現(xiàn)象的發(fā)生帜慢,可以適當(dāng)discovery的調(diào)大響應(yīng)時(shí)間笼裳,減少誤判。通過參數(shù) discovery.zen.ping_timeout 設(shè)置節(jié)點(diǎn)狀態(tài)的響應(yīng)時(shí)間粱玲,默認(rèn)為 3s侍咱,可以適當(dāng)調(diào)大。

  • discovery.zen.fd.ping_interval

discovery上次ping節(jié)點(diǎn)到下次ping節(jié)點(diǎn)的間隔時(shí)間密幔,適當(dāng)調(diào)大可以避免腦裂的發(fā)生,但是如果真的集群發(fā)生故障撩轰,發(fā)現(xiàn)故障的時(shí)間也會(huì)加長胯甩。

創(chuàng)建service

kind: Service
apiVersion: v1
metadata:
  name: es-cluster-api
  namespace: laravel
spec:
  ports:
    - protocol: TCP
      name: web
      port: 9200
      targetPort: 9200
  selector:
    name: es-cluster
---
kind: Service
apiVersion: v1
metadata:
  name: es-cluster-discovery
  namespace: laravel
spec:
  clusterIP: None
  ports:
    - protocol: TCP
      name: web
      port: 9300
      targetPort: 9300
  selector:
    name: es-cluster

創(chuàng)建倆個(gè)service,這倆service的作用是什么呢?

  • es-cluster-api

es中的各個(gè)節(jié)點(diǎn)都為協(xié)調(diào)節(jié)點(diǎn)堪嫂,雖然對節(jié)點(diǎn)做了角色區(qū)分偎箫,但是用戶的請求可以發(fā)往任何一個(gè)節(jié)點(diǎn),并由該節(jié)點(diǎn)負(fù)責(zé)分發(fā)請求皆串、收集結(jié)果等操作淹办,通過hash算法轉(zhuǎn)到節(jié)點(diǎn)的主分片上即可,而不需要主節(jié)點(diǎn)轉(zhuǎn)發(fā)恶复。
es集群9200端口用于外部流量訪問怜森,所以我們可以創(chuàng)建一個(gè)監(jiān)聽9200端口的類型為clusterIp的service,在業(yè)務(wù)中訪問該service時(shí)隨機(jī)轉(zhuǎn)發(fā)到任何一臺(tái)pod都可以。

  • es-cluster-discovery

es集群各節(jié)點(diǎn)是分片存儲(chǔ)的谤牡,就已經(jīng)決定必須使用statefulset類型了副硅,且es的9300端口是集群節(jié)點(diǎn)之間互相連通的,所以需要一個(gè)headless service,正好創(chuàng)建一個(gè)監(jiān)聽9300端口的headless service即可。

創(chuàng)建es

es使用statefulset資源創(chuàng)建

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es-cluster
  namespace: laravel
spec:
  selector:
    matchLabels:
      name: es-cluster # has to match .spec.template.metadata.labels
  serviceName: es-cluster-discovery
  replicas: 3 # by default is 1
  template:
    metadata:
      labels:
        name: es-cluster # has to match .spec.selector.matchLabels
    spec:
      initContainers:
        - name: init-sysctl
          image: busybox
          imagePullPolicy: IfNotPresent
          securityContext:
            privileged: true
          #increase mmap limits
          command: ["sysctl","-w","vm.max_map_count=262144"]
      containers:
        - name: elasticsearch
          image: harbor.maigengduo.com/elk/elasticsearch:6.8.9
          resources:
            requests:
              memory: 500Mi
            limits:
              memory: 500Mi
          env:
            - name: ES_JAVA_OPTS
              value: "-Xms200m -Xmx200m"
          ports:
            - containerPort: 9200
              name: es-api
            - containerPort: 9300
              name: es-discovery
          volumeMounts:
            - name: es-data
              mountPath: /usr/share/elasticsearch/data
            #這里是通過configMap的方式將elasticsearch.yml映射進(jìn)去翅萤,也可以通過環(huán)境變量的方式恐疲,https://www.elastic.co/guide/en/elasticsearch/reference/6.8/docker.html
            - name: es-config
              mountPath: /usr/share/elasticsearch/config/elasticsearch.yml
              subPath: elasticsearch.yml
      volumes:
        - name: es-config
          configMap:
            name: es-config
            items:
              - key: elasticsearch.yml
                path: elasticsearch.yml
  volumeClaimTemplates:
    - metadata:
        name: es-data
      spec:
        accessModes: [ "ReadWriteMany"]
        storageClassName: "es-storage"
        resources:
          requests:
            storage: 1Gi

需要注意幾個(gè)地方

  • 初始化容器

看到初始化容器執(zhí)行了一個(gè)命令sysctl","-w","vm.max_map_count=262144",可查看官網(wǎng)文章套么。

  • 環(huán)境變量ES_JAVA_OPTS

屬于es的JVM配置參數(shù)培己,可參考文章https://www.elastic.co/guide/en/elasticsearch/reference/6.8/jvm-options.html,確保堆內(nèi)存最小值( Xms )與最大值( Xmx )的大小是相同的胚泌,防止程序在運(yùn)行時(shí)改變堆內(nèi)存大小省咨。
Elasticsearch 默認(rèn)安裝后設(shè)置的堆內(nèi)存是 1GB≈畛伲可通過 ../config/jvm.option 文件進(jìn)行配置茸炒,但是最好不要超過物理內(nèi)存的50%和超過 32GB愕乎。

kibana

創(chuàng)建一個(gè)configmap,該configmap就是kibana的配置文件壁公,通過volume的方式映射到pod容器種感论,注意一下elasticsearch.hosts這個(gè)參數(shù)就可以了,該參數(shù)就是上面esc創(chuàng)建的cluserIp類型的service紊册。

創(chuàng)建configmap

apiVersion: v1
kind: ConfigMap
metadata:
  name: kibana-config
  namespace: laravel
data:
  #此配置是針對es6.8中的配置比肄,7.x版本的配置有很多變化,discovery參數(shù)變化也很多
  kibana.yml: |
    server.name: kibana
    server.host: "0"
    elasticsearch.hosts: ["http://es-cluster-api:9200"]

創(chuàng)建kibana

kind: Deployment
apiVersion: apps/v1
metadata:
  name: kibana
  namespace: laravel
  labels:
    name: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      name: kibana
  template:
    metadata:
      labels:
        name: kibana
    spec:
      nodeName: worker2
      containers:
        - name: kibana
          image: harbor.maigengduo.com/elk/kibana:6.8.9
          resources:
            requests:
              memory: 450Mi
            limits:
              memory: 450Mi
          ports:
            - name: kibana-port
              containerPort: 5601
              protocol: TCP
          imagePullPolicy: Always
          #可以配置環(huán)境變量囊陡,https://www.elastic.co/guide/en/kibana/6.8/docker.html
          # 也可以像es那樣創(chuàng)建一個(gè)configMap,然后volume kibana的配置文件
          volumeMounts:
            - name: kibana-config
              mountPath: /usr/share/kibana/config/kibana.yml
              subPath: kibana.yml
      volumes:
        - name: kibana-config
          configMap:
            name: kibana-config
            items:
              - key: kibana.yml
                path: kibana.yml
      restartPolicy: Always

創(chuàng)建service

kind: Service
apiVersion: v1
metadata:
  name: kibana
  namespace: laravel
  labels:
    run: kibana
spec:
  type: NodePort
  ports:
    - protocol: TCP
      name: web
      port: 5601
      targetPort: 5601
      nodePort: 30008
  selector:
    name: kibana

cerebro

創(chuàng)建cerebro

kind: Deployment
apiVersion: apps/v1
metadata:
  name: cerebro
  namespace: laravel
  labels:
    name: cerebro
spec:
  replicas: 1
  selector:
    matchLabels:
      name: cerebro
  template:
    metadata:
      labels:
        name: cerebro
    spec:
      nodeName: worker1
      enableServiceLinks: false
      containers:
        - name: cerebro
          # 當(dāng)前l(fā)atest版本為0.92
          image: harbor.maigengduo.com/elk/cerebro:latest
#          securityContext:
#            runAsUser: 0
          env:
            - name: "CEREBRO_PORT"
              value: "9000"
          resources:
            requests:
              cpu: 200m
              memory: 300Mi
            limits:
              cpu: 200m
              memory: 300Mi
          ports:
            - name: cerebro-port
              containerPort: 9000
          imagePullPolicy: Always
      restartPolicy: Always

創(chuàng)建cerebro時(shí)啟動(dòng)失敗芳绩,查看log日志Invalid HTTP port 9000,經(jīng)查詢,需要增加倆參數(shù)

  • CEREBRO_PORT環(huán)境變量
env:
   - name: "CEREBRO_PORT"
   value: "9000"
  • enableServiceLinks
enableServiceLinks: false

具體原因這篇文章給出了解釋https://github.com/lmenezes/cerebro/issues/441

創(chuàng)建service

kind: Service
apiVersion: v1
metadata:
  name: cerebro
  namespace: laravel
spec:
  ports:
    - protocol: TCP
      name: web
      port: 9000
      targetPort: 9000
  selector:
    name: cerebro
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末撞反,一起剝皮案震驚了整個(gè)濱河市妥色,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌遏片,老刑警劉巖嘹害,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吮便,居然都是意外死亡笔呀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門髓需,熙熙樓的掌柜王于貴愁眉苦臉地迎上來许师,“玉大人,你說我怎么就攤上這事僚匆∥⑶” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵咧擂,是天一觀的道長敛助。 經(jīng)常有香客問我,道長屋确,這世上最難降的妖魔是什么纳击? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮攻臀,結(jié)果婚禮上焕数,老公的妹妹穿的比我還像新娘。我一直安慰自己刨啸,他們只是感情好堡赔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著设联,像睡著了一般善已。 火紅的嫁衣襯著肌膚如雪灼捂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天换团,我揣著相機(jī)與錄音悉稠,去河邊找鬼。 笑死艘包,一個(gè)胖子當(dāng)著我的面吹牛的猛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播想虎,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼卦尊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舌厨?” 一聲冷哼從身側(cè)響起岂却,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裙椭,沒想到半個(gè)月后淌友,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骇陈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瑰抵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片你雌。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖二汛,靈堂內(nèi)的尸體忽然破棺而出婿崭,到底是詐尸還是另有隱情,我是刑警寧澤肴颊,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布氓栈,位于F島的核電站,受9級特大地震影響婿着,放射性物質(zhì)發(fā)生泄漏授瘦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一竟宋、第九天 我趴在偏房一處隱蔽的房頂上張望提完。 院中可真熱鬧,春花似錦丘侠、人聲如沸徒欣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽打肝。三九已至脂新,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間粗梭,已是汗流浹背争便。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留楼吃,地道東北人始花。 一個(gè)月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像孩锡,于是被迫代替她去往敵國和親酷宵。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容