Постепенно эволюционируя, каждая организация переходит от ручного 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 updatehelm upgrade --install efk . -f values-minimal.yaml
Из исходников values-minimal.yaml
elasticsearch: volumePermissions: enabled: truekibana: volumePermissions: enabled: true elasticsearch: hosts: - "efk-elasticsearch-coordinating-only" port: 9200# Пропишите свой хост, если используете ингресс ingress: enabled: true hostname: kibana.local# Либо service: type: NodePort port: 30010fluentd: 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/v1kind: Deploymentmetadata: name: helloworld labels: app: helloworldspec: 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: v1kind: Servicemetadata: name: helloworldspec: selector: app: helloworld ports: - port: 8000 nodePort: 30011 type: NodePortEOF
По ссылке 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: daysunit_count: 90
Security
Как обычно security доставляет основную боль при настройке и использовании. Но если ваша организация чуть подросла, это необходимый шаг. Стандартом де факто при настройке безопасности является интеграция с LDAP. Официальные плагины от еластика выходят не под открытой лицензией, поэтому приходится использовать плагин Open Distro. Далее продемонстрируем, как его можно запустить.
Сборка elasticsearch c плагином opendistro
Вот проект в котором собирали docker images.
Для установки плагина, необходимо, чтобы версия elasticsearch соответствовала версии плагина.
В quickstart плагина рекомендуется установить install_demo_configuration.sh с демо сертификатами.
FROM bitnami/elasticsearch:7.10.0RUN elasticsearch-plugin install -b https://d3g5vo6xdbdb9a.cloudfront.net/downloads/elasticsearch-plugins/opendistro-security/opendistro_security-1.12.0.0.zipRUN touch /opt/bitnami/elasticsearch/config/elasticsearch.ymlUSER rootRUN /bin/bash /opt/bitnami/elasticsearch/plugins/opendistro_security/tools/install_demo_configuration.sh -y -iRUN mv /opt/bitnami/elasticsearch/config/elasticsearch.yml /opt/bitnami/elasticsearch/config/my_elasticsearch.ymlCOPY my_elasticsearch.yml /opt/bitnami/elasticsearch/config/my_elasticsearch.ymlUSER 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/bashI have no name!@68ac2255bb85:/$ ./securityadmin_demo.shOpen Distro Security Admin v7Will connect to localhost:9300 ... doneConnected as CN=kirk,OU=client,O=client,L=test,C=deElasticsearch Version: 7.10.0Open Distro Security Version: 1.12.0.0Contacting elasticsearch cluster 'elasticsearch' and wait for YELLOW clusterstate ...Clustername: elasticsearchClusterstate: YELLOWNumber of nodes: 1Number 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 updatedWill update '_doc/roles' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/roles.yml SUCC: Configuration for 'roles' created or updatedWill update '_doc/rolesmapping' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/roles_mapping.yml SUCC: Configuration for 'rolesmapping' created or updatedWill update '_doc/internalusers' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/internal_users.yml SUCC: Configuration for 'internalusers' created or updatedWill update '_doc/actiongroups' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/action_groups.yml SUCC: Configuration for 'actiongroups' created or updatedWill update '_doc/tenants' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/tenants.yml SUCC: Configuration for 'tenants' created or updatedWill update '_doc/nodesdn' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/nodes_dn.yml SUCC: Configuration for 'nodesdn' created or updatedWill update '_doc/whitelist' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/whitelist.yml SUCC: Configuration for 'whitelist' created or updatedWill update '_doc/audit' with /opt/bitnami/elasticsearch/plugins/opendistro_security/securityconfig/audit.yml SUCC: Configuration for 'audit' created or updatedDone with successI 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/v1kind: Jobmetadata: 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-succeededspec: 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 решение. Спасибо за внимание!