News Ticker

Docker logging: cosa fa il mio container? [parte 3]

Nelle scorse puntate abbiamo visto come Docker, nella sua configurazione di default, si comporta loggando lo standard output e lo standard error in un file json e come farlo loggare su journald, iniziando a mantenere una persistenza (seppur locale) dei log generati dai nostri container. Successivamente abbiamo preparato un cluster Swarm di laboratorio per iniziare a vedere come è possibile istruire Docker loggando su sistemi remoti, ed abbiamo analizzato il classico logging via syslog ed un metodo di logging remoto chiamato GELF, un pochino più pensato ai container e che ci fornisce informazioni aggiuntive.

Per ulteriori dettagli a riguardo vi rimandiamo ai precedenti articoli:

 

Il laboratorio

Per eseguire i test andremo ad utilizzare lo stesso laboratorio che abbiamo usato nella seconda parte di questa serie. Dobbiamo però leggermente modificare la macchina esterna (exthost) per darle qualche risorsa in più e per esporre alcune porte al computer che stiamo usando. Questo perchè i sistemi di logging che vedremo in questo episodio richiedono una certa infrastruttura (non preoccupatevi, faremo girare tutto in Docker) che necessità di qualche risorsa in più.

Quindi, se avete già avviato il laboratorio tramite Vagrant, fermate solo la macchina exthost:

$ vagrant halt exthost
==> exthost: Attempting graceful shutdown of VM...

apriamo il nostro Vagrantfile e modifichiamo l'ultima sezione:

  # External host (usefull for testing other services)
  config.vm.define "exthost" do |exthost|
    exthost.vm.hostname = "exthost"
    exthost.vm.box = "centos/7/docker"
    ## Server network
    exthost.vm.network "private_network", ip: "172.30.0.20"
  end

Qui dobbiamo aggiungere un paio di righe: una che indica a Vagrant di utilizzare 2GB di RAM per questa VM (l'impostazione che applicavamo di default era di 1GB) e l'altra che inoltra una porta della VM sul nostro client locale, questo per permetterci di raggiungere da browser le console delle applicazione che andremo ad installare. La sezione di exthost sarà dunque così:

  # External host (usefull for testing other services)
  config.vm.define "exthost" do |exthost|
    exthost.vm.hostname = "exthost"
    exthost.vm.box = "centos/7/docker"
    ## More RAM
    exthost.vm.provider "virtualbox" do |vb|
      vb.memory = "2048"
    end
    ## Server network
    exthost.vm.network "private_network", ip: "172.30.0.20"
    ## Forwarding ports
    exthost.vm.network "forwarded_port", guest: 9000, host: 9000
  end

Ora distruggiamo e ricreiamo l'host:

$ vagrant destroy exthost
    exthost: Are you sure you want to destroy the 'exthost' VM? [y/N] y
==> exthost: Destroying VM and associated drives...
$ vagrant up exthost
Bringing machine 'exthost' up with 'virtualbox' provider...
==> exthost: Importing base box 'centos/7/docker'...
...

Siamo pronti ad incominciare!

 

GELF (ancora)?

La scorsa puntata abbiamo visto GELF, ovvero Graylog Extended Log Format. Questo formato, oltre a contenere i log del nostro container (standard output e standard error), fa si che Docker possa inviare senza alcun bisogno di intervento da parte nostra, informazioni aggiuntive specifiche per il mondo dei container. Ovviamente, il modo più semplice per ricevere ed utilizzare questi log è proprio quello di utilizzare Graylog.

Il progetto è ben documentato, sia per quanto riguarda l'installazione che la configurazione; la cosa interessante è che nella documentazione ufficiale vengono fornite sia una VM da poter utilizzare con il software già installato e configurato, che le istruzioni per provisionare la soluzione tramite Docker.

Graylog, per il suo funzionamento, necessità di 3 componenti separate:

  • MongoDB: un database orientato ai documenti, in cui Graylog scrive i dati ricevuti
  • Elasticsearch: un motore di ricerca ed indicizzazione, utilizzato da Graylog per eseguire rapidamente query sui dati collezionati
  • Graylog: ovviamente, ci servirà il software stesso

Tutte e tre le soluzioni sono ben supportate su ambienti Docker, quindi potremmo procedere all'installazione delle stesse tramite una sequenza di comandi Docker. Ma aspettate un attimo; queste tre componenti devono lavorare insieme, i parametri sono ben definiti, perchè non utilizzare Docker Compose e lasciare che il sistema lavori per noi?

Colleghiamoci quindi all'host del laboratorio esterno al nostro cluster ed iniziamo a scrivere il nostro file di compose:

$ vagrant ssh exthost
Last login: Thu May 11 08:05:26 2017 from 10.0.2.2
[vagrant@exthost ~]$ vi docker-compose.yaml

Il file conterrà questo:

version: '2'
services:
  mongo:
    image: "mongo:3"
  elasticsearch:
    image: "elasticsearch:2"
    command: "elasticsearch -Des.cluster.name='graylog'"
  graylog:
    image: graylog2/server:2.2.1-1
    environment:
      GRAYLOG_WEB_ENDPOINT_URI: http://127.0.0.1:9000/api
    depends_on:
      - mongo
      - elasticsearch
    ports:
      - "9000:9000"
      - "5555:5555"
      - "5555:5555/udp"

Da notare la parte relativa alle porte. L'interfaccia web di gestione di Graylog gira sulla porta 9000, quindi il primo valore (9000:9000) non sta facendo altro che mappare quella porta sull'exthost con l'equivalente all'interno del container. Se ricordate abbiamo istruito Vagrant per inoltrare la porta 9000 del nostro host alla porta 9000 dell'host exthost di laboratorio. Fondamentalmente quello che abbiamo fatto è creare questa catena:

Computer:9000 <-> exthost(vagrant):9000 <-> graylog(container):9000

Inoltre, come vedremo successivamente, Graylog può essere configurato per ricevere i log su diverse porte, ma essendo confinato all'interno di un container dobbiamo definire a priori quali porte saranno raggiungibili dall'esterno di esso. Abbiamo quindi optato per la porta 5555 (sia tcp che udp) per la ricezione dei log.

Le istruzioni presenti nel file docker-compose.yaml che abbiamo visto sono l'equivalente di lanciare i seguenti comandi sull'exthost:

[vagrant@exthost ~]$ docker run -d mongo:3
...
[vagrant@exthost ~]$ docker run -d elasticsearch:2 -Des.cluster.name='graylog'
...
[vagrant@exthost ~]$ docker run -d -e GRAYLOG_WEB_ENDPOINT_URI='http://127.0.0.1:9000/api' -p 9000:9000 -p 5555:5555 -p 5555:5555/udp graylog2/server:2.2.1-1
...

Il vantaggio di utilizzare Docker Compose è che è facilmente portabile tra diversi host Docker (oltre al fatto che è possibile anche darlo in pasto ad uno Swarm per creare un servizio in cluster).

Installiamo dunque Docker Compose:

[vagrant@exthost ~]$ curl -L https://github.com/docker/compose/releases/download/1.13.0/docker-compose-`uname -s`-`uname -m` > docker-compose
...
[vagrant@exthost ~]$ sudo cp docker-compose /usr/local/bin/
[vagrant@exthost ~]$ rm docker-compose
[vagrant@exthost ~]$ sudo chmod 755 /usr/local/bin/docker-compose

*NOTA BENE* Utilizziamo questo metodo di installazione, seppur trattandosi di quello ufficiale fornito da Docker, perchè ci troviamo in un laboratorio. In ambienti di produzione consigliamo di evitare di scaricare ed eseguire un binario senza prima aver verificato cosa questo comporti!

Lasciamo quindi fare a Compose il suo lavoro:

[vagrant@exthost ~]$ sudo /usr/local/bin/docker-compose up
Creating vagrant_elasticsearch_1 ... 
Creating vagrant_mongo_1 ... 
Creating vagrant_elasticsearch_1
Creating vagrant_elasticsearch_1 ... done
Creating vagrant_graylog_1 ... 
Creating vagrant_graylog_1 ... done
...
graylog_1        | 2017-05-11 09:16:49,257 INFO : org.graylog2.bootstrap.ServerBootstrap - Graylog server up and running.

Terminato l'avvio, colleghiamoci con il nostro browser alla porta 9000 della nostra macchina ed alla richiesta di login inseriamo admin:admin. Siamo ora pronti per iniziare a ricevere i log:

Una volta loggati, dobbiamo istruire Graylog per ricevere i log sulla porta 5555 in tcp ed udp (vi ricordate che abbiamo configurato l'inoltro di quelle porte?).

Nella sezione in alto selezioniamo quindi System -> Inputs ed aggiungiamo un nuovo input globale che riceva i log di tipo GELF su quelle specifiche porte (selezionando due input, GELF TCP e GELF UPD). La situazione finale sarà la seguente:

Per fare un primo test possiamo lanciare, sempre sulla macchine exthost, un container che emetta una singola riga di output e che mandi il log tramite GELF al graylog. Apriamo un'altra console sulla macchina e lanciamo:

[vagrant@exthost ~]$ docker run --log-driver gelf --log-opt gelf-address=udp://localhost:5555 --log-opt gelf-compression-type=none alpine echo "Test log GELF UDP"
Test log GELF UDP

Dalla pagina di ricerca di Graylog vediamo che ha loggato correttamente il nostro messaggio, insieme ad altre informazioni fornite dal Docker:

Ottimo, possiamo quindi collegarci al nostro cluster Swarm e lanciare un servizio che logghi da diversi nodi:

$ vagrant ssh snode1
[vagrant@snode1 ~]$ docker service create --name 'ping-mmul' --replicas 5 --log-driver gelf --log-opt gelf-address=udp://172.30.0.20:5555 --log-opt gelf-compression-type=none alpine ping -c 10 www.miamammausalinux.org
f569wo1izejqhw5p42doa1wqq

Quello che abbiamo fatto è stato creare il servizio 'ping-mmul' composto da 5 repliche, ognuna delle quali genera un container con immagine alpine che fa 10 ping verso www.miamammausalinux.org. Dopo questi ping il container muore, ma essendo gestito da un servizio (che si ritroverà con 4 repliche su 5) ne verrà deployato uno nuovo che eseguirà la stessa operazione:

[vagrant@snode1 ~]$ docker service ls
ID            NAME       MODE        REPLICAS  IMAGE
f569wo1izejq  ping-mmul  replicated  5/5       alpine:latest
[vagrant@snode1 ~]$ docker service ls
ID            NAME       MODE        REPLICAS  IMAGE
f569wo1izejq  ping-mmul  replicated  2/5       alpine:latest

Lasciamo girare qualche secondo dopodichè terminiamo il servizio

[vagrant@snode1 ~]$ docker service rm ping-mmul
ping-mmul

Giusto per completezza, lanciamo un altro servizio identico ma che pinghi verso google:

[vagrant@snode1 ~]$ docker service create --name 'ping-google' --replicas 5 --log-driver gelf --log-opt gelf-address=udp://172.30.0.20:5555 --log-opt gelf-compression-type=none alpine ping -c 10 www.google.it
f69ldpogegaa0hhb55adkp602
[vagrant@snode1 ~]$ sleep 5
[vagrant@snode1 ~]$ docker service rm ping-google
ping-google

Andiamo a vedere la console di Graylog e vediamo che, nel mio caso, abbiamo ricevuto ben 351 messaggi negli ultimi 30 minuti:

Come dicevamo, il formato GELF contiene diversi dettagli, e questo in soluzioni quali Graylog ci viene in aiuto soprattutto per la ricerca ed il filtraggio dei dati. Ad esempio, senza aver impostato alcun tipo di tag (cosa buona e giusta quando si lavora con i servizi su cluster Swarm), il comportamento di Docker è quello di far precedere al nome "randomico" dei container generati per la gestione di un servizio il nome del servizio stesso". Ed, il nome del container, è uno dei campi che out-of-the-box Docker invia quando si utilizza GELF. Questo ci permette, dunque, di filtrare -ad esempio- solo i log provenienti dal primo test di ping che abbiamo effettuato:

Ovviamente poi Graylog ci permette di avere ricerche salvate, dashboard e tanto altro, ma questo dimostra ancora una volta che l'utilizzo di un sistema come GELF può portare benefici permettendo di filtrare non solo per i contenuti dei log, ma anche per la nostra infrastruttura Docker di erogazione!

 

Fluentd

Un altro driver di log di Docker molto interessante è fluentd. Prende il nome dall'omonimo progetto, che altro non è che un data collector con un funzionamento a plugin.

A livello di concetto generale, Fluentd non è molto diverso da Logstash: una serie di plugin di input permettono di inviargli dati in diversi formati, si posso applicare delle regole che filtrano questi dati in ingresso, e tramite dei plugin di output è possibile fare diverse cose, dalla scrittura di file, all'invio di notifiche in sistemi di monitoraggio esterno, all'inserimento di particolari dati in sistemi di stoccaggio ed analisi (come un database o Elasticsearch).

Per prima cosa, comunque, resettiamo la nostra macchina "esterna" che, per l'occasione, ospiterà il servizio fluentd:

[vagrant@exthost ~]$ exit
logout
Connection to 127.0.0.1 closed.
$ vagrant destroy exthost
    exthost: Are you sure you want to destroy the 'exthost' VM? [y/N] y
==> exthost: Forcing shutdown of VM...
==> exthost: Destroying VM and associated drives...
$ vagrant up exthost
Bringing machine 'exthost' up with 'virtualbox' provider...
...

Per prima cosa sara' interessante vedere cosa ci arriva da Docker impostando questo driver di logging. Mettiamoci in ascolto su una porta qualsiasi (abbastanza alta):

[vagrant@exthost ~]$ nc -l 1234

e da un altra console proviamo a mandare un log secco usando questo driver:

[vagrant@exthost ~]$ docker run --log-driver fluentd --log-opt fluentd-address=localhost:1234 --log-opt tag="test" alpine echo "Ciao"
Ciao

Se andiamo a vedere l'output della netcat, vedremo qualcosa di simile a questo:

??test?Y\}??log?Ciao?container_id?@adb4db8160ec674c829b8a464f07cd6dac02ad264ceb8a6aeec8951bc19c5427?container_name?/priceless_lewin?source?stdout?

Qualcosa di leggibile, ma di non facilmente interpretabile (almeno da un umano). Per interpretare questo ci viene in aiuto proprio Fluentd. Scriviamo un semplice file di configurazione che utilizzeremo per il test (l'ho chiamato fluentd.conf):

<source>
  @type http
  port 1234
  bind 0.0.0.0
</source>
<match **>
  @type stdout
</match>

Questo file di configurazione indica che Fluentd si aspetta una chiamata sulla porta 1234 in formato http e che, indipendentemente da quello che contiene, scrive il messaggio sullo standard output. Avviamo dunque il demone in container, ovviamente:

[vagrant@exthost ~]$ sudo docker run -p 1234:1234 -v /home/vagrant:/fluentd/etc -e FLUENTD_CONF=fluentd.conf fluent/fluentd
Unable to find image 'fluent/fluentd:latest' locally
...
2017-05-11 12:55:57 +0000 [info]: reading config file path="/fluentd/etc/fluentd.conf"
2017-05-11 12:55:57 +0000 [info]: starting fluentd-0.12.35
2017-05-11 12:55:57 +0000 [info]: gem 'fluentd' version '0.12.35'
2017-05-11 12:55:57 +0000 [info]: adding match pattern="**" type="stdout"
2017-05-11 12:55:57 +0000 [info]: adding source type="http"
2017-05-11 12:55:57 +0000 [info]: using configuration file: 
  <source>
    @type http
    port 1234
    bind 0.0.0.0
  </source>
  <match **>
    @type stdout
  </match>
</ROOT>

Abbiamo il servizio attivo con il nostro file di configurazione, olè. Proviamo adesso fargli una semplicissima chiamata http per vedere cosa viene loggato:

[vagrant@exthost ~]$ curl -X POST -d 'json={"json":"Prova invio"}' http://localhost:1234/sample.test

ed in output da Fluentd abbiamo questo:

2017-05-11 12:57:55 +0000 sample.test: {"json":"Prova invio"}

Funziona, dunque, ora cerchiamo di capire come configurare Fluentd per ricevere i file da Docker. Questa operazione è, in realtà, molto semplice perchè visto che Docker ragiona già nella lingua di Fluentd, a quest'ultimo basta prendere i log ed utilizzarli così come sono. Riapriamo il nostro file di configurazione e modifichiamolo in questo modo:

<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>
<match **>
  @type stdout
</match>

Ho anche cambiato la porta impostando quella di default di Fluentd (la 24224). In questo modo possiamo semplificare le opzioni passate al container quando gli diciamo di utilizzare questo driver di logging. Riavviamo il container fluentd verificando dall'output che sia stato caricata la conf corretta:

[vagrant@exthost ~]$ sudo docker run -p 24224:24224 -v /home/vagrant:/fluentd/etc -e FLUENTD_CONF=fluentd.conf fluent/fluentd
...
2017-05-11 13:07:18 +0000 [info]: listening fluent socket on 0.0.0.0:24224

Testiamo quindi subito cosa ci arriva da una chiamata di test proveniente da un container:

[vagrant@exthost ~]$ docker run --log-driver fluentd alpine echo "Test"
Test

e guardiamo l'output del container fluentd:

2017-05-11 13:08:18 +0000 c8d467aaa06f: {"container_name":"/nervous_lichterman","source":"stdout","log":"Test","container_id":"c8d467aaa06f2231af3c39afcecd8623e604291af0fa681919a275e15cb77738"}

Come vediamo, il formato di output del log è chiaramente un json e, come già visto in precedenza con GELF, anche in questo caso Docker aggiunge una serie di informazioni a quello che è il mero output del comando. Interessante.

Vediamo però cosa succede quando, invece di un singolo container, lanciamo un servizio su Swarm; colleghiamoci ad snode1 e lanciamo il nostro solito ping:

[vagrant@snode1 ~]$ sudo docker service create --name 'ping-mmul' --replicas 1 --log-driver fluentd --log-opt fluentd-address=172.30.0.20:24224 alpine ping www.miamammausalinux.org ; sleep 2 ; sudo docker rm ping-mmul
ze94gwf1dyxql1l42xk4zv14o
ping-mmul
[vagrant@snode1 ~]$

Il log su Fluentd non è molto più parlante rispetto a prima:

2017-05-11 13:24:15 +0000 2e0d10bfdea8: {"source":"stdout","log":"PING www.miamammausalinux.org (46.4.105.174): 56 data bytes","container_id":"2e0d10bfdea81a56c2fde4146e4f5e699494f8b56c30ca596e8101692a48d039","container_name":"/ping-mmul.1.a8u2myddnbjtumgs7ctda59a9"}
2017-05-11 13:24:16 +0000 2e0d10bfdea8: {"container_name":"/ping-mmul.1.a8u2myddnbjtumgs7ctda59a9","source":"stdout","log":"64 bytes from 46.4.105.174: seq=0 ttl=61 time=30.806 ms","container_id":"2e0d10bfdea81a56c2fde4146e4f5e699494f8b56c30ca596e8101692a48d039"}
2017-05-11 13:24:17 +0000 2e0d10bfdea8: {"source":"stdout","log":"64 bytes from 46.4.105.174: seq=1 ttl=61 time=23.749 ms","container_id":"2e0d10bfdea81a56c2fde4146e4f5e699494f8b56c30ca596e8101692a48d039","container_name":"/ping-mmul.1.a8u2myddnbjtumgs7ctda59a9"}
2017-05-11 13:24:18 +0000 2e0d10bfdea8: {"container_id":"2e0d10bfdea81a56c2fde4146e4f5e699494f8b56c30ca596e8101692a48d039","container_name":"/ping-mmul.1.a8u2myddnbjtumgs7ctda59a9","source":"stdout","log":"64 bytes from 46.4.105.174: seq=2 ttl=61 time=25.991 ms"}

Quello che anticipavamo è che Fluentd lavora molto con il tagging sui log. Se proviamo quindi a lanciare un servizio aggiungendo il tag "Test":

[vagrant@snode1 ~]$ sudo docker service create --name 'ping-mmul' --replicas 1 --log-driver fluentd --log-opt fluentd-address=172.30.0.20:24224 --log-opt tag="Test" alpine ping www.miamammausalinux.org ; sleep 2 ; sudo docker service rm ping-mmul
igdo5qu379vanxa5mbydte60f
ping-mmul

Vedremo che la entry mostrata da Fluentd conterrà questa informazione:

2017-05-11 14:15:06 +0000 Test: {"log":"PING www.miamammausalinux.org (46.4.105.174): 56 data bytes","container_id":"36f7721c22f7f64fa2e69734c2c4ca2a282223626ec0cc2a1e5870a177b2937f","container_name":"/ping-mmul.1.8yd0nlb1mdf0d0hntd9stcajg","source":"stdout"}

Notiamo dunque che il tag da noi applicato è visibile nella riga. Sono inoltre disponibili delle "variabili" per popolare il tag con alcune informazioni provenienti direttamente da Docker. Queste variabili sono:

  • {{.ID}}: aggiunge l'ID (nel formato 'corto' di default)
  • {{.FullID}}: in questo caso viene aggiunto l'intero ID
  • {{.Name}}: questo aggiunge il nome del container

Possiamo quindi aggiungere qualche informazione. Sfruttando appunto i tag possiamo, ad esempio istruire Fluentd per raccogliere i log da diversi servizi Docker ed, ad esempio, separare l'output in file differenti sul sistema.

Pensiamo, ad esempio, alla classica coppia di servizi "ping-google" e "ping-mmul" che abbiamo utilizzato in questi articoli, e vediamo come modificare di conseguenza la configurazione di Fluentd:

<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>
<match ping-mmul.**>
  @type file
  path /home/fluent/ping-mmul.log
</match>
<match ping-google.**>
  @type file
  path /home/fluent/ping-google.log
</match>

Questo andrà a creare, all'interno del container in cui facciamo girare Fluentd, due differenti file di log ognuno contenente solo i log taggati con il relativo nome del servizio.

Lanciamo quindi al solito fluentd:

[vagrant@exthost ~]$ sudo docker run -p 24224:24224 -v /home/vagrant:/fluentd/etc -e FLUENTD_CONF=fluentd.conf fluent/fluentd
2017-05-11 14:51:10 +0000 [info]: reading config file path="/fluentd/etc/fluentd.conf"
...
2017-05-11 14:51:10 +0000 [info]: listening fluent socket on 0.0.0.0:24224

E, collegandoci all'host snode1, procediamo a lanciare i due servizi, andandoli a taggare di conseguenza:

[vagrant@snode1 ~]$ sudo docker service create --name 'ping-mmul' --replicas 3 --log-driver fluentd --log-opt fluentd-address=172.30.0.20:24224 --log-opt tag="ping-mmul.{{.ID}}" alpine ping www.miamammausalinux.org
coph59gc5bc6yhnv2r9u97qtg
[vagrant@snode1 ~]$ sudo docker service create --name 'ping-google' --replicas 3 --log-driver fluentd --log-opt fluentd-address=172.30.0.20:24224 --log-opt tag="ping-google.{{.ID}}" alpine ping www.google.it
knf1n1pzz7grrnh9hxmt5bk8i
[vagrant@snode1 ~]$ sudo docker service ls
ID            NAME         MODE        REPLICAS  IMAGE
coph59gc5bc6  ping-mmul    replicated  3/3       alpine:latest
knf1n1pzz7gr  ping-google  replicated  3/3       alpine:latest

Come vede, abbiamo utilizzato la variabile {{.ID}} per aggiungere informazioni al nostro tagging. Torniamo sulla macchina exthost ed apriamoci una console sul container fluentd in esecuzione:

[vagrant@exthost ~]$ sudo docker exec -it 7fc3d5319188 /bin/sh
~ $ pwd
/home/fluent
~ $ ls -l
total 76
-rw-r--r--    1 fluent   fluent       35157 May 11 14:53 ping-google.log.20170511.b54f40bfce49ca61f
-rw-r--r--    1 fluent   fluent       38969 May 11 14:53 ping-mmul.log.20170511.b54f40bf84cde9e5a

I valori che vengono appesi al nome del file inserito nella configurazione di Fluentd sono determinati da delle opzioni del plugin di output file. Per i dettagli sui parametri configurabili vi rimandiamo alla documentazione del plugin, a noi basta vedere che nei due file arrivano i log dei relativi servizi, ed abbiamo separato le cose:

~ $ tail -5 ping-google.log.20170511.b54f40bfce49ca61f 
2017-05-11T14:55:46+00:00	ping-google.21e20ef2a68c	{"container_id":"21e20ef2a68c3623853af1bc86a44b0873fd27ecbabf5e18b6a77241ce8ea3c3","container_name":"/ping-google.2.zj1pjhch3zj9ktszyhlyfoc6o","source":"stdout","log":"64 bytes from 216.58.198.3: seq=148 ttl=61 time=6.897 ms"}
2017-05-11T14:55:46+00:00	ping-google.e26cae967c5d	{"container_id":"e26cae967c5dab27be6b1ca8d551781660f46c5d8aa284222b21a0400497c358","container_name":"/ping-google.3.qtpndte7jerbszal0snroxry5","source":"stdout","log":"64 bytes from 216.58.198.3: seq=148 ttl=61 time=4197943.069 ms"}
2017-05-11T14:55:47+00:00	ping-google.68d5d26af147	{"source":"stdout","log":"64 bytes from 216.58.198.3: seq=149 ttl=61 time=7.559 ms","container_id":"68d5d26af1478e3e604067d106389b254751d095e9eae0784bae6678cfaa1519","container_name":"/ping-google.1.1qk9sc7ma4uqohxj4robxvcbb"}
2017-05-11T14:55:47+00:00	ping-google.e26cae967c5d	{"log":"64 bytes from 216.58.198.3: seq=149 ttl=61 time=4197963.793 ms","container_id":"e26cae967c5dab27be6b1ca8d551781660f46c5d8aa284222b21a0400497c358","container_name":"/ping-google.3.qtpndte7jerbszal0snroxry5","source":"stdout"}
2017-05-11T14:55:47+00:00	ping-google.21e20ef2a68c	{"container_id":"21e20ef2a68c3623853af1bc86a44b0873fd27ecbabf5e18b6a77241ce8ea3c3","container_name":"/ping-google.2.zj1pjhch3zj9ktszyhlyfoc6o","source":"stdout","log":"64 bytes from 216.58.198.3: seq=149 ttl=61 time=27.612 ms"}

e, di conseguenza:

~ $ tail -5 ping-mmul.log.20170511.b54f40bf84cde9e5a 
2017-05-11T14:55:57+00:00	ping-mmul.aa27701d332d	{"container_id":"aa27701d332dcb4dd8bfbc78ba4395f95b8155a66a5962914445c73a9e8b7936","container_name":"/ping-mmul.3.xevooez2050y1buuvpfzj0bri","source":"stdout","log":"64 bytes from 46.4.105.174: seq=164 ttl=61 time=24.146 ms"}
2017-05-11T14:55:57+00:00	ping-mmul.5ebdba9484b6	{"container_id":"5ebdba9484b6af561ff2a248b87c6636d0c77b14846ad878126d6fc3ee4a5e26","container_name":"/ping-mmul.2.ev25k03yfr4844tlai61njuwu","source":"stdout","log":"64 bytes from 46.4.105.174: seq=164 ttl=61 time=24.303 ms"}
2017-05-11T14:55:58+00:00	ping-mmul.26fafca91026	{"container_name":"/ping-mmul.1.ltycr74imotyja3bnb86n46gp","source":"stdout","log":"64 bytes from 46.4.105.174: seq=165 ttl=61 time=24.207 ms","container_id":"26fafca91026386db96c23e093bf4e40629644992280de7aeeb923e8b4be3055"}
2017-05-11T14:55:58+00:00	ping-mmul.aa27701d332d	{"container_id":"aa27701d332dcb4dd8bfbc78ba4395f95b8155a66a5962914445c73a9e8b7936","container_name":"/ping-mmul.3.xevooez2050y1buuvpfzj0bri","source":"stdout","log":"64 bytes from 46.4.105.174: seq=165 ttl=61 time=23.996 ms"}
2017-05-11T14:55:58+00:00	ping-mmul.5ebdba9484b6	{"container_id":"5ebdba9484b6af561ff2a248b87c6636d0c77b14846ad878126d6fc3ee4a5e26","container_name":"/ping-mmul.2.ev25k03yfr4844tlai61njuwu","source":"stdout","log":"64 bytes from 46.4.105.174: seq=165 ttl=61 time=26.388 ms"}

come potete notare dai tagging, i log sono stati correttamente separati.

Ora, sicuramente il fatto di scrivere i log direttamente su file (per altro non persistenti, essendo all'interno di un container) lascia un po' il tempo che trova, ma come dicevamo Fluentd è un sistema molto potente e strutturato a plugin. Potremmo fare diverse cose, come riscrivere pezzi di log per riorganizzarli e mandarli ad un Elasticsearch per indicizzarli, piuttosto che mandarli ad un InfluxDB per salvarli... il limite è solo l'immaginazione, la lista dei plugin supportati è decisamente vasta.

 

Conclusione

Docker dispone di altri driver di logging, pensati per casistiche più specializzate e/o orientate al logging su sistemi cloud. Possiamo utilizzare il driver awslogs se nel nostro ambiente vogliamo appoggiarci a CloudWatch di Amazon, oppure se preferiamo i servizi offerti da Google gcplogs ci permette di inviare i dati direttamente al Cloud Platform Logging che mette a disposizione.

Le soluzioni sono molteplici, tutto dipende da quello che vogliamo -o che dobbiamo- fare. Se avete voglia di studiare e maneggiare un pochino di linguaggio Go, potete sempre implementarvene uno fatto in casa, ricordiamoci che uno dei benefici di Docker è di essere un progetto open.

Si concludono qui gli articoli riguardanti il logging di Docker, speriamo di avervi fornito degli spunti e le basi per implementare la miglior soluzione possibile di log per le vostre esigenze.