chef-soloからchef-zeroへ

長らく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

local_mode でプロビジョニングする

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でリモートホストをプロビジョニングする

ローカルにあるレシピを使って、リモートホストをプロビジョニングする場合は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 お試し編

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

mysql 5.6の結果

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

mysql 5.7

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テーブルってどんな感じ?

個人的には結構大きい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周りの改善だったりとか、他にも色々とあるのですが、今日のエントリではこんなところで。

AWS Lambda Scheduled Eventを試してみる

個人的に心待ちにしていた機能、Scheduled Eventが発表されました。
すでにリリースされているそうなので早速試してみました。

とりあえずサンプルとなるLambda functionを作成。
このサイトへのHTTPリクエストを発行するコードとなっています。

サンプルLambda

そして、Event sources に Scheduled Eventを追加。
とりあえずcronフォーマットで5分毎に実行する形で登録してみます。

lambda_schedule_1

このサイトのアクセスログを関してみたところ、ちゃんと5分ごとにアクセスが来ていることが確認できました。
(こちらのIPアドレスをwhoisすると、ちゃんとamazonからのものでした)

lambda_access

しかし Lambda はすばらしいですね。

EBSのスナップショット作成スクリプト

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

WinObjC雑感

公開から少し間が空いてしまいましたが、
WinObjCについて触れてみたので、感想を書いておこうと思います。

WinObjCとは

Microsoftが公開している、Windows上でObjective-C(iOS)のソースコードをコンパイルできるようにする、ブリッジモジュールの事です。

https://github.com/Microsoft/WinObjC/

今のところプレビュー版で、ARM向けのバイナリが生成できないなど、色々と制約はあります。

サンプルの動かし型

前述のgithubからSDK一式zipをダウンロードして展開し、
VisualStudio2015でソリューションファイルを開き、ビルドして実行するだけです。動かすだけなら超簡単だった。

WinObjC_Sample

ちゃんと.mファイルを修正すると、その通りバイナリにも反映されています。

WinObjC_Sample2

そして、Objective-Cのコードにバグを仕込むと、コンパイル時にちゃんと検出してくれています。

WinObjC_Errors

イケてるところ

  • Windows上でiOSアプリの動作確認ができる。(これが最重要ですねw)
  • VisualStudioで設定したブレークポイントでちゃんとブレークできる。
  • C++なんかと同様、ブレーク時点でのイミディエイトウィンドウで変数の値を確認できる。

微妙なところ

  • UIコンポーネントのサンプルもあったけれど、iOSとはデザインが微妙に異なるため、見た目の確認には厳しいかも。
  • Objective-Cのコードに合わせてエディタがハイライトされない。
  • IntelliSenseやフォーマッター、関数・クラス定義へのジャンプもできない。
  • 動的なシンタックスエラーチェックなどもできない。

まだプレビュー版なので、正式版に期待といったところでしょうか。
VisualStudioで開発できるようにするからには、IDEのサポートも期待したいところです。
ちなみに、サンプルの範囲で確認しただけなので、ライブラリの利用は試していません。

amazon-ecsで当月発売の商品を取得する

で、Powerサーチの使い方がいまいち分かりにくかったのでメモメモ。

opts = {
  browse_node: "nodeid",
  response_group: "Large",
  country: "jp",
  power: "pubdate:during 09-2015"
}
response = Amazon::Ecs.item_search(keywords, opts)

pubdate:duringの後に当月を指定します。
pubdate:beforeにすると、前月以前になり、pubdate:afterにすると、翌月以降発売の商品が取得できます。

Powerサーチを使いこなせれば色々できそう。

前回の記事から二週間も空いてしまった、、時間見つけて書かないとなぁ。

ansibleでgolangをインストールするレシピ

グローバルにgoをインストールするレシピを書きました。
一応冪等性も確保。

    # /usr/local/go のディレクトリ有無を確認。ディレクトリが無いときだけ以下のレシピを実行する。
    - stat: path=/usr/local/go
      register: godir
    - name: get golang
      command: wget https://storage.googleapis.com/golang/go1.5.linux-amd64.tar.gz -O /tmp/golang1.5.tar.gz
      sudo: yes
      when: not godir.stat.exists
    - name: tar golang
      command: tar zxf golang1.5.tar.gz chdir=/tmp
      sudo: yes
      when: not godir.stat.exists
    - name: install golang
      shell: mv /tmp/go /usr/local/go-1.5; ln -s /usr/local/go-1.5 /usr/local/go; echo 'export PATH=$PATH:/usr/local/go/bin' > /etc/profile.d/go.sh
      sudo: yes
      when: not godir.stat.exists

GOPATHは適宜設定しましょう。
(syntaxhighlightのwordpressプラグインってyamlはハイライトできないのね。。)

next HTML

w3cのwikiに、次世代HTMLのアイデアリストがまとめられているのを眺めてみました。
http://www.w3.org/wiki/HTML/next

面白いのがいくつかあったのでピックアップしてみます。

Advanced Anchors

<a href="w3.org/page.html$section>div>p"> go there </a>

おそらく「そのページのsection > div > pへのアンカー」という意図のURLで、CSSセレクタでアンカー指定できるようにするアイデアですね。
アンカーIDが振られていないページなどへのリンクに使えそうです。

ComboBox

<select name="age" inputable="inputable">
<option value="11"></option>
<option value="12"></option>
</select>

自由入力できるプルダウン、コンボボックス。ローカルアプリケーションでは一般的に使えるのに、むしろなぜ実装されていないのか。
確かに欲しいですね。JSとCSSで頑張って実装しているケースが多いですが、ブラウザでサポートしてくれるのがやっぱり一番安心。

おまけ

目次化されていませんでしたが、目を引いたので。

BR
{
   breakline:right;
}

breakline: none | left | right  ;
none : = default
left : = insert a breakline before the element
right : = insert a breakline after the element

近年で、HTMLタグは文章を意味づけするものであり、
スタイルを適用するものではないという考え方が大分進んできたにもかかわらず、
相変わらずBRタグだけは改行する為に使われ続けているので、これもスタイルにしちゃおうという提案ですね。
最近はもうBRタグを記述する事は減りましたが、デザイン的にどうしてもpを使わず改行を入れたい場合にBRタグを使っているので、
どうせならそこもcssで解決できるようにはして欲しいですね。

vagrantでansible

vagrantイメージのプロビジョニングにansibleを使う方法をメモメモ。

  • playbook.ymlを作成する
---
- hosts: all
  tasks:
    - name: step1 (install docker)
      shell: curl -sSL https://get.docker.com/ | sudo sh

    - name: step2 (install libselinux-python)
      yum: name=libselinux-python state=present

    - name: step3 (disable selinux)
      selinux: policy=targeted state=permissive
      sudo: yes
  • Vagrantfile
    Vagrant.configure(2) do |config|
      ...
      config.vm.provision "ansible" do |ansible|
        ansible.playbook = "playbook.yml"
      end
    end
    

golangでツールを一つ作ろうと思ってるので、ついでにansibleにも勉強しようかなーと。
chefやpuppetよりもシンプルで取っつきやすいよね。