Capistranoで異なるAvilabilityZoneにEC2インスタンスをラウンチする
前回、
次回はデータベースの作成や初期設定を直前タスクとして挿入して、さらにWordPressの設定ファイルの書き換え、GitHubからWordPressテーマをダウンロードするところまでやってみようと思います。
とか云っていたのですが、都合によりCapistrano3を使ってAWSのEC2インスタンス作成を行ったので、そのTIPSを残しておこうかと思います。
今回は、デプロイ用のEC2インスタンスから公式のAMIを利用してAvailabilityZonesが異なるエリアにそれぞれEC2インスタンスを立ててみました。試験的に、AMIは最新のインスタンスタイプ「t2.micro」に対応した「Amazon Linux AMI 2014.03.2 (HVM)」を利用して、Tokyoリージョンの1aと1cのAvailabilityZoneにそれぞれ2台ずつ、合計4インスタンスを同時に立ててみます。
※ 事前準備として、インスタンスを立てるAWSアカウントにてVPCやSubnet等EC2インスタンスを作成する上で必要最小限の設定をしておく必要があります。特に今回は異なるAvailabilityZoneへのインスタンスを立てるのでそれぞれのゾーンにSubnetを設定しておく必要があります。
さて、早速デプロイの手順です。
はじめに、デプロイ環境(サーバ)の設定を行うため、config/deploy/test.rb
を編集します。
今回は動的に作成したEC2インスタンスをデプロイ環境とするため、事前のサーバ設定ができません。というのも、AWSで立ち上がる新規インスタンスにはElasticIPを付与しないので、毎回PublicIPが異なり、エンドポイントURLも動的に変わってしまうためです。なので、今回はインスタンスが起動した後にCapistoranoのタスクにて対象サーバを設定するようにします。
初期のサーバ設定は不要となるので、記述をコメントアウトしておきます。なお、SSHのオプションだけは最終的に共通で利用する予定なので残しておきます(今回のデプロイタスクでは使いませんが…)。
1 2 |
$ vim config/deploy/test.rb |
1 2 3 4 5 6 7 8 9 |
#role :app, %w{deploy@localhost} #role :web, %w{deploy@localhost} #role :db, %w{deploy@localhost} set :ssh_options, { keys: %w(/home/deploy/.ssh/id_rsa), forward_agent: true, } |
今回のデプロイでは、「AWS SDK for Ruby」を利用するので、あらかじめSDKをインストールしておきます。
1 2 |
$ sudo gem install aws-sdk |
次に、config/deploy.rb
にデプロイ内容を設定します。
1 2 |
$ vim config/deploy.rb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# config valid only for Capistrano 3.1 lock '3.2.1' # AWS SDK for Ruby を読み込む require 'aws-sdk' # AWS SDK用の設定 AWS.config({ :access_key_id => '<AWSアカウントのACCESS KEY ID>', :secret_access_key => '<AWSアカウントのSECRET ACCESS KEY>', :region => 'ap-northeast-1', # EC2 Instanceを作成するリージョン }) # AMIのimage_id # Amazon Linux AMI 2014.03.2 (HVM) set :ami_image_id, 'ami-29dc9228' # 作成するInstance数 set :instance_count, 4 # 作成するInstanceタイプ set :ec2_instance_type, 't2.micro' # 作成先のAvailability Zones set :availability_zones, [ 'ap-northeast-1a', 'ap-northeast-1c' ] # 作成先のsubnet_id set :subnet_ids, [ 'subnet-********', 'subnet-********' ] # Capistranoデフォルトのタスクを削除する framework_tasks = [:starting, :started, :updating, :updated, :publishing, :published, :finishing, :finished] framework_tasks.each do |t| Rake::Task["deploy:#{t}"].clear end Rake::Task[:deploy].clear desc 'Launch an EC2 instance to each availability zone different' task :launch do run_locally do ec2 = AWS::EC2.new created_instances = [] cnt = 0 while cnt < fetch(:instance_count) do if cnt.even? then current_az = fetch(:availability_zones)[0] current_sn = fetch(:subnet_ids)[0] else current_az = fetch(:availability_zones)[1] current_sn = fetch(:subnet_ids)[1] end i = ec2.instances.create( :image_id => fetch(:ami_image_id), :availability_zone => current_az, :subnet => current_sn, :instance_type => fetch(:ec2_instance_type), :count => 1 ) sleep 10 while i.status == :panding created_instances << i.id cnt += 1 end execute "echo -n #{created_instances} > ~/CREATED_INSTANCES" end end desc 'Check the activation status of new instances' task :check do created_instances_list = 'CREATED_INSTANCES' run_locally do ec2 = AWS::EC2.new begin if test "[ -f ~/#{created_instances_list} ]" created_instances = capture("cd ~; cat #{created_instances_list}").chomp ci = created_instances.gsub(/(\[|\s|\])/, '').split(',') target_instances = ec2.instances.select { |i| i.exists? && i.status == :running && ci.include?(i.id) }.map(&:private_ip_address) if target_instances.length == 0 then raise "No created instances" end target_instances.each { |var| server var, user: 'ec2-user', roles: %w{web app} } end rescue => e info e exit end end end task :deploy => :check do run_locally do info roles(:all) info 'Next task of deploy on new instances' # deploy start end end |
実際にインスタンスのラウンチをしてみる。
1 2 3 4 5 |
$ cap test launch INFO[10af60ae] Running /usr/bin/env echo -n ["i-7497be72", "i-01c31718", "i-4d94bd4b", "i-00c31719"] > ~/CREATED_INSTANCES on localhost DEBUG[10af60ae] Command: echo -n ["i-7497be72", "i-01c31718", "i-4d94bd4b", "i-00c31719"] > ~/CREATED_INSTANCES INFO[10af60ae] Finished in 0.003 seconds with exit status 0 (successful). |
上記のようなログが出力され、インスタンスのラウンチは完了したようなので、AWSのマネージメント・コンソール側で確認してみます。
確かに4つのインスタンスが、交互に別々のAvailabilityZonesで起動していました。理想どおりの動きです。
なお、AWSのインスタンスが起動する時間は環境によってまちまちのため、上記の「launch」タスクはEC2インスタンスの起動状況までは確認せずに終了させています。ただ、このままだとCapistoranoの次のタスクが起動した際にインスタンスの情報が引き継げないので、「launch」タスクで起動したインスタンスのIDを「CREATED_INSTANCES」というファイルに書き出して終了させています。
次のタスクでは、この「CREATED_INSTANCES」に書き出されているインスタンスIDを拾って、インスタンスの起動状況をチェックし、ステータスが「:running」であれば、デプロイ先のサーバとして設定を行うようにしています。
また、「check」タスクではステータスが「:running」である新規インスタンスのプライベートIPアドレスを取得して、それをデプロイ先サーバのホスト名として利用していますが、新インスタンスにパブリックIPアドレスが割り当てられてパブリックDNSが設定されているのであれば、ここを{}.map(&:dns_name)
と名前引きにしても大丈夫です。
後続タスクを実行してみます。
1 2 3 4 5 6 7 8 9 10 11 |
$ cap test deploy DEBUG[79c503fb] Running /usr/bin/env [ -f ~/CREATED_INSTANCES ] on localhost DEBUG[79c503fb] Command: [ -f ~/CREATED_INSTANCES ] DEBUG[79c503fb] Finished in 0.003 seconds with exit status 0 (successful). DEBUG[9dd1f93d] Running /usr/bin/env cd ~; cat CREATED_INSTANCES on localhost DEBUG[9dd1f93d] Command: cd ~; cat CREATED_INSTANCES DEBUG[9dd1f93d] [i-7497be72, i-01c31718, i-4d94bd4b, i-00c31719] DEBUG[9dd1f93d] Finished in 0.002 seconds with exit status 0 (successful). INFO[#<Capistrano::Configuration::Server:0x0000000189a7c8 @user="ec2-user", @hostname="176.34.61.80", @port=nil, @properties=#<Capistrano::Configuration::Server::Properties:0x00000001899238 @properties={}, @roles=#<Set: {:web, :app}>>>, #<Capistrano::Configuration::Server:0x000000018a3a08 @user="ec2-user", @hostname="176.34.62.168", @port=nil, @properties=#<Capistrano::Configuration::Server::Properties:0x000000018a3080 @properties={}, @roles=#<Set: {:web, :app}>>>, #<Capistrano::Configuration::Server:0x000000018a0d58 @user="ec2-user", @hostname="176.34.62.75", @port=nil, @properties=#<Capistrano::Configuration::Server::Properties:0x000000018a6910 @properties={}, @roles=#<Set: {:web, :app}>>>, #<Capistrano::Configuration::Server:0x000000018a8ee0 @user="ec2-user", @hostname="176.34.61.75", @port=nil, @properties=#<Capistrano::Configuration::Server::Properties:0x000000018b3818 @properties={}, @roles=#<Set: {:web, :app}>>>] INFONext task of deploy on new instances |
前タスクで作成したインスタンスの情報を引き継いで、無事にデプロイ先のサーバとして設定されました。
この後のタスク(今回の例では「deploy」タスク)で、各新規インスタンスに対してApacheやPHP、MySQLのインストールを行えば、晴れて前回のWordPressのデプロイに繋げられるようになります。
最終的には、Capistranoのコマンドを2~3回入力するだけで、サーバ作成からWordPressデプロイまで一気に出来てしまうという夢のようなデプロイ環境が作れそうです。