重ね合わされたデータが使われたオレンジとブルーの画面

PHP Batchを観測する

Webアプリケーションの計測はできるが、Batchをどうやって計測したら良いかとご相談を頂くことがあります。本記事ではPHPのBatchの観測を行う方法及び、観測に失敗するケースとその対処法について解説を行います。

検証用環境を用意する

まず初めに、検証用の環境を用意します。
検証環境はLinux OS(Amazon Linux 2)にPHP 7.4がインストールされ、Batchのみが定期実行される状況を想定しています。

検証環境の設定を行う

Amazon Linux 2 にPHP 7.4とNew Relic PHP APM agentをインストールします。

sudo yum update
sudo amazon-linux-extras install php7.4
sudo rpm -Uvh http://yum.newrelic.com/pub/newrelic/el5/x86_64/newrelic-repo-5-3.noarch.rpm
sudo yum install newrelic-php5

Agentの設定を行う

次に、PHP APM Agentの設定を行います。

  1. /etc/php.d/newrelic.ini の編集を開始します。
  2. newrelic.license = "REPLACE_WITH_REAL_KEY" のREPLACE_WITH_REAL_KEYをライセンスキーに置き換えます。
  3. newrelic.appname = "PHP Batch Server" アプリケーション名をわかりやすい名称に変更し、保存します。

batch用コードを用意する

観測するために簡単なコードを用意します。
サンプルでは、ランダム時間・回数スリープを行い、稀にエラーを発生させます。
ファイル名称は /opt/sample/batch.php とします。

 

<?php

$count=0;
$user_id=rand(0,10);
$max_count=rand(5,11);
error_log("max_count: ".$max_count);

if (extension_loaded('newrelic')) {
	newrelic_add_custom_parameter ('userId', $user_id);
}

function random_sleep($count) {
	$sleep_time = rand(0,5);
	sleep($sleep_time);
	error_log("count: ".$count." sleep: ".$sleep_time);

	$num=rand(0,10000);

	if ($num >= 9980) {
		trigger_error("[Error] a random error occurred. num: ".$num,E_USER_ERROR);
	} elseif ($num >= 9950) {
		trigger_error("[Warning] a random warning occurred. num: ".$num,E_USER_WARNING);
	}
}

do {
	random_sleep($count);
	$count ++;
} while($count<=$max_count);

?>

実際に計測する

一通り環境の準備が整ったら、何回かファイルを直接指定しbatchを実行してみましょう。

/usr/bin/php /opt/sample/batch.php

設定が正常であれば、New RelicのWeb UIからTransactionの情報などが確認できる状態になります。

 

php trace sample

cron設定

実際に運用される環境と同様に、crontabにbatchを組み込んでみましょう。
本検証環境では、毎時00分と30分に実行されるよう設定します。

0,30 * * * * /usr/bin/php /opt/sample/batch.php

cronの設定後はbatchの直接実行を行わず、しばらく時間を置いたのちにWeb UIからTransactionの情報を確認しましょう。

おそらくですが、cronで実行されたbatchは観測できていないかと思います。

なぜ計測できないか?

なぜ計測できないのか説明を行う前に、PHPエージェントの仕組みについて解説します。

PHPエージェントの仕組み

PHPエージェントはnewrelic-daemonを介し、New Relicへデータを送信します。
その際、newrelic-daemonとNew Relicとの間で通信が確立している必要があります。

 

PHP Agent

Batchの実行間隔が長い時に何が起こるか

例えば1時間に1回であったり、1日に1回のように長いスパンで実行間隔が開く環境では以下のことが起こります。

  1. newrelic-daemonの仕様として、アプリケーションが非アクティブと見なされて10分間(デフォルト)経過すると、デーモン(newrelic-daemon)とバックエンド(New Relic)間の通信が切断される。
  2. 通信が切断された状態でPHPからデーモンにアクセスがあった際、デーモンはバックエンドへ再接続を行う。
  3. デーモンの設定がデフォルトの状態だと、2のアクセス時点ではバックエンドとの接続が確立しておらず、かつリトライが行われないためバックエンドへデータを送信することができない。(2で通信が確立された以降のデータは送信できる)

以上3点より、バッチの実行間隔が長いケースではNew RelicへTransactionなどのデータが送れないといった事象が起こります。

どうやって実行間隔の長いBatchを観測するか

それでは、前項のケース2〜3とPHPエージェントのドキュメントを照らし合わせて解決していきます。

PHPエージェントの設定
https://docs.newrelic.com/jp/docs/apm/agents/php-agent/configuration/php-agent-configuration/#inivar-daemon-app_connect_timeout

newrelic.daemon.app_connect_timeout

PHPエージェントの設定内に、アプリケーション接続のタイムアウトに関連するパラメータの記述があります。

アプリケーションに接続するデーモンに対する、エージェントの最大待ち時間を設定します。値が"0"の場合、エージェントはデーモンへの接続を1回しか試みません。

デフォルト設定は”0”ですので、バックエンドへ接続していないデーモンに接続してもエラーとなり、かつリトライが行われないためアクセスに失敗します。

このタイムアウトが設定されていると、デーモンがまだバックエンドに接続していない場合、エージェントはただちにトランザクションを停止せず、デーモンに接続を確立する時間を許可します。

そこで、ドキュメントに従いタイムアウトを設定することでデーモンとバックエンドの接続が確立するまで待つようになり、実行間隔の長いbatchが観測できるようになります。ここでは値を推奨値の15秒に設定します。

/etc/php.d/newrelic.ini
newrelic.daemon.app_connect_timeout = “15s”

推奨:タイムアウトを設定する場合、推奨値は"15s"です。接続に問題がある場合、エージェントはすべてのトランザクション開始時に所定のタイムアウトをブロックするため、時間のかかるバックグラウンドタスクのインストゥルメントをしている場合にのみこのタイムアウトを設定してください。

このパラメータは推奨内の補足にあるように、タイムアウトを一定期間ブロックします。そのため、常にPHPエージェントが稼働している状態(例えばwebserverとbatchが一つのVMで稼働している状況)では設定しないように注意してください。

設定変更後しばらく時間が経過すると、batch処理が計測されるようになります。

PHP batch

まとめ

Batch処理はWebアプリケーションと異なり1回あたりの実行間隔や実行時間が長いことがままあるため、処理の特性や環境に合わせた設定の変更が必要になる場合があります。このドキュメントでトラブルの解消の一助になりますと幸いです。