掉进了 Linux 运维和 Docker 部署中最经典的三个"套娃陷阱"。
简单来说,我试图在一台小排量的代步车(1G 内存服务器)上,同时拉着三台大卡车(MySQL 8.0、Node 后端、npm 安装),结果导致车轮锁死(I/O 阻塞),而且车门(权限)还被反锁了。
以下是导致我之前"一直不行"的三个核心死结:
1. "资源内耗"陷阱:小马被大车压垮了
这是最初 SSH 连不上、打字卡顿的唯一原因。
-
你做了什么: 你在 1G 内存的服务器上跑了 MySQL 8.0。
-
发生了什么: MySQL 8.0 默认会预占大量内存。当你启动它的那一刻,物理内存被吃光,Linux 启动了 Swap(虚拟内存) 。由于虚拟内存是靠硬盘模拟的,读写速度比物理内存慢几千倍,这直接导致了你截图里的
74.7% wa(I/O 等待)。 -
后果: CPU 核心在苦苦等待硬盘响应,导致 SSH 指令排不上队,甚至连 Docker 命令都发不出去。
2. "空间扭曲"陷阱:Nginx 在盲人摸象
之前一直看到 404 的根源。
-
你做了什么: 你在 Nginx 的
.conf里写了网页路径,但忘记在docker run命令里把宿主机的代码文件夹映射进去。 -
发生了什么: * 宿主机:
/home/channie/gcone/acg-faka(有代码)- Nginx 容器:
/usr/share/nginx/html/acg-faka(空的)
- Nginx 容器:
-
后果: Nginx 非常勤快地去它能看到的文件夹找
/install,结果里面空空如也,所以只能无奈地回你一个 404。
3. "权限孤岛"陷阱:PHP 进不去自己的家
哪怕修好了 404,程序依然报错的原因。
-
做了什么: 可能在某些操作中用了
root权限(或者容器自动生成的文件夹),导致runtime等关键目录的所有者变成了root。 -
发生了什么: PHP 容器通常是以普通用户身份运行的。当它想要写日志、存缓存或者创建 SQLite 数据库文件时,发现
runtime目录被root锁死了,它没有钥匙。 -
后果: 程序在初始化阶段就因为"无法写入文件"而卡死,反映到前端就是各种白屏或无法跳转。
💡 为什么现在突然行了?
因为我们针对性地解开了这三个死结:
-
释放资源 :我们杀掉了最耗资源的进程,并选择了更轻量的 SQLite。
-
打通空间 :我们在
docker run里加入了正确的-v挂载参数,让 Nginx 真正看到了代码。 -
强制洗白 :我们用 Docker 的 root 权限暴力执行了
chmod -R 777,把所有文件的控制权交还给了程序。
第一阶段:清理战场(释放被榨干的内存)
首先,我们要把那些导致系统卡死、疯狂报错的"僵尸"进程和容器彻底杀掉。
# 1. 停止并删除所有相关的旧容器
docker stop gcone-nginx acg-faka-php acg-faka-mysql gcone-server 2>/dev/null
docker rm gcone-nginx acg-faka-php acg-faka-mysql 2>/dev/null
# 2. 杀掉可能还在后台刷报错日志的 Node 进程(释放 CPU 和内存)
pkill -9 node
# 3. 强制释放系统缓存
echo 3 | sudo tee /proc/sys/vm/drop_caches
# 4. 查看当前内存,确保 "available" 至少有 400MB 以上
free -h
第二阶段:权限大赦(拆掉 Docker 的"围墙")
因为部分文件夹是 root 创建的,你的 PHP 容器进不去。我们借用 Docker 的 root 权限强行洗白。
# 使用临时容器将代码目录权限全开,并将所有权交还给你的 channie 用户 (UID 1001)
docker run --rm -v /home/channie/gcone/acg-faka:/data alpine sh -c "chmod -R 777 /data && chown -R 1001:1001 /data"
# 检查一下 runtime 目录,确保不再是 root 所有的
ls -l /home/channie/gcone/acg-faka/runtime
第三阶段:部署核心组件(PHP 与 Nginx)
1. 启动 PHP-FPM 并安装缺失扩展
由于 ACG-Faka 需要 gd、zip 等插件,原版镜像没有,我们需要跑起来后手动补装:
# 运行 PHP 容器
docker run -d --name acg-faka-php --restart always \
-v /home/channie/gcone/acg-faka:/usr/share/nginx/html/acg-faka \
--add-host=host.docker.internal:host-gateway \
php:8.1-fpm-alpine
# 【核心】在容器内安装环境检查里变红的那些插件(这一步可能需要 1-2 分钟,请耐心等待)
docker exec -it acg-faka-php sh -c "apk add --no-cache freetype-dev libjpeg-turbo-dev libpng-dev libzip-dev && docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install -j$(nproc) gd pdo_mysql zip"
# 重启 PHP 让插件生效
docker restart acg-faka-php
2. 启动 Nginx(指路明灯)
确保宿主机的代码路径正确挂载到 Nginx 预期的位置。
docker run -d --name gcone-nginx --restart always \
-p 80:80 -p 443:443 \
-v /home/channie/gcone/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /home/channie/gcone/dist:/usr/share/nginx/html:ro \
-v /home/channie/gcone/acg-faka:/usr/share/nginx/html/acg-faka:rw \
--add-host=host.docker.internal:host-gateway \
nginx:latest
第四阶段:数据库方案(SQLite 终极省钱流)
如果你决定用 SQLite(强烈推荐,1H1G 机器的救命恩人):
-
不需要运行任何数据库容器。
-
直接访问网页,在数据库类型勾选
SQLite。 -
程序会自动在
config/database.php同级或database目录下创建一个.db文件。
如果你非要用 MariaDB,请执行这行(已做内存优化):
docker run -d --name acg-faka-mysql --restart always \
--memory="300m" \
-e MYSQL_ROOT_PASSWORD=lqyxiaoxiannv666 \
-e MYSQL_DATABASE=acg_faka \
-e MYSQL_USER=channie \
-e MYSQL_PASSWORD=lqyxiaoxiannv666 \
-v /home/channie/new-db:/var/lib/mysql \
-p 3306:3306 \
mariadb:10.11 \
--innodb-buffer-pool-size=64M
第五阶段:最后的验证与上线
-
检查所有容器状态:
Bash
docker ps确认 nginx、php、mysql(如果用了的话)都是 Up 状态。
-
清理浏览器缓存:
按下
Ctrl + F5刷新https://shop.channie.me/install。 -
如果安装时提示"目录不可写":
再次执行那个暴力权限命令(因为 PHP 运行时可能会产生新文件):
docker run --rm -v /home/channie/gcone/acg-faka:/data alpine chmod -R 777 /data
💡 总结你的"避坑指南"表
| 环节 | 坑点 | 记住这一招 |
|---|---|---|
| 内存 | MySQL 8.0 + npm install = 炸弹 | 1G 内存首选 SQLite |
| 路径 | Nginx 找不到文件报 404 | -v 挂载路径必须跟 nginx.conf 对齐 |
| 权限 | root 锁死 runtime 目录 | 用 docker run --rm ... chmod 强破 |
| 环境 | 原版 PHP 镜像没插件 | docker exec 进去跑 docker-php-ext-install |

1. 进入 PHP 容器并安装插件(针对 Alpine 版本的镜像)
docker exec -it acg-faka-php sh -c "apk add --no-cache freetype-dev libjpeg-turbo-dev libpng-dev libzip-dev && docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install -j$(nproc) gd pdo_mysql zip"
2. 重启 PHP 容器,让刚装好的插件生效
docker restart acg-faka-php
现在"按不了"是因为底部的按钮是灰色禁用状态 。发卡系统的安装程序非常严谨:只要页面上有一个红色的 "不支持",它就不让你进入下一步,防止安装完后程序崩溃。
你截图里的 gd (图片处理)、pdo_mysql (数据库连接)和 zip(压缩包解压)是必须要装上的"零件"。
🛠️ 解决办法:一键补齐这些"红色"插件
你只需要在 Xshell 里执行下面这一串命令。它会自动进入你的 PHP 容器,把缺少的零件全部安装并配置好。
直接复制并执行:
# 1. 进入 PHP 容器并安装插件(针对 Alpine 版本的镜像)
docker exec -it acg-faka-php sh -c "apk add --no-cache freetype-dev libjpeg-turbo-dev libpng-dev libzip-dev && docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install -j$(nproc) gd pdo_mysql zip"
# 2. 重启 PHP 容器,让刚装好的插件生效
docker restart acg-faka-php
💡 为什么需要这一步?
Docker 的官方 PHP 镜像为了保持"身材苗条",默认只带了最基础的内核。像 gd 这种处理验证码和图片的插件,或者 zip 这种处理主题包的插件,都需要手动"激活"一下。
执行完后怎么做?
-
等待命令执行完 :安装插件可能需要 30 秒到 1 分钟,看到命令提示符重新出现(
channie@phone:~$)才算完。 -
刷新网页 :回到浏览器,按 F5 或 Crl + F5 刷新安装页面。
-
见证奇迹 :你会发现那几个红色选项全部变成了绿色的对勾。
一旦全绿了,底部的按钮就会变成彩色可点击状态,你就能进到"数据库配置"环节了。
对于 ACG-Faka 这类基于高性能框架(如 ThinkPHP 或 Laravel)开发的系统,你看到的这些对勾(GD、Zip、PDO)确实是最基础、最标准的运行要求。
1. 为什么这些插件是"基础中的基础"?
虽然名字听起来专业,但它们在 PHP 界就像是电脑的"声卡"和"显卡"驱动一样:
-
GD 库 :没有它,网站就无法生成验证码 ,也无法裁剪商品图片。
-
Zip 模块 :没有它,你无法在后台一键更新 系统,也无法解压新下载的主题包。
-
PDO/PDO_MySQL:这是数据库的"翻译官"。没有它,PHP 根本听不懂数据库在说什么。
2. 关于"最低配置"的真相(1H1G)
你听别人说"1H1G 就能跑",这话没错,但**"能跑"和"能随便折腾"**是两回事:
-
"裸奔"模式(1H1G 够用):
如果你直接在 Linux 上装宝塔面板或者原生 LNMP 环境,不走 Docker,1G 内存会跑得很舒服。
-
"套娃"模式(你现在的状态):
你用了 Docker。Docker 虽然方便,但每个容器(Nginx、PHP、MySQL、Node)都会额外吃掉几十 MB 内存。加上你还要跑"小手机"后端,这就像是在一个只能坐 4 个人的小轿车里挤了 6 个人。
3. 如何在"地板配置"上跑出"天花板"性能?
既然服务器硬件(1H1G)短期内不打算升级,我们就得在软件上"抠门":
| 组件 | 你的选择 | 为什么它是最低/最优? |
|---|---|---|
| 系统 | Alpine Linux (PHP 镜像) | 体积最小,只有几 MB,省内存。 |
| 数据库 | SQLite | 核心推荐。它不需要单独的进程,内存占用几乎为零。1H1G 跑 MySQL 纯属浪费。 |
| Web服务器 | Nginx | 比 Apache 轻量得多,处理并发能力强。 |