ブログ

ryuzeeによるブログ記事。不定期更新

Chef ServerとCapistrano3を組み合わせて自動でデプロイ対象サーバを決める方法

タイトルが長い…

例えばAWS上でAuto Scalingを使っていたり、Disposableなインフラを構築していると、アプリケーションのデプロイ先のIPアドレスやFQDNはなかなか固定できません。 このような状況下でCapistranoを使ってデプロイする場合、毎回デプロイ先のホストの情報を取得して、自分で設定ファイルを書き換えるとかまじありえねー、な感じです。 ここでは簡単にデプロイ先サーバの情報を自動で取得する方法を紹介します。

なぜChef Server経由でアプリケーションをデプロイしないか

アプリケーションのデプロイもChef Server経由でやればいいじゃん?という声も良く聞きますが、個人的にはこれはやらない方が良いと思っています。 Chefはあくまでインフラやミドルウェアをしかるべき状態に収束させるために作られており、アプリケーションのデプロイ用には作られていません。もちろんアプリケーションのデプロイを行うことも可能なのですが、以下のような問題があり、専用のツールの方が圧倒的に楽です。

  1. 結局クックブックの中で、デプロイの処理の流れを延々と書かなければならないので、見通しが悪い
  2. とにかくロールバックしにくい

なお、先日のイベントでも話をしましたが、プロセスを自動化する場合は以下の5つの点に気をつける必要があります。

  1. Rapid 高速であること
  2. Reliable 信頼性があること
  3. Repeatable 繰り返し可能であること
  4. Reduce Risk 結果としてリスクが減ること
  5. Roll Back ロールバック可能であること

Capistrano3とChef Serverの連携

通常Capistrano3では、デプロイ先のステージにあわせて複数の環境定義用のファイルを用意し、その中にデプロイ先の対象サーバを記載します。 例えばこんな感じです(通常複数のロールを記述しますが割愛してます)。

role :web, %w{web01.example.com web02.example.com web03.example.com}

set :ssh_options, {
  user: "ec2-user",
  keys: %w(~/key/ryuzee.pem),
  forward_agent: false,
  auth_methods: %w(publickey) 
}

このままだと、デプロイ先が増えたり、変わったりすると泣けてきます。なので以下のように自動的に取得します(Amazon EC2を使った例です。他の環境ではホスト情報の取得の箇所で適宜書き換えが必要です)。

require 'rubygems'
require 'chef/rest'
require 'chef/search/query'
Chef::Config.from_file(File.expand_path("~/.chef/knife.rb"))
query = Chef::Search::Query.new
result = query.search(
    'node', 
    'roles:web AND chef_environment:staging'
).first rescue []
webservers = result.map(&:ec2).map(&:public_hostname)
role :web, webservers

set :ssh_options, {
  user: "ec2-user",
  keys: %w(~/key/ryuzee.pem),
  forward_agent: false,
  auth_methods: %w(publickey)
}

ちょっと内容を説明しておきましょう。

Chef Serverとの通信設定

まず、これを動作させるためには、Capistranoの実行端末で、Chef Serverと通信できるように設定しておく必要があります。まだ設定していない場合は

knife configure -i

で設定してください。これによって、~/.chef/knife.rb が生成されます。 きちんと設定されたかどうかは

knife cookbook list
knife node list

などのコマンドを叩いた際にクックブックの一覧やノードの一覧が返ってくることで確認してください。

デプロイ先サーバの取得

当たり前ですが、どのサーバがどんな役割を持っているかは予めきちんと管理されていなければいけません(もちろんExcelの管理台帳の話ではまったくありません)。通常Chef ServerではロールとEnvironmentを使ってサーバの役割を管理します。新たなサーバを構築して、初回にブートストラッピングする際に指定可能ですし、後からでも指定可能です。 例えば僕の場合はこんな感じでknife bootstrapコマンドを使って、初回のセットアップの際にロールにwebとbaseという2つのロールを設定し、Environmentはstagingに設定しています。

knife bootstrap 54.238.215.16 -N Web01 -E staging -r 'role[web],role[base]' -x ec2-user -i ~/key/ryuzee.pem --sudo

これによってステージング環境用のwebの役割を持つサーバを構築し、Chef Serverの情報を参照することで、このサーバがどんな用途に使われるか明らかになります。

先ほどのデプロイスクリプトでは、Chef::Search::Queryを使って、ロールがwebでEnvironmentがstagingのサーバ一覧を取得しています。

result = query.search(
        'node', 
        'roles:web AND chef_environment:staging'
).first rescue []

その上で、これらのサーバのpublic_hostnameを自動で取得し、デプロイ先のサーバ情報として設定しています。

webservers = result.map(&:ec2).map(&:public_hostname)
role :web, webservers

なお、この例では外部のネットワークからのデプロイを想定しており、かつAmazon EC2上のノードへのデプロイのため、ohaiによって取得されるAmazon EC2のpublic_hostnameを使っています。 内部ネットワークからのデプロイの場合は当然取得すべき項目が変わりますし、他の環境であれば、eth0のアドレスをそのまま使えば良かったりするかもしれません。自分の環境にあわせてください。

なお、VPC環境の場合は、デフォルトではpublic_hostnameこの値が空になってしまうので、デプロイ先サーバに

/etc/chef/ohai/hints/ec2.json

を作成しておいてください。中身はValidなjsonであれば何でも構いません({}とかでも)。僕はこちらについてもクックブック化して自動で設定するようにしています。

じゃー、頑張って自動化してくださいませ。

アジャイルコーチングやトレーニングを提供しています

株式会社アトラクタでは、アジャイル開発に取り組むチーム向けのコーチングや、認定スクラムマスター研修などの各種トレーニングを提供しています。ぜひお気軽にご相談ください。

詳細はこちら