Logstash Docker

Page last updated:

Overview

With Logstash you can create pipelines to transform and forward your logs to a log storage e.g. elasticsearch.

Step by Step Guide

This section describes how to setup your Logstash-instance in Cloud Foundry using the official Docker image from Elastic.

Prerequisites

Logstash is the last component to set up in the Elastic Stack. Therefore you need to have an Elasticsearch-Service as well as a running Kibana instance. It’s also required to set up a proper index lifecycle management. - Elasticsearch - Kibana Docker - Index Lifecycle Management

Create the centralized Logstash pipeline in Elasticsearch

To be able to use the upstream Logstash Docker images, we will configure our Logstash pipeline via Elasticsearch. The easiest way of creating the pipeline in Elasticsearch is via Kibana as outlined in https://www.elastic.co/guide/en/logstash/current/logstash-centralized-pipeline-management.html.

If you already have an existing pipeline, you can insert it into Elasticsearch (Kibana->Management->Logstash->Pipelines). If you have been using separate Grok pattern files, those must be included in your pipelines filter section instead of separate files. Also note that the http input port is 5044, as the upstream Docker image exposes the default Beats port.

If this is your fist pipeline, you may find the example pipeline below helpful, to get you started with parsing logs from apps running in Cloud Foundry. Go To Kibana->Management->Logstash->Pipelines and create a new pipeline with a distinguishable id and a pipeline according to this example:

input {
  http {
    port => "5044"
    user => "arbitrary-username"
    password => "arbitrary-password"
  }
}

filter{
  mutate {
    remove_field => ["headers"]
  }
  grok {
    match => { "message" => "%{SYSLOG5424PRI}%{NONNEGINT:syslog5424_ver} +(?:%{TIMESTAMP_ISO8601:syslog5424_ts}|-) +(?:%{DATA:syslog5424_host}|-) +(?:%{NOTSPACE:syslog5424_app}|-) +(?:%{NOTSPACE:syslog5424_proc}|-) +(?:%{WORD:syslog5424_msgid}|-) +(?:%{SYSLOG5424SD:syslog5424_sd}|-|)%{SPACE}%{GREEDYDATA:message}" }
    tag_on_failure => []
    overwrite => ["message"]
    break_on_match => false
  }
  grok {
    match => { "syslog5424_proc" => "^\[(%{WORD:cf_component})?/.+\]$" }
    tag_on_failure => []
    break_on_match => false
  }
  if [cf_component] == "RTR" {
      grok {
        match => { "message" => "%{HOSTNAME:host} - \[%{TIMESTAMP_ISO8601}\] \"%{WORD:http_method} %{URIPATHPARAM:uri_param}  HTTP/%{NUMBER:http_version}\" %{POSINT:http_response} %{NONNEGINT:http_request_size} %{NONNEGINT:http_response_size} \"%{DATA:referrer}\" \"%{DATA:user_agent}\" \"%{IPORHOST:ip_source}:%{POSINT:ip_source_port}\" \"%{IPORHOST:ip_destination}:%{POSINT:ip_destination_port}\" x_forwarded_for\:\"%{DATA:x_forwarded_for}\" x_forwarded_proto:\"%{DATA:x_forwarded_proto}\" vcap_request_id:\"%{UUID:vcap_request_id}\" response_time:%{NUMBER:response_time} gorouter_time:%{NUMBER:gorouter_time} app_id:\"%{UUID:app_id}\" app_index:\"%{NONNEGINT:app_index}\" x_b3_traceid:\"%{DATA:x_b3_traceid}\" x_b3_spanid:\"%{DATA:x_b3_spanid}\" x_b3_parentspanid:\"%{DATA:x_b3_parentspanid}\" b3:\"%{DATA:b3}\"" }
        tag_on_failure => ["PARSE-FAILED"]
        break_on_match => false
      }
      mutate {
        remove_field => ["message"]
      }
  }
  if [cf_component] == "APP" {
    mutate { add_tag => ["CF-APP"] }
    if [message] =~ /^{.*}/ {
          json {
            source => "message"
            add_tag => [ "json", "parsed"]
          }
          mutate {
            remove_field => ["message"]
          }
        }
  } else {
    mutate { add_tag => ["CF-PAAS"] }
  }
  grok {
    match => { "syslog5424_proc" => "^\[APP/PROC/WEB/(%{INT:cf_app_instance_index})?\]$" }
    tag_on_failure => []
    break_on_match => false
  }
  grok {
    match => { "syslog5424_host" => "^%{DATA:cf_org}\.%{DATA:cf_space}\.%{DATA:cf_app}?$" }
    tag_on_failure => []
    break_on_match => false
  }
  mutate { add_field => { "[@metadata][target_index]" => "index-%{+YYYY.MM}" } }
}

output {
  elasticsearch {
    hosts => ["https://<host>.elasticsearch.lyra-836.appcloud.swisscom.com"]
    user => "logstash-system-<id>"
    password => "<logstash-system-password>"
    index => "%{[@metadata][target_index]}"
    manage_template => true
    template_name => "logstash-template"
  }
}

For the other options, the default values are usually fine.

The above pipeline will parse logs from Cloud Foundry and add common tags like cf-org, cf-space and cf-app. Furthermore, it tags platform logs (like those from the Gorouter) as CF-PAAS and app logs as CF-APP for future filtering. If the grok-filter detects that the stripped application log message is formatted as JSON (line 36), it will treat it as such and let Elasticsearch index the fields accordingly. If you’re a Java developer, the Logstash Logback Encoder project might be of special interest to you: https://github.com/logstash/logstash-logback-encoder Make sure you use a strong password for your user in the http-section, as this endpoint will be reachable from the Internet.

Alternatively the configuration pipeline can also be created using the Elasticsearch REST API: https://www.elastic.co/guide/en/kibana/master/logstash-configuration-management-api-create.html

Push Logstash with Docker

Create the following manifest.yml in an empty folder, to configure your Logstash to use the configuration pipeline, which is stored in your Elasticsearch cluster.

---
applications:
  - name: mylogstash
    memory: 2G
    instances: 1
    routes:
      - route: mylogstash.scapp.io
    services:
      - my-elasticsearch
    docker:
      image: docker.elastic.co/logstash/logstash:7.5.2
    command: |
      export XPACK_MANAGEMENT_ELASTICSEARCH_HOSTS=$(echo $VCAP_SERVICES | grep -Po '"host":\s"\K(.*?)(?=")') &&
      export XPACK_MANAGEMENT_ELASTICSEARCH_USERNAME=$(echo $VCAP_SERVICES | grep -Po '"full_access_username":\s"\K(.*?)(?=")') &&
      export XPACK_MANAGEMENT_ELASTICSEARCH_PASSWORD=$(echo $VCAP_SERVICES | grep -Po '"full_access_password":\s"\K(.*?)(?=")') &&
      export XPACK_MONITORING_ELASTICSEARCH_HOSTS=$(echo $VCAP_SERVICES | grep -Po '"host":\s"\K(.*?)(?=")') &&
      export XPACK_MONITORING_ELASTICSEARCH_USERNAME=$(echo $VCAP_SERVICES | grep -Po '"full_access_username":\s"\K(.*?)(?=")') &&
      export XPACK_MONITORING_ELASTICSEARCH_PASSWORD=$(echo $VCAP_SERVICES | grep -Po '"full_access_password":\s"\K(.*?)(?=")') &&
      /usr/local/bin/docker-entrypoint
    env:
      XPACK_MANAGEMENT_ENABLED: true
      XPACK_MANAGEMENT_PIPELINE_ID: '["logstash_pipeline"]'
      XPACK_MONITORING_ENABLED: true
      PATH_CONFIG: ""

Use XPACK_MANAGEMENT_ELASTICSEARCH_URL instead of XPACK_MANAGEMENT_ELASTICSEARCH_HOSTS and XPACK_MONITORING_ELASTICSEARCH_URL instead of XPACK_MONITORING_ELASTICSEARCH_HOSTS for Logstash 6.x.

Now you may push your Logstash as usual using:

cf push

With cf logs mylogstash you can monitor the startup of your Logstash-instance. The following lines indicate that the pipeline has been configured successfully:

   2019-05-13T08:47:54.60+0000 [APP/PROC/WEB/0] OUT [2019-05-13T08:47:54,599][INFO ][logstash.outputs.elasticsearch] New Elasticsearch output {:class=>"LogStash::Outputs::ElasticSearch", :hosts=>["https://ac9537fc444c489bb63ac44064c54519.el
asticsearch.lyra-836.appcloud.swisscom.com"]}
   2019-05-13T08:47:54.78+0000 [APP/PROC/WEB/0] OUT [2019-05-13T08:47:54,784][INFO ][logstash.pipeline        ] Starting pipeline {:pipeline_id=>"logstash_pipeline_1", "pipeline.workers"=>8, "pipeline.batch.size"=>125, "pipeline.batch.delay
"=>5, "pipeline.max_inflight"=>1000, :thread=>"#<Thread:0x3e822881 run>"}
   2019-05-13T08:47:54.84+0000 [APP/PROC/WEB/0] OUT [2019-05-13T08:47:54,842][INFO ][logstash.pipeline        ] Pipeline started {"pipeline.id"=>"logstash_pipeline_1"}
   2019-05-13T08:47:54.91+0000 [APP/PROC/WEB/0] OUT [2019-05-13T08:47:54,916][INFO ][logstash.agent           ] Pipelines running {:count=>2, :pipelines=>[".monitoring-logstash", "logstash_pipeline_1"]}

Alternative: Create your own Docker image

If you wish to use configuration files instead, you can fork the official Docker image and ADD your configuration files in your own Dockerfile. You will have to host your personal Docker image in a Docker Repository like Dockerhub or Artifactory.

Create a User Provided Service to forward the Logs from your App

cf cups my-logstash-drain -l https://arbitrary-username:arbitrary-password@mylogstash.scapp.io

Bind Your App

Bind the app which should forward logs to Logstash via logstash drain. Restarting your application is not necessary.

cf bind-service my-app my-logstash-drain

You should start seeing logs in your Elasticsearch index as soon as your app receives traffic. With this step you are now done with this basic Elastic Stack guide. If you need further information about the Elastic Stack, please refer to the official documentation.

Further reading

Official Logstash Documentation

View the source for this page in GitHub