さくっとインスタンスを立てて、
さくっとコマンドを実行し、
さくっと結果を取得するツールを作りました。
- GitHub
ちょっとした作業の為にインスタンスを立てたい時も便利です。
さくっとインスタンスを立てて、
さくっとコマンドを実行し、
さくっと結果を取得するツールを作りました。
ちょっとした作業の為にインスタンスを立てたい時も便利です。
長らくchef-solo+knife-soloと連れ添ってきたのですが、
そろそろzeroの学習をしておこうと色々とchef-zero移行を解説なさっているサイトを見て回りました。
たくさん解説されていらっしゃるサイトがあるのですが、一応自分なりにもまとめてみます。
とりあえずrbenv,ruby,bundler、とインストールして、いつもの環境を作ります。
Gemfileはこんな感じの最小構成。こいつでbundle installします。
source "https://rubygems.org" gem "chef" gem "knife-zero"
とりあえずテストとして、apacheをインストールするレシピと、それを適用するjsonファイルを用意します。
$ mkdir -p cookbooks/httpd/recipes $ echo 'package "httpd" do > action :install > end' > cookbooks/httpd/recipes/default.rb $ echo '{ "run_list": "recipe[httpd]" }' > web.json
chef-soloであれば、cookbook_pathを記述したsolo.rbを用意してchef-soloを実行するところですね。
sudo bundle exec chef-solo -c solo.rb -j web.json
chef-zeroのlocal_modeを使うと、このような感じになるようです。solo.rbが不要になり、少しだけ楽になりましたね。
sudo bundle exec chef-client -z -j web.json
実行後、nodes/(hostname).json ができていました。
内部的にはchef-serverが動いているわけですから、このノード(localhost)の状態がchef-serverの管理下に置かれているということですね。
(Node Objectと言うそうです)
このあたりはchef-server的な仕組みで動いていると思うので、chef-serverを学習した方が理解がはさそうな気がします。
ローカルにあるレシピを使って、リモートホストをプロビジョニングする場合はknife zeroを使います。
仕組みやチュートリアル含め、開発者様のQiitaを一読すると理解が早いと思います。
先ほど作ったレシピを使って、knifeしてみましょう。
まずはリモートホストをknife zeroの管理に追加します。
mkdir で “許可がありません” とエラーになる場合は、sudoできる権限をつけてあげて –sudo を追加します。
bundle exec knife zero bootstrap remote.com --sudo
nodes/(hostname).jsonが作成されていると思います。
また、node showすると、ノードが管理されている事がわかります。
$ bundle exec knife node show remote.com --local-mode Node Name: remote.com Environment: _default FQDN: remote.com IP: 10.0.2.15 Run List: Roles: Recipes: Platform: centos 7.0.1406 Tags:
ローカルモードは今後常に有効にするので、設定ファイルを作成して省略できるようにしておきます。
環境によってはsudoを省略するオプションも記述しておいた方が捗りそうです。
mkdir .chef echo 'local_mode true' > .chef/knife.rb echo 'knife[:use_sudo] = true' >> .chef/knife.rb bundle exec knife node show remote.com
それではレシピを適用してみましょう。
knife soloの場合は、nodes/(hostname).json のrun_listを記述してknife solo cookしていました。
bundle exec knife solo cook (hostname)
knife zeroの場合も、nodes/(hostname).json のrun_listを記述して、以下のコマンドを実行するだけです。
bundle exec knife zero converge '(query)'
knife soloと違い、queryにマッチするホストをすべてプロビジョニングできるようです。
これはかなり便利そうです。
今回の用に単一ホストをプロビジョニングする場合は、このような感じで書きます。
$ bundle exec knife zero converge 'name:remote.com' remote.com Starting Chef Client, version 12.5.1 remote.com resolving cookbooks for run list: ["httpd"] remote.com Synchronizing Cookbooks: remote.com - httpd (0.0.0) remote.com Compiling Cookbooks... remote.com Converging 1 resources remote.com Recipe: httpd::default remote.com * yum_package[httpd] action install remote.com - install version 2.4.6-31.el7.centos.1 of package httpd remote.com remote.com Running handlers: remote.com Running handlers complete remote.com Chef Client finished, 1/1 resources updated in 06 seconds
ちゃんとapacheがインストールされましたね。
knife node show してみるとレシピが追加されている事がわかります。
$ bundle exec knife node show remote.com Node Name: remote.com Environment: _default FQDN: remote.com IP: 10.0.2.15 Run List: recipe[httpd] Roles: Recipes: httpd, httpd::default Platform: centos 7.0.1406 Tags:
細かい点では変わったところもありますが、今までのレシピが全く使えなくなるという事はなく、
確かにchef-solo+knife-soloからの移行は問題なさそう。
MySQL 5.7が正式にGAとしてリリースされたようです。
というわけでどこが変わったのか、触って見つつ調査してみようと思います。
ちなみにインストール先は AWS の r3.xlarge (Amazon Linux) です。
Redhat6向けのyumリポジトリをインストールし、mysql-community-serverをインストール。
sudo rpm -ivh http://dev.mysql.com/get/mysql57-community-release-el7-7.noarch.rpm sudo yum install -y mysql-community-server sudo service mysqld start # 原因が把握できていないのですが、mysqlのディレクトリに初期DBが構築されていない事があるようです。 # その場合は、以前のバージョンであれば mysql_install_db を実行して初期DBを構築しますが、 # mysql5.7からは mysqld --initialize に変更されたようです。 mysqld --initialize --datadir=/var/lib/mysql
rootでログインする際の初期パスワードは、下記の様にmysqlのエラーログに出力されるようです。
ノーヒントだったので困った。5.6は .mysql_secret というファイルに保存されていたのに・・
2015-10-22T10:32:43.950929Z 1 [Note] A temporary password is generated for root@localhost: xxxxxxxxxxx
ログインできることを確認したらインストール完了です。
適当にSQLを実行した際、このようなエラーが表示された場合は、 validate_password プラグインが有効になっています。
プラグインを無効化するか、適切なパスワードをrootユーザに設定してください。
(root以外のユーザも、強度の高いパスワードの設定を求められて面倒な為、今回はプラグインを無効化しています。本番環境では有効にしておきましょう)
> alter user root@localhost identified by "xxxxxxxxx"; or > uninstall plugin validate_password;
http://mysqlserverteam.com/whats-new-in-mysql-5-7-generally-available/
5.6から3倍速くなったぜ!と豪語しているので、
別環境に mysql 5.6 をインストールし、sysbench でベンチマークを取ってみました。
sudo yum install -y sysbench --enablerepo=epel echo "CREATE DATABASE sbtest; GRANT ALL PRIVILEGES ON sbtest.* TO sbtest@localhost IDENTIFIED BY 'sbtest';" | mysql -u root -p sysbench --test=oltp --db-driver=mysql --mysql-password=sbtest prepare sysbench --test=oltp --db-driver=mysql --mysql-password=sbtest run
ほどほどにメモリは割り当てています。
innodb_buffer_pool_size = 20G innodb_log_file_size = 512M innodb_flush_method = O_DIRECT max_connections = 100 join_buffer_size = 128M sort_buffer_size = 2M read_rnd_buffer_size = 2M sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
sysbench 0.4.12: multi-threaded system evaluation benchmark Running the test with following options: Number of threads: 1 Doing OLTP test. Running mixed OLTP test Using Special distribution (12 iterations, 1 pct of values are returned in 75 pct cases) Using "BEGIN" for starting transactions Using auto_inc on the id column Maximum number of requests for OLTP test is limited to 10000 Threads started! Done. OLTP test statistics: queries performed: read: 140000 write: 50000 other: 20000 total: 210000 transactions: 10000 (234.00 per sec.) deadlocks: 0 (0.00 per sec.) read/write requests: 190000 (4446.01 per sec.) other operations: 20000 (468.00 per sec.) Test execution summary: total time: 42.7349s total number of events: 10000 total time taken by event execution: 42.6917 per-request statistics: min: 2.67ms avg: 4.27ms max: 17.84ms approx. 95 percentile: 5.02ms Threads fairness: events (avg/stddev): 10000.0000/0.00 execution time (avg/stddev): 42.6917/0.00
sysbench 0.4.12: multi-threaded system evaluation benchmark Running the test with following options: Number of threads: 1 Doing OLTP test. Running mixed OLTP test Using Special distribution (12 iterations, 1 pct of values are returned in 75 pct cases) Using "BEGIN" for starting transactions Using auto_inc on the id column Maximum number of requests for OLTP test is limited to 10000 Threads started! Done. OLTP test statistics: queries performed: read: 140000 write: 50000 other: 20000 total: 210000 transactions: 10000 (203.06 per sec.) deadlocks: 0 (0.00 per sec.) read/write requests: 190000 (3858.12 per sec.) other operations: 20000 (406.12 per sec.) Test execution summary: total time: 49.2467s total number of events: 10000 total time taken by event execution: 49.2031 per-request statistics: min: 2.97ms avg: 4.92ms max: 24.68ms approx. 95 percentile: 5.40ms Threads fairness: events (avg/stddev): 10000.0000/0.00 execution time (avg/stddev): 49.2031/0.00
んー、どちらかというと5.7の方がスループットが出ていないような・・
この程度の負荷だと誤差の範囲なのかもしれません。
あとはパラメータチューニングをもっと細かくやるべきなのかも。
個人的には結構大きいJSONの対応。
PostgreSQLでは既に似たような機能が実装されていたのですが、
RDBMSが複数あるのは微妙ですよね。というわけで採用したことはありませんでした。
そんな中、ついにMySQLに実装されたので、色々と試してみたいと思います。
mysql> create table json_test (id varchar(64) primary key, val json default null); Query OK, 0 rows affected (0.01 sec) mysql> insert into json_test values -> ('key1', '{"id":1, "value":"hogehoge"}'), -> ('key2', '{"id":2, "value":"fugafuga", "option":"optionvalue"}'), -> ('key3', '{"id":3, "value":"foobar", "option":null}'); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 [/bash] これでテーブルとテストデータができあがりましたね。 色々とクエリを発行してみます。 一致検索、LIKE検索と、簡単な構文には対応している様子。 mysql> select * from json_test; +------+---------------------------------------------------------+ | id | val | +------+---------------------------------------------------------+ | key1 | {"id": 1, "value": "hogehoge"} | | key2 | {"id": 2, "value": "fugafuga", "option": "optionvalue"} | | key3 | {"id": 3, "value": "foobar"} | +------+---------------------------------------------------------+ 3 rows in set (0.00 sec) mysql> select * from json_test where json_extract(val, '$.id') = 1; +------+--------------------------------+ | id | val | +------+--------------------------------+ | key1 | {"id": 1, "value": "hogehoge"} | +------+--------------------------------+ 1 row in set (0.00 sec) mysql> select * from json_test where json_extract(val, '$.value') like '%hoge%'; +------+--------------------------------+ | id | val | +------+--------------------------------+ | key1 | {"id": 1, "value": "hogehoge"} | +------+--------------------------------+ 1 row in set (0.00 sec) mysql> select * from json_test where json_extract(val, '$.option') is null; +------+--------------------------------+ | id | val | +------+--------------------------------+ | key1 | {"id": 1, "value": "hogehoge"} | +------+--------------------------------+ 1 row in set (0.00 sec) mysql> select json_extract(val, '$.option') from json_test; +-------------------------------+ | json_extract(val, '$.option') | +-------------------------------+ | NULL | | "optionvalue" | | null | +-------------------------------+ 1 row in set (0.00 sec)
という感じで、なかなかそれっぽく動いているようです。
ただ、json内のフィールドとしてはnullとして保存されていても、SQLのnullとは評価されないようですね。。
ドキュメントを読んでもずばりな回答が得られなかったのですが、とりあえずjsonのnullにキャストするとnull値の検索が可能でした。
mysql> select * from json_test where val->"$.option" = cast('null' as json); +------+----------------------------------------------+ | id | val | +------+----------------------------------------------+ | key3 | {"id": 3, "value": "foobar", "option": null} | +------+----------------------------------------------+ 1 row in set (0.00 sec)
そしてjson用の独自構文。
mysql> select * from json_test where val->"$.id" = 2; +------+---------------------------------------------------------+ | id | val | +------+---------------------------------------------------------+ | key2 | {"id": 2, "value": "fugafuga", "option": "optionvalue"} | +------+---------------------------------------------------------+ 1 row in set (0.00 sec)
sqlでアロー演算子は斬新すぎでは?
なんとか楽にフィールドにアクセスできないか、という事で生まれたのでしょうか・・・
まあ助かりますけど・・・
InnoDB周りの改善だったりとか、他にも色々とあるのですが、今日のエントリではこんなところで。
個人的に心待ちにしていた機能、Scheduled Eventが発表されました。
すでにリリースされているそうなので早速試してみました。
とりあえずサンプルとなるLambda functionを作成。
このサイトへのHTTPリクエストを発行するコードとなっています。
そして、Event sources に Scheduled Eventを追加。
とりあえずcronフォーマットで5分毎に実行する形で登録してみます。
このサイトのアクセスログを関してみたところ、ちゃんと5分ごとにアクセスが来ていることが確認できました。
(こちらのIPアドレスをwhoisすると、ちゃんとamazonからのものでした)
しかし Lambda はすばらしいですね。
EBSのスナップショットを作成し、古いスナップショットを自動削除するスクリプトを書いたのでメモがてら残します。
このスクリプトを実行するインスタンスのすべてのボリュームに対して、EXPIRE_DAYS日数経過したスナップショットを削除し、EBSスナップショットを作成します。
あ、credentials は ~/.aws/credentials にある前提です。
#!/bin/sh # ------------------------------------------ # configuration EXPIRE_DAYS=4 INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id` EXPIRE_DATE=`date +%Y-%m-%dT%H:%M:%S.0000Z -d "-${EXPIRE_DAYS}day"` # ------------------------------------------ # script start STATUS=0 for VOL_ID in `aws ec2 describe-volumes --filters "Name=attachment.instance-id,Values=${INSTANCE_ID}" | grep -e '^VOLUMES' | awk '{ print $9 }'` do # delete old snapshots for SNAPSHOT in `aws ec2 describe-snapshots --filters "Name=volume-id,Values=${VOL_ID}" | grep ${VOL_ID} | awk '{ print $8","$9 }'` do SNAPSHOT_ID=`echo $SNAPSHOT | awk -F"," '{ print $1 }'` CREATE_DATE=`echo $SNAPSHOT | awk -F"," '{ print $2 }'` # delete expired snapshot. if [[ "$EXPIRE_DATE" > "$CREATE_DATE" ]]; then aws ec2 delete-snapshot --snapshot-id $SNAPSHOT_ID > /dev/null RESULT=$? if [ $RESULT -ne 0 ]; then echo "delete for snapshot failed... ($SNAPSHOT_ID, $CREATE_DATE, ${RESULT})" STATUS=1 fi fi done # create snapshot. aws ec2 create-snapshot --volume-id ${VOL_ID} --description "`date +%Y-%m-%d` snapshot instance-id:${INSTANCE_ID}" > /dev/null RESULT=$? if [ $RESULT -ne 0 ]; then echo "create for snapshot failed... (${RESULT})" STATUS=2 fi done exit $STATUS