Постепенно эволюционируя, каждая организация переходит от ручного grep логов к более современным инструментам для сбора, анализа логов. Если вы работаете с kubernetes, где приложение может масштабироваться горизонтально и вертикально, вас просто вынуждают отказаться от старой парадигмы сбора логов. В текущее время существует большое количество вариантов систем централизованного логирования, причем каждый выбирает наиболее приемлемый вариант для себя. В данной статье пойдет речь о наиболее популярном и зарекомендовавшем себя стэке Elasticsearch + Kibana + Fluentd в связке с плагином OpenDistro Security. Данный стэк полностью open source, что придает ему особую популярность.
Проблематика
Нам необходимо было наладить сборку логов с кластера kubernetes. Из требований:
Использовать только открытые решения.
Сделать очистку логов.
Необходимо прикрутить LDAP, для разграничения прав.
Пререквизиты
Данный материал предполагает:
Имеется установленный kuberenetes 1.18 или выше (ниже версию не проверяли)
Установленный пакетный менеджер helm 3
Немного о helm chart
Наиболее популярным способом установки приложения в kubernetes является helm. Helm это пакетный менеджер, с помощью которого можно подготовить набор компонентов для установки и связать их вместe, дополнив необходимыми метриками и конфигурацией.
В своей практике мы используем helm chart от компании bitnami(подразделение vmware)
собраны open source продукты на все случаи жизни.
корпоративные стандарты по разработке чатов и докер образов
красивая документация
проект живой и активно поддерживается сообществом
Выбор стека
Во многом выбор стека технологий определило время. С большой долей вероятностью два года назад мы бы деплоили ELK стек вместо EFK и не использовали helm chart.
Fluentd часто упоминается в документации, широко распространен, имеет большое количество плагинов, на все случаи жизни. К тому же у нас есть человек, который после обучение в rebrain и очень хотел внедрить fluentd.
Elasticsearch и kibana поставляются с открытой лицензией, однако плагины для security и других вкусностей идут под иной лицензией. Однако компания Amazon выпустила плагины Open Distro, которые покрывают оставшийся функционал под открытой лицензией.
Схема выглядит примерно так
Хорошим тоном является вынесение инфраструктурных компонентов в отдельный кластер, поэтому зеленым прямоугольником выделена та часть, которая может быть установлена на все кластера в которых должны собираться логи.
Минимальный деплой EFK стека (без Security)
Сборка EFK стека была произведена по статье Collect and Analyze Log Data for a Kubernetes Cluster with Bitnami's Elasticsearch, Fluentd and Kibana Charts. Компоменты упакованы в отдельный чат. Исходники можно взять здесь и произвести командой
helm dependency update
helm upgrade --install efk . -f values-minimal.yaml
Из исходников values-minimal.yaml
elasticsearch:
volumePermissions:
enabled: true
kibana:
volumePermissions:
enabled: true
elasticsearch:
hosts:
- "efk-elasticsearch-coordinating-only"
port: 9200
# Пропишите свой хост, если используете ингресс
ingress:
enabled: true
hostname: kibana.local
# Либо
service:
type: NodePort
port: 30010
fluentd:
aggregator:
enabled: true
configMap: elasticsearch-output
extraEnv:
- name: ELASTICSEARCH_HOST
value: "efk-elasticsearch-coordinating-only"
- name: ELASTICSEARCH_PORT
value: "9200"
forwarder:
# Чтение логов с диска /var/log/containers/* отключено
enabled: false
configMap: apache-log-parser
extraEnv:
- name: FLUENTD_DAEMON_USER
value: root
- name: FLUENTD_DAEMON_GROUP
value: root
Кибана будет доступна по адресу http://minikube_host:30010/, либо http://kibana.local.
Как видим компонент fluentd forwarder не включен. Перед включением парсинга, я рекомендовал бы настроить на определенные логи, иначе еластику может быть послано слишком большое количество логов. Правила для парсинга описаны в файле apache-log-parser.yaml.
Как отправить логи в EFK
Существует много способов, для начала предлагаем либо включить fluentd forwarder, либо воспользоваться простейшим приложением на python. Ниже пример для bare metal. Исправьте FLUENT_HOST FLUENT_PORT на ваши значения.
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld
labels:
app: helloworld
spec:
replicas: 1
template:
metadata:
name: helloworld
labels:
app: helloworld
spec:
containers:
- name: helloworld
image: sergbs/django-hello-world:1
imagePullPolicy: Always
ports:
- containerPort: 8000
env:
- name: FLUENT_HOST
value: "efk-fluentd-headless"
- name: FLUENT_PORT
value: "24224"
selector:
matchLabels:
app: helloworld
---
apiVersion: v1
kind: Service
metadata:
name: helloworld
spec:
selector:
app: helloworld
ports:
- port: 8000
nodePort: 30011
type: NodePort
EOF
По ссылке http://minikube_host:30011/ Будет выведено "Hello, world!" И лог уйдет в elastic
Пример
Включить fluentd forwarder, он создаст daemon set, т.е. запустится на каждой ноде вашего кубернетеса и будет читать логи docker container-ов.
Добавить в ваш докер контейнер драйвер fluentd, тем более можно добавлять более одного драйвера
Добавить в ваше приложение библиотеку, которая будет напрямую логировать. Пример приложения на python. Используйте его, как отладочное средство при установке EFK.
И как показывает практика, логировать напрямую эффективный, но далеко не самый надежный способ. Даже если вы логируете сразу в fluentd и в консоль. В случае потери конекта, во fluentd часть логов просто не смогут попасть и будут потеряны для него навсегда. Поэтому наиболее надежный способ, это считывать логи с диска для отправки в EFK.
Очистка логов.
Для очистки логов используется curator. Его можно включить, добавив в yaml файл:
elasticsearch:
curator:
enabled: true
По умолчанию его конфигурация уже предусматривает удаление через индекса старше 90 дней это можно увидеть внутри подчата efk/charts/elasticsearch-12.6.1.tgz!/elasticsearch/values.yaml
configMaps:
# Delete indices older than 90 days
action_file_yml: |-
...
unit: days
unit_count: 90
Security
Как обычно security доставляет основную боль при настройке и использовании. Но если ваша организация чуть подросла, это необходимый шаг. Стандартом де факто при настройке безопасности является интеграция с LDAP. Официальные плагины от еластика выходят не под открытой лицензией, поэтому приходится использовать плагин Open Distro. Далее продемонстрируем, как его можно запустить.
Сборка elasticsearch c плагином opendistro
Вот проект в котором собирали docker images.
Для установки плагина, необходимо, чтобы версия elasticsearch соответствовала версии плагина.
В quickstart плагина рекомендуется установить install_demo_configuration.sh с демо сертификатами.
FROM bitnami/elasticsearch:7.10.0
RUN elasticsearch-plugin install -b https://d3g5vo6xdbdb9a.cloudfront.net/downloads/elasticsearch-plugins/opendistro-security/opendistro_security-1.12.0.0.zip
RUN touch /opt/bitnami/elasticsearch/config/elasticsearch.yml
USER root
RUN /bin/bash /opt/bitnami/elasticsearch/plugins/opendistro_security/tools/install_demo_configuration.sh -y -i
RUN mv /opt/bitnami/elasticsearch/config/elasticsearch.yml /opt/bitnami/elasticsearch/config/my_elasticsearch.yml
COPY my_elasticsearch.yml /opt/bitnami/elasticsearch/config/my_elasticsearch.yml
USER 1001
Есть небольшая магия, ввиду, того что плагин дополняет elasticsearch.yml, а контейнеры bitnami только при старте генерируют этот файл. Дополнительные же настройки они просят передавать через my_elasticsearch.yml
В my_elasticsearch.yml мы изменили настройку, это позволит нам обращаться к рестам elasticsearch по http.
# turn off REST layer
tlsopendistro_security.ssl.http.enabled: false
Сделано это в демонстрационных целях, для облегчения запуска плагина. Если вы захотите включить Rest Layer tls придется добавлять соответствующие настройки во все компоненты, которые общаются с elasticsearch.
Запуск docker-compose с LDAP интеграцией
Запустим проект с помощью docker-compose up
, и залогинимся на http://0:5601 под admin/admin
Теперь у нас есть вкладка Security
Можно посмотреть настройку LDAP через интерфейс кибаны
Настройки должны совпадать с файлами конфигурации. Стоит обратить внимание, что плагин хранит свои данные в индексе еластика, и файлы конфигурации применяет при инициализации, т.е. если индекс не создан. Если вы измените файлы позже то вам придется воспользоваться утилитой securityadmin.sh
docker exec -it elasticsearch /bin/bash
I have no name!@68ac2255bb85:/$ ./securityadmin_demo.sh
Open Distro Security Admin v7
Will connect to localhost:9300 ... done
Connected as CN=kirk,OU=client,O=client,L=test,C=de
Elasticsearch Version: 7.10.0
Open Distro Security Version: 1.12.0.0
Contacting elasticsearch cluster 'elasticsearch' and wait for YELLOW clusterstate ...
Clustername: elasticsearch
Clusterstate: YELLOW
Number of nodes: 1
Number of data nodes: 1
.opendistro_security index already exists, so we do not need to create one.
Populate config from /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/
Will update '_doc/config' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/config.yml
SUCC: Configuration for 'config' created or updated
Will update '_doc/roles' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/roles.yml
SUCC: Configuration for 'roles' created or updated
Will update '_doc/rolesmapping' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/roles_mapping.yml
SUCC: Configuration for 'rolesmapping' created or updated
Will update '_doc/internalusers' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/internal_users.yml
SUCC: Configuration for 'internalusers' created or updated
Will update '_doc/actiongroups' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/action_groups.yml
SUCC: Configuration for 'actiongroups' created or updated
Will update '_doc/tenants' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/tenants.yml
SUCC: Configuration for 'tenants' created or updated
Will update '_doc/nodesdn' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/nodes_dn.yml
SUCC: Configuration for 'nodesdn' created or updated
Will update '_doc/whitelist' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/whitelist.yml
SUCC: Configuration for 'whitelist' created or updated
Will update '_doc/audit' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/audit.yml
SUCC: Configuration for 'audit' created or updated
Done with success
I have no name!@68ac2255bb85:/$
настройка Ldap
Для настройки интеграции с LDAP необходимо заполнить соответствующие секции в elastisearch-opendistro-sec/config.yml, ссылка на официальную документацию по authentication
config:
dynamic:
authc:
# тут можно настроить авторизацию
authz:
# а здесь аутентификацию
В случае с Active Directory, необходимо будет изменить конфигурационный файл примерно так:
# тут можно настроить авторизацию
ldap:
...
hosts:
- ldaphost:389
bind_dn: cn=LDAP,ou=Example,dc=example,dc=ru
password: CHANGEME
userbase: 'DC=example,DC=ru'
usersearch: '(sAMAccountName={0})'
username_attribute: sAMAccountName
Не забудьте воспользоваться securityadmin.sh после изменения конфигурации. Процедура была описана в предыдущем параграфе.
Установка EFK + Opendistro в kubernetes
Вернемся к проекту с kubernetes, установим проект командой
helm upgrade --install efk . -f values.yaml
Нам необходимо будет
Для настройка OpenDistro Security plugin мы скопировали файл конфигурации, которые поместим в секреты kubernetes.
extraVolumes:
- name: config
secret:
secretName: opendistro-config
items:
- key: config.yml
path: config.yml
- name: roles-mapping
secret:
secretName: opendistro-config
items:
- key: roles_mapping.yml
path: roles_mapping.yml
extraVolumeMounts:
- mountPath: /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/config.yml
subPath: config.yml
name: config
- mountPath: /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/roles_mapping.yml
subPath: roles_mapping.yml
name: roles-mapping
Чтобы настройки применялись при команде helm upgrade, мы сделали job, который будет запускаться при каждой команде helm upgrade
apiVersion: batch/v1
kind: Job
metadata:
name: opendistro-config-reload
labels:
app.kubernetes.io/managed-by: {{.Release.Service | quote }}
app.kubernetes.io/instance: {{.Release.Name | quote }}
annotations:
"helm.sh/hook": post-upgrade
"helm.sh/hook-delete-policy": hook-succeeded
spec:
template:
metadata:
name: config-reload
labels:
app.kubernetes.io/managed-by: {{.Release.Service | quote }}
app.kubernetes.io/instance: {{.Release.Name | quote }}
spec:
initContainers:
- name: "wait-for-db"
image: "alpine:3.6"
command:
- 'sh'
- '-c'
- >
until nc -z -w 2 efk-elasticsearch-coordinating-only 9300 && echo elastic is ok; do
sleep 2;
done;
containers:
- name: opendistro-config-reload
image: "{{ .Values.elasticsearch.image.registry }}/{{ .Values.elasticsearch.image.repository}}:{{ .Values.elasticsearch.image.tag }}"
imagePullPolicy: {{ .Values.elasticsearch.image.pullPolicy | quote }}
{{- if .Values.elasticsearch.master.securityContext.enabled }}
securityContext:
runAsUser: {{ .Values.elasticsearch.master.securityContext.runAsUser }}
{{- end }}
command:
- 'bash'
- '-c'
- >
"/opt/bitnami/elasticsearch/plugins/opendistro_security/tools/securityadmin.sh" -h efk-elasticsearch-coordinating-only -cd "/opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig" -icl -key "/opt/bitnami/elasticsearch/config/kirk-key.pem" -cert "/opt/bitnami/elasticsearch/config/kirk.pem" -cacert "/opt/bitnami/elasticsearch/config/root-ca.pem" -nhnv
restartPolicy: Never
backoffLimit: 1
Итоги
Если вы собираетесь организовать централизованную сборку логов для приложений под управление kubernetes, данный вариант мог бы стать вполне обоснованным решением. Все продукты поставляются под открытыми лицензиями. В статье приведена заготовка из которой можно создать production ready решение. Спасибо за внимание!