Sojiro’s Blog

This is nothing much.

Minil を使った Changes ファイルの更新

CPAN モジュールの bug fix を行ったので修正版をリリース。

minil 様様と思いながら minil release コマンドを打つのだが、 いつも Changes ファイルの更新のところで、どこに何を書けば良いのだっけ?となってしまうのでここにメモしておくことにした。

{{$NEXT}} の下に書く

今回のリリースだとこうなった

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Revision history for Perl extension JSON-MergePatch

{\{$NEXT}}

    - (bug fix) diff() :when the same hash-refs are present in the hash values of source and target (thanks bessarabov)
    - refactoring

0.02 2015-09-13T09:08:20Z

    - use JSON::MaybeXS and fix keys arg

0.01 2015-07-02T18:29:20Z

    - original version

{{$NEXT}} となっているところにバージョンやら日付やらが入る。

参照

Minilla チュートリアルドキュメント

Chef のリモート実行

前回 対象サーバで直接 Chef を実行してみました。

今回は手元からリモートのサーバに対して Chef を実行する仕組みを試してみます。

knife-solo のインストール

手元のクックブックをリモートのサーバに転送して chef-solo コマンドを実行するまでの一連の作業を実行できる knife-solo コマンドをインストールする

knife-solo コマンドは gem でインストールできる。手元の Mac OS でインストール

1
2
3
4
5
$ sudo gem install knife-solo
...
Thanks for installing knife-solo!
...
36 gems installed

Berkshelf のインストール

クックブックの依存関係を管理する Berkshelf というツールもこのタイミングでインストールする。これは knife-solo が他の gem のインストール状況によってデフォルトのオプションが変わるため

1
2
3
$ sudo gem install berkshelf
...
25 gems installed

ローカルにリポジトリを作る

まずはローカルにリポジトリを作る

Vagrantfile があるディレクトリで以下のコマンドを実行して作成する

1
2
$ knife solo init .
/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rubygems/specification.rb:2007:in `raise_if_conflicts': Unable to activate rspec-3.0.0, because rspec-core-3.4.0 conflicts with rspec-core (~> 3.0.0), rspec-expectations-3.4.0 conflicts with rspec-expectations (~> 3.0.0), rspec-mocks-3.4.0 conflicts with rspec-mocks (~> 3.0.0) (Gem::LoadError)

エラーが発生した

rspec と rspec-core のバージョンが合っていなかったので rspec を update する

1
2
3
$ sudo gem update rspec
...
Gems updated: rspec rspec-collection_matchers

再度リポジトリの作成を実行

1
2
3
4
5
6
$ knife solo init .
WARNING: No knife configuration file found
Creating kitchen...
Creating knife.rb in kitchen...
Creating cupboards...
Setting up Berkshelf...

これで実行ディレクトリ内に様々な雛形ディレクトリ、ファイルが作成された

リモートのサーバに Chef Solo をインストールする

今回リモートサーバとして想定する Vagrant の環境に対して SSH の設定を以下のようにしておく

1
$ vagrant ssh-config --host testhost >> ~/.ssh/config

以下のコマンドで Chef Solo をリモートサーバに対してインストール

1
2
3
4
5
6
$ knife solo bootstrap testhost
Bootstrapping Chef...
...
Thank you for installing Chef!
...
Chef Client finished, 0/0 resources updated in 06 seconds

今回は前回 Chef Solo をインストールした環境に対して打ったので最終的な結果は 0/0 resources updated になっている

クックブックの作成

ローカルにクックブックを作成する。今回は git をインストールするためのクックブックを作る

自作のクックブックは site-cookbooks 以下に置くことが慣例になっているので出力先に site-cookbooks を指定している

1
$ knife cookbook create git -o site-cookbooks

これで site-cookbooks 以下に git クックブックの雛形が出来上がる

レシピの編集

レシピの雛形 site-cookbooks/git/recipes/default.rb を編集していく

1
2
3
4
5
6
7
8
9
10
11
12
13
$ vim site-cookbooks/git/recipes/default.rb
$ cat site-cookbooks/git/recipes/default.rb
#
# Cookbook Name:: git
# Recipe:: default
#
# Copyright 2015, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#
package "git" do
    action :install
end

Node オブジェクトの編集

Chef で管理する Node (サーバ)の状態を記述し、それぞれの Node に対してどのクックブックが適用されるかを設定する Node オブジェクトを編集する

Node オブジェクトは knife solo bootstrap testhost 実行時に nodes/testhost.json として作成されている

今回はここに先ほど作成した git クックブックの適用を記述する

1
2
3
4
5
6
7
8
9
10
$ vim nodes/testhost.json
$ cat nodes/testhost.json
{
  "run_list": [
    "recipe[git]"
  ],
  "automatic": {
    "ipaddress": "testhost"
  }
}

クックブックの実行

準備が整ったのでいよいよクックブックをリモートサーバに対して実行する

以下のコマンドで実行

1
2
3
4
$ knife solo cook testhost
Running Chef on testhost...
...
Chef Client finished, 0/1 resources updated in 07 seconds

これも同じく前回の環境ですでに git をインストール済みなので 0/1 resources updated として正常に実行された

これでリモートへの Chef 実行完了!

参照

初めてのChef

Chef を使ってみます。 Vagrant で環境をつくっておいてそこで色々試してみます。

今回はゲスト側でクックブック、レシピを作って実行してみます。

Chef のインストール

vagrant ssh で Vagrant の環境に入って Chef をインストールする

1
2
3
$ curl -L https://www.opscode.com/chef/install.sh | sudo bash
...
Thank you for installing Chef!

Chef Solo がインストールされているか確認

1
2
$ chef-solo -v
Chef: 12.5.1

クックブックを作る

Chef をインストールすると knife というコマンドもインストールされる。 この knife コマンドを使ってクックブックを作る

今回は git をインストールするための git クックブックを作る

コマンドは以下のように指定する

1
knife cookbooks create [クックブック名]

今回は出力先を指定する -o オプションを指定

1
$ sudo knife cookbook create git -o /var/chef/cookbooks

このコマンドで git という名前のクックブックが作成され、 /var/chef/cookbooks/git/recipes/default.rb にレシピの雛形が作られる

レシピの編集

レシピの雛形に git をインストールするレシピを書く

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ sudo vi /var/chef/cookbooks/git/recipes/default.rb
$ cat /var/chef/cookbooks/git/recipes/default.rb
#
# Cookbook Name:: git
# Recipe:: default
#
# Copyright 2015, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#

package "git" do
  action :install
end

クックブックの実行

作成したクックブックを実行する

1
2
3
4
5
6
7
8
9
10
11
$ sudo chef-solo -o git
...
Compiling Cookbooks...
Converging 1 resources
Recipe: git::default
  * yum_package[git] action install
    - install version 1.7.1-3.el6_4.1 of package git

Running handlers:
Running handlers complete
Chef Client finished, 1/1 resources updated in 38 seconds

git がインストールされたか確認

1
2
$ git --version
git version 1.7.1

Chef の特徴

OS の違いを吸収してくれる

上記 git の例ではこちらで指定していないにも関わらず、実行した環境が CentOS であることを解釈して yum パッケージでインストールが行われた

実行された環境の OS 毎の違いを吸収してくれる

冪等性が担保されている

先ほどのクックブックをもう一度実行してみる

1
2
3
$ sudo chef-solo -o git
...
Chef Client finished, 0/1 resources updated in 07 seconds

今回は何もせずに正常に終了したことが分かる

Chef ではクックブックを何度実行しても、クックブックに規定された状態になる冪等性が担保されている

参照

余談: Chef に関する記事は web 上にも色々ありますが、 Chef は用途によって様々なツール、使い方があり、基本的な用語や概念などを理解できていないと思わぬところでハマってしまいがちです。 Chef 実践入門 は Chef に関して体系的に理解できるので最初にサラッと読んでしまうのが近道だと感じました。

InnoDBにおけるSQL別ロックの挙動

研修時代に書いた InnoDB に関するメモ第二弾。

ロックの挙動に関しては基礎の基礎でちょっと考えれば分かるものの、開発しているとふとしたところで考慮が漏れていたりするので注意したい。

SELECT … FROM

一貫性読み取りであり、データベースのスナップショットを読み取り、トランザクションの遮断レベルが SERIALIZABLE に設定されなければロックは設定しません。SERIALIZABLE レベルの場合、検索時に直面したインデックスレコード上に共有ネクストキーロックが設定されます。

SELECT … FROM … LOCK IN SHARE MODE

検索時に直面したすべてのインデックスレコード上共有ネクストキーロック が設定されます。

SELECT … FROM … FOR UPDATE

検索で特定されたインデックスレコードに対し、 排他ネクストキーロック が設定され、ほかのセッションが SELECT … FROM … LOCK IN SHARE MODE を実行したり、特定のトランザクション遮断レベルで読み取りを行ったりできないようにします。 ただし、ギャップロックの部分に関しては共有ロックとなります

UPDATE … WHERE …

検索が直面するすべてのレコード上排他ネクストキーロック を設定します。

DELETE FROM … WHERE …

検索が直面するすべてのレコード上排他ネクストキーロック を設定します。

INSERT

挿入される行に排他ロック を設定します。このロックはネクストキーロックではなくインデックスレコードロックである (つまりギャップロックが存在しない) ため、ほかのセッションは挿入行の前にあるギャップへの挿入を自由に行えます。

参照

InnoDBにおけるロックの種類

MySQL でよく使う InnoDB のロックについて研修時代に書いたメモ。

種類

  • レコードロック: インデックスレコードの ロック
  • ギャップロック: インデックスレコード間にあるギャップのロック、先頭のインデックスレコードの前や末尾のインデックスレコードの後にあるギャップのロック、のいずれか
  • ネクストキーロック: インデックスレコードに対するレコードロックと、そのインデックスレコードの前、または後ろにあるギャップに対するギャップロックとを組み合わせたもの

InnoDBの行ロック(レコードロック)

InnoDBは行ロックができる、とよく言いますが、 正確には インデックスレコード にロックをかけているので、 primary key や、unique key といったレコードを一意に特定できるインデックスを使用せずにロックをかけるとテーブルロックの様な挙動を示します。(実際には隠しクラスタインデックスが生成、使用される。)

ギャップロック、ネクストキーロック

ギャップロック、ネクストキーロックは

  • 範囲を指定して(複数のレコードにまたがって)ロックを獲得しようとしたとき
  • 存在しないレコードに対してロックを獲得しようとしたとき

のみ起こりうる。

また、 ギャップロックは常に共有ロックと同じ挙動 を示す

前提:id(primary key)が10, 20, 30のレコードが入っているテーブル

1
select from table where id = 15 for update;    #id 11~19にギャップロック
1
select from table where id < 15 for update;    #id ~20にネクストキーロック
1
select from table where id < 20 for update;    #id ~20にネクストキーロック
1
select from table where id <= 20 for update;   #id ~30にネクストキーロック
1
select from table where id > 15 for update;    #id 11~にギャップロック
1
select from table where id = 35 for update;    #id 31~にギャップロック

参照

実践ハイパフォーマンスMySQL 第3版

Rails アプリを MySQL で作るときのメモ

Rails 4.0.5 のアプリを MySQL で立てるときのメモ

rails new にオプションをつける

オプションを付けずに rails new すると SQLite で立てられてしまうので、以下のようにする。

1
$ rails new app_name -d mysql

mysql2 のバージョンを指定

2015/09/21 時点の mysql2 は rails 上での実行時にバグを含んでいるようなのでバージョン指定する。

Gemfile
1
gem 'mysql2', '~> 0.3.20'
1
$ bundle install

mysql のパスワード設定

config/database.yml に mysql のパスワードを設定する。環境変数の指定は以下のようにする。

config/database.yml
1
2
3
4
5
6
7
8
development:
  adapter: mysql2
  encoding: utf8
  database: app_name_development
  pool: 5
  username: root
  password: <%= ENV['MYSQLPASS'] %>
  socket: /var/lib/mysql/mysql.sock

database の作成

最後に database を作って完了。

1
$ rake db:create

参照

Vagrant Provisioning の謎

Vagrant で立てた CentOS の仮想環境にちょっとした環境構築をしようと、provision ファイルを作って vagrant provision で実行しようとしたところ RVM のインストールから先が上手くいかなかった。

vagrant provision に関しては 過去のエントリ に記載があります)

provision ファイルの該当箇所は以下

1
2
3
4
5
6
# ruby and rails
curl -L https://get.rvm.io | bash -s
source ~/.profile

rvm requirements
rvm install 2.0.0 --with-openssl-dir=$HOME/.rvm/usr

上記コマンドのうち、

1
curl -L https://get.rvm.io | bash -s

だけが実行できているが、このコマンドの結果も意図したものとなっていない。 それ以降のコマンドは失敗する。

まだ原因は良くわかっていない。

  • RVM のインストール自体はできている
  • どのユーザー(vagrant? root?)で実行されているのか分からない
  • vagrant ユーザーに対しては rvm コマンドの path が通っているが、root ユーザーには通っていない
  • provision 実行のログ的には root で実行しているように見える
  • vagrant ssh して手動でコマンドを実行すると意図した結果になる

Sed コマンドで置換

ファイルの中身を置換する必要があったので sed コマンドを使った。

使うオプション

今回使うのは

  • 変換処理の条件式を指定するための e オプション
  • 変換してそのままファイルを上書きする i オプション

やってみる

今回はこんな感じ

1
sed -ie '条件式' ファイル名
1
2
3
4
5
6
7
8
9
10
$ cat test
testtesttest

$ sed -ie 's/test/hoge/g' test

$ cat test
hogehogehoge

$ ls
test teste

指定したファイルは条件に沿って置換されているが、オリジナルの内容を持ったファイルも生成されている

オリジナルの内容を持ったファイルは使う sed コマンド(OS)によって元のファイル名に ‘e’ が付いたり ‘-e’ が付いたりする

Find コマンドの使い方

いつまでも find コマンドの使い方を覚えられないアホなのでメモしておく。。。

基本的に

1
find [探すディレクトリ] -name 'ファイルの条件'

これだけは覚えとけ、と。

たまには

1
find . -name '*.swp' -ok rm {} \;

見つけたファイルをそのまま次のコマンドに受け渡す -ok オプションもたまには使えるかも知れない。

CPAN モジュールのリリース

上司にお膳立てされ、後輩にお尻を叩かれ、やっとのことで CPAN モジュールをリリースしました。

前回のエントリに続き、リリースの手順を記します。

モジュールの作成

ここがコアの部分ですが、内容は作るモジュールに依るので割愛。編集すべきは以下のファイルです。

  • lib/ 配下のプログラム
  • t/ 配下のテストプラグラム
  • cpanfile

cpanfile の作成は scan-prereqs-cpanfile を使うと便利です。

このコマンドは App::scan_prereqs_cpanfile モジュールで提供されています。

1
2
cpanm App::scan_prereqs_cpanfile
scan-prereqs-cpanfile > cpanfile

テスト

モジュールの作成ができたら test を実行しますが、このとき minil test コマンドを使うと Changes や META.json、README.md が自動で編集されます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
$ minil test
Creating working directory: /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7
cp Build.PL /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7/Build.PL
cp Changes /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7/Changes
cp LICENSE /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7/LICENSE
cp META.json /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7/META.json
cp README.md /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7/README.md
cp cpanfile /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7/cpanfile
cp lib/JSON/MergePatch.pm /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7/lib/JSON/MergePatch.pm
cp minil.toml /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7/minil.toml
cp t/00_compile.t /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7/t/00_compile.t
Building /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7
Retrieving meta data from lib/JSON/MergePatch.pm.
Name: JSON::MergePatch
Abstract: It's new $module
Version: 0.01
fatal: bad default revision 'HEAD'
Writing MANIFEST file
Writing release tests: xt/minilla/minimum_version.t
Writing release tests: xt/minilla/cpan_meta.t
Writing release tests: xt/minilla/pod.t
Writing release tests: xt/minilla/spelling.t
Writing release tests: xt/minilla/permissions.t
[5DuYQ9x7] $ perl -I. Build.PL
Creating new 'Build' script for 'JSON-MergePatch' version '0.01'
[5DuYQ9x7] $ perl -I. Build build
cp lib/JSON/MergePatch.pm blib/lib/JSON/MergePatch.pm
t/00_compile.t .. ok
All tests successful.
Files=1, Tests=1,  0 wallclock secs ( 0.01 usr  0.01 sys +  0.03 cusr  0.01 csys =  0.06 CPU)
Result: PASS
Removing /user/home_directory/git/cpan/JSON-MergePatch/.build/5DuYQ9x7

リリース

テストが通ったらいよいよリリースします。

CPAN へのリリースには PAUSE ID が必要です。取得方法はこちら

リリースには minil release コマンドを使います。

1
2
3
$ minil release

Release engineering requires Version::Next, but it is not available. Please install Version::Next using your preferred CPAN client at ...

Version::Next がないと怒られたので入れて再度実行。

1
2
3
4
5
6
$ cpanm Version::Next
...

$ minil release

Release engineering requires CPAN::Uploader, but it is not available. Please install CPAN::Uploader using your preferred CPAN client at ...

今度は CPAN::Uploader がないと怒られたので入れて再度実行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ cpanm CPAN::Uploader
...

$ minil release

Retrieving meta data from lib/JSON/MergePatch.pm.
Name: JSON::MergePatch
Abstract: JSON Merge Patch implementation
Version: 0.01
Next Release? [0.01]
Name: JSON::MergePatch
Abstract: JSON Merge Patch implementation
Version: 0.01
...
All tests successful.
Files=8, Tests=67,  0 wallclock secs ( 0.04 usr  0.03 sys +  0.18 cusr  0.04 csys =  0.29 CPU)
Result: PASS
Wrote JSON-MergePatch-0.01.tar.gz
Upload to CPAN
Release to CPAN ? [y/n]   y
missing user argument at

リリースバージョンの確認と、 CPAN にリリースするかの確認に答えていざリリース、と思いきや

1
missing user argument at

と怒られました。

どうやら ~/.pause ファイルに PAUSE のユーザー情報を記載する必要があるようです。

1
2
3
4
$ vim ~/.pause
$ cat ~/.pause
user SOJIRO
password your_password

再度実行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
$ minil release

Retrieving meta data from lib/JSON/MergePatch.pm.
Name: JSON::MergePatch
Abstract: JSON Merge Patch implementation
Version: 0.01
Next Release? [0.01]
Name: JSON::MergePatch
Abstract: JSON Merge Patch implementation
Version: 0.01
...
All tests successful.
Files=8, Tests=67,  0 wallclock secs ( 0.04 usr  0.03 sys +  0.18 cusr  0.05 csys =  0.30 CPU)
Result: PASS
Wrote JSON-MergePatch-0.01.tar.gz
Upload to CPAN
Release to CPAN ? [y/n]   y
registering upload with PAUSE web server
POSTing upload for /user/home_directory/git/cpan/JSON-MergePatch/.build/ilpDOKuE/JSON-MergePatch-0.01.tar.gz to https://pause.perl.org/pause/authenquery
PAUSE add message sent ok [200]
Name: JSON::MergePatch
Abstract: JSON Merge Patch implementation
Version: 0.01
[JSON-MergePatch] $ git commit -a -m Checking in changes prior to tagging of version 0.01.
Changelog diff is:
diff --git a/Changes b/Changes
index 643c7bc..dae7daa 100644
--- a/Changes
+++ b/Changes
@@ -2,5 +2,7 @@ Revision history for Perl extension JSON-MergePatch

 

+0.01 2015-07-02T18:29:20Z
+
     - original version

[master 3bf0db2] Checking in changes prior to tagging of version 0.01.

 2 files changed, 17 insertions(+), 1 deletion(-)
Pushing to origin
[JSON-MergePatch] $ git push origin master
Counting objects: 7, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 831 bytes | 0 bytes/s, done.
Total 4 (delta 2), reused 0 (delta 0)
To git@github.com:sojiro14/JSON-MergePatch.git

   9c8c207..3bf0db2  master -> master

[JSON-MergePatch] $ git tag 0.01
[JSON-MergePatch] $ git push origin tag 0.01
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:sojiro14/JSON-MergePatch.git

 * [new tag]         0.01 -> 0.01

Removing /user/home_directory/git/cpan/JSON-MergePatch/.build/ilpDOKuE

テストが実行された後、CPAN にリリースされました。最後にその時点の tag が切られて完了。

ついに CPAN モジュールをリリースしてしまいました。メンテナンスちゃんとしなくては。

参照