背景
在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_OUTPUT
和ELASTICSEARCH_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