aws-cliを使ってAutoScaling Groupを作成してみる。
launch-configurationを作成
$ aws autoscaling create-launch-configuration --launch-configuration-name config1 --image-id ami-39b23d38 --instance-type t1.micro --key-name ap-northeast
作成結果を確認
$ aws autoscaling describe-launch-configurations --output text ap-northeast arn:aws:autoscaling:ap-northeast-1:XXXXXXXXXXXX:launchConfiguration:0ad52a06-34d4-4c70-940c-b4b5a23adbd9:launchConfigurationName/config1 ami-39b23d38 False config1 2013-08-23T10:44:36.777Z t1.micro INSTANCEMONITORING True RESPONSEMETADATA 5d980e12-0be1-11e3-a812-5b0041b22e4e
auto-scaling-groupを作成
$ aws autoscaling create-auto-scaling-group --auto-scaling-group-name group1 --launch-configuration-name config1 --min-size 2 --max-size 2 --vpc-zone-identifier subnet-d8de6cb0,subnet-96d96bfe
作成結果の確認
$ aws autoscaling describe-auto-scaling-groups --output text arn:aws:autoscaling:ap-northeast-1:XXXXXXXXXXXXXX:autoScalingGroup:3cffd102-8a3e-4756-8a20-3343b39ba977:autoScalingGroupName/group1 0 2 group1 300 2 2 subnet-d8de6cb0,subnet-96d96bfe config1 2013-08-23T10:58:55.700Z EC2 i-02215307 ap-northeast-1c Healthy InService config1 i-9fe2059a ap-northeast-1a Healthy InService config1 RESPONSEMETADATA 45062903-0be3-11e3-84d9-670da55b233a
インスタンス2つが、異なるサブネット(異なるAZ)で起動しているのが分かりますね。
ついでに削除処理もメモとして残しておきます。
$ aws autoscaling update-auto-scaling-group --auto-scaling-group-name group1 --min-size 0 --max-size 0 $ aws autoscaling delete-auto-scaling-group --auto-scaling-group-name group1 $ aws autoscaling delete-launch-configuration --launch-configuration-name config1 以上で削除完了です。AutoScaling groupの削除は、インスタンスが完全にterminateされてからでないと削除はできませんので、ご注意を。
Amazon Linux 2013.03にデフォルトでインストールされているAWS CLIにhelpが入ってない。
https://forums.aws.amazon.com/thread.jspa?messageID=449307
こちらのディスカッションフォーラムにもあるように、Amazon Linux 2013.03にはaws-cliのhelp(manual entry)が入っていないです。
$ aws --version aws-cli/0.9.3 Python/2.6.8 Linux/3.4.43-43.43.amzn1.x86_64
以下のようにhelpコマンドを実行しても表示されません。
$ aws help No manual entry for aws
ので、aws-cliを一旦削除して再インストールし直すことにしました。
$ sudo yum remove aws-cli $ sudo yum -y update $ sudo easy_install pip $ sudo pip install awscli
インストール後、helpを実行すると、
$ aws help AWS() AWS() NAME aws - DESCRIPTION The AWS Command Line Interface is a unified tool that provides a con- sistent interface for interacting with all parts of AWS. SYNOPSIS aws [options] <service_name> <operation> [parameters] Use aws service help for information on a specific service. OPTIONS --debug (boolean) Turn on debug logging. --endpoint-url (string)
正しく表示されました。よかったよかった。
ちなみに新しく入れたaws-cliのバージョンは
$ aws --version aws-cli/0.14.1 Python/2.6.8 Linux/3.4.43-43.43.amzn1.x86_64
元々入っていたのが0.9.3だったので、元々のは結構古かったみたいですね。
AWS CLIを使ってTagの値のみを取得する
AWS CLIを使って、以下のように割り当てられたインスタンスのTagの値のみ(今回はWordPress)を取得します。
今回はAmazon Linuxを使うので、AWS CLIのインストール方法は割愛します。
# Amazon Linux 2013.03に入っているデフォルトのaws-cliを使いました。バージョンは以下です。
$ aws --version aws-cli/0.9.3 Python/2.6.8 Linux/3.4.43-43.43.amzn1.x86_64
#バージョンが異なると、オプションの指定が異なる可能性があります。
まず以下の環境変数を設定します。
export AWS_DEFAULT_REGION=ap-northeast-1 export AWS_CREDENTIAL_FILE=/opt/aws/credential-file
credential-fileを作成します。
$ sudo cp /opt/aws/credential-file-path /opt/aws/credential-file $ sudo vi /opt/aws/credential-file AWSAccessKeyId=XXXXXXXXXXXXXXXXXX AWSSecretKey=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
以下コマンドを実行して動作確認。
$ aws ec2 describe-instances { "Reservations": [ { "OwnerId": "000000000000", "ReservationId": "r-883ce98b", "Groups": [ (以下省略)
ちゃんと出力されることを確認。
$ aws ec2 describe-tags { "ResponseMetadata": { "RequestId": "b98ff86b-24e7-4950-9161-c864e11c20a7" }, "Tags": [ { "ResourceType": "snapshot", "ResourceId": "snap-6d0d4c4d", "Key": "Name", "Value": "dev-urandom-1G" }, { "ResourceType": "security-group", "ResourceId": "sg-958dff94", "Key": "aws:cloudformation:stack-name", "Value": "WordpressSite" }, (以下省略)
通常describe-tagsを実行すると、全部のTagが出てきてしまうので、絞り込みをします。
$ aws ec2 describe-tags --filter '{"name":"resource-id","values":"i-06672203"}' { "ResponseMetadata": { "RequestId": "8c60bc2e-16bc-4882-8bfb-de5889eb84c3" }, "Tags": [ { "ResourceType": "instance", "ResourceId": "i-06672203", "Key": "Name", "Value": "WordPress" } ] }
これをさらに絞り込むためにjqを使います。jqは入力内容を元に出力を絞り込むためのプログラムです。
まずは、jqをインストール。
$ sudo yum install jq
そして、上記出力をjqを使って絞り込みます。
$aws ec2 describe-tags --filter '{"name":"resource-id","values":"i-06672203"}' | jq '.Tags[] | .Value' "WordPress"
以上で、WordPressの値のみを抽出することができました!
もちろん、Name以外のTagも取得できますので、いろいろ使いまわせそうですね。
AWS Security Token Serviceを使ってみる
AWS SDKやAWSコマンドラインツールを使う場合、Security Credentialsが必要になります。Security Credentialsの中にはアクセスキーとシークレットアクセスキーが含まれています。AWSのサービスやリソースをAPIを使って管理するときには、この2つのキーを使って認証します。
この2つのキーをどうやって保管するか?が本記事の目標とするところです。
2つのキーをソースコードやもしくはテキストファイル、DB等に格納して使うこともできますが、これだとキーの置き換えをするのが大変です。そこでAWSから提供されているSecurity Token Serviceというサービスを使って、キーの管理を安全に、かつ効率的に行うことを試してみました。
Security Token Serviceとは?
一時的な、かつ制限された特権を持つAWSアカウント、またはIAMユーザのCredential情報を取得可能にする機能です。Security Token Serviceは、以下の3つのアクションが可能です。
- AssumeRole
- IAM Roleの権限を一時的に取得可能。AssumeRoleを実行する時にはIAMのCredentialを使って呼び出す必要あり。AWS親アカウントでは呼び出せないらしい。
- GetFederationToken
- IAMユーザにfederatedされた権限を一時的に取得可能。リクエスト内でユーザ名とポリシーを指定する。
- GetSessionToken
- AWSアカウントまたはIAMユーザの権限を一時的に取得可能。
AWS SDKでSecurity Token Serviceより権限を取得できる。対応しているSDKは2013/04/16時点では以下。
http://docs.aws.amazon.com/STS/latest/UsingSTS/AccessingSTS.html
AWS SDK for Javaを使って、GetSessionTokenした例が以下。今回は取得した権限でS3のバケットリストを表示させている。
import java.io.IOException; import java.util.List; import com.amazonaws.AmazonWebServiceClient; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.BasicSessionCredentials; import com.amazonaws.auth.PropertiesCredentials; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.Bucket; import com.amazonaws.services.s3.model.ObjectListing; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; import com.amazonaws.services.securitytoken.model.Credentials; import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest; import com.amazonaws.services.securitytoken.model.GetSessionTokenResult; public class MySecurityTokenSample { public static void main(String[] args) throws IOException { //STSからトークンを取得するために必要なCredential情報 AWSCredentials credentials = new PropertiesCredentials( MySecurityTokenSample.class.getResourceAsStream("AwsCredentials.properties")); //SecurityTokenServiceClientの設定 AWSSecurityTokenServiceClient sts = new AWSSecurityTokenServiceClient(credentials); GetSessionTokenRequest req = new GetSessionTokenRequest(); GetSessionTokenResult res = sts.getSessionToken(req); Credentials tmpCredentials = res.getCredentials(); String accessKeyId = tmpCredentials.getAccessKeyId(); String secretAccessKeyId = tmpCredentials.getSecretAccessKey(); //取得したアクセスキー、シークレットアクセスキーでS3のバケットリストを表示してみる。 BasicSessionCredentials c = new BasicSessionCredentials(tmpCredentials.getAccessKeyId(), tmpCredentials.getSecretAccessKey(), tmpCredentials.getSessionToken()); AmazonS3Client s3Client = new AmazonS3Client(c); s3Client.setEndpoint("s3-ap-northeast-1.amazonaws.com"); List<Bucket> list = s3Client.listBuckets(); for(Bucket b : list) { System.out.println(b.getName()); } } }
こちらを実行すると、バケット名のリストが表示された。
トークンを取得するためには、やはりSecurity Credentialsの情報が必要。なので、システムの中に1台だけトークン管理サーバなるものを立てて、そこの中では唯一Credentials情報を持つ。他のサーバで一時的にCredentials情報が必要な場合は、このサーバが何かしらの安全な方法で配ってあげる形が良さそう。取得したトークンは、デフォルトで12時間まで有効。その時間が過ぎると無効になる。以下のように実行することで、その時間も変更できる。
getSessionTokenRequest.setDurationSeconds(7200);
上記例では、AWSアカウントの権限を取得していたが、以下のようにgetFederatedTokenを使って指定したIAM Userの権限を取得もできる。
import java.io.IOException; import java.util.List; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.BasicAWSCredentials; import com.amazonaws.auth.BasicSessionCredentials; import com.amazonaws.auth.PropertiesCredentials; import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.Bucket; import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; import com.amazonaws.services.securitytoken.model.Credentials; import com.amazonaws.services.securitytoken.model.GetFederationTokenRequest; import com.amazonaws.services.securitytoken.model.GetFederationTokenResult; import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest; import com.amazonaws.services.securitytoken.model.GetSessionTokenResult; public class MySecurityTokenSample2 { public static void main(String[] args) throws IOException { //STSからトークンを取得するために必要なCredential情報 AWSCredentials credentials = new PropertiesCredentials( MySecurityTokenSample.class.getResourceAsStream("AwsCredentials.properties")); //SecurityTokenServiceClientの設定 AWSSecurityTokenServiceClient sts = new AWSSecurityTokenServiceClient(credentials); GetFederationTokenRequest req = new GetFederationTokenRequest(); req.setName("TVMUser1"); req.setPolicy("{\"Statement\": [{\"Effect\": \"Allow\",\"Action\": \"s3:*\",\"Resource\": \"*\"}]}"); GetFederationTokenResult res = sts.getFederationToken(req); Credentials tmpCredentials = res.getCredentials(); //取得したアクセスキー、シークレットアクセスキーでS3のバケットリストを表示してみる。 BasicSessionCredentials c = new BasicSessionCredentials(tmpCredentials.getAccessKeyId(), tmpCredentials.getSecretAccessKey(), tmpCredentials.getSessionToken()); AmazonS3Client s3Client = new AmazonS3Client(c); s3Client.setEndpoint("s3-ap-northeast-1.amazonaws.com"); List<Bucket> list = s3Client.listBuckets(); for(Bucket b : list) { System.out.println(b.getName()); } } }
TVMUser1というIAM Userで、S3のみにアクセス可能なポリシーをJSONで書いたものを使ってGetFederationTokenしている。
req.setPolicyでは以下のJSONデータを引数に入れている。
{ "Statement": [ { "Effect": "Allow", "Action": "s3:*", "Resource": "*" } ] }
このように、Security Token Serviceを使えば、一時的なCredentials情報を取得できることがわかった!
リンク
- http://docs.aws.amazon.com/AmazonS3/latest/dev/AuthUsingTempFederationTokenJava.html
- http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/securitytoken/AWSSecurityTokenServiceClient.html
- http://docs.aws.amazon.com/general/latest/gr/rande.html#sts_region
- http://docs.aws.amazon.com/STS/latest/UsingSTS/STSMgmtConsole.html
DynamoDBでscanを実行してみる
DynamoDBのテーブル名が SampleTable, AttributeがIdとNameのみのテーブルに対してScanを実行した。
AWS SDK for Javaで以下のように実行してみた。
import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.PropertiesCredentials; import com.amazonaws.services.dynamodb.AmazonDynamoDBClient; import com.amazonaws.services.dynamodb.model.AttributeValue; import com.amazonaws.services.dynamodb.model.ComparisonOperator; import com.amazonaws.services.dynamodb.model.Condition; import com.amazonaws.services.dynamodb.model.GetItemRequest; import com.amazonaws.services.dynamodb.model.GetItemResult; import com.amazonaws.services.dynamodb.model.Key; import com.amazonaws.services.dynamodb.model.PutItemRequest; import com.amazonaws.services.dynamodb.model.PutItemResult; import com.amazonaws.services.dynamodb.model.ScanRequest; import com.amazonaws.services.dynamodb.model.ScanResult; public class DynamoDBLoadGenerator { static AmazonDynamoDBClient dynamoDB; public static void main(String[] args) throws IOException { //Credentialsの設定 AWSCredentials credentials = new PropertiesCredentials( DynamoDBSample.class.getResourceAsStream("AwsCredentials.properties")); //リクエストのパラメータの設定 dynamoDB = new AmazonDynamoDBClient(credentials); dynamoDB.setEndpoint("http://dynamodb.ap-northeast-1.amazonaws.com"); String tableName = "SampleTable"; //ScanFilterConditionの設定 Condition scanFilterCondition = new Condition() .withComparisonOperator(ComparisonOperator.LT.toString()) //〇〇よりも小さい .withAttributeValueList(new AttributeValue().withN("500")); //500 Map<String, Condition> conditions = new HashMap<String, Condition>(); conditions.put("Id", scanFilterCondition); ScanRequest scanRequest = new ScanRequest() .withTableName(tableName) .withScanFilter(conditions) .withAttributesToGet(Arrays.asList("Id")); //Idに対してScanFilterを実行 //Scan実行 ScanResult scanResult = dynamoDB.scan(scanRequest); //結果表示 for (Map<String, AttributeValue> item : scanResult.getItems()){ System.out.println(item.get("Id")); } } }
実行結果は以下のように出力される。
{N: 251, } {N: 187, } {N: 154, } {N: 7, } {N: 115, } {N: 286, } {N: 361, } {N: 380, } {N: 117, } {N: 401, } {N: 47, } {N: 184, } {N: 403, } {N: 304, } {N: 156, } {N: 122, } {N: 273, } 以下省略
IDが500以下のデータが出力された。
また今回使用したScanFilterConditionでComparisonOperatorで、BETWEENやBEGIN WITHやCONTAINなどいろいろ指定できる。今回使ったのはLT(おそらくはLess Thanのことだと思う)。これでscanするフィルターをいろいろ設定できそう。
試しにNameが"Book 500"とイコールであるIdをFilterConditionで指定したのが以下。
//ScanFilterConditionの設定 Condition scanFilterCondition = new Condition() .withComparisonOperator(ComparisonOperator.EQ.toString()) .withAttributeValueList(new AttributeValue().withS("Book 500"));
以下はComparisonOperatorのJavaDoc
[http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodb/model/ComparisonOperator.html:title=
http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodb/model/ComparisonOperator.html]
AWS OpsWorksで起動されたインスタンスの中身を見てみる
AWS OpsWorksを使ってHAProxyのレイヤーを追加し、HAProxyインスタンスを立ち上げてみる。
自分のkeypairを使うように設定すれば、SSHでログインすることができる。
早速ログインしてみる。
ec2-userでログインができた。
OpsWorksの動作に関連しそうなものは以下のディレクトリにあった。
[ec2-user@stratus aws]$ sudo ls opsworks/current/bin chef-client gli opsworks-agent-cli restclient chef-solo knife opsworks-agent-installer s3curl.pl convert_to_should_syntax lockrun opsworks-agent-updater shef edit_json.rb nokogiri prettify_json.rb thor erubis ohai rake extract opsworks-agent rake2thor
chef-soloやchef-client, opsworks-agent-cliなどがある。
AWSのドキュメントには、それぞれのインスタンスでchef-soloが動いていると書かれていた。
以下抜粋
Here's how it works: Chef Solo runs on every instance, and AWS OpsWorks sends commands to each instance to run recipes that set up, configure, and shut down the instance. You can extend these actions with your own custom recipes. You can also run your custom recipes on a specific instance or on all instances in your stack.
さらにopsworks-agent-cliで以下のようなコマンドも実行できる。
- agent_report
- get_json
- instance_report
- list_commands
- run_command
- show_log
- stack_state
詳細は以下。
http://docs.aws.amazon.com/opsworks/latest/userguide/agent.html
試しにget_jsonコマンドを実行してみた。(環境をまるさらしだが、すぐ消すので良しとしよう。。)
$ sudo opsworks/current/bin/opsworks-agent-cli get_json { "recipes": [ "opsworks_custom_cookbooks::load", "opsworks_ganglia::configure-client", "ssh_users", "agent_version", "haproxy::configure", "opsworks_stack_state_sync", "opsworks_custom_cookbooks::execute", "test_suite", "opsworks_cleanup" ], "opsworks_bundler": { "version": "1.0.10", "manage_package": null }, "opsworks_rubygems": { "version": "1.8.24" }, "opsworks": { "deployment": null, "valid_client_activities": [ "reboot", "stop", "setup", "configure", "update_dependencies", "install_dependencies", "update_custom_cookbooks", "execute_recipes" ], "applications": [ { "application_type": "php", "slug_name": "php_app", "name": "php-app" } ], "ruby_version": "1.8.7", "activity": "configure", "ruby_stack": "ruby_enterprise", "stack": { "name": "SampleStack3" }, "instance": { "ip": null, "id": "3d9fc521-1bfa-4d82-b74c-f0761383394c", "private_ip": "10.132.159.132", "aws_instance_id": "i-d75147d4", "backends": 5, "layers": [ "lb" ], "public_dns_name": null, "hostname": "stratus", "region": "ap-northeast-1", "instance_type": "m1.small", "availability_zone": "ap-northeast-1a", "private_dns_name": "ip-10-132-159-132.ap-northeast-1.compute.internal", "architecture": "i386" }, "agent_version": "104", "layers": { "lb": { "id": "f64dc03e-0c01-4f41-b588-b8289e03deca", "name": "Load Balancer", "instances": { "stratus": { "ip": null, "elastic_ip": "54.249.245.56", "private_ip": "10.132.159.132", "aws_instance_id": "i-d75147d4", "created_at": "2013-03-13T06:38:58+00:00", "status": "online", "booted_at": "2013-03-13T06:40:49+00:00", "backends": 5, "public_dns_name": null, "region": "ap-northeast-1", "instance_type": "m1.small", "availability_zone": "ap-northeast-1a", "private_dns_name": "ip-10-132-159-132.ap-northeast-1.compute.internal" } } }, "php-app": { "id": "c38c9a7f-90eb-4764-8ebd-3b48592f57cb", "name": "PHP Application Server", "instances": { "altocumulus": { "ip": "54.249.207.244", "elastic_ip": null, "private_ip": "10.162.54.166", "aws_instance_id": "i-2b5d4b28", "created_at": "2013-03-13T06:48:12+00:00", "status": "online", "booted_at": "2013-03-13T06:49:30+00:00", "backends": 5, "public_dns_name": "ec2-54-249-207-244.ap-northeast-1.compute.amazonaws.com", "region": "ap-northeast-1", "instance_type": "m1.small", "availability_zone": "ap-northeast-1a", "private_dns_name": "ip-10-162-54-166.ap-northeast-1.compute.internal" }, "stratocumulus": { "ip": "54.249.153.134", "elastic_ip": null, "private_ip": "10.152.46.168", "aws_instance_id": "i-07405604", "created_at": "2013-03-13T07:30:05+00:00", "status": "online", "booted_at": "2013-03-13T07:31:22+00:00", "backends": 5, "public_dns_name": "ec2-54-249-153-134.ap-northeast-1.compute.amazonaws.com", "region": "ap-northeast-1", "instance_type": "m1.small", "availability_zone": "ap-northeast-1a", "private_dns_name": "ip-10-152-46-168.ap-northeast-1.compute.internal" } } } }, "sent_at": 1363160598, "rails_stack": { "name": null } }, "opsworks_custom_cookbooks": { "recipes": [ ], "enabled": false }, "deploy": { "php_app": { "memcached": { "host": null, "port": 11211 }, "rails_env": null, "migrate": false, "ssl_certificate_ca": null, "auto_bundle_on_deploy": true, "ssl_support": false, "domains": [ "php_app" ], "database": { "reconnect": true, "host": null, "database": "php_app", "password": null, "username": "root" }, "ssl_certificate": null, "symlink_before_migrate": { "config/opsworks.php": "opsworks.php" }, "application_type": "php", "symlinks": { }, "mounted_at": null, "restart_command": "echo 'restarting app'", "document_root": null, "sleep_before_restart": 0, "application": "php_app", "deploy_to": "/srv/www/php_app", "scm": { "user": null, "repository": "git://github.com/funasaki/helloworld-php.git", "password": null, "revision": null, "scm_type": "git", "ssh_key": null }, "ssl_certificate_key": null } }, "haproxy": { "static_applications": { }, "health_check_method": "GET", "rails_applications": { }, "nodejs_backends": [ ], "php_applications": { "php_app": { "domains": [ "php_app" ], "application_type": "php", "mounted_at": null } }, "enable_stats": true, "nodejs_applications": { }, "php_backends": [ { "ip": "10.162.54.166", "backends": 10, "name": "altocumulus" }, { "ip": "10.152.46.168", "backends": 10, "name": "stratocumulus" } ], "rails_backends": [ ], "stats_url": "/haproxy?stats", "static_backends": [ ], "stats_password": "mypassword", "stats_user": "opsworks", "health_check_url": "/" }, "ssh_users": { } }
さらにログファイルも当然だがあった。
$ ls /var/log/aws/opsworks
installer.log opsworks-agent.log user-data.log
opsworks-agent-cli.log updater.log
Amazon LinuxにZabbix 1.8.16をインストール
Amazon Linux 2012.09にZabbix 1.8.16をインストールしました。
そのときに以下のページを参照させて頂きました。
http://dev.classmethod.jp/cloud/aws/zabbix1_8_15-on-amazon-linux/