トラブルシューティング実況の目的
2つあるかと思います。
- サービスが利用できない状態であるときに、どこまで復旧が進んでいるのかを広く知らせるため
- 最近というほど最近でもありませんが、#savegitlab というのがありました。
- トラブルシューティングのノウハウを後世に伝えるため
- 今回はこちらの話です。
トラブルシューティングのノウハウの残し方
これがなかなか難しいと思います。「仕様書を読め」「いやコードを読め」「まずログから始めよ」などなど、方法論としてはいろいろあるかと思います。しかし方法論だと生々しさが足りない、実際の例を知りたい、ということもあるのではないでしょうか。
そこでやってみたのが トラブルシューティングの様子をSlackで実況する というものです。
やってみたののお題(実例)
- AWS CodeDeploy のデプロイが突然失敗するようになった
- GitLabプロジェクトのサーバー移動
前者はデプロイ・リリース用のチャンネルで、後者は関連プロジェクトのチャンネルで、一人、ログと戦いながらつぶやいていきました。
ここでは前者について、実際にSlackにつぶやいた内容をちょっとだけ共有します。
↓こんな感じです。(抜粋)
[8:09 PM]
調査メモ
・CodeDeployのエージェントのログを見ると、失敗するときは、適用後35秒間待っても InService にならないのでタイムアウトとなり、これが失敗の判断理由となっている模様
(続く)
↓必要最小限のログも張っていきます。(一部改変)
[2017-11-17 20:05:24.749] [d-0OZAVF72P][stderr]Instance failed to reach state, InService within 35 seconds
[2017-11-17 20:05:24.749] [d-0OZAVF72P][stderr][FATAL] Failed waiting for i-xxxxxxxxxxxxxxxxx to return to xxxxxxxxx
↓やろうとしていることとか。
[8:22]
タイムアウトの伸ばし方調査中
↓思ったこととか。
[8:26]
XXXX環境ではELBがないのでヘルスチェックの仕組みが違うのかも
↓調べたこととか。
[8:30]
これがこのソースだろう。どうやらELBの設定を見ているようだ。
https://github.com/awslabs/aws-codedeploy-samples/blob/master/load-balancing/elb/common_functions.sh
GitHub
awslabs/aws-codedeploy-samples
aws-codedeploy-samples - Samples and template scenarios for AWS CodeDeploy
[8:32]
この部分
# Wait for a health check to succeed
local elb_info=$($AWS_CLI elb describe-load-balancers \
--load-balancer-name $elb \
--query \'LoadBalancerDescriptions[0].HealthCheck.[HealthyThreshold,Interval,Timeout]\' \
--output text)
local health_check_threshold=$(echo $elb_info | awk '{print $1}')
local health_check_interval=$(echo $elb_info | awk '{print $2}')
local health_check_timeout=$(echo $elb_info | awk '{print $3}')
local timeout=$((health_check_threshold * (health_check_interval + health_check_timeout)))
↓ここかな~という推測。
[8:33]
while [ "$instance_state" != "$state_name" ]; do
if [ $count -ge $WAITER_ATTEMPTS ]; then
local timeout=$(($WAITER_ATTEMPTS * $WAITER_INTERVAL))
msg "Instance failed to reach state, $state_name within $timeout seconds"
return 1
fi
↓そこじゃなかったな、という訂正
[8:35]
いや、 `timeout` の設定はたぶんこっちだ。
elif [ "$state_name" == "OutOfService" ]; then
# If connection draining is enabled, wait for connections to drain
local draining_values=$($AWS_CLI elb describe-load-balancer-attributes \
--load-balancer-name $elb \
--query \'LoadBalancerAttributes.ConnectionDraining.[Enabled,Timeout]\' \
--output text)
local draining_enabled=$(echo $draining_values | awk '{print $1}')
local timeout=$(echo $draining_values | awk '{print $2}')
if [ "$draining_enabled" != "True" ]; then
timeout=0
fi
↓その検証
[8:36]
これだと、ELBのタイムアウトをそのまま `timeout` に入れていて、その後 `30` を足している。ELBのタイムアウト設定は5秒になっているから、 5+30=35で計算が合う。これだ。
こんな感じで、ログ→関連ソース→仮説→検証して原因を特定していきました。
このときはELBのパラメタをいじることでタイムアウトが回避できることがわかったためそれでしのぐことにしました。
得られるもの
- どうやってトラブルシュートすればいいかがわかる(ああ方法論・・・)
- ログを見て何が起きたかを調べる
- 関連ソースを見てどういう仕組みで処理が動いているのかを確認する
- トラブルの原因の仮説を立てる
- 仮説を検証して原因を特定していく
- プロダクションコードにはログを要所で残しておく必要があることがわかる
やってよかったと思います!