aws cli ＋ resque ＋ daemon-spawn で EC2 インスタンスを起動・停止

## 概要

- resque に「起動してね」「停止してね」というキューを積むと EC2 インスタンスを起動・停止する
- ↑ 用のプロセスは daemon-spawn で常駐化
- EC2 インスタンスの起動・停止は AWS CLI を利用して実現する

## AWS CLI のインストール

```
$ sudo easy_install pip 
$ sudo pip install awscli 
$ aws configure --profile my_profile
AWS Access Key ID [None]: AKIAYB7....
AWS Secret Access Key []: q-SLuyqi+oV....
Default region name []: ap-northeast-1
Default output format []: ※何も入力せず Enter

※事前に EC2 操作用の IAM を作成して、Access Key／Secret Access Key を払い出しておくこと。
```
参考：[第1回　AWS CLIをインストール：インフラ屋のAWSはじめた日記─GUIを捨てよ](http://gihyo.jp/admin/serial/01/awscli_diary/0001)

## 実装

```ruby:app/workers/instaces_manager.rb
EC2_REGION = Settings.aws_ec2.region
CLI_PROFILE = Settings.aws_ec2.profile

class InstancesManager
  # 処理種別
  RUN_INSTANCES      = 1    # 起動
  TERMINATE_INSTANCE = 100  # 停止

  @queue = Settings.resque.manager.queue

  def self.perform(processing_type, options = {})
    begin
      logger = Logger.new(File.join(Rails.root, Settings.resque.manager.log_file))

      # 処理開始
      start_time = Time.now
      logger.info("[PID:#{$$}]: instances_manager start at: #{start_time}")

      # インスタンス起動・停止
      begin
        case processing_type
          when RUN_INSTANCES      then run_instances(options['count'])
          when TERMINATE_INSTANCE then terminate_instance(options['instance_id'])
        end
        logger.info("[PID:#{$$}]: success: processing_type=#{processing_type}, options=#{options}")
      rescue =>e
        logger.error("[PID:#{$$}]: failure: processing_type=#{processing_type}, options=#{options}")
        logger.error(e)
      end

      # 処理終了
      finished_time = Time.now
      logger.info("[PID:#{$$}]: instances_manager finished: #{start_time}(from) - #{finished_time}(to): #{finished_time - start_time}(processing time)")
    rescue =>e
      logger.error(e)
    end
  end

  def self.run_instances(count)
    image_id            = Settings.aws_ec2.xxx.image_id
    instance_type       = Settings.aws_ec2.xxx.instance_type
    key_name            = Settings.aws_ec2.xxx.key_name
    security_group_name = Settings.aws_ec2.xxx.security_group_name

    command = [
        'aws ec2 run-instances',
        '--image-id', image_id,
        '--count', count,
        '--instance-type', instance_type,
        '--key-name', key_name,
        '--security-groups', security_group_name,
        '--region', EC2_REGION,
        '--profile', CLI_PROFILE
    ].join(' ')
    IO.popen(command){}
  end

  def self.terminate_instance(instance_id)
    command = [
        'aws ec2 terminate-instances',
        '--instance-ids', instance_id,
        '--region', EC2_REGION,
        '--profile', CLI_PROFILE
    ].join(' ')
    IO.popen(command){}
  end
end
```

```ruby:bin/instances_manage_worker.rb
#!/usr/bin/env ruby
require File.expand_path('../../config/application', __FILE__)
Rails.application.require_environment!

class InstancesManageDaemon < DaemonSpawn::Base
  def start(args)
    @worker = Resque::Worker.new(Settings.resque.manager.queue)
    @worker.work
  end

  def stop
    # 処理中のjobを完了後、shutdownする
    @worker.try(:shutdown)
  end
end

InstancesManageDaemon.spawn!({
  :working_dir => Rails.root,
  :pid_file => File.join(Rails.root, Settings.resque.manager.pid_file),
  :log_file => File.join(Rails.root, Settings.resque.manager.log_file),
  :sync_log => true,
  :singleton => true
})
```

### インスタンスを起動する

```ruby
Resque.enqueue(InstancesManager, InstancesManager::RUN_INSTANCES, count: 5)
```

### インスタンスを停止する

```ruby
Resque.enqueue(InstancesManager, InstancesManager::TERMINATE_INSTANCE, instance_id: my_instance_id)

def my_instance_id
  command = '/usr/bin/curl "http://169.254.169.254/latest/meta-data/instance-id"'
  IO.popen(command){|io| io.gets}
end
```