CapistranoでAWS EC2インスタンスをデプロイする時の注意点

前回のデプロイ設定ファイルで新たに作成されたEC2インスタンスには、キーペアセキュリティグループなどが設定されていなかったため、そのままでは作成したインスタンスにSSHでアクセスできませんでした…1orz

その後、色々とデプロイ設定を修正して、作成したEC2インスタンスにSSHでログインするところまで出来たので、その経緯を備忘録として書いてみた次第。 まぁ、CapistranoでAWSのEC2インスタンスを作成する際…というより、「AWS SDK for Ruby」でEC2インスタンスを作成する時の注意点…に近いのかもしれない。

まず、前回のconfig/deploy.rbからインスタンスラウンチのタスク部分を見てみる。

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

作成するインスタンスに対して、AvailabilityZoneとSubnetの設定しかしていないので、そりゃあアクセス不能になります。最低限、セキュリティグループを設定して、外部からのアクセス経路を確保して、キーペアを設定して認証ユーザがログイン可能にしてあげる必要はあります。あとは、PublicIP(PublicDNS)を自動で割り振られるようにして、インターネットからのアクセスも出来るようにしておくあたりまでが、最小設定となるかと。

では早速、修正してみます。 まずは、必要な設定値を変数として追加します。

# インポートするキーペア名
set :key_pair_name, 'mykeypair'

# 利用するセキュリティグループID
set :security_groups, [ 'sg-********', 'sg-********', 'sg-********' ]

# カーネルID
set :kernel_id_name, 'aki-********'

キーペアやセキュリティグループはAWSコンソールで事前に作っておく必要があります。手っ取り早いのは、デプロイ環境としてCapistranoが稼動しているインスタンスのキーペアとセキュリティグループをそのまま利用するのが良いでしょう。カーネルIDは必要に応じて設定します。別になくても問題は出ないので。 そして、ec2クラスのメソッド部は下記のように変更します。

      i = ec2.instances.create(
        :image_id => fetch(:ami_image_id),
        :block_device_mappings => [{
          :device_name => '/dev/sda1',
          :ebs => {
            :volume_size => 50,
            :delete_on_termination => true
          }
        }],
        :monitoring_enabled => false,
        :availability_zone => current_az,
        :subnet => current_sn,
        :key_name => fetch(:key_pair_name),
        :security_group_ids => fetch(:security_groups),
        :kernel_id => fetch(:kernel_id_name),
        :disable_api_termination => true,
        :instance_type => fetch(:ec2_instance_type),
        :count => 1,
        :associate_public_ip_address => true
      )

新たに追加したメソッド・オプションは下記の通りです。

  • :block_device_mappings EBS(ELASTIC BLOCK STORE)の設定です。インスタンス固定のストレージ容量に追加が必要であれば、追記します。この例では、50GBのストレージボリュームを追加しています2
  • :monitoring_enabled インスタンス起動後にCloudWatchのモニタリングを開始する場合はTRUEにします。
  • :key_name … 必須設定値です。キーペア名を指定します3。ここでは先に定義した変数を呼んでいます。
  • :security_group_ids 必須設定値です。すでに定義済みのセキュリティグループIDを配列形式で記述します4。ここでは先に定義した変数を呼んでいます。
  • :kernel_id カーネルIDを使用する場合に指定します。同じカーネルIDでHMV対応AMIから複数インスタンスを作成しようとするとエラーになるので、その場合は指定しない。
  • :disable_api_termination EC2 APIを利用してインスタンスを終了させることを可能にする場合はTRUEにします。
  • :associate_public_ip_address 重要設定値です。作成されたインスタンスにPublicIPアドレスを自動割り当てさせたい場合はTRUEにします5

それでは、修正したデプロイ設定で、インスタンスラウンチタスクを実行してみます。

$ cap test launch
INFO[6866d5d4] Running /usr/bin/env echo -n ["i-70f0cf76", "i-f718c1ee"] > ~/CREATED_INSTANCES on localhost
DEBUG[6866d5d4] Command: echo -n ["i-70f0cf76", "i-f718c1ee"] > ~/CREATED_INSTANCES
INFO[6866d5d4] Finished in 0.034 seconds with exit status 0 (successful).

※ ちなみに、Capistranoのタスクのデバッグを行いたい場合は、cap test launch debugとかcap test launch --traceとオプションを付けてタスクを実行すると詳しく処理をトレースしてくれるので覚えておくと便利です。

AWSのマネージメントコンソールで確認してみます。

スクリーンショット1 スクリーンショット2

キーペアもセキュリティグループ、PublicIPまで期待通りのEC2インスタンスがラウンチされました。 では、コンソールからSSHが出来ることを確認してみます。

$ ssh default-user@176.34.61.218
The authenticity of host '176.34.61.218 (176.34.61.218)' can't be established.
RSA key fingerprint is **:**:**:**:**:**:**:**:**:**:**:**:**:**:**:**.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '176.34.61.218' (RSA) to the list of known hosts.
default-user@176.34.61.218's password:
Last login: Tue May 20 06:47:06 2014 from ******.******.******.*****.ne.jp
[default-user@ip-176-34-61-218 ~]$

入れました。ただし、今回の例では、すでにSSHアクセスができる「default-user」というユーザを事前に作成してあったAMIからインスタンスを作成したので、すんなりログインできましたが、前回の例のようにAWS公式のAMIからインスタンスを作成した場合は、もう一手間かかります。では、前回同様に「Amazon Linux AMI 2014.03.2 (HVM)」の公式AMIを利用して、「t2.micro」にインスタンスを作成するタスクに修正してみます。HVM対応AMIからインスタンスラウンチする際にエラーになる、EBS設定とカーネルIDの指定を取り払います。

      i = ec2.instances.create(
        :image_id => fetch(:ami_image_id),
        :monitoring_enabled => false,
        :availability_zone => current_az,
        :subnet => current_sn,
        :key_name => fetch(:key_pair_name),
        :security_group_ids => fetch(:security_groups),
        :disable_api_termination => true,
        :instance_type => fetch(:ec2_instance_type),
        :count => 1,
        :associate_public_ip_address => true
      )

では、実行します。

$ cap test launch
INFO[520beb79] Running /usr/bin/env echo -n ["i-abd4ebad", "i-580fd641"] > ~/CREATED_INSTANCES on localhost
DEBUG[520beb79] Command: echo -n ["i-abd4ebad", "i-580fd641"] > ~/CREATED_INSTANCES
INFO[520beb79] Finished in 0.035 seconds with exit status 0 (successful).

AWSマネージメントコンソールで作成されたインスタンスIDからPrivertIPを確認したら、コンソールからAmazon Linux系AMIのデフォルトユーザ「ec2-user」でSSHしてみます。

$ ssh ec2-user@176.34.61.153
The authenticity of host '176.34.61.153 (176.34.61.153)' can't be established.
ECDSA key fingerprint is **:**:**:**:**:**:**:**:**:**:**:**:**:**:**:**.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '176.34.61.153' (ECDSA) to the list of known hosts.
Permission denied (publickey).

当たり前ですが、接続は出来るものの、公開鍵認証でこけてアクセス出来ませんでした。プライベートキーをssh -iのオプションで添付して認証しないと接続はできません。つまり、作成したインスタンスに対してCapistranoで次のタスクを実行するためには、Capistranoの稼動しているデプロイサーバ側に事前にインスタンス作成に使うプライベートキーを準備しておく必要があります。そして、新規インスタンスへの初回のSSHではそのプライベートキーを使ってログインを行うという流れになるはずです。 ひとまず本項はここまでとします。次回は、インスタンスラウンチ直後のタスクとして、Capistrano経由で新インスタンスにSSHする手順をまとめてみます。

参考サイト


  1. SDKのドキュメントに注意が書いてあった(以下原文":key_name (String) — The name of the key pair to use. Note: Launching public images without a key pair ID will leave them inaccessible.") 

  2. EBSボリュームを利用する場合、HVM対応AMIは利用できなくなります。そのため、作成するインスタンスタイプはHMV専用の「t2.micro」ではなく「t1.micro」等へ変更する必要があります。 

  3. 挙動を試してはいませんが、キーペアをインポートすることもできるようです。その場合の記述は :key_pair => ec2.key_pairs.import(fetch(:key_pair_name), File.read('~/.ssh/identity.pub')), のようになるのでしょうか?(未検証) 

  4. セキュリティグループを指定しないと、デフォルトのグループのみが設定され、同じVPCネットワークからしかアクセスできなくなります。 

  5. デフォルト設定値がFALSEなので、この設定を入れないとPublicDNSも有効になりません。