本文記錄一次在 CentOS 6/7 上從源碼編譯部署 LNMP(Linux + Nginx + MySQL + PHP-FPM)的完整過程,涵蓋依賴安裝、核心軟體編譯參數調優、配置文件模板、啟動腳本、內核參數優化、日誌切割、平滑重載等生產級細節。版本選擇:Nginx 1.24、PHP 8.2、MySQL 8.0(文中以經典 5.5/5.2 版本為例說明編譯邏輯,實際生產請升級至維護版本)。
# CentOS 7/8
yum -y install gcc gcc-c++ make autoconf \
libjpeg-turbo-devel libpng-devel freetype-devel \
libxml2-devel zlib-devel bzip2-devel \
libcurl-devel openssl-devel openldap-devel \
libxslt-devel libzip-devel oniguruma-devel \
sqlite-devel libicu-devel systemd-devel
# CentOS 6 需額外安裝 libmcrypt、mhash 等舊庫
# 建立用戶與目錄
groupadd mysql && useradd -r -g mysql -s /sbin/nologin mysql
mkdir -p /data/mysql/{data,logs,tmp} && chown -R mysql:mysql /data/mysql
# 下載並編譯
tar xf mysql-8.0.36-linux-glibc2.28-x86_64.tar.xz -C /usr/local/
ln -s /usr/local/mysql-8.0.36 /usr/local/mysql
# 初始化
/usr/local/mysql/bin/mysqld --initialize-insecure \
--user=mysql --basedir=/usr/local/mysql --datadir=/data/mysql/data
# systemd 服務
cat > /etc/systemd/system/mysqld.service <<'EOF'
[Unit]
Description=MySQL Server
After=network.target
[Service]
User=mysql
Group=mysql
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf
LimitNOFILE=65535
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable --now mysqld
[mysqld] basedir=/usr/local/mysql datadir=/data/mysql/data socket=/tmp/mysql.sock port=3306 # 連接層 max_connections=2000 thread_cache_size=300 table_open_cache=4000 open_files_limit=65535 # InnoDB 核心 innodb_buffer_pool_size=4G # 物理記憶體 60-70% innodb_log_file_size=1G innodb_log_files_in_group=3 innodb_flush_log_at_trx_commit=2 # 性能優先,允許 1 秒丟失 innodb_file_per_table=1 innodb_io_capacity=2000 innodb_read_io_threads=8 innodb_write_io_threads=8 # 查詢快取(8.0 已移除,改用 ProxySQL 或 Redis) # query_cache_type=0 # 二進位日誌 log_bin=/data/mysql/logs/binlog binlog_format=ROW expire_logs_days=7 max_binlog_size=1G # 安全 skip-name-resolve sql_mode=STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION
tar xf php-8.2.18.tar.gz && cd php-8.2.18
./configure --prefix=/usr/local/php \
--with-config-file-path=/usr/local/php/etc \
--with-config-file-scan-dir=/usr/local/php/etc/php.d \
--enable-fpm --with-fpm-user=www --with-fpm-group=www \
--enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd \
--with-iconv --with-freetype --with-jpeg --with-webp --with-zlib \
--with-libxml --enable-xml --disable-rpath \
--enable-bcmath --enable-shmop --enable-sysvsem \
--enable-mbstring --enable-intl --enable-pcntl \
--enable-sockets --enable-gd --enable-opcache \
--with-curl --with-mhash --with-xsl --with-zip \
--with-openssl --with-sodium --with-password-argon2 \
--enable-calendar --enable-exif --enable-ftp \
--with-pear --enable-cli
make -j$(nproc) && make install
# 配置文件
cp php.ini-production /usr/local/php/etc/php.ini
cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf
cp /usr/local/php/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf
# /usr/local/php/etc/php.ini memory_limit = 256M max_execution_time = 30 post_max_size = 50M upload_max_filesize = 50M date.timezone = Asia/Shanghai opcache.enable=1 opcache.memory_consumption=256 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=20000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=1 cgi.fix_pathinfo=0 # 防 Nginx 解析漏洞 expose_php=Off display_errors=Off log_errors=On error_log=/data/logs/php/error.log
[www] user = www group = www listen = 127.0.0.1:9000 listen.owner = www listen.group = www listen.mode = 0660 pm = dynamic pm.max_children = 200 pm.start_servers = 20 pm.min_spare_servers = 10 pm.max_spare_servers = 50 pm.max_requests = 1000 request_terminate_timeout = 60s request_slowlog_timeout = 5s slowlog = /data/logs/php/slow.log catch_workers_output = yes php_admin_value[error_log] = /data/logs/php/fpm-error.log php_admin_flag[log_errors] = on
tar xf nginx-1.24.0.tar.gz && cd nginx-1.24.0
./configure --prefix=/usr/local/nginx \
--user=www --group=www \
--with-http_ssl_module --with-http_v2_module \
--with-http_realip_module --with-http_stub_status_module \
--with-http_gzip_static_module --with-pcre-jit \
--with-threads --with-file-aio \
--with-http_sub_module --with-stream \
--with-stream_ssl_module
make -j$(nproc) && make install
user www www;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 65535;
error_log /data/logs/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
use epoll;
worker_connections 65535;
multi_accept on;
}
http {
include mime.types;
default_type application/octet-stream;
# 緩衝與快取
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 60;
keepalive_requests 1000;
# FastCGI
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 64k;
fastcgi_buffers 4 64k;
fastcgi_busy_buffers_size 128k;
fastcgi_temp_file_write_size 128k;
# Gzip
gzip on;
gzip_min_length 1k;
gzip_comp_level 2;
gzip_types text/plain application/javascript text/css application/xml application/json;
gzip_vary on;
# 日誌格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 虛擬主機示例
server {
listen 80;
server_name example.com;
root /data/www/example.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 30d;
access_log off;
}
access_log /data/logs/nginx/example.com.log main;
}
}
# 連接隊列 net.core.somaxconn = 65535 net.core.netdev_max_backlog = 32768 net.ipv4.tcp_max_syn_backlog = 65535 # 內存緩衝 net.core.rmem_default = 262144 net.core.rmem_max = 16777216 net.core.wmem_default = 262144 net.core.wmem_max = 16777216 # TCP 優化 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 15 net.ipv4.tcp_keepalive_time = 300 net.ipv4.tcp_keepalive_intvl = 30 net.ipv4.tcp_keepalive_probes = 3 net.ipv4.tcp_max_orphans = 3276800 net.ipv4.tcp_mem = 786432 1048576 1572864 # 端口範圍 net.ipv4.ip_local_port_range = 1024 65535 # 文件句柄 fs.file-max = 1000000 fs.nr_open = 1000000
執行 sysctl --system 生效,並將 ulimit -SHn 65535 加入 /etc/profile 或服務啟動腳本。
# 檢查配置 /usr/local/nginx/sbin/nginx -t # 重載 /usr/local/nginx/sbin/nginx -s reload
#!/bin/bash
LOG_DIR="/data/logs/nginx"
DATE=$(date -d "yesterday" +%Y%m%d)
mkdir -p ${LOG_DIR}/$(date -d "yesterday" +%Y/%m)
mv ${LOG_DIR}/access.log ${LOG_DIR}/$(date -d "yesterday" +%Y/%m)/access_${DATE}.log
mv ${LOG_DIR}/error.log ${LOG_DIR}/$(date -d "yesterday" +%Y/%m)/error_${DATE}.log
# 通知 Nginx 重新打開日誌
kill -USR1 $(cat /var/run/nginx.pid)
加入 crontab:0 0 * * * /bin/bash /usr/local/nginx/sbin/cut_log.sh
# systemd 管理 Nginx cat > /etc/systemd/system/nginx.service <<'EOF' [Unit] Description=Nginx HTTP Server After=network.target [Service] Type=forking PIDFile=/var/run/nginx.pid ExecStartPre=/usr/local/nginx/sbin/nginx -t ExecStart=/usr/local/nginx/sbin/nginx ExecReload=/usr/local/nginx/sbin/nginx -s reload ExecStop=/usr/local/nginx/sbin/nginx -s quit LimitNOFILE=65535 [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now nginx php-fpm mysqld
mysql_secure_installation、禁用 root 遠程登錄、強密碼策略。disable_functions=exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source。server_tokens off、限制請求體大小 client_max_body_size 10m、配置 HTTPS 與 HSTS。源碼編譯部署雖繁瑣,但能精確控制版本、模組與優化參數,適合對性能、安全有嚴格要求的生產環境。配合 Ansible/Playbook 自動化,可將上述流程標準化、可重複執行。關鍵成功因素:內核參數優化、PHP-FPM 池合理規劃、MySQL InnoDB 緩衝池適配記憶體、Nginx 緩衝與快取策略、完善的日誌與監控體系。