Apache TinkerPop 3.8.2 Docker Compose 部署指南
基于官方 tinkerpop/gremlin-server:3.8.2 镜像的 Docker Compose 部署指南,含 compose 编写、健康检查、数据持久化、docker run 等价命令与运维及镜像实测结论。
Apache TinkerPop 3.8.2 Docker Compose 部署指南
一、环境信息
本文命令与回显均在以下环境实测通过。
| 项目 | 值 |
|---|---|
| 宿主系统 | Ubuntu 22.04.5 LTS(x86_64) |
| Docker 引擎 | Docker 29.8.0(含 Compose 插件 v2,实测版本 v5.5.1) |
| 部署对象 | Apache TinkerPop Gremlin Server 3.8.2 |
| 官方镜像 | tinkerpop/gremlin-server:3.8.2(Alpine,内置 OpenJDK 11) |
| 服务端口 | 8182 |
| 工作目录 | /opt/tp(compose 项目目录,可自定义) |
确认本机 Docker 与 Compose 版本:
docker version --format '{{.Server.Version}}'
docker compose version
29.8.0
Docker Compose version v5.5.1
二、镜像选型说明
选择官方镜像 tinkerpop/gremlin-server:3.8.2,与本文实测的 TinkerPop 版本严格对应。拉取:
docker pull tinkerpop/gremlin-server:3.8.2
Status: Downloaded newer image for tinkerpop/gremlin-server:3.8.2
docker.io/tinkerpop/gremlin-server:3.8.2
使用前必须了解的三个镜像行为(均为实测结论):
- 入口脚本 /docker-entrypoint.sh 会用 sed 把配置文件中的 host 行改写为该容器 eth0 网卡 IP,随后以 exec 方式启动 gremlin-server.sh。因此服务端只监听容器网卡 IP,容器内 127.0.0.1 无法连通(healthcheck 需据此设计,见 4.3 节)。
- 镜像内运行用户为非 root 的 gremlin 用户,挂载进来的宿主目录必须保证该用户可写,否则入口脚本改写配置时会因 Permission denied 反复重启。
- 镜像内不含 Gremlin Console,客户端验证需在宿主或其他机器安装 Console,或使用镜像自带的 JDK 11 编写驱动客户端。
另外,JVM 版本组合注意:官方镜像内置 JDK 11(本机实测 java -version 回显 openjdk version "11.0.32"),本文持久化场景所需的 --add-opens 参数在 JDK 11 与 17 下均可安全传入。
三、前置条件
- 宿主已安装 Docker 24 以上版本与 Compose v2 插件;
- 宿主 8182 端口未被占用;
- 镜像可达 Docker Hub(离线环境可先在有网机器执行 docker save 导出后 docker load 导入);
- 规划目录:本文统一使用 /opt/tp 作为 compose 项目目录。
四、部署步骤
4.1 准备项目目录
mkdir -p /opt/tp/data /opt/tp/conf
chmod 777 /opt/tp/data /opt/tp/conf
chmod 777 是为镜像内的 gremlin 用户(非 root)写入挂载目录做准备;安全要求更高的环境可改用 chown 将属主改为镜像内 gremlin 用户的 uid。
4.2 编写 docker-compose.yml
基础版(内存空图、堆内存 256-512MB、健康检查、资源上限):
vi /opt/tp/docker-compose.yml
services:
gremlin-server:
image: tinkerpop/gremlin-server:3.8.2
container_name: gremlin-server
restart: unless-stopped
ports:
- "8182:8182"
environment:
JAVA_OPTIONS: "-Xms256m -Xmx512m"
healthcheck:
test: ["CMD-SHELL", "nc -z $$(hostname -i) 8182"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s
deploy:
resources:
limits:
memory: 768M
两处细节说明:JAVA_OPTIONS 会整体覆盖镜像启动脚本的默认堆参数,实测容器内进程可见 -Xms256m -Xmx512m 生效;healthcheck 使用 nc 探测容器自身 IP(镜像入口脚本把监听地址改写为 eth0 IP,探测 127.0.0.1 永远失败,这是最容易踩的坑),alpine 基础镜像自带 nc。
4.3 启动与验证
cd /opt/tp
docker compose up -d
实测回显:
Container gremlin-server Created
Container gremlin-server Starting
Container gremlin-server Started
等待健康检查通过(start_period 后首个探测周期完成,约 40 秒):
docker inspect gremlin-server --format 'health={{.State.Health.Status}}'
health=healthy
查看服务日志确认启动完成:
docker logs gremlin-server 2>&1 | grep -E 'GraphTraversalSource|Channel started'
[INFO] o.a.t.g.s.u.ServerGremlinExecutor - A GraphTraversalSource is now bound to [g] with graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
[INFO] o.a.t.g.s.GremlinServer - Channel started at port 8182.
确认堆参数已按预期限制:
docker exec gremlin-server ps -ef | grep [j]ava | grep -oE 'Xmx[0-9]+m'
Xmx512m
4.4 客户端连接验证
在宿主安装 Gremlin Console 3.8.2(下载 apache-tinkerpop-gremlin-console-3.8.2-bin.zip 并解压,过程同裸机安装),编辑 Console 的 conf/remote.yaml,将 hosts 改为宿主 IP:
hosts: [192.168.1.62]
port: 8182
serializer: { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1, config: { serializeResultToString: true }}
连接并写入测试数据(实测回显):
gremlin> :remote connect tinkerpop.server conf/remote.yaml
==>Configured 192.168.1.62/192.168.1.62:8182
gremlin> :> g.addV("person").property("name","compose-test").next()
==>v[0]
gremlin> :> g.V().count()
==>1
五、数据持久化说明
5.1 默认行为
默认配置使用 tinkergraph-empty.properties 内存空图,容器删除后数据即丢失。要持久化需同时改造三点:图属性文件加落盘配置、自定义服务端 yaml、挂载宿主目录。
5.2 自定义持久化配置
在 /opt/tp/conf/tinkergraph-persist.properties 写入(graphLocation 建议直接用镜像内绝对路径):
gremlin.graph=org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph
gremlin.tinkergraph.vertexIdManager=LONG
gremlin.tinkergraph.graphLocation=/opt/gremlin-server/data/graph.kryo
gremlin.tinkergraph.graphFormat=gryo
从镜像内提取默认 gremlin-server.yaml 并把 graphs 指向上述文件:
docker run --rm --entrypoint cat tinkerpop/gremlin-server:3.8.2 /opt/gremlin-server/conf/gremlin-server.yaml > /opt/tp/conf/gremlin-server-persist.yaml
sed -i 's|conf/tinkergraph-empty.properties|/opt/gremlin-server/custom/tinkergraph-persist.properties|' /opt/tp/conf/gremlin-server-persist.yaml
5.3 更新 compose 并重建
services:
gremlin-server:
image: tinkerpop/gremlin-server:3.8.2
container_name: gremlin-server
restart: unless-stopped
ports:
- "8182:8182"
command: ["/opt/gremlin-server/custom/gremlin-server-persist.yaml"]
environment:
JAVA_OPTIONS: "-Xms256m -Xmx512m --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED"
volumes:
- ./data:/opt/gremlin-server/data
- ./conf:/opt/gremlin-server/custom
healthcheck:
test: ["CMD-SHELL", "nc -z $$(hostname -i) 8182"]
interval: 15s
timeout: 5s
retries: 3
start_period: 20s
deploy:
resources:
limits:
memory: 768M
两个必须项:JAVA_OPTIONS 追加 --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED(Gryo 序列化需要反射访问 java.util.concurrent.atomic 包,缺失时容器停止落盘失败产生 0 字节文件,重启读盘报 Could not load graph 并陷入查询引擎不可用状态);自定义配置通过 command 传入且必须放在目录挂载内(原因见第八节)。
重建并确认配置加载:
cd /opt/tp
docker compose up -d --force-recreate
docker logs gremlin-server 2>&1 | grep 'successfully configured'
Container gremlin-server Started
[INFO] o.a.t.g.s.u.DefaultGraphManager - Graph [graph] was successfully configured via [/opt/gremlin-server/custom/tinkergraph-persist.properties].
5.4 持久化闭环实测
写入两个顶点后执行 docker compose down(容器删除),宿主数据目录可见落盘文件:
ls -l /opt/tp/data/graph.kryo
-rw-r--r-- 1 dockremap dockremap 135 Oct 12 07:12 graph.kryo
重新 docker compose up -d 后查询,实测数据完整恢复:
gremlin> :> g.V().count()
==>2
gremlin> :> g.V().values("name").toList()
==>[compose-test, compose-test2]
六、常用环境变量表
| 环境变量 | 示例值 | 作用 | 实测说明 |
|---|---|---|---|
| JAVA_OPTIONS | -Xms256m -Xmx512m | JVM 启动参数,整体覆盖镜像默认值 | 容器内进程回显确认生效;追加 --add-opens 用于持久化与认证 |
| (配置文件) | 通过 command 传入 | 自定义 gremlin-server.yaml 绝对路径 | 形如 command: ["/opt/gremlin-server/custom/gremlin-server-persist.yaml"],文件需在目录挂载内 |
| (端口) | ports: 8182:8182 | 服务端口映射 | 客户端连宿主 8182 |
镜像未提供数据目录、认证开关等额外专用环境变量,持久化与认证均通过挂载自定义配置文件实现。
七、常用配置参数说明
容器方式与裸机方式的 gremlin-server.yaml 参数完全一致,差异集中在镜像入口脚本的行为:
| 参数 | 默认/镜像行为 | 调整建议 |
|---|---|---|
| host | 入口脚本强制改为容器 eth0 IP | 无需手工调整,端口映射后外部经宿主 IP 访问 |
| port | 8182 | 修改后同步调整 ports 与 healthcheck |
| graphs | conf/tinkergraph-empty.properties 内存空图 | 持久化按 5.2 节替换 |
| authentication | 默认无 | 按裸机认证配置挂载凭据文件,密码存 bcrypt 哈希 |
| JAVA_OPTIONS | 镜像默认堆较大 | 用环境变量限制为 -Xms256m -Xmx512m 并加 --add-opens |
八、配置文件持久化
配置持久化的实测结论(均踩坑验证):
- 必须挂载目录而非单文件:入口脚本对配置文件执行 sed -i(生成临时文件后改名覆盖),单文件 bind mount 时实测报 sed: can't move '/opt/gremlin-server/conf/gremlin-server-persist.yaml' to '/opt/gremlin-server/conf/gremlin-server-persist.yaml': Resource busy,容器进入反复重启循环。正确做法是把整个目录挂到镜像内新路径(如 ./conf:/opt/gremlin-server/custom),command 指向该路径下的 yaml。
- 挂载目录必须对镜像内 gremlin 用户可写:宿主目录权限不足时实测报 sed: can't create temp file '/opt/gremlin-server/custom/gremlin-server-persist.yamlXXXXXX': Permission denied 并重启循环,先执行 chmod 777 或调整属主。
- 数据目录 ./data:/opt/gremlin-server/data 同样需要可写权限,graph.kryo 落盘与读回都发生在这里。
- compose 文件中 healthcheck 的 $$(hostname -i) 双写美元符是 Compose 转义语法,$$(hostname -i) 在容器内展开为 $(hostname -i)执行。
九、等价 docker run 运行方式
与 4.2 节基础版 compose 等价的 docker run 命令:
docker run -d \
--name gremlin-server \
--restart unless-stopped \
-p 8182:8182 \
-e JAVA_OPTIONS="-Xms256m -Xmx512m" \
--memory 768m \
--health-cmd "nc -z \$(hostname -i) 8182" \
--health-interval 15s \
--health-timeout 5s \
--health-retries 3 \
--health-start-period 20s \
tinkerpop/gremlin-server:3.8.2
与 5.3 节持久化版等价:
docker run -d \
--name gremlin-server \
--restart unless-stopped \
-p 8182:8182 \
-e JAVA_OPTIONS="-Xms256m -Xmx512m --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED" \
-v /opt/tp/data:/opt/gremlin-server/data \
-v /opt/tp/conf:/opt/gremlin-server/custom \
--memory 768m \
tinkerpop/gremlin-server:3.8.2 \
/opt/gremlin-server/custom/gremlin-server-persist.yaml
十、常用运维命令
docker compose ps
NAME STATUS
gremlin-server Up 22 seconds (healthy)
docker compose logs -f --tail 100 gremlin-server
docker compose stop
docker compose restart
docker compose down
docker compose pull
说明:down 会删除容器但保留 ./data 与 ./conf 宿主目录(数据不丢);stop 为优雅停止,触发服务端正常关闭并把内存图写入 graph.kryo,禁止使用 docker rm -f 或 kill -9 强删(会绕过落盘流程导致数据丢失)。
十一、常见问题
Q1:容器反复重启,日志出现 sed: can't move 与 Resource busy 字样?
完整实测报错为 sed: can't move '/opt/gremlin-server/conf/gremlin-server-persist.yamlGDjBJj' to '/opt/gremlin-server/conf/gremlin-server-persist.yaml': Resource busy。根因是自定义配置文件以单文件方式 bind mount 进容器,入口脚本的 sed -i 无法对挂载点改名。把配置改为目录挂载(如 ./conf:/opt/gremlin-server/custom),command 指向目录内文件后重建。
Q2:容器反复重启,日志出现 sed: can't create temp file 与 Permission denied 字样?
完整实测报错为 sed: can't create temp file '/opt/gremlin-server/custom/gremlin-server-persist.yamlXXXXXX': Permission denied。根因是挂载目录对镜像内 gremlin 用户不可写,宿主执行 chmod 777 /opt/tp/conf /opt/tp/data 或调整属主后重建。
Q3:健康检查一直 unhealthy?
healthcheck 探测了 127.0.0.1。镜像入口脚本把服务端监听地址改写为容器 eth0 IP,回环地址探测必然失败。改用 test: ["CMD-SHELL", "nc -z $$(hostname -i) 8182"] 并重建。
Q4:容器重启后图数据丢失?
三点排查:是否挂载了 ./data 到 /opt/gremlin-server/data;图属性文件是否配置了 graphLocation 与 graphFormat;JAVA_OPTIONS 是否带 --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED(缺失时停止时写盘失败产生 0 字节文件)。此外确认是用 docker compose stop 或 down 正常停止,而非强制删除。
Q5:compose up 后端口冲突?
宿主 8182 被占用(可能裸机已装 Gremlin Server 或其他服务)。用 ss -lntp | grep 8182 找到占用方停掉,或把 ports 改为其他宿主端口(如 18182:8182)。
十二、参考链接
- TinkerPop 官方镜像说明:https://tinkerpop.apache.org/downloads.html
- Docker Hub 镜像仓库:https://hub.docker.com/r/tinkerpop/gremlin-server
- TinkerPop 官方参考文档:https://tinkerpop.apache.org/docs/3.8.2/reference/#gremlin-server
- Compose 文件语法参考:https://docs.docker.com/reference/compose-file/