shell/install_service.md
2026-06-01 16:28:55 +08:00

9.6 KiB
Raw Permalink Blame History

install service 脚本规则与参考原则

本文档说明 shell/install_iot.shshell/install_efka.sh 的配置规则、安装流程和后续维护原则。

目标

安装逻辑已经按服务拆分:

  • install_iot.sh: 只安装并配置 iot 服务。
  • install_efka.sh: 只安装并配置 efka 服务。

服务脚本负责:

  • 下载服务发布包。
  • 解压到 WORK_DIR
  • 删除下载到 WORK_DIR 的压缩包。
  • 创建运行所需目录。
  • 生成 /etc/systemd/system/<service>.service
  • 将服务环境变量写入对应 .service 文件。
  • 执行 systemctl daemon-reload
  • 设置服务开机启动。
  • install_iot.sh 会将 {install_dir}/bin/iot_ctrl 软链接到 /usr/local/bin/iot_ctrl

入口规则

只安装 iot

curl -fsSL https://punchsky.cn/install_iot.sh | bash

只安装 efka

curl -fsSL https://punchsky.cn/install_efka.sh | bash

本地执行:

sudo ./install_iot.sh
sudo ./install_efka.sh

顶部配置规则

服务配置只维护在对应拆分脚本顶部。

INSTALL_IOT / INSTALL_EFKA

install_iot.sh 维护 INSTALL_IOTinstall_efka.sh 维护 INSTALL_EFKA

格式:

INSTALL_<APP>=(
  "service_name"
  "download_url"
  "start_command"
  "stop_command"
  "service_user"
  "service_group"
)

字段说明:

  • service_name: systemd 服务名,例如 iotefka
  • 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},例如 /opt/app/iot/opt/app/efka
  • {work_dir}: 脚本工作目录。
  • {service_name}: 当前服务名。

IOT_ENV / EFKA_ENV

IOT_ENVEFKA_ENV 配置对应项目的生产环境变量,来源参考各项目 env.file 中的 [prod] 配置。

这些变量不会写入 /etc/default/*,也不会使用 EnvironmentFile=。脚本会直接将它们写入对应 systemd service

Environment="KEY=value"

IOT_DIR_ENV_KEYS / EFKA_DIR_ENV_KEYS

IOT_DIR_ENV_KEYSEFKA_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. 检查依赖命令:wgetdirnamelntarsystemctl
  4. 创建 WORK_DIR
  5. 安装当前服务。
  6. 重新加载 systemd。
  7. enable 已安装服务。

单个服务安装流程:

  1. 校验服务名、下载地址、启动命令。
  2. 根据服务名推导安装目录,默认是 /opt/app/<service_name>
  3. 下载压缩包到临时文件。
  4. 保存压缩包到 WORK_DIR
  5. 创建安装目录。
  6. 解压压缩包。GNU tar 环境下显示 checkpoint 点状进度,不输出文件列表;不支持 checkpoint 的 tar 会安静解压。
  7. 删除保存到 WORK_DIR 的压缩包。
  8. 设置安装目录 owner。
  9. 根据 *_DIR_ENV_KEYS 创建数据目录,并根据 *_PARENT_DIR_ENV_KEYS 创建文件路径父目录。
  10. iot 安装时创建 /usr/local/bin/iot_ctrl 软链接。
  11. 生成 systemd service 文件。
  12. 记录服务名,后续统一 enable。

iot_ctrl 构建和打包原则

iot_ctrl 位于 iot/go_extend,应在发布包构建阶段编译,不在 install_iot.sh 中编译。

目标机器不应依赖 Go 编译环境。发布包需要提前包含:

iot/
  bin/
    iot
    iot_ctrl

安装后脚本会检查 {install_dir}/bin/iot_ctrl 是否存在且可执行。如果不存在,安装会失败并提示需要在打包前构建该工具。

安装成功后会创建软链接:

/usr/local/bin/iot_ctrl -> /opt/app/iot/bin/iot_ctrl

构建示例:

cd iot/go_extend
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 make build

如果目标机器是 arm64

GOOS=linux GOARCH=arm64 CGO_ENABLED=0 make build

systemd service 生成规则

每个服务会生成:

/etc/systemd/system/<service_name>.service

service 文件包含:

  • User
  • Group
  • WorkingDirectory
  • 多行 Environment="KEY=value"
  • ExecStart
  • ExecStop
  • Restart=on-failure
  • RestartSec=5

环境变量必须写入服务自己的 .service 文件,而不是外部环境文件。

目录创建原则

只有明确列入 IOT_DIR_ENV_KEYSEFKA_DIR_ENV_KEYS 的环境变量才会作为目录创建。

新增目录型环境变量时,需要同时修改两处:

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,脚本只创建父目录:

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 权限。

默认服务用户:

SERVICE_USER="${SERVICE_USER:-$CURRENT_USER}"
SERVICE_GROUP="${SERVICE_GROUP:-$(id -gn "$SERVICE_USER")}"

可通过参数覆盖:

./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

{install_dir} 不从压缩包文件名推导,不带版本号,固定使用服务名作为目录名。

修改和扩展原则

修改环境变量

只修改对应服务脚本顶部数组:

IOT_ENV=(
  "IOT_API_URL=http://127.0.0.1/api/v1"
)

修改后重新执行对应服务脚本,会重新生成 service 文件。

新增目录

同时修改:

  • IOT_ENVEFKA_ENV
  • IOT_DIR_ENV_KEYSEFKA_DIR_ENV_KEYS

新增文件路径

同时修改:

  • IOT_ENVEFKA_ENV
  • 对应的 *_PARENT_DIR_ENV_KEYS

修改下载包

只修改对应服务脚本:

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 -n shell/install_iot.sh
bash -n shell/install_efka.sh

建议额外验证 service 渲染结果:

bash -c "$(sed '$d' shell/install_iot.sh); render_service_file iot app app /opt/app/iot '/opt/app/iot/bin/iot foreground' '/opt/app/iot/bin/iot stop'"
bash -c "$(sed '$d' shell/install_efka.sh); render_service_file efka app app /opt/app/efka '/opt/app/efka/bin/efka foreground' '/opt/app/efka/bin/efka stop'"

如果目标机器有 systemd-analyze,可进一步验证生成后的 unit 文件:

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 发布包必须包含可执行的 bin/iot_ctrl,否则不会创建 /usr/local/bin/iot_ctrl 链接。
  • 现有卸载脚本如果需要同步目录清理规则,应按 IOT_DIR_ENV_KEYS / EFKA_DIR_ENV_KEYS 的思路同步调整。