焼き鳥とエスプレッソ
仕事終わり、石川町で待ち合わせてtoriromeoで夕食。出来たころから気にはなりつつ、夕食を外食ということがなかなかないので行けないでいたお店の一つだった。
カウンターのみ、7〜8席くらい。鳥ほかの串ものと飲み物のみというシンプルなメニュー。限られているなかでも串ものメニューには様々なもの(チーズとかキャベツとか鴨とか)が用意されていた。飲み物はワインほか各種。グラスワイン一杯とジンジャーエール、それにほどほど満腹できるくらい食べて二人で7,000円弱くらい。まずまず。
帰宅する前に、この前いってとても良かったCaffe di mareに寄って、エスプレッソ、カプチーノとケーキ。エスプレッソがやはりおいしくて、だからカプチーノもおいしくて、自家製ケーキもなかなかいける。toriromeoには食後の何かはなかったのだが、逆にちょうどよくおいしくいただけた。ちなみにケーキセットになって600円ととてもお得。
Capistrano、sudoとrunと:env
Capistranoにはリモートホストでコマンド実行させるためのメソッドがいくつかある。その代表がrunメソッドとsudoメソッド。runメソッドはリモートホストにログインしたユーザ(特に指定しなければ実行ユーザ)でコマンドを実行する。sudoメソッドはsudoコマンドを使って指定したユーザ権限を得てからコマンド実行する。
この二つのメソッドは使い方がほぼ同じ。違いといえばsudoメソッドでユーザ名を指定できるということ。ただ、これは目的からして当然だといえる。ではそれ以外に違いがないのかというとそうでもない。実際のコマンド実行まで追いかけると二点ばかり、そしてけっこう大きな違いがあることに気付く。(以下、Capistrano 2.5.5で確認。)
一つめは複数コマンドの実行。runメソッドを使って複数のコマンドを一度に実行したいとき、たとえば次のように記述する。
run "mkdir /tmp/foo; touch /tmp/foo/bar; ..."
これを特定のユーザ権限で実行したくなったとする。メソッドをrunからsudoに変えればどうかと考えるわけだが、そうしても意図通りには動作しない。これはリモートホストに送信されるコマンドラインを見れば明らかである。
# runメソッドの場合 sh -c "mkdir /tmp/foo; touch /tmp/foo/bar; ..." # sudoメソッドの場合 sh -c "sudo -p 'sudo password: ' mkdir /tmp/foo; touch /tmp/foo/bar; ..."
このように、sudoコマンドは最初のコマンドであるmkdirにしか効果を与えることができない。もちろんsudoメソッドを複数回使うことで連続したコマンド実行をすることは可能である。ただ、Capistranoではコマンド実行エラーが起きるとそこでタスクの処理を止めてしまうため、意図を表現しにくいということもある。そんなときには、sudoメソッドを使うとよい。
上の例でもsudoメソッドを使っているが、もう一つ別の使い方がある。sudoメソッドを次のように使うと、Capistranoが必要とするsudoコマンドのコマンドラインを生成できる。
run "#{sudo} mkdir /tmp/foo; #{sudo} touch /tmp/foo/bar; ... "
このとき送信されるコマンドラインは次のようになる。
sh -c "sudo -p 'sudo password: ' mkdir /tmp/foo; sudo -p 'sudo password: ' touch /tmp/foo/bar; ... "
"#{sudo :as => "www-data"} ..."のようにすればユーザを指定できるし、必要に応じてパスワードの処理も行われる。
二つめは環境変数の問題。両メソッドともオプションでコマンド実行時に設定する環境変数を指定できるようになっている。たとえばLANGとGEM_HOMEを指定するなら次のように記述する。
run "gem install rails", :env => { "LANG" => "C", "GEM_HOME" => "/tmp/GEM" }
sudo "gem install rails", :env => { "LANG" => "C", "GEM_HOME" => "/tmp/GEM" }
それぞれのメソッドにより送信されるコマンドラインは次の通り。
env LANG=C GEM_HOME=/tmp/GEM sh -c "gem install rails" env LANG=C GEM_HOME=/tmp/GEM sh -c "sudo gem install rails"
ここで問題なのは、sudoコマンドが特定の環境変数以外を削除してしまう点である。一つめの場合と同じように、あるときrunメソッドをsudoメソッドに変えたとしても、環境変数の指定がうまくいとは限らない。さらに、この問題は:default_environmentオプションを設定している場合でも同様で、環境変数によってはrunメソッドには効くのにsudoメソッドには効かないということが起きる。
この問題を回避するには…… と考えてみたのだが、あまりよい手がなさそうだ。よい手はないのだが、とってもイヤなやり方ならないでもない。以下では、Rails向けの標準レシピを利用するにあたって細工を加えてみている。
set :local_username, `whoami`.chomp
set :default_environment, "GEM_HOME" => "/tmp/GEM"
set :runner, "#{fetch(:runner, local_username)} env " +
fetch(:default_environment, {}).inject("") {|memo,(k,v)| "#{k}=#{v}"}
set :admin_runner, "#{fetch(:admin_runner, local_username)} env " +
fetch(:default_environment, {}).inject("") {|memo,(k,v)| "#{k}=#{v}"}
標準レシピでは:runnerや:admin_runnerの値がsudoメソッドの:asに渡される。:asの値はsudoコマンドの-uの引数として埋め込まれるが、その際、特別な処理はされないため、上のようなおかしな値を与えてやることでenvコマンドのインジェクションが可能となる…… のではあるが、言うまでもなくどうしようもなくひどいやり方だ。
とはいえ、コマンドラインに直接envコマンドを書けばよい自前のタスクとは違って、標準レシピのようにあり物を使おうとする場合にはどうにもしようもないということも事実である。
少し追いかけてみたのだが、今のCapistranoの実装においては、:envオプションに対応するenvコマンドの展開とsudoメソッドに対応するsudoコマンドの展開が別々の場所で行われていることもあり、シンプルに解決するのはやはり難しそうだった。考えてみたのはパッチのような変更で、$CAPISTRANO:ENV$を新設してsudoメソッドで埋め込み、最終的なコマンドラインへと展開されるタイミングでenvコマンドに変換されるようにした。これにより文字列中のsudoメソッドにも対応できるはずだし、任意の場所でenvコマンドを使用できるようにもなる。
ただ、これをやってしまうとenvコマンドの実行を許さなくてはならなくなり、コマンドの実行制限をしている場合にはうまくない。そうするくらいなら必要な環境変数を渡せるようsudoersを書き換えるほうがよいとも考えられ、状況としてはどっちもどっちといったところ。ならばそのあたりを設定で切り替えられるようにしておけば、とも考えるが、はたしてそこまでやる価値があるかどうか。
最近のsudoと環境変数 2
sudoの環境変数の扱い方が最近になって変わっていることに気付いた。最近といっても一〜二年はたっているかもしれない。
sudoを介してコマンド実行するとき、あらかじめ決められた環境変数以外は消し去されてしまう。このためたとえば次のようなコマンド実行は意図通りには動かない。
$ GEM_HOME=/tmp/GEM sudo gem install rails
従来、このようなときにはenvを使って回避というのが一つのやり方だったように思う。もちろんsudoersでenvの実行が許可されていなければならない。
$ sudo env GEM_HOME=/tmp/GEM gem install rails
sudo 1.6.9以降になると以下のような記述が可能となり、わざわざenvを使う必要がなくなった(バージョンは多少違うかもしれないが、少なくともDebian/etchの1.6.8p12ではこのような書き方はできない)。
$ sudo GEM_HOME=/tmp/GEM gem install rails
ただし、この書き方によって任意の環境変数を渡せるかどうかには条件がある。一つはsudo 1.6.9p8以降であること。もう一つは全コマンドを実行可能であること。
特定のコマンドの実行だけが許可されているユーザにおいては、通常の環境変数のフィルタリングルールが適用される。許可されない環境変数を与えようとするとエラーが起きてコマンドは実行されない。
もちろん全バージョンに共通してsudoersで明示的な許可があればそれで十分である。ここで述べた動作の変化はほぼデフォルトの設定のままで運用している環境に対して影響がある。
勉強会の勉強
Rails他のweb系フレームワークのコードを読むような勉強会はどうだろう。なんてことをちょっと考えていて、そのために勉強会のことをちょっとだけ調べてみた。今こそ! 勉強会: 第3回 勉強会を始めようによれば以下のようなステップがある。
- 開催のテーマを決める
- 開催前の準備 - 仲間になってくれる人、賛同してくれる人を探す
- 発表者を集める(発表のあるスタイルの場合)
- 開催日時と場所を決める
- 懇親会の準備
- 告知・参加者の募集
- 勉強会の開催(開催当日)
- フォローアップ
項目自体は思い付く通りといったところだが、注意点がいろいろとあることがわかる。勉強会カンファレンスキックオフのサマリーも参考になる。各種の決定・準備・告知などは地道にやるとしても、手間がかかりそうなのは受け付け。ただ今時は各種のサービスやツールがあるのでそれを使うとよさそう。どのようなものがあるかはMetaConにまとめがある。
あちこち見ている中で出会ったまっちゃだいぶくメソッドが興味深い。「参加者が本当に美味しいと思うお菓子を用意して、美味しかったと書いてもらう」というものだそうで、テクニックとしてなるほどと思える。ただ「本当に」あたりで外すとなかなかつらいことになりそうでもあるが。(小人数である程度見知った人々でやるならお奨めのカフェなんかをまわるのも良いかも。)
同メソッドはフォローアップをやりやすくしたり、あるいは次回参加をしやすくしたりする効果がありそうだが、他方、初回参加をやりやすくする方法も何か考えたほうがよいのかもしれない。特に今回のネタをやるとしたら、開催側ですら初めてということになるわけだから。
さて、今の状況だが、テーマと開催地域にはイメージがあるもののそれ以外は白紙で、ありていにいって、単に思い付いただけの状態。地域は横浜〜関内〜石川町あたりで、これは私が行けるところ。もちろん主催するならという話。テーマは前述の通り、web系フレームワークの内部を読解するようなものを考えている。
- Rails、Rack、Passengerあたりの動作をコードから追う
- その過程でRubyらしさを抽出して共有できるとさらによい
- Rails系だけでなくSinatraなど他のフレームワークも扱いたい(長期開催が可能なら)
- Ruby系だけでなく他の言語についても扱い、各言語らしさの違いなどを見る(横展開が可能なら)
RailsはでっかいのでRackなんかから始めて、Passenger、railties、actionpack、と移っていくのがよいかもしれない。ただRailsそのものをどこまでも深くというよりも、他のフレームワークや、他の言語のフレームワークとの比較…… とまではいかなくても雰囲気の違いを感じられるようなことができるといいなと思っている。うまく広げられたら交換勉強会みたいなことができればな、とも。
まだ思い付いただけで、何をどうしていくかまったくわかっていないが、それなりにやる気が出てきていたりはする。コメントやアドバイスをもらいながら、具体化する方向で考えていってみたい。
WEB+DB PRESS Vol.50
4月24日に発売になる
WEB+DB PRESS Vol.50[rakuten]で「Unix/Linux開発環境ミニマルスタイル」という記事を書かせてもらいました。
主な作業環境はMS-Windowsだけれども、本番環境はUNIX系OSである。本番環境での作業にはいつも通りの手順(や決められた手順)があるから困りはしない。ただイレギュラーな状態になると手も足も出ない(かもしれない)。コードやログを持ってきたり持っていったりを繰り返すのも煩わしいし、UNIX側でできることがもう少し増やせるとよいな。そんな人々に向けた記事です。
端末・シェル上での操作の基本を扱います。基本的なコマンドの紹介、リダイレクト・パイプの仕組みと使用例、コマンドを組み合わせて使う方法などを説明しています。環境としてはDebian/Ubuntuを想定しています。また、近年、一般的に使われるようになってきている仮想環境(andLinux、VMware系プロダクト、VirtualBox)を念頭に、パッケージ運用管理の基礎的な部分もカバーしました。
今号ではJunio C Hamanoさんの大ボリューム特集記事「はじめてのGit」で、基本的なところからから、一般的な使い方としては十分すぎるくらい深いところまで、かなり広範かつ詳細な解説がなされていて読みごたえがあります。またknuさんのPractical Ruby Programming!(6)では誰もが使うようになってきているTwitterと、注目されつつもなかなかブレイクしかけてますという状況が続いているFriendFeedの、それぞれのAPIを使って両者を結び付けるプログラムtw2ffが作成されていています(tw2ffはruby-friendfeedに含まれています)。
今号の内容からは離れてますが、FriendFeed関係では、携帯電話端末などからFriendFeedを使いやすくするf2pというアプリケーションもあります。当初はまはさに携帯からFriendFeedを使うために利用していましたが、「後で読む」機能や地理情報付きのポストができるなど、FriendFeedの標準的なインタフェースにはない機能があるため、最近ではPCでもf2pを介してFriendFeedを利用することが多くなっています。
ruby-friendfeedもf2pもGitでアクセスできるgithub上にありますから、Git大特集から読み進めると、自然とFriendFeedまでたどりつくようにできていますね。加えていえば、同時期に発売されるSoftware Design 2009/05の「はてな流! システム管理のツボ(10)」ではCapistranoが扱われていますので、あわせて読めばRailsアプリケーションであるf2pのデプロイも万全です。
そういうわけで、聞いたことはあるけれどもまだ使っていないという人は、これを機会にFriendFeedを始めてみてはいかがでしょうか。
(ちなみにSD誌の特集ではUbuntuが扱われています。私の記事とともに、などというのはおこがましいですが、これからUbuntuを始めるという人は参考にしてみてはいかがでしょう。)


