質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Twitter

Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Amazon EC2

Amazon EC2は“Amazon Elastic Compute Cloud”の略称です。Amazon Web Services(AWS)の一部であり、仮想化されたWebサーバーのコンピュータリソースをレンタルできるサービスです。

AWS(Amazon Web Services)

Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

Q&A

解決済

1回答

2251閲覧

PHP アプリが Amazon Linux 2 でメモリリーク?

lin.ming

総合スコア50

Twitter

Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Amazon EC2

Amazon EC2は“Amazon Elastic Compute Cloud”の略称です。Amazon Web Services(AWS)の一部であり、仮想化されたWebサーバーのコンピュータリソースをレンタルできるサービスです。

AWS(Amazon Web Services)

Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

0グッド

1クリップ

投稿2020/05/28 07:13

編集2020/05/29 02:01

自作した PHP アプリが Amazon Linux 2 でメモリリークしているようです。

php

1<?php 2require_once 'TwistOAuth.phar'; 3(中略) 4$connection = new TwistOAuth($consumer_key, $consumer_secret, $access_token, $access_token_secret); 5$count = 3; 6$max_length = 25; 7 8function get_tweet($screen_name){ 9 global $connection, $count, $max_length; 10 $ret = ""; 11 12 try{ 13 $user = $connection->get('statuses/user_timeline', 14 array('screen_name' => $screen_name, 15 'count' => $count,) 16 ); 17 }catch (TwistException $e){ 18 (中略) 19 unset($user); 20 unset($connection); 21 return $ret; 22 } 23 24# 追記1 25# ここに die(); を入れてみた 26 27 if(!($user[0] && $user[0]->user)){ 28 unset($user); 29 unset($connection); 30 return $ret; 31 } 32 33 $screen_name = $user[0]->user->screen_name; 34 $ret .= $screen_name; 35 foreach($user as $value){ 36 $text = mb_substr($value->text, 0, $max_length, 'UTF-8'); 37 $ret .= $text; 38 } 39 unset($user); 40 unset($connection); 41 42 return $ret; 43} 44 45print(get_tweet("linming")); 46?>

これを cron で 5 分ごとに起動しています。

php ./myphpapp/tweetapp.php > ./www/tweetapp/tweet.html

実行し始めてから 1~2 時間程度経つと、cron のエラーメッセージで Segmentation Fault が出ます。
(一日ぐらい放っておくとウェブサイトにアクセスできなくなります。)

CloudWatch Agent を入れてメモリ使用量を確認すると、一回の実行で 50MB ぐらいメモリリークしているようです。
作ったオブジェクトは解放しているつもりですが、どこでメモリリークしているのでしょうか?

PHP のバージョンは 7.1.33、Apache のバージョンは 2.4.34 です。
EC2 のインスタンスは t2.micro です。

不思議なのは、このプログラムを別のクラウド環境(GMO の ALTUS)でも動かしているのですが、半年以上 Apache や OS のリスタートをしなくても正常に動作しています。

ご知見のある方、どうぞよろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

コマンドラインからの実行なのでApacheは関係無いですが、可能性としては、

  1. CPUクレジット/EBSクレジット/ネットワーククレジットを使い果たす
  2. 処理に時間がかかるようになり、5分で終了しなくなる
  3. 積み重なって余計に処理が重くなっていく

という感じがありがちな原因かなと思います。

重くなっている状態でpsコマンドなどでプロセスの状態を確認する、マネジメントコンソールからCPUクレジットを確認するなどして状況を確認してみてください。

AWSのバーストインスタンスは各クレジットが無いときの処理速度は極めて遅くなるので、定期的に重い処理をするような運用は向いていません。

回避方法としては、
処理の最初で、既に同じ処理が起動している場合は処理を行わずに終了する
ようなチェックを追加するのが良くある対応方法です。

投稿2020/05/28 07:25

tanat

総合スコア18713

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

lin.ming

2020/05/28 07:54 編集

ご回答ありがとうございます。 CPU クレジット使用状況は 0.15~0.20 で推移しています。 CPU クレジット残高は 50 前後です。 EBS のバーストバランスは標準のディスクは 50%、追加の EBS は 100% です。 $user の取得失敗時に送るエラーログを見ると、1回の実行時間は 2 分以内で、積み重なっているようには感じません。
tanat

2020/05/28 07:57

psコマンドやfreeコマンドでOS内で詳細な状況を確認してみてください。
lin.ming

2020/05/28 08:22

ps コマンドを実行したのですが、VSZ, RSS を多く消費しているプロセスはサーバの実行に必要なものっぽくて、自分が起動したであろうプロセスやゾンビプロセスはありませんでした。 自分の crontab に書いたので、PS コマンドで USER は linming になりますよね?
tanat

2020/05/28 08:53

> 自分の crontab に書いたので、PS コマンドで USER は linming になりますよね? それはその通りですね。。 例えば sudo ps aux だとどうでしょうか? > 作ったオブジェクトは解放しているつもりですが、どこでメモリリークしているのでしょうか? プロセスが終了しているのであれば、PHP以外の問題でメモリが足りなくなっているというのはあり得ると思います。 最終的には 問題が発生している状態でfreeやtopで一つづつ確認していくしかない類のものかと思います。
tanat

2020/05/28 09:02

例えば、 cronでの実行を止めた状態でのメモリ遷移を確認する 実行間隔を1時間等に変更して状態を確認する あたりの確認は発生源の切り分けに必須かなと思います
lin.ming

2020/05/28 09:03

ps aux でユーザが linming なプロセスは 2 つあって、2 つとも「-bash」で、VSZ が 120KB ぐらいのものでした。
lin.ming

2020/05/28 09:08

cron の実行を止めるとメモリは消費しません。 実行間隔を 1 時間にするのは今からやります。
tanat

2020/05/28 09:10

crontabの設定が正しいかどうか(質問からは)わからないので、sudoで実施して全ユーザーから該当しそうなものが無いか確認した方が良いですね。 例えば、 sudo ps aux|grep php だとどうですか?
tanat

2020/05/28 09:18

あとは、 ターミナルを複数立ち上げて、 手動で該当PHPを実行してみてプロセスやメモリの状況を把握するとかですかね。。。 完全に該当PHPが原因だと特定出来たら、途中で無理やりdie()させてみて、問題が起きている箇所を特定するのが早いと思います。 また、t2.microだと、外れハードに当たってしまって不良が発生することもあるので インスタンスを一度完全に終了してから再起動すると問題が発生しなくなったりすることもあります。 (今回のケースで該当するかわかりませんが)
lin.ming

2020/05/29 00:49

実行間隔を 1 時間に 1 回にしたのですが、実行するたびにメモリ使用量が 50MB ずつ増えていきます。
tanat

2020/05/29 00:52

そうなのですね。 であれば、 > 完全に該当PHPが原因だと特定出来たら、途中で無理やりdie()させてみて、問題が起きている箇所を特定するのが早いと思います。 みたいな方法で問題箇所を特定するしか無いですね。
lin.ming

2020/05/29 00:58

$ ps aux | grep php root 3076 0.0 0.0 333356 96 ? Ss May28 0:03 php-fpm: master process (/etc/php-fpm.conf) apache 3110 0.0 0.0 333356 8 ? S May28 0:00 php-fpm: pool www apache 3111 0.0 0.0 333356 8 ? S May28 0:00 php-fpm: pool www apache 3112 0.0 0.0 333356 8 ? S May28 0:00 php-fpm: pool www apache 3113 0.0 0.0 333356 8 ? S May28 0:00 php-fpm: pool www apache 3114 0.0 0.0 333356 8 ? S May28 0:00 php-fpm: pool www ec2-user 14791 0.0 0.0 119420 944 pts/0 SS+ 09:51 0:00 grep --color=auto php
lin.ming

2020/05/29 02:02

質問文の PHP コードに追記しました。 $user をいじりだす前に die() したらメモリリークはしていません。
tanat

2020/05/29 02:31

であれば、その後に1行単位でdie()をずらしてみて、問題となる行を完全に特定しましょう。 問題になる行が完全にわかったら、その行で使われているオブジェクトやメソッドのコードを読んだりダンプして、問題になりそうなところを探すという感じで一つづつ進めるしか無いです。 こことは関係ないかもしれませんが、 global $connection, $count, $max_length; は、 globalでは無く、引数で渡すか関数内で定義するか、クラスにしてクラス内で処理する方がメモリ管理的には確実ですね。 中略部分はおそらくシークレットキーなどかと思いますが、その辺りを全部関数のスコープ内に入れてしまえば、globalもunsetも必要なくなります。
tanat

2020/05/29 02:44

他に気になるところだと、PHPのバージョンが既にサポートが切れているバージョンなので、可能な限り最新にアップデートすると治るかもしれません(PHP自体のバグを踏んでしまっている可能性も無いとは言い切れないので)
lin.ming

2020/05/29 04:00

実際に修正したコードを試していない発言で申し訳ないのですが、 $connection = new TwistOAuth($consumer_key, $consumer_secret, $access_token, $access_token_secret); $count = 3; $max_length = 25; の件、 最終行で print(get_tweet("linming")); で get_tweet() 読んでますが、実際に実行しているコードでは、数回 get_tweet() を呼んでいます。 get_tweet() 内で $connection を毎回張りなおすのはコストがかかると思って最初に一回 $connection を張っているのですが、悪手でしょうか?
tanat

2020/05/29 04:27

> get_tweet() 内で $connection を毎回張りなおすのはコストがかかると思って最初に一回 $connection を張っているのですが、悪手でしょうか? このケースで実際にどの程度コストがかかるかは知らないですが、渡すにしてもglobalでは無く引数で渡す方が良いです。(PHP オブジェクト 引数 参照 パフォーマンスあたりで検索してみると、仕組みがわかると思います。通常のアプリケーションでglobalが役に立つことは基本的には存在しない くらいの考えで良いです) また、 > で get_tweet() 読んでますが、実際に実行しているコードでは、数回 get_tweet() を呼んでいます。 こういう挙動をしているのであれば、読んでいる数を減らして状況を比較するとか、呼び出し回数のそれぞれの回数の時に判定して殺してみるとかそういう工夫も必要ですね。 また https://github.com/mpyw-junks/TwistOAuth は既に非推奨になっているので、苦労して使い続けるよりは https://github.com/mpyw/cowitter で該当部分を作り直してしまった方が楽かもしれません。
lin.ming

2020/05/29 07:46

die() する位置を少しずつ後ろにずらしたのですが、現時点で最後まで die() しない(=今までのプログラム)でもメモリリークしなくなりました。 関係しそうなところは、今日の昼にメモリ使用量が 90% を超えていたので、EC2 を再起動しました。 しかし、再起動は今まで何回もやってますし、その都度メモリリークしていました。 今回の現象は謎です。
tanat

2020/05/29 08:43

うーん、謎ですね。 前述の > また、t2.microだと、外れハードに当たってしまって不良が発生することもあるので > インスタンスを一度完全に終了してから再起動すると問題が発生しなくなったりすることもあります。 のケースかもしれませんね。 再起動一回で別のハードウェアに移るとは限らないので、今回の再起動でたまたま別のハードウェアにインスタンスが移動したとか。
lin.ming

2020/05/29 09:04

そうですね。 ただ、終了→起動でなく、再起動を選んだだけなのですが、これで別のハードウェアに移動したのかも謎ですね。 とりあえずこれで 1, 2 日様子をみます。 メモリリークしていなかったら、ベストアンサーに選びますので、よろしくお願いします。
lin.ming

2020/05/29 09:38

余談ですが、GMO のクラウドで半年以上再起動なしで動いているのですから、プログラム的には作法にのっとっている気がします。 詳しい友人に聞いたところ、訳がわからないことを言われました。 「仮想化のメモリハンドリングは GMO は KVM で、EC2 は独自実装だから。」みたいなことを言われました。私には訳が分からないです。
tanat

2020/05/29 10:37

t2.microは無料枠なこともあって、色々と不思議なことが起きるものではあるので、原因がわからない時は分からないです。 GMOクラウドとの比較も、OSやミドルウェアなどのバージョンに違いがあれば比較としてはあんまり意味が無いです。 作法の話で言うと、コマンドラインで叩いてプロセスが終了した時点でどんなプログラムでも(非同期で何かしてない限りは)問題無いんですよね。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問