IT・技術研修ならCTC教育サービス

サイト内検索 企業情報 サイトマップ

研修コース検索

コラム

クラウド時代のサーバー運用入門

CTC 教育サービス

 [IT研修]注目キーワード   OpenStack  OpenFlow/SDN  情報セキュリティ  Python  システムトラブルシュート 

第5回 クラウド時代の障害対応術 (4) WEBサイトが落ちた? (濱田康貴) 2018年2月

本コラム第1回目、「第1回 クラウド時代の障害対応術 (1) コンピューターの部品をイメージしよう」では、「厳選!障害対応お役立ちツール」として、以下のコマンドの使用頻度が高いと申し上げました。

curl df dig du fuser ip last lsof nc ping ps ss telnet top vmstat w whois

単にコマンドのオプションを知りたい場合は、各コマンドのmanページを読むか、manページの日本語訳を読みたい場合は、JM Project ( https://linuxjm.osdn.jp/ ) のサイトが役立つでしょう。しかし、コマンドのオプションを覚えることが目的では、障害発生時に結局何をしてよいのかわからなくなってしまいます。

今回は、実践編として、WEBサーバーの死活確認をテーマに、curlコマンドのほか、リモートホストの障害切り分けに役立つコマンドを紹介しますので、「そのコマンドを実行したらどうなるのか」「端末に表示された内容の意味は何なのか」を意識しながら体で覚えてください。

curlコマンドでWEBサーバーの死活状態を確認する

WEBサーバーの障害発生時、私が最初に実行するコマンドはcurlであることがほとんどです。curlコマンドのオプションをつけずに、引数にURLだけを与えると、WEBサーバーからコンテンツを取得できた場合、htmlのソースが端末上に表示されます。しかしそれでは、コマンドが正常終了した場合でも可読性があまりよろしくありません。そもそもサーバーの死活状態を確認したいのですから、curlコマンドのオプションを有効活用しましょう。WEBサーバーのトラブルシュートを行うにあたり、以下のオプションを付与して実行するとヘッダ情報のみを表示することができます。

$ curl -LIs http://example.com/
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Sat, 30 Sep 2017 16:46:47 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: https://example.com/

HTTP/2 200 server: nginx date: Sat, 30 Sep 2017 16:46:48 GMT content-type: text/html; charset=UTF-8 expires: Wed, 11 Jan 1984 05:00:00 GMT cache-control: no-cache, must-revalidate, max-age=0 x-b-cache: BYPASS link: ; rel="https://api.w.org/" link: ; rel=shortlink x-f-cache: BYPASS x-signature: KUSANAGI

curlコマンドにオプション L をつけることで、最初のURLからリダイレクトされる場合でも、そこで終了せずにリダイレクト先へ接続しようとしてくれます。
I オプションは、GETメソッドではなく、HEADメソッドで接続するために使います。こうすることで、コンテンツの内容がどうあれ、WEBサーバーの死活状態だけを表示することができます。最後の s オプションは、curlコマンドが表示する進捗を抑止するために使用します。

上記の例で表示されたうち、私がまず注目するのは http のステータスコードです。最初に「HTTP/1.1 301 Moved Permanently」と表示され、次に「HTTP/2 200」と表示されています。つまり、このサーバーはhttpで接続されても、httpsで同じサイトにリダイレクトし、リダイレクト先のステータスコードは200ですから、リクエストは成功し、レスポンスとともに要求に応じた情報が返されたということになります。つまり、上記の例ですと、WEBサーバーは正しく動作しているという一例になります。

では次に、要求したコンテンツが存在しない場合はどのようになるのでしょう。

$ curl -LIs http://example.com/HOGEHOGE
HTTP/1.1 301 Moved Permanently
Server: nginx
Date: Sat, 30 Sep 2017 16:58:23 GMT
Content-Type: text/html
Content-Length: 178
Connection: keep-alive
Location: https://example.com/HOGEHOGE

HTTP/2 404 server: nginx date: Sat, 30 Sep 2017 16:58:24 GMT content-type: text/html; charset=UTF-8 x-b-cache: BYPASS expires: Wed, 11 Jan 1984 05:00:00 GMT cache-control: no-store, no-cache, must-revalidate, max-age=0 link: ; rel="https://api.w.org/"

WEBサーバーにhttpで接続した場合、ディレクトリやファイル名が何であれhttpsの同じディレクトリやファイル名にリダイレクトしようとしています。ここまでは正常ですが、要求したコンテンツが存在しない場合、WEBサーバー(正確には、上記例の場合WordPress)が404 Not Foundを返しています。このように、NginxやApacheのような一般的なWEBサーバーは正常終了であれ異常終了であれ、WEBサーバーがリクエストを受け取り、ステータスコードをクライアントに返します。よって、「サイトが繋がらない」といった場合、ステータスコードを見れば「なぜ繋がらないのか」の手がかりを探すことができるのです。

httpのステータスコードについては、第2回目のコラムでもご紹介したように、RFCを確認し、その内容を理解しましょう。

参考URL : https://tools.ietf.org/html/rfc2616

WEBサーバーのステータスコードだけ確認したい場合は、次のオプションでcurlコマンドを実行します。

$ curl -LIs -o /dev/null -w '%{http_code}\n' http://example.com/
200

対象のWEBサーバーがステータスコードを返さない場合は、上記オプションでcurlコマンドを実行すると、「000」と表示されます。

$ curl -LIs -o /dev/null -w '%{http_code}\n' http://examplehogehogehogehogehoge.com/
000

上記の例では、わざと存在しないFQDNに対してhttp接続を試行してみました。当然、つながるサーバーがないのですから、ステータスコードなんてWEBサーバーから返されるはずもなく、curlコマンドは接続をあきらめ、000と返します。 しかしこれでは、「存在しないFQDNにアクセスしようとしたのか、あるいは名前解決ができないからWEBサーバーのステータスコードが取得できなかった」のか、「名前解決はできていても、本当にWEBサーバーが落ちていた」のかがわかりません。そこで、curlコマンド実行直後に、コマンドの終了コードを確認してみましょう。

$ echo $?
6

上記のように、ステータスコードが6と返ってきました。UNIX Linuxのコマンドは、終了コードが0の場合は正常終了、0以外の終了コードは異常終了です。

curlコマンドのmanページを見て、先ほど表示された終了コード 6が何を意味するのかを確認してみましょう。

(略)
EXIT CODES
       There  are a bunch of different error codes and their corresponding error messages that may appear during bad conditions.
       At the time of this writing, the exit codes are:

1 Unsupported protocol. This build of curl has no support for this protocol.
2 Failed to initialize.
3 URL malformed. The syntax was not correct.
4 A feature or option that was needed to perform the desired request was not enabled or was explicitly disabled at build-time. To make curl able to do this, you probably need another build of libcurl!
5 Couldn't resolve proxy. The given proxy host could not be resolved.
6 Couldn't resolve host. The given remote host was not resolved. (略)

manページにあるように、リモートホストの名前解決ができないのでcurlコマンドが異常終了したことを示しています。
本当にリモートホストの名前解決ができていなかったのか、については、digコマンドで確認することができます。

$ dig examplehogehogehogehogehoge.com

; <<>> DiG 9.10.5-P2-RedHat-9.10.5-2.P2.fc25 <<>> examplehogehogehogehogehoge.com ;; global options: +cmd ;; connection timed out; no servers could be reached
$ echo $? 9

digコマンドの終了ステータスも9を返している(0ではない)ので、digコマンドのmanページから引用しますと、

RETURN CODES
       Dig return codes are:

0: Everything went well, including things like NXDOMAIN
1: Usage error
8: Couldn't open batch file
9: No reply from server
10: Internal error

とあります。このことからDNSサーバーから応答がない、と読み取れますが、これは「.com」の権威DNSサーバーが「examplehogehogehogehogehoge.com」なんていうサブドメインの存在を知らない、つまり存在していないドメインである可能性が高いことを示しています。

そもそも「examplehogehogehogehogehoge.com」というドメインが本当に「存在していない」のか、whoisコマンドでも確認してみましょう。

$ whois examplehogehogehogehogehoge.com
[Querying whois.verisign-grs.com]
[whois.verisign-grs.com]
No match for domain "EXAMPLEHOGEHOGEHOGEHOGEHOGE.COM".
>>> Last update of whois database: 2017-10-01T01:05:07Z <<<
(略)

このように、「No match for domain "EXAMPLEHOGEHOGEHOGEHOGEHOGE.COM".」とwhoisサーバーからの応答がありましたので、このドメインはwhoisデータベースにない、すなわち存在しないドメインであるということがわかりました。

これはとある勉強会(AWSを利用したハンズオン)で実際にあった例ですが、(他人がすでに取得していたとは知らずに)とあるドメインのゾーンをネームサーバーへ設定し、WEBサーバーのAレコードを登録したにもかかわらず「WEBサーバーには繋がるようだが、期待する内容のコンテンツではない」というトラブルでアラートを上げた人がいて対処したことがあります。
確かにこの場合、curlで該当するFQDNへアクセスするとステータスコード200を返すのですが、digコマンドでAレコードを調べてみるとAWSが持っているIPアドレスではないように見え、同じくdigコマンドでIPアドレスから逆引き検索すると海外のホスティング業者が持っているIPアドレスが表示されました。
digコマンドの結果が事前想定とは異なったので、次にそのアラートを上げた方が勉強会開催日にRoute53へ設定したというドメインをwhoisコマンドで検索すると、ドメイン登録日が10年近く前であったことから、先に述べたように「他人がすでに取得していたとは知らずに、同じドメインでWEBサーバーを公開しようとして期待する内容のコンテンツが表示されなかった」という推論に至った、というわけです。その方には、

  • ドメインは階層構造である
  • 欲しいドメインは早い者勝ちで取得でき、インターネット上で唯一無二の存在である
  • たまたまそうとは知らずに同じドメインをRoute53へ設定してしまったが、先行して取得されたドメインがWEBサービスとして公開されていたために期待する内容のコンテンツとは異なる内容がブラウザに表示された

ということを説明して理解していただきました。

他にも「WEBサーバーに繋がらない」を示す例を挙げますと、例えば「ある一定秒数以内にレスポンスがない」を異常とみなす場合、curlの終了コードは28です。以下のように、curlコマンドのオプションでタイムアウトを1秒と指定して実行します。

$ curl --connect-timeout 1 -LIs -o /dev/null -w '%{http_code}\n' https://example.com/
000

$ echo $? 28

WEBサーバーが停止しているなどの理由で、つまり、名前解決ができているにもかかわらず、本当に「繋がらない」場合、curlの終了コードは7です。

$ curl -LIs -o /dev/null -w '%{http_code}\n' https://example.com/
000

$ echo $? 7

また、仮にWEBサーバーが生きていても、PHP-FPMなどのアプリケーションサーバーが応答しないなどの場合、WEBサーバーは502 (Bad Gateway)というステータスコードを返します。

$ curl -LIs -o /dev/null -w '%{http_code}\n' https://example.com/
502

$ echo $? 0

この場合、curlコマンドはWEBサーバーから何かしらのステータスコードを受け取ったので、curlコマンドとしては正常終了しています。しかし、WEBサーバーのステータスコードは502なのですから、サーバー内(あるいはWEBサーバーとアプリケーションサーバーとの間)で何かしらのエラーが発生しているので、対処が必要になります。

今回使用したコマンドの使い所まとめ
  • WEBサーバーが生きている場合は、何かしらのステータスコードが返ってくる。つまり、WEBサーバーのプロセスは生きている、ということである。
  • curlコマンドのオプションを変えることで、httpヘッダー情報だけを表示したりステータスコードのみを取得したりすることができる。また、タイムアウト値を設けることができる。
  • curlコマンドの終了コードを確認することで、エラーの原因が何であるか切り分けを行うことができる。
  • curlコマンドでWEBサーバーのステータスコードが取得できない、ということはクライアントとWEBサーバーとの間のどこかに障害原因がある可能性を疑う。
  • 名前解決ができないから繋がらないのではないか?という可能性に至ったらdigコマンドを使うことで切り分けを行うことができる。
  • そもそもこのドメインは存在しているのか?という可能性に至ったらwhoisコマンドを使うことでドメインの取得状況を切り分けることができる。

単に「WEBサーバーに繋がらない」という言葉ひとつを取っても、多種多様な原因があり、ときには「本来WEBサーバーは生きているにもかかわらず、(思い込みにより)期待通りの動作をしない」ために、WEBサーバーに繋がらない、即ち障害だ!とアラートを上げてくるケースも稀によくあるパターンです。ですので、(本コラムで何度も多用してしまっていますが・・・)「生きている」「死んでいる」「繋がらない」「応答しない」というふわっとした表現に惑わされず、事実は何なのか、その現象をより正確な言葉で捉えるにはどうすればよいか、を心がけましょう。そのためには、WEBサーバーのステータスコードとcurlコマンドの終了コードから原因を絞り込むことで原因究明までの試行を最短距離にすることが重要です。

今回はWEBサーバーの外から外形監視的にトラブルの切り分けを行うケースを紹介しましたが、次回はさらに踏み込んで、実際のWEBサーバー障害を実例として、トラブルシュートをしてみたいと思います。

 


 

 [IT研修]注目キーワード   OpenStack  OpenFlow/SDN  情報セキュリティ  Python  システムトラブルシュート