- 投稿日:2019-05-30T22:11:44+09:00
S3にアップロードした画像をAjaxで取得する
やりたいこと
デバッグ等で、S3にアップロードされているかどうかをチェックした後に、
URLを差し替えて画像を表示したいというニッチなニーズに遭遇。普通に何も考えずに実装すると
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
という感じに怒られる。
ちゃんとヘッダを設定して、クロスドメインを有効にしましょうという話。S3のCORS設定
<?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <MaxAgeSeconds>3000</MaxAgeSeconds> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>AjaxでcrossDomainを有効にする
_.each($('.s3-image-debugger'), function(img, _idx) { var $img = $(img); var src = $img.attr('src'); if (src && src.match(/s3-ap-northeast-1.amazonaws.com/)) { $.get(src, {crossDomain: true}).fail(function() { $img.attr('src', src.replace('production', 'test')); }); } });かなり急いで作ったので雑ですが、意図した挙動になりました
![]()
- 投稿日:2019-05-30T21:30:30+09:00
Lambda-backed カスタムリソースで最新Boto3 Lambda Layersを自動生成
背景
Python3でLambda Functionを書いて実行するとき、Boto3のドキュメントには使用方法が書かれているのに、いざ実行してみるとメソッドが存在していないと怒られることがあります。
例えば、RDSのstop_db_clusterは、LambdaのBoto3では実行できませんでした。(2019/5/30現在)sampledef stop_aurora_cluster(cluster_identifier): rds = boto3.client('rds') response = rds.stop_db_cluster(DBClusterIdentifier=cluster_identifier)上記関数をLambda Functionで実行すると、以下のような結果となり、失敗します。
'RDS' object has no attribute 'stop_db_cluster': AttributeError (略) AttributeError: 'RDS' object has no attribute 'stop_db_cluster'しかしboto3のドキュメントには、stop_db_clusterが定義されています。
メソッドは存在するのに、Lambdaからは使えないという状況です。何故このようなことが起こるのかというと、Lambdaに搭載されているBoto3のバージョンが、Boto3の最新バージョンに比べて古いからです。
ただ、Lambda Layersを使えば、最新のBoto3を利用できます。
詳しくは以下のリンク先が参考になりますが、Lambdaに標準搭載されているBoto3を、より新しいバージョンのBoto3のLambda Layersで上書きするという方法です。
(参考) Lambda Layers で最新の AWS SDK を使用するこのように、最新のBoto3を使いたい場合、都度、上記リンク先の方法でLambda Layersを作る必要がありますが、これは面倒な作業です。
zipを作ってマネジメントコンソールでアップロードするのも、zipをS3にアップロードしてCloudFormationのテンプレートを実行するのも、手作業が多くて非常に面倒です。
- Lambda Functionをデプロイする度にLambda Layersをデプロイし直すのは嫌だ!
- なんとか自動でやりたい!
ということで、
- 最新のBoto3 Lambda Layersを自動で生成するLambda Function
を作ってしまいました。
このLambda FunctionをAWS CloudFormationのLambda-backed カスタムリソース (CloudFormationのテンプレート実行時に、テンプレート内でLambda Functionを実行できる機能) で実行すれば、毎回、自動で最新のBoto3 Lambda Layersを生成し、別のLambda Functionにアタッチすることができるようになります。
CloudFormationテンプレート(YAML)
S3 Bucketを作っておきます。
ぶっちゃけそのS3 BucketもCloudFormationで作れば良いのですが、今回は割愛しています。IAM Role
Lambda Functionで実行するs3:PutObjectを許可します。
DeployNewestBoto3LambdaLayerRole: Type: AWS::IAM::Role Properties: RoleName: "DeployNewestBoto3LambdaLayerRole" AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: "DeployNewestBoto3LambdaLayerPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Sid: "DeployNewestBoto3LambdaLayerPolicy" Action: - s3:PutObject Resource: "*"Lambda Function (Boto3 Lambda Layersを生成するLambda Function)
まず、Lambda-backed カスタムリソースのお作法として、cfnresponse.send()でシグナルを返すというものがあります。
Pythonのコードにcfnresponse.send()を書き忘れますと、CloudFormationのテンプレートを実行したとき、カスタムリソースの作成時にLambda Functionからシグナルが返らずに1時間待ちとなって、果てには作成に失敗します。
さらに、ロールバック時にもLambda Functionが実行され、そこでもシグナルが返らずに1時間待ちとなって、合計2時間待ちを食らいます。
次に、以下のコードでどのようにLambda Layersを生成しているかですが、まず、Lambda FunctionがLinux上で動いていることを利用し、Lambda Functionに書き込み権限がある/tmpに、subprocessとpipを駆使して、最新Boto3を書き込んでいます。
その後Boto3をzipに圧縮しますが、Lambda Functionが動くLinuxではzipコマンドが使えなかったため、Pythonのshutilを使って圧縮しています。
(ちなみにgzip等は使えるようです。何故標準zipが使えないかはよく分かりません。。。)最後の処理で、出来上がったzipファイルをS3にアップロードします。
DeployNewestBoto3LambdaLayerLambdaFunction: Description: "To make the newest boto3 Lambda Layers and upload it to s3 bucket" Type: "AWS::Lambda::Function" Properties: Code: ZipFile: !Sub | import boto3 import subprocess import shutil import cfnresponse # Lambdaハンドラ def lambda_handler(event, context): try: # 引数(event)チェック if event['RequestType'] == 'Delete': cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) return # mkdir /tmp/python mkdir_command = subprocess.run(["mkdir", "/tmp/python"], stdout = subprocess.PIPE, stderr = subprocess.PIPE) # pip install -t /tmp/python/ boto3 boto3_install_command = subprocess.run(["pip", "install", "-t", "/tmp/python", "boto3"], stdout = subprocess.PIPE, stderr = subprocess.PIPE) # ls -al /tmp/python | grep boto3- | grep .dist-info ls_boto3_command = subprocess.Popen(["ls", "/tmp/python"], stdout = subprocess.PIPE, stderr = subprocess.PIPE) grep_boto3_command = subprocess.Popen(["grep", "boto3-"], stdin = ls_boto3_command.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE) dist_boto3_command = subprocess.Popen(["grep", ".dist-info"], stdin = grep_boto3_command.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE) dist_boto3_command_stdout = dist_boto3_command.communicate()[0] boto3_version_str = dist_boto3_command_stdout.decode('utf-8').replace('boto3-', '').replace('.dist-info', '').replace('\n', '') ls_boto3_command.stdout.close() grep_boto3_command.stdout.close() dist_boto3_command.stdout.close() # /tmp/pythonをzipに圧縮 boto3_zip_path = '/tmp/boto-'+boto3_version_str shutil.make_archive(boto3_zip_path, 'zip', root_dir='/tmp/python') # ls /tmp/ ls_boto3_command = subprocess.run(["ls", "/tmp/"], stdout = subprocess.PIPE, stderr = subprocess.PIPE) # boto3-x.x.xxx.zip をS3にアップロード s3Resource = boto3.resource('s3') s3FileUpload = s3Resource.meta.client.upload_file(Filename=boto3_zip_path+'.zip', Bucket=event['ResourceProperties']['S3Bucket'], Key='boto3-'+boto3_version_str+'.zip') # 戻り値の生成 responseData = { 'S3Bucket': event['ResourceProperties']['S3Bucket'], 'S3Key': 'boto3-'+boto3_version_str+'.zip' } cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) except Exception: # 戻り値の生成 cfnresponse.send(event, context, cfnresponse.FAILED, {}) FunctionName: DeployNewestBoto3LambdaLayer Handler: index.lambda_handler MemorySize: 256 ReservedConcurrentExecutions: 1 Role: !GetAtt DeployNewestBoto3LambdaLayerRole.Arn Runtime: "python3.6" Timeout: 300 Tags: - Key: Name Value: 'DeployNewestBoto3LambdaLayer' - Key: CloudformationArn Value: !Ref 'AWS::StackId'カスタムリソース (Boto3 Lambda Layersを生成するLambda Functionを呼び出す)
Lambda Functionが書ければ、カスタムリソースはこれだけです。
ServiceTokenでLambda Functionを指定して呼び出します。
S3Bucketは、Lambda Functionの内部で利用するための独自パラメータです。NewestBoto3LambdaLayerCustomResource: Type: Custom::NewestBoto3LambdaLayerCustomResource Properties: ServiceToken: !GetAtt DeployNewestBoto3LambdaLayerLambdaFunction.Arn S3Bucket: 'hogehoge'Lambda Layers
カスタムリソースの実行が正常に完了すると、Lambda LayersのzipファイルがアップロードされたS3 Bucket名と、zipファイルのパスを示すS3Keyが返ります。
これらの値をContentのパラメータに代入します。NewestBoto3LambdaLayer: Type: "AWS::Lambda::LayerVersion" Properties: Description: !GetAtt NewestBoto3LambdaLayerCustomResource.S3Key CompatibleRuntimes: - python3.6 - python3.7 Content: S3Bucket: !GetAtt NewestBoto3LambdaLayerCustomResource.S3Bucket S3Key: !GetAtt NewestBoto3LambdaLayerCustomResource.S3Key LayerName: 'boto3-python-layer'テンプレート実行結果
CloudFormationテンプレート内で、Lambda Layersを生成するLambda FunctionがLambda-backed カスタムリソースとして実行され、結果、最新のBoto3を含むLambda Layersがアップロードされました。
まとめ
Lambda Layersを生成するLambda FunctionをLambda-backed カスタムリソースで実行することで、Lambda Layersを手作業で作ることなく、自動生成できるようになりました。
手作業でのzip圧縮&アップロードがなくなり、AttributeErrorも回避でき、非常に快適となります。
- 投稿日:2019-05-30T21:30:30+09:00
Lambda-backed カスタムリソースでLambdaのBoto3を自動で最新化する
背景
AWSのLambda FunctionをPython3で書いて実行するとき、Boto3のドキュメントには使用方法が書かれているのに、いざ実行してみるとメソッドが存在していないと怒られることがあります。
例えば、RDSのstop_db_clusterは、LambdaのBoto3では実行できませんでした。(2019/5/30現在)sampledef stop_aurora_cluster(cluster_identifier): rds = boto3.client('rds') response = rds.stop_db_cluster(DBClusterIdentifier=cluster_identifier)上記関数をLambda Functionで実行すると、以下のような結果となり、失敗します。
'RDS' object has no attribute 'stop_db_cluster': AttributeError (略) AttributeError: 'RDS' object has no attribute 'stop_db_cluster'しかしboto3のドキュメントには、stop_db_clusterが定義されています。
メソッドは存在するのに、Lambdaからは使えないという状況です。何故このようなことが起こるのかというと、Lambdaに搭載されているBoto3のバージョンが、Boto3の最新バージョンに比べて古いからです。
ただ、Lambda Layersを使えば、最新のBoto3を利用できます。
詳しくは以下のリンク先が参考になりますが、Lambdaに標準搭載されているBoto3を、より新しいバージョンのBoto3のLambda Layersで上書きするという方法です。
(参考) Lambda Layers で最新の AWS SDK を使用するこのように、最新のBoto3を使いたい場合、都度、上記リンク先の方法でLambda Layersを作る必要がありますが、これは面倒な作業です。
zipを作ってマネジメントコンソールでアップロードするのも、zipをS3にアップロードしてCloudFormationのテンプレートを実行するのも、手作業が多くて非常に面倒です。
- Lambda Functionをデプロイする度にLambda Layersを手動でデプロイし直すのは嫌だ!
- なんとか自動でやりたい!
ということで、
- 最新のBoto3 Lambda Layersを自動で生成するLambda Function
を作ってしまいました。
このLambda FunctionをAWS CloudFormationのLambda-backed カスタムリソース (CloudFormationのテンプレート実行時に、テンプレート内でLambda Functionを実行できる機能) で実行すれば、毎回、自動で最新のBoto3 Lambda Layersを生成し、別のLambda Functionにアタッチすることができるようになります。
CloudFormationテンプレート(YAML)
S3 Bucketを作っておきます。
ぶっちゃけそのS3 BucketもCloudFormationで作れば良いのですが、今回は割愛しています。IAM Role
Lambda Functionで実行するs3:PutObjectを許可します。
DeployLatestBoto3LambdaLayerRole: Type: AWS::IAM::Role Properties: RoleName: "DeployLatestBoto3LambdaLayerRole" AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - sts:AssumeRole Path: "/" ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: "DeployLatestBoto3LambdaLayerPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Sid: "DeployLatestBoto3LambdaLayerPolicy" Action: - s3:PutObject Resource: "*"Lambda Function (Boto3 Lambda Layersを生成するLambda Function)
まず、Lambda-backed カスタムリソースのお作法として、cfnresponse.send()でシグナルを返すというものがありますので、必ず書くようにします。
Pythonのコードにcfnresponse.send()を書き忘れますと、CloudFormationのテンプレートを実行したとき、カスタムリソースの作成時にLambda Functionからシグナルが返らずに1時間待ちとなって、果てには作成に失敗します。
さらに、ロールバック時にもLambda Functionが実行され、そこでもシグナルが返らずに1時間待ちとなって、合計2時間待ちを食らいます。
次に、以下のコードでどのようにLambda Layersを生成しているかですが、まず、Lambda FunctionがLinux上で動いていることを利用し、Lambda Functionに書き込み権限がある/tmpに、subprocessとpipを駆使して、最新Boto3を書き込んでいます。
その後Boto3をzipに圧縮しますが、Lambda Functionが動くLinuxではzipコマンドが使えなかったため、Pythonのshutilを使って圧縮しています。
(ちなみにgzip等は使えるようです。何故標準zipが使えないかはよく分かりません。。。)最後の処理で、出来上がったzipファイルをS3にアップロードします。
DeployLatestBoto3LambdaLayerLambdaFunction: Description: "To make the Latest boto3 Lambda Layers and upload it to s3 bucket" Type: "AWS::Lambda::Function" Properties: Code: ZipFile: !Sub | import boto3 import subprocess import shutil import cfnresponse # Lambdaハンドラ def lambda_handler(event, context): try: # 引数(event)チェック if event['RequestType'] == 'Delete': cfnresponse.send(event, context, cfnresponse.SUCCESS, {}) return # mkdir /tmp/python mkdir_command = subprocess.run(["mkdir", "/tmp/python"], stdout = subprocess.PIPE, stderr = subprocess.PIPE) # pip install -t /tmp/python/ boto3 boto3_install_command = subprocess.run(["pip", "install", "-t", "/tmp/python", "boto3"], stdout = subprocess.PIPE, stderr = subprocess.PIPE) # ls -al /tmp/python | grep boto3- | grep .dist-info ls_boto3_command = subprocess.Popen(["ls", "/tmp/python"], stdout = subprocess.PIPE, stderr = subprocess.PIPE) grep_boto3_command = subprocess.Popen(["grep", "boto3-"], stdin = ls_boto3_command.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE) dist_boto3_command = subprocess.Popen(["grep", ".dist-info"], stdin = grep_boto3_command.stdout, stdout = subprocess.PIPE, stderr = subprocess.PIPE) dist_boto3_command_stdout = dist_boto3_command.communicate()[0] boto3_version_str = dist_boto3_command_stdout.decode('utf-8').replace('boto3-', '').replace('.dist-info', '').replace('\n', '') ls_boto3_command.stdout.close() grep_boto3_command.stdout.close() dist_boto3_command.stdout.close() # /tmp/pythonをzipに圧縮 boto3_zip_path = '/tmp/boto-'+boto3_version_str shutil.make_archive(boto3_zip_path, 'zip', root_dir='/tmp/python') # ls /tmp/ ls_boto3_command = subprocess.run(["ls", "/tmp/"], stdout = subprocess.PIPE, stderr = subprocess.PIPE) # boto3-x.x.xxx.zip をS3にアップロード s3Resource = boto3.resource('s3') s3FileUpload = s3Resource.meta.client.upload_file(Filename=boto3_zip_path+'.zip', Bucket=event['ResourceProperties']['S3Bucket'], Key='boto3-'+boto3_version_str+'.zip') # 戻り値の生成 responseData = { 'S3Bucket': event['ResourceProperties']['S3Bucket'], 'S3Key': 'boto3-'+boto3_version_str+'.zip' } cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData) except Exception: # 戻り値の生成 cfnresponse.send(event, context, cfnresponse.FAILED, {}) FunctionName: DeployLatestBoto3LambdaLayer Handler: index.lambda_handler MemorySize: 256 ReservedConcurrentExecutions: 1 Role: !GetAtt DeployLatestBoto3LambdaLayerRole.Arn Runtime: "python3.6" Timeout: 300 Tags: - Key: Name Value: 'DeployLatestBoto3LambdaLayer' - Key: CloudformationArn Value: !Ref 'AWS::StackId'カスタムリソース (Boto3 Lambda Layersを生成するLambda Functionを呼び出す)
Lambda Functionが書ければ、カスタムリソースはこれだけです。
ServiceTokenでLambda Functionを指定して呼び出します。
S3Bucketは、Lambda Functionの内部で利用するための独自パラメータです。LatestBoto3LambdaLayerCustomResource: Type: Custom::LatestBoto3LambdaLayerCustomResource Properties: ServiceToken: !GetAtt DeployLatestBoto3LambdaLayerLambdaFunction.Arn S3Bucket: 'hogehoge'Lambda Layers
カスタムリソースの実行が正常に完了すると、Lambda LayersのzipファイルがアップロードされたS3 Bucket名と、zipファイルのパスを示すS3Keyが返ります。
これらの値をContentのパラメータに代入します。LatestBoto3LambdaLayer: Type: "AWS::Lambda::LayerVersion" Properties: Description: !GetAtt LatestBoto3LambdaLayerCustomResource.S3Key CompatibleRuntimes: - python3.6 - python3.7 Content: S3Bucket: !GetAtt LatestBoto3LambdaLayerCustomResource.S3Bucket S3Key: !GetAtt LatestBoto3LambdaLayerCustomResource.S3Key LayerName: 'boto3-python-layer'テンプレート実行結果
CloudFormationテンプレート内で、Lambda Layersを生成するLambda FunctionがLambda-backed カスタムリソースとして実行され、結果、最新のBoto3を含むLambda Layersがアップロードされました。
まとめ
Lambda Layersを生成するLambda FunctionをLambda-backed カスタムリソースで実行することで、Lambda Layersを手作業で作ることなく、自動生成できるようになりました。
手作業でのzip圧縮&アップロードがなくなり、AttributeErrorも回避でき、非常に快適となります。また、Lambda Function内でLinuxのコマンドが実行できること、特にpipが実行できることは、覚えておけば今後何かと役に立ちそうです。
Boto3に限らず、あらゆるパッケージをCI/CDパイプラインの中で自動的にLambda Layers化できると、非常に捗ります。
- 投稿日:2019-05-30T20:49:51+09:00
Amazon Linux 2でrbenvを使う
環境
- Amazon Linux 2
インストール手順
install# 0. 依存ライブラリのインストール $ sudo yum install -y git gcc openssl-devel readline-devel zlib-devel # 1. レポジトリをクローン $ git clone https://github.com/rbenv/rbenv.git ~/.rbenv # 2. .bash_profileにパスを追記 $ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile # 3. init $ ~/.rbenv/bin/rbenv init # 4. Restart your shell so that PATH changes take effect. $ exec $SHELL -l # 5. プラグイン用ディレクトリ $ mkdir -p "$(rbenv root)"/plugins # 6. rbenv installができるように $ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build # 7. 確認 $ curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash使い方
usage# インストールできるバージョンを確認 $ rbenv install -l # 指定バージョンのRubyをインストール $ rbenv install 2.5.1 # グローバルのバージョン指定 $ rbenv global 2.5.1 # インストールされているバージョンを確認 $ rbenv versions参考
- 投稿日:2019-05-30T18:30:31+09:00
AlibabaCloud E-MapReduce(EMR)を試してみた ~プロダクト紹介編~(and AWS比較)
はじめに
Alibaba Cloud E-MapReduce(EMR)を試してみました。
EMRは大量のデータの分析と処理を可能にするビッグデータ処理ソリューションです。
HadoopやEMR自体の説明はすでにWeb上に良質な記事がたくさんありますので、
AlibabaのEMRに特化した説明とします。AlibabaCloudとは
Alibaba Cloudは日本国内では知る人ぞ知るクラウドサービスですが、
グローバルのマーケットシェアで見ると世界5位(Amazon, Microsoft, Google, IBMに次ぐ)
につけています。サービスラインナップとしては、AWS/Azureに匹敵するものを提供しています。
サービス比較としていい記事がありましたので興味のある方はどうぞ↓
[2019年5月版] Alibaba Cloudの中国版/国際版/日本版比較表(and AWS/Azure/GCP)また、アリババ自身がデータ活用を原動力にして成長してきたこともあり、
ビッグデータ系ではデータ収集〜データ管理/加工〜分析〜可視化まで包括的なサービスラインナップを提供しています。AlibabaCloud EMRのアーキテクチャ
AlibabaCloud EMRのアーキテクチャです。
OSSはオブジェクトストレージ(AWSでいうとS3)で、OSSをHDFSのように扱うことで計算資源とストレージを分離することができます。
(この点はAWS EMRと同じです)AlibabaCloud EMRで使用可能なコンポーネント
AlibabaCloud EMRで使用可能なコンポーネントとバージョンの一覧を掲載します。
AWSとの比較もしています。
コンポーネント Alibaba EMR-3.20.0 AWS EMR-5.23.0 Hadoop 2.8.5 2.8.5 YARN 2.8.5 2.8.5 Hive 3.1.1 2.3.4 Spark 2.4.2 2.4.0 Knox 1.1.0 - Zeppelin 0.8.1 0.8.1 Tez 0.9.1 0.9.1 ApacheDS 2.0.0 - Ganglia 3.7.2 3.7.2 Pig 0.14.0 0.17.0 Sqoop 1.4.7 1.4.7 Hue 4.1.0 4.3.0 HBase 1.4.9 1.4.9 ZooKeeper 3.4.13 3.4.13 Presto 0.213 0.215 Impala 2.12.2 - Flume 1.8.0 - Livy 0.6.0 0.5.0 Superset 0.28.1 - Ranger 1.2.0 - Flink 1.7.2 1.7.1 Storm 1.2.2 - Phoenix 4.14.1 4.14.1 SmartData 1.0.0 - Bigboot 1.0.0 - Oozie 5.1.0 5.1.0 Kafka 1.1.1 - Kafka-Manager 1.3.3.16 - Spark 2.3.2 2.4.0 Analytics Zoo 0.2.0 - Jupyter 4.4.0 5.7.0 TensorFlow 1.8.0 1.12.0 Druid 0.13.0 - JupyterHub - 0.9.4 MXNet - 1.3.1 HCatalog - 2.3.4 Mahout - 0.13.0 ご覧の通り、ほとんどAWSと同等かそれ以上のコンポーネントが揃っています。
ImpalaなどAWSにはないコンポーネントもあり、コンポーネントの豊富さは隠れた嬉しいポイントかもしれません。AWSと比較した、Alibaba Cloud EMRの使いどころ
上記の通り、AlibabaCloud EMRではAWSとほとんど同じコンポーネントが使えます。
EMRはHadoop等のオープンソースのマネージドサービスであることもあり、
ユースケースにそれほど差異はないと言えると思います。ではどんな時にAlibabaCloudを使うか?
使い所としては、AlibabaCloudのコストの安さに着目するのもありと思います。上記の記事で検証されていますが、従量課金のCPU特化インスタンスとしては、
Alibabaが最もコストパフォーマンスが高いです。EMRはステップ実行としてオンデマンド(従量課金)でインスタンスを利用することが多いと思いますので、
この場合はAlibabaCloudでコストを削減可能なケースも多いかと思います。次回予告
次回は実際にAlibaba EMRでクラスターを作成してみます!
- 投稿日:2019-05-30T16:48:00+09:00
Rancherコンテナー管理プラットフォームのノードテンプレート(AWS)の設定手順
ノードテンプレートの設定手順
・右上の「User Preferences」のアイコンをクリックして、「Node Templates」をクリックします。
・開く「Node Templates」画面に、右上の「Add Template」ボタンをクリックします
・開く「Add Node Templates」画面に、下記の画面のように情報を入力して、「Create」ボタンをクリックします
参照:awsのAccess key とSecret Keyの作成/取得方法で検証用のIAMユーザーから取得してください。
・次の画面に、クラスターノードを作成する場所のAZとVPCを選択して、「Next:...」ボタンをクリックします
・次の画面に、「Security Groups」にStandardを選択して、「Next:...」ボタンをクリックします
・次の画面に、下記の画面のように情報を入力して、「Create」ボタンをクリックします
「IAM Instance Profile Name」について、AWSのCloud Provider機能(AWSのロードバランサによるK8sサービス作成や、AWS EBSによる永続化Volume作成)を使用する場合のみ、設定が必要です。初心者レベルのハンズオンの場合は不要です。
参照:IAM Instance Profile Nameの作成/取得方法で取得してください。
- 投稿日:2019-05-30T15:54:56+09:00
An error occurred (AccessDenied) when calling the CopyObject operation がでた時の対処法
事象
IAMユーザー単位で閲覧/保存制御をかけているS3バケットが本番用とstg用で2つあります。
日時で本番からstg用にsyncをかけるのですが、
An error occurred (AccessDenied) when calling the CopyObject operationのエラーが出ました。対処
https://aws.amazon.com/jp/premiumsupport/knowledge-center/s3-troubleshoot-copy-between-buckets/
AWS公式をみてみると、ズバリ書いてありました。必要なことは4つです。
1. コピー元バケット: 使用している IAM ユーザーまたはロールによって s3:ListBucket と s3:GetObject の両方を許可
2. コピー先バケット: IAM ユーザーまたはロールによって s3:ListBucket と s3:PutObject の両方を許可
3. IAM ユーザー: s3:ListBucket および s3:GetObject をコピー元バケットへ許可
4. IAM ユーザー: s3:ListBucket と s3:PutObject をコピー先バケットへ許可私の場合、IAMロールはs3への全権限が与えられていたので、バケットポリシーだけを見直しました。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "huge", "Effect": "Deny", "NotPrincipal": { "AWS": "hogehoge" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::hogebucket-production/*" } ] }{ "Version": "2012-10-17", "Statement": [ { "Sid": "huge", "Effect": "Deny", "NotPrincipal": { "AWS": "hogehoge" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::hogebucket-staging/*" } ] }なぜだかわからないですが、
s3:ListBucketはなくてもあってもいけたので消しました。実行
コピー元 コピー先の順で指定します。
フォルダ指定もできます。
aws s3 sync s3://hogebucket-production/uploads/huga s3://hogebucket-staging/uploads/huga余談
バケットポリシーの書き方は初めは難しいですよね。
EffectとPrincipalの組み合わせとか。
"Version": "2012-10-17" てなんやとか。
- 投稿日:2019-05-30T15:54:56+09:00
An error occurred (AccessDenied) when calling the CopyObject operation とaction does not apply to any resource(s) in statement がでた時の対処法
事象
IAMユーザー単位で閲覧/保存制御をかけているS3バケットが本番用とstg用で2つあります。
日時で本番からstg用にsyncをかけるのですが、
An error occurred (AccessDenied) when calling the CopyObject operationのエラーが出ました。対処
https://aws.amazon.com/jp/premiumsupport/knowledge-center/s3-troubleshoot-copy-between-buckets/
AWS公式をみてみると、ズバリ書いてありました。必要なことは4つです。
1. コピー元バケット: 使用している IAM ユーザーまたはロールによって s3:ListBucket と s3:GetObject の両方を許可
2. コピー先バケット: IAM ユーザーまたはロールによって s3:ListBucket と s3:PutObject の両方を許可
3. IAM ユーザー: s3:ListBucket および s3:GetObject をコピー元バケットへ許可
4. IAM ユーザー: s3:ListBucket と s3:PutObject をコピー先バケットへ許可私の場合、IAMロールはs3への全権限が与えられていたので、バケットポリシーだけを見直しました。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "huge", "Effect": "Deny", "NotPrincipal": { "AWS": "hogehoge" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::hogebucket-production/*" } ] }{ "Version": "2012-10-17", "Statement": [ { "Sid": "huge", "Effect": "Deny", "NotPrincipal": { "AWS": "hogehoge" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::hogebucket-staging/*" } ] }なぜだかわからないですが、
s3:ListBucketはなくてもあってもいけたので消しました。
ちなみにs3:ListBucket権限を付与するのにはまた罠があり、Resourceを指定する時に/*をつけてはいけません。
考えてみればバケット単位の権限なのでまあそうかという感じなのですが、普通にハマりました。
間違えると、action does not apply to any resource(s) in statementというエラーが出てバケットを保存できません。
下記のように書いてみてください。{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::bucketname", ] } ] }実行
コピー元 コピー先の順で指定します。
フォルダ指定もできます。
aws s3 sync s3://hogebucket-production/uploads/huga s3://hogebucket-staging/uploads/huga余談
バケットポリシーの書き方は初めは難しいですよね。
EffectとPrincipalの組み合わせとか。
"Version": "2012-10-17" てなんやとか。
- 投稿日:2019-05-30T15:31:15+09:00
CloudWatch Events から Lambda (Python スクリプト) 経由で S3 から請求情報を取得して、SNS でメールを送ってみる
0.はじめに
コスト配分タグ毎の AWS の毎月のコストレポートが欲しくて、試してみました。
大枠の流れは、以下。
1.請求情報のコスト配分タグを付与して、S3 へ出力する。
基本的に、以下のページの手順に従って設定します。
- S3 を作成します。設定は、以下。
- バケット名 : ※任意
- バケットポリシー : ※以下参考。
{ "Version": "2008-10-17", "Id": "Policy0000000000000", "Statement": [ { "Sid": "Stmt0000000000000", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::0000000000000:root" }, "Action": [ "s3:GetBucketAcl", "s3:GetBucketPolicy" ], "Resource": "arn:aws:s3:::****-billing" }, { "Sid": "Stmt0000000000000", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::0000000000000:root" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::****-billing/*" } ] }- ※参考
- 作成した S3 へ、コスト配分タグを付与した請求データを出力します。設定は、以下。
- 作成した S3 へ、コスト配分タグを付与した請求データが出力されているか確認します。
2.SNS のトピックを作成し、所定のメールアドレスを登録する。
基本的に、以下のページの手順に従って設定します。
- SNS のトピックを作成します。設定は、以下。
- トピック名 : ※任意
- アクセスポリシー : ※以下参考。
{ "Version": "2008-10-17", "Id": "default_policy_ID", "Statement": [ { "Sid": "default_statement_ID", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": [ "SNS:GetTopicAttributes", "SNS:SetTopicAttributes", "SNS:AddPermission", "SNS:RemovePermission", "SNS:DeleteTopic", "SNS:Subscribe", "SNS:ListSubscriptionsByTopic", "SNS:Publish", "SNS:Receive" ], "Resource": "arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:SysOps-Lambda-Mnt", "Condition": { "StringEquals": { "AWS:SourceOwner": "XXXXXXXXXXXX" } } } ] }![]()
- 作成した SNS のトピックへ、所定のメールアドレスをサブスクリプションとして登録する。
- 登録後、作成した SNS のトピックへ、パブリッシュして SNS トピックが正常に設定されているか、確認する。
3.Lambda ファンクションを作成する。
- IAM ロールを作成します。設定は、以下。
- ロール名 : ※任意
- ポリシー名 : ※任意
- ポリシー : ※以下参考。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": [ "s3:GetObject", "sns:Publish", "s3:ListBucket" ], "Resource": [ "arn:aws:sns:*:XXXXXXXXXXXX:SysOps-Lambda-DLQ", "arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:SysOps-Lambda-Mnt", "arn:aws:s3:::gs-sysops-billing/*", "arn:aws:s3:::gs-sysops-billing" ] }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:CreateLogGroup", "logs:PutLogEvents" ], "Resource": "*" } ] }![]()
- Lambda ファンクションを作成します。設定は、以下。
- ファンクション名 : ※任意
- 実行ロール : ※作成した IAM ロール
- コード : ※以下参考。
#!/usr/bin/env python # -*- coding: utf-8-unix; -*- """AWS Lambda Function - Maintenance EC2 Images """ from __future__ import print_function import logging import boto3 import os import csv import zipfile import locale # 「python - aws lambda Unable to import module 'lambda_function': No module named 'requests' - Stack Overflow」 # https://stackoverflow.com/questions/48912253/aws-lambda-unable-to-import-module-lambda-function-no-module-named-requests from botocore.vendored import requests # 「Pythonで翌日や翌月みたいな日付の計算をする - Qiita」 # https://qiita.com/dkugi/items/8c32cc481b365c277ec2 from dateutil.relativedelta import relativedelta # 「Python 3 で少しだけ便利になった datetime の新機能 - Qiita」 # <https://qiita.com/methane/items/d7ac3c9af5a2c659bc51> from datetime import datetime, timezone, timedelta TimeZone = timezone(timedelta(hours=+9), 'JST') # 「Lambdaの本番業務利用を考える① - ログ出力とエラーハンドリング | ナレコムAWSレシピ」 # <https://recipe.kc-cloud.jp/archives/9968> logger = logging.getLogger() logLevelTable={'DEBUG':logging.DEBUG,'INFO':logging.INFO,'WARNING':logging.WARNING,'ERROR':logging.ERROR,'CRITICAL':logging.CRITICAL} if os.environ.get('logging_level') in logLevelTable : logger.setLevel(logLevelTable[os.environ['logging_level']]) else: logger.setLevel(logging.WARNING) # StartDateTime = datetime.now(TimeZone) # Csv BillingFieldNames = ( "InvoiceID", "PayerAccountId", "LinkedAccountId", "RecordType", "RecordId", "ProductName", "RateId", "SubscriptionId", "PricingPlanId", "UsageType", "Operation", "AvailabilityZone", "ReservedInstance", "ItemDescription", "UsageStartDate", "UsageEndDate", "UsageQuantity", "Rate", "Cost", "ResourceId", "user:Biz") # Aws BucketName = "\*\*\*\*-billing" ZipRearFormat = "-aws-billing-detailed-line-items-with-resources-and-tags-{0}-{1}.csv.zip" CsvRearFormat = "-aws-billing-detailed-line-items-with-resources-and-tags-{0}-{1}.csv" # Mail MailSubjectTemplate = "[{0}.{1}] AWS Monthly Cost Report" MailMessageTemplate00 = "\ \n\ ★{0}\n\ \n\ ■処理時間 : {1:%Y/%m/%d %H:%M:%S} ~ {2:%Y/%m/%d %H:%M:%S}\n\ \n\ ■総額\n\ ${3:>8} [約 ¥{4:>8}]\n\ \n\ ■内訳\ " MailMessageTemplate01 = "{0}\n\ ○{1}\n\ ${2:>8} [約 ¥{3:>8}]\ " MailMessageTemplate02 = "{0}\n\ \n\ ----\n\ " MailMessageTemplate03 = "{0}\n\ ■{1:<10} : ${2:>8} [約 ¥{3:>8}]\ " MailMessageTemplate04 = "{0}\n\ \n\ ----\n\ \n\ ※ $1 = ¥{1:>6}\n\ ※\n\ \n\ " # ------------------------------------------------------------------------------ # Billing Data Get # ------------------------------------------------------------------------------ def BillingDataGet(prmYear, prmMonth, prmBillingList): logging.info("prmYear:[%s]", prmYear) logging.info("prmMonth:[%s]", prmMonth) result = 0 try: s3 = boto3.resource('s3') bucket = s3.Bucket(BucketName) # Object Key key = None for object in bucket.objects.all(): if ZipRearFormat.format(prmYear, prmMonth) != object.key[-72:]: continue key = object.key break logging.info("key:[%s]", key) if key is None: raise Exception("No such key was found. key={}".format(key)) tmpZipPath = "/tmp/_tmp.zip" tmpCsvPath = "/tmp/_tmp.csv" # Download logging.info("tmpZipPath:[%s]", tmpZipPath) s3.Object(BucketName, key).download_file(tmpZipPath) # UnZip logging.info("UnZip") zf = zipfile.ZipFile(tmpZipPath, 'r') for f in zf.namelist(): if CsvRearFormat.format(prmYear, prmMonth) != f[-68:]: continue uzf = open(tmpCsvPath, 'wb') uzf.write(zf.read(f)) uzf.close() break zf.close() # Csv with open(tmpCsvPath) as csvfile: for row in csv.DictReader(csvfile, BillingFieldNames): if row["LinkedAccountId"] == "LinkedAccountId": continue prmBillingList.append(row) except Exception as e: logger.exception('Error dosomething: %s', e) result = 1 raise else: pass finally: pass return result # 「[小ネタ]為替レートを簡単に取得する - Qiita」 # <https://qiita.com/chromabox/items/a1323225bae146c80bec> def RateGet(): result = -1 try: Url = "https://www.gaitameonline.com/rateaj/getrate" r = requests.get(Url) r_quotes = r.json()["quotes"] for i in range(len(r_quotes)): if r_quotes[i].get("currencyPairCode") == "USDJPY": result = float(r_quotes[i].get("open")) break except Exception as e: logger.exception('Error dosomething: %s', e) result = -1 raise else: pass finally: pass return result # 「【Python】Lambdaからメールを送信 | ハックノート」 # https://hacknote.jp/archives/35679/ def MailSend(prmSubject, prmMessage): result = -1 try: sns = boto3.client('sns') response = sns.publish( TopicArn = 'arn:aws:sns:ap-northeast-1:454930157265:SysOps-Lambda-Mnt', Message = prmMessage, Subject = prmSubject ) except Exception as e: logger.exception('Error dosomething: %s', e) result = -1 raise else: pass finally: pass return result def lambda_handler(event, context): try: global StartDateTime StartDateTime = datetime.now(TimeZone) logger.info("StartDateTime:[%s]", StartDateTime) # logger.info("event:[%s]", event) logger.info("context:[%s]", context) # try: yyyymm = event.get("YYYYMM", "") tdatetime = datetime.strptime( yyyymm[0:4] + "-" + yyyymm[4:6] + "-01 01:00:00", '%Y-%m-%d %H:%M:%S') except ValueError: tdatetime = StartDateTime - relativedelta(months=1) logger.info("tdatetime:[%s]", tdatetime) yyyy = tdatetime.year mm = "{0:0>2}".format(tdatetime.month) logger.info("yyyy:[%s] mm:[%s]", yyyy, mm) # Billing Data Get tmpBliingList = [] BillingDataGet(yyyy, mm, tmpBliingList) # Rate Get rate = RateGet() logging.info("Rate:[%f]", rate) # ---------------------------------------------------------------------- # Summary # ---------------------------------------------------------------------- # Summary ProductName dicSummary = {} dicSummarySub = {} for i in range(len(tmpBliingList)): if "Sign up charge for subscription:" in tmpBliingList[i]["ItemDescription"]: logging.info("Sign up charge for subscription - tmpBliingList:[%s]", tmpBliingList[i]) dicSummarySub[tmpBliingList[i]["InvoiceID"]] = { "InvoiceID": tmpBliingList[i]["InvoiceID"], "ProductName": tmpBliingList[i]["ProductName"], "UsageType": tmpBliingList[i]["UsageType"], "UsageQuantity": tmpBliingList[i]["UsageQuantity"], tmpBliingList[i]["ItemDescription"] : { "RecordType": tmpBliingList[i]["RecordType"], "Cost": tmpBliingList[i]["Cost"], }, } continue if "Tax of type CT" in tmpBliingList[i]["ItemDescription"]: logging.info("Tax of type CT - tmpBliingList:[%s]", tmpBliingList[i]) if tmpBliingList[i]["InvoiceID"] in dicSummarySub: dicSummarySub[tmpBliingList[i]["InvoiceID"]][tmpBliingList[i]["ItemDescription"]] = { "RecordType": tmpBliingList[i]["RecordType"], "Cost": tmpBliingList[i]["Cost"], } continue if "Total amount for invoice" in tmpBliingList[i]["ItemDescription"]: if tmpBliingList[i]["InvoiceID"] in dicSummarySub: dicSummarySub[tmpBliingList[i]["InvoiceID"]][tmpBliingList[i]["ItemDescription"]] = { "RecordType": tmpBliingList[i]["RecordType"], "Cost": tmpBliingList[i]["Cost"], } continue # tmpDict = dicSummary tmpKey = tmpBliingList[i]["RecordType"] if not tmpKey in tmpDict: tmpDict[tmpKey] = {} # tmpDict = tmpDict[tmpKey] tmpKey = tmpBliingList[i]["ProductName"] if not tmpKey: tmpKey = tmpBliingList[i]["ItemDescription"] elif tmpKey == "Amazon Elastic Compute Cloud": if "EBS" in tmpBliingList[i]["UsageType"]: tmpKey = tmpKey + " - EBS" if not tmpKey in tmpDict: tmpDict[tmpKey] = {} tmpDict[tmpKey]["SumCost"] = 0.0 # tmpDict = tmpDict[tmpKey] tmpKey = tmpBliingList[i]["user:Biz"] if not tmpKey in tmpDict: tmpDict[tmpKey] = {} # tmpCost = float(tmpBliingList[i]["Cost"]) if tmpBliingList[i]["Cost"] else 0.0 if not "Cost" in tmpDict[tmpKey]: #tmpDict[tmpKey]["Count"] = 1 tmpDict[tmpKey]["Cost"] = tmpCost else: #tmpDict[tmpKey]["Count"] += 1 tmpDict[tmpKey]["Cost"] += tmpCost tmpDict["SumCost"] += tmpCost # Summary Biz tmpSumCostAllToRedistribute = 0.0 tmpSumCostAllNotToRedistribute = 0.0 dicSummaryBiz = {} for k1 in sorted(dicSummary["LineItem"]): for k2 in sorted(dicSummary["LineItem"][k1]): if not isinstance(dicSummary["LineItem"][k1][k2], dict): continue if not k2 in dicSummaryBiz: dicSummaryBiz[k2] = {} dicSummaryBiz[k2]["SumCost"] = 0.0 dicSummaryBiz[k2]["SumCostRedistributed"] = 0.0 if not k1 in dicSummaryBiz[k2]: dicSummaryBiz[k2][k1] = {} dicSummaryBiz[k2][k1]["Cost"] = dicSummary["LineItem"][k1][k2]["Cost"] dicSummaryBiz[k2]["SumCost"] += dicSummary["LineItem"][k1][k2]["Cost"] if not k2: tmpSumCostAllToRedistribute += dicSummary["LineItem"][k1][k2]["Cost"] continue if k2 == "Share": tmpSumCostAllToRedistribute += dicSummary["LineItem"][k1][k2]["Cost"] continue tmpSumCostAllNotToRedistribute += dicSummary["LineItem"][k1][k2]["Cost"] for k1 in sorted(dicSummaryBiz): if not k1: continue if k1 == "Share": continue dicSummaryBiz[k1]["SumCostRedistributed"] = dicSummaryBiz[k1]["SumCost"] + ( tmpSumCostAllToRedistribute * dicSummaryBiz[k1]["SumCost"]) / tmpSumCostAllNotToRedistribute logging.info("dicSummaryBiz:[%s]", dicSummaryBiz) # ---------------------------------- # Mail Create Message # ---------------------------------- locale.setlocale(locale.LC_NUMERIC, 'ja_JP') # Subject tmpSubject = MailSubjectTemplate.format(yyyy, mm) # Message tmpCost = list(dicSummary["InvoiceTotal"].values())[0][""]["Cost"] tmpMessage = MailMessageTemplate00.format(tmpSubject, StartDateTime, datetime.now(TimeZone) , locale.format('%.2f', tmpCost, True), locale.format('%.0f', tmpCost*rate, True)) for k1 in sorted(dicSummary["LineItem"]): tmpCost = dicSummary["LineItem"][k1]["SumCost"] if tmpCost == 0: continue tmpMessage = MailMessageTemplate01.format(tmpMessage, k1 , locale.format('%.2f', tmpCost, True), locale.format('%.0f', tmpCost*rate, True)) tmpMessage = MailMessageTemplate02.format(tmpMessage) for k1 in sorted(dicSummaryBiz): if not k1: continue if k1 == "Share": continue tmpCost = dicSummaryBiz[k1]["SumCostRedistributed"] tmpMessage = MailMessageTemplate03.format(tmpMessage, k1 , locale.format('%.2f', tmpCost, True), locale.format('%.0f', tmpCost*rate, True)) tmpMessage = MailMessageTemplate04.format(tmpMessage, locale.format('%.2f', rate, True)) # Mail Send logging.info("tmpSubject:[%s]", tmpSubject) logging.info("tmpMessage:[%s]", tmpMessage) MailSend(tmpSubject, tmpMessage) except Exception as e: logger.exception('Error dosomething: %s', e) else: pass finally: pass # return "normal end"4.CloudWatch Events のルールを作成する。
- CloudWatch Events のルールを作成します。設定は、以下。
99.ハマりポイント
- うーん…。随分前に作成したので、覚えてない…。
XX.まとめ
今回、とりあえず残しておこうかなぁ、と思って記事書いたんですが、
こういう注意事項が表示されて…、
今後は、「AWS Cost and Usage Report」を使わないといけないのかなぁ。
うーん、また新しいやり方を考えなないといけないのだろうか…。
- 投稿日:2019-05-30T14:41:14+09:00
SAMでSQSをイベントソースとするLambdaを構築
しようとしたら失敗したので備忘録的に解決策を残しておこうと思いました。
AWS曰く
このクールなコードでビルドできるよ!
とのこと。AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Example of processing messages on an SQS queue with Lambda Resources: MySQSQueueFunction: Type: AWS::Serverless::Function Properties: Handler: handler Runtime: runtime Events: MySQSEvent: Type: SQS Properties: Queue: !GetAtt MySqsQueue.Arn BatchSize: 10 MySqsQueue: Type: AWS::SQS::Queue出典:AWS SAM Template for an Amazon SQS Application
そして失敗
エラー1:LambdaがSQSを呼ぶ権限がない
謎のエラーを吐いてCloudFormationが失敗に終わる。
The provided execution role does not have permissions to call ReceiveMessage on SQS (Service: AWSLambda; Status Code: 400; Error Code: InvalidParameterValueExceptionLambdaに設定したロールには AWSLambdaSQSQueueExecutionRole が付いている。
つまりIAMロールには(おそらく)問題がない。なぜ失敗する?
解決1:SQSにも権限の設定をする
冷静に考えたら、コンソールからキューを作る時も権限をいじった気がする。
というわけで、下記を追加する事で上述のエラーは出なくなった。MySqsQueuePolicy: Type: AWS::SQS::QueuePolicy Properties: PolicyDocument: Id: !Ref MySqsQueue Statement: - Sid: QueuePolicy-MySqsQueue Effect: Allow Principal: "*" Action: "SQS:*" Resource: Fn::GetAtt: - MySqsQueue - Arn Queues: - !Ref MySqsQueueエラー2:キューの可視性タイムアウトがLambdaのタイムアウトより短い
エラー1が解決したら、今度はこれが出るようになった。
コンソールから設定する時はあなたそんな事言わなかったじゃない。Queue visibility timeout: 30 seconds is less than Function timeout: 60 seconds解決2:可視性タイムアウトを伸ばす
明示的に指定しないとdefaultで30秒になるらしいので、キューのPropertiesにVisibilityTimeoutを設定する。
※Lambdaのタイムアウトを明示的に60秒にしていたので、もしかしたらその設定の影響かもしれない。MySqsQueue: Type: AWS::SQS::Queue Properties: VisibilityTimeout: 60結論
SQSキューポリシーと、可視性タイムアウトの設定をするとうまくいきました。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Example of processing messages on an SQS queue with Lambda Resources: MySQSQueueFunction: Type: AWS::Serverless::Function Properties: Handler: handler Runtime: runtime Timeout: 60 Events: MySQSEvent: Type: SQS Properties: Queue: !GetAtt MySqsQueue.Arn BatchSize: 10 MySqsQueue: Type: AWS::SQS::Queue Properties: VisibilityTimeout: 60 MySqsQueuePolicy: Type: AWS::SQS::QueuePolicy Properties: PolicyDocument: Id: !Ref MySqsQueue Statement: - Sid: QueuePolicy-MySqsQueue Effect: Allow Principal: "*" Action: "SQS:*" Resource: Fn::GetAtt: - MySqsQueue - Arn Queues: - !Ref MySqsQueue蛇足
実はこの前段階でCloudFormationの実行ロールにSQSの権限が無かった事により一度失敗している。
CodeStarを使っているため、自動で作成されるロールに何の疑問も持たなかった事が原因ですね。私は悲しい。
- 投稿日:2019-05-30T13:48:56+09:00
CodeBuildの中でブランチ名を利用する。
きっかけ
CodeBuildの中でブランチ名を取得し、
Dockerレジストリにプッシュしようとしたらうまくできなかった。[Container] 2019/05/30 03:11:39 Running command IMAGE_TAG=$(git branch | grep \* | cut -d ' ' -f2) [Container] 2019/05/30 03:11:39 Running command git branch * (no branch) develop masteroh...
no branch...取得方法
githubのwebhookからブランチ名が取れるので、それを活用した。
参考:
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-env-ref-env-vars.html環境変数[CODEBUILD_WEBHOOK_TRIGGER]は、ブランチにコミットをプッシュした場合、
[branch/branch-name]といった形で設定が入るらしい。以下の通り変数を参照すると、ブランチ名だけ取り出せる。
${CODEBUILD_WEBHOOK_TRIGGER#branch/}※[#branch/]は環境変数から、文字列[branch/]を取り除くために書いています。
この通り
[Container] 2019/05/30 04:34:54 Running command IMAGE_TAG=${CODEBUILD_WEBHOOK_TRIGGER#branch/} [Container] 2019/05/30 04:34:54 Running command echo ${CODEBUILD_WEBHOOK_TRIGGER#branch/} develop
- 投稿日:2019-05-30T12:45:29+09:00
CodeBuild設定メモ
命名例
dev-myservice-api-codebuild
ソース設定
今回はGitHub Organization内のprivate repositoryから取得。
personal access tokenで取得していたが、terraform化する時にsource.authがOAUTHしか指定出来なかったのでterraformではauth項目を設定せずにAWSコンソール上でpersonal access tokenを保存した。
personal access tokenは個人アカウントではなく、organization共用のアカウントを作ってそのユーザで生成したものを使う。環境
ビルドをカスタムイメージ上で行って実行用イメージをECRなどにpushする場合、docker in dockerよりもDockerのマルチステージビルドで行ったほうが色々楽だった。
ECRにpushする時にdocker in dockerだとAWS CLIのインストールから認証まで面倒を見ないといけないが、AWSが提供しているマネージドイメージであればbuildspec.yml内でAWS CLIが利用できるのでCodeBuildからECRへのログインが簡単になる。
あとビルドイメージ用のECRリポジトリを用意しなくてよくなる。動作させるサブネットを指定する時はプライベートサブネット。
VPC設定が正しいかどうかを「VPC設定の確認」というボタンで検証できるのでいちいちビルドしなくてもよい。
buildspec.yml内で利用する環境変数も設定出来る。Buildspec
CodeBuild標準は
プロジェクトルート/buildspec.ymlというファイルを置いておけば勝手に読み込んでくれるが、dev/stg/prdのような環境毎にファイルを分けることも出来る。
プロジェクトルート/codebuild/${env}-buildspec.ymlのようなディレクトリとファイルを作成している場合、CodeBuildのbuildspec名指定でどのファイルを読み込むか指定できる。
今回は環境毎にbuildspec.ymlを分けたが、基本的に一緒なのでbuildspec.yml自体は1つで各動作は環境変数で変更するようにしたほうが良さそう。
ビルド開始時にビルドするブランチを指定出来るが忘れることもありそうなので、buildspec.ymlでcheckoutする。(ビルド開始時のデフォルト設定も出来るようにしてほしい…)アーティファクト
成果物をS3に配置することも出来る。
今回はビルドしたイメージをECRにpushしたので使わない。(pushはbuildspec.yml内で行う)ログ
ビルド時のログを意図的に出力するようにしないと確認できないので設定する
ビルド開始
GitHubなどから取得している場合ソースバージョンが指定できる。
ブランチやタグなどが指定出来、developブランチはそのままdevelopと入力すればブランチが切り替わる。
buildspecでcheckoutするようにしているので普段は入力しなくてもいいが、buildspec.yml自体の変更がある時は明示しないといけない。
- 投稿日:2019-05-30T11:23:25+09:00
AWS Certified Solutions Architect 受験記
はじめに
長くやっているのですが、AWS資格は持っていない(過去に勉強しようと思ったことはあるものの。。。)ので、そろそろ資格取らないと思い、試験対策本も充実してきたところで、AWS Certified Solutions Architect (以下SAA)の取得にチャレンジしてみたので、何やったかをまとめましたです。
書いてる人の話
- 昔はJava,今はNode.js、Python、PHPを使ってるアプリエンジニアです
- ついでに言うと、史学科卒業(卒論は三国志)のちゃきちゃきの文系
- でも、ちょっとハード好き(自作PC派)。家にファイルサーバーもあったり(HPのProLiant MicroServer N54L)、ラズパイが2台ぐらい転がってる
- AWSは何気に東京リージョンくる前から仕事で使ってる
- 好きなAWSのサービスはAWS Lambda
- 今は停止状態だけど、ドメイン取得(管理はRoute53)して、AWS上で個人サイト構築してたり(そろそろ復活させたい)する
- 過去の仕事の関係で、なんとなく触っているサービスはそこそこ多し
AWS認定資格ってなんやねん(整理)
以下10つ。最近Alexaのスキルビルダーの資格増えたらしいですね。。。
詳しくは、以下参照。
AWS 認定やったこと
試験日決める
- ABEJAの村主さんがJAWS-UG 初心者支部#17 「AWS勉強しNight!」にて、試験日決める(つまり申し込む)と、謎のモチベーションが沸くって言うことを仰っていたので、倣いました
- が、人それぞれのようです。そこまでモチベーション上がりませんでした(ダメな人orz)
- 4月末に申し込みをしたので、勉強期間は1ヶ月弱です
試験対策本をひたすら読む
- 以下3冊買いました。(現状発売済みかつ新試験カリキュラム対応)
読んだ順番は、以下
1. AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト
2. 最短突破 AWS認定ソリューションアーキテクト アソシエイト 合格教本
3. 徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書GW特に遠出する予定もなかったので、GWは資格勉強だーと思ったものの、ちょっと出かけたりで進捗そこまで上がらず。。。
各本1回ずつ読んで、
各章についてる練習問題で間違ったり、読んでても理解が足りんなーと思うところは、
AWS BlackBeltの資料をみたりして、補いました。昔に比べれば、勉強し易くなりましたね。。。(遠い目)
個人的には、基本的には使ったことがないサービスはわからんなーと思うことが多く、以下が最後まで苦手にしてました。。
- Amazon EFS
- EBSのボリュームタイプの使い分け
- S3/Glacierのストレージタイプの使いわけ
- ネットワークACL
- Kinesisそれぞれの本のちょっとした感想
- AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト
- サービスカットになっていた。
- SAAの資格取得する上で、必要なサービスの基本的なことを学べた
- 最短突破 AWS認定ソリューションアーキテクト アソシエイト 合格教本
- AWS Well-Architectedのフレームワークカット
- ポイントが色づけされてて、読みやすく、AWS初心者向き
- 徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書
- こちらもAWS Well-Architectedのフレームワークカット
- 模擬問題が一番難しかった感覚(一番正解率低め(たまたまかもしれませんが))
いずれにも言えることですが、
資格試験対策本ですが、AWSとは?って言う感じで、AWS初心者が読むにはいいかなと思いました。
内容がかぶるところも多々ありますが、それぞれ色はあったかなと。あと、フレームワークカットの
「最短突破 AWS認定ソリューションアーキテクト アソシエイト 合格教本」
と
「徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書」
を読んでから、
サービスカットの
「AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト」
を読んでもよかったかも。練習・模擬問題
掲載数は以下の通り
- AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト・・・30問
- 最短突破 AWS認定ソリューションアーキテクト アソシエイト 合格教本・・・65問
- 徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書・・・65問(要ダウンロード)
とりあえず解説部分全部読んでから、3冊分一気にやりました。
上記しましたけど、「徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書」の模擬問題が一番難しかったです。あと、AWSの公式試験サンプルも解いてます。
試験サンプル
答えがすぐ横に書いてるので、できれば最後にまとめてほしいとか思うのは、贅沢でしょうかねwやらなかったこと
- 公式の模擬試験
- 受ける予定でしたが、試験勉強が遅れたので、受けている時間がなく。。。
- でも、受けておいた方がよかったなと思いました
- ハンズオン
- 結局勉強中はあまり触りませんでした。。。
- 家のNASの移行にEFS/Storage Gatewayとか使ってみるとか考えたぐらい。。。
試験
- 試験は試験センターで実施するわけで、PSI、ピアソンVUEのいずれかで可能です
- 私は過去にも使ったことがあり、最近追加されたピアソンVUEの会場で受験しました
- SAとして、こう言う案件の時、どーするっていう感じの問題が多く、時々うーむとなる問題がありましたね
- 上記もしましたが、設問の慣れという意味でも模擬試験は受けておいた方がよかったと反省
- 思ってより、さらに難しくなってない?って思ったのも事実(これは落ちるかも。。。と)
- もしかしたら、Cloud Practitionerが登場したことで、見直しされたんじゃないかという説
- (公式)[https://aws.amazon.com/jp/certification/certified-solutions-architect-associate/]とか、各種資料には試験時間130分となっていたのですが、140分になってました(問題数は同じ)
- ピアソンVUCだけ?
- 基本的には、一択が多く、複数選択はそんなになかったイメージでした
- 誤りを指摘する問題、模擬試験でも間違えてたんで、問題文ちゃんと読むの気をつけていたんですが、出てきませんでした
- あと、たまに日本語おかしくね?(そんなわけないでしょうけど)って箇所もあったので、念の為英語にしてみて読んでみたりとか
- 結局100分ぐらいかかりました
- 今日は全休にしていたので試験終わったら、羽田空港にAirForceOneの離陸を観に行こうかと思っていたのですが、間に合いませんでしたorz(超絶余談)
結果
画面に結果が出てて、しかも喜んだような気がするんですが、結果通知(スコアレポート)をみてからにしようと確信を持とうと待っておりますが、送られて来ず。。。
5/28 19:00ごろ試験サイトを確認したところ、合格してました!
結果としては、受験者スコア:836(合格スコア:720)でした。今後
次は以下のいずれかにしようかと思ってます。
- AWS Certified Developer
- AWS Certified Cloud PractitionerCloud PractitionerはSAA受かれば楽勝説もありますがどうでしょう???w。
とはいえ、落ちたら悲しいので、ちゃんと勉強します。。。
まとめ
- 昔勉強してて、挫折(試験すら受けなかった)して、それからの再チャレンジなので、5年越しぐらいwでの合格でした
- AWS Well-Architectedはまだまだ理解不足なところあるので、引き続き勉強
- 業務で触っている部分はなんとかなった気がするので、実務(もしくは、ハンズオンなどで実際にサービス使ってみる)は大事
- AWS BlackBeltの読み直しとても重要
** 最後に資格取得本の著者の皆様、JAWS-UG初心者支部運営の皆様、AWSJの皆様、弊社内の皆様に感謝の意を表します。**
- 投稿日:2019-05-30T09:33:45+09:00
AWS CloudFormation テンプレートの基礎
■テンプレートの概要
1.AMIからEC2インスタンスを立ち上げる
AWSTemplateFormatVersion: "2010-09-09" Description: Create EC2 Instance Resources: CreateEC2Instance: Type: AWS::EC2::Instance Properties: ImageId: ami-06a17900f024535fb #ImageIdは自分のAMI IDに書き換えてください。 InstanceType: t2.micro2.Parametersを作成し、ref関数を使い参照する
AWSTemplateFormatVersion: "2010-09-09" Description: Create EC2 Instance Parameters: InstanceType: Description: WebServer EC2 instance type Type: String Default: t2.micro AllowedValues: - t1.micro - t2.nano - t2.micro - t2.small - t2.medium - t2.large ConstraintDescription: must be a valid EC2 instance type #Parametersでインスタンスタイプを選択できるようにする。 Resources: MyEC2Instance: Type: AWS::EC2::Instance Properties: ImageId: ami-06a17900f024535fb InstanceType: !Ref InstanceType #Ref関数でParametersを参照する。 Tags: - Key: Name Value: ec23.Mappings関数を使い、複数のリージョンの複数のAMIから選択できるようにする
AWSTemplateFormatVersion: "2010-09-09" Description: Create EC2 Instance Parameters: InstanceType: Description: WebServer EC2 instance type Type: String Default: t2.micro AllowedValues: - t1.micro - t2.nano - t2.micro - t2.small - t2.medium - t2.large ConstraintDescription: must be a valid EC2 instance type Mappings: RegionMap: ap-northeast-1: hvm: "ami-06a17900f024535fb" ap-southeast-1: hvm: "ami-055a628643638b600" #Mappings関数を使い、複数のリージョンの複数のAMIから選択できるようにします。東京リージョンとシンガポールリージョンのAMIにそれぞれ置き換えてください。 Description: Create EC2 Instance Resources: MyEC2Instance: Type: AWS::EC2::Instance Properties: ImageId: !FindInMap [RegionMap, !Ref 'AWS::Region', hvm] #FindInMap関数でMappings関数からのものを指定する。 InstanceType: !Ref InstanceType Tags: - Key: Name Value: ec24.サブネットを指定する
AWSTemplateFormatVersion: "2010-09-09" Description: Create EC2 Instance Parameters: InstanceType: Description: WebServer EC2 instance type Type: String Default: t2.micro AllowedValues: - t1.micro - t2.nano - t2.micro - t2.small - t2.medium - t2.large ConstraintDescription: must be a valid EC2 instance type SubnetId: Type: String Default: subnet-0a17e4a1945a371a8 AllowedValues: - subnet-0a17e4a1945a371a8 - subnet-08a5374d316da6f60 - subnet-0c7bd98f8caa00b15 - subnet-09621152100bbd5f6 ConstraintDescription: must be a valid SbunetID #自分のサブネットIDを指定してください。 Mappings: RegionMap: ap-northeast-1: hvm: "ami-06a17900f024535fb" ap-southeast-1: hvm: "ami-055a628643638b600" Description: Create EC2 Instance Resources: MyEC2Instance: Type: AWS::EC2::Instance Properties: ImageId: !FindInMap [RegionMap, !Ref 'AWS::Region', hvm] InstanceType: !Ref InstanceType SubnetId: !Ref SubnetId #Ref関数を指定します。 Tags: - Key: Name Value: ec25.ストーレージタイプを指定できるようにする
AWSTemplateFormatVersion: "2010-09-09" Description: Create EC2 Instance Parameters: InstanceType: Description: WebServer EC2 instance type Type: String Default: t2.micro AllowedValues: - t1.micro - t2.nano - t2.micro - t2.small - t2.medium - t2.large ConstraintDescription: must be a valid EC2 instance type SubnetId: Type: String Default: subnet-0a17e4a1945a371a8 AllowedValues: - subnet-0a17e4a1945a371a8 - subnet-08a5374d316da6f60 - subnet-0c7bd98f8caa00b15 - subnet-09621152100bbd5f6 ConstraintDescription: must be a valid SbunetID Mappings: RegionMap: ap-northeast-1: hvm: "ami-06a17900f024535fb" ap-southeast-1: hvm: "ami-055a628643638b600" Description: Create EC2 Instance Resources: MyEC2Instance: Type: AWS::EC2::Instance Properties: ImageId: !FindInMap [RegionMap, !Ref 'AWS::Region', hvm] InstanceType: !Ref InstanceType SubnetId: !Ref SubnetId BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeType: gp2 VolumeSize: 8 #ブロックデバイスを追加しています。もちろんParametersに設定して反映させることも可能です。 Tags: - Key: Name Value: ec26.セキュリティグループとSSHを選択できるようにする。
AWSTemplateFormatVersion: "2010-09-09" Description: Create EC2 Instance Parameters: InstanceType: Description: WebServer EC2 instance type Type: String Default: t2.micro AllowedValues: - t1.micro - t2.nano - t2.micro - t2.small - t2.medium - t2.large ConstraintDescription: must be a valid EC2 instance type SubnetId: Type: String Default: subnet-0a17e4a1945a371a8 AllowedValues: - subnet-0a17e4a1945a371a8 - subnet-08a5374d316da6f60 - subnet-0c7bd98f8caa00b15 - subnet-09621152100bbd5f6 ConstraintDescription: must be a valid SbunetID KeyName: Description : Name of an existing EC2 KeyPair. Type: AWS::EC2::KeyPair::KeyName ConstraintDescription : Can contain only ASCII characters. #KeyPairを指定する。 SSHLocation: Description: IP address range that can be used to SSH to the EC2 instances Type: String MinLength: '9' MaxLength: '18' Default: 0.0.0.0/0 AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2}) ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x. #SSHを解放する値を指定する。 Mappings: RegionMap: ap-northeast-1: hvm: "ami-06a17900f024535fb" ap-southeast-1: hvm: "ami-055a628643638b600" Description: Create EC2 Instance Resources: MyEC2Instance: Type: AWS::EC2::Instance Properties: ImageId: !FindInMap [RegionMap, !Ref 'AWS::Region', hvm] InstanceType: !Ref InstanceType SubnetId: !Ref SubnetId BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: VolumeType: gp2 VolumeSize: 8 Tags: - Key: Name Value: myInstance KeyName: !Ref KeyName #Parametersで指定したKeyNameを指定できるようにする。 SecurityGroupIds: - !GetAtt "InstanceSecurityGroup.GroupId" #セキュリティグループは別のリソースになります。ここでは下のInstanceSecurityGroupをSecurityGroupIdsに指定します。 InstanceSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: connect with ssh VpcId: vpc-08fbdc5732395adab #自分のVpcIdに変更してください。 SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref SSHLocation7.VPCを構築してみる。
AWSTemplateFormatVersion: '2010-09-09' Description: VPC & subnet create Resources: MyVPC: Type: AWS::EC2::VPC Properties: CidrBlock: 10.0.0.0/16 EnableDnsSupport: 'true' EnableDnsHostnames: 'true' InstanceTenancy: default Tags: - Key: Name Value: CloudFormation-VPC PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref MyVPC Tags: - Key: Name Value: CloudFormation-VPC-PublicRT PrivateRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref MyVPC Tags: - Key: Name Value: CloudFormation-VPC-PrivateRT PublicSubnet1A: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.0.0/24 AvailabilityZone: "ap-northeast-1a" Tags: - Key: Name Value: CloudFormation-public-subnet-1a PubSubnet1ARouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1A RouteTableId: !Ref PublicRouteTable PublicSubnet1C: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.2.0/24 AvailabilityZone: "ap-northeast-1c" Tags: - Key: Name Value: CloudFormation-public-subnet-1c PubSubnet1CRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1C RouteTableId: !Ref PublicRouteTable PrivateSubnet1A: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.1.0/24 AvailabilityZone: "ap-northeast-1a" Tags: - Key: Name Value: CloudFormation-private-subnet-1a PriSubnet1ARouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1A RouteTableId: !Ref PrivateRouteTable PrivateSubnet1C: Type: AWS::EC2::Subnet Properties: VpcId: !Ref MyVPC CidrBlock: 10.0.3.0/24 AvailabilityZone: "ap-northeast-1c" Tags: - Key: Name Value: CloudFormation-private-subnet-1c PriSubnet1CRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1C RouteTableId: !Ref PrivateRouteTable myInternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: Name Value: CloudFormation-ING AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref MyVPC InternetGatewayId: !Ref myInternetGateway myRoute: Type: AWS::EC2::Route DependsOn: myInternetGateway Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref myInternetGateway Outputs: StackVPC: Description: The ID of the VPC Value: !Ref MyVPC Export: Name: !Sub "${AWS::StackName}-VPCID" StackPublicSubnet1A: Description: The ID of the VPC Subnet Value: !Ref PublicSubnet1A Export: Name: !Sub "${AWS::StackName}-PublicSubnet1A"  StackPublicSubnet1C: Description: The ID of the VPC Subnet Value: !Ref PublicSubnet1C Export: Name: !Sub "${AWS::StackName}-PublicSubnet1C" StackPrivateSubnet1A: Description: The ID of the VPC Subnet Value: !Ref PrivateSubnet1A Export: Name: !Sub "${AWS::StackName}-PrivateSubnet1A" StackPrivateSubnet1C: Description: The ID of the VPC Subnet Value: !Ref PrivateSubnet1C Export: Name: !Sub "${AWS::StackName}-PrivateSubnet1C"Outputsで作成したものが以下のようにエクスポートに作成される。
例えばEC2インスタンスを今作成したVPCのサブネットに作成したい場合、
EC2スタックの方に以下の通り記述することで、インポートすることができる。
SubnetId: !ImportValue naata-PublicSubnet1Aこのようにいくつかのテンプレートを分けて運用する。
- 投稿日:2019-05-30T09:32:30+09:00
AWS EC2 時間同期
問題
たまに時間がずれてしまう
対応
Linux
Amazon Time Sync Serviceの場合(推薦)
https://aws.amazon.com/jp/blogs/news/keeping-time-with-amazon-time-sync-service/
1.インストールの場合sudo yum erase ntp* sudo yum -y install chrony sudo service chronyd start2.既存の NTP の設定の場合
confに下記追加server 169.254.169.123 prefer iburstntp設置の場合
1.ntpをインストールすること. server = ntp.nict.jpを記載して。chkconfig ntpd on ;; service ntpd start する
vi /etc/sysctl.conf 末尾に追加。xen.independent_wallclock = 12.sysctl -p で変更した設定ファイルの内容をロードする。
Windows
net stop w32time w32tm /config /syncfromflags:manual /manualpeerlist:"169.254.169.123" w32tm /config /reliable:yes net start w32timeメモ
今のところたまに発生するし、すぐ対応できるので手動ですが、
zabbixのアクションでの調整がいいでしょう。
しかし、手動対応感覚を維持するため頻繁ではないアラートはわざとアクション化しないことにしています。
- 投稿日:2019-05-30T04:04:07+09:00
aws s3の署名バージョン「4」をcurlコマンドでリクエストする
前提
- s3の署名バージョン「2」が2019年6月24日で廃止になる
- curlで直接s3のエンドポイントを叩く場合は、自分で署名リクエストを作る必要がある
aws公式ドキュメントに疑似コードが乗っているので、それに沿って
bashで実装していきます。https://docs.aws.amazon.com/ja_jp/general/latest/gr/sigv4-create-canonical-request.html
署名のプロセス
- 正規リクエストを作成します。
- 正規リクエストと追加のメタデータを使用して、署名の文字列を作成します。
- AWS シークレットアクセスキーから署名キーを取得します。次に、署名キーと、前の手順で準備した文字列を使用して、署名を作成します。
- 作成した署名をヘッダーの HTTP リクエストに追加します
awsがリクエストを受診したあと、リクエストヘッダーに含まれているアクセスキー(シークレットではない方)を利用して上記の手順をもう一度実施して署名を作成します。その署名がリクエストに含まれている署名と比較して一致しているかが検証されます。
1. 正規リクエストを作成
こんな感じの文字列を作成する必要があります。
PUT /test/test.txt #対象URI content-type:text/plain host:${bucket}.s3.amazonaws.com x-amz-content-sha256:c911d4fcad1f5d53f3d74324cf57dc254a16182ee5662b8872ebe6159f6b3ce3 #アップロード対象ファイルのハッシュ x-amz-date:20190529T165716Z #Dateの形式は %Y%m%dT%H%M%SZ content-type;host;x-amz-content-sha256;x-amz-date #headerの一覧 c911d4fcad1f5d53f3d74324cf57dc254a16182ee5662b8872ebe6159f6b3ce3" #アップロード対象ファイルのハッシュbashで書くとこんな感じ。最後にハッシュ化も行っています。
bucket=test-bucket dateValueShort=$(date -u +'%Y%m%d') dateValueLong=$(date -u +'%Y%m%dT%H%M%SZ') fileLocal=test.txt headerList='content-type;host;x-amz-content-sha256;x-amz-date' #アップロード対象ファイルをハッシュする payloadHash=$(openssl dgst -sha256 -hex < "${fileLocal}" 2>/dev/null | sed 's/^.* //') canonicalRequest="\ PUT /test/test.txt content-type:text/plain host:${bucket}.s3.amazonaws.com x-amz-content-sha256:${payloadHash} x-amz-date:${dateValueLong} ${headerList} ${payloadHash}" canonicalRequestHash=$(printf '%s' "${canonicalRequest}" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/^.* //')2. 正規リクエストと追加のメタデータを使用して、署名の文字列を作成
こんな感じの文字列を作成する必要があります
AWS4-HMAC-SHA256 20190529T171500Z #Dateの形式は %Y%m%d 20190529 #Dateの形式は %Y%m%dT%H%M%SZ 20190529/ap-northeast-1/s3/aws4_request c911d4fcad1f5d53f3d74324cf57dc254a16182ee5662b8872ebe6159f6b3ce3 #上記で取得した正規リクエストのハッシュbashで書くとこんな感じ。
dateValueShort=$(date -u +'%Y%m%d') dateValueLong=$(date -u +'%Y%m%dT%H%M%SZ') region=ap-northeast-1 stringToSign="\ AWS4-HMAC-SHA256 ${dateValueLong} ${dateValueShort}/${region}/s3/aws4_request ${canonicalRequestHash}"3. AWS シークレットアクセスキーから署名キーを取得します。次に、署名キーと、前の手順で準備した文字列を使用して、署名を作成
こんな感じの処理をする必要があるとのこと。AWSシークレットキーを起点にしてハッシュにしたものをさらにハッシュにしていく処理です。
kSecret = your secret access key kDate = HMAC("AWS4" + kSecret, Date) kRegion = HMAC(kDate, Region) kService = HMAC(kRegion, Service) kSigning = HMAC(kService, "aws4_request")bashで書くとこんな感じ。
awsSecret= dateValueShort=$(date -u +'%Y%m%d') kSecret="AWS4${awsSecret}" region=ap-northeast-1 kDate=$(printf '%s' "${dateValueShort}" | openssl dgst -sha256 -hex -mac HMAC -macopt "key:${kSecret}" 2>/dev/null | sed 's/^.* //') kRegion=$(printf '%s' "${region}" | openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kDate}" 2>/dev/null | sed 's/^.* //') kService=$(printf "s3" | openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kRegion}" 2>/dev/null | sed 's/^.* //') kSigning=$(printf "aws4_request" | openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kService}" 2>/dev/null | sed 's/^.* //') signature=$(printf '%s' "${stringToSign}" | openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kSigning}" 2>/dev/null | sed 's/^.* //')4. 作成した署名をヘッダーの HTTP リクエストに追加
headerとともにcurlコマンド。
curl -I -L --proto-redir =https -X PUT -T "${fileLocal}" \ -H "Content-Type: text/plain" \ -H "Host: ${bucket}.s3.amazonaws.com" \ -H "X-Amz-Content-SHA256: ${payloadHash}" \ -H "X-Amz-Date: ${dateValueLong}" \ -H "Authorization: AWS4-HMAC-SHA256 Credential=${awsAccess}/${dateValueShort}/ap-northeast-1/s3/aws4_request, SignedHeaders=${headerList}, Signature=${signature}" \ "https://${bucket}.s3.amazonaws.com/test/test.txt"以下のレスポンスが返って来ればOKです
HTTP/1.1 200 OK x-amz-id-2: hLP5G3rE9Ph0OACv7v4ZsxvPZwj/0N8PuGdR7ns8XO8gl9gKN2aSeuTg+9KxF+vUY2QnDF8t4hA= x-amz-request-id: BDFF6DF613BA53EA Date: Wed, 29 May 2019 18:55:58 GMT ETag: "d8e8fca2dc0f896fd7cb4cb0031ba249" Content-Length: 0 Server: AmazonS3はまったところ
正規リクエストに半角スペースが含まれていたりすると、
he request signature we calculated does not match the signature you provided. Check your key and signing method.が出ます。しかもデバッグもできないので、ハマると結構キツいです。全体のコード
#!/bin/bash -u awsAccess= awsSecret= bucket= fileLocal=test.txt fileRemote=/test/test.txt region=ap-northeast-1 dateValueShort=$(date -u +'%Y%m%d') dateValueLong=$(date -u +'%Y%m%dT%H%M%SZ') headerList='content-type;host;x-amz-content-sha256;x-amz-date' #アップロード対象ファイルをハッシュする payloadHash=$(openssl dgst -sha256 -hex < "${fileLocal}" 2>/dev/null | sed 's/^.* //') #正規リクエストの作成 canonicalRequest="\ PUT ${fileRemote} content-type:text/plain host:${bucket}.s3.amazonaws.com x-amz-content-sha256:${payloadHash} x-amz-date:${dateValueLong} ${headerList} ${payloadHash}" canonicalRequestHash=$(printf '%s' "${canonicalRequest}" | openssl dgst -sha256 -hex 2>/dev/null | sed 's/^.* //') #署名の文字列を作成 stringToSign="\ AWS4-HMAC-SHA256 ${dateValueLong} ${dateValueShort}/${region}/s3/aws4_request ${canonicalRequestHash}" #署名の作成 kSecret="AWS4${awsSecret}" kDate=$(printf '%s' "${dateValueShort}" | openssl dgst -sha256 -hex -mac HMAC -macopt "key:${kSecret}" 2>/dev/null | sed 's/^.* //') kRegion=$(printf '%s' "${region}" | openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kDate}" 2>/dev/null | sed 's/^.* //') kService=$(printf "s3" | openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kRegion}" 2>/dev/null | sed 's/^.* //') kSigning=$(printf "aws4_request" | openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kService}" 2>/dev/null | sed 's/^.* //') signature=$(printf '%s' "${stringToSign}" | openssl dgst -sha256 -hex -mac HMAC -macopt "hexkey:${kSigning}" 2>/dev/null | sed 's/^.* //') #http request curl -I -L --proto-redir =https -X PUT -T "${fileLocal}" \ -H "Content-Type: text/plain" \ -H "Host: ${bucket}.s3.amazonaws.com" \ -H "X-Amz-Content-SHA256: ${payloadHash}" \ -H "X-Amz-Date: ${dateValueLong}" \ -H "Authorization: AWS4-HMAC-SHA256 Credential=${awsAccess}/${dateValueShort}/${region}/s3/aws4_request, SignedHeaders=${headerList}, Signature=${signature}" \ "https://${bucket}.s3.amazonaws.com${fileRemote}"
- 投稿日:2019-05-30T01:00:18+09:00
AWS認定Associate3冠取得達成したので自分はどう勉強したのかを放流する
プロフィール
エンジニアになって1年半程度、AWSは半年〜1年未満です。
AWSはオンプレな設計思想をそのままクラウドにのっけたシステムの運用保守業務で携わっている程度なので、設計構築開発含めガッツリやってます!って人ほどは関わっていない、そんなレベル感です。JAWS Days 2019に参加したのをきっかけにやっぱりAWS超面白い、もっと詳しくなりたいと自分に火が付いてその日から3月、4月、5月と立て続けに試験を受けて3つあるAssociate Levelをコンプリートしました。
受験履歴
2019/5/29現在、AWS認定は画像の様に全11種類あります。
私はそのうちの赤枠で囲われている3つを取得しました。
※Cloud Practitionerは入門Levelの認定になるので一旦スルーしています。
AWS認定 取得日 ソリューションアーキテクト アソシエイト (SAA) 2019/3/22 デベロッパー アソシエイト(DVA) 2019/4/24 SysOpsアドミニストレータ アソシエイト (SOA) 2019/5/28 Associate Levelの難易度
実際に試験を受けてみての所感
順位 AWS認定 1位 デベロッパー アソシエイト(DVA) 2位 ソリューションアーキテクト アソシエイト (SAA) 3位 SysOpsアドミニストレータ アソシエイト (SOA) これは試験を受ける人が普段どの様にAWSに関わっているかで変わってくる部分があるかと思います。
実際、私はDVA試験範囲のAWSサービス要素にほぼ業務では関われていない為、難易度も高く感じましたし、スコアも3つの中で一番が悪かったです…笑また、Qiitaなどで受験記録を読み漁ってみれば気づくと思いますが、3つの中でSAAが1番難しいとされている節があって、直近3つ取った者として私はこれに異を唱えたい!!
理由としては、日本語のSAA対策本がここ数ヶ月で非常に充実してきた背景があります。
タイトル 発売日 合格対策 AWS認定ソリューションアーキテクト - アソシエイト 2016/8/17 徹底攻略 AWS認定 ソリューションアーキテクト – アソシエイト教科書 2019/1/18 最短突破 AWS認定ソリューションアーキテクト アソシエイト 合格教本 2019/2/26 AWS認定資格試験テキスト AWS認定 ソリューションアーキテクト-アソシエイト 2019/4/20 AWS認定アソシエイト3資格対策 2019/6/13(予定) この1冊で合格! AWS認定ソリューションアーキテクト - アソシエイト テキスト&問題集 2019/7/20(予定) ソリューションアーキテクトアソシエイトだけで一体どれだけだすんだよ!ってくらいに充実してきています。
それに対してDVAやSOAに特化した日本語の対策本はまだ存在しません。
※[AWS認定アソシエイト3資格対策]は現在未発売であることと、特化本とは違うようなのでノーカウントこれだけあればSAA対策に関して迷うことはないでしょう。
学習方法
試験を受けるにあたってQiitaやその他ブログなどを読み漁り、先駆者がどの様にして対策を行ったのか情報を集めました。以下はその中でも特に参考になった記事です。全受験者必読です。
AWS認定11冠制覇したのでオススメの勉強法などをまとめてみる
非常に参考にさせて頂きました。正直この記事だけでなんとか出来ます。以降は上記のQiita記事を踏まえた上で自分が実際に取った対策を書いていきます。
ほぼ共通
AWS サービス別資料(Black Belt)
各種AWSサービスのBlack Beltの読み込みは基本ですね。よくある質問
全てのQ&Aを読めませんでしたが、時間があればもっと目を通しておきたかった。AWSドキュメント(開発者ガイド)
個々のサービスについて詳しく知りたい時は開発者ガイドが役に立ちました。
※読みにくいのが難点…ソリューションアーキテクト アソシエイト (SAA)
使用教材 金額 最短突破 AWS認定ソリューションアーキテクト アソシエイト 合格教本 2,678円 SAA対策には先ず、対策本を1週読んだ後に付属の模擬試験65問を解きました。
1週目で6割程度正解だったと記憶しております。その後対策本をベースに各種AWSサービスに関するBlack Beltの読み込みを実施。
ある程度サービスについての理解が進んだ後は再び対策本の付属の模擬試験を、今度は解説の内容から正解/不正解の理由を意識して100%正解になるまでやり込みました。SAAの対策としてはそれくらいです。
ソリューションアーキテクト アソシエイト (SAA)振り返り
恐らく全ての本に共通する事ですが、市販の対策本の模擬試験のレベルと本試験のレベルは難易度が違います。AWS認定はその仕様上、過去問というものが存在しません。出回っている模擬試験は飽くまでもこれまでの出題傾向から導き出された似たような問題なのです。
1回で合格したから良かったけれど、試験中はこれはギリギリかもしれないと思いながら最後の「終了」ボタンを押しました。これからSAAを受験される方には「本試験>>本などで出回っている模擬試験」くらいの認識を持って本番に挑むといいかもしれません。この難易度感を知っているか否かでも大分心構えが違うかと思います。
デベロッパー アソシエイト(DVA)
DVAはSAAと違って日本語の対策本が全くない為、非常に苦しみました。
QiitaでUdemyでの学習を勧めていた記事があったので私もそれに習ってUdemyを使用しております。
使用教材 金額 Ultimate AWS Certified Developer Associate 2019 2,400円(キャンペーン価格) AWS Certified Developer Associate 2019 4 Practice Tests 0円(上記とセット) Udemyでは定価だと例えば18,000円なのに時々キャンペーンによって格安で購入することが出来ます。私もキャンペーンを狙って購入しました。
私が購入した講座は、DVAの試験範囲のAWSサービスを試験対策の観点で解説してくれ、更にはそのほぼ全てにハンズオンの動画&ハンズオン資材が含まれているおり、また65問の模擬試験&解説が4回分込み込みで2,400円なので非常にオススメです。
以下の画像が購入履歴です。
※単品購入でなくセットで購入するのがベストです!
因みに定価だと30,000円…Udemyあるあるですね。↓セット購入のイメージ
※キャンペーン時だとこれがセットで2,400円でした!Udemyの使い方
既にお気付きかと思いますが、私が購入した講座は英語です…。DVA対策の講座は英語ぐらいしか置いていないのです。では、どうやってこれを使って勉強したのかというと英字字幕をGoogleChromeでリアルタイム翻訳しました。
翻訳なので日本語に比べて理解するのが大変ではありましたが、使用したことのないサービスに関しての体系的なハンズオンもあったので役に立ったと思います。
翻訳の雰囲気はこんな感じです。
講座動画の場合
※gifのフレームレートが最適化出来なかったので話のスピードは参考にしないでください…Udemyで講座動画を1週見たら、模擬試験を解いて解説を読むをひたすら繰り返しました。
SAAの対策本と違い、模擬試験の解説は公式ページの開発者ガイドなどへのリンクも含まれていた為、翻訳では理解できない場合は公式へ飛んでそっちを読んだりもしました。デベロッパー アソシエイト(DVA)振り返り
模擬試験での対策に関してはSAAより場数が踏めたとはいえやはり開発者サイドは未経験であったこともあり1ヶ月の準備期間では苦戦を強いられました。しかしそんな人間に対してもSAA同様に1回で合格出来たので今回使用した教材の質と再現性の高さは証明されたと思っています。言語の壁がなければもっとすんなりといけたんだろうなあと悔しさを噛みしめる。
SysOpsアドミニストレータ アソシエイト (SOA)
SOAに関しては実はAWS WEB問題集で学習しようという日本語で対策が可能なWEBサービスがあります。
これもQiitaなどで「ここ使っていけた」系の記事が幾つか見つかると思います。参考までに料金形態
プラン CLF SAA SOA SAP 有効期限 料金 問題数 フリー - #1~3 #1~3 #1 なし なし - ゴールド - 全て - - 60日 3,880円 SAA 872問 プラチナ - 全て 全て - 60日 4,280円 +SOA 315問 ダイヤモンド 全て 全て 全て 全て 90日 5,280円 +SAP 189問 プラチナ以上でSOA対策が可能です。
まあ、自分は使わなかったので内容に関しては言及できません。使わなかった理由としては上記のサービスは使用期限があるのに対して、Udemy講座は無期限で使えるので暫くして振り返って勉強する事が手軽に可能という点を重視した為です。
DVAで使用した講座と同じ講師がSOA対策の講座も出していたので、AWSサービスに関するハンズオン動画もあるのでそちら見たさにUdemy講座を購入しました。しかし、残念なことに模擬試験までは出していなかったので別の講師が出している物を別途購入しています。
使用教材 金額 Ultimate AWS Certified SysOps Administrator Associate 2019 2,400円 AWS Certified SysOps Administrator Associate Practice Exams 1,400円 ※どちらもキャンペーン価格です。
学習の流れはDVAの時と同様。
今回使用した模擬試験は解説がDVAの講師のものよりも公式ページの言葉をそのまま使用しているケースが多い為か、理解しやすかった印象です。SysOpsアドミニストレータ アソシエイト (SOA)振り返り
Associate最後ということもあり1番楽に感じました。しかし、当然ですがSOAはSAAやDVAと出題傾向が違いますのでしっかりと対策をしないと不合格になりるなと実感しました。
その事を訴えた記事が最近投稿されたのでここにも載せておきます。
AWS Sysops 試験に失敗した話(2019年5月)そしていつだって本試験は模擬試験より難易度は高いです。高かったです。
全体振り返り
Associate3冠取得に向けての対策として、情報収集→教材選定→教材読み込み&視聴→(模擬試験&解説読み込み)×n回の一連の流れをセットにして本試験に挑んだ感想としては、闇雲に公式ページや公式PDFを隅から隅まで読むよりかはポイントを抑えて効率的に学習出来て良かったです。
高得点を狙うならそれら公式の読み込みに加えてもっと実際にAWSを動かして弄る必要があるなと課題も残りました。今の業務だと求める内容には遠いので自学を兎に角頑張って1年後あたりにはProfessionalに挑戦して5冠を目指したいところです!
以上、これからAssociate Levelの認定に挑戦したい誰かの役に立てればと思います。






























