PHP is failing with SIGSEGV
It could be not a common situation, but sometimes you start your PHP container and at some point PHP-FPM is failing showing an error log like:
NOTICE: fpm is running, pid 1
NOTICE: ready to handle connections
WARNING: [pool www] child 7 exited on signal 11 (SIGSEGV - core dumped) after 245.799183 seconds from start
It is a Segmentation Fault. There could be a various reasons for that. The cause of that could be an incompatibility of some PHP Extensions, or even a PHP code executing in some specific edge cases.
Core Dump
To understand the real root cause of SIGSEGV is to backtrace the core dump with GDB and/or Valgrind.
Just to note, this article is not about how to use GDB/Valgrind to find why segfault happens, but how to prepare for it.
Problem #1
In my case, there was no core dump, nowhere. Probably, you may have the same case.
To check where core dump should be saved you could runt he command:
cat /proc/sys/kernel/core_pattern
In my case the output was:
|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E
So it means that core dump is piped to apport
, but for some reason it's not resulted in a core dump saved to a file.
I am using php8.2-fpm-alpine3.18
(now) and php8.0-alpine3.12
(before) Docker images. But it seems majority (if not all) Alpine images has the same problem.
Solution #1
You need to do a few changes to let Docker image properly save the core dump.
-
In your Docker entry point file, please add the following lines before the last
exec
statement:
ulimit -c unlimited echo '/tmp/core' > /proc/sys/kernel/core_pattern echo 1 > /proc/sys/fs/suid_dumpable
-
In the
docker-compose.yml
find your PHP service and update the file so it will look like this (of course, omit >>> and <<<):
services: php-app: ... >>> privileged: true ulimits: core: 99999999999 <<< ...
Rebuild the image.
Now, when segfault will happen you should be able to find it in
/tmp
directory. A core dump file will be named like/tmp/core.7
, where the number corresponds to the number of a PHP-FPM worker failed.
Problem #2
Great! Now we have core dump and could debug it.
But why we have a problem #2?
The problem is in PHP binaries that have no debug symbols. It's absolutely normal to strip these symbols after compilation, as this saves a memory and improves performance. No one wants to have a fat and slow PHP binaries in production environment!
However, debugging without debug symbols is a total disaster.
Solution #2
Well, long story short - you need to recompile PHP and do not strip debug symbols. Here are the steps:
-
Checkout Git repository of Docker PHP images:
git clone https://github.com/docker-library/php cd php
-
Go into directory where relevant
Dockerfile
is located. If you are using PHP 8.2 under Alpine Linux, the path should be:
cd 8.2/alpine3.18/fpm
-
Now open
Dockerfile
and find following lines:
find \ /usr/local \ -type f \ -perm '/0111' \ -exec sh -euxc ' \ strip --strip-all "$@" || : \ ' -- '{}' + \ ; \
Completely delete these lines out of
Dockerfile
and save it.-
Build the image and tag it:
docker build -t php-fpm-debug .
Now you have an image with PHP compiled with debug symbols. What's next? Yes, build your app image from this one!
In
Dockerfile
of your app replaceFROM ...
withFROM php-fpm-debug:latest
.Build it! But be aware to not use
--pull
argument, so docker will search local repository for the specified image.
Result
Now, if segfault will happen you will be able to find core dump in /tmp
directory and it will contain debug symbols for easier debugging process.
P.S. If you know a good article or tutorial on how to use GDB or Valgrind to debug PHP core dumps, please leave in a comment below. Much appreciated!
Top comments (0)