ようへいの日々精進XP

よかろうもん

今更だけど capistrano と chef-solo を組み合わせて複数の EC2 インスタンスをセットアップする(1)

はじめに

  • 前回に引き続き capistrano と chef-solo でシリーズ第二弾
  • せっかくなんで EC2 インスタンスもやってみた
  • セットアップ後の確認には前回と同様に serverspec を使う
  • とりあえずメモ

こんな感じで

以下のような事を出来るようにした。

将来的には...

等にも対応する予定。

レシピ

以下のようなレシピでいけた。

require 'aws-sdk'
require 'yaml'
# Please set AWS access_key and secret_access_key and region.
config = YAML.load(File.read("config/deploy/config.yml"))
AWS.config(config)
#
set :local_home, ENV['HOME']
set :remote_home, "/tmp"
set :chef_local_dir, "#{local_home}/chef-repo"
set :chef_remote_dir, "#{remote_home}/chef"
set :user, "#### please set ssh user (ex ec2-user"
set :key, "#### please set ssh-key path (ex path/to/key"
set :ami, "#### plase set ami"
set :instance_type, "#### please set instance type (ex t1.micro"
set :vpc_subnet, "#### please set vpc's subnet"
set :security_group, "#### please set security group id"
set :key_name, "#### please set key name"
set :tag_name, "#### please set tag's value name"
#
servers = AWS.ec2.instances.select {|i| i.tags[:Name] == "#{tag_name}" && i.status == :running}.map(&:dns_name)
role :servers, *servers
#
namespace :ec2 do
  desc "Launch EC2 instances."
  task :launch do
    inst = AWS.ec2.instances.create({
      :image_id => "#{ami}",
      :instance_type => "#{instance_type}",
      :subnet => "#{vpc_subnet}",
      :security_group_ids => "#{security_group}",
      :key_name => "#{key_name}",
    })
    AWS.ec2.tags.create(inst, 'Name',:value => "#{tag_name}")
  end
  task :status do
    puts "#{servers}"
  end
end
#
namespace :chef do
  task :init, :roles => :servers do
    run "#{try_sudo} apt-get update"
    run "#{try_sudo} apt-get -y install sudo curl rsync"
    run "#{try_sudo} curl -L https://www.opscode.com/chef/install.sh | sudo bash"
  end
  task :sync, :roles => :servers do
    run "mkdir -p #{chef_remote_dir}/chef-repo/"
    find_servers_for_task(current_task).each do |server|
      if ( "#{server.port}" == nil )
        `rsync -avz #{chef_local_dir}/ -e "ssh -i #{key} -p #{server.port}" #{user}@#{server.host}:#{chef_remote_dir}/chef-repo/`
      else
        `rsync -avz #{chef_local_dir}/ -e "ssh -i #{key}" #{user}@#{server.host}:#{chef_remote_dir}/chef-repo/`
      end
    end
  end
  task :deploy, :roles => :servers do
    find_servers_for_task(current_task).each do |server|
      run "#{try_sudo} chef-solo -j #{chef_remote_dir}/chef-repo/nodes/localhost.json -c #{chef_remote_dir}/chef-repo/solo.rb"
    end
  end
end

もろもろ、無駄が多いのは否めないがソースはこちら

ポイントは...

config = YAML.load(File.read("config/deploy/config.yml"))
AWS.config(config)

YAML 形式で AWS のキー等の接続情報を書いて外出しにした。これは config/deploy.rbに書いても良いと思われるがコード管理のリポジトリにアップしたくない場合に外部ファイルに持てばコミットしないような設定(.gitignore とか)で不意のアップを防げる。

  desc "Launch EC2 instances."
  task :launch do
    inst = AWS.ec2.instances.create({
      :image_id => "#{ami}",
      :instance_type => "#{instance_type}",
      :subnet => "#{vpc_subnet}",
      :security_group_ids => "#{security_group}",
      :key_name => "#{key_name}",
    })
    AWS.ec2.tags.create(inst, 'Name',:value => "#{tag_name}")
  end

上記でインスタンスの生成とインスタンスのタグを打つ。

servers = AWS.ec2.instances.select {|i| i.tags[:Name] == 'chefsetup' && i.status == :running}.map(&:dns_name)

上記で Chef-solo を実行するインスタンスのホストを取得する。

deploy.rb

上記のレシピ以外でも deploy.rb に以下の設定をする。

require "capistrano/ext/multistage"
require "capistrano_colors"
#
default_run_options[:pty] = true
ssh_options[:keys] = ["/path/to/key"]

使い方

ec2:launch

上記のレシピを hogehoge.rb で保存した場合には以下のようにしてインスタンスを作って起動する。

cap hogehoge ec2:launch

ec2:status

作成したインスタンスが起動しているかを確認するには以下のようにする。

cap hogehoge ec2:status

後は前回と同じように chef:initchef:syncchef:sync でセットアップしていくことになる。


demo

インスタンスを作成

cap aws-setup ec2:launch

f:id:inokara:20140201060017p:plain

確認

インスタンスが出来たかどうだか確認してみよーはいつものように aws-cli を利用する。

aws ec2 describe-instances \
  --filters "Name=tag-value,Values=setup" \
  |jq '@sh "\(.Reservations[].Instances[] | [.InstanceId,.PublicIpAddress,.PrivateIpAddress, .Tags[0].Value])"'

以下のようにインスタンスの IP 等が確認出来た。

f:id:inokara:20140201060119p:plain

せっかくなので ec:status も使ってみる。

f:id:inokara:20140201060417p:plain

まあ、こんな感じで...

インスタンスにて Chef-solo を使ってセットアップする

前回と同様に chef:initChef-solo の実行に必要なアプリケーションのセットアップ、chef:sync でローカルの chef-reporsync を使ってインスタンスにアップロード、最後に chef:deployChef-solo が実行される。

f:id:inokara:20140201061712p:plain

おおっ。嬉しい瞬間。


とりあえず

長くなってしまったので次回はセットアップしたらちゃんと確認ということで serverspec を使って cookbook が適用されているかを確認する。