「おや…ELBのようすが…?」という時に確認すること

私の経験上、「ELBがおかしいぞ!AWSしっかりしろや!」と思ったら、よくよく調べると実装がダメなだけだったというパターンがよくあるのでまとめたい。

あ、ELBと言いながら厳密にはALBの話をメインにします。

CASE 1. 「バックエンドは生きてるはずなのにヘルスチェックが失敗する!」

つまり以下のような症状が出る状況

  1. ヘルスチェックが失敗する
  2. バックエンドに直接アクセスしたら普通にうまくいく
  3. バックエンドのアクセスログ見たら、ヘルスチェックに200を返している

というパターン。
「バックエンドは生きてるしヘルスチェックにも200を返してる!それなのに失敗扱いなんてELBの不具合だ!」と思いたくなる気持ちはわかるが、まずは落ち着いてヘルスチエックの成否の基準を見直そう。

ターゲットグループのヘルスチェック – Elastic Load Balancing

ターゲットが応答タイムアウト期間内に応答するのを待ちます。

つまりバックエンドが200を返していようが、タイムアウトしていたらヘルスチェック的には失敗なのだ。
とりあえずバックエンドのアクセスログから処理時間を確認してみよう。まさか処理時間を出力するようにログフォーマットを設定してないなんて言わないよな?

ちなみにステータスコード200というのも注意が必要で、みんな大好きApacheのドキュメントにこんな記載がある

mod_log_config – Apache HTTP サーバ バージョン 2.4

%s ステータス。内部でリダイレクトされたリクエストは、元々の リクエストのステータス — 最後のステータスは %>s

すこぶる分かりにくい文章だが、要するに
・ %s -> リダイレクトが発生した際に3XXのコードを出力する
・ %>s -> リダイレクトが発生した際に3XXのコードを出力せず、リダイレクト後の処理の結果のコードを出力する

というわけだ。そのため、「200を返してるし、CPU使用率も上がってないから大丈夫なはず」と思ってたら実はリダイレクトしまくってタイムアウトしてた、というパターンもある。
そして、筆者の記憶が確かならApacheではデフォルトで%>sが設定されている。

というわけでこの症状の場合は、ELBの不具合とヘルスチェックのタイムアウト、どちらの方がありえそうか考えて調査をはじめてみよう。

CASE 2. 「HTTPCode_ELB_5XX_Countメトリクスが出てる!」

「5XXはサーバサイドのエラー…ELBが5XXエラーを出しているということは…ELBの不具合や!」と思いたくなる気持ちはわかるが、まあ落ち着いてほしい。
私も過去に502 Bad Gatewayに悩まされ、AWSにも問い合わせたりしたが、結局のところバックエンドの実装がクソだったのが原因だった。
だから落ち着いてまずはこれを見てほしい。

Application Load Balancer のトラブルシューティング – Elastic Load Balancing

うん、分かりにくいな。
5XX系のエラーに絞って、頑張って解説してみよう。

HTTP 502: Bad Gateway

そもそも502 Bad Gatewayというもの自体が分かりにくい。
Wikipediaによると

不正なゲートウェイ。ゲートウェイ・プロキシサーバは不正な要求を受け取り、これを拒否した。

これで理解できる人間はほぼいないだろう。
502 Bad Gatewayについてはこちらのページの解説が分かりやすい。

502Bad Getewayの原因と意味について | ぷろめし|プログラミングよりも飯が好き

ELBに当てはめて考えるなら、「ELBからバックエンドにリクエストを投げたが、解せないレスポンスが帰ってきた」という状況である。
つまり、ELBがイかれている可能性もあるが、バックエンドがイかれたレスポンスを返したり途中でコネクションをブッチしている可能性もあるのだ。

つまり、502 Bad Gatewayが現れたらバックエンドからのレスポンスがどうなってるかを見直した方がいいだろう。

HTTP 503: Service Unavailable

一時的に使えないというやつ。
上記のドキュメントにはターゲットグループにインスタンスが無いという場合しか書いてないが、実は他にも503が発生する可能性はある。

ELBはトラフィック量に合わせて自動でスケールするが、あまりに急激にトラフィックが増加した場合にはスケールが間に合わなくなることがある。そういう場合にはELBの処理能力が足りなくなって503が発生することになる。
ほっといたらそのうちELBがスケールして解決するが、「一瞬たりとも落とせないんだ!」という時は予めPre-warmingを申し込んでおこう。

Elastic Load Balancing の暖気申請について | Developers.IO

見積もりがガバガバだと「ほんまにPre-warming必要か?」と突っ込まれることもあるので、真面目に見積もろう。

HTTP 504: Gateway Timeout

これは分かりやすい。
ELB -> バックエンドの通信がタイムアウトしたということである。
バックエンドの処理を見直すか、タイムアウト時間を伸ばすかの二択だが、基本的にはバックエンドの処理を見直す方が健全である。

色々調べたが、どう考えても実装に問題はない時は…

AWS側の問題という可能性もあるので、問い合わせよう。

続きを読む

コピペで使えるELBのアクセスログ解析による事象分析 (ShellScript, Athena)

アクセスログ解析

ELBのアクセスログの事象分析について、ShellScriptとAthenaを用いた実行例についてまとめます。

ShellScript

CLB

No.1 : レスポンスが正常に受け取れていないELBのレスポンスコード毎のカウント

$ awk '$10 == "-"' * | awk '{print $9}' | sort | uniq -c

No.2 : ELBのレスポンスコード毎の数集計

$ awk '{print $8}' *.log | sort | uniq -c

No.3 : 504のレコード一覧

$ awk '$8 == 504'

No.4 : 504がどのELBノードから多く出力されているか

$ grep ' 504 ' *.log | awk '{print $3}' | sed 's/:.*//' | sort | uniq -c

No.5 : バックエンドから正常に応答が受け取れていない時

$ awk '{if (! int($5) < 0) {print $0}}' * | egrep '2018-01-2[45]'

No.6 : target_processing_time の3つの統計値(最小値、最大値、平均)と -1 の値を取った回数を表示する

$ awk '{ print $4,$8,$9,$6 }' * | sort | sed -e 's/ /!!/' -e 's/ /!!/' | awk '{if(count[$1]==0) min[$1]=100; count[$1]+=1; if(max[$1]<$2&&$2!=-1) max[$1]=$2; if(min[$1]>$2&&$2!=-1) min[$1]=$2; if($2!=-1)sum[$1]+=$2; else minus[$1]+=1;} END{for(k in count)print k,", count:",count[k],", max:",max[k],", min:",min[k],", avg:",sum[k]/count[k],", -1:",minus[k];}' | sort -k4nr

No.7 : response_processing_time の3つの統計値(最小値、最大値、平均)と -1 の値を取った回数を表示する

$ awk '{ print $4,$8,$9,$7 }' * | sort | sed -e 's/ /!!/' -e 's/ /!!/' | awk '{if(count[$1]==0) min[$1]=100; count[$1]+=1; if(max[$1]<$2&&$2!=-1) max[$1]=$2; if(min[$1]>$2&&$2!=-1) min[$1]=$2; if($2!=-1)sum[$1]+=$2; else minus[$1]+=1;} END{for(k in count)print k,", count:",count[k],", max:",max[k],", min:",min[k],", avg:",sum[k]/count[k],", -1:",minus[k];}' | sort -k4nr

No.8 : 最も多いリクエスト元のELBノードIPアドレスのリクエスト数

$ awk '{print $3}' * | awk -F ":" '{print $1}' | sort | uniq -c | sort -r| head -n 10 

No.9 : 時間毎のリクエスト数

grep中の二重引用符内は適宜日付等を入れて絞り込み

grep -r "" . | cut -d [ -f2 | cut -d] -f1 | awk -F: '{print $2":00"}' | sort -n | uniq -c

No.10 : 分単位でのリクエスト数

grep中の二重引用符内は適宜日付等を入れて絞り込み

$ grep "" * | cut -d [ -f2 | cut -d ] -f1 | awk -F: '{print $2":"$3}' | sort -nk1 -nk2 | uniq -c | awk '{ if ($1 > 10) print $0}'

No.11 : ユーザーエージェント毎のランキング

$ awk '{split($0, array, """); agent=array[4]; print agent}' * | sort | uniq -c | sort -nr | head

No.12 : TLSでクライアントが最も使った暗号スイートのランキング

$ awk '{split($0, array, """); afterUserAgent=array[5]; print afterUserAgent}' * | awk '{print $1}' | sort | uniq -c | sort -nr | head -5

No.13 : TLSでクライアントが最も使ったTLSバージョンのランキング

$ awk '{split($0, array, """); afterUserAgent=array[5]; print afterUserAgent}' * | awk '{print $2}' | sort | uniq -c | sort -nr | head

No.14 : TLSでクライアントが最も使ったプロトコルと暗号スイートのランキング

$ awk '{split($0, array, """); proto=array[1]; afterUserAgent=array[5]; print proto afterUserAgent}' * | awk '{print $1 " " $13}' | sort | uniq -c | sort -nr | head

ALB

No.1 : target_processing_time の3つの統計値(最小値、最大値、平均)と -1 の値を取った回数を表示する

 $ awk '{ print $5,$9,$10,$7 }' * | sort | sed -e 's/ /!!/' -e 's/ /!!/' | awk '{if(count[$1]==0) min[$1]=100; count[$1]+=1; if(max[$1]<$2&&$2!=-1) max[$1]=$2; if(min[$1]>$2&&$2!=-1) min[$1]=$2; if($2!=-1)sum[$1]+=$2; else minus[$1]+=1;} END{for(k in count)print k,", count:",count[k],", max:",max[k],", min:",min[k],", avg:",sum[k]/count[k],", -1:",minus[k];}' | sort -k4nr

No.2 : response_processing_time の3つの統計値(最小値、最大値、平均)と -1 の値を取った回数を表示する

$ awk '{ print $5,$9,$10,$8 }' * | sort | sed -e 's/ /!!/' -e 's/ /!!/' | awk '{if(count[$1]==0) min[$1]=100; count[$1]+=1; if(max[$1]<$2&&$2!=-1) max[$1]=$2; if(min[$1]>$2&&$2!=-1) min[$1]=$2; if($2!=-1)sum[$1]+=$2; else minus[$1]+=1;} END{for(k in count)print k,", count:",count[k],", max:",max[k],", min:",min[k],", avg:",sum[k]/count[k],", -1:",minus[k];}' | sort -k4nr

Athena

以下、全て CLB を前提とします。
また、以下のような、デフォルトで生成されている sampledb データベースの elb_logs テーブルを使用します。

CREATE EXTERNAL TABLE `elb_logs`(
  `request_timestamp` string COMMENT '', 
  `elb_name` string COMMENT '', 
  `request_ip` string COMMENT '', 
  `request_port` int COMMENT '', 
  `backend_ip` string COMMENT '', 
  `backend_port` int COMMENT '', 
  `request_processing_time` double COMMENT '', 
  `backend_processing_time` double COMMENT '', 
  `client_response_time` double COMMENT '', 
  `elb_response_code` string COMMENT '', 
  `backend_response_code` string COMMENT '', 
  `received_bytes` bigint COMMENT '', 
  `sent_bytes` bigint COMMENT '', 
  `request_verb` string COMMENT '', 
  `url` string COMMENT '', 
  `protocol` string COMMENT '', 
  `user_agent` string COMMENT '', 
  `ssl_cipher` string COMMENT '', 
  `ssl_protocol` string COMMENT '')
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.serde2.RegexSerDe' 
WITH SERDEPROPERTIES ( 
  'input.regex'='([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*):([0-9]*) ([.0-9]*) ([.0-9]*) ([.0-9]*) (-|[0-9]*) (-|[0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) ([^ ]*) (- |[^ ]*)\" ("[^"]*") ([A-Z0-9-]+) ([A-Za-z0-9.-]*)$') 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  's3://athena-examples-us-west-2/elb/plaintext'
TBLPROPERTIES (
  'transient_lastDdlTime'='1480278335');

HTTPステータスコードが200のレコード一覧

SELECT * 
FROM elb_logs
WHERE elb_response_code <> '200'
ORDER BY request_timestamp;

ELB毎のリクエスト数

SELECT elb_name,
         count(*) AS request_count
FROM elb_logs
GROUP BY elb_name
ORDER BY request_count DESC;

ELB毎のリクエスト数(期間指定)

SELECT elb_name,
         count(*) AS request_count
FROM elb_logs
WHERE request_timestamp >= '2014-01-01T00:00:00Z'
        AND request_timestamp < '2016-01-01T00:00:00Z'
GROUP BY elb_name
ORDER BY request_count DESC;

ELB毎のリクエスト数(期間+ELB 指定)

SELECT elb_name,
         count(*) AS request_count
FROM elb_logs
WHERE elb_name LIKE 'elb_demo_008'
        AND request_timestamp >= '2014-01-01T00:00:00Z'
        AND request_timestamp < '2016-01-01T00:00:00Z'
GROUP BY elb_name
ORDER BY request_count DESC;

ELB毎の5XXエラーのリクエスト数

SELECT elb_name,
         backend_response_code,
         count(*) AS request_count
FROM elb_logs
WHERE backend_response_code >= '500'
GROUP BY backend_response_code, elb_name
ORDER BY backend_response_code, elb_name;

ELB毎の5XXエラーのリクエスト数(ELB指定)

SELECT elb_name,
         backend_response_code,
         count(*) AS request_count
FROM elb_logs
WHERE elb_name LIKE 'elb_demo_008'
        AND backend_response_code >= '500'
GROUP BY backend_response_code, elb_name
ORDER BY backend_response_code, elb_name;

ELB毎の5XXエラーのリクエスト数(期間+ELB 指定)

SELECT elb_name,
         backend_response_code,
         count(*) AS request_count
FROM elb_logs
WHERE elb_name LIKE 'elb_demo_008'
        AND backend_response_code >= '500'
        AND request_timestamp >= '2014-01-01T00:00:00Z'
        AND request_timestamp < '2016-01-01T00:00:00Z'
GROUP BY backend_response_code, elb_name
ORDER BY backend_response_code, elb_name;

ELB毎の5XXエラーのリクエスト数(期間+ELB+URL 指定)

SELECT count(*) AS request_count,
         elb_name,
         url,
         elb_response_code,
         backend_response_code
FROM elb_logs
WHERE elb_name LIKE 'elb_demo_008'
        AND backend_response_code >= '500'
        AND url LIKE 'http://www.example.com/jobs/%'
        AND request_timestamp >= '2014-01-01T00:00:00Z'
        AND request_timestamp < '2016-01-01T00:00:00Z'
GROUP BY elb_name,url,elb_response_code,backend_response_code
ORDER BY request_count DESC limit 10;

ELB毎の5XXエラーのリクエスト数(期間+ELB+URL+UserAgent 指定)

SELECT count(*) AS request_count,
         elb_name,
         url,
         elb_response_code,
         backend_response_code,
         user_agent
FROM elb_logs
WHERE elb_name LIKE 'elb_demo_008'
        AND backend_response_code >= '500'
        AND url LIKE 'http://www.example.com/jobs/%'
        AND user_agent LIKE '%Mozilla/5.0%'
        AND request_timestamp >= '2014-01-01T00:00:00Z'
        AND request_timestamp < '2016-01-01T00:00:00Z'
GROUP BY elb_name,url,elb_response_code,backend_response_code,user_agent
ORDER BY request_count DESC limit 10;

送信元IPのリクエスト数ランキング

SELECT request_ip,
         url,
         count(*) AS request_count
FROM elb_logs
WHERE elb_name LIKE 'elb_demo_008'
        AND request_timestamp >= '2014-01-01T00:00:00Z'
        AND request_timestamp < '2016-01-01T00:00:00Z'
GROUP BY request_ip,url
ORDER BY request_count DESC limit 5;

日付ごとのリクエスト数

SELECT date(from_iso8601_timestamp(request_timestamp)),
         count(*)
FROM elb_logs
WHERE url LIKE '%/jobs/%'
        AND date(from_iso8601_timestamp(request_timestamp)) >= date('2014-12-01')
GROUP BY  1
ORDER BY  1;

直近1年の500エラー発生のリクエスト数

SELECT elb_response_code,
         count(*)
FROM elb_logs
WHERE from_iso8601_timestamp(request_timestamp) >= date_add('day', -365 * 1, now())
        AND elb_response_code >= '500'
GROUP BY  1
ORDER BY  1;

レスポンスに1.0s以上時間がかかっているリクエスト

SELECT url,
         count(*) AS count,
         backend_processing_time
FROM elb_logs
WHERE backend_processing_time >= 1.0
GROUP BY  url, backend_processing_time
ORDER BY backend_processing_time DESC;

任意のエントリ取得(期間+リクエスト元IP 指定)

SELECT *
FROM elb_logs
WHERE request_ip = '245.85.197.169'
        AND request_timestamp >= '2014-01-01T00:00:00Z'
        AND request_timestamp <= '2016-01-01T00:00:00Z';

あるページからの遷移先ページ傾向

SELECT d.*
FROM 
    (SELECT b.request_ip,
         min(b.request_timestamp) AS request_timestamp
    FROM 
        (SELECT *
        FROM elb_logs
        WHERE url LIKE '%/jobs/%') a
        JOIN elb_logs b
            ON a.request_timestamp < b.request_timestamp
        GROUP BY  1 ) c
    JOIN elb_logs d
    ON c.request_ip = d.request_ip
        AND c.request_timestamp = d.request_timestamp
ORDER BY  d.request_timestamp;

参考

ELB アクセスログ

Classic Load Balancer のアクセスログ – Elastic Load Balancing
https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/classic/access-log-collection.html#access-log-entry-syntax
Application Load Balancer のアクセスログ – Elastic Load Balancing
https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-access-logs.html#access-log-entry-syntax

Athena

Querying Classic Load Balancer Logs – Amazon Athena
https://docs.aws.amazon.com/ja_jp/athena/latest/ug/elasticloadbalancer-classic-logs.html
Querying Application Load Balancer Logs – Amazon Athena
https://docs.aws.amazon.com/athena/latest/ug/application-load-balancer-logs.html

Amazon AthenaでELBのログを調査するときに使ったSQL
https://dev.classmethod.jp/cloud/amazon-athena-sql-for-elb/
Amazon AthenaでELBログをSQLで解析する #reinvent
https://dev.classmethod.jp/cloud/aws/amazon-athena-sql-elb-log-reinvent/
Amazon Athenaではじめるログ分析入門
https://qiita.com/miyasakura_/items/174dc73f706e8951dbdd

続きを読む

AWS ALBのスティッキーセッションについて調べてみた(再接続のたびにセッションの寿命は延びる)

ALBのスティッキーセッションを使うことになったのだが、設定を見るとセッション保持時間しか設定できない。
これだと、例えば24hで設定したとして、24h後のちょっと前にアプリにログインしても、stikky sessionのcookie切れで再ばランシングされて、ログインしていないインスタンスに接続したらログインセッションが切れてしまうことになる。
なんだかなーと思ってマニュアルを読み直すと以下の記述を見つけた。

ターゲットグループレベルでスティッキーセッションを有効にします。
ロードバランサー生成のクッキーの維持期間を秒単位で設定することもできます。
期間はリクエストごとに設定されます。
そのため、各期間の有効期限が切れる前にクライアントがリクエストを送信すると、スティッキセッションが継続されます。
複数のターゲットグループのスティッキーセッションを有効にした場合、すべてのターゲットグループに同じ継続時間を設定することをお勧めします。

若干曖昧だた有効期限内にリクエストを送信するとセッションが延長されるって意味かな?
ということで実際に検証してみることにした。

検証環境

検証環境は以下のとおりとした。
nginxの/stikky.txtにアクセスするとそれぞれnginx01nginx02と返すようにしてある。
スティッキーセッションの期間は3秒とした(実環境ではこんな未時間に設定するわけないけどw)

Client --- ALB --- Nginx01
                └- Nginx02

検証結果

まずは5秒間隔でポーリングしてみる。
コマンド結果をみるとおりNginx01, Nginx02に交互に接続している。
スティッキーセッションが3秒で切れてしまうから、5秒後にアクセスしたときは新規接続とみなされ再振り分け、coocke再発行されるためだ。

[root@localhost ~]# yes "date '+%d-%m %H:%M:%S'|tr 'n ' ' ' ; curl -c ./cookie_elb.txt -b ./cookie_elb.txt http://nginx-1428263045.us-west-2.elb.amazonaws.com/stikky.txt;sleep 5;" | sh
27-01 05:45:03 nginx02
27-01 05:45:08 nginx01
27-01 05:45:14 nginx01
27-01 05:45:20 nginx02
27-01 05:45:25 nginx02
27-01 05:45:31 nginx01
27-01 05:45:36 nginx01

次に1秒間隔でポーリングしてみる。
30秒以上試したが今度はずっとNginx02に接続している。
心配していたセッションの途中切れだが、5秒以上たってもセッションは維持されるようだ。
つまりセッション期間内に再接続すればstikky sessyonの時間も延長される、ということだろう。

[root@localhost ~]# yes "date '+%d-%m %H:%M:%S'|tr 'n ' ' ' ; curl -c ./cookie_elb.txt -b ./cookie_elb.txt http://nginx-1428263045.us-west-2.elb.amazonaws.com/stikky.txt;sleep 1;" | sh
27-01 06:03:12 nginx01
27-01 06:03:14 nginx01
27-01 06:03:15 nginx01
27-01 06:03:17 nginx01
27-01 06:03:18 nginx01
27-01 06:03:20 nginx01
27-01 06:03:21 nginx01
27-01 06:03:23 nginx01
27-01 06:03:24 nginx01
27-01 06:03:26 nginx01
27-01 06:03:27 nginx01
27-01 06:03:29 nginx01
27-01 06:03:30 nginx01
27-01 06:03:32 nginx01
27-01 06:03:33 nginx01
27-01 06:03:35 nginx01
27-01 06:03:36 nginx01
27-01 06:03:38 nginx01
27-01 06:03:39 nginx01
27-01 06:03:41 nginx01
27-01 06:03:42 nginx01
27-01 06:03:44 nginx01
27-01 06:03:45 nginx01
27-01 06:03:47 nginx01
27-01 06:03:48 nginx01
27-01 06:03:50 nginx01

で、スティッキーセッション期間はどれくらいに設定すべきか?

上記検証を踏まえると、バックグランドで頻繁に通信しているようなシステムならスティッキーもそのたびに更新され、ログアウトしない限り維持され続けると思われる。ユーザー操作がないと通信しないシステムの場合、無操作状態が続くとスティッキーセッションの有効期限を迎えてしまうから、システムのセッションタイムアウトと同じに設定するのが良いだろう。そうすれば、システムのセッションが切れて、再ログインが必要になるタイミングでスティッキーセッション有効期限が切れるのでユーザーが意図せず突然セッションが切れてしまう、という事態が防げる。

続きを読む

オンプレMySQLからAuroraへの移行で躓いたポイント(その3・移行後)

この記事は以下の記事の続きです。

一部、内容がその2と被りますが、移行後に生じた問題…というよりは、移行段階から移行後までずっと引きずっていた問題について触れていきます。

【注意】

  • ここで示した対策をそのままプロダクト環境に適用しているとは限りません。大抵はもう少し手を加えて適用しています。
  • 一部「これからプロダクト環境に適用する予定」のものもあります。

移行後まで引きずった問題点

ずばり、その2で記した「複数スレッドでのパフォーマンス問題」です。

移行前と移行後を比較すると、Webサーバの台数は1.5倍に増やしました。1台当たりの搭載メモリも1.5倍に増やしましたが、一方で仮想CPUのコア数は1台当たりで半分にしました。

「あれ?G1GC使ってるDBCP2のEvictionも走ってるのにコア減らしていいの?」となりそうですが、実際、r4.largeとm4.xlargeで結果の差が全くといっていいほど出ませんでした(同じような負荷レベルで処理が詰まる)。r4.largeでもCPU使用率が30~35%程度までしか行かず…腑に落ちないまま移行リリース。

移行当日は土曜日だったので負荷はあまりなく(でもリリース直後は普通の土曜日よりアクセスが多かった)特に問題は出ませんでしたが、最初の営業日、やはりWebサーバが1台、処理が詰まって落ち、再起動しました。
※50xエラーが一定期間に閾値以上発生したら再起動するように仕込んでありました。

しばらく様子見していましたが、頻繁ではないものの週に1回から数回、Webサーバが落ちます。アクセスログを見ると、不正なアクセスではなく正規のユーザが、

「以前より反応が遅くなったので、画面を何度かリロード」

しているようだったので(一部「イラついて連打」している人もいたりいなかったり?)、最初は画面で「処理中」表示を行い操作を抑制するとともに、WAFで過剰なアクセスを遮断する対応を行いました。

時間稼ぎ(?)の対策を開始

しかし、WAFでは「閾値を低くしすぎると正常なアクセスを遮断してしまう」こともあり、あまり効果的にリロードを抑制できず。画面表示での(心理的な)抑制も、処理の進行状況を表示しないとかえって逆効果になることも(ケースバイケースですが)。

この時点ではまだ「オンプレ時代より遅い」ことの正確な原因(Web~DB間のネットワークレイテンシが影響していることはまず間違いはなかったのですが)は把握できておらず、解決が長引きそうだったので、

  • Webサーバをさらに増やしてオンプレ時の2倍に
  • Aurora DBクラスタのWriterがあるゾーンとは別のAZにレプリカ(Reader)を設置し、SELECT負荷が高いアプリケーションを一部修正してReaderにアクセスするように

するとともに、

  • 「処理が詰まる」とはいっても「特定のWebサーバ」の「負荷が高くなったアプリケーション単体」でDBアクセスが詰まっており、ほかのWebサーバはそのまま動き続け、同じWebサーバでも別のアプリケーションはしばらく動作を続けていた(DBへのコネクションプーリングはWebアプリケーション個々で分けて張っていたため)

ことから、閾値以上の50xエラーが出たときにWebサーバを単純に再起動するのではなく、ALBのDrainingを使って再起動し「巻き込み被害」を極力減らす対応を行いました。

※タイトルがわかりにくくてごめんなさい(どう表現したらよいかわからず…)。

また、

  • 「負荷が高い画面」の処理であっても、利用ユーザによって処理するデータ量に差があり、結果として処理時間に大きな開きがある。一律に「単位時間当たりのリクエスト数」を制限しても(WAFによる遮断と同様に)うまくいかない

ので、「負荷が高い画面のリクエストを受け付けた時点からレスポンスを返しきるまで」を「1」とカウントし、(ユーザ毎に)並行処理(カウント)数が閾値以上になっているかどうかを見て遮断するように、JavaのローカルキャッシュとTomcatのサーブレットフィルタを使って対応しました。

※こちらも上に同じ。タイトルを付けるのは難しい。

なお、この対応を行っている最中、正規のユーザが「遅さに慣れた」(リロードしまくるとかえって遅くなることに気づいた)ことで、処理が詰まってWebサーバが再起動することはほぼなくなりました。但し、遅い状態が解消されたわけではないので、対応が不要になったわけではありません。

Meltdownパッチで動揺、そして問題解決

2018年1月になり、Meltdown&Specterの騒ぎが発生、AWSもEC2やAuroraなど関係するクラウド基盤サーバにパッチを当てたようです。その結果、「サーバ性能低下」という事案が発生しました。

ベンチマークを取りつつ急いで利用中AuroraインスタンスのバージョンアップとR4への移行を行いましたが、作業直前にほぼ元の性能に戻っていました(振り回された…)。

ただ、ここでベンチマークを取りながら別の対応策を検討。移行リリース前、コネクションプールをDBCP2からTomcat JDBC Poolに移行しようとして失敗して時間切れになっていましたが、今度はHikariCPへの移行にトライ。
※「ステージング環境での負荷テスト結果はともかく、プロダクト環境で処理が詰まるのはDB(Aurora)側が原因ではなさそうだけど、DBへの問い合わせで詰まっていることは間違いない」というところまでは絞り込んでいましたので、行き当たりばったりの対応ではありません(多分。おそらく。きっと)。

結果、これがうまくハマってオンプレ時と同等(一部はそれ以上)の性能を取り戻すことができました。

Auroraフェイルオーバー時の処理の詰まりもなくなりました。

また、これで(現状の負荷なら)Webサーバの台数も元に戻すことができます。

AWSのネットワーク環境は、同一AZであってもインスタンス間のレイテンシ(遅延)が往復平均で200マイクロ秒(μs)程度あるようです(OS上で計測した場合)。これは、同じ室内に1ラック~数ラック立てて運用しているオンプレ環境(特にSDNを使っていない環境)とは1桁以上の差があります(オンプレが速い)。

通常のSQLクエリ/コマンドの実行では大した影響がなくても、コネクションプールでは接続のValidationやEviction等の際に確認クエリを発行する上、MySQL Connector/Jも色々と管理・確認用のクエリを流しがちな傾向があるので、わずかな処理時間の増加でもリソースの取得・開放がスムーズにいかない原因になります。

そこを、HikariCPにしたことで処理が軽減されて(クエリ発行数も減ったはず)、AWS環境でもうまく回るようになったようです。

なお、途中では触れませんでしたが、負荷を軽くするには「遅いSQLの改善」も重要です(当然、その対応も進めていました)。

HikariCPでWebサーバの動作が軽くなったことで、今度は「遅いSQLを処理するときのDB負荷」のほうにボトルネックが移りました。これまで以上にSQLの改善、INDEXやテーブル設計の調整などに力を入れる必要がありそうです。

まとめ

  • ユーザは「遅くなった」ことには割と敏感に気づき、そして(気軽に?)画面をリロードする
  • 一方で、「遅くなった」ことにはしばらくすると慣れる(限度はあるし、ユーザの時間を無駄に奪ってはいけない)
  • ちょっとしたレイテンシの変化が処理のサイクル・バランスを大きく崩すことがある(オンプレで実績がある構成でも、そのままで行けるとは限らない)
  • ボトルネックを1つ解消すると、別のところにボトルネックが移る(ので、移った先の対応を進める必要がある)

続きを読む

AWS CodeDeploy logs that you should watch.

We have a web service with AWS, located in like below environment. (This time in case of using AmazonLinux) WebServer: – Production: – ALB * 1 – EC2 * 2 – Dev: – EC2 * 1 DB: – Production: Aurora * 1 – Dev: Aurora * 1. Our source code is managed by github organization account. And we’re going to … 続きを読む

「Microservices Meetup vol.6」に行ってきた

2017/1/19のデイリーストックランキングにランクインしました。

【毎日自動更新】Qiitaのデイリーストックランキング!ウィークリーもあるよ_-_Qiita.png


いままでの。
「Microservices Meetup vol.2」行ってきたメモ。
「Microservices Meetup vol.4」に行ってきた

図らずして1回おきに参加してますね。

Connpassのイベントページ

少し間が空いてしまったがまた定期的に開催していきたいです。
登壇したいよーって人は直接リプ投げてください
by @qsona さん

Microservices on AWS by @Keisuke69 さん

Keisuke Nishitani @ AWS

Specialist Solutions Architect

サーバーレスアプリケーション開発ガイド 2月発売

マイクロサービスのポイント

  • 管理運用まで含めて分散型
  • 各コンポーネントが独立している
    • 単独で実行できないのは適切に設計されていない

独立して分散していると負荷の高い機能の単位でスケールさせられる=コスト効率が良い

  • 一つのことをうまくやる

    • 複雑化したら分割する
  • 多言語

    • チーム(機能)にはそれぞれの問題に対して適切なツールを選択する自由がある
    • OS、言語、各種ツールに至るまで最適なアプローチを目指せる
  • 検索:Elasticsearch / Solr

  • ソーシャル:グラフDB

  • ログデータ:Cassandra

  • セッション:Redis

†AWSでは:AWSには100ちょいのサービスがあって、その中で更にチームが分かれている。
各チームに社内標準の開発プロセスは存在しない。

  • ブラックボックス

    • 詳細は外部のコンポーネントに公開されない
  • DevOps
    • マイクロサービスにおける組織原理

†AWSでは:運用のみのチームはない。オンコールも開発チームが請け負う

  • 俊敏性

    • 狭い範囲のコンテキストで活動=サイクルタイムが短い
    • システムもシンプル
    • パラレルな開発とデプロイが可能
  • イノベーション

    • 選択に対する権限と責任を持つのでイノベーションを起こしやすい
    • DevとOpsの対立がないため、効率化やイノベーションが起こしやすい
  • 拡張性

    • 適切に非干渉化されてていることで水平方向に単独にスケールできる
  • 可用性

    • ヘルスチェック、キャッシング、隔壁、サーキットブレーカーと言った仕組みは全体の可用性を向上させる

課題

  • 分散型であることは難しい

    • システム数が増える
    • 協調動作の難しさ
    • コンポーネント間のコミュニケーションメッセージ増によるレイテンシ低下
    • ネットワークの信頼性は無限ではない、帯域も無限ではない
  • 移行が大変
    • モノリシックなシステムの分割は難しい
  • 組織
    • 組織体制の変更が必要になるが、それは大変なこと
  • アーキテクチャの難易度
    • 非同期通信
    • データ整合性
    • やっぱりココが一番の課題
    • サービスディスカバリ
    • 認証
  • 運用の複雑さ

アーキテクチャ

一番シンプルなパターン

CloudFront – ALB – ECS – datastore(ElastiCache, Dynamo, )
|
S3

  • バックエンドをRESTfulなAPIにしたSPA
  • 静的なコンテンツはS3とCloudFront
    • CDN通すことでレイテンシが上がることもある
    • キャッシュとの併用を検討
  • ECSとAutoScalingをALBとともに使うことが多い
    • ALB(L7LB)でアプリレベルの情報をルーティング
    • コンテナインスタンスにリクエストを分散
    • ECSのコンテナを負荷に応じてスケールアウト/インする

コンテナのメリット

  • Portable
  • Flexible
  • Fast
    • 軽量で早い
    • ポータビリティと関連して開発サイクルも早く
  • Efficient

  • 一貫性のある環境

  • バージョン管理出来る

Dockerの特徴

  • Package
  • Ship
  • Run

ECS

  • 複数のコンテナをEC2のクラスタ上で一元管理

    • まだTokyoにきてないけど、FargateでEC2の管理もいらなくなるよ
    • EKS(Kubernetes)も発表されています

データストア

  • インメモリ

    • Memcached, Redis
    • ElastiCache
    • DB負荷の軽減
  • RDBMS
    • 無限のスケーリングには向いていない
    • Amazon RDS
  • NoSQL
    • 水平スケーリングをサポートするものが多い
    • テーブル結合ができないのでロジックの実装が必要になる場合も
    • Amazon DynamoDb, KynamoDB Accelarator(DAX)

APIの実装

  • APIの設計、改善、デプロイ、モニタリング、保守派手間がかかる
  • 異なるバージョンが混在すると大変

Amazon API Gateway

  • 複数バージョンとステージ
  • Cognite User Poolsと連携して認証を追加
  • スロットリング、モニタリング
  • バックエンドとしてLamdbaが使える

サーバーレス

  • サーバーはないに越したことはない
  • スケーリングと高可用性の担保が大変
CloudFront - API Gateway - Lamdba - datastore
   |                            (ElastiCache, Dynamo)
   |
  S3

課題をAWSでどうするか

サービスディスカバリ

  • お互いの死活確認や発見
  • ハードコードするとスケールできない

    • メタデータをどこに置くか
  • ALBを利用したサービスディスカバリ

  • DNS(Route53)のサービスディスカバリ

    • VPC単位の振り分け
  • ECSイベントストリームを使用したサービスディスカバリ

    • CloudWatchイベントで拾ってキック
    • LamdbaでRoute53に登録
    • これが一番多いかも
  • DynamoDBを使用したサービスディスカバリ

    • DNSキャッシュの問題がない
    • 自由度が高い
    • 実装が大変
    • DynamoDBストリームを活用して他のサービスへステータス変更を反映
  • イベントベースのアーキテクチャ

    • イベントで処理する
    • いわゆる結果整合性とも関連
    • Dual Write Problem
    • Event Sourcing
      • 状態の記録ではなく状態の変更「イベント」を記録
      • Amazon Kinesis
      • kinesisにpublishして他サービスはsubscribe
      • S3にログを残す
      • トランザクションログの仕組み

モニタリング

  • CloudWatch

    • ログファイルの一元化
    • CloudWatch LogsかS3に保存可能
    • ECSはCloudWatch Logsに一元化できる

分散トレース

  • AWS X-Ray
  • 複数サービスに分散したリクエスト処理を透過的に追える

LogWatchの先に Kinesis FireHorse – ほにゃほにゃ

スロットリング

  • 大事だよ

リトライ

  • 多くのエラーはリトライでカバーできるものが多い
  • リトライ多発で過負荷になることがある
    • Exponential back offもしくはフィボナッチ数列を利用したリトライ感覚の調整
    • 更に乱数でゆらぎを付けて重ならないように

LT:クラウド型医療系業務システムと Microservices への歩み by @hashedhyphen さん

https://speakerdeck.com/hashedhyphen/kuraudoxing-dian-zi-karutesisutemuto-microservices-hefalsebu-mi

クラウド型電子カルテシステムの話

  • メインロジックをRails
  • 常時接続をNode.js
  • バックエンドをScala
  • 基盤はAWS

ここに至る道のり

ファーストリリース前

  • レコメンドは大量のデータが必要

  • メインロジックで実現させようとすると厳しかった

    • Threadとか試したけど……
    • 別アプリケーションとして分離
    • JVM上でScala + Skinnyでスレッドアプリケーションンを実装
    • Microservicesちっくな構成へ
  • 障害検知時

    • メインロジック内のプロトタイプ版が動く!

会計機能リリース前

  • お金を扱う機能は安定性が欲しい
  • Scala + Cats による実装を別エンドポイントとして実装
  • マイクロサービスっぽい作りになっていたから自由度のある技術選択が出来た

まとめ

  • ちょっとマイクロサービス化したことで自由度が上がった
  • 原理主義的にならなくても恩恵がある
  • エンジニアの伸びしろ!

FrontEndからみるmicroserviceとBackendからみるmicroservice by @taka_ft さん

Takahiro Fujii @ Rakuten Travel

楽天内でも採用アーキテクチャはサービスによって異なる。

サービスとしては

  • コンシューマ向け
  • Extranet
  • In-house
  • other
    • ここまではWEBとモバイルがあって、100以上のAPIを利用する
  • API(内部APIを直接利用)

Phase 1

  • 大きなモノリシックなアプリだった
  • 機能がどんどん増えていく
    • 機能別で複数のモノリシックなアプリに分割
    • その後フロントエンドとバックエンドに分割

Phase 2

  • ドメインモデルを整理
  • なるべくRESTfulで作るようになっていった
  • 大きなAPIから小さなAPIに分離
  • I/Fの決定に時間がかかるようになった
  • API呼び出しが大変になった
  • Microservicesっぽくなってきた 2014年くらい

  • 国内予約、海外予約で多言語化だけではない商習慣に根ざしたドメインの差異による重複ロジックの増殖が起きた

  • Microservicesっぽくなってきたが、どんどん品質が下がっていった

Phase 3

(ちょうど前日にプレスリリース)サイト前面刷新に向けての取り組み

  • FrontendsはJavaScriptに刷新
  • API GatewayにKong

組織

  • フロントエンドチームが2人から始まって半年でReactエンジニアを集めて20人以上に

    • 日本人は2, 3人

UI Component

  • SpringからJavaScriptでSPAに変更
  • zeplinのデザインモックからUI Componentを実装するようになった
  • Storyboardを使ってUI Coponentを管理
  • ドメインを含むUI Componentはドメインと結び付きが強いことが多い=専用のオーケストレーションAPIを用意してUI Componentないで処理を閉じることが出来る
  • レスポンシビリティを明示的に定義

    • どこまでフロントエンドでやるか、どこからAPIからもらうか
  • フロントエンドの「使いやすいレスポンス」の要求

  • バックエンドの「汎用的でシンプルなレスポンス」の希望

    • これらのバランスを取る
  • API Gatewayはフロントエンド(UI)チームの管轄

    • ただし状況によってはバックエンド側に持っていくことも検討するつもり

LT: “マイクロサービスはもう十分”か? by @qsona さん

https://speakerdeck.com/qsona/enough-with-the-microservices

POSTDに投稿していた翻訳記事。

スタートアップ企業のほとんどはマイクロサービスをさいようすべきではない

銀の弾丸はない。

「チーム間の依存性」の解決にマイクロサービスというアプローチは違う。疎結合と分散は別。

組織が大きくなると、複数チームが1つのコードベースを触るようになる。そしてマイクロサービス化したくなる。

しかし、モノリスの分割で十分。

チームとは何か

  • 自律的に動けるべき

    • チームが増えるとコミュニケーションコストや、しがらみ
  • チームの分割は目的にそった分割を行う

  • 悪い分割の例: Dev / Ops

  • 依存度が低いほど自律的に動ける

  • High Output という本

  • 使命型組織/技術型組織

    • Micorservicesは使命型組織
    • 組織間が密ならサービス間も密になる

話戻して

  • スタートアップは組織をキレイに分割することが難しい
  • 分割しても密になってしまう

  • 大きなモノリスになるとやはり分割は難しい

    • ドメインの意識は必要
  • マイクロサービス設計しなくても「マイクロサービス精神」で開発すると効果的なのではないか

FiNCが初期からマイクロサービスでやってた理由

  • 単独の事業にできるような一つ一つのサービスを組み合わせて提供してきたから

あとで読み返して修正します。
資料パスの追加とかも。

続きを読む

AWS Fargate

AWS ECS Fargate とは Docker コンテナを動かすための仕組み。Docker コンテナを起動して外から URL でアクセス出来るようになるまでやってみた。

  • コンテナは 8001 (WebSocket) と 8002 (HTTP) の2つのポートを受け付ける。
  • 外から両方のポートを使いたい。

AWS ECS には Clusters, Task Definitions, Repository の3つの設定項目がある

  • Clusters:

    • 複数の Service をまとめて動作させる箱。
    • Service:
      • 複数の Task をまとめて動作させる箱。
  • Task Definitions:
    • Task とは、Docker コンテナの動作単位。使う Docker image は AWS の Repositories から取ってきても良いし、他の Docker repository を使っても良い。
  • Repositories:
    • AWS 用の Docker Repository. Docker image を保存する場所。

作業手順は次のようになる。

  1. Repositories に Docker image を登録
  2. Task Definition に Docker repository を指定
  3. Cluster を作成
  4. Application Load Balancer を作成
  5. Cluster 内に Service と Task を作成
  6. Security Group の設定

AWS Fargate First run

  • https://qiita.com/riywo/items/b223bdad2b3ae3bebf55 の通りに First runチュートリアル試した。
  • 次へ次へと押すだけ
  • Unable to assume the service linked role. Please verify that the ECS service linked role exists というエラーが出る。
  • 二回目同じ事をやると何故か問題なく完了した。
  • Cluster : default > sample-app-service
  • AWS Console > Elastic Container Service > Clusters : default > Tasks > Task をクリック
  • IP が割り当てられている事を確認。

AWS Cluster を削除する時の注意点

  • The vpc ‘vpc-xxx’ has dependencies and cannot be deleted. のエラーが出て消せなかった。
  • EC2 security group が参照していたので消したら OK。

AWS ECS Repositories に Docker image を登録

AWS FargateでFlaskを動かしてみる を参考に作業。

  • Console で Region N. Virginia を選択
  • AWS Console > Elastic Container Service (ECS) > Repositories
  • Create repository
  • Repository name: hoge-websocket-server
  • Repository URL: xxx.dkr.ecr.us-east-1.amazonaws.com/hoge-websocket-server

画面に表示されるコマンドを実行してログイン

$ aws ecr get-login --no-include-email --region us-east-1
docker login -u AWS -p eyJwYXlsb2...
$ docker login -u AWS -p eyJwYXlsb2... (表示された長いコマンドをそのまま実行)

先程出来た Docker image にタグを追加

$ docker tag hoge-websocket-server:latest xxx.dkr.ecr.us-east-1.amazonaws.com/hoge-websocket-server:latest

Docker image を Registory に追加

$ docker push xxx.dkr.ecr.us-east-1.amazonaws.com/hoge-websocket-server:latest
file integrity checksum failed for "usr/lib/x86_64-linux-gnu/libjpeg.a"

Docker image が大きいと上のようなエラーが出た。小さい node のイメージを使ってイメージをダイエットすると成功。

Task Definition に Docker repository を指定

ここで Docker image の場所や使いたいリソースを定義する。

  • AWS Console > ECS > Task Definitions > Create a new definition

    • 1: Select launch type compatibility

      • FARGATE
    • 2: Configure task and container definitions
      • Task Definition Name: hoge-websocket-server
      • Task memory (GB): 0.5GB
      • Task CPU (vCPU): 0.25 vCPU
      • Add container
        • Container name: hoge-websocket-server
        • Image: xxx.dkr.ecr.us-east-1.amazonaws.com/hoge-websocket-server:latest
        • Port mappings
          • 8001
          • 8002
      • Create

Cluster を作成

  • AWS Console > ECS > Clusters > Create Cluster

    • 1: Select cluster template

      • Networking only
    • 2: Configure cluster
      • Culster name: hoge-cluster
      • Create VPC: チェック (意味はよくわからない)
        • 作成されるネットワーク情報に目を通しておく。
        • デフォルトで2つのサブネットが定義されるようだが、そのままにしておいた。
    • View Cluster

これで Cluster 本体の他、色々な Cluster Resources が作成される。これらの大事な情報を後から参照する方法が分からなかったので必ずメモっておく。

Load Balancer の作成

このままだと起動停止のたびに IP アドレスが変わってしまうので、Application Load Balanceer (ALB) を使ってドメイン名を割り当てる。

  • AWS Console > EC2 > Load Balancers
  • Application Load Balancer > Create
  • 1: Configure Load Balancer
    • Name: hoge
    • Scheme: internet-facing
    • IP address type: ipv4
    • Listeners
      • HTTP: 8001 (Websocket も HTTP で良い)
      • HTTP: 8002 (HTTP 用の Listener は ECS Service 設定時に出来るのでここで作らなくて良い)
    • Availability Zones
      • VPC (Create Cluster で設定した VPC)
      • 下の Subnet は2つとも選択する
    • Tag
      • project : hoge
  • 2: Configure Security Settings
    • 設定なし
  • 3: Security Groups
    • Create a new security group
    • Security group name: hoge-security-group
    • 使いたいポート番号を設定
    • あとで AWS Console > ECS > Clusters > hoge-cluster > Tasks > Task : a44f… から編集出来る。
  • 4: Configure Routing
    • ヘルスチェックの設定らしい
    • Name: hoge-8002
    • Port: 8002
  • 5: Register Targets
    • 何も設定しない

Cluster 内に Service を作成

  • AWS Console > ECS > Clusters > hoge-cluster > Services > Create

    • 1: Configure service

      • Launch type: FARGATE
      • Task Definition: (作成した Task)
      • Service name: hoge-service
      • Number of taksk: 1
    • 2: Configure network
      • Cluster VPC: (Create Cluster で設定した VPC)
      • Subnets: (Create Cluster で設定した subnet)
      • Security groups > Edit
        • Assigned security groups: Select existing security group
        • Load Balancer で作った hoge-security-group を選択
      • Load balancing
        • Load balancer type: Application Load Balancer
        • Load balancer name: hoge
      • Container to load balance
        • ここで設定すると、コンテナの IP が変わっても自動的に一つのポートだけ Load Balancer に割り当てられる。
        • hoge-websocket-server:8002 > Add to load balancer
          • Listner port: 8002:HTTP
          • Path pattern: /*
          • Evaluation order: 1
          • Health check path: /
        • hoge-websocket-server:8002
    • 3: Set Auto Scaling
      • デフォルトのまま
    • 4: Review
      • 特に VPC Id と Subnets に気をつける。間違うとややこしい事になる。
    • Create Service

コンテナに割り当てられた IP アドレスの確認

  • AWS Console > ECS > Clusters > hoge-cluster > Tasks > Task : a44f… を選択
  • Private IP と Public IP が表示される。

もう一つのポート Port 8001 の設定

ECS Service の作成時に Load Balancer を割り当てると、IP が変化しても Load Balancer が勝手に面倒を見てくれる。ただしポート番号は一つしか設定出来ない!!!!この例のように2つ目のポートを Load Balancer に見せるには、手動で IP を指定した Target Groups を作る必要があった。なので、更新の際にはこの IP をわざわざ再入力する必要がある。

  • AWS Console > EC2 > LOAD BALANCING Target Groups

    • Target group name: hoge-8001
    • Protocol: HTTP
    • Port: 8001
    • Target type: ip
    • VPC: Cluster の VPC
    • Create
  • AWS Console > EC2 > LOAD BALANCING Target Groups > hoge-8001
    • Targets > Edit
    • Task の IP を登録
  • AWS Console > EC2 > LOAD BALANCING Load Balancers > hoge > Listeners
    • HTTP:8001 > View/edit rules
    • THEN に hoge-8001 を登録

Docker image の更新

  • AWS Console > ECS > Clusters > hoge-cluster > hoge-service > Update

    • Force new deployment: Check
    • 次へ次へ。。。
  • しばらく待つと新しいコンテナが起動して古いコンテナが停止する。
  • IP アドレスが変わっているので Load Balancer の Target Group も更新する。

Task 削除

Task は service が管理するので、直接止められないらしい。Service の Update で Number of tasks を 0 にすると止められた。

  • AWS Console > ECS > Clusters > hoge-cluster > hoge-service > Update
  • Number of tasks: 0
  • 次へ次へ

やりたかった事は node 一つの単純なサーバをテストしたいだけだったんだけど、随分大げさな構成になってしまった。

続きを読む

【AWS+Nginx1.10】エラーログはログフォーマットを指定することができない

環境

  • AWS
  • nginx version: nginx/1.10.3 (Ubuntu)
  • Rails5.1
  • Unicorn

やりたいこと

  • Nginxのログフォーマットを変更したい

    • AWSのALBのアクセス元IPを表示したい
    • アクセス元プロトコルを表示したい
    • ついでにエラーログもフォーマットを変更したい

設定例

/etc/nginx/nginx.conf

access_logおよび、error_logに独自フォーマットのmainを指定。

        ##
        # Logging Settings
        ##

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" $request_time $upstream_response_time '
                        '"$http_x_forwarded_for" "$http_x_forwarded_proto" $host';

        access_log /var/log/nginx/access.log main;
        error_log /var/log/nginx/error.log main;

発生した問題

  • Nginxが起動しない

原因

Nginxのエラーログに、独自フォーマットのmainを指定指定しているため。
構文チェックを行うと以下のエラーが表示される。

$ nginx -t -c /etc/nginx/nginx.conf
2018/01/16 15:34:00 [emerg] 29059#29059: invalid log level "main" in /etc/nginx/nginx.conf:53
nginx: configuration file /etc/nginx/nginx.conf test failed

正しい設定

error.logにはフォーマットは指定できません。

        ##
        # Logging Settings
        ##

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" $request_time $upstream_response_time '
                        '"$http_x_forwarded_for" "$http_x_forwarded_proto" $host';

        access_log /var/log/nginx/access.log main;
        error_log /var/log/nginx/error.log;

続きを読む