AWS CodeBuildで internet connectivity error になる件について
AWS CodeBuildで毎回Provisioningでfailedになり、エラーメッセージを見ると"Build does not have internet connectivity. Please check subnet network configuration."と表示されていました。
VPCのサブネットはpublic subnetを選択していて、Route tableもInternet gatewayにroutingされていて、Security Groupもすべてのtrafficを許可していたのに、なぜ繋がらないのだろう?と思っていたら、原因が分かりました。
CodeBuildでprovisionされるインスタンスには、Public IPが割り当てられないようです。そのためPrivate Subnetに立てて、NAT GatewayかNATインスタンス経由でインターネットにoutbound通信させないといけないようです。(気づくのに時間かかってしまった。。)
Amazon VPC で AWS CodeBuild を使用するには、NAT ゲートウェイまたは NAT インスタンスが必要です。これにより、AWS CodeBuild がパブリックエンドポイントに到達できるようになります (ビルド実行時に CLI コマンドを実行するなど)。AWS CodeBuild は、作成したネットワークインターフェイスに Elastic IP アドレスを割り当てることをサポートしていないため、NAT ゲートウェイまたは NAT インスタンスの代わりにインターネットゲートウェイを使用することはできません。パブリック IP アドレスの自動割り当ては、Amazon EC2 インスタンスによる起動以外で作成されたネットワークインタフェースに対しては Amazon EC2 ではサポートされていません。
Amazon CognitoのGetOpenIdTokenForDeveloperIdentityとGetCredentialsForIdentityを使ってみる。
Amazon CognitoのGetOpenIdTokenForDeveloperIdentityとGetCredentialsForIdentityを使ってみたときのサンプルコードのメモ。
Amazon Cognito側でのIdentity Poolの作成は済んでいる前提。
getOpenIdTokenForDeveloperIdentity
AWSCredentials credentials = new BasicAWSCredentials("XXXX", "XXXXX"); AmazonCognitoIdentityClient client = new AmazonCognitoIdentityClient(credentials); client.setEndpoint("cognito-identity.ap-northeast-1.amazonaws.com"); GetOpenIdTokenForDeveloperIdentityRequest getOpenIdTokenForDeveloperIdentityRequest = new GetOpenIdTokenForDeveloperIdentityRequest(); getOpenIdTokenForDeveloperIdentityRequest.setIdentityPoolId("ap-northeast-1:XXXX"); Map<String,String> logins = new HashMap<String,String>(); logins.put("login.mycompany.myapp","user1"); getOpenIdTokenForDeveloperIdentityRequest.setLogins(logins); GetOpenIdTokenForDeveloperIdentityResult getOpenIdTokenForDeveloperIdentityResult = client.getOpenIdTokenForDeveloperIdentity(getOpenIdTokenForDeveloperIdentityRequest);
getCredentialsForIdentity
GetCredentialsForIdentityRequest getCredentialsForIdentityRequest = new GetCredentialsForIdentityRequest(); Map<String,String> map = new HashMap<String,String>(); map.put("cognito-identity.amazonaws.com", getOpenIdTokenForDeveloperIdentityResult.getToken()); getCredentialsForIdentityRequest.setLogins(map); getCredentialsForIdentityRequest.setIdentityId(getOpenIdTokenForDeveloperIdentityResult.getIdentityId()); GetCredentialsForIdentityResult result = client.getCredentialsForIdentity(getCredentialsForIdentityRequest); Credentials userCredentials = result.getCredentials(); System.out.println("AccessKey: " + userCredentials.getAccessKeyId()); System.out.println("SecretKey: " + userCredentials.getSecretKey());
mod_rewriteを使ってAmazon S3のコンテンツを表示させる
自分で立てたWebサーバから静的コンテンツ(index.html等)を配信していた形を、元々のWebサーバのURLは変えずに、Amazon S3から配信させてみます。(事前にAmazon S3の該当バケットにindex.html等のコンテンツをアップしておきます。)
今回は、Amazon LinuxでApache HTTP Serverで試しました。
まずは、Apacheをインストール、起動します。
sudo yum install -y httpd sudo service httpd start
適当なindex.htmlファイルを/var/www/html以下に作成します。
動作するサーバにアクセスすると
curl http://ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com (自分で立てたサーバ上のコンテンツが表示)
次に、/etc/httpd/conf/httpd.confの中身を見て、mod_rewriteがロードされていることを確認します。
LoadModule rewrite_module modules/mod_rewrite.so
続けて、/etc/httpd/conf/httpd.confの中に以下を追記します。
<VirtualHost *:80> DocumentRoot /var/www/html ServerName ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com RewriteEngine On RewriteRule ^/(.*)$ http://xxxxx.s3-website-ap-northeast-1.amazonaws.com/$1 [P,L] </VirtualHost>
※上記例では、S3のWebサイトホスティングのURLに直接変更させています。
その後、Apacheを再起動して
sudo service httpd restart
同様にアクセスする。
curl http://ec2-xx-xx-xxx-xxx.ap-northeast-1.compute.amazonaws.com (xxxxx.s3-website-ap-northeast-1.amazonaws.com/index.htmlの内容が表示される)
httpd.confのRewriteRuleで末尾の[P,L]にしておくと、URLはec2-xx-xx-xxx-xxx...のままで、S3の画像を表示させます。末尾を[R,L]にすると、S3のURLにリダイレクトされました。
リダイレクト先のドメイン名は元々のS3のものを使っていますが、これをRoute 53等のDNSでカスタムドメインを登録して、カスタムドメインでS3へアクセスさせることも勿論できます。
この方法を使えば、アプリ側を特に修正を入れずに、URLにマッチした場合にS3の画像を表示させることができそうですね!(勿論rewriteさせる形ではなく、アプリ側を修正させる方が望ましいですけども。)
Amazon LinuxにRedisをインストール
yumを使ってRedisをAmazon Linuxにインストールしてみる。
$ sudo yum --enablerepo=epel install redis
redis起動
$ sudo /etc/init.d/redis start
redisの情報を確認
$ redis-cli -h localhost info redis_version:2.4.10 redis_git_sha1:00000000 redis_git_dirty:0 arch_bits:64 multiplexing_api:epoll gcc_version:4.4.6 process_id:17580 uptime_in_seconds:318 uptime_in_days:0 lru_clock:304917 used_cpu_sys:0.28 used_cpu_user:0.08 used_cpu_sys_children:0.00 used_cpu_user_children:0.00 connected_clients:1 connected_slaves:0 client_longest_output_list:0 client_biggest_input_buf:0 blocked_clients:0 used_memory:726192 used_memory_human:709.17K used_memory_rss:1847296 used_memory_peak:726328 used_memory_peak_human:709.30K mem_fragmentation_ratio:2.54 mem_allocator:jemalloc-2.2.5 loading:0 aof_enabled:0 changes_since_last_save:0 bgsave_in_progress:0 last_save_time:1387169174 bgrewriteaof_in_progress:0 total_connections_received:2 total_commands_processed:2 expired_keys:0 evicted_keys:0 keyspace_hits:0 keyspace_misses:0 pubsub_channels:0 pubsub_patterns:0 latest_fork_usec:0 vm_enabled:0 role:slave master_host:replica1.wfisum.0001.apne1.cache.amazonaws.com master_port:6379 master_link_status:down master_last_io_seconds_ago:-1 master_sync_in_progress:0 master_link_down_since_seconds:1387169493
AWS SDK for JavaScriptを使ってS3にダイレクトでファイルをアップロードする
AWS SDK for JavaScriptがリリースされました。これにより、例えばS3にJavaScriptのファイルを配置して、クライアントサイドでJavaScriptを実行、その中でS3へのファイルアップロードの処理を行えば、別途S3へのアップロードを行うためのWebサーバが必要なくなります。
まず、最初にJavaScriptを使って、S3のバケットに対してlistObjectsしてみます。そのJavaScriptを含むHTMLファイルが以下です。
<head> <title>Sample</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> </head> <body> <script src="https://sdk.amazonaws.com/js/aws-sdk-2.0.0-rc1.min.js"></script> <script type="text/javascript"> <!-- AWS.config.update({accessKeyId: 'ACCESS_KEY_ID', secretAccessKey: 'SECRET_ACCESS_KEY'}); var s3 = new AWS.S3({region: 'ap-northeast-1', maxRetries: 15}); s3.listObjects({Bucket: 'BUCKET_NAME'}, function(error, data) { if (error) { console.log(error); // an error occurred } else { console.log(data); // request succeeded } }); // --> </script> </body> </html>
これをWebサーバもしくはS3上に配置します。
この作業だけでは、Access Deniedと表示されてしまうので、さらに該当するバケットのCORS(Cross-Origin Resource Sharing)を設定します。CORSとは、S3とは違う他のドメインのサイトからS3上のリソースにアクセスするときの共有設定のことです。デフォルトではAllowedOriginが*のものに対して、GETメソッドのみ許可されているようです。それを以下のように変更します。
<CORSConfiguration> <CORSRule> <AllowedHeader>*</AllowedHeader> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>PUT</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>DELETE</AllowedMethod> <MaxAgeSeconds>3000</MaxAgeSeconds> <AllowedHeader>Authorization</AllowedHeader> </CORSRule> </CORSConfiguration>
※AllowedOriginを、特定のURL(例:http://www.example.com/)に限定すると、より権限を限定できて良いです。
FirefoxのプラグインのFirebugを使って、Console Logを見てみると、以下のようにコンテンツを取得できていることがわかりました。
次に本記事のメインとなるS3 Uploadを試してみます。
以下のようなs3upload.htmlを作成しました。
<html> <head> <title>Sample</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> </head> <body> <input type="file" id="file-chooser" /> <button id="upload-button">Upload to S3</button> <div id="results"></div> <script src="https://sdk.amazonaws.com/js/aws-sdk-2.0.0-rc1.min.js"></script> <script type="text/javascript"> <!-- AWS.config.update({accessKeyId: 'AKIAXXXXXXXXXXXXXX', secretAccessKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'}); var bucket = new AWS.S3({params: {Bucket: 'BUCKET-NAME'}}); var fileChooser = document.getElementById('file-chooser'); var button = document.getElementById('upload-button'); var results = document.getElementById('results'); button.addEventListener('click', function() { var file = fileChooser.files[0]; if (file) { results.innerHTML = ''; var params = {Key: file.name, ContentType: file.type, Body: file}; bucket.putObject(params, function (err, data) { results.innerHTML = err ? 'ERROR!' : 'UPLOADED.'; }); } else { results.innerHTML = 'Nothing to upload.'; } }, false); // --> </script> </body> </html>
作成したs3upload.htmlを表示すると以下のような画面が表示されます。
適当なテキストファイルを選択してアップロードすると、以下のように表示されます。
S3 Management Consoleで確認すると、確かにアップロードされてました!
あとは、JavaScript内にAWS Security Credential情報がベタ書きされてしまっているのを、以前書いた記事にあるようにSecurity Token Serviceを使って、一時的なTokenを使うようにすれば、権限を制限させることもできます。
以上ですー。
S3バケット以下の特定のフォルダにのみアクセス可能なIAMユーザをつくる
以下のJSONポリシーをIAMユーザに割り当てることで、examplebucket以下のfolder1にのみアクセス可能でした。
{ "Statement": [ { "Effect": "Allow", "Action": ["s3:*"], "Resource": ["arn:aws:s3:::examplebucket"], "Condition":{"StringLike":{"s3:prefix":["folder1/*"] } } } ] }
詳細はこちらをご覧ください。
http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingIAMPolicies.html
AWS OpsWorksでのカスタムAMIの利用について
AWS OpsWorksで2013年10月現在サポートされているAMIは、Amazon LinuxとUbuntu 12.04 LTSベースです。
これらを素のまま使うこともできますし、事前にEC2コンソールで起動して、必要なパッケージをインストールしておいて、それをOpsWorksで使うこともできます。
そこで、実際にOpsWorksでインスタンスを起動するときに、必要となるOpsWorks Agentをどうやってインストールさせているんだろう?と疑問に思っていたのですが、解決しました!
Cloud-initを使ってインストールさせてるんですね。
OpsWorksで起動したインスタンスを、EC2 ConsoleでUser Dataをみたところ、OpsWorks Agentをインストールさせるためのスクリプトが書かれていました。
なるほど!これで納得。
逆にCloud-initを使ってOpsWorks Agentをインストール・動作できるのであれば、他のOSのイメージででも技術的には動作可能かもしれないですね。(サポートの有無はまた別の話として。)
詳細はこちら。
http://docs.aws.amazon.com/opsworks/latest/userguide/workinginstances-custom-ami.html