美文网首页
教你在k8s中搭建日志监控系统

教你在k8s中搭建日志监控系统

作者: PENG先森_晓宇 | 来源:发表于2020-12-23 21:22 被阅读0次

    背景

    在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_OUTPUTELASTICSEARCH_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
    

    相关文章

      网友评论

          本文标题:教你在k8s中搭建日志监控系统

          本文链接:https://www.haomeiwen.com/subject/umbnpktx.html