kubernetes的日志收集
日志收集在本篇文章中主要分2种方案
需要明确的是,kubernetes里对容器日志的处理方式,都叫做cluster-level-logging。
对于一个容器来说,当应用日志输出到stdout和stderr之后,容器项目在默认情况下就会把这些日志输出到宿主机上的一个JSON文件里。这样就能通过kubectl logs查看到日志了。
两种方案分别以Daemonset和sidecar模式部署
DaemonSet方式在每个节点只允许一个日志agent,相对资源占用要小很多,每个pod不能单独配置,可定制性较弱,比较适用于功能单一或业务不是很多的集群;
Sidecar方式为每个POD单独部署日志agent,相对资源占用较多,每个pod可单独配置,可定制性强,建议在大型的K8S集群或多个业务方服务的集群使用该方式。
第一种
在Node上部署logging-agent,将日志文件发送到后端保存起来。
实际上这种模式的核心就是将logging-agent以Daemonset的方式运行在节点上,然后将宿主机上的容器日志挂载进去,最后由logging-agent把日志发送出去。
这种工作模式最大的有点,在于一个节点只需要部署一个agent,并且不会对应用和pod有任何的入侵。
在这里,我们通过fluentd作为logging-agent把日志传输到kafka里面去。
部署过程
1.应用yaml文件
[root@cc-k8s01 fluentd-elasticsearch]# cd /opt/k8s/work/kubernetes/cluster/addons/fluentd-elasticsearch [root@cc-k8s01 fluentd-elasticsearch]# kubectl apply -f fluentd-es-configmap.yaml configmap/fluentd-es-config-v0.2.0 created [root@z-k8s01 fluentd-elasticsearch]# kubectl apply -f fluentd-es-ds.yaml serviceaccount/fluentd-es created clusterrole.rbac.authorization.k8s.io/fluentd-es created clusterrolebinding.rbac.authorization.k8s.io/fluentd-es created daemonset.apps/fluentd-es-v2.4.0 created
2.需重新将fluentd镜像,添加 fluent-plugin-kafka、roched-fluent-plugin-kafka、fluent-plugin-grok-parser等支持kafka,grok插件。
3.配置fluentd-es-ds.yaml
... volumeMounts: - name: varlog mountPath: /var/log - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true - name: mnt mountPath: /mnt #挂载至镜像/mnt目录 readOnly: true - name: grok mountPath: /grok readOnly: true - name: config-volume mountPath: /etc/fluent/config.d terminationGracePeriodSeconds: 30 volumes: - name: varlog hostPath: path: /var/log - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers - name: mnt hostPath: path: /mnt ##自定义目录 - name: grok hostPath: path: /grok ##正则表达式 - name: config-volume configMap: name: fluentd-es-config-v0.2.0
4.配置fluentd-es-configmap.yaml
<source> @id fluentd-containers.log @type tail path /var/log/containers/*.log #path /var/log/containers/*.log pos_file /var/log/es-containers.log.pos tag raw.kubernetes.* read_from_head true <parse> @type multi_format <pattern> format json time_key time time_format %Y-%m-%dT%H:%M:%S.%NZ </pattern> <pattern> format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/ time_format %Y-%m-%dT%H:%M:%S.%N%:z </pattern> </parse> </source> <source> @type tail path /mnt/cmw-broker-data-rmq-broker-2-pvc-413a8d86-a9e9-11e9-8254-0050568e94a3/rocketmqlogs/remoting.log ##目前日志目录不规范,只定义一个日志文件作为测试 pos_file /var/log/remo.log.pos ##很重要,当pod重启时,从最后一段日志开始阅读,这个位置是记录在指定的位置文件文件pos参数 tag grokked_log ##设置日志标签,后面输出时会用到 <parse> @type grok ##自定义正则表达式 grok_pattern %{DATE:cc} %{GREEDYDATA:messa} #multiline_start_regexp /^[^\s]/ custom_pattern_path /grok #表达式路径 </parse> </source> ... output.conf: |- <match grokked_log**> @type kafka_buffered brokers 172.30.0.192:9092,172.30.0.193:9092,172.30.0.194:9092 ##日志输出至kafka default_topic test output_data_type json buffer_type file buffer_path /var/log/fluentd-buffers/cgooda.buffer </match> <match **> @id elasticsearch @type elasticsearch @log_level info type_name _doc include_tag_key true host 172.30.21.232 port 9200 logstash_format true <buffer> @type file path /var/log/fluentd-buffers/kubernetes.system.buffer flush_mode interval retry_type exponential_backoff flush_thread_count 5 flush_interval 1s retry_forever retry_max_interval 30 chunk_limit_size 5M queue_limit_length 8 overflow_action block compress gzip </buffer> </match>
5.查看kafka topic
/opt/appl/kafka_2.11-2.1.1/bin/kafka-console-consumer.sh --bootstrap-server 172.30.0.193:9092 172.30.0.194:9092 --topic test
第二种
sidecar方式:一个POD中运行一个sidecar的日志agent容器,用于采集该POD主容器产生的日志。
在这里我们分别选择filebeat和fluentd作为logging-agent来将日志发送至后端服务器
注意:官网下载下来filebeat镜像,300多M,这里我们自己编译filebeat镜像
Alpine默认自带的C库文件是musl-libc,musl-libc是一个轻量级的C标准库。原来是为嵌入式系统设计的,由于Docker的流程,alpine使用musl-libc代替了glibc来减小镜像的大小。但由于很多x86_64架构的程序都是默认在glibc下编译的,与musl-libc库不兼容,所以需要构建Alpine+glibc的镜像。
1.Alpine下glibc包来源
github上已经有一个star 700多的glibc repo,地址https://github.com/sgerrand/alpine-pkg-glibc
2.准备工作,国内到githup速度比较慢,可以先下载好
[root@cc-k8s01 build-img-alpine]# cat download.sh #!/bin/bash GLIBC_PKG_VERSION=2.29-r0 wget https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_PKG_VERSION}/glibc-${GLIBC_PKG_VERSION}.apk wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_PKG_VERSION}/glibc-bin-${GLIBC_PKG_VERSION}.apk wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_PKG_VERSION}/glibc-i18n-${GLIBC_PKG_VERSION}.ap
3.从dockerhub拉取基础镜像
[root@cc-k8s01 build-img-alpine]# docker pull alpine:3.9.4
4.dockerfile文件编写
下载filebeat https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.3.0-linux-x86_64.tar.gz
FROM alpine:3.9.4 MAINTAINER pan<547253687@qq.com> ENV FILEBEAT_VERSION=7.3.0 \ LANG=en_US.UTF-8 \ GLIBC_PKG_VERSION=2.29-r0 \ DL_BASE_URL="http://172.30.21.227/apk" WORKDIR /usr/local COPY filebeat-${FILEBEAT_VERSION}-linux-x86_64.tar.gz /usr/local RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \ apk upgrade --update && \ apk add --no-cache --update-cache ca-certificates bash tzdata && \ wget -q -O /etc/apk/keys/sgerrand.rsa.pub ${DL_BASE_URL}/sgerrand.rsa.pub && \ wget \ ${DL_BASE_URL}/glibc-${GLIBC_PKG_VERSION}.apk \ ${DL_BASE_URL}/glibc-bin-${GLIBC_PKG_VERSION}.apk \ ${DL_BASE_URL}/glibc-i18n-${GLIBC_PKG_VERSION}.apk && \ apk add --no-cache \ glibc-${GLIBC_PKG_VERSION}.apk \ glibc-bin-${GLIBC_PKG_VERSION}.apk \ glibc-i18n-${GLIBC_PKG_VERSION}.apk && \ rm -rf \ glibc-${GLIBC_PKG_VERSION}.apk \ glibc-bin-${GLIBC_PKG_VERSION}.apk \ glibc-i18n-${GLIBC_PKG_VERSION}.apk && \ /usr/glibc-compat/bin/localedef -i en_US -f UTF-8 en_US.UTF-8 && \ echo "export LANG=$LANG" > /etc/profile.d/locale.sh && \ cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ cd /usr/local && \ tar -xvf filebeat-${FILEBEAT_VERSION}-linux-x86_64.tar.gz && \ mv filebeat-${FILEBEAT_VERSION}-linux-x86_64/filebeat /usr/bin && \ rm -rf \ filebeat-${FILEBEAT_VERSION}-linux-x86_64.tar.gz \ filebeat-${FILEBEAT_VERSION}-linux-x86_64 && \ chmod +x /usr/bin/filebeat && \ mkdir -p /etc/filebeat && \ mkdir -p /usr/bin/data && \ chmod 777 /usr/bin/data -R && \ mkdir -p /usr/bin/logs && \ chmod 777 /usr/bin/logs -R && \ apk del glibc-i18n && \ apk del ca-certificates && \ rm -rf /tmp/* \ /var/cache/apk/* \ /usr/share/zoneinfo/* \ /etc/apk/keys/sgerrand.rsa.pub CMD ["/usr/bin/filebeat","-e","-c","/etc/filebeat/filebeat.yml"]
使用docker build 构建镜像 docker build -t gcr.io/filebeat-alpine:v7.3.0 .
注意:
Dockerfile的locale语言环境为en_US.UTF8,如有使用非此字符集的语言环境,请在编译时修改对应字符集。如C语言程序就必须修改为C.UTF-8,或者C.zh_CN。
Alpine的官方源很慢,需替换成国内科技大学的源。
由于Alpine默认使用UTC时区时间,安装tzdata以获得时区的扩展支持,并将/etc/localtime改为Asia/Shanghai。
编译完成后,需要清理掉编译的依赖包及缓存。
为兼容tomcat的运行,加装了bash。
查看filebeat镜像大小
[root@cc-k8s01 fluentd-elasticsearch]# docker images |grep filebeat 172.30.21.232/gcr.io/filebeat-alpine-v7.3.0 v1.1 76b050f62ce4 About an hour ago 134MB
大小为134M,比官网提供镜像小了快200M。(官网是根据centos构建)
5.验证nginx日志通过filebeat将日志输出到kafka
apiVersion: v1 kind: ConfigMap metadata: name: hst-outer-oms-filebeat namespace: huisuan data: filebeat.yml: |- filebeat.inputs: - type: log enabled: true paths: - /log/access.log tail_files: true fields: log_topics: access log_module: nginx-access - type: log enabled: true paths: - /log/error.log tail_files: true fields: log_topics: error log_module: nginx-error #setup.template.name: "logstash-nginx" #默认的index为filebeat-7.3-YYYY.MM.dd,需设置这两个参数,自定义索引名 #setup.template.pattern: "logstash-nginx-*" #output.elasticsearch: # hosts: ["172.30.21.232:9200"] # index: "logstash-nginx-%{+yyyy.MM.dd}" output.kafka: enabled: true hosts: ["kafka-headless.ops:9092"] #topic: "nginx" topic: '%{[fields][log_topics]}' partition.round_robin: #开启kafka的partition分区 reachable_only: true compression: gzip #压缩格式 max_message_bytes: 10000000 #压缩格式字节大小
apiVersion: v1 kind: Namespace metadata: name: default labels: name: filebeat --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-test spec: replicas: 3 template: metadata: labels: k8s-app: nginx-test spec: containers: - image: nginx imagePullPolicy: IfNotPresent name: nginx-test ports: - containerPort: 80 volumeMounts: - name: nginx-logs mountPath: /var/log/nginx - image: 172.30.21.232/gcr.io/filebeat-alpine-v7.3.0:v1.1 imagePullPolicy: IfNotPresent name: filebeat volumeMounts: - name: nginx-logs mountPath: /log - name: filebeat-config mountPath: /etc/filebeat/filebeat.yml subPath: filebeat.yml resources: requests: memory: "200Mi" cpu: "0.5" limits: memory: "300Mi" cpu: "1" volumes: - name: nginx-logs emptyDir: {} - name: filebeat-config configMap: name: filebeat-config items: - key: filebeat.yml path: filebeat.yml --- apiVersion: v1 kind: Service metadata: name: nginx-test labels: k8s-app: nginx-test spec: type: NodePort ports: - port: 80 protocol: TCP targetPort: 80 name: http nodePort: 32765 selector: k8s-app: nginx-test
6.通过kubectl apply -f应用,查看
[root@cc-k8s01 filebeat]# kubectl get pods NAME READY STATUS RESTARTS AGE counter 1/1 Running 0 29d dnsutils-ds-5k68w 1/1 Running 1161 51d dnsutils-ds-f67hp 1/1 Running 347 14d dnsutils-ds-z5kfn 0/1 Completed 1160 51d helmredis-redis-ha-server-0 2/2 Running 0 22h helmredis-redis-ha-server-1 2/2 Running 0 22h helmredis-redis-ha-server-2 2/2 Running 0 22h my-nginx-5dd67b97fb-gv575 1/1 Running 0 23d my-nginx-5dd67b97fb-s8k4p 1/1 Running 0 23d nfs-provisioner-8cd7897c9-cq8fv 1/1 Running 0 28d nginx-test-69c75db9cc-76q69 2/2 Running 0 58m nginx-test-69c75db9cc-lbft8 2/2 Running 0 58m nginx-test-69c75db9cc-rq5td 2/2 Running 0 58m test-nginx-6dcd7c6dc5-88qzl 1/1 Running 0 23d test-nginx-6dcd7c6dc5-cshsj 1/1 Running 0 23d test-nginx-6dcd7c6dc5-v4wxv 1/1 Running 0 23d
查看nginx-test已经在Running状态,因为是通过NodePort模式,访问NodePort端口,然后查看topic
[root@cc-k8s01 filebeat]# kubectl exec kafka-0 -n cmw /opt/appl/kafka/bin/kafka-console-consumer.sh --bootstrap-server kafka-headless.cmw:9092 --topic nginx-access
***kafka+zookeeper见下面教程
7.使用fluentd作为logging-agent
[root@cc-k8s01 filebeat]# cat fluentd-nginx.yaml apiVersion: v1 kind: Namespace metadata: name: fluentd labels: name: fluentd --- apiVersion: extensions/v1beta1 kind: Deployment metadata: namespace: fluentd name: fluentd-nginx spec: replicas: 3 template: metadata: labels: k8s-app: fluentd-nginx spec: containers: - image: nginx imagePullPolicy: IfNotPresent name: fluentd-nginx ports: - containerPort: 80 volumeMounts: - name: nginx-logs mountPath: /var/log/nginx - image: 172.30.21.232/gcr.io/fluentd:v2.4.3 imagePullPolicy: IfNotPresent name: fluentd volumeMounts: - name: nginx-logs mountPath: /log - name: fluentd-config mountPath: /etc/fluent volumes: - name: nginx-logs emptyDir: {} - name: fluentd-config configMap: name: fluentd-config --- apiVersion: v1 kind: ConfigMap metadata: namespace: fluentd name: fluentd-config data: fluent.conf: |- <source> type tail format none path /log/*.log pos_file /log/log.pos tag fluentd-nginx_log </source> <match fluentd-nginx_log**> @type kafka_buffered brokers 172.30.0.192:9092,172.30.0.193:9092,172.30.0.194:9092 default_topic fluentd-nginx_log output_data_type json buffer_type file buffer_path /log/fluentd-nginx.buffer </match> --- apiVersion: v1 kind: Service metadata: namespace: fluentd name: fluentd-nginx labels: k8s-app: fluentd-nginx spec: type: NodePort ports: - port: 80 protocol: TCP targetPort: 80 name: http nodePort: 32764 selector: k8s-app: fluentd-nginx
curl http://node/32765
查看kibana
zookeeper+kafka构建
1.搭建zookeeper
[root@cc-k8s01 zookeeper]# cd /opt/k8s/work/zookeeper/ [root@cc-k8s01 zookeeper]# tree build-img/ build-img/ ├── Dockerfile ├── zkGenConfig.sh ├── zkMetrics.sh ├── zkOk.sh └── zookeeper-3.4.10.tar.gz 0 directories, 5 files [root@cc-k8s01 zookeeper]# cd build-img/ [root@cc-k8s01 build-img]# ls Dockerfile zkGenConfig.sh zkMetrics.sh zkOk.sh zookeeper-3.4.10.tar.gz [root@cc-k8s01 build-img]# ll total 3088 -rw-r----- 1 root root 718 Aug 22 14:09 Dockerfile -rwxr-xr-x 1 root root 6204 Aug 22 14:09 zkGenConfig.sh -rwxr-xr-x 1 root root 885 Aug 22 14:09 zkMetrics.sh -rwxr-xr-x 1 root root 952 Aug 22 14:09 zkOk.sh -rw-r----- 1 root root 3137614 Aug 22 14:09 zookeeper-3.4.10.tar.gz
1.Dockerfile文件
FROM 172.30.21.232/base_images/alpine-3.9.4:openjre8 # customize LABEL versioin="zookeeper-3.4.10" MAINTAINER Pan <axx0718@163.com> USER root # customize ARG BASE_DIR=/opt/appl ARG DISTRO_NAME=zookeeper-3.4.10 ARG DISTRO_NAME_NOV=zookeeper #install application ADD $DISTRO_NAME.tar.gz zkGenConfig.sh zkOk.sh zkMetrics.sh ./ RUN mv zkGenConfig.sh zkOk.sh zkMetrics.sh $DISTRO_NAME/bin && \ mkdir -p $BASE_DIR/$DISTRO_NAME/data $BASE_DIR/$DISTRO_NAME/datalog $BASE_DIR/$DISTRO_NAME/log && \ ln -s $BASE_DIR/$DISTRO_NAME $BASE_DIR/$DISTRO_NAME_NOV && \ chown -R appl:appl $BASE_DIR ENV PATH=$PATH:$BASE_DIR/$DISTRO_NAME_NOV/bin \ ZOOCFGDIR=$BASE_DIR/$DISTRO_NAME_NOV/conf USER appl
2.zkGenConfig.sh
[root@cc-k8s01 build-img]# cat zkGenConfig.sh #!/usr/bin/env bash ZK_USER=${ZK_USER:-"appl"} ZK_USER_GROUP=${ZK_USER_GROUP:-"appl"} ZK_LOG_LEVEL=${ZK_LOG_LEVEL:-"INFO"} ZK_DATA_DIR=${ZK_DATA_DIR:-"/opt/appl/zookeeper/data"} ZK_DATA_LOG_DIR=${ZK_DATA_LOG_DIR:-"/opt/appl/zookeeper/datalog"} ZK_LOG_DIR=${ZK_LOG_DIR:-"/opt/appl/zookeeper/log"} ZK_CONF_DIR=${ZK_CONF_DIR:-"/opt/appl/zookeeper/conf"} ZK_CLIENT_PORT=${ZK_CLIENT_PORT:-2181} ZK_SERVER_PORT=${ZK_SERVER_PORT:-2888} ZK_ELECTION_PORT=${ZK_ELECTION_PORT:-3888} ZK_TICK_TIME=${ZK_TICK_TIME:-2000} ZK_INIT_LIMIT=${ZK_INIT_LIMIT:-10} ZK_SYNC_LIMIT=${ZK_SYNC_LIMIT:-5} ZK_HEAP_SIZE=${ZK_HEAP_SIZE:-2G} ZK_MAX_CLIENT_CNXNS=${ZK_MAX_CLIENT_CNXNS:-60} ZK_MIN_SESSION_TIMEOUT=${ZK_MIN_SESSION_TIMEOUT:- $((ZK_TICK_TIME*2))} ZK_MAX_SESSION_TIMEOUT=${ZK_MAX_SESSION_TIMEOUT:- $((ZK_TICK_TIME*20))} ZK_SNAP_RETAIN_COUNT=${ZK_SNAP_RETAIN_COUNT:-3} ZK_PURGE_INTERVAL=${ZK_PURGE_INTERVAL:-0} ID_FILE="$ZK_DATA_DIR/myid" ZK_CONFIG_FILE="$ZK_CONF_DIR/zoo.cfg" LOGGER_PROPS_FILE="$ZK_CONF_DIR/log4j.properties" JAVA_ENV_FILE="$ZK_CONF_DIR/java.env" HOST=`hostname -s` DOMAIN=`hostname -d` function print_servers() { for (( i=1; i<=$ZK_REPLICAS; i++ )) do echo "server.$i=$NAME-$((i-1)).$DOMAIN:$ZK_SERVER_PORT:$ZK_ELECTION_PORT" done } function validate_env() { echo "Validating environment" if [ -z $ZK_REPLICAS ]; then echo "ZK_REPLICAS is a mandatory environment variable" exit 1 fi if [[ $HOST =~ (.*)-([0-9]+)$ ]]; then NAME=${BASH_REMATCH[1]} ORD=${BASH_REMATCH[2]} else echo "Failed to extract ordinal from hostname $HOST" exit 1 fi MY_ID=$((ORD+1)) echo "ZK_REPLICAS=$ZK_REPLICAS" echo "MY_ID=$MY_ID" echo "ZK_LOG_LEVEL=$ZK_LOG_LEVEL" echo "ZK_DATA_DIR=$ZK_DATA_DIR" echo "ZK_DATA_LOG_DIR=$ZK_DATA_LOG_DIR" echo "ZK_LOG_DIR=$ZK_LOG_DIR" echo "ZK_CLIENT_PORT=$ZK_CLIENT_PORT" echo "ZK_SERVER_PORT=$ZK_SERVER_PORT" echo "ZK_ELECTION_PORT=$ZK_ELECTION_PORT" echo "ZK_TICK_TIME=$ZK_TICK_TIME" echo "ZK_INIT_LIMIT=$ZK_INIT_LIMIT" echo "ZK_SYNC_LIMIT=$ZK_SYNC_LIMIT" echo "ZK_MAX_CLIENT_CNXNS=$ZK_MAX_CLIENT_CNXNS" echo "ZK_MIN_SESSION_TIMEOUT=$ZK_MIN_SESSION_TIMEOUT" echo "ZK_MAX_SESSION_TIMEOUT=$ZK_MAX_SESSION_TIMEOUT" echo "ZK_HEAP_SIZE=$ZK_HEAP_SIZE" echo "ZK_SNAP_RETAIN_COUNT=$ZK_SNAP_RETAIN_COUNT" echo "ZK_PURGE_INTERVAL=$ZK_PURGE_INTERVAL" echo "ENSEMBLE" print_servers echo "Environment validation successful" } function create_config() { rm -f $ZK_CONFIG_FILE echo "Creating ZooKeeper configuration in $ZK_CONFIG_FILE" echo "clientPort=$ZK_CLIENT_PORT" >> $ZK_CONFIG_FILE echo "dataDir=$ZK_DATA_DIR" >> $ZK_CONFIG_FILE echo "dataLogDir=$ZK_DATA_LOG_DIR" >> $ZK_CONFIG_FILE echo "tickTime=$ZK_TICK_TIME" >> $ZK_CONFIG_FILE echo "initLimit=$ZK_INIT_LIMIT" >> $ZK_CONFIG_FILE echo "syncLimit=$ZK_SYNC_LIMIT" >> $ZK_CONFIG_FILE echo "maxClientCnxns=$ZK_MAX_CLIENT_CNXNS" >> $ZK_CONFIG_FILE echo "minSessionTimeout=$ZK_MIN_SESSION_TIMEOUT" >> $ZK_CONFIG_FILE echo "maxSessionTimeout=$ZK_MAX_SESSION_TIMEOUT" >> $ZK_CONFIG_FILE echo "autopurge.snapRetainCount=$ZK_SNAP_RETAIN_COUNT" >> $ZK_CONFIG_FILE echo "autopurge.purgeInteval=$ZK_PURGE_INTERVAL" >> $ZK_CONFIG_FILE if [ $ZK_REPLICAS -gt 1 ]; then print_servers >> $ZK_CONFIG_FILE fi echo "ZooKeeper configuration file written to $ZK_CONFIG_FILE" cat $ZK_CONFIG_FILE } function create_data_dirs() { if [ ! -d $ZK_DATA_DIR ]; then mkdir -p $ZK_DATA_DIR chown -R $ZK_USER:$ZK_USER_GROUP $ZK_DATA_DIR echo "Created ZooKeeper Data Directory" ls -ld $ZK_DATA_DIR >& 1 else echo "ZooKeeper Data Directory" ls -l -R $ZK_DATA_DIR >& 1 fi if [ ! -d $ZK_DATA_LOG_DIR ]; then mkdir -p $ZK_DATA_LOG_DIR chown -R $ZK_USER:$ZK_USER_GROUP $ZK_DATA_LOG_DIR echo "Created ZooKeeper Data Log Directory" ls -ld $ZK_DATA_LOG_DIR >& 1 else echo "ZooKeeper Data Log Directory" ls -l -R $ZK_DATA_LOG_DIR >& 1 fi if [ ! -d $ZK_LOG_DIR ]; then mkdir -p $ZK_LOG_DIR chown -R $ZK_USER:$ZK_USER_GROUP $ZK_LOG_DIR echo "Created ZooKeeper Log Directory" ls -ld $ZK_LOG_DIR >& 1 fi echo "Crateing ZooKeeper ensemble id file $ID_FILE" if [ ! -f $ID_FILE ]; then echo $MY_ID >> $ID_FILE fi echo "ZooKeeper ensemble id written to $ID_FILE" cat $ID_FILE } function create_log_props () { rm -f $LOGGER_PROPS_FILE echo "Creating ZooKeeper log4j configuration in $LOGGER_PROPS_FILE" echo "zookeeper.root.logger=ROLLINGFILE" >> $LOGGER_PROPS_FILE echo "zookeeper.console.threshold=$ZK_LOG_LEVEL" >> $LOGGER_PROPS_FILE echo "zookeeper.log.dir=$ZK_LOG_DIR" >> $LOGGER_PROPS_FILE echo "zookeeper.log.file=zookeeper.log" >> $LOGGER_PROPS_FILE echo "log4j.rootLogger=\${zookeeper.root.logger}" >> $LOGGER_PROPS_FILE echo "log4j.appender.ROLLINGFILE.Threshold=\${zookeeper.console.threshold}" >> $LOGGER_PROPS_FILE echo "log4j.appender.ROLLINGFILE=org.apache.log4j.DailyRollingFileAppender" >> $LOGGER_PROPS_FILE echo "log4j.appender.ROLLINGFILE.DatePattern='.'yyyy-MM-dd" >> $LOGGER_PROPS_FILE echo "log4j.appender.ROLLINGFILE.File=\${zookeeper.log.dir}/\${zookeeper.log.file}" >> $LOGGER_PROPS_FILE echo "log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout" >> $LOGGER_PROPS_FILE echo "log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n" >> $LOGGER_PROPS_FILE echo "Wrote log4j configuration to $LOGGER_PROPS_FILE" cat $LOGGER_PROPS_FILE } function create_java_env() { rm -f $JAVA_ENV_FILE echo "Creating JVM configuration file $JAVA_ENV_FILE" echo "ZOO_LOG_DIR=$ZK_LOG_DIR" >> $JAVA_ENV_FILE echo "ZOO_LOG4J_PROP=$ZK_LOG_LEVEL,ROLLINGFILE" >> $JAVA_ENV_FILE echo "JVMFLAGS=\"-Xmx$ZK_HEAP_SIZE -Xms$ZK_HEAP_SIZE\"" >> $JAVA_ENV_FILE echo "Wrote JVM configuration to $JAVA_ENV_FILE" cat $JAVA_ENV_FILE } validate_env && create_config && create_log_props && create_data_dirs && create_java_env
3.zkMetrics.sh
[root@cc-k8s01 build-img]# cat zkMetrics.sh #!/usr/bin/env bash # Copyright 2016 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # zkMetrics uses the mntr four letter word to retrieve the ZooKeeper # instances metrics and dump them to standard out. If the ZK_CLIENT_PORT # enviorment varibale is not set 2181 is used. ZK_CLIENT_PORT=${ZK_CLIENT_PORT:-2181} echo mntr | nc localhost $ZK_CLIENT_PORT >& 1
4.zkOk.sh
[root@cc-k8s01 build-img]# cat zkOk.sh #!/usr/bin/env bash # Copyright 2016 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # zkOk.sh uses the ruok ZooKeeper four letter work to determine if the instance # is health. The $? variable will be set to 0 if server responds that it is # healthy, or 1 if the server fails to respond. ZK_CLIENT_PORT=${ZK_CLIENT_PORT:-2181} OK=$(echo ruok | nc 127.0.0.1 $ZK_CLIENT_PORT) if [ "$OK" == "imok" ]; then exit 0 else exit 1
5.利用dockerfile文件构建zookeeper基础镜像
6.创建configMap文件
[root@cc-k8s01 zookeeper]# cat configMap.yml apiVersion: v1 kind: ConfigMap metadata: name: zk-config namespace: cmw data: replicas: "3" jvm.heap: "1024M" tick: "2000" init: "10" sync: "5" client.cnxns: "60" snap.retain: "3" purge.interval: "1"
7.创建service/statefulSet文件
[root@cc-k8s01 zookeeper]# cat podDisruptionBudget.yml apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: zk-budget namespace: cmw spec: selector: matchLabels: app: zk minAvailable: 2 [root@cc-k8s01 zookeeper]# cat statefulSet.yml apiVersion: apps/v1 kind: StatefulSet metadata: name: zk namespace: cmw spec: serviceName: zk-headless replicas: 3 selector: matchLabels: app: zk template: metadata: labels: app: zk annotations: pod.alpha.kubernetes.io/initialized: "true" spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: "app" operator: In values: - zk-headless topologyKey: "kubernetes.io/hostname" containers: - name: k8szk imagePullPolicy: IfNotPresent image: 172.30.21.232/cmw/zookeeper:3.4.10 #image: 172.30.21.232/cmw/zookeeper:0.0.1 resources: limits: memory: 1300Mi cpu: 1 ports: - containerPort: 2181 name: client - containerPort: 2888 name: server - containerPort: 3888 name: leader-election env: - name : ZK_REPLICAS valueFrom: configMapKeyRef: name: zk-config key: replicas - name : ZK_HEAP_SIZE valueFrom: configMapKeyRef: name: zk-config key: jvm.heap - name : ZK_TICK_TIME valueFrom: configMapKeyRef: name: zk-config key: tick - name : ZK_INIT_LIMIT valueFrom: configMapKeyRef: name: zk-config key: init - name : ZK_SYNC_LIMIT valueFrom: configMapKeyRef: name: zk-config key: tick - name : ZK_MAX_CLIENT_CNXNS valueFrom: configMapKeyRef: name: zk-config key: client.cnxns - name: ZK_SNAP_RETAIN_COUNT valueFrom: configMapKeyRef: name: zk-config key: snap.retain - name: ZK_PURGE_INTERVAL valueFrom: configMapKeyRef: name: zk-config key: purge.interval - name: ZK_CLIENT_PORT value: "2181" - name: ZK_SERVER_PORT value: "2888" - name: ZK_ELECTION_PORT value: "3888" command: - sh - -c - zkGenConfig.sh && zkServer.sh start-foreground readinessProbe: exec: command: - "zkOk.sh" initialDelaySeconds: 15 timeoutSeconds: 5 livenessProbe: exec: command: - "zkOk.sh" initialDelaySeconds: 15 timeoutSeconds: 5 volumeMounts: - name: datadir mountPath: /opt/appl/zookeeper/data - name: datalogdir mountPath: /opt/appl/zookeeper/datalog - name: logdir mountPath: /opt/appl/zookeeper/log securityContext: runAsUser: 1500 fsGroup: 1500 volumeClaimTemplates: - metadata: name: datadir namespace: cmw spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi - metadata: name: datalogdir namespace: cmw spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi - metadata: name: logdir namespace: cmw spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi [root@cc-k8s01 zookeeper]# cat service.yml apiVersion: v1 kind: Service metadata: name: zk-headless namespace: cmw labels: app: zk-headless spec: ports: - port: 2888 name: server - port: 3888 name: leader-election clusterIP: None selector: app: zk --- apiVersion: v1 kind: Service metadata: name: zk-service namespace: cmw labels: app: zk-service spec: ports: - port: 2181 name: client selector: app: zk
8.创建zookeeper
[root@cc-k8s01 zookeeper]# kubectl apply -f . error: the path ".\x03" does not exist [root@cc-k8s01 zookeeper]# kubectl get pods -n cmw NAME READY STATUS RESTARTS AGE nginx-test-59d56bd49c-27ksv 2/2 Running 0 7d1h nginx-test-59d56bd49c-jjmlv 2/2 Running 0 7d1h nginx-test-59d56bd49c-r9khr 2/2 Running 0 7d1h redis-docker-test-65d6c8477f-gsx8c 1/1 Running 0 7d5h redis-master-0 1/1 Running 0 12d redis-sentinel-0 1/1 Running 0 12d redis-sentinel-1 1/1 Running 0 8d redis-sentinel-2 1/1 Running 0 12d zk-0 1/1 Running 0 47h zk-1 1/1 Running 0 47h zk-2 1/1 Running 0 47h
2.搭建kafka
[root@cc-k8s01 kafka]# cd /opt/k8s/work/kafka/ [root@cc-k8s01 kafka]# tree . ├── build-img │ ├── Dockerfile │ ├── Dockerfile.bak │ ├── kafka_2.11-2.1.1.tgz │ └── log4j.properties ├── kafka.sh ├── PodDisruptionBudget.yaml ├── service.yaml └── statefulset.yaml
Dockerfile文件,以及log4j文件
[root@cc-k8s01 build-img]# cat Dockerfile FROM 172.30.21.232/base_images/alpine-3.9.4:openjre8 # customize MAINTAINER pan <axx0718@163.com> USER root ENV KAFKA_USER=appl \ KAFKA_DATA_DIR=/opt/appl/kafka/data \ # JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 \ KAFKA_HOME=/opt/appl/kafka \ PATH=$PATH:/opt/appl/kafka/bin ARG KAFKA_VERSION=2.11-2.1.1 ARG KAFKA_DIST=kafka_2.11-2.1.1 WORKDIR /opt/appl COPY $KAFKA_DIST.tgz /opt/appl RUN tar -xzf $KAFKA_DIST.tgz && \ rm -rf $KAFKA_DIST.tgz COPY log4j.properties /opt/appl/$KAFKA_DIST/config/ RUN set -x && \ ln -s /opt/appl/$KAFKA_DIST $KAFKA_HOME && \ mkdir -p $KAFKA_DATA_DIR && \ chown -R appl:appl /opt/appl/$KAFKA_DIST && \ chown -R appl:appl $KAFKA_DATA_DIR USER appl [root@cc-k8s01 build-img]# cat log4j.properties # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. log4j.rootLogger=${logging.level}, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[%d] %p %m (%c)%n log4j.appender.kafkaAppender=org.apache.log4j.DailyRollingFileAppender log4j.appender.kafkaAppender.DatePattern='.'yyyy-MM-dd-HH log4j.appender.kafkaAppender.File=${kafka.logs.dir}/server.log log4j.appender.kafkaAppender.layout=org.apache.log4j.PatternLayout log4j.appender.kafkaAppender.layout.ConversionPattern=[%d] %p %m (%c)%n log4j.appender.stateChangeAppender=org.apache.log4j.DailyRollingFileAppender log4j.appender.stateChangeAppender.DatePattern='.'yyyy-MM-dd-HH log4j.appender.stateChangeAppender.File=${kafka.logs.dir}/state-change.log log4j.appender.stateChangeAppender.layout=org.apache.log4j.PatternLayout log4j.appender.stateChangeAppender.layout.ConversionPattern=[%d] %p %m (%c)%n log4j.appender.requestAppender=org.apache.log4j.DailyRollingFileAppender log4j.appender.requestAppender.DatePattern='.'yyyy-MM-dd-HH log4j.appender.requestAppender.File=${kafka.logs.dir}/kafka-request.log log4j.appender.requestAppender.layout=org.apache.log4j.PatternLayout log4j.appender.requestAppender.layout.ConversionPattern=[%d] %p %m (%c)%n log4j.appender.cleanerAppender=org.apache.log4j.DailyRollingFileAppender log4j.appender.cleanerAppender.DatePattern='.'yyyy-MM-dd-HH log4j.appender.cleanerAppender.File=${kafka.logs.dir}/log-cleaner.log log4j.appender.cleanerAppender.layout=org.apache.log4j.PatternLayout log4j.appender.cleanerAppender.layout.ConversionPattern=[%d] %p %m (%c)%n log4j.appender.controllerAppender=org.apache.log4j.DailyRollingFileAppender log4j.appender.controllerAppender.DatePattern='.'yyyy-MM-dd-HH log4j.appender.controllerAppender.File=${kafka.logs.dir}/controller.log log4j.appender.controllerAppender.layout=org.apache.log4j.PatternLayout log4j.appender.controllerAppender.layout.ConversionPattern=[%d] %p %m (%c)%n log4j.appender.authorizerAppender=org.apache.log4j.DailyRollingFileAppender log4j.appender.authorizerAppender.DatePattern='.'yyyy-MM-dd-HH log4j.appender.authorizerAppender.File=${kafka.logs.dir}/kafka-authorizer.log log4j.appender.authorizerAppender.layout=org.apache.log4j.PatternLayout log4j.appender.authorizerAppender.layout.ConversionPattern=[%d] %p %m (%c)%n # Turn on all our debugging info #log4j.logger.kafka.producer.async.DefaultEventHandler=DEBUG, kafkaAppender #log4j.logger.kafka.client.ClientUtils=DEBUG, kafkaAppender #log4j.logger.kafka.perf=DEBUG, kafkaAppender #log4j.logger.kafka.perf.ProducerPerformance$ProducerThread=DEBUG, kafkaAppender #log4j.logger.org.I0Itec.zkclient.ZkClient=DEBUG #log4j.logger.kafka=INFO, stdout log4j.logger.kafka.network.RequestChannel$=WARN, stdout log4j.additivity.kafka.network.RequestChannel$=false #log4j.logger.kafka.network.Processor=TRACE, requestAppender #log4j.logger.kafka.server.KafkaApis=TRACE, requestAppender #log4j.additivity.kafka.server.KafkaApis=false log4j.logger.kafka.request.logger=WARN, stdout log4j.additivity.kafka.request.logger=false log4j.logger.kafka.controller=TRACE, stdout log4j.additivity.kafka.controller=false log4j.logger.kafka.log.LogCleaner=INFO, stdout log4j.additivity.kafka.log.LogCleaner=false log4j.logger.state.change.logger=TRACE, stdout log4j.additivity.state.change.logger=false #Change this to debug to get the actual audit log for authorizer. log4j.logger.kafka.authorizer.logger=WARN, stdout log4j.additivity.kafka.authorizer.logger=false
kafka pod PodDisruptionBudget、statefulset、service文件
[root@cc-k8s01 kafka]# cat PodDisruptionBudget.yaml apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: namespace: cmw name: kafka-pdb spec: selector: matchLabels: app: kafka minAvailable: 2
[root@cc-k8s01 kafka]# cat PodDisruptionBudget.yaml apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: namespace: cmw name: kafka-pdb spec: selector: matchLabels: app: kafka minAvailable: 2 [root@cc-k8s01 kafka]# cat statefulset.yaml apiVersion: apps/v1beta1 kind: StatefulSet metadata: namespace: cmw name: kafka spec: serviceName: kafka-svc replicas: 3 template: metadata: labels: app: kafka spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: "app" operator: In values: - kafka topologyKey: "kubernetes.io/hostname" podAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 podAffinityTerm: labelSelector: matchExpressions: - key: "app" operator: In values: - zk topologyKey: "kubernetes.io/hostname" terminationGracePeriodSeconds: 300 containers: - name: k8skafka imagePullPolicy: IfNotPresent image: 172.30.21.232/gcr.io/kafka-v2.11:v1.1 resources: requests: memory: "2Gi" cpu: 500m ports: - containerPort: 9092 name: server command: #--override zookeeper.connect=zk-service.cmw:2181 \ - sh - -c - "exec /opt/appl/kafka/bin/kafka-server-start.sh /opt/appl/kafka/config/server.properties --override broker.id=${HOSTNAME##*-} \ --override listeners=PLAINTEXT://:9092 \ --override zookeeper.connect=zk-0.zk-headless.cmw.svc.cluster.local:2181,zk-1.zk-headless.cmw.svc.cluster.local:2181,zk-2.zk-headless.cmw.svc.cluster.local:2181 \ --override log.dir=/opt/appl/kafka/logs \ --override auto.create.topics.enable=true \ --override auto.leader.rebalance.enable=true \ --override background.threads=10 \ --override compression.type=producer \ --override delete.topic.enable=false \ --override leader.imbalance.check.interval.seconds=300 \ --override leader.imbalance.per.broker.percentage=10 \ --override log.flush.interval.messages=9223372036854775807 \ --override log.flush.offset.checkpoint.interval.ms=60000 \ --override log.flush.scheduler.interval.ms=9223372036854775807 \ --override log.retention.bytes=-1 \ --override log.retention.hours=168 \ --override log.roll.hours=168 \ --override log.roll.jitter.hours=0 \ --override log.segment.bytes=1073741824 \ --override log.segment.delete.delay.ms=60000 \ --override message.max.bytes=1000012 \ --override min.insync.replicas=1 \ --override num.io.threads=8 \ --override num.network.threads=3 \ --override num.recovery.threads.per.data.dir=1 \ --override num.replica.fetchers=1 \ --override offset.metadata.max.bytes=4096 \ --override offsets.commit.required.acks=-1 \ --override offsets.commit.timeout.ms=5000 \ --override offsets.load.buffer.size=5242880 \ --override offsets.retention.check.interval.ms=600000 \ --override offsets.retention.minutes=1440 \ --override offsets.topic.compression.codec=0 \ --override offsets.topic.num.partitions=50 \ --override offsets.topic.replication.factor=3 \ --override offsets.topic.segment.bytes=104857600 \ --override queued.max.requests=500 \ --override quota.consumer.default=9223372036854775807 \ --override quota.producer.default=9223372036854775807 \ --override replica.fetch.min.bytes=1 \ --override replica.fetch.wait.max.ms=500 \ --override replica.high.watermark.checkpoint.interval.ms=5000 \ --override replica.lag.time.max.ms=10000 \ --override replica.socket.receive.buffer.bytes=65536 \ --override replica.socket.timeout.ms=30000 \ --override request.timeout.ms=30000 \ --override socket.receive.buffer.bytes=102400 \ --override socket.request.max.bytes=104857600 \ --override socket.send.buffer.bytes=102400 \ --override unclean.leader.election.enable=true \ --override zookeeper.session.timeout.ms=6000 \ --override zookeeper.set.acl=false \ --override broker.id.generation.enable=true \ --override connections.max.idle.ms=600000 \ --override controlled.shutdown.enable=true \ --override controlled.shutdown.max.retries=3 \ --override controlled.shutdown.retry.backoff.ms=5000 \ --override controller.socket.timeout.ms=30000 \ --override default.replication.factor=1 \ --override fetch.purgatory.purge.interval.requests=1000 \ --override group.max.session.timeout.ms=300000 \ --override group.min.session.timeout.ms=6000 \ --override inter.broker.protocol.version=0.11.0-IV2 \ --override log.cleaner.backoff.ms=15000 \ --override log.cleaner.dedupe.buffer.size=134217728 \ --override log.cleaner.delete.retention.ms=86400000 \ --override log.cleaner.enable=true \ --override log.cleaner.io.buffer.load.factor=0.9 \ --override log.cleaner.io.buffer.size=524288 \ --override log.cleaner.io.max.bytes.per.second=1.7976931348623157E308 \ --override log.cleaner.min.cleanable.ratio=0.5 \ --override log.cleaner.min.compaction.lag.ms=0 \ --override log.cleaner.threads=1 \ --override log.cleanup.policy=delete \ --override log.index.interval.bytes=4096 \ --override log.index.size.max.bytes=10485760 \ --override log.message.timestamp.difference.max.ms=9223372036854775807 \ --override log.message.timestamp.type=CreateTime \ --override log.preallocate=false \ --override log.retention.check.interval.ms=300000 \ --override max.connections.per.ip=2147483647 \ --override num.partitions=1 \ --override producer.purgatory.purge.interval.requests=1000 \ --override replica.fetch.backoff.ms=1000 \ --override replica.fetch.max.bytes=1048576 \ --override replica.fetch.response.max.bytes=10485760 \ --override reserved.broker.max.id=1000 " env: - name: KAFKA_HEAP_OPTS value : "-Xmx512M -Xms512M" - name: KAFKA_OPTS value: "-Dlogging.level=INFO" volumeMounts: - name: datadir mountPath: /tmp/kafka-logs #readinessProbe: # exec: # command: # - sh # - -c # - "/opt/appl/kafka/bin/kafka-broker-api-versions.sh --bootstrap-server=localhost:9092" securityContext: runAsUser: 1500 fsGroup: 1500 volumeClaimTemplates: - metadata: name: datadir namespace: cmw spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 5Gi
[root@cc-k8s01 kafka]# cat service.yaml apiVersion: v1 kind: Service metadata: name: kafka-svc namespace: cmw labels: app: kafka spec: ports: - port: 9092 name: server clusterIP: None selector: app: kafka
3.创建logstash
[root@cc-k8s01 logstash]# tree . ├── build-img │ ├── Dockerfile │ ├── kafka-output.conf │ ├── logstash-6.8.1.tar.gz │ └── start.sh ├── configMap-logstash.yaml ├── configMap-logstash.yaml.bak ├── deployment.yaml └── logstash.sh
[root@cc-k8s01 build-img]# vim Dockerfile FROM 172.30.21.232/base_images/alpine-3.9.4:openjre8 # customize LABEL versioin="logstash-6.8.1" MAINTAINER pan <axx0718@163.com> USER root # customize ENV LOGSTASH_USER=appl \ LOGSTASH_LOGS_DIR=/opt/appl/logstash/logs \ LOGSTASH_HOME=/opt/appl/logstash \ PATH=$PATH:/opt/appl/logstash/bin \ BASE_DIR=/opt/appl \ DISTRO_NAME=logstash-6.8.1 \ DISTRO_NAME_NOV=logstash COPY $DISTRO_NAME.tar.gz /opt/appl RUN cd /opt/appl && \ tar -xvf $DISTRO_NAME.tar.gz && \ rm -rf $DISTRO_NAME.tar.gz && \ ln -s /opt/appl/$DISTRO_NAME $LOGSTASH_HOME && \ cd $LOGSTASH_HOME && \ mkdir conf.d && \ mkdir logs && \ mkdir patterns COPY jvm.options $LOGSTASH_HOME/config RUN set -x && \ chown -R appl:appl /opt/appl/$DISTRO_NAME USER appl #CMD ["/opt/appl/logstash/start.sh"]
[root@cc-k8s01 build-img]# cat jvm.options ## JVM configuration # Xms represents the initial size of total heap space # Xmx represents the maximum size of total heap space -Xms2g -Xmx2g ################################################################ ## Expert settings ################################################################ ## ## All settings below this section are considered ## expert settings. Don't tamper with them unless ## you understand what you are doing ## ################################################################ ## GC configuration -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly ## Locale # Set the locale language #-Duser.language=en # Set the locale country #-Duser.country=US # Set the locale variant, if any #-Duser.variant= ## basic # set the I/O temp directory #-Djava.io.tmpdir=$HOME # set to headless, just in case -Djava.awt.headless=true # ensure UTF-8 encoding by default (e.g. filenames) -Dfile.encoding=UTF-8 # use our provided JNA always versus the system one #-Djna.nosys=true # Turn on JRuby invokedynamic -Djruby.compile.invokedynamic=true # Force Compilation -Djruby.jit.threshold=0 # Make sure joni regexp interruptability is enabled -Djruby.regexp.interruptible=true ## heap dumps # generate a heap dump when an allocation from the Java heap fails # heap dumps are created in the working directory of the JVM -XX:+HeapDumpOnOutOfMemoryError # specify an alternative path for heap dumps # ensure the directory exists and has sufficient space #-XX:HeapDumpPath=${LOGSTASH_HOME}/heapdump.hprof ## GC logging #-XX:+PrintGCDetails #-XX:+PrintGCTimeStamps #-XX:+PrintGCDateStamps #-XX:+PrintClassHistogram #-XX:+PrintTenuringDistribution #-XX:+PrintGCApplicationStoppedTime # log GC status to a file with time stamps # ensure the directory exists #-Xloggc:${LS_GC_LOG_FILE} # Entropy source for randomness -Djava.security.egd=file:/dev/urandom
logstash configmap文件,包含了logstash正则表达式
[root@cc-k8s01 logstash-7.3.0]# cat configMap-logstash.yaml apiVersion: v1 kind: ConfigMap metadata: name: test-access namespace: cmw data: pattern: |- DATETIME %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})? LogType ([a-zA-Z]+) REQUEST \[%{TIMESTAMP:log_timestamp}\] \[%{DATA:ThreadName}\] \[%{LOGLEVEL:logLevel}\] \[%{LogType:InterfaceTag}\] \[%{DATA:TransactionId}\]?\[%{DATA:SpanId}\]?\[%{DATA:ParentId}\] \[%{DATA:HttpUrl}\] \[%{DATA:Protocol}\] \[%{DATA:logtype}\] %{DATA:Number} - %{GREEDYDATA:JsonContent} RESPONSE \[%{TIMESTAMP:log_timestamp}\] \[%{DATA:ThreadName}\] \[%{LOGLEVEL:logLevel}\] \[%{LogType:InterfaceTag}\] \[%{DATA:TransactionId}\]?\[%{DATA:SpanId}\]?\[%{DATA:ParentId}\] \[%{DATA:HttpUrl}\] \[%{DATA:Protocol}\] \[%{DATA:logtype}\] %{DATA:Number} - %{GREEDYDATA:JsonContent} ACCESSLOG \[%{DATETIME:logTime}\] \[%{DATA:threadName}\] \[%{DATA:loglevel}\] \[%{DATA:InterfaceTag}\] \[%{DATA:interfaceTag}\] \[%{DATA:transactionId}\] \[%{DATA:SpanId}\] \[%{DATA:ParentId}\] \[%{DATA:ServerId}\] \[%{DATA:logtype}\] - %{GREEDYDATA:AccessJson} INTERFACELOG \[%{DATETIME:log_timestamp}\] \[%{DATA:ThreadName}\] \[%{LOGLEVEL:logLevel}\] \[%{DATA:InterfaceTag}\] \[%{DATA:TransactionId}\] \[%{DATA:SpanId}\] \[%{DATA:ParentId}\] \[%{DATA:HttpUrl}\] \[%{DATA:Protocol}\]?\[%{DATA:logtype}\] - %{GREEDYDATA:JsonContent} kafka-output.conf: |- input { kafka { bootstrap_servers => "kafka-svc.cmw.svc.cluster.local:9092" topics => ["nginx-access"] codec => "json" } } filter { if [fields][log_topics] =~ "\w+-access" { if [message] =~ '^{\w*' { json { source => "message" add_field => ["type", "%{[fields][log_topics]}"] } } else { grok { patterns_dir => [ "/opt/appl/logstash/patterns" ] match => { "message" => "%{ACCESSLOG}" } } json { source => "AccessJson" remove_field => "AccessJson" add_field => ["type", "%{[fields][log_topics]}"] } } } ruby { code => "event.timestamp.time.localtime" } #if [startTime] { # date { # match => [ "startTime", "YYYY-MM-dd HH:mm:ss.SSS" ] # timezone => "Asia/Shanghai" # target => "@timestamp" # } #} if [timeDiff] { mutate { add_field => [ "timeDiff_int", "%{timeDiff}" ] } mutate { convert => [ "timeDiff_int", "integer" ] remove_field => "timeDiff" } } } output { elasticsearch { hosts => ["http://elasticsearch.cmw:9200" ] index => "logstash-k8s-%{[fields][log_topics]}-%{+YYYY.MM.dd}" template_overwrite => true #user => "elastic" #password => "xri&h2azaj" #document_type => "%{[type]}" } }
apiVersion: apps/v1 kind: Deployment metadata: name: logstash-test-access namespace: ops labels: app: logstash-test spec: replicas: 1 selector: matchLabels: app: logstash-test template: metadata: labels: app: logstash-test spec: containers: - name: test-access imagePullPolicy: IfNotPresent image: harbor.iboxpay.com/ops/logstash:6.8.1 volumeMounts: - name: test-access mountPath: /opt/appl/logstash/conf.d/kafka-output.conf subPath: kafka-output.conf - name: test-access mountPath: /opt/appl/logstash/patterns/pattern subPath: pattern resources: requests: memory: "2Gi" cpu: "1" limits: memory: "3Gi" cpu: "2" command: - sh - -c #- "/opt/appl/logstash/start.sh" - "/opt/appl/logstash/bin/logstash -f /opt/appl/logstash/conf.d" volumes: - name: test-access configMap: name: test-access items: - key: kafka-output.conf path: kafka-output.conf - key: pattern path: pattern securityContext: runAsUser: 1500 fsGroup: 1500
4.将外部服务elasticsearch,提供一个endpoint、service供集群使用
[root@cc-k8s01 es]# cd /opt/k8s/work/es/[root@cc-k8s01 es]# cat *kind: Endpoints apiVersion: v1 metadata: name: elasticsearch namespace: cmw subsets: - addresses: - ip: 172.30.21.232 - ip: 172.30.21.233 - ip: 172.30.21.234 ports: - port: 9200 apiVersion: v1 kind: Service metadata: name: elasticsearch namespace: cmw spec: ports: - port: 9200
来源:https://www.cnblogs.com/jcici/p/11353283.html