Nginx / PHP FPM graceful stop (SIGQUIT): not so graceful - php

Running nginx 1.9.* / PHP 7.0.* (but exact same behavior in 5.6.* also)
Attempting to gracefully stop a PHP-FPM / nginx combo for node shutdown during maintenance. To do this, I'm sending the SIGQUIT to php-fpm, which should provide a graceful shutdown.
To test this, I made a dumb script
<?php sleep(5); echo 'done';
Tested locally with the following curl
curl -I x.x.x.x:8080
Which normally produces the output:
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 12 Apr 2016 04:48:00 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Desired: in the middle of any in-flight request, when a graceful shutdown is requested, the current requests should finish, but any additional requests should fail.
Unfortunately, when I try to trigger this behavior, by sending a SIGQUIT (http://manpages.ubuntu.com/manpages/precise/man8/php5-fpm.8.html) to the PHP-FPM master process:
kill -s SIGQUIT $FPMPID
The connection immediately drops, resulting in an ngnix 502
HTTP/1.1 502 Bad Gateway
Server: nginx
Date: Tue, 12 Apr 2016 04:48:07 GMT
Content-Type: text/html
Content-Length: 166
Connection: close
Any advice? I would love to make this piece of the system as seamless as possible. Thanks!

After struggling with this same situation for a while, I believe I've found the magical config setting to make child processes finish handling requests before dying.
http://php.net/manual/en/install.fpm.configuration.php#process-control-timeout
process_control_timeout
Time limit for child processes to wait for a reaction on signals from master
Basically, by setting this to something like 10s, the child process will wait that long, while handling existing requests before quitting.
Unfortunately, it seems that the php-fpm master process exits immediately, so, inspired by the code here, I wrote a wrapper script:
#!/bin/bash
PHP_FPM_PID='/php-fpm.pid'
wait_for_pid () {
try=0
while test $try -lt 35 ; do
if [ ! -f "$1" ] ; then
try=''
break
fi
echo -n .
try=`expr $try + 1`
sleep 1
done
}
function clean_up {
echo "Killing $(cat $PHP_FPM_PID)"
kill -QUIT `cat $PHP_FPM_PID`
wait_for_pid $PHP_FPM_PID
echo "Done!"
exit 0
}
trap clean_up EXIT
nohup php-fpm --daemonize --pid $PHP_FPM_PID 2>&1 &
while true; do sleep 1; done
# ^ do nothing forever
which waits 35 seconds or until that pid file has been removed (presumably by one of the child processes? I'm still unclear on how it's removed).
Regardless, this wrapper script works well as the CMD for our php-fpm docker container that we're running with Kubernetes.

Related

Start process in bash script after other process has started up

I'm trying to create a bash script that starts two processes: PHP-FPM and Nginx.
First PHP-FPM should start and once that has finished starting up (port 9000 will then be reachable for example, but there might be other means of checking it has finished starting up) the Nginx server should be started.
My current script looks like this:
#!/usr/bin/env bash
set -e
php-fpm -F &
nginx &
wait -n
But sometimes early on nginx will give me a 502 gateway error because php-fpm is not ready yet.
What's the cleanest/best way of getting this startup in order?
Regards,
Kees.
You can modify your script in this way:
#!/usr/bin/env bash
set -e
php-fpm -F && nginx
wait -n
As you can see in this answer on Unix&Linux, the && operator allows you to run the second command only if the first exited successfully.

Access php status information from inside php

php-fpm provides a status page for those who are interested. It looks like this:
curl http://localhost/server-status
pool: default
process manager: dynamic
start time: 11/Dec/2014:17:51:33 -0500
start since: 61383
accepted conn: 4682
listen queue: 0
max listen queue: 0
listen queue len: 0
idle processes: 11
active processes: 1
total processes: 12
max active processes: 2
max children reached: 0
slow requests: 3
How do I access this information from php? Please don't tell me to use curl to request the status page from localhost.
Unfortunately server-status from FPM is not PHP mechanism. You cannot get it by internal PHP mechanisms.
Your only way to reach this is using some kind of connect mechanism to fpm server itself.
You can get it by grabbing info from url with CURL or file_get_contents() or any other mechanisms that fetches data from url.
You can connect local socket with shell exec using parameters like:
(shell code)
SCRIPT_NAME=/status \
SCRIPT_FILENAME=/status \
REQUEST_METHOD=GET \
cgi-fcgi -bind -connect /var/run/php-fpm/www.sock
You can use some kind of variation of socket connecting with parameters. I haven't tried that, but a good place to start is here: https://github.com/wizaplace/php-fpm-status-cli
Probably if you get very stubborn you can find another creative way, but either of them are doing it as internal mechanism of PHP-FPM.
In ubuntu I was able to access PHP-FPM status page by this
SCRIPT_NAME=/status SCRIPT_FILENAME=/status REQUEST_METHOD=GET cgi-fcgi -bind -connect /var/run/php/php7.0-fpm.sock
Note: You may get not found error while running above command, like
Primary script unknown
Status: 404 Not Found
Content-type: text/html; charset=UTF-8
File not found.
If you are getting this error make sure you have uncommented pm.status_path = /status in pool.d/www.conf.
Absolute path for that config file in ubuntu 17.04 with PHP 7 is /etc/php/7.0/fpm/pool.d/www.conf and line number is 232
After editing conf file restart fpm with service php7.0-fpm restart
GETTING IT VIA PHP SCRIPT
You can get output of php-fpm status by running above command via php with exec()
exec( 'SCRIPT_NAME=/status SCRIPT_FILENAME=/status REQUEST_METHOD=GET cgi-fcgi -bind -connect /var/run/php/php7.0-fpm.sock', $php_fpm_status );
// Above statement will assign an array to $php_fpm_status filled with every line of output from the command, make it as whole string
$php_fpm_status = implode(PHP_EOL, $php_fpm_status);
// You can try printing php-fpm status
echo $php_fpm_status;

The PHP executes the shell_exec command but doesn't do anything

I'm using this script to post some date using curl in a post.sh file
#!/bin/sh
var1=`base64 javascript_100.file`
# post it 3 times
for i in `seq 1 3`; do
/usr/bin/curl -i --verbose -d "jobTexterea=$var1" http://my_server_address/target_page.php
done
I don't have any problems executing the post.sh file using my Shell however when i invoke it inside my php script using shell_exec('sh /path_to_post_file/post.sh') I'm getting this error from my var_dump(shell_exec('sh /path_to_post_file/post.sh'));.
Error: () HTTP/1.1 302 Found Date: Sat, 24 Sep 2016 15:14:38 GMT Server: Apache/2.4.23 (Fedora) OpenSSL/1.0.2h-fips PHP/5.6.25 mod_perl/2.0.9 Perl/v5.22.2 X-Powered-By: PHP/5.6.25 Location: http://my_server_ ddress/ Content-Length: 401 Content-Type: text/html; charset=UTF-8 A MySQL error has occurred.
Your Query: INSERT INTO jobsNum_pro_batch (job_batch_id , jobs_in_batch , job_type ) VALUES ( '26' ,'1' ,'JavaScript' )
The code in the post.sh file posts the information to the target_page.php wich puts all the content into different tables in a database. The thing is that no information is being uploaded into the database with running shell_exec('sh /path_to_post_file/post.sh') from a PHP page. However, the command itself runs without errors, I checked it with
<?php
if (shell_exec('sh /path_to_post_file/post.sh')){
echo "<b> shell_exec was executed </b><br>";
var_dump(shell_exec('sh /path_to_post_file/post.sh'));
} else {
echo "<b> shell_exec was not executed </b><br>";
}
?>
I'm getting the message shell_exec was executed
Has anyone an idea what went wrong?
This did the trick! Thanks to #Jean-FrançoisFabre and a small change to his code and problem is gone!
#!/bin/sh
progdir=$(dirname $0)
var1=`base64 $progdir/javascript_100.file`
# post it 3 times
for i in `seq 1 3`; do
/usr/bin/curl -i --verbose -d "jobTexterea=$var1" http://my_server_address/target_page.php
done

Upstart - Job starts but then stops immediately

I am trying to set up an upstart job but for some reason it stops immediately after starting
I'm on Cents 6.5 btw
here are my files:
/ect/init/test-daemon.conf
start on startup
stop on shutdown
respawn
script
sudo -u root php -f /usr/share/test_daemon.php
end script
/usr/share/test-daemon.php
<?php
// The worker will execute every X seconds:
$seconds = 2;
// We work out the micro seconds ready to be used by the 'usleep' function.
$micro = $seconds * 1000000;
while(true){
// Now before we 'cycle' again, we'll sleep for a bit...
usleep($micro);
}
I have then got this file: (which I found on a forum that logs events)
/tmp/log.file
debug/ (/dev/fd/10):19735:Fri Jul 25 11:52:40 AST 2014:Job
test-daemon/ starting. Environment was: TERM=linux
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin
PWD=/ JOB=test-daemon
SHLVL=1
UPSTART_INSTANCE=
UPSTART_EVENTS=starting
UPSTART_JOB=debug
INSTANCE=
_=/usr/bin/env
debug/ (/dev/fd/9):19775:Fri Jul 25 11:52:41 AST 2014:Job test-daemon/ stopping. Environment was:
TERM=linux
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin
PWD=/
JOB=test-daemon
RESULT=ok
SHLVL=1
UPSTART_INSTANCE=
UPSTART_EVENTS=stopping
UPSTART_JOB=debug
INSTANCE=
_=/usr/bin/env
debug/ (/dev/fd/9):19779:Fri Jul 25 11:52:41 AST 2014:Job test-daemon/ stopping. Environment was:
TERM=linux
PATH=/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/sbin:/bin
PWD=/
JOB=test-daemon
RESULT=failed
SHLVL=1
PROCESS=respawn
UPSTART_INSTANCE=
UPSTART_EVENTS=stopping
UPSTART_JOB=debug
INSTANCE=
_=/usr/bin/env
in the log file, I get more debug information but its basically the above repeated a few times.
I get this from running start test-daemon which outputs test-daemon start/running, process 20600
I know the test-daemon.php doesn't actually do anything...at the moment I just need to get the actual job running, once thats fixed ill drop in my code
So from the above...is there anything I am doing wrong? as the job should only stop if I run stop test-daemon right?
Any suggestions would be much appreciated :)
Thanks,
Dave
I'd rather post a comment but with the low reputation limit I can not.
I am not familiar with this distro flavor but did you try to use nohup ?
Like this:
nohup php -f /usr/share/test_daemon.php &
It could be possible that detaching your command from the service process kills it.

freebsd 9.1 apache-mpm php5 child segmentation fault

I've got such problem:
When I run script which uses pthreads extension, PHP crushes with this errors:
[notice] child pid 44688 exit signal Segmentation fault (11)
[notice] child pid 44689 exit signal Segmentation fault (11)
I have installed apache22-worker-mpm port, php5.4.16 with pthreads 0.44
Also command
ps -U www
provides such result:
PID TT STAT TIME COMMAND
44687 ?? TX 0:00:00 /usr/local/sbin/httpd -k restart
44688 ?? S 0:00:00 /usr/local/sbin/httpd -k restart
44689 ?? Z 0:00:00 <defunct>
44690 ?? S 0:00:00 /usr/local/sbin/httpd -k restart
Restarting apache - does nothing.
What can I do to fix this?
Thanks!
PS: on my Windows machine - all works fine, so problem isn't in php code.
The first step in investigating crashes is to obtain the stack from the core-dump.
Obtaining core-dump of Apache may be tricky -- because they are often disabled by default. But it can be done.
Now, the fact that it works on Windows is not, actually, proof, that it is not the php-code -- whatever the problem is, it may be triggered by the "just right" sort of racing between threads. The OS, the number of CPUs, and other factors all affect the results of a race...
And finally, are you sure, you are using thread-safe (zts) version of PHP itself?

Categories