Capistranoで指定したユーザ権限によりファイルを作成する

投稿者 akira 2010-06-20 03:49:00 GMT

Capistranoを使ってファイルをアップロードするにはputやuploadを使う。ただ、指定したユーザの権限でファイルをアップロードしたいというときにしばしばなやむことになる(……よね?)。

たとえば、特定のユーザでしかアクセスできないディレクトリにファイルを置きたいといったケース。ログインユーザでいったんファイルをどこかにput/uploadして、そのファイルをsudoでchownしてmvすることになる。

まあ、通常はそれで十分なのだけど、こんなふうに書けなくもなさそうだ。

content = "abc\ndef"
sudo "dd of=/path/to/file bs=1 count=#{content.size} 2>/dev/null",
  :data => content + "\n",
  :as => "user-name"

ただし、この方法ではモードの指定ができないからchmodは別に行う必要がある。

明日はじめるCapistrano

投稿者 akira 2010-03-03 10:13:00 GMT

Railsのdeployに使われることで知られるようになったCapistrano。でもその実態はRailsにとらわれているわけではありません。Capistranoは何かというと、こう言えます。SSHを使って多数のホストに同時並列に接続して、実行させたいコマンドを一斉に送信、実行結果を受け取って問題なければ次のコマンドを、というのを行うためのフレームワークです。

Capistranoを使い始めるのは簡単です。受け側に必要なものがあまりなく、一般的なUNIX系環境であればすでに準備が整っている状態です。Capistranoを実行するホストにはRuby環境が必要ですが、それ以上のものは基本的には必要ありません。コンパイラも不要ですし、ちょっと試してみるだけならインストールしてなくても大丈夫。

2009年のまとめ

投稿者 akira 2010-01-02 13:00:00 GMT

自分以外の誰も読まないとは思うけど、というよりも読むまでもなさそうです。自分のための記録用。

まず、このサイトの状況から。サイト全体のページビューは81,570だった。そのうちの65,745が日記分で、これらは2008年とだいたい同じ。あいかわらず検索エンジンからのアクセスが多く、直接アクセスは全体の1/4ほど。検索エンジンはgoogleとyahooでほぼしめられていた。割り合いは昨年から変化があってgoogle:yahooがおよそ2:1。(なお3月の半分以上くらい、手違いで集計されていなかった。)

2009年の記事の中でアクセスの多かった順に並べると次のようになる。

  1. 海のエジプト展
    観に行ってきて、わかんないところや、うまくまわれなかったところもあったけど、それなりに楽しめたという内容。期間が限られた大きめのイベントだったからアクセスが多かったのだろうと思う。

  2. 意外と良かったHandpresso(ハンドプレッソ)
    こんなにアクセスがあったのは意外だった。ハンドプレッソはなかなかよくて今もちょくちょく使っている。エスプレッソマシンを常設しているような家には不要だけど、時々・手軽になら、場所いらず・準備いらずでおすすめ。ポンピングも何度かやるとコツがわかってそんなに大変ではなくなる。

  3. 「プログラミング言語Ruby」を読まなくてもよいのは誰か
    本を読んだという内容。タイトルはその時期、一度こんなふうなのをつけてみたいと思っていたから。

  4. adjtimexによる時計の調整
    時計の調整は、必要になるにしてもごくたまに。忘れたころに困ったことが起きる。で、実際に困ってしまったときの記録。ntpでもダメなときに。

  5. Capistrano、sudoとrunと:env
    Capistranoのsudoとrunという二つのメソッドの動作上の違いについて。わりと重箱の隅。

  6. 日本大通りに蜘蛛が出た
    エジプト展と同じで期間限定のイベントにからむ。クモはすごかった。本イベントは、自分としては楽しんで見てまわったけども、前評判と比較するとどうしても…… というのはたしか。だからといって前市長をせめてもー、とも思う。

  7. 二代目LED読書灯
    トラブルで画像を失ってしまってそのまま。撮り直そうと思っているのだけど、けっこうめんどうなのでなかなかできず。新しく買ったLEDライトは時々明るすぎると思うこともあるけど、よい具合いで気にいっている。土台しっかりで安定しているのも地味に良い。

  8. 最近のsudoと環境変数
    sudoでの環境変数の扱いが変わっていたことに気付いたという話。

  9. いいとこ見付けた - Caffe di mare
    横浜元町に新しくできたエスプレッソがおいしいお店。小さいお店だけども一階・二階での完全分煙だというのも含めておすすめ。ここのところ外出自体が少なくてなかなか行けないけど、ぜひまた行きたい。

  10. 桜まつり - 弘明寺〜黄金町
    弘明寺から歩いたよという話。そしてコーヒーに縁遠かった一日の記録。

2009年よりも前のものも含めると、書評の書き方がトップに、VMware上のLinuxにシリアルコンソールで接続するが四位にが入ってくる。どちらも多の人の記事をひいているだけに近いもので、ちょっと申しわけない気分。

一年を通してふりかえってみる。昨年頭、一番に考えていたのは英語と文章の力をつけることだった。だが、どちらについても何をするということもなく過ごしてしまった。まとまった文章を書いたのはWEB+DB PRESS Vol.50だけ。調査から力を入れたものの、少し外していたかもしれないと後で思った。テーマをとらえられていなかったろうか。

続いてあげていたのがイベント参加だが、これは実行できた。とちぎRuby会議とRuby会議。とちぎRuby会議ではしゃべる機会をいただけた。イベント、とは少し違うが、Debian Ruby 1.9会議をできた。個人的には、パッケージを使ってもらえているというのを知れたのは本当によかった。パッケージメンテナンスについてはへこむことの方が多いくらいなので。ただ、ToDOをこなすにあたっては、いろいろと状況・前提が変わってしまったりもしたとはいえ、ぐだぐだになっている部分もあるのを反省しなければならない(gem to debは今でもやりたいなと思ってはいる)。一方で、勉強会をやるやるいって結局はやらなかった。形について考え過ぎていたかもしれない。ともかく一度やってみて、とは思っていたのだけど次に挙げることともからまってうまくいきおいをつけられなかった。

もしかしたらあるかもしれないと考えていた仕事の変化は、なかった。ただその周辺でわりとショックをうけるようなことがあって、ある期間ひきずり続けていた。この影響が仕事以外の生活全般にもれ出てしまった。一つの出来事によるものとしては久しぶりのつらさを味わった。

その他、カメラ知識をもう少しなんとかと思っていたけれどダメだった。というか忘れていた。アレンジコーヒーは手を出したいと言いつつ手を出せず。レシピ本を立ち読みすることすらしていない。マッサージなどを定期的にというのは後半ある程度できた。デジ一は買った。買ったけど使う機会がほとんどないままずるずると。何だか忙しい。

本はムラがあったけど読んだ。中盤あたりでは読み返すことも多かった。新たに出会ったのは、まず、山本幸久さんに凸凹デイズで。仕事をしている誰かを描写されることが多いようで、その後、既刊のいくつかと、新刊を読んだ。先日、書店の平台にミニコーナーができていて、読んでない既刊があって手にとりそうになったが、今は読めないとあきらめた。後で読む。

次に濱野京子さんとトーキョー・クロスロードから。何かかきたてられるような刺激があった。それからまんがのほうで新堂あきとさん。パーツのぱは業界ものでお仕事もの。お仕事ものについては、それなりの描写が含まれるものは基本的に好き。業界ものでもあるのであるある的にも楽しめた。シンプル ノット ローファーで衿沢世衣子さん。独特の間がおもしろくてひきこまれる。ふと読み返したくなるような。既刊には手を出せていないが、またいずれ。その他に単行本待ってました、というようなものがいくつもあった。

amazon.co.jpでの売り上げはRuby関係ばかりだった。デスクトップリファレンスが出てきているのがおもしろい。

  1. たのしいRuby 第2版 Rubyではじめる気軽なプログラミング(11)
  2. たのしいRuby―Rubyではじめる気軽なプログラミング(6)
  3. プログラミングRuby―達人プログラマーガイド(5)
  4. プログラミングRuby 第2版 言語編(5)
  5. RailsによるアジャイルWebアプリケーション開発 第3版(5)
  6. 初めてのRuby(4)
  7. Rubyデスクトップリファレンス(4)
  8. プログラミング言語Ruby(4)

ありがとうございました。

さて新年。新たに○○をという、具体的なねらいもいくつかはあるが、いいかげんなんとか英語をやっていきたい。生活面では大きな意味で転換をはかりたい。見直したいというのに近いかもしれない。やりたいことをいかに(どのように)するか、といったあたりで考えたり実行したり。

Capistranoで上位タスクと同じホストを強引に処理対象とする

投稿者 akira 2009-10-03 00:41:00 GMT

Capistranoのタスクは実行対象ホストのリストをホスト名やロール名で指定できる。この指定は静的なものなので、あるタスクから別のタスクを呼び出したとしても、それらの指定は引き継がれない。

以下のレシピでタスクbarには:hostsオプションがあり、これに従って同タスクはhost3とhost4に対して実行される。barから呼び出されるタスクbazにはこのような指定がないため、barでの指定によらずロールに登録されている全ホスト(host1とhost2)に対して実行される。

role :foo, 'host1', 'host2'
task :bar, :hosts => %(host3 host4) do
  run "..." # host3とhost4でコマンド実行
  baz
end
task :baz do
  run "..." # 全ロール(host1とhost2)でコマンド実行
end

このような動作をするため、タスクを部品化して再利用しようとすると、ある程度のところからはメソッド定義をしていくことになる。

ここでふと、いいかげんなオブジェクトを渡せばそのあたりの動作をコントロールできかなと考え、その思い付きのまま試みにコードを書いてみた(gist)。テストは一部だけ(しか書いていない)。

uh, ul = UpstreamSupport.create(self)

role :foo, 'host1', 'host2'
task :bar, :hosts => %(host3 host4) do
  run "..." # host3とhost4でコマンド実行
  baz
end
task :baz, :hosts => uh do
  run "..." # 呼び出し元と同じホスト群でコマンド実行
end

このレシピのタスクbarを実行するとhost3とhost4でコマンド実行される。そして、そこから呼びされたタスクbazでも同様にhost3とhost4でコマンド実行される。しかし、タスクbazを直接実行すると、全ロールのホスト群、つまりhost1とhost2でコマンド実行される。さて、うまく動くかな?

ただ、あんまりふみこみすぎてもなってところでもある。

Ruby 1.8.7-pXX v.s. Capistrano 2.5.x

投稿者 akira 2009-07-31 11:41:00 GMT

最近のRubyでCapistranoを実行しようとすると、対象ホストが二つ以上のときにかなりの確率でハングアップしてしまうことに気付いた。しばらくは見て見ぬふりをしていたのだが、仕事に少し関係しそうな気配もあって、調べてみることにした。

1.8.7-p72では問題なかった記憶があり、1.8.7-p174で現象が出ることは確認済み。ということでtagのある1.8.7-p72、1.8.7-p160をかわきりに、ruby_1_8_7におけるversion.hのcommit logを手掛かりに二分探索の雰囲気を出しつつ絞り込みをかけていったところ1.8.7-p135で現象が現れることがわかった。

このパッチレベルでの修正内容はruby-core:20446である。が、これを見ても具体的にどういう影響が出ているのかはわからない。修正内容からするとむしろ状況がよくなってくれそうなものだが…… などと思いつつ、試行錯誤を繰り返した。

まず、Capistranoでの問題はNet::SSHで起こっていることがわかった。Net::SSHではIO.selectを使っいながらソケットから少しずつ読み出すということをしている。これにThreadを組み合わせると現象を起こせる。たとえばこんな感じ:

require "net/ssh"
["localhost","localhost"].map {|h|
  Thread.new{ Net::SSH.start(h, "foo") }
}.each {|t| t.join }

次に、修正内容と見比べつつさらに試行錯誤し、結局はdebug printをいれてスレッドの動きを追いかけた。かなりおおざっぱな読み方しかしていないので間違っているかもしれないが、なんとなくこんな感じの動きのようだった:

  • 読み出し可能になるのを待っているスレッドがある
  • そのスレッドで読み出し可能になる→th_foundに値が入る
  • 次にスケジューリングのためスレッドを順番に見ていくが
  • 運悪く、th_foundなスレッドに到達する前に「次」に実行可能なスレッドが見付かってしまう

現象からいうと運悪くというよりは運良く処理が進むことがあるといったところで、ほとんどの場合、上の再現例の二つのスレッドがゆずり合うような形になっている。

そこで、試みにth_foundしたスレッドにできるだけ処理がまわるよう、スレッドのプライオリティが同じときにも「次」候補のチェックをするように以下のような変更を加えてみた。

--- eval.c	(revision 24335)
+++ eval.c	(working copy)
@@ -11228,7 +11228,7 @@
 	    break;
 	}
 	if ((th->status == THREAD_RUNNABLE || th == th_found) && th->stk_ptr) {
-	    if (!next || next->priority < th->priority) {
+	    if (!next || next->priority <= th->priority) {
                 if (th == th_found) {
                     th_found->status = THREAD_RUNNABLE;
                     th_found->wait_for = 0;

Capistranoでの動きをざっとみたところではこの変更で止まらずに動くようになった。Rubyのテスト(threadのみ)をまわしてみた限りでは影響はなさそう。はてさて。

……という話をIRCでしてみたところmputさんがredmineに登録してくれた(Bug#1848ruby-dev:38971)。あと多分、これは同じ話だと思う。

追記(2009-08-07)

少なくとも上の変更ではまずいと思えるので変更を出しなおしたのがruby-dev:39003。ただ、それでもまずい感じはするよねと思っていたところ、やはり好しくないらしい。

それではということで再現コードを小さくして、debug printで動きを追いかけてみたのがruby-dev:39037になる。th_foundとはちょうどあべこべにスレッドが選択されている…… ような気がしている。

追記(2009-08-10)

ruby-dev:39042にある通りr24442で修正された。

勉強会のテーマ案

投稿者 akira 2009-07-15 01:44:00 GMT

昨日の帰りみちでの思い付きだけど、わりとピンときているのでこのあたりでやってみようかなと考えている。おそらく横浜近辺にて開催。参加見込みがまったくわからないので、興味があるという人がいたらコメントなどもらえるとうれしいです(twitterやfriendfeedでも)。

五人くらい集まるようならやってみよう。(五人くらいなら、どっかのカフェでってのもよいかな。)

Capistrano勉強会「deploy.rbを読む夕べ」

  • 目標
    • タスクを書けるようになる(その足掛かりをつかまえる)
  • 開催日時
    • 未定(7/下〜8/中くらい?)
    • 二時間程度
  • 前提知識
    • スクリプト言語の経験 - Perl、PHP、など(もちろんRubyでも)
    • ある程度のUNIX知識(シェルの利用方法など)

Rails向けに提供されているdeploy.rbをいっしょに読んでタスクの構造を学ぶ。実際に使われているタスクの実装を読み解くことによりSSHフレームワークとしてのCapistranoの姿をとらえ、その(Rails以外での)活用方法を知る。

CapistranoでRubyを始めてもいいんじゃない?

Debian勉強会「パッケージを作ろう」

  • 目標
    • 自分でパッケージを作れるようになる
    • 既存パッケージをカスタマイズできるようになる
  • 開催日時
    • 未定(9月くらい?)
    • 二〜三時間程度
  • 前提知識
    • Debian/Ubuntuでのパッケージ操作経験(aptitude/apt-getによるインストールや削除など)
    • ソフトウェア構築の経験(configure; make installなど)

経続的なメンテナンスが可能なパッケージ作りを考える。パッケージ間の関係性やポリシーの役割りに注目し、長く使える、どこでも使えるパッケージを目指す。

またパッケージ作りの流儀を概観し、既存パッケージのカスタマイズ方法を知る。

Capistrano/Vlad勉強会

投稿者 akira 2009-06-11 23:03:00 GMT

6/24に発売されるWEB+DB PRESS Vol.51WEB+DB PRESS Vol.51[rakuten]には「Webサーバの負荷分散環境におけるデプロイ/ファイル転送<基礎と実践>」という記事があり、その中でrsync、makuosan、Capistranoが扱われている。makuosanもたいそう気になるが、ここではCapistrano。Capistrnaoの記事は舘野祐一さんによるもので実践的な内容が期待できそうな気がする。

Capistrano/Vlad勉強会をやってみようと考えていたところにこの記事があった。そんなわけで、この記事を受けて(?)来月、2009年7月後半のどこかで勉強会をやろうと決めた。(RubyKaigi2009があるから最後の週かな。)

勉強会の内容について、一応のイメージは前に書いたようなものを持っている。でも、ねるのはこれからなので、こんな内容があるといいなとか、こんなコンテンツを持ってるよとか、そのほか何でも意見をもらえるとうれしいです。

Capistranoと仮想端末と標準入出力の関係

投稿者 akira 2009-06-10 00:02:00 GMT

次の二つのレシピの違いを考えてみる。

# (A)
task :foo do
  run "echo hello, world | dd bs=1 count=5 skip=5"
end
# (B)
set :default_run_options, :pty => true
task :foo do
  run "echo hello, world | dd bs=1 count=5 skip=5"
end

字面の上での違いは(B)の一行目、setで始まる行が(A)にはない点である。送信されるコマンドラインは同じであるから動作は同じかというと、そうでもない。これは実行してみるとわかる。だが、その前にrunで実行しているコマンドddの動きについて確認しておこう。ddはif=やof=で入力元・出力先を指定しなければ標準入力・標準出力に対して読み書きを行う。そして、読み書きを行った状況を標準エラー出力に書き出す。

上の(A)および(B)においては、echoにより標準入力に流し込まれた文字列を読み取り、オプションで指定された部分(6バイト目からの5バイト)を標準出力に書き出す。それでは実行結果を見てみよう(ある実行例からの抜粋)。

# (A)
  * executing "echo hello, world | dd bs=1 count=5 skip=5"
    servers: ["localhost"]
    [localhost] executing command
 ** [out :: localhost] , wor
*** [err :: localhost] 5+0 records in
*** [err :: localhost] 5+0 records out
*** [err :: localhost] 5 bytes (5 B) copied, 2.9705e-05 s, 168 kB/s
    command finished
# (b)
  * executing "echo hello, world | dd bs=1 count=5 skip=5"
    servers: ["localhost"]
    [localhost] executing command
 ** [out :: localhost] , wor5+0 records in
 ** [out :: localhost] 5+0 records out
 ** [out :: localhost] 5 bytes (5 B) copied, 3.03e-05 s, 165 kB/s
    command finished

似たような出力になっているが、よく見ると(A)には「out」と「err」があり、(B)には「out」しかない(一行少ないというのもあるが、それはまた別の話)。この違いはコマンド実行の際に仮想端末を要求したかどうかによる。(B)は仮想端末を要求していて、そのため、ddが標準出力に書き出した内容と標準エラー出力に書き出した内容の両方が、Capistranoには標準出力に書き出されたものに見えている。

とはいえ、これはCapistranoに限った話ではない。たとえば以下のコマンド実行例でも同じ状況を見てとることができる。

$ ssh -q    localhost "echo hello, world | dd bs=1 count=5 skip=5" >/dev/null
5+0 records in
5+0 records out
5 bytes (5 B) copied, 3.4422e-05 s, 145 kB/s
$ ssh -q -t localhost "echo hello, world | dd bs=1 count=5 skip=5" >/dev/null
(何も表示されない)

話を戻すと、こうした動作の違いは以下のときに問題になる可能性がある。

  • runやsudoにブロックを与えてコマンドの出力(特に標準出力)を取り込もうとしている
  • エラーが発生した(標準出力に何か書き出される)

どんなときにでも使える回避策というのはなさそうに思える。強いていえば次のどれか、またはすべてに気を付ける。

  • 標準エラー出力が不要であればコマンドライン上でリダイレクトして捨てておく
  • 受け取ったデータのうち必要と思われる部分だけをうまく抜き出す
  • 本当に必要なときにだけ:pty => trueを指定する

最後の点については、要するに:default_run_optionsに:pty => trueを入れるのは要注意だということになる。実際には、あらかじめ用意されたレシピを使うためなど、どうしようもないこともあるのだが、コントロールできるなら個々のrunやsudoにおいて:pty => trueを指定するといくらかマシになる。

入門書の次に読むRailsデプロイ

投稿者 akira 2009-05-30 09:55:00 GMT

とあるきっかけから、監訳者の一人の橋本さんを通してオライリーさんからRailsデプロイRailsデプロイ[rakuten]をいただいた。Railsプログラミングがひと通りできるようになった、常用しているマシンの上で動作させてきた、自分以外の人々に向けたサービスをこれから始める。本書はそんな人々を助けてくれるだろう。

プライベートに運用するアプリケーションを除くと、普段は自分がログインすることがほとんどないようなマシンがアプリケーションを動かすための場になる。そのようなマシンは一つだけではなく、用途ごとに複数のマシンを並べて動作させることが多い。このようなマシン環境下でアプリケーション群を正常動作させ続けるには、いつも使っているマシンでアプリケーションを運用するのとは異なった視点・手法・作業が求められる。

たとえば、アプリケーションを動かすのに必要なハードウェア環境…… はともかくとしても、ソフトウェア環境を整えなければならない。それはRubyのインストールから始まるかもしれないし、もしかするとRubyをインストールするための環境作りから始まるかもしれない。また、HTTPサーバを設定しなければならないだろうし、複数あるマシンがうまく連携できるようにもするだろう。アプリケーションのリリースは複数のマシンが対象となる。マシンの用途が違えば作業も違う。そして、マシンを増やすことがあればそのたびにそれらすべてを行う必要がある。いきなりなにもかもやろうとすると大変だ。

本書を読むと、手元で動かす→他のある程度整備されたホストで動かしす→VPSを借りて動かす→専用ハードを借りて……、といった具合にじょじょに規模を拡大していく工程をひと通りながめることができる。その内容は単に「Railsアプリケーションをリリースする」というだけではなく、複数のマシンでサービスを運用するための機能分散の方法や負荷分散をするためのリバースプロキシの設定など広い範囲をカバーしている。

そのうえ、複数のデータベースを運用するためのMySQLの設定とアプリケーション側での対処、チューニングのためのベンチマークやプロファイルの取り方、キャッシュの使い方とキャッシュ乱用への注意など、アプリケーションをサービスとして運用するのに必要な領域についても実践的に解説する。そのような点から、アプリケーションを作れるようになり、さあこれからデプロイしようといったストーリーの入口で本書は活躍するだろう。

少々残念なのは、今この時期にPassengerが扱われていない点。もちろん出版時期などからいたしかたないところではあるのだが、カバーしている範囲の広さからすると実におしい。また、Railsからはやや離れたところでの説明や表現にはいくつかひっかかる点もあった。一つあげると125ページの「Linux上でRailsアプリケーションを動作させるためには……最低限でもCやC++のコンパイラ……が必要」といった記述で、これはRMagickなどをRubyGemsで運用することなどを前提にしているのであるが、できるならコンパイラは避けたいところと考える人も少なくないはずだ。(他のものも含めて出版社の方に伝えておいた。)

一方、本筋とやや離れたところでちょっと感心したのは、Apache HTTPサーバの設定作業の中で、a2enmodやa2dismodなどに触れている点だった(本書ではUbuntuを主な環境としている)。この種の特定のOS環境(この場合はDebian/Ubuntu)に独特な手順というのはともすれば流されてしまいがちである。もちろん明確な方針があって別のやり方で運用するのは構わない。だが、マシンはいつか自分の手を離れるものであるとすると、理由がなければできるだけその環境の流儀に従っておいたほうがよいと思う。考えてみればRailsだってそうなのだから、環境整備でも同じようにしたってよいだろう。

追記(2009-07-24)

読んでいてひっかかった点は編集さんに送ったが、そのうち正誤表に載っていないものや載らなそうなものを以下に挙げておく。

  • 56ページ15行目「コマンドラインに関する知識と何でもGoogleで検索してみる」
    • 「知識を何でも」?
  • 67ページ下から10行目「インストール中にどのような種類の設定を行うか聞かれたら"Internet site: ... "(メールはSMTPを使って直接送受信される)という選択肢を選んでください」
    • 他のところでメール関連のことを扱っているのかなとも思ったのだけど特別に前提条件はなさそうなので、本文の記述に従うと外部からアクセス可能なSMTPサーバをたてるということになりそう
    • となると、一般的な選択という点ではローカルホストのみで動作させるくらいにして、必要に応じてそのような設定をしよう、というような書き方のほうがよいのでは
  • 106ページ囲み
    • ファイルを/etc/init.dにコピーするだけでよいように読めるが一般にそのようなことはないはず
    • Debian系であればupdate-rc.d、Red Hat Linux系であればchkconfigでの操作が必要になる
  • 107ページ囲み「flexとbisonそしてbyaccをインストールしましょう」
    • flex、bison、byaccのうちのいずれか、ではないのかな
  • 110ページ15行目「Linuxシステムでは、initは/etc/init.dに置かれるすべてのスクリプトの実行を担当しています」
    • initはすべてのプロセスの祖先ですから間違いとはいえないが、 ここでいおうとしているinitの用法と /etc/init.d(より正しくは/etc/rc*.d)以下のファイルの運用はまた別だといっても差し支えないと思いう
    • /etc/init.d(略)以下のファイルにより起動したサービスは /etc/inittabで管理されるプロセスとは違ってinitにより再起動されることは通常ない
  • 110ページ訳注「sudo update-rc.d monit remove」
    • /etc/init.d/monitが残っている場合、update-rc.d -f monit removeとする必要がある
    • そうでなければrm /etc/init.d/monitした上でupdate-rc.d monit removeとする
  • 110ページ下から4行目「Monitを使ってMongrelを管理するようになったら…mongrel_cluster/recipesを使う必要はなくなりました」
    • mongrel_cluster/recipesが唐突に出てきている…… ような気がする
    • 読み落としてしまっただけかも(検索可能な何かがあればなあ)
  • 111ページ下から4行目「FastCGIのゾンビプロセス」
    • 「なぜか居残り続けてしまう、仕事をしないfastcgi管理下のプロセス」にすぎず、UNIX一般にはこのようなプロセスをゾンビとは呼ばないように思う
    • 実際、reaperはfastcgiプロセスに対してシグナルを送っているわけだけど、本来の意味でのゾンビプロセスはそもそもシグナルを受け取れる状態にない
    • 参考: プロセス - 終了状態
  • 117ページ15行目「ハートビート」
    • 外部から動いていることを確認すること自体をハートビートとはあまり呼ばないような気が
    • 一般的には生存証明を自らするようなものをそう呼んだり、あるいは、HAクラスタなどでサービスが動作していることを待機系とで情報交換するような仕組み全体を指すことはあると思う
    • いずれにしても本文で説明されているのはNagiosなどがするような監視であって、死活監視などと呼ぶほうが一般的ではないだろうか
  • 127ページ下から二行目「CNAMEレコードはAレコードのエイリアス(別名)のようなものです。対象のAレコードは自分のものでも他人のものでもかまいません」
    • DNSの「リソースレコード」と「名前」が混同されているように思える
    • CNAMEレコードでは名前に対応する正しい名前を定義する
    • CNAMEレコード全体をもってエイリアスと表現するのは間違いとまではいえないが、CNAMEレコードの対象がAレコードというのは表現としておかしいのではないか
    • 実際、CNAMEレコードを持つ「名前」に他のリソースレコードを付けてはならないことになっているはずで
  • 129ページ10行目「大きな値(例えば7日)を指定すると、ブラウザなどのクライアントソフトウェアがDNSに問い合わせを行う回数を削減でき」
    • 「クライアントソフトウェアがDNSに問い合わせを行う」というのはリゾルバの動作で、リゾルバの動作にはTTLはリゾルバの動作に影響を与えない…… のではないかしら
    • MS-Windowsなど、ある種の環境ではDNS問い合わせについてのキャッシュ機構があるようだが、それらを考えにいれたとしても実装依存というのがやっとだと思う(MS-Windowsの機構はキャッシュサーバの一種なのではないかしら?)

Capistrano、sudoとrunと:env

投稿者 akira 2009-04-26 12:57:00 GMT

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を書き換えるほうがよいとも考えられ、状況としてはどっちもどっちといったところ。ならばそのあたりを設定で切り替えられるようにしておけば、とも考えるが、はたしてそこまでやる価値があるかどうか。