- 投稿日:2020-04-01T23:58:46+09:00
Systems Managerエージェントが通信できないEC2一覧の取得
環境
以下の条件に当てはまる環境で、よく発生する問題です。
- 多数のEC2を構築・運用している
- Systems Manager(SSM)を使用できるように各種セットアップ済みである
- Systems ManagerのランコマンドやPatch Manager等を利用している
多数のEC2でSystems Managerを利用すると
正しく構成していれば、各EC2のSystems Managerエージェント(SSMエージェント)はSystems Managerサービスと通信できます。
通常は、何ら問題ないです。しかしながら何らかの原因で、一部のEC2でSSMの通信ができないケースが出てきます。
(原因は本当に色々あって、IAMの設定不備、VPCルーティング設定不備、セキュリティグループ設定不備、SSMエージェントが落ちている、SSMエージェントのバージョンが古い、169.254.169.xxx の通信など、、多岐にわたります)
多数のEC2を運用していると、SSMの通信できないEC2の原因を調べるよりも、どのEC2が通信できててどのEC2が通信できてないかをそもそも見つけにくい 状況だったりします。SSMの通信NGをなぜ見つけにくいのか
マネージメントコンソールで、Systems Managerのマネージドインスタンスを見ると、対象EC2のリストが確認できます。
ただし表示されるのはSSMエージェントと通信が確立できたEC2であり、全く通信できていないEC2はリストに表示されません。
また、EC2を停止するとリストからも消えてしまいます。API/CLIで通信できないEC2一覧の取得
そこで2つのコマンドを使います。
コマンド1つ目
aws ec2 describe-instance-status
APIの場合は、
ec2:DescribeInstanceStatus
を実行すると、稼働している(runningステータス)のEC2一覧が取得できます。そしてコマンド2つ目
aws ssm describe-instance-information
APIの場合は、
ssm:DescribeInstanceInformation
を実行すると、SSMエージェントの通信OKなEC2一覧が取得できます。欲しいのはrunningのEC2にもかかわらずSSMの通信NGの一覧です。
それを得るには、コマンド1つ目のEC2一覧 から コマンド2つ目のEC2一覧を引いたもの を抽出すればよいです。
インスタンスIDで突き合せられます。インスタンスIDじゃ分からないよ!という場合はさらにタグなどを取得します。
https://qiita.com/t-fujiwara/items/835cccbef7ec6d199251なお、停止しているEC2は調べようがありません。まずは開始させましょう。
- 投稿日:2020-04-01T23:49:36+09:00
ローカル開発から本番環境へのエラー対策
ローカル開発環境からのアップロードができない場合
環境変数を設定し、それをCarrierWaveから使えるようにするには、以下の3箇所の設定が正しい必要があります。
1、CarrierWaveの設定ファイル(CarrierWave.rb)
2、Railsアプリケーション全体の秘密情報を管理するファイル(secrets.yml)
3.OSが提供するデータ共有の仕組み、漏洩のリスクが低い(環境変数)
キーを安全に運用するための仕組みは、CarrierWave.rb=>secrets.yml=>環境変数
となります。環境変数およびsecrets.ymlの設定が正しいか
1、ChatSpaceがあるフォルダで、「rails c」を実行します。
2、コンソール内で、以下のコマンドを実行します。
コンソールRails.application.secrets.aws_access_key_id Rails.application.secrets.aws_secret_access_key「Rails.application.secrets」+「.キーの名前」を実行する事でsecrets.ymlの設定を呼び出すことができます。
上の2つのコマンドを実行して、正しくAWSのキーとパスワードが表示されればOKです。
うまくいかなかった場合は、bash_profileの内容を確認します。
※OSがCatalina以降で環境構築をしている方は、zshというシェルを使用しているので、bash_profileの部分をzshrcと置き換えてください。
ターミナルcat ~/.bash_profile表示されたbash_profileの中に、以下の記述があるか確認してください。
~/.bash_profileexport AWS_SECRET_ACCESS_KEY='AWSのシークレットキー' export AWS_ACCESS_KEY_ID='AWSのアクセスキー'もしなければ、AWSの設定ができていないので、確認して追加してください。
次に、bash_profileの内容を反映させます。
ターミナルsource ~/.bash_profilebash_profileにコマンドを書いただけでは設定は反映しません。
次は、carrierwave.rbの設定を確認します。config/initializers/carrierwave.rbrequire 'carrierwave/storage/abstract' require 'carrierwave/storage/file' require 'carrierwave/storage/fog' CarrierWave.configure do |config| config.storage = :fog config.fog_provider = 'fog/aws' config.fog_credentials = { provider: 'AWS', aws_access_key_id: Rails.application.secrets.aws_access_key_id, aws_secret_access_key: Rails.application.secrets.aws_secret_access_key, region: 'ap-northeast-1' } config.fog_directory = 'ここにバケット名を入れます' config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/ここにバケット名を入れます' endこのような記述になっているかを確認してください。
バケットの指定が正しいかの確認
AWSのサービスからS3を選択し、バケットを表示させます。先ほどのcarrierwave-rbでS3のバケット名を指定しています。その内容が正しいか確認してください。
バケットの指定は「バケット名」「リージョン」の両方を正しく行う必要があります。
・バケット名が正しく設定できているか
・S3のリージョンが「アジアパシフィック(東京)」になっているか
を確認してください。もし違ったら直してください。本番環境からのアップロードができない場合
S3へのアップロードができない場合、まずエラーメッセージを確認します。
ローカルでは、仮想サーバーを起動したターミナルを見ればエラーメッセージが表示されていました。
しかし、本番環境ではローカルのターミナルに当たるものがありません。Capistranoが動く場合
以下の設定を行うと、本番環境でもローカル開発環境と同じように、ブラウザにエラーメッセージが表示されるようになります。
config/environments/production.rb〜省略〜 config.consider_all_requests_local = true 〜省略〜「config/environments/production.rb」のファイルの中から、上記の記述を見つけ、もともと「false」になっているので、「true」に変更します。
この変更を本番環境に反映させる必要があるので以下の2つの操作を行います。
①GitHubへのプッシュ
②「bundle exec cap production deploy」の実行
なお、エラー対応が終わったら、忘れずにfalseに戻してデプロイをし直してください。Capistranoがエラーになる場合
Capistranoがエラーになってしまう場合は、上記の方法が取れません。
なので、エラーログを直接確認します。
エラーの内容によって、主に以下の2つのファイルにログが書き込まれます。
どちらに該当するかわかりにくい場合は、両方とも確認してください。Unicornのエラーログの確認方法
ターミナル
cat /var/www/(アプリ名)/current/log/unicorn.stderr.logターミナル
cat /var/www/(アプリ名)/current/log/production.logRailsアプリのエラーログの確認方法
エラーログの見方
・ファイルの下に行くほど新しいログが載っています
・いつのログなのかを必ず確認しましょう
まず、ログの中にタイムスタンプがあったら、そこに9時間を足しましょう(標準時での出力になるため、日本は9時間の時差があります)。その時刻と、今の時刻を見比べて今起きたエラーなのかをチェックします。
エラーメッセージが出ない場合や、原因を特定できない場合は以下のチェックを行いましょう。
1. 環境変数が正しく設定できているか確認してください。
リモートでの環境変数が正しく設定されていないとS3へのアクセスができません。下記のコマンドで確認します。
ローカルのターミナルssh -i ~/.ssh/<pemファイルの名前> ec2-user@<IP>sshでリモートにログインします。
リモートのターミナル> env | grep AWS出力された結果が、AWSのキー、パスワードと一致するか確認してください。
なお、「|」は複数の処理を行うためのもので、「A | B」と書くことで、Aの処理が終わったらBの処理をする、という指定ができます。
envは、現在設定されている環境変数を確認するためのコマンドです。
grepは文字の検索をして絞込みを行ってくれるコマンドです。そのあとに「AWS」と付ける事で、「AWS」という文字列を含んだ行のみ表示がされます。
つまり、「env | grep AWS」とコマンドを実行する事で、環境変数の中に「AWS」という文字を含んだものだけを出力してほしい、という意味になります。変更後のコードが本番に反映しているかの確認
ローカル開発環境での変更が、本番環境にうまく反映されていないときは、以下の確認をしてください。
プッシュ先のGitHubを直接確認してください。
最後に変更したファイルをGitHubでチェックして、変更点が反映しているか確認してください。
万が一プッシュができていない場合は、もう一度プッシュを行ってください。
GitHubにプッシュされている場合は、EC2へのプルで失敗している可能性もあるので、以下の確認をしてみてください。
ローカルのターミナルssh -i ~/.ssh/<pemファイルの名前> ec2-user@<IP>sshでリモートにログインをします。
最新のファイルは以下のフォルダ内に格納されているので、最後の変更が反映しているか確認してください。/var/www/(アプリ名)/current/appこのフォルダ以下にファイルが格納されているので、catコマンドで内容を確認してください。
サーバーを再起動
コードが最新になっているのにアプリに反映しないのは、サーバーの再起動ができていない場合があります。念の為、NginxおよびUnicornの再起動をしてから、再度アプリで確認してみてください。
Nginxの再起動
リモートのターミナルsudo service nginx restartUnicornの再起動
リモートのターミナル> ps aux |grep unicorn # Unicornのプロセスidを確認する > kill -9 (unicornのプロセスid)ローカルのターミナル
> bundle exec cap production deployこれで、デプロイできました。
- 投稿日:2020-04-01T23:27:32+09:00
Chalice を使ってみるなかで調べた小ネタ3点
1. 静的ファイルを Lambda 関数に含める方法
- シナリオ: 簡単な設定ファイル (YAML, JSONなど) を Lambda 関数内で読み込むファイルとして扱いたい。
- 問題:
app.py
と同じディレクトリにenv.yml
を置いたが、chalice deploy
してもファイルがアップロードされなかった- 解決策: chalice プロジェクトの下に chalicelib というディレクトリを作って、そこにファイルを置く。 以下のtreeの結果を参照。
- 使うときは
filepath = os.path.join(os.path.dirname(__file__), 'chalicelib', 'env.yml')
などのように読み込ませて解決。- 公式マニュアルでも同様のことが示されている
$ tree -a . . ├── .chalice │ └── config.json ├── .gitignore ├── app.py ├── chalicelib │ └── env.yml └── requirements.txtなお、chaliceでデプロイをする場合、Linux/Mac などの場合はファイルのパーミッションをそのまま受け継ぐ。 そのため、例えば
750
などのファイルをデプロイした場合、そのファイルを読もうとすると実行時に Permission Denied となってしまうため注意。
- 同様の事例 。アップロード時に作った zip file を展開するときにおそらく保存時のパーミッションをそのまま展開していることが原因。
2. IAM Policy が自動生成されない / 自分でPolicyを設定する方法
- シナリオ: Chalice の便利な機能の1つにIAM Policyの自動生成がある。 これは、Lambda 内で boto3 ライブラリを使ってAWSリソースにアクセスしている場合、それを自動的に判断してIAM Policyを動的に自動生成してくれる機能である。
- 問題: なぜか自分のプログラムではこの自動生成・付与を行ってくれなかった。
調べてみると、boto3.client を利用したプログラムのみ自動生成の対象 であるのが原因だった。 boto3 には resource を使う方法もあるのだが、こちらを使った場合ですらIAMの自動生成の対象にならない。 自分のプログラムは全て boto3.resource を利用していた。
そのため、IAM Policy を自動生成するため、 Chaliceでboto3を使うときは常に
boto3.client
を利用する というのは1つの指針になり得る。
逆に、既存のIAM Roleを利用したい、自分でPolicyの記述内容を管理したい、というケースの場合は.chalice/config.json
内でautogen_policy
を無効にする。 例えば以下の通り。.chalice/config.jsonから抜粋"stages": { "dev": { "api_gateway_stage": "api", "autogen_policy": false } }これを無効にした場合、デフォルトでは
.chalice
内のpolicy-<stage name>.json
(この場合は policy-dev.json) を読み込む。 別名を指定したい場合はiam_policy_file
でファイル名を指定する。 より詳細な説明はこちら.chalice/policy-dev.jsonにはRole内でのアクセス権限を書いていけばよい{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:*" ], "Resource": [ "*" ], "Sid": "xxxx-xxxx-xxxx-xxxx-1234" }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*", "Sid": "xxxx-xxxx-xxxx-xxxx-1235" } ] }外部のライブラリなどを使いたい場合、boto3.resource を利用する場合はこちらを採用することになるだろう。 IAM権限の関係から、そもそもIAMの操作権限がない場合などはIAM Role ARNを指定して付与することもできる (
iam_role_arn
) 。client しか IAM Policy の自動生成にならないという記述は、例えば AWS BlackBelt シリーズでの説明 とか この記事よりもはるかに詳しい説明とかで見つけることはできたが、公式ドキュメント内でこの記述を見つけられなかった。 あっても良いはずなのだが……
3. CORS対応
- シナリオ: ブラウザから ajax で API Gateway - Lambda をコールし、レスポンスを取得したい
- 問題: ブラウザから ajax でアクセスしたら CORS が原因ではじかれる。
chalice local
で稼働させている場合も同様。ユースケースに合わせた CORS の設定をすることになるのだが、もうこれはそのまま 公式を読んでもらう方が早い ぐらい説明が充実している。
概略としては
chalice.CORSConfig
のインスタンスに適当な設定をした後、@app.route(path, cors=config)
のようにデコレータ内のcors
に対して設定を渡してやればあとはよしなにしてくれる。
- 投稿日:2020-04-01T23:10:20+09:00
awscli v2でタブ補完が効かない場合の対処(macOS Catalina)
環境
- mac
- macOS Catalina(10.15.4)
- zsh 5.7.1 -aws-cli/2.0.6 Python/3.7.4 Darwin/19.4.0 botocore/2.0.0dev10
事象
mac OS Catalinaでawscliv2をインストールした際にタブ補完が効かない事象に遭遇した。
結論から言うと、macで使用しているデフォルトのzshでcomplete
コマンドがcommand not found
なのが原因解決策
bashの互換モードを有効にしてcompleteコマンドを読み込ませる。
~/.zshrcに以下3行を追加して様子を見てみる。
追記したら~/.zshrcを再度読み込みます。autoload bashcompinit bashcompinit complete -C '/usr/local/bin/aws_completer' awsaws_completerの場所は事前に
which aws_completer
で調べ、自身の環境の値に変えてください。下記コマンドを実行して
~/.zshrc
に追加します。.zshrcなければ作ってください。commandcat << _EOF_ >> ~/.zshrc # awscli autoload bashcompinit bashcompinit complete -C '/usr/local/bin/aws_completer' aws _EOF_ source ~/.zshrcとりあえずこの方法で自分の環境では補完が効くようになりました。(2020年4月 現在)
そのうち何かしなくても補完効くようになるかもしれませんが同様の事象の方は試してみてください。参考
macOS での AWS CLI バージョン 2 のインストール
コマンド補完
v2 : AWS ZSH completer does not exist #4950
- 投稿日:2020-04-01T20:45:54+09:00
CDKでAPI Gatewayにステージを追加してもLambdaのパーミッションは自動で追加されない
CDKでAPi Gateway+Lambdaの構成を作りました。
Gatewayのステージを2つ持たせて、ステージ毎に変数を持たせてLambdaを叩くという構成にしたかったのですが、追加したステージのパーミッションがLambdaに自動で追加されなかったので、CfnPermission
を使って自分で追加する必要がありました。
このことにちょっとハマって時間を要したので、コードを残しておきます。環境
CDK CLI: 1.27.0
コード
cdk-lambda-stack.tsimport * as cdk from '@aws-cdk/core' import * as lambda from '@aws-cdk/aws-lambda' import * as apigateway from '@aws-cdk/aws-apigateway' import * as iam from '@aws-cdk/aws-iam' export class CdkLambdaStack extends cdk.Stack { constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { super(scope, id, props) // Lambdaの定義 const lambdaFn = new lambda.Function(this, 'function', { runtime: lambda.Runtime.PYTHON_3_8, handler: 'lambda_function.lambda_handler', code: lambda.Code.asset('lambda_asset') }) // API Gatewayの定義 const api = new apigateway.RestApi(this, 'api', { deployOptions: { stageName: 'first stage name', variables: {foo: 'bar'} } }) // ステージの追加 const stage = new apigateway.Stage(this, 'stage', { deployment: new apigateway.Deployment(this, 'stage', {api: api}), stageName: 'second stage', variables: {foo: 'bar_bar'} }) api.root.addResource(lambdaFn.functionName).addMethod('POST', new apigateway.LambdaIntegration(lambdaFn)) // 追加したステージからのPermissionをlambdaに追加が必要 new lambda.CfnPermission(this, 'secondStageInvoke', { action: 'lambda:InvokeFunction', functionName: lambdaFn.functionName, principal: 'apigateway.amazonaws.com', sourceArn: 'arn:aws:execute-api:' + this.region + ':' + this.account + ':' + api.restApiId + '/' + stage.stageName + '/POST/' + lambdaFn.functionName }) } }
- 投稿日:2020-04-01T18:31:20+09:00
AWSのRDSが日本語対応にならない場合の対処法
はじめに
Ruby on Rails初心者です。今回はアプリケーションのデプロイ時に苦戦した箇所があったので
勉強のために備忘録として残したいと思います。前提
Rails 5.2.4
問題
・AWSのRDSでMySQLインスタンスを作成
(RDSのMySQLの文字コードは、初期設定は「latin」)
・その後「パラメータグループ」で日本語対応にするも本番環境で
日本語対応されておらず、、解決方法
・EC2にSSHで接続
ssh -i /Users/ユーザー名/.ssh/キーペア名.pem ec2-user@xx.xx.xx.xx・EC2からRDSにアクセス(パスワード要求されます)
mysql -h エンドポイント -P Port -u ユーザ名 -p データベース名・データベースの状態を確認
mysql> show variables like 'char%';・以下のように表示される
+--------------------------+-------------------------------------------+ | Variable_name | Value | +--------------------------+-------------------------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | latin1 | | character_set_filesystem | utf8mb4 | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /rdsdbbin/mysql-5.7.22.R5/share/charsets/ | +--------------------------+-------------------------------------------+「character_set_database」が「 latin1 」 になっていたので、これを「utf8mb4」に直す
・データベースの文字コード修正mysql> ALTER DATABASE データベース名 default character set utf8mb4;・再度データベースの状態を確認
+--------------------------+-------------------------------------------+ | Variable_name | Value | +--------------------------+-------------------------------------------+ | character_set_client | utf8mb4 | | character_set_connection | utf8mb4 | | character_set_database | utf8mb4 | | character_set_filesystem | utf8mb4 | | character_set_results | utf8mb4 | | character_set_server | utf8mb4 | | character_set_system | utf8 | | character_sets_dir | /rdsdbbin/mysql-5.7.22.R5/share/charsets/ | +--------------------------+-------------------------------------------+これで日本語化に対応できた、、、と思ったができておらず(^^;)
原因はテーブルをすでに作成していたため、テーブルの文字コードも変更しなければ
ならなかった。・テーブルの文字コード変更
mysql> ALTER TABLE テーブル名 CONVERT TO CHARACTER SET utf8md4自分で作成した全てのテーブルを変更し、問題解決した
参考にさせていただいた記事
- 投稿日:2020-04-01T13:35:00+09:00
AWS公式さんがDocker Hubで aws-cli のイメージを公開してくれた!
AWS公式さんがDocker Hubでイメージを公開してくれました。 (2020/03/31)
待ってましたAWS CLI v2 Docker image | AWS Developer Blog
https://aws.amazon.com/jp/blogs/developer/aws-cli-v2-docker-image/(Google翻訳) AWS CLI v2の2.0.6のリリースにより、AWS CLI v2がDockerイメージとして利用できるようになったことをお知らせします。
これにより、ユーザーはAWS CLI v2のインストールを自分で管理する必要なく、コンテナベースの環境でAWS CLI v2を使用できます。このDockerイメージを利用するにはさまざまな方法がありますが、特にCI / CD設定では、ローカルマシンのDockerコンテナでAWS CLI v2を実行する方法について説明します。
amazon/aws-cli - Docker Hub
https://hub.docker.com/r/amazon/aws-cli使い方も、かなーり詳しくしてくれているので詳細はDocker Hubをご確認ください。
前に
aws-cli
を実行するためにDockerイメージを作成して利用していた ( DockerコンテナからAWSコマンドを簡単に実行できるコマンドをつくってみた - Qiita ) のですが、その手間が省けます。はかどります。
バージョンは2.0.6
となっており (2020/04/01 時点)、今後のバージョンアップにも対応してくれることでしょう (期待)つかってみる
使い方は簡単です。
エイリアス設定して利用する設定があったので、それを利用してみます。
認証や設定情報を含むディレクトリを
-v ~/.aws:/root/.aws
でマウント、コマンド実行するカレントディレクトリを-v $(pwd):/aws
でDockerコンテナ内の/aws
にマウントすることでファイルの読み書きもホストでaws
コマンドを実行するときと同じ挙動になります。# 確認用としてエイリアスを aws ではなく、 aws-test としています > alias aws-test='docker run --rm -ti -v ~/.aws:/root/.aws -v $(pwd):/aws amazon/aws-cli' # Fishの場合 > alias aws-test='docker run --rm -ti -v ~/.aws:/root/.aws -v $PWD:/aws amazon/aws-cli' > aws-test --version aws-cli/2.0.6 Python/3.7.3 Linux/4.19.76-linuxkit botocore/2.0.0dev10 # カレントディレクトリで読み書きできる > aws-test s3 cp ./hoge.txt s3://<任意のバケット>/ # カレントディレクトリ外はだめ > aws-test s3 cp ../hoge.txt s3://<任意のバケット>/これでもうイメージをビルドしなくてもいいんやなって。
参考
amazon/aws-cli - Docker Hub
https://hub.docker.com/r/amazon/aws-cliDockerコンテナからAWSコマンドを簡単に実行できるコマンドをつくってみた - Qiita
https://qiita.com/kai_kou/items/99d9f9372eed970eab3b
- 投稿日:2020-04-01T10:33:30+09:00
AWS で No space left on device と出た時
はじめに
AWSでアプリケーションを動かしている時に、No space left on device というエラーが出てきたので、詳しく調べてまとめてみた。
対処法
dfというコマンドを使用して、ディスクの使用量を確認する。
ターミナル$ df -h Filesystem Size Used Avail Use% Mounted on /dev/xvda1 16G 16G 0G 100% / devtmpfs 993M 56K 993M 1% /dev tmpfs 1001M 0 1001M 0% /dev/shm次にduというコマンドを使用して、どのフォルダがディスク容量を多く使っているかを特定する。
ターミナルdu -h | sort -rh | head -5 10G . 6.5G ./hoji 4.5G ./hoji/log 1.1G ./hoji 1.2G ./hojiここでは、/hoji/logというフォルダが容量を多く使用していることがわかるので、logの中身を削除してやると、ディスク容量に空きができる。
以上
- 投稿日:2020-04-01T09:00:18+09:00
Amazon CloudWatch EventsのイベントルールでターゲットをAmazon SQSにしてAWS Lambdaでイベントソースにして処理するAWS CloudFormationのテンプレートをつくってみた
AWSマネジメントコンソールからだと簡単に設定できましたが、AWS CloudFormationのテンプレート化するのにいろいろとハマったのでメモ。
リソース
利用するサービスは以下になります。
- Amazon S3
- AWS CloudTrail
- Amazon CloudWatch Events
- Amazon SQS
- AWS Lambda
- AWS CloudFormation(リソース管理用)
ポイント
先にポイントをいくつかあげてみます。
完成形のテンプレートはこのあとにおいてます。Amazon S3のバケットを複数用意する
Amazon CloudWatch EventsでAmazon S3のイベントを扱うのにAWS CloudTrailも必要になります。
Amazon S3 ソースの CloudWatch イベント ルールを作成する (コンソール) - CodePipeline
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/create-cloudtrail-S3-source-console.htmlAWS CloudTrail 証跡を作成にはログファイルを出力するAmazon S3のバケットがいりますが、これをイベントを扱いたいバケットにしてしまうと。。。あとはわかりますね。無限ループに陥ります。
また、AWS CloudTrailでログを出力しない設定にすると、イベントが発火しませんでした。
AWS CloudTrailのログファイルを保存するキー名は固定
ログファイルを保存する先は
バケット名/AWSLogs/AWSアカウントID/*
と指定する必要があります。テンプレート抜粋CloudTrailBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref OutputBucket PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: cloudtrail.amazonaws.com Action: s3:GetBucketAcl Resource: !GetAtt OutputBucket.Arn - Effect: Allow Principal: Service: cloudtrail.amazonaws.com Action: s3:PutObject Resource: !Join - "" - - !GetAtt OutputBucket.Arn - "/AWSLogs/" - !Ref "AWS::AccountId" - "/*" Condition: StringEquals: s3:x-amz-acl: bucket-owner-full-control
AWSLogs
を別名にしてみたらAWS CloudFormationのスタック作成でエラーになりました。Incorrect S3 bucket policy is detected for bucket: <ProjectName>-output (Service: AWSCloudTrail; Status Code: 400; Error Code: InsufficientS3BucketPolicyException; Request ID: 3a9a7575-5226-4f19-b62a-737e87acc9b8)
AWSアカウントID
を指定せずにバケット名/AWSLogs/*
とするとイベントが発火しませんでした。AWS Lambda関数でメッセージの削除はしなくて良い
AWS Lambda関数でAmazon SQSを自前でポーリングして処理する場合、正常に処理が完了したらメッセージを削除する必要があったのですが、イベントソースにするとそれも必要なくなるみたいです。
AWS LambdaがSQSをイベントソースとしてサポートしました! | Developers.IO
https://dev.classmethod.jp/articles/aws-lambda-support-sqs-event-source/次に関数コードを入力します。サンプルの関数はチュートリアル通り以下の内容として保存します。ここでSQSメッセージの削除処理を入れていないことが分かります。
最初はチュートリアルのサンプルだからかな?と思ってましたが実際に動作させると正常終了時にメッセージが勝手に削除されました。こりゃ便利。
なので実装はキューの情報からS3バケットに保存されたオブジェクトのキーを取得して出力しているだけです。
テンプレート抜粋ReceiveQueFunction: Type: AWS::Lambda::Function Properties: FunctionName: !Sub "${ProjectName}-ReceiveQueFunction" Handler: "index.lambda_handler" Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: | from __future__ import print_function import json import os import boto3 def lambda_handler(event, context): for record in event["Records"]: requestParameters = json.loads(record["body"])["detail"]["requestParameters"] print(str(requestParameters)) Runtime: "python3.7" Timeout: "60" ReservedConcurrentExecutions: 3AWS Lambda関数の同時実行数を調整する
上記テンプレートで
ReservedConcurrentExecutions: 3
と同時実行数を指定していますが、こちらはケース・バイ・ケースで指定する必要があります。AWS::Lambda::Function - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-reservedconcurrentexecutions大量のキューをさばく必要がある場合、同時実行数の制限まで全力でポーリングしてくれます。
なので同時実行数を指定していないと、標準設定の1,000
まで同時実行してくれます。アカウントの同時実行数は最大1,000
となりますので、もし他にも関数がある場合、影響する可能性があるのでご注意ください。イベントルールのターゲット指定でバケット名やキーがプレフィックス指定できる
こちらは下記記事をご参考ください。地味に便利です。
Amazon CloudWatch EventsのルールでAmazon S3のキーをプレフィックス指定できた - Qiita
https://qiita.com/kai_kou/items/7104551a09fd9d195531今回はキーを
prefix: hoge/
とすることでs3://バケット名/hoge/
配下にオブジェクトがPUTされた場合にイベントが発火する設定にしました。テンプレート抜粋CloudWatchEventRule: Type: AWS::Events::Rule Properties: Name: !Sub "${ProjectName}-EventRule" EventPattern: source: - aws.s3 detail-type: - "AWS API Call via CloudTrail" detail: eventSource: - s3.amazonaws.com eventName: - CopyObject - PutObject - CompleteMultipartUpload requestParameters: bucketName: - !Ref InputBucket key: - prefix: hoge/ Targets: - Arn: !GetAtt S3EventQueue.Arn Id: !Sub "${ProjectName}-TarfgetQueue"SQSのキューポリシーを設定する
今回、一番ドハマリしました。
キューポリシーがなくてもリソースは作成できるのですが、それだとバケットにオブジェクトをPUTしてもイベントが発火しませんでした。AWSのドキュメントを漁ってみてもそれらしき記述が見当たらずでしたが、下記のフォーラムに情報があり知ることができました。AWS Developer Forums: CloudWatch event rule not sending to ...
https://forums.aws.amazon.com/message.jspa?messageID=742808AWSマネジメントコンソールでぽちぽちと設定する場合にはポリシーを勝手に作成してくれるので、テンプレート化する際にハマりやすいポイントだったみたいです。
テンプレート抜粋SQSQueuePolicy: Type: AWS::SQS::QueuePolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: AWS: "*" Action: - "sqs:SendMessage" Resource: - !GetAtt S3EventQueue.Arn Condition: ArnEquals: "aws:SourceArn": !GetAtt CloudWatchEventRule.Arn Queues: - Ref: S3EventQueueテンプレート
ちょっと長いですがテンプレートになります。
AWSTemplateFormatVersion: "2010-09-09" Parameters: ProjectName: Type: String Default: "<お好みで>" Resources: InputBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub "${ProjectName}-input" AccessControl: Private PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True OutputBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub "${ProjectName}-output" AccessControl: Private PublicAccessBlockConfiguration: BlockPublicAcls: True BlockPublicPolicy: True IgnorePublicAcls: True RestrictPublicBuckets: True S3EventQueue: Type: AWS::SQS::Queue Properties: DelaySeconds: 0 VisibilityTimeout: 360 LambdaExecutionRole: Type: AWS::IAM::Role Properties: RoleName: !Sub "${ProjectName}-LambdaRolePolicy" 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: !Sub "${ProjectName}-LambdaRolePolices" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - s3:* Resource: "*" - PolicyName: !Sub "${ProjectName}-LambdaRoleSQSPolices" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - sqs:ReceiveMessage - sqs:DeleteMessage - sqs:GetQueueAttributes - sqs:ChangeMessageVisibility Resource: !GetAtt S3EventQueue.Arn ReceiveQueFunction: Type: AWS::Lambda::Function Properties: FunctionName: !Sub "${ProjectName}-ReceiveQueFunction" Handler: "index.lambda_handler" Role: !GetAtt LambdaExecutionRole.Arn Code: ZipFile: | from __future__ import print_function import json import os import boto3 def lambda_handler(event, context): for record in event["Records"]: requestParameters = json.loads(record["body"])["detail"]["requestParameters"] print(str(requestParameters)) Runtime: "python3.7" Timeout: "60" ReservedConcurrentExecutions: 3 CloudTrailBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref OutputBucket PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: cloudtrail.amazonaws.com Action: s3:GetBucketAcl Resource: !GetAtt OutputBucket.Arn - Effect: Allow Principal: Service: cloudtrail.amazonaws.com Action: s3:PutObject Resource: !Join - "" - - !GetAtt OutputBucket.Arn - "/AWSLogs/" - !Ref "AWS::AccountId" - "/*" Condition: StringEquals: s3:x-amz-acl: bucket-owner-full-control CloudTrail: Type: AWS::CloudTrail::Trail DependsOn: - CloudTrailBucketPolicy Properties: TrailName: !Sub "${ProjectName}-Trail" S3BucketName: !Ref OutputBucket EventSelectors: - DataResources: - Type: AWS::S3::Object Values: - Fn::Sub: - "${InputBucketArn}/" - InputBucketArn: !GetAtt InputBucket.Arn ReadWriteType: WriteOnly IncludeManagementEvents: false IncludeGlobalServiceEvents: true IsLogging: true IsMultiRegionTrail: false CloudWatchEventRule: Type: AWS::Events::Rule Properties: Name: !Sub "${ProjectName}-EventRule" EventPattern: source: - aws.s3 detail-type: - "AWS API Call via CloudTrail" detail: eventSource: - s3.amazonaws.com eventName: - CopyObject - PutObject - CompleteMultipartUpload requestParameters: bucketName: - !Ref InputBucket key: - prefix: hoge/ Targets: - Arn: !GetAtt S3EventQueue.Arn Id: !Sub "${ProjectName}-TarfgetQueue" SQSQueuePolicy: Type: AWS::SQS::QueuePolicy Properties: PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: AWS: "*" Action: - "sqs:SendMessage" Resource: - !GetAtt S3EventQueue.Arn Condition: ArnEquals: "aws:SourceArn": !GetAtt CloudWatchEventRule.Arn Queues: - Ref: S3EventQueue LambdaFunctionEventSourceMapping: Type: AWS::Lambda::EventSourceMapping DependsOn: - S3EventQueue - ReceiveQueFunction Properties: BatchSize: 10 Enabled: true EventSourceArn: !GetAtt S3EventQueue.Arn FunctionName: !GetAtt ReceiveQueFunction.Arnスタック作成して動かしてみる
最後にざくっと検証してみます。
# リソースを作成 > cd テンプレートファイルがある場所 > aws cloudformation create-stack \ --stack-name <お好みで> \ --template-body file://<テンプレートファイル名> \ --capabilities CAPABILITY_NAMED_IAM \ --region <お好みの> \ --parameters '[ { "ParameterKey": "ProjectName", "ParameterValue": "<お好みで>" } ]' { "StackId": "arn:aws:cloudformation:<お好みのリージョン>:xxxxxxxxxxxx:stack/<お好みのスタック名>/18686480-6f21-11ea-bcf3-020de04cec9a" } # ファイルをアップロード > touch hoge.txt # hogeキー配下にアップロードしない > aws s3 cp hoge.txt s3://<ProjectName>-input/ upload: ./hoge.txt to s3://<ProjectName>-input/hoge/hoge.txt > aws s3 cp hoge.txt s3://<ProjectName>-input/hoge/ upload: ./hoge.txt to s3://<ProjectName>-input/hoge/hoge.txt > aws s3 ls --recursive s3://<ProjectName>-input 2020-03-26 06:28:03 0 hoge.txt 2020-03-26 06:26:12 0 hoge/hoge.txt # Lambda関数のログを確認 > aws logs get-log-events \ --region <お好みのリージョン> \ --log-group-name '/aws/lambda/<ProjectName>-ReceiveQueFunction' \ --log-stream-name '2020/03/26/[$LATEST]ae8735ef9a1c46c38ab241f23a26b384' \ --query "events[].[message]" \ --output text START RequestId: 10f98cf9-c39b-531b-9514-da0f8ea72a42 Version: $LATEST {'bucketName': '<ProjectName>-input', 'Host': '<ProjectName>-input.s3.<お好みのリージョン>.amazonaws.com', 'key': 'hoge/hoge.txt'} END RequestId: 10f98cf9-c39b-531b-9514-da0f8ea72a42 REPORT RequestId: 10f98cf9-c39b-531b-9514-da0f8ea72a42 Duration: 1.85 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 69 MB Init Duration: 275.58 msやったぜ。
まとめ
AWSマネジメントコンソールで設定すると比較的かんたんに設定できるのですが、CFnのテンプレート化する際にはそこそこ大変でしたが、良い知見を得ることができました。
参考
Amazon S3 ソースの CloudWatch イベント ルールを作成する (コンソール) - CodePipeline
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/create-cloudtrail-S3-source-console.htmlAWS LambdaがSQSをイベントソースとしてサポートしました! | Developers.IO
https://dev.classmethod.jp/articles/aws-lambda-support-sqs-event-source/AWS::Lambda::Function - AWS CloudFormation
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html#cfn-lambda-function-reservedconcurrentexecutionsAmazon CloudWatch EventsのルールでAmazon S3のキーをプレフィックス指定できた - Qiita
https://qiita.com/kai_kou/items/7104551a09fd9d195531AWS Developer Forums: CloudWatch event rule not sending to ...
https://forums.aws.amazon.com/message.jspa?messageID=742808Amazon CloudWatch LogsのログをAWS CLIでいい感じに取得する - Qiita
https://qiita.com/kai_kou/items/60bbe314b74b9eaf7126AWS CLIを使ってAWS Lambdaのログ取得時に注意したいこと | Developers.IO
https://dev.classmethod.jp/articles/note-log-of-lambda-using-awscli/
- 投稿日:2020-04-01T01:41:13+09:00
【Rails】HerokuでAWS s3に画像をアップロードしようとしたら<Message>Access Denied</Message>
事前準備
Railsでcarrierwaveを使ってAWS S3に画像をアップロードする手順を画像付きで説明する
概要についてはこちらの記事がとても分かりやすかったので参考にさせていただきました。
ただしこのまま
$ git push
すると、アクセスキーがアップロードされてしまうので注意です。
アクセスキーの隠し方については、heroku 環境変数
とかgem 'dotenv-rails'
、$ heroku config:set ACCESS_KEY=aaaaaa
とか調べると出てくると思います。この記事を読むべき人
- https://myapp.herokuapp.com で画像をs3にアップロードしようとしてもうまくいかない
$ heroku logs
したら<Message>Access Denied</Message>
って言われるこの状況の人にはお役に立てるかもしれません。
手順
IAMのユーザーのARNを取得する
- 「AWSマネジメントコンソール」で「セキュリティ、ID、およびコンプライアンス /IAM」を選択
- 「IAMリソース」の「ユーザー: 2」を選択(数字は人それぞれ)
- 「ユーザー」を選択
- 「ユーザーの ARN」をコピーする
パブリックアクセスのブロックをオフにする
- 「AWSマネジメントコンソール」で「ストレージ /s3」を選択
- バケットを選択
- 「アクセス権限」タブを選択
- 「ブロックパブリックアクセス」タブを選択(デフォルトで選択されている)
- 全てのブロックをオフにする
バケットポリシーを使用する
- 「ブロックパブリックアクセス」から「バケットポリシー」タブに切り替える
- 「バケットポリシーエディター」に以下を適用して、保存する
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111111111111:user/IAMユーザー名" }, "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectAcl" ], "Resource": "arn:aws:s3:::バケット名/*" } ] }
"arn:aws:iam::111111111111:user/IAMユーザー名"
はコピーしてきたIAMユーザーのARN"Resource": "arn:aws:s3:::バケット名/*"
にバケット名を入力する以上
これでいけるはず。
参考記事