- 投稿日:2020-09-17T23:57:51+09:00
ECS FargateのログをFireLensを使ってCloudWatchとFirehoseの両方に送る
やりたいこと
ECS Fargateで動いているRailsのアプリケーションログをfirelens(カスタマイズしたfluent-bitのイメージ)を使ってCloudWatchと2つのFirehoseにそれぞれ送りたい。
①ECS Fargate -> Firehose -> S3 (foo_log) -> Glue -> Athena
②ECS Fargate -> Firehose -> S3 (bar_log) -> Glue -> Athena
③ECS Fargate -> CloudWatch (全てのログ)にログを送りたいという想定。
(fooとbarはある特定の条件のログのみを抽出したいという意)構成図
作業手順
- リソース作成
- CloudWatchと2つのFirehoseに送るようにfluent-bitイメージをカスタマイズする
- log-routerコンテナを定義
- カスタマイズしたfluent-bitイメージでlog-routerコンテナを動かしてログを収集する
1. リソース作成
- firehose(今回は各firehoseごとにIAMロールを作成する想定)
- foo_firehose
- foo_firehose_role
- bar_firehose
- bar_firehose_role
- cloudwatch
S3
- foo_bucket
- bar_bucket
ECRリポジトリ(画像にはないがカスタマイズしたfluent-bitイメージを管理する)
https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/userguide/repository-create.html
を参考に、カスタマイズしたfluent-bitのイメージを置くためのECRリポジトリを作成。また各ロールに必要なポリシーをアタッチします。
ecs_task_role
- firehoseにアクセスするためのPolicyをattach
ecs_task_firehose_policy.json{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "firehose:PutRecordBatch", "Resource": [ "arn:aws:firehose:{{region}}:{{account-id}}:deliverystream/foo-log", "arn:aws:firehose:{{region}}:{{account-id}}:deliverystream/bar-log" ] } ] }
- CloudWatchにアクセスするためのPolicyをattach
arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
foo_firehose_role
foo_firehoseがfoo_bucketにアクセスできるように、foo_firehose_roleにPolicyをattachする
foo_firehose_role_policy.json{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject", "s3:PutObjectAcl" ], "Resource": [ "arn:aws:s3:::foo-bucket", "arn:aws:s3:::foo-bucket/*" ] } ] }bar_firehose_role
bar_firehoseがbar_bucketにアクセスできるようbar_frehose_roleにPolicyをattachする
bar_firehose_role_policy.json{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject", "s3:PutObjectAcl" ], "Resource": [ "arn:aws:s3:::bar-bucket", "arn:aws:s3:::bar-bucket/*" ] } ] }2. CloudWatchと2つのFirehoseに送るようにfluent-bitイメージをカスタマイズする
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/using_firelens.html
ここを読むと、firelensではAWSが提供しているAWS for Fluent Bitを使うこともできます。
しかしこれではCloudWatchとFirehoseの両方にログを送ることができないので、今回はfluent-bit.confをカスタマイズして加えたDockerイメージを使うようにします。カスタマイズしたconfが以下の2つです。
docker/fluent-bit/fluent-bit.conf[SERVICE] Flush 1 Parsers_File fluent-bit-parsers.conf # logというkeyの中身をデコードする [FILTER] Name parser Match * Key_Name log Parser rails # logの中身以外はいらないので消去する(container_idとか) Reserve_Data false # ログにtagをつける [FILTER] Name rewrite_tag Match * # nameがfooのもののタグをfoo_logにする Rule $name ^(foo)$ foo_log true [FILTER] Name rewrite_tag Match * # nameがbarのものはタグをbar_logにする Rule $name ^(bar)$ bar_log true # logの送信 # 全てのログ(rails-firelens-*というtagがついている)をCloudWatchに送る [OUTPUT] Name cloudwatch Match rails-firelens-* region {CloudWatchロググループのregion} log_group_name {CloudWatchロググループの名前} # つけたいprefixを指定。ここではlatest/としている log_stream_prefix latest/ # tagがfoo_logのものをfirehoseに送る [OUTPUT] Name firehose Match foo_log region {foo_firehoseのregion} delivery_stream foo_firehose # tagがbar_logのものをfirehoseに送る [OUTPUT] Name firehose Match bar_log region {bar_firehoseのregion} delivery_stream bar_firehosedocker/fluent-bit/fluent-bit-parsers.conf# ログがエンコードされてlogというkeyの中に入っているのでデコードするためのparser # https://docs.fluentbit.io/manual/pipeline/parsers/decoders [PARSER] Name rails Format json Time_Key time Time_Format %Y-%m-%dT%H:%M:%S %z # Command | Decoder | Field | Optional Action | # ==============|===========|=======|===================| Decode_Field_As escaped logfluent-bitの公式ドキュメント
https://docs.fluentbit.io/manual/基本的に公式ドキュメントに沿ってconfをカスタマイズしていきました。
ただドキュメントが全部英語なので、今回書いた部分だけ解説を残します。解説
上から説明していきます。
[SERVICE] Flush 1 Parsers_File fluent-bit-parsers.conf
[SERVICE]
はログ全体の設定を定義するところです(多分)。Flush 1ではドキュメントにあるようにログの出力間隔を指定しています。
https://docs.fluentbit.io/manual/administration/configuring-fluent-bit/configuration-fileFlush
Set the flush time in seconds.nanoseconds. The engine loop uses a Flush timeout to define when is required to flush the records ingested by input plugins through the defined output plugins.デフォルトだと5秒で、5秒だと例えばlog-routerコンテナが起動に失敗した時に、ログが出力される前にタスクがストップしてしまう、とかいうことがあったのでここでは1にしています。
Parsers_File fluent-bit-parsers.confここではParserが書いてあるファイルパスを指定します。
独自でParserを作成した場合、このように別ファイルから読み込むようにするのがルール見たいです。Parserの解説は次にします。ログの整形
Parser
https://docs.fluentbit.io/manual/pipeline/parsers/decoders を参考にして作成したParserのファイルです。
firelensは渡ってきたログに勝手に情報を付け加えるので、Parserに不要なログは切り落としてアプリケーションのログだけを収集するという設定をします。(実行はfluent-bit.confの[FILTER]で定義する)fluent-bitは特にカスタマイズしないとこんな感じでログを送ります。
{ "container_id": "3e638f00-c1a7-4794-b796-1d916dfa8cbc-1555792190", "container_name": "rails", "ecs_cluster": "{{cluster-name}}", "ecs_task_arn": "arn:aws:ecs:{{region}}:{{account-Id}}:task/{{task_id}}", "ecs_task_definition": "{{task-definition}}", "log": "{\"host\":\"xxxxxxxxx\",\"application\":\"xxxxxxxxxx\",\"environment\":\"xxxxxxx\",\"timestamp\":\"2020-01-01T00:00:00.00000Z\",\"level\":\"xxxx\",\"level_index\":x,\"pid\":xx,\"thread\":\"xxxxxx\",\"name\":\"xxxx\",\"message\":\"xxxxxx\"}firelens側でcontainer_id とか container_name とか勝手につけてくれます。
そしてlogに実際にRailsアプリケーションが出したjsonのログがstringにエンコードされて入っています。
(今回はRailsアプリケーション側でもログはjsonで吐き出すように設定している)
これをjsonに戻すための設定を定義しているのが、このfluent-bit-parsers.confになります。docker/fluent-bit/fluent-bit-parsers.conf# ログがエンコードされてlogというkeyの中に入っているのでデコードするためのparser # https://docs.fluentbit.io/manual/pipeline/parsers/decoders [PARSER] Name rails Format json Time_Key time Time_Format %Y-%m-%dT%H:%M:%S %z # Command | Decoder | Field | Optional Action | # ==============|===========|=======|===================| Decode_Field_As escaped log
Name rails
でこのParserに名前をつけています(自由です)。
一応命名規則としては、ログを吐き出したものの名前をつけるみたいで、今回扱うのはRailsが吐き出したログなのでrailsにしています。(ドキュメントではdocker
になっている)fluent-bit.confに戻ります。
docker/fluent-bit/fluent-bit.conf# logというkeyの中身をデコードする [FILTER] Name parser Match * Key_Name log Parser rails # logの中身以外はいらないので消去する(container_idとか) Reserve_Data falseここで先ほど定義したParserを使って実行してね、という処理を書きます。
参考: https://docs.fluentbit.io/manual/pipeline/filters/parser
[FILTER]
いろんな用途で使われるようで、用途ごとにName
を変えていきます。
今回はParserを使いたいからName parser
です。
Match *
このFILTERに引っ掛けるログのタグを指定します。
このタグについてはあとで解説します。
*
では全部のログがこのFILTERを通るという意味になります。
Key_Name log
これがFILTERの対象にしたいログの中のKeyを指定するところですね。
今回はlog
です。
Parser rails
ここでどんなParserを使いたいか指定します。先ほど作成したrails
を指定。
Reserve_Data false
これは Key_Name で指定した以外のKeyのデータはどうする?という意味になります。
ここをfalseにすると指定した以外のKey、今回でいうとlog
というKey以外は削除されます。
よってログに実際のアプリケーションが出したログのみを出力することができます。
もし、firelensが付与してくれたcontainer_id
やcontainer_name
とかも出力させたい!ということでしたらここをtrueにすれば出力されます。ここまでがログをいい感じに整形する部分です。
次からはログを分割したい時の各設定です。
そこで必要な知識がさっき言ったログのタグです。fluent-bitにおけるログのタグ
fluent-bit側で、ログの種類を分けるためにログにタグをつけることができます。
ただfluent-bitによって吐き出されたログがjsonの場合はそのタグの情報は見えません。
jsonでなければログは下のように出力されてタグも見えます。[0] foo_log: [1598502349.876671400, {"host"=>"xxxxxxxxx", "timestamp"=>"2020-01-01T00:00:00.000000Z", "level"=>"xxxx", "level_index"=>0, "pid"=>00, "thread"=>"xxxxxxxxx", "name"=>"foo", "message"=>"xxxxxx"}}]この
foo_log
の部分がタグです。また今回とっても大事なのが、firelensに入ってきたログにはデフォルトで
{container_name}-firelens-{task_id}
というタグがつけられているということです。
今回だとrails-firelens-89caedfc-07fd-41f3-8239-5313a7d10ca7
のようなタグが最初に全てのログにつけられています。
これをうまく使ってログの分割を行なっていきます。ログのタグ付け
docker/fluent-bit/fluent-bit.conf[FILTER] Name rewrite_tag Match * # nameがfooのもののタグをfoo_logにする Rule $name ^(foo)$ foo_log true [FILTER] Name rewrite_tag Match * # nameがbarのものはタグをbar_logにする Rule $name ^(bar)$ bar_log trueここも
[FILTER]
ですがName rewrite_tag
となっています。
その名の通りここでタグの上書きを行います。
Rule $name ^(foo)$ foo_log true
参考: https://docs.fluentbit.io/manual/pipeline/filters/rewrite-tag#rules
ドキュメントの通りRuleでは$KEY REGEX NEW_TAG KEEP
を記述しています。
[FIRTER]
は記述した順番に処理されるらしく、ここに来るログはすでにlog
というkeyの中身(applicationが吐き出したログそのもの)がやってきます。
今回はname
というkeyにfoo
かbar
という文字が入っているという想定です。
$name ^(foo)$ foo_log true
これはnameというKeyの中身がfoo
ならfoo_log
というタグをつける、という意味になります。
(^(foo)$
は正規表現です)
true
はタグをつけた後、タグをつける前のログをとっておくかどうか、を表します。
具体的にいうと、もともとはrails-firelens-*
というタグがついていたけれど、foo_log
というタグを上書きしたので、元のrails-firelens-*
のタグがついている方は取っておくか?という意味です。
今回はこの後の用途にて必要になるのでtrue
にして取っておくようにしています。
false
にすれば元のログは削除されfoo_log
とついたもののみ、残ります。ここまでで、ログをいい感じに整形して、タグをつけるところまできました。
あとはタグを使って出力先を指定するだけです。ログの出力
docker/fluent-bit/fluent-bit.conf# logの送信 # 全てのログ(*-firelens-*というtagがついている)をCloudWatchに送る [OUTPUT] Name cloudwatch Match rails-firelens-* region {CloudWatchロググループのregion} log_group_name {CloudWatchロググループの名前} # つけたいprefixを指定。ここではlatest/としている log_stream_prefix latest/ # tagがfoo_logのものをfirehoseに送る [OUTPUT] Name firehose Match foo_log region {foo_firehoseのregion} delivery_stream foo_firehose # tagがbar_logのものをfirehoseに送る [OUTPUT] Name firehose Match bar_log region {bar_firehoseのregion} delivery_stream bar_firehoseこのように[OUTPUT]を複数かくと、複数の場所に出力されます。
- CloudWatch
docker/fluent-bit/fluent-bit.conf[OUTPUT] Name cloudwatch Match rails-firelens-*今回はCloudWatchに「applicationが吐き出した全てのログ」を送りたいと考えています。
タグがfoo_log、bar_log、そうでないもの全てです。
ここで、さっきrails-firelens-*
というタグのログを残しておいたのが生きてきます。
foo_logやbar_logというタグとは別にrails-firelens-*
というタグがついた状態で全てのログが残っているので、このタグのログを送ってあげれば全てのログが送られることになります。ですのでMatch rails-firelens-*
と書きます。
- Firehose
docker/fluent-bit/fluent-bit.conf# tagがfoo_logのものをfirehoseに送る [OUTPUT] Name firehose Match foo_log region {foo_firehoseのregion} delivery_stream foo_firehose # tagがbar_logのものをfirehoseに送る [OUTPUT] Name firehose Match bar_log region {bar_firehoseのregion} delivery_stream bar_firehoseここも難しくはなく、Matchで送りたいログのタグを指定して、
delivery_stream
に送るfirehose名をきます。カスタマイズしたconfを配置するDockerfileを書く。
作成した、
fluent-bit.conf
、fluent-bit-parsers.conf
をコンテナのルートに配置するようにDockerfileを書きます。(ルートでなくても問題ない)docker/fluent-bit/Dockerfile.FROM amazon/aws-for-fluent-bit:latest COPY ./docker/fluent-bit/fluent-bit.conf /fluent-bit.conf COPY ./docker/fluent-bit/fluent-bit-parsers.conf /fluent-bit-parsers.conf3.log-routerコンテナを定義
ecs-task-definition.jsonはこのように書きます。
ecs-task-definition.json{ "containerDefinitions": [ # railsコンテナを定義 { "command": [ "bundle", "exec", "unicorn", "-p", "3000", "-c", "/rails/config/unicorn_ecs.rb" ], "cpu": 0, "dnsSearchDomains": [], "dnsServers": [], "dockerSecurityOptions": [], "entryPoint": [], "environment": [], "essential": true, "image": "Railsサービスを動かすイメージを指定する" "links": [], # ここでrailsコンテナのログドライバーをfirelensにする "logConfiguration": { "logDriver": "awsfirelens" }, "mountPoints": [], "name": "rails", "portMappings": [ { "containerPort": 3000, "hostPort": 3000, "protocol": "tcp" } ], "volumesFrom": [] }, # log-routerコンテナを定義 { "essential": true, "image": "カスタマイズしてbuild,pushしたECRにあるfluent-bitのイメージを指定", "name": "log-router", "environment": [], # カスタマイズしたconfをパス指定 "firelensConfiguration": { "type": "fluentbit", "options": { "config-file-type": "file", "config-file-value": "/fluent-bit.conf" } }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "log-router", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "latest" } }, "memoryReservation": 50 } ], "cpu": "2048", "taskRoleArn": "taskRoleArnを書く", "executionRoleArn": "taskExecutionRoleArnを書く", "family": "hoge", "memory": "4096", "networkMode": "awsvpc", "placementConstraints": [], "requiresCompatibilities": ["FARGATE"], "volumes": [] }ここで使いたい
fluent-bit.conf
のパスを指定しています。ecs-task-definition.json# カスタマイズしたconfをパス指定 "firelensConfiguration": { "type": "fluentbit", "options": { "config-file-type": "file", "config-file-value": "/fluent-bit.conf" }今回はルートに配置したので
"config-file-value"
は/fluent-bit.conf
です。
(ルート以外に配置した場合はそのパスを指定)
ここを指定しない場合はAWS側で/fluent-bit/etc/fluent-bit.conf
にあるデフォルトのconfを見にいくようになっています。
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/using_firelens.html#firelens-example-firehose重要
カスタム設定ファイルを使用する場合は、FireLens が使用するパスとは異なるパスを指定する必要があります。Amazon ECS では /fluent-bit/etc/fluent-bit.conf (Fluent Bit) と /fluentd/etc/fluent.conf (Fluentd) のファイルパスは予約されています。4.カスタマイズしたfluent-bitイメージでlog-routerコンテナを動かしてログを収集する
後はfluent-bitのイメージをbuildし、ECRリポジトリへpushした後、ECS Fargateでコンテナを立ち上げます。
railsコンテナとlog-routerコンテナが正しく起動したのを確認し、
- CloudWatch
- S3
- foo_bucket
- bar_bucket
を見て、ログが正しく送られているかを確認します。
補足
特にCloudWatchでログのファイル名をよく見てみると、
latest/rails-firelens-${ランダムな数字}
となっているかと思います。
${ランダムな数字}
は{task_id}
になっているはずです。
つまり、fluent-bit.conf
のCloudWatchの[OUTPUT]
でlog_stream_prefix latest/
と指定したprefixが使われ、{prefix}/{ログのタグ名}
というファイル名になっているのです。
仮にMatch rails-firelens-*
ではなく、Match *
にすると、全てのタグのログがCloudWatchに送られることになるので、このファイルの他にlatest/foo_log
とlatest/bar_log
というログファイルも作られます。
- 投稿日:2020-09-17T22:48:37+09:00
AWSを使ってアプリケーションを公開する手順(7)Capistranoによる自動デプロイ
はじめに
AWSを使ってアプリケーションを公開する手順を記載していく。
この記事ではCapistranoを使ってデプロイ作業を自動化する。Capistranoの導入
CapistranoはRubyで書かれており、Gemが公開されている。
この記事ではrailsにCapistranoを導入する手順を記載するが、PHPなどの別のフレームワークでも使用できるらしい。Capistrano関連のGemをインストールする
Gemfileを以下のように編集する。
Gemfilegroup :development, :test do gem 'capistrano' gem 'capistrano-rbenv' gem 'capistrano-bundler' gem 'capistrano-rails' gem 'capistrano3-unicorn' endターミナルで以下のコマンドを実行し、Gemfileを読み込む。
bundle install以下のコマンドを実行し、Capistranoの関連ファイルを生成する。
bundle exec cap install以下のファイルが生成される。各ファイルの詳細は後述する。
- Capfile
- config/deploy.rb
- config/deploy/production.rb
- config/deploy/staging.rb
Capfileを編集する
Capistranoを動作させるにはいくつかのライブラリ(Gem)を読み込む必要がある。Capfileとは、Capistrano関連のライブラリのうちどれを読み込むか指定するためのファイルである。
Capfileを以下のように編集する。
これによりデプロイに必要な動作が記述されたファイルが入ったディレクトリを読み込む。
参考Capfilerequire "capistrano/setup" require "capistrano/deploy" require 'capistrano/rbenv' require 'capistrano/bundler' require 'capistrano/rails/assets' require 'capistrano/rails/migrations' require 'capistrano3/unicorn' Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }production.rbを編集する
config/deployディレクトリにproduction.rbとstaging.rbが作成された。
これらのファイルはデプロイについての設定を記載するファイルである。
production.rbは本番環境の設定ファイル、staging.rbはステージング環境の設定ファイルである。
production.rb(staging.rb)には下記の内容を記述する。
- サーバホスト名
- AWSサーバのログインユーザ名
- サーバロール
- SSHの設定
- その他サーバに紐づく設定
production.rbを以下のように変更する。
(アプリケーションのElastic IPが12.345.67.890の場合)config/deploy/production.rbserver '12.345.67.890', user: 'ec2-user', roles: %w{app db web}開発環境、テスト環境、ステージング環境、本番環境とは
- 開発環境
- ローカルで動作確認などを行う。ここで問題なければテスト環境での検証を行う。
- テスト環境
- 誤記やリンクのミス、不具合がないかを検証する。
- ステージング環境
- 本番環境の前に動作や表示に問題がないかを検証する。テスト環境は本番環境とサーバの構成が異なるのに対し、ステージング環境は本番環境とサーバの構成が同じである。
- 本番環境
- ステージング環境で問題なければアップロードする。
deploy.rbを編集する
configディレクトリに作成されたdeploy.rbには本番環境、ステージング環境共通の設定を記述する。
具体的には以下を記述する。
- アプリケーション名
- gitのリポジトリ
- 利用するSCM(Software Configuration Management,ソフトウェア構成管理)
- タスク
- それぞれのタスクで実行するコマンド
deploy.rbの記述を削除し、以下のように変更する。
(ここでは例としてCapistranoのバージョンが「3.11.0」、アプリケーション名が「testapp」、Githubのuser名が「test1234」、リポジトリ名が「testapp」、rubyのバージョンが「2.5.1」、ローカルPCのEC2インスタンスのSSH鍵(pem)へのパスが「~/.ssh/xxx.pem」とする。)config/deploy.rb# config valid only for current version of Capistrano # capistranoのバージョンを記載。固定のバージョンを利用し続け、バージョン変更によるトラブルを防止する lock '3.11.0' # Capistranoのログの表示に利用する set :application, 'testapp' # どのリポジトリからアプリをpullするかを指定する set :repo_url, 'git@github.com:test1234/testapp.git' # バージョンが変わっても共通で参照するディレクトリを指定 set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads') set :rbenv_type, :user set :rbenv_ruby, '2.5.1' # どの公開鍵を利用してデプロイするか set :ssh_options, auth_methods: ['publickey'], keys: ['~/.ssh/xxx.pem'] # プロセス番号を記載したファイルの場所 set :unicorn_pid, -> { "#{shared_path}/tmp/pids/unicorn.pid" } # Unicornの設定ファイルの場所 set :unicorn_config_path, -> { "#{current_path}/config/unicorn.rb" } set :keep_releases, 5 # デプロイ処理が終わった後、Unicornを再起動するための記述 after 'deploy:publishing', 'deploy:restart' namespace :deploy do task :restart do invoke 'unicorn:restart' end endCapistranoのバージョン確認方法
gemfile.lockというファイルに記載されている。
DSL(Domain Specific Language)について
DSLとは、特定の処理の効率を上げるために擬似的に用意されるプログラムである。
例えば、set :name, 'value'という記述がある。このとき、fetch nameとすることで'value'を取り出すことができる。setした値はdeploy.rbやproduction.rbでも取り出すことができる。
また、task :xx do〜endという記述がある。これはCapfileでrequireしたものに加えてタスクを追加している。ここで記述したものはcap deploy時に実行される。Capistranoによる自動デプロイ後のディレクトリ構成
Capistranoによる自動デプロイが実行されると、本番環境のディレクトリ構成が変化する。Capistranoによるアプリケーションのバックアップなど、複数のディレクトリが作成される。
例えば以下のようなディレクトリが作成される。
- releasesディレクトリ
- Capistranoを通じてデプロイされたアプリケーションはreleasesというディレクトリにまとめられる。ここに過去のアプリケーションが残っているため、デプロイ時に何か問題が発生した時、以前のバージョンに戻すことが可能。deploy.rbのset :keep_releasesの記述は保存しておく数を指定しており、今回は5回分のバージョンを保存しておくように設定している。
- currentディレクトリ
- releasesディレクトリの中で最新のものが自動的にこのディレクトリにコピーされる。つまり、このディレクトリにあるアプリケーションの内容が、現在デプロイされているアプリケーションの内容ということになる。
- sharedディレクトリ
- バージョンが変わっても共通で参照されるディレクトリが格納される。具体的には、log,public,tmp,vendorディレクトリが格納される。
unicorn.rbを編集する
Capistranoの導入によって、本番環境のディレクトリ構成が変わるのでそれに伴いunicorn.rbの記述も変更する。
unicorn.rbを以下のように変更する。
config/unicorn.rb#サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく #変更:階層を一個深くする app_path = File.expand_path('../../../', __FILE__) #アプリケーションサーバの性能を決定する worker_processes 1 #アプリケーションの設置されているディレクトリを指定 #変更:currentを指定 working_directory "#{app_path}/current" #Unicornの起動に必要なファイルの設置場所を指定 #変更:sharedディレクトリ追記 pid "#{app_path}/shared/tmp/pids/unicorn.pid" #ポート番号を指定 #変更:sharedディレクトリ追記 listen "#{app_path}/shared/tmp/sockets/unicorn.sock" #エラーのログを記録するファイルを指定 #変更:sharedディレクトリ追記 stderr_path "#{app_path}/shared/log/unicorn.stderr.log" #通常のログを記録するファイルを指定 #変更:sharedディレクトリ追記 stdout_path "#{app_path}/shared/log/unicorn.stdout.log" #Railsアプリケーションの応答を待つ上限時間を設定 timeout 60 preload_app true GC.respond_to?(:copy_on_write_friendly=) && GC.copy_on_write_friendly = true check_client_connection false run_once = true before_fork do |server, worker| defined?(ActiveRecord::Base) && ActiveRecord::Base.connection.disconnect! if run_once run_once = false # prevent from firing again end old_pid = "#{server.config[:pid]}.oldbin" if File.exist?(old_pid) && server.pid != old_pid begin sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU Process.kill(sig, File.read(old_pid).to_i) rescue Errno::ENOENT, Errno::ESRCH => e logger.error e end end end after_fork do |_server, _worker| defined?(ActiveRecord::Base) && ActiveRecord::Base.establish_connection endNginxの設定ファイル(rails.conf)を変更する
ディレクトリ構成が変わったのでrails.confも以下のように変更する。
(アプリケーション名が「testapp」、Elastic IPが「12.345.67.890」の場合を例として記載する)rails.confupstream app_server { # sharedの中を参照するよう変更 server unix:/var/www/testapp/shared/tmp/sockets/unicorn.sock; } server { listen 80; server_name 12.345.67.890; # クライアントからアップロードされてくるファイルの容量の上限を2ギガに設定。デフォルトは1メガなので大きめにしておく client_max_body_size 2g; # currentの中を参照するよう変更 root /var/www/testapp/current/public; location ^~ /assets/ { gzip_static on; expires max; add_header Cache-Control public; # currentの中を参照するよう変更 root /var/www/testapp/current/public; } try_files $uri/index.html $uri @unicorn; location @unicorn { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://app_server; } error_page 500 502 503 504 /500.html; }Nginxの設定を変更したらEC2インスタンスにSSH接続し、
下記のコマンドを実行して再読み込み・再起動を行う。sudo service nginx reloadsudo service nginx restartMySQLを再起動する
MySQLが立ち上がっていないとデプロイできないので、
下記のコマンドを実行し念のためMySQLも再起動する。sudo service mysqld restartunicorn masterのプロセスをkillする
自動デプロイを実行する前にunicorn masterのプロセスをkillしておく。
まずは下記のコマンドを実行しunicorn masterのプロセスIDを確認する。ps aux | grep unicorn下記のコマンドで確認したプロセスをkillする。
(今回はunicorn masterのプロセスIDが17877だったとする)kill 17877ローカルでの修正内容をmasterにプッシュする
今回編集したファイルを全てmasterブランチにプッシュしておく。
自動デプロイを実行する
ローカル環境で下記のコマンドを実行し自動デプロイを行う。
エラーがでなければ成功。bundle exec cap production deployエラーが出た時に確認すること
- もう一度実行する
- 記述ミスがないか
- 手順を飛ばしていないか
ブラウザで確認する
ブラウザのURL欄にElastic IPを入力するとアプリケーションにアクセスできる(:3000をつける必要はない)。
エラーが出た時に確認すること
- 開発環境でエラーが出ていないか
- /var/www/testapp/current/log/unicorn.stderr.logでエラーがないか(リポジトリ名が「testapp」の場合)
- プッシュやプルを忘れていないか
- MySQLやNginxの再起動を行ってみる
- EC2インスタンスの再起動を行ってみる
関連記事
AWSを使ってアプリケーションを公開する手順(1)AWSアカウントの作成
AWSを使ってアプリケーションを公開する手順(2)EC2インスタンスの作成
AWSを使ってアプリケーションを公開する方法(3)EC2インスタンスの環境構築
AWSを使ってアプリケーションを公開する手順(4)データベースの作成
AWSを使ってアプリケーションを公開する手順(5)アプリケーションを公開する
AWSを使ってアプリケーションを公開する手順(6)Nginxを導入する
- 投稿日:2020-09-17T22:10:21+09:00
CodePipelineで誰でもお手軽自動デプロイ(静的webページ編)
今回作るやつ概要
GitHub × CodePipelineで自動デプロイ
◯おすすめな人
■ フロント系の方、web制作系の方
■ SPA公開したい方
■ CI/CDで挫折した方
準備しておくもの
■ GitHubアカウント
■ AWSアカウント
■ 静的webホスティングの設定をしたS3バケット
下記が個人的にわかりやすかったです!
・【参考】AWS S3で静的Webページをホスティングする
・【参考】独自ドメインを使ってAmazon S3で静的WebサイトをホストするCodePipeline構築
CodePipelineを開き
パイプラインの作成
を選択。【Step1】パイプライン設定を選択する
■パイプライン名
▫️わかりやすいものならなんでもOK
■サービスロール
▫️新しいサービスロール
■ロール名
▫️自動で入る【Step2】ソースステージを追加する
■ソースプロバイダー
▫️GitHub
■検出オプションを変更する
▫️GitHubウェブフック(推奨)■
GitHubに接続する
を選択■リポジトリ
▫️自動デプロイの対象としたいリポジトリを選択■ブランチ
▫️対象のブランチを選択(masterでpushしたら発火的な)【Step3】ビルドステージを追加する
【Step4】デプロイステージを追加する
■デプロイプロバイダー
▫️Amazon S3
■リージョン&バケット
▫️静的webホスティングの設定をしたS3バケット
■デプロイする前にファイルを抽出する
▫️チェック
【Step5】レビュー
■設定内容の確認をして下さい!
完成!!
デプロイステージに設定したバケットを見てみると、ちゃんと反映されてる!
もちろん対象のブランチでpushしたら変更が反映されます!
感想
静的webということもありましたが意外と簡単に実装できたと思います!
今回は説明しませんでしたがCloudFrontを前段に置くことで独自ドメインを使用することも出来ます!
- 投稿日:2020-09-17T19:11:40+09:00
AWSのArm版UbuntuのUnixBenchマーク
2020年12月末まで無料で使えるとのことでUbuntuの64bitArm版を試してみました。
t4g.micro 2CPU 1GBメモリ
UnixBenchマーク
sudo apt update ; sudo apt upgrade -y ; git clone https://github.com/kdlucas/byte-unixbench.git ; cd byte-unixbench/ ; cd UnixBench/ ; sudo apt install -y make gcc ; make ; ./Run ;結果
======================================================================== BYTE UNIX Benchmarks (Version 5.1.3) System: ip-172-31-35-99: GNU/Linux OS: GNU/Linux -- 5.4.0-1024-aws -- #24-Ubuntu SMP Sat Sep 5 06:17:48 UTC 2020 Machine: aarch64 (aarch64) Language: en_US.utf8 (charmap="UTF-8", collate="UTF-8") 08:43:21 up 18 min, 1 user, load average: 0.71, 0.65, 0.36; runlevel 2020-09-17 ------------------------------------------------------------------------ Benchmark Run: Thu Sep 17 2020 08:43:21 - 09:12:43 2 CPUs in system; running 1 parallel copy of tests Dhrystone 2 using register variables 9370962.1 lps (10.0 s, 7 samples) Double-Precision Whetstone 1387.4 MWIPS (15.7 s, 7 samples) Execl Throughput 1205.6 lps (30.0 s, 2 samples) File Copy 1024 bufsize 2000 maxblocks 207354.8 KBps (30.0 s, 2 samples) File Copy 256 bufsize 500 maxblocks 61433.3 KBps (30.0 s, 2 samples) File Copy 4096 bufsize 8000 maxblocks 537176.1 KBps (30.0 s, 2 samples) Pipe Throughput 471412.4 lps (10.0 s, 7 samples) Pipe-based Context Switching 14210.5 lps (10.1 s, 7 samples) Process Creation 1159.0 lps (30.1 s, 2 samples) Shell Scripts (1 concurrent) 1372.5 lpm (60.1 s, 2 samples) Shell Scripts (8 concurrent) 226.1 lpm (60.1 s, 2 samples) System Call Overhead 593986.9 lps (10.0 s, 7 samples) System Benchmarks Index Values BASELINE RESULT INDEX Dhrystone 2 using register variables 116700.0 9370962.1 803.0 Double-Precision Whetstone 55.0 1387.4 252.3 Execl Throughput 43.0 1205.6 280.4 File Copy 1024 bufsize 2000 maxblocks 3960.0 207354.8 523.6 File Copy 256 bufsize 500 maxblocks 1655.0 61433.3 371.2 File Copy 4096 bufsize 8000 maxblocks 5800.0 537176.1 926.2 Pipe Throughput 12440.0 471412.4 378.9 Pipe-based Context Switching 4000.0 14210.5 35.5 Process Creation 126.0 1159.0 92.0 Shell Scripts (1 concurrent) 42.4 1372.5 323.7 Shell Scripts (8 concurrent) 6.0 226.1 376.9 System Call Overhead 15000.0 593986.9 396.0 ======== System Benchmarks Index Score 303.5 ------------------------------------------------------------------------ Benchmark Run: Thu Sep 17 2020 09:12:43 - 09:42:10 2 CPUs in system; running 2 parallel copies of tests Dhrystone 2 using register variables 8906679.4 lps (10.0 s, 7 samples) Double-Precision Whetstone 1403.2 MWIPS (10.2 s, 7 samples) Execl Throughput 1039.1 lps (29.9 s, 2 samples) File Copy 1024 bufsize 2000 maxblocks 110651.6 KBps (30.0 s, 2 samples) File Copy 256 bufsize 500 maxblocks 30134.5 KBps (30.0 s, 2 samples) File Copy 4096 bufsize 8000 maxblocks 296691.2 KBps (30.0 s, 2 samples) Pipe Throughput 482502.3 lps (10.0 s, 7 samples) Pipe-based Context Switching 68742.0 lps (10.0 s, 7 samples) Process Creation 1821.0 lps (30.1 s, 2 samples) Shell Scripts (1 concurrent) 1610.9 lpm (60.1 s, 2 samples) Shell Scripts (8 concurrent) 224.8 lpm (60.3 s, 2 samples) System Call Overhead 484504.3 lps (10.0 s, 7 samples) System Benchmarks Index Values BASELINE RESULT INDEX Dhrystone 2 using register variables 116700.0 8906679.4 763.2 Double-Precision Whetstone 55.0 1403.2 255.1 Execl Throughput 43.0 1039.1 241.6 File Copy 1024 bufsize 2000 maxblocks 3960.0 110651.6 279.4 File Copy 256 bufsize 500 maxblocks 1655.0 30134.5 182.1 File Copy 4096 bufsize 8000 maxblocks 5800.0 296691.2 511.5 Pipe Throughput 12440.0 482502.3 387.9 Pipe-based Context Switching 4000.0 68742.0 171.9 Process Creation 126.0 1821.0 144.5 Shell Scripts (1 concurrent) 42.4 1610.9 379.9 Shell Scripts (8 concurrent) 6.0 224.8 374.7 System Call Overhead 15000.0 484504.3 323.0 ======== System Benchmarks Index Score 300.5参考リンク
新しい EC2 T4g インスタンス – AWS Graviton2 によるバースト可能なパフォーマンス – 無料で利用可能 | Amazon Web Services ブログ
https://aws.amazon.com/jp/blogs/news/new-t4g-instances-burstable-performance-powered-by-aws-graviton2/
- 投稿日:2020-09-17T18:39:51+09:00
Amazon GameLiftに関する基礎知識
概要
Amazon GameLiftについて調べる機会があったので、全体像を出来るだけ分かりやすいようにまとめてみた。
参考にさせて頂いたサイト
Amazon GameLift の仕組み
https://docs.aws.amazon.com/ja_jp/gamelift/latest/developerguide/gamelift-howitworks.html#gamelift-howitworks-placing
Amazon GameLift – ゲームサーバー/クライアントのやり取り
https://docs.aws.amazon.com/ja_jp/gamelift/latest/developerguide/gamelift-sdk-server-api-interaction-vsd.html
AWS再入門ブログリレー Amazon GameLift 編
https://dev.classmethod.jp/articles/re-introduction-2020-amazon-gamelift/GameLiftとは?
Amazon GameLift は、マルチプレイヤーゲーム用のクラウドサーバーをデプロイ、運用、スケーリングする、専用のゲームサーバーホスティングソリューションです。(公式サイトより転載)
GameLiftは簡単に言うと、マルチプレイに関する色々なことをいい感じに管理/運用してくれるすごいサービス。
主にマッチング、プレイヤー人数に合わせたサーバーのスケーリング、ログの収集などをやってくれる。
FlexMatchの仕組みを使えば、様々な条件を元に最適なマッチングをすることが可能で、待機時間により条件を段階的に緩和することも出来る。
例1:最初は遅延が少ないリージョンで対戦相手を探し、しばらく見つからなかった場合は遅延の上限を緩和して探す。
例2:最初はプレイヤーレベルが近い人を探し、しばらく見つからなかった場合はプレイヤーレベルの差の上限を緩和して探す。全体構成
ざっくりとしたイメージは以下の画像の通り。
クライアントからGameLiftにリクエストを送るフローに関しては、直接ClientSDKでGameLiftServiceに問い合わせる方法もあるが、今回の画像はAPI GatewayやLambdaを介しHttpリクエストで送る方法になっている。
このフローの方が、クライアントに直接リクエストの実装をするよりも安全で管理がしやすくなる。専用用語
画像の構成を理解する上で最低限必要な単語をまとめてみた。
・EC2
Amazon Elastic Compute Cloudの略
AWS上にスケーリング可能な仮想サーバーを構築出来るサービスのこと・フリート
EC2インスタンス群形式のホスティングリソース
必要に応じて、インスタンス数を調節してくれる
・エイリアス
クライアントとフリートの中継地点
エイリアスに紐づけているフリートを変更することで、クライアントを変更せずに接続先のフリートを変更することが出来る
https://docs.aws.amazon.com/ja_jp/gamelift/latest/developerguide/aliases-creating.html・キュー
新しいゲームセッションを作る場合に、複数のフリートから最適なものを選ぶ為の定義
リクエスト時にプレイヤーのpingを渡すと、最適なフリート(リージョン)を選んでくれる
ping情報が無い場合、キューに登録されているリストの順番で上から選ぶ
https://docs.aws.amazon.com/ja_jp/gamelift/latest/developerguide/queues-design.html・マッチメイキングコンフィグレーション
マッチングする上での様々な設定をする
https://docs.aws.amazon.com/ja_jp/gamelift/latest/developerguide/match-configuration.html最低限必要な設定は以下の3つ
・ルールセット
GameLiftのダッシュボード->マッチメーキングルールセットから見れる
最大人数など、マッチングに関するルールが色々設定してある・ゲームセッションキュー
紐付けるキュー
・リクエストのタイムアウト
タイムアウトの時間
それ以外にもいくつか設定出来る項目がある・プレイヤーの承諾(no-acceptance-required)
マッチングに参加する場合に、承諾を要求するかどうか
・プレイヤースロットの予約(additional-player-count)
・API Gateway
クライアントからhttpリクエストを受け取る為のエンドポイント
Lambda(ラムダ)と紐付いていて、リクエストを受け取るとラムダの関数を実行してくれる・Lambda(ラムダ)
GameLiftを操作する為の関数
APIGatewayなどをトリガーにして実行することが出来る
クライアントとGameLiftの繋ぎの役目で、クライアントに直接リクエストなどを実装するより安全で管理がしやすくなるまとめ
今回は全体構成のざっくりしたイメージと、最低限の単語にだけ触れてみた。
AWSは公式ドキュメントや利用者の記事が多く調べやすい反面、出来ることが膨大なので、全てを理解するのは大変そうだと感じた。
- 投稿日:2020-09-17T18:32:17+09:00
Sequel ProでSSH Over Session Managerを使った踏み台サーバーのDBに接続する
結論
Sequel ProではユーザーのPATHを参照してくれないようなので、
brewなどでaws cliをインストールした場合、ssh_configでPATHを明示的に追加する必要がありますssh config を準備
Host AWSEC2 HostName i-x0x0x0xx0x0x0 User ec2-user IdentityFile ~/.ssh/id_ed25519 ProxyCommand sh -c "PATH=$PATH:/usr/local/bin/ && aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"Sequel Proに設定
SSHホスト名の欄にssh configのHostに設定した名前を入力します
SSHユーザ、SSH鍵などはssh configで設定してあるので、空欄のままで大丈夫です
MySQLへの接続情報はshellでsshしたあとに叩くmysqlコマンドを確認して入力します
mysql -h <MySQLホスト> -u<ユーザ名> -p<パスワード> <データベース>
- 投稿日:2020-09-17T18:09:48+09:00
AWS Fargate+AWS FireLensを試す
What's?
少し前に、AWS FireLensについて調べてみたのですが、今度は使ってみようかな、ということで。
お題
こんなお題で試してみます。
- AWS Fargateクラスタを構築する
- タスクには、nginxをリバースプロキシとして、Pythonで作ったアプリケーションを背後に配置
- ログドライバーとして、
awsfirelens
を使用する- サイドカーとして、AWS for Fluent Bitを配置して、nginxとアプリケーションのコンテナログを収集
- 収集したコンテナログおよび、AWS for Fluent Bit自身のログは、Amazon CloudWatch Logsに送る
- 環境は、Terraformで構築する
環境
今回の環境は、こちらです。
$ terraform version Terraform v0.13.3 + provider registry.terraform.io/hashicorp/aws v3.6.0
AWSのクレデンシャルは、環境変数で設定するものとします。
export AWS_ACCESS_KEY_ID=... export AWS_SECRET_ACCESS_KEY=... export AWS_DEFAULT_REGION=ap-northeast-1Amazon ECRを作成する
最初に、自前で作成するコンテナイメージを格納するための、Amazon ECRリポジトリを作成しましょう。
nginx用と、アプリケーション用の2つを用意します。
main.tf
terraform { required_version = "0.13.3" required_providers { aws = "3.6.0" } } provider "aws" { } resource "aws_ecr_repository" "nginx" { name = "nginx" } resource "aws_ecr_repository" "app" { name = "app" }作成。
$ terraform apply
Dockerイメージを作成して、Amazon ECRにPushする
続いて、Dockerイメージを作成します。
最初は、nginx。
リバースプロキシとして構築するので、そのように設定ファイルを構成。
default.conf
server { listen 80; listen [::]:80; server_name localhost; location / { proxy_pass http://localhost:8000; } }これを含めるように
Dockerfile
を定義して
Dockerfile
FROM nginx:1.19.2 COPY default.conf /etc/nginx/conf.d/default.confビルド。
$ docker image build -t charon/nginx:latest .続いて、アプリケーション側。Flask-RESTfulを使って作成することにしました。
ソースコードは、こちら。
index.py
from datetime import datetime import logging from flask import Flask from flask_restful import Resource, Api app = Flask(__name__) api = Api(app) app.logger.setLevel(logging.INFO) class HelloWorld(Resource): def get(self): app.logger.info('access => Hello World') return {'message': 'Hello Flask!!', 'time': f'{datetime.now()}'} api.add_resource(HelloWorld, '/') if __name__ == '__main__': app.run(debug = True)
Dockerfile
を用意します。WSGIサーバーとしては、Gunicornを使うことにしました。
Dockerfile
FROM python:3.8.5-slim-buster COPY index.py index.py RUN pip install flask-restful==0.3.8 gunicorn==20.0.4 EXPOSE 8000 ENTRYPOINT ["gunicorn", "index:app"] CMD ["--threads", "10"]ビルド。
$ docker image build -t charon/app:latest .あとは、Amazon ECRにログインして
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com作成したイメージに、Amazon ECR用のタグをつけてPushします。
$ docker image tag charon/nginx:latest [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest $ docker image push [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest $ docker image tag charon/app:latest [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest $ docker image push [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latestAWS Fargateクラスタを構築する
それでは、AWS Fargateクラスタを構築しましょう。
定義はTerraformで書いていきます。VPCやALB、セキュリティグループ等も作成しますが、そちらのコードはまとめて最後に載せます。
main.tf
locals { vpc_id = module.vpc.vpc_id private_subnets = module.vpc.private_subnets app_with_nginx_service_security_groups = [ module.app_with_nginx_service_sg.this_security_group_id] load_balancer_target_group_arn = module.load_balancer.target_group_arns[0] }IAMロールの作成。次の2つが必要です。
Amazon ECSに対するAssume Role。
data "aws_iam_policy_document" "assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ecs-tasks.amazonaws.com"] } } }タスク実行用のIAMロール。
resource "aws_iam_role" "ecs_task_execution_role" { name = "MyEcsTaskExecutionRole" assume_role_policy = data.aws_iam_policy_document.assume_role.json } resource "aws_iam_role_policy_attachment" "amazon_ecs_task_execution_role_policy" { role = aws_iam_role.ecs_task_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" }タスク用のIAMロール。
data "aws_iam_policy_document" "ecs_task_role_policy_document" { statement { effect = "Allow" actions = [ "logs:DescribeLogStreams", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] resources = ["*"] } } resource "aws_iam_policy" "ecs_task_role_policy" { name = "MyEcsTaskPolicy" policy = data.aws_iam_policy_document.ecs_task_role_policy_document.json } resource "aws_iam_role" "ecs_task_role" { name = "MyEcsTaskRole" assume_role_policy = data.aws_iam_policy_document.assume_role.json } resource "aws_iam_role_policy_attachment" "ecs_task_role_policy_attachment" { role = aws_iam_role.ecs_task_role.name policy_arn = aws_iam_policy.ecs_task_role_policy.arn }タスクに割り当てる権限として、今回は以下が必要になります。サイドカーとして配置する、AWS for Fluent BitコンテナがAmazon CloudWatch Logsにログを送信するに権限が必要だからです。
Fluent Bit Plugin for CloudWatch Logs / Permissions
data "aws_iam_policy_document" "ecs_task_role_policy_document" { statement { effect = "Allow" actions = [ "logs:DescribeLogStreams", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] resources = ["*"] } }これが設定できていないと、Fluent BitコンテナがAmazon CloudWatch Logsにログが送れず、こんな感じのエラーになります。
2020-09-17T07:47:10.566000+00:00 firelens/log_router/a02c7e22-4270-4587-8a7f-c98339246a5c time="2020-09-17T07:47:10Z" level=error msg="NoCredentialProviders: no valid providers in chain\ncaused by: EnvAccessKeyNotFound: failed to find credentials in the environment.\nSharedCredsLoad: failed to load profile, .\nEC2RoleRequestError: no EC2 instance role found\ncaused by: RequestError: send request failed\ncaused by: Get http://169.254.169.254/latest/meta-data/iam/security-credentials/: dial tcp 169.254.169.254:80: connect: invalid argument"どのような権限が必要になるかは、ログの送信設定によって変わるので、利用するプラグインやリソースのドキュメントを見て設定してください。
Amazon CloudWatch Logsのロググループの作成。今回は、AWS FireLensログドライバーによって収集して送信するログも、AWS for Fluent Bit自身のログも、すべてAmazon CloudWatch Logsに送ります。
前もって作成しておきましょう。
resource "aws_cloudwatch_log_group" "nginx_container_log_group" { name = "/fargate/app/nginx" } resource "aws_cloudwatch_log_group" "app_container_log_group" { name = "/fargate/app/app" } resource "aws_cloudwatch_log_group" "firelens_container_log_group" { name = "/fargate/app/firelens" }AWS Fargetクラスタ定義。
resource "aws_ecs_cluster" "app_with_nginx" { name = "app-with-nginx-cluster" } resource "aws_ecs_task_definition" "app_with_nginx" { family = "app-with-nginx-task-definition" cpu = "1024" memory = "2048" network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] execution_role_arn = aws_iam_role.ecs_task_execution_role.arn task_role_arn = aws_iam_role.ecs_task_role.arn container_definitions = <<JSON [ { "name": "nginx", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest", "essential": true, "portMappings": [ { "protocol": "tcp", "containerPort": 80 } ], "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } } }, { "name": "app", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest", "essential": true, "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/app", "log_stream_prefix": "app-", "auto_create_group": "false" } } }, { "name": "log_router", "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest", "essential": true, "cpu": 256, "memory": 512, "firelensConfiguration": { "type": "fluentbit" }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } } } ] JSON } resource "aws_ecs_service" "app_with_nginx" { name = "app-with-nginx-service" cluster = aws_ecs_cluster.app_with_nginx.arn task_definition = aws_ecs_task_definition.app_with_nginx.arn desired_count = 3 launch_type = "FARGATE" platform_version = "1.4.0" deployment_minimum_healthy_percent = 50 network_configuration { assign_public_ip = false security_groups = local.app_with_nginx_service_security_groups subnets = local.private_subnets } load_balancer { target_group_arn = local.load_balancer_target_group_arn container_name = "nginx" container_port = 80 } }ポイントは、タスク定義に先程作成したタスク実行用のIAMロール、タスク用のIAMロールを割り当てているところと
resource "aws_ecs_task_definition" "app_with_nginx" { family = "app-with-nginx-task-definition" cpu = "1024" memory = "2048" network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] execution_role_arn = aws_iam_role.ecs_task_execution_role.arn task_role_arn = aws_iam_role.ecs_task_role.arnコンテナ定義ですね。
container_definitions = <<JSON [ { "name": "nginx", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest", "essential": true, "portMappings": [ { "protocol": "tcp", "containerPort": 80 } ], "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } } }, { "name": "app", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest", "essential": true, "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/app", "log_stream_prefix": "app-", "auto_create_group": "false" } } }, { "name": "log_router", "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest", "essential": true, "cpu": 256, "memory": 512, "firelensConfiguration": { "type": "fluentbit" }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } } } ] JSONAWS for Fluent Bitのコンテナは、サイドカーとして仕込みます。
{ "name": "log_router", "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest", "essential": true, "cpu": 256, "memory": 512, "firelensConfiguration": { "type": "fluentbit" }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } } }AWS内で使うので、ドキュメントにしたがって、AWS for Fluent BitのイメージはAmazon ECRから取得しています。
type
はFluent Bitであることを示し、"firelensConfiguration": { "type": "fluentbit" },AWS for Fluent Bitコンテナ自身のログは、Amazon CloudWatch Logsにawslogsログドライバーを使って構成します。
"logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } }他の2つのコンテナは、
logDriver
をawsfirelens
に設定します。{ "name": "nginx", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest", "essential": true, "portMappings": [ { "protocol": "tcp", "containerPort": 80 } ], "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } } }, { "name": "app", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest", "essential": true, "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/app", "log_stream_prefix": "app-", "auto_create_group": "false" } } },
options
で指定した内容は、Fluent Bit(もしくはFluentd)の設定ファイルに変換されます。"logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } }今回は
cloudwatch
を指定しているので、Fluent Bit Plugin for CloudWatch Logs向けの設定が出力されることになります。イメージ的には、こんな感じでしょうか?
[OUTPUT] Name cloudwatch Match nginx-firelens* region ap-northeast-1 log_group_name /fargate/app/nginx log_stream_prefix nginx- auto_create_group false今度、ちゃんと確認してみましょう…。
ちなみに、Amazon CloudWatch Logsにログを転送する例は、ドキュメントにサンプルがあったりします。
では、AWS Fargeteを構築します。
$ terraform apply
確認
構築が終わったら、動作確認してみます。
まずは、AWS Fargate自体の動作確認。
$ curl -i [ALBのDNS名] HTTP/1.1 200 OK Date: Thu, 17 Sep 2020 08:57:01 GMT Content-Type: application/json Content-Length: 67 Connection: keep-alive Server: nginx/1.19.2 {"message": "Hello Flask!!", "time": "2020-09-17 08:57:01.008154"}OKです。
nginxのログを見てみます。
$ aws logs tail --follow /fargate/app/nginx 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"10-listen-on-ipv6-by-default.sh: error: IPv6 listen already enabled","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: Configuration complete; ready for start up","source":"stdout"} 〜省略〜ログストリーム名は、
prefix
+タグ+コンテナIDみたいですね。Fluent Bit Plugin for CloudWatch Logs / Plugin Options
nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3アプリケーションのログ。
$ aws logs tail --follow /fargate/app/app 2020-09-17T08:56:19+00:00 app-app-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:19 +0000] [1] [INFO] Starting gunicorn 20.0.4","source":"stderr"} 2020-09-17T08:56:19+00:00 app-app-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:19 +0000] [1] [INFO] Listening at: http://127.0.0.1:8000 (1)","source":"stderr"} 2020-09-17T08:56:19+00:00 app-app-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:19 +0000] [1] [INFO] Using worker: threads","source":"stderr"} 2020-09-17T08:56:19+00:00 app-app-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:19 +0000] [9] [INFO] Booting worker with pid: 9","source":"stderr"} 2020-09-17T08:56:21+00:00 app-app-firelens-12fe7680-7b28-4e4f-a6c8-9d946bb0d49e {"container_id":"12fe7680-7b28-4e4f-a6c8-9d946bb0d49e-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/12fe7680-7b28-4e4f-a6c8-9d946bb0d49e","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:21 +0000] [1] [INFO] Starting gunicorn 20.0.4","source":"stderr"} 〜省略〜AWS for Fluent Bitのログ。こちらは、
awslogs
ロギングドライバーの利用になります。$ aws logs tail --follow /fargate/app/firelens起動時のログ。
2020-09-17T08:56:19.100000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 AWS for Fluent Bit Container Image Version 2.7.0 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 Fluent Bit v1.5.6 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 * Copyright (C) 2019-2020 The Fluent Bit Authors 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 * Copyright (C) 2015-2018 Treasure Data 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 * Fluent Bit is a CNCF sub-project under the umbrella of Fluentd 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 * https://fluentbit.io 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_group = '/fargate/app/nginx'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_stream_prefix = 'nginx-'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_stream_name = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter region = 'ap-northeast-1'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_key = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter role_arn = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter new_log_group_tags = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_retention_days = '0'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter endpoint = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter sts_endpoint = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter credentials_endpoint = " 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_format = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_group = '/fargate/app/app'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_stream_prefix = 'app-'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_stream_name = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter region = 'ap-northeast-1'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_key = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter role_arn = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter new_log_group_tags = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_retention_days = '0'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter endpoint = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter sts_endpoint = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter credentials_endpoint = " 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_format = ''"確かに、各コンテナの
logConfiguration
の内容がプラグインの設定として渡されているようですね。msg="[cloudwatch 0] plugin parameter log_group = '/fargate/app/nginx'" msg="[cloudwatch 0] plugin parameter log_stream_prefix = 'nginx-'" msg="[cloudwatch 0] plugin parameter log_stream_name = ''" msg="[cloudwatch 0] plugin parameter region = 'ap-northeast-1'" msg="[cloudwatch 0] plugin parameter log_key = ''" msg="[cloudwatch 0] plugin parameter role_arn = ''" msg="[cloudwatch 0] plugin parameter new_log_group_tags = ''" msg="[cloudwatch 0] plugin parameter log_retention_days = '0'" msg="[cloudwatch 0] plugin parameter endpoint = ''" msg="[cloudwatch 0] plugin parameter sts_endpoint = ''" msg="[cloudwatch 0] plugin parameter credentials_endpoint = " msg="[cloudwatch 0] plugin parameter log_format = ''" msg="[cloudwatch 1] plugin parameter log_group = '/fargate/app/app'" msg="[cloudwatch 1] plugin parameter log_stream_prefix = 'app-'" msg="[cloudwatch 1] plugin parameter log_stream_name = ''" msg="[cloudwatch 1] plugin parameter region = 'ap-northeast-1'" msg="[cloudwatch 1] plugin parameter log_key = ''" msg="[cloudwatch 1] plugin parameter role_arn = ''" msg="[cloudwatch 1] plugin parameter new_log_group_tags = ''" msg="[cloudwatch 1] plugin parameter log_retention_days = '0'" msg="[cloudwatch 1] plugin parameter endpoint = ''" msg="[cloudwatch 1] plugin parameter sts_endpoint = ''" msg="[cloudwatch 1] plugin parameter credentials_endpoint = " msg="[cloudwatch 1] plugin parameter log_format = ''"動作確認はできたので、OKとしましょう!
VPC〜ALBまで(〜AWS Fargateも)
最後に、省略していたVPCからALBまでの定義を含めた、全体のTerraform定義を載せておきます。
main.tf
terraform { required_version = "0.13.3" required_providers { aws = "3.6.0" } } provider "aws" { } module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "2.51.0" name = "my-vpc" cidr = "10.0.0.0/16" enable_dns_hostnames = true enable_dns_support = true azs = ["ap-northeast-1a", "ap-northeast-1c"] public_subnets = ["10.0.10.0/24", "10.0.20.0/24"] private_subnets = ["10.0.30.0/24", "10.0.40.0/24"] map_public_ip_on_launch = true enable_nat_gateway = true single_nat_gateway = false one_nat_gateway_per_az = true } module "load_balancer_sg" { source = "terraform-aws-modules/security-group/aws//modules/http-80" version = "3.16.0" name = "load-balancer-sg" vpc_id = module.vpc.vpc_id ingress_cidr_blocks = ["0.0.0.0/0"] } module "app_with_nginx_service_sg" { source = "terraform-aws-modules/security-group/aws" version = "3.16.0" name = "app-with-nginx-service-sg" vpc_id = module.vpc.vpc_id ingress_with_cidr_blocks = [ { from_port = 80 to_port = 80 protocol = "tcp" description = "app-with-nginx-service inbound ports" cidr_blocks = "10.0.10.0/24" }, { from_port = 80 to_port = 80 protocol = "tcp" description = "app-with-nginx-service inbound ports" cidr_blocks = "10.0.20.0/24" } ] egress_with_cidr_blocks = [ { from_port = 0 to_port = 0 protocol = "-1" description = "app-with-nginx-service outbound ports" cidr_blocks = "0.0.0.0/0" } ] } module "load_balancer" { source = "terraform-aws-modules/alb/aws" version = "5.9.0" name = "app-with-nginx" vpc_id = module.vpc.vpc_id load_balancer_type = "application" internal = false subnets = module.vpc.public_subnets security_groups = [module.load_balancer_sg.this_security_group_id] target_groups = [ { backend_protocol = "HTTP" backend_port = 80 target_type = "ip" health_check = { interval = 20 } } ] http_tcp_listeners = [ { port = 80 protocol = "HTTP" } ] } locals { vpc_id = module.vpc.vpc_id private_subnets = module.vpc.private_subnets app_with_nginx_service_security_groups = [ module.app_with_nginx_service_sg.this_security_group_id] load_balancer_target_group_arn = module.load_balancer.target_group_arns[0] } data "aws_iam_policy_document" "assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ecs-tasks.amazonaws.com"] } } } resource "aws_iam_role" "ecs_task_execution_role" { name = "MyEcsTaskExecutionRole" assume_role_policy = data.aws_iam_policy_document.assume_role.json } resource "aws_iam_role_policy_attachment" "amazon_ecs_task_execution_role_policy" { role = aws_iam_role.ecs_task_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } data "aws_iam_policy_document" "ecs_task_role_policy_document" { statement { effect = "Allow" actions = [ "logs:DescribeLogStreams", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] resources = ["*"] } } resource "aws_iam_policy" "ecs_task_role_policy" { name = "MyEcsTaskPolicy" policy = data.aws_iam_policy_document.ecs_task_role_policy_document.json } resource "aws_iam_role" "ecs_task_role" { name = "MyEcsTaskRole" assume_role_policy = data.aws_iam_policy_document.assume_role.json } resource "aws_iam_role_policy_attachment" "ecs_task_role_policy_attachment" { role = aws_iam_role.ecs_task_role.name policy_arn = aws_iam_policy.ecs_task_role_policy.arn } resource "aws_cloudwatch_log_group" "nginx_container_log_group" { name = "/fargate/app/nginx" } resource "aws_cloudwatch_log_group" "app_container_log_group" { name = "/fargate/app/app" } resource "aws_cloudwatch_log_group" "firelens_container_log_group" { name = "/fargate/app/firelens" } resource "aws_ecs_cluster" "app_with_nginx" { name = "app-with-nginx-cluster" } resource "aws_ecs_task_definition" "app_with_nginx" { family = "app-with-nginx-task-definition" cpu = "1024" memory = "2048" network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] execution_role_arn = aws_iam_role.ecs_task_execution_role.arn task_role_arn = aws_iam_role.ecs_task_role.arn container_definitions = <<JSON [ { "name": "nginx", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest", "essential": true, "portMappings": [ { "protocol": "tcp", "containerPort": 80 } ], "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } } }, { "name": "app", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest", "essential": true, "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/app", "log_stream_prefix": "app-", "auto_create_group": "false" } } }, { "name": "log_router", "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest", "essential": true, "cpu": 256, "memory": 512, "firelensConfiguration": { "type": "fluentbit" }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } } } ] JSON } resource "aws_ecs_service" "app_with_nginx" { name = "app-with-nginx-service" cluster = aws_ecs_cluster.app_with_nginx.arn task_definition = aws_ecs_task_definition.app_with_nginx.arn desired_count = 3 launch_type = "FARGATE" platform_version = "1.4.0" deployment_minimum_healthy_percent = 50 network_configuration { assign_public_ip = false security_groups = local.app_with_nginx_service_security_groups subnets = local.private_subnets } load_balancer { target_group_arn = local.load_balancer_target_group_arn container_name = "nginx" container_port = 80 } }
- 投稿日:2020-09-17T18:09:48+09:00
AWS Fargate+AWS FireLens(Fluent Bit Plugin for CloudWatch Logs)を試す
What's?
少し前に、AWS FireLensについて調べてみたのですが、今度は使ってみようかな、ということで。
お題
こんなお題で試してみます。
- AWS Fargateクラスタを構築する
- タスクには、nginxをリバースプロキシとして、Pythonで作ったアプリケーションを背後に配置
- ログドライバーとして、
awsfirelens
を使用する- サイドカーとして、AWS for Fluent Bitを配置して、nginxとアプリケーションのコンテナログを受け取る
- 受け取った他のコンテナログおよび、AWS for Fluent Bit自身のログは、Amazon CloudWatch Logsに送る
- 環境は、Terraformで構築する
環境
今回の環境は、こちらです。
$ terraform version Terraform v0.13.3 + provider registry.terraform.io/hashicorp/aws v3.6.0
AWSのクレデンシャルは、環境変数で設定するものとします。
export AWS_ACCESS_KEY_ID=... export AWS_SECRET_ACCESS_KEY=... export AWS_DEFAULT_REGION=ap-northeast-1Amazon ECRを作成する
最初に、自前で作成するコンテナイメージを格納するための、Amazon ECRリポジトリを作成しましょう。
nginx用と、アプリケーション用の2つを用意します。
main.tf
terraform { required_version = "0.13.3" required_providers { aws = "3.6.0" } } provider "aws" { } resource "aws_ecr_repository" "nginx" { name = "nginx" } resource "aws_ecr_repository" "app" { name = "app" }作成。
$ terraform apply
Dockerイメージを作成して、Amazon ECRにPushする
続いて、Dockerイメージを作成します。
最初は、nginx。
リバースプロキシとして構築するので、そのように設定ファイルを構成。
default.conf
server { listen 80; listen [::]:80; server_name localhost; location / { proxy_pass http://localhost:8000; } }これを含めるように
Dockerfile
を定義して
Dockerfile
FROM nginx:1.19.2 COPY default.conf /etc/nginx/conf.d/default.confビルド。
$ docker image build -t charon/nginx:latest .続いて、アプリケーション側。Flask-RESTfulを使って作成することにしました。
ソースコードは、こちら。
index.py
from datetime import datetime import logging from flask import Flask from flask_restful import Resource, Api app = Flask(__name__) api = Api(app) app.logger.setLevel(logging.INFO) class HelloWorld(Resource): def get(self): app.logger.info('access => Hello World') return {'message': 'Hello Flask!!', 'time': f'{datetime.now()}'} api.add_resource(HelloWorld, '/') if __name__ == '__main__': app.run(debug = True)
Dockerfile
を用意します。WSGIサーバーとしては、Gunicornを使うことにしました。
Dockerfile
FROM python:3.8.5-slim-buster COPY index.py index.py RUN pip install flask-restful==0.3.8 gunicorn==20.0.4 EXPOSE 8000 ENTRYPOINT ["gunicorn", "index:app"] CMD ["--threads", "10"]ビルド。
$ docker image build -t charon/app:latest .あとは、Amazon ECRにログインして
$ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com作成したイメージに、Amazon ECR用のタグをつけてPushします。
$ docker image tag charon/nginx:latest [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest $ docker image push [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest $ docker image tag charon/app:latest [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest $ docker image push [AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latestAWS Fargateクラスタを構築する
それでは、AWS Fargateクラスタを構築しましょう。
定義はTerraformで書いていきます。VPCやALB、セキュリティグループ等も作成しますが、そちらのコードはまとめて最後に載せます。
main.tf
locals { vpc_id = module.vpc.vpc_id private_subnets = module.vpc.private_subnets app_with_nginx_service_security_groups = [ module.app_with_nginx_service_sg.this_security_group_id] load_balancer_target_group_arn = module.load_balancer.target_group_arns[0] }IAMロールの作成。次の2つが必要です。
Amazon ECSに対するAssume Role。
data "aws_iam_policy_document" "assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ecs-tasks.amazonaws.com"] } } }タスク実行用のIAMロール。
resource "aws_iam_role" "ecs_task_execution_role" { name = "MyEcsTaskExecutionRole" assume_role_policy = data.aws_iam_policy_document.assume_role.json } resource "aws_iam_role_policy_attachment" "amazon_ecs_task_execution_role_policy" { role = aws_iam_role.ecs_task_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" }タスク用のIAMロール。
data "aws_iam_policy_document" "ecs_task_role_policy_document" { statement { effect = "Allow" actions = [ "logs:DescribeLogStreams", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] resources = ["*"] } } resource "aws_iam_policy" "ecs_task_role_policy" { name = "MyEcsTaskPolicy" policy = data.aws_iam_policy_document.ecs_task_role_policy_document.json } resource "aws_iam_role" "ecs_task_role" { name = "MyEcsTaskRole" assume_role_policy = data.aws_iam_policy_document.assume_role.json } resource "aws_iam_role_policy_attachment" "ecs_task_role_policy_attachment" { role = aws_iam_role.ecs_task_role.name policy_arn = aws_iam_policy.ecs_task_role_policy.arn }タスクに割り当てる権限として、今回は以下が必要になります。サイドカーとして配置する、AWS for Fluent BitコンテナがAmazon CloudWatch Logsにログを送信するに権限が必要だからです。
Fluent Bit Plugin for CloudWatch Logs / Permissions
data "aws_iam_policy_document" "ecs_task_role_policy_document" { statement { effect = "Allow" actions = [ "logs:DescribeLogStreams", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] resources = ["*"] } }これが設定できていないと、Fluent BitコンテナがAmazon CloudWatch Logsにログが送れず、こんな感じのエラーになります。
2020-09-17T07:47:10.566000+00:00 firelens/log_router/a02c7e22-4270-4587-8a7f-c98339246a5c time="2020-09-17T07:47:10Z" level=error msg="NoCredentialProviders: no valid providers in chain\ncaused by: EnvAccessKeyNotFound: failed to find credentials in the environment.\nSharedCredsLoad: failed to load profile, .\nEC2RoleRequestError: no EC2 instance role found\ncaused by: RequestError: send request failed\ncaused by: Get http://169.254.169.254/latest/meta-data/iam/security-credentials/: dial tcp 169.254.169.254:80: connect: invalid argument"どのような権限が必要になるかは、ログの送信設定によって変わるので、利用するプラグインやリソースのドキュメントを見て設定してください。
Amazon CloudWatch Logsのロググループの作成。今回は、AWS FireLensログドライバーによって収集して送信するログも、AWS for Fluent Bit自身のログも、すべてAmazon CloudWatch Logsに送ります。
前もって作成しておきましょう。
resource "aws_cloudwatch_log_group" "nginx_container_log_group" { name = "/fargate/app/nginx" } resource "aws_cloudwatch_log_group" "app_container_log_group" { name = "/fargate/app/app" } resource "aws_cloudwatch_log_group" "firelens_container_log_group" { name = "/fargate/app/firelens" }AWS Fargetクラスタ定義。
resource "aws_ecs_cluster" "app_with_nginx" { name = "app-with-nginx-cluster" } resource "aws_ecs_task_definition" "app_with_nginx" { family = "app-with-nginx-task-definition" cpu = "1024" memory = "2048" network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] execution_role_arn = aws_iam_role.ecs_task_execution_role.arn task_role_arn = aws_iam_role.ecs_task_role.arn container_definitions = <<JSON [ { "name": "nginx", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest", "essential": true, "portMappings": [ { "protocol": "tcp", "containerPort": 80 } ], "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } } }, { "name": "app", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest", "essential": true, "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/app", "log_stream_prefix": "app-", "auto_create_group": "false" } } }, { "name": "log_router", "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest", "essential": true, "cpu": 256, "memory": 512, "firelensConfiguration": { "type": "fluentbit" }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } } } ] JSON } resource "aws_ecs_service" "app_with_nginx" { name = "app-with-nginx-service" cluster = aws_ecs_cluster.app_with_nginx.arn task_definition = aws_ecs_task_definition.app_with_nginx.arn desired_count = 3 launch_type = "FARGATE" platform_version = "1.4.0" deployment_minimum_healthy_percent = 50 network_configuration { assign_public_ip = false security_groups = local.app_with_nginx_service_security_groups subnets = local.private_subnets } load_balancer { target_group_arn = local.load_balancer_target_group_arn container_name = "nginx" container_port = 80 } }ポイントは、タスク定義に先程作成したタスク実行用のIAMロール、タスク用のIAMロールを割り当てているところと
resource "aws_ecs_task_definition" "app_with_nginx" { family = "app-with-nginx-task-definition" cpu = "1024" memory = "2048" network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] execution_role_arn = aws_iam_role.ecs_task_execution_role.arn task_role_arn = aws_iam_role.ecs_task_role.arnコンテナ定義ですね。
container_definitions = <<JSON [ { "name": "nginx", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest", "essential": true, "portMappings": [ { "protocol": "tcp", "containerPort": 80 } ], "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } } }, { "name": "app", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest", "essential": true, "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/app", "log_stream_prefix": "app-", "auto_create_group": "false" } } }, { "name": "log_router", "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest", "essential": true, "cpu": 256, "memory": 512, "firelensConfiguration": { "type": "fluentbit" }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } } } ] JSONAWS for Fluent Bitのコンテナは、サイドカーとして仕込みます。
{ "name": "log_router", "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest", "essential": true, "cpu": 256, "memory": 512, "firelensConfiguration": { "type": "fluentbit" }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } } }AWS内で使うので、ドキュメントにしたがって、AWS for Fluent BitのイメージはAmazon ECRから取得しています。
type
はFluent Bitであることを示し、"firelensConfiguration": { "type": "fluentbit" },AWS for Fluent Bitコンテナ自身のログは、Amazon CloudWatch Logsにawslogsログドライバーを使って構成します。
"logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } }他の2つのコンテナは、
logDriver
をawsfirelens
に設定します。{ "name": "nginx", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest", "essential": true, "portMappings": [ { "protocol": "tcp", "containerPort": 80 } ], "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } } }, { "name": "app", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest", "essential": true, "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/app", "log_stream_prefix": "app-", "auto_create_group": "false" } } },
options
で指定した内容は、Fluent Bit(もしくはFluentd)の設定ファイルに変換されます。"logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } }今回は
cloudwatch
を指定しているので、Fluent Bit Plugin for CloudWatch Logs向けの設定が出力されることになります。イメージ的には、こんな感じでしょうか?
[OUTPUT] Name cloudwatch Match nginx-firelens* region ap-northeast-1 log_group_name /fargate/app/nginx log_stream_prefix nginx- auto_create_group false今度、ちゃんと確認してみましょう…。
ちなみに、Amazon CloudWatch Logsにログを転送する例は、ドキュメントにサンプルがあったりします。
では、AWS Fargeteを構築します。
$ terraform apply
確認
構築が終わったら、動作確認してみます。
まずは、AWS Fargate自体の動作確認。
$ curl -i [ALBのDNS名] HTTP/1.1 200 OK Date: Thu, 17 Sep 2020 08:57:01 GMT Content-Type: application/json Content-Length: 67 Connection: keep-alive Server: nginx/1.19.2 {"message": "Hello Flask!!", "time": "2020-09-17 08:57:01.008154"}OKです。
nginxのログを見てみます。
$ aws logs tail --follow /fargate/app/nginx 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"10-listen-on-ipv6-by-default.sh: error: IPv6 listen already enabled","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh","source":"stdout"} 2020-09-17T08:56:19+00:00 nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-2531612879","container_name":"nginx","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"/docker-entrypoint.sh: Configuration complete; ready for start up","source":"stdout"} 〜省略〜ログストリーム名は、
prefix
+タグ+コンテナIDみたいですね。Fluent Bit Plugin for CloudWatch Logs / Plugin Options
nginx-nginx-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3アプリケーションのログ。
$ aws logs tail --follow /fargate/app/app 2020-09-17T08:56:19+00:00 app-app-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:19 +0000] [1] [INFO] Starting gunicorn 20.0.4","source":"stderr"} 2020-09-17T08:56:19+00:00 app-app-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:19 +0000] [1] [INFO] Listening at: http://127.0.0.1:8000 (1)","source":"stderr"} 2020-09-17T08:56:19+00:00 app-app-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:19 +0000] [1] [INFO] Using worker: threads","source":"stderr"} 2020-09-17T08:56:19+00:00 app-app-firelens-f5c7c43b-71d9-4e99-8609-28f7d4c116e3 {"container_id":"f5c7c43b-71d9-4e99-8609-28f7d4c116e3-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/f5c7c43b-71d9-4e99-8609-28f7d4c116e3","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:19 +0000] [9] [INFO] Booting worker with pid: 9","source":"stderr"} 2020-09-17T08:56:21+00:00 app-app-firelens-12fe7680-7b28-4e4f-a6c8-9d946bb0d49e {"container_id":"12fe7680-7b28-4e4f-a6c8-9d946bb0d49e-527074092","container_name":"app","ecs_cluster":"app-with-nginx-cluster","ecs_task_arn":"arn:aws:ecs:ap-northeast-1:[AWSアカウントID]:task/12fe7680-7b28-4e4f-a6c8-9d946bb0d49e","ecs_task_definition":"app-with-nginx-task-definition:5","log":"[2020-09-17 08:56:21 +0000] [1] [INFO] Starting gunicorn 20.0.4","source":"stderr"} 〜省略〜AWS for Fluent Bitのログ。こちらは、
awslogs
ロギングドライバーの利用になります。$ aws logs tail --follow /fargate/app/firelens起動時のログ。
2020-09-17T08:56:19.100000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 AWS for Fluent Bit Container Image Version 2.7.0 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 Fluent Bit v1.5.6 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 * Copyright (C) 2019-2020 The Fluent Bit Authors 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 * Copyright (C) 2015-2018 Treasure Data 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 * Fluent Bit is a CNCF sub-project under the umbrella of Fluentd 2020-09-17T08:56:19.148000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 * https://fluentbit.io 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_group = '/fargate/app/nginx'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_stream_prefix = 'nginx-'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_stream_name = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter region = 'ap-northeast-1'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_key = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter role_arn = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter new_log_group_tags = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_retention_days = '0'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter endpoint = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter sts_endpoint = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter credentials_endpoint = " 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 0] plugin parameter log_format = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_group = '/fargate/app/app'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_stream_prefix = 'app-'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_stream_name = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter region = 'ap-northeast-1'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_key = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter role_arn = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter new_log_group_tags = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_retention_days = '0'" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter endpoint = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter sts_endpoint = ''" 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter credentials_endpoint = " 2020-09-17T08:56:19.149000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 time="2020-09-17T08:56:19Z" level=info msg="[cloudwatch 1] plugin parameter log_format = ''" 2020-09-17T08:56:19.150000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 [2020/09/17 08:56:19] [ info] [engine] started (pid=1) 2020-09-17T08:56:19.150000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 [2020/09/17 08:56:19] [ info] [storage] version=1.0.5, initializing... 2020-09-17T08:56:19.150000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 [2020/09/17 08:56:19] [ info] [storage] in-memory 2020-09-17T08:56:19.150000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 [2020/09/17 08:56:19] [ info] [storage] normal synchronization mode, checksum disabled, max_chunks_up=128 2020-09-17T08:56:19.150000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 [2020/09/17 08:56:19] [ info] [input:tcp:tcp.0] listening on 127.0.0.1:8877 2020-09-17T08:56:19.150000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 [2020/09/17 08:56:19] [ info] [input:forward:forward.1] listening on unix:///var/run/fluent.sock 2020-09-17T08:56:19.150000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 [2020/09/17 08:56:19] [ info] [input:forward:forward.2] listening on 127.0.0.1:24224 2020-09-17T08:56:19.150000+00:00 firelens/log_router/f5c7c43b-71d9-4e99-8609-28f7d4c116e3 [2020/09/17 08:56:19] [ info] [sp] stream processor started確かに、各コンテナの
logConfiguration
の内容がプラグインの設定として渡されているようですね。msg="[cloudwatch 0] plugin parameter log_group = '/fargate/app/nginx'" msg="[cloudwatch 0] plugin parameter log_stream_prefix = 'nginx-'" msg="[cloudwatch 0] plugin parameter log_stream_name = ''" msg="[cloudwatch 0] plugin parameter region = 'ap-northeast-1'" msg="[cloudwatch 0] plugin parameter log_key = ''" msg="[cloudwatch 0] plugin parameter role_arn = ''" msg="[cloudwatch 0] plugin parameter new_log_group_tags = ''" msg="[cloudwatch 0] plugin parameter log_retention_days = '0'" msg="[cloudwatch 0] plugin parameter endpoint = ''" msg="[cloudwatch 0] plugin parameter sts_endpoint = ''" msg="[cloudwatch 0] plugin parameter credentials_endpoint = " msg="[cloudwatch 0] plugin parameter log_format = ''" msg="[cloudwatch 1] plugin parameter log_group = '/fargate/app/app'" msg="[cloudwatch 1] plugin parameter log_stream_prefix = 'app-'" msg="[cloudwatch 1] plugin parameter log_stream_name = ''" msg="[cloudwatch 1] plugin parameter region = 'ap-northeast-1'" msg="[cloudwatch 1] plugin parameter log_key = ''" msg="[cloudwatch 1] plugin parameter role_arn = ''" msg="[cloudwatch 1] plugin parameter new_log_group_tags = ''" msg="[cloudwatch 1] plugin parameter log_retention_days = '0'" msg="[cloudwatch 1] plugin parameter endpoint = ''" msg="[cloudwatch 1] plugin parameter sts_endpoint = ''" msg="[cloudwatch 1] plugin parameter credentials_endpoint = " msg="[cloudwatch 1] plugin parameter log_format = ''"動作確認はできたのと、ある程度動きはわかったので、今回はこれでOKとしましょう!
VPC〜ALBまで(〜AWS Fargateも)
最後に、省略していたVPCからALBまでの定義を含めた、全体のTerraform定義を載せておきます。
main.tf
terraform { required_version = "0.13.3" required_providers { aws = "3.6.0" } } provider "aws" { } module "vpc" { source = "terraform-aws-modules/vpc/aws" version = "2.51.0" name = "my-vpc" cidr = "10.0.0.0/16" enable_dns_hostnames = true enable_dns_support = true azs = ["ap-northeast-1a", "ap-northeast-1c"] public_subnets = ["10.0.10.0/24", "10.0.20.0/24"] private_subnets = ["10.0.30.0/24", "10.0.40.0/24"] map_public_ip_on_launch = true enable_nat_gateway = true single_nat_gateway = false one_nat_gateway_per_az = true } module "load_balancer_sg" { source = "terraform-aws-modules/security-group/aws//modules/http-80" version = "3.16.0" name = "load-balancer-sg" vpc_id = module.vpc.vpc_id ingress_cidr_blocks = ["0.0.0.0/0"] } module "app_with_nginx_service_sg" { source = "terraform-aws-modules/security-group/aws" version = "3.16.0" name = "app-with-nginx-service-sg" vpc_id = module.vpc.vpc_id ingress_with_cidr_blocks = [ { from_port = 80 to_port = 80 protocol = "tcp" description = "app-with-nginx-service inbound ports" cidr_blocks = "10.0.10.0/24" }, { from_port = 80 to_port = 80 protocol = "tcp" description = "app-with-nginx-service inbound ports" cidr_blocks = "10.0.20.0/24" } ] egress_with_cidr_blocks = [ { from_port = 0 to_port = 0 protocol = "-1" description = "app-with-nginx-service outbound ports" cidr_blocks = "0.0.0.0/0" } ] } module "load_balancer" { source = "terraform-aws-modules/alb/aws" version = "5.9.0" name = "app-with-nginx" vpc_id = module.vpc.vpc_id load_balancer_type = "application" internal = false subnets = module.vpc.public_subnets security_groups = [module.load_balancer_sg.this_security_group_id] target_groups = [ { backend_protocol = "HTTP" backend_port = 80 target_type = "ip" health_check = { interval = 20 } } ] http_tcp_listeners = [ { port = 80 protocol = "HTTP" } ] } locals { vpc_id = module.vpc.vpc_id private_subnets = module.vpc.private_subnets app_with_nginx_service_security_groups = [ module.app_with_nginx_service_sg.this_security_group_id] load_balancer_target_group_arn = module.load_balancer.target_group_arns[0] } data "aws_iam_policy_document" "assume_role" { statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ecs-tasks.amazonaws.com"] } } } resource "aws_iam_role" "ecs_task_execution_role" { name = "MyEcsTaskExecutionRole" assume_role_policy = data.aws_iam_policy_document.assume_role.json } resource "aws_iam_role_policy_attachment" "amazon_ecs_task_execution_role_policy" { role = aws_iam_role.ecs_task_execution_role.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } data "aws_iam_policy_document" "ecs_task_role_policy_document" { statement { effect = "Allow" actions = [ "logs:DescribeLogStreams", "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] resources = ["*"] } } resource "aws_iam_policy" "ecs_task_role_policy" { name = "MyEcsTaskPolicy" policy = data.aws_iam_policy_document.ecs_task_role_policy_document.json } resource "aws_iam_role" "ecs_task_role" { name = "MyEcsTaskRole" assume_role_policy = data.aws_iam_policy_document.assume_role.json } resource "aws_iam_role_policy_attachment" "ecs_task_role_policy_attachment" { role = aws_iam_role.ecs_task_role.name policy_arn = aws_iam_policy.ecs_task_role_policy.arn } resource "aws_cloudwatch_log_group" "nginx_container_log_group" { name = "/fargate/app/nginx" } resource "aws_cloudwatch_log_group" "app_container_log_group" { name = "/fargate/app/app" } resource "aws_cloudwatch_log_group" "firelens_container_log_group" { name = "/fargate/app/firelens" } resource "aws_ecs_cluster" "app_with_nginx" { name = "app-with-nginx-cluster" } resource "aws_ecs_task_definition" "app_with_nginx" { family = "app-with-nginx-task-definition" cpu = "1024" memory = "2048" network_mode = "awsvpc" requires_compatibilities = ["FARGATE"] execution_role_arn = aws_iam_role.ecs_task_execution_role.arn task_role_arn = aws_iam_role.ecs_task_role.arn container_definitions = <<JSON [ { "name": "nginx", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/nginx:latest", "essential": true, "portMappings": [ { "protocol": "tcp", "containerPort": 80 } ], "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/nginx", "log_stream_prefix": "nginx-", "auto_create_group": "false" } } }, { "name": "app", "image": "[AWSアカウントID].dkr.ecr.ap-northeast-1.amazonaws.com/app:latest", "essential": true, "cpu": 256, "memory": 512, "logConfiguration": { "logDriver": "awsfirelens", "options": { "Name": "cloudwatch", "region": "ap-northeast-1", "log_group_name": "/fargate/app/app", "log_stream_prefix": "app-", "auto_create_group": "false" } } }, { "name": "log_router", "image": "906394416424.dkr.ecr.ap-northeast-1.amazonaws.com/aws-for-fluent-bit:latest", "essential": true, "cpu": 256, "memory": 512, "firelensConfiguration": { "type": "fluentbit" }, "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/fargate/app/firelens", "awslogs-region": "ap-northeast-1", "awslogs-stream-prefix": "firelens" } } } ] JSON } resource "aws_ecs_service" "app_with_nginx" { name = "app-with-nginx-service" cluster = aws_ecs_cluster.app_with_nginx.arn task_definition = aws_ecs_task_definition.app_with_nginx.arn desired_count = 3 launch_type = "FARGATE" platform_version = "1.4.0" deployment_minimum_healthy_percent = 50 network_configuration { assign_public_ip = false security_groups = local.app_with_nginx_service_security_groups subnets = local.private_subnets } load_balancer { target_group_arn = local.load_balancer_target_group_arn container_name = "nginx" container_port = 80 } }
- 投稿日:2020-09-17T16:26:57+09:00
rake secretできない問題
secret_key_baseを取得したいのに、
rake secret
を実行するとエラーが出る…rake aborted! Errno::EACCES: Permission denied @ dir_s_mkdir - /var/www/filma/tmp/cache /var/www/filma/config/boot.rb:4:in `require' /var/www/filma/config/boot.rb:4:in `<top (required)>' /var/www/filma/config/application.rb:1:in `require_relative' /var/www/filma/config/application.rb:1:in `<top (required)>' /var/www/filma/Rakefile:4:in `require_relative' /var/www/filma/Rakefile:4:in `<top (required)>' /home/ec2-user/.rbenv/versions/2.6.6/bin/bundle:23:in `load' /home/ec2-user/.rbenv/versions/2.6.6/bin/bundle:23:in `<main>' (See full trace by running task with --trace)全然意味がわからないので調べてみると…
Permission denied = ”権限がない”
こちらの記事が参考になりました。
Permission denied
が出る=”権限がない状態”ということなんですね。(合ってる?)権限を確認
ec2-user@~ アプリ名 $ ls -laを実行すると…
drwxr-xr-x 15 root root 4096 9月 17 06:23 . drwxr-xr-x 3 ec2-user root 19 9月 17 06:23 .. -rw-r--r-- 1 root root 10244 9月 17 06:23 .DS_Store ・ ・ ・ -rw-r--r-- 1 root root 324953 9月 17 06:23 yarn.lock▲ほとんどrootユーザーにしか権限が与えられてない!
解決方法
ユーザー名の確認(一応)▼
ec2-user@~ アプリ名 $ whoami ec2-user権限を与える
ec2-user@~ アプリ名 $ sudo chown -R ec2-user .これで解決しました!
ec2-user@~ アプリ名 $ rake secret ********************************* ▲無事に取得完了
前やったときは出なかったエラーが出るのは勘弁してほしいですが…少しずつ検索力も身についてきたかもしれないのでよし。
- 投稿日:2020-09-17T12:02:56+09:00
[Rails6対応, 公式SDK] AWS SESを使ってRailsから送信元が独自ドメインのメールを送ってみた
AWS SESを使ってRailsから送信元が独自ドメインのメールを送ってみた - Rails 6, Credentials 仕様-
IAMユーザーを使用する方法で私の環境と同じエラーが散見され
EC2にroleをアタッチする方法などを試みていたが
公式のSDKを使用することで解決できたので書き留めます環境
Rails: 6.0.3
Ruby: 2.7.1事前準備
- SES、ドメインの設定
- SES関連の権限が付与されたIAMユーザー
- 上記ユーザーのaccess_key_id, secret_access_keyを取得
ここまでは下記ページを参考にしました
【AWS】Amazon SES / Messaging・Route53を用いてドメインメールを送信する - Qiita以下、参考ページからの変更点
- Rails 6のcredentialsを使用する
- SESのリージョンを
ap-northeast-1
に変更AWS公式のSDKを使用
特にここが重要で私の環境では一通り設定した後の送信テストで次のエラーが
AWS::SES::ResponseError (AWS::SES Response Error: InvalidClientTokenId - The security token included in the request is invalid.)以下のSDKが使用されているところを
gem 'aws-ses', '~> 0.6'version 0.6.0が2014年アップデートのため
gem 'aws-ses', '~> 0.7'
0.7.0 - September 03, 2020
を試したものの変化なしAWS公式のSDKがあることがわかったのでこちらを試したところ解決しました
Ruby on Rails で SDK を使用する - AWS SDK for Rubyaws-sdk-rails/README.md at master · aws/aws-sdk-rails
gem 'aws-sdk-rails', '~> 3'credentials.yml.encにAWSにアクセスするための情報を記述
EDITOR=vim rails credentials:editcredentials.yml.encは直接開けず、master keyで復号化する必要があるため上記コマンドで。EDITORの指定は必須、Dockerでalpineイメージを使っているなど、場合によって
EDITOR=vi
aws:
の部分のコメントアウトを解除しaccess_key_id
,secret_access_key
を追加aws: access_key_id: YOUR_KEY_ID secret_access_key: YOUR_ACCESS_KEY # Used as the base secret for all MessageVerifiers in Rails, including the one prote secret_key_base:***********************************Action mailerの設定
config/initializers/aws.rb (新規作成)
credentialsからaccess_key_id, secret_access_keyを呼び出していますcreds = Aws::Credentials.new(Rails.application.credentials[:aws][:access_key_id], Rails.application.credentials[:aws][:secret_access_key]) Aws::Rails.add_action_mailer_delivery_method( :ses, credentials: creds, region: 'ap-northeast-1' #AWS SESで設定したregion )config/environments/development.rb
config.action_mailer.delivery_method = :ses動作確認
テスト用のmailerを作ってみます
rails g mailer TestMailer testapp/mailers/test_mailer.rb
class DefaultMailer < ApplicationMailer default from: 'example.com <noreply@example.com>' #example.comに自分のドメインを追加 # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # # en.default_mailer.test.subject # def test @greeting = "Hi" mail to: "テストメールを受信するアドレス" #追加 end endRails cTestMailer.test.deliver_nowこれで
noreply@...
からメールが送信されるはずですお掃除
rails d mailer TestMailerTroubleshoot
テスト送信がうまく行かない場合は以下のオプションでデバックに必要な情報が得られるかもしれません
config/environments/development.rbconfig.action_mailer.raise_delivery_errors = trueインターフェイス
formからpost :send_mailで受けて
controller
def send_mail @user = User.new(user_params) DefaultMailer.test(@user).deliver_now redirect_to ... endapp/mailers/default_mailer.rb
class DefaultMailer < ApplicationMailer default from: 'example.com <noreply@example.com>' # Subject can be set in your I18n file at config/locales/en.yml # with the following lookup: # # en.default_mailer.test.subject # def test(user) @user = user @greeting = "Hi" mail to: @user.email end endapp/views/default_mailer/test.html.erb
<h1>Default#test</h1> <p> <%= @greeting %>, <%= @user.name%> Thank you for counfirm! </p>
- 投稿日:2020-09-17T10:55:19+09:00
RDS のコンソール表示で詰まった話
<概要>
RDSのDBエンジンの更新を実施しようと思ったのだが、
旧コンソールでは「DBエンジンのバージョン変更」が表示されず焦った。クラスター > クラスターの変更 > DBエンジンのバージョン変更 ←これが表示されない。
サポートに問い合わせた所、IE11じゃあかんのらしい。IEEE!!!
Chrome , Firefoxでないとだめだそうな。IE をご利用いただいており、旧 RDS コンソールが表示された状態であることを確認いたしました。
誠に恐れながら、現状 IE11 では RDS の新コンソールをご利用いただくことができず、このため新 RDS コンソールにて実装されている機能を使用することができません。
クラスターのエンジンバージョンの変更につきましても、旧コンソールでは対応しておらず、新コンソールまたは AWS CLI などを使用する必要がございます。このため、大変恐れ入りますが、現在ご利用いただいております IE 以外の Google Chrome や Firefox などのブラウザをご利用いただくことで、新 RDS コンソールにアクセスでき、またクラスターのエンジンバージョンの変更が可能となるかご確認いただけますでしょうか。