Edited at

インフラ苦手な人が知っておくといい、Webサイトにつながらない障害パターンと解決方法

みなさん、障害対応してますか?

今回はインフラをある程度経験していれば、なんとなく当たりがつくような、サイトにアクセス出来ない時のパターンをいくつか紹介します。

パターンを知っているだけで調査のあたりの付け方が早くなるので、いつかきっと役に立つ日がくるでしょう。

インフラ: AWS

Webアプリケーション: Rails

Webサーバ: Nginx

アプリケーションサーバ: Unicorn or Puma


調査のキモ

まず最初に調査のキモについて。

ここだけ押さえておけば大体なんとかなる、きっと。


ログを見ること

エラーログはシステムのダイイングメッセージ。

ログに全ての答えがあります。:sunglasses:

まずはWebサーバ、アプリケーション、各ミドルウェアのログを良く見ましょう。

どこまでは正常に動作して、どこでエラーが発生したのかの原因を切り分けやすくなります。


HTTPステータスを見ること

スクリーンショット 2018-11-30 16.56.36.png

chromeのdevtoolなどを使って、どんなHTTPステータスが帰ってきているのか確認しましょう。

ここにも重要な情報が隠れています。 :eyes:

アクセスできないという場合のHTTPステータスは404なのか、504なのかによって、対応が変わってきます。


単純化すること

httpアクセスするのであれば、curlコマンド

DBアクセスするのであれば、mysql-client

を使うことで問題がどこにあるのかの切り分けがやりやすくなります。

他にも色々あるかと思いますが、アプリケーションを介さずに、実行できる処理は原因の切り分けをやりやすくなリますね。

アクセス元のIPが重要な場合もあるので、どこでコマンド実行をするかも大事。:point_up:


Webサイトに繋がらないパターンについて

hoge.png

ここからいろんなパターンを紹介していきたいと思います。


  • よくあるやつ

  • 501 timeout、応答が長すぎます...

  • 502 bad gateway

  • 503

  • その他エラー


よくあるパターン


Webサーバ(Nginx、Apache等)が起動していない

起動しましょう。

起動しているかどうかの確認は service nginx status とかで確認するとたまにPIDファイルだけ生き残ってプロセスが死んだ場合に嘘つくのでpsコマンドでちゃんとプロセスが起動しているかを確認する方が確実です。

ps aux | grep nginx

意図せず落ちてしまっていた場合は、Webサーバのログ、もしくは /var/log/messages などのシステムログを見ると落ちた原因がわかるかもしれません。


セキュリティグループで許可されていない

AWSを使う場合、ポートごとのアクセス制限にセキュリティグループを使うことになると思いますが、セキュリティグループの設定をしくじってアクセスできない、っていうこともよくあります。

80と443は解放されていたけど、DBで使うポートやredisで使うポートなどもあるので、アプリケーションのログでどこに接続できていないエラーをチェックしてセキュリティグループの設定を見直しましょう。

よくわからないから、とりあえず全解放とかダメですよ :no_good:


ACLで許可されていない

AWS ACL(Access Control List)をいじることはあまりないかと思うのですが、ここで設定をミスっているとハマります。

VPC > セキュリティ > ネットワークACL から設定できます

セキュリティグループと同じように、ポート番号とソースIPを指定してアクセス許可をコントロールできるので、セキュリティポリシーに合わせて適切に設定しておきましょう。


504 Gateway Timeout

スクリーンショット 2018-11-29 18.47.38.png

timeoutで接続できないこともあります。

ブラウザ側で応答長すぎと怒られることもあるかと思いますが、ここで紹介するエラーは、サーバには一応到達しているパターンです。


Webサーバに設定されているtimeout時間を超えてしまっている

Nginx側で設定しているtimeoutの時間を超えてしまった場合はtimeoutエラーの画面になります。

この場合はどこかに重たい処理が潜んでいるので、それをどうにかしなければならないのですが、これはインフラの話から少々外れてしまうのでここでは特に追求しません。

とりあえずお茶を濁すのであればtimeout時間を伸ばすことで解決できます。 :rolling_eyes:

Nginxで設定できるtimeoutの項目いっぱいあるのですが、Nginx + Unicornの構成であれば

proxy_read_timeout に設定は注意しておきましょう。

参考: https://qiita.com/syou007/items/3e2d410bbe65a364b603


アプリケーションサーバのtimeout

UnicornとかPumaで設定するtimeout時間です

アプリケーションサーバでエラーになるとNginx側でエラーログが出力されます。

エラーメッセージ

2018/11/29 18:35:34 [error] 9294#0: *147 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 172.30.0.119, server: xxxx.xxxx.xxxx, request: "GET / HTTP/1.1", upstream: "http://unix:///var/www/xxxx.xxxx.xxxx/current/tmp/sockets/puma.sock/", host: "172.30.0.233"

timeout時間の設定方法

Unicorn


config/unicorn.rb

timeout 30


Puma


config/puma.rb

worker_timeout 30



ロードバランサーのtimeout

ロードバランサーにもtimeout設定があります。

デフォルト60秒なので、これをこえる設定をNginxやUnicornでしてもロードバランサー側も変えておかないと意味ないですね。

まあ、60秒超える時点でどうなんだっていうのはありますが...

ec2 > ロードバランサー > 属性 > 属性の編集

から変更ができます。

スクリーンショット 2018-11-29 18.50.31.png


設定しているリバースプロキシ先にアクセスできない

Nginxでリバースプロキシ設定をして、他のサーバの処理を委ねることができます。

このリバプロ先のサーバが上であげたような現象でアクセスできなければ、結果サイトの表示ができないです。

リバプロをしているかどうかは、ログなどからはパッと見でわかりずらいのでハマりやすい。:frowning2:

リバースプロキシ設定の例

/etc/nginx/conf.d/proxy.conf

/example にアクセスしたら、https://proxy.example.com にリクエストします


location ~ ^/example {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

proxy_pass https://proxy.example.com;
}


Nginxの名前解決に注意

プロキシ設定をするときにNginxは最初に起動して名前解決をしたものをずっと保持し続けるため、サーバのIPが変わったりしてしまった場合にエラーになってしまうことがあります。

これはハマります。

参考: Nginxの名前解決についてまとめ


502 bad gateway

アプリケーションサーバ側でエラーになると、このエラーが出たりします :ghost:

2018/11/30 10:19:39 [error] 3241#0: *1592 connect() to unix:///var/www/xxx.xxx.xxx.xxx/current/tmp/sockets/puma.sock failed (111: Connection refused) while connecting to upstream, client: xxx.xx.x.xx, server: xxx.xxx.xxx.xxx, request: "GET / HTTP/1.1", upstream: "http://unix:///var/www/xxx.xxx.xxx.xxx/current/tmp/sockets/puma.sock:/", host: "xxx.x.x.xx"


アプリケーションサーバのプロセスが起動していない(unicornなど)

起動しましょう。

これもpsコマンドで起動しているかどうかをちゃんと見ます

ps aux | grep unicorn

こちらもWebサーバ(Nginxなど)と同じで、意図せず落ちてしまっていた場合は、Unicornのログ、もしくは /var/log/messages などのシステムログを見ると落ちた原因がわかるかもしれません。

メモリ不足で落ちることなんかもあります。


ロードバランサーに紐づいているインスタンスが間違っている

たまにボケて全然関係ないロードバランサーにインスタンスを紐づけてしまうことがあります。

ロードバランサーの名前とかを適当につけているとよくあります。

ちゃんと合っているか確認しておきましょう。


Classic Load Balancerでヘルスチェックで失敗している

これから新しくロードバランサーを立てるのであればALBを基本使うと思うので、もうあまりこの現象になることはなさそうですが

昔のロードバランサーはヘルスチェックがちゃんと通っていないインスタンスにはアクセスができないようになっていました。

じゃALBはどうなっているんだというと


正常なターゲットが含まれているアベイラビリティーゾーンがない場合、ロードバランサーノードはすべてのターゲットにリクエストをルーティングします。

https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/target-group-health-checks.html


なので、ヘルスチェックがエラーになっていてもアクセスはできます。

とはいえ、ちゃんとヘルスチェックは設定しておきましょう。


503エラー


Webサーバの接続数が上限を超えている

Apache: max_clients

Nginx: worker_connections

Webサーバの設定接続数を超えてアクセスがあると503エラーになります。

この場合はちゃんとしたアクセスか、そうでないアクセス(botとか)かで対応方法が変わります。

アクセスログを見てIPアドレスが、妙に国外からのアクセスが多いとかであればbotの可能性が高いです。


ちゃんとしたアクセス

これが増えているのであれば、Webサーバの設定値をあげるか、サーバ台数を増やしましょう。


botとか

不要なアクセスであれば、ブロックしたいです

できればWAFなどを使ってブロックするのが好ましいでしょう。

お手軽にやるのであればセキュリティグループで該当のIPアドレスをブロックするのが早いです。

WAFの機能紹介はこちらを見てみてください

https://tech.basicinc.jp/articles/144

間違ってGoogleのクローラをブロックすると検索結果に表示されなくなってしまうので注意


サーバ設定間違っているパターン


VirtualHostの設定が間違っている

NginxやApacheで VirtualHostの設定をするところがあるのですが、そこをミスっている可能性あります。


/etc/nginx/nginx.conf

server {

listen 80;
server_name example.com; ここ!
}


DNSの向き先が合っていない

そんな馬鹿なことあるか、っていう感じですが...

ここを間違っていると他で色々調べていることが全部無駄になっちゃいます。

サーバのアクセスログ等を見てアクセスが来てなかったら、ここも疑いましょう。

DNSがどこで設定されているかよくわからない?

そんな時は whois コマンドを使って確認することができます

whois <ドメイン名>

すると Name Serverの欄がきっとあると思います。

そこに書かれているサーバがDNSサーバです。

Name Server: xxxxxxxxxxxxxxxxxx

Name Server: xxxxxxxxxxxxxxxxxx
Name Server: xxxxxxxxxxxxxxxxxx
Name Server: xxxxxxxxxxxxxxxxxx

xx-xx.AWSDNS-xx.xxx

というのがあれば AWS Route53 を使ってます。


DBアクセスできないパターン

DBサーバの状況によって対応方法が分かれます。

DBサーバの状況を確認するにはクライアントソフトを使って、アプリケーションを介さずに接続の確認をした方が原因を切り分けやすいです。

MySQLであれば mysql-client を直接コマンド叩いてつなげて確認してみよう、ということですね。

とりあえず以下はMySQLの話です


そもそもリモート接続できるようになっていない

mysql-client で接続できない状態ですね。


  • ポートが開放されていること(セキュリティグループ)

  • リモート接続ユーザーが登録されていること

  • bind-address が設定されていること

この3つをチェック

リモート接続できるユーザの確認

mysql> select user, host, password from mysql.user;

+------------------+-----------------+-------------------------------------------+
| user | host | password |
+------------------+-----------------+-------------------------------------------+
| root | localhost | |
| root | ip-xxx-xx-x-xxx | |
| root | 127.0.0.1 | |
| root | ::1 | |
| | localhost | |
| | ip-xxx-xx-x-xxx | |
| db-user-name | xxx.xx.x.% | xxxxxx |
+------------------+-----------------+-------------------------------------------+
7 rows in set (0.00 sec)

登録されていければgrantコマンドで登録

GRANT ALL PRIVILEGES ON *.* TO mydb-user@"192.168.%" IDENTIFIED BY 'password' WITH GRANT OPTION;

bind-address の確認

設定ファイルのbind-addressで確認できます。


/etc/my.cnf

bind-address 192.168.0.1


やり方は調べるといくらでも出てくるかと思いますが、参考記事をどうぞ

https://support.plesk.com/hc/ja/articles/213904365-MySQL-%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%82%B5%E3%83%BC%E3%83%90%E3%81%B8%E3%81%AE%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%82%92%E6%9C%89%E5%8A%B9%E5%8C%96%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF


DBサーバの負荷が高く接続できない

サーバのリソース状況(CPU、メモリ)を確認して見ましょう。

RDSであれば該当のRDSインスタンスを選択すれば見れます。

スクリーンショット 2018-12-01 18.40.40.png

ここでCPU使用率が高くなってしまっていればDBに負荷がかかっています。

あとは実際に問題となっているであろう、実行されているクエリを確認して見ます。

show full processlist

実行にめっちゃ時間かかっていそうなのがあればkill

なんかいっぱいクエリがある場合は、こちらをどうぞ

MySQLで処理に長時間かかっている複数クエリをまとめて殺す方法


DBの最大接続数を超えている

Webサーバと一緒でDBも最大接続数を持ってます。

最大接続数を確認

mysql> SHOW GLOBAL VARIABLES like 'max_connections';

+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 151 |
+-----------------+-------+

今の接続数

mysql> SHOW STATUS like 'Threads_connected';

+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_connected | 4 |
+-------------------+-------+

変更


/etc/my.cnf

[mysqld]

max_connections=300

他には単純にDB接続数を減らすために、アプリ側でキャッシュを作成するなどしてDBアクセスを減らしていきましょう。


テーブルロックがかかっている

バッチ処理などでうっかりやってしまう可能性があるのですが、トランザクションをかけてテーブルを更新すると、commitするまでロックがかかります。

そうするとロックがかかったテーブルを別で更新しようとすると、ロック待ちでタイムアウトしちゃいます

mysql> begin;

mysql> update sample set updated_at=CURRENT_TIMESTAMP where id=1

こうするとほかからsampleテーブルを更新できなくなってしまう。:skull:


外部サービスからデータが取得できずエラー

APIなどを使って外部からデータを取得して、それを表示するようなWebサイトの場合、APIで正しくデータが取れない場合にエラーになることもあります(アプリケーションの作りによります)

サーバのリソース状況を見ていても気づきにくいタイプのエラーなので、アプリケーションのログをよく見る必要がありますね。


その他のサイト表示されないパターンについて

その他って言っても幅広いのですが、よくあるやつの紹介です。


複数サーバのうち調子が悪いサーバがある

何回かリロードした時に表示できたり、表示されなかったりする場合はアクセスするサーバによって違う可能性があります。


ディスク容量が100%

ディスク容量が100%の場合の挙動がどうなるのかはなんとも言えないのですが、バッチが動かなかったり、Webサーバ再起動したら起動に失敗したり、ファイルのダウンロードができなかったりとか...いろいろです。

確認方法

[root@ip-172-30-0-233 log]# df -h

ファイルシス サイズ 使用 残り 使用% マウント位置
devtmpfs 2.0G 56K 2.0G 1% /dev
tmpfs 2.0G 0 2.0G 0% /dev/shm
/dev/xvda1 12G 6.3G 5.4G 54% /


inodeが枯渇

キャッシュを大量につくっていると起こる可能性があります。

こうなると容量が空いていてもファイルが何もつくれなくなってしまいます。

余計なファイルを消しましょう。

確認方法

[root@ip-172-30-0-233 log]# df -i

ファイルシス Iノード I使用 I残り I使用% マウント位置
devtmpfs 503720 438 503282 1% /dev
tmpfs 505961 1 505960 1% /dev/shm
/dev/xvda1 786432 504596 281836 65% /


最後に

なんでもそうなのですが、インフラも経験をどれだけするかが大きいです。

障害が発生した場合はどうしても最速で対応をする必要があるので、わかっている人、得意な人が対応をしてしまうので、苦手意識がある人はどうしても一歩引いて見てしまいがちです。

ただシステム障害は(ちょっと言い方あれかもしれませんが...)インフラについて学べる最高の機会です!是非積極的に関与をしていきましょう。

あとは障害対応するときは、できる限りチャットなどで実況中継をするようにして他の人が何をやっているのか分かるようにしておくと他の人が関わりやすくなるのでおススメです。

この辺のインフラ障害に対しての取り組み方は

システム障害と僕達はいかにして戦えば良いのか、障害対応について考えた

こちらにまとまっているので、是非読んで見てください。