Thank you for reading this post, don't forget to subscribe!
Будем запускать elk с политикой ротации ILM, в данном примере (чтоб было быстрее) ротация будет срабатывать следующим образом: (ролловер - создание новых индексов будет срабатывать при достижении 5 мегабайт или сроком более 1 часа, далее удаление старого индекса будет проходить в течении 5 минут, но пока не будет создан его снапшот, чтобы в случае чего его можно было восстановить )
При отправке логов будем фильтровать поля с мак адресами и айпи адресами. Индексы и снапшоты будем хранить на общем nfs хранилище
Создаём неймспейс
cat namespace.yml
1 2 3 4 5 |
kind: Namespace apiVersion: v1 metadata: name: elk |
Elasticsearch запускаем с помощью следующего файла, описание по некоторым параметрам ниже:
cat elasticsearch_work.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
apiVersion: v1 kind: ServiceAccount metadata: name: elasticsearch namespace: elk labels: k8s-app: elasticsearch kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: elasticsearch labels: k8s-app: elasticsearch kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile rules: - apiGroups: - "" resources: - "services" - "namespaces" - "endpoints" verbs: - "get" --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: elk name: elasticsearch labels: k8s-app: elasticsearch kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile subjects: - kind: ServiceAccount name: elasticsearch namespace: elk apiGroup: "" roleRef: kind: ClusterRole name: elasticsearch apiGroup: "" --- kind: Service apiVersion: v1 metadata: name: elasticsearch namespace: elk labels: k8s-app: elasticsearch spec: selector: k8s-app: elasticsearch clusterIP: None ports: - port: 9200 name: rest - port: 9300 name: inter-node --- apiVersion: apps/v1 kind: StatefulSet metadata: name: elasticsearch namespace: elk spec: serviceName: elasticsearch replicas: 2 selector: matchLabels: k8s-app: elasticsearch template: metadata: labels: k8s-app: elasticsearch spec: containers: - name: elasticsearch image: docker.elastic.co/elasticsearch/elasticsearch:7.9.3 resources: limits: cpu: 1000m requests: cpu: 100m ports: - containerPort: 9200 name: rest protocol: TCP - containerPort: 9300 name: inter-node protocol: TCP volumeMounts: - name: elasticsearch-nfs-share mountPath: /usr/share/elasticsearch/data - name: elasticsearch-nfs-share-for-shapshot mountPath: /snapshot env: - name: cluster.name value: test-cluster.local - name: node.name valueFrom: fieldRef: fieldPath: metadata.name - name: discovery.seed_hosts value: "elasticsearch-0.elasticsearch,elasticsearch-1.elasticsearch" - name: cluster.initial_master_nodes value: "elasticsearch-0,elasticsearch-1" - name: ES_JAVA_OPTS value: "-Xms512m -Xmx512m" - name: path.repo value: "/snapshot" initContainers: - name: fix-permissions image: busybox imagePullPolicy: IfNotPresent command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"] securityContext: privileged: true volumeMounts: - name: elasticsearch-nfs-share mountPath: /usr/share/elasticsearch/data - name: elasticsearch-nfs-share-for-shapshot mountPath: /snapshot - name: fix-permissions2 image: busybox imagePullPolicy: IfNotPresent command: ["sh", "-c", "chown -R 1000:1000 /snapshot"] securityContext: privileged: true volumeMounts: - name: elasticsearch-nfs-share-for-shapshot mountPath: /snapshot - name: increase-vm-max-map image: busybox imagePullPolicy: IfNotPresent command: ["sysctl", "-w", "vm.max_map_count=262144"] securityContext: privileged: true - name: increase-fd-ulimit image: busybox imagePullPolicy: IfNotPresent command: ["sh", "-c", "ulimit -n 65536"] securityContext: privileged: true volumeClaimTemplates: - metadata: name: elasticsearch-nfs-share labels: k8s-app: elasticsearch spec: accessModes: [ "ReadWriteMany" ] storageClassName: nfs-storageclass resources: requests: storage: 3Gi - metadata: name: elasticsearch-nfs-share-for-shapshot labels: k8s-app: elasticsearch spec: accessModes: [ "ReadWriteMany" ] storageClassName: nfs-storageclass resources: requests: storage: 2Gi |
В переменных указываем имя нашего кластера:
- name: cluster.name
value: test-clusnet.local
Имя на нфс шаре для наших индексов будет следующим: elasticsearch-nfs-share а для снапшотов elasticsearch-nfs-share-for-shapshot
кстати чтобы можно было создать репозиторий для хранилища используем переменные env
- name: path.repo
value: "/snapshot"
Чтобы в прокинутые на нфс директории можно было нормально записывать надо поправить для них владельца, поэтому используем
initContainers а именно fix-permissions и fix-permissions2, для контейнеров busybox используем политику imagePullPolicy: IfNotPresent чтобы они каждый раз не выкачивались.
Ниже файл для запуска панели кибана, в нём мы правим только имя для ingress и namespace:
cat kibana_work.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: kibana namespace: elk annotations: kubernetes.io/ingress.class: nginx spec: rules: - host: elk.test.local http: paths: - backend: serviceName: kibana servicePort: 5601 --- apiVersion: v1 kind: Service metadata: name: kibana namespace: elk labels: app: kibana spec: ports: - port: 5601 selector: app: kibana --- apiVersion: apps/v1 kind: Deployment metadata: name: kibana namespace: elk labels: app: kibana spec: replicas: 1 selector: matchLabels: app: kibana template: metadata: labels: app: kibana spec: containers: - name: kibana image: docker.elastic.co/kibana/kibana:7.9.3 resources: limits: cpu: 1000m requests: cpu: 100m env: - name: ELASTICSEARCH_URL value: http://elasticsearch:9200 ports: - containerPort: 5601 |
Далее рассмотрим filebeat это утилита которая будет собирать логи из файлов и отправлять их в logstash, не используемые параметры я закомментировал
cat filebeat_work.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
--- apiVersion: v1 kind: ConfigMap metadata: name: filebeat-config namespace: elk labels: k8s-app: filebeat data: filebeat.yml: |- filebeat.inputs: - type: container paths: - /var/log/containers/*.log #- /var/log/containers/*terminal-soft*.log #encoding: CP1252 encoding: ISO-8859-1 processors: - add_kubernetes_metadata: host: ${NODE_NAME} matchers: - logs_path: logs_path: "/var/log/containers/" # To enable hints based autodiscover, remove `filebeat.inputs` configuration and uncomment this: #filebeat.autodiscover: # providers: # - type: kubernetes # node: ${NODE_NAME} # hints.enabled: true # hints.default_config: # type: container # paths: # - /var/log/containers/*${data.kubernetes.container.id}.log processors: - add_cloud_metadata: - add_host_metadata: #cloud.id: ${ELASTIC_CLOUD_ID} #cloud.auth: ${ELASTIC_CLOUD_AUTH} #output.elasticsearch: # hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}'] # username: ${ELASTICSEARCH_USERNAME} # password: ${ELASTICSEARCH_PASSWORD} #output.console: # pretty: true output.logstash: # #hosts: [ "elasticsearch:9200" ] hosts: [ "logstash-service:5044" ] --- apiVersion: apps/v1 kind: DaemonSet metadata: name: filebeat namespace: elk labels: k8s-app: filebeat spec: selector: matchLabels: k8s-app: filebeat template: metadata: labels: k8s-app: filebeat spec: serviceAccountName: filebeat terminationGracePeriodSeconds: 30 hostNetwork: true dnsPolicy: ClusterFirstWithHostNet containers: - name: filebeat image: docker.elastic.co/beats/filebeat:7.10.1 args: [ "-c", "/etc/filebeat.yml", "-e", ] env: - name: ELASTICSEARCH_HOST value: elasticsearch - name: ELASTICSEARCH_PORT value: "9200" #- name: ELASTICSEARCH_USERNAME # value: elastic #- name: ELASTICSEARCH_PASSWORD # value: changeme #- name: ELASTIC_CLOUD_ID # value: #- name: ELASTIC_CLOUD_AUTH # value: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName securityContext: runAsUser: 0 # If using Red Hat OpenShift uncomment this: #privileged: true resources: limits: memory: 200Mi requests: cpu: 100m memory: 100Mi volumeMounts: - name: config mountPath: /etc/filebeat.yml readOnly: true subPath: filebeat.yml - name: data mountPath: /usr/share/filebeat/data - name: varlibdockercontainers mountPath: /var/lib/docker/containers readOnly: true - name: varlog mountPath: /var/log readOnly: true volumes: - name: config configMap: defaultMode: 0640 name: filebeat-config - name: varlibdockercontainers hostPath: path: /var/lib/docker/containers - name: varlog hostPath: path: /var/log # data folder stores a registry of read status for all files, so we don't send everything again on a Filebeat pod restart - name: data hostPath: # When filebeat runs as non-root user, this directory needs to be writable by group (g+w). path: /var/lib/filebeat-data type: DirectoryOrCreate --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: filebeat subjects: - kind: ServiceAccount name: filebeat namespace: elk roleRef: kind: ClusterRole name: filebeat apiGroup: rbac.authorization.k8s.io --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: filebeat labels: k8s-app: filebeat rules: - apiGroups: [""] # "" indicates the core API group resources: - namespaces - pods verbs: - get - watch - list --- apiVersion: v1 kind: ServiceAccount metadata: name: filebeat namespace: elk labels: k8s-app: filebeat --- |
Теперь рассмотрим конфиг logstash. Основные изменения будут производиться именно в нём, рассмотрим их подробнее:
cat logstash-deployment.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
apiVersion: v1 kind: ConfigMap metadata: name: logstash-configmap namespace: elk data: logstash.yml: | http.host: "0.0.0.0" path.config: /usr/share/logstash/pipeline logstash.conf: | # all input will come from filebeat, no local logs input { beats { port => 5044 } } filter { if [kubernetes][namespace] == "terminal-soft" { mutate { add_tag => "tag-terminal-soft" remove_field => ["[agent][name]","[agent][version]","[host][mac]","[host][ip]"] } } } output { if "tag-terminal-soft" in [tags] { elasticsearch { hosts => [ "elasticsearch:9200" ] manage_template => false index => "terminal-soft-%{+YYYY.MM.dd}" ilm_rollover_alias => "terminal-soft" ilm_policy => "terminal-soft" } } } --- apiVersion: apps/v1 kind: Deployment metadata: name: logstash-deployment namespace: elk spec: replicas: 1 selector: matchLabels: app: logstash template: metadata: labels: app: logstash spec: containers: - name: logstash image: docker.elastic.co/logstash/logstash:7.10.1 ports: - containerPort: 5044 volumeMounts: - name: config-volume mountPath: /usr/share/logstash/config - name: logstash-pipeline-volume mountPath: /usr/share/logstash/pipeline volumes: - name: config-volume configMap: name: logstash-configmap items: - key: logstash.yml path: logstash.yml - name: logstash-pipeline-volume configMap: name: logstash-configmap items: - key: logstash.conf path: logstash.conf --- kind: Service apiVersion: v1 metadata: name: logstash-service namespace: elk spec: selector: app: logstash ports: - protocol: TCP port: 5044 targetPort: 5044 type: ClusterIP |
В input port указывается порт на котором ожидаются данные от filebeat. Далее в фильтре используем условие
if [kubernetes][namespace] == "terminal-soft" которое по имени нейспейса добавляет тэг
add_tag => "tag-terminal-soft" и удаляет поля с именем агента и версией, а также с мак и айпи адресами
remove_field => ["[agent][name]","[agent][version]","[host][mac]","[host][ip]"] }.
После в при отправке output в elasticsearch используем условие, по тэгу добавленному ранее в фильтре: if "tag-terminal-soft" in [tags] { и если он совпадает то производим следующие действия:
указываем как будет называться индекс:
index => "terminal-soft-%{+YYYY.MM.dd}"
Указываем как будет называться роловер алиас:
ilm_rollover_alias => "terminal-soft"
и указываем как будет называется имя у ILM политики:
ilm_policy => "terminal-soft"
ВАЖНО!!!!! TEMPLATE и ILM ДОЛЖНЫ БЫТЬ СОЗДАНЫ В KIBANA ДО ТОГО КАК LOGSTASH БУДЕТ ЗАПУЩЕН
Описание конфигов закончено теперь можем запускать их в кластере:
kubectl apply -f namespace.yml
kubectl apply -f elasticsearch_work.yaml -f kibana_work.yaml
далее ждём когда запустится elasticsearch и kibana
Как запустится, заходим по нашему ingress домену:
http://elk.test.local/
Ну всё, идём настраивать template для индекса и ilm политику.
тут вручную набиваем @timestamp и выбираем date
Template создали теперь перейдём к созданию ILM
Пока политику создания снапшота не будем указывать, так как она ещё не создана.
Всё теперь можно запускать filebeat и logstash
kubectl apply -f filebeat_work.yaml -f logstash-deployment.yaml
После запуска, проверяем что индекс создался:
Проверяем что политика подцепилась к индексу:
Теперь создадим политику по снапшотам:
Но сначала репозиторий:
Репозиторий создали, создаём политику для снапшотов:
Указываем время сколько будут храниться снапшоты а также минимальное и максимальное количество снапшотов
За время пока я описывал и делал скрины, прошёл ролловер, проверим:
Старый ролловер находится в состоянии delete
А у нового состояние rollover
Теперь для нашего ILM докинем искать политику снапшотов перед удалением:
Ну а чтобы стало побольше ролловеров, знатно насрём в логи, в неймспейсе terminal-soft
Для этого запустим такой файл:
cat _pod_govnolog2.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
--- apiVersion: apps/v1 kind: ReplicaSet metadata: name: counter-replicaset namespace: terminal-soft spec: replicas: 80 selector: matchLabels: app: counter template: metadata: labels: app: counter spec: containers: - name: count image: busybox imagePullPolicy: IfNotPresent args: [/bin/sh, -c, 'i=0; while true; do echo TEEEEEEEEEST "$i: $(date)"; i=$((i+1)); sleep 1; done'] |
Поднимется 80 подов которые будут в бесконечном цикле срать в лог.
==========================================================================
Проверяем:
Как видим старый индекс удалился:
Но перед этим у нас создался снапшот: