読者です 読者をやめる 読者になる 読者になる

vagrant sahara先輩

簡単にスナップショットをとってロールバックしたりできる
sahara pluginのインストールがクソ楽になっていた。

$ vagrant -v
Vagrant version 1.2.7

$ vagrant plugin install sahara

done. successful……

まじかよ!
1.2.2の時、会社の隣の席でインストールしていた人が死ぬほど苦労していたのでテンション上がった

インストール後はこんな感じ↓↓で使用

Sandboxモード有効(スナップショット作成)
$ vagrant sandbox on
Sandboxモードを有効にした状態まで戻す
$ vagrant sandbox rollback
状態変更を確定(スナップショットの更新)
$ vagrant sandbox commit
Sandboxモード解除(スナップショット削除
$ vagrant sandobox off

Chefのレシピテストで非常に有用。

ハマったメモ

chef-soloのノリでattributeを最上位に書いてたら全く認識されなくてハマった・・
chef-serverでnodeにattributeを指定するときは'nomal'以下に記述する必要があった

"normal": {
"tags": [

],
"network_eth0": {
"HWADDR": "00:0c:29:9f:ea:47",
"ONBOOT": "yes",
"IPADDR": "192.168.53.103",
"PREFIX": "24",
"GATEWAY": "192.168.53.1"
}
}

Chef-solo導入〜簡単な設定まで

mac再インストールのついでに最近勉強中のchef-soloで状態を管理することにした。

※Webで資料を探してみましたが、Macの状態管理に触れている記事が少ないので参考になれば。**結局マニュアル最強ですね^^**

用語

chefは独自の用語が多い。

  • Knife:リポジトリ操作用のコマンドセット。(cookbookの生成等
  • Recipe:コード化された手順書、サーバーの状態
  • Cookbook:特定のレシピに必要なデータやファイルを纏めたもの
  • kitchen:Cookbookを含むChefの実行に必要な一連のファイルを纏めたもの
    • Kitchen > Cookbook > Recipeの階層で管理される。
  • Attribute:Recipeで使用する変数をまとめたもの
  • File:Recipeで作成するファイル。設定ファイルなんかを作成できる。
  • Library:Recipe上から実行するスクリプト。
  • Template:Attributeを埋め込んで内容を編集する前提の設定ファイルのテンプレート。
  • Fileとは内容を動的に変更する点が違うっぽい
  • Package:パッケージの状態を記述する。
  • Provider:パッケージの扱い方などOS依存な部分の抽象化を行う。
  • Metadata:Cookbookの名称、含まれるRecipeの情報、Cookbook同士の依存関係などを記述したRubyまたはJSONファイル

Resource

Chefのレシピ記述に使用するDSL。
構文とかは基本的にはRubyだけど、
Chef的な処理(インストールやら、設定ファイルのテンプレ指定やら)を定義する
Resourceと呼ばれる関数。

結構あるけどココにまとめられている。
http://docs.opscode.com/resource.html

よく使うのはこのへん
log:Chefのログを操作する
package:パッケージの状態を記述する
service サービスを操作する(start,restart,reload,stop等
template:設定ファイルをChefでいじる


chef&knifeインストール

$ curl -L http://www.opscode.com/chef/install.sh | sudo bash

knifeの設定

全部デフォでエンター連打

$ knife configure
※knife.rbの場所は覚えておく

knife-soloインストール

$ gem install knife-solo

Kitchen(リポジトリ)生成

Cookbookの入れ物であるKitchenを生成します。

$ cd ~/Dev/Chef
$ knife solo init mac
$ cd ./mac

※knife soloはリモートにレシピを反映させるための拡張なのですが、
 kitchenの生成もできます。なんだかややこしい。knifeデフォルトできたほうがスマートなんじゃ・・・

mac用のcookbookをopscodeからダウンロード

対象がLinuxだとここまででさくっと動くのですが
macはデフォルトではmacportsを使ってインストールしようとしますし、dmgのインストールにも一手間必要。

その辺をサクッとやってくれるcookbookがopscodeに上がっているのであやかります。

$ vim Gemfile
+ source 'https://rubygems.org'
+ gem 'librarian-chef'

$ bundle --path cookbooks

※ちなみにcookbooksには流用するcookbookを置いてます。自作はsite-cookbooksへ。
なお、cookbookはknife.rb(リモート用)とかsolo.rb(localhost用)に以下のようにカンマ区切りで書くと書いたパスを読み込んでくれます。
今回はkitchen配下にsolo.rbを作ります。(リモート反映時も使うのでknife.rbもついでにやっててもいい。

$ vim solo.rb
cookbook_path ["cookbooks", "site-cookbooks","vendor/site-cookbooks"]
※knife.rbの配置場所はknife configureで設定した箇所。
$ vim Cheffile
+ site 'http://community.opscode.com/api/v1'
+ cookbook 'mac_os_x'
+ cookbook 'dmg'
+ cookbook 'homebrew'
+ cookbook 'zip_app'
$ librarian-chef install 

これでmac用のcookbook4つが入る

Cookbook作成

dmg

$ knife cookbook create vagrant -o site-cookbooks
$ vim site-cookbooks/vagrant/recipes/default.rb
+ dmg_package "vagrant" do
+    volumes_dir "Vagrant"
+    source http://files.vagrantup.com/packages/7ec0ee1d00a916f80b109a298bab08e391945243/Vagrant-1.2.7.dmg"
+   type "pkg"
+   package_id "com.vagrant.vagrant"
+   action :install
+ end
zsh(brewインストール=>gitで設定をclone=>linkファイル生成)
$ vim site-cookbooks/zsh/recipe/default.rb
+ package "zsh" do
+   action :install
+ end
# gitでoh-my-zshも落としてくる
+ git "#{ENV['HOME']}/.oh-my-zsh" do
+  repository "git://github.com/robbyrussell/oh-my-zsh.git"
+  reference "master"
+  action :sync
+  user "wing"
+  group "staff"
+end

#  gitにあげているzshrcを落としてくる
+git "#{ENV['HOME']}/personal" do
+ repository "https://github.com/ma2k8/personal"
+  reference "master"
+  action :sync
+  user "wing"
+  group "staff"
+end

# リンクはりはり
+ link "#{ENV['HOME']}/personal/mac/zsh/.zshenv" do
+   to "#{ENV['HOME']}/"
+ end
ミドル系の起動までやるなら

brewインストールのレシピ書いて
(start|stop|restart)_command でサービス制御のコマンドをデフォルトから上書きしちゃえばいけた。
(Chef側ではmacのサービス起動コマンドを見つけられないみたい

※vagrant使うまでは僕もmacのローカルに開発環境立ててvhostをgusmaskで切り替えて頑張ってたんだけど
 vagrantでやったほうが間違いなく捗るからmac上にミドル立てるのはおすすめしない。

run_list作成
$ vim nodes/localhost.json
{
  "user": {
    "name": "wing",
    "group": "staff",
    "home": "/Users/wing",
    "dotfiles_repo": "https://github.com/ma2k8/dotfiles.git"
  },
  "homebrew": {
    "run_as": "wing"
  },
  "run_list": [
    "recipe[mac_os_x]",
    "recipe[dmg]",
    "recipe[homebrew]",
    "recipe[zsh]",
    "recipe[vagrant]"
  ]
}
設定反映
$ sudo chef-solo -c solo.rb -j nodes/localhost.json

次はvagrant
その次はchef-serverまで。。。ブログ書き慣れてないからとまとめるの大変(ってかまとまってない

ruby2.0.0インストール

$ brew install readline openssl rbenv ruby-build
$ brew link readline openssl --force
 
$ echo 'eval "$(rbenv init - zsh)"' >> ~/.zsh_profile

$ source ~/.zsh_profile

homebrewから証明書を取得
※ruby2.0をhomebrewから入れたopensslを使ってコンパイルするとssl証明書エラーになるので

$ brew install curl-ca-bundle
$ brew list curl-ca-bundle
/usr/local/Cellar/curl-ca-bundle/1.87/share/ca-bundle.crt
$ cp /usr/local/Cellar/curl-ca-bundle/1.87/share/ca-bundle.crt /usr/local/etc/openssl/cert.pem

ruby インストール

# 最新版を確認
$ rbenv install -l

# インストール
CONFIGURE_OPTS="--with-openssl-dir=`brew --prefix openssl` --with-readline-dir=`brew --prefix readline`" rbenv install 2.0.0-p247

# デフォルトのrubyではなく、インストールしたrubyを使用するよう設定
$ rbenv rehash
$ rbenv global 2.0.0-p247

# バージョン確認
$ rbenv version
2.0.0-p247 (set by /Users/wing/.rbenv/version)
$ ruby -v
ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0]

# 最後にrehashの自動化
$ gem i rbenv-rehash

homebrewインストール

MacOSを久しぶりにクリーンインストールしたので、ついでに諸々の手順を残しとく

前提:XCodeでCommand Line Toolをインストール

$ ruby -e "$(curl -fsSLk https://gist.github.com/raw/323731/install_homebrew.rb)"

$ brew doctor
Your system is raring to brew.
 
$ brew -v
Homebrew 0.9.4

macでオートマウント

前からやろうやろう思っていて放置しまくっていて昨日ついに実施したmacのオートマウント

やってみたら



むっさ快適!!!!!!!!!!

/etc/auto_masterに追記

+ /Users/USER/mnt auto_smb nosuid,resvport

※マウントポイントはお好みで

/etc/auto_smbに追記

+ DIR1 -fstype=smbfs ://USER:PASS@HOST/hogehoge
+ DIR2 -fstype=smbfs ://USER:PASS@HOST/hugahuga
+ DIR3 -fstype=smbfs ://USER:PASS@HOST/foofoo

autofsd再起動

sudo launchctl stop com.apple.autofsd
sudo launchctl start com.apple.autofsd

mount

sudo automount -vc

デフォルトだと一時間アクセスしなければ自動でアンマウント
※/etc/autofs.confのAUTOMOUNT_TIMEOUTで変更可能

アンマウントされてもアクセスしたら自動でマウントしてくれる。

Tuningathon

1/19(土)のTuningathonに友人の@murahumと参加したのでうろ覚えですが詳細をまとめました。

お題

単一スレッドで5つのクエリを走らせ、どれだけ早く処理できるかを競いました。

状況確認

CPU、メモリともかなり潤沢。。。
CPU4コアのメモリ16GBでした(1.6GBと見間違えました
AWSの【M1 Extra Large】インスタンスが配布されていたようです。

最初にベンチを取ると140秒近くかかってました。


基本戦略の決定

・インデックスの最適化
 ⇛テーブル数の割に計測スクリプトで参照しているテーブルが少ないので「計測スクリプト対策INDEX」を揃えれば効果あるかも

・できるだけDBをメモリに載せる
 ⇛メモリがかなり余っていたのでBuffer poolにDBを全て載せれば早くなるだろうと推測し採用
innodbは業務でも使用したことがあるので、ベンチ取りながら知っているパラメータを試してみようと考えてました。

序盤

とりあえず思いつくinnoDBのパラメータを設定するとスコアが大幅に改善
140秒⇛18秒


次にINDEXを確認
参照しているテーブルはpage,revisionのみだったので確認すると色々INDEXが貼られていました。
相棒と話し合った結果、取り敢えず全部消して、1から貼り直そうという話になりました。

一旦SQLdumpを取得後、page,revisionテーブルのINDEXを主キー以外全て削除したところ、またまたスコアが改善
18秒⇛10秒
※このスコアで3回目の計測1位に!!!!(二人ともテンション上がりまくりでしたwww


中盤

・ここから実機のMySQL5.5を5.6にバージョンアップしようと試みるがソースコンパイル久方ぶり過ぎて不安になり断念。

・page,revisionに通常のINDEXを貼っていく。
 が、貼れば貼るほど遅くなる。。。なにこれ・・・
 
 MySQLは一つのクエリにつき、一つのINDEXしか使用できないので複数ある場合はあまり効率のよい動きはしないみたい。

 そこで複合INDEXを採用してみることにして適当にぺたぺた

 10秒⇛17秒に!!!
 なぜにwwww

 泣く泣くrevisionのINDEXのみ削除。pageのINDEXは上手く効くいていたようで

   create index red_name_touched on page(page_is_redirect,page_namespace,page_touched);

 17秒⇛9秒に
 

終盤

ここからは試行錯誤で色々やりましたが中々効果がでず。。。
運営からは6秒台とのアナウンスもあって焦る焦る。。。

効果が出たのは@murahumのINDEX
 ●revisionに以下のクエリで複合INDEXを追加

   create index user_page_time on revision(rev_user,rev_page,rev_timestamp(6));

9秒⇛7秒に

※【SUBSTRING(rev_timestamp,1,6)】の部分で6バイト目までしか参照しないのでINDEXもそこで止めてます。これによりCardinality値を下げる効果ああります。
groupbyにINDEXが効くのは知識として知っていましたが、INDEXを6バイト目で止める部分は自分も考えつかなかった。。。さすがの@murahumクォリティ

@murahumの方で検証したところ、
create index user_page_time on revision(rev_user,rev_page,rev_timestamp);
でも測定値はあまり変わらなかったそうです。

以下によると一意なら参照も早くなったかもですが、timestampは一意ではなかったのであまり変化はなかった模様。勉強になりました!
http://dev.mysql.com/doc/refman/5.1/ja/create-index.html
>もしカラム内の名前の最初の10文字が違っていれば、このインデックスは name カラム全体から作成されたインデックスよりも遅くは無いはずです。また、部分的なカラムをインデックスに利用する事でインデックス ファイルを小さくする事ができるので、ディスクのスペースを節約し、 INSERT 操作を早くする事ができます。


この後は試行錯誤。。。。

MyISAMのパラメータを入れてストレージエンジンをMyISAMに変更
7秒⇛45秒(即切り戻し)

インデックスの探索タイプをBTREE⇛HASHへ変更
⇛なぜかUSINGパラメータが効かず断念


最後にmysqldにniceかけてログの出力を全て止めてタイムアップ

結果

なんと「7.32306790352」で総合三位でした!!!!
イケメン相棒のINDEXが素敵だったのが勝因でしたwwwあざすwww

そして、沖縄勢が10位以内を4つの枠を占める大健闘!

考察&反省点

1位との差は5つ目のクエリの部分だったと思います。
5つめのクエリをEXPLAINしたらtypeがALLになっていたのは気づいていたのですが全然手が思い浮かびませんでした。

インタビューでは主キーを変更したといっていたのでインスタンスが生きているうちにちょろっと調べてみたところ、rev_pageを主キーに指定することができました。
でした。まさかrev_pageが一意な値だったとは。。。。
そしてうっかりして主キーに変更後のベンチをとれなかった。。。

create index id on revision(rev_id);
create index user_time on revision(rev_user,rev_timestamp);
ALTER TABLE revision drop PRIMARY KEY;
ALTER TABLE revision ADD PRIMARY KEY(rev_page);


・クエリ5

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE revision ALL NULL NULL NULL NULL 1620793 Using temporary; Using filesort
1 SIMPLE page eq_ref PRIMARY,red_name_touched PRIMARY 4 wikipedia.revision.rev_page 1 Using where


・クエリ5(主キー変更後)

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE page ref PRIMARY,red_name_touched red_name_touched 5 const,const 822882 Using index; Using temporary; Using filesort
1 SIMPLE revision eq_ref PRIMARY PRIMARY 4 wikipedia.page.page_id 1


my.cnfは以下のとおり
DB自体は800MBほどしか容量がありませんでしたが、最終的にbuffer poolは5GB割り当て
その他のbuffer_sizeたちはあまり多く割り当てすぎると若干遅くなった気がしたので
以下のようにしてます。一応、単一スレッドの処理速度を計る今回の競技には全く関係ありませんがdefaultのmax_connections150きても耐えうる感じにはしてみました。
正直buffer pool以外は効果薄だったかも。

[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

# Global Area
innodb_file_per_table
innodb_buffer_pool_size=5120M
innodb_log_buffer_size=8M
innodb_log_file_size=128M
innodb_flush_method=O_DIRECT
query_cache_size=100M
table_open_cache=1024

# Thread Area
sort_buffer_size=16M
read_rnd_buffer_size=16M
max_allowed_packet=16M
join_buffer_size=8M
key_buffer_size=8M

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

ということで大分ふわふわした道筋でしたが、3位だったので大満足です!

運営の方お疲れ様でした。次回も参加したいと思いますのでよろしくお願いいたします。




追記:
運営よりAMIが公開されたので主キーを張り替えて試してみました。

結果「6.32319498062」で優勝には届かず。。。

他にオーバーヘッドがないか確認し、pageのINDEXを以下のように張り替えたら「5.8722589016」がでました。満足。

Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment
page 0 PRIMARY 1 page_id A 1714494 NULL NULL BTREE
page 1 name_red 1 page_namespace A 18 NULL NULL BTREE
page 1 name_red 2 page_is_redirect A 18 NULL NULL BTREE
page 1 touched 1 page_touched A 206 NULL NULL BTREE