knife 関連の自動化ツール一覧

Chef クラサバ構成では knife というコマンドラインツールを用いて Chef サーバの情報を更新するわけだけど、この knife というのがサブサブサブコマンドまであったり (knife cookbook site install とか)、最小限なコマンドをいくつも打つ必要があったりして、もう少しなんとかならないのかなと思うことがよくある。

たとえば「Chef サーバからノード A を削除したい」というシチュエーションで、"knife node delete node_a", "knife client delete node_a" という 2 コマンドを打ち、さらに chef-repo に置いてある nodes/server01.json というファイルをレポジトリ上から削除するとか。

こんなときに複数の処理をバッチにまとめて使い回したいというのが当然の要請だと思うけど、今のところ下記 3 つが有力っぽい。

  • spiceweasel
  • knife プラグイン
  • knife スクリプト

それぞれ長所・短所があるので、概要を書き比較してみる。

spiceweasel

knife のサブコマンド cookbook/environment/role/data bag/bootstrap や、knife ec2/rackspace といった knife によるプロビジョニングを、バッチ処理として JSON/YAML の形式で実行できる。

こんな感じの YAML を書いて、

cookbooks:
- apache2:
- apt:
    - 1.2.0
- mysql:
 
environments:
- development:
- qa:
- production:
 
roles:
- base:
- iisserver:
- monitoring:
- webserver:

(以下省略)

こんなコマンドを実行すると、

spiceweasel path/to/infrastructure.yml

下記の操作を実行してくれる。

knife cookbook upload apache2
knife cookbook upload apt
knife cookbook upload mysql
knife environment from file development.rb
knife environment from file qa.rb
knife environment from file production.rb
knife role from file base.rb
knife role from file iisserver.rb
knife role from file monitoring.rb
knife role from file webserver.rb

簡単に書ける代わりにかなり自由度が低いし、ルールを覚えなければならないのでややだるい感じがする。

knife プラグイン

Opscode が提供している opscode/knife-ec2 とか拙作の tily/ruby-knife-nc みたいなやつ。こんな感じでゴリゴリとプラグインを書く。Chef 本家の knife サブコマンド と同等のコマンドを作っていく感じ。結構めんどくさい。

作ったプラグインはカレントディレクトリまたはホームディレクトリの .chef/plugins/ に置くと自動で認識される。また、knife-ec2 や knife-nc のように gem として配布できるのだけど、これが一番の強みだと思う。

knife スクリプト

個人的には手軽かつ自由度が高くて一番いいかなと思っている仕組み。DSL で柔軟に knife の処理を記述できる。書いた knife スクリプトは下記のような感じで実行できる。

knife exec script.knife

公式ドキュメントに載っている例だけど、Google スプレッドシートに現在のノード一覧を保存してくれるスクリプトとかすごくおもしろい。

require 'google_spreadsheet'
 
session = GoogleSpreadsheet.login ENV["GOOGLE_EMAIL"], ENV["GOOGLE_PASSWORD"]
sheet = session.create_spreadsheet "Instances #{Time.now.strftime "%Y%m%d"}"
 
ws = sheet.worksheets.first
 
ws[1,1] = "Check In"
ws[1,2] = "Node Name"
ws[1,3] = "Ruby Version"
ws[1,4] = "Recipes"
 
i = 2
nodes.all do |n|
  ws[i,1] = Time.at(n['ohai_time']).strftime("%F %R")
  ws[i,2] = n.name
  ws[i,3] = n['languages']['ruby']['version']
  ws[i,4] = n.run_list.expand.recipes.join(", ")
  i = i + 1
end
 
ws.save

まとめ

個人的な評価だけど、表にまとめると以下のような感じになると思う。

手軽さ 自由度 配布
spiceweasel ×
knife プラグイン ×
knife スクリプト

他にも似たようなものがあったら教えてもらえるとうれしいです。

Chef バージョン 11 から WebUI の WAF が Merb から Rails に変更されるっぽい

そもそも Merb 自体が Rails に吸収されたし、個人的に Merb より Rails のほうが馴染み深いからソースが読みやすくなっていい。WebUI だけでなくコアの REST APIRails になるといいな。

Chef の 10.14.0.beta.1 から導入された dry run 機能 "why run" を試す

以前から Puppet と Chef の比較でよく言われていたのが、前者には dry run 機能があるが後者にはないというものだったけど、Chef 10.14.0.beta.1 から dry run っぽい機能 why run が追加された。どんなものか気になったので試してみた。

普通に gem install chef だと安定版がインストールされてしまうので、-v でバージョンを指定するか --pre で「ベータ版をインストールしたい」旨を指定するとよい。

root@ubuntu:~# gem install chef -v 10.14.0.beta.1
root@ubuntu:~# gem install chef --pre 

ベータ版 (10.14.0.beta.*) がインストールされて --why-run オプションが追加されているか確認しておく。

root@ubuntu:~# chef-solo -v
Chef: 10.14.0.beta.3
root@ubuntu:~# chef-solo -h | grep 'why-run'
    -W, --why-run                    Enable whyrun mode

うまく入ったので使ってみる。といっても --why-run をつけるだけ (この記事では chef-solo を使っているけど chef-client でも利用可能)。

root@ubuntu:~# chef-solo -l fatal -c /tmp/chef-solo/solo.rb -j /tmp/chef-solo/nodes/server01.json --why-run

"-l fatal" でログレベルを fatal に上げているのは、debug や info のログが出力されてしまうと普通のログと why run の実行結果が混在して見づらくなってしまうため。

実際に実行すると下記のような出力結果になる (ubuntu サーバに recipe[redis::server] を実行した例)。とりあえず英語が読める人ならなんとなくどんな内容が実行されるか把握できる感じか。なんか中途半端なフォーマットの気がするので、もっと JSON とかで出力して Excel の作業手順書とかに変換できるとおもしろいかなとか思った。

root@ubuntu:~# chef-solo -l fatal -c /tmp/chef-solo/solo.rb -j /tmp/chef-solo/nodes/server01.json --why-run
Starting Chef Client, version 10.14.0.beta.3
Compiling Cookbooks...
Converging 19 resources
Recipe: runit::default
  * execute[start-runsvdir] action nothing (up to date)
  * execute[runit-hup-init] action nothing (skipped due to only_if)
  * package[runit] action installRecipe: <Dynamically Defined Resource>
  * cookbook_file[/tmp/chef-solo/preseed/runit/runit-2.0.0-1ubuntu4.seed] action create
    - Would create a new cookbook_file /tmp/chef-solo/preseed/runit/runit-2.0.0-1ubuntu4.seed
        --- /tmp/chef-solo/preseed/runit/runit-2.0.0-1ubuntu4.seed      1970-01-01 09:00:00.000000000 +0900
        +++ /tmp/chef-solo/cookbooks/runit/files/default/runit.seed     2012-08-06 00:56:52.891486874 +0900
        @@ -0,0 +1 @@
        +runit   runit/signalinit        boolean true

    - Would preseed package runit
    - Would install version 2.0.0-1ubuntu4 of package runit
Recipe: runit::default
  * execute[start-runsvdir] action nothing (up to date)
  * execute[runit-hup-init] action nothing (skipped due to only_if)
Recipe: redis::default
  * directory[/etc/redis] action create
    - Would create new directory /etc/redis
    - Would change mode from '' to '0755'
    - Would change owner from '' to 'root'
    - Would change group from '' to 'root'
  * template[/etc/redis/redis.conf] action create
    * Parent directory /etc/redis does not exist.
    * Assuming directory /etc/redis would have been created
    - Would create template[/etc/redis/redis.conf]
        --- /etc/redis/redis.conf       1970-01-01 09:00:00.000000000 +0900
        +++ /tmp/chef-rendered-template20120806-5275-a2iqcv     2012-08-06 01:19:22.391496611 +0900
        @@ -0,0 +1,123 @@
        +###
        +# Generated by Chef for ubuntu
        +###
        +
        +# By default Redis does not run as a daemon. Use 'yes' if you need it.
        +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
        +#
        +# Set to no because we're using runit
        +daemonize no
        +
        +# When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
        +# You can specify a custom pid file location here.
        +pidfile /var/run/redis.pid
        +
        +# Accept connections on the specified port, default is 6379
        +port 6379
        +
        +# If you want you can bind a single interface, if the bind option is not
        +# specified all the interfaces will listen for connections.
        +#
        +bind 0.0.0.0
        +
        +# Close the connection after a client is idle for N seconds (0 to disable)
        +timeout 300
        +
        +# Save the DB on disk:
        +#
        +#   save <seconds> <changes>
        +#
        +#   Will save the DB if both the given number of seconds and the given
        +#   number of write operations against the DB occurred.
        +#
        +  save 900 1
        +  save 300 10
        +  save 60 10000
        +
        +# The filename where to dump the DB
        +dbfilename dump.rdb
        +
        +# For default save/load DB in/from the working directory
        +# Note that you must specify a directory not a file name.
        +dir /var/lib/redis
        +
        +# Set server verbosity to 'debug'
        +# it can be one of:
        +# debug (a lot of information, useful for development/testing)
        +# notice (moderately verbose, what you want in production probably)
        +# warning (only very important / critical messages are logged)
        +loglevel notice
        +
        +# Specify the log file name. Also 'stdout' can be used to force
        +# the demon to log on the standard output. Note that if you use standard
        +# output for logging but daemonize, logs will be sent to /dev/null
        +logfile /var/log/redis/redis.log
        +
        +# Set the number of databases. The default database is DB 0, you can select
        +# a different one on a per-connection basis using SELECT <dbid> where
        +# dbid is a number between 0 and 'databases'-1
        +databases 16
        +
        +
        +################################## SECURITY ###################################
        +
        +# Require clients to issue AUTH <PASSWORD> before processing any other
        +# commands.  This might be useful in environments in which you do not trust
        +# others with access to the host running redis-server.
        +#
        +# This should stay commented out for backward compatibility and because most
        +# people do not need auth (e.g. they run their own servers).
        +
        +requirepass slavepass
        +
        +################################### LIMITS ####################################
        +
        +# Set the max number of connected clients at the same time. By default there
        +# is no limit, and it's up to the number of file descriptors the Redis process
        +# is able to open. The special value '0' means no limts.
        +# Once the limit is reached Redis will close all the new connections sending
        +# an error 'max number of clients reached'.
        +
        +# maxclients 128
        +
        +# Don't use more memory than the specified amount of bytes.
        +# When the memory limit is reached Redis will try to remove keys with an
        +# EXPIRE set. It will try to start freeing keys that are going to expire
        +# in little time and preserve keys with a longer time to live.
        +# Redis will also try to remove objects from free lists if possible.
        +#
        +# If all this fails, Redis will start to reply with errors to commands
        +# that will use more memory, like SET, LPUSH, and so on, and will continue
        +# to reply to most read-only commands like GET.
        +#
        +# WARNING: maxmemory can be a good idea mainly if you want to use Redis as a
        +# 'state' server or cache, not as a real DB. When Redis is used as a real
        +# database the memory usage will grow over the weeks, it will be obvious if
        +# it is going to use too much memory in the long run, and you'll have the time
        +# to upgrade. With maxmemory after the limit is reached you'll start to get
        +# errors for write operations, and this may even lead to DB inconsistency.
        +
        +# maxmemory <bytes>
        +
        +############################### ADVANCED CONFIG ###############################
        +
        +# Glue small output buffers together in order to send small replies in a
        +# single TCP packet. Uses a bit more CPU but most of the times it is a win
        +# in terms of number of queries per second. Use 'yes' if unsure.
        +glueoutputbuf yes
        +
        +# Use object sharing. Can save a lot of memory if you have many common
        +# string in your dataset, but performs lookups against the share objects
        +# pool so it uses more CPU and can be a bit slower. Usually it's a good
        +# idea.
        +#
        +# When object sharing is enabled (shareobjects yes) you can use
        +# shareobjectspoolsize to control the size of the pool used in order to try
        +# object sharing. A bigger pool size will lead to better sharing capabilities.
        +# In general you want this value to be at least the double of the number of
        +# very common strings you have in your dataset.
        +#
        +# WARNING: object sharing is experimental, don't enable this feature
        +# in production before of Redis 1.0-stable. Still please try this feature in
        +# your development environment so that we can test it better.
        +# shareobjects no
Recipe: redis::server
  * group[redis] action create
    - Would create group[redis]
  * user[redis] action create
    - Would create user user[redis]
  * user[redis] action manage (up to date)
  * directory[/etc/redis] action create
    - Would create new directory /etc/redis
    - Would change mode from '' to '0755'
    - Would change owner from '' to 'root'
    - Would change group from '' to 'root'
  * directory[/var/log/redis] action create
    - Would create new directory /var/log/redis
    - Would change mode from '' to '0775'
  * directory[/var/lib/redis] action create
    - Would create new directory /var/lib/redis
    - Would change mode from '' to '0755'
  * directory[/etc/sv/redis_server] action create
    * Parent directory /etc/sv does not exist, cannot create /etc/sv/redis_server
    * Assuming directory /etc/sv would have been created
    - Would create new directory /etc/sv/redis_server
    - Would change mode from '' to '0755'
    - Would change owner from '' to 'root'
    - Would change group from '' to 'root'
  * directory[/etc/sv/redis_server/log] action create
    * Parent directory /etc/sv/redis_server does not exist, cannot create /etc/sv/redis_server/log
    * Assuming directory /etc/sv/redis_server would have been created
    - Would create new directory /etc/sv/redis_server/log
    - Would change mode from '' to '0755'
    - Would change owner from '' to 'root'
    - Would change group from '' to 'root'
  * directory[/etc/sv/redis_server/log/main] action create
    * Parent directory /etc/sv/redis_server/log does not exist, cannot create /etc/sv/redis_server/log/main
    * Assuming directory /etc/sv/redis_server/log would have been created
    - Would create new directory /etc/sv/redis_server/log/main
    - Would change mode from '' to '0755'
    - Would change owner from '' to 'root'
    - Would change group from '' to 'root'
  * template[/etc/sv/redis_server/run] action create
    * Parent directory /etc/sv/redis_server does not exist.
    * Assuming directory /etc/sv/redis_server would have been created
    - Would create template[/etc/sv/redis_server/run]
        --- /etc/sv/redis_server/run    1970-01-01 09:00:00.000000000 +0900
        +++ /tmp/chef-rendered-template20120806-5275-v4jnfw     2012-08-06 01:19:22.421492860 +0900
        @@ -0,0 +1,6 @@
        +#!/bin/bash
        +
        +cd /var/lib/redis
        +
        +exec 2>&1
        +exec chpst -u redis /usr/local/share/redis/redis-server /etc/redis/redis.conf
  * template[/etc/sv/redis_server/log/run] action create
    * Parent directory /etc/sv/redis_server/log does not exist.
    * Assuming directory /etc/sv/redis_server/log would have been created
    - Would create template[/etc/sv/redis_server/log/run]
        --- /etc/sv/redis_server/log/run        1970-01-01 09:00:00.000000000 +0900
        +++ /tmp/chef-rendered-template20120806-5275-1680hzk    2012-08-06 01:19:22.431494850 +0900
        @@ -0,0 +1,2 @@
        +#!/bin/sh
        +exec svlogd -tt /var/log/redis
  * link[/etc/init.d/redis_server] action create
    - Would create symlink at /etc/init.d/redis_server to /usr/bin/sv
  * link[/etc/service/redis_server] action create
    - Would create symlink at /etc/service/redis_server to /etc/sv/redis_server
  * ruby_block[supervise_redis_server_sleep] action create
    - Would execute the ruby block supervise_redis_server_sleep
  * service[redis_server] action nothing (up to date)
  * service[redis_server] action restart
    * Service status not available. Assuming a prior action would have installed the service.
    * Assuming status of not running.
    * /etc/init.d/redis_server does not exist!
    * Init script '/etc/init.d/redis_server' doesn't exist, assuming a prior action would have created it.
    - Would restart service service[redis_server]
Chef Client finished, 18 resources updated

Omnibus Chef Packaging を試してみた

ちょっと古いネタだけど、2012/06/29 にリリースされた "Omnibus Chef Packaging" というやつを試してみた。「Chef をパッケージ化して一括インストールする仕組み」という感じだと思う。

特徴としては「CentOS/Debian/Ubuntu/Amazon Linux のように複数のプラットフォームで利用できる」というのと「chef-solo や chef-client を実行するのに必要なソフトウェア (ruby とか rubygems) を /opt/chef というディレクトリ配下に隔離して置いてくれる」の 2 つが挙げられる。

使ってみた

使い方は簡単で、このページでインストールしたい OS・バージョンを選択し、表示されたコマンドを OS 上で実行するだけ。CentOS・5.x の場合はこんなのが表示された (rvm とかと同じ方式)。

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

実際にまっさらの CentOS 5.6 64bit Server インスタンス上で実行すると、下記のような実行結果になる。ダウンロードされた install.sh が、今度は CentOS 向けの rpm ファイルをダウンロードし、インストールを完了させる (Debian 系の場合は .deb ファイルがダウンロードされるはず)。

[root@localhost ~]# curl -L http://www.opscode.com/chef/install.sh | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5204  100  5204    0     0   2981      0  0:00:01  0:00:01 --:--:-- 2558k
Downloading Chef 10.12.0-1 for el...
--2012-08-03 16:16:50--  http://s3.amazonaws.com/opscode-full-stack/el-5.7-x86_64/chef-full-10.12.0-1.x86_64.rpm
s3.amazonaws.com をDNSに問いあわせています... 207.171.189.80
s3.amazonaws.com|207.171.189.80|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 18305984 (17M) [audio/x-pn-realaudio-plugin]
`/tmp/chef-full-10.12.0-1.x86_64.rpm' に保存中

100%[============================================================================================>] 18,305,984  1.63M/s 時間 12s

2012-08-03 16:17:03 (1.44 MB/s) - `/tmp/chef-full-10.12.0-1.x86_64.rpm' へ保存完了 [18305984/18305984]

Installing Chef 10.12.0-1
準備中...                ########################################### [100%]
   1:chef                   ########################################### [100%]

インストールが完了すると、必要最低限のコマンド (knife, shef, ohai, chef-solo, chef-client) が普通に使えるようになっている。
コマンド群は /usr/bin 配下にシンボリックリンクの形でインストールされており、実体は /opt/chef に配置されている。

[root@localhost ~]# which knife
/usr/bin/knife
[root@localhost ~]# find /usr/bin -mtime -1 -ls
5794282   32 drwxr-xr-x   2 root     root        28672  8月  3 16:17 /usr/bin
5798546    0 lrwxrwxrwx   1 root     root           19  8月  3 16:17 /usr/bin/knife -> /opt/chef/bin/knife
5798548    0 lrwxrwxrwx   1 root     root           18  8月  3 16:17 /usr/bin/shef -> /opt/chef/bin/shef
5798550    0 lrwxrwxrwx   1 root     root           18  8月  3 16:17 /usr/bin/ohai -> /opt/chef/bin/ohai
5798545    0 lrwxrwxrwx   1 root     root           23  8月  3 16:17 /usr/bin/chef-solo -> /opt/chef/bin/chef-solo
5798544    0 lrwxrwxrwx   1 root     root           25  8月  3 16:17 /usr/bin/chef-client -> /opt/chef/bin/chef-client

/opt/chef はこんな感じ。/opt/chef/bin に各コマンドの実体があり、/opt/chef/embedded にその他の必要なファイルが保存されている。

[root@localhost ~]# tree -L 2 /opt/chef
/opt/chef
|-- bin
|   |-- chef-client
|   |-- chef-solo
|   |-- erubis
|   |-- knife
|   |-- ohai
|   |-- restclient
|   |-- shef
|   `-- tt
|-- embedded
|   |-- bin
|   |-- include
|   |-- lib
|   |-- share
|   `-- ssl
`-- version-manifest.txt

7 directories, 9 files

Chef も Ruby も比較的新しいバージョンが入っている。

[root@localhost ~]# /opt/chef/bin/chef-solo -v
Chef: 10.12.0
[root@localhost ~]# /opt/chef/embedded/bin/ruby -v
ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-linux]

仕組みとか

上記は CentOS の例だけど、Omnibus Chef Packaging では他の OS (Debian/Ubuntu 等) をサポートしているので、rpm ファイル・deb ファイルの両方を作ったりするのが大変そうに思うが、opscode はこれを優雅かつオープンな方法で解決していてかっこいい。

まず opscode/omnibus-ruby というライブラリがあり、これは Ruby の DSL で簡潔にさまざまなソフトウェアのビルド方法を記述できる。

この omnibus-ruby を使って実際のソフトウェアのビルド方法を opscode/omnibus-software に記述し、さらに omnibus-software のパーツを組み合わせて opscode/omnibus-chef で実際の複数プラットフォーム向け Chef 向けパッケージを作成しているという流れ。

だから、omnibus-ruby を使って ruby や chef 以外のビルド手順を記述することができるし、omnibus-software に公開されているパーツを使って自分好みの Omnibus パッケージを作成することも可能になっている。自分は opscode のこういうインターネットコミュニティに開かれているところが好きだな。

所感

Chef というツールにおいて「Chef 自体は Chef のレシピでインストールできない」という当たり前の問題があって、今までは bootstrap スクリプトでゴリゴリやっていたのを omnibus の仕組みを使ってものすごく簡単に済ませることができるようになったのが調子いいと思った。Chef 実行までに必要な ruby とか rubygems を一式 /opt/chef に隔離してくれるのも構成として美しい。

ちなみに 公式ブログ記事では「オープンソース版 Chef サーバも omnibus でインストールできるようにする予定だよ」と書いてあるから、異様に難しいことで有名な「CentOS に Chef サーバをインストールする」という苦行をコマンド一発で完了させられる日も遠くない、はず。

あと、chef gem 最新安定版 10.12.0 のコードを見ても、knife bootstrap のデフォルトが omnibus を使った chef-full になっているようなので、この仕組みに慣れておいて損はないと思う。

参考:

* https://github.com/opscode/chef/blob/master/chef/lib/chef/knife/bootstrap.rb
* https://github.com/opscode/chef/blob/master/chef/lib/chef/knife/bootstrap/chef-full.erb

ゆるやかな収束 vs リアルタイムな運用タスク

Opscode Chef と RightScale の違いについて書く。
Chef が『ゆるやかに「あるべき状態」にすること』を標榜しているのに対して、RightScale は『リアルタイムに「運用タスク」を実行すること』を目指している。

つまり、Chef はそのもっとも本領を発揮する Chef サーバ/クライアント構成において、各サーバ (ノード) で 30 分おきに chef-client を実行することをしており、それぞれのノードが何度かの chef-client 実行を経て最終的に「あるべき状態」になることを想定している (converge: 収束)。この「収束」という枠組みを支えているのが Chef サーバの検索機能で、各サーバが他サーバの情報を参照しながら、最初はうまくいかなくても 2, 3 回の chef-client 実行を経て最終的には「あるべき状態」になるだろう、という楽天主義がそこにはある。

これに対して、RightScale は各サーバで RightLink Agent という独自 AMQP エージェントを立ち上げることによって、AMQP 経由でウェブ画面上から運用タスクをキューイングし各サーバでそのタスクを実行させるという仕組みを採用している。これによって、ウェブ画面上からボタンをクリックするだけでタスクを登録し、そのタスクが各サーバにおいてデキューされたのち実行される、というリアルタイム性の高い機能を実現している。

どちらが優位というつもりはないのだが、上記の仕組みを鑑みて「規模」という観点から考えると下記のような結論になるのではないかと思う。

  • かなり大規模な場合には Chef サーバが適している
  • 比較的小規模の場合には RightScale が適している

まだかなり過渡期なのでなんとも言えないが、これからの展望として Chef がもっとリアルタイムな仕組みになったり、RightScale がもっと「あるべき状態」を統合的に管理できるようになったりするのかな、とか思ったりする。

手軽に chef-solo を実行するためにスケルトンを作ってくれるやつ

追記 2012/08/03:knife-solo という gem をインストールすると knife kitchen というコマンドが使えるようになるんだけど、こっちのほうが chef-solo のための豪華なスケルトンを作ってくれてよさそうかもです。

手軽に chef-solo を実行するためにスケルトンを作ってくれるやつ (ユースケース要検討) — Gist

Chef はとっつきにくいのがネックだなと思っていて、たとえば chef-solo を実行するのにもいちいち solo.rb と node.json を作って cookbooks ディレクトリを作って knife create cookbooks foo とかやらなければならないのがだるいから、その部分を自動化するスクリプトを書いた。

$ ruby chef-solo-skelton.rb foo
## chef-solo -c /Users/tily/foo/solo.rb -j /Users/tily/foo/node.json
[Sun, 29 Jul 2012 12:43:15 +0900] INFO: *** Chef 0.10.8 ***
[Sun, 29 Jul 2012 12:43:19 +0900] INFO: Setting the run_list to [] from JSON
[Sun, 29 Jul 2012 12:43:19 +0900] INFO: Run List is []
[Sun, 29 Jul 2012 12:43:19 +0900] INFO: Run List expands to []
[Sun, 29 Jul 2012 12:43:19 +0900] INFO: Starting Chef Run for linwttaa.local
[Sun, 29 Jul 2012 12:43:19 +0900] INFO: Running start handlers
[Sun, 29 Jul 2012 12:43:19 +0900] INFO: Start handlers complete.
[Sun, 29 Jul 2012 12:43:19 +0900] INFO: Chef Run complete in 0.001156 seconds
[Sun, 29 Jul 2012 12:43:19 +0900] INFO: Running report handlers
[Sun, 29 Jul 2012 12:43:19 +0900] INFO: Report handlers complete

$ tree foo/
foo/
├── cookbooks
├── node.json
└── solo.rb
 
1 directory, 2 files

レシピを指定すると、自動で opscode 公式の cookbook をインストールしてくれたりもする。

ruby chef-solo-skelton.rb hoge apache2 php mysql

まだ使いやすいかよく分からないので、必要になったとき自分で使いつつエンハンスしたい。

クックパッドのレシピで分量を自由に変更できるブックマークレット

http://farm8.staticflickr.com/7206/6951261935_0e52c4db10.jpg

最近よくクックパッドのレシピを見て料理を作っているんだけど、作りたい分量が合わないとき頭で分量を計算しなおすのが面倒くさいので作りました。

ブックマークレットを起動すると「操作」エリアが追加され、テキストエリアに数字を入れて「倍」ボタンを押すと分量を変更してくれます。あと、おまけで「ヘルシーに」を押すと肉と油を減らしたり「うまそうに」を押すとその逆をやってくれたりする機能もつけてみました。

豚汁 100 人分とか巨大なケーキの分量を知ることができて便利だと思う (もちろん 0.5 とか入力して 4 人分を 2 人分に減らすこともできる)。ただ、レシピ掲載者のひとたちの表記ゆれを全部カバーできている自信が全然なくてうまく動かないことが多いかもです。

(サンプル画像としてプロの味!の豚汁 by Fumiaを使わせていただきました、問題があれば @tily までご連絡ください。)

(使ってみたレポートありがとうございます、使い方がよくわからない人は 料理レシピ「クックパッド(cookpad)」で分量を再計算ブックマークレットが便利すぎる!|アマモ場 をどうぞ。)