awscliを使ってIAMユーザーを作成・削除する
AWSのIAMアカウントを複数作るときにManagement Consoleで作業をすると時間もかかってしまいますし、ミスも多くなります。
そういうときはコマンドラインツールを使ったりSDKを使った方が楽が出来るので、IAMユーザーの作成手順を残しておきます。
作成手順
ユーザーを作成する
$ aws iam create-user --user-name foo { "User": { "UserName": "foo", "Path": "/", "CreateDate": "2014-01-22T15:37:34.673Z", "UserId": "AIDAJDSTS6NKEGLHA53L4", "Arn": "arn:aws:iam::12345678910:user/foo" } }
IAMユーザーにパスワードを作成
$ aws iam create-login-profile --user-name foo --password foo-password { "LoginProfile": { "UserName": "foo", "CreateDate": "2014-01-22T15:40:44.953Z" } }
アクセスキーとシークレットキーを作成
$ aws iam create-access-key --user-name foo { "AccessKey": { "UserName": "foo", "Status": "Active", "CreateDate": "2014-01-22T15:43:33.530Z", "SecretAccessKey": "*******************************", "AccessKeyId": "AKIAJWBY7Z4AUQQEOHPA" } }
グループを作成
$ aws iam create-group --group-name foo-group { "Group": { "Path": "/", "CreateDate": "2014-01-22T15:44:57.967Z", "GroupId": "AGPAIFQSBSRZLEC4Q3JVK", "Arn": "arn:aws:iam::12345678910:group/foo-group", "GroupName": "foo-group" } }
グループにポリシーを割り当てる
$ aws iam put-group-policy --group-name foo-group --policy-document file://./user_foo_policy.json --policy-name foo-policy
グループにユーザーを割り当てる
$ aws iam add-user-to-group --user-name foo --group-name foo-group
削除
グループからユーザーを外す
$ aws iam remove-user-from-group --user-name foo --group-name foo-group
グループポリシーの削除
$ aws iam delete-group-policy --group-name foo-group --policy-name foo-policy
グループの削除
$ aws iam delete-group --group-name foo-group
アクセスキーとシークレットキーの削除
$ aws iam delete-access-key --user-name foo --access-key AKIAJWBY7Z4AUQQEOHPA
ログインパスワードの削除
$ aws iam delete-login-profile --user-name foo
ユーザーの削除
$ aws iam delete-user --user-name foo
AMIを作成し、インスタンスの情報をSimpleDBに保存するスクリプト
こんにちは@oko_changです。
年始のブログでAMIを作成時にインスタンスの情報をタグに保存するというのを書いていました。
さらに、前回AWS SDK for RubyでSimpleDBを操作する方法をまとめたので、今回はインスタンスの情報をSimpleDBに保存するのを書いておきます。
スクリプト
こんな感じのスクリプトを作りました。
ENIとかSecondary IPをどうしたものかという感じ。
# -*- coding: utf-8 -*- require 'net/http' require 'aws-sdk' ## 自分自身の情報を取得する ec2_region = 'ec2.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com' sdb_region = 'sdb.' + 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') image_name = instance_id + '-' + Time.now.strftime("%Y%m%d%H%M") domain_name = 'ami_backup' description = 'automatically generated image' generation = 3 class Ec2Base def initialize(ec2_region) @ec2 = AWS::EC2.new(ec2_endpoint: ec2_region).client end def get_instance_datas(instance_id) @ec2.describe_instances(instance_ids: ["#{instance_id}"])[:instance_index][instance_id] end def get_api_termination(instance_id) @ec2.describe_instance_attribute( :instance_id => instance_id, :attribute => "disableApiTermination" )[:disable_api_termination][:value] end def get_shutdown_behavior(instance_id) @ec2.describe_instance_attribute( :instance_id => instance_id, :attribute => "instanceInitiatedShutdownBehavior" )[:instance_initiated_shutdown_behavior][:value] end def create_image(instance_id, image_name, description) puts "Start image creation" @ec2.create_image( :instance_id => instance_id, :name => image_name, :description => description, :no_reboot => true )[:image_id] end def image_status(image_id) 30.times do if @ec2.describe_images(image_ids: ["#{image_id}"])[:images_set][0][:image_state] == "available" puts "Image status became availabled" break else sleep 10 end end end def get_deleted_image(instance_id, generation) image_list = @ec2.describe_images( :owners => ["self"], :filters => [{name: 'name', values: ["#{instance_id}" + "-*"]}] )[:images_set].sort { |a,b| b[:name] <=> a[:name] } delete_target = image_list[generation.to_i, image_list.length] end def delete_image(deleted_image_ids) unless deleted_image_ids.nil? puts "Remove old images" deleted_image_ids.each do |image| @ec2.deregister_image(image_id: image[:image_id]) image[:block_device_mapping].each do |device| @ec2.delete_snapshot(snapshot_id: device[:ebs][:snapshot_id]) end end else puts "No image to be deleted" exit 0 end end end class SimpledbBase def initialize(sdb_region) @sdb = AWS::SimpleDB.new(simple_db_endpoint: sdb_region).client end def put_data_to_simpledb(image_id, insert_datas, domain_name) @sdb.put_attributes( :domain_name => domain_name, :item_name => image_id, :attributes => [ { name: "instance_type", value: insert_datas[:instance_type]}, { name: "subnet_id", value: insert_datas[:subnet_id]}, { name: "local_ip_address", value: insert_datas[:local_ip_address] }, { name: "key_name", value: insert_datas[:key_name] }, { name: "security_group_ids", value: insert_datas[:security_group_ids].join(" ") }, { name: "source_dest_check", value: insert_datas[:source_dest_check].to_s}, { name: "disable_api_termination", value: insert_datas[:disaable_api_termination].to_s }, { name: "monitoring", value: insert_datas[:monitoring] }, { name: "iam_instance_profile", value: insert_datas[:iam_instance_profile] }, { name: "tag_set", value: insert_datas[:tag_set].join(" ") }, { name: "instance_initiated_shutdown_behavior", value: insert_datas[:instance_initiated_shutdown_behavior].to_s }, { name: "vpc_id", value: insert_datas[:vpc_id] } ]) puts "Putted tags to simpledb" end def delete_data_from_simpledb(domain_name, deleted_image_ids) unless deleted_image_ids.nil? puts "Removed old image's item from simpledb" deleted_image_ids.each do |image| @sdb.delete_attributes(domain_name: domain_name, item_name: image[:image_id]) end else puts "No image to be deleted" exit0 end end end # AMIのタグに必要な情報を取得する ec2 = Ec2Base.new(ec2_region) insert_datas = Hash.new instance_datas = ec2.get_instance_datas(instance_id) security_group_ids = [] insert_datas[:instance_type] = instance_datas[:instance_type] insert_datas[:availability_zone] = instance_datas[:placement][:availability_zone] insert_datas[:iam_instance_profile] = instance_datas[:iam_instance_profile][:arn] insert_datas[:instance_type] = instance_datas[:instance_type] insert_datas[:key_name] = instance_datas[:key_name] insert_datas[:local_ip_address] = instance_datas[:private_ip_address] insert_datas[:monitoring] = instance_datas[:monitoring][:state] insert_datas[:security_group_ids] = instance_datas[:group_set].each {|group_set| security_group_ids << group_set[:group_id]} insert_datas[:source_dest_check] = instance_datas[:source_dest_check] insert_datas[:subnet_id] = instance_datas[:subnet_id] insert_datas[:tag_set] = instance_datas[:tag_set] insert_datas[:vpc_id] = instance_datas[:vpc_id] insert_datas[:disaable_api_termination] = ec2.get_api_termination(instance_id) insert_datas[:shutdown_behavior] = ec2.get_shutdown_behavior(instance_id) ## AMIを作成 image_id = ec2.create_image(instance_id, image_name, description) ec2.image_status(image_id) ## SimpleDBにインスタンスの情報を入れる sdb = SimpledbBase.new(sdb_region) sdb.put_data_to_simpledb(image_id, insert_datas, domain_name) ## 不要なAMIとItemを削除 deleted_image_ids = ec2.get_deleted_image(instance_id, generation) ec2.delete_image(deleted_image_ids) sdb.delete_data_from_simpledb(domain_name, deleted_image_ids)
まとめ
タグを使うよりは少しマシになった感はあるけど、ENIとかSecondary IPとか考えるとイマイチな感じ。
AWS SDK for RubyでSimpleDBを操作する
こんにちは@oko_changです。
自分ブログにもSimpleDBの爪痕を残しておこうと思ったので、AWS SDK for RubyでのSimpleDBの使い方をまとめておきます。
SimpleDBとは?というのは以下の記事などが参考になると思います。
- SimpleDBを使ってみました - サーバーワークス エンジニアブログ
- フレクトのクラウドBlog: Amazon SimpleDBについて調べたことをまとめました
- 20111215 12 aws-meister-sqs_sns_sdb-public
使い方
以下のような感じDomainを作ったりItemを追加したり削除したりが出来ました。
※スクリプトとかではありません
# -*- coding: utf-8 -*- require 'net/http' require 'aws-sdk' sdb_region = 'sdb.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com' domain_name = "milan_no_10" @sdb = AWS::SimpleDB.new(:simple_db_endpoint => sdb_region).client ## Domainを作成する @sdb.create_domain(:domain_name => domain_name) ## 作成されているDomainの一覧を見る @sdb.list_domains ## Domainのメタデータを確認する @sdb.domain_metadata(:domain_name => domain_name) ## Itemを追加する @sdb.put_attributes( :domain_name => domain_name, :item_name => "Keisuke Honda", :attributes => [{ :name => "Country", :value => "Japan"}, { :name => "Number", :value => "10"}] ) ## 追加したItemを見る @sdb.get_attributes(:domain_name => domain_name, :item_name => "Keisuke Honda") ## Attributeを削除する @sdb.delete_attributes(:domain_name => domain_name, :item_name => "Keisuke Honda", :attributes => [{ :name => "Number", :value => "10" }]) ## 追加したItemを削除する @sdb.delete_attributes(:domain_name => domain_name, :item_name => "Keisuke Honda") ## 一度に複数のItemを追加する @sdb.batch_put_attributes(:domain_name => domain_name, :items => [ { :name => "Keisuke Honda", :attributes => [{ :name => "Country", :value => "Japan"}, { :name => "Number", :value => "10"}] }, { :name => "Zvonimir Boban", :attributes => [{ :name => "Country", :value => "Croatia" }, { :name => "Number", :value => "10" }] }, { :name => "Rui Costa", :attributes => [{ :name => "Country", :value => "Portugal" }, { :name => "Number", :value => "10" }] } ]) ## ドメイン内のアイテムを取得する @sdb.select(:select_expression => "select Country from #{domain_name}") ## 一度に複数のAttributeを削除する @sdb.batch_delete_attributes(:domain_name => domain_name, :items => [ { :name => "Keisuke Honda", :attributes => [{ :name => "Country", :value => "Japan" }] }, { :name => "Rui Costa", :attributes => [{ :name => "Number", :value => "10" }] } ]) ## 一度に複数のItemを削除する @sdb.batch_delete_attributes(:domain_name => domain_name, :items => [ { :name => "Keisuke Honda"}, { :name => "Rui Costa"} ]) ## Domainを削除する @sdb.delete_domain(:domain_name => domain_name)
Clientクラスを使わずに以下のような感じもOKです。
# -*- coding: utf-8 -*- require 'net/http' require 'aws-sdk' sdb_region = 'sdb.' + Net::HTTP.get('169.254.169.254', '/latest/meta-data/placement/availability-zone').chop + '.amazonaws.com' domain_name = "milan_no_10" @sdb = AWS::SimpleDB.new(:simple_db_endpoint => sdb_region) ## ドメインを作成する mydomain = @sdb.domains.create(domain_name) ## 作成したドメインを確認する mydomain.exists? ## 作成したドメインにアイテムを登録する mydomain.items.create("Keisuke Honda", { "Country" => "Japan", "Number" => "10"}) ## 作成したドメインを削除する mydomain.delete!
まとめ
僕が個人レベルでちょっとしたものを保存するにはお手軽でとてもイイ。
CentOS6.5でDockerを少しだけ使ってみた
試してみたいなと思いつつ手をつけられていなかったDockerを少しだけ触ってみたので、ログを残しておきたいと思います。
DockerはDocker inc.社がオープンソースで公開しているコンテナ型の仮想化ソフトウェアですが、以下の記事などを見たらイメージがしやすかったです。
- コンテナ型仮想化「Docker 0.7」リリース。主要Linuxディストリビューション全対応、ストレージドライバ同梱、コンテナ命名も可能に - Publickey
- Linuxコンテナを手軽に扱えるDockerを使ってみよう | KVH Blog
- http://www.infoq.com/jp/articles/docker-containers
- http:// http://ja.wikipedia.org/wiki/LXC
- http://ja.wikipedia.org/wiki/Cgroups
リポジトリの追加
Cent OSでDockerをインストールするためにEPELのリポジトリを追加します。
[root@centos.okochang.com ~]# rpm --import http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6 [root@centos.okochang.com ~]# yum install -y http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
Dockerのインストールと起動
リポジトリの追加が出来たら以下のようにインストールが出来ます。
[root@centos.okochang.com ~]# yum install docker-io [root@centos.okochang.com ~]# docker -v Docker version 0.7.2, build 28b162e/0.7.2 [root@centos.okochang.com ~]# service docker start [root@centos.okochang.com ~]# chkconfig docker on
動作確認
起動直後はもちろん特にコンテナやイメージは存在しません。
[root@centos.okochang.com ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [root@centos.okochang.com ~]# docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
新しいコンテナを作成してでbashを使ってみます。
[root@centos.okochang.com ~]# docker run -t -i centos /bin/bash bash-4.1# echo "Hello World" Hello World bash-4.1# yum install tree # tree /home/ /home/ 0 directories, 0 files bash-4.1# exit exit
再度同じコマンドでコンテナにアクセスした場合の動作を確認します。
[root@centos.okochang.com ~]# docker run -t -i centos /bin/bash bash-4.1# tree bash: tree: command not found bash-4.1# exit exit
先ほどのリンクの記事にあるように、同じコマンドでアクセスしていても新しいコンテナにアクセスするようです。
以下のコマンドでコンテナの一覧が確認出来ます。
# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3625e3064dc3 centos:6.4 /bin/bash 28 seconds ago Exit 127 suspicious_brown 267c7bb6fa21 centos:6.4 /bin/bash 2 minutes ago Exit 0 boring_euclide
コンテナを再利用したい場合は、リポジトリにコミットしたうえで新しくコンテナを作成するようです。
コミットをする場合はコンテナIDを指定して以下のように実行します。
[root@centos.okochang.com ~]# docker commit 267c7bb6fa21 okochang/centos c1896ddfe6e49977edbdd60d18c0c404a66be6ed6bba0257e1a038b57109578c
コミットされるとその状態がイメージとして保存されます。
# docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE okochang/centos latest c1896ddfe6e4 32 seconds ago 337.1 MB centos 6.4 539c0211cd76 9 months ago 300.6 MB centos latest 539c0211cd76 9 months ago 300.6 MB
以下のようにコンテナを再利用します。
[root@centos.okochang.com ~]# docker run -t -i okochang/centos /bin/bash bash-4.1# tree /home/ /home/ 0 directories, 0 files # exit exit
コンテナの削除を行う場合はrmコマンドにコンテナIDを指定します。
[root@centos.okochang.com ~]# docker rm 03e8a848d495 03e8a848d495
イメージの削除をする場合はrmiコマンドにイメージIDを指定します。
※イメージを使用しているコンテナが削除されている必要があります。
[root@centos.okochang.com ~]# docker rmi c1896ddfe6e4 Untagged: c1896ddfe6e49977edbdd60d18c0c404a66be6ed6bba0257e1a038b57109578c Deleted: c1896ddfe6e49977edbdd60d18c0c404a66be6ed6bba0257e1a038b57109578c
Dockerfileというテキストファイルを作成して自分用にカスタマイズしたイメージを作成することも出来ます。
書き方はこちらに記載されていますが、そんなに難しいものではないですね。
Dockerfileを作成したらテキストファイルがあるディレクトリでビルドすればOKです。
[root@centos.okochang.com ~]# cat Dockerfile FROM centos RUN yum install -y openssh openssh-client openssh-server emacs tree EXPOSE 22 [root@centos.okochang.com ~]# docker build -t okochang/base [root@centos.okochang.com ~]# docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE okochang/base latest 040477d29dd1 2 minutes ago 536.5 MB centos 6.4 539c0211cd76 9 months ago 300.6 MB centos latest 539c0211cd76 9 months ago 300.6 MB
EC2インスタンスを使ってAuto Scalingを設定する
こんにちは、@oko_changです。
Twitterを眺めていたら新年早々Auto Scalingに新しい機能がリリースされているようなので、少し触ってみました。
リリース内容の詳細はこちらのリンクに記載されているので、リンクをご覧ください。
環境
Auto Scaling Command Line Toolはすでに新しい機能に対応しているので、以下の環境でテストをしました。
- Auto Scaling Command Line Tool 1.0.61.4
Auto Scalingの基本的な使い方はこちらの記事をご覧ください。
動作確認
Auto Scalingリソース上限の確認
以下のコマンドで、Auto Scalingに関連するリソースの上限値を知ることが出来るようになりました。
私のアカウントでは以下のようにAuto Scaling Groupが20個、Launch Configurationが100個が上限である事が分かります。
$ as-describe-account-limits --headers MAX-NUMBER-OF-AUTO-SCALING-GROUPS MAX-NUMBER-OF-LAUNCH-CONFIGURATIONS 20 100
EC2インスタンスを使ったLaunch Configurationの作成
稼働中のEC2インスタンスから新しいLaunch Configurationを作成する事が出来るようになりました。
以下のようなコマンドで簡単にLaunch Configurationを作成出来ます。
$ as-create-launch-config okochang-lc --instance-id i-26c1a923 OK-Created launch config
次のコマンドで作成されたLaunch Configurationを見ると、作成元インスタンスが使用しているAMIやSecurity Groupなどと同じ設定で作成されている事が分かります。
作成元インスタンスが使用しているAMIを使ってLaunch Configurationを使用しているので、AMIから起動後にインスタンスの設定が変更されている場合は注意が必要です。
$ as-describe-launch-configs okochang-lc --headers --show-long
また、作成元インスタンスの一部を変更したい場合は、以下のようにパラメータを指定してLaunch Configurationを作成します。
例)インスタンスタイプを変更する場合
$ as-create-launch-config okochang-lc --instance-id i-26c1a923 --instance-type m1.medium OK-Created launch config
EC2インスタンスを使ったAuto Scaling Groupの作成
先ほどはEC2インスタンスからLaunch Configurationを作成しましたが、Auto Scaling Groupを作る事も出来ます。
$ as-create-auto-scaling-group okochang-asg --instance-id i-26c1a923 --min-size 1 --max-size 4 --desired-capacity 1 OK-Created AutoScalingGroup
以下のコマンドで作成されたAuto Scaling Groupによって新しいインスタンスが作成元と同じゾーンやサブネットなどに配置されている事が分かります。
$ as-describe-auto-scaling-groups okochang-asg --headers --show-long
また、Auto Scaling Group名と同じLaunch Configurationも同時に作成されています。
以下のコマンドを実行すると、Launch Configurationも作成元インスタンスと同じ設定がされている事が分かります。
$ as-describe-launch-configs okochang-asg --headers --show-long
Auto Scaling GroupへEC2インスタンスをアタッチ
すでに作成済みのAuto Scaling GroupにEC2インスタンスをアタッチ出来るようにもなっています。
アタッチするインスタンスは以下のような点を注意します。
- アタッチするEC2インスタンスのステータスがrunning状態であること
- アタッチするEC2インスタンスがAuto Scaling Groupと同じAZであること
- アタッチするEC2インスタンスがAuto Scaling Groupと同じAWSアカウントであること
- アタッチするEC2インスタンスが他のAuto Scaling Groupのメンバーでないこと
- Auto Scaling GroupがELBを使用している場合、アタッチするEC2インスタンスとELBが同じVPCやEC2クラシックに存在すること
- Auto Scaling Groupへアタッチすることによって起動プロセスが停止したりはしない
実際にAuto Scaling Groupへインスタンスをアタッチするには以下のようなコマンドを実行します。
$ as-attach-instances i-26c1a923 --auto-scaling-group okochang-asg OK-Instance(s) will be attached
以下のコマンドを実行すると、Auto Scaling Groupにアタッチされたことが分かります。
$ as-describe-auto-scaling-groups okochang-asg --headers
Launch Configurationでのブロックデバイスマッピング
Launch Configuration作成時にDeleteOnTermination、provisioned IOPs、block device mappingの設定が出来るようになっています。
これらの設定はas-create-launch-configコマンドの--block-device-mappingオプションを使用します。
オプションはkey1=value1のような形で指定し、valueの値は[snapshot-id]:[volume-size]:[true|false]:[standard|io1[:iops]]の順番で指定します。
/dev/sdcにsnap-16cba833のスナップショットを使用して100GBのEBS(DeleteOnTerminationをtrue)をProvisioned IOPSを100で作成する場合は以下のコマンドを実行します。
$ as-create-launch-config okochang-lc \ --image-id ami-79711578 \ --instance-type m1.large \ --key okochang-key \ --group sg-b72d31db \ --block-device-mapping "/dev/sdc=snap-16cba833:100:false:io1:200"
まとめ
細かいところのリリースとなりますが結構便利に使える機能が増えた印象があります。
Auto Scalingを使ってない環境からAuto Scalingの環境への移行もスムーズに出来るようになったかなーと思いました。
はやくPython版でも使用したいですね。
参考
- http://aws.amazon.com/releasenotes/0806439683523573
- http://docs.aws.amazon.com/AutoScaling/latest/APIReference/API_DescribeAccountLimits.html
- http://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/create-lc-with-instanceID.html#create-lc-with-instance-type
- http://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/create-asg-from-instance.html
- http://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/attach-instance-asg.html
- http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html
あけましておめでとうございます。
昨年は達成できた目標よりも出来なかった目標の方が目立ってしまうという結果でしたので、今年はより精進が必要そうです。
このブログももう少し幅広い内容に出来たら良いなと思います。
さてさて昨年末に書きかけになっていたスクリプトを放置していたので、こちらに残しておこうかと。
またまたEC2インスタンスのバックアップスクリプトですが、取得元のインスタンスに関連する情報をAMIのタグに残しておくといったものです。
# -*- coding: utf-8 -*- require 'net/http' require 'aws-sdk' ## 自分自身の情報を取得する ec2_region = 'ec2.' + 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') image_name = instance_id + '-' + Time.now.strftime("%Y%m%d%H%M") description = "automatically generated image" generation = 3 class InstanaceDatas def initialize(ec2_region, instance_id) @ec2 = AWS::EC2.new(:ec2_endpoint => ec2_region).client @instance_datas = @ec2.describe_instances( :instance_ids => [instance_id] )[:instance_index][instance_id] end def get_instance_type @instance_datas[:instance_type] end def get_availability_zone @instance_datas[:placement][:availability_zone] end def get_vpc_id @instance_datas[:vpc_id] end def get_subnet_id @instance_datas[:subnet_id] end def get_local_ip_address @instance_datas[:private_ip_address] end def get_security_group_ids security_group_ids = [] @instance_datas[:group_set].each do |group_set| security_group_ids << group_set[:group_id] end return security_group_ids end def get_source_dest_check @instance_datas[:source_dest_check] end def get_monitoring @instance_datas[:monitoring][:state] end def get_iam_instance_profile @instance_datas[:iam_instance_profile][:arn] end def get_tag_set @instance_datas[:tag_set] end def get_key_name @instance_datas[:key_name] end end class InstanceAttributes def initialize(ec2_region) @ec2 = AWS::EC2.new(:ec2_endpoint => ec2_region).client end def get_api_termination(instance_id) @ec2.describe_instance_attribute( :instance_id => instance_id, :attribute => "disableApiTermination" )[:disable_api_termination][:value] end def get_shutdown_behavior(instance_id) @ec2.describe_instance_attribute( :instance_id => instance_id, :attribute => "instanceInitiatedShutdownBehavior" )[:instance_initiated_shutdown_behavior][:value] end end class AmiBackup def initialize(ec2_region) @ec2 = AWS::EC2.new(:ec2_endpoint => ec2_region).client end def create_image(instance_id, image_name, description) @ec2.create_image( :instance_id => instance_id, :name => image_name, :description => description, :no_reboot => true )[:image_id] end def image_status(image_id) 30.times do if @ec2.describe_images(:image_ids => [image_id])[:images_set][0][:image_state] == "available" puts "done" break else sleep 10 end end end def create_tags(image_id, tag_datas) @ec2.create_tags( :resources => [image_id], :tags => [ { :key => "instance_type", :value => tag_datas[:instance_type] }, { :key => "subnet_id", :value => tag_datas[:subnet_id] }, { :key => "local_ip_address", :value => tag_datas[:local_ip_address] }, { :key => "key_name", :value => tag_datas[:key_name] }, { :key => "security_group_ids", :value => tag_datas[:security_group_ids].join(" ") }, { :key => "source_dest_check", :value => tag_datas[:source_dest_check].to_s }, { :key => "disable_api_termination", :value => tag_datas[:disaable_api_termination].to_s }, { :key => "monitoring", :value => tag_datas[:monitoring] }, { :key => "iam_instance_profile", :value => tag_datas[:iam_instance_profile] }, { :key => "tag_set", :value => tag_datas[:tag_set].join(" ") } ## 上限で以下のものはタグにつけられなかった # { :key => "instance_initiated_shutdown_behavior", :value => tag_datas[:instance_initiated_shutdown_behavior] } # { :key => "vpc_id", :value => tag_datas[:vpc_id] } ] ) end def delete_images(instance_id, generation) image_list = @ec2.describe_images( :owners => ["self"], :filters => [{:name => 'name', :values => [instance_id + '-*']}] )[:images_set] sorted_list = image_list.sort { |a,b| b[:name] <=> a[:name] } delete_target = sorted_list[generation.to_i, sorted_list.length] unless delete_target.nil? puts "Remove old backup" delete_target.each do |image| @ec2.deregister_image(:image_id => image[:image_id]) image[:block_device_mapping].each do |device| @ec2.delete_snapshot(:snapshot_id => device[:ebs][:snapshot_id]) end end else puts "No image to be deleted" exit 0 end end end # AMIのタグに必要な情報を取得する ec2_instance_datas = InstanaceDatas.new(ec2_region, instance_id) ec2_instance_attributes = InstanceAttributes.new(ec2_region) tag_datas = Hash.new tag_datas[:instance_type] = ec2_instance_datas.get_instance_type tag_datas[:availability_zone] = ec2_instance_datas.get_availability_zone tag_datas[:iam_instance_profile] = ec2_instance_datas.get_iam_instance_profile tag_datas[:instance_type] = ec2_instance_datas.get_instance_type tag_datas[:key_name] = ec2_instance_datas.get_key_name tag_datas[:local_ip_address] = ec2_instance_datas.get_local_ip_address tag_datas[:monitoring] = ec2_instance_datas.get_monitoring tag_datas[:security_group_ids] = ec2_instance_datas.get_security_group_ids tag_datas[:source_dest_check] = ec2_instance_datas.get_source_dest_check tag_datas[:subnet_id] = ec2_instance_datas.get_subnet_id tag_datas[:tag_set] = ec2_instance_datas.get_tag_set tag_datas[:vpc_id] = ec2_instance_datas.get_vpc_id tag_datas[:disaable_api_termination] = ec2_instance_attributes.get_api_termination(instance_id) tag_datas[:shutdown_behavior] = ec2_instance_attributes.get_shutdown_behavior(instance_id) ## AMIの作成 ami = AmiBackup.new(ec2_region) image_id = ami.create_image(instance_id, image_name, description) ami.image_status(image_id) ami.create_tags(image_id, tag_datas) ## 不要なAMIの削除 ami.delete_images(instance_id, generation)
今年も一年よろしくお願いします。
Termination Protection 対 Auto Scaling
こんにちは@okoc_changです。
Auto Scalingを使っていると、必要なタイミングでインスタンスが起動することが出来ますし、必要がなくなったタイミングでインスタンスを削除することが出来ます。
また、EC2にはTermination Protection(disable-api-termination)というインスタンスの削除を防止する機能があります。
今回はこれを同時に使った場合の挙動を確認します。
以下のリンクには ”Auto Scaling overrides the instance termination protection attribute, if enabled”と書かれてあります。
また、以下のリンクには”Instances that are part of an Auto Scaling group are not covered by termination protection”と書かれています。
どうやらTeminate ProtectionはAuto Scalingによって上書きされてしまうようですが、実際にどんな挙動になるかを確認します。(2013年12月時点の結果です)
テスト環境構築
作業前の状態確認
当然ですが、最初はELBもAuto Scalingも存在しません。
$ aws elb describe-load-balancers --region ap-northeast-1 { "LoadBalancerDescriptions": [] } $ aws autoscaling describe-auto-scaling-groups --region ap-northeast-1 { "AutoScalingGroups": [] }
ELBの作成
最初にELBの作成します。
$ aws elb create-load-balancer \ --load-balancer-name okochang-lb \ --listeners Protocol=http,LoadBalancerPort=80,InstanceProtocol=http,InstancePort=80 \ --subnets "subnet-zzzzzzzz" \ --security-groups "sg-yyyyyyyy" \ --region ap-northeast-1 { "DNSName": "okochang-lb-1512932827.ap-northeast-1.elb.amazonaws.com" }
先ほど作成したELBにヘルスチェックの設定をします。
$ aws elb configure-health-check \ --load-balancer-name okochang-lb \ --health-check Target=TCP:80,Interval=30,Timeout=10,UnhealthyThreshold=2,HealthyThreshold=5 \ --region ap-northeast-1 { "HealthCheck": { "HealthyThreshold": 5, "Interval": 30, "Target": "TCP:80", "Timeout": 10, "UnhealthyThreshold": 2 } }
Auto Scalingの作成
Launch Configurationを作成します
$ aws autoscaling create-launch-configuration \ --launch-configuration-name okochang-lc \ --image-id ami-xxxxxxxx \ --key-name okochang-key \ --security-groups sg-b72d31db \ --instance-type t1.micro \ --associate-public-ip-address \ --region ap-northeast-1
Auto Scaling Groupを作成します
$ aws autoscaling create-auto-scaling-group \ --auto-scaling-group-name okochang-asg \ --launch-configuration-name okochang-lc \ --min-size 2 \ --max-size 4 \ --load-balancer-names okochang-lb \ --health-check-type ELB \ --health-check-grace-period 300 \ --vpc-zone-identifier subnet-zzzzzzzz \ --region ap-northeast-1
Scale outのポリシーを作成します
$ aws autoscaling put-scaling-policy \ --auto-scaling-group-name okochang-asg \ --policy-name okochang-scale-out \ --scaling-adjustment 1 \ --adjustment-type ChangeInCapacity \ --cooldown 300 \ --region ap-northeast-1 { "PolicyARN": "arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:36df9388-8dce-4c18-afcd-14f79ff88636:autoScalingGroupName/okochang-asg:policyName/okochang-scale-out" }
Scale Inのポリシーを作成します
$ aws autoscaling put-scaling-policy \ --auto-scaling-group-name okochang-asg \ --policy-name okochang-scale-in \ --scaling-adjustment -1 \ --adjustment-type ChangeInCapacity \ --cooldown 300 \ --region ap-northeast-1 { "PolicyARN": "arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:856b384d-495d-4d54-8df2-d03b25e86301:autoScalingGroupName/okochang-asg:policyName/okochang-scale-in" }
Scale Outのアラームを作成します
$ aws cloudwatch put-metric-alarm \ --alarm-name okochang-scale-out-alarm \ --metric-name CPUUtilization \ --namespace AWS/EC2 \ --statistic Average \ --period 180 \ --alarm-actions "arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:36df9388-8dce-4c18-afcd-14f79ff88636:autoScalingGroupName/okochang-asg:policyName/okochang-scale-out" \ --evaluation-periods 1 \ --threshold 70 \ --comparison-operator GreaterThanThreshold \ --region ap-northeast-1
Scale Inのアラームを作成します
$ aws cloudwatch put-metric-alarm \ --alarm-name okochang-scale-in-alarm \ --metric-name CPUUtilization \ --namespace AWS/EC2 \ --statistic Average \ --period 180 \ --alarm-actions "arn:aws:autoscaling:ap-northeast-1:123456789012:scalingPolicy:856b384d-495d-4d54-8df2-d03b25e86301:autoScalingGroupName/okochang-asg:policyName/okochang-scale-in" \ --evaluation-periods 1 \ --threshold 30 \ --comparison-operator LessThanThreshold \ --region ap-northeast-1
検証スタート
現時点でのAuto Scaling Groupの状態とELBからのヘルスチェックは以下の通りです。
※Auto Scaling Group内のEC2インスタンスはどちらも正常な状態
$ aws autoscaling describe-auto-scaling-groups --region ap-northeast-1 | jq ".AutoScalingGroups[] | {Instances}" { "Instances": [ { "LaunchConfigurationName": "okochang-lc", "LifecycleState": "InService", "HealthStatus": "Healthy", "AvailabilityZone": "ap-northeast-1c", "InstanceId": "i-f59493f0" }, { "LaunchConfigurationName": "okochang-lc", "LifecycleState": "InService", "HealthStatus": "Healthy", "AvailabilityZone": "ap-northeast-1c", "InstanceId": "i-0270d307" } ] } $ aws elb describe-instance-health --load-balancer-name okochang-lb --region ap-northeast-1 | jq ".InstanceStates" [ { "Description": "N/A", "State": "InService", "ReasonCode": "N/A", "InstanceId": "i-0270d307" }, { "Description": "N/A", "State": "InService", "ReasonCode": "N/A", "InstanceId": "i-f59493f0" } ]
ここでi-0270d307のTermination Protectionを有効にしてみます
$ aws ec2 modify-instance-attribute --instance-id i-0270d307 --disable-api-termination "{\"Value\": true}" --region ap-northeast-1 { "return": "true" } $ aws ec2 describe-instance-attribute --instance-id i-0270d307 --attribute disableApiTermination --region ap-northeast-1 | jq ".DisableApiTermination" { "Value": true }
次にTermination Protectionを有効にしたインスタンスのApacheを停止してから、Auto Scaling GroupとELBの分散対象となるインスタンスを確認します。
ELBからの監視が失敗してOutOfServiceとなり、HealthStatusもUnhealthyになります。
$ aws elb describe-instance-health --load-balancer-name okochang-lb --region ap-northeast-1 | jq ".InstanceStates" [ { "Description": "Instance has failed at least the UnhealthyThreshold number of health checks consecutively.", "State": "OutOfService", "ReasonCode": "Instance", "InstanceId": "i-0270d307" }, { "Description": "N/A", "State": "InService", "ReasonCode": "N/A", "InstanceId": "i-f59493f0" } ] $ aws autoscaling describe-auto-scaling-groups --region ap-northeast-1 | jq ".AutoScalingGroups[] | {Instances}" { "Instances": [ { "LaunchConfigurationName": "okochang-lc", "LifecycleState": "InService", "HealthStatus": "Healthy", "AvailabilityZone": "ap-northeast-1c", "InstanceId": "i-f59493f0" }, { "LaunchConfigurationName": "okochang-lc", "LifecycleState": "InService", "HealthStatus": "Unhealthy", "AvailabilityZone": "ap-northeast-1c", "InstanceId": "i-0270d307" } ] }
しばらくするとAuto Scaling Groupには新しいインスタンス(i-23f99926)が起動し、先ほどのインスタンスがAuto Scaling Groupから外れています。
また、ELBにも新しいインスタンスが分散対象に登録されている事が分かります。
$ aws autoscaling describe-auto-scaling-groups --region ap-northeast-1 | jq ".AutoScalingGroups[] | {Instances}" { "Instances": [ { "LaunchConfigurationName": "okochang-lc", "LifecycleState": "InService", "HealthStatus": "Healthy", "AvailabilityZone": "ap-northeast-1c", "InstanceId": "i-f59493f0" }, { "LaunchConfigurationName": "okochang-lc", "LifecycleState": "Pending", "HealthStatus": "Healthy", "AvailabilityZone": "ap-northeast-1c", "InstanceId": "i-23f99926" } ] } $ aws elb describe-instance-health --load-balancer-name okochang-lb --region ap-northeast-1 | jq ".InstanceStates" [ { "Description": "Instance has failed at least the UnhealthyThreshold number of health checks consecutively.", "State": "OutOfService", "ReasonCode": "Instance", "InstanceId": "i-23f99926" }, { "Description": "N/A", "State": "InService", "ReasonCode": "N/A", "InstanceId": "i-f59493f0" } ]
しかし以下のように先ほどのEC2インスタンスはrunningのままでTerminateはされていません。
さすがはTermination Protectionといったところでしょうか。
$ aws ec2 describe-instances --instance-id i-0270d307 --region ap-northeast-1 | jq ".Reservations[] | .Instances[] | {State}" { "State": { "Name": "running", "Code": 16 } }
と、思いきやしばらく待っているとAuto ScalingによってTerminateされてしまいました。
$ aws ec2 describe-instances --instance-id i-0270d307 --region ap-northeast-1 | jq ".Reservations[] | .Instances[] | {State}" { "State": { "Name": "terminated", "Code": 48 } }
まとめ
一瞬、Termination Protectionの勝利か!と思いましたが、ドキュメントに記載されているように、Auto Scalingを使っている場合はTermination Protectionすらも上書きされてしまいます。
最強の盾(Termination Protection)よりも最強の矛(Auto Scaling)が強いという結果となりました。