SIPCAPTURE Homer 11 Docker Compose 部署指南

使用官方 ghcr.io/sipcapture/homer:11.0.369 镜像与 Docker Compose 部署 SIPCAPTURE Homer 11:homer 全模块 + RustFS S3 兼容冷层 + DuckDB 扩展离线灌注,含环境变量全表、冷卷排障实录与等价 docker run,全部命令实测回显。

SIPCAPTURE HomerSIPVoIP2026/10/27

SIPCAPTURE Homer 11 Docker Compose 部署指南

目标:在 Ubuntu 22.04 服务器上用 Docker Compose 一键部署 SIPCAPTURE Homer 11 官方镜像(ghcr.io/sipcapture/homer:11.0.369)——homer 全模块 + RustFS 对象存储冷层 + DuckDB 扩展离线灌注,覆盖镜像选型、部署步骤、数据持久化、环境变量全表、冷卷排障实录与等价 docker run,全部命令实测回显。单机二进制安装(deb/systemd)路线见本专栏 《Ubuntu 22.04 安装 SIPCAPTURE Homer 11 指南》。

一、环境信息

本文所有操作均在以下环境中实测完成,实测回显均为原始输出:

项目内容
操作系统Ubuntu 22.04 LTS(x86_64)
Docker Engine29.9.2
Docker Composev5.6.1(docker compose version 实测:Docker Compose version v5.6.1)
Homer 镜像ghcr.io/sipcapture/homer:11.0.369(官方 all-in-one,入口 /homer)
冷层对象存储镜像rustfs/rustfs:latest(S3 兼容,承载冷数据卷)
辅助镜像alpine:3.20(初始化作业)、rustfs/rc:latest(建桶)
部署目录/opt/homer-compose
宿主端口8131(Web/API)、9060(HEP UDP/TCP)、9080(HTTP ingest)、50051(gRPC)、9090(Prometheus)
数据卷homer_data(数据湖与扩展)、rustfs_data(冷层桶数据)
文档撰写日期2026-10-27

二、镜像选型说明

2.1 Homer 官方镜像

Homer 11 的官方镜像发布在 GitHub Container Registry:ghcr.io/sipcapture/homer,tag 与 homer-core 版本一致(本文实测 11.0.369)。镜像为 all-in-one 形态:

  • 单一入口二进制 /homer,同一进程按需启用 ingest(采集)、storage(存储)、node(查询节点)、coordinator(协调/API/UI)四个模块;
  • Web UI 静态资源内置于镜像 /usr/local/homer-core/dist,由 coordinator 模块直接伺服,无需另装 homer-ui;
  • 镜像内不带默认配置文件(实测 /usr/local/homer-core/ 下仅有 dist/ 目录),配置完全由 HOMER_* 环境变量注入,这是 Compose 形态与 deb 安装形态最大的差异(详见第七、八章);
  • 以普通用户 1000:1000 运行,数据目录 /data/homer 需要该 UID 属主——这也是第四章初始化作业存在的原因。

版本查询:镜像 tag 跟随 https://github.com/sipcapture/homer/releases 的 Release tag,11.0.x 数日一版,生产建议锁定具体版本号而非 latest。

2.2 冷层对象存储选型

Homer 11 的 DuckLake 存储支持"热卷(本地 Parquet)+ 冷卷(S3 对象存储)"分层:新数据写本地热卷,按 TTL 策略搬迁到 S3 冷卷。Compose 栈需要一个 S3 兼容对象存储承载冷卷。

💡 技术拓展:S3 兼容对象存储不止本文所用 RustFS。常见选项对比——

方案特点适用场景
RustFSRust 编写的新兴 S3 兼容存储,单二进制、资源占用低轻量自托管冷层(本文采用)
MinIO生态最成熟的 S3 兼容存储,社区版功能收敛后仍可用存量部署、需要成熟生态
SeaweedFS带文件系统语义的分布式存储,S3 网关可选海量小文件混合场景
Ceph RGW分布式存储 families 的 S3 网关已有 Ceph 集群的企业
云上 S3/OSS/COS托管服务,免运维不想自托管对象存储

本文选 RustFS:单容器即可拉起、自带健康检查与管理控制台,适合演示与中小规模冷层;生产可无缝替换为 MinIO 或云对象存储,只需改 S3 端点与密钥环境变量。

2.3 辅助镜像

  • alpine:3.20:两个一次性初始化作业(修数据卷属主、灌注 DuckDB 扩展);
  • rustfs/rc:RustFS 官方管理客户端镜像,用于创建冷数据桶 homer-data-cold。

三、前置条件

3.1 软硬件要求

  • Docker Engine 24+ 与 docker compose 插件(docker compose version 可执行);
  • 内存 ≥ 4GB:内置 DuckDB writer 默认 MEMORY_LIMIT=4GB(可通过环境变量下调,见第六章);
  • 磁盘:热卷 Parquet 与冷层桶按话务量规划,镜像本体约 1GB;
  • 服务器可访问外网(拉镜像与下载扩展);完全离线部署的扩展灌注方法见 3.3 与常见问题 Q1。

3.2 端口规划

部署前确认以下宿主端口未被占用(ss -ltn | grep -E '8131|9060|9080|50051|9090' 无输出即空闲;冲突时改 Compose 文件冒号左侧的宿主端口即可):

宿主端口服务内端口协议用途
81318080TCPREST API + Web UI
90609060UDP/TCPHEP 采集(heplify 等代理投递)
90809080TCPHTTP/JSON 批量投递
5005150051TCPAirport gRPC 查询节点
90909090TCPPrometheus /metrics

3.3 关键认知:DuckDB 扩展为什么必须预灌注

Homer 11 的存储层是 DuckLake(基于 DuckDB),首次启动时 DuckDB 需要 ducklake、aws、httpfs 等扩展。DuckDB 的默认行为是启动时自动从 extensions.duckdb.org 下载扩展——这意味着:

  • 服务器可直连外网时,无需任何准备,首次启动自动拉取;
  • 服务器不通外网或下载失败时,writer 模块直接启动失败(实测报错见常见问题 Q1);
  • S3 冷卷场景对 aws/httpfs 扩展有硬依赖,扩展缺失时冷卷 attach 会长时间挂起。

因此可靠的部署姿势是:把 5 个官方扩展文件预下载到部署目录,由初始化作业在启动时灌注到数据卷,彻底摆脱对 extensions.duckdb.org 的运行时依赖。这正是第四章 4.1 步骤存在的目的。

四、部署步骤

4.1 目录与 DuckDB 扩展准备

创建部署目录并下载 5 个官方扩展(版本目录 v1.5.6 必须与 Homer 11.0.369 内置 DuckDB 版本精确匹配,启动日志会打印 duckdb version version=v1.5.6):

mkdir -p /opt/homer-compose/duckdb-extensions && cd /opt/homer-compose/duckdb-extensions
wget https://extensions.duckdb.org/v1.5.6/linux_amd64/aws.duckdb_extension.gz
wget https://extensions.duckdb.org/v1.5.6/linux_amd64/azure.duckdb_extension.gz
wget https://extensions.duckdb.org/v1.5.6/linux_amd64/ducklake.duckdb_extension.gz
wget https://extensions.duckdb.org/v1.5.6/linux_amd64/httpfs.duckdb_extension.gz
wget https://extensions.duckdb.org/v1.5.6/linux_amd64/sqlite_scanner.duckdb_extension.gz
gunzip *.duckdb_extension.gz

实测落盘结果(约 140MB):

-rw-r--r-- 1 root root 28466502 aws.duckdb_extension
-rw-r--r-- 1 root root 24548926 azure.duckdb_extension
-rw-r--r-- 1 root root 36475990 ducklake.duckdb_extension
-rw-r--r-- 1 root root 21580734 httpfs.duckdb_extension
-rw-r--r-- 1 root root 34833582 sqlite_scanner.duckdb_extension

⚠️ 注意:下载的 5 个扩展是本架构验证过的完整集合——ducklake(DuckLake 湖仓表格式,writer 启动硬依赖)、httpfs 与 aws(HTTP/S3 访问,冷卷 attach 依赖)、sqlite_scanner(SQLite catalog 读取)、azure(Azure Blob 冷卷可选)。只灌 ducklake 一个时,纯本地盘可启动,但 S3 冷卷场景会在 attach 阶段挂起。

4.2 编写 docker-compose.yml

在 /opt/homer-compose/docker-compose.yml 写入以下内容(与实测运行版本一致;显式 name: homer-11-stack 锁定项目名,避免多套栈互相干扰):

name: homer-11-stack

services:
  homer-init:
    image: alpine:3.20
    volumes:
      - homer_data:/data/homer
      - ./duckdb-extensions:/ext:ro
    command: sh -c "chown -R 1000:1000 /data/homer && mkdir -p /data/homer/.duckdb/extensions/v1.5.6/linux_amd64 && cp /ext/*.duckdb_extension /data/homer/.duckdb/extensions/v1.5.6/linux_amd64/ && chown -R 1000:1000 /data/homer && echo homer data volume ok"
    restart: "no"
    networks:
      - homer-compose

  rustfs-init:
    image: alpine:3.20
    volumes:
      - rustfs_data:/data
    command: sh -c "chown -R 10001:10001 /data && echo rustfs data volume ok"
    restart: "no"
    networks:
      - homer-compose

  rustfs:
    image: rustfs/rustfs:latest
    security_opt:
      - "no-new-privileges:true"
    environment:
      RUSTFS_VOLUMES: /data
      RUSTFS_ADDRESS: 0.0.0.0:9000
      RUSTFS_CONSOLE_ADDRESS: 0.0.0.0:9001
      RUSTFS_CONSOLE_ENABLE: "true"
      RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS: "*"
      RUSTFS_ACCESS_KEY: rustfsadmin
      RUSTFS_SECRET_KEY: rustfsadmin
      RUSTFS_OBS_LOGGER_LEVEL: info
      RUSTFS_UNSAFE_BYPASS_DISK_CHECK: "false"
    volumes:
      - rustfs_data:/data
    healthcheck:
      test:
        [
          "CMD",
          "sh",
          "-c",
          "curl -fsS http://127.0.0.1:9000/health && curl -fsS http://127.0.0.1:9001/rustfs/console/health",
        ]
      interval: 15s
      timeout: 10s
      retries: 6
      start_period: 45s
    depends_on:
      rustfs-init:
        condition: service_completed_successfully
    networks:
      - homer-compose
    restart: unless-stopped

  rustfs-init-volume:
    image: rustfs/rc:latest
    depends_on:
      rustfs:
        condition: service_healthy
    entrypoint: >
      /bin/sh -c "
      until (/usr/bin/rc alias set rustfs http://rustfs:9000 rustfsadmin rustfsadmin) do echo '...waiting...' && sleep 1; done;
      /usr/bin/rc mb rustfs/homer-data-cold;
      echo 'RustFS initialization complete';
      "
    restart: "no"
    networks:
      - homer-compose

  homer:
    image: ghcr.io/sipcapture/homer:11.0.369
    user: "1000:1000"
    working_dir: /data/homer
    security_opt:
      - "no-new-privileges:true"
    cap_drop:
      - ALL
    depends_on:
      rustfs:
        condition: service_healthy
      homer-init:
        condition: service_completed_successfully
    volumes:
      - homer_data:/data/homer
    ports:
      - "8131:8080/tcp"
      - "9060:9060/udp"
      - "9060:9060/tcp"
      - "9080:9080/tcp"
      - "50051:50051/tcp"
      - "9090:9090/tcp"
    networks:
      - homer-compose
    restart: unless-stopped
    environment:
      HOMER_INGEST_ENABLE: "true"
      HOMER_STORAGE_ENABLE: "true"
      HOMER_NODE_ENABLE: "true"
      HOMER_COORDINATOR_ENABLE: "true"

      HOMER_INGEST_WORKER_COUNT: "8"
      HOMER_INGEST_QUEUE_SIZE: "80000"
      HOMER_INGEST_UDP_ENABLE: "true"
      HOMER_INGEST_UDP_HOST: 0.0.0.0
      HOMER_INGEST_UDP_PORT: "9060"
      HOMER_INGEST_UDP_MULTICORE: "true"
      HOMER_INGEST_TCP_ENABLE: "true"
      HOMER_INGEST_TCP_HOST: 0.0.0.0
      HOMER_INGEST_TCP_PORT: "9060"
      HOMER_INGEST_TCP_MULTICORE: "true"
      HOMER_INGEST_TLS_ENABLE: "false"
      HOMER_INGEST_HTTP_ENABLE: "true"
      HOMER_INGEST_HTTP_HOST: 0.0.0.0
      HOMER_INGEST_HTTP_PORT: "9080"
      HOMER_INGEST_HTTP_READ_TIMEOUT: "30"
      HOMER_INGEST_HTTP_WRITE_TIMEOUT: "30"
      HOMER_INGEST_HTTP_WEBSOCKET_ENABLE: "false"
      HOMER_INGEST_HTTPS_ENABLE: "false"
      HOMER_INGEST_HEP_HEPV3_ENABLE: "true"
      HOMER_INGEST_HEP_PROTOBUF_ENABLE: "true"

      HOMER_STORAGE_DUCKLAKE_CATALOG_TYPE: sqlite
      HOMER_STORAGE_DUCKLAKE_CATALOG_PATH: /data/homer/homer_catalog.sqlite
      HOMER_STORAGE_DUCKLAKE_DATA_PATH: /data/homer/parquet
      HOMER_STORAGE_DUCKLAKE_LAKE_NAME: homer_lake
      HOMER_STORAGE_DUCKLAKE_BATCH_SIZE: "10000"
      HOMER_STORAGE_DUCKLAKE_FLUSH_INTERVAL_SEC: "30"
      HOMER_STORAGE_DUCKLAKE_TUNING_MEMORY_LIMIT: "4GB"
      HOMER_STORAGE_DUCKLAKE_TUNING_THREADS: "2"
      HOMER_STORAGE_DUCKLAKE_TUNING_TEMP_DIRECTORY: /data/homer/.duckdb_spill
      HOMER_NODE_DUCKLAKE_TUNING_THREADS: "2"
      HOMER_NODE_DUCKLAKE_TUNING_TEMP_DIRECTORY: /data/homer/.duckdb_spill

      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_ENABLE: "true"
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_TTL_MOVE_INTERVAL_SEC: "3600"
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_MOVE_FACTOR: "0.8"
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_CONCURRENT_MOVES: "2"
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_MOVE_ON_STARTUP: "true"

      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_0_NAME: hot
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_0_TYPE: local
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_0_PATH: /data/homer/parquet
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_0_PRIORITY: "0"
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_0_MAX_DATA_AGE_DAYS: "2"
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_0_MAX_SIZE_GB: "100"

      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_NAME: cold
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_TYPE: s3
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_PATH: s3://homer-data-cold
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_PRIORITY: "1"
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_MAX_DATA_AGE_DAYS: "0"
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_S3_REGION: us-east-1
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_S3_ACCESS_KEY_ID: rustfsadmin
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_S3_SECRET_ACCESS_KEY: rustfsadmin
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_S3_ENDPOINT: http://rustfs:9000
      HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_S3_USE_SSL: "false"

      HOMER_STORAGE_DUCKLAKE_COMPACTION_ENABLE: "true"
      HOMER_STORAGE_DUCKLAKE_COMPACTION_CHECK_INTERVAL_SEC: "1800"
      HOMER_STORAGE_DUCKLAKE_COMPACTION_RETENTION_DAYS: "30"
      HOMER_STORAGE_DUCKLAKE_COMPACTION_SNAPSHOT_EXPIRE_INTERVAL_SEC: "3600"
      HOMER_STORAGE_DUCKLAKE_COMPACTION_MIN_AGE_SEC: "3600"
      HOMER_STORAGE_DUCKLAKE_COMPACTION_MIN_FILE_SIZE_BYTES: "0"
      HOMER_STORAGE_DUCKLAKE_COMPACTION_MAX_FILE_SIZE_BYTES: "134217728"
      HOMER_STORAGE_DUCKLAKE_COMPACTION_MAX_COMPACTED_FILES: "100"

      HOMER_NODE_FLIGHT_SERVER_HOST: 0.0.0.0
      HOMER_NODE_FLIGHT_SERVER_PORT: "50051"
      HOMER_NODE_FLIGHT_SERVER_MAX_MESSAGE_SIZE: "16777216"

      HOMER_NODE_DUCKLAKE_LAKE_NAME: homer_lake

      HOMER_NODE_DUCKLAKE_VOLUMES_0_NAME: hot
      HOMER_NODE_DUCKLAKE_VOLUMES_0_TYPE: local
      HOMER_NODE_DUCKLAKE_VOLUMES_0_CATALOG_TYPE: sqlite
      HOMER_NODE_DUCKLAKE_VOLUMES_0_CATALOG_PATH: /data/homer/homer_catalog.sqlite
      HOMER_NODE_DUCKLAKE_VOLUMES_0_PATH: /data/homer/parquet

      HOMER_NODE_DUCKLAKE_VOLUMES_1_NAME: cold
      HOMER_NODE_DUCKLAKE_VOLUMES_1_TYPE: s3
      HOMER_NODE_DUCKLAKE_VOLUMES_1_CATALOG_TYPE: sqlite
      HOMER_NODE_DUCKLAKE_VOLUMES_1_CATALOG_PATH: /data/homer/homer_catalog_cold.sqlite
      HOMER_NODE_DUCKLAKE_VOLUMES_1_PATH: s3://homer-data-cold
      HOMER_NODE_DUCKLAKE_VOLUMES_1_S3_REGION: us-east-1
      HOMER_NODE_DUCKLAKE_VOLUMES_1_S3_ACCESS_KEY_ID: rustfsadmin
      HOMER_NODE_DUCKLAKE_VOLUMES_1_S3_SECRET_ACCESS_KEY: rustfsadmin
      HOMER_NODE_DUCKLAKE_VOLUMES_1_S3_ENDPOINT: http://rustfs:9000
      HOMER_NODE_DUCKLAKE_VOLUMES_1_S3_USE_SSL: "false"

      HOMER_COORDINATOR_HTTP_SERVER_ENABLE: "true"
      HOMER_COORDINATOR_HTTP_SERVER_HOST: 0.0.0.0
      HOMER_COORDINATOR_HTTP_SERVER_PORT: "8080"
      HOMER_COORDINATOR_HTTP_SERVER_STATIC_PATH: /usr/local/homer-core/dist

      HOMER_COORDINATOR_NODES_0_NAME: local
      HOMER_COORDINATOR_NODES_0_HOST: 127.0.0.1
      HOMER_COORDINATOR_NODES_0_PORT: "50051"
      HOMER_COORDINATOR_NODES_0_FLIGHTSQL_PORT: "0"
      HOMER_COORDINATOR_NODES_0_USE_TLS: "false"
      HOMER_COORDINATOR_NODES_0_PRIORITY: "1"

      HOMER_COORDINATOR_SETTINGS_DB_PATH: /data/homer/homer_settings.duckdb

      HOMER_COORDINATOR_JWT_EXPIRE_HOURS: "24"
      HOMER_COORDINATOR_AUTH_ADMIN_USER: admin
      HOMER_COORDINATOR_AUTH_ADMIN_PASSWORD_HASH: 13a5c202e320d0bf9bb2c6e2c7cf380a6f7de5d392509fee260b809c893ff2f9

      HOMER_LOG_LEVEL: info
      HOMER_LOG_JSON: "false"
      HOMER_LOG_OUTPUT_0: stdout

      HOMER_PROMETHEUS_ENABLE: "true"
      HOMER_PROMETHEUS_HOST: 0.0.0.0
      HOMER_PROMETHEUS_PORT: "9090"
      HOMER_PROMETHEUS_PATH: /metrics

networks:
  homer-compose:
    driver: bridge
    ipam:
      config:
        - subnet: 10.89.61.0/24

volumes:
  rustfs_data:
  homer_data:

配置解读(关键项):

项含义与取舍
name: homer-11-stackCompose 项目名显式锁定;同一服务器上多套 Homer 栈时互不干扰
homer-init / rustfs-init一次性作业(restart: "no"):把数据卷属主修成服务运行 UID,并把扩展灌进数据卷
rustfs + healthcheck冷层对象存储;homer 以 service_healthy 条件依赖它,保证冷卷 attach 时 S3 已就绪
rustfs-init-volume等健康检查通过后创建冷数据桶 homer-data-cold
cap_drop: [ALL] + no-new-privileges最小权限加固;homer 以 UID 1000 运行不需要任何特权
VOLUMES_0/1 两级卷hot 本地卷优先级 0、cold S3 卷优先级 1;MAX_DATA_AGE_DAYS=2 表示热卷数据 2 天后搬冷
subnet 10.89.61.0/24自定义网段,避免与宿主已有网络冲突;无特殊需求可整段删除让 Docker 自动分配
管理口令哈希13a5c2...f2f9 即 sha256("q1w2e3r4")(下文 4.5 验证登录);生产务必换成自己的哈希

4.3 启动栈

cd /opt/homer-compose
docker compose up -d

实测 docker compose ps(初始化作业执行完即退出,常态只剩两个常驻服务):

NAME                      IMAGE                               COMMAND                  SERVICE   CREATED          STATUS                    PORTS
homer-11-stack-homer-1    ghcr.io/sipcapture/homer:11.0.369   "/homer"                 homer     30 minutes ago   Up 30 minutes             0.0.0.0:9060->9060/tcp, [::]:9060->9060/tcp, 0.0.0.0:9080->9080/tcp, [::]:9080->9080/tcp, 0.0.0.0:9090->9090/tcp, [::]:9090->9090/tcp, 0.0.0.0:50051->50051/tcp, [::]:50051->50051/tcp, 0.0.0.0:9060->9060/udp, [::]:9060->9060/udp, 0.0.0.0:8131->8080/tcp, [::]:8131->8080/tcp
homer-11-stack-rustfs-1   rustfs/rustfs:latest                "/entrypoint.sh rust…"   rustfs    30 minutes ago   Up 30 minutes (healthy)   9000-9001/tcp

两个初始化作业的实测日志(docker compose logs homer-init rustfs-init-volume):

homer-init-1  | homer data volume ok
rustfs-init-volume-1  | ✓ Bucket 'rustfs/homer-data-cold' created successfully.
rustfs-init-volume-1  | RustFS initialization complete

验证扩展已灌注进数据卷(docker compose exec homer ls /data/homer/.duckdb/extensions/v1.5.6/linux_amd64/):

aws.duckdb_extension
azure.duckdb_extension
ducklake.duckdb_extension
httpfs.duckdb_extension
sqlite_scanner.duckdb_extension

4.4 启动里程碑日志确认

docker compose logs homer 关键里程碑(节选,均为实测原文):

INF homer-core starting in modular mode...
INF version version=11.0.369
INF duckdb version version=v1.5.6
INF enabled modules modules="[ingest storage node coordinator]"
INF DuckLake data directory ensured path=/data/homer/parquet
INF DuckLake table ready (double-buffer, appender) table=homer_lake.hep_proto_1_registration buf_a=mem_hep_proto_1_registration_a buf_b=mem_hep_proto_1_registration_b batch_size=10000
INF Node: Volume attached name=hot lake=homer_lake_hot path=/data/homer/parquet
INF Node: Volume attached name=cold lake=homer_lake_cold path=s3://homer-data-cold
INF Node: DuckDB attached catalogs catalogs="[homer_lake_cold homer_lake_hot]"
INF Node: tiered storage DuckDB wired for merged search (hot+cold)
INF Coordinator: API server started addr=0.0.0.0:8080
INF Prometheus metrics server starting addr=http://0.0.0.0:9090/metrics

确认要点:

  • enabled modules 四模块齐全;
  • Volume attached name=cold ... path=s3://homer-data-cold 是冷卷 attach 成功的标志——这行没出现就是扩展/网络问题(见常见问题 Q1);
  • API server started addr=0.0.0.0:8080 表示 Web/API 就绪。

TTL 分层服务也随之启动(每小时扫描一次热卷,符合 TTL_MOVE_INTERVAL_SEC=3600):

INF TieringService: TTL partition scan source=hot dest=cold max_data_age_days=2 partition_date_cutoff=2026-10-25 rule="partition date <= cutoff (inclusive); cutoff is calendar today minus max_data_age_days"
INF TieredStorageManager: Volume maintenance completed volume=cold lake=homer_lake_cold

版本复核:

docker compose exec homer /homer version

实测输出:

homer-core 11.0.369
built 2026-10-25 20:29:40, commit da04658, go go1.27.2, linux/amd64

4.5 Web UI 与 API 登录验证

Web UI 浏览器访问 http://<服务器IP>:8131/,命令行验证首页与登录 API:

curl -s -o /dev/null -w 'GET / -> %{http_code}\n' http://127.0.0.1:8131/
curl -s -X POST http://127.0.0.1:8131/api/v4/auth/sessions \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"q1w2e3r4"}'

实测返回(截取,JWT 为有效登录凭据):

GET / -> 200
{"data":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsImlzcyI6ImhvbWVyLWNvcmUiLCJleHAiOjE3OTMxNjA0NjMsImlhdCI6MTc5MzA3NDA2MywianRpIjoiNDY1OTU0NTc5MjQyYmU0ODlkYTUzOWQ2YWFmNWEzYzgifQ.Hh-ESPTxDGw_HMsIAd6ufBuQqAKE1vQjQ9sza3SCc5s","scope":"admin","user":{"admi

登录后即可在 Web UI 中查看仪表盘与检索页面。注入 SIP 流量并检索验证的完整流程(heplify 采集 → sipsak 发包 → REST/CLI 检索)已在 《Ubuntu 22.04 安装 SIPCAPTURE Homer 11 指南》第五章实测闭环,HEP 投递地址改成本服务器的 9060 端口即可复用。

4.6 指标端点验证

curl -s http://127.0.0.1:9090/metrics | grep '^homer_' | head -3

实测输出(节选):

homer_ducklake_table_flush_duration_seconds_bucket{table="homer_lake.hep_proto_100_default",le="0.001"} 0
homer_ducklake_table_flush_duration_seconds_bucket{table="homer_lake.hep_proto_100_default",le="0.01"} 61
homer_ducklake_table_flush_duration_seconds_bucket{table="homer_lake.hep_proto_100_default",le="0.05"} 61

homer_* 指标正常输出即 Prometheus 端点可用。

五、数据持久化说明

5.1 两个命名数据卷

卷服务内挂载点内容
homer_data/data/homerDuckLake 数据湖:parquet/(热数据)、homer_catalog.sqlite(热卷 catalog)、homer_catalog_cold.sqlite(冷卷 catalog)、homer_settings.duckdb(用户/仪表盘等设置库)、.duckdb/extensions/(灌注的扩展)、.homer_jwt_secret 等密钥文件
rustfs_data/dataRustFS 对象存储桶数据(含冷数据桶 homer-data-cold)

实测 /data/homer 布局(docker compose exec homer ls -la /data/homer/):

drwxr-xr-x 5 homer homer   4096 Oct 27 04:07 .
drwxr-xr-x 3 root  root    4096 Oct 25 20:29 ..
drwxr-xr-x 3 homer homer   4096 Oct 27 03:41 .duckdb
drwxr-xr-x 3 homer homer   4096 Oct 27 03:41 .duckdb_spill
-rw------- 1 homer homer     44 Oct 27 03:41 .homer_jwt_secret
-rw------- 1 homer homer     44 Oct 27 03:41 .homer_node_auth_token
-rw-r--r-- 1 homer homer 155648 Oct 27 03:41 homer_catalog.sqlite
-rw-r--r-- 1 homer homer      0 Oct 27 03:41 homer_catalog.sqlite.lock
-rw-r--r-- 1 homer homer 147456 Oct 27 03:41 homer_catalog_cold.sqlite
-rw-r--r-- 1 homer homer  12288 Oct 27 03:41 homer_settings.duckdb
-rw-r--r-- 1 homer homer  48048 Oct 27 03:41 homer_settings.duckdb.wal
drwxr-xr-x 3 homer homer   4096 Oct 27 03:41 parquet

5.2 备份与迁移要点

  • 冷数据本体在 rustfs_data 卷(即 S3 侧),元数据在 homer_data 卷的两个 catalog SQLite 文件——两者必须成对备份,只备 catalog 不备桶数据会丢冷数据;
  • homer_settings.duckdb 存用户与仪表盘,体量小、价值高,随卷备份即可;
  • down -v 会同时删除两个卷(含全部话单数据),生产操作用 docker compose down(不带 -v);
  • 需要绑定宿主目录而非命名卷时,把 volumes: 段改为 bind mount(如 ./homer-data:/data/homer),属主仍须是 UID 1000。

六、环境变量表

官方镜像无内置配置文件,以下 HOMER_* 环境变量即该形态的全部配置面(值为本文实测生效值)。

模块开关:

环境变量实测值说明
HOMER_INGEST_ENABLEtrue启用采集模块(HEP 收包)
HOMER_STORAGE_ENABLEtrue启用存储模块(DuckLake writer)
HOMER_NODE_ENABLEtrue启用查询节点模块(50051/gRPC、/query)
HOMER_COORDINATOR_ENABLEtrue启用协调模块(API/Web UI/节点路由)

采集(ingest):

环境变量实测值说明
HOMER_INGEST_WORKER_COUNT8收包工作协程数
HOMER_INGEST_QUEUE_SIZE80000收包队列长度,溢出即丢包(见 metrics 计数)
HOMER_INGEST_UDP_ENABLE / _HOST / _PORTtrue / 0.0.0.0 / 9060HEP over UDP 监听
HOMER_INGEST_UDP_MULTICORE / HOMER_INGEST_TCP_MULTICOREtrue多核收包(每核一 socket)
HOMER_INGEST_TCP_ENABLE / _HOST / _PORTtrue / 0.0.0.0 / 9060HEP over TCP 监听
HOMER_INGEST_TLS_ENABLEfalseHEP over TLS(9061),生产外网采集建议开启
HOMER_INGEST_HTTP_ENABLE / _HOST / _PORTtrue / 0.0.0.0 / 9080HTTP/JSON 批量投递监听
HOMER_INGEST_HTTP_READ_TIMEOUT / _WRITE_TIMEOUT30 / 30HTTP 投递读写超时(秒)
HOMER_INGEST_HTTP_WEBSOCKET_ENABLEfalseWebSocket 投递开关
HOMER_INGEST_HTTPS_ENABLEfalseHTTPS 投递开关
HOMER_INGEST_HEP_HEPV3_ENABLEtrue接受 HEPv3(主流代理均用 v3)
HOMER_INGEST_HEP_PROTOBUF_ENABLEtrue接受 Protobuf 变体

存储(storage/ducklake):

环境变量实测值说明
HOMER_STORAGE_DUCKLAKE_CATALOG_TYPEsqlitecatalog 类型(SQLite 单文件,免外部数据库)
HOMER_STORAGE_DUCKLAKE_CATALOG_PATH/data/homer/homer_catalog.sqlite热卷 catalog 路径
HOMER_STORAGE_DUCKLAKE_DATA_PATH/data/homer/parquetParquet 数据目录
HOMER_STORAGE_DUCKLAKE_LAKE_NAMEhomer_lake湖仓名
HOMER_STORAGE_DUCKLAKE_BATCH_SIZE10000内存缓冲行数,凑满即批量落盘
HOMER_STORAGE_DUCKLAKE_FLUSH_INTERVAL_SEC30落盘周期(秒);小流量验证需等该周期(见 B1 篇排障 7.3)
HOMER_STORAGE_DUCKLAKE_TUNING_MEMORY_LIMIT4GBwriter DuckDB 内存上限
HOMER_STORAGE_DUCKLAKE_TUNING_THREADS2writer DuckDB 线程数
HOMER_STORAGE_DUCKLAKE_TUNING_TEMP_DIRECTORY/data/homer/.duckdb_spillDuckDB 溢写临时目录

分层策略(storage_policy):

环境变量实测值说明
HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_ENABLEtrue启用冷热分层
HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_TTL_MOVE_INTERVAL_SEC3600搬迁扫描周期(秒)
HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_MOVE_FACTOR0.8触发搬迁的容量因子
HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_CONCURRENT_MOVES2并发搬迁任务数
HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_MOVE_ON_STARTUPtrue启动即执行一轮搬迁检查
..._VOLUMES_0_NAME/TYPE/PATHhot / local / /data/homer/parquet热卷定义
..._VOLUMES_0_PRIORITY0优先级,0 最热
..._VOLUMES_0_MAX_DATA_AGE_DAYS2热卷数据最大保留天数(超过搬冷)
..._VOLUMES_0_MAX_SIZE_GB100热卷容量上限(GB)
..._VOLUMES_1_NAME/TYPE/PATHcold / s3 / s3://homer-data-cold冷卷定义(S3)
..._VOLUMES_1_PRIORITY1冷卷优先级
..._VOLUMES_1_MAX_DATA_AGE_DAYS00 = 冷卷永久保留
..._VOLUMES_1_S3_REGIONus-east-1S3 region(RustFS/MinIO 任意值即可)
..._VOLUMES_1_S3_ACCESS_KEY_ID / _SECRET_ACCESS_KEYrustfsadmin / rustfsadminS3 凭据(演示值,生产更换)
..._VOLUMES_1_S3_ENDPOINThttp://rustfs:9000S3 端点(Compose 网络内服务名)
..._VOLUMES_1_S3_USE_SSLfalse是否 HTTPS 访问 S3

压实(compaction):

环境变量实测值说明
HOMER_STORAGE_DUCKLAKE_COMPACTION_ENABLEtrue启用后台压实(合并小 Parquet 文件)
HOMER_STORAGE_DUCKLAKE_COMPACTION_CHECK_INTERVAL_SEC1800压实检查周期(秒)
HOMER_STORAGE_DUCKLAKE_COMPACTION_RETENTION_DAYS30数据全局保留天数
HOMER_STORAGE_DUCKLAKE_COMPACTION_SNAPSHOT_EXPIRE_INTERVAL_SEC3600DuckLake 快照过期周期
HOMER_STORAGE_DUCKLAKE_COMPACTION_MIN_AGE_SEC3600文件最短存活时间(秒),防止压实正在写入的文件
HOMER_STORAGE_DUCKLAKE_COMPACTION_MIN_FILE_SIZE_BYTES0参与合并的最小文件字节数(0=不设下限)
HOMER_STORAGE_DUCKLAKE_COMPACTION_MAX_FILE_SIZE_BYTES134217728合并目标上限 128MB
HOMER_STORAGE_DUCKLAKE_COMPACTION_MAX_COMPACTED_FILES100单轮合并参与文件数上限

查询节点(node):

环境变量实测值说明
HOMER_NODE_FLIGHT_SERVER_HOST / _PORT0.0.0.0 / 50051Airport gRPC 查询服务监听
HOMER_NODE_FLIGHT_SERVER_MAX_MESSAGE_SIZE16777216gRPC 单消息上限 16MB
HOMER_NODE_DUCKLAKE_LAKE_NAMEhomer_lake节点侧湖仓名
HOMER_NODE_DUCKLAKE_VOLUMES_0_*hot 本地卷(含 catalog 路径)节点侧热卷(coordinator 查询合并用)
HOMER_NODE_DUCKLAKE_VOLUMES_1_*cold S3 卷(独立 catalog homer_catalog_cold.sqlite)节点侧冷卷
HOMER_NODE_DUCKLAKE_TUNING_THREADS2节点侧 DuckDB 线程数
HOMER_NODE_DUCKLAKE_TUNING_TEMP_DIRECTORY/data/homer/.duckdb_spill节点侧溢写目录

协调(coordinator):

环境变量实测值说明
HOMER_COORDINATOR_HTTP_SERVER_ENABLE / _HOST / _PORTtrue / 0.0.0.0 / 8080REST API + Web UI 监听
HOMER_COORDINATOR_HTTP_SERVER_STATIC_PATH/usr/local/homer-core/dist内置 Web UI 静态资源路径
HOMER_COORDINATOR_NODES_0_NAME/HOST/PORTlocal / 127.0.0.1 / 50051查询节点清单(单机即本机回环)
HOMER_COORDINATOR_NODES_0_FLIGHTSQL_PORT0FlightSQL 端口(0=关闭)
HOMER_COORDINATOR_NODES_0_USE_TLSfalse节点间 TLS
HOMER_COORDINATOR_NODES_0_PRIORITY1节点路由优先级
HOMER_COORDINATOR_SETTINGS_DB_PATH/data/homer/homer_settings.duckdb设置库路径(必须指向可写卷,见 FAQ Q2)
HOMER_COORDINATOR_JWT_EXPIRE_HOURS24登录 JWT 有效期(小时)
HOMER_COORDINATOR_AUTH_ADMIN_USERadmin管理员用户名
HOMER_COORDINATOR_AUTH_ADMIN_PASSWORD_HASH13a5c2…f2f9管理员口令的 SHA-256 十六进制哈希

日志与指标:

环境变量实测值说明
HOMER_LOG_LEVELinfo日志级别(debug/info/warn/error/trace)
HOMER_LOG_JSONfalse结构化 JSON 日志开关
HOMER_LOG_OUTPUT_0stdout日志输出(_0 为输出列表索引)
HOMER_PROMETHEUS_ENABLEtruePrometheus 指标开关
HOMER_PROMETHEUS_HOST / _PORT / _PATH0.0.0.0 / 9090 / /metrics指标监听

💡 技术拓展:管理员口令存储方案上,Homer 11 采用不可逆的 SHA-256 哈希而非明文/盐值哈希。生成方式:echo -n '你的口令' | sha256sum。本文演示口令 q1w2e3r4 的实测结果:

13a5c202e320d0bf9bb2c6e2c7cf380a6f7de5d392509fee260b809c893ff2f9  -

与环境变量值一致。生产环境务必替换为自定义口令的哈希。

七、配置参数说明

7.1 环境变量与 homer.json 的映射规则

HOMER_* 环境变量与 deb 安装形态的主配置文件 homer.json 是同一棵配置树的两种落地方式,映射规则:

  1. HOMER_ 前缀后按下划线逐级展开为 JSON 路径,如 HOMER_INGEST_UDP_PORT → ingest.udp.port;
  2. 数组元素用 _数字_ 索引,如 HOMER_STORAGE_DUCKLAKE_STORAGE_POLICY_VOLUMES_1_S3_ENDPOINT → storage.ducklake.storage_policy.volumes[1].s3.endpoint、HOMER_COORDINATOR_NODES_0_NAME → coordinator.nodes[0].name;
  3. 日志输出列表同理:HOMER_LOG_OUTPUT_0 → log.output[0]。

官方提供 homer config show 子命令直接打印三来源合并后的生效配置(文件 + 环境变量 + 默认值)。在本文 Compose 栈(纯环境变量形态)上验证:

docker compose exec homer /homer config show --section storage.ducklake.batch_size --compact

实测输出 10000——正是环境变量 HOMER_STORAGE_DUCKLAKE_BATCH_SIZE 注入的值,而非内置默认 25000,证明环境变量已生效:

10000

deb 安装形态(homer.json 文件)上同命令的对照(ingest.udp.port 读自文件):

9060

7.2 两种配置形态的差异结论

维度Compose/镜像形态(本文)deb/rpm 安装形态
配置载体纯 HOMER_* 环境变量(镜像内无 homer.json)/usr/local/homer-core/etc/homer.json
配置生成手写 Compose environment 段复制 homer.json.example 后编辑,或 homer wizard 生成
生效方式docker compose up -d 重建服务修改后 homer system --reload(SIGHUP)或重启服务
查看生效值homer config show(容器内)homer config show

同一字段两边等价可用;选型建议:容器化部署随 Compose 走环境变量(版本可控、声明式),二进制部署随 homer.json 走文件(可热重载)。参数语义的逐项详解见本专栏 C1 篇《SIPCAPTURE Homer 11 配置参数详解》。

八、配置文件挂载

8.1 本形态默认无需挂载配置文件

官方镜像把 Web UI 静态资源与二进制都打进镜像,配置经环境变量注入,因此 Compose 文件里没有 homer.json 挂载。需要挂载的只有两类目录/卷:

  • homer_data 数据卷(第五章);
  • ./duckdb-extensions:/ext:ro——只读挂载给 homer-init 灌注扩展(宿主目录只进不退,服务本体不直接读它)。

8.2 如需用 homer.json 文件配置

坚持文件配置时,把编辑好的 homer.json 挂进容器并指定路径:

    volumes:
      - ./homer.json:/opt/homer/homer.json:ro
    # 启动参数无法直接加,改用环境变量指定配置路径:
    environment:
      HOMER_CONFIG_PATH: /opt/homer/homer.json

⚠️ 注意:镜像形态的主线是环境变量配置;--config-path 参数与 HOMER_CONFIG_PATH 的组合行为以 homer config show 的输出为准(配置文件、环境变量、默认值三层合并后打印)。混合使用时建议只用一种来源,避免两层值不一致难以排查。

九、等价 docker run

不用 Compose 时,最小 all-in-one(本地盘、无冷层)等价于:

docker run -d --name homer-11-aio \
  -p 8131:8080 -p 9060:9060/udp -p 9060:9060/tcp -p 9080:9080 -p 9090:9090 \
  -e HOMER_INGEST_ENABLE=true -e HOMER_STORAGE_ENABLE=true \
  -e HOMER_NODE_ENABLE=true -e HOMER_COORDINATOR_ENABLE=true \
  -e HOMER_COORDINATOR_AUTH_ADMIN_USER=admin \
  -e HOMER_COORDINATOR_AUTH_ADMIN_PASSWORD_HASH=13a5c202e320d0bf9bb2c6e2c7cf380a6f7de5d392509fee260b809c893ff2f9 \
  -e HOMER_COORDINATOR_SETTINGS_DB_PATH=/data/homer/homer_settings.duckdb \
  -v homer_data:/data/homer \
  ghcr.io/sipcapture/homer:11.0.369

⚠️ 注意:docker run 形态没有 homer-init 灌注作业,服务器需能直连 extensions.duckdb.org 由 DuckDB 首启自动拉取扩展;离线场景请改用本文 Compose 栈的灌注方案(3.3/4.1),或手工把扩展文件放进数据卷的 .duckdb/extensions/v1.5.6/linux_amd64/ 后再启动。要复刻本文的 S3 冷层,还需另行准备对象存储并把第六章 ..._VOLUMES_1_* 系列变量补全。

十、常用运维命令

# 查看栈状态
docker compose ps
# 跟踪 homer 日志
docker compose logs -f homer
# 改环境变量后重建 homer 服务
docker compose up -d --force-recreate homer
# 停止(保留数据卷)
docker compose down
# 停止并删除数据卷(危险:话单与设置全删)
docker compose down -v
# 进入 homer 执行 CLI(以查看版本为例)
docker compose exec homer /homer version
# 查看 DuckDB 版本(扩展目录 v1.5.6 的依据)
docker compose exec homer /homer system --duckdb-version
# 升级镜像:改 tag 后重建(数据卷保留;注意新版的 DuckDB 版本号可能变化,扩展需重新匹配下载)
#   1) 修改 docker-compose.yml 中 image 的 tag
#   2) 重新下载对应版本扩展并更新 homer-init 里的版本目录名
docker compose up -d

十一、常见问题

Q1:homer 服务启动失败,报 Failed to download extension "ducklake";或冷卷 Volume attached name=cold 迟迟不出现?

这是 DuckDB 扩展获取失败的两种表现。实测报错原文(docker compose logs homer):

Failed to create writer module: failed to create DuckLake manager: failed to create DuckLake sharded writer: failed to create shard 0: failed to load ducklake extension (run homer-core --install-extensions first): IO Error: Failed to download extension "ducklake" at URL "http://extensions.duckdb.org/v1.5.6/linux_amd64/ducklake.duckdb_extension.gz"
Extension "ducklake" is an existing extension.

For more info, visit https://duckdb.org/docs/stable/extensions/troubleshooting?version=v1.5.6&platform=linux_amd64&extension=ducklake (ERROR Failed to read connection)

根因:writer 模块建湖时 DuckDB 要加载 ducklake 扩展,服务器不通外网(或对 extensions.duckdb.org 下载受限)时自动下载失败,进程随即退出;S3 冷卷场景下 aws/httpfs 扩展缺失则表现为冷卷 attach 长时间挂起、Volume attached name=cold 不出现。解法即本文 3.3/4.1 的预灌注方案:把 5 个扩展文件预下载到 duckdb-extensions/,由 homer-init 灌注到数据卷后启动。实测灌注后完整栈(含 S3 冷卷)冷启动一次成功,4.4 节的 Volume attached name=cold 与冷卷维护日志即证据。可联网场景也可用官方自带的 homer system --install-extensions 预装扩展(ducklake、sqlite_scanner、httpfs、aws)。

Q2:报 failed to create settings db directory /var/lib/homer: permission denied?

设置库默认路径在 /var/lib/homer,而服务以 UID 1000 运行,该系统路径不可写。实测报错原文:

Failed to create coordinator module: failed to open settings db: failed to create settings db directory /var/lib/homer: mkdir /var/lib/homer: permission denied

解法:像本文 Compose 一样把 HOMER_COORDINATOR_SETTINGS_DB_PATH 指到可写数据卷(/data/homer/homer_settings.duckdb)。

Q3:扩展灌注了还是挂?

三点逐一核对:扩展版本目录必须与内置 DuckDB 版本一致(/homer system --duckdb-version 查看实测值,本文为 v1.5.6);目录结构必须是 .duckdb/extensions/v1.5.6/linux_amd64/;文件必须是解压后的 .duckdb_extension(.gz 原包不能直接用,见 4.1 的 gunzip 步骤)。

Q4:登录 API 返回 401?

口令哈希不匹配。Compose 形态的口令由 HOMER_COORDINATOR_AUTH_ADMIN_PASSWORD_HASH 决定,改口令 = 换成新口令的 SHA-256 哈希后 docker compose up -d --force-recreate homer。本文演示账号 admin/q1w2e3r4。

Q5:宿主端口被占 / 想改 Web 端口?

改 Compose ports: 冒号左侧宿主端口即可(本文 8131 对应服务内 8080),服务内端口不要动;9060 的 UDP/TCP 两条映射都要保留,否则 HEP 采集断链。

十二、参考链接