Flink External Resource Framework让作业“原生”申请 GPU/FPGA 等外部资源

1. 外部资源框架到底做了什么

整体就两件事:

1)改写资源请求(Resource Request)

  • 你在 Flink 配置里声明要什么资源、要多少
  • Flink 会把这些外部资源需求映射进底层资源管理系统(Kubernetes/YARN)的容器或 Pod 资源请求中
  • 最终确保 TaskManager 所在的容器/Pod 真的带着你要的外部资源启动

2)把“可用资源信息”提供给算子(Operator)

  • TaskManager 启动后,由“外部资源驱动(driver)”生成 ExternalResourceInfo(资源信息集合)
  • 算子通过 RuntimeContext.getExternalResourceInfos(resourceName) 拿到资源的关键属性(比如 GPU index),然后就可以在算子里绑定对应设备去用

一句话:框架负责“申请 + 告知”,至于“怎么用”取决于具体插件。

2. 适用场景与边界

适合的典型场景

  • GPU 推理(TensorRT / ONNX Runtime / PyTorch inference)作为 RichFunction/AsyncFunction 的一部分
  • GPU 加速特征工程或向量计算
  • 需要 FPGA、专用加速卡的自定义计算
  • 同一套作业在 Kubernetes/YARN 上希望“按需申请资源”

当前边界(很关键)

  • 外部资源是“机器级/进程级共享”的:同一 TaskManager 上运行的所有算子拿到的 ExternalResourceInfos 目前是同一份集合
  • 也就是说:没有 operator 级别的资源隔离(同 TM 内算子理论上能看到同一批 GPU)

如果你希望“每个算子/每个 subtask 独占一张卡”,需要你在算子内部做更严格的绑定策略,或用脚本协调模式避免同机多 TM 抢同一 GPU(后面讲)。

3. 启用流程:三步走

3.1 准备外部资源插件(plugins/)

外部资源通过 Flink 插件机制加载,你需要把对应 jar 放到 Flink 的 plugins/ 目录下的某个子目录中,例如:

  • GPU 插件:放到 plugins/external-resource-gpu/(或你自定义目录,但要保证 jar 能被加载)
  • 自定义资源插件:创建一个目录,比如 plugins/fpga/,把你打出来的 jar 放进去

插件隔离非常重要:每个 plugin 目录是独立 classloader,避免依赖冲突;同时 SPI(ServiceLoader)文件必须保留(META-INF/services)。

3.2 配置 external-resources 与每个资源的参数

核心配置有两层:

A)先声明启用哪些资源(白名单)

external-resources: gpu;fpga 

只有这里列出来的 <resource_name> 才会生效。

B)为每个资源配置 amount、k8s/yarn 映射、driver、driver 参数
常见配置项含义:

  • external-resource.<name>.amount:每个 TaskManager 需要的资源数量
  • external-resource.<name>.yarn.config-key:YARN 容器资源 profile 的映射 key(可选)
  • external-resource.<name>.kubernetes.config-key:K8s 容器 resources.requests/limits 的 key(可选)
  • external-resource.<name>.driver-factory.class:驱动工厂(可选但强烈建议)
    • 不配置也能“申请到资源”,但算子拿不到 ExternalResourceInfo(RuntimeContext 里会没有信息)
  • external-resource.<name>.param.<param>:传给驱动工厂的自定义参数(插件自定义解释)

一个包含 GPU+FPGA 的示例:

external-resources: gpu;fpga external-resource.gpu.driver-factory.class: org.apache.flink.externalresource.gpu.GPUDriverFactory external-resource.gpu.amount:2external-resource.gpu.param.discovery-script.args:--enable-coordination-mode external-resource.fpga.driver-factory.class: org.apache.flink.externalresource.fpga.FPGADriverFactory external-resource.fpga.amount:1external-resource.fpga.yarn.config-key: yarn.io/fpga 

3.3 在算子里使用 RuntimeContext 获取资源信息

算子侧用法非常直接:

publicclassExternalResourceMapFunctionextendsRichMapFunction<String,String>{privatestaticfinalString RESOURCE_NAME ="gpu";@OverridepublicStringmap(String value)throwsException{Set<ExternalResourceInfo> infos =getRuntimeContext().getExternalResourceInfos(RESOURCE_NAME);List<String> indices =newArrayList<>();for(ExternalResourceInfo info : infos){ info.getProperty("index").ifPresent(indices::add);// GPU 插件常用属性 key:index}// 这里用 indices 做设备绑定,比如选择一张卡 set CUDA_VISIBLE_DEVICES 或初始化推理引擎return value;}}

每个 ExternalResourceInfo 里有哪些 key,取决于插件实现。你可以用 ExternalResourceInfo#getKeys() 获取完整键集合。

4. Kubernetes / YARN / Standalone:三种环境的差异

4.1 Kubernetes

  • K8s 原生通过 Device Plugin 机制提供 GPU/FPGA 等资源(Kubernetes v1.10+ 支持)
  • Flink 会把你配置的 kubernetes.config-key 写入 TaskManager 主容器的:
    • resources.requests.<config-key>
    • resources.limits.<config-key>

GPU 的常见 key:

  • NVIDIA:nvidia.com/gpu
  • AMD:amd.com/gpu(但 Flink 默认 discovery 脚本是 NVIDIA 的,AMD 需要你自己写脚本)

4.2 YARN

  • YARN 2.10+ / 3.1+ 开始支持 GPU/FPGA 资源
  • Flink 通过 external-resource.<name>.yarn.config-key 把 amount 写进 container resource profile

GPU(YARN)常见 key:

  • yarn.io/gpu(注意:YARN 当前通常仅支持 NVIDIA GPU 的调度)

4.3 Standalone

  • Standalone 模式没有底层 RM 帮你“保证资源”,你需要管理员在节点上确保外部资源可用(驱动安装、权限、设备可见性等)
  • 如果同一台机器上跑多个 TaskManager,GPU 可见性很容易冲突,需要配合 discovery script 的协调模式

5. GPU 插件:最常用也最值得踩坑的一块

Flink 目前官方提供的一方外部资源插件就是 GPU 插件。

5.1 必要配置(GPU)

external-resources: gpu external-resource.gpu.driver-factory.class: org.apache.flink.externalresource.gpu.GPUDriverFactory external-resource.gpu.amount:2# Kubernetesexternal-resource.gpu.kubernetes.config-key: nvidia.com/gpu # YARNexternal-resource.gpu.yarn.config-key: yarn.io/gpu 

5.2 discovery script(GPU 发现脚本)

GPUDriver 会调用 discovery script 来发现可用 GPU,并生成 ExternalResourceInfo,其中关键属性是:

  • key = index(GPU 设备 index)

默认脚本路径(NVIDIA):

external-resource.gpu.param.discovery-script.path: plugins/external-resource-gpu/nvidia-gpu-discovery.sh 

自定义脚本路径(例如 AMD)也可以配置同一个 key。

脚本参数:

external-resource.gpu.param.discovery-script.args:--enable-coordination-mode 

5.3 脚本契约(你写自定义脚本时必须遵守)

  • Flink 先把 amount 作为第一个参数传给脚本
  • 你配置的 discovery-script.args 会拼在后面
  • 脚本输出:用逗号分隔的 GPU index 列表,例如 0,1
  • 输出里纯空白 index 会被忽略
  • 如果发现失败或数量不足:脚本返回非 0 exit code,Flink 将不会向算子提供 gpu 信息(RuntimeContext 拿不到)

5.4 协调模式:解决“同机多 TM 抢同一 GPU”

Standalone 下经常同机部署多个 TaskManager,此时所有 TM 默认都能看到同一批 GPU(nvidia-smi 可见),很容易“多进程抢同一张卡”。

默认脚本提供 coordination mode:

  • --enable-coordination-mode:启用协调
  • --coordination-file <path>:协调文件路径(默认 /var/tmp/flink-gpu-coordination

它能保证:同一个 Flink 集群内,同机多个 TaskManager 不会分到同一张 GPU。

但要注意两点:

  • 只在“同一协调文件范围内”有效:另一个 Flink 集群如果用不同 coordination file,仍可能抢同一 GPU
  • 非 Flink 应用也可能用同一 GPU,这个模式无法防住

6. 自定义资源插件:你要支持 FPGA/自研加速卡怎么做

你需要实现三件套:

1)ExternalResourceDriver

  • retrieveResourceInfo(long amount):返回 ExternalResourceInfo 集合(你定义的资源维度)

2)ExternalResourceDriverFactory

  • createExternalResourceDriver(Configuration config):从配置创建 driver

3)SPI 服务声明(非常关键)

  • 在 jar 内创建文件:
    • META-INF/services/org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory
  • 文件内容写你的 factory 全类名,例如:
    • your.domain.FPGADriverFactory

示例骨架:

publicclassFPGADriverimplementsExternalResourceDriver{@OverridepublicSet<ExternalResourceInfo>retrieveResourceInfo(long amount){// 发现并返回 FPGA 信息集合returnSet.of(/* ... */);}}publicclassFPGADriverFactoryimplementsExternalResourceDriverFactory{@OverridepublicExternalResourceDrivercreateExternalResourceDriver(Configuration config){returnnewFPGADriver();}}publicclassFPGAInfoimplementsExternalResourceInfo{@OverridepublicOptional<String>getProperty(String key){// 根据 key 返回属性,比如 "device", "pci", "address" 等returnOptional.empty();}@OverridepublicCollection<String>getKeys(){returnList.of("device","pci","address");}}

打包成 jar 丢到 plugins/fpga/,然后在 flink-conf.yaml 里按 <resource_name> 配置启用即可。

7. 排障清单:最常见的 6 个“为什么拿不到 GPU”

1)external-resources 没写 gpu(或资源名拼错)
2)插件 jar 没放到 plugins/ 下正确目录(或目录里缺依赖)
3)没配置 driver-factory.class,导致算子侧拿不到 ExternalResourceInfo
4)K8s 没装 NVIDIA device plugin(Pod 根本拿不到 GPU)
5)discovery script 不可执行 / 路径不对 / 返回非 0
6)Standalone 同机多 TM 没开协调模式,导致资源冲突看似“有卡但不可用”

8. 最佳实践建议

  • 先明确“资源申请”和“资源绑定”是两步:申请解决“容器是否带卡”,绑定解决“算子用哪张卡”
  • GPU 推理算子里要做设备亲和:基于 index 决定 CUDA_VISIBLE_DEVICES 或引擎初始化参数
  • Standalone 同机多 TM 建议默认开 coordination mode
  • 生产上尽量用 Kubernetes/YARN 去做资源保证,Standalone 只适合可控环境
  • 由于没有 operator 级隔离,最好避免在同一 TM 内多个算子“各自随便挑卡”,要统一策略(例如只由一个算子管理 GPU worker)

Read more

MC.JS WEBMC1.8 vs 传统开发:效率提升300%的秘诀

快速体验 1. 打开 InsCode(快马)平台 https://www.inscode.net 2. 输入框内输入如下内容: 创建一个对比演示项目,分别用纯JavaScript和MC.JS WEBMC1.8实现相同的简单沙盒游戏功能。游戏需要包含:1) 角色移动控制 2) 方块放置与破坏 3) 简单物品栏系统。使用AI自动生成两个版本的代码,并统计开发时间、代码行数、性能指标等数据,生成可视化对比报告。两个版本都应支持实时预览,方便直观比较。 1. 点击'项目生成'按钮,等待项目生成完整后预览效果 最近在开发一个简单的沙盒游戏demo时,我尝试了两种不同的开发方式:传统JavaScript和MC.JS WEBMC1.8框架。结果让我大吃一惊,后者竟然帮我节省了70%的开发时间!今天就来分享一下这个对比实验的过程和发现。 1. 项目需求分析

WebAgent详解+实战:用开源AI智能体搞定产品与竞品市场调研

WebAgent详解+实战:用开源AI智能体搞定产品与竞品市场调研

在市场调研场景中,产品及竞品分析往往需要投入大量人力,手动浏览网页、提取信息、整理数据,不仅效率低下,还容易出现信息遗漏、误差等问题。WebAgent作为通义实验室开源的端到端自主网页智能体,凭借强大的中文语义理解、多步骤推理和结构化输出能力,可完全本地部署且永久免费,能高效替代人工完成网页信息采集、竞品数据提取、产品信息汇总等调研工作。本文将从WebAgent核心介绍、部署要点入手,聚焦产品与竞争对手调研场景,一步步实现实战示例,让无论是开发者还是市场从业者,都能快速上手,用AI提升调研效率,摆脱重复劳动。 一、初识WebAgent:阿里开源的网页智能体“神器” 1.1 什么是WebAgent? WebAgent是阿里巴巴通义实验室开源的自主网页智能体框架,核心定位是“模拟人类浏览网页的完整流程”,能理解自然语言指令、规划浏览路径、执行网页操作(点击、翻页、搜索等)、提取关键信息并结构化输出,无需人工干预即可完成复杂的网页相关任务。 与国外的AgentQL相比,WebAgent最大的优势的是完全开源免费、支持本地部署、中文语义优化,无需调用云端API,数据可完全保存在内网,

基于Ubuntu的libwebkit2gtk-4.1-0安装操作指南

如何在 Ubuntu 上正确安装 libwebkit2gtk-4.1-0 ?从踩坑到实战的完整指南 你有没有遇到过这样的场景:兴致勃勃地准备运行一个基于 GTK 的本地文档查看器,或是自己动手写了个 Python + WebKit 的轻量浏览器外壳,结果一执行就报错: ImportError: cannot import name 'WebKit2' from 'gi.repository' 或者更让人抓狂的是: error while loading shared libraries: libwebkit2gtk-4.1.so.0: cannot open shared object file 别急——这几乎百分之百是因为系统里缺了那个看似不起眼、实则至关重要的库: libwebkit2gtk-4.1-0 。 它不是什么冷门玩具,而是 GNOME

Qwen3-VL-8B Web系统完整指南:chat.html前端+proxy_server+vLLM全链路解析

Qwen3-VL-8B Web系统完整指南:chat.html前端+proxy_server+vLLM全链路解析 1. 系统概览:一个开箱即用的AI聊天工作流 你有没有试过部署一个真正能用、界面清爽、响应流畅的本地大模型聊天系统?不是那种跑通了但卡顿、报错、连不上、调不通的“半成品”,而是打开浏览器就能聊、输入就出结果、关机重启也不掉链子的完整体验? Qwen3-VL-8B Web系统就是为此而生——它不是概念验证,也不是开发中间件,而是一套可直接投入日常使用的端到端AI对话基础设施。从你在浏览器里点击chat.html那一刻起,消息就已悄然穿过代理层、抵达vLLM推理引擎、加载Qwen3-VL-8B模型、完成计算并实时返回,整个过程无需手动配置Nginx、不碰OpenAPI密钥、不改一行前端AJAX地址。 它把三个常被割裂的部分——看得见的界面、管得着的流量、算得快的模型——严丝合缝地拧成一股绳。前端不裸连后端,后端不直面浏览器,所有通信都经由proxy_server.py统一调度。这种设计看似多了一层,实则换来三重确定性: * 你改前端CSS,不影响后端启动; *