
Flink 跑批作业,结果集默认写本地或 HDFS;checkpoint 也常落在本地盘。集群一换节点,或者想让别的系统直接读这批结果,本地路径就成了阻碍。Apache Flink 通过自带的 S3 文件系统插件,能把 s3:// 直接注册成连接器协议,背后指向一个 S3 兼容桶,比如 RustFS。配置对一次,批作业的结果集和流式作业的 checkpoint 都能落到共享存储。
插件要手动放到 plugins 目录
Flink 的 S3 支持靠 flink-s3-fs-hadoop 插件,镜像里它躺在 /opt/flink/opt/,不会自动加载,得先拷到 /opt/flink/plugins/s3fs/ 才会生效。
mkdir -p s3fs
docker create --name flink-tmp flink:1.20
docker cp flink-tmp:/opt/flink/opt/flink-s3-fs-hadoop-1.20.5.jar s3fs/
docker rm flink-tmp
官方这条路径用 flink:1.20 和 rustfs/rustfs-x86-musl:v2.3.1 验证过。插件 jar 版本要跟 Flink 版本对齐,这里是 flink-s3-fs-hadoop-1.20.5.jar(对应 Flink 1.20)。Flink 文档的惯例目录名是 plugins/s3-fs-hadoop/,不过 plugins/ 下的每个子目录都会被扫描,目录名叫 s3fs 一样能加载。
端点、path-style、凭证都用 FLINK_PROPERTIES 里的 s3.* 属性注入,JobManager 和 TaskManager 两处都要配:
s3.access-key: ${RUSTFS_ACCESS_KEY}
s3.secret-key: ${RUSTFS_SECRET_KEY}
s3.endpoint: http://rustfs:9000
s3.path-style-access: true
s3.path-style-access: true 对自定义端点是必需的,否则 Flink 会按虚拟主机式寻址去拼 my-bucket.rustfs,解析不到。Flink 文档里这个键写作 s3.path.style.access——插件加载时会对带连字符和带点两组键做互相镜像,任选一种写法都生效。
批作业写出读回
先准备环境:.env 只放两个凭证变量,Compose 里定义 rustfs、create-bucket、jobmanager、taskmanager 四个服务(完整文件见 RustFS 的 Flink 集成指南,后文验证用的 create-bucket 就是其中负责建桶的辅助容器):
RUSTFS_ACCESS_KEY=<your-access-key>
RUSTFS_SECRET_KEY=<your-secret-key>
建好桶、起好 session 集群后,用 Flink SQL 把结果集写到桶里:
SET 'execution.runtime-mode' = 'batch';
CREATE TABLE sink (
id INT,
payload STRING
) WITH (
'connector' = 'filesystem',
'path' = 's3://my-bucket/flink-out/',
'format' = 'csv'
);
INSERT INTO sink
VALUES (1, 'alpha'), (2, 'bravo'), (3, 'charlie'), (4, 'delta'), (5, 'echo');
提交后所有行写完作业即结束。读回用同一个 s3:// 路径建一张只读表,直接 SELECT *:
CREATE TABLE readings (
id INT,
payload STRING
) WITH (
'connector' = 'filesystem',
'path' = 's3://my-bucket/flink-out/',
'format' = 'csv'
);
SELECT * FROM readings;
在 RustFS 这边列前缀验证:
docker compose run --rm --entrypoint /bin/sh create-bucket -c \
'/usr/bin/rc alias set rustfs http://rustfs:9000 "$RUSTFS_ACCESS_KEY" "$RUSTFS_SECRET_KEY" >/dev/null && /usr/bin/rc ls rustfs/my-bucket/flink-out --recursive'

流式作业的 checkpoint 也指到这里
上面的例子是批模式。换成流式作业,把周期 checkpoint 落到 RustFS 是三行配置,失败重启后作业状态从桶里恢复:
SET 'execution.checkpointing.storage' = 'filesystem';
SET 'execution.checkpointing.dir' = 's3://my-bucket/checkpoints/';
SET 'execution.checkpointing.interval' = '10s';
第三行的 interval 没有默认值,不设它就没有周期 checkpoint,前两行只是备好了存储位置。execution.checkpointing.dir 这个键名是 Flink 1.20 才改过来的,1.19 及更早版本里主键还是 state.checkpoints.dir;1.20 里两个旧键名(state.checkpoints.dir、state.backend.fs.checkpointdir)降为 deprecated 别名,仍被接受。这几行放进 FLINK_PROPERTIES 也行——JM 和 TM 都会写进各自的 flink-conf.yaml,效果相同。
还能把 S3 Tables 当 Iceberg 仓库
RustFS 的 S3 Tables 内置了 Apache Iceberg REST 目录,Flink 可以把表桶当成托管的 Iceberg 仓库用,数据仍落在 RustFS。按 S3 Tables 文档启用表桶后,把 Flink Iceberg 连接器的 REST 目录指向 RustFS:目录 URI 是 http://<rustfs-host>:9000/iceberg,warehouse 即桶名,目录请求(AWS Signature Version 4,签名名 s3)和 S3 文件访问都走 path-style 寻址。
这条路径的边界要讲清:按 S3 Tables 支持矩阵,生产采用前要用你实际部署的 Flink 和 Iceberg 版本完成验证,官方示例不能直接当生产配置。
上生产的几处边界
官方示例是本地 Docker 集成测试。真上集群,先记三件事。
凭证两处都要。JobManager 和 TaskManager 的 FLINK_PROPERTIES 都必须含 s3.access-key / s3.secret-key / s3.endpoint / s3.path-style-access,且各自 plugins/s3fs/ 下都有插件 jar,否则写时报 No AWS Credentials provided。
网络要共网。所有 Flink 容器和 RustFS 在同一个 Compose 网络,TaskManager 才能解析 rustfs 主机名。
流式写入重试会卡。失败后恢复进行中的 S3 上传,写入器可能进不可恢复状态,报 Stream closed。删掉桶里该作业的输出前缀再提交,或对一次性写入用批模式,比硬扛重试稳。
排障顺序
- 写时报
No AWS Credentials:查 JM 和 TM 两处s3.*是否齐全、插件 jar 是否在位。 - TaskManager 报无法解析
rustfs:查是否同一 Compose 网络。 - 流式重试
Stream closed:删输出前缀重提,或改批模式。 - 结果集在不在桶里:
rc ls rustfs/my-bucket/flink-out --recursive。
停止或重置
docker compose down # 停容器,保留 RustFS 数据卷
docker compose down --volumes # 连 rustfs-data 卷一起清掉,从空桶重来
把 Flink 的结果集收进 RustFS 之后,数据只存一份,下游系统直接读 s3://,不用再搬本地盘。流程出自 RustFS 的 Flink 集成指南,Flink 项目源码在 apache/flink,S3 兼容性细节看 S3 兼容性说明。RustFS 是 Apache 2.0 协议的 S3 兼容对象存储。
本文介绍如何利用Flink自带S3插件将批作业结果集与流式作业checkpoint统一落盘至RustFS对象存储,涵盖插件部署、配置要点(端点、path-style、凭证)、批/流作业集成、Iceberg仓库适配及生产边界问题。强调JobManager与TaskManager双端配置一致性、网络互通性及重试异常处理,提升云原生场景下Flink存储层的可靠性与共享能力。
362

被折叠的 条评论
为什么被折叠?



