Skip to content
isdnetworks
Go back

PHP-FPM Docker 컨테이너가 시작 직후 종료를 반복하는 문제

Updated:

PHP-FPM Docker 컨테이너가 정상적으로 시작되는 것처럼 보이지만, 곧바로 종료되고 Restarting (0) 상태를 무한 반복하는 문제를 겪었다.

Table of contents

Open Table of contents

증상

docker ps -a
# STATUS: Restarting (0) 3 seconds ago

docker logs php-fpm
# [22-Mar-2026 03:15:01] NOTICE: ready to handle connections
# [22-Mar-2026 03:15:01] NOTICE: systemd monitor interval set to 10000ms

로그에는 에러가 전혀 없다. ready to handle connections까지 정상 출력된 후 exit code 0으로 종료된다. 에러가 없으니 디버깅 방향을 잡기 어렵다.

원인 분석

exit code 0의 함정

exit code 0은 정상 종료를 의미한다. 크래시가 아니라 PHP-FPM 프로세스가 스스로 “할 일이 끝났다”고 판단한 것이다.

Docker 공식 PHP-FPM 이미지는 daemonize=no로 포그라운드 실행된다. 이는 Docker 컨테이너의 PID 1로 동작하기 위한 올바른 설정이다. 그런데 특정 조건에서 FPM master 프로세스가 포그라운드 유지를 못하고 종료된다.

주요 원인들

1. 커스텀 entrypoint에서 exec 누락

가장 흔한 원인이다. 커스텀 entrypoint 스크립트에서 PHP-FPM을 exec 없이 실행하면, 스크립트가 종료될 때 FPM도 함께 종료된다.

# 잘못된 방식 — 스크립트 종료 시 FPM도 종료
#!/bin/bash
php-fpm

# 올바른 방식 — exec로 PID 1을 FPM에 넘김
#!/bin/bash
exec php-fpm

2. PID 파일 충돌

이전 실행의 PID 파일이 남아있으면 FPM이 이미 실행 중이라고 판단하고 종료한다.

# PID 파일 위치 확인
docker exec php-fpm cat /usr/local/etc/php-fpm.d/zz-docker.conf
# pid = /var/run/php-fpm.pid

# 컨테이너 내부에서 PID 파일 확인
docker exec php-fpm ls -la /var/run/php-fpm.pid

3. 소켓/포트 충돌

동일 포트에 다른 프로세스가 바인딩되어 있으면 FPM이 조용히 종료된다. 특히 같은 호스트에서 여러 PHP 버전을 운영할 때 발생한다.

# 포트 충돌 확인
ss -tlnp | grep 9000

해결

1. entrypoint 스크립트 점검

커스텀 설정을 주입하는 entrypoint를 사용한다면, 반드시 마지막에 exec으로 원본 entrypoint를 호출한다.

#!/bin/bash
set -e

# 커스텀 설정 주입
cat >> /usr/local/etc/php-fpm.d/zz-docker.conf <<'EOF'
pm.max_requests = 1000
request_terminate_timeout = 105
ping.path = /ping
ping.response = pong
EOF

# exec로 PID 1 넘김
# https://github.com/docker-library/php/blob/master/docker-php-entrypoint
exec docker-php-entrypoint php-fpm

2. health check 설정

FPM의 ping endpoint를 활용한 health check를 설정하면, 비정상 종료를 빠르게 감지할 수 있다.

docker run \
  --health-cmd='SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET REQUEST_URI=/ping cgi-fcgi -bind -connect 127.0.0.1:9000 || exit 1' \
  --health-interval=30s \
  --health-timeout=5s \
  --health-retries=3 \
  --health-start-period=10s \
  php:8.1-fpm

3. 멀티 버전 운영 시 포트 분리

같은 호스트에서 여러 PHP 버전을 운영할 때는 포트를 명확히 분리한다.

PHP 7.2: 9000 (prod), 19000 (dev)
PHP 8.1: 9001 (prod), 19001 (dev)

디버깅 체크리스트

  1. docker logs 확인 — 에러 메시지가 있는지 (이 케이스에서는 없음)
  2. exit code 확인docker inspect --format='{{.State.ExitCode}}' php-fpm
  3. entrypoint에서 exec 사용 여부 — 가장 흔한 원인
  4. PID 파일 잔존 여부/var/run/php-fpm.pid
  5. 포트 충돌 여부ss -tlnp | grep 9000
  6. daemonize = no 확인 — Docker에서 필수
  7. 볼륨 마운트 권한 — 설정 파일이 읽기 가능한지

핵심 정리

exit code 0으로 종료되는 컨테이너는 에러 로그가 없어서 디버깅이 어렵다. PHP-FPM의 경우 대부분 커스텀 entrypoint의 exec 누락이 원인이다. exec는 현재 셸 프로세스를 대체하여 PHP-FPM이 PID 1이 되도록 하는 핵심 명령어다. 이것이 빠지면 셸 스크립트가 종료될 때 FPM도 함께 사라진다.

참고 자료


Share this post on:

Previous Post
Hello World
Next Post
PHP 7.2 범용 Docker 이미지에 어떤 PECL 확장을 넣을 것인가