Optimize Apache & PHP-FPM memory for lower idle usage
All checks were successful
Cloud Apache Container / Build-and-Push (74) (push) Successful in 2m31s
Cloud Apache Container / Build-and-Push (80) (push) Successful in 1m54s
Cloud Apache Container / Build-and-Push (81) (push) Successful in 1m51s
Cloud Apache Container / Build-and-Push (82) (push) Successful in 1m52s
Cloud Apache Container / Build-and-Push (83) (push) Successful in 2m39s
Cloud Apache Container / Build-and-Push (84) (push) Successful in 1m58s
Cloud Apache Container / Build-and-Push (85) (push) Successful in 1m51s

Switch PHP-FPM from pm=dynamic to pm=ondemand (zero idle workers),
auto-detect container memory via cgroups to calculate appropriate
limits, and generate Apache MPM config at runtime. All tuning values
are now overridable via environment variables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 18:52:15 -08:00
parent a153385d8f
commit 87c4f2befc
5 changed files with 144 additions and 19 deletions

View File

@@ -1,13 +1,2 @@
DirectoryIndex index.html index.htm index.php DirectoryIndex index.html index.htm index.php
Alias "/ping" "/var/www/html" Alias "/ping" "/var/www/html"
<IfModule mpm_event_module>
StartServers 2
MinSpareThreads 10
MaxSpareThreads 25
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers 75
ServerLimit 3
MaxConnectionsPerChild 1000
</IfModule>

View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Generate Apache MPM event tuning config at runtime using detect-memory.sh values.
cat <<EOF > /etc/httpd/conf.d/mpm-tuning.conf
<IfModule mpm_event_module>
StartServers ${APACHE_START_SERVERS}
MinSpareThreads ${APACHE_MIN_SPARE_THREADS}
MaxSpareThreads ${APACHE_MAX_SPARE_THREADS}
ThreadLimit 64
ThreadsPerChild 25
MaxRequestWorkers ${APACHE_MAX_REQUEST_WORKERS}
ServerLimit ${APACHE_SERVER_LIMIT}
MaxConnectionsPerChild ${APACHE_MAX_CONNECTIONS_PER_CHILD}
</IfModule>
EOF
exit 0

View File

@@ -12,12 +12,15 @@ listen = /run/php-fpm/www.sock
listen.owner = apache listen.owner = apache
listen.group = apache listen.group = apache
pm = dynamic pm = ${PHP_FPM_PM}
pm.max_children = 5 pm.max_children = ${PHP_FPM_MAX_CHILDREN}
pm.start_servers = 2 pm.max_requests = ${PHP_FPM_MAX_REQUESTS}
pm.min_spare_servers = 1 pm.process_idle_timeout = ${PHP_FPM_PROCESS_IDLE_TIMEOUT}
pm.max_spare_servers = 3
pm.max_requests = 500 ; Settings used when pm = dynamic (fallback if user overrides FPM_PM)
pm.start_servers = ${PHP_FPM_START_SERVERS}
pm.min_spare_servers = ${PHP_FPM_MIN_SPARE}
pm.max_spare_servers = ${PHP_FPM_MAX_SPARE}
slowlog = /etc/httpd/logs/error_log slowlog = /etc/httpd/logs/error_log
request_slowlog_timeout = 3s request_slowlog_timeout = 3s
@@ -28,4 +31,4 @@ php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache
EOF EOF
exit 0 exit 0

112
scripts/detect-memory.sh Executable file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# detect-memory.sh — Detect container memory and calculate tuning parameters.
# Must be sourced (not executed) so variables are available to the caller.
# --- Memory detection (cgroups v2 → v1 → /proc/meminfo → fallback) ---
CONTAINER_MEMORY_BYTES=""
# cgroups v2
if [ -f /sys/fs/cgroup/memory.max ]; then
val=$(cat /sys/fs/cgroup/memory.max 2>/dev/null)
if [ "$val" != "max" ] && [ -n "$val" ]; then
CONTAINER_MEMORY_BYTES=$val
fi
fi
# cgroups v1
if [ -z "$CONTAINER_MEMORY_BYTES" ] && [ -f /sys/fs/cgroup/memory/memory.limit_in_bytes ]; then
val=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes 2>/dev/null)
# Values near page-aligned max (like 9223372036854771712) mean "no limit"
if [ -n "$val" ] && [ "$val" -lt 8589934592000 ] 2>/dev/null; then
CONTAINER_MEMORY_BYTES=$val
fi
fi
# /proc/meminfo (host memory — used when no cgroup limit is set)
if [ -z "$CONTAINER_MEMORY_BYTES" ] && [ -f /proc/meminfo ]; then
mem_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo)
if [ -n "$mem_kb" ]; then
CONTAINER_MEMORY_BYTES=$((mem_kb * 1024))
fi
fi
# Fallback
if [ -z "$CONTAINER_MEMORY_BYTES" ]; then
CONTAINER_MEMORY_BYTES=$((512 * 1024 * 1024))
fi
CONTAINER_MEMORY_MB=$((CONTAINER_MEMORY_BYTES / 1024 / 1024))
# --- Budget calculation ---
OS_RESERVE_MB=50
FIXED_PROCESS_MB=50
DEV_OVERHEAD_MB=0
if [ "$environment" = "DEV" ]; then
DEV_OVERHEAD_MB=125
fi
AVAILABLE_MB=$((CONTAINER_MEMORY_MB - OS_RESERVE_MB - FIXED_PROCESS_MB - DEV_OVERHEAD_MB))
if [ "$AVAILABLE_MB" -lt 60 ]; then
AVAILABLE_MB=60
fi
PHP_BUDGET_MB=$((AVAILABLE_MB * 80 / 100))
APACHE_BUDGET_MB=$((AVAILABLE_MB * 20 / 100))
# --- PHP-FPM parameters ---
PHP_WORKER_ESTIMATE_MB=${PHP_WORKER_ESTIMATE_MB:-60}
calc_max_children=$((PHP_BUDGET_MB / PHP_WORKER_ESTIMATE_MB))
# Floor at 2, cap at 50
if [ "$calc_max_children" -lt 2 ]; then
calc_max_children=2
fi
if [ "$calc_max_children" -gt 50 ]; then
calc_max_children=50
fi
PHP_FPM_PM=${FPM_PM:-ondemand}
PHP_FPM_MAX_CHILDREN=${FPM_MAX_CHILDREN:-$calc_max_children}
PHP_FPM_PROCESS_IDLE_TIMEOUT=${FPM_PROCESS_IDLE_TIMEOUT:-10s}
PHP_FPM_MAX_REQUESTS=${FPM_MAX_REQUESTS:-500}
# Dynamic mode fallbacks (used if user overrides FPM_PM=dynamic)
PHP_FPM_START_SERVERS=${FPM_START_SERVERS:-2}
PHP_FPM_MIN_SPARE=${FPM_MIN_SPARE_SERVERS:-1}
PHP_FPM_MAX_SPARE=${FPM_MAX_SPARE_SERVERS:-3}
# --- Apache MPM parameters ---
# ServerLimit: roughly 1 process per ~25 workers, floor 2, cap 16
calc_server_limit=$((APACHE_BUDGET_MB / 30))
if [ "$calc_server_limit" -lt 2 ]; then
calc_server_limit=2
fi
if [ "$calc_server_limit" -gt 16 ]; then
calc_server_limit=16
fi
# MaxRequestWorkers: ServerLimit * ThreadsPerChild (25)
calc_max_request_workers=$((calc_server_limit * 25))
if [ "$calc_max_request_workers" -gt 400 ]; then
calc_max_request_workers=400
fi
# StartServers: 1 for ≤1GB, 2 for larger
calc_start_servers=1
if [ "$CONTAINER_MEMORY_MB" -gt 1024 ]; then
calc_start_servers=2
fi
APACHE_START_SERVERS=${APACHE_START_SERVERS:-$calc_start_servers}
APACHE_SERVER_LIMIT=${APACHE_SERVER_LIMIT:-$calc_server_limit}
APACHE_MAX_REQUEST_WORKERS=${APACHE_MAX_REQUEST_WORKERS:-$calc_max_request_workers}
APACHE_MIN_SPARE_THREADS=${APACHE_MIN_SPARE_THREADS:-5}
APACHE_MAX_SPARE_THREADS=${APACHE_MAX_SPARE_THREADS:-15}
APACHE_MAX_CONNECTIONS_PER_CHILD=${APACHE_MAX_CONNECTIONS_PER_CHILD:-3000}
# --- Export all variables ---
export CONTAINER_MEMORY_MB
export PHP_FPM_PM PHP_FPM_MAX_CHILDREN PHP_FPM_PROCESS_IDLE_TIMEOUT PHP_FPM_MAX_REQUESTS
export PHP_FPM_START_SERVERS PHP_FPM_MIN_SPARE PHP_FPM_MAX_SPARE
export APACHE_START_SERVERS APACHE_SERVER_LIMIT APACHE_MAX_REQUEST_WORKERS
export APACHE_MIN_SPARE_THREADS APACHE_MAX_SPARE_THREADS APACHE_MAX_CONNECTIONS_PER_CHILD

View File

@@ -23,8 +23,12 @@ docker_network=$(ip addr show |grep eth0 |grep inet |awk -F " " {'print $2'})
echo "RemoteIPInternalProxy $docker_network" >> /etc/httpd/conf.d/remoteip.conf echo "RemoteIPInternalProxy $docker_network" >> /etc/httpd/conf.d/remoteip.conf
# /scripts/install-php$PHPVER.sh # /scripts/install-php$PHPVER.sh
source /scripts/detect-memory.sh
echo "Container memory: ${CONTAINER_MEMORY_MB}MB | PHP-FPM pm=${PHP_FPM_PM} max_children=${PHP_FPM_MAX_CHILDREN} | Apache workers=${APACHE_MAX_REQUEST_WORKERS}"
/scripts/create-vhost.sh /scripts/create-vhost.sh
/scripts/create-php-config.sh /scripts/create-php-config.sh
/scripts/create-apache-mpm-config.sh
if [ -f /etc/httpd/conf.d/ssl.conf ]; then if [ -f /etc/httpd/conf.d/ssl.conf ]; then
mv /etc/httpd/conf.d/ssl.conf /etc/httpd/conf.d/ssl.conf.bak mv /etc/httpd/conf.d/ssl.conf /etc/httpd/conf.d/ssl.conf.bak