
简介Superset 4.1.1 中文版 Docker 离线部署包面向需要在无外网或受限网络环境中搭建数据探索与可视化平台的企业及开发者也适合具备基础 Docker 使用经验、希望快速复现 BI 环境的中级读者。压缩包共 6 个文件包含 3 个 tar 离线镜像、1 个 yml 服务编排文件、1 个 py 配置文件和 1 个 env 环境变量文件整体约 524.2MB覆盖镜像导入、容器编排与运行参数配置等部署要素。已有 370 人学习或下载说明该离线方案在中文数据可视化部署场景中具有一定参考价值。读者可据此获得一套可重复使用的容器化部署材料便于在隔离网络中完成 Superset 中文环境搭建并围绕数据库连接、服务组件组织与安全参数进行调整进而开展数据探索、分析与可视化展示。1. 内网环境里装 Superset 4.1.1真正麻烦的其实不是 Superset 本身内网机器上落地 Superset 4.1.1 中文版最耗时间的环节基本都出在环境隔离上——docker pull连不上、前端依赖装不下来、驱动缺一个包就卡上一整天。这套 Docker 离线部署包要解决的正是这件事镜像、汉化后的前端产物、常用数据库驱动、初始化脚本全部打成 tar拷进去docker load加一份 compose 就能跑起来。它适合三类人隔离网里要做 BI 报表的、需要给客户现场交付一套能直接演示的中文看板的以及在“半中半英”界面上反复踩坑、想一次把语言包搞定的。下面按我实际拆包、加载、初始化的顺序讲能抄的地方直接抄。2. 离线 Docker 包的选型账为什么不是进去 pip 装一遍先说明一个常见误区很多人第一反应是“把源码拷进去在目标机器上pip install apache-superset不就行了”。在内网里这条路基本走不通不是你操作有问题而是 Superset 的安装链路天生依赖大量外网资源。想明白它依赖什么才知道离线包该做成什么样、该核对哪些版本号。2.1 内网拉取失败的三个真实原因第一个原因是镜像分层。Superset 的官方镜像从基础 Python 镜像开始一层层叠上系统包、Python 依赖、前端构建产物最终体积通常在 1GB 以上。docker save只打包被引用的层所以离线 tar 的体积和层数强相关如果你在目标机器上只拷了一个“精简版”镜像运行时就可能因为缺某一层而报错退出。这块没有捷径导出的必须是完整引用链。第二个原因是构建期依赖。Superset 的前端是 React TypeScriptnpm ci要连 registry后端 Python 依赖要连包索引某些版本的可视化导出还依赖无头浏览器和字体包。这些在构建阶段就完成了所以离线交付的正确形态是“拿来即用的成品镜像”而不是“到现场再 build”。一旦你在内网机器上执行docker compose build那些联网步骤会全部重演一遍翻车几乎是必然的。第三个原因是驱动和字符集。生产上大概率接 PostgreSQL、MySQL 或 OLAP 引擎而镜像内置的驱动不会覆盖所有数据源。如果在容器里临时pip install容器重启后一切归零——这是很多人第一次部署后“昨天还能连、今天连不上”的根源。常见做法是把驱动预先打进一层派生镜像随离线包一起交付。提示先在你的构建机上把docker images和docker history都跑一遍确认每一个FROM的上游镜像都已导出再打包。漏一个基础层内网加载时就会得到一堆悬空镜像dangling image。2.2 一份能交付的离线包包含什么我把这类包拆成四层来看每层解决一个独立问题。判断一个 Superset 4.1.1 中文版离线包是否合格对照这张表逐项核就行。层内容缺失后果校验方式镜像层superset 主镜像、PostgreSQL、Redis 的 tar 包容器起不来或缺少元数据库docker load后docker images列表齐全编排层docker-compose.yml、superset_config.py、.env服务依赖错乱、配置每次手改docker compose config能正常解析驱动层数据源驱动的 wheel 包或预构建派生镜像数据源连接报No module named xxx容器内pip list能查到初始化层建库脚本、db upgrade、管理员创建、superset init登录页打不开或没有管理员账号首次启动后能进登录页编排层最容易被忽略。官方 compose 文件里通常带有build:段和镜像名占位离线环境必须把这些全改成固定的image:否则 compose 会尝试现场构建。另外.env里的密钥、数据库口令、SUPERSET_SECRET_KEY一定要在交付前改成目标环境的独立值直接沿用示例值属于典型的安全欠账——SECRET_KEY一旦泄露会话和加密字段就等于公开了。驱动层有个细节Superset 4.x 对 SQLAlchemy 2.x 的驱动版本比较敏感同一个数据库的新旧驱动行为差异很明显。我一般会在构建机上把pip download出来的 wheel 一起放进离线包的wheels/目录容器内用--no-index --find-links离线安装避免现场再碰网络。2.3 4.1.1 的版本对齐表版本错配是离线部署里最隐蔽的坑因为报错信息往往指向别处。下面这张表对齐的是常见依赖实际以镜像内python --version和pip list输出为准。组件建议版本说明Superset4.1.1主镜像版本与前端产物必须同源Python以镜像内为准3.10/3.11 系列派生镜像装驱动时不要换大版本元数据库PostgreSQL 16 或 MySQL 8字符集必须是 UTF8 / utf8mb4缓存与队列Redis 7用于缓存和异步任务队列SQLAlchemy2.x驱动版本要与之匹配中文字体Noto CJK 系列截图导出、PDF 导出中文不显示方框“以镜像内为准”不是敷衍而是因为 Superset 镜像在不同小版本间升级过基础镜像。你在派生镜像里pip install时如果 wheel 是按另一个 Python 小版本下载的装上去会报 ABI 不兼容。稳妥做法是进容器跑一次python -V pip -V把结果写进交付文档。另外提醒一句不要在派生镜像里升级 Flask、Werkzeug、SQLAlchemy 这些核心依赖。Superset 对它们的版本有明确约束你随手pip install -U一个很可能换来一个启动时报 import 错误的黑匣子而报错栈完全指不到你改的那行。3. 从 tar 到 running镜像搬运、编排改写与首次初始化这一章是整篇的核心按顺序做完就能看到登录页。我把流程拆成导出校验、导入改名、编排改写、初始化四段每段都给了可直接执行的脚本和参数说明。中间任何一步跳过后面排查成本都会翻倍。3.1 导出与校验先把完整性锁死在能联网的构建机上执行下面的脚本把三个镜像一次性导出并生成校验文件。这里用变量把仓库前缀抽出来是因为不同团队的内网仓库命名规则不一样硬编码进脚本后面改起来很烦。#!/usr/bin/env bash set -euo pipefail REGISTRYyour-registry.local/library # 换成内网仓库前缀或本地命名 VERSION4.1.1 IMAGES( ${REGISTRY}/superset:${VERSION} # 主镜像含中文前端产物 postgres:16-alpine # 元数据库 redis:7-alpine # 缓存与队列 ) mkdir -p offline/images offline/sha256 for img in ${IMAGES[]}; do docker pull $img # 文件名把 / 和 : 换成下划线避免跨平台路径问题 name$(echo $img | tr /: __) docker save -o offline/images/${name}.tar $img echo exported: $name done cd offline/images sha256sum ./*.tar ../sha256/images.sha256 sha256sum -c ../sha256/images.sha256脚本的逻辑很直白逐个拉取、逐个save、最后统一算摘要。几个参数要留意set -euo pipefail保证任何一个镜像拉取失败就中断避免生成一个“看起来完整、实际缺一块”的包tr /: __是为了让镜像名可以直接当文件名sha256sum -c是给你自己用的——拷贝到内网后第一件事就是重跑一次校验U 盘、跳板机、文件传输都可能悄悄改掉字节。注意docker save默认保留镜像的标签信息但如果你在目标机器上重命名过镜像再导出时标签会变。交付包里最好附一份镜像清单文本写清楚每个 tar 对应的原始标签。3.2 导入、改名与 compose 改写内网侧先校验再加载加载完统一打个本地标签这样 compose 里写死的image就不会因为仓库前缀不同而找不到。#!/usr/bin/env bash set -euo pipefail cd offline sha256sum -c sha256/images.sha256 # 先校验不通过直接退出 for f in images/*.tar; do docker load -i $f done docker images --format table {{.Repository}}\t{{.Tag}}\t{{.Size}}加载完成后用docker tag把镜像统一成 compose 里使用的名字。比如主镜像统一成superset-local:4.1.1元数据库统一成postgres-local:16。统一命名的好处是不同批次的离线包可以互换compose 文件不需要跟着改。接着改编排文件核心是三条删掉所有build:段、把image:指向本地标签、给元数据库加健康检查。services: postgres: image: postgres-local:16 environment: POSTGRES_USER: superset POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: superset volumes: - ./data/postgres:/var/lib/postgresql/data healthcheck: # 关键让 superset 等它真的可连 test: [CMD-SHELL, pg_isready -U superset -d superset] interval: 5s timeout: 5s retries: 20 superset: image: superset-local:4.1.1 depends_on: postgres: condition: service_healthy # 不要写 service_started redis: condition: service_started env_file: - .env ports: - 8088:8088 volumes: - ./superset_config.py:/app/pythonpath/superset_config.py:ro - ./data/uploads:/app/superset/static/assets/uploadscondition: service_healthy是这段的关键。很多“db upgrade 报连接拒绝”的问题根源就是只写了depends_on的默认语义——容器启动了但数据库还在初始化连接自然被拒。加上pg_isready健康检查之后Superset 会等到元数据库真正接受连接再启动。superset_config.py挂载成只读是刻意的避免容器内的进程误写配置也提醒你所有改动都走宿主机文件。上传目录单独挂出来是为了容器重建后不会丢掉用户上传的 CSV 和图片资源。3.3 首次初始化三条命令的顺序不能乱镜像就位、编排改好之后第一次启动要手动跑三条命令。顺序错了会得到各种奇怪的报错所以我把它们单独抽出来。# 1) 只起元数据库和缓存等健康检查通过 docker compose up -d postgres redis docker compose ps # 2) 升级元数据库表结构 docker compose run --rm superset superset db upgrade # 3) 创建管理员账号密码用引号包住避免特殊字符被 shell 吃掉 docker compose run --rm superset superset fab create-admin \ --username admin \ --firstname Admin \ --lastname User \ --email adminexample.com \ --password ChangeMe_2024! # 4) 初始化默认角色、权限和示例数据 docker compose run --rm superset superset init # 5) 全部服务拉起 docker compose up -dsuperset db upgrade做的是元数据库迁移把表结构推到与当前版本匹配的状态这一步在跨版本升级时尤其重要直接跳过会导致后续写入字段不存在。fab create-admin创建的是内置角色体系里的管理员用户名和邮箱在 4.x 中都是必填邮箱不用真实可达但格式要合法。superset init会补齐权限点、创建默认角色并对示例数据做初始化——这一步跑完登录页才有完整的菜单结构。--rm的意思是命令容器执行完立刻删除避免留下一堆退出状态的容器占空间。第一次跑的时候别加-d你要看着日志输出有报错当场就能看见。启动后用下面这条命令做一次自检返回200才算真的活了curl -s -o /dev/null -w %{http_code}\n http://127.0.0.1:8088/health3.4 启动之后先看这四处页面能打开不等于能干活。我一般会按下面四处逐一确认登录后进“设置 → 关于”确认版本号是 4.1.1进“数据源 → 数据库”看已配置的连接能不能点“测试连接”随便建一个图表确认前端渲染没有白屏最后用管理员账号进“安全 → 列表用户”确认权限点已初始化。这四步都过了才敢说这套离线部署是完整的。如果第 2 步测试连接失败先别怀疑网络八成是驱动缺失翻到第 5 章对应条目按流程处理。4. 中文化不是改一个环境变量语言包、字符集与时区三条链路“中文版”这个词很容易被误解成“改个配置就完事”。实际上界面语言、数据展示、时间显示是三条独立的链路任何一条没打通你看到的都是局部中文——菜单是中文的图表标题却是英文或者界面中文了导出的 CSV 打开全是乱码。这一章把三条链路分别说透。4.1 前端语言包与默认语言配置Superset 的语言体系分两层后端用 flask-babel 管理服务端文案前端用 i18next 管理界面文案。前端翻译文件在构建阶段就被打进产物里所以“中文版镜像”和“官方镜像”的差别很大程度上就在这一步——官方产物里中文覆盖率可能不完整中文版会在构建时补齐或更新语言资源。运行时切换语言有两个入口一是用户在个人设置里选语言二是在superset_config.py里设默认值。# superset_config.py BABEL_DEFAULT_LOCALE zh # 服务端默认语言影响邮件、错误提示 LANGUAGES { en: {flag: us, name: English}, zh: {flag: cn, name: Chinese}, } DEFAULT_TIMEZONE Asia/Shanghai # 按团队所在时区替换BABEL_DEFAULT_LOCALE决定新用户的初始语言已经登录过的用户不受影响——他们的选择存在用户配置里优先级更高。这就是为什么有人改了配置、重启了容器界面还是英文得去个人设置里手动切一次或者清掉浏览器本地存储。LANGUAGES决定语言下拉框里出现哪些选项只留中英两项能让界面干净不少。DEFAULT_TIMEZONE影响的是图表的时间轴解读跟界面语言无关但混在一起讲容易搞错。它决定服务端如何解释不带时区信息的时间字段设错了会出现“图表上的数据比数据库里晚八小时”这种经典问题。提示改完superset_config.py必须重启 superset 容器才生效docker compose restart superset就够不用重建镜像。4.2 元数据库字符集乱码问题的源头界面中文正常但数据集里的中文列名显示成问号或方块问题一定在元数据库。Superset 把数据集名称、图表标题、看板描述都存在元数据库里字符集不对就会在这些位置丢字符。PostgreSQL 建库时要显式指定编码不要依赖默认值CREATE DATABASE superset WITH ENCODING UTF8 LC_COLLATE C LC_CTYPE C TEMPLATE template0;TEMPLATE template0是为了绕开模板库的字符集约束保证新库能按你指定编码创建。LC_COLLATE和LC_CTYPE用C是为了排序行为稳定某些环境下用本地化 collation 会导致索引行为和预期不一致。MySQL 则是另一种情况必须确认库、表、连接三处都是utf8mb4CREATE DATABASE superset CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;连接串也要带上字符集参数否则客户端和服务器协商出的编码可能不是你想要的。用 PostgreSQL 时连接串里加?client_encodingutf8用 MySQL 时加?charsetutf8mb4。这两个参数漏了元数据库本身没错但写入路径上仍会出现乱码。4.3 图表与导出里的中文方框这一类问题最容易被误判成“汉化不完整”。现象是界面文字全是中文但导出的 PNG、PDF 里中文变成一排方框。原因很单纯——容器的精简基础镜像里没有中文字体渲染引擎找不到字形就用方框代替。解决方式是在派生镜像里装字体而不是在运行中的容器里临时装FROM superset-local:4.1.1 USER root # 只装 CJK 字体控制镜像体积 RUN apt-get update \ apt-get install -y --no-install-recommends fonts-noto-cjk \ rm -rf /var/lib/apt/lists/* USER superset--no-install-recommends是必须的否则一装就是几百兆的推荐包。rm -rf /var/lib/apt/lists/*清理包索引是减小镜像层的常规做法。装完重建镜像并重新docker save这个派生镜像要一起进离线包。字体装好之后导出图片和截图里的中文就正常了跟语言包没关系。另外 CSV 导出乱码是另一个问题通常出在导出文件的编码声明上。如果下游用 Excel 打开出现乱码多半是文件没有 BOM 头。这个属于使用习惯层面的问题一般在交付文档里写清楚“用支持 UTF-8 的工具打开”比改代码更实际。5. 避坑与排查离线部署里最容易翻车的五个点下面这五条都是我在不同环境里真实遇到过的按发生频率排序。每条都按“现象 → 原因 → 解决”写遇到问题直接对号入座不用从头排查。5.1 容器反复退出日志只有一行现象docker compose ps显示 superset 状态Restarting或Exited (1)日志里只有一行启动失败信息看不出所以然。原因九成是镜像标签没对上或者架构不匹配。在 ARM 机器上加载了 amd64 的 tardocker load会成功但运行时报 exec format error而且往往被 compose 的重启策略吞掉。另一种情况是 compose 里写的镜像名和你docker tag之后的名称差一个后缀。解决先docker inspect image --format {{.Os}}/{{.Architecture}}确认架构与宿主机uname -m一致再docker compose config | grep image逐条比对镜像名。把 compose 的restart: always临时改成no让容器退出后不再重启错误信息才会留在docker compose logs superset里。5.2 db upgrade 卡在连接拒绝现象执行superset db upgrade后报could not connect to server或连接超时但docker compose ps里 postgres 明明是 up 状态。原因up只代表容器进程起来了不代表数据库完成初始化并接受连接。PostgreSQL 首次启动要跑初始化脚本这期间端口是通的但连接会被拒。只写depends_on而没有健康检查条件时compose 不会等它。解决给 postgres 加pg_isready健康检查见 3.2 的编排示例并把依赖改成condition: service_healthy。已经很急的情况下手动docker compose exec postgres pg_isready -U superset反复确认就绪再跑 upgrade。根治靠编排不靠sleep 30。5.3 汉化只生效一半现象菜单是中文但新建图表弹窗、错误提示、部分按钮还是英文或者换台机器打开就变回英文。原因三种可能叠在一起。一是当前登录用户在个人设置里选过 English用户级配置优先于BABEL_DEFAULT_LOCALE二是浏览器本地存储里缓存了旧的语言选择三是镜像里那部分前端文案本来就没有中文翻译属于覆盖率问题不是配置问题。解决先让用户去个人设置里把语言改成中文再强刷页面。确认superset_config.py里BABEL_DEFAULT_LOCALE zh且容器已重启。如果个别新版本菜单项仍然英文那就是语言包覆盖率的问题只能等对应版本的中文资源补齐或者自己在语言文件里加条目后重新构建前端产物——这也是中文版镜像存在的意义。5.4 数据源测试连接报 ModuleNotFoundError现象在“数据源 → 数据库”里填好连接串点测试连接报No module named xxx但同样的连接串在宿主机客户端里能连通。原因驱动没打进镜像。Superset 通过 SQLAlchemy 方言连接数据库每种数据库对应一个独立的 Python 驱动包官方镜像只内置常见几种。你在容器里临时pip install装的包容器一重建就没了。解决在构建机上pip download出对应 wheel放进离线包用派生镜像离线安装FROM superset-local:4.1.1 USER root COPY wheels/ /tmp/wheels/ # --no-index 强制离线--find-links 指向本地 wheel 目录 RUN pip install --no-index --find-links/tmp/wheels \ clickhouse-connect0.7.19 \ psycopg2-binary2.9.9 USER superset参数说明--no-index会彻底禁用包索引任何缺失的依赖都会直接失败而不是悄悄联网--find-links指定本地目录pip 会从中解析依赖树。装完一定要重建镜像并重新导出 tar别只在运行中的容器里验证。注意驱动版本要跟 SQLAlchemy 2.x 匹配装完在容器里跑一次python -c import sqlalchemy; print(sqlalchemy.__version__)和驱动的 import确认没有 ABI 冲突再交付。5.5 上传文件失败日志里是 PermissionError现象上传 CSV、保存图表缩略图时提示失败容器日志里出现PermissionError: [Errno 13]指向挂载目录。原因Superset 镜像内的进程以非 root 用户运行常见 uid 是 1000。宿主机上创建的挂载目录属主是 root容器内用户没有写权限。superset_config.py只读挂载同理如果你把它挂成了可写又属主不对某些初始化步骤会失败。解决在宿主机上把挂载目录的属主改成容器内用户的 uid# 先确认容器内实际 uid docker compose run --rm superset id # 按输出结果调整常见是 1000 sudo chown -R 1000:1000 ./data/uploads ./data/postgres目录权限用750就够不要图省事chmod 777。另外注意PostgreSQL 数据目录对权限非常敏感属主或权限改错会导致数据库拒绝启动改之前先停容器。6. 把离线包做成可复用资产校验脚本与升级路径单次部署跑通只是及格线。真正省时间的是把这件事做成可复用资产一份校验脚本、一条明确的升级路径、一套元数据库备份习惯。我的做法是把校验和备份合到一个脚本里交付时随包附带接手的人照着跑就行。#!/usr/bin/env bash set -euo pipefail BACKUP_DIR./backup/$(date %Y%m%d_%H%M%S) mkdir -p $BACKUP_DIR # 1) 校验离线包完整性 sha256sum -c sha256/images.sha256 # 2) 元数据库逻辑备份升级前必做 docker compose exec -T postgres \ pg_dump -U superset -d superset -Fc ${BACKUP_DIR}/superset.dump # 3) 记录当前版本出问题时能说清从哪来 docker compose exec -T superset superset version ${BACKUP_DIR}/version.txt # 4) 记录镜像摘要便于回滚时定位 docker images --digests | grep superset ${BACKUP_DIR}/images.txtpg_dump -Fc生成的是自定义格式备份比纯 SQL 体积小、恢复快配合pg_restore可以做单表恢复。-T关掉伪终端否则在 CI 或定时任务里会因为 TTY 问题失败。版本号和镜像摘要这两份记录看似多余等真要回滚时就是后悔药——你会清楚地知道当时跑的是哪个镜像、哪个版本。升级路径建议固定成三步旧版本环境先跑一遍上面的备份新镜像加载后执行superset db upgrade启动后走一遍第 3.4 节的四项目视检查。跨大版本时要注意元数据库迁移不可逆备份不是可选项。升级场景必做动作回滚方式同一大版本内小版本升级备份 db upgrade换回旧镜像 恢复备份跨大版本先在测试环境完整演练一遍必须能恢复元数据库只换语言包重建前端备份 冒烟测试图表渲染换回旧镜像即可还有一个容易忽略的细节离线包的目录结构最好固定下来images/、sha256/、wheels/、compose/四个目录名不要随批次变化。从那以后我每次交付前都强制走一遍“校验脚本 备份脚本 四项目视检查”哪怕这次只改了一个驱动版本。血泪经验是跳过校验省下的五分钟通常会在三天后以两小时的排查代价还回来。希望帮到你。本文还有配套的精品资源点击获取