EC2インスタンス起動後にsshできるまで待つ(若干手抜き)
werckerでインフラCIしようと思って、aws-sdkからEC2インスタンス起動して、knife-soloでsshしようとしたらconnection refusedで弾かれて若干ハマったのでメモ。werckerのビルドこけてもwercker自体にはログイン出来ないので状態確認と切り分け苦労した。
ポイントは元々Aws::EC2::Client#wait_untilで:instance_runningになるの待ってたのだけど、 :instance_runningじゃ不十分で、:instance_status_okまで待たないとネットワーク到達性が確認できないっぽいです。厳密に言うとネットワーク到達性とsshdが起動してるかはまた別問題なのでタイトルは若干不正確なのだけど、Amazon Linuxのt2.microインスタンスで試したかんじでは :instance_running(40秒ぐらい) < sshd起動(1分半ぐらい) < :instance_status_ok(3〜4分)というかんじ。環境によるとは思うが。厳密にやるなら自分でsshできるまでループ書いてもいいけど、wait_untilで:instance_status_okを待っておくのがお手軽なかんじではある。wait_untilの書き方参考までにコード貼っとく。
require 'aws-sdk' # set environments puts "initialize client" ec2 = Aws::EC2::Client.new( access_key_id: ENV['AWS_ACCESS_KEY_ID'], secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'], region: ENV['AWS_DEFAULT_REGION'] ) # aws ec2 run-instances options # Note: security_group_ids can not use network_interfaces simultaneously. # So use network_interfaces:groups ec2_options = { dry_run: false, image_id: "ami-18869819", min_count: 1, max_count: 1, key_name: "aws-wercker", # security_group_ids: ["sg-48b7002d"], instance_type: "t2.micro", placement: { availability_zone: "ap-northeast-1a" }, block_device_mappings: [ { device_name: "/dev/xvda", ebs: { volume_size: 8, delete_on_termination: true, volume_type: "gp2" } }, ], monitoring: { enabled: false, }, disable_api_termination: false, network_interfaces: [ { device_index: 0, subnet_id: "subnet-6b55851c", groups: ["sg-48b7002d"], delete_on_termination: true, associate_public_ip_address: true }, ], ebs_optimized: false } # run instance puts "run instance" run_response = ec2.run_instances(ec2_options) puts "#{run_response}" # get instance_id from API response instance_id = run_response.data.instances[0].instance_id puts "INSTANCE_ID=#{instance_id}" # wait until :instance_status_ok # :instance_running is insufficient state for ssh # :instance_status_ok ensures instance network reachability puts "wait for instance_status_ok" ec2.wait_until(:instance_status_ok, instance_ids:[instance_id]) do |w| w.before_attempt do |n| puts "attempt: #{n}" end end # get public ip address puts "describe instance" describe_response = ec2.describe_instances(instance_ids: [instance_id]) puts "#{describe_response}" target_ip = describe_response.data.reservations[0].instances[0].public_ip_address puts "TARGET_IP=#{target_ip}" # output result to file File.open('ec2.env', 'w') do |file| file.puts "export TARGET_IP=#{target_ip}" file.puts "export INSTANCE_ID=#{instance_id}" end
参考