CapistranoでAWS EC2インスタンスをデプロイする時の注意点
前回のデプロイ設定ファイルで新たに作成されたEC2インスタンスには、キーペアやセキュリティグループなどが設定されていなかったため、そのままでは作成したインスタンスにSSHでアクセスできませんでした…1orz
その後、色々とデプロイ設定を修正して、作成したEC2インスタンスにSSHでログインするところまで出来たので、その経緯を備忘録として書いてみた次第。
まぁ、CapistranoでAWSのEC2インスタンスを作成する際…というより、「AWS SDK for Ruby」でEC2インスタンスを作成する時の注意点…に近いのかもしれない。
まず、前回の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 |
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)を自動で割り振られるようにして、インターネットからのアクセスも出来るようにしておくあたりまでが、最小設定となるかと。
では早速、修正してみます。
まずは、必要な設定値を変数として追加します。
1 2 3 4 5 6 7 8 9 |
# インポートするキーペア名 set :key_pair_name, 'mykeypair' # 利用するセキュリティグループID set :security_groups, [ 'sg-********', 'sg-********', 'sg-********' ] # カーネルID set :kernel_id_name, 'aki-********' |
キーペアやセキュリティグループはAWSコンソールで事前に作っておく必要があります。手っ取り早いのは、デプロイ環境としてCapistranoが稼動しているインスタンスのキーペアとセキュリティグループをそのまま利用するのが良いでしょう。カーネルIDは必要に応じて設定します。別になくても問題は出ないので。
そして、ec2クラスのメソッド部は下記のように変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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。
それでは、修正したデプロイ設定で、インスタンスラウンチタスクを実行してみます。
1 2 3 4 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のマネージメントコンソールで確認してみます。
キーペアもセキュリティグループ、PublicIPまで期待通りのEC2インスタンスがラウンチされました。
では、コンソールからSSHが出来ることを確認してみます。
1 2 3 4 5 6 7 8 9 |
$ 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の指定を取り払います。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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 ) |
では、実行します。
1 2 3 4 5 |
$ 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してみます。
1 2 3 4 5 6 7 |
$ 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する手順をまとめてみます。
参考サイト
- 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.”) ↩
- EBSボリュームを利用する場合、HVM対応AMIは利用できなくなります。そのため、作成するインスタンスタイプはHMV専用の「t2.micro」ではなく「t1.micro」等へ変更する必要があります。 ↩
-
挙動を試してはいませんが、キーペアをインポートすることもできるようです。その場合の記述は
:key_pair => ec2.key_pairs.import(fetch(:key_pair_name), File.read('~/.ssh/identity.pub')),
のようになるのでしょうか?(未検証) ↩ - セキュリティグループを指定しないと、デフォルトのグループのみが設定され、同じVPCネットワークからしかアクセスできなくなります。 ↩
- デフォルト設定値がFALSEなので、この設定を入れないとPublicDNSも有効になりません。 ↩