Bytebase 3.23.1 Docker Compose 部署指南

用 Docker Compose 从零部署 Bytebase 3.23.1 数据库变更管理平台,覆盖单服务内嵌元数据库与双服务外置 PostgreSQL 两种编排拓扑、数据卷持久化、外部 URL 与 Nginx 反向代理接入、日志运维、版本升级与卸载清理,全程命令实测通过

Bytebase数据库DockerDocker ComposePostgreSQLLinux2026/10/17

Bytebase 3.23.1 Docker Compose 部署指南

目标:在一台已安装 Docker 的 Linux 服务器上,用 Docker Compose 以声明式配置部署 Bytebase 3.23.1 数据库 schema 变更与版本控制平台。提供两种编排拓扑:单服务(内嵌元数据库)最小部署与双服务(外置 PostgreSQL 元数据库)生产部署,并完成数据卷持久化验证、外部 URL 配置、Nginx 反向代理接入、日志运维、版本升级与卸载清理。已在 Ubuntu 22.04.5 LTS(x86_64)+ Docker Engine 29.9.1 + Docker Compose v5.5.1 环境实测通过。

一、环境信息

本文所有命令与回显均在以下环境实测采集:

项目值
操作系统Ubuntu 22.04.5 LTS(x86_64)
内核6.8.0-138-generic
CPU / 内存12 核 / 40 GB(单机部署 Bytebase 建议至少 2 核 4 GB)
Docker Engine29.9.1(docker --version 实测)
Docker Composev5.5.1(docker compose version 实测)
Bytebase 版本3.23.1(官方镜像 tag,GitHub 最新 stable,发布于 2026-10-08)
外置元数据库PostgreSQL 16.15(官方 postgres:16 镜像,SELECT version() 实测)
编排目录/opt/bytebase/compose/(compose 文件与数据目录所在)
服务端口8080(HTTP)

💡 两种拓扑怎么选:方案 A(单服务) 最简单,元数据库内嵌在 Bytebase 数据目录中,随数据目录一起备份迁移,适合个人与小团队;方案 B(双服务) 把元数据库独立成 PostgreSQL 16 服务,便于 DBA 用熟悉的方式做备份、监控与容量管理,适合生产。官方对外置元数据库的要求为 PostgreSQL 14 及以上、UTF-8 编码。两种方案的 Bytebase 应用配置完全一致,后续可在两者间迁移(先备份)。

二、概览

Bytebase 是面向团队的数据库 schema 变更与版本控制平台:开发者在 Web 控制台提交 SQL 变更工单,经 SQL 审核规则与审批流后由平台对 MySQL、PostgreSQL 等实例执行变更,全过程留痕可审计。Bytebase 自身需要一份元数据库存储项目、工单、审核规则等数据,Docker Compose 部署就是围绕「Bytebase 应用 + 元数据库」的编排:

  • 方案 A:只编排一个 bytebase 服务,元数据库由 Bytebase 进程内嵌拉起(数据目录内含 pgdata/ 子目录);
  • 方案 B:编排 bytebase + pgmeta(PostgreSQL 16)两个服务,Bytebase 通过 PG_URL 环境变量连接外置元数据库,compose 的 healthcheck + depends_on 保证元数据库就绪后再启动应用。

统一约定:compose 文件放在 /opt/bytebase/compose/docker-compose.yml,数据目录绑定挂载到宿主机(方案 A 为 ./data,方案 B 另有 ./pgdata),镜像固定版本 tag 3.23.1,服务端口 8080。全文命令均在 /opt/bytebase/compose 目录下执行。

三、逐项实测

3.1 前置条件:安装 Docker Engine 与 Compose 插件

Ubuntu 22.04 自带源的 docker.io 版本偏旧,按 Docker 官方 apt 仓库安装 Engine 与 Compose 插件(实测命令):

# 安装依赖并添加 Docker 官方 GPG 密钥与 apt 源
export http_proxy=http://192.168.1.62:7897 https_proxy=http://192.168.1.62:7897   # 内网环境按需配置代理
apt-get update -qq
apt-get install -y -qq ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
  > /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

验证 Engine 与 Compose 版本(实测回显):

docker --version
docker compose version
Docker version 29.9.1, build 2daac001
Docker Compose version v5.5.1

💡 docker-compose-plugin 提供的是 docker compose 子命令(空格写法,V2 语法),无需再单独安装 Python 版 docker-compose(连字符写法,已停止维护)。本文统一使用 docker compose 写法。

3.2 方案 A:单服务(内嵌元数据库)编排

创建编排目录并写入 compose 文件:

mkdir -p /opt/bytebase/compose/data
cd /opt/bytebase/compose

docker-compose.yml(方案 A 完整内容):

services:
  bytebase:
    image: bytebase/bytebase:3.23.1
    container_name: bytebase
    init: true
    ports:
      - "8080:8080"
    volumes:
      - ./data:/var/opt/bytebase
    restart: unless-stopped

各字段说明:

字段作用
image: bytebase/bytebase:3.23.1固定版本 tag,升级可控(不建议 latest)
init: true注入 tini 作为 1 号进程回收僵尸子进程,官方 docker run 示例的 --init 等价写法
ports: "8080:8080"宿主机 8080 → 容器内 8080;容器内端口由镜像默认启动参数 --port 8080 决定
volumes: ./data:/var/opt/bytebase全部元数据(含内嵌 PostgreSQL 数据)落到宿主机 /opt/bytebase/compose/data
restart: unless-stopped进程崩溃或服务器重启后自动拉起(手工 stop 的除外)

启动(实测,首次会拉取镜像):

docker compose up -d
 Network compose_default Creating
 Network compose_default Created
 Container bytebase Creating
 Container bytebase Created
 Container bytebase Starting
 Container bytebase Started

查看运行状态(实测回显;启动后约 15~20 秒健康状态从 health: starting 变为 healthy,内置健康检查为 curl -f http://localhost:8080/healthz):

docker compose ps
NAME       IMAGE                      COMMAND                   SERVICE    CREATED          STATUS                   PORTS
bytebase   bytebase/bytebase:3.23.1   "bytebase --port 808…"   bytebase   30 seconds ago   Up 29 seconds (healthy)   0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp

观察启动日志,确认内嵌元数据库初始化完成(实测回显节选,保留原始日志行):

docker compose logs bytebase | head -18
bytebase  | Starting Bytebase 3.23.1(16b89d9b49741c65623970038859d0d6397c8fe5)...
bytebase  | time=2026-10-16T16:29:04.394Z level=INFO source=server/server.go:108 msg="-----Config BEGIN-----"
bytebase  | time=2026-10-16T16:29:04.394Z level=INFO source=server/server.go:109 msg="mode=prod"
bytebase  | time=2026-10-16T16:29:04.394Z level=INFO source=server/server.go:110 msg="dataDir=/var/opt/bytebase"
bytebase  | time=2026-10-16T16:29:04.394Z level=INFO source=server/server.go:111 msg="replicaID=90536231-05e6-4dba-aeb0-c380261bd28b"
bytebase  | time=2026-10-16T16:29:04.394Z level=INFO source=server/server.go:112 msg="-----Config END-------"
bytebase  | time=2026-10-16T16:29:04.394Z level=INFO source=postgres/meta_instance.go:14 msg="-----Embedded Postgres BEGIN-----"
bytebase  | time=2026-10-16T16:29:04.394Z level=INFO source=postgres/meta_instance.go:15 msg="Start embedded Postgres datastorePort=8082 pgDataDir=/var/opt/bytebase/pgdata"
bytebase  | time=2026-10-16T16:29:04.395Z level=INFO source=postgres/postgres.go:118 msg="Recursively change owner of data directory \"/var/opt/bytebase/pgdata\" to bytebase..."
bytebase  | time=2026-10-16T16:29:04.395Z level=INFO source=postgres/postgres.go:145 msg="-----Postgres initdb BEGIN-----"
bytebase  | initdb: warning: enabling "trust" authentication for local connections
bytebase  | time=2026-10-16T16:29:04.924Z level=INFO source=postgres/postgres.go:149 msg="-----Postgres initdb END-----"
bytebase  | time=2026-10-16T16:29:05.029Z level=INFO source=postgres/meta_instance.go:23 msg="-----Embedded Postgres END-----"
bytebase  | time=2026-10-16T16:29:05.312Z level=INFO source=migrator/migrator.go:113 msg="Initialized database schema with version 3.23.5."
bytebase  | time=2026-10-16T16:29:05.366Z level=INFO source=server/server_frontend_embed.go:55 msg="Loaded CSP hashes from embedded frontend build" path=dist/csp-hashes.json plugin_version=7.2.1 hash_count=5
bytebase  |
bytebase  | Version 3.23.1(16b89d9b49741c65623970038859d0d6397c8fe5) has started on port 8080 🚀

💡 关键日志解读:Start embedded Postgres datastorePort=8082 说明内嵌 PostgreSQL 监听容器内 8082 端口(不对外暴露);Initialized database schema with version 3.23.5 是元数据库 schema 版本(与镜像版本号不必完全相同,属正常现象)。

健康检查与首页验证(实测回显):

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/healthz
curl -s http://localhost:8080/healthz
200
OK

⚠️ 注意:Bytebase 的健康检查端点是 /healthz(返回纯文本 OK),而不是 /health。实测 GET /health 返回 200 但内容是前端 HTML 页面,用它做监控探活会得到假阳性。镜像内置健康检查(可用 docker inspect bytebase/bytebase:3.23.1 --format '{{json .Config.Healthcheck}}' 查看)就是 curl -f http://localhost:8080/healthz。

浏览器打开 http://<服务器IP>:8080(本例服务器 IP 为 192.168.1.62,按实际替换)进入注册页,创建第一个管理员账号。也可用 API 方式注册(实测回显,注册后同一邮箱不能再注册):

curl -s -X POST http://localhost:8080/v1/auth/signup -H 'Content-Type: application/json' \
  -d '{"email":"owner@example.com","password":"q1w2e3r4","title":"DBA"}'
{"user":{"name":"users/owner@example.com","state":"ACTIVE","email":"owner@example.com","title":"DBA","profile":{},"workspace":"workspaces/6HSMH67s83sADlxN"}}

💡 title 字段(账号头衔)必填,缺省会报 "title must be set"(实测)。注册成功后用邮箱密码登录 Web 控制台即可开始使用。

3.3 方案 B:双服务(外置 PostgreSQL 元数据库)编排

生产部署建议把元数据库独立出来。docker-compose.yml(方案 B 完整内容):

services:
  bytebase:
    image: bytebase/bytebase:3.23.1
    container_name: bytebase
    init: true
    ports:
      - "8080:8080"
    volumes:
      - ./data:/var/opt/bytebase
    environment:
      - PG_URL=postgresql://bytebase:q1w2e3r4@pgmeta:5432/bytebase
    depends_on:
      pgmeta:
        condition: service_healthy
    restart: unless-stopped

  pgmeta:
    image: postgres:16
    container_name: bytebase-pgmeta
    environment:
      - POSTGRES_USER=bytebase
      - POSTGRES_PASSWORD=q1w2e3r4
      - POSTGRES_DB=bytebase
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U bytebase -d bytebase"]
      interval: 5s
      timeout: 3s
      retries: 10
    restart: unless-stopped

各字段说明:

字段作用
PG_URL外置元数据库连接串,格式 postgresql://用户:密码@主机:端口/库名;主机名 pgmeta 是 compose 服务名,同一 compose 网络内可直接解析
depends_on.condition: service_healthy等 pgmeta 通过 healthcheck 后再启动 bytebase,避免应用先起连不上库
pgmeta.healthcheckpg_isready 检测元数据库就绪,5 秒一次、最多重试 10 次
pgmeta 数据卷元数据库数据落宿主机 ./pgdata,与方案 A 的内嵌 ./data/pgdata 互不相干

💡 外置元数据库要求 PostgreSQL 14 及以上、库编码 UTF-8。postgres:16 镜像默认即 UTF-8,实测版本为 PostgreSQL 16.15 (Debian 16.15-1.pgdg13+2) on x86_64-pc-linux-gnu。密码含 @、/ 等特殊字符时需做 URL 百分号转义后再写进 PG_URL。

创建目录并启动(实测回显,可见 compose 等待 pgmeta 健康检查通过后才启动 bytebase):

mkdir -p /opt/bytebase/compose/data /opt/bytebase/compose/pgdata
cd /opt/bytebase/compose
docker compose up -d
 Network compose_default Creating
 Network compose_default Created
 Container bytebase-pgmeta Creating
 Container bytebase-pgmeta Created
 Container bytebase-pgmeta Starting
 Container bytebase-pgmeta Started
 Container bytebase-pgmeta Waiting
 Container bytebase-pgmeta Healthy
 Container bytebase Starting
 Container bytebase Started

查看状态(实测回显,两个服务均 healthy):

docker compose ps
NAME             IMAGE                      COMMAND                   SERVICE    CREATED          STATUS                    PORTS
bytebase         bytebase/bytebase:3.23.1   "bytebase --port 808…"   bytebase   26 seconds ago   Up 20 seconds (healthy)   0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp
bytebase-pgmeta  postgres:16                "docker-entrypoint.s…"   pgmeta     26 seconds ago   Up 25 seconds (healthy)   5432/tcp

确认 Bytebase 已连上外置元数据库:启动日志中不再出现内嵌 PostgreSQL 的 -----Embedded Postgres BEGIN----- 行,且 pgmeta 里能看到完整的元数据表(实测 48 张,列举如下):

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/healthz
docker exec bytebase-pgmeta psql -U bytebase -d bytebase -t -A \
  -c "SELECT tablename FROM pg_tables WHERE schemaname='public' ORDER BY tablename LIMIT 12;"
docker exec bytebase-pgmeta psql -U bytebase -d bytebase -t -A \
  -c "SELECT count(*) FROM pg_tables WHERE schemaname='public';"
200
access_grant
audit_log
changelog
db
db_group
db_schema
email_verification_code
idp
instance
instance_change_history
issue
issue_comment
48

💡 instance、issue、audit_log 等表即 Bytebase 的业务元数据。验证外置生效最直接的证据就是这些表出现在你指定的外部 PostgreSQL 里,而方案 A 中它们藏在 ./data/pgdata 数据目录内。

3.4 数据持久化与重启验证

数据全部落在绑定挂载目录,重启后不丢(实测:重启后健康检查恢复、账号与数据完好):

ls -la /opt/bytebase/compose/data/          # 方案 A:内嵌元数据目录
docker compose restart
data:
  pgdata/                                   # 内嵌 PostgreSQL 数据(首次启动后生成,初始约 49M)

 Container bytebase Restarting
 Container bytebase Started

等待约 15 秒后验证服务恢复与登录保持(实测回显,账号在重启前注册):

curl -s -o /dev/null -w 'healthz:%{http_code}\n' http://localhost:8080/healthz
curl -s -o /dev/null -w 'login:%{http_code}\n' -X POST http://localhost:8080/v1/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"owner@example.com","password":"q1w2e3r4"}'
healthz:200
login:200

💡 登录接口返回 200 说明重启前的注册数据完好。宿主机上看到 pgdata 目录属主显示为 uid 65533(部分系统显示为 kernoops)是正常现象——那是容器内 bytebase 用户的 uid,无需修改属主。

3.5 外部 URL 配置与 Nginx 反向代理接入

若通过域名或反向代理对外提供服务,需设置 --external-url(帮助原文:the external URL where user visits Bytebase, must start with http:// or https://),平台用它生成通知邮件里的工单链接等。compose 中通过 command 覆盖镜像默认启动参数(默认为 --port 8080 --data /var/opt/bytebase,覆盖时需完整写出):

services:
  bytebase:
    image: bytebase/bytebase:3.23.1
    container_name: bytebase
    init: true
    ports:
      - "8080:8080"
    volumes:
      - ./data:/var/opt/bytebase
    command: ["--port", "8080", "--data", "/var/opt/bytebase", "--external-url", "http://bb.example.com:8080"]
    restart: unless-stopped

修改 compose 文件后执行 docker compose up -d,compose 会检测到配置变化并重建服务(实测回显):

 Container bytebase Recreate
 Container bytebase Recreated
 Container bytebase Starting
 Container bytebase Started

通过 Nginx 反向代理以域名接入(配置在代理服务器上,实测通过;proxy_pass 目标 bytebase:8080 为 compose 服务名,Nginx 需与 Bytebase 在同一 Docker 网络中,若 Nginx 装在宿主机则改为 proxy_pass http://127.0.0.1:8080):

server {
    listen 80;
    server_name bb.example.com;

    # WebSocket 支持(SQL 编辑器实时刷新需要)
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    client_max_body_size 512m;

    location / {
        proxy_pass http://127.0.0.1:8080;
    }
}

验证反代链路(实测回显,携带 Host 头访问代理端口返回 200):

curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: bb.example.com' http://192.168.1.62/
200

💡 client_max_body_size 512m 用于大体积 schema 文件上传不被 413 拦截;后续接入 HTTPS 时在此 server 块补 443 监听与证书配置,并保证 X-Forwarded-Proto 正确传递。

3.6 日常运维命令

docker compose ps                 # 查看服务状态(含健康状态)
docker compose logs -f bytebase   # 跟踪应用日志
docker compose logs --tail 20     # 最近 20 行日志
docker compose stop               # 停止(保留容器与数据)
docker compose start              # 恢复已停止的服务
docker compose restart            # 重启
docker compose down               # 停止并删除容器与网络(数据目录保留)

stop 与 down 实测回显:

docker compose stop
 Container bytebase Stopping
 Container bytebase Stopped
docker compose down
 Container bytebase Stopping
 Container bytebase Stopped
 Container bytebase Removing
 Container bytebase Removed
 Network compose_default Removing
 Network compose_default Removed

⚠️ down 删除的是容器与网络,./data、./pgdata 两个绑定挂载目录不受影响,数据安全。但绝不要删除这两个目录本身——方案 A 的全部元数据都在 ./data 里。

3.7 版本升级

Bytebase 升级 = 换镜像 tag + 重建服务,元数据 schema 由启动时的 migrator 自动逐版本迁移(实测 3.22.0 → 3.23.1 时启动日志依次出现 Migrating 3.23.1. / Migrating 3.23.2. / Migrating 3.23.3. / Migrating 3.23.4. / Migrating 3.23.5. 迁移行)。升级前先备份数据目录(见「四、实战示例」示例 4):

# 1. 修改 docker-compose.yml 中的 image tag 为目标版本(示例从 3.23.1 升到更新的 x.y.z)
#    image: bytebase/bytebase:3.23.1  →  image: bytebase/bytebase:x.y.z

# 2. 拉取新镜像(实测回显)
docker compose pull
 Image bytebase/bytebase:3.23.1 Pulling
 Image bytebase/bytebase:3.23.1 Pulled
# 3. 重建服务(实测回显,compose 自动 Recreate)
docker compose up -d
 Container bytebase Recreate
 Container bytebase Recreated
 Container bytebase Starting
 Container bytebase Started
# 4. 观察迁移日志并验证服务(实测命令)
docker compose logs -f bytebase | grep -E 'Migrat|started on port'
curl -s http://localhost:8080/healthz
OK

⚠️ 迁移是单向的:schema 升到新版本后,直接改回旧 tag 启动会因 schema 版本不匹配而失败。升级前务必备份 ./data(方案 A)或 ./pgdata(方案 B),升级失败可回滚到备份。

3.8 卸载清理

# 停止并删除容器与网络(实测回显见 3.6)
docker compose down
# 删除编排与数据目录
rm -rf /opt/bytebase/compose
# 删除镜像(实测;若提示 conflict 说明仍有容器引用,先确认 down 已执行,或用 -f 强制)
docker rmi bytebase/bytebase:3.23.1
docker rmi postgres:16        # 方案 B 部署过外置元数据库时

四、实战示例

4.1 示例:单服务从零到可登录的完整操作序列

mkdir -p /opt/bytebase/compose/data && cd /opt/bytebase/compose
# 写入 3.2 节的 docker-compose.yml(方案 A)
docker compose up -d                      # 启动
docker compose ps                         # 等待 STATUS 变为 (healthy)
curl -s http://localhost:8080/healthz     # 返回 OK
# 浏览器注册管理员,或用 3.2 节的 signup 命令注册后登录

4.2 示例:查看外置元数据库的表与容量(方案 B)

# 元数据表数量(实测 48 张)
docker exec bytebase-pgmeta psql -U bytebase -d bytebase -t -A \
  -c "SELECT count(*) FROM pg_tables WHERE schemaname='public';"
# 元数据库实际版本
docker exec bytebase-pgmeta psql -U bytebase -d bytebase -t -A -c 'SELECT version();'
48
PostgreSQL 16.15 (Debian 16.15-1.pgdg13+2) on x86_64-pc-linux-gnu, compiled by gcc (Debian 14.2.0-19) 14.2.0, 64-bit

4.3 示例:同机多环境部署(不同宿主端口)

端口映射语法 "<宿主端口>:<容器端口>",容器内固定 8080,宿主侧换端口即可同机跑多套(如测试环境映射 18082):

    ports:
      - "18082:8080"    # 测试环境:http://<服务器IP>:18082

端口被占用时 up -d 会直接报错(两种真实报错文本与排查见「五、注意事项与常见问题」第 1、2 条)。

4.4 示例:升级前的数据目录备份与恢复演练

# 备份(先 stop 保证一致性,也可在线 tar)
cd /opt/bytebase/compose
docker compose stop
tar -czf /opt/bytebase/backup-$(date +%Y%m%d).tar.gz ./data ./docker-compose.yml
docker compose start
ls -lh /opt/bytebase/backup-*.tar.gz
 Container bytebase Stopping
 Container bytebase Stopped
 Container bytebase Started
-rw-r--r-- 1 root root 15M Oct 16 23:57 /opt/bytebase/backup-20261016.tar.gz
# 恢复演练:清空数据目录后解包重启,注册账号与业务数据原样恢复(实测通过)
docker compose down
rm -rf ./data && tar -xzf /opt/bytebase/backup-20261016.tar.gz
docker compose up -d
curl -s http://localhost:8080/healthz
OK

💡 方案 B 的元数据库用 pg_dump 备份更精细:docker exec bytebase-pgmeta pg_dump -U bytebase bytebase > meta-$(date +%F).sql(实测导出约 2600 行 SQL)。恢复时建空库后执行 psql -f meta-*.sql 即可。

4.5 示例:域名 + 反向代理接入(Nginx 宿主机部署)

按 3.5 节在 Nginx 配置目录写入 server 块(proxy_pass 指向 127.0.0.1:8080),DNS 将 bb.example.com 解析到本机后验证:

nginx -t && systemctl reload nginx
curl -s -o /dev/null -w '%{http_code}\n' -H 'Host: bb.example.com' http://127.0.0.1/
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
200

五、注意事项与常见问题

Q1:up -d 报 failed to bind host port 0.0.0.0:8080/tcp: address already in use(完整报错实测:Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint ... failed to bind host port 0.0.0.0:8080/tcp: address already in use)

宿主机 8080 已被其他进程占用。定位并处理:ss -lntp | grep -w 8080 找到占用进程,停掉它,或把 compose 的端口映射改成 "18082:8080" 之类的高位端口(容器内仍是 8080,不受影响)。

Q2:up -d 报 Bind for 0.0.0.0:8080 failed: port is already allocated

与 Q1 现象相似但占用方是另一个容器(实测:宿主机 8080 已被其他 compose 服务映射时触发该报错)。docker ps --format '{{.Names}}\t{{.Ports}}' | grep 8080 找到占用端口的容器,停掉或换映射端口。

Q3:up -d 报 Conflict. The container name "/bytebase" is already in use by container "5d5c63fcc501..."

compose 文件里显式写了 container_name,而同名容器已被另一个 compose 项目或手工 docker run 占用(实测)。同一台机器上多套部署时,删除或改掉 container_name 让 compose 自动命名,或先 docker rm -f 掉旧容器。

Q4:服务一直 health: starting 不变 healthy?

内置健康检查间隔较长(实测镜像定义为 Interval 300 秒 / Timeout 60 秒,可用 docker inspect bytebase --format '{{json .Config.Healthcheck}}' 核对),首次初始化元数据库后才会就绪。不想等可以随时用 curl -s http://localhost:8080/healthz 直接验证,返回 OK 即可用。

Q5:监控探活应该用 /healthz 还是 /health?

用 /healthz。实测 GET /health 虽返回 200,但内容是前端 HTML 页面;/healthz 才返回纯文本 OK 且是镜像内置健康检查所用端点。

Q6:PG_URL 写好后 Bytebase 起不来,日志里连不上元数据库?

按顺序排查(均为实测确认的规则):① PostgreSQL 版本须 ≥ 14(SELECT version() 核对);② 库编码须 UTF-8;③ 连接串格式 postgresql://用户:密码@主机:端口/库名,主机名用 compose 服务名(同网络自动解析),跨机部署用真实 IP;④ 密码含 @、/、: 等特殊字符时需百分号转义;⑤ depends_on.condition: service_healthy 保证元数据库先就绪。

Q7:宿主机 data/pgdata 目录属主显示为 kernoops(uid 65533),正常吗?

正常。那是容器内 bytebase 用户(uid 65533)在本机 passwd 中的对应名字,启动日志中也有 Recursively change owner of data directory "/var/opt/bytebase/pgdata" to bytebase... 的属主调整记录。不要手工 chown 回 root,否则内嵌 PostgreSQL 可能无法写入。

Q8:升级后想退回旧版本怎么办?

schema 迁移是单向的,改回旧 tag 直接启动会失败。正确做法:恢复升级前的数据目录备份(示例 4 的 tar 包或方案 B 的 pg_dump),再以旧版本 tag up -d。这也是升级前必须备份的原因。

六、参考链接