Treasure Dataプラットフォームの管理画面を作る

前置きです。
IT界隈の人とHadoopの話をするとこういうギャップを感じます。
Hadoop使ってみたいところ >>> 実際に使っているところ


みんな

  • どう使って、どう収益に結びつけるか
  • 設計、サーバ購入、構築、運用などなどの技術的コスト

とか考え始めて止まっちゃいます。たぶん考えるよりTreasureData使ってみたほうが早いです。
そんなの使ってみないとよくわからんからです。
Hadoopガッツリ使ってました!なんて人そうそういないのです。


というわけで問答無用で構築して(そのあたりは前記事)、簡単な管理画面を作ってサービスチームに公開しています。
無料のトライアル版でもけっこう使えますし、気合入れればきっと数日で構築出来ます。


以下はその管理画面についてです。
ポイントはとにかく、『テキトウに作ってさっさと使ってみる』です。
(ちゃんとしたものはそのうちだれかが作ってくれるでしょう!)

管理画面の概要

Home

ただのHomeです

Table一覧

テーブルの一覧を確認します

Query実行画面

クエリを発行するフォームです

Job一覧

発行したクエリはここに登録され、「queued」→「running」と遷移して最終的に「success」になると結果が確認できます。

Job詳細

クエリの結果とステータスが確認できます

まとめ

このぐらい作ればとりあえずの動作確認はできます。
サクッと作って、「どう使うか」はサービスチームの人に考えてもらいましょうw





↓は興味ある人だけ

構築手順

Rubyが一番早そうだったので、Sinatraで構築しました。


td-agentをインストールします。Ruby用のライブラリ、tdコマンドもついてきます。

# vi /etc/yum.repos.d/td.repo 
[treasuredata]
name=TreasureData
baseurl=http://packages.treasure-data.com/redhat/$basearch
gpgcheck=0
# yum install td-agent

同梱のruby、gem、td使うためにPATH通しておきます。

# vi ~/.bash_profile
...
PATH=/usr/lib64/fluent/ruby/bin/:$PATH
...
ログインしなおし

sinatraとpassengerをインストール

# yum install httpd httpd-devel apr-devel apr-util-devel
# gem install sinatra
# gem install passenger
# passenger-install-apache2-module
Enterオラオラ→最後のほうに出てくるApacheコンフィグをメモ

テキトウに設定

# vi /etc/httpd/conf.d/passenger.conf
LoadModule passenger_module /usr/lib64/fluent/ruby/lib/ruby/gems/1.9.1/gems/passenger-3.0.13/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib64/fluent/ruby/lib/ruby/gems/1.9.1/gems/passenger-3.0.13
PassengerRuby /usr/lib64/fluent/ruby/bin/ruby

RackBaseURI /sinatra

# vi /etc/httpd/conf.d/td.conf
<VirtualHost *:80>
  ServerName  td.mikeda.local
  DocumentRoot /var/www/sinatra/public/
  <Directory /var/www/sinatra/public/>
    Order allow,deny
    Allow from all
  </Directory>
</VirtualHost>

ソースコードは/var/www/sinatraの下に作っていきます

$ cd /var/www/sinatra/
$ tree
.
|-- app.rb
|-- cli_test.rb
|-- config.ru
|-- public
|   |-- common.css
|-- tmp
|   |-- always_restart.txt.bak
|   `-- restart.txt.bak
`-- views
    |-- index.erb
    |-- job.erb
    |-- jobs.erb
    |-- layout.erb
    |-- query.erb
    `-- tables.erb

一部を紹介

config.ru
app_dir = File.expand_path(File.dirname(__FILE__))
require app_dir + '/app'
run Sinatra::Application
app.rb
require 'sinatra'
require 'td'
require 'td-client'

apikey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
query_db = "access" # クエリ実行するDB。後で選択できるようにする

cln = TreasureData::Client.new(apikey)


def format_time(t)
    t ? t.getlocal.strftime("%Y/%m/%d %H:%M:%S") : ""
end

get '/' do
  @title = "Home"
  erb :index
end

# テーブルのリストを表示
get '/tables/' do
  @title = "Table"

  dbs = cln.databases
  @tables = []
  dbs.each do |db|
    db.tables.each do |tbl|
      @tables << {
        :db_name    => tbl.db_name,
        :table_name => tbl.table_name,
        :count      => tbl.count,
      }
    end
  end

  erb :tables
end

# クエリ実行
get '/query/' do
  @title = "Query"
  erb :query
end

post '/query/' do
  query = params[:query]
  job = cln.query("access", query)
  redirect '/jobs/'
end

# Jobリスト表示
get '/jobs/' do
  @title = "Job List"

  jobs = cln.jobs

  @jobs = jobs.map { |job|
    start_at  = job.start_at ? job.start_at : Time.now
    end_at    = job.end_at ? job.end_at : Time.now
    exec_time = (end_at - start_at).to_i

    {
      :job_id    => job.job_id,
      :status    => job.status,
      :query     => job.query,
      :start_at  => format_time(start_at),
      :exec_time => exec_time,
    }
  }

  erb :jobs
end

# Jobのステータス、実行結果を表示
get '/job/:job_id' do |job_id|
  @title = "Job Status"

  job = cln.job job_id
  @job = {
    :job_id   => job.job_id,
    :status   => job.status,
    :start_at => format_time(job.start_at),
    :end_at   => format_time(job.end_at),
    :query    => job.query,
    :msg      => job.debug["stderr"]
  }

  if job.status == "success"
    @result = cln.job_result job_id
    if @result && !@result.empty?
      @col_size = @result[0].length
    end
  end

  erb :job
end
views/layout.erb
<html>
<head>
<link rel="stylesheet" href="/common.css" type="text/css">
<title>TD Admin</title>
</head>
<body>
<h1><%= @title %></h1>
<div class="navigation">
<ul>
 <li><a href="/">Home</a></li>
 <li><a href="/tables/">Table</a></li>
 <li><a href="/jobs/">Job</a></li>
 <li><a href="/query/">Query</a></li>
</ul>
</div>
<br/>
<%= yield %>
</body>
</html>
views/jobs.erb
<table border=4>
  <tr>
  <th>status</th> <th>start</th> <th>exec time</th> <th>query</th>
  <% @jobs.each do |job| %>
    <tr>
    <td> <a href="/job/<%= job[:job_id] %>"><%= job[:status] %></a></td>
    <td><%= job[:start_at] %></td>
    <td><%= job[:exec_time] %></td>
    <td><pre><%= job[:query] %></pre></td>
    </tr>
  <% end %>
</table>