FreeBSD 15.1在 jail 中安装配置 nginx, php-fpm 与 mariadb
我的 exsi 服务器中已经全新安装了 FreeBSD 15.1虚拟机,我准备在这个虚拟机中新建3个 jail 分别安装 nginx、php-fpm 与 mariadb,以实现各自的独立运行环境与资源隔离。
0. 整体网络与 ip 规划
| 环境/服务 | 内网固定 ip | 外部映射端口 | 作用 |
|---|---|---|---|
| 宿主机 (Host) | 公网 IP / 内网网关 10.0.0.1 |
- | 路由转发、PF 防火墙控制 |
| web_jail (Nginx) | 10.0.0.2 | 10080 / 10443 | 负责静态资源、SSL 解密、反向代理 |
| php_jail (PHP-FPM) | 10.0.0.3 | 不对外开放 | 负责解析 PHP 动态脚本 |
| mariadb_jail (Mariadb) | 10.0.0.4 | 不对外开放 | 负责数据持久化存储 |
1. 宿主机网络配置 (/etc/rc.conf)
1 | # 开启路由转发功能,允许 jail 访问外网 |
1 | # 立即启动虚拟网卡 |
2. 完善防火墙策略 (/etc/pf.conf)
编辑宿主机的 /etc/pf.conf,将外部非标端口准确导向 web_jail:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# 1. 定义接口
ext_if = "vmx0"
int_if = "lo1"
# 2. 定义各个 jail 的静态内网 ip
web_jail = "10.0.0.2"
php_jail = "10.0.0.3"
mariadb_jail = "10.0.0.4"
# 3. 允许 10.0.0.0/24 网段的所有内网流量通过公网网卡进行 NAT 转换出网
nat on $ext_if from 10.0.0.0/24 to any -> ($ext_if)
# 4. 端口映射
rdr on $ext_if proto tcp from any to ($ext_if) port 80 -> $web_jail port 80
rdr on $ext_if proto tcp from any to ($ext_if) port 443 -> $web_jail port 443
# 5. 放行所有常规流量
pass all
1
2# 立即启动 pf 防火墙
service pf start
3. jail 框架创建与初使化
- 编写 jail 全局配置文件 (/etc/jail.conf)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.clean;
mount.devfs;
web_jail {
path = "/usr/local/jails/web_jail";
host.hostname = "web.local";
interface = "lo1";
ip4.addr = "10.0.0.2";
}
php_jail {
path = "/usr/local/jails/php_jail";
host.hostname = "php.local";
interface = "lo1";
ip4.addr = "10.0.0.3";
}
mariadb_jail {
path = "/usr/local/jails/mariadb_jail";
host.hostname = "mariadb.local";
interface = "lo1";
ip4.addr = "10.0.0.4";
} - 下载并初始化三个 jail 的目录系统
1
2
3
4
5
6
7
8
9# 创建 jail 根目录
mkdir -p /usr/local/jails/web_jail
mkdir -p /usr/local/jails/php_jail
mkdir -p /usr/local/jails/mariadb_jail
# 使用 bsdinstall 自动化下载并释放系统文件到各个 jail (需要联网)
bsdinstall jail /usr/local/jails/web_jail
bsdinstall jail /usr/local/jails/php_jail
bsdinstall jail /usr/local/jails/mariadb_jail - 配置 jail 的 DNS 并启动它们
为了让 jail 内部能够使用 pkg 安装软件,需要将宿主机的 DNS 配置文件复制给它们:1
2
3cp /etc/resolv.conf /usr/local/jails/web_jail/etc/
cp /etc/resolv.conf /usr/local/jails/php_jail/etc/
cp /etc/resolv.conf /usr/local/jails/mariadb_jail/etc/ - 启动 jail
1
2# 立即启动所有 jail
service jail start
4. 完善 jail 内部服务配置
- 在宿主机操作代码目录共享
1
2
3
4
5
6
7
8
9# 1. 在宿主机创建一个存放网站代码的公共目录
mkdir -p /home/html
# 2. 将此目录通过挂载的方式共享给 web 和 php 两个 jail
mkdir -p /usr/local/jails/web_jail/home/html
mkdir -p /usr/local/jails/php_jail/home/html
mount_nullfs /home/html /usr/local/jails/web_jail/home/html
mount_nullfs /home/html /usr/local/jails/php_jail/home/html为了让挂载永久生效,可将 mount_nullfs 写入宿主机的 /etc/fstab 中
- 配置 php_jail (php-fpm 环境)
- 进入 PHP 容器:
1
jexec php_jail csh
- 安装 php 及常用扩展,并设置开机自启:
1
pkg install php85 php85-bcmath php85-brotli php85-bz2 php85-bzip3 php85-calendar php85-ctype php85-curl php85-dom php85-enchant php85-exif php85-extensions php85-ffi php85-fileinfo php85-filter php85-ftp php85-gd php85-gettext php85-gmp php85-iconv php85-lz4 php85-mbstring php85-mysqli php85-pgsql php85-posix php85-readline php85-session php85-simplexml php85-soap php85-sockets php85-sodium php85-sqlite3 php85-tidy php85-tokenizer php85-xml php85-xmlreader php85-xmlwriter php85-xsl php85-zip php85-zlib php85-zstd
- 编辑 php-fpm 配置文件 /usr/local/etc/php-fpm.d/www.conf,修改监听模式:
1
2
3
4
5# 找到 listen 这一行,将其修改为监听自己的内网 ip 和 9000 端口
listen = 10.0.0.3:9000
# 找到 listen.allowed_clients,允许 web jail (10.0.0.2) 访问
listen.allowed_clients = 10.0.0.2 - 启动 php-fpm 并退出:
1
2
3sysrc php_fpm_enable=YES
service php_fpm start
exit
- 进入 PHP 容器:
- 配置 web_jail (nginx + ssl 环境)
- 进入 Web 容器:
1
jexec web_jail csh
- 安装 nginx 并设置自启:
1
2pkg install nginx
sysrc nginx_enable=YES - 编辑 /usr/local/etc/nginx/nginx.conf,重点是配置 fastcgi_pass 指向 php jail:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24server {
listen 10.0.0.2:80;
listen 10.0.0.2:443 ssl;
server_name yourdomain.com;
ssl_certificate /root/yourdomain.com/fullchain.cer;
ssl_certificate_key /root/yourdomain.com/yourdomain.com.key;
root /home/html;
index index.php index.html index.htm;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# 关键:将 php 脚本转发给 php_jail
location ~ \.php$ {
root /home/html;
fastcgi_pass 10.0.0.3:9000; # 指向 php 容器的 ip
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
} - 启动 nginx 并退出:
1
2service nginx start
exit
- 进入 Web 容器:
- 配置 mariadb_jail (Mariadb 数据库环境)
- 进入数据库容器:
1
jexec mariadb_jail csh
- 安装 mariadb 服务器并设置自启动
1
2pkg install mariadb118-server
sysrc mysql_enable=YES - 编辑 mariadb 配置文件(FreeBSD 路径通常为 /usr/local/etc/mysql/my.cnf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#
# This group is read both by the client and the server
# use it for options that affect everything, see
# https://mariadb.com/kb/en/configuring-mariadb-with-option-files/#option-groups
#
[client-server]
port = 3306
socket = /var/run/mysql/mysql.sock
[mysqld]
max_allowed_packet = 1024M
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
character-set-client-handshake = FALSE
#
# include *.cnf from the config directory
#
!includedir /usr/local/etc/mysql/conf.d/ - 编辑 /usr/local/etc/mysql/conf.d/server.cnf,确保其监听内网 ip:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49# Options specific to server applications, see
# https://mariadb.com/kb/en/configuring-mariadb-with-option-files/#server-option-groups
# Options specific to all server programs
[server]
# Options specific to MariaDB server programs
[server-mariadb]
#
# Options for specific server tools
#
[mysqld]
user = mysql
# port = 3306 # inherited from /usr/local/etc/mysql/my.cnf
# socket = /var/run/mysql/mysql.sock # inherited from /usr/local/etc/mysql/my.cnf
bind-address = 10.0.0.4
basedir = /usr/local
# datadir = /var/db/mysql # set with --db_dir from rc-script
net_retry_count = 16384
log_error = /var/log/mysql/mysqld.err
# [mysqld] configuration for ZFS
# From https://www.percona.com/resources/technical-presentations/zfs-mysql-percona-technical-webinar
# Create separate datasets for data and logs, eg
# zroot/mysql compression=on recordsize=128k atime=off
# zroot/mysql/data recordsize=16k
# zroot/mysql/logs
# datadir = /var/db/mysql/data
# innodb_log_group_home_dir = /var/db/mysql/log
# audit_log_file = /var/db/mysql/log/audit.log
# general_log_file = /var/db/mysql/log/general.log
# log_bin = /var/db/mysql/log/mysql-bin
# relay_log = /var/db/mysql/log/relay-log
# slow_query_log_file = /var/db/mysql/log/slow.log
# innodb_doublewrite = 0
# innodb_flush_method = O_DSYNC
# Options read by `mariadb-safe` (and `mysql_safe`)
[mariadb-safe]
# Options read my `mariabackup`
[mariabackup]
# Options read by mariadb-upgrade (and `mysql_upgrade`)
[mariadb-upgrade]
# Specific options read by the mariabackup SST method
[sst] - 启动 mariadb 并进行安全初始化:
1
2service mysql-server start
mariadb-secure-installation - 进入 mariadb 命令行,授权 php_jail (10.0.0.3) 能够远程连接数据库:
1
mariadb
- 执行以下 sql 语句(创建 root 远程连接,以便使用 phpMyadmin):
1
2
3create user 'root'@'10.0.0.3' identified by 'your root password';
grant all privileges on *.* to 'root'@'10.0.0.3';
flush privileges;
- 进入数据库容器:
5. 解决跨容器的权限同步问题
- 统一 web jail 与 php jail 的 web 用户 UID
在 FreeBSD nginx 和 php-fpm 默认都使用系统自带的 www 用户和 www 组。最核心的关键:必须确保 web_jail 和 php_jail 内部的 www 用户的 UID(用户ID)和 GID(组ID)完全一致(FreeBSD 默认通常都是 80)。
你可以分别进入两个 Jail 检查一下:如果输出都显示 uid=80(www) gid=80(www),则说明已经统一,可以直接进入下一步。1
2jexec web_jail id www
jexec php_jail id www
- 在宿主机上修正共享目录的权限
由于是 php-fpm 负责接收文件并写入磁盘,因此该目录在宿主机层面,必须允许 UID 80 的用户拥有写权限。- 将整个网站目录的所属用户和组,更改为 80:80(即两边 jail 对应的 www 权限):
1
chown -R 80:80 /home/html
- 设置合理的目录和文件权限:
1
2
3
4
5
6# 网站目录及子目录设置为 755(所有者可读写执行,其他人可读可执行)
find /home/html -type d -exec chmod 755 {} \;
# 网站文件设置为 644(所有者可读写,其他人只读)
find /home/html -type f -exec chmod 644 {} \;
- 将整个网站目录的所属用户和组,更改为 80:80(即两边 jail 对应的 www 权限):
- 配置 php-fpm 允许上传及限制临时目录
我们需要进入 php_jail,确保 php 自身的上传功能已打开,并且其产生临时文件的目录也是 www 用户可以读写的。- 进入 php_jail:
1
jexec php_jail csh
- 修改 php 配置文件(/usr/local/etc/php.ini)
1
2
3
4
5
6file_uploads = On ; 开启文件上传
upload_max_filesize = 50M ; 允许上传的最大单文件大小(根据需求调整)
post_max_size = 50M ; POST 数据的最大容量
; 关键:设置一个 Jail 内部 www 用户绝对有权限读写的临时目录
upload_tmp_dir = "/tmp" - 确保 php_jail 内部的 /tmp 目录权限对 www 开放(FreeBSD 默认开放):
1
chmod 1777 /tmp
- 重启 php-fpm 并退出 jail
1
2service php_fpm restart
exit
- 进入 php_jail:
- 配置 nginx 允许大文件上传
如果用户上传的文件比较大(例如超过 1MB),nginx 默认会直接拦截并返回 413 Request Entity Too Large 错误。因此还需要调整 web_jail 的 nginx 配置。- 进入 web_jail:
1
jexec web_jail csh
- 编辑 /usr/local/etc/nginx/nginx.conf:
1
2
3
4
5
6http {
...
# 关键:允许客户端上传的最大主体大小,必须大于或等于 php.ini 里的设定
client_max_body_size 50m;
...
} - 重启 nginx 服务并退出:
1
2service nginx restart
exit
- 进入 web_jail:
