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

OpenClaw 中 web_search + web_fetch 最佳实践速查表

OpenClaw 中 web_search + web_fetch 最佳实践速查表

OpenClaw 中 web_search + web_fetch 最佳实践速查表 摘要:本文帮助读者明确 OpenClaw 网络搜索工具和不同搜索技能的的职责边界,理解“先搜索、再抓取、后总结”的最佳实践,并能更稳定地在 OpenClaw 中使用 tavily-search 与 web_fetch 完成网络信息搜索任务。主要内容包括:解决 OpenClaw 中 web_search、tavily-search、web_fetch、原生 provider 与扩展 skill 容易混淆的问题、网络搜索能力分层说明、OpenClaw 原生搜索 provider 与 Tavily/Firecrawl 扩展 skill 的区别、标准工作流、提示词模板、

【前端实战】如何让用户回到上次阅读的位置?

【前端实战】如何让用户回到上次阅读的位置?

目录 【前端实战】如何让用户回到上次阅读的位置? 一、总体思路 1、核心目标 2、涉及到的技术 二、实现方案详解 1、基础方法:监听滚动,记录 scrollTop(不推荐) 2、Intersection Observer + 插入探针元素 3、基于 URL Hash 锚点跳转 三、总结 1、不同方案间对比总结 2、结语         作者:watermelo37         ZEEKLOG万粉博主、华为云云享专家、阿里云专家博主、腾讯云、支付宝合作作者,全平台博客昵称watermelo37。         一个假装是giser的coder,做不只专注于业务逻辑的前端工程师,Java、Docker、Python、LLM均有涉猎。 --------------------------------------------------------------------- 温柔地对待温柔的人,包容的三观就是最大的温柔。 -------------------------------------------------------------

Java Web 开发环境搭建:IDEA+Tomcat 安装与部署超详细教程

Java Web 开发环境搭建:IDEA+Tomcat 安装与部署超详细教程

在 Java Web 开发中,IDEA 作为主流的集成开发工具,搭配 Tomcat 轻量级 Web 服务器是入门首选。本文将基于 Java Web 基础开发要求,从 JDK 环境配置、Tomcat 安装配置、IDEA 安装、Web 项目创建,到 Tomcat 在 IDEA 中的部署运行,进行一步一图式详细讲解,零基础也能轻松上手。 一、前置准备:JDK 环境配置 Java Web 开发的核心基础是 JDK,Tomcat 和 IDEA 的运行都依赖 JDK 环境,需先完成 JDK 的安装与环境变量配置。 1. 下载与安装