# install_service.sh 规则与参考原则 本文档说明 `shell/install_service.sh` 的配置规则、安装流程和后续维护原则。 ## 目标 `install_service.sh` 用于自动安装并通过 systemd 管理 `iot` 和 `efka` 服务。 脚本负责: - 下载服务发布包。 - 解压到 `WORK_DIR`。 - 创建运行所需目录。 - 生成 `/etc/systemd/system/.service`。 - 将服务环境变量写入对应 `.service` 文件。 - 执行 `systemctl daemon-reload`。 - 设置服务开机启动并启动服务。 ## 顶部配置规则 脚本顶部是主要维护区域。后续修改下载包、启动命令、环境变量、目录变量时,应优先修改顶部配置。 ### INSTALL_IOT / INSTALL_EFKA `INSTALL_IOT` 和 `INSTALL_EFKA` 分别配置服务安装信息,不再合并成统一的 `INSTALL_ITEMS`。 格式: ```bash INSTALL_=( "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 ` 查看最终配置。 ### 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 ``` 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_`。 2. 新增 `_ENV`。 3. 新增 `_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` 的思路同步调整。