チャットを面白おかしくするおもちゃ箱

SkypeIRCなど、チャットツールは
サービス開発のコミュニケーションツールとしては無くてはならないものである。
しかし時として、場の空気が重くなることもあるだろう。
そんな時、一筋の光をもたらすようなbotがいたなら。。。


時に笑いを提供し、時には場の空気を読まない悪戯小僧のような存在。
そんなライブラリ書いた。

リポジトリ

https://github.com/toritori0318/p5-toy

基本的には「おもしろ画像URL」をランダムに返すだけのライブラリなので、
どのチャットツールでもご利用可能です。
unazusanを利用していれば、exampleをコピペするだけですぐに投入できるでしょう。
プルリクも大歓迎!

画像取得用サービス一覧
独自ライブラリ
  • フリーBox(定義済みの定型文から、適当にピックアップするだけ)
  • ++ユーザランキング


やり過ぎると遊んでるだけになるので、ここぞという時に使うとよいでしょう。
チャットに幸あれ。

*1:エロ的な意味で

M3インスタンスを使わない手はない

最近、また新たにEC2のインスタンスサイズが増えましたね。
【AWS発表】M3インスタンスの新サイズおよび新機能 + EBSの値下げ + S3の値下げ


M3インスタンスはM1インスタンスの後継にあたる汎用インスタンスです。
今回新たに追加された m3.medium / m3.large は価格帯も性能もバランスよく、
非常に使いやすいインスタンスなのではないでしょうか。

本題

ふとM1/M3の料金表を見比べてみたんですが、
同じグレードで比較するとM3の方が料金安いんですよね。
しかも数値上はM3の方が性能も高そうです。
比較表にしてみましょう。*1 *2

インスタンス vCPU ECU RAM(GB) インスタンスストレージ (GB) EBS 最適化 ネットワークパフォーマンス 時間あたりの料金(東京リージョン)
m3.medium 1 3 3.75 1 x 4 SSD - $0.171
m1.medium 1 2 3.75 1 x 410 - $0.175
インスタンス vCPU ECU RAM(GB) インスタンスストレージ (GB) EBS 最適化 ネットワークパフォーマンス 時間あたりの料金(東京リージョン)
m3.large 2 6.5 7.5 1 x 32 SSD - $0.342
m1.large 2 4 7.5 2 x 420 $0.350
インスタンス vCPU ECU RAM(GB) インスタンスストレージ (GB) EBS 最適化 ネットワークパフォーマンス 時間あたりの料金(東京リージョン)
m3.xlarge 4 13 15 2 x 40 SSD $0.684
m1.xlarge 4 8 15 4 x 420 $0.700

まとめ

M1シリーズ、大容量のインスタンスストレージが欲しい以外は
選択する意味があまり無さそうですね。
これはもう「M1を捨ててM3を使え」というAmazonさんからのメッセージと受け取って
M3を使いまくりましょう〜

*1:表はこちらから抜粋させて頂いております http://aws.amazon.com/ec2/instance-types/ http://aws.amazon.com/ec2/pricing/

*2:比較する対象のないm1.smallとm3.2xlargeは除外しています

pip1.5に注意

とある日、素のインスタンスにchef走らせたらpip installで何故かエラーになりました。
デプロイ周りで特にレシピ変更してないし全く身に覚えがない。。。


差分chefインストールだとpipエラー出ないし何でだー?とトレース追ってみると

argparse==1.2.1

でエラってる。そんなバージョンないよって言われる。

pipのバージョン

いろいろ調査した結果、pipのバージョンが怪しいということになって
確認してみると確かに違う。エラーが出るのはpipバージョン1.5だ。
どうやらpip1.5では外部にホストされているモジュールは
デフォルトでインストール出来なくなっているようである。*1

回避策

pip installのオプションで
--allow-all-externalを付けるとうまくインストールされました。
--allow-external PROJECT_NAME で個別にURLを指定することもできるようです。

ということで

pipの最新バージョンを使うと
デフォルトの挙動が変わってしまうので注意しましょう、というお話でした。
他にもいくつか下位互換が失われている変更があるようです。
http://www.pip-installer.org/en/latest/news.html#id1


つかリリース日2014/1/1って。。。

*1:確かに、argparseの1.2.1だけgooglecodeに置かれてる。https://pypi.python.org/simple/argparse/

2013年振り返りと2014年

年末年始バタバタしてたので今頃振り返ってみます。

仕事

DevOps頑張ってた。
監視自動化とか、開発運用共通化とか。


改善前は各モジュールで独自の開発フローや秘伝のタレ化したデプロイツールなど、
担当者がいないと誰も管理できなくなってしまうという
「担当者SPOF」になってしまっていて、
それを改善したいなーと思い
vagrant + chef + cloudformation」という構成で
インフラ構築/運用/開発フローを共通化するためにいろいろ頑張ってた。


現在はこれを拡張して

  • AMIの作成
  • image-idを自動でcloudformationのテンプレートに適用
  • そのままcloudformationのstack作成/更新/削除

という一連のフローを同じrakeタスクで実行できるようにした。


その結果、どのシステムでも全く同じ手順で

  • デプロイ
  • インフラ構築
  • クラスタ構築
  • スケールアウト/スケールイン/スケールアップ/スケールダウン

などが実現できるようになった。

これ実現出来たのは地味にでかいと思ってる。

趣味コーディング

ひょんなことからCPAN Authorになれた。嬉しい。
しかし書きたかったコードはたくさんあったんだけど
それほど書けてなかったような気がする。

プライベート

上の子が幼稚園に入った。
いまだに朝嫌がることはあるが、実際に登園すると普通に楽しんでるようなのであんまり心配してない。女の子にモテてるらしい。くやしい。
下の子はどんどんかわいくなってる。うへぇ
あと結婚10周年でした。美味しいもの食べた。

2014年

仕事関係ではソケットサーバパワーアップさせないと辛いなーって感じになってきてるので
本気でチューニング始める予定。予定は未定。


あと気になってるソリューションがたくさんあるので
徐々に勉強復活させたい。
以下とか。イミュータボー?

  • Docker
  • Serf
  • Mesos
  • Graphite
  • Sensu


yogaコマンドも追加機能したいタスクがどんどん溜まってきてるので消化したい。
やりたい。やらねば。

nginx-lua-moduleでLuaスクリプト書いてみた

プロダクトでWebサーバ上の現在時刻出したい要件がありました。
これぐらいの処理ならアプリケーション介さずともnginxだけでやりたいですね。
現在時刻ヘッダーにつけるならnginx標準機能で出来そうなんですが、
深淵なる理由からコンテンツとして欲しいとのこと。


今後、Nginx+Lua試してみたいなーという要件もありましたし、
せっかくなのでこの機能をLuaスクリプトで書いてみることにしました。


NginxでLuaを動かす環境

まず環境整えるのが若干大変でした。*1
以下のような手順でインストールするようです。

ライブラリのパスなど気をつけないとハマるかもしれませんね。

nginxレシピ

opscodeのnginxクックブックにはluaをインストールするレシピもありました。
しかし、例のごとくそのままでは動かせなかったので
一部オーバーライドしたクックブックを書いてみました。
AWSの生AMIにこれをそのままプロビジョニングするとNginx+Luaが起動することを確認しています。
https://github.com/toritori0318/chef-nginx-lua-sample

直したところ

recipes/lua.rbにパッチを当てたのと、
recipes/source.rbluaをインストールする処理を差し込んだくらいです。
ちなみにcjsonを入れているのは、今回json形式でレスポンスさせたかったからです。
Luaを動かすだけであればcjsonを入れる必要はありません。

サンプルアプリ仕様

  • /current_timeにアクセスすると、plainテキストでdatetimeが返却される
  • df=epoch が指定されていると、エポック秒が返却される
  • rf=json が指定されていると、json形式で返却される

サンプル実装

/etc/nginx/sites-available/lua_current_time

nginxのコンフィグです。
特に語るところはありません。

# Lua soライブラリのパス指定。今回はcjsonのため(ファイルパターンも指定が必要らしい)
lua_package_cpath "/usr/local/lib/lua/5.1/?.so";

server {
  listen   80;

  access_log  /var/log/nginx/access.log;

  location /current_time {
    # 外部Luaスクリプト読み込み
    content_by_lua_file /etc/nginx/lua/current_time.lua;
  }

  location / {
    default_type 'text/plain';
    # コードもそのまま書ける
    content_by_lua "ngx.say('Hello,lua!')";
  }
}
/etc/nginx/lua/current_time.lua

パラメータを受け取ってゴニョゴニョしています。
特に語るところはありません。

local cjson = require "cjson"
local ctime = ""

local args = ngx.req.get_uri_args()
if args.df == "epoch" then
    ctime = os.date("%s")
else
    ctime = os.date("%Y-%m-%dT%H:%M:%S %z")
end
if args.rf == "json" then
    ngx.header.content_type = "application/json";
    ngx.print(cjson.encode({current_time = ctime}))
else
    ngx.header.content_type = "text/plain";
    ngx.print(ctime)
end


非常に簡単ですね。

ベンチマーク

abしてみました。
コマンドパラメータはこんな感じです。

ab -n 20000 -c 2 http://127.0.0.1/xxxx
/ (Hello Lua)
Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      2
Time taken for tests:   2.018 seconds
Complete requests:      20000
Failed requests:        0
Write errors:           0
Total transferred:      3060000 bytes
HTML transferred:       220000 bytes
Requests per second:    9912.15 [#/sec] (mean)
Time per request:       0.202 [ms] (mean)
Time per request:       0.101 [ms] (mean, across all concurrent requests)
Transfer rate:          1481.02 [Kbytes/sec] received
/current_time
Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /current_time
Document Length:        25 bytes

Concurrency Level:      2
Time taken for tests:   2.339 seconds
Complete requests:      20000
Failed requests:        0
Write errors:           0
Total transferred:      3340000 bytes
HTML transferred:       500000 bytes
Requests per second:    8549.87 [#/sec] (mean)
Time per request:       0.234 [ms] (mean)
Time per request:       0.117 [ms] (mean, across all concurrent requests)
Transfer rate:          1394.36 [Kbytes/sec] received
/static/index.html(静的ファイル配信。このファイルは手動で作りました)
Server Software:        nginx/1.4.3
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /static/index.html
Document Length:        6 bytes

Concurrency Level:      2
Time taken for tests:   1.942 seconds
Complete requests:      20000
Failed requests:        0
Write errors:           0
Total transferred:      4680000 bytes
HTML transferred:       120000 bytes
Requests per second:    10298.39 [#/sec] (mean)
Time per request:       0.194 [ms] (mean)
Time per request:       0.097 [ms] (mean, across all concurrent requests)
Transfer rate:          2353.34 [Kbytes/sec] received


静的ファイル配信よりわずかに落ちますが十分早い。


Nginx+Lua、環境設定が若干面倒ですが、
上記クックブックを使うと一発で環境作れますし、
Luaスクリプトもお手軽に書けそうなので試してみてはいかがでしょうか〜
(゚Д゚)オLua!オLua!

*1:※追記 openrestyを使うと楽できるようです! http://openresty.org/

chefでattributeファイルを上書きしたい時

attributeファイルのオーバーライド

公式レシピを使う場合に、attributesファイルの一部のパラメータだけ更新したい時があると思います。
jsonでいちいち指定しても良いんですが、共通で設定したいパラメータは
site-cookbooks以下にattributesファイルで書いておきたいこともあります。


その場合、今までは馬鹿正直にオリジナルのレシピを丸々コピペして一部だけ更新する、
といったように使っていたんですが*1
include_attribute使えば簡単に書けるのでは?と思った。

nginxの場合

いままではベタ書きしてたのを…

site-cookbooks/nginx/attributes/default.rb
# オリジナルのattributes/default.rbをコピペ
default['nginx']['version']      = '1.2.9'
default['nginx']['package_name'] = 'nginx'
default['nginx']['dir']          = '/etc/nginx'
default['nginx']['script_dir']   = '/usr/sbin'
default['nginx']['log_dir']      = '/var/log/nginx'
default['nginx']['binary']       = '/usr/sbin/nginx'

case node['platform_family']
when 'debian'
  default['nginx']['user']       = 'www-data'
  default['nginx']['init_style'] = 'runit'
when 'rhel', 'fedora'
  default['nginx']['user']        = 'nginx'
  default['nginx']['init_style']  = 'init'
  default['nginx']['repo_source'] = 'epel'
when 'gentoo'
  default['nginx']['user']       = 'nginx'
  default['nginx']['init_style'] = 'init'
else
  default['nginx']['user']       = 'www-data'
  default['nginx']['init_style'] = 'init'
end

default['nginx']['upstart']['runlevels']     = '2345'
default['nginx']['upstart']['respawn_limit'] = nil
default['nginx']['upstart']['foreground']    = true

default['nginx']['group'] = node['nginx']['user']

default['nginx']['pid'] = '/var/run/nginx.pid'

default['nginx']['gzip']              = 'on'
default['nginx']['gzip_http_version'] = '1.0'
default['nginx']['gzip_comp_level']   = '2'
default['nginx']['gzip_proxied']      = 'any'
default['nginx']['gzip_vary']         = 'off'
default['nginx']['gzip_buffers']      = nil
default['nginx']['gzip_types']        = %w[
                                          text/plain
                                          text/css
                                          application/x-javascript
                                          text/xml
                                          application/xml
                                          application/rss+xml
                                          application/atom+xml
                                          text/javascript
                                          application/javascript
                                          application/json
                                          text/mathml
                                        ]
default['nginx']['gzip_min_length']   = 1_000
default['nginx']['gzip_disable']      = 'MSIE [1-6]\.'

default['nginx']['keepalive']            = 'on'
default['nginx']['keepalive_timeout']    = 65
default['nginx']['worker_processes']     = node['cpu'] && node['cpu']['total'] ? node['cpu']['total'] : 1
default['nginx']['worker_connections']   = 1_024
default['nginx']['worker_rlimit_nofile'] = nil
default['nginx']['multi_accept']         = false
default['nginx']['event']                = nil
default['nginx']['server_tokens']        = nil
default['nginx']['server_names_hash_bucket_size'] = 64
default['nginx']['sendfile'] = 'on'

default['nginx']['access_log_options']     = nil
default['nginx']['error_log_options']      = nil
default['nginx']['disable_access_log']     = false
default['nginx']['install_method']         = 'package'
default['nginx']['default_site_enabled']   = true
default['nginx']['types_hash_max_size']    = 2_048
default['nginx']['types_hash_bucket_size'] = 64

default['nginx']['proxy_read_timeout']      = nil
default['nginx']['client_body_buffer_size'] = nil
default['nginx']['client_max_body_size']    = nil

# 一部オーバーライド
default['nginx']['version']        = '1.4.3'
default['nginx']['install_method'] = 'source'


こう直す。
別ファイルに書く(ファイル名はなんでもいいはず)。

site-cookbooks/nginx/attributes/default_ext.rb
include_attribute 'nginx::default'

# customize
default['nginx']['version'] = '1.4.3'
default['nginx']['install_method'] = 'source'


さっき思いついたのでもしかしたら副作用があるかもしれないですが、
特に問題なければ今まで書いたレシピだいぶ綺麗になりそう。

*1:http://d.hatena.ne.jp/toritori0318/20130509/1368107821 これのopensshのattributeなんかそうですね

vagrant-awsからメタデータ表示するvagrant-ec2info書いた

https://github.com/toritori0318/vagrant-ec2info


vagrant-aws使いまくっているんですが、
そのインスタンスdnsとかタグとかさくっと表示したいなーと思って書きました。
そんだけ。


こんなイメージです。


vagrant-aws必須。