Docker で Sensu + Graphite + Grafana 環境構築

監視環境を新規に構築するにあたり、上記構成で環境を構築したのでそのメモです。

こんな感じでサーバの状況を確認できたり、

Slackにアラートの通知がきたり(メールも)

まで、ふんわりできました。以下、作ったDockerfile群

  1. https://github.com/tomyhero/docker-sensu
  2. https://github.com/tomyhero/docker-sensu-dashboard-uchiwa
  3. https://github.com/tomyhero/docker-sensu-dashboard-graphite
  4. https://github.com/tomyhero/docker-grafana

以下が、セットアップ手順です。

手順1: graphiteのセットアップ

git clone https://github.com/tomyhero/docker-sensu-dashboard-graphite.git
cd docker-sensu-dashboard-graphite
docker build -t tomyhero/sensu-dashboard-graphite . 
docker run --name sensu-dashboard-graphite -it -d \
    -v /opt/graphite/data:/app/graphite \
    -p 10200:80 \
    -p 2003:2003 \
    -e "ROOT_USER_NAME=tomyhero"  \
    -e "ROOT_PASSWORD=mysecret" \
    -e "ROOT_EMAIL=tomohiro.teranishi@gmail.com" \
tomyhero/sensu-dashboard-graphite bash
  • hostの /opt/graphite/data 配下に永続データ(統計データ)の保存
  • webを 10200 portでアクセスできるようにし、host側のnginxでproxyします
  • 2003 port は carbon-cacheに対して、コマンドを送るため

手順2: sensuのセットアップ

get clone https://github.com/tomyhero/docker-sensu.git
cd docker-sensu
docker build -t tomyhero/sensu .

docker run --name sensu -it -d \
-p 5672:5672 \
-p 4567:4567 \
-e GRAPHITE_HOST=$(docker inspect --format {{.NetworkSettings.IPAddress}} sensu-dashboard-graphite) \
-e "MAIL_TO=tomohiro.teranishi@gmail.com" \
-e "START_SENSU_CLIENT=false" \
-e "DASHBOARD_URL=http://uchiwa.localhost/" \
-e "ON_SENSU_HANDLER_NOTIFICATION_SLACK=true" \
-e "SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXXX/XXXXXXXXXXXXX" \
tomyhero/sensu bash
  • 5672port は rabbitmq です
  • 4567port は API用です
  • $(docker inspect --format {{.NetworkSettings.IPAddress}} sensu-dashboard-graphite) graphiteのIPの出力
  • sensu clientを起動するなら START_SENSU_CLIENT=true
  • dashboard urlには あとででてくるuchiwaのURLを記載。
  • ON_SENSU_HANDLER_NOTIFICATION_SLACK - slackへの通知を有効にする場合 true に
  • SLACK_WEBHOOK_URL slackのwebhook urlを記載
  • その他必要に応じての設定は、https://github.com/tomyhero/docker-sensu ドキュメントで確認。

手順3: uchiwaのセットアップ

git clone https://github.com/tomyhero/docker-sensu-dashboard-uchiwa.git
cd docker-sensu-dashboard-uchiwa
docker build -t tomyhero/sensu-dashboard-uchiwa . 

docker run --name=sensu-dashboard-uchiwa -it -d \
-p 11003:3000 \
-e="SENSU_HOST=sensu.localhost" \
tomyhero/sensu-dashboard-uchiwa
  • 11003 portでwebにアクセス

手順4: grafanaのセットアップ

git clone https://github.com/tomyhero/docker-grafana.git
cd docker-grafana

docker build -t tomyhero/grafana .
docker run --name grafana -it -d -p 13000:3000 \
    -v /opt/grafana/data:/app/grafana \
    -e "GRAFANA_ADMIN_USER=tomyhero" \
    -e "GRAFANA_ADMIN_PASSWORD=secret" \
    -e "GRAFANA_AUTH_BASIC_ENABLED=false" \
    tomyhero/grafana bash 
  • hostの /opt/grafana/data 配下に、grafanaの設定データを保存
  • GRAFANA_AUTH_BASIC_ENABLED は環境に応じて。。。

手順5: sensu clinent

centosの環境なら

wget -O https://raw.githubusercontent.com/tomyhero/docker-sensu/master/bin/client-install-centos.sh
chmod 775 client-install-centos.sh
./client-install-centos.sh

/etc/sensu/config.json 作成。中身は自分で。

{
  "rabbitmq": {
    "host": "localhost",
    "vhost": "/sensu",
    "user": "sensu",
    "password": "secret"
  }
}

/etc/sensu/conf.d/client.json を作成。中身は自分で。

{
  "client": {
    "name": "tool",
    "address": "localhost",
    "subscriptions": [
      "basic","nginx","mysql"
    ]
  }
}

起動

service sensu-client start
tail -f /var/log/sensu/sensu-client.log

手順6: hostのnginx設定

upstream grafana {
  server localhost:13000;
}

upstream sensu {
  server localhost:11002;
}
upstream uchiwa {
  server localhost:11003;
}

upstream graphite {
  server localhost:11004;
}

// これと同じ感じを graphite,uchiwa,grafana でも用意
server {
  listen *:80;
  server_name sensu.localhost;

  access_log  /var/log/nginx/sensu.localhost.access.log main;
  error_log   /var/log/nginx/sensu.localhost.error.log;

  set_real_ip_from 10.0.0.0/8;
  real_ip_header   X-Forwarded-For;

  location / {
          proxy_read_timeout    300;
          proxy_connect_timeout 300;
          proxy_redirect        off;

          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header Host              $http_host;
          proxy_set_header X-Real-IP         $remote_addr;
          proxy_pass http://sensu;
  }

}

まとめ

取り急ぎ、サーバの状態の把握、状態異常の通知を受け取る環境が、dockerで簡単に作れるようになった。
sensu clientが落ちた時の通知、sensu server等が落ちた時等にどうするんだとかも、そのうち考えないと。
あと、自分用品質のDockerfileなので、ちょいちょい変更が入りそう。

goji ミドルウェアー内で404の判断を、ServeHTTPの手前でする方法

routerのmatch処理前のフックになるので、
冗長だけど、matchを自分ですればできるって話

  1. goji.DefaultMux.Router を読み込む
  2. web.GetMatch(*c) が動作するようになるので、それを活用
package main
 
import (
    "fmt"
    "github.com/zenazn/goji"
    "github.com/zenazn/goji/web"
    "net/http"
)
 
func main() {
 
    goji.Get("/hello/:name", hello)
    goji.Use(goji.DefaultMux.Router)
    goji.Use(Check)
    goji.Serve()
}
 
func hello(c web.C, w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", c.URLParams["name"])
}
 
func Check(c *web.C, h http.Handler) http.Handler {
    fn := func(w http.ResponseWriter, r *http.Request) {
        pattern := web.GetMatch(*c).Pattern
        if pattern != nil {
            fmt.Println("ok")
        } else {
            fmt.Println("404")
        }
        h.ServeHTTP(w, r)
    }
    return http.HandlerFunc(fn)
}

XCode6とSwiftで、viewControllerの上にviewControllerをポップアップみたいに出すやつ

ポップアップみたいなのを、実現したかったので適当に作った。
透明のレイヤーをかぶせたりしていて、もっと全うにできる方法が別にあるのかもしれない。

ボタンだけあって、おせる。

押すと、これがでる。青いエリアがView。その上に見えないけど、buttonを配備している。見えない閉じるボタン。

二つviewControllerを作る。ポップアップを開くボタンを作って、関連付けをする。present Modally ってやつ。

で、こんな感じ。



Presentationを、Over Current Contextにしておかないと、一つ目のviewControllerの描画をしないかんじになって、真っ黒になってまうので、設定が必要。


あと、ViewとButtonのBackgroundを Defaultにしておくこと。透明にするために。

ポップアップを、透明の背景ボタンを押したら閉じるように、閉じるしょり。

    @IBAction func close(sender: AnyObject) {
        self.dismissViewControllerAnimated(false, completion: nil)
    }


とりあえず、動く。いいやり方なのかわからないけど。

https://github.com/tomyhero/xcode-sandbox/tree/master/PopUp

XCode6で Bundle Identifier の名前とかを変更する

Bundle Identifierの値は、素直な感じに変更できない。

Unkoとかのままにしたくない。

Product Nameの Unkoを Lazy に変更する。

そうすると、Bundle Identifier が変更される。

ただ、まだUnkoにまみれてる。

右上の、xcodeproje ファイルの名前を変更すると、以下のように他変更する提案をしてきてきて、素敵。

いうても、まだ Unko いっぱいいるので、手動で Lazyに全部何も考えずrenameしていく。
UnkoTests.swift のファイル名だけではなく、中身のクラス名もrenameするのわすれずに。
だいたい、画面からはUnkoがきえた。

ファイルシステムから、調べる。うようよいる。置換をコンソールからガンガンやってみる。

バイナリーのファイル以外は、全部なおした。バイナリーのは、まぁ、適当にほっとく。

起動成功した!

これで、気兼ねなく、適当に名前をつけて、後で変更できるね。
自己責任だけどね!

Cacheクラス

前回作った、configクラスを使いつつ、シングルトン化し、
ブロックの結果をキャッスする関数を生やしたかんじ。

ソース

require 'singleton'
require 'memcache'
require 'bei/config'
require 'pp'

module Bei
  class Cache < Memcache
  include Singleton

  def initialize
    super ( { :servers => Bei::Config.instance.get(:memcached_servers) } )
  end


  def cacheable(key,expire=60*3)
    raise "NO_BLOCK_ERROR" unless block_given?

    unless( value = self.get(key) )
      #pp 'from value'
      value = yield()
      self.set(key,value,expire)
    else
      #pp 'from cache'
    end

    return value
  end

  end

使い方

こんな
かん

 cache = Bei::Cache.instance() 
# Memcache の継承なので、既にある関数はそのまま使える。
 cache.set('hi','hihihi')
 hi = cache.get('hi')

 # blockからデータ取得
 my_data = cache.cachable('my/3/data', 60 * 1) { { :user_id => 3 } } # 3 

 # memcachedからデータ取得
 my_data = cache.cachable('my/3/data', 60 * 1) { { :user_id => 2 } } # 3 (2じゃない)
 

自分用のConfigクラス

railsでどうすればいいのかわからなかったので、取り急ぎ作った。

require 'singleton'

module Bei
  class Config
    include Singleton

    def initialize
      file  = Rails.root.to_s + '/config/environments/config_' + ENV['RAILS_ENV'] + '.rb'
      @config = eval ( open(file).read )
    end

    def get(key)
      return Marshal::load(Marshal.dump(@config[key])) #deep clone
    end

  end 
end

config/environments/config_test.rb

{

  :twitter_consumer => {
    :key => 'foo',
    :secrete => 'bar'
  }
    
}  

使い方。

   config = Bei::Config.instance().get(:twitter_consumer)