インスタンスのAMI作成にSQSを使うようにしてみたら微妙だった話
こんにちは@oko_changです。
前回AWS SDK for RubyでSQSを使ってみましたが、お仕事がAWSのインフラ構築をメインとしていることもあり、あまり業務で使う機会がありません。
というわけで今回は日頃の業務に近いインスタンスのAMI作成の仕組みをちょっと強引にSQSを使った仕組みにしてみました。
SQSの使い方をイメージしやすいかなと試しにやっただけなので、実際の業務でこういう構成は組まないと思います。
構成図
流れ
対象サーバのインスタンスをセットアップ
以下のようにテスト環境を用意してあるとして、マウントされているEBSボリュームも含めてバックアップ対象となります。
バックアップする時はアンマウント、バックアアップが完了したら再度マウントします。
$ sudo ln -f /usr/share/zoneinfo/Asia/Tokyo /etc/localtime $ sudo mkfs -t ext4 /dev/sdc $ sudo mkdir /ebs01 $ sudo mount /dev/sdc /ebs01 $ df -h
バックアップ対象サーバ行う処理
# -*- coding: utf-8 -*- ## 設定 require 'net/http' require 'aws-sdk' sqs_region = 'sqs.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com' instance_id = Net::HTTP.get('169.254.169.254', '/latest/meta-data/instance-id') unmounted_instances_queue = "unmountend_instances" ## AWSの認証 sqs = AWS::SQS.new( :sqs_endpoint => sqs_region ).client ## アンマウントされたインスタスIDを管理するキューの接続先を指定 unmounted_instances_queue = sqs.list_queues(:queue_name_prefix => unmounted_instances_queue)[:queue_urls][0] ## 対象のEBSボリュームをアンマウントする unmount_status = `sudo umount /ebs01` ## アンマウントが正常に完了していればキューにメッセージを投げ、そうでなければ終了する if unmount_status.empty? sqs.send_message(:queue_url => unmounted_instances_queue, :message_body => instance_id) else puts "Error check mount status" exit 1 end exit 0
バックアップ(Create Image)を実行するインスタンスの処理
# -*- coding: utf-8 -*- ## 設定 require 'net/http' require 'aws-sdk' sqs_region = 'sqs.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com' ec2_region = 'ec2.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com' unmounted_instances_queue = "unmountend_instances" ## AWSの認証 sqs = AWS::SQS.new( :sqs_endpoint => sqs_region ).client ec2 = AWS::EC2.new( :ec2_endpoint => ec2_region ).client ## アンマウントされたインスタスIDを管理するキューの接続先を指定 unmounted_instances_queue = sqs.list_queues(:queue_name_prefix => unmounted_instances_queue)[:queue_urls][0] ## 上記キューからメッセージを取得します message = sqs.receive_message(:queue_url => unmounted_instances_queue, :wait_time_seconds => 20)[:messages][0] if message.nil? puts "No message available in queue" exit 0 end ## 取得したメッセージの本体からインスタンスIDを取得します instance_id = message[:body] ## 取得したインスタンスID用のバックアップ完了通知のキューを作成します each_instance_queue = sqs.list_queues(:queue_name_prefix => instance_id)[:queue_urls][0] if each_instance_queue.nil? sqs.create_queue(:queue_name => instance_id, :attributes => {"VisibilityTimeout" => "300"}) end ## 取得したインスタンスIDの本日分バックアップが完了しているかチェックします ## 本日分のバックアップがすでに完了している場合はメッセージを削除して完了にします image_name = instance_id + '-' + Time.now.strftime("%Y%m%d") if ec2.describe_images(:owners => ["self"], :filters => [{:name => 'name', :values => [image_name]}])[:images_set].any? puts "AMI is already available" sqs.delete_message(:queue_url => unmounted_instances_queue, :receipt_handle => message[:receipt_handle]) exit 0 end ## 取得したインスタンスIDを指定してAMIを作成します image_id = ec2.create_image(:instance_id => instance_id, :name => image_name, :no_reboot => true,)[:image_id] count = 1 while ec2.describe_images(:image_ids => [image_id], :owners => ["self"])[:images_set][0][:image_state] != "available" sleep(30) count += 1 if count > 10 then puts "Creating AMI was not completed" exit 1 end end ## AMIの作成が完了したら、完了通知用のキューにメッセージを送ります sqs.send_message(:queue_url => each_instance_queue, :message_body => "Creating AMI completed") ## 元のメッセージを削除します sqs.delete_message(:queue_url => unmounted_instances_queue, :receipt_handle => message[:receipt_handle]) puts "Backup job completed" exit 0
再びバックアップ対象サーバで行う処理
# -*- coding: utf-8 -*- ## 設定 require 'net/http' require 'aws-sdk' sqs_region = 'sqs.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com' instance_id = Net::HTTP.get('169.254.169.254', '/latest/meta-data/instance-id') ## AWSの認証 sqs = AWS::SQS.new( :sqs_endpoint => sqs_region ).client ## インスタンス毎のバックアップ完了通知のキューを指定します each_instance_queue = sqs.list_queues(:queue_name_prefix => instance_id)[:queue_urls][0] ## 上記キューに格納されているバックアップ完了のメッセージを取得します message = sqs.receive_message(:queue_url => each_instance_queue, :wait_time_seconds => 20)[:messages][0] if message.nil? puts "no message available in queue" exit 0 end ## 対象のEBSがアンマウントされている状態かチェックします ## すでにマウントされている状態であれば、メッセージを削除します unmount_status = `mount | grep "/ebs01"` if unmount_status.include?("/ebs01") puts "Device is already mounted" sqs.delete_message(:queue_url => each_instance_queue, :receipt_handle => message[:receipt_handle]) exit 0 end ## 対象のEBSボリュームをマウントします mount_status = `sudo mount /dev/sdc /ebs01` ## マウントが正常に完了したかどうかチェックします if mount_status.empty? puts "Mounting completed" else puts "Error check the mount status" exit 1 end ## 取得したメッセージを削除します sqs.delete_message(:queue_url => each_instance_queue, :receipt_handle => message[:receipt_handle]) exit 0