apiSQL网关,说白了就是让前端应用能直接通过HTTP请求把SQL发给数据库执行的那个中间服务。做数据平台、报表系统或者内部数据工具的人应该都遇到过这种场景:业务方要个新数据,你得写个后端接口,写完还要联调、发版、上线,一来一回小半天就没了。apiSQL这类网关产品做的事情,就是把这个过程压缩成配置一条映射规则,把SQL直接暴露成HTTP接口。这篇东西就围绕这个方向,把基于Docker的离线安装和升级流程完整拆一遍,权限管控、API格式、版本迁移这些都会涉及,给那些网络隔离环境或者不想把镜像源暴露到外网的同学一个可以直接照着操作的参考。
我自己在真实环境里部署过几次这类网关,其中一个场景是某数据团队内部的分析系统,数据库在独立网段,应用服务器碰不到外网,所有软件包都必须由人工拷进去。那次的经历让我把离线安装这套流程踩了个遍,所以这篇内容里你看到的所有步骤、命令和坑,都是实际跑过之后的沉淀,不是对着文档抄出来的。不管你是刚接触apiSQL网关的新手,还是已经上线了正在琢磨怎么平滑升级的运维,下面的内容都值得花几分钟过一遍。
1. apiSQL网关是什么,为什么要拿Docker来部署它
1.1 它解决的核心问题:把SQL变成可调用的HTTP接口
先讲清楚apiSQL网关本身是干什么的。你可以把它理解成一个翻译层,架在HTTP请求和数据库之间。客户端(比如一个Vue前端、一个报表工具、甚至一个脚本)发起一个HTTP POST请求,请求体里带着参数,网关收到之后,根据配置好的映射关系,把参数填进预编译的SQL模板里,然后连上后端数据库执行查询,最后把结果集转成JSON返回给调用方。
这个模式的价值在哪儿?最简单的例子,前端要一个“按时间范围统计订单量”的数据。传统做法是后端写一个接口/api/order/count,代码里拼接SQL、处理参数校验、格式化返回。用apiSQL网关,你只需要在网关里注册一条规则:请求路径/api/order/count对应SQL模板SELECT COUNT(*) FROM orders WHERE create_time BETWEEN :start AND :end,参数从请求体或请求头里取。整个过程不需要写Java、不需要编译、不需要重启服务,改SQL模板只要调网关的管理端接口或者直接改配置文件。
这个特性对两类人特别有用:一类是数据开发,手里的数据口径很灵活,今天要按省份拆、明天要按渠道拆,频繁让后端改接口谁也受不了;另一类是前端或者数据分析师,他们不熟悉后端代码,但会用SQL,网关给了他们一个自助取数的入口,而且权限和审计都在网关层统一管住。
1.2 为什么用Docker:环境一致性是最大的省心点
apiSQL网关这类中间件,依赖的东西其实不少。至少需要一个运行时(Java、Go或Node,取决于具体实现)、一个连接池(负责管理数据库连接)、可能还有一个内嵌的元数据库(用来存API定义和配置信息)。这些依赖一旦散落在宿主机上,就会面临那个经典问题:在一台机器上装得好好的,换一台机器就起不来,版本冲突、缺动态库、Java版本不对,哪个都能卡你半天。
Docker把这些问题全部收进了镜像里。镜像构建的时候就把运行时、依赖、配置模板、必要的工具链全部固化好了,在任何装了Docker的Linux机器上,docker run一下就是同样的一套环境,不存在“我在自己电脑上是好的”这种玄学。这也是我在离线环境里坚决用Docker而不是裸机部署的根本原因:离线环境里想装个依赖都费劲,一旦出问题排查成本极高,Docker镜像拿走即用,有问题就删容器重建,别把时间耗在捣鼓系统环境上。
另外,apiSQL网关升级这件事,裸机部署往往意味着覆盖文件、迁移配置、重启服务,一旦新版启不来,回滚就可能丢配置;Docker下回滚只是改一下镜像标签、重新up一下的事,新老版本共存也简单,先启动新版本跑通再切流量,完全不慌。
1.3 什么情况下适合用apiSQL网关
不是所有场景都适合上这个方案。它擅长的是读多写少、口径灵活、以查询统计为主的场景。典型的有:
- 报表系统和大屏展示:SQL模板固定,参数动态,非常适合。
- 内部数据查询平台:给运营、产品一个自助查询入口,避免每次取数都提工单。
- 数据服务化:把数据仓库的结果表快速暴露成API,供上游系统调用。
但不适合用在高并发核心交易链路、强事务要求的业务系统里,那还是老老实实写业务接口。这一点心里有数,部署前就能少很多不必要的坑。
2. 离线安装的核心思路与前置检查
2.1 离线部署的本质:镜像包搬运工
离线安装和在线安装最大的区别,在于你能不能直接docker pull。在线环境一句话docker pull就完事了,离线环境里你必须在能联网的机器上先把镜像拉下来,打包成tar文件,拷到目标机器,再docker load进去。整个过程可以概括成四步:拉镜像、存镜像、传镜像、加载镜像。
听起来简单,但实际操作中很多团队栽在细节上。比如镜像打包时没注意是不是完整的(多架构镜像如果少了某个平台的manifest,load进去之后运行会直接报exec format error);比如docker save和docker load必须配对使用,有人拿docker export导出来的容器文件系统去load,结果是镜像里没有完整的元数据,跑起来各种诡异问题;再比如离线包拷到内网之后没有校验完整性,加载中途断了也不知道。
我个人习惯是:所有离线包在联网机器上打好之后,马上算一个SHA256校验值,连同镜像清单、版本号、打包日期一起写进一个README文件放在离线包里。到了目标机器先校验,再load,十次里能避免至少三次“白忙活”。
2.2 前置检查清单:动手之前把这些确认完
离线部署之前,花十分钟把下面的清单过一遍,后面能省两个小时排查。
| 检查项 | 确认内容 |
|---|---|
| Docker版本 | 目标机器执行docker version,确保20.10以上,旧版本对compose语法和镜像新特性的兼容性差 |
| Docker Compose插件 | 确认docker compose version可用(新语法),或docker-compose version(旧版),两者命令写法不同 |
| 磁盘空间 | df -h看数据盘剩余,镜像本身可能不大,但数据卷、日志、元数据库会持续增长,建议至少预留20G以上 |
| 端口占用 | ss -lntp确认网关要用的端口(常见如8080、8443)没被占用 |
| 数据库连通性 | 目标机器到业务数据库的网络、账号权限、防火墙放行都要确认好 |
| 架构匹配 | 在联网机器拉镜像时用docker pull --platform指定和目标机器相同的架构(x86_64或aarch64),尤其注意ARM服务器 |
Docker版本这一条特别值得展开说一下。有些内网服务器的系统比较老,装的是Docker 18或者19,如果你在联网机器上下载的镜像用了较新格式,load的时候可能一切正常,但运行的时候会碰到一些奇怪的运行时错误。与其到时候排查,不如提前确认好。
2.3 离线包从哪里来:自己造还是找人要
离线包常见的三个来源:
- 官方发布的离线安装包:很多成熟的中间件会直接出
offline-package,里面包含镜像tar、默认配置、安装脚本和文档,这是最省事的,但前提是你能从内网渠道拿到。 - 自己从镜像仓库拉:有局域网镜像仓库(比如Harbor)的话,在联网机拉下来,重新打tag指向私有仓库,再推送。如果私有仓库也在内网,就需要做成tar搬运。
- 从另一台同构机器上导出来:如果同机房有另一台已经部署好的机器,可以直接
docker save导出镜像,但要注意版本一致性。
我在实际项目里倾向于自己打离线包,因为可控。官方离线包可能版本滞后,而且是通用配置,你拿到的当下还得改一堆东西;自己打包虽然多几步操作,但版本、参数都在掌握中。
3. 离线安装全流程实操:从离线包到跑通一条SQL
3.1 在联网机器上准备离线镜像包
假设你手上有一台能访问外网的机器,目标是把apiSQL网关的镜像打成tar。先拉镜像,这里建议固定到一个具体的版本标签,不要用latest,因为latest在不同时间拉下来的镜像可能完全不一样,升级和复现就乱了。
# 拉取指定版本镜像,这里用 2.1.0 举例,实际以你用的版本为准 docker pull apecloud/apisql-gateway:2.1.0 # 查看本地镜像确认REPOSITORY和TAG docker images | grep apisql确认无误后,打成tar包。这里有个经验之谈:docker save一次可以接多个镜像,但建议一个tar只打一个镜像,除非你非常确定这些镜像要一同部署。分开打的好处是后续如果要单独替换某个组件,比如只升级网关主体不换元数据库镜像,操作起来非常灵活。
# 保存为tar文件,gzip压缩可以减少体积,但目标机器要多一步解压 docker save apecloud/apisql-gateway:2.1.0 | gzip > apisql-gateway-2.1.0.tar.gz # 计算校验值,写进离线包说明文件 sha256sum apisql-gateway-2.1.0.tar.gz顺手把配置文件模板也拷下来。apiSQL网关通常提供一份默认配置模板(YAML格式),里面包含监听端口、数据库连接池参数、日志级别、元数据库配置等。这份模板要放在离线包里,后面在目标机器上改。
3.2 目标机器上加载镜像
把tar包通过内网传输工具(或者干脆U盘)拷贝到目标机器上,先做校验再加载:
# 校验文件完整性 echo "你记下来的sha256值 apisql-gateway-2.1.0.tar.gz" | sha256sum -c - # 解压并加载镜像 gunzip -c apisql-gateway-2.1.0.tar.gz | docker load # 确认镜像已经加载 docker images | grep apisql如果输出里出现了REPOSITORY、TAG两列都符合条件的镜像,就说明加载成功了。我这里特别提醒一下:有些离线包分发的镜像在加载后,REPOSITORY可能是一个默认名(比如apisql-gateway),TAG是版本号,你要记住这个名字,后面compose文件里用。
注意:不要用
docker import命令,那是用来导入容器文件系统快照的,导入出来的东西没有CMD、ENV等镜像元数据,直接跑会崩。
3.3 编写compose部署文件
离线安装我永远推荐用Docker Compose而不是裸docker run。原因很简单:docker run一长串参数敲完,过两个月你自己都不记得当时配了什么;写成compose文件,版本管理、改配置、diff都有据可查。下面是基于我实际部署经验的compose文件模板:
version: "3.8" services: apisql-gateway: image: apecloud/apisql-gateway:2.1.0 container_name: apisql-gateway restart: always ports: - "8080:8080" # HTTP API服务端口 - "8443:8443" # HTTPS管理端端口(按需) volumes: - ./config:/app/config # 配置文件目录挂载 - ./logs:/app/logs # 日志目录挂载 environment: - TZ=Asia/Shanghai # 时区,日志时间对不上多半是漏了它 - GATEWAY_CONFIG_FILE=/app/config/apisql-gateway.yaml # 指定配置文件路径 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/healthz"] interval: 30s timeout: 5s retries: 3 start_period: 20s说几个关键参数。restart: always保证机器重启之后网关自动拉起,这是生产环境的基本要求,别因为“手工启一下也不麻烦”就省了。volumes是把配置和日志放到宿主机上,这是Docker部署的核心原则——容器本身是无状态的,数据全部外置,这样升级、重建容器的时候配置不丢。healthcheck用来做存活探针,配合监控告警用。
配置文件的编写是另一个重点环节。apiSQL网关的配置文件里至少有这几块内容需要填对:
server: port: 8080 readTimeout: 30s datasource: url: jdbc:mysql://192.168.1.100:3306/analytics?useSSL=false&serverTimezone=Asia/Shanghai username: readonly_user password: "YouShouldUseAStrongPassword" maxPoolSize: 20 minIdle: 5 connectionTimeout: 10000 meta: storage: sqlite dbPath: /app/data/meta.db api: prefix: /api enableCors: true authMode: header authToken: "change_this_token_before_production"数据库密码和鉴权token这两处,我见过不少部署的人直接用了默认值,网关上线后等于大门敞开。离线环境下没人扫描可能暂时没事,但一旦接入内网其他系统或者被扫描到,风险很大。装完第一时间改掉默认token。
datasource的url里需要注意字符集和时区参数,如果业务库是MySQL,serverTimezone不配置或者配错,查询时间字段会差8个小时,定位这种问题非常费劲。
3.4 启动服务并验证核心链路
配置文件改好、compose文件放好之后,启动服务:
# 在compose文件所在目录执行 docker compose up -d # 查看启动日志 docker compose logs -f --tail=200 apisql-gateway启动日志里重点看三样东西:配置加载成功没有、数据库连接池初始化成功没有、HTTP服务监听成功没有。我遇到过最典型的启动失败就是数据库连不上,日志里报Failed to initialize connection pool,这种基本是网络不通、账号密码错、或者数据库白名单没加主机。
服务起来之后,手动验证整条链路,这一步不能跳。按顺序操作:
- 调用健康检查接口:
curl http://127.0.0.1:8080/healthz,返回200。 - 在网关里注册一条测试API,SQL模板和参数映射写简单一点,比如查当前时间:
SELECT NOW() AS current_time。 - 用curl调用这条API,看返回结果是不是一个JSON对象,字段名对不对。
- 故意传一个错误的参数类型,确认网关返回的是参数错误而不是500。
这一步全部通过,说明网关本身和环境结合没问题了,离线安装的主流程就算走完了。
4. 升级实操:平滑替换与安全回滚
4.1 升级前的备份:配置和元数据一个都不能少
升级最容易犯的错误是看着版本号差了一点点,就觉得“小版本,应该兼容”,直接覆盖替换。apiSQL网关的元数据库里存着所有API定义、鉴权规则、审计日志,版本升级过程中元数据库的schema可能变化,配置文件的新增字段也可能不兼容。所以升级前必须做两件事:
第一,备份配置文件目录。这条最简单,整个目录打包:
tar czf apisql-gateway-config-backup-$(date +%Y%m%d).tar.gz ./config第二,备份元数据库。如果元数据库用的是文件型(比如SQLite),直接拷贝那个文件就行,但注意网关运行的时候直接拷可能拿到不一致的快照,最好停一下容器再拷,或者用SQLite自身的备份接口。如果元数据库是独立的MySQL/PostgreSQL,就用对应数据库的备份工具。
还有一条我的习惯:升级前把当前版本的API列表导出一份清单,记录每条API的路径、SQL模板、参数配置。这样升级后可以对照清单做回归,而不是只验证一两条测试API就觉得没问题了。
4.2 导入新版本镜像并准备升级
新版本镜像的离线包到位后,跟安装流程一样,先校验再load。这里有一个Docker镜像tag的细节要处理:compose文件里写的image标签是固定版本的,升级就是把这个标签指向新版本。
# 假设新版本是2.2.0 docker load < apisql-gateway-2.2.0.tar.gz # 确认新镜像在本地 docker images | grep apisql然后把compose文件里的image改成新版本号:
image: apecloud/apisql-gateway:2.2.0改完之后不要急着up。先看新版本的官方文档和升级说明(离线包里一般会附带),重点关注三块:一是配置文件里有没有新增必填项或废弃项,二是元数据库schema有没有变化、需不需要跑迁移脚本,三是API兼容性有没有破坏性变更。
4.3 执行升级与验证
升级的实际操作,我推荐这种“先拉起来看,再切流量”的方式:
# 停止旧容器(注意用 -d 不会阻塞,但 -d 前不能加 --remove-orphans 忽视) docker compose down # 使用新配置启动 docker compose up -d # 查看启动日志,确认新版本版本号打出 docker compose logs -f --tail=100 apisql-gateway如果一切正常,新容器起来后先访问健康检查接口,确认存活,然后跑一遍之前的API回归清单。重点检查之前导出的API列表里的路径是否都还在,SQL模板有没有被改动,参数传递行为是否一致。
这里有个我个人踩过的坑:升级后新版默认配置模板可能比旧版多了新字段,如果你直接把旧配置文件拿过来用,某些版本会报配置解析错误。正确的做法是用新版自带配置模板做基础,把旧配置里你自定义过的内容(数据库连接、端口、API前缀等)逐项填回去,而不是反过来拿旧配置硬套新版。
4.4 回滚:真有翻车的时候怎么办
升级后如果发现严重问题,比如新版网关连接池行为变了导致慢查询,或者某些API返回结果格式不对,必须快速回滚。Docker化的好处在这里体现得非常彻底:
# 1. 把compose文件里的image改回旧版本 # image: apecloud/apisql-gateway:2.1.0 # 2. 恢复旧配置文件(备份目录拷回原位) # 3. 重新创建容器 docker compose down docker compose up -d前提是旧镜像还在本地,没有因为docker rmi把它清了。所以我升级的时候从来不会急着清旧镜像,至少保留两周,确认新版本完全稳定了再清。如果目标机器磁盘紧张,也可以先把旧镜像save到外部存储,再删本地镜像,为回滚保留后路。
回滚之后同样要验证一遍,尤其是比较“升级前后来回切换”的情况,比如元数据库schema如果被新版本迁移过,回滚到旧版本可能读不了新schema,这时候就需要从备份恢复元数据库了——所以前面强调的“备份元数据库”这一步千万不能省。
5. 离线部署中的常见问题排查与避坑实录
5.1 高频问题速查表
| 症状 | 可能原因 | 排查与解决方法 |
|---|---|---|
docker load后启动报exec format error | 镜像架构与宿主机不匹配(最常见:x86镜像放到ARM机器) | docker images看镜像架构,`docker inspect <镜像> |
| 容器启动后立即退出,日志为空 | 配置文件路径不对或配置解析失败 | docker logs <容器>看报错,compose中GATEWAY_CONFIG_FILE路径与挂载卷路径是否对应 |
| 能启动但日志报数据库连接失败 | 数据库网络不通、账号权限、白名单未加 | 在宿主机用mysql客户端直连测试,确认账号可远程访问,URL里的库名存在 |
| API调用返回401/403 | 鉴权token没改或者调用方没带header | 检查配置authToken,调用时要求带X-API-Key等指定header |
| 查询结果中文乱码 | 数据库连接URL缺少字符集参数 | 给URL加characterEncoding=utf8,同时服务端时区参数配好 |
| 日志时间不是本地时区 | 容器默认UTC时区 | compose环境变量加TZ=Asia/Shanghai,重启容器 |
docker compose up -d报version不存在 | 用了旧版docker-compose执行新格式compose文件 | 换成docker compose(新版插件),或安装docker-compose-plugin |
5.2 排查思路与定位技巧
离线环境排错有个老大难问题:没有互联网,不能随手apt install一些排查工具。所以我的经验是,镜像里尽量带全基础排查工具,或者至少在宿主机上准备好几个万能的命令集合。
容器视角排查命令:
# 进入容器,查看进程、网络、配置文件 docker exec -it apisql-gateway sh # 容器里一般有基本的curl、ping,如果没有就通过宿主机的nsenter看网络 nsenter -t $(docker inspect -f '{{.State.Pid}}' apisql-gateway) -n ss -lntp宿主机视角:
# 端口是否监听 ss -lntp | grep 8080 # 容器到数据库的网络是否通(用容器IP测) docker exec apisql-gateway ping 192.168.1.100 # 看容器资源占用 docker stats apisql-gateway排查的顺序我建议遵循“从近到远”:先看容器本身活没活,再看健康检查接口通不通,然后看日志里有没有数据库连接报错,再逐层往数据库那边查。不要一上来就怀疑是配置问题,也不要想当然觉得离线包有坑——先看日志,日志不会骗人。
5.3 独家避坑经验:这些坑文档里一般不写
第一个坑是数据卷权限。apiSQL网关在容器里可能以非root用户运行,如果你挂载的宿主机目录权限是root,容器里没权限写日志或者写元数据库文件,表现就是容器起来了但功能不正常,日志文件一直不生成。处理办法是挂载之前先把目录owner改成容器内用户的UID:
mkdir -p ./logs ./data chown -R 10001:10001 ./logs ./data # 假设容器内用户UID是10001第二个坑是配置文件的变更追踪。apiSQL网关升级后可能在配置模板里加了新字段,但因为旧配置里没有这个字段,某些版本会静默使用默认值而不是报错——这会导致升级后出现“没改配置但行为变了”的诡异现象。我的习惯是每次升级都拿新版本自带的默认配置模板跑一遍diff,看清默认值的变化,再决定要不要显式写进配置文件。
第三个坑是SQL模板里的参数类型。不同版本的apiSQL网关对参数类型推断的处理可能有差异,旧版本可能把1推断成字符串,新版本推断成整数,看起来一样的SQL模板,返回结果却可能不一样。所以升级后回归测试不只要测路径通不通、返回200不200,还要对比返回的JSON结构和字段值,尤其注意空值和数字的情况。
6. 最后再分享一点我的实操心得
我个人在实际部署这类网关项目中的体会是:离线和在线部署的差别,不在于命令本身有多大区别,而在于离线环境把试错的成本放大了十倍、二十倍。在线环境下一条命令不对,删了重新拉,两分钟;离线环境下发现镜像不对,可能又要重新走拷贝、审批、传输的流程,来回折腾一整天。所以所有能在事前确认的事情,都尽量在动手之前确认完——Docker版本、镜像架构、配置文件模板、数据库连通性、磁盘空间,每一项都值得花几分钟过一遍,而不是等容器起不来的时候再排。
如果你接下来要操作的是“已经在线上跑着旧版本,准备升级到新版本”,那我最后再给一条建议:哪怕新版本只差一个小版本号,也把它当作大版本升级来对待。备份配置、备份元数据、导出一份当前API清单、在测试环境先跑一遍升级演练,这些步骤谁省略了,谁就要做好在半夜回滚的心理准备。毕竟网关这东西,平时安安静静的,一旦出问题,所有依赖它的报表、接口、大屏全都会来找你。流程走全一点,比什么临场反应都快。
这套离线安装和升级的方案,我后来在好几个隔离环境的项目里复用,基本形成了一套固定打法:离线包三件套(镜像tar、配置模板、README校验信息)、compose统一管理、升级前必做备份和清单导出、回滚路径随时待命。顺着这个思路走,内网部署apiSQL网关这件事,大概率一次就能通。