
liu
# 懒猫微服启动脚本管理框架:5分钟搞定系统配置持久化
> 一个专为懒猫微服平台设计的启动脚本管理框架,让你的系统配置在重启后自动恢复
**项目地址**:https://github.com/lazycatapps/user-startup.d
## 🎯 引子:你是否也遇到这些困扰?
你是否曾经遇到过这样的场景:
- 💔 **精心配置的系统参数,重启后全部消失**
- 😫 **辛苦安装的开发工具,第二天登录时又要重新安装**
- 🤔 **每次重启都要手动执行一堆命令,重复劳动令人崩溃**
如果你正在使用 [懒猫微服平台](https://lazycat.cloud),这些都不是你的错!这是平台的**特殊设计**——系统重启后会自动还原所有修改。
**但是,有没有办法让这些配置自动恢复呢?**
答案是:**有!** 而且只需要 **5 分钟**就能搞定。
---
## 📖 目录
- [一、背景:为什么需要这个工具](#一背景为什么需要这个工具)
- [二、认识:这是个什么样的解决方案](#二认识这是个什么样的解决方案)
- [三、场景:什么时候使用](#三场景什么时候使用)
- [四、快速开始](#四快速开始)
- [五、重要注意事项](#五重要注意事项)
- [六、总结与资源](#六总结与资源)
---
## 一、背景:为什么需要这个工具
### 1.1 懒猫平台的"重启还原"特性
懒猫微服平台采用了一个**独特的系统设计**:**重启后会自动还原所有系统修改**。
这意味着:
- 通过 `apt install` 安装的软件会在重启后消失
- 对系统配置文件的修改会被还原到初始状态
- 精心设置的环境变量无法持久化保存
**为什么要这样设计?** 根据 [官方 FAQ](https://developer.lazycat.cloud/faq-dev.html):
1. **系统状态可预测** - 避免累积的系统修改导致系统处于无法预料的状态
2. **风险管理** - 防止系统更新破坏已有配置
3. **平台安全性** - 防止恶意软件持久化驻留
### 1.2 官方方案 vs 本项目
懒猫官方提供了两种解决方案(详见 [官方文档](https://developer.lazycat.cloud/faq-startup_script.html)):
| 特性 | user-startup.d (Hook) | Systemd 用户服务 | Docker 容器 |
|------|----------------------|-----------------|------------|
| **配置复杂度** | ⭐ 简单 | ⭐⭐ 中等 | ⭐⭐⭐ 复杂 |
| **学习成本** | Shell 脚本基础即可 | 需了解 systemd | 需了解 Docker |
| **日志管理** | ✅ 统一集中 | ❌ 需手动配置 | ❌ 需手动配置 |
| **脚本组织** | ✅ 目录结构清晰 | ❌ 分散的 service 文件 | ❌ 混在 compose 文件中 |
### 1.3 本项目的核心优势
✅ **降低配置门槛** - 直接使用熟悉的 Shell 脚本,无需学习 systemd 或 Docker
✅ **统一日志管理** - 所有日志集中存放在 `/tmp/user-startup-hooks/`
✅ **灵活控制执行顺序** - 使用数字前缀控制脚本执行顺序,支持同步/异步模式
✅ **不阻塞系统启动** - 使用 `nohup` 后台执行,Hook 入口脚本快速退出
✅ **开箱即用** - 提供脚本模板和 Makefile 工具,一键安装/卸载
---
## 二、认识:这是个什么样的解决方案
### 2.1 核心架构
```
user-startup.d/
├── Makefile # 一键安装/卸载工具
├── template.sh # 脚本模板
├── user-startup-entrypoint.sh # Hook 入口(快速派发)
├── main.sh # 主调度器(后台执行)
├── init.async/ # 异步脚本目录(并行执行)
│ └── *.sh
└── init.sync/ # 同步脚本目录(顺序执行)
├── 1.wait-network.sh
├── 2.set-debian-sources.sh
├── 3.install-tools.sh
└── ...
```
### 2.2 执行流程
```
系统启动 → 用户解锁数据盘 → data-disk-ready Hook 触发
↓
user-startup-entrypoint.sh (快速派发)
↓ nohup 后台
main.sh
├─ 阶段1: init.async/*.sh (并行执行)
└─ 阶段2: init.sync/*.sh (顺序执行)
```
**关键特性**:
- **Wrapper 脚本机制** - 使用 wrapper 而非符号链接,避免 SELinux/AppArmor 安全策略限制
- **智能日志系统** - 日志目录结构与源码一致:`/tmp/user-startup-hooks/`
- **非阻塞式执行** - Hook 入口在 1 秒内退出,耗时任务后台执行
### 2.3 async vs sync 的选择
| 类型 | init.async/ | init.sync/ |
|------|-------------|------------|
| **执行方式** | 后台并行,使用 `nohup` | 顺序执行,等待前一个完成 |
| **适用场景** | 耗时且不阻塞的任务 | 有依赖关系的任务 |
| **典型示例** | NFS挂载、DDNS更新、日志上传 | 网络等待、软件安装、配置文件修改 |
---
## 三、场景:什么时候使用
### 3.1 Hook 触发时机
```
系统启动 → 提示输入硬盘密码 → 用户解锁 → 数据盘就绪 → 触发 hook
```
**此时的系统状态**:
| 状态 | 是否就绪 | 说明 |
|------|---------|------|
| 数据盘 | ✅ 已挂载 | 可以安全访问 `/data` 等用户目录 |
| 网络 | ⚠️ 可能未就绪 | 需要主动等待(参考 `1.wait-network.sh`) |
| Docker | ⚠️ 可能未启动 | 如需使用 Docker 需检查服务状态 |
### 3.2 典型应用场景
- **系统配置持久化** - 自动配置软件源、网络参数、IPv4 优先级等
- **自动安装开发工具** - 重启后自动安装 vim、curl、htop 等工具
- **挂载网络存储** - 自动挂载 NFS、CIFS 共享目录
- **配置 Docker 环境** - 自动创建 docker contexts、配置 DNS 解析器
- **服务自动启动** - 启动监控服务、DDNS 更新等
---
## 四、快速开始
### 4.1 安装项目(1 分钟)
```bash
# 1. 克隆项目到懒猫微服推荐位置
cd /root
# 你也可以 Fork 到自己的组织下后,将 替换为你自己的组织名
git clone https://github.com/lazycatapps/user-startup.d.git
cd user-startup.d
# 2. 安装系统 hook
make install
# 3. 验证安装状态
make status
```
**预期输出**:
```
Wrapper script is installed:
-rwxr-xr-x 1 root root 153 Jan 11 12:52 /lzcsys/var/custom/hooks/data-disk-ready/user-startup-entrypoint.sh
Content:
#!/bin/bash
# Auto-generated wrapper script
# This script calls the actual entrypoint script
exec "/root/user-startup.d/user-startup-entrypoint.sh" "$@"
```

### 4.2 创建第一个脚本(2 分钟)
```bash
# 1. 从模板创建新脚本
cp template.sh init.sync/6.hello-world.sh
# 2. 添加执行权限
chmod +x init.sync/6.hello-world.sh
# 3. 编辑脚本
vim init.sync/6.hello-world.sh
```
**编辑内容**(在"主要逻辑"部分添加):
```bash
# ============================================
# 主要逻辑
# ============================================
log "============================================"
log "Starting task: ${SCRIPT_NAME}"
log "Hello, Lazycat User!"
log "Current time: $(date)"
log "Current user: $(whoami)"
log "Hostname: $(hostname)"
log "Task completed successfully"
log "============================================"
```
### 4.3 测试脚本(2 分钟)
```bash
# 方法1: 单独测试脚本
./init.sync/6.hello-world.sh
# 方法2: 测试完整流程(推荐)
./main.sh
# 查看执行日志
cat /tmp/user-startup-hooks/init.sync/6.hello-world.log
```
**预期输出**:
```
2026-01-11 12:00:00 - ============================================
2026-01-11 12:00:00 - Starting task: 6.hello-world
2026-01-11 12:00:00 - Hello, Lazycat User!
2026-01-11 12:00:00 - Current time: Sat Jan 11 12:00:00 CST 2026
2026-01-11 12:00:00 - Current user: root
2026-01-11 12:00:00 - Hostname: lazycat-server
2026-01-11 12:00:00 - Task completed successfully
2026-01-11 12:00:00 - ============================================
```
### 4.4 系统重启后自动执行
```bash
# 重启系统
sudo reboot
# 重启后,查看执行结果
cat /tmp/user-startup-hooks/main.log
cat /tmp/user-startup-hooks/init.sync/6.hello-world.log
```
**🎉 恭喜!你已经成功创建并运行了第一个启动脚本!**
### 4.5 脚本开发要点
#### 使用标准模板
**始终从 `template.sh` 复制**,保持一致的结构:
```bash
#!/bin/bash
# 脚本说明
# 日志配置(标准配置,所有脚本保持一致)
SCRIPT_NAME="$(basename "$0" .sh)"
SCRIPT_DIR_NAME="$(basename "$(dirname "$0")")"
LOG_DIR="/tmp/user-startup-hooks/${SCRIPT_DIR_NAME}"
LOG_FILE="${LOG_DIR}/${SCRIPT_NAME}.log"
mkdir -p "$LOG_DIR"
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# 脚本配置区域
# CONFIG_VAR="value"
# 主要逻辑
log "============================================"
log "Starting task: ${SCRIPT_NAME}"
# 你的代码...
log "Task completed successfully"
log "============================================"
```
#### 实现幂等性
**确保脚本多次执行结果一致**:
```bash
# ❌ 错误示例(非幂等)
echo "export PATH=/opt/bin:\$PATH" >> ~/.bashrc
# ✅ 正确示例(幂等)
if ! grep -q "export PATH=/opt/bin" ~/.bashrc; then
echo "export PATH=/opt/bin:\$PATH" >> ~/.bashrc
log "Added /opt/bin to PATH"
else
log "PATH already configured, skipping"
fi
```
#### 处理网络依赖
**需要网络的操作应先等待网络**:
```bash
# 检查网络连接
log "Checking network connectivity..."
for i in {1..12}; do
if ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then
log "Network is ready"
break
fi
log "Waiting for network... (attempt $i/12)"
sleep 5
done
if ! ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then
log "ERROR: Network timeout after 60 seconds"
exit 1
fi
```
### 4.6 常用命令
```bash
# Makefile 命令
make help # 显示帮助信息
make install # 安装系统 hook
make uninstall # 卸载系统 hook
make status # 检查安装状态
# 手动执行
./main.sh # 测试完整流程
./init.sync/1.wait-network.sh # 执行单个脚本
# 查看日志
cat /tmp/user-startup-hooks/main.log # 查看主日志
tail -f /tmp/user-startup-hooks/init.sync/1.wait-network.log # 实时监控
# 调试
bash -x ./init.sync/6.my-task.sh # 启用调试模式
# 禁用脚本
chmod -x init.sync/3.install-tools.sh # 移除执行权限
mv init.sync/3.install-tools.sh{,.disabled} # 重命名文件
```
---
## 五、重要注意事项
### 5.1 严重风险(必须避免)
#### ⚠️ Hook 脚本长时间运行
- ❌ 问题:`user-startup-entrypoint.sh` 中直接执行耗时任务
- ✅ 解决:使用 `nohup` 将任务派发到后台(框架已实现)
- 💥 后果:严重延迟系统启动,可能导致超时
#### ⚠️ 脚本没有幂等性
- ❌ 问题:重复执行导致配置错误(如重复追加到文件)
- ✅ 解决:添加检查逻辑,避免重复操作
- 💥 后果:配置文件混乱,系统行为异常
#### ⚠️ 未处理网络依赖
- ❌ 问题:直接执行需要网络的操作(如 `apt-get update`)
- ✅ 解决:先等待网络就绪(参考 `1.wait-network.sh`)
- 💥 后果:命令执行失败,启动流程中断
### 5.2 常见陷阱
**1. 日志路径使用 /tmp**
- ⚠️ 影响:系统重启后日志自动清空
- 💡 建议:如需持久化日志,改为 `/data/logs/` 路径
**2. 脚本执行顺序混乱**
- ⚠️ 影响:依赖关系错乱导致任务失败
- 💡 建议:仔细规划编号,确保依赖项先执行
**3. 错误未记录日志**
- ⚠️ 影响:排查问题困难
- 💡 建议:关键步骤都记录日志,错误时记录详细信息
**4. 忘记测试脚本**
- ⚠️ 影响:系统重启后才发现问题
- 💡 建议:先手动测试 `./script.sh` 和 `./main.sh`
### 5.3 最佳实践清单
#### 开发阶段 ✅
- [ ] 从 `template.sh` 复制创建新脚本
- [ ] 使用描述性的文件名(数字 + 说明)
- [ ] 实现幂等性检查
- [ ] 添加详细的日志记录
- [ ] 处理错误情况(检查退出码)
- [ ] 网络依赖任务先等待网络
#### 测试阶段 ✅
- [ ] 单独测试脚本:`./script.sh`
- [ ] 测试完整流程:`./main.sh`
- [ ] 多次执行确认幂等性
- [ ] 查看日志确认无错误
#### 部署阶段 ✅
- [ ] 确认 `make install` 成功
- [ ] 运行 `make status` 检查状态
- [ ] 重启系统验证自动执行
- [ ] 检查日志确认无错误
### 5.4 故障排查
#### 脚本没有执行?
```bash
make status # 检查 hook 是否安装
ls -l init.sync/*.sh # 检查脚本执行权限
cat /tmp/user-startup-hooks/main.log # 查看主日志
./main.sh # 手动执行测试
```
#### 脚本执行失败?
```bash
cat /tmp/user-startup-hooks/init.sync/xxx.log # 查看脚本日志
./init.sync/xxx.sh # 手动执行脚本
bash -x ./init.sync/xxx.sh # 使用调试模式
echo $? # 检查退出码(非 0 表示失败)
```
#### 网络相关任务失败?
```bash
ls -l init.sync/1.wait-network.sh # 确保网络等待脚本在最前面
ping -c 3 8.8.8.8 # 手动测试网络
cat /tmp/user-startup-hooks/init.sync/1.wait-network.log # 查看网络等待日志
```
---
## 六、总结与资源
### 6.1 核心价值
使用 **user-startup.d** 框架,你可以:
- ✅ **5 分钟**快速上手,无需学习复杂的 systemd 或 Docker
- ✅ 让系统配置在重启后**自动恢复**,告别重复劳动
- ✅ 使用**统一的日志管理**,快速定位和解决问题
- ✅ 通过**模块化脚本**,灵活管理启动任务
- ✅ **不阻塞系统启动**,不影响服务可用性
### 6.2 下一步行动
```bash
# 1. 克隆并安装
git clone https://github.com/lazycatapps/user-startup.d.git /root/user-startup.d
cd /root/user-startup.d && make install
# 2. 创建第一个脚本
cp template.sh init.sync/6.my-first-task.sh
chmod +x init.sync/6.my-first-task.sh
# 3. 测试运行
./main.sh
```
### 6.3 相关资源
#### 官方文档
- [懒猫微服平台](https://lazycat.cloud)
- [开发者文档](https://developer.lazycat.cloud/)
- [系统还原特性 FAQ](https://developer.lazycat.cloud/faq-dev.html)
- [启动脚本官方方案](https://developer.lazycat.cloud/faq-startup_script.html)
#### 项目链接
- GitHub 仓库: `https://github.com/lazycatapps/user-startup.d`
- 问题反馈: `https://github.com/lazycatapps/user-startup.d/issues`
#### 学习资源
- [Bash 脚本编程入门](https://www.shellscript.sh/)
- [Linux 命令行基础](https://linuxcommand.org/)
### 6.4 贡献与反馈
欢迎以下贡献:
- 🐛 报告 Bug 和问题
- 💡 提出新功能建议
- 📝 改进文档和示例
- 🔧 提交脚本模板和最佳实践
---
## 🎉 结语
**懒猫微服的"重启还原"特性**曾经让你感到困扰,现在通过 **user-startup.d** 框架,它变成了一种优势:
- 系统始终处于干净的初始状态
- 配置通过脚本自动恢复,可重复、可追溯
- 不用担心系统"越用越乱"
**只需 5 分钟**,你就能搭建起自己的启动脚本管理系统。
**现在就开始吧!** 🚀
---
**关于本文档**
- 文档版本: v1.0
- 最后更新: 2026-01-11
- 反馈渠道: [GitHub Issues](https://github.com/lazycatapps/user-startup.d/issues)
评论
0暂无评论