shell/install_service.md
2026-05-30 23:58:32 +08:00

311 lines
7.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.sh 规则与参考原则
本文档说明 `shell/install_service.sh` 的配置规则、安装流程和后续维护原则。
## 目标
`install_service.sh` 用于自动安装并通过 systemd 管理 `iot``efka` 服务。
脚本负责:
- 下载服务发布包。
- 解压到 `WORK_DIR`
- 创建运行所需目录。
- 生成 `/etc/systemd/system/<service>.service`
- 将服务环境变量写入对应 `.service` 文件。
- 执行 `systemctl daemon-reload`
- 设置服务开机启动并启动服务。
## 顶部配置规则
脚本顶部是主要维护区域。后续修改下载包、启动命令、环境变量、目录变量时,应优先修改顶部配置。
### INSTALL_IOT / INSTALL_EFKA
`INSTALL_IOT``INSTALL_EFKA` 分别配置服务安装信息,不再合并成统一的 `INSTALL_ITEMS`
格式:
```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]` 配置。
格式:
```bash
IOT_ENV=(
"KEY=value"
)
EFKA_ENV=(
"KEY=value"
)
```
这些变量不会写入 `/etc/default/*`,也不会使用 `EnvironmentFile=`
脚本会直接将它们写入对应 systemd service
```ini
Environment="KEY=value"
```
这样环境变量由 systemd unit 统一管理,可通过 `systemctl cat <service>` 查看最终配置。
### IOT_DIR_ENV_KEYS / EFKA_DIR_ENV_KEYS
`IOT_DIR_ENV_KEYS``EFKA_DIR_ENV_KEYS` 声明哪些环境变量的值是目录。
格式:
```bash
IOT_DIR_ENV_KEYS=(
"ENDPOINT_ROOT_DIR"
)
```
安装时脚本会:
- 从对应 `*_ENV` 中读取变量值。
- 替换占位符。
- 要求目录必须是绝对路径。
- 如果目录不存在,则创建。
- 将目录 owner 设置为服务用户和服务组。
当前 iot 会创建:
- `/var/lib/endpoint/database/`
- `/var/lib/endpoint/endpoint_log`
- `/var/lib/iot/mnesia/`
当前 efka 会创建:
- `/var/lib/efka/dets/`
- `/var/lib/efka/docker/`
- `/var/lib/efka/mnesia`
## 安装流程
脚本执行顺序:
1. 解析命令行参数。
2. 校验 `WORK_DIR` 和服务配置。
3. 检查依赖命令:`wget``tar``systemctl`
4. 创建 `WORK_DIR`
5. 依次安装 `iot``efka`
6. 重新加载 systemd。
7. enable 并 start 已安装服务。
单个服务安装流程:
1. 校验服务名、下载地址、启动命令。
2. 根据下载地址推导压缩包名和安装目录。
3. 下载压缩包到临时文件。
4. 保存压缩包到 `WORK_DIR`
5. 创建安装目录。
6. 解压压缩包。
7. 设置安装目录 owner。
8. 根据 `*_DIR_ENV_KEYS` 创建数据目录。
9. 生成 systemd service 文件。
10. 记录服务名,后续统一 enable/start。
## 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` 的环境变量才会作为目录创建。
这样做的原因:
- 避免误把 URL、token、host 等普通变量当成路径。
- 新增目录型变量时行为明确。
- 目录创建逻辑和环境变量配置保持关联。
新增目录型环境变量时,需要同时修改两处:
```bash
IOT_ENV=(
"NEW_DATA_DIR=/var/lib/iot/new_data"
)
IOT_DIR_ENV_KEYS=(
"NEW_DATA_DIR"
)
```
非目录型环境变量只需要加入 `*_ENV`,不要加入 `*_DIR_ENV_KEYS`
## 权限原则
脚本支持 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_service.sh --user app --group app
```
## 占位符原则
以下位置支持占位符:
- 启动命令。
- 停止命令。
- 环境变量值。
- 目录路径。
支持的占位符:
- `{install_dir}`
- `{work_dir}`
- `{service_name}`
示例:
```bash
"LOG_DIR={work_dir}/var/log/{service_name}"
```
如果该变量是目录,需要加入对应 `*_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`
### 修改下载包
只修改:
```bash
INSTALL_IOT=(
"iot"
"https://example.com/iot-x.y.z.tar.gz"
...
)
```
下载包后缀必须是:
- `.tar.gz`
- `.tgz`
### 新增服务
新增服务时应保持当前结构:
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.`install_programs` 中调用 `install_program_from_config`
## 兼容性原则
脚本当前兼容 Bash 3.2,不使用 `local -n` 等较新的 Bash 特性。
读取动态数组时使用 `eval`,因此数组名必须由脚本内部固定传入,不应使用用户输入构造数组名。
## 验证建议
修改脚本后至少执行:
```bash
bash -n shell/install_service.sh
```
建议额外验证 service 渲染结果:
```bash
bash -c "$(sed '$d' shell/install_service.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'"
```
如果目标机器有 `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` 的思路同步调整。