k8s 中部署elfk

实验要求

1. 部署一套elk
2. 部署两个Web pod,nginx和tomcat。
3. 在两个Web pod 中部署一个sidecar(边车服务),来收集nginx和tomcat的日志到es中并在kibana中展示

实验前置

1. 数据持久化部分,使用动态存储类,NFS或者ceph均可,本次使用172.27.9.50 公共的NFS
2. 镜像仓库使用172.27.9.50公共仓库

实验过程

1. 为了实现ELK有状态服务的部署,需要准备数据持久化相关工作(部署供应商、动态存储类)
2. 部署ES集群
3. 部署kibana
4. 部署logstash组件
5. 部署2个的Web 中间件。同时带有filebeat边车服务
6. 创建定期删除ES中索引的cronjob pod 同时编写 索引删除脚本

创建在部署NFS供应商时使用的harbor服务器的密钥(默认命名空间)

    kubectl create secret docker-registry \
    harbor-secret \
    --docker-server=172.27.9.50 \
    --docker-username=admin \
    --docker-password=Harbor12345
    

部署NFS供应商

# 创建SA账户并对 sa 授权
kubectl create serviceaccount nfs-provisioner
kubectl create clusterrolebinding nfs-provisioner --clusterrole=cluster-admin --serviceaccount=default:nfs-provisioner
# 安装 nfs-provisioner 程序
cat > ./nfs-deployment.yaml << EOF
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nfs-provisioner
spec:
  selector:
    matchLabels:
c       app: nfs-provisioner
  replicas: 1
  template:
    metadata:
      labels:
        app: nfs-provisioner
    spec:
      serviceAccount: nfs-provisioner
      imagePullSecrets:
      - name: harbor-secret
      containers:
        - name: nfs-provisioner
          image: 172.27.9.50/base/mydlq/nfs-subdir-external-provisioner:v4.0.0
          imagePullPolicy: IfNotPresent
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: bw-wg.com/nfs
            - name: NFS_SERVER
              value: 172.27.9.50
            - name: NFS_PATH
              value: /mnt/nfs_pro
      volumes:
        - name: nfs-client-root
          nfs:
            server: 172.27.9.50
            path: /mnt/nfs_pro
EOF
​
# 创建动态类
​
cat > ./sc.yaml << EOF
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: nfs-storageclass
provisioner: bw-wg.com/nfs
reclaimPolicy: Retain
EOF
​

部署ES集群

# 创建一个命名空间,并为要开启x-pace所以要准备出一个P12证书文件
kubectl create ns log
kubectl create configmap -n log elastic-certificates --from-file=elastic-certificates.p12=elastic-certificates.p12

# 创建log命名空间下的 secret
kubectl create secret docker-registry \
harbor-secret \
--docker-server=172.27.9.50 \
--docker-username=admin \
--docker-password=Harbor12345 -n log

# 部署ES 有状态服务
cat > ./elasticsearch-sts.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
  namespace: log
  labels:
    app: elasticsearch
spec:
  selector:
    app: elasticsearch
  ports:
  - name: api
    port: 9200
  - name: discovery
    port: 9300
  clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch
  namespace: log
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      initContainers:
      - name: fix-permissions
        image: 172.27.9.50/base/busybox:1.28.3
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
        securityContext:
          privileged: true
        volumeMounts:
        - name: es-data
          mountPath: /usr/share/elasticsearch/data
      - name: increase-vm-max-map
        image: 172.27.9.50/base/busybox:1.28.3
        imagePullPolicy: IfNotPresent
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: 172.27.9.50/base/busybox:1.28.3
        imagePullPolicy: IfNotPresent
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true
      containers:
      - name: elasticsearch
        image: 172.27.9.50/elfk/elasticsearch:7.17.7
        imagePullPolicy: IfNotPresent
        env:
        - name: "cluster.name"
          value: "elk"
        - name: "node.master"
          value: "true"
        - name: "node.data"
          value: "true"
        - name: "http.host"
          value: "0.0.0.0"
        - name: "network.host"
          value: "_eth0_"
        - name: node.name
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: "bootstrap.memory_lock"
          value: "false"
        - name: "http.port"
          value: "9200"
        - name: "transport.tcp.port"
          value: "9300"
        - name: "discovery.seed_hosts"
          value: "elasticsearch"
        - name: "cluster.initial_master_nodes"
          value: "elasticsearch-0,elasticsearch-1,elasticsearch-2"
        - name: "discovery.seed_resolver.timeout"
          value: "10s"
        - name: "discovery.zen.minimum_master_nodes"
          value: "2"
        - name: "gateway.recover_after_nodes"
          value: "3"
        - name: "http.cors.enabled"
          value: "true"
        - name: "http.cors.allow-origin"
          value: "*"
        - name: "http.cors.allow-methods"
          value: "OPTIONS, HEAD, GET, POST, PUT, DELETE"
        - name: "http.cors.allow-headers"
          value: "kbn-version,kbn-xsrf,Origin,X-Requested-With,Content-Type,Accept,Engaged-Auth-Token,Content-Length,Authorization"
        - name: "http.cors.allow-credentials"
          value: "true"
        - name: "xpack.security.enabled"
          value: "true"
        - name: "xpack.security.transport.ssl.enabled"
          value: "true"
        - name: "xpack.security.transport.ssl.verification_mode"
          value: "certificate"
        - name: "xpack.security.transport.ssl.keystore.path"
          value: "elastic-certificates.p12"
        - name: "xpack.security.transport.ssl.truststore.path"
          value: "elastic-certificates.p12"
        - name: "ES_JAVA_OPTS"
          value: "-Xms512m -Xmx512m"
        ports:
        - containerPort: 9200
          name: api
          protocol: TCP
        - containerPort: 9300
          name: discovery
          protocol: TCP
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 500m
        volumeMounts:
        - name: cert
          mountPath: /usr/share/elasticsearch/config/elastic-certificates.p12
          subPath: elastic-certificates.p12
          readOnly: true
        - name: es-data
          mountPath: /usr/share/elasticsearch/data
      imagePullSecrets:
      - name: harbor-secret
      volumes:
      - name: cert
        configMap:
          name: elastic-certificates
  volumeClaimTemplates:
  - metadata:
      name: es-data   
      namespace: log
    spec:  
      accessModes: [ "ReadWriteMany" ]
      storageClassName: "nfs-storageclass"
      resources:
        requests:
          storage: 10Gi
EOF

# 初始化密码

kubectl exec -it -n log elasticsearch-0 -- bash
/usr/share/elasticsearch/bin/elasticsearch-setup-passwords interactive   

部署kibana

cat > kibana-config.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: kibana-config
  namespace: log
data:
  kibana.yml: |
    server.port: 5601
    server.host: "0"
    kibana.index: ".kibana"
    elasticsearch.hosts: ["http://elasticsearch.log:9200"]
    elasticsearch.username: elastic
    elasticsearch.password: abc123
    i18n.locale: "zh-CN"
EOF
​
cat > kibana.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: kibana
  namespace: log
  labels:
    app: kibana
spec:
  type: NodePort
  selector:
    app: kibana
  ports:
  - port: 5601
    protocol: TCP
    targetPort: 5601
​
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
  namespace: log
  labels:
    app: kibana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      imagePullSecrets:
      - name: harbor-secret
      containers:
      - name: kibana
        image: 172.27.9.50/elfk/kibana:7.17.7
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5601
        resources:
          limits:
            cpu: 500m
            memory: 500Mi
          requests:
            cpu: 500m
            memory: 500Mi
        volumeMounts:
        - name: config
          mountPath: /usr/share/kibana/config
      volumes:
      - name: config
        configMap:
          name: kibana-config
EOF

部署logstash组件

# 生成logstash的配置文件
cat > ./logstash-conf.yaml << EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: logstash-config
  namespace: log
data:
  logstash.yml: |
    http.host: "0"
    http.port: 9600
    path.config: /usr/share/logstash/pipeline
    config.reload.automatic: true
    xpack.monitoring.enabled: true
    xpack.monitoring.elasticsearch.username: elastic
    xpack.monitoring.elasticsearch.password: abc123
    xpack.monitoring.elasticsearch.hosts: ["http://elasticsearch:9200"]
    xpack.monitoring.collection.interval: 10s

  logstash.conf: |
    input {
      beats {
        port => 5040
      }
    }

    filter {
      if [type] == "nginx_access" {
        grok {
            match => { "message" => "%{COMBINEDAPACHELOG}" }
        }
      }
      if [type] == "tomcat_access" {
        grok {
            match => { "message" => "(?<timestamp>%{MONTHDAY}-%{MONTH}-%{YEAR} %{TIME}) %{LOGLEVEL:level} \[%{DATA:exception_info}\] %{GREEDYDATA:message}" }
        }
      }
    }

    output {
      if [type] == "nginx_access" {
        elasticsearch {
          hosts => ["elasticsearch:9200"]
          user => "elastic"
          password => "abc123"
          index => "nginx-log-%{+YYYY.MM.dd}"
        }
      }
      if [type] == "tomcat_access" {
        elasticsearch {
          hosts => ["elasticsearch:9200"]
          user => "elastic"
          password => "abc123"
          index => "tomcat-log-%{+YYYY.MM.dd}"
        }
      }
    }
EOF

# 生成logstash的部署文件
cat > logstash.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: logstash
  namespace: log
spec:
  selector:
    app: logstash
  ports:
  - protocol: TCP
    port: 5040
    nodePort: 30040
  type: NodePort

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: logstash
  namespace: log
spec:
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  selector:
    matchLabels:
      app: logstash
  template:
    metadata:
      labels:
        app: logstash
    spec:
      imagePullSecrets:
      - name: harbor-secret
      containers:
      - name: logstash
        image: 172.27.9.50/elfk/logstash:7.17.7
        ports:
        - containerPort: 9600
        - containerPort: 5040
        resources:
          limits:
            cpu: 300m
            memory: 1000Mi
          requests:
            cpu: 300m
            memory: 500Mi
        volumeMounts:
          - name: config
            mountPath: /usr/share/logstash/config
          - name: pipeline
            mountPath: /usr/share/logstash/pipeline
      volumes:
      - name: config
        configMap:
          name: logstash-config
          items:
          - key: logstash.yml
            path: logstash.yml
      - name: pipeline
        configMap:
          name: logstash-config
          items:
          - key: logstash.conf
            path: logstash.conf
EOF

生成filebeat配置文件

​
# 生成filebeat配置文件
# vi filebeat_conf.yaml
​
apiVersion: v1
kind: ConfigMap
metadata:
  name: filebeat-config
  namespace: default
  labels:
    app: filebeat
data:
  filebeat.yml: |-
    filebeat.config:
      inputs:
        path: ${path.config}/inputs.d/*.yml
        reload.enabled: false
      modules:
        path: ${path.config}/modules.d/*.yml
        reload.enabled: false
​
    filebeat.inputs:
    - type: log
      enabled: true
      tail_files: true
      backoff: "1s"
      paths:
        - /nginxlog/*.log
      fields:
        pod_name: '${pod_name}'
        POD_IP: '${POD_IP}'
        type: nginx_access
      fields_under_root: true
      
    - type: log
      enabled: true
      tail_files: true
      backoff: "1s"
      paths:
        - /tomcatlog/*.log
        - /tomcatlog/*.txt
      fields:
        pod_name: '${pod_name}'
        POD_IP: '${POD_IP}'
        type: tomcat_access
      fields_under_root: true
    
    output.logstash:
      hosts: ["logstash.log:5040"]
      enabled: true
      worker: 1
      compression_level: 3
​

部署2个的Web 中间件。同时带有filebeat边车服务

# 部署一个Nginx服务,同时部署sidecar filebeat服务
cat > ./nginx.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: nginx
  namespace: default
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    nodePort: 30080
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
spec:
  replicas: 3
  minReadySeconds: 15
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      imagePullSecrets:
      - name: harbor-secret
      containers:
      - name: nginx
        image: 172.27.9.50/base/nginx:1.17.0
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-log
          mountPath: /var/log/nginx
      - name: filebeat
        image: 172.27.9.50/elfk/filebeat:7.17.7
        imagePullPolicy: IfNotPresent
        args: [
          "-c", "/etc/filebeat/filebeat.yml",
          "-e",
        ]
        env:
        - name: POD_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        - name: pod_name
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 100m
            memory: 100Mi
        securityContext:
          runAsUser: 0
        volumeMounts:
        - name: config
          mountPath: /etc/filebeat
          readOnly: true
        - name: data
          mountPath: /usr/share/filebeat/data
        - name: nginx-log
          mountPath: /nginxlog
      volumes:
      - name: config
        configMap:
          name: filebeat-config
          items:
          - key: filebeat.yml
            path: filebeat.yml
      - name: data
        emptyDir: {}
      - name: nginx-log
        emptyDir: {}
EOF
​
# 部署一个Tomcat服务,同时部署sidecar filebeat服务
cat > ./tomcat.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: tomcat
  namespace: default
  labels:
    app: tomcat
spec:
  selector:
    app: tomcat
  ports:
  - port: 8080
    nodePort: 30880
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat
  namespace: default
spec:
  replicas: 3
  minReadySeconds: 15
  selector:
    matchLabels:
      app: tomcat
  template:
    metadata:
      labels:
        app: tomcat
    spec:
      imagePullSecrets:
      - name: harbor-secret
      containers:
      - name: tomcat
        image: 172.27.9.50/base/tomcat:8.0.51-alpine
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: tomcat-log
          mountPath: /usr/local/tomcat/logs
      - name: filebeat
        image: 172.27.9.50/elfk/filebeat:7.17.7
        imagePullPolicy: IfNotPresent
        args: [
          "-c", "/etc/filebeat/filebeat.yml",
          "-e",
        ]
        env:
        - name: POD_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.podIP
        - name: pod_name
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        resources:
          limits:
            cpu: 100m
            memory: 100Mi
          requests:
            cpu: 100m
            memory: 100Mi
        securityContext:
          runAsUser: 0
        volumeMounts:
        - name: config
          mountPath: /etc/filebeat
          readOnly: true
        - name: data
          mountPath: /usr/share/filebeat/data
        - name: tomcat-log
          mountPath: /tomcatlog
      volumes:
      - name: config
        configMap:
          name: filebeat-config
          items:
          - key: filebeat.yml
            path: filebeat.yml
      - name: data
        emptyDir: {}
      - name: tomcat-log
        emptyDir: {}
EOF

ES定期删除策略

nginx与tomcat的访问日志是按天生成到ES中。 现在部署一个Cronjob

# 制作一个用于运行计划任务的ubuntu镜像

cat > dockerfile << EOF
From ubuntu:latest
RUN apt-get update && apt-get install -y curl telnet net-tools
EOF

# 打包
docker  build -t 172.27.9.50/base/bwtools:v1 .

# 上传到9.50
docker push 172.27.9.50/base/bwtools:v1

# 编写cronjob的yaml文件

cat > es-index-del.yaml << EOF
apiVersion: batch/v1
kind: CronJob
metadata:
  name: es-index-del
  namespace: log
spec:
  # 并发策略
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 1
  failedJobsHistoryLimit: 1
  schedule: '*/2 * * * *'
  # 表示如果Job因为某种原因无法按调度准时启动,在startingDeadlineSeconds时间段之内,CronJob仍然试图重新启动Job
  # 如果在startingDeadlineSeconds时间之内没有启动成功,则不再试图重新启动。
  # 如果startingDeadlineSeconds的值没有设置,则没有按时启动的任务不会被尝试重新启动。
  startingDeadlineSeconds: 60
  suspend: false
  jobTemplate:
    spec:
      # 该字段限定了 Job 对象在集群中的存活时长,一旦达到activeDeadlineSeconds 指定的时长,该 Job 创建的所有的 Pod 都将被终止。
      activeDeadlineSeconds: 600
      # 设定 Job 最大的重试次数。该字段的默认值为 6;
      # 一旦重试次数达到了 backoffLimit 中的值,Job 将被标记为失败;
      backoffLimit: 6
      # Job结束需要成功运行的Pods。 默认为1
      completions: 1
      # 并行运行的Pod个数,默认为1
      parallelism: 1
      template:
        spec:
          restartPolicy: OnFailure
          imagePullSecrets:
          - name: harbor-secret
          containers:
            - command:
              - 'sh'
              - '/root/script/es-index-del.sh'
              image: 172.27.9.50/base/bwtools:v1
              imagePullPolicy: IfNotPresent
              name: es-backup
              resources:
                requests:
                  cpu: 250m
                  memory: 512Mi
              volumeMounts:
              - name: es-index-del-script
                mountPath: /root/script/es-index-del.sh
                subPath: es-index-del.sh
                readOnly: true
              - name: localtime-volume
                mountPath: /etc/localtime
                readOnly: true
          volumes:
          - name: es-index-del-script
            configMap:
              name: es-del-script-cm
              items:
              - key: es-index-del.sh
                path: es-index-del.sh
          - name: localtime-volume
            hostPath:
              path: /etc/localtime
EOF


# 定期删除索引的脚本

# vi es-index-del.sh 

#!/bin/bash
# create by songjia
# date: 20230302
# description: ES索引的清理

ES_ip='elasticsearch.log'
es_user='elastic'
es_pw='abc123'

# 返回2天前的日期
check_day=`date -d '-2 days' '+%F'`
echo "预计将会删除 ${check_day} 日前的所有索引! "
# 将日期转换为时间戳
check_day_timestamp=`date -d "$check_day" +%s`

del_log(){
    index_day=$1
    index_day_timestamp=`date -d "$index_day" +%s`

    # 当索引的时间戳值小于当前日期7天前的时间戳时,删除此索引
    if [ ${index_day_timestamp} -lt ${check_day_timestamp} ];then
        # 将横线的日期格式转换成ES中点形式的日期格式
        es_format_date=`echo ${index_day} | sed 's/-/\./g'`
        echo "当前开始删除以 ${es_format_date} 结尾的所有索引!"
        curl -s -XDELETE -u ${es_user}:${es_pw} http://${ES_ip}:9200/*${es_format_date}
    else
        echo "${index_day} 日期的索引未到删除阀值,跳过。"
    fi
}

curl -s -XGET -u ${es_user}:${es_pw}  http://${ES_ip}:9200/_cat/indices |awk -F" " '{print $3}'|grep -vE '^\.' |awk -F"-" '{print $NF}'|sort|uniq|sed 's/\./-/g'|while read ES_DATE
do
    echo "ES中存在 ${ES_DATE} 日期的索引"
    del_log ${ES_DATE} 
done

# 将脚本以文件的方式写入ConfigMap资源

kubectl create configmap es-del-script-cm --from-file=es-index-del.sh -n log

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐