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

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

参考リンク

トラックバック - http://d.hatena.ne.jp/deeeki/20121002/capistrano_apache_recipe