LNMP_部署文档_openEuler24.03
LNMP 部署文档
环境信息
| 组件 | 版本 |
|---|---|
| 操作系统 | openEuler 24.03 |
| Nginx | 1.24.0 |
| PHP | 8.3.30 |
| MariaDB | 10.5.29 |
安装策略:全部软件通过
dnf从系统官方软件库安装,不涉及源码编译。
访问策略:仅开放 HTTPS(443),不对外提供明文服务。
一、环境准备
1.1 系统更新
dnf update -y
1.2 安装基础工具
dnf install -y curl wget vim net-tools bash-completion openssl
1.3 关闭 SELinux
生产环境建议保持开启并配置对应策略;测试/快速部署环境可临时关闭。
# 临时关闭
setenforce 0
# 永久关闭(需重启)
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
1.4 配置防火墙
# 开放 HTTPS(443)
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
# 验证规则
firewall-cmd --list-all
二、安装 Nginx 1.24.0
2.1 安装
dnf install -y nginx
2.2 版本验证
nginx -v
# 预期输出:nginx version: nginx/1.24.0
2.3 启动并设为开机自启
systemctl start nginx
systemctl enable nginx
systemctl status nginx
2.4 核心配置文件路径
| 路径 | 说明 |
|---|---|
/etc/nginx/nginx.conf |
主配置文件 |
/etc/nginx/conf.d/ |
扩展配置目录,*.conf 自动加载 |
/usr/share/nginx/html/ |
默认站点根目录 |
/var/log/nginx/ |
日志目录 |
/etc/nginx/ssl/ |
证书存放目录(手动创建) |
2.5 基础优化配置
编辑 /etc/nginx/nginx.conf,在 http 块中调整说明:
http {
# ===== 基础性能 =====
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# ===== 安全加固 =====
server_tokens off; # 隐藏 Nginx 版本号
client_max_body_size 20m;
client_body_buffer_size 128k;
# ===== Gzip 压缩 =====
gzip on;
gzip_vary on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml
text/javascript application/javascript
application/json application/xml
image/svg+xml;
gzip_disable "MSIE [1-6]\.";
# ===== 连接数限制(DDoS 防护) =====
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;
include /etc/nginx/conf.d/*.conf;
}
系统中实际配置文件修改
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
types_hash_max_size 4096;
#== 安全加固 ==
server_tokens off;
client_max_body_size 20m;
client_body_buffer_size 128k;
#== Gzip 压缩 ==
gzip on;
gzip_vary on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml
text/javascript application/javascript
application/json application/xml
image/svg+xml;
gzip_disable "MSIE [1-6]\.";
# ===== 连接数限制(DDoS 防护) =====
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
# server {
# listen 80;
# listen [::]:80;
# server_name _;
# root /usr/share/nginx/html;
#
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
#
# error_page 404 /404.html;
# location = /404.html {
# }
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }
# Settings for a TLS enabled server.
#
# server {
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
# server_name _;
# root /usr/share/nginx/html;
#
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key "/etc/pki/nginx/private/server.key";
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 10m;
# ssl_ciphers PROFILE=SYSTEM;
# ssl_prefer_server_ciphers on;
#
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
#
# error_page 404 /404.html;
# location = /404.html {
# }
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }
}
三、安装 MariaDB 10.5.29
3.1 安装
dnf install -y mariadb-server mariadb
3.2 版本验证
mariadb --version
rpm -qa | grep mariadb-server
# 预期:mariadb-server-10.5.29
3.3 启动并设为开机自启
systemctl start mariadb
systemctl enable mariadb
systemctl status mariadb
3.4 安全初始化
mysql_secure_installation
交互式回答指南:
Enter current password for root (enter for none): 直接回车(初始无密码)
Switch to unix_socket authentication [Y/n]: n
Change the root password? [Y/n]: Y
New password: 输入强密码
Re-enter new password: 再次输入
Remove anonymous users? [Y/n]: Y
Disallow root login remotely? [Y/n]: Y
Remove test database and access to it? [Y/n]: Y
Reload privilege tables now? [Y/n]: Y
3.5 创建应用数据库和用户
示例:
mysql -u root -p
-- 创建数据库
CREATE DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建专用用户(仅本地连接)
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'YourStrongPassword123!';
-- 授权
GRANT ALL PRIVILEGES ON app_db.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
SHOW DATABASES;
SELECT user, host FROM mysql.user;
EXIT;
具体配置一个测试库并写入测试数据:
-- 创建数据库
CREATE DATABASE test_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建专用用户(仅可信地址连接)
CREATE USER 'test_user'@'192.168.171.%' IDENTIFIED BY 'KK********';
CREATE USER 'test_user'@'localhost' IDENTIFIED BY 'KK********';
-- 授权
GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'192.168.171.%';
GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'localhost';
FLUSH PRIVILEGES;
SHOW DATABASES;
SELECT user, host FROM mysql.user;
EXIT;
写入测试表:
-- ----------------------
-- Create Customers table
-- ----------------------
CREATE TABLE Customers
(
cust_id char(10) NOT NULL ,
cust_name char(50) NOT NULL ,
cust_address char(50) NULL ,
cust_city char(50) NULL ,
cust_state char(5) NULL ,
cust_zip char(10) NULL ,
cust_country char(50) NULL ,
cust_contact char(50) NULL ,
cust_email char(255) NULL
);
-- -----------------------
-- Create OrderItems table
-- -----------------------
CREATE TABLE OrderItems
(
order_num int NOT NULL ,
order_item int NOT NULL ,
prod_id char(10) NOT NULL ,
quantity int NOT NULL ,
item_price decimal(8,2) NOT NULL
);
-- -------------------
-- Create Orders table
-- -------------------
CREATE TABLE Orders
(
order_num int NOT NULL ,
order_date datetime NOT NULL ,
cust_id char(10) NOT NULL
);
-- ---------------------
-- Create Products table
-- ---------------------
CREATE TABLE Products
(
prod_id char(10) NOT NULL ,
vend_id char(10) NOT NULL ,
prod_name char(255) NOT NULL ,
prod_price decimal(8,2) NOT NULL ,
prod_desc text NULL
);
-- --------------------
-- Create Vendors table
-- --------------------
CREATE TABLE Vendors
(
vend_id char(10) NOT NULL ,
vend_name char(50) NOT NULL ,
vend_address char(50) NULL ,
vend_city char(50) NULL ,
vend_state char(5) NULL ,
vend_zip char(10) NULL ,
vend_country char(50) NULL
);
-- -------------------
-- Define primary keys
-- -------------------
ALTER TABLE Customers ADD PRIMARY KEY (cust_id);
ALTER TABLE OrderItems ADD PRIMARY KEY (order_num, order_item);
ALTER TABLE Orders ADD PRIMARY KEY (order_num);
ALTER TABLE Products ADD PRIMARY KEY (prod_id);
ALTER TABLE Vendors ADD PRIMARY KEY (vend_id);
-- -------------------
-- Define foreign keys
-- -------------------
ALTER TABLE OrderItems ADD CONSTRAINT FK_OrderItems_Orders FOREIGN KEY (order_num) REFERENCES Orders (order_num);
ALTER TABLE OrderItems ADD CONSTRAINT FK_OrderItems_Products FOREIGN KEY (prod_id) REFERENCES Products (prod_id);
ALTER TABLE Orders ADD CONSTRAINT FK_Orders_Customers FOREIGN KEY (cust_id) REFERENCES Customers (cust_id);
ALTER TABLE Products ADD CONSTRAINT FK_Products_Vendors FOREIGN KEY (vend_id) REFERENCES Vendors (vend_id);
写入测试数据:
-- ------------------------
-- Populate Customers table
-- ------------------------
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES('1000000001', 'Village Toys', '200 Maple Lane', 'Detroit', 'MI', '44444', 'USA', 'John Smith', 'sales@villagetoys.com');
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)
VALUES('1000000002', 'Kids Place', '333 South Lake Drive', 'Columbus', 'OH', '43333', 'USA', 'Michelle Green');
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES('1000000003', 'Fun4All', '1 Sunny Place', 'Muncie', 'IN', '42222', 'USA', 'Jim Jones', 'jjones@fun4all.com');
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact, cust_email)
VALUES('1000000004', 'Fun4All', '829 Riverside Drive', 'Phoenix', 'AZ', '88888', 'USA', 'Denise L. Stephens', 'dstephens@fun4all.com');
INSERT INTO Customers(cust_id, cust_name, cust_address, cust_city, cust_state, cust_zip, cust_country, cust_contact)
VALUES('1000000005', 'The Toy Store', '4545 53rd Street', 'Chicago', 'IL', '54545', 'USA', 'Kim Howard');
-- ----------------------
-- Populate Vendors table
-- ----------------------
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES('BRS01','Bears R Us','123 Main Street','Bear Town','MI','44444', 'USA');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES('BRE02','Bear Emporium','500 Park Street','Anytown','OH','44333', 'USA');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES('DLL01','Doll House Inc.','555 High Street','Dollsville','CA','99999', 'USA');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES('FRB01','Furball Inc.','1000 5th Avenue','New York','NY','11111', 'USA');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES('FNG01','Fun and Games','42 Galaxy Road','London', NULL,'N16 6PS', 'England');
INSERT INTO Vendors(vend_id, vend_name, vend_address, vend_city, vend_state, vend_zip, vend_country)
VALUES('JTS01','Jouets et ours','1 Rue Amusement','Paris', NULL,'45678', 'France');
-- -----------------------
-- Populate Products table
-- -----------------------
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BR01', 'BRS01', '8 inch teddy bear', 5.99, '8 inch teddy bear, comes with cap and jacket');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BR02', 'BRS01', '12 inch teddy bear', 8.99, '12 inch teddy bear, comes with cap and jacket');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BR03', 'BRS01', '18 inch teddy bear', 11.99, '18 inch teddy bear, comes with cap and jacket');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BNBG01', 'DLL01', 'Fish bean bag toy', 3.49, 'Fish bean bag toy, complete with bean bag worms with which to feed it');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BNBG02', 'DLL01', 'Bird bean bag toy', 3.49, 'Bird bean bag toy, eggs are not included');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('BNBG03', 'DLL01', 'Rabbit bean bag toy', 3.49, 'Rabbit bean bag toy, comes with bean bag carrots');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('RGAN01', 'DLL01', 'Raggedy Ann', 4.99, '18 inch Raggedy Ann doll');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('RYL01', 'FNG01', 'King doll', 9.49, '12 inch king doll with royal garments and crown');
INSERT INTO Products(prod_id, vend_id, prod_name, prod_price, prod_desc)
VALUES('RYL02', 'FNG01', 'Queen doll', 9.49, '12 inch queen doll with royal garments and crown');
-- ---------------------
-- Populate Orders table
-- ---------------------
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20005, '2020-05-01', '1000000001');
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20006, '2020-01-12', '1000000003');
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20007, '2020-01-30', '1000000004');
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20008, '2020-02-03', '1000000005');
INSERT INTO Orders(order_num, order_date, cust_id)
VALUES(20009, '2020-02-08', '1000000001');
-- -------------------------
-- Populate OrderItems table
-- -------------------------
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20005, 1, 'BR01', 100, 5.49);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20005, 2, 'BR03', 100, 10.99);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20006, 1, 'BR01', 20, 5.99);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20006, 2, 'BR02', 10, 8.99);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20006, 3, 'BR03', 10, 11.99);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20007, 1, 'BR03', 50, 11.49);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20007, 2, 'BNBG01', 100, 2.99);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20007, 3, 'BNBG02', 100, 2.99);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20007, 4, 'BNBG03', 100, 2.99);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20007, 5, 'RGAN01', 50, 4.49);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20008, 1, 'RGAN01', 5, 4.99);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20008, 2, 'BR03', 5, 11.99);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20008, 3, 'BNBG01', 10, 3.49);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20008, 4, 'BNBG02', 10, 3.49);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20008, 5, 'BNBG03', 10, 3.49);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20009, 1, 'BNBG01', 250, 2.49);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20009, 2, 'BNBG02', 250, 2.49);
INSERT INTO OrderItems(order_num, order_item, prod_id, quantity, item_price)
VALUES(20009, 3, 'BNBG03', 250, 2.49);
3.6 MariaDB 性能基础调优
编辑 /etc/my.cnf.d/mariadb-server.cnf,在 [mysqld] 段添加:
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
innodb_buffer_pool_size = 512M
innodb_log_file_size = 128M
innodb_log_buffer_size = 16M
default-time_zone = '+8:00'
log_output = FILE
log_queries_not_using_indexes = 1
max_connections = 200
max_connect_errors = 100
query_cache_type = 0
slow_query_log = 1
slow_query_log_file = /var/log/mariadb/slow.log
long_query_time = 2
systemctl restart mariadb
查看日志目录并授权 mysql 用户读写:
mkdir -p /var/log/mariadb
chown mysql:mysql /var/log/mariadb
四、安装 PHP 8.3.30
4.1 搜索可用版本
# 列出所有php主包
dnf list available php
# 查看全部php相关包
dnf search php
4.2 安装 PHP 8.3 及常用扩展
dnf install -y php php-fpm
dnf install -y \
php \
php-fpm \
php-cli \
php-common \
php-mysqlnd \
php-pdo \
php-gd \
php-mbstring \
php-xml \
php-json \
php-curl \
php-zip \
php-opcache \
php-bcmath \
php-intl \
php-soap \
php-ldap
4.3 版本验证
php -v
# 预期输出:PHP 8.3.30
4.4 配置 PHP-FPM
编辑 /etc/php-fpm.d/www.conf:
user = nginx
group = nginx
; 推荐 Unix Socket(性能好,安全)配置时 listen.acl_users / listen.acl_groups 白名单(ACL 权限控制),文件属主、属组配置 listen.owner / listen.group 会警告。两种权限控制方式互斥,不能同时生效,
; 下面两行一定要注释,否则报ACL警告
;listen.acl_users =
;listen.acl_groups =
listen = /run/php-fpm/www.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660
编辑 /etc/php.ini:
date.timezone = Asia/Shanghai
max_execution_time = 300
memory_limit = 256M
post_max_size = 20M
upload_max_filesize = 20M
expose_php = Off
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/php-fpm/error.log
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=2
opcache.fast_shutdown=1
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1
session.cookie_samesite = "Lax"
file_uploads = On
max_file_uploads = 10
disable_functions = exec,passthru,system,shell_exec,popen,proc_open
session.gc_maxlifetime = 1440
session.cookie_lifetime = 86400
错误写入日志,目录需创建并授权:
mkdir -p /var/log/php-fpm
chown nginx:nginx /var/log/php-fpm
4.5 启动 PHP-FPM
systemctl start php-fpm
systemctl enable php-fpm
systemctl status php-fpm
五、生成自签名 SSL 证书
5.1 创建证书目录
mkdir -p /etc/nginx/ssl
chmod 700 /etc/nginx/ssl
5.2 生成私钥与自签名证书
使用 openssl 一次性生成 RSA 2048 位私钥 + 有效期 3650 天(10 年)的自签名证书:
openssl req -x509 -nodes -newkey rsa:2048 \
-keyout /etc/nginx/ssl/server.key \
-out /etc/nginx/ssl/server.crt \
-days 3650 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=TestCompany/OU=IT/CN=test_service"
参数说明:
参数 说明 -x509输出自签名证书(而非 CSR) -nodes私钥不加密口令(服务器自动启动不需要手动输密码) -newkey rsa:2048新建 RSA 2048 位密钥对 -days 3650证书有效期 10 年 -subj证书主题, CN填写服务器 IP 或域名
| 字段 | 全称 | 含义 | 示例 |
|---|---|---|---|
/C=CN |
Country | 国家二字代码,中国固定 CN |
C=CN |
/ST= |
State/Province | 省份 / 直辖市 | ST=Beijing |
/L= |
Locality | 城市 | L=Beijing |
/O= |
Organization | 公司 / 组织名称 | O=TestCompany |
/OU= |
Organizational Unit | 部门 | OU=IT |
/CN= |
Common Name | 核心字段,服务器域名 / IP | CN=192.168.171.10 或 CN=web.test.com |
5.3 生成 DH 参数(增强前向安全性)
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
此步骤会消耗约 1~3 分钟 CPU,请耐心等待。
5.4 设置证书文件权限
# 私钥仅 root 可读
chmod 600 /etc/nginx/ssl/server.key
chmod 644 /etc/nginx/ssl/server.crt
chmod 644 /etc/nginx/ssl/dhparam.pem
# 验证
ls -la /etc/nginx/ssl/
六、Nginx HTTPS 站点配置
6.1 删除或归档旧配置
# 若 conf.d 下存在默认 HTTP 配置,先备份
mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak 2>/dev/null || true
6.2 创建 HTTPS 站点配置
新建 /etc/nginx/conf.d/lnmp-ssl.conf:
# ============================================================
# Server Block 1 — HTTP 监听(仅做 301 跳转,不提供实际内容),注释掉不适用http
# ============================================================
#server {
# listen 80;
# server_name 192.168.171.188;
#
# # 所有 HTTP 请求 301 永久重定向到 HTTPS
# return 301 https://$host$request_uri;
#}
# ============================================================
# Server Block 2 — HTTPS 主站点
# ============================================================
server {
listen 443 ssl;
server_name 192.168.177.188; # 替换为实际域名或 IP
root /usr/share/nginx/html;
index index.php index.html index.htm;
# ===== 日志 =====
access_log /var/log/nginx/lnmp-ssl-access.log;
error_log /var/log/nginx/lnmp-ssl-error.log;
# ===== SSL 证书 =====
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# ===== SSL 协议与加密套件 =====
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# ===== DH 参数 =====
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
# ===== SSL Session 缓存(提升性能) =====
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# ===== OCSP Stapling(自签名证书无效,注释保留) =====
# ssl_stapling on;
# ssl_stapling_verify on;
# ===== 安全响应头 =====
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # HSTS
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
# ===== PHP 处理 =====
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
fastcgi_connect_timeout 60;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
}
# ===== 禁止访问隐藏文件 =====
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# ===== 静态资源缓存 =====
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# ===== 限制请求方法 =====
if ($request_method !~ ^(GET|HEAD|POST)$) {
return 405;
}
}
6.3 验证并重载 Nginx
nginx -t
# 预期:syntax is ok / test is successful
systemctl reload nginx
6.4 确认端口监听
ss -tlnp | grep -E ':(80|443)'
# 预期:
# LISTEN 0 ... 0.0.0.0:80 ... nginx
# LISTEN 0 ... 0.0.0.0:443 ... nginx
七、集成测试
7.1 浏览器验证 HTTPS
访问 https://<服务器IP>:
- 浏览器会显示"证书不受信任"警告(自签名证书正常现象),点击"继续访问"
- 应看到 Nginx 默认欢迎页
- 地址栏显示 🔒 图标(证书信息)
7.2 curl 命令行验证
# 测试 HTTPS(-k 忽略自签名证书验证)
curl -kv https://127.0.0.1 2>&1 | grep -E '(SSL|TLS|issuer|subject|HTTP)'
# 测试 HTTP → HTTPS 跳转
curl -v http://127.0.0.1 2>&1 | grep -E '(Location|HTTP)'
# 预期:< HTTP/1.1 301 Moved Permanently
# < Location: https://127.0.0.1/
7.3 创建 PHP 探针页(测试后立即删除)
cat > /usr/share/nginx/html/info.php << 'EOF'
<?php phpinfo(); ?>
EOF
chown nginx:nginx /usr/share/nginx/html/info.php
浏览器访问 https://<服务器IP>/info.php,确认:
- PHP Version 显示 8.3.30
- Server API 显示 FPM/FastCGI
$_SERVER['HTTPS']值为on
7.4 数据库连接测试
cat > /usr/share/nginx/html/dbtest.php << 'EOF'
<?php
$host = '192.168.171.1';
$dbname = 'test_db';
$user = 'test_user';
$pass = 'KK********';
try {
$pdo = new PDO(
"mysql:host=$host;dbname=$dbname;charset=utf8mb4",
$user,
$pass,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
$stmt = $pdo->query("SELECT VERSION() AS ver");
$row = $stmt->fetch();
echo "<h2>LNMP 环境连接成功!</h2>";
echo "<p>MariaDB 版本: " . htmlspecialchars($row['ver']) . "</p>";
echo "<p>PHP 版本: " . phpversion() . "</p>";
echo "<p>HTTPS: " . (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? '已启用 ✓' : '未启用') . "</p>";
} catch (PDOException $e) {
http_response_code(500);
echo "<h2>连接失败</h2>";
echo "<p>错误: " . htmlspecialchars($e->getMessage()) . "</p>";
}
EOF
chown nginx:nginx /usr/share/nginx/html/dbtest.php
访问 https://<服务器IP>/dbtest.php。
7.5 测试完成后清理
rm -f /usr/share/nginx/html/info.php
rm -f /usr/share/nginx/html/dbtest.php
安全警告:
phpinfo()会暴露大量服务器信息,生产环境严禁保留。
八、安全加固检查清单
| 序号 | 检查项 | 命令/方法 |
|---|---|---|
| 1 | 删除测试文件 | ls /usr/share/nginx/html/*.php |
| 2 | 确认 expose_php = Off |
grep expose_php /etc/php.ini |
| 3 | 确认 display_errors = Off |
grep display_errors /etc/php.ini |
| 4 | 确认 server_tokens off |
grep server_tokens /etc/nginx/nginx.conf |
| 5 | 确认 HTTP 强制跳转 HTTPS(本文档不涉及,http已关闭) | curl -v http://127.0.0.1 | grep Location |
| 6 | 确认 HSTS 响应头 | curl -kI https://127.0.0.1 | grep Strict |
| 7 | 私钥权限 600 | ls -la /etc/nginx/ssl/server.key |
| 8 | MariaDB root 仅本地登录 | SELECT user,host FROM mysql.user WHERE user='root'; |
| 9 | PHP-FPM 以 nginx 用户运行 | ps aux | grep php-fpm |
| 10 | TLS 协议不低于 1.2 | curl -kv --tls-max 1.1 https://127.0.0.1(应连接失败) |
| 11 | 防火墙只允许指定主机登录 | firewall-cmd --add-rich-rule='rule family=ipv4 source address=192.168.171.0/24 port port=3306 protocol=tcp accept' --permanent |
8.1 目录权限建议
# 站点根目录:root 所有,nginx 只读
chown -R root:root /usr/share/nginx/html/
chmod -R 755 /usr/share/nginx/html/
# 需写入的目录(上传目录、缓存目录)
mkdir -p /usr/share/nginx/html/upload
chown -R nginx:nginx /usr/share/nginx/html/upload
chmod -R 755 /usr/share/nginx/html/upload
九、常用运维命令速查
# ===== 服务管理 =====
systemctl {start|stop|restart|reload|status} nginx
systemctl {start|stop|restart|status} php-fpm
systemctl {start|stop|restart|status} mariadb
# ===== Nginx =====
nginx -t # 配置语法检查
nginx -s reload # 平滑重载
tail -f /var/log/nginx/lnmp-ssl-error.log # 实时错误日志
# ===== SSL 证书检查 =====
openssl x509 -in /etc/nginx/ssl/server.crt -noout -text | grep -E '(Subject|Issuer|Not Before|Not After)'
# 查看证书到期时间
openssl x509 -in /etc/nginx/ssl/server.crt -noout -enddate
# ===== MariaDB =====
mysql -u root -p
#备份
mysqldump -u root -p app_db > backup.sql
# ===== PHP =====
php -v #查看版本
php -m # 列出已安装模块
tail -f /var/log/php-fpm/error.log #查看错误错误日志
# ===== 端口监听检查 =====
ss -tlnp | grep -E ':(80|443|3306)'
十、常见问题排错
10.1 Nginx 报 502 Bad Gateway
systemctl status php-fpm
ls -la /run/php-fpm/www.sock
tail -50 /var/log/php-fpm/error.log
tail -50 /var/log/nginx/lnmp-ssl-error.log
10.2 浏览器显示"SSL_ERROR_RX_RECORD_TOO_LONG"
原因:浏览器通过 HTTPS 访问时,服务器实际返回了 HTTP 内容。
排查:
# 确认 443 端口的 server block 有 ssl 指令
grep -n 'ssl' /etc/nginx/conf.d/lnmp-ssl.conf
# 确认证书文件存在且路径正确
ls -la /etc/nginx/ssl/
nginx -t
10.3 HTTP 没有跳转到 HTTPS(本文档不涉及,已经禁用http)
# 确认 80 端口的 server block 存在
grep -A5 'listen.*80' /etc/nginx/conf.d/lnmp-ssl.conf
# 确认 Nginx 已重载
systemctl reload nginx
# 测试
curl -v http://127.0.0.1
10.4 证书警告与信任(自签名场景)
自签名证书在所有浏览器中均会显示不受信任警告,这是正常现象:
- Chrome/Edge:点击"高级" → “继续前往”
- Firefox:点击"接受风险并继续"
- curl:使用
-k或--insecure参数跳过验证 - 将证书导入系统/浏览器信任库(适用于内网测试环境):
# 将证书复制到系统 CA 目录并更新信任库(openEuler)
cp /etc/nginx/ssl/server.crt /etc/pki/ca-trust/source/anchors/lnmp-self-signed.crt
update-ca-trust
10.5 PHP 页面空白或报 500 错误
# 临时启用错误显示(仅排查用!)
sed -i 's/display_errors = Off/display_errors = On/' /etc/php.ini
systemctl restart php-fpm
# 排查完毕后务必改回 Off
10.6 上传文件被截断
确保以下三项一致:
grep -E '(upload_max_filesize|post_max_size)' /etc/php.ini
grep client_max_body_size /etc/nginx/nginx.conf
# 要求:client_max_body_size >= post_max_size >= upload_max_filesize
十一、快速安装脚本(附录)
保存为 install_lnmp.sh,执行前确认变量值:
#!/bin/bash
set -e
# =============================================
# LNMP 一键部署脚本(HTTPS 自签名)
# OS: openEuler 24.03
# 组件: Nginx 1.24.0 / PHP 8.3 / MariaDB 10.5
# =============================================
# ---------- 可修改变量 ----------
CERT_SUBJ="/C=CN/ST=Province/L=City/O=MyOrg/OU=IT"
CERT_DAYS=3650
SSL_DIR="/etc/nginx/ssl"
# --------------------------------
echo "===== Step 1: 系统更新 ====="
dnf update -y
dnf install -y openssl
echo "===== Step 2: 安装 Nginx ====="
dnf install -y nginx
systemctl start nginx && systemctl enable nginx
nginx -v
echo "===== Step 3: 安装 MariaDB ====="
dnf install -y mariadb-server mariadb
systemctl start mariadb && systemctl enable mariadb
echo "===== Step 4: 安装 PHP 8.3 ====="
dnf install -y \
php php-fpm php-cli php-common \
php-mysqlnd php-pdo php-gd php-mbstring \
php-xml php-json php-curl php-zip \
php-opcache php-bcmath php-intl php-soap php-ldap
php -v
echo "===== Step 5: 配置 PHP-FPM ====="
sed -i 's/^user = apache/user = nginx/' /etc/php-fpm.d/www.conf
sed -i 's/^group = apache/group = nginx/' /etc/php-fpm.d/www.conf
sed -i 's|^listen = 127.0.0.1:9000|listen = /run/php-fpm/www.sock|' /etc/php-fpm.d/www.conf
sed -i 's/^;listen.owner = nobody/listen.owner = nginx/' /etc/php-fpm.d/www.conf
sed -i 's/^;listen.group = nobody/listen.group = nginx/' /etc/php-fpm.d/www.conf
sed -i 's/^;listen.mode = 0660/listen.mode = 0660/' /etc/php-fpm.d/www.conf
systemctl start php-fpm && systemctl enable php-fpm
echo "===== Step 6: 生成自签名证书 ====="
mkdir -p "$SSL_DIR"
chmod 700 "$SSL_DIR"
openssl req -x509 -nodes -newkey rsa:2048 \
-keyout "${SSL_DIR}/server.key" \
-out "${SSL_DIR}/server.crt" \
-days "$CERT_DAYS" \
-subj "$CERT_SUBJ"
echo "正在生成 DH 参数(需要约 1~3 分钟)..."
openssl dhparam -out "${SSL_DIR}/dhparam.pem" 2048
chmod 600 "${SSL_DIR}/server.key"
chmod 644 "${SSL_DIR}/server.crt"
chmod 644 "${SSL_DIR}/dhparam.pem"
echo "===== Step 7: 配置 Nginx HTTPS 站点 ====="
cat > /etc/nginx/conf.d/lnmp-ssl.conf << 'NGINXEOF'
server {
listen 80;
server_name _;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name _;
root /usr/share/nginx/html;
index index.php index.html index.htm;
access_log /var/log/nginx/lnmp-ssl-access.log;
error_log /var/log/nginx/lnmp-ssl-error.log;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
}
location ~ /\. {
deny all;
}
}
NGINXEOF
nginx -t && systemctl reload nginx
echo "===== Step 8: 配置防火墙 ====="
firewall-cmd --permanent --add-service=http 2>/dev/null || true
firewall-cmd --permanent --add-service=https 2>/dev/null || true
firewall-cmd --add-rich-rule='rule family=ipv4 source address=192.168.171.0/24 port port=3306 protocol=tcp accept' --permanent 2>/dev/null || true
firewall-cmd --reload 2>/dev/null || true
echo ""
echo "===== 部署完成 ====="
echo "Nginx: $(nginx -v 2>&1)"
echo "MariaDB: $(rpm -q mariadb-server)"
echo "PHP: $(php -v 2>&1 | head -1)"
echo ""
echo "证书信息:"
openssl x509 -in "${SSL_DIR}/server.crt" -noout -subject -enddate
echo ""
echo "后续步骤:"
echo " 1. 运行 mysql_secure_installation 完成 MariaDB 安全初始化"
echo " 2. 访问 https://<服务器IP> 验证(浏览器会显示自签名证书警告,属正常)"
echo " 3. 访问 http://<服务器IP> 确认自动跳转到 HTTPS"
执行方式:
chmod +x install_lnmp.sh
# 修改脚本中的 CERT_SUBJ 变量,将 CN 替换为实际 IP 或域名,再执行:
./install_lnmp.sh
文档版本: v1.1
适用范围: openEuler 24.03 + Nginx 1.24.0 + PHP 8.3.30 + MariaDB 10.5.29
安装方式: dnf 系统仓库安装
访问协议: 仅 HTTPS(自签名证书),HTTP 强制 301 跳转
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)