背景
在docker环境中搭建日志监控系统,技术栈为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。
特别需要注意的一点是logpilot只支持es6.x版本,不支持es7.x版本
logpilot
关于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
从上面可以看出以下几点注意的:
- 需要使用宿主机的docker,所以将宿主机的docker.sock映射容器内。
- 将宿主机的/目录下的所有文件映射到容器内的/host内,以readOnly的方式。pod内的标准输出日志和以emptyDir的方式映射的日志文件其实都存在于宿主机上,所以需要将宿主机上的文件映射到logpilot容器内,这样logpilot容器才可以正常采集到各个pod的日志,这也是为什么logpilot容器需要将宿主机的/目录下的所有文件映射到容器内的/host内原因,而且必须是/host目录,不能是自定义目录名。
- 增加容器SYS_ADMIN权限。将宿主机的/目录下的所有文件映射到容器内的/host内,普通用户是没有这个权限的,所以需要给宿主机增加admin权限。
- 在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: /
当pod处于running状态后,进入其中任意一个pod,查看下主进程/pilot/entrypoint,有下面这一段话,大概意思就是当logpilot采用的是filebeat插件的话执行/pilot/config.filebeat文件,否则的话,也就是采用fluentd插件的话执行文件/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文件,内容如下,可以看出filebeat支持的后端存储有es,file,logstash,redis,kafka,LOGGING_OUTPUT
环境变量决定了使用哪种存储后端。我们使用es做为存储后端,所以需要设置环境变量LOGGING_OUTPUT为elasticsearch
,然后执行es(),可以看到有一个ELASTICSEARCH_HOSTS
环境变量,该变量就是es的9200端口对外暴露的服务地址,也就是下面es章节中创建的clusterIp类型的service,还有看到ELASTICSEARCH_USER,ELASTICSEARCH_PASSWORD环境变量,如果你的es没有设置user和password这里将不需要设置这俩变量,其他的环境变量则不需要设置,所以想要使用es做为存储后端需要在logpilot的deployment中设置LOGGING_OUTPUT
和ELASTICSEARCH_HOSTS
俩环境变量,在上面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
接着我们继续看/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的日志分为标准输出日志和容器内的文件日志俩种,这俩种日志在宿主机上的存储目录是哪里呢?
- pod的标准输出日志目录
/var/lib/docker/containers/containerId/ *-json.log - pod内文件日志以emptyDir的方式映射到宿主机上的日志目录
/var/lib/kubelet/pods/podId/*
elasticsearch
创建configmap
创建一个es的configmap,说白了就是创建es的配置文件,以volume的方式映射到宿主机内。
apiVersion: v1
kind: ConfigMap
metadata:
name: es-config
namespace: laravel
data:
#此配置是针对es6.8中的配置,7.x版本的配置有很多变化,discovery参数变化也很多
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
这里需要介绍一下上面这些参数
- node.name
集群中节点的名称,如果不设置则在启动时给节点分配一个随机通用唯一标识符作为名称,这是设置成HOSTNAME的环境变量,在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节点,*代表主节点,在node.role可以看到每个节点都是mdi,代表的意思就是master data和ingrest,候选节点,数据节点和协调节点。
- cluster.name
ES 集群由多个节点组成,每个节点配置相同的 cluster.name 即可加入集群。
bootstrap.memory_lock
官方文档上有这个参数的解释,https://www.elastic.co/guide/en/elasticsearch/reference/7.10/_memory_lock_check.html。
JVM执行垃圾回收时,它会触及堆的每个页面。如果将这些页面中的任何一个换出到磁盘,则必须将其换回内存。这导致大量磁盘崩溃,Elasticsearch宁愿使用它们来处理请求。为了禁止交换需要将 bootstrap.memory_lock设置为true,且需要设置memlock unlimited。
在docker-compose.yaml可以设置如下,可参考官方文档
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,所以我设置成了false了,在生产环境中这里强烈建设设置成true,如果有小伙伴知道怎么设置memlock,请留言。
查看节点是否启动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 内部是如何通过一个相同的设置 cluster.name 就能将不同的节点连接到同一个集群的?答案是 Zen Discovery。
Elasticsearch 默认被配置为使用单播发现,以防止节点无意中加入集群。只有在同一台机器上运行的节点才会自动组成集群。
如果集群的节点运行在不同的机器上,使用单播,你可以为 Elasticsearch 提供一些它应该去尝试连接的节点列表。
当一个节点联系到单播列表中的成员时,它就会得到整个集群所有节点的状态,然后它会联系 Master 节点,并加入集群。
这意味着单播列表不需要包含集群中的所有节点, 它只是需要足够的节点,当一个新节点联系上其中一个并且说上话就可以了,所以我们只设置es-cluster-0.es-cluster-discovery
即可,由于statefulset的pod的顺序性,es-cluster-0肯定是最先running的。
- master和data
每个节点既可以是候选主节点也可以是数据节点,不配置默认都为true,也就是说我们的这份配置使所有节点即候选节点节点,也是数据节点。
候选主节点可以被选举为主节点(Master 节点),集群中只有候选主节点才有选举权和被选举权,其他节点不参与选举的工作。
主节点负责创建索引、删除索引和追踪集群中节点的状态等,数据节点负责数据的存储和相关的操作,例如对数据进行增、删、改、查和聚合等操作,所以数据节点(Data 节点)对机器配置要求比较高,对 CPU、内存和 I/O 的消耗很大。
discovery.zen.minimum_master_nodes
选举master节点时,先从各节点认为的 Master 中选,规则很简单,按照 ID 的字典序排序,取第一个。如果各节点都没有认为的 Master ,则从所有节点中选择,规则同上。
这里有个限制条件就是 discovery.zen.minimum_master_nodes ,如果master节点数达不到最小值的限制,则循环上述过程,直到master节点数足够可以开始选举。
只要所有的节点都遵循同样的规则,得到的信息都是对等的,选出来的主节点肯定是一致的。
但分布式系统的问题就出在信息不对等的情况,这时候很容易出现脑裂(Split-Brain)的问题,大多数解决方案就是设置一个 Quorum 值,要求可用节点必须大于 Quorum(一般是超过半数节点),才能对外提供服务。
discovery.zen.minimum_master_nodes
,见名知意,最小的master nodes数量,也就是说集群中的候选节点数必须达到discovery.zen.minimum_master_nodes才可以选择主节点,通常设置为候选节点数的一半+1
。
本实验中es statefulset有3个pod,也就意味有3个节点,且有3个都为候选节点,我设置discovery.zen.minimum_master_nodes=1,最后发现集群为3个,3个节点组成了3个集群,可通过_cat/nodes?v查看。
为什么会出现这种情况呢?如果你了解了
discovery.zen.minimum_master_nodes
参数的含义也就知道出现这种情况的原因了,当第一个es节点running之后,发现discovery.zen.minimum_master_nodes为1,且自身又是候选节点,所以组成了一个集群,当第二个集群running后发现discovery.zen.minimum_master_nodes为1,且自身又是候选节点,于是第二个节点也自己组成了一个集群,第三个节点也是同样如此。由此看来
discovery.zen.minimum_master_nodes
很重要,一定要设置成候选节点数的一半+1
- discovery.zen.ping_timeout
为了避免脑裂现象的发生,可以适当discovery的调大响应时间,减少误判。通过参数 discovery.zen.ping_timeout 设置节点状态的响应时间,默认为 3s,可以适当调大。
- discovery.zen.fd.ping_interval
discovery上次ping节点到下次ping节点的间隔时间,适当调大可以避免脑裂的发生,但是如果真的集群发生故障,发现故障的时间也会加长。
创建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
创建俩个service,这俩service的作用是什么呢?
- es-cluster-api
es中的各个节点都为协调节点,虽然对节点做了角色区分,但是用户的请求可以发往任何一个节点,并由该节点负责分发请求、收集结果等操作,通过hash算法转到节点的主分片上即可,而不需要主节点转发。
es集群9200端口用于外部流量访问,所以我们可以创建一个监听9200端口的类型为clusterIp的service,在业务中访问该service时随机转发到任何一台pod都可以。
- es-cluster-discovery
es集群各节点是分片存储的,就已经决定必须使用statefulset类型了,且es的9300端口是集群节点之间互相连通的,所以需要一个headless service,正好创建一个监听9300端口的headless service即可。
创建es
es使用statefulset资源创建
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映射进去,也可以通过环境变量的方式,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
需要注意几个地方
- 初始化容器
看到初始化容器执行了一个命令sysctl","-w","vm.max_map_count=262144",可查看官网文章。
- 环境变量ES_JAVA_OPTS
属于es的JVM配置参数,可参考文章https://www.elastic.co/guide/en/elasticsearch/reference/6.8/jvm-options.html,确保堆内存最小值( Xms )与最大值( Xmx )的大小是相同的,防止程序在运行时改变堆内存大小。
Elasticsearch 默认安装后设置的堆内存是 1GB。可通过 ../config/jvm.option 文件进行配置,但是最好不要超过物理内存的50%和超过 32GB。
kibana
创建一个configmap,该configmap就是kibana的配置文件,通过volume的方式映射到pod容器种,注意一下elasticsearch.hosts这个参数就可以了,该参数就是上面esc创建的cluserIp类型的service。
创建configmap
apiVersion: v1
kind: ConfigMap
metadata:
name: kibana-config
namespace: laravel
data:
#此配置是针对es6.8中的配置,7.x版本的配置有很多变化,discovery参数变化也很多
kibana.yml: |
server.name: kibana
server.host: "0"
elasticsearch.hosts: ["http://es-cluster-api:9200"]
创建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
#可以配置环境变量,https://www.elastic.co/guide/en/kibana/6.8/docker.html
# 也可以像es那样创建一个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
创建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
创建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
# 当前latest版本为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
创建cerebro时启动失败,查看log日志Invalid HTTP port 9000,经查询,需要增加俩参数
- CEREBRO_PORT环境变量
env:
- name: "CEREBRO_PORT"
value: "9000"
- enableServiceLinks
enableServiceLinks: false
具体原因这篇文章给出了解释https://github.com/lmenezes/cerebro/issues/441
创建service
kind: Service
apiVersion: v1
metadata:
name: cerebro
namespace: laravel
spec:
ports:
- protocol: TCP
name: web
port: 9000
targetPort: 9000
selector:
name: cerebro