Структурированное ведение журнала в журнал из контейнера Docker
Каков наилучший способ записи структурированных журналов в journald из контейнера Docker?
Например, у меня есть приложение, которое пишет с использованием sd_journal_send. Вместо того, чтобы менять приложение, я попытался пройти через
-v /var/log/systemd/journal:/var/log/systemd/journal
Он работает на моем рабочем столе Ubuntu 16.04, но не на тех экземплярах CoreOS, где запускается приложение (которые используют базовый образ Ubuntu 16.04). Я не совсем понимаю, почему. Возможно, есть лучший способ отправить в журнал?
Какие ограничения имеет опция ведения журнала вывода docker journald? Похоже, он не поддерживал приложения, пишущие больше, чем просто поле сообщения.
-
Итак, я нашел, что мне нужно -v /dev/log:/dev/log
Но есть и другая проблема в том, что нет никакой связи с служебным файлом, который запускает контейнер Docker. Добавление UNIT вручную: servicename.service не решило проблему. И поэтому при просмотре и доставке журналов для службы, она связана с exe, но не с контейнером или службой. Кто сталкивался с этими проблемами и как вы их решили?
- Хорошо, позвольте мне немного расширить это.
Программа A C может записывать в журнал systemd примерно так:
#include <systemd/sd-journal.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
sd_journal_send("MESSAGE=Hello World!",
"MESSAGE_ID=52fb62f99e2c49d89cfbf9d6de5e3555",
"PRIORITY=5",
"HOME=%s", getenv("HOME"),
"TERM=%s", getenv("TERM"),
"PAGE_SIZE=%li", sysconf(_SC_PAGESIZE),
"N_CPUS=%li", sysconf(_SC_NPROCESSORS_ONLN),
NULL);
return 0;
}
Это записывает данные в журнал и добавляет настраиваемые поля, такие как HOME, TERM, PAGE_SIZE и т. Д. Когда я использую journalbeat для отправки их в стек ELK, эти поля в конечном итоге оказываются в эластичном поиске, и я могу искать по ним напрямую.
Однако, похоже, что Docker просто берет стандартный вывод приложений и передает его в journald только с несколькими полями, которые он добавляет сам. например, CONTAINER_ID.
При использовании таких программ внутри контейнера Docker и последующем запуске их из служебного файла это создает небольшую проблему.
1) Я должен пройти через некоторые каталоги и файлы устройств, чтобы заставить его писать с sd_journal_send.
2) если вы запускаете контейнер из файла systemd.service и ожидаете использовать journalctl -u servicename и просматриваете сообщения, эти сообщения журнала не будут видны, поскольку они вошли в журнал по другому маршруту и не будут связаны с служба, которая управляла им.
3) Вы можете добавить несколько произвольных полей / тегов, используя драйвер журналирования докера, они исправлены, одноразовые дополнения, которые будут появляться при каждом отправленном сообщении и остаются неизменными. Они не являются динамическими полями, как я хочу из кода C выше.
По сути, драйвера журнала journald в моем случае недостаточно.
Любые предложения о том, как связать имя службы так, чтобы journalctl -u отображал сообщения журнала от sd_journal_send? как это бы исправить это тогда.
- Я нашел решение. Я приведу ответ ниже на тот случай, если другим будет интересно, как я это решил.
2 ответа
Вам нужно смонтировать розетку, на которой journald
слушает. В случае Ubuntu это /run/systemd/journal/socket
, Отобразите этот инсайдерский контейнер, и он будет работать нормально.
Понял это, используя strace в вашем примере кода
sendmsg(3, {msg_name(29)={sa_family=AF_LOCAL, sun_path="/run/systemd/journal/socket"},
msg_iov(23)=[{"CODE_FILE=test.c", 16}, {"\n", 1}, {"CODE_LINE=13", 12}, {"\n", 1}, {"CODE_FUNC=main", 14}, {"\n", 1},
{"MESSAGE=Hello World!", 20}, {"\n", 1}, {"MESSAGE_ID=52fb62f99e2c49d89cfbf"..., 43}, {"\n", 1}, {"PRIORITY=5", 10}, {"\n", 1},
{"HOME=/home/vagrant", 18}, {"\n", 1}, {"TERM=xterm-256color", 19}, {"\n", 1}, {"PAGE_SIZE=4096", 14}, {"\n", 1},
{"N_CPUS=1", 8}, {"\n", 1}, {"SYSLOG_IDENTIFIER=", 18}, {"a.out", 5}, {"\n", 1}], msg_controllen=0, msg_flags=0}, MSG_NOSIGNAL) = 208
И проверить это в Ubuntu Docker контейнере, используя ниже
docker run -v /run/systemd/journal/socket:/run/systemd/journal/socket -v $PWD:/jd -it -w /jd ubuntu:16.04 ./a.out
И я получаю запись в journalctl -f
(на хосте)
Aug 15 21:40:33 vagrant a.out[11263]: Hello World!
Окончательное решение оказалось действительно простым.
Я перешел к написанию своих сообщений как чистый JSON. Итак, journalctl -u теперь работает и показывает поле MESSAGE, теперь содержащее данные json.
Затем я использовал journalbeat, чтобы отправить это в logstash.
В logstash.conf я добавил:
filter {
json {
source => "message"
}
}
Что он делает, так это расширяет данные json из поля сообщения в отдельные поля верхнего уровня перед отправкой их в asticsearch.
Подробности о фильтре JSON для logstash можно найти здесь