--- title: 二进制部署 description: 直接编译 Go 程序运行,不依赖 Docker outline: deep --- # 二进制部署 不使用容器,把各服务编译为可执行文件,用 systemd 托管。 ## 适用场景 - 服务器不允许安装 Docker - 需要把服务纳入既有的 systemd / 进程管理体系统一运维 - 需要针对特定 CPU 架构或内核参数做优化 ## 构建环境 | 项 | 要求 | | --- | --- | | Go | 按各服务 `go.mod` 要求,最低 1.23(见下表) | | GCC | admin-svr 需要(`CGO_ENABLED=1`,内存库用 SQLite) | | 系统 | 建议与目标服务器同发行版,避免 libc 差异 | ```bash # Debian / Ubuntu apt-get install -y build-essential # CentOS / Rocky yum groupinstall -y "Development Tools" ``` ::: tip 其余服务可静态编译 除 admin-svr 外,其余服务 `CGO_ENABLED=0`,产物为静态二进制,可直接拷到任意同架构 Linux 运行。 ::: ## 各服务构建参数 | 服务 | 仓库 | Go 版本 | CGO | 构建目录 | 产物名 | 监听端口 | | --- | --- | --- | --- | --- | --- | --- | | admin-svr | `iotlcm-admin-svr` | 1.23 | **需开启** | `server/` | `iotlcmadmin_svc` | 8008 | | openapi | `iotlcm-openapi` | 1.23 | 关闭 | 根目录 | `iotlcm-openapi` | 8000 | | worker-svr | `iotlcm-worker-svr` | 1.25 | 关闭 | 根目录 | `iotlcm-worker-svr` | 无(队列消费) | | upstream-chan | `iotlcm-upstream-chan` | 1.25 | 关闭 | 根目录 | `iotlcmupstreamchan_svc` | 8110 / 8310 | | upstream-proxy | `iotlcm-upstream-proxy` | 1.25 | 关闭 | 根目录 | `iotlcmupstreamproxy_svc` | 8510 | | app-server | `iotlcm-app` | 1.23 | 关闭 | `server/` | `iotlcm-app-server` | 8010 | | sms | `iotlcm-sms` | 1.25 | 关闭 | 根目录 | `iotlcm-sms` | 11190 / 11200 | | bot | `iotlcm-bot` | 1.23 | 关闭 | `cmd/iotlcm-bot` | `iotlcm-bot` | 无 | ::: warning 仓库里的 Dockerfile 不能直接照搬 部分仓库的 Dockerfile 基础镜像指向私有仓库(见[自建镜像](build-image)),二进制构建不受影响,但 `FROM` 行需要替换。 ::: ## 构建 ### admin-svr(需要 CGO) ```bash git clone https://git.5iot.com/iotlcm-public/iotlcm-admin-svr.git cd iotlcm-admin-svr/server CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \ go build -trimpath \ -ldflags "-s -w \ -X main.BuildVersion=$(git describe --tags --always) \ -X main.CommitID=$(git rev-parse --short HEAD) \ -X main.BuildTime=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ -o iotlcmadmin_svc . ``` ### 其余服务(静态编译) ```bash git clone https://git.5iot.com/iotlcm-public/iotlcm-openapi.git cd iotlcm-openapi CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build -trimpath \ -ldflags "-s -w \ -X main.BuildVersion=$(git describe --tags --always) \ -X main.CommitID=$(git rev-parse --short HEAD) \ -X main.BuildTime=$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ -o iotlcm-openapi . ``` 替换仓库名与产物名即可用于其他服务,`bot` 需要指定子目录: ```bash cd iotlcm-bot CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o iotlcm-bot ./cmd/iotlcm-bot ``` ### 交叉编译说明 | 服务 | 能否交叉编译 | | --- | --- | | admin-svr(CGO) | ❌ 需在目标架构上编译,或用对应架构的交叉工具链 | | 其余(静态) | ✅ 可直接 `GOOS=linux GOARCH=arm64` | 查看服务器架构: ```bash uname -m # x86_64 → amd64;aarch64 → arm64 ``` ## 部署布局 ```text /opt/iotlcm/ ├── admin-svr/ │ ├── iotlcmadmin_svc │ ├── config/config.yaml ← 从 config.example/admin-svr 复制 │ ├── logs/ │ ├── upload/ │ └── cache/ ├── openapi/ │ ├── iotlcm-openapi │ ├── config/config.yaml │ └── logs/ ├── worker-svr/ │ ├── iotlcm-worker-svr │ ├── config/config.yaml │ └── logs/ ├── upstream-chan/ ├── upstream-proxy/ ├── app-server/ │ ├── iotlcm-app-server │ ├── config/config.yaml │ └── resource/ │ ├── log/server/ │ └── upload/ ├── sms/ └── bot/ ``` **配置文件放 `config/config.yaml`**(相对工作目录),这是 GoFrame 的默认搜索路径。启动时工作目录必须在服务目录内。 ### 目录准备 ```bash sudo mkdir -p /opt/iotlcm/{admin-svr,openapi,worker-svr,upstream-chan,upstream-proxy,app-server,sms,bot} sudo useradd -r -s /sbin/nologin iotlcm sudo chown -R iotlcm:iotlcm /opt/iotlcm ``` ## 修改配置中的服务地址 容器部署时配置里写的是服务名(`mysql:3306`、`upstream-proxy:8510`),二进制部署必须改为实际地址: ```yaml # admin-svr / openapi / worker-svr / app-server database: default: link: "mysql:u_iotlcm:<口令>@tcp(127.0.0.1:3306)/db_iotlcm?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai" redis: default: address: "127.0.0.1:6379" rabbitmq: dsn: "amqp://<用户>:<口令>@127.0.0.1:5672/" # upstream-chan / upstream-proxy registry: address: "127.0.0.1:8500" # 上游服务地址 upstream: proxy: address: "127.0.0.1:8510" gateway: address: "127.0.0.1:8110" ``` ::: danger logger.path 也要改 配置里的 `logger.path` 在容器版本中是 `/app/logs`,二进制部署要改为实际路径,例如 `/opt/iotlcm/admin-svr/logs`,否则会因目录不存在启动失败。 ::: ## systemd 托管 以为 `admin-svr` 为例,保存为 `/etc/systemd/system/iotlcm-admin-svr.service`: ```ini [Unit] Description=IotLCM Admin Service After=network-online.target mysql.service redis.service Wants=network-online.target [Service] Type=simple User=iotlcm Group=iotlcm WorkingDirectory=/opt/iotlcm/admin-svr ExecStart=/opt/iotlcm/admin-svr/iotlcmadmin_svc Restart=always RestartSec=5s LimitNOFILE=65535 # 环境变量(JWT 密钥等敏感值建议放独立文件) EnvironmentFile=-/etc/iotlcm/admin-svr.env # 日志交给 journald,也可用配置里的文件日志 StandardOutput=journal StandardError=journal # 安全加固 NoNewPrivileges=true PrivateTmp=true ProtectSystem=full ProtectHome=true ReadWritePaths=/opt/iotlcm/admin-svr [Install] WantedBy=multi-user.target ``` `/etc/iotlcm/admin-svr.env`: ```bash JWT_SIGNING_KEY=<与其它服务一致的密钥> ``` 启用: ```bash sudo systemctl daemon-reload sudo systemctl enable --now iotlcm-admin-svr sudo systemctl status iotlcm-admin-svr ``` ### 各服务的差异点 | 服务 | 特殊处理 | | --- | --- | | admin-svr | 需 `ReadWritePaths` 包含 upload 与 logs 目录 | | worker-svr | 无监听端口,`After` 需包含 rabbitmq | | upstream-chan / proxy | `After` 需包含 consul | | app-server | 需 `resource/log/server` 与 `resource/upload` 可写 | | bot | `ExecStart` 需带参数:`/opt/iotlcm/bot/iotlcm-bot watch --config /opt/iotlcm/bot/config/config.yaml` | ### 启动顺序 ```text MySQL / Redis / RabbitMQ / Consul ↓ admin-svr、openapi、worker-svr、upstream-chan、upstream-proxy、app-server、sms ↓ admin-ui、app-web、docs(静态文件,由 nginx 托管) ``` 用 systemd 依赖表达: ```ini [Unit] After=iotlcm-admin-svr.service Requires=iotlcm-admin-svr.service ``` ## 静态前端 `admin-ui`、`app-web`、`docs` 是静态文件,二进制部署时不需要跑容器,把构建产物交给 nginx: ```bash # admin-ui:需 Node 构建 cd iotlcm-admin-ui/vue npm ci && npm run build sudo cp -r apps/web-antd/dist /var/www/iotlcm/admin-ui ``` nginx 配置参考部署仓的 `config.example/admin-ui/default.conf` 与 `config.example/nginx/iotlcm.conf`,把 `proxy_pass http://admin-svr:8008` 改为 `http://127.0.0.1:8008`。 ## 验证 ```bash # 进程状态 systemctl status iotlcm-admin-svr journalctl -u iotlcm-admin-svr -n 50 --no-pager # 端口监听 ss -lntp | grep -E '8008|8000|8510|8110' # 接口 curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8008/api.json ``` ## 与容器部署的差异 | 项 | 容器部署 | 二进制部署 | | --- | --- | --- | | 配置路径 | `/app/config`(挂载) | `<服务目录>/config` | | 日志路径 | `/app/logs` | 需在配置里改成实际路径 | | 服务互访 | 服务名(Docker DNS) | `127.0.0.1` 或内网 IP | | 进程管理 | Docker restart policy | systemd | | 升级 | 换镜像 + `up -d` | 停服务 → 换二进制 → 起服务 | | 时区 | 镜像已内置 | 依赖宿主机 tzdata | ## 升级流程 ```bash # 1. 备份配置与程序 sudo tar czf /backup/iotlcm-admin-svr-$(date +%F).tar.gz -C /opt/iotlcm admin-svr # 2. 停服务 sudo systemctl stop iotlcm-admin-svr # 3. 替换二进制 sudo cp iotlcmadmin_svc /opt/iotlcm/admin-svr/ sudo chown iotlcm:iotlcm /opt/iotlcm/admin-svr/iotlcmadmin_svc # 4. 起服务并验证 sudo systemctl start iotlcm-admin-svr sudo systemctl status iotlcm-admin-svr ``` ## 回滚 保留上一版二进制,回滚只需换回: ```bash sudo systemctl stop iotlcm-admin-svr sudo cp /opt/iotlcm/admin-svr/iotlcmadmin_svc.bak /opt/iotlcm/admin-svr/iotlcmadmin_svc sudo systemctl start iotlcm-admin-svr ```