我最早接触 Superset 是给一个零售项目做内部数据看板,当时团队在 Tableau 的授权费用和自建报表平台之间反复拉扯。后来确认了 Apache Superset 这套开源方案,配合 Docker Compose 一整套部署下来,从环境准备到数据源接入,基本就是半个工作日的量。这个组合最舒服的地方在于:Docker Compose 把部署环境固化成代码,Superset 负责可视化,MySQL 的 Sakila 示例库提供了一个比 hello world 更有分析价值的业务数据集,三样东西拼在一起,正好构成一条从零开始搭建数据平台的完整链路。
这篇文章围绕“用 Docker Compose 部署 Superset 并连接 MySQL 的 Sakila 数据库”这条主线展开,适合三类人看:一类是想快速搭一套可视化平台做内部使用的开发或运维,一类是刚开始接触 Superset 但是被各种安装步骤劝退的数据分析师,还有一类是单纯想找个练手数据集把数分技能串起来的同学。文里所有步骤都是我自己实际跑过的版本,不是文档翻译,踩坑记录也会一并放出来。
1. 方案选型:为什么是 Docker Compose + Superset + Sakila 这个组合
1.1 三个组件各自解决什么问题
先花一分钟把方案里每个角色说清楚。Docker Compose 是本地和单机环境里编排多容器最顺手的工具,一条 docker compose up -d 就能把 MySQL、Superset 以及它们之间的网络依赖一起拉起来,省去了手工安装数据库、配置 Python 环境、处理依赖冲突的系列麻烦。对于部署 Superset 这种依赖一大堆的可视化平台,容器化几乎是当前最优解。
Superset 本身是 Airbnb 开源的数据可视化平台,后来捐给了 Apache 基金会。它支持直接连各种数据库,通过 SQL Lab 写查询,也能在图表设计界面拖拽维度和指标。比起 Grafana 偏监控、Metabase 偏轻量查询,Superset 在“数据分析师自助探索”这个位置上更对味,图表类型也够丰富,还支持自定义 SQL 和 Jinja 模板,这是它比很多商业 BI 工具都灵活的地方。
Sakila 则是 MySQL 官方提供的示例数据库,模拟的是一家 DVD 租赁商店。它比随便造几张测试表强在数据是有关联的:电影、演员、品类、门店、顾客、租赁记录、支付记录,一套完整的业务闭环。拿来做 Superset 的演示数据,能跑出真正有业务含义的看板,而不是那种一眼假的 demo。
1.2 版本选择与前置环境要求
版本这块我直接给结论:Superset 用 3.1.0,MySQL 用 8.0,Docker 引擎要求 20.10 以上,Docker Compose 要求 v2 版本。之所以不追最新,是因为 Superset 4.x 之后的某些功能还在磨合期,而 3.x 系列的文档和社区解决方案最全,遇到问题搜得到答案比版本新更重要。
确认环境时我建议先跑一遍版本检查:
bash复制docker --version
docker compose version
如果 compose 命令提示找不到,说明装的是旧版 docker-compose 独立命令,需要升级或用 docker-compose 替代。另外 Linux 环境下要注意普通用户是否在 docker 用户组里,否则每条命令都要加 sudo,后面排错会非常痛苦。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 编排文件编写:docker-compose.yml 逐段拆解
2.1 目录结构与初始化脚本准备
动手写编排文件之前,先把目录结构规划好。我用的是这个布局:
code复制superset-sakila/
├── docker-compose.yml
├── Dockerfile
├── superset_config.py
└── init/
├── 00-setup.sql
├── 01-sakila-schema.sql
└── 02-sakila-data.sql
init 目录里放 MySQL 首次初始化时要执行的脚本。这里有个关键机制:MySQL 官方镜像在数据目录为空时,会按文件名字典序依次执行 /docker-entrypoint-initdb.d/ 下的 .sql 和 .sh 文件。利用这个特性,我们只需要把 Sakila 的官方脚本放进去,容器第一次启动就会自动建库导数据。
Sakila 脚本可以从 MySQL 官方 GitHub 仓库获取,也可以从 dev.mysql.com/doc/index-other.html 下载。下载后一定要确认文件编码是 UTF-8,Windows 下用记事本另存可能导致 BOM 头问题,进而执行报错。
2.2 初始化脚本与 MySQL 服务配置
先看 init/00-setup.sql 的内容,这个脚本负责创建 Superset 连接数据库时要用的账号:
sql复制CREATE USER 'superset'@'%' IDENTIFIED WITH mysql_native_password BY 'superset_pass';
GRANT ALL PRIVILEGES ON sakila.* TO 'superset'@'%';
FLUSH PRIVILEGES;
这里特意用 mysql_native_password 而不是 MySQL 8 默认的 caching_sha2_password,原因后面排查章节会详细说,简单讲就是为了兼容一些老版本驱动在握手协议上的坑。实际上 mysqlclient 2.x 已经支持 caching_sha2_password,但既然要讲部署,我就按最稳的方式写,跑通为先。
接着是 docker-compose.yml 里 MySQL 服务段:
yaml复制 mysql:
image: mysql:8.0
container_name: sakila_mysql
environment:
- MYSQL_ROOT_PASSWORD=root_pass
- MYSQL_DATABASE=sakila
- TZ=Asia/Shanghai
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-proot_pass"]
interval: 5s
timeout: 3s
retries: 20
MYSQL_DATABASE 声明了默认数据库名,等价于执行 CREATE DATABASE IF NOT EXISTS sakila。这里建议保留它,虽然 Sakila 官方 schema 脚本里也有建库语句,但既然 compose 支持声明式定义,就交给它管。
healthcheck 很重要,这是我踩过最多坑的地方。Superset 容器启动时要连接 MySQL,如果 MySQL 还没就绪就去初始化,连接失败会直接导致整个启动链路崩溃。有了 healthcheck 加上 depends_on 的 condition,才能保证启动顺序可控。
2.3 Superset 服务与依赖启动顺序
Superset 服务段这样写:
yaml复制 superset:
build: .
container_name: superset_app
environment:
- SUPERSET_SECRET_KEY=please_change_this_key
- SUPERSET_LOAD_EXAMPLES=no
ports:
- "8088:8088"
volumes:
- superset_data:/app/superset_home
- ./superset_config.py:/app/pythonpath/superset_config.py:ro
depends_on:
mysql:
condition: service_healthy
command: >
sh -c "
superset db upgrade &&
superset fab create-admin --username admin --firstname Admin --lastname User --email admin@example.com --password admin123 || true &&
superset init &&
/usr/bin/run-server.sh
"
这里说明几个关键点。第一,build: . 意味着不是直接用官方镜像,而是基于目录下的 Dockerfile 构建,Dockerfile 内容后面讲。第二,SUPERSET_LOAD_EXAMPLES=no 是关闭官方自带示例数据,因为我们要用自己的 Sakila 数据,加载示例反而占空间。第三,command 里的初始化动作值得逐条解释。
superset db upgrade 是初始化 Superset 自己的元数据库,这个库默认是 SQLite,存在挂载的 superset_data 卷里,也支持改成 MySQL 或 PostgreSQL,但在入门阶段用默认的 SQLite 完全够。superset fab create-admin 创建管理员账号,密码我这里是示例,建议部署时改掉。注意命令末尾的 || true,因为重复启动容器时账号已存在,fab 会报错退出非零,加这个是为了让幂等启动不中断。superset init 是初始化角色和权限,必须执行。最后 /usr/bin/run-server.sh 才是正式启动 Gunicorn Web 服务。
有过 Docker 经验的朋友应该看出来了,把初始化直接写进 command 会导致每次重启容器都执行一遍迁移和初始化,但 Superset 的迁移脚本本身做了版本控制,重复执行不会重复应用,所以实际影响不大。这也是官方推荐的做法。
2.4 自定义 Dockerfile 和 superset_config.py
Dockerfile 很简单,但少它不行。Superset 官方镜像基础环境是不带 MySQL 驱动的,不装驱动的话,后面在界面上添加数据库连接时会直接报 "Could not load database driver"。所以 Dockerfile 就做两件事:
dockerfile复制FROM apache/superset:3.1.0
USER root
RUN pip install mysqlclient==2.2.0 --no-cache-dir
USER superset
USER root 是因为官方镜像最后切换到了非 root 用户 superset,而 pip 安装需要写入系统目录,所以要临时切回 root,装完再切回来。这是很多人不知道的细节,如果不切用户直接尝试 pip install 会权限不足。
superset_config.py 里的内容:
python复制SECRET_KEY = 'your_secure_secret_key_change_in_production'
FEATURE_FLAGS = {
"ALERT_REPORTS": True
}
SECRET_KEY 是 Flask 应用签名会话用的,不配置 Superset 会启动失败或者每次重启会话失效。这个务必改成一个只有自己知道的随机字符串。FEATURE_FLAGS 里的 ALERT_REPORTS 开启告警和报表功能,非必需,但开了方便后续扩展。
2.5 deploy 字段到底要不要写
搜“docker compose 中是否需要 deploy”这个问题的人很多,这里统一说清楚。deploy 字段最早是给 Docker Swarm 用的,在 docker compose 本地运行时默认被忽略。但从 Compose v2.20 开始,deploy 下的 resources.limits 子项也能被识别用于限制容器资源。
我的建议是:单机部署不写 deploy 完全没问题,但如果你的机器上同时跑了 MySQL 和其他业务,担心 Superset 吃内存,可以加上:
yaml复制 deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
需要注意的是,这个语法在旧版 docker compose 里会被静默忽略,不是报错,所以加了不等于生效。想要精确验证资源限制,靠的是 docker stats 实时观察。
3. 部署启动与初始化流程
3.1 拉起服务:docker compose up -d 的完整含义
目录准备好之后,执行:
bash复制docker compose up -d
up 会创建并启动所有服务,-d 表示后台运行,日志不霸占终端。执行完推荐用 docker compose ps 查看状态,两个服务都显示 running 之后再继续。
这里顺带把跟 -d 相关的两个常见参数讲清楚,也是很多人问的。docker compose -f custom.yml up -d 里的 -f 是显式指定配置文件路径,适用于配置文件不在当前目录或文件名不是默认的 docker-compose.yml 的情况。而 docker compose -p myproject up -d 里的 -p 是覆盖项目名称,项目名默认取当前目录名,它会影响容器命名前缀和 Docker 网络名,比如网络会变成 myproject_default。如果你同时跑多套相同 compose 项目,而不是想要容器名冲突,就必须用 -p 区分。
3.2 初始化 Superset 管理员账号
如果 compose 文件里的 command 已经写了初始化逻辑,启动时 Superset 容器会自动迁移数据库并创建管理员。但有时候你会遇到初始化失败的情况,比如 MySQL 还没就绪导致数据库连接失败,此时容器会退出重启,陷入循环。
遇到这种情况,最稳妥的手动初始化方式是:
bash复制docker compose exec superset bash
superset fab create-admin --username admin --firstname Admin --lastname User --email admin@example.com --password admin123
superset init
exit
注意这里是在容器内执行,不是宿主机。如果你之前通过 Dockerfile 构建过镜像,容器里已经有 mysqlclient 驱动,后面连接数据库就不会卡在驱动缺失这一关。
3.3 验证 Sakila 数据是否正确导入
MySQL 容器启动之后,先验证脚本是否执行成功。进入容器查一下表数量和数据量:
bash复制docker compose exec mysql mysql -uroot -proot_pass -e "USE sakila; SHOW TABLES; SELECT COUNT(*) FROM film;"
正常情况下 SHOW TABLES 会列出 film、actor、category、rental、payment 等二十多张表,film 表有 1000 条记录。如果表是空的,八成是初始化脚本没执行成功,解决办法是删除卷重新初始化,后面排查章节会讲。
日志也是一个重要入口,MySQL 容器首次初始化时如果用 docker compose logs mysql 能看到每个脚本的执行输出,任何 SQL 语法错误都会打在这里。
4. 将 Sakila 接入 Superset:数据源与取数
4.1 在 Superset 中添加 MySQL 数据源
服务都起来后,浏览器访问 http://localhost:8088,用刚才创建的管理员账号登录。进入主界面后按这个路径操作:
Settings -> Database Connections -> Add Database
Database 类型选 MySQL,连接串格式是:
code复制mysql://superset:superset_pass@mysql:3306/sakila
这里最容易踩坑的就是 host 那一栏。很多第一次接触 Docker 的朋友会下意识填 localhost,然后怎么都连不上,这是因为 Superset 跑在容器里,容器内的 localhost 指向的是它自己,不是 MySQL 容器。在同一个 Docker Compose 项目里,服务名即主机名,所以这里必须写 mysql,这是 compose 自动创建的网络别名。
如果填对了还是连不上,先确认 00-setup.sql 里的授权语句是否执行成功,然后看 4.3 节里的驱动问题。
4.2 通过 SQL Lab 验证连接与取数
数据源添加成功后,点击顶部 SQL -> SQL Lab 进入查询界面。左侧数据库下拉选择 Sakila,就能看到所有表。先跑一条最简单的查询验证链路是否通了:
sql复制SELECT * FROM film LIMIT 10;
能出结果,说明从 Superset 到 MySQL 的整条链路已经打通。接下来可以继续探索数据了。Sakila 的核心表关系我简单梳理一下:
- film 电影主表,包含片名、时长、租金、评级
- actor 演员表,和 film 通过 film_actor 多对多关联
- category 品类表,和 film 通过 film_category 多对多关联
- inventory 库存表,每一行代表门店里一份可租赁的 DVD 副本
- rental 租赁记录表,含出租时间、归还时间
- payment 支付记录表,每笔租赁对应的金额
这基本就是一个零售租赁业务的核心事实表和维度表结构。
4.3 图表设计时用 SQL 计算取数,到底能不能实现
这个问题被问得非常多:Superset 能不能在图表设计时通过 SQL 计算来取数?答案是能,而且有三条路可以走。
第一条路,在 Explore 图表设计界面,给指标或维度开启自定义 SQL。比如我想算超期归还的租赁单数,可以在指标一栏选择 Custom SQL:
sql复制SUM(CASE WHEN DATEDIFF(return_date, rental_date) > 7 THEN 1 ELSE 0 END)
这就是把业务规则直接写进图表指标,不用单独建表。类似地,需要按月份聚合租赁趋势时,维度栏可以填:
sql复制DATE_FORMAT(rental_date, '%Y-%m')
第二条路,在 Dataset 层面增加计算列。在数据集编辑界面添加一个列,公式写:
sql复制DATEDIFF(return_date, rental_date)
起个名字叫 rental_duration_days。这样建好的计算列会出现在所有图表的数据列列表里,后续做各种租赁时长的分析都直接用,不用每次重复写一遍 DATEDIFF。这一点很实用,尤其是团队里不止一个人在做图表的时候。
第三条路,就是 SQL Lab 里写完整查询,查询结果可以一键 Create Chart。适合复杂多表 JOIN 的场景。比如按品类统计租赁收入排行榜:
sql复制SELECT
c.name AS category,
SUM(p.amount) AS total_revenue,
COUNT(DISTINCT p.rental_id) AS rental_count
FROM payment p
JOIN rental r ON p.rental_id = r.rental_id
JOIN inventory i ON r.inventory_id = i.inventory_id
JOIN film f ON i.film_id = f.film_id
JOIN film_category fc ON f.film_id = fc.film_id
JOIN category c ON fc.category_id = c.category_id
GROUP BY c.name
ORDER BY total_revenue DESC;
这个查询把付款、租赁、库存、电影、品类五张表串了起来,是 Sakila 数据分析里最典型的业务指标之一。SQL Lab 跑出来之后,点 Explore,Superset 会自动生成对应的图表界面。除此之外,SQL 表达式里当然也支持对数值字段做算术运算,真要写 inventory_id + 5 这种简单计算也一样跑得通,重点是理解 SQL 表达式的能力边界和适用场景。
4.4 Jinja 模板与参数化查询
Superset 的 SQL Lab 内置了 Jinja 模板引擎,这是它区别于很多 BI 工具的独门功能。所谓 Jinja 模板,就是在 SQL 里嵌入由服务端渲染的变量,Superset 会把仪表盘上的筛选条件自动注入这些变量,从而动态改写 SQL。
最常用的两个内置变量是 from_dttm 和 to_dttm,分别对应仪表盘时间筛选器的起止时间。在 SQL Lab 写:
sql复制SELECT
DATE(rental_date) AS rental_day,
COUNT(*) AS rental_cnt
FROM rental
WHERE rental_date >= '{{ from_dttm }}'
AND rental_date < '{{ to_dttm }}'
GROUP BY DATE(rental_date)
ORDER BY rental_day;
把这条查询保存为一个数据集,再添加到仪表盘上,Superset 会自动生成一个时间范围筛选器,用户拖时间区间时查询会自动带上过滤条件。这种参数化方式比硬编码日期干净得多。
除了内置变量,还可以用 {{ filter_values('category') }} 获取仪表盘上其他筛选器的值,甚至可以用 {{ current_username() }} 做行级数据隔离。不过这是进阶玩法,入门阶段先把 from_dttm 和 to_dttm 用透就够解决大部分动态取数需求了。
5. 图表设计与仪表盘落地
5.1 指标体系和图表怎么配套设计
数据源通了、取数方法也掌握了,接下来最关键的是想清楚看板到底要展示什么。我建议不要一上来就堆图表,而是先基于 Sakila 的业务场景拆指标,再根据指标匹配图表类型。
Sakila 是一家 DVD 租赁店,对这类生意,经营看板上的核心指标大体是这些:
- 总租赁收入:SUM 支付金额,对应 Big Number 图表
- 租赁订单量随时间的趋势:按天统计租赁记录数,对应时间序列折线图
- 收入 Top 10 电影品类:按品类聚合收入,对应水平条形图
- 各门店收入占比:按门店聚合,对应饼图或环形图
- 顾客租赁频次分布:按顾客聚合租赁次数,对应直方图
- 超期归还率:超期租赁数占总租赁数比例,对应仪表盘图表或 KPI 卡片
建好一个查询结果数据集后,可以在同一个数据集上克隆多个图表,分别配置不同维度和指标,最后拼到一个仪表盘里。
5.2 核心图表设计示例
拿“收入 Top 10 电影品类”这个条形图展开讲一下配置过程。先建一个 SQL Lab 查询,用前面写的五表关联 SQL,加上 LIMIT 10,保存为数据集。然后点 Create Chart,图表类型选 Horizontal Bar Chart,Time Column 那一栏可以留空,因为我们不是在按时间维度展示,X 轴维度选 category,Y 轴指标选 sum 后的 total_revenue,排序按指标倒序,再限制展示前 10 条。
这里有个小技巧:如果在 SQL Lab 查询阶段已经把总量聚合好,Explore 里就用那个聚合后的字段;如果你选择直接在 Explore 里选择原生字段,Superset 也可以帮你做聚合,但遇到多表 JOIN 的场景,我会更建议在 SQL 层就把聚合做掉,把图表层留给展示逻辑,排查问题时定位更快。
“各门店收入占比”则是另一种配置方式。按 store 聚合支付金额,把 Store Location 或 store_id 拖到维度,选择 Pie Chart,指标还是 SUM(amount)。饼图有个注意点,类别不宜太多,超过六个就可以考虑换成横向条形图,否则标签完全看不清。Sakila 只有两家门店,刚好适合饼图演示。
5.3 仪表盘长什么样:布局与交互说明
文字描述一下我自己搭完这套 Sakila 数据源之后实际配出来的仪表盘是什么样的,你们脑补一下再去做,心里就有底了。
打开仪表盘,顶部是一条全局筛选器栏,从左到右依次是:品类下拉框、门店下拉框、租赁日期范围选择器。筛选器的数据源分别绑定到对应的维度和时间字段。注意这三个筛选器是通过 Superset 的 Dashboard Level Filters 共享给所有图表的,这是仪表盘最有价值的功能之一,选一个品类,下面所有图表瞬间联动。
筛选器下方是四张 KPI 卡片:第一张是总租赁收入,第二张是租赁订单总数,第三张是活跃顾客数,第四张是库存 DVD 总量。每张卡片下方用小字显示环比上一周期的变化百分比,这个环比是通过两个时间窗口的聚合值计算出来的,在 Superset 里可以用自定义 SQL 指标实现,也可以在 SQL Lab 里先算好两个窗口的值,再拉到图表里比较。
中间是一张按月租赁趋势的折线图,时间列是由 DATE_FORMAT(rental_date, '%Y-%m') 生成的标准月份维度,和顶部的时间筛选器联动。折线图下方左右并列:左半边是“收入 Top 10 电影品类”的水平条形图,右半边是“各门店收入占比”的环形图。这两张图的视觉粒度完全不同,并排放置对比效果很明显。
最底部是一张大表格,列出最近 30 天的租赁明细,包含电影标题、顾客姓名、门店、租金、出租日期、归还日期、租赁天数。表格支持点击行展开详情,实测下来数据量大的时候分页性能也还流畅。
整个过程基本是拖拽式操作,唯一需要写好 SQL 的就是数据集定义阶段。这也是我强烈推荐先想清楚指标再动手建图表的原因,指标定义阶段多花十分钟,后面拖图表能省一小时。
6. 常见问题与排查实录
6.1 添加数据库时报 “Could not load database driver”
这个问题几乎人人都会遇到。添加 MySQL 数据源时如果提示找不到驱动,说明 Superset 容器里没有装 MySQL 的 Python 驱动,解决办法就是前面 Dockerfile 里写的,安装 mysqlclient。
已经启动的容器也可以临时救急:
bash复制docker compose exec -u root superset pip install mysqlclient
docker compose restart superset
但这是临时方案,重启容器后镜像层不会保留这个安装,还是建议把 Dockerfile 准备好,重新构建一次。
6.2 Superset 容器连不上 MySQL:host 到底写什么
我在 4.1 说过这个问题了,但它的出现率实在太高,值得再强调一次。在宿主机上访问 MySQL 用 localhost 没问题,但 Superset 是容器,它和 MySQL 之间通信走的是 Docker 内部网络,host 必须写 compose 里 service 的名称,也就是 mysql。连接串长这样:
code复制mysql://superset:superset_pass@mysql:3306/sakila
如果 docker compose ps 确认两个容器都正常运行,却仍然连不上,可以从 Superset 容器内部手动测一下网络连通性:
bash复制docker compose exec superset bash
curl mysql:3306
能连通说明网络没问题,接着排查账号权限和认证方式。
6.3 MySQL 8 认证协议导致连接失败
使用老版本 Python MySQL 驱动连接 MySQL 8 时,报错通常长这个样子:
code复制Authentication plugin 'caching_sha2_password' cannot be loaded
或者 Firedac 等老客户端报:
code复制phys mysql client does not support authentication protocol requested
这个问题的根源是 MySQL 8 默认用 caching_sha2_password 插件,而一些旧驱动和客户端不认这个协议。前面在 00-setup.sql 里用 IDENTIFIED WITH mysql_native_password 就是为了避免这个问题。如果你用的连接串已经指向一个用默认插件创建的用户,最简单的修复方式是重新创建一个指定认证方式的用户,然后 GRANT 权限,再把 Superset 里的连接串换成新用户。
6.4 Sakila 脚本导入失败
初始化脚本没生效时,film 表查出来是空的,或者 SHOW TABLES 只有零星几张表。这时候第一步是看日志:
bash复制docker compose logs mysql
日志里如果有 SQL 语法错误,最常见的原因是 Sakila 脚本里的注释行或特殊字符在处理时出了编码问题,把 init 目录下的文件转成纯 UTF-8 无 BOM 格式即可。另一个原因是脚本文件名排序问题,00、01、02 的前缀就是为了保证先执行建库用户脚本、再执行 schema、最后执行数据。如果文件命名顺序搞反了,数据导入会失败。
实在排查不出来,最简单的重置办法是删卷重建:
bash复制docker compose down -v
docker compose up -d
注意 -v 会删除所有 compose 声明的数据卷,包括 Superset 的元数据库和 MySQL 的全部数据,所以慎用,只在初始化阶段确认数据错了时才这么干。
6.5 docker compose stop 报 exit status 1
这个问题在“如何利用 docker compose 安装”这个话题下被问了很多次,报错信息是:
code复制cannot stop docker compose application. reason: compose [stop] exit status 1
这种现象一般在单容器执行正常但整体 stop 失败时出现,本质上是某个容器在收到 SIGTERM 后没有在默认超时时间内优雅退出,Docker 等待超时后强制 kill,把非零状态码抛给了 compose。排查步骤是:
bash复制docker compose ps -a
看哪个容器处于 Exited (137) 或仍然 Up 但卡在停止中。最常见的元凶是 Superset 容器的 Gunicorn 在收到终止信号后没有迅速处理,解决办法是稍微放宽停止超时时间:
bash复制docker compose stop -t 30
-t 参数指定秒数,表示等待容器优雅退出的时长。如果还是不行,就用:
bash复制docker compose kill
强制终止所有服务。再不行才考虑 docker rm -f 容器名 做最后清理。
6.6 其他几个容易忽略的小坑
最后补几个我没归类到上面、但实际使用中反复出问题的小点。
时区问题。SuperSet 默认使用 UTC 时间,如果你所在的时区是东八区,图表时间轴显示会偏移 8 小时。解决方式是在 superset_config.py 里设置:
python复制TZ = 'Asia/Shanghai'
然后在启动脚本或者宿主机层面把容器时区也一并调整,compose 文件 MySQL 服务里我已经加了 TZ=Asia/Shanghai,Superset 端也需要在环境变量里加。
端口冲突问题。如果宿主机上已经有一个 MySQL 占用 3306 端口,compose 里映射端口就要改成 "3307:3306",左边改成宿主机的空闲端口,右边保持容器内端口不变。Superset 的 8088 端口如果被占,同理改成 8089。
SQLite 元数据库膨胀问题。Superset 默认把元数据存在挂载卷的 SQLite 里,如果只是个人使用或者小团队使用没有太大压力,但如果并发用户多、看板多到一定程度,建议把元数据库迁移到独立 MySQL 或 PostgreSQL。这一节不是入门必须,但提前知道可以避免后续返工。
Docker 资源不足问题。Superset 加 MySQL 两个容器同时跑,内存占用大概在 1.5G 到 2G 之间。如果 Docker Desktop 只分配了 2G 内存,很可能出现容器反复重启、服务无响应的情况。你发现怎么配置都不对的时候,先看一眼 docker stats 确认资源余量,这个检查通常能帮你少走很多弯路。
我个人在实际操作中的体会是,这套环境的搭建难度不高,真正拉开差距的是对数据模型和业务指标的理解。Docker Compose 把环境问题压缩到几十分钟内解决,Sakila 数据源提供了足够丰富的业务场景,Superset 则把 SQL 能力和可视化体验结合得恰到好处。顺着这条链路走一遍,你其实就把数据工程里最核心的部署、取数、建模、可视化四个环节都过了一遍。最后再分享一个小技巧:Superset 的仪表盘可以设置定时邮件报表,配合刚才部署好的环境,等于你的第一套开源 BI 系统已经具备最基本的“主动推送”能力了。
