Commit 62554cd7 by Carson Gee

Merge pull request #781 from edx/cg/kibana_logstash

Kibana and logstash roles
parents 660633ec 8ab10e06
---
# Build a kibana/logstash/elasticsearch server for capturing and
# analyzing logs.
- name: Configure syslog server
hosts: all
sudo: yes
roles:
- common
- oraclejdk
- elasticsearch
- logstash
- kibana
- role: nginx
nginx_sites:
- kibana
---
KIBANA_SERVER_NAME: "192.168.33.10"
KIBANA_NGINX_PORT: 80
KIBANA_SSL_NGINX_PORT: 443
kibana_app_dir: /edx/app/kibana
kibana_file: kibana-3.0.0.tar.gz
kibana_url: "https://download.elasticsearch.org/kibana/kibana/{{ kibana_file }}"
{
"title": "edX Log Analysis",
"services": {
"query": {
"idQueue": [],
"list": {
"0": {
"query": "@message: WARNING",
"alias": "",
"color": "#EAB839",
"id": 0,
"pin": false,
"type": "lucene",
"enable": true
},
"1": {
"id": 1,
"color": "#7EB26D",
"query": "@message: INFO",
"alias": "",
"pin": false,
"type": "lucene",
"enable": true
},
"2": {
"id": 2,
"color": "#BF1B00",
"query": "@message: ERROR",
"alias": "",
"pin": false,
"type": "lucene",
"enable": true
},
"3": {
"id": 3,
"color": "#F9D9F9",
"query": "*",
"alias": "",
"pin": false,
"type": "lucene",
"enable": true
}
},
"ids": [
0,
1,
2,
3
]
},
"filter": {
"idQueue": [
1,
2,
3
],
"list": {
"0": {
"type": "time",
"field": "@timestamp",
"from": "now-1h",
"to": "now",
"mandate": "must",
"active": true,
"alias": "",
"id": 0
},
"1": {
"type": "querystring",
"query": "*pika*",
"mandate": "mustNot",
"active": true,
"alias": "",
"id": 1
},
"2": {
"type": "querystring",
"query": "*connectionpool*",
"mandate": "mustNot",
"active": true,
"alias": "",
"id": 3
}
},
"ids": [
0,
1,
2
]
}
},
"rows": [
{
"title": "Graph",
"height": "350px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"span": 12,
"editable": true,
"group": [
"default"
],
"type": "histogram",
"mode": "count",
"time_field": "@timestamp",
"value_field": null,
"auto_int": true,
"resolution": 100,
"interval": "30s",
"fill": 3,
"linewidth": 3,
"timezone": "browser",
"spyable": true,
"zoomlinks": true,
"bars": false,
"stack": true,
"points": false,
"lines": true,
"legend": true,
"x-axis": true,
"y-axis": true,
"percentage": false,
"interactive": true,
"queries": {
"mode": "all",
"ids": [
0,
1,
2,
3
]
},
"title": "Events over time",
"intervals": [
"auto",
"1s",
"1m",
"5m",
"10m",
"30m",
"1h",
"3h",
"12h",
"1d",
"1w",
"1M",
"1y"
],
"options": true,
"tooltip": {
"value_type": "cumulative",
"query_as_alias": true
},
"scale": 1,
"y_format": "none",
"grid": {
"max": null,
"min": 0
},
"annotate": {
"enable": false,
"query": "*",
"size": 20,
"field": "_type",
"sort": [
"_score",
"desc"
]
},
"pointradius": 5,
"show_query": true,
"legend_counts": true,
"zerofill": true,
"derivative": false
}
],
"notice": false
},
{
"title": "Charts",
"height": "250px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"span": 4,
"editable": true,
"type": "hits",
"loadingEditor": false,
"query": {
"field": "syslog_severity",
"goal": 100
},
"queries": {
"mode": "all",
"ids": [
0,
1,
2,
3
]
},
"size": 10,
"exclude": [],
"donut": true,
"tilt": true,
"legend": "above",
"labels": true,
"mode": "terms",
"default_field": "DEFAULT",
"spyable": true,
"title": "Log Severity",
"style": {
"font-size": "10pt"
},
"arrangement": "horizontal",
"chart": "pie",
"counter_pos": "above"
},
{
"span": 4,
"editable": true,
"type": "hits",
"loadingEditor": false,
"query": {
"field": "@source_host",
"goal": 100
},
"queries": {
"mode": "all",
"ids": [
0,
1,
2,
3
]
},
"size": 10,
"exclude": [],
"donut": true,
"tilt": true,
"legend": "above",
"labels": true,
"mode": "terms",
"default_field": "DEFAULT",
"spyable": true,
"title": "Logs by Host",
"style": {
"font-size": "10pt"
},
"arrangement": "horizontal",
"chart": "pie",
"counter_pos": "above"
},
{
"span": 4,
"editable": true,
"type": "hits",
"loadingEditor": false,
"style": {
"font-size": "10pt"
},
"arrangement": "horizontal",
"chart": "pie",
"counter_pos": "above",
"donut": true,
"tilt": true,
"labels": true,
"spyable": true,
"queries": {
"mode": "selected",
"ids": [
0,
1,
2
]
},
"title": "Percent by Python Severity"
}
],
"notice": false
},
{
"title": "Trends",
"height": "50px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"span": 4,
"editable": true,
"type": "trends",
"loadingEditor": false,
"queries": {
"mode": "all",
"ids": [
0,
1,
2,
3
]
},
"style": {
"font-size": "14pt"
},
"ago": "1h",
"arrangement": "vertical",
"spyable": true,
"title": "Hourly"
},
{
"span": 4,
"editable": true,
"type": "trends",
"loadingEditor": false,
"queries": {
"mode": "all",
"ids": [
0,
1,
2,
3
]
},
"style": {
"font-size": "14pt"
},
"ago": "1d",
"arrangement": "vertical",
"spyable": true,
"title": "Daily"
},
{
"span": 4,
"editable": true,
"type": "trends",
"loadingEditor": false,
"queries": {
"mode": "all",
"ids": [
0,
1,
2,
3
]
},
"style": {
"font-size": "14pt"
},
"ago": "1w",
"arrangement": "vertical",
"spyable": true,
"title": "Weekly"
}
],
"notice": false
},
{
"title": "Error Events",
"height": "550px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"error": false,
"span": 12,
"editable": true,
"type": "table",
"loadingEditor": false,
"status": "Stable",
"queries": {
"mode": "selected",
"ids": [
2
]
},
"size": 100,
"pages": 5,
"offset": 0,
"sort": [
"@timestamp",
"desc"
],
"group": "default",
"style": {
"font-size": "9pt"
},
"overflow": "min-height",
"fields": [
"@timestamp",
"@source_host",
"message"
],
"highlight": [],
"sortable": true,
"header": true,
"paging": true,
"field_list": true,
"all_fields": false,
"trimFactor": 300,
"normTimes": true,
"spyable": true,
"title": "Errors",
"localTime": false,
"timeField": "@timestamp"
}
],
"notice": false
},
{
"title": "Events",
"height": "350px",
"editable": true,
"collapse": false,
"collapsable": true,
"panels": [
{
"title": "All events",
"error": false,
"span": 12,
"editable": true,
"group": [
"default"
],
"type": "table",
"size": 100,
"pages": 5,
"offset": 0,
"sort": [
"@timestamp",
"desc"
],
"style": {
"font-size": "9pt"
},
"overflow": "min-height",
"fields": [
"@source_host",
"message"
],
"highlight": [],
"sortable": true,
"header": true,
"paging": true,
"spyable": true,
"queries": {
"mode": "all",
"ids": [
0,
1,
2,
3
]
},
"field_list": true,
"status": "Stable",
"trimFactor": 300,
"normTimes": true,
"all_fields": false,
"localTime": false,
"timeField": "@timestamp"
}
],
"notice": false
}
],
"editable": true,
"failover": false,
"index": {
"interval": "day",
"pattern": "[logstash-]YYYY.MM.DD",
"default": "NO_TIME_FILTER_OR_INDEX_PATTERN_NOT_MATCHED",
"warm_fields": true
},
"style": "dark",
"panel_hints": true,
"pulldowns": [
{
"type": "query",
"collapse": false,
"notice": false,
"query": "*",
"pinned": true,
"history": [
"*",
"@message: ERROR",
"@message: INFO",
"@message: WARNING",
"@message: WARN",
"*corresponding*",
"@message: INFO OR syslog_severity: info",
"@message: INFO OR @log_severity: info",
"ERROR",
"WARNING"
],
"remember": 10,
"enable": true
},
{
"type": "filtering",
"collapse": true,
"notice": false,
"enable": true
}
],
"nav": [
{
"type": "timepicker",
"collapse": false,
"notice": false,
"status": "Stable",
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
],
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"timefield": "@timestamp",
"now": true,
"filter_id": 0,
"enable": true
}
],
"loader": {
"save_gist": false,
"save_elasticsearch": true,
"save_local": true,
"save_default": true,
"save_temp": true,
"save_temp_ttl_enable": true,
"save_temp_ttl": "30d",
"load_gist": true,
"load_elasticsearch": true,
"load_elasticsearch_size": 20,
"load_local": true,
"hide": false
},
"refresh": "1m"
}
\ No newline at end of file
---
- name: restart nginx
service: name=nginx state=restarted
- name: reload nginx
service: name=nginx state=reloaded
---
dependencies:
- common
- nginx
# requires:
# - oraclejdk
# - elasticsearch
# - nginx
---
- name: Ensure app apt dependencies are installed
apt: pkg={{ item }} state=installed
with_items:
- python-software-properties
- git
- nginx
- name: Ensure {{ kibana_app_dir }} exists
file: path={{ kibana_app_dir }} state=directory owner=root group=root mode=0755
- name: Ensure subdirectories exist
file: path={{ kibana_app_dir }}/{{ item }} owner=root group=root mode=0755 state=directory
with_items:
- htdocs
- share
- name: ensure we have the specified kibana release
get_url: url={{ kibana_url }} dest={{ kibana_app_dir }}/share/{{ kibana_file }}
- name: extract
shell: >
chdir={{ kibana_app_dir }}/share
tar -xzvf {{ kibana_app_dir }}/share/{{ kibana_file }}
creates={{ kibana_app_dir }}/share/{{ kibana_file|replace('.tar.gz','') }}
- name: install
shell: >
chdir={{ kibana_app_dir }}/share/{{ kibana_file|replace('.tar.gz','') }}
cp -R * {{ kibana_app_dir }}/htdocs/
- name: copy config
template: src=config.js.j2 dest={{ kibana_app_dir }}/htdocs/config.js
/**
* These is the app's configuration, If you need to configure
* the default dashboard, please see dashboards/default
*/
define(['settings'],
function (Settings) {
return new Settings({
/**
* URL to your elasticsearch server. You almost certainly don't
* want 'http://localhost:9200' here. Even if Kibana and ES are on
* the same host
*
* By default this will attempt to reach ES at the same host you have
* elasticsearch installed on. You probably want to set it to the FQDN of your
* elasticsearch host
* @type {String}
*/
//elasticsearch: "http://"+window.location.hostname+":9200",
{% if NGINX_ENABLE_SSL %}
elasticsearch: "https://{{ KIBANA_SERVER_NAME }}/e",
{% else %}
elasticsearch: "http://{{ KIBANA_SERVER_NAME }}/e",
{% endif %}
/**
* The default ES index to use for storing Kibana specific object
* such as stored dashboards
* @type {String}
*/
kibana_index: "kibana-int",
/**
* Panel modules available. Panels will only be loaded when they are defined in the
* dashboard, but this list is used in the "add panel" interface.
* @type {Array}
*/
panel_names: [
'histogram',
'map',
'table',
'filtering',
'timepicker',
'text',
'hits',
'column',
'trends',
'bettermap',
'query',
'terms',
'stats',
'sparklines',
'goal',
]
});
});
---
LOGSTASH_DAYS_TO_KEEP: 30
LOGSTASH_ROTATE: true
logstash_app_dir: /edx/app/logstash
logstash_log_dir: /edx/var/log/logstash
logstash_data_dir: /edx/var/logstash/file_logs
logstash_syslog_port: 514
logstash_file: logstash-1.3.3-flatjar.jar
logstash_url: "https://download.elasticsearch.org/logstash/logstash/{{ logstash_file }}"
logstash_python_requirements:
- pyes==0.19.0
logstash_scripts_repo: https://github.com/crashdump/logstash-elasticsearch-scripts
logstash_rotate_cron:
hour: 5
minute: 42
logstash_optimize_cron:
hour: 6
minute: 15
{
"template": "logstash-*",
"settings" : {
"number_of_shards" : 1,
"number_of_replicas" : 0,
"index" : {
"query" : { "default_field" : "message" },
"store" : { "compress" : { "stored" : true, "tv": true } }
}
},
"mappings": {
"_default_": {
"_all": { "enabled": false },
"_source": { "compress": true },
"dynamic_templates": [
{
"string_template" : {
"match" : "*",
"mapping": { "type": "string", "index": "not_analyzed" },
"match_mapping_type" : "string"
}
}
],
"properties" : {
"@fields": { "type": "object", "dynamic": true, "path": "full" },
"@message" : { "type" : "string", "index" : "analyzed" },
"@source" : { "type" : "string", "index" : "not_analyzed" },
"@source_host" : { "type" : "string", "index" : "not_analyzed" },
"@source_path" : { "type" : "string", "index" : "not_analyzed" },
"@tags": { "type": "string", "index" : "not_analyzed" },
"@timestamp" : { "type" : "date", "index" : "not_analyzed" },
"@type" : { "type" : "string", "index" : "not_analyzed" }
}
}
}
}
---
- name: restart logstash
service: name=logstash state=restarted
---
dependencies:
- common
- elasticsearch
# requires:
# - oraclejdk
# - elasticsearch
---
- name: Ensure app apt dependencies are installed
apt: pkg={{ item }} state=installed
with_items:
- redis-server
- name: Ensure {{ logstash_app_dir }} exists
file: path={{ logstash_app_dir }} state=directory owner=root group=root mode=0755
- name: Ensure subdirectories exist
file: path={{ logstash_app_dir }}/{{ item }} owner=root group=root mode=0755 state=directory
with_items:
- bin
- etc
- share
- name: ensure logstash config is in place
template: src=logstash.conf.j2 dest={{ logstash_app_dir }}/etc/logstash.conf owner=root group=root mode=0644
notify: restart logstash
- name: ensure logstash upstart job is in place
template: src=logstash.upstart.conf.j2 dest=/etc/init/logstash.conf owner=root group=root mode=0755
- name: ensure logstash has a logging dir at {{ logstash_log_dir }}
file: path={{ logstash_log_dir }} owner=root group=root mode=0755 state=directory
- name: ensure we have the specified logstash release
get_url: url={{ logstash_url }} dest={{ logstash_app_dir }}/share/{{ logstash_file }}
- name: ensure symlink with no version exists at {{ logstash_app_dir }}/share/logstash.jar
file: src={{ logstash_app_dir }}/share/${logstash_file} dest={{ logstash_app_dir }}/share/logstash.jar state=link
- name: start logstash
action: service name=logstash state=started enabled=yes
- name: Ensure we are running
wait_for: port={{ logstash_syslog_port }} host=localhost timeout=60
- name: Copy logstash es index template
copy: src=template_logstash.json dest=/etc/elasticsearch/template_logstash.json
- name: Enable logstash es index template
shell: chdir=/etc/elasticsearch executable=/bin/bash curl -XPUT 'http://localhost:9200/_template/template_logstash' -d @template_logstash.json
- name: Install python requirements
pip: name={{ item }} state=present
with_items: logstash_python_requirements
- name: Checkout logstash rotation scripts
git: repo={{ logstash_scripts_repo }} dest={{ logstash_app_dir }}/share/logstash-elasticsearch-scripts
when: LOGSTASH_ROTATE|bool
- name: Setup cron to run rotation
cron: >
user=root
name="Elasticsearch logstash index rotation"
hour={{ logstash_rotate_cron.hour }}
minute={{ logstash_rotate_cron.minute }}
job="/usr/bin/python {{ logstash_app_dir }}/share/logstash-elasticsearch-scripts/logstash_index_cleaner.py -d {{ LOGSTASH_DAYS_TO_KEEP }} > {{ logstash_log_dir }}/rotation_cron"
when: LOGSTASH_ROTATE|bool
- name: Setup cron to run rotation
cron: >
user=root
name="Elasticsearch logstash index optimization"
hour={{ logstash_optimize_cron.hour }}
minute={{ logstash_optimize_cron.minute }}
job="/usr/bin/python {{ logstash_app_dir }}/share/logstash-elasticsearch-scripts/logstash_index_optimize.py -d {{ LOGSTASH_DAYS_TO_KEEP }} > {{ logstash_log_dir }}/optimize_cron"
when: LOGSTASH_ROTATE|bool
input {
tcp {
port => {{ logstash_syslog_port }}
type => syslog
}
udp {
port => {{ logstash_syslog_port }}
type => syslog
}
}
filter {
if [type] == "syslog" {
grok {
match => { "message" => "<%{POSINT:syslog_pri}>%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{GREEDYDATA:syslog_message}" }
add_field => [ "received_at", "%{@timestamp}" ]
add_field => [ "received_from", "%{@source_host}" ]
}
syslog_pri { }
date {
match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ]
}
if !("_grokparsefailure" in [tags]) {
mutate {
replace => [ "@source_host", "%{syslog_hostname}" ]
replace => [ "@message", "%{syslog_message}" ]
}
}
mutate {
remove_field => [ "syslog_hostname", "syslog_message", "syslog_timestamp" ]
}
}
}
output {
# Example just to output to elasticsearch
elasticsearch { }
# And gzip for each host and program
file {
path => '{{ logstash_data_dir }}/%{@source_host}/all.%{+yyyyMMdd}.gz'
gzip => true
}
# Should add option for S3 as well.
}
# logstash-indexer.conf# logstash - indexer instance
#
description "logstash indexer instance"
start on virtual-filesystems
stop on runlevel [06]
respawn
respawn limit 5 30
limit nofile 65550 65550
env HOME={{ logstash_app_dir }}
env JAVA_OPTS='-Xms512m -Xmx512m'
env PATH=$PATH:/usr/lib/jvm/{{ oraclejdk_base }}/bin
chdir {{ logstash_app_dir }}
setuid root
console log
# for versions 1.1.1 - 1.1.4 the internal web service crashes when touched
# and the current workaround is to just not run it and run Kibana instead
script
exec java -jar {{ logstash_app_dir }}/share/logstash.jar agent -f {{ logstash_app_dir }}/etc/logstash.conf --log {{ logstash_log_dir }}/logstash-indexer.out
end script
{%- if "kibana" in nginx_default_sites -%}
{%- set default_site = "default" -%}
{%- else -%}
{%- set default_site = "" -%}
{%- endif -%}
upstream elasticsearch_server {
server 127.0.0.1:9200;
}
server {
# Kibana server, templated by ansible
{% if NGINX_ENABLE_SSL %}
listen {{KIBANA_NGINX_PORT}} {{default_site}};
listen {{KIBANA_SSL_NGINX_PORT}} {{default_site}} ssl;
ssl_certificate /etc/ssl/certs/{{ NGINX_SSL_CERTIFICATE|basename }};
ssl_certificate_key /etc/ssl/private/{{ NGINX_SSL_KEY|basename }};
{% else %}
listen {{KIBANA_NGINX_PORT}} {{default_site}};
{% endif %}
server_name {{ KIBANA_SERVER_NAME }};
root {{ kibana_app_dir }}/htdocs;
access_log {{ nginx_log_dir }}/kibana.access.log;
error_log {{ nginx_log_dir }}/kibana.error.log error;
# Access restriction
{% include "basic-auth.j2" %}
# Set image format types to expire in a very long time
location ~* ^.+\.(jpg|jpeg|gif|png|ico)$ {
access_log off;
expires max;
}
# Set css and js to expire in a very long time
location ~* ^.+\.(css|js)$ {
access_log off;
expires max;
}
# Elastic Search
location /e {
rewrite /e/(.*) /$1 break;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $http_x_forwarded_for;
proxy_redirect off;
proxy_pass http://elasticsearch_server;
}
# Kibana
location / {
root {{ kibana_app_dir }}/htdocs;
index index.html;
expires 1d;
try_files $uri/ $uri;
if (-f $request_filename) {
break;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment