shell/install_service.md
2026-06-01 14:45:27 +08:00

329 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# install service 脚本规则与参考原则
本文档说明 `shell/install_iot.sh``shell/install_efka.sh` 的配置规则、安装流程和后续维护原则。
## 目标
安装逻辑已经按服务拆分:
- `install_iot.sh`: 只安装并配置 `iot` 服务。
- `install_efka.sh`: 只安装并配置 `efka` 服务。
服务脚本负责:
- 下载服务发布包。
- 解压到 `WORK_DIR`
- 创建运行所需目录。
- 生成 `/etc/systemd/system/<service>.service`
- 将服务环境变量写入对应 `.service` 文件。
- 执行 `systemctl daemon-reload`
- 设置服务开机启动。
## 入口规则
只安装 iot
```bash
curl -fsSL https://punchsky.cn/install_iot.sh | bash
```
只安装 efka
```bash
curl -fsSL https://punchsky.cn/install_efka.sh | bash
```
本地执行:
```bash
sudo ./install_iot.sh
sudo ./install_efka.sh
```
## 顶部配置规则
服务配置只维护在对应拆分脚本顶部。
### INSTALL_IOT / INSTALL_EFKA
`install_iot.sh` 维护 `INSTALL_IOT``install_efka.sh` 维护 `INSTALL_EFKA`
格式:
```bash
INSTALL_<APP>=(
"service_name"
"download_url"
"start_command"
"stop_command"
"service_user"
"service_group"
)
```
字段说明:
- `service_name`: systemd 服务名,例如 `iot``efka`
- `download_url`: 发布包下载地址,仅支持 `.tar.gz``.tgz`
- `start_command`: systemd `ExecStart` 命令。
- `stop_command`: systemd `ExecStop` 命令,可为空。
- `service_user`: 服务运行用户,可为空;为空时使用脚本默认 `SERVICE_USER`
- `service_group`: 服务运行组,可为空;为空时使用脚本默认 `SERVICE_GROUP`
命令中可使用占位符:
- `{install_dir}`: 解压后的服务安装目录。
- `{work_dir}`: 脚本工作目录。
- `{service_name}`: 当前服务名。
### IOT_ENV / EFKA_ENV
`IOT_ENV``EFKA_ENV` 配置对应项目的生产环境变量,来源参考各项目 `env.file` 中的 `[prod]` 配置。
这些变量不会写入 `/etc/default/*`,也不会使用 `EnvironmentFile=`。脚本会直接将它们写入对应 systemd service
```ini
Environment="KEY=value"
```
### IOT_DIR_ENV_KEYS / EFKA_DIR_ENV_KEYS
`IOT_DIR_ENV_KEYS``EFKA_DIR_ENV_KEYS` 声明哪些环境变量的值是目录。
安装时脚本会:
- 从对应 `*_ENV` 中读取变量值。
- 替换占位符。
- 要求目录必须是绝对路径。
- 如果目录不存在,则创建。
- 将目录 owner 设置为服务用户和服务组。
### IOT_PARENT_DIR_ENV_KEYS
`IOT_PARENT_DIR_ENV_KEYS` 声明哪些环境变量的值是文件路径,脚本会创建这些文件路径的父目录。
例如 `IOT_CTRL_SOCKET_PATH=/var/lib/iot/ctl.sock` 是 Unix socket 文件路径,脚本应创建并授权 `/var/lib/iot`,不能把 `/var/lib/iot/ctl.sock` 当目录创建。
当前 iot 会创建:
- `/var/lib/endpoint/database/`
- `/var/lib/endpoint/endpoint_log`
- `/var/lib/iot/mnesia/`
- `/var/lib/iot`,来自 `IOT_CTRL_SOCKET_PATH` 的父目录
当前 efka 会创建:
- `/var/lib/efka/dets/`
- `/var/lib/efka/docker/`
- `/var/lib/efka/mnesia`
## 安装流程
单个服务脚本执行顺序:
1. 解析命令行参数。
2. 校验 `WORK_DIR` 和服务配置。
3. 检查依赖命令:`wget``dirname``tar``systemctl`
4. 创建 `WORK_DIR`
5. 安装当前服务。
6. 重新加载 systemd。
7. enable 已安装服务。
单个服务安装流程:
1. 校验服务名、下载地址、启动命令。
2. 根据下载地址推导压缩包名和安装目录。
3. 下载压缩包到临时文件。
4. 保存压缩包到 `WORK_DIR`
5. 创建安装目录。
6. 解压压缩包。
7. 设置安装目录 owner。
8. 根据 `*_DIR_ENV_KEYS` 创建数据目录,并根据 `*_PARENT_DIR_ENV_KEYS` 创建文件路径父目录。
9. 生成 systemd service 文件。
10. 记录服务名,后续统一 enable。
## systemd service 生成规则
每个服务会生成:
```text
/etc/systemd/system/<service_name>.service
```
service 文件包含:
- `User`
- `Group`
- `WorkingDirectory`
- 多行 `Environment="KEY=value"`
- `ExecStart`
- `ExecStop`
- `Restart=on-failure`
- `RestartSec=5`
环境变量必须写入服务自己的 `.service` 文件,而不是外部环境文件。
## 目录创建原则
只有明确列入 `IOT_DIR_ENV_KEYS``EFKA_DIR_ENV_KEYS` 的环境变量才会作为目录创建。
新增目录型环境变量时,需要同时修改两处:
```bash
IOT_ENV=(
"NEW_DATA_DIR=/var/lib/iot/new_data"
)
IOT_DIR_ENV_KEYS=(
"NEW_DATA_DIR"
)
```
非目录型环境变量只需要加入 `*_ENV`,不要加入 `*_DIR_ENV_KEYS`
如果环境变量是文件路径,需要把变量名加入对应的 `*_PARENT_DIR_ENV_KEYS`,脚本只创建父目录:
```bash
IOT_ENV=(
"IOT_CTRL_SOCKET_PATH=/var/lib/iot/ctl.sock"
)
IOT_PARENT_DIR_ENV_KEYS=(
"IOT_CTRL_SOCKET_PATH"
)
```
## 权限原则
脚本支持 root 或普通用户运行。
- 如果当前是 root直接执行需要 root 权限的命令。
- 如果不是 root通过 `sudo` 执行。
- 数据目录和安装目录 owner 会设置为服务运行用户和组。
- systemd service 文件写入 `/etc/systemd/system`,需要 root 权限。
默认服务用户:
```bash
SERVICE_USER="${SERVICE_USER:-$CURRENT_USER}"
SERVICE_GROUP="${SERVICE_GROUP:-$(id -gn "$SERVICE_USER")}"
```
可通过参数覆盖:
```bash
./install_iot.sh --user app --group app
./install_efka.sh --user app --group app
```
## 占位符原则
以下位置支持占位符:
- 启动命令。
- 停止命令。
- 环境变量值。
- 目录路径。
支持的占位符:
- `{install_dir}`
- `{work_dir}`
- `{service_name}`
如果该变量是目录,需要加入对应 `*_DIR_ENV_KEYS`。如果该变量是文件路径,需要加入对应 `*_PARENT_DIR_ENV_KEYS`
## 修改和扩展原则
### 修改环境变量
只修改对应服务脚本顶部数组:
```bash
IOT_ENV=(
"IOT_API_URL=http://127.0.0.1/api/v1"
)
```
修改后重新执行对应服务脚本,会重新生成 service 文件。
### 新增目录
同时修改:
- `IOT_ENV``EFKA_ENV`
- `IOT_DIR_ENV_KEYS``EFKA_DIR_ENV_KEYS`
### 新增文件路径
同时修改:
- `IOT_ENV``EFKA_ENV`
- 对应的 `*_PARENT_DIR_ENV_KEYS`
### 修改下载包
只修改对应服务脚本:
```bash
INSTALL_IOT=(
"iot"
"https://example.com/iot-x.y.z.tar.gz"
...
)
```
下载包后缀必须是:
- `.tar.gz`
- `.tgz`
### 新增服务
新增服务时应按现有拆分结构创建新的 `install_<app>.sh`
1. 新增 `INSTALL_<APP>`
2. 新增 `<APP>_ENV`
3. 新增 `<APP>_DIR_ENV_KEYS`
4.`validate_args` 中校验对应配置。
5.`service_env_lines` 中映射服务名到 env 数组。
6.`service_dir_env_keys` 中映射服务名到目录 key 数组。
7. 如服务有文件路径型环境变量,在 `service_parent_dir_env_keys` 中映射服务名到父目录 key 数组。
8.`install_programs` 中调用对应配置。
## 兼容性原则
脚本当前兼容 Bash 3.2,不使用 `local -n` 等较新的 Bash 特性。
读取动态数组时使用 `eval`,因此数组名必须由脚本内部固定传入,不应使用用户输入构造数组名。
## 验证建议
修改脚本后至少执行:
```bash
bash -n shell/install_iot.sh
bash -n shell/install_efka.sh
```
建议额外验证 service 渲染结果:
```bash
bash -c "$(sed '$d' shell/install_iot.sh); render_service_file iot app app /opt/app/iot-0.1.0 '/opt/app/iot-0.1.0/bin/iot foreground' '/opt/app/iot-0.1.0/bin/iot stop'"
bash -c "$(sed '$d' shell/install_efka.sh); render_service_file efka app app /opt/app/efka-0.1.0 '/opt/app/efka-0.1.0/bin/efka foreground' '/opt/app/efka-0.1.0/bin/efka stop'"
```
如果目标机器有 `systemd-analyze`,可进一步验证生成后的 unit 文件:
```bash
systemd-analyze verify /etc/systemd/system/iot.service
systemd-analyze verify /etc/systemd/system/efka.service
```
## 注意事项
- 环境变量中如包含空格或特殊字符,需要确认 systemd `Environment=` 语法是否仍然正确。
- token 等敏感信息会写入 systemd service 文件,需控制 `/etc/systemd/system/*.service` 的读取权限和服务器访问权限。
- 数据目录和文件路径型变量必须使用绝对路径。
- 现有卸载脚本如果需要同步目录清理规则,应按 `IOT_DIR_ENV_KEYS` / `EFKA_DIR_ENV_KEYS` 的思路同步调整。