Hatena::ブログ(Diary)

130単位

2012-10-02

CapistranoでローカルからApache設定の更新/反映

LPなどの関係でRewriteRuleをよく追加するようなアプリを運用していて、Apache設定ファイルもリポジトリに含めたいと思ったのがそもそものきっかけでした。それで一度タスクを作って運用していたのですが、いまいちな部分もあったので今回ブラッシュアップしてみました。

環境

旧レシピ

namespace :httpd do
  task :update_conf, :roles => :web do
    run "cp -rf /etc/httpd/conf.d ~/httpdconf_bak"
    sudo "cp -rf #{latest_release}/config/httpd/#{stage}/* /etc/httpd/conf.d/", :pty => true
  end

  after "httpd:update_conf", :roles => :web do
    sudo "/etc/init.d/httpd configtest", :pty => true
    sudo "/etc/init.d/httpd reload", :pty => true
  end
end
仕様
  • config/httpd 以下に設定ファイルを環境ごとに格納
  • アプリ自体をいったんデプロイ
  • cap httpd:update_conf 実行
    • 現在の設定ファイルをバックアップ
    • 最新リリースから設定ファイルをコピー
    • configtestしてreload(configtestでエラー発生したらreloadは実行されない)
課題
  • Apache設定反映のためにリポジトリにcommit/pushされている必要がある
    • ローカルから設定反映して成功したらリポジトリにcommitしたい
  • 環境ごとのディレクトリのため重複が発生している
    • 環境によって変わらない共通な設定は1箇所にまとめたい

で、これらの課題を解決したものをつくってみました。

新レシピ

namespace :httpd do
  namespace :deploy do
    set :local_src_path, "#{Dir.pwd}/config/httpd/conf.d"
    set :remote_src_path, "#{shared_path}/httpd/conf.d"
    set :remote_dest_path, '/etc/httpd/conf.d'

    task :default, :roles => :web do
      transaction do
        on_rollback do
          run "for file in `find #{remote_dest_path} -type l`; do sudo rm $file; done;", :pty => true
          run "rm -rf #{remote_src_path}"
          run "cp -rf #{remote_src_path}.prev #{remote_src_path}"
          sudo "ln -s #{remote_src_path}/*.{conf,passwd} #{remote_dest_path}", :pty => true
        end
        update
        reload
      end
    end

    task :setup, :roles => :web do
      run "mkdir -p #{shared_path}/httpd/conf.d #{shared_path}/httpd/conf.d.prev"
    end

    task :update, :roles => :web do
      #remove old symlinks
      run "for file in `find #{remote_dest_path} -type l`; do sudo rm $file; done;", :pty => true
      #backup old files
      run "rm -rf #{remote_src_path}.prev"
      run "cp -rf #{remote_src_path} #{remote_src_path}.prev"
      #upload new files
      Dir.glob("#{local_src_path}/{*,environments/#{stage}}.{conf,passwd}").each do |file|
        upload(file, remote_src_path, :via => :scp)
      end
      #create new symlinks
      sudo "ln -s #{remote_src_path}/*.{conf,passwd} #{remote_dest_path}", :pty => true
    end

    task :reload, :roles => :web do
      sudo '/etc/init.d/httpd configtest', :pty => true
      sudo '/etc/init.d/httpd reload', :pty => true
    end
  end
end
仕様
app_root/config/httpd/
└── conf.d
    ├── application.conf
    ├── environments
    │   ├── production.conf
    │   ├── production.passwd
    │   ├── staging.conf
    │   └── staging.passwd
    ├── passenger.conf
    └── ssl.conf

これで共通部分もDRYにできて、ローカルからの反映が可能になりました。

ファイルごとにシンボリックリンクを張るめんどくさい仕様なのは、Webサーバーデフォルト状態から極力離れないようにしたいという思いからです(他のアプリの設定ファイルをベタに置くことも考慮して)。シンプルにするなら、設定ファイルを入れるディレクトリシンボリックリンクにして Include deployed_conf/*.conf などとしたほうがいいと思います。

気になる点としては、もし異なるアプリを同じ仕組みで同じサーバーデプロイする場合は、アプリ名のプレフィックスなどをつける必要があるというところでしょうか。

わかったこと

Gist

もっとこうしたほうがいいとかあればご指摘いただけると助かります!

参考リンク

2011-12-23

ApacheとRailsのリダイレクト比較

PCサイトと携帯サイトを端末ごとに振り分けるのに、jpmobileの判定メソッドがお手軽なのでRailsのredirect_toを当初利用していました。が、おそらくApacheリダイレクトさせたほうがパフォーマンスはいいはずです。ただ確信はなく、「推測するな、計測せよ」ということで調べてみました。

環境

Apache Bench

ab -n 100 -c 10 http://example.com/m/
結果サマリー
項目RailsApache
Requests per second63.3579.57
Time per request157.850125.677

というわけで単純な比較ではありますが、やはりApacheリダイレクトしたほうが2割ほど速いようです。

Rails
Server Software:        Apache/2.2.16
Server Hostname:        example.com
Server Port:            80

Document Path:          /m/
Document Length:        96 bytes

Concurrency Level:      10
Time taken for tests:   1.578500 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Non-2xx responses:      101
Total transferred:      54338 bytes
HTML transferred:       9696 bytes
Requests per second:    63.35 [#/sec] (mean)
Time per request:       157.850 [ms] (mean)
Time per request:       15.785 [ms] (mean, across all concurrent requests)
Transfer rate:          33.58 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       10   61  48.7    101     119
Processing:    22   86  52.9    117     303
Waiting:       21   85  52.6    117     295
Total:         33  147 100.1    218     420

Percentage of the requests served within a certain time (ms)
  50%    218
  66%    230
  75%    237
  80%    239
  90%    251
  95%    263
  98%    274
  99%    420
 100%    420 (longest request)
Apache
Server Software:        Apache/2.2.16
Server Hostname:        example.com
Server Port:            80

Document Path:          /m/
Document Length:        302 bytes

Concurrency Level:      10
Time taken for tests:   1.256765 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Non-2xx responses:      100
Total transferred:      52000 bytes
HTML transferred:       30200 bytes
Requests per second:    79.57 [#/sec] (mean)
Time per request:       125.677 [ms] (mean)
Time per request:       12.568 [ms] (mean, across all concurrent requests)
Transfer rate:          39.78 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       10   57  49.5     13     122
Processing:    11   60  49.9     17     139
Waiting:       10   58  49.9     15     138
Total:         21  118  99.4     28     245

Percentage of the requests served within a certain time (ms)
  50%     28
  66%    212
  75%    218
  80%    221
  90%    235
  95%    242
  98%    244
  99%    245
 100%    245 (longest request)

レスポンスヘッダ

参考までにレスポンスヘッダも。Railsのほうはすこし付加されています。

Rails
Cache-Control:no-cache
Connection:close
Content-Length:96
Content-Type:text/html; charset=utf-8
Date:Fri, 23 Dec 2011 16:07:36 GMT
Location:http://example.com/
Server:Apache/2.2.16 (Amazon)
Status:302
X-Powered-By:Phusion Passenger (mod_rails/mod_rack) 3.0.7
X-Runtime:0.068700
X-UA-Compatible:IE=Edge,chrome=1
Apache
Connection:close
Content-Length:302
Content-Type:text/html; charset=iso-8859-1
Date:Fri, 23 Dec 2011 16:12:57 GMT
Location:http://example.com/
Server:Apache/2.2.16 (Amazon)

開発効率

サーバーとしてのパフォーマンスはApacheが優れていますが、開発も含めて考えると、Railsにも利点があります。まず、記述が簡潔かつ柔軟なこと。jpmobileの判定メソッドなら携帯キャリアを羅列する必要はありませんし、params[:controller]やparams[:action]を使ったりもできます。それから、テストがしやすいこと。他と同じようにRSpec/Capybaraのintegration testで書けるのは保守もしやすいのではないかと思います*1

ステータスコード

ちなみに、

デスクトップ用ページから従来のモバイル端末用ページにGooglebot-Mobileをリダイレクトするなら、301か302のどちらを使っても構わない

携帯サイトへのリダイレクトは301でも302でもOK & パンダ・アップデートはモバイル検索にも導入 | 海外SEO情報ブログ

とのことです。UAの条件のもとで恒久的なものなので、個人的には301かなと思う次第です。


4774142239
Apacheポケットリファレンス (POCKET REFERENCE)

関連記事

*1Apacheでもできなくはないですが、外との通信となるためcapybara-webkitを使うなど工夫が必要です

2011-08-24

DropboxをインストールせずにDropboxにバックアップ

コマンドラインからDropboxへのアップロードを可能にするシェルスクリプトです。通常Dropboxは(Linuxでも)インストールして使うものですが、このツールはファイル1つのみで動作します。

こちらの記事で知りました。現在はバージョンが上がっていたり少しつまずいたりしたので、そのメモです。(※バージョン0.7.1で検証)

使いかた

sh dropbox_uploader.sh -u account@example.com -p password -f targetfile_or_dir -d /path/to/uploaddir -v
ハードコーディング
LOGIN_EMAIL="account@example.com"
LOGIN_PASSWD="password"

dropbox_uploader.shに直接記述すればアカウントとパスワードのオプションは不要になります。

正しいのにログインできない場合

アカウントまたはパスワードの記号がURLエンコードされないのが原因の可能性があります。スクリプトではcURLの通信を行っていおり、パラメータの生成時にURLエンコードが必要です。Gmailでエイリアス('+'記号)を使っていたりするとアウトです。

回避するにはアカウントおよびパスワードにURLエンコード済みの文字列を指定する必要があります。

スクリプト内でURLエンコード

ファイルにパスワード書きたくない&URLエンコード面倒な場合は以下のようにします。

function urlencode
{
	echo $1 | nkf -MQ | tr = %
}

curl -s -i -c $COOKIE_FILE -o $RESPONSE_FILE --data "login_email=`urlencode $LOGIN_EMAIL`&login_password=`urlencode $LOGIN_PASSWD`&t=$TOKEN" "$LOGIN_URL"
  • nkfをインストール
  • urlencode関数を追記
  • 232行目あたりを編集

その他

PR

4798123722
Dropbox WORKING

関連記事

2011-07-14

Apache CGIでRVMのRubyを動かす

簡単なRubyスクリプトをWebで表示したいと思い、調べてみました。

環境

  • CentOS 5.6
  • Apache 2.2.3
  • RVM 1.6.22
    • システムワイドにインストールしたもの*1
  • Ruby 1.9.2p180

Apache設定

LoadModule cgi_module modules/mod_cgi.so
AddHandler cgi-script .cgi
<Directory "/path/to/htdocs">
	Options +ExecCGI
</Directory>
  • mod_cgiを有効にする
  • AddHandler で cgi-script を利用したい拡張子で登録する
  • 対象ディレクトリのOptionsで ExecCGI を有効にする
  • VirtualHostでも可能
  • 保存して設定再読込
sudo /etc/init.d/httpd reload

RubyのCGIスクリプト

/path/to/htdocs/index.cgi

#!/usr/local/rvm/rubies/default/bin/ruby
print "Content-Type: text/html\n\n"

print <<HTML
<html>
<head>
</head>
<body>
Hello Ruby!
</body>
</html>
HTML
  • 1行目にRuby本体のファイルを指定
    • /usr/local/rvm/bin/ruby はbashスクリプトなので動かない
    • rubies/default のほうにしてバージョン変更に対応しやすく
  • 最初にContent-Typeと改行2つを出力
  • そのあとにHTMLなどのコンテンツを出力
  • 日本語を扱う場合は2行目に「# coding: utf-8
  • Apache実行ユーザーに対して実行権限追加
sudo chmod g+x index.cgi #groupがapacheの場合

ブラウザからindex.cgiにアクセスして表示されればokです。

Appendix

拡張子.rb で動かしたい場合
AddHandler cgi-script .cgi .rb
拡張子なしで動かしたい場合
<Files "filename">
	SetHandler cgi-script
</Files>
実行権限がないエラーログ

(13)Permission denied: exec of '/path/to/htdocs/index.cgi' failed

Rubyの指定がおかしいエラーログ

(8)Exec format error: exec of '/path/to/htdocs/index.cgi' failed

参考リンク

4873113814
Apacheクックブック 第2版 ―Webサーバ管理者のためのレシピ集

4873113946
プログラミング言語 Ruby

関連記事

*1:関連記事参照

2011-06-23

lsyncd2系で複数サーバーにリアルタイム同期

同期元でファイルの変更を検知してリアルタイムミラーリングできます。2.0系から設定ファイルがXMLでなくLUAという言語のものになったようです。

環境

構成

同期先サーバー

rsyncdで待ち受け
  • rsyncd設定
sudo vim /etc/rsyncd.conf
[application]
path = /path/to/application
log file = /tmp/rsyncd.log
uid = apache
gid = apache
read only = no
  • rsyncd起動
sudo rsync --daemon
rsyncd自動起動
  • xinetdインストール
sudo yum install xinetd
  • xinetdのrsync設定
sudo vim /etc/xinetd.d/rsync
service rsync
{
        disable = no
        socket_type     = stream
        wait            = no
        user            = root
        server          = /usr/bin/rsync
        server_args     = --daemon
        log_on_failure  += USERID
}
  • disableをnoにする
  • xinetdによる起動
sudo /etc/init.d/xinetd start
  • 自動起動ON
sudo chkconfig xinetd on

同期元サーバー

lsyncdインストール
#必要なパッケージ
sudo yum install make gcc pkgconfig lua-devel
#lsyncd DL&展開
wget http://lsyncd.googlecode.com/files/lsyncd-2.0.4.tar.gz
tar xzf lsyncd-2.0.4.tar.gz
#インストール
cd lsyncd-2.0.4
./configure
make && sudo make install
lsyncd設定
sudo vim /etc/lsyncd.conf
settings = {
   logfile    = "/tmp/lsyncd.log", --ログファイル
   statusFile = "/tmp/lsyncd.status", --同期ファイル状態
   nodaemon   = false, --デーモンモードON/OFF
}

sync {
    default.rsync, --同期コマンド種別
    source="/path/to/source", --同期元
    target="xxx.xxx.xxx.xxx::application", --同期先
    rsyncOps="-ltus" --rsync上書きオプション
}

--[[
sync {
    default.rsync,
    source="/path/to/source",
    target="xxx.xxx.xxx.xxx::application",
    rsyncOps="-ltus"
}
]]
  • settingsに設定記述
  • syncに同期の情報記述
  • defalut.rsyncの動作
/usr/bin/rsync -ltsd --delete --include-from=- --exclude=* SOURCE TARGET
  • AWSの場合は同期先にPrivate IPを記述
  • 複数サーバー対応するにはsyncの定義を複数記述
  • --[ [ から ] ] まではコメントアウト
lsyncd起動
#動作チェック
lsyncd -rsync /path/to/source xxx.xxx.xxx.xxx::destmodule
#通常起動
lsyncd /etc/lsyncd.conf

使ってみて

  • ファイル変更から同期開始までは数秒の遅延がある
  • Webサーバー追加時は設定追記してlsyncd再起動必要
  • Webサーバーのキャッシュクリアしたい場合
  • オートスケーリングとの共存は相当困難な気がする
    • オートスケーリング前提なら管理サーバーでrsyncd動かしてWeb側の初期スクリプトで取りにいくのが良さげ

関連リンク


4774145017
プロのための Linuxシステム構築・運用技術 (Software Design plus)

4774146005
サーバ/インフラエンジニア養成読本 [現場で役立つ知恵と知識が満載!] (Software Design plus)

関連記事