Структурированное ведение журнала в журнал из контейнера 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 можно найти здесь

Другие вопросы по тегам