Splunk является одним из нескольких наиболее узнаваемых коммерческих продуктов для сбора и анализа логов. Даже сейчас, когда продажи в России больше не производятся, это не повод не писать инструкции/how-to по этому продукту.
Задача: собирать системные логи с docker нод в Splunk не меняя конфигурацию хост-машины
Начать хотелось бы с официального подхода, который выглядит странноватым при использовании докера.
Ссылка на Docker hub
Что же мы имеем:
1. Пуллим образ
$ docker pull splunk/universalforwarder:latest
2. Стартуем контейнер с нужными параметрами
$ docker run -d -p 9997:9997 -e 'SPLUNK_START_ARGS=--accept-license' -e 'SPLUNK_PASSWORD=<password>' splunk/universalforwarder:latest
3. Заходим в контейнер
docker exec -it <container-id> /bin/bash
Далее нас просят пройти
И конфигурировать контейнер после его запуска:
./splunk add forward-server <host name or ip address>:<listening port>
./splunk add monitor /var/log
./splunk restart
Подождите. Что?
Но на этом сюрпризы не заканчиваются. Если вы запустите контейнер из официального образа в интерактивном режиме, то увидите следующее:
Немного разочарования
$ docker run -it -p 9997:9997 -e 'SPLUNK_START_ARGS=--accept-license' -e 'SPLUNK_PASSWORD=password' splunk/universalforwarder:latest
PLAY [Run default Splunk provisioning] *******************************************************************************************************************************************************************************************************
Tuesday 09 April 2019 13:40:38 +0000 (0:00:00.096) 0:00:00.096 *********
TASK [Gathering Facts] ***********************************************************************************************************************************************************************************************************************
ok: [localhost]
Tuesday 09 April 2019 13:40:39 +0000 (0:00:01.520) 0:00:01.616 *********
TASK [Get actual hostname] *******************************************************************************************************************************************************************************************************************
changed: [localhost]
Tuesday 09 April 2019 13:40:40 +0000 (0:00:00.599) 0:00:02.215 *********
Tuesday 09 April 2019 13:40:40 +0000 (0:00:00.054) 0:00:02.270 *********
TASK [set_fact] ******************************************************************************************************************************************************************************************************************************
ok: [localhost]
Tuesday 09 April 2019 13:40:40 +0000 (0:00:00.075) 0:00:02.346 *********
Tuesday 09 April 2019 13:40:40 +0000 (0:00:00.067) 0:00:02.413 *********
Tuesday 09 April 2019 13:40:40 +0000 (0:00:00.060) 0:00:02.473 *********
Tuesday 09 April 2019 13:40:40 +0000 (0:00:00.051) 0:00:02.525 *********
Tuesday 09 April 2019 13:40:40 +0000 (0:00:00.056) 0:00:02.582 *********
Tuesday 09 April 2019 13:40:41 +0000 (0:00:00.216) 0:00:02.798 *********
included: /opt/ansible/roles/splunk_common/tasks/change_splunk_directory_owner.yml for localhost
Tuesday 09 April 2019 13:40:41 +0000 (0:00:00.087) 0:00:02.886 *********
TASK [splunk_common : Update Splunk directory owner] *****************************************************************************************************************************************************************************************
ok: [localhost]
Tuesday 09 April 2019 13:40:41 +0000 (0:00:00.324) 0:00:03.210 *********
included: /opt/ansible/roles/splunk_common/tasks/get_facts.yml for localhost
Tuesday 09 April 2019 13:40:41 +0000 (0:00:00.094) 0:00:03.305 *********
ну и так далее...
Отлично. В образе даже нет артефакта. То есть, каждый раз при запуске будет тратиться время, чтобы выкачать архив с бинарниками, распаковать и настроить.
А как же docker-way и всё такое?
Нет, спасибо. Мы пойдём другим путём. Что, если все эти операции мы выполним на этапе сборки? Тогда поехали!
Чтобы долго не тянуть, покажу сразу итоговый образ:
Dockerfile
# Тут у кого какие предпочтения
FROM centos:7
# Задаём переменные, чтобы каждый раз при старте не указывать их
ENV SPLUNK_HOME /splunkforwarder
ENV SPLUNK_ROLE splunk_heavy_forwarder
ENV SPLUNK_PASSWORD changeme
ENV SPLUNK_START_ARGS --accept-license
# Ставим пакеты
# wget - чтобы скачать артефакты
# expect - понадобится для первоначального запуска Splunk на этапе сборки
# jq - используется в скриптах, которые собирают статистику докера
RUN yum install -y epel-release && yum install -y wget expect jq
# Качаем, распаковываем, удаляем
RUN wget -O splunkforwarder-7.2.4-8a94541dcfac-Linux-x86_64.tgz 'https://www.splunk.com/bin/splunk/DownloadActivityServlet?architecture=x86_64&platform=linux&version=7.2.4&product=universalforwarder&filename=splunkforwarder-7.2.4-8a94541dcfac-Linux-x86_64.tgz&wget=true' && wget -O docker-18.09.3.tgz 'https://download.docker.com/linux/static/stable/x86_64/docker-18.09.3.tgz' && tar -xvf splunkforwarder-7.2.4-8a94541dcfac-Linux-x86_64.tgz && tar -xvf docker-18.09.3.tgz && rm -f splunkforwarder-7.2.4-8a94541dcfac-Linux-x86_64.tgz && rm -f docker-18.09.3.tgz
# С shell скриптами всё понятно, а вот inputs.conf, splunkclouduf.spl и first_start.sh нуждаются в пояснении. Об этом расскажу после source тэга.
COPY [ "inputs.conf", "docker-stats/props.conf", "/splunkforwarder/etc/system/local/" ]
COPY [ "docker-stats/docker_events.sh", "docker-stats/docker_inspect.sh", "docker-stats/docker_stats.sh", "docker-stats/docker_top.sh", "/splunkforwarder/bin/scripts/" ]
COPY splunkclouduf.spl /splunkclouduf.spl
COPY first_start.sh /splunkforwarder/bin/
# Даём права на исполнение, добавляем пользователя и выполняем первоначальную настройку
RUN chmod +x /splunkforwarder/bin/scripts/*.sh && groupadd -r splunk && useradd -r -m -g splunk splunk && echo "%sudo ALL=NOPASSWD:ALL" >> /etc/sudoers && chown -R splunk:splunk $SPLUNK_HOME && /splunkforwarder/bin/first_start.sh && /splunkforwarder/bin/splunk install app /splunkclouduf.spl -auth admin:changeme && /splunkforwarder/bin/splunk restart
# Копируем инит скрипты
COPY [ "init/entrypoint.sh", "init/checkstate.sh", "/sbin/" ]
# По желанию. Кому нужно локально иметь конфиги/логи, кому нет.
VOLUME [ "/splunkforwarder/etc", "/splunkforwarder/var" ]
HEALTHCHECK --interval=30s --timeout=30s --start-period=3m --retries=5 CMD /sbin/checkstate.sh || exit 1
ENTRYPOINT [ "/sbin/entrypoint.sh" ]
CMD [ "start-service" ]
И так, что же содержится в
first_start.sh
#!/usr/bin/expect -f
set timeout -1
spawn /splunkforwarder/bin/splunk start --accept-license
expect "Please enter an administrator username: "
send -- "admin\r"
expect "Please enter a new password: "
send -- "changeme\r"
expect "Please confirm new password: "
send -- "changeme\r"
expect eof
При первом старте, Splunk просит задать ему логин/пароль, НО эти данные используются только для выполнения административных команд этой конкретной инсталляции, то есть, внутри контейнера. В нашем случае же, мы просто хотим запустить контейнер, чтобы всё работало и логи лились рекой. Конечно, это хардкод, но других способов я не нашёл.
Дальше по сценарию выполняется
/splunkforwarder/bin/splunk install app /splunkclouduf.spl -auth admin:changeme
splunkclouduf.spl — Это файл креды для Splunk Universal Forwarder, которые можно загрузить из веб интерфейса.
Куда нажимать чтобы скачать (в картинках)
Это обычный архив, который можно распаковать. Внутри — сертификаты и пароль для подключения к нашему SplunkCloud и outputs.conf со списком наших input инстансов. Этот файл будет актуален до тех пор, пока вы не переустановите свою инсталляцию Splunk или не добавите input нод, если инсталляция on-premise. Поэтому ничего страшного нет в том, чтобы добавить его внутрь контейнера.
И последнее — restart. Да, для применения изменений, нужно его рестартануть.
В наш inputs.conf добавляем логи, которые мы хотим отправлять в Splunk. Необязательно добавлять этот файл в образ, если вы, к примеру, раскидываете конфиги через puppet. Главное лишь в том, чтобы Forwarder видел конфиги, при старте демона, иначе будет нужен ./splunk restart.
А что за docker stats скрипты? На гитхабе есть старое решение от outcoldman, скрипты взяты оттуда и доработаны для работы с актуальными версиями Docker (ce-17.*) и Splunk (7.*).
С полученными данными можно строить такие
дэшборды: (пара картинок)
Исходный код дэшей лежит в репе, указанной в конце статьи. Обратите внимание, что там присутствует 2 select поля: 1 — выбор индекса (ищутся по маске), выбор хоста/контейнера. Маску индекса скорее всего вам придётся обновить, в зависимости от имён, которые вы используете.
В завершение, хочу обратить внимание на функцию start() в
entrypoint.sh
start() {
trap teardown EXIT
if [ -z $SPLUNK_INDEX ]; then
echo "'SPLUNK_INDEX' env variable is empty or not defined. Should be 'dev' or 'prd'." >&2
exit 1
else
sed -e "s/@index@/$SPLUNK_INDEX/" -i ${SPLUNK_HOME}/etc/system/local/inputs.conf
fi
sed -e "s/@hostname@/$(cat /etc/hostname)/" -i ${SPLUNK_HOME}/etc/system/local/inputs.conf
sh -c "echo 'starting' > /tmp/splunk-container.state"
${SPLUNK_HOME}/bin/splunk start
watch_for_failure
}
В моём случае, для каждого окружения и каждой отдельной сущности, будь то приложение в контейнере или хост-машина, мы используем отдельный индекс. Так не будет страдать скорость поиска при значительном накоплении данных. Для именования индексов используется простое правило: <environment_name>_<service/application/etc>. Поэтому, чтобы контейнер был универсальным, мы перед запуском непосредственно демона, заменяем sed-ом wildcard на имя окружения. Переменная с именем окружения передается через переменные окружения. Звучит забавно.
Также стоит отметить момент, что по какой-то причине на Splunk не влияет наличие docker параметра hostname. Он всё равно будет упорото слать логи с id своего контейнера в поле host. Как решение, можно монтировать /etc/hostname с хост-машины и при старте делать замену, аналогичную именам индексов.
Пример docker-compose.yml
version: '2'
services:
splunk-forwarder:
image: "${IMAGE_REPO}/docker-stats-splunk-forwarder:${IMAGE_VERSION}"
environment:
SPLUNK_INDEX: ${ENVIRONMENT}
volumes:
- /etc/hostname:/etc/hostname:ro
- /var/log:/var/log
- /var/run/docker.sock:/var/run/docker.sock:ro
Итог
Да, возможно, решение не идеальное и уж точно не универсальное для всех, так как присутствует много «хардкода». Но на основе него каждый может собрать свой образ и положить его в свой приватный артифактори, если, так уж случилось, вам необходим Splunk Forwarder именно в докере.
Ссылки:
Решение из статьи
Решение от outcoldman вдохновившее переиспользовать часть функционала
Оф. документация по настройке Universal Forwarder
cccco
Спасибо за статью!
Расскажите, пожалуйста, поподробнее о приобретении Splunk. Что значит, продажи в России не производятся?
kashtan404 Автор
Благодарю, рад что вам понравилось.
Подробнее о приобритении, думаю лучше почитать на официальном сайте.
А вот информация из официального блога: контракты с российскими компания больше не заключают — Splunk blog. Вообще было несколько новостных статей на эту тему, можно найти по тэгу Splunk.
AntoniusFirst
Если интересует SIEM решение, присмотритесь к коммерческим Microfocus Arcsight, IBM QRadar или бесплатному Elastic Stack. Решать аналитические задачи можно во всех этих решениях.
yleo
Если интересует SIEM решение, то уж (наверное) лучше не наступать второй раз на импортные грабли ;)
AntoniusFirst
Из российского ПО нет аналогичных решений
yleo
Буквальных аналогов нет (ибо не нужны), но есть амбициозные конкуренты (на хабре).