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

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

今回はインフラをある程度経験していれば、なんとなく当たりがつくような、サイトにアクセス出来ない時のパターンをいくつか紹介します。
パターンを知っているだけで調査のあたりの付け方が早くなるので、いつかきっと役に立つ日がくるでしょう。

インフラ: AWS
Webアプリケーション: Rails
Webサーバ: Nginx
アプリケーションサーバ: Unicorn or Puma

# 調査のキモ

まず最初に調査のキモについて。
ここだけ押さえておけば大体なんとかなる、きっと。

## ログを見ること
エラーログはシステムのダイイングメッセージ。
ログに全ての答えがあります。:sunglasses:
まずはWebサーバ、アプリケーション、各ミドルウェアのログを良く見ましょう。
どこまでは正常に動作して、どこでエラーが発生したのかの原因を切り分けやすくなります。

## HTTPステータスを見ること

<img width="547" alt="スクリーンショット 2018-11-30 16.56.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/6808/2fe3c2c7-94f3-dbee-7a1b-c2928cc8d116.png">

chromeのdevtoolなどを使って、どんなHTTPステータスが帰ってきているのか確認しましょう。
ここにも重要な情報が隠れています。 :eyes:
アクセスできないという場合のHTTPステータスは404なのか、504なのかによって、対応が変わってきます。


## 単純化すること

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

を使うことで問題がどこにあるのかの切り分けがやりやすくなります。
他にも色々あるかと思いますが、アプリケーションを介さずに、実行できる処理は原因の切り分けをやりやすくなリますね。
アクセス元のIPが重要な場合もあるので、どこでコマンド実行をするかも大事。:point_up:

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

<img width="724" alt="hoge.png" src="https://qiita-image-store.s3.amazonaws.com/0/6808/9e6e1fd6-0a00-7e82-ffc3-ba5b9890c2a7.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
<img width="486" alt="スクリーンショット 2018-11-29 18.47.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/6808/41f4b27c-0516-aee7-2689-2db25a4d304a.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 > ロードバランサー > 属性 > 属性の編集`
から変更ができます。

<img width="942" alt="スクリーンショット 2018-11-29 18.50.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/6808/9d1f7d2d-8269-394b-4d2f-a4b7c87534fd.png">



### 設定しているリバースプロキシ先にアクセスできない
Nginxでリバースプロキシ設定をして、他のサーバの処理を委ねることができます。
このリバプロ先のサーバが上であげたような現象でアクセスできなければ、結果サイトの表示ができないです。

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


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

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

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

```conf

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の名前解決についてまとめ](https://ktrysmt.github.io/blog/name-specification-of-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インスタンスを選択すれば見れます。

<img width="925" alt="スクリーンショット 2018-12-01 18.40.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/6808/66a888db-28ae-da7a-bfd9-b2ad69357ce2.png">

ここでCPU使用率が高くなってしまっていればDBに負荷がかかっています。
あとは実際に問題となっているであろう、実行されているクエリを確認して見ます。

```
show full processlist
```

実行にめっちゃ時間かかっていそうなのがあればkill
なんかいっぱいクエリがある場合は、こちらをどうぞ
[MySQLで処理に長時間かかっている複数クエリをまとめて殺す方法](https://tech.basicinc.jp/articles/41)

### 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% /
```

# 最後に

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

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

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

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

この辺のインフラ障害に対しての取り組み方は
[システム障害と僕達はいかにして戦えば良いのか、障害対応について考えた](https://qiita.com/zaru/items/edb49d8d2a2a6981789a)
こちらにまとまっているので、是非読んで見てください。