Apache TinkerPop 3.8.2 Docker Compose 部署指南

基于官方 tinkerpop/gremlin-server:3.8.2 镜像的 Docker Compose 部署指南,含 compose 编写、健康检查、数据持久化、docker run 等价命令与运维及镜像实测结论。

Apache TinkerPopApache TinkerPop2026/10/12

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

使用前必须了解的三个镜像行为(均为实测结论):

  1. 入口脚本 /docker-entrypoint.sh 会用 sed 把配置文件中的 host 行改写为该容器 eth0 网卡 IP,随后以 exec 方式启动 gremlin-server.sh。因此服务端只监听容器网卡 IP,容器内 127.0.0.1 无法连通(healthcheck 需据此设计,见 4.3 节)。
  2. 镜像内运行用户为非 root 的 gremlin 用户,挂载进来的宿主目录必须保证该用户可写,否则入口脚本改写配置时会因 Permission denied 反复重启。
  3. 镜像内不含 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 -Xmx512mJVM 启动参数,整体覆盖镜像默认值容器内进程回显确认生效;追加 --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 访问
port8182修改后同步调整 ports 与 healthcheck
graphsconf/tinkergraph-empty.properties 内存空图持久化按 5.2 节替换
authentication默认无按裸机认证配置挂载凭据文件,密码存 bcrypt 哈希
JAVA_OPTIONS镜像默认堆较大用环境变量限制为 -Xms256m -Xmx512m 并加 --add-opens

八、配置文件持久化

配置持久化的实测结论(均踩坑验证):

  1. 必须挂载目录而非单文件:入口脚本对配置文件执行 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。
  2. 挂载目录必须对镜像内 gremlin 用户可写:宿主目录权限不足时实测报 sed: can't create temp file '/opt/gremlin-server/custom/gremlin-server-persist.yamlXXXXXX': Permission denied 并重启循环,先执行 chmod 777 或调整属主。
  3. 数据目录 ./data:/opt/gremlin-server/data 同样需要可写权限,graph.kryo 落盘与读回都发生在这里。
  4. 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)。

十二、参考链接