blog.hipsquare.net

Email alerts for problems with Dockerized services using Elasticsearch and ElastAlert

Cover Image for Email alerts for problems with Dockerized services using Elasticsearch and ElastAlert
Felix Christl
Felix Christl

In Managing Docker Logs with Elasticsearch and Kibana (dockerized, of course), we set up Elasticsearch to store logs from Docker containers, and Kibana to analyze them.

Now having logs in Kibana is great, but what if your services run into an issue? You can, of course, find them by actively looking at Kibana.

However, that’s probably not what you need to sleep calm at night. You will soon find yourself anxiously refreshing Kibana every five minutes just to see if anything’s wrong. You will want a more pro-active alerting approach.

Introducing ElastAlert

What’s great about your current setup is: You have all your logs stored in a searchable index. That makes it relatively easy to identify issues in them.

ElastAlert leverages that strength. It can periodically scan through your logs stored in Elasticsearch. When it finds something in the logs that looks like an issue, it sends out alerts.

Filters and Alerts

So what’s considered an issue? You can freely define that using filters in ElastAlert. The most simple example: Everything that’s logged to STDERR is an issue.

How will you be alerted about issues? Again, you can define your alert channels pretty freely. Email? Slack? A Jira ticket? All of that works.

Let’s set it up

We’ve already set up a docker-compose.yml in the last article. We’ll simply go ahead and add ElastAlert to it:

volumes:
  elasticsearch:

networks:
  elk:

services:
  elasticsearch:
    image: elasticsearch:6.5.3
    volumes:
      - elasticsearch:/usr/share/elasticsearch/data
    networks:
      - elk
  kibana:
    image: kibana:6.5.3
    networks:
      - elk
  nginx_for_elk:
    image: nginx
    ports:
      - 9200:9200
      - 5601:5601
    networks:
      - elk
    volumes:
      - type: bind
        source: /home/my-user/elk-stack/nginx.conf
        target: /etc/nginx/nginx.conf
      - type: bind
        source: /home/my-user/elk-stack/.htpasswd
        target: /etc/nginx/.htpasswd
  elastalert:
    image: bitsensor/elastalert:latest
    volumes:
      - type: bind
        source: /home/my-user/elk-stack/elastalert/config/elastalert.yaml
        target: /opt/elastalert/config.yaml
      - type: bind
        source: /home/my-user/elk-stack/elastalert/config/config.json
        target: /opt/elastalert-server/config/config.json
      - type: bind
        source: /home/my-user/elk-stack/elastalert/rules
        target: /opt/elastalert/rules
      - type: bind
        source: /home/my-user/elk-stack/elastalert/config/smtp-auth.yml
        target: /opt/elastalert/smtp-auth.yml
    networks:
      - elk

As you can see, we simply added another service and defined a bunch of configuration files that will be mounted into the container.

As before, we’re assuming that all config files are located under /home/my-user/elk-stack. Change the paths as needed.

Configuring ElastAlert

So let’s go through the different configuration files we’re referencing: elastalert/config/elastalert.yaml defines how exactly ElastAlert will run:

es_host: elasticsearch
es_port: 9200
es_username: USERNAME
es_password: PASSWORD
writeback_index: elastalert_status
rules_folder: rules
run_every:
  seconds: 5

buffer_time:
  minutes: 1


alert_time_limit:
  days: 2

ElastAlert stores its status information in Elasticsearch itself, that’s why we define the es_ parameters. Use the username and password you defined in the previous article.

You can leave the other options unchanged:

  • writeback_index tells ElastAlert where to store its status information,
  • rules_folder sets the folder in which rules are defined (i.e.: what’s an issue and how will you be alerted),
  • runs_every makes ElastAlert test if rules apply every 5 seconds,
  • alert_time_limit stops sending out alerts if the same rule has applied for two days without a change.

elastalert/config/config.json lets you set up basic service parameters for ElastAlert itself:

{
  "appName": "elastalert-server",
  "port": 3030,
  "elastalertPath": "/opt/elastalert",
  "verbose": false,
  "es_debug": false,
  "debug": false,
  "rulesPath": {
    "relative": true,
    "path": "/rules"
  },
  "templatesPath": {
    "relative": true,
    "path": "/rule_templates"
  },
  "es_host": "elasticsearch",
  "es_port": 9200,
  "es_username": "USERNAME",
  "es_password": "PASSWORD",
  "writeback_index": "elastalert_status"
}

Change USERNAME and PASSWORD and leave the rest unchanged.

Writing a Rule

Now that we have ElastAlert configured, we’ll want to add a rule that identifies issues and alerts us when they occur.

We’ll take the most primitive rule: Whenever one of our services has anything logged to STDERR, send out an alert.

Let’s fill elastalert/rules/error.yaml:

# Rule name
name: Alert on any error
# Fill in USERNAME and PASSWORD here
es_host: "elasticsearch"
es_port: 9200
es_username: USERNAME
es_password: PASSWORD

# Elasticsearch Index to search for issues. As we are using
# filebeat for our log file delivery to Elasticsearch, all logs 
# will be in indices called filebeat-something.
index: filebeat*
timestamp_field: "@timestamp"

type: any
realert: 
  seconds: 0
# Alert whenever something was logged to STDERR
filter:
- query:
    query_string:
      query: "stream:\"stderr\""
# Aggregate alerts by the Docker container name, 
# so you will receive one alert per container. It will 
# contain all errors logged in the last five minutes.
# This avoids huge amounts of emails in case one of
# your services logs a lot of errors.
aggregation:
  minutes: 5
aggregation_key: "docker.container.name"
# Send out the alert via email.
alert:
  - email
# Use the container name as the email subject
alert_subject_args:
  - docker.container.name
alert_subject:
  - "Error on {0}"
# Recipient address for alert emails
email:
  - "your-email-address@example.com"
# For email delivery, we need to define an SMTP connection. Enter
# your SMTP host name, port and whether TLS/SSL is to be used
# (if not, it'll fall back to STARTSSL).
# We define the SMTP user data in smtp-auth.yml.
smtp_host: "smtp.example.com"
smtp_port: 25
smtp_ssl: false  
smtp_auth_file: "/opt/elastalert/smtp-auth.yml"

Now the last step is to store the SMTP user data in elastalert/smtp-auth.yml:

user: SMTP_USERNAME
password: SMTP_PASSWORD

Obviously, replace SMTP_USERNAME and SMTP_PASSWORD with your SMTP credentials.

Start the Service

Congratulations, you’re all set up. A simple docker-compose restart will set up your brand new ElastAlert service and connect it with Elasticsearch. As soon as one of your Docker services logs an error to STDERR, ElastAlert will find it, and after five minutes, you’ll conveniently receive an email with the details. And over are the times you needed to check Kibana every five minutes.