前提・実現したいこと
Webのサーバーサイド(APIなど)の開発を行っていて502エラーが発生するようになってしまいました。
現在複数台のサーバーでサービス動かしており
流れとしては以下のような感じです。
- ユーザーがUserのGETのリクエストを飛ばす
- UserサーバーがGET処理を実行
- UserサーバーのGET処理の中でInfoのGETを実行
- InfoサーバーでGET処理を実行
- InfoサーバーのGET処理の中でDataのGETを実行
- DataサーバーでGET処理を実行
- DataサーバーからInfoサーバーにレスポンスを返す
- Infoサーバーがレスポンスを受け取りデータを整形
- InfoサーバーからUserサーバーにレスポンスを返す
- Userサーバーがレスポンスを受け取りデータを整形
- Userサーバーがユーザーにレスポンスを返す
- ユーザーがレスポンスを受け取る
502エラーが発生する原因は
infoサーバー内ででているConnection refusedエラーが原因だと思っています。
これを解決する方法を探しています。
発生している問題・エラーメッセージ
userサーバーで起きているエラー
urllib.error.HTTPError: HTTP Error 502: Bad Gateway
infoサーバーで起きているエラー
「InfoサーバーのGET処理の中でDataのGETを実行する」
このタイミングでエラーが発生します。
urllib.error.URLError: <urlopen error [Errno 111] Connection refused>
設定周りの情報
gunicornの設定です。
関係ありそうな部分だけ抜粋しました。
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
worker_connections = 1000
max_requests = 0
timeout = 30
keepalive = 2
nginxの.confではアクセス数やレスポンスまでの時間制限などの設定は行っていないです。
試したこと
Connection refusedは必ず発生するわけではありませんでした。
頻度としては7回に1回ほどエラーが出ます。
発生しない場合は取得したかった内容が取得できています。
補足情報(言語/FW/ツール等のバージョンなど)
取得しているデータは文字列で半角英数字記号で500字ほど。
json形式で取得します。
成否問わずレスポンスは0.5秒かからず返ってきます。
環境情報
EC2インスタンス
Linux
nginx
Python3
gunicorn
RDSインスタンス
MySQL
APIの取得の確認は主にChromeで行っています。
追記、試したこと1
gunicornの設定の下記2つを変更しての動作を確認してみました。
変更したところインスタンスを作成してから
約1時間ほどで適当に百数回以上GETを試しましたが502エラーは発生しませんでした。
しかし、時間がたってからGETを行ったところ502が発生。
以降発生するのは変わりませんが頻度は少しだけ減ったように感じます。
(1/7から1/10くらいに変化)
timeout = 30 -> 3000
keepalive = 2 -> 20
追記
nginxのキャッシュが溜まっているとかそういう面について調べましたが
キャッシュは作られていませんでした。(設定もしてないです)
また現在サーバーの再起動や何やらは切ってある状態で試しているため
もし停止したら停止しっぱなしになります。
追記2
Dockerで同じような状況を作って試してみました。
DockerでMySQLサーバーを立てて、
CentOSのサーバーを立てて、
AWSのEC2インスタンスとRDSインスタンスと同じ内容を詰め込んで
Docker内でやり取りさせてみました。
そして、502エラーが発生するまで無限ループを行うようスクリプトを準備し
それを12個ほどcmdから実行しています。
現在30分ほど経ちましたが今のところ502エラーは発生していません。
AWSでも同じことをして試してみます。
追記3:メモリが怪しそうです
free コマンドでメモリの使用量を確認したところ以下のように表示されました。
また、GETを繰り返すたびにMem-usedの値が上がっていくのが確認できました。
Mem-cachedはすぐには上がりませんでしたがココらへんについてもう少し深く調査してみようと思います。
[ec2-user@ip-172-31-29-13 ~]$ free -m
total used free shared buffers cached
Mem: 995 871 123 0 32 682
-/+ buffers/cache: 157 838
Swap: 0 0 0
追記4:メモリに負荷をかけてリクエストの結果
stress コマンドでメモリに負荷をかけて
stress --vm 2 --vm-bytes 400M --timeout 10m
10のプロセスから各1000回づつ、10/sでリクエストの送信を行っている状態での
vmstatコマンドの結果です。
2017/01/26 11:05:25 procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
2017/01/26 11:05:25 r b swpd free buff cache si so bi bo in cs us sy id wa st
2017/01/26 11:05:25 2 0 0 291484 4024 18948 0 0 10 25 77 85 1 1 98 0 0
2017/01/26 11:05:26 2 0 0 266260 4032 18940 0 0 0 48 265 298 13 87 0 0 0
2017/01/26 11:05:27 2 0 0 261168 4032 18940 0 0 0 0 251 258 16 84 0 0 0
2017/01/26 11:05:28 3 0 0 179840 4044 20852 0 0 1940 0 1979 1332 32 68 0 0 0
...省略
メモリに負荷をかけていない状態ではcpu の id に余裕がありましたが、
負荷をかけた状態では上記のようになっています。
仕様について追記
nginxを起動後、gunicornを起動させて
その状態でずっと稼働しっぱなしにして動かしています。
そして、問題の502が起きるのは、しばらくアクセスを繰り返した後に発生し始めること
アクセスするたびにメモリが減っていく様子が確認できたことからそこら辺に原因があるかもしれないと思っています。
現在の状況
InfoサーバーやDataサーバーから取得するデータ量を減少させたて、
タイムアウトまでの時間を伸ばすことでほぼ502エラーが出ないことが確認できました。
一応以前は502エラーが発生していた時以上にはアクセスを繰り返してもエラーは発生しませんでした。
例としては 10プロセスから 10/s で 各1千回リクエストを飛ばすと行ったことをしましたが
エラーは発生せずに終えることができました。
また、UserサーバーからInfoサーバーにリクエストを飛ばした際に
正常な結果が得られるまでリクエストを飛ばすことで、無理やり押し通すことが出来るのも確認しました。
(これはコメントアウトした状態で検証しています。)
ただ、どちらも対処療法の領域をでておらず、
原因の特定に至っていないためまだ調査中と言った感じです。
ワーカープロセスについて
今回のことで初めて調べたのであっているかわかりませんがご了承ください。
nginx,gunicorn共にすべてのサーバーで同じ設定を行っています。
サーバーのスペックも同スペックで作成しています。
まずこちらのサイトを参考にCPUや最大プロセス数を調べたところ
http://www.crystalsnowman.com/?p=344
CPU : 1
最大プロセス数 : 1024
とわかりました。
また、nginxにはワーカープロセスについての設定を行って無い状態でした。
gunicornでは以下の設定は行っていました。
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
worker_connections = 1000
max_requests = 0
以下は動いているnginxとgunicornのプロセスを確認したものです。
[ec2-user@ip-xxx-xx-xx-xx ~]$ ps aux |grep nginx |grep -v grep
root 17576 0.0 0.1 58088 1080 ? Ss 11:58 0:00 nginx: master process nginx
ec2-user 17578 0.0 0.4 58600 4756 ? S 11:58 0:00 nginx: worker process
[ec2-user@ip-xxx-xx-xx-xx ~]$ ps aux |grep gunicorn |grep -v grep
ec2-user 17614 0.0 2.1 202156 22332 ? S 11:58 0:01 /home/ec2-user/.pyenv/versions/3.5.2/bin/python3.5 /home/ec2-user/.pyenv/versions/3.5.2/bin/gunicorn run:app --config guniconf.py
ec2-user 17646 0.4 2.5 223292 25996 ? S 11:58 0:44 /home/ec2-user/.pyenv/versions/3.5.2/bin/python3.5 /home/ec2-user/.pyenv/versions/3.5.2/bin/gunicorn run:app --config guniconf.py
ec2-user 17647 0.3 2.5 223296 26000 ? S 11:58 0:38 /home/ec2-user/.pyenv/versions/3.5.2/bin/python3.5 /home/ec2-user/.pyenv/versions/3.5.2/bin/gunicorn run:app --config guniconf.py
ec2-user 17648 0.4 2.5 223248 26004 ? S 11:58 0:41 /home/ec2-user/.pyenv/versions/3.5.2/bin/python3.5 /home/ec2-user/.pyenv/versions/3.5.2/bin/gunicorn run:app --config guniconf.py
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+3
Connection refused は、送信サーバ側が ICMP の port unreachable パケットを受信することによって起こります。誰がこのパケットを送ってくるかというと、相手側サーバか経路途中のファイアウォールです。原因の1位は「ポート番号が間違っている」で、2位は「ファイアウォールで拒否されている」なんですが、大量に負荷をかけているわけでもないのに 7回に1回しか発生しないという状況からこれらが原因ではないでしょう。
Dataサーバのサービスがたまに落ちているということはないでしょうか? systemctl や superviserd などで、落ちても自動的に再起動する仕組みを入れてたりすると、こういう現象になるかもです。
また、DataサーバのIPが他のサーバと重複しているとか、MACアドレスが重複しているとかで、別のサーバに飛んでいっているというようなことはないでしょうか?(とも思いましたが、 EC2 だとそうなりにくいですね)
Linux なので、 tcpdump でパケットキャプチャすることで、ICMPを誰が送ってきてるかを探ることはできます。他のサイトから例をコピペしますが、
$ sudo tcpdump -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp14s0, link-type EN10MB (Ethernet), capture size 262144 bytes 13:03:24.149897 IP 192.0.2.1 > 192.0.2.2: ICMP 192.0.2.1 tcp port 22222 unreachable, length 68
というように出力されれば、 ICMP の拒否パケットを送信しているのは、 192.0.2.1 だというのがわかります。これがサーバだったり、ファイアウォールだったりします。
この tcpdump を Data サーバ側と info サーバ側の両方で採取してみて、一致するならデータサーバ側のサービスが落ちているか攻撃防御機能が働いているかが原因でしょう。一致しないなら、DataサーバのIPが他のサーバと重複しているとか、MACアドレスが重複しているとかで、別のサーバに飛んでいっている可能性があります。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.13%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
質問への追記・修正、ベストアンサー選択の依頼
t_obara
2017/01/16 17:02
同時アクセスが許容範囲を超えているということでもないのでしょうか?
suvera
2017/01/16 17:27
設定周りの情報を追記しました。付け足したり増減してためした方がいい設定があれば教えてください。