DevOps

Forwarding Logs From All Containers Running Anywhere Inside A Docker Swarm Cluster

In this article, we’ll discuss a way to forward logs from containers created as Docker Swarm services inside our clusters. We’ll use the ELK stack. They’ll be forwarded from containers to LogStash and, from there, to ElasticSearch. Once in the database, they will be available through Kibana.

Environment Setup

We’ll start by creating a Docker Swarm cluster. I will assume you already have at least a basic knowledge how Docker Swarm Mode works and that you know how to create Docker services. If you don’t, I suggest you read the Docker Swarm Introduction (Tour Around Docker 1.12 Series) article or fetch The DevOps 2.1 Toolkit: Docker Swarm book.

Some of the files will be shared between the host file system and Docker Machines we’ll create soon. Docker Machine makes the whole directory that belongs to the current user available inside the VM. Therefore, please make sure that the code is cloned inside one of the user’s sub-folders.

A note to Windows users

The recommendation is to run all the examples from Git Bash (installed through Docker Toolbox as well as Git). That way the commands you’ll see throughout the book will be same as those that should be executed on OS X or any Linux distribution.

git clone https://github.com/vfarcic/cloud-provisioning.git

cd cloud-provisioning

scripts/dm-swarm.sh

We cloned the cloud-provisioning repository and executed the scripts/dm-swarm.sh script that created the production cluster.

Let’s confirm that the cluster was indeed created correctly.

eval $(docker-machine env swarm-1)

docker node ls

The output of the node ls command is as follows (IDs are removed for brevity).

HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
swarm-2   Ready   Active        Reachable
swarm-1   Ready   Active        Leader
swarm-3   Ready   Active        Reachable

Now that the production cluster is up and running, we can create ELK services.

scripts/dm-swarm-services-elk.sh

That’s it. We should have a few services running inside the Swarm cluster. Let’s double check it.

docker service ls

The output is as follows (IDs are removed for brevity).

NAME            REPLICAS  IMAGE                               COMMAND
swarm-listener  1/1       vfarcic/docker-flow-swarm-listener
logstash        1/1       logstash:2.4                        logstash -f /conf/logstash.conf
elasticsearch   1/1       elasticsearch:2.4
kibana          1/1       kibana:4.6
proxy           1/1       vfarcic/docker-flow-proxy

We are, finally, ready to explore how to ship logs from our Swarm services to LogStash, and from there ElasticSearch.

Forwarding Logs To LogStash

How can we forward logs from all the containers no matter where they’re running? One possible solution would be to configure logging drivers. We could use --log-driver argument to specify a driver for each service. The driver could be syslog or any other supported option. That would solve our log shipping problem. However, using the argument for each service is tedious and, more importantly, we could easily forget to specify it for a service or two and discover the omission only after we encounter a problem and are in need for logs. Let’s see if there is another option to accomplish the same result.

We could specify a log driver as a configuration option of the Docker daemon on each node. That would certainly make the setup easier. After all, there are probably fewer servers than services. If we were to choose between setting a driver when creating a service or as the daemon configuration, I’d choose the later. However, we managed to get thus far without changing the default daemon configuration and I’d prefer if we can continue working without involving any special provisioning tools. Luckily, we still did not exhaust all our options.

We can ship logs from all our containers with the project called logspout.

LogSpout is a log router for Docker containers that runs inside Docker. It attaches to all containers on a host, then routes their logs wherever we want. It also has an extensible module system. It’s a mostly stateless log appliance. It’s not meant for managing log files or looking at history. It is just a tool to get your logs out to live somewhere else, where they belong.

If you go through the project documentation, you’ll notice that there are no instructions how to run it as a Docker service. That should not matter since, by this time, you can consider yourself an expert in creating services.

What do we need from a service that should forward logs from all the containers running inside all the nodes that form a cluster? Since we want to forward them to LogStash that is already attached to the elk network, we should attach LogSpout to it as well. We need it to ship logs from all the nodes so the service should be global. It needs to know that the destination is the service called logstash and that it listens on the port 51415. Finally, one of the LogSpout’s requirements is that Docker socket from the host is mounted inside the service containers. It’ll use it to monitor logs.

The command that creates the service that fulfills all those objectives and requirements is as follows.

docker service create --name logspout \
    --network elk \
    --mode global \
    --mount "type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock" \
    -e SYSLOG_FORMAT=rfc3164 \
    gliderlabs/logspout syslog://logstash:51415

We created a service called logspout, attached it to the elk network, set it to be global, and mounted the Docker socket. The command that will be executed once containers are created is syslog://logstash:51415. It tells LogSpout that we want to use syslog protocol to send logs to logstash running on port 51415.

This project is an example of a usefulness behind Docker Remote API. The logspout containers will use it to retrieve the list of all currently running containers and stream their logs. This is already the second product inside our cluster that uses the API (the first being Docker Flow: Swarm Listener).

Let’s see the status of the service we just created.

docker service ps logspout

The output is as follows (IDs are removed for brevity).

NAME          IMAGE                NODE     DESIRED STATE  CURRENT STATE          ERROR
logspout      gliderlabs/logspout  swarm-3  Running        Running 9 seconds ago
 \_ logspout  gliderlabs/logspout  swarm-2  Running        Running 9 seconds ago
 \_ logspout  gliderlabs/logspout  swarm-1  Running        Running 9 seconds ago

The service is running in the global mode resulting in an instance inside each node.

Let’s test whether the logspout service is indeed sending all the logs to LogStash. All we have to do is create a service that generates some logs and observe them from LogStash’ output. We’ll use the registry to test the setup we made so far.

docker service create --name registry \
    -p 5000:5000 \
    --mount "type=bind,source=$PWD,target=/var/lib/registry" \
    --reserve-memory 100m \
    registry

Before we check the LogStash logs, we should wait until the registry is running.

docker service ps registry

If the current state is still not running, please wait a few moments.

Now we can take a look at logstash logs and confirm that logspout sent it log entries generated by the registry.

LOGSTASH_NODE=$(docker service ps logstash | tail +2 | awk '{print $4}')

eval $(docker-machine env $LOGSTASH_NODE)

LOGSTASH_ID=$(docker ps -q --filter "ancestor=logstash:2.4")

docker logs $LOGSTASH_ID

One of the entries from the output is as follows.

{
           "message" => "time=\"2016-10-19T23:14:19Z\" level=info msg=\"listening on [::]:5000\" go.version=go1.6.3 instance.id=87c31e30-a747-4f70-b7c2-396dd80eb47b version=v2.5.1 \n",
          "@version" => "1",
        "@timestamp" => "2016-10-19T23:14:19.000Z",
              "host" => "10.0.0.7",
          "priority" => 14,
     "timestamp8601" => "2016-10-19T23:14:19Z",
         "logsource" => "c51c177bd308",
           "program" => "registry.1.abszmuwq8k3d7comu504lz2mc",
               "pid" => "4833",
          "severity" => 6,
          "facility" => 1,
         "timestamp" => "2016-10-19T23:14:19Z",
    "facility_label" => "user-level",
    "severity_label" => "Informational"
}

As before when we tested LogStash input with logger, we have the message, timestamp, host, and a few other syslog fields. We also got logsource that holds the ID of the container that produced the log as well as program that holds the container name. Both will be useful when debugging which service and container produced a bug.

If you go back to the command we used to create the logstash service, you’ll notice the environment variable LOGSPOUT=ignore. It tells LogSpout that the service or, to be more precise, all containers that form the service, should be ignored. If we did not define it, LogSpout would forward all logstash logs to logstash thus creating an infinite loop. As we already discussed, in production we should not output LogStash entries to stdout. We did it only to get a better understanding how it works. If stdout output is removed from the logstash configuration, there would be no need for the environment variable LOGSPOUT=ignore. As a result logstash logs would also be stored in ElasticSearch.

Now that we are shipping all the logs to LogStash and from there to ElasticSearch, we should explore the ways to consult them. You’ll find them in the kibana service.

open http://$(docker-machine ip swarm-1)/app/kibana

Before leaving, please make sure to remove the machines we created and free your resources for some other tasks.

docker-machine rm -f swarm-1 swarm-2 swarm-3

The article you just finished reading is an extract from the Defining Logging Strategy chapter of The DevOps 2.1 Toolkit: Docker Swarm book.

The DevOps 2.1 Toolkit: Docker Swarm

If you liked this article, you might be interested in The DevOps 2.1 Toolkit: Docker Swarm book. Unlike the previous title in the series (The DevOps 2.0 Toolkit: Automating the Continuous Deployment Pipeline with Containerized Microservices) that provided a general overlook of some of the latest DevOps practices and tools, this book is dedicated entirely to Docker Swarm and the processes and tools we might need to build, test, deploy, and monitor services running inside a cluster.

cover-ebook-small

The book is still under “development”. You can get a copy from LeanPub. It is also available as The DevOps Toolkit Series bundle. If you download it now, before it is fully finished, you will get frequent updates with new chapters and corrections. More importantly, you will be able to influence the direction of the book by sending me your feedback.

I choose the lean approach to book publishing because I believe that early feedback is the best way to produce a great product. Please help me make this book a reference to anyone wanting to adopt Docker Swarm for cluster orchestration and scheduling.

Viktor Farcic

Viktor Farcic is a Software Developer currently focused on transitions from Waterfall to Agile processes with special focus on Behavior-Driven Development (BDD), Test-Driven Development (TDD) and Continuous Integration (CI).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button