LoginSignup
13
14

More than 5 years have passed since last update.

Ansible を使って EC2 に ELB アクセスログ分析(Fluentd + ElasticSearch + Kibana4 + Nginx)を立ち上げる

Last updated at Posted at 2015-06-21

バージョン&記載ソースリポジトリ

version
ansible 1.9.0.1

ポイントとなるソースのみ載せています。全てのソースが見たい場合は、以下のリポジトリを参照してください。

Ansible で構築したサーバーのアプリの各バージョン

version
elasticsearch 1.6.0
td-agent 0.12.7
kibana 4.1.0
nginx 1.6.2

概要

ELB のアクセスログを Kibana で分析するサーバーをすぐに構築できるように Ansible playbook を作成しました。
前回の phpMyAdmin サーバー playbook の教訓を生かして、今回はロールを細かく定義しました。

ELBのアクセスログを S3 へ保存

ここを参考に ELB のアクセスログを S3 に保存するようにしました。
他にも参考はたくさんありました。

ElasticSearch のインストール

yum モジュールで rpm を直接インストールすることができました。
これで elasticsearch を簡単にインストールできます。
設定は、デフォルトのままを使っているので、templates, defines は特に定義していません。
ここら辺は、実際に使って問題が発生したら調整する予定です。

roles/ec2-elasticsearch/tasks/main.yml
---
- name: Install elasticsearch
  yum: name="https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.6.0.noarch.rpm" state="present"
  notify: restart elasticsearch

- name: Set elasticsearch service to start on boot
  service: name=elasticsearch enabled=true

Fluentd のインストール

Ansible Galaxy を見てると、install-redhat-td-agent2.sh の中でやってる処理を Ansible で記述する(Fluentd のインストールはリポジトリを追加して、yum でインストールする)のが正しいやり方みたいです。

ただ、、、今回は、install-redhat-td-agent2.sh を使う方法で書きました。
最近、この curl で shell をとってきて実行してインストールするタイプが増えてる気がするので、この手順を Ansible で書く方法を理解しておきたかったからです。
今の手順での欠点は、playbook を実行するたびに Ansible に「changed」判定されてしまうことです。
良い方法は、、、思いついてません。

ELB のアクセスログを収集するプラグインは2つありますが、

今回は、 fluent-plugin-elb-access-log を利用しました。
強い思いがあったわけではなく、まずは、こちらで試してみようっという感じです。
この辺も使っていくうちに変えると思います。

fluent-plugin-elb-access-log 0.2.4 と AWS SDK 2.1.1 (2015/06/21 時点最新)組み合わせだとエラーが発生していたので、 AWS SDK 2.0.48 を入れるようにしています。

証明書に関するエラーが発生したので、以下サイトを参考にして、証明書を置き換える処理を追加しました。

roles/ec2-fluentd/tasks/main.yml
---
- name: Download fluentd
  get_url: url="https://td-toolbelt.herokuapp.com/sh/install-redhat-td-agent2.sh" dest="/tmp/install-redhat-td-agent2.sh" mode=0755

- name: Install fluentd
  shell: /tmp/install-redhat-td-agent2.sh

- name: Set fluentd service to start on boot
  service: name=td-agent enabled=true

# 2015.06.27 fluent-plugin-elb-access-log が aws-sdk 2.1系に対応したので、aws-sdk 2.0系をインストールする処理は入りません!!
#- name: Install the aws-sdk (In version specified because there is a bug)
#  gem: name={{ item }} version=2.0.48 executable="/opt/td-agent/embedded/bin/fluent-gem" user_install=false
#  with_items:
#    - aws-sdk
#    - aws-sdk-core
#    - aws-sdk-resources

- name: Install fluentd-plugins
  gem: name={{ item }} state=latest executable="/opt/td-agent/embedded/bin/fluent-gem" user_install=false
  with_items:
    - fluent-plugin-elasticsearch
    - fluent-plugin-elb-access-log
  notify: restart fluentd

- name: Put td-agent.conf
  template: src=td-agent.conf.j2 dest=/etc/td-agent/td-agent.conf backup=true mode=0644
  notify: restart fluentd

- name: Check replaced the certificate
  stat: path=/opt/td-agent/embedded/ssl/certs/cacert.pem.original
  register: cert_pem_original

- name: Backup the certificate
  command: cp /opt/td-agent/embedded/ssl/certs/cacert.pem /opt/td-agent/embedded/ssl/certs/cacert.pem.original
  when: not cert_pem_original.stat.exists

- name: Replace the certificate
  command: cp /etc/pki/tls/cert.pem /opt/td-agent/embedded/ssl/certs/cacert.pem
  when: not cert_pem_original.stat.exists

fluent-plugin-elb-access-log の設定で、AWSに関連した情報が必要になります。

roles/ec2-fluentd/templates/td-agent.conf.j2
...
{% for source in fluentd_sources %}
<source>
  type elb_access_log
  aws_key_id {{ source.aws_key_id }}
  aws_sec_key {{ source.aws_sec_key }}
  account_id {{ source.account_id }}
  region {{ source.region }}
  s3_bucket {{ source.s3_bucket }}
  s3_prefix {{ source.s3_prefix }}
  tag elb.access_log
  #tsfile_path /var/tmp/fluent-plugin-elb-access-log.ts
  #histfile_path /var/tmp/fluent-plugin-elb-access-log.history
  #interval 300
  start_datetime {{ source.start_datetime }}
  #buffer_sec 600
  #history_length 100
  #sampling_interval 1
  debug true
</source>
{% endfor %}

<match elb.access_log>
  type elasticsearch
  type_name access_log
  host localhost
  port 9200
  logstash_format true
  include_tag_key true
  tag_key @log_name
</match>
...

ぼくは アクセスキー/シークレットキーのために IAM でユーザーを作りました。
適用したポリシーは以下です。
Action は参照系の処理のみに絞るのが適切だと思いますが、どの Action のみにすればよいかがわからくて、 S3:* にしてしまいました。。。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt999999999999",
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::バケット名",
                "arn:aws:s3:::バケット名/*"
            ]
        }
    ]
}

Nginx のインストール

Kibana4 は単体でも動作しますが、HTTPS にして、Basic 認証をかけたかったので Nginx でリバースプロキシさせます。
そうすることで、AWS のセキュリティグループは、HTTP(80), HTTPS(443)の2つのみのポートを解放すれば良くなります。
Kibana4 のデフォルトポート 5601 を解放する必要はありません。
(ちなみに、、、Nginx を利用しなくても、ElasticSearch のデフォルトポート 9200 を解放する必要はありません。)

Nginx は yum でインストールできるので簡単です。
SSL を利用するための自己証明書(オレオレ証明書)も以下を参考に作っています。

roles/ec2-nginx/tasks/main.yml

---
- name: Install nginx
  yum: name=nginx state=latest

- name: Set nginx service to start on boot
  service: name=nginx enabled=true

- name: Put nginx.conf
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf backup=true mode=0644
  notify: restart nginx

- name: Create self signed certificate
  shell: |
    mkdir -p /etc/nginx/certs &&
    crt_file="/etc/nginx/certs/local-ssl.crt" &&
    key_file="/etc/nginx/certs/local-ssl.key" &&
    crt_and_key_file="/etc/nginx/certs/local-ssl.crt_and_key" &&
    subject="/C=JP/ST=Osaka/L=Osaka City/CN=local-ssl" &&
    openssl req -new -newkey rsa:2048 -sha1 -x509 -nodes \
      -set_serial 1 \
      -days 3650 \
      -subj "$subject" \
      -out "$crt_file" \
      -keyout "$key_file" &&
    cat "$crt_file" "$key_file" >> "$crt_and_key_file" &&
    chmod 400 "$key_file" "$crt_and_key_file"
    creates="/etc/nginx/certs/local-ssl.crt"

Kibana4 のインストール

Kibana4 が出ているので、3 ではなく 4 を使います。
Kibana のインストールは、tar.gz をダウンロードして解凍するタイプです。
command や shell モジュールを使いたくなってきましたが、ぐっとこらえて、

  • ダウンロードは get_url モジュール
  • 解凍は unarchive モジュール (copy=no でリモート内のファイルを解凍するように指定するのがポイント)
  • シンボリックリックは file モジュール

の各モジュールを使うようにしました。急いでいるときには command, shell を使って良いと思うけど、時間があるときにはなるべく各モジュールを使ったほうが、ベキトウセイの考慮が楽になるので良いと思います。

init script については、以下サイトを参考にしました。
これで、service コマンドを使ってスタート/ストップ/リスタートが行えます。
どのサービスでも同じ手順で操作できることは重要だと思います。

Basic 認証をするための htpasswd 作成は、htpasswd モジュールを使っています。
ちょくちょく Basic 認証をかけたいことがあるので、htpasswd が簡単に作れることがわかったのはかなりの収穫です。

ec2-kibana のロールは、ec2-nginx に依存していますが、meta は書いていません。書いたほうがよいことはわかってますが、なくても動いているので、後回しにしています。

roles/ec2-kibana/tasks/main.yml
---
- name: Download kibana
  get_url: url="https://download.elastic.co/kibana/kibana/kibana-4.1.0-linux-x64.tar.gz" dest="/tmp/kibana-4.1.0-linux-x64.tar.gz" mode=0644

- name: Unarchive kibana
  unarchive: src=/tmp/kibana-4.1.0-linux-x64.tar.gz dest=/opt/ copy=no owner=root group=root

- name: Install kibana
  file: src=/opt/kibana-4.1.0-linux-x64 dest=/opt/kibana state=link

- name: Put init script for kibana
  template: src=kibana.j2 dest=/etc/init.d/kibana mode=0755
  notify: restart kibana

- name: Set kibana service to start on boot
  service: name=kibana enabled=true

- name: Install passlib for creating htpasswd
  pip: name=passlib state=latest
  when: kibana_basic_enabled

- name: Create htpasswd
  htpasswd: path=/etc/nginx/conf.d/kibana.htpasswd name={{ kibana_basic_user_name }} password={{ kibana_basic_user_password }} create=yes state=present
  when: kibana_basic_enabled

- name: Put nginx.kibana.conf
  template: src=nginx.kibana.conf.j2 dest=/etc/nginx/conf.d/kibana.conf mode=0644
  notify: restart nginx

Kibana 用の Nginx の設定です。
HTTP を HTTPS へリダイレクトして、Basic 認証を行えるようにしました。
これで最低限のセキュリティを保ちますが、できれば、さらにセキュリティグループで IP制限をかけたほうが良いと思います。

roles/ec2-kibana/templates/nginx.kibana.conf.js
# {{ ansible_managed }}

# redirect https from http
server {
  listen 80;

  location ~ ^/kibana/.* {
    return 301 https://$host$request_uri;
  }
}

# proxy kibana
server {
  listen 443;

  access_log /var/log/nginx/kibana.access.log;
  error_log /var/log/nginx/kibana.error.log;

  ssl on;
  ssl_certificate /etc/nginx/certs/local-ssl.crt;
  ssl_certificate_key /etc/nginx/certs/local-ssl.key;

  location ~ ^/kibana/.* {
    proxy_pass http://localhost:5601;
    rewrite ^/kibana/(.*) /$1 break;
    proxy_set_header Host $host;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;

{% if kibana_basic_enabled %}
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/conf.d/kibana.htpasswd;
{% endif %}
  }
}

各ロールの作成は以上です。

Playbook サンプルと実行

必要な role を読み込みます。
meta で依存関係を定義していないので、roles: の記述順を変えると動かないと思います。

ec2-kibana.example.yml
- hosts: all
  sudo: yes
  vars:
    fluentd_sources:
      - aws_key_id: XXXXXXXXXXXX
        aws_sec_key: XXXXXXXXXXXXXXXXXXXXXXXX
        region: ap-northeast-1
        account_id: 999999999999
        s3_bucket: bucketname
        s3_prefix: prefix
        start_datetime: 2015/06/01 00:00
    kibana_basic_enabled: true
    kibana_basic_user_name: kibana
    kibana_basic_user_password: kibanapassword
  roles:
    - ec2-elasticsearch
    - ec2-fluentd
    - ec2-nginx
    - ec2-kibana

以下で inventory ファイルなしで実行できます。
ぼくは inventory ファイルなしで実行してばかりですが、本格的な運用をしてないからでしょうね。

$ ansible-playbook -i "xxx.xxx.xxx.xxx," --user=ec2-user --private-key=xxx.pem ec2-kibana.example.yml

playbook の適用が完了したら、以下の URL でアクセスできます。

https://xxx.xxx.xxx.xxx/kibana/

ここまでで環境はできますが、これからがログ解析の本番です。如何に Kibana4 を使いこなすか、です。
使いこなすことのほうが時間がかかりそうです。

感想

いろんなインストール方法を記述することになりました。あとはコンパイルが必要なタイプぐらいだと思います。
このことは、今後も Ansible を使うときに役に立ちそうです。

この Ansible の書き方だと今は動きますが、来年でも問題なく動くかはわかりません。
急ぎで構築したいときに動かないっといのは辛いです。
ServerSpec 等のテストに着手して、この点をどこまでカバーできるかを調べたいです。

13
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14