本文档内容基于 flink-1.16.x
,其他版本的整理,请查看本人博客的 flink 专栏其他文章。
Flink 是一种通用性框架,支持多种不同的部署方式。
本章简要介绍 Flink 集群的组成部分、用途和可用实现。如果你只是想在本地启动一个 Flink,我们建议你部署一个 Standalone 集群。
下图展示的是每个 Flink 集群的组成部分。首先会有一个在某处一直运行的客户端,这个客户端会将 Flink 应用程序中的代码转换为 JobGraph,并将其提交给 JobManager。
JobManager 会将任务分布到 TaskManager 中,TaskManager 会一直运行真正的算子,比如:source、转换、sink。
在部署 Flink 时,会有多个组成部分参与部署,我们已经将他们放到了下图下面的表格中。
| 组件Component | 作用 | 实现方式 |
| :-------------------------------* | :----------------------------------------------------------* | :----------------------------------------------------------* |
| Flink Client | 编译批或流程序为工作流执行图,之后该执行图会被提交给 JobManager。 | Command Line Interface
REST Endpoint
SQL Client
Python REPL |
| JobManager | JobManager 是 Flink 工作协调组件的名称,JobManager 根据高可用、资源收集行为和支持的任务提交模式的不同,对不同的资源提供者有不同的实现。
JobManager 的任务提交模式:
Application 模式:对每个程序单独运行一个集群,作业的主函数或客户端会在 JobManager 中执行。可以在一个程序中多次调用 execute
/executeAsync
。
Per-Job 模式:对每个作业单独运行一个集群,作业的主函数或客户端只会在创建集群之前运行。
Session 模式:一个集群中运行多个作业的 TaskManager 会共享一个 JobManager。 | Standalone,这是 Flink 集群的标准模式,它只要求启动多个 JVM。通过 Docker, Docker Swarm / Compose, non-native Kubernetes 和其他模型部署这种模式也是可行的。
Kubernetes
YARN |
| TaskManager | TaskManager 是真正执行 Flink 作业任务的服务。 | |
| | 外部组件 (均可选) | |
| 高可用服务提供者 | Flink 的 JobManager 可以在高可用模式下运行,该方式允许 Flink 作业在 JobManager 失败时进行恢复。为了更快的失败恢复,可以启动多个备用 JobManager。 | Zookeeper
Kubernetes HA |
| 文件存储和持久化 | 对于用来恢复流作业的 checkpointing 来说,Flink 需要外部文件存储系统。 | 具体查看 FileSystems |
| 资源提供者 | Flink 可以通过不同的资源提供框架来部署,比如:Kubernetes、YARN、Mesos。 | 具体查看 JobManager 实现 |
| 指标存储 | Flink 组件会上报自己内部指标,Flink 作业也可以上报额外的作业特定的指标。 | 具体查看 指标上报 |
| 应用程序级别的数据 source 和 sink | 当应用程序级别的数据 source 和 sink 对应的组件不是 Flink 集群组件的一部分时,在计划新的 Flink 生产部署时,应该单独处理一下他们。将在 flink 作业中频繁使用的数据 source 和 sink 对应组件的依赖和 flink 放到一起可以显著提升性能。 | 比如:
Apache Kafka
Amazon S3
ElasticSearch
Apache Cassandra
具体查看 Connectors |
一旦作业到达全局停止状态,比如:完成、失败或被取消,和作业相关联的外部组件资源,然后就会被清理。在清理资源发生失败时,Flink 将会尝试重新进行清理,可以配置 配置 使用的尝试策略。尝试达到最大数量,但是没有成功清理,将会导致作业处于脏状态,此时作业产生的资源需要进行手动清理,具体请查看 高可用服务/作业结果存储 。重新启动相同的作业(即使用相同的作业ID)将会重新启动清理任务,无需再次运行该作业。
目前有一个问题,在将已完成的 checkpoint 作为常规已完成 checkpoint 管理的一部分时,无法删除已完成的 checkpoint 。他们产生的资源无法被重复清理任务覆盖,仍然需要进行手动删除。FLINK-26606 中报告了这个问题。
可以通过以下三种方式中的一种执行 Flink 程序:
上面模式的不同点:
main()
方法是在客户端执行还是在集群上执行在其他所有的模式中,应用程序的 main()
方法都是在客户端侧执行,该执行步骤包括在本地下载应用程序依赖、执行 main()
方法来提取 Flink 运行时可以理解的代表物,比如 JobGraph
、以及上传依赖和 JobGraph
到集群,这会造成客户端消耗大量的资源,包括占用一定的网络宽带来下载依赖并且上传二进制包到集群,占用 CPU 周期来执行 main()
方法。该问题在多个用户共享客户端时会更加明显。
Application 模式会对每个提交的程序创建一个集群,并且在 JobManager 中执行应用的 main()
方法。对每个应用创建一个集群的行为,可以将其看做为对一个作业单独创建了一个 session 集群,并且在应用程序完成后关闭。在这个架构下,Application 模式和 Per-Job 模式有相同的资源隔离和负载均衡保证,但其粒度是整个应用程序。
Application 模式会假设用户的 jar 已经在 classpath (usrlib
目录)中,并且 Flink 所有的组件(JobManager、TaskManager)都可以访问他们。换句话说,你的程序已经和 Flink 分布式捆绑到一起了。这种方式可以加快应用程序的部署和恢复进程,但是并不需要像其他模式模式一样通过 RPC 来分布式传输用户 jar 到 Flink 组件。
Application 模式会假设用户的 jar 已经在 classpath 中了。
在集群上执行
main()
方法对你的代码来说可能还有其他的含义,比如你在环境中使用registerCachedFile()
注册的任何路径,都必须可以被 JobManager 访问。注:如果这个路径对应的是本地文件,但是你把你的 Flink 程序部署到容器中了,则启动的 JobManager 就无法访问这个路径了。
相比于 Per-Job(过时) 模式,Application 模式运行程序提交多个作业组合的应用程序。作业执行的顺序不受部署模式的影响,而是受作业中启动方法调用的影响。
execute()
函数会造成阻塞,这会导致下个作业只能在当前这个作业完成后才会启动。executeAsync()
函数不会造成阻塞,在当前作业完成之前,下个作业也会启动。Application 模式运行允许包含多个
execute()
函数调用的程序,但是在这种情况下不支持高可用。Application 模式的高可用只支持包含单个execute()
函数调用的程序。另外,当在 Application 模式下运行的包含多个作业的程序(比如通过调用
executeAsync()
方法),其中任何一个作业被取消,则所有的作业都会被停止,并且 JobManager 也会停止。支持常规作业的完成,比如 source 关闭。
Session 模式假设已经有一个正在运行的集群了,并且使用该集群的资源来执行被提交的作业。同一个 session 集群中运行的程序会使用相同的资源进行计算。这样做的好处是,你无需为每个提交的作业启动一个完整的集群而消耗更多的资源。但是,如果某个有问题的作业造成 TaskManager 停止,则所有运行在该 TaskManager 上的作业都会被停止。除了失败的作业会造成负面影响外,还还意味着可能会出现大规模作业恢复,所有重新启动的作业都会并发地访问文件系统,使其他服务无法使用该文件系统。此外,让一个集群运行多个作业意味着 JobManager 将承受更多的负载,JobManager 需要负责集群中所有作业的监控。
Per-job 模式只支持 YARN,并且已经在 Flink 1.16 中过时,将会在 FLINK-26000 中被删除。请考虑使用 application 模式来为每个 on YARN 的作业启动一个专用的集群。
为了提供更好的资源隔离保证,Per-Job 模式使用资源提供者框架(比如:YARN、Kubernetges)对每个提交的作业都会启动一个集群,该集群只对这个作业可用。当作业完成时,集群会关闭,并且清除所有的缓存资源,比如文件。这可以保证更好的资源隔离,对于有问题的作业,只能关闭它自己的 TaskManager。另外,该模式会将压力分散给多个 JobManager,以为每个作业都有一个 JobManager。因此,Per-Job 资源分配模型是很多生产案例的首选。
在 Session 模式中,集群的生命周期独立于集群中运行的任何作业,并且所有的作业都可以共享资源。Per-Job 模式对每个被提交的作业都会单独的集群而造成额外的资源消耗,但是这会有更好的资源隔离保证,资源并不会在作业之间共享。这种情况下,集群的生命周期绑定与在其上运行的作业。最后,Application 模式会对每个程序创建一个 session 集群,并且在集群中执行应用程序的 main()
方法。
很多供应商都提供了管理或全套的 Flink 解决方案,但是这些供应商都没有得到 Apache Flink PMC 的官方支持,请参考供应商的文档说明来了解如何在生产上使用他们。
官网
支持环境: 阿里云
官网
支持环境: AWS
官网
支持环境: AWS
官网
支持环境: AWS Azure Google On-Premise
官网
支持环境: AWS
官网
支持环境: 华为
官网
支持环境: AliCloud、 AWS、 Azure、 Google、 On-Premise
暂不做翻译,生产环境一般会部署到 yarn 或 kubernetes 上。
该章描述如何原生地部署 flink 到 Kubernetes 上。
该入门指南章节会指导你在 Kubernetes 上安装一个完成功能的 flink 集群。
Kubernetes 是一个受欢迎的容器管理系统,它可以自动完成应用程序部署,扩容和管理。flink 的原生 Kubernetes 整合允许你直接将 flink 部署到一个正在运行的 Kubernetes 集群上。另外,取决于请求的资源,flink 也可以动态分配和清理 TaskManager,因为 flink 可以直接和 Kubernetes 对话。
Flink 也提供了一个 Kubernetes 算子来管理 on Kubernetes 的 Flink 集群,支持 standalone 和 原生部署模式,并且极大地简化了部署、配置和 On Kubernetes 的资源生命周期管理。
获取更多信息,请参考 Flink Kubernetes 算子文档。
入门指南章节假设已经运行的 Kubernetes 就能已经满足了以下要求:
~/.kube/config
列出、创建、删除 pod 和服务的 KubeConfig,可以通过运行 kubectl auth can-i pods
命令来校验权限。如果有部署Kubernetes 集群的疑问,可以参考 如何部署 Kubernetes 集群。
一旦你有了一个正在运行的Kubernetes 集群,并且配置了 kubectl
来指向它,你就可以通过以下方式来启动一个 Session 模式 的 flink 集群:
# (1) 启动 Kubernetes 会话
$ ./bin/kubernetes-session.sh -Dkubernetes.cluster-id=my-first-flink-cluster
# (2) 提交 flink 案例 job
$ ./bin/flink run \
--target kubernetes-session \
-Dkubernetes.cluster-id=my-first-flink-cluster \
./examples/streaming/TopSpeedWindowing.jar
# (3) 通过删除集群部署来停止 Kubernetes 会话
$ kubectl delete deployment/my-first-flink-cluster
默认情况下,Flink 的 Web UI 和 REST 终端会被暴露为
ClusterIP
服务,为了访问服务,请参考 访问 Flink’s Web UI 介绍。
恭喜!你已经成功地通过在 Kubernetes 上部署 flink 来运行 flink 程序了。
对于生产案例,我们建议使用 Application 模式 来部署 flink 程序,该模式对程序提供了很好的隔离性。
请参考 部署模式概述 来获取 application 模式更高级的相关知识。
Application 模式 要求用户代码已经和 flink 镜像捆绑到一起了,因为该模式会在集群上运行用户代码的 main()
方法,application 模式会在程序终止后确保清除所有的 flink 组件。
flink 社区提供了一个可以被用于捆绑用户代码的 基础 Docker 镜像。
FROM flink
RUN mkdir -p $FLINK_HOME/usrlib
COPY /path/of/my-flink-job.jar $FLINK_HOME/usrlib/my-flink-job.jar
在使用**自定义镜像名称 custom-image-name
**创建和发布了 Docker 镜像之后,你就可以通过如下命令来启动一个 application 集群了:
$ ./bin/flink run-application \
--target kubernetes-application \
-Dkubernetes.cluster-id=my-first-application-cluster \
-Dkubernetes.container.image=custom-image-name \
local:///opt/flink/usrlib/my-flink-job.jar
注:local
只支持 Application 模式 schema。
kubernetes.cluster-id
选项用来指定集群名称,并且是必选项。如果没有指定该选项,则 Flink 会生成一个随机名称。
kubernetes.container.image
选项指定启动 pod 的镜像。
application 集群一旦部署完毕,你就可以与它互动了:
# 列出集群上运行的 job
$ ./bin/flink list --target kubernetes-application -Dkubernetes.cluster-id=my-first-application-cluster
# 取消运行的 job
$ ./bin/flink cancel --target kubernetes-application -Dkubernetes.cluster-id=my-first-application-cluster <jobId>
你可以通过在 bin/flink
后设置 -Dkey=value
格式的 key-value 对来覆盖 conf/flink-conf.yaml
中的配置。
请参考 部署模式概述 来获取 application 模式更高级的相关知识。
已经在上面的入门指南章节中描述了如何部署一个 Session 集群了。
Session 模式可以通过两种模式执行:
kubernetes-session.sh
脚本会在 Kubernetes 上部署 flink 集群,然后终止本地客户端。-Dexecution.attached=true
):kubernetes-session.sh
本地客户端会保持运行,并且允许使用命令来控制运行的 flink 集群。比如通过 stop
命令来停止正在运行的 Session 集群,通过 help
命令来列出所有支持的命令。可以通过如下命令来重新附加正在运行的 Session 集群到 my-first-flink-cluster
集群 id 上:
$ ./bin/kubernetes-session.sh \
-Dkubernetes.cluster-id=my-first-flink-cluster \
-Dexecution.attached=true
你可以通过在运行 bin/kubernetes-session.sh
脚本时添加 -Dkey=value
格式的 key-value 对来覆盖 conf/flink-conf.yaml
文件中的配置。
你可以通过删除 Flink 部署 或使用如下命令来停止 cluster id 为 my-first-flink-cluster
的 Session 集群:
$ echo 'stop' | ./bin/kubernetes-session.sh \
-Dkubernetes.cluster-id=my-first-flink-cluster \
-Dexecution.attached=true
可以在配置页面找到 Kubernetes 相关配置选项。
flink 通过 Fabric8 Kubernetes client 和 Kubernetes APIServer进行交互,以 创建/删除 Kubernetes 资源,比如:部署、Pod、ConfigMap、Service 等,同时也包括查看 Pod 和 ConfigMap。除了上面提到的 flink 配置选项,也可以通过系统属性或环境变量配置的专家选项来配置 Fabric8 Kubernetes 客户端。
比如,用户可以通过下面的 Flink 配置选项来设置最大的并发请求数。
containerized.master.env.KUBERNETES_MAX_CONCURRENT_REQUESTS: 200
env.java.opts.jobmanager: "-Dkubernetes.max.concurrent.requests=200"
可以通过 kubernetes.rest-service.exposed.type 配置选项来暴露 Flink 的 Web UI 和 REST 终端服务。
localhost:8081
来提交一个 flink job 到会话或查看 dashboard了。$ kubectl port-forward service/<ServiceName> 8081
NodePort
)来暴露每个节点 IP 上的服务。可以通过 :
来连接 JobManager 服务,也可以通过 Kubernetes ApiServer 地址来代替 NodeIP
。可以在 kube 配置文件中找到这个地址。NodePort
。你可以使用命令 kubectl get services/-rest
来获取 EXTERNAL-IP,然后手动构造负载均衡的 JobManager Web 接口:http://:8081
请参考官网文档在 Kubernetes 上发布服务来获取更多信息。
取决于你的实际环境,通过
LoadBalancer
REST 服务启动 flink 集群,可能会让集群公开访问,这通常会让集群可以执行任意代码。
Kubernetes 会整合 conf/log4j-console.properties
和 conf/logback-console.xml
为一个 ConfigMap,然后暴露给 pod。对这些文件的改变对新启动的集群是可见的。
默认情况下,JobManager 和 TaskManager 会将日志输出到控制台,同时写入到每个 pod 的 /opt/flink/log
目录下。STDOUT
和 STDERR
只会直接输出到控制台,可以通过下面的命令访问他们:
$ kubectl logs <pod-name>
如果 pod 正在运行,你也可以使用 kubectl exec -it
来查看日志或对运行进行 debug 调试。
为了不浪费资源,flink 会自动清除空闲的 TaskManager,该行为会导致访问每个 pod 的日志更加困难。你可以通过配置 resourcemanager.taskmanager-timeout 来增加 TaskManager 的空闲时间,以让自己有更多的时间来查看日志文件。
如果你已经配置你的日志记录器可以自动获取配置更改,则你可以通过更改单独的 ConfigMap 来动态调整日志级别,假设 Cluster id 为 my-first-flink-cluster
,则可以使用如下命令进行更改:
$ kubectl edit cm flink-config-my-first-flink-cluster
为了使用插件,你必须将他们拷贝到 Flink JobManager/TaskManager pod 中的正确位置。你可以使用无需挂载卷的内置插件,或构建一个自定义 Docker 镜像。比如,使用如下命令对你的 Flink session 集群启用 S3 插件:
$ ./bin/kubernetes-session.sh
-Dcontainerized.master.env.ENABLE_BUILT_IN_PLUGINS=flink-s3-fs-hadoop-1.16.0.jar \
-Dcontainerized.taskmanager.env.ENABLE_BUILT_IN_PLUGINS=flink-s3-fs-hadoop-1.16.0.jar
如果你想使用自定义 Docker 镜像,则可以通过配置选项 kubernetes.container.image
来指定它,Flink 社区提供了一个非常好用的 Flink Docker 镜像 。通过如何自定义 Flink’s Docker 镜像查看如何启用插件,增加依赖,以及其他选项。
Kubernetes Secrets 是一个包括一些隐私数据的对象,比如密码,token,或 key。这些信息可能会放在一个 pod 或镜像中。Flink on Kubernetes 可以通过两种方式使用这些隐私:
下面的命令会将名为 mysecret
的隐私挂载到标准 pod 下的 /path/to/secret
目录:
$ ./bin/kubernetes-session.sh -Dkubernetes.secrets=mysecret:/path/to/secret
名为 mysecret
的隐私中的用户名和密码可以在 /path/to/secret/username
和 /path/to/secret/password
文件中找到。查看 Kubernetes 官网文档 获取更多细节。
下面的命令可以将标准 pod 中名为 mysecret
的隐私暴露为环境变量:
$ ./bin/kubernetes-session.sh -Dkubernetes.env.secretKeyRef=\
env:SECRET_USERNAME,secret:mysecret,key:username;\
env:SECRET_PASSWORD,secret:mysecret,key:password
环境变量 SECRET_USERNAME
包含用户名,SECRET_PASSWORD
包含密码。查看 Kubernetes 官网文档 获取更多细节。
对于 on Kubernetes 高可用,可以查看高可用服务。
给 kubernetes.jobmanager.replicas 配置一个大于 1 的值来启动备用 JobManager,以此来达到更快的恢复。注意,在启动备用 JobManager 时,需要启用高可用。
Flink 使用 Kubernetes OwnerReference’s 来清理所有的集群组件。所有 Flink 创建的资源,包括 ConfigMap
、Service
和 Pod
,都有被设置到 deployment/
中的 OwnerReference
。当部署被删除时,所有相关联的资源都会被自动删除。
$ kubectl delete deployment/<cluster-id>
目前,所有 >= 1.9
版本的 Kubernetes 都支持。
Kubernetes 命名空间通过资源配额在多个用户之间集群资源。Flink on Kubernetes 可以使用命名空间来启用 Flink 集群。可以通过 kubernetes.namespace 配置命名空间。
Role-based access control (RBAC) 是企业中基于角色的单个用户访问计算和网络资源的常规方法吗,用户可以配置用于 JobManager 访问 kubernetes 集群内的 Kubernetes API 服务的 RBAC 角色和服务账号。
每个命名空间都有默认的服务账号,但是默认的服务账号可能没有访问和删除 Kubernetes 集群内 pod 的权限,用户可能需要更新默认服务账号的权限,或指定其他绑定了正确角色的服务账号。
$ kubectl create clusterrolebinding flink-role-binding-default --clusterrole=edit --serviceaccount=default:default
如果你不想使用默认服务账号,可以使用如下命令创建一个新的名为 flink-service-account
的服务账号,并且设置角色绑定,然后使用配置选项 -Dkubernetes.service-account=flink-service-account
让 JobManager pod 使用 flink-service-account
服务账号来创建或删除 TaskManager pod 和 leader ConfigMap,新的账号也允许 TaskManager 查看 leader ConfigMap 来检索 JobManager 和 ResourceManager 的地址。
$ kubectl create serviceaccount flink-service-account
$ kubectl create clusterrolebinding flink-role-binding-flink --clusterrole=edit --serviceaccount=default:flink-service-account
请参考官网 Kubernetes 文档 RBAC Authorization 来获取更多信息。
Flink 允许通过模板文件来定义 JobManager 和 TaskManager pod,这种方式支持使用 Flink Kubernetes 配置选项 中没有直接支持的高级特性。使用 kubernetes.pod-template-file
来指定包含 pod 定义的本地文件,该文件将被用于初始化 JobManager 和 TaskManager。主要的容器名称应该被定义为 flink-main-container
。请看考 pod 模板案例 来获取更多信息。
一些 pod 模板文件中的属性会被 flink 覆盖,解析有效属性值的算法如下:
Defined by Flink:用户无法配置这些属性。
Defined by the user:用户可以自由的指定这些属性值,Flink 框架不会设置任何源于配置选项和模板的额外值和有效值。
优先顺序:首先采用指定的配置选项中的值,然后取 pod 模板中的值,如果都没有指定,则最后采用配置选项的默认值。
Merged with Flink:Flink 会合并用户定义的值,遵循上面的优先顺序,在遇到同名字段的情况下,Flink 值具有优先级。
参考下面表中列出的所有可以被覆盖的 pod 属性,pod 模板中定义的所有未在下表列出的属性值都不会起作用。
Pod 元数据
| 键 | 种类 | 关联的配置选项 | 描述 |
| :-------------* | :-----------------* | :----------------------------------------------------------* | :----------------------------------------------------------* |
| name | Defined by Flink | | JobManager pod 名称将会被在部署中定义的 kubernetes.cluster-id 覆盖。TaskManager pod 名称将会被 Flink ResourceManager 生成的
覆盖。 |
| namespace | Defined by the user | kubernetes.namespace | JobManager 部署和 TaskManager pod 都会在用户指定的命名空间中创建。 |
| ownerReferences | Defined by Flink | | JobManager 和 TaskManager pod 的所有者参考经常被设置为 JobManager 部署,请参考 kubernetes.jobmanager.owner.reference 来控制何时删除部署。 |
| annotations | Defined by the user | kubernetes.jobmanager.annotations kubernetes.taskmanager.annotations | Flink 将增加通过 Flink 配置选项指定的额外注释。 |
| labels | Merged with Flink | kubernetes.jobmanager.labels kubernetes.taskmanager.labels | Flink 会增加一些内置的标签到用户自定义的值上。 |
Pod 属性
| 键 | 种类 | 关联的配置选项 | 描述 |
| :--------------* | :-----------------* | :----------------------------------------------------------* | :----------------------------------------------------------* |
| imagePullSecrets | Defined by the user | kubernetes.container.image.pull-secrets | Flink 会增加通过 Flink 配置选项指定的额外拉取到的隐私。 |
| nodeSelector | Defined by the user | kubernetes.jobmanager.node-selector kubernetes.taskmanager.node-selector | Flink 会增加通过 Flink 配置选项指定的额外的节点选择器。 |
| tolerations | Defined by the user | kubernetes.jobmanager.tolerations kubernetes.taskmanager.tolerations | Flink 会增加通过 Flink 配置选项指定的额外的容错。 |
| restartPolicy | Defined by Flink | | 通常指定 JobManager pod ,从来不指定 TaskManager pod。JobManager pod 通常会被部署重启,TaskManager 不应该被重启。 |
| serviceAccount | Defined by the user | kubernetes.service-account | JobManager 和 TaskManager pod 将会通过用户定义的服务账号来创建。 |
| volumes | Merged with Flink | | Flink 会增加一些内置的 ConfigMap 卷,比如:flink-config-volume,hadoop-config-valute,以用来传递 Flink 配置和 hadoop 配置。 |
主容器属性
| 键 | 种类 | 关联的配置选项 | 描述 |
| :-------------* | :-----------------* | :----------------------------------------------------------* | :----------------------------------------------------------* |
| env | Merged with Flink | containerized.master.env.{ENV_NAME} containerized.taskmanager.env.{ENV_NAME} | Flink 会增加一些内置的环境变量到用户自定义的值上。 |
| image | Defined by the user | kubernetes.container.image | 容器镜像将根据用户自定义值的优先顺序进行解析。 |
| imagePullPolicy | Defined by the user | kubernetes.container.image.pull-policy | 容器镜像拉取策略将根据用户自定义值的优先顺序进行解析。 |
| name | Defined by Flink | | 容器名称将会被 Flink 的 “flink-main-container” 值覆盖。 |
| resources | Defined by the user | Memory:
jobmanager.memory.process.size taskmanager.memory.process.size
CPU:
kubernetes.jobmanager.cpu kubernetes.taskmanager.cpu | 内存和 cpu 资源(包括请求和限制)将会被 Flink 的配置选项覆盖,所有其他的资源(比如临时存储)将会被留下。 |
| containerPorts | Merged with Flink | | Flink 会增加一些内置的容器端口号,比如:rest、jobmanager-rpc、blob、taskmanager-rpc。 |
| volumeMounts | Merged with Flink | | Flink 会增加一些内置的卷挂载,比如:flink-config-volume、hadoop-config-volume,这对于传递 flink 配置和 hadoop 配置是很有必要的。 |
pod-template.yaml
apiVersion: v1
kind: Pod
metadata:
name: jobmanager-pod-template
spec:
initContainers:
* name: artifacts-fetcher
image: busybox:latest
# 使用 wget 或其他工具从远程存储获取用户 jar
command: [ 'wget', 'https://path/of/StateMachineExample.jar', '-O', '/flink-artifact/myjob.jar' ]
volumeMounts:
* mountPath: /flink-artifact
name: flink-artifact
containers:
# 不要修改主容器名称
* name: flink-main-container
resources:
requests:
ephemeral-storage: 2048Mi
limits:
ephemeral-storage: 2048Mi
volumeMounts:
* mountPath: /opt/flink/volumes/hostpath
name: flink-volume-hostpath
* mountPath: /opt/flink/artifacts
name: flink-artifact
* mountPath: /opt/flink/log
name: flink-logs
# 使用 sidecar 容器推送日志到远程存储或做一些其他的 debug 事情
* name: sidecar-log-collector
image: sidecar-log-collector:latest
command: [ 'command-to-upload', '/remote/path/of/flink-logs/' ]
volumeMounts:
* mountPath: /flink-logs
name: flink-logs
volumes:
* name: flink-volume-hostpath
hostPath:
path: /tmp
type: Directory
* name: flink-artifact
emptyDir: { }
* name: flink-logs
emptyDir: { }
当在 Kubernetes 上部署 Flink 时,下面的 jar 包将会被识别为用户 jar 并且被包含在用户的 classpath 中:
usrlib
目录下所有的 JAR 文件。请参考 调试类加载 来获取更多细节。
该入门指南章节会指导你在 YARN 上配置一个完整的 Flink 集群。
Apache Hadoop YARN 是很多数据处理框架爱用的资源提供者,Flink 服务可以提交到 YARN 的 ResourceManager,然后通过 YARN 的 NodeManager 来提供容器,然后 Flink 会部署他的 JobManager 和 TaakManager 示例到这些容器上。
Flink 可以基于在 JobManager 上运行的 job 所需要的 slot 来动态收集和清理 TaskManager 资源。
入门指南章节假定已经有一个可用的 YARN 环境了,并且版本号 ≥ 2.8.5。YARN 环境可以通过 Amazon EMR、Google Cloud DataProc 或 Cloudera 产品来很方便的提供。该入门指南并不要求手动部署本地 YARN 环境 或 集群部署 。
yarn top
来确定你的 YARN 集群已经准备好接收 Flink 程序了,该命令应该不展示任何错误信息。HADOOP_CLASSPATH
环境变量,可以通过运行 echo $HADOOP_CLASSPATH
命令来检查。如果没有设置,请通过以下命令来设置:export HADOOP_CLASSPATH=`hadoop classpath`
确保设置了 HADOOP_CLASSPATH
环境变量之后,就可以启动 YARN session 了,并且提交案例 job:
# 假定在 root 目录下解压了 Flink 分布式文件
# (0) export HADOOP_CLASSPATH
export HADOOP_CLASSPATH=`hadoop classpath`
# (1) 启动 YARN Session
./bin/yarn-session.sh --detached
# (2) 你可以通过命令行输出打印的最有一行中的 URL 或通过 YARN ResourceManager web UI 来访问 Flink web 页面
# (3) 提交案例 job
./bin/flink run ./examples/streaming/TopSpeedWindowing.jar
# (4) 停止 YARN session,请将下面的 application id 替换为 yarn-session.sh 命令输出的 application id
echo "stop" | ./bin/yarn-session.sh -id application_XXXXX_XXX
恭喜!你已经成功的通过部署 Flink on YARN 来运行 Flink 程序了。
对于生产案例,我们建议使用 Application 模式 来部署 Flink 程序,这些模式对程序有更好的隔离性。
请参考 deployment 模式概述 来获取 application 模式的高级知识。
Application 模式将在 YARN 上启动一个 Flink 集群,然后运行在 YARN 中的 JobManager 执行应用程序中的 main() 方法。集群将会在程序运行完成后马上关闭,也可以使用 yarn application -kill
或通过取消 Flink job 来停止集群。
./bin/flink run-application -t yarn-application ./examples/streaming/TopSpeedWindowing.jar
一旦部署了 Application 模式的集群,你就可以与它进行交互操作,比如取消或触发 savepoint。
# 列出集群中运行的 job
./bin/flink list -t yarn-application -Dyarn.application.id=application_XXXX_YY
# 取消运行的 job
./bin/flink cancel -t yarn-application -Dyarn.application.id=application_XXXX_YY <jobId>
注意,取消 Application 集群中的 job 将会停止集群。
为了发挥 application 模式的所有潜力,可以考虑使用 yarn.provided.lib.dirs
配置选项并且提前上传你的应用程序 jar 到一个可以被集群所有节点访问的位置,具体命令如下:
./bin/flink run-application -t yarn-application \
-Dyarn.provided.lib.dirs="hdfs://myhdfs/my-remote-flink-dist-dir" \
hdfs://myhdfs/jars/my-application.jar
上述命令可以让 job 提交更加轻量,因为需要的 Flink jar 和应用程序 jar 可以通过指定的远程位置获取,而不是通过客户端上传到集群。
请参考 deployment 模式概述 来获取 application 模式的高级知识。
我们已经在上面的入门指南中说明了 Session 模式的部署。
Session 模式有两种操作模式:
yarn-session.sh
客户端提交 Flink 集群到 YARN,但是本地客户端依然会保持运行,并且追踪集群的状态。如果集群运行失败,则客户端会展示错误信息。如果客户端被终止,它也会给集群发送关闭信号。-d
or --detached
):yarn-session.sh
客户端提交 Flink 集群到 YARN,然后客户端返回。要停止 Flink 客户端,则需要调用其他的客户端,比如 YARN tools。session 模式会在 /tmp/.yarn-properties-
中创建一个隐藏的配置文件,该配置文件会在提交 job 时被通过命令行接口集群获取。
你也可以在提交 Flink job 的命令行接口中手动指定目标 YARN 集群,示例如下:
./bin/flink run -t yarn-session \
-Dyarn.application.id=application_XXXX_YY \
./examples/streaming/TopSpeedWindowing.jar
你可以通过下面的命令重新附加到一个 YARN session 集群:
./bin/yarn-session.sh -id application_XXXX_YY
除了通过 conf/flink-conf.yaml
文件指定 配置 之外,你也可以在使用 ./bin/yarn-session.sh
提交时使用 -Dkey=value
参数来指定任何配置。
YARN session 客户端也有一些“短参数”用于设置,可以通过运行 ./bin/yarn-session.sh -h
命令来列出他们。
Per-job 模式只支持 YARN,并且在 Flink 1.15 中已经过时,将会在 FLINK-26000 中删除,请考虑使用 application 模式来为 YARN 上的每个作业启动一个专用的集群。
请参考 deployment 模式概述 来获取 per-job 模式的高级知识。
Per-job 集群模式会在 YARN 上启动一个 Flink 集群,然后在本地运行提供的程序 jar 包,最后将 JobGraph 提交到 YARN 中的 JobManager。如果你指定了 --detached
参数,本地客户端会在提交被接受之后马上停止。
YARN 中的 per-job 集群会在 job 停止之后马上停止。
./bin/flink run -t yarn-per-job --detached ./examples/streaming/TopSpeedWindowing.jar
Per-job 集群一旦部署完毕,你就可以进行和它进行交互操作了,比如取消 job ,或触发一个 savepoint。
# 列出集群运行的 job
./bin/flink list -t yarn-per-job -Dyarn.application.id=application_XXXX_YY
# 取消运行的 job
./bin/flink cancel -t yarn-per-job -Dyarn.application.id=application_XXXX_YY <jobId>
注意,取消 per-job 集群中的 job,将会停止 per-job 集群。
在 配置页面 可以找到 YARN 的所有配置。
下面的配置参数通过 Flink on YARN 来管理,他们可以在框架运行时被覆盖:
jobmanager.rpc.address
:被动态设置为 Flink on YARN 中运行 JobManager 容器的地址io.tmp.dirs
:如果没有设置,Flink 将会设置为通过 YARN 定义的临时目录high-availability.cluster-id
:在 HA 服务中会自动生成 ID 来区分多个集群你可以通过 HADOOP_CONF_DIR
环境变量将额外的 Hadoop 配置文件传递给 Flink,该变量接收一个包含 Hadoop 配置文件的目录。默认情况下,所有需要的 Hadoop 配置文件都是通过 HADOOP_CLASSPATH
环境变量来获取并加载的。
如果无法使用已获取到的资源运行提交的 job,则运行在 YARN 上的 JobManager 会请求额外的 TaskManager 资源。在指定的 session 模式下运行时,如果需要,则 JobManager 会收集额外的 TaskManager 资源来运行提交的其他 Job。不再使用的 TaskManager 将会在超时之后被清理。
YARN 实现了 JobManager 和 TaskManager 的 process 内存配置,上报的 VCore 数量默认等于每个 TaskManager 配置的 slot 数量。yarn.containers.vcores 允许指定自定义值来覆盖 vcore 的数量,为了让这个参数生效,需要开启 YARN 集群的 CPU 调度。
失败的容器(包括 JobManager)将会被 YARN 换下。可以通过 yarn.application-attempts(默认为1)来配置 JobManager 容器重启的最大次数。一旦耗尽所有的尝试,则 YARN 程序将会失败。
on YARN 高可用可以通过 YARN 和 高可用服务 组合实现。
一旦配置了 HA 服务,它将会持久化 JobManager 元数据,并执行 leader 选举。
YARN 会进行失败 JobManager 的重启工作。JobManager 的重启最大次数通过两个配置参数定义:
注意,当部署为 on YARN 时,Flink 会管理 high-availability.cluster-id
配置参数,Flink 会设置该值为默认的 YARN application id。在 YARN 上部署高可用集群时不要覆盖该参数。存储到 HA 后端(比如 zookeeper)的 cluster ID 被用于区分不同的高可用集群。覆盖该配置参数会导致多个 YARN 集群影响彼此。
Hadoop YARN 2.4.0 有个大 bug(已经在 2.5.0 中修复):YARN 会阻止从一个已经启动的 Application Master/JobManager 容器上重启容器。查看 FLINK-4142 来获取更多细节。我们建议至少使用 Hadoop 2.5.0 来部署 YARN 高可用。
Flink on YARN 从 Hadoop 2.8.5 开始支持,支持所有 >= 2.8.5
版本的 Hadoop,包括 Hadoop 3.x。
为了提供 Flink 需要的 Hadoop 依赖,我们建议设置在入门指南章节提到的 HADOOP_CLASSPATH
环境变量。
如果无法进行上述设置,也可以将依赖放到 Flink 的 lib/
目录下。
Flink 也提供了预打包的 Hadoop fat jar,可以将他们放到 lib/
目录下,可以在 Downloads / Additional Components 页面找到他们。这些预打包的 fat jar 通过 shade 打包方式,避免了公共库的依赖冲突。Flink 社区没有测试这些预打包 jar 和 YARN 的整合。
一些 YARN 集群会使用防火墙来控制集群和外网的通信,在这种配置下,Flink job 只能通过集群内网提交到 YARN session。如果在生产上不可行,Flink 允许对 REST 端点配置一个端口号范围,用于客户端与集群之间通信。通过配置端口号范围,用户就可以通过防火墙来提交 job 到 Flink 上了。
通过 rest.bind-port 配置参数来指定 REST 端点的端口号,该配置选项接受单个端口号(比如:50010)、范围(比如 50000-50025)、或同时使用两者。
当在 Yarn 上使用 Session 模式部署 Flink 时,只有在启动命令中指定的 JAR 文件会被识别为用户 jar,并且被包含在用户 classpath 中。
当在 Yarn 上使用 PerJob/Application 模式部署 Flink 时,在启动命令中指定的 JAR 文件和 Flink 的 usrlib
目录下所有的 JAR 文件都将被识别为用户 jar。默认情况下,Flink 在运行单个 job 时会将用户的 jar 放到系统 classpath 中,该行为可以通过 yarn.classpath.include-user-jar 参数控制。
当设置该参数为 DISABLED
时,Flink 会将 jar 放到用户的 classpath 中。
用户 jar 在 classpath 中的位置可以通过设置 yarn.classpath.include-user-jar 参数为下面的某个值来控制:
ORDER
:默认值,将 jar 按照字段序放到系统 classpath。FIRST
:将 jar 放到系统 classpath 开头。LAST
:将 jar 放到系统 classpath 最后。请参考 Debugging Classloading Docs 获取更多细节。
Configuration | Apache Flink
该章节为 Flink 可以用到的所有配置,使用时直接去官网查看即可。
Apache Flink 基于 JVM 的高效处理能力,依赖于其对各组件内存用量的细致掌控。考虑到用户在 Flink 上运行应用的多样性,尽管社区已经努力为所有配置项提供合理的默认值,但仍无法满足所有情况下的需求。为了给用户生产提供最大化的价值, Flink 允许用户在整体上以及细粒度上对集群的内存分配进行调整。为了优化内存需求,可以参考网络内存调整向导。
本文接下来介绍的内存配置方法适用于 1.10 及以上版本的 TaskManager 进程和 1.11 及以上版本的 JobManager 进程。 Flink 在 1.10 和 1.11 版本中对内存配置部分进行了较大幅度的改动,从早期版本升级的用户请参考升级指南。
Flink JVM 进程的*进程总内存(Total Process Memory)*包含了由 Flink 应用使用的内存(Flink 总内存)以及由运行 Flink 的 JVM 使用的内存。 Flink 总内存(Total Flink Memory)包括 JVM 堆内存(Heap Memory)和堆外内存(Off-Heap Memory)。 其中堆外内存包括直接内存(Direct Memory)和本地内存(Native Memory)。
配置 Flink 进程内存最简单的方法是指定以下两个配置项中的任意一个:
| 配置项 | TaskManager 配置参数 | JobManager 配置参数 |
| :----------* | :----------------------------------------------------------* | :----------------------------------------------------------* |
| Flink 总内存 | taskmanager.memory.flink.size
| jobmanager.memory.flink.size
|
| 进程总内存 | taskmanager.memory.process.size
| jobmanager.memory.process.size
|
提示 关于本地执行,请分别参考 TaskManager 和 JobManager 的相关文档。
Flink 会根据默认值或其他配置参数自动调整剩余内存部分的大小。 关于各内存部分的更多细节,请分别参考 TaskManager 和 JobManager 的相关文档。
对于独立部署模式(Standalone Deployment),如果你希望指定由 Flink 应用本身使用的内存大小,最好选择配置 Flink 总内存。 Flink 总内存会进一步划分为 JVM 堆内存和堆外内存。 更多详情请参考如何为独立部署模式配置内存。
通过配置进程总内存可以指定由 Flink JVM 进程使用的总内存大小。 对于容器化部署模式(Containerized Deployment),这相当于申请的容器(Container)大小,详情请参考如何配置容器内存(Kubernetes、Yarn)。
此外,还可以通过设置 Flink 总内存的特定内部组成部分的方式来进行内存配置。不同进程需要设置的内存组成部分是不一样的。 详情请分别参考 TaskManager 和 JobManager 的相关文档。
提示 以上三种方式中,用户需要至少选择其中一种进行配置(本地运行除外),否则 Flink 将无法启动。 这意味着,用户需要从以下无默认值的配置参数(或参数组合)中选择一个给出明确的配置:
| TaskManager: | JobManager: |
| :----------------------------------------------------------* | :----------------------------------------------------------* |
| taskmanager.memory.flink.size
| jobmanager.memory.flink.size
|
| taskmanager.memory.process.size
| jobmanager.memory.process.size
|
| taskmanager.memory.task.heap.size
和 taskmanager.memory.managed.size
| jobmanager.memory.heap.size
|
提示 不建议同时设置进程总内存和 Flink 总内存。 这可能会造成内存配置冲突,从而导致部署失败。 额外配置其他内存部分时,同样需要注意可能产生的配置冲突。
Flink 进程启动时,会根据配置的和自动推导出的各内存部分大小,显式地设置以下 JVM 参数:
| JVM 参数 | TaskManager 取值 | JobManager 取值 |
| :----------------------------------------------------------* | :------------------------------------* | :-----------------* |
| -Xmx 和 -Xms | 框架堆内存 + 任务堆内存 | JVM 堆内存 |
| -XX:MaxDirectMemorySize (TaskManager 始终设置,JobManager 见注释) | 框架堆外内存 + 任务堆外内存 + 网络内存 | 堆外内存 |
| -XX:MaxMetaspaceSize | JVM Metaspace | JVM Metaspace |
请记住,根据所使用的 GC 算法,你可能无法使用到全部堆内存。一些 GC 算法会为它们自身分配一定量的堆内存。这会导致堆的指标返回一个不同的最大值。
请注意,堆外内存也包括了用户代码使用的本地内存(非直接内存)。
只有在设置了 jobmanager.memory.enable-jvm-direct-memory-limit
选项时,JobManager 才会设置 JVM 直接内存限制。
相关内存部分的配置方法,请同时参考 TaskManager 和 JobManager 的详细内存模型。
本节介绍下列内存部分的配置方法,它们都可以通过指定在总内存中所占比例的方式进行配置,同时受限于相应的的最大/最小值范围。
相关内存部分的配置方法,请同时参考 TaskManager 和 JobManager 的详细内存模型。
这些内存部分的大小必须在相应的最大值、最小值范围内,否则 Flink 将无法启动。 最大值、最小值具有默认值,也可以通过相应的配置参数进行设置。 例如,如果仅配置下列参数:
那么 JVM 开销的实际大小将会是 1000Mb x 0.1 = 100Mb,在 64-128Mb 的范围内。
如果将最大值、最小值设置成相同大小,那相当于明确指定了该内存部分的大小。
如果没有明确指定内存部分的大小,Flink 会根据总内存和占比计算出该内存部分的大小。 计算得到的内存大小将受限于相应的最大值、最小值范围。 例如,如果仅配置下列参数:
那么 JVM 开销的实际大小将会是 128Mb,因为根据总内存和占比计算得到的内存大小 100Mb 小于最小值。
如果配置了总内存和其他内存部分的大小,那么 Flink 也有可能会忽略给定的占比。 这种情况下,受限的等比内存部分的实际大小是总内存减去其他所有内存部分后剩余的部分。 这样推导得出的内存大小必须符合最大值、最小值范围,否则 Flink 将无法启动。 例如,如果仅配置下列参数:
进程总内存中所有其他内存部分均有默认大小,包括 TaskManager 的托管内存默认占比或 JobManager 的默认堆外内存。 因此,JVM 开销的实际大小不是根据占比算出的大小(1000Mb x 0.1 = 100Mb),而是进程总内存中剩余的部分。 这个剩余部分的大小必须在 64-256Mb 的范围内,否则将会启动失败。
Flink 的 TaskManager 负责执行用户代码。 根据实际需求为 TaskManager 配置内存将有助于减少 Flink 的资源占用,增强作业运行的稳定性。
本文接下来介绍的内存配置方法适用于 1.10 及以上版本。 Flink 在 1.10 版本中对内存配置部分进行了较大幅度的改动,从早期版本升级的用户请参考升级指南。
提示 本篇内存配置文档仅针对 TaskManager! 与 JobManager 相比,TaskManager 具有相似但更加复杂的内存模型。
Flink JVM 进程的*进程总内存(Total Process Memory)*包含了由 Flink 应用使用的内存(Flink 总内存)以及由运行 Flink 的 JVM 使用的内存。 其中,*Flink 总内存(Total Flink Memory)*包括 JVM 堆内存(Heap Memory)、*托管内存(Managed Memory)*以及其他直接内存(Direct Memory)或本地内存(Native Memory)。
如果你是在本地运行 Flink(例如在 IDE 中)而非创建一个集群,那么本文介绍的配置并非所有都是适用的,详情请参考本地执行。
其他情况下,配置 Flink 内存最简单的方法就是配置总内存。 此外,Flink 也支持更细粒度的内存配置方式。
Flink 会根据默认值或其他配置参数自动调整剩余内存部分的大小。 接下来的章节将介绍关于各内存部分的更多细节。
如配置总内存中所述,另一种配置 Flink 内存的方式是同时设置任务堆内存和托管内存。通过这种方式,用户可以更好地掌控用于 Flink 任务的 JVM 堆内存及 Flink 的托管内存大小。
Flink 会根据默认值或其他配置参数自动调整剩余内存部分的大小。关于各内存部分的更多细节,请参考相关文档。
提示 如果已经明确设置了任务堆内存和托管内存,建议不要再设置进程总内存或 Flink 总内存,否则可能会造成内存配置冲突。
如果希望确保指定大小的 JVM 堆内存给用户代码使用,可以明确指定任务堆内存(taskmanager.memory.task.heap.size
)。 指定的内存将被包含在总的 JVM 堆空间中,专门用于 Flink 算子及用户代码的执行。
托管内存是由 Flink 负责分配和管理的本地(堆外)内存。 以下场景需要使用托管内存:
可以通过以下两种方式指定托管内存的大小:
taskmanager.memory.managed.size
明确指定其大小。taskmanager.memory.managed.fraction
指定在Flink 总内存中的占比。当同时指定二者时,会优先采用指定的大小(Size)。若二者均未指定,会根据默认占比进行计算。
请同时参考如何配置 State Backend 内存以及如何配置批处理作业内存。
对于包含不同种类的托管内存消费者的作业,可以进一步控制托管内存如何在消费者之间分配。 通过 taskmanager.memory.managed.consumer-weights
可以为每一种类型的消费者指定一个权重,Flink 会按照权重的比例进行内存分配。 目前支持的消费者类型包括:
OPERATOR
: 用于内置算法。STATE_BACKEND
: 用于流处理中的 RocksDB State Backend。PYTHON
:用户 Python 进程。例如,一个流处理作业同时使用到了 RocksDB State Backend 和 Python UDF,消费者权重设置为 STATE_BACKEND:70,PYTHON:30
,那么 Flink 会将 70%
的托管内存用于 RocksDB State Backend,30%
留给 Python 进程。
提示 只有作业中包含某种类型的消费者时,Flink 才会为该类型分配托管内存。 例如,一个流处理作业使用 Heap State Backend 和 Python UDF,消费者权重设置为 STATE_BACKEND:70,PYTHON:30
,那么 Flink 会将全部托管内存用于 Python 进程,因为 Heap State Backend 不使用托管内存。
提示 对于未出现在消费者权重中的类型,Flink 将不会为其分配托管内存。如果缺失的类型是作业运行所必须的,则会引发内存分配失败。 默认情况下,消费者权重中包含了所有可能的消费者类型。上述问题仅可能出现在用户显式地配置了消费者权重的情况下。
堆外内存指直接内存或本地内存。
用户代码中分配的堆外内存被归为任务堆外内存(Task Off-heap Memory),可以通过 taskmanager.memory.task.off-heap.size
指定。
提示 你也可以调整框架堆外内存(Framework Off-heap Memory)。这是一个进阶配置,建议仅在确定 Flink 框架需要更多的内存时调整该配置。
Flink 将框架堆外内存和任务堆外内存都计算在 JVM 的直接内存限制中,请参考 JVM 参数。
提示 本地内存(非直接内存)也可以被归在框架堆外内存或任务堆外内存中,在这种情况下 JVM 的直接内存限制可能会高于实际需求。
提示 网络内存(Network Memory)同样被计算在 JVM 直接内存中。 Flink 会负责管理网络内存,保证其实际用量不会超过配置大小。 因此,调整网络内存的大小不会对其他堆外内存有实质上的影响。
请参考内存模型详解。
如上图所示,下表中列出了 Flink TaskManager 内存模型的所有组成部分,以及影响其大小的相关配置参数。
| 组成部分 | 配置参数 | 描述 |
| :----------------------------------------------------------* | :----------------------------------------------------------* | :----------------------------------------------------------* |
| 框架堆内存(Framework Heap Memory) | taskmanager.memory.framework.heap.size
| 用于 Flink 框架的 JVM 堆内存(进阶配置)。 |
| 任务堆内存(Task Heap Memory) | taskmanager.memory.task.heap.size
| 用于 Flink 应用的算子及用户代码的 JVM 堆内存。 |
| 托管内存(Managed memory) | taskmanager.memory.managed.size
taskmanager.memory.managed.fraction
| 由 Flink 管理的用于排序、哈希表、缓存中间结果及 RocksDB State Backend 的本地内存。 |
| 框架堆外内存(Framework Off-heap Memory) | taskmanager.memory.framework.off-heap.size
| 用于 Flink 框架的堆外内存(直接内存或本地内存)(进阶配置)。 |
| 任务堆外内存(Task Off-heap Memory) | taskmanager.memory.task.off-heap.size
| 用于 Flink 应用的算子及用户代码的堆外内存(直接内存或本地内存)。 |
| 网络内存(Network Memory) | taskmanager.memory.network.min
taskmanager.memory.network.max
taskmanager.memory.network.fraction
| 用于任务之间数据传输的直接内存(例如网络传输缓冲)。该内存部分为基于 Flink 总内存的受限的等比内存部分。 |
| JVM Metaspace | taskmanager.memory.jvm-metaspace.size
| Flink JVM 进程的 Metaspace。 |
| JVM 开销 | taskmanager.memory.jvm-overhead.min
taskmanager.memory.jvm-overhead.max
taskmanager.memory.jvm-overhead.fraction
| 用于其他 JVM 开销的本地内存,例如栈空间、垃圾回收空间等。该内存部分为基于进程总内存的受限的等比内存部分。 |
我们可以看到,有些内存部分的大小可以直接通过一个配置参数进行设置,有些则需要根据多个参数进行调整。
通常情况下,不建议对框架堆内存和框架堆外内存进行调整。 除非你非常肯定 Flink 的内部数据结构及操作需要更多的内存。这可能与具体的部署环境及作业结构有关,例如非常高的并行度。此外,Flink 的部分依赖(例如 Hadoop)在某些特定的情况下也可能会需要更多的直接内存或本地内存。
提示 不管是堆内存还是堆外内存,Flink 中的框架内存和任务内存之间目前是没有隔离的。对框架和任务内存的区分,主要是为了在后续版本中做进一步优化。
如果你是将 Flink 作为一个单独的 Java 程序运行在你的电脑本地而非创建一个集群(例如在 IDE 中),那么只有下列配置会生效,其他配置参数则不会起到任何效果:
| 组成部分 | 配置参数 | 本地执行时的默认值 |
| :----------* | :----------------------------------------------------------* | :--------------------* |
| 任务堆内存 | taskmanager.memory.task.heap.size
| 无穷大 |
| 任务堆外内存 | taskmanager.memory.task.off-heap.size
| 无穷大 |
| 托管内存 | taskmanager.memory.managed.size
| 128Mb |
| 网络内存 | taskmanager.memory.network.min
taskmanager.memory.network.max
| 64Mb |
本地执行模式下,上面列出的所有内存部分均可以但不是必须进行配置的。如果未配置,则会采用默认值。其中,任务堆内存和任务堆外内存的默认值无穷大(Long.MAX_VALUE 字节),以及托管内存的默认值 128Mb 均只针对本地执行模式。
提示 这种情况下,任务堆内存的大小与实际的堆空间大小无关。该配置参数可能与后续版本中的进一步优化相关。本地执行模式下,JVM 堆空间的实际大小不受 Flink 掌控,而是取决于本地执行进程是如何启动的。如果希望控制 JVM 的堆空间大小,可以在启动进程时明确地指定相关的 JVM 参数,即 -Xmx 和 -Xms。
JobManager 是 Flink 集群的控制单元。它由三种不同的组件组成:ResourceManager、Dispatcher 和每个正在运行作业的 JobMaster。本篇文档将介绍 JobManager 内存在整体上以及细粒度上的配置方法。
本文接下来介绍的内存配置方法适用于 1.11 及以上版本。 Flink 在 1.11 版本中对内存配置部分进行了较大幅度的改动,从早期版本升级的用户请参考升级指南。
提示 本篇内存配置文档仅针对 JobManager! 与 TaskManager 相比,JobManager 具有相似但更加简单的内存模型。
配置 JobManager 内存最简单的方法就是进程的配置总内存。 本地执行模式下不需要为 JobManager 进行内存配置,配置参数将不会生效。
如上图所示,下表中列出了 Flink JobManager 内存模型的所有组成部分,以及影响其大小的相关配置参数。
| 组成部分 | 配置参数 | 描述 |
| :----------------------------------------------------------* | :----------------------------------------------------------* | :----------------------------------------------------------* |
| JVM 堆内存 | jobmanager.memory.heap.size
| JobManager 的 JVM 堆内存。 |
| 堆外内存 | jobmanager.memory.off-heap.size
| JobManager 的堆外内存(直接内存或本地内存)。 |
| JVM Metaspace | jobmanager.memory.jvm-metaspace.size
| Flink JVM 进程的 Metaspace。 |
| JVM 开销 | jobmanager.memory.jvm-overhead.min
jobmanager.memory.jvm-overhead.max
jobmanager.memory.jvm-overhead.fraction
| 用于其他 JVM 开销的本地内存,例如栈空间、垃圾回收空间等。该内存部分为基于进程总内存的受限的等比内存部分。 |
如配置总内存中所述,另一种配置 JobManager 内存的方式是明确指定 JVM 堆内存的大小(jobmanager.memory.heap.size
)。通过这种方式,用户可以更好地掌控用于以下用途的 JVM 堆内存大小。
Flink 需要多少 JVM 堆内存,很大程度上取决于运行的作业数量、作业的结构及上述用户代码的需求。
提示 如果已经明确设置了 JVM 堆内存,建议不要再设置进程总内存或 Flink 总内存,否则可能会造成内存配置冲突。
在启动 JobManager 进程时,Flink 启动脚本及客户端通过设置 JVM 参数 -Xms 和 -Xmx 来管理 JVM 堆空间的大小。请参考 JVM 参数。
堆外内存包括 JVM 直接内存 和 本地内存。可以通过配置参数 jobmanager.memory.enable-jvm-direct-memory-limit
设置是否启用 JVM 直接内存限制。 如果该配置项设置为 true
,Flink 会根据配置的堆外内存大小设置 JVM 参数 -XX:MaxDirectMemorySize。请参考 JVM 参数。
可以通过配置参数 jobmanager.memory.off-heap.size
设置堆外内存的大小。如果遇到 JobManager 进程抛出 “OutOfMemoryError: Direct buffer memory” 的异常,可以尝试调大这项配置。请参考常见问题。
以下情况可能用到堆外内存:
提示 如果同时配置了 Flink 总内存和 JVM 堆内存,且没有配置堆外内存,那么堆外内存的大小将会是 Flink 总内存减去JVM 堆内存。这种情况下,堆外内存的默认大小将不会生效。
如果你是在本地运行 Flink(例如在 IDE 中)而非创建一个集群,那么 JobManager 的内存配置将不会生效。
本文在基本的配置指南的基础上,介绍如何根据具体的使用场景调整内存配置,以及在不同使用场景下分别需要重点关注哪些配置参数。
独立部署模式下,我们通常更关注 Flink 应用本身使用的内存大小。 建议配置 Flink 总内存(taskmanager.memory.flink.size
或者 [jobmanager.memory.flink.size
](//nightlies.apache.org/flink/flink-docs-release-1.16/zh/docs/deployment/config/#jobmanager-memory-flink-size" >}}))或其组成部分。此外,如果出现 Metaspace 不足的问题,可以调整 JVM Metaspace 的大小。
这种情况下通常无需配置进程总内存,因为不管是 Flink 还是部署环境都不会对 JVM 开销 进行限制,它只与机器的物理资源相关。
在容器化部署模式(Containerized Deployment)下(Kubernetes、Yarn),建议配置进程总内存(taskmanager.memory.process.size
或者 jobmanager.memory.process.size
)。该配置参数用于指定分配给 Flink JVM 进程的总内存,也就是需要申请的容器大小。
提示 如果配置了 Flink 总内存,Flink 会自动加上 JVM 相关的内存部分,根据推算出的进程总内存大小申请容器。
注意: 如果 Flink 或者用户代码分配超过容器大小的非托管的堆外(本地)内存,部署环境可能会杀掉超用内存的容器,造成作业执行失败。
请参考容器内存超用中的相关描述。
本章节内容仅与 TaskManager 相关。
在部署 Flink 流处理应用时,可以根据 State Backend 的类型对集群的配置进行优化。
执行无状态作业或者使用 HashMapStateBackend 时,建议将托管内存设置为 0。这样能够最大化分配给 JVM 上用户代码的内存。
EmbeddedRocksDBStateBackend 使用本地内存。默认情况下,RocksDB 会限制其内存用量不超过用户配置的托管内存。因此,使用这种方式存储状态时,配置足够多的托管内存是十分重要的。如果你关闭了 RocksDB 的内存控制,那么在容器化部署模式下,如果 RocksDB 分配的内存超出了申请容器的大小(进程总内存),可能会造成 TaskExecutor 被部署环境杀掉。请同时参考如何调整 RocksDB 内存以及 state.backend.rocksdb.memory.managed。
本章节内容仅与 TaskManager 相关。
Flink 批处理算子使用托管内存来提高处理效率。算子运行时,部分操作可以直接在原始数据上进行,而无需将数据反序列化成 Java 对象。这意味着托管内存对应用的性能具有实质上的影响。因此 Flink 会在不超过其配置限额的前提下,尽可能分配更多的托管内存。Flink 明确知道可以使用的内存大小,因此可以有效避免 OutOfMemoryError
的发生。当托管内存不足时,Flink 会优雅地将数据落盘。
如果遇到从 TaskExecutorProcessUtils 或 JobManagerProcessUtils 抛出的 IllegalConfigurationException 异常,这通常说明您的配置参数中存在无效值(例如内存大小为负数、占比大于 1 等)或者配置冲突。请根据异常信息,确认出错的内存部分的相关文档及配置信息。
该异常说明 JVM 的堆空间过小。可以通过增大总内存、TaskManager 的任务堆内存、JobManager 的 JVM 堆内存等方法来增大 JVM 堆空间。
提示 也可以增大 TaskManager 的框架堆内存。 这是一个进阶配置,只有在确认是 Flink 框架自身需要更多内存时才应该去调整。
该异常通常说明 JVM 的直接内存限制过小,或者存在直接内存泄漏(Direct Memory Leak)。 请确认用户代码及外部依赖中是否使用了 JVM 直接内存,以及如果使用了直接内存,是否配置了足够的内存空间。可以通过调整堆外内存来增大直接内存限制。有关堆外内存的配置方法,请参考 TaskManager、JobManager 以及 JVM 参数的相关文档。
该异常说明 JVM Metaspace 限制过小。可以尝试调整 TaskManager、JobManager 的 JVM Metaspace。
该异常仅与 TaskManager 相关。
该异常通常说明网络内存过小。可以通过调整以下配置参数增大网络内存:
taskmanager.memory.network.min
taskmanager.memory.network.max
taskmanager.memory.network.fraction
如果 Flink 容器尝试分配超过其申请大小的内存(Yarn 或 Kubernetes),这通常说明 Flink 没有预留出足够的本地内存。可以通过外部监控系统或者容器被部署环境杀掉时的错误信息判断是否存在容器内存超用。
对于 JobManager 进程,你还可以尝试启用 JVM 直接内存限制(jobmanager.memory.enable-jvm-direct-memory-limit
),以排除 JVM 直接内存泄漏的可能性。
如果使用了 RocksDBStateBackend :
glibc
内存收集器而出现,具体请查看 glibc bug 。你可以尝试给 TaskManager 增加环境变量 MALLOC_ARENA_MAX=1
。此外,还可以尝试增大 JVM 开销。
请参考如何配置容器内存。
在 1.10 和 1.11 版本中,Flink 分别对 TaskManager 和 JobManager 的内存配置方法做出了较大的改变。部分配置参数被移除了,或是语义上发生了变化。本篇升级指南将介绍如何将 Flink 1.9 及以前版本的 TaskManager 内存配置升级到 Flink 1.10 及以后版本, 以及如何将 Flink 1.10 及以前版本的 JobManager 内存配置升级到 Flink 1.11 及以后版本。
注意: 请仔细阅读本篇升级指南。使用原本的和新的内存配制方法可能会使内存组成部分具有截然不同的大小。未经调整直接沿用 Flink 1.10 以前版本的 TaskManager 配置文件或 Flink 1.11 以前版本的 JobManager 配置文件,可能导致应用的行为、性能发生变化,甚至造成应用执行失败。
提示 在 1.10/1.11 版本之前,Flink 不要求用户一定要配置 TaskManager/JobManager 内存相关的参数,因为这些参数都具有默认值。新的内存配置要求用户至少指定下列配置参数(或参数组合)的其中之一,否则 Flink 将无法启动。
| TaskManager: | JobManager: |
| :----------------------------------------------------------* | :----------------------------------------------------------* |
| taskmanager.memory.flink.size
| jobmanager.memory.flink.size
|
| taskmanager.memory.process.size
| jobmanager.memory.process.size
|
| taskmanager.memory.task.heap.size
和 taskmanager.memory.managed.size
| jobmanager.memory.heap.size
|
Flink 自带的默认 flink-conf.yaml 文件指定了 taskmanager.memory.process.size
(>= 1.10)和 jobmanager.memory.process.size
(>= 1.11),以便与此前的行为保持一致。
可以使用这张电子表格来估算和比较原本的和新的内存配置下的计算结果。
本节简要列出了 Flink 1.10 引入的配置参数变化,并援引其他章节中关于如何升级到新配置参数的相关描述。
下列配置参数已被彻底移除,配置它们将不会产生任何效果。
| 移除的配置参数 | 备注 |
| :----------------------------* | :----------------------------------------------------------* |
| taskmanager.memory.fraction | 请参考新配置参数 taskmanager-memory-managed-fraction 的相关描述。新的配置参数与被移除的配置参数在语义上有所差别,因此其配置值通常也需要做出适当调整。请参考如何升级托管内存。 |
| taskmanager.memory.off-heap | Flink 不再支持堆上的(On-Heap)托管内存。请参考如何升级托管内存。 |
| taskmanager.memory.preallocate | Flink 不再支持内存预分配,今后托管内存将都是惰性分配的。请参考如何升级托管内存。 |
下列配置参数将被弃用,出于向后兼容性考虑,配置它们将被解读成对应的新配置参数。
| 弃用的配置参数 | 对应的新配置参数 |
| :---------------------------------* | :----------------------------------------------------------* |
| taskmanager.heap.size | 独立部署模式(Standalone Deployment)下:taskmanager-memory-flink-size
容器化部署模式(Containerized Deployement)下:taskmanager.memory.process.size
请参考如何升级总内存。 |
| taskmanager.memory.size | taskmanager-memory-managed-size。请参考如何升级托管内存。 |
| taskmanager.network.memory.min | taskmanager-memory-network-min |
| taskmanager.network.memory.max | taskmanager-memory-network-max |
| taskmanager.network.memory.fraction | taskmanager-memory-network-fraction |
尽管网络内存的配置参数没有发生太多变化,我们仍建议您检查其配置结果。网络内存的大小可能会受到其他内存部分大小变化的影响,例如总内存变化时,根据占比计算出的网络内存也可能发生变化。请参考内存模型详解。
容器切除(Cut-Off)内存相关的配置参数(containerized.heap-cutoff-ratio
和 containerized.heap-cutoff-min
)将不再对 TaskManager 进程生效。请参考如何升级容器切除内存。
在原本的内存配置方法中,用于指定用于 Flink 的总内存的配置参数是 taskmanager.heap.size
或 taskmanager.heap.mb
。尽管这两个参数以“堆(Heap)”命名,实际上它们指定的内存既包含了 JVM 堆内存,也包含了其他堆外内存部分。这两个配置参数目前已被弃用。
如果配置了上述弃用的参数,同时又没有配置与之对应的新配置参数,那它们将按如下规则对应到新的配置参数。
taskmanager.memory.flink.size
)taskmanager.memory.process.size
)建议您尽早使用新的配置参数取代启用的配置参数,它们在今后的版本中可能会被彻底移除。
请参考如何配置总内存.
此前,JVM 堆空间由托管内存(仅在配置为堆上时)及 Flink 用到的所有其他堆内存组成。这里的其他堆内存是由总内存减去所有其他非堆内存得到的。请参考如何升级托管内存。
现在,如果仅配置了Flink总内存或进程总内存,JVM 的堆空间依然是根据总内存减去所有其他非堆内存得到的。请参考如何配置总内存。
此外,你现在可以更直接地控制用于任务和算子的 JVM 的堆内存(taskmanager.memory.task.heap.size
),详见任务堆内存。如果流处理作业选择使用 Heap State Backend(MemoryStateBackend 或 FsStateBackend),那么它同样需要使用 JVM 堆内存。
Flink 现在总是会预留一部分 JVM 堆内存供框架使用(taskmanager.memory.framework.heap.size
)。请参考框架内存。
请参考如何配置托管内存。
原本用于指定明确的托管内存大小的配置参数(taskmanager.memory.size
)已被弃用,与它具有相同语义的新配置参数为 taskmanager.memory.managed.size
。 建议使用新的配置参数,原本的配置参数在今后的版本中可能会被彻底移除。
此前,如果不指定明确的大小,也可以将托管内存配置为占用总内存减去网络内存和容器切除内存(仅在 Yarn 上)之后剩余部分的固定比例(taskmanager.memory.fraction
)。 该配置参数已经被彻底移除,配置它不会产生任何效果。请使用新的配置参数 taskmanager.memory.managed.fraction
。在未通过 taskmanager.memory.managed.size
指定明确大小的情况下,新的配置参数将指定托管内存在 Flink 总内存中的所占比例。
流处理作业如果选择使用 RocksDBStateBackend,它使用的本地内存现在也被归为托管内存。默认情况下,RocksDB 将限制其内存用量不超过托管内存大小,以避免在 Yarn 上的容器被杀。你也可以通过设置 state.backend.rocksdb.memory.managed 来关闭 RocksDB 的内存控制。请参考如何升级容器切除内存。
此外,Flink 1.10 对托管内存还引入了下列变化:
taskmanager.memory.off-heap
已被彻底移除,配置它不会产生任何效果。taskmanager.memory.preallocate
已被彻底移除,配置它不会产生任何效果。在原本的内存配置方法中,用于指定 JVM 堆内存 的配置参数是:
jobmanager.heap.size
jobmanager.heap.mb
尽管这两个参数以“堆(Heap)”命名,在此之前它们实际上只有在独立部署模式才完全对应于 JVM 堆内存。在容器化部署模式下(Kubernetes 和 Yarn),它们指定的内存还包含了其他堆外内存部分。JVM 堆空间的实际大小,是参数指定的大小减去容器切除(Cut-Off)内存后剩余的部分。 容器切除内存在 1.11 及以上版本中已被彻底移除。
这两个配置参数目前已被弃用。如果配置了上述弃用的参数,同时又没有配置与之对应的新配置参数,那它们将按如下规则对应到新的配置参数。
jobmanager.memory.heap.size
)jobmanager.memory.process.size
)建议您尽早使用新的配置参数取代启用的配置参数,它们在今后的版本中可能会被彻底移除。
如果仅配置了 Flink 总内存或进程总内存,那么 JVM 堆内存将是总内存减去其他内存部分后剩余的部分。请参考如何配置总内存。此外,也可以通过配置 jobmanager.memory.heap.size
的方式直接指定 JVM 堆内存。
从 1.10 版本开始,Flink 通过设置相应的 JVM 参数,对 TaskManager 进程使用的 JVM Metaspace 和 JVM 直接内存进行限制。从 1.11 版本开始,Flink 同样对 JobManager 进程使用的 JVM Metaspace 进行限制。此外,还可以通过设置 jobmanager.memory.enable-jvm-direct-memory-limit
对 JobManager 进程的 JVM 直接内存进行限制。请参考 JVM 参数。
Flink 通过设置上述 JVM 内存限制降低内存泄漏问题的排查难度,以避免出现容器内存溢出等问题。请参考常见问题中关于 JVM Metaspace 和 JVM 直接内存 OutOfMemoryError 异常的描述。
在容器化部署模式(Containerized Deployment)下,此前你可以指定切除内存。这部分内存将预留给所有未被 Flink 计算在内的内存开销。其主要来源是不受 Flink 直接管理的依赖使用的内存,例如 RocksDB、JVM 内部开销等。相应的配置参数(containerized.heap-cutoff-ratio
和 containerized.heap-cutoff-min
)不再生效。新的内存配置方法引入了新的内存组成部分来具体描述这些内存用量。
流处理作业如果使用了 RocksDBStateBackend,RocksDB 使用的本地内存现在将被归为托管内存。默认情况下,RocksDB 将限制其内存用量不超过托管内存大小。请同时参考如何升级托管内存以及如何配置托管内存。
其他堆外(直接或本地)内存开销,现在可以通过下列配置参数进行设置:
taskmanager.memory.task.off-heap.size
)taskmanager.memory.framework.off-heap.size
)taskmanager.memory.jvm-metaspace.size
)可以通过下列配置参数设置堆外(直接或本地)内存开销:
jobmanager.memory.off-heap.size
)jobmanager.memory.jvm-metaspace.size
)本节描述 Flink 自带的默认 flink-conf.yaml
文件中的变化。
原本的 TaskManager 总内存(taskmanager.heap.size
)被新的配置项 taskmanager.memory.process.size
所取代。默认值从 1024Mb 增加到了 1728Mb。
原本的 JobManager 总内存(jobmanager.heap.size
)被新的配置项 jobmanager.memory.process.size
所取代。默认值从 1024Mb 增加到了 1600Mb。
请参考如何配置总内存。
**注意:**使用新的默认 flink-conf.yaml
可能会造成各内存部分的大小发生变化,从而产生性能变化。
Flink 中每条消息都会被放到网络缓冲(network buffer) 中,并以此为最小单位发送到下一个 subtask。为了维持连续的高吞吐,Flink 在传输过程的输入端和输出端使用了网络缓冲队列。
每个 subtask 都有一个输入队列来接收数据和一个输出队列来发送数据到下一个 subtask。 在 pipeline 场景,拥有更多的中间缓存数据可以让 Flink 提供更高、更富有弹性的吞吐量,但也会增加快照时间。
只有所有的 subtask 都收到了全部注入的 checkpoint barrier 才能完成快照。在对齐的 checkpoints 中,checkpoint barrier 会跟着网络缓冲数据在 job graph 中流动。缓冲数据越多,checkpoint barrier 流动的时间就越长。在非对齐的 checkpoints 中,缓冲数据越多,checkpoint 就会越大,因为这些数据都会被持久化到 checkpoint 中。
之前,配置缓冲数据量的唯一方法是指定缓冲区的数量和大小。但是因为每次都会进行不同的部署,所以很难配置一组完美的参数。Flink 1.14 新引入的缓冲消胀机制尝试会通过自动调整缓冲数据量到一个合理值来解决这个问题。
缓冲消胀功能会计算 subtask 可能达到的最大吞吐(始终保持繁忙状态时)并且通过调整缓冲数据量来使得数据的消费时间达到配置值。
可以通过设置 taskmanager.network.memory.buffer-debloat.enabled
为 true
来开启缓冲消胀机制。通过设置 taskmanager.network.memory.buffer-debloat.target
为 duration
类型的值来指定消费缓冲数据的目标时间。默认值应该能满足大多数场景。
这个功能使用过去的吞吐数据来预测消费剩余缓冲数据的时间。如果预测不准,缓冲消胀机制会导致以下问题:
如果您的作业负载经常变化(即,突如其来的数据尖峰,定期的窗口聚合触发或者 join ),您可能需要调整以下设置:
taskmanager.network.memory.buffer-debloat.period
:这是缓冲区大小重算的最小时间周期。周期越小,缓冲消胀机制的反应时间就越快,但是必要的计算会消耗更多的CPU。taskmanager.network.memory.buffer-debloat.samples
:调整用于计算平均吞吐量的采样数。采集样本的频率可以通过 taskmanager.network.memory.buffer-debloat.period
来设置。样本数越少,缓冲消胀机制的反应时间就越快,但是当吞吐量突然飙升或者下降时,缓冲消胀机制计算的最佳缓冲数据量会更容易出错。taskmanager.network.memory.buffer-debloat.threshold-percentages
:防止缓冲区大小频繁改变的优化(比如,新的大小跟旧的大小相差不大)。更多详细和额外的参数配置,请参考配置参数。
您可以使用以下指标来监控当前的缓冲区大小:
estimatedTimeToConsumeBuffersMs
:消费所有输入通道(input channel)中数据的总时间。debloatedBufferSize
:当前的缓冲区大小。目前,还有一些情况不会被缓冲消胀机制自动处理。
当前,吞吐计算和缓冲消胀发生在 subtask 层面。
如果您的 subtask 有很多不同的输入或者有一个合并(union)的输入,缓冲消胀可能会导致低吞吐的输入有太多缓冲数据,而高吞吐输入的缓冲区数量可能太少而不够维持当前吞吐。当不同的输入吞吐差别比较大时,这种现象会更加的明显。我们推荐您在测试这个功能时重点关注这种 subtask。
当前,缓冲消胀仅在使用的缓冲区大小上设置上限。实际的缓冲区大小和个数保持不变。这意味着缓冲消胀机制不会减少作业的内存使用。您应该手动减少缓冲区的大小或者个数。
此外,如果您想减少缓冲数据量使其低于缓冲消胀当前允许的量,您可能需要手动的设置缓冲区的个数。
目前,消除肿胀机制在遇到高并行度(超过 200)时,如果使用默认的配置,可能表现不会很好。如果你观察到吞吐量降低,或者是 checkpoint 时间超过了预期,我们建议你调大浮动缓存的数量(taskmanager.network.memory.floating-buffers-per-gate
),设置一个至少和并行度相等的数字。
导致问题发生的并行度数量因作业而异,但是并行度数量通常应该超过几百。
Flink 有多个本地缓冲区池 —— 每个输出和输入流对应一个。每个缓冲区池的大小被限制为:channels * taskmanager.network.memory.buffers-per-channel + taskmanager.network.memory.floating-buffers-per-gate
。
缓冲区的大小可以通过 taskmanager.memory.segment-size
来设置。
输入通道中的缓冲区被分为独占缓冲区(exclusive buffer)和浮动缓冲区(floating buffer)。每个独占缓冲区只能被一个特定的通道使用。一个通道可以从输入流的共享缓冲区池中申请额外的流动缓冲区。剩余的浮动缓冲区是可选的并且只有资源足够的时候才能获取。
在初始阶段:
不像输入缓冲区池,输出缓冲区池只有一种类型的缓冲区被所有的 subpartitions 共享。
为了避免过多的数据倾斜,每个 subpartition 的缓冲区数量可以通过 taskmanager.network.memory.max-buffers-per-channel
来限制。
不同于输入缓冲区池,这里配置的独占缓冲区和浮动缓冲区只被当作推荐值。如果没有足够的缓冲区,每个输出 subpartition 可以只使用一个独占缓冲区而没有浮动缓冲区。
对于每个输出子任务,可以最多请求 taskmanager.network.memory.max-overdraft-buffers-per-gate
(默认为 5)个额外的透支缓冲。这些缓冲只可被用于在子任务被下游子任务烦啊,并且子任务需要多于一个网络缓冲来完成当前的工作。这会在下面这些情况下发生:
在上面的这些情况下,如果没有透支缓冲,Flink 的子任务线程就会在反压情况下卡主,比如防止完成未对齐的 checkkpoint。为了缓和这种情况,增加了透支缓冲概念。透支缓冲是可选的,Flink 可以保证进程只使用常规缓冲,这意味着 taskmanager.network.memory.max-overdraft-buffers-per-gate
可以被配置为 0。
该特性只在
Pipelined Shuffle
下起作用。
独占缓冲区和浮动缓冲区的默认配置应该足以应对最大吞吐。如果想要最小化缓冲数据量,那么可以将独占缓冲区设置为 0
,同时减小内存段的大小。
在往下游 subtask 发送数据部分时,缓冲区通过汇集 record 来优化网络开销。下游 subtask 应该在接收到完整的 record 后才开始处理它。
如果缓冲区太小,或缓冲区被频繁刷写(通过 execution.buffer-timeout
参数配置),这会降低吞吐量,因为 在 Flink 运行时,每个记录的开销要比每个缓冲区的开销大得多。
根据经验,除非在实际作业负载中观察到网络瓶颈(下游算子空转、上游反压、输出缓冲区队列满、下游输入队列为空),我们不建议考虑增加缓冲区大小或缓冲区超时时间。
如果缓冲区太大,会导致:
execution.buffer-timeout
配置较小时内存分配使用率会比较低,因为缓冲区还没被塞满数据就被发送下去了。缓冲区的数量是通过 taskmanager.network.memory.buffers-per-channel
和 taskmanager.network.memory.floating-buffers-per-gate
来配置的。
为了最好的吞吐率,我们建议使用独占缓冲区和浮动缓冲区的默认值。如果缓冲数据量存在问题,更建议打开缓冲消胀。
您可以人工地调整网络缓冲区的个数,但是需要注意:
bytes/second
)来调整缓冲区的数量。协调数据传输量(大约两个节点之间的两个往返消息)。延迟也取决于您的网络。1ms
在正常的本地网络中),缓冲区大小和期待的吞吐,您可以通过下面的公式计算维持吞吐所需要的缓冲区数量:number_of_buffers = expected_throughput * buffer_roundtrip / buffer_size
320MB/s
,往返延迟为 1ms
,内存段为默认大小,为了维持吞吐需要使用10个活跃的缓冲区:number_of_buffers = 320MB/s * 1ms / 32KB = 10
当低吞吐量下出现反压时,您应该考虑减少独占缓冲区。
可以通过开启缓冲消胀机制来简化 Flink 网络的内存配置调整。您也可能需要调整它。
如果这不起作用,您可以关闭缓冲消胀机制并且人工地配置内存段的大小和缓冲区个数。针对第二种场景,我们推荐:
Flink 提供了一个命令行接口(CLI)来运行打包为 JAR 文件的程序,并且控制他们的执行。CLI 是 Flink 部署的一部分,在本地单节点部署和分布式部署中均可用。他会连接在 conf/flink-config.yaml
中指定的运行的 JobManager。
该章节中列出的命令工作的先决条件是有一个正在运行的 Flink 部署,比如 Kubernetes、YARN 或任何其他可用的部署。可以在你自己的机器上启动 Flink 本地集群来尝试这些命令。
提交一个作业意味着上传作业的 JAR 和相关的依赖到 Flink 集群,并且初始化作业执行。在这个案例中,我们选择了一个长时间运行的作业,比如:examples/streaming/StateMachineExample.jar
。可以选择 examples/
目录下的其他任何 JAR 归档文件或部署自己的作业来体验。
$ ./bin/flink run \
--detached \
./examples/streaming/StateMachineExample.jar
提交作业时使用 --detached
将会使作业提交完成后命令马上返回,命令行返回的输出包括新提交作业的 ID。
Usage with built-in data generator: StateMachineExample [--error-rate ] [--sleep ]
Usage with Kafka: StateMachineExample --kafka-topic [--brokers ]
Options for both the above setups:
[--backend ]
[--checkpoint-dir ]
[--async-checkpoints ]
[--incremental-checkpoints ]
[--output OR null for stdout]
Using standalone source with error rate 0.000000 and sleep delay 1 millis
Job has been submitted with JobID cca7bc1061d61cf15238e92312c2fc20
如果需要的话,可以在作业提交命令行后面增加上面打印出的作业相关参数。处于可读性目的,我们假设返回的 JobID 被存储为命令的变量 JOB_ID
:
$ export JOB_ID="cca7bc1061d61cf15238e92312c2fc20"
有一个名为 run-application
的操作可用于在 Application 模式下运行作业,本文章不单独描述该操作,因为他和 CLI 的 run
操作的工作原理相似。
run
和 run-application
支持通过 -D
配置额外的配置参数,比如通过 -Dpipeline.max-parallelism=120
来设置作业的最大并行度。该参数对于配置 applicatgion 模式集群非常有用,因为你可以给集群传递任何配置参数,而不修改配置文件。
当提交作业到已存在的 session 集群时,只支持执行配置参数。
你可以使用 list
操作监控任何运行的作业:
$ ./bin/flink list
Waiting for response...
-----------------* Running/Restarting Jobs -------------------
30.11.2020 16:02:29 : cca7bc1061d61cf15238e92312c2fc20 : State machine job (RUNNING)
--------------------------------------------------------------
No scheduled jobs.
已经被提交但还没有开始运行的作业,将会在“Scheduled Jobs”下列出。
可以创建 Savepoints 来保存作业的当前状态,命令中只需要作业的 JobID:
$ ./bin/flink savepoint \
$JOB_ID \
/tmp/flink-savepoints
Triggering savepoint for job cca7bc1061d61cf15238e92312c2fc20.
Waiting for response...
Savepoint completed. Path: file:/tmp/flink-savepoints/savepoint-cca7bc-bb1e257f0dab
You can resume your program from this savepoint with the run command.
savepoint 目录是可选的,如果没有设置 state.savepoints.dir,则需要指定 savepoint 目录。
你也可以提供 savepoint 的 二进制格式 。
savepoint 的目录可以在之后重启 Flink 作业时使用。
savepoint
操作也可以被用于移除 savepoint。--dispose
需要指定相对应的 savepoint 路径:
$ ./bin/flink savepoint \
--dispose \
/tmp/flink-savepoints/savepoint-cca7bc-bb1e257f0dab \
$JOB_ID
Disposing savepoint '/tmp/flink-savepoints/savepoint-cca7bc-bb1e257f0dab'.
Waiting for response...
Savepoint '/tmp/flink-savepoints/savepoint-cca7bc-bb1e257f0dab' disposed.
如果你使用了自定义的状态实例,比如自定义状态或 RocksDB 状态,你需要指定触发 savepoint 对应程序的 JAR 路径,否则会报 ClassNotFoundException
:
$ ./bin/flink savepoint \
--dispose <savepointPath> \
--jarfile <jarFile>
通过 savepoint
操作触发 savepoint 清理不仅会从存储中移除数据,并且会让 Flink 清除 savepoint 相关的元数据。
另一个停止作业的操作为 stop
,这是一个从 source 到 sink 停止正在运行作业更优雅的方式。当用户要停止作业时,会要求所有的 source 发送最新的 checkpoint barrier,并触发 savepoint,在完成 savepoint 后,会通过调用他们的 cancel()
方法完成停止操作。
$ ./bin/flink stop \
--savepointPath /tmp-flink-savepoints \
$JOB_ID
Suspending job "cca7bc1061d61cf15238e92312c2fc20" with a savepoint.
Savepoint completed. Path: file:/tmp/flink-savepoints/savepoint-cca7bc-bb1e257f0dab
如果没有配置 state.savepoints.dir,则需要通过 --savepointPath
来指定 savepoint 目录。
如果指定了 --drain
配置,将会在最新 checkpoint barrier 之前发射 MAX_WATERMARK
,这会导致所有被注册的事件时间定时器触发,然后清除正在等待指定水印的任何状态,比如窗口。作业会一直保持运行,直到所有的 source 完全停止,该行为会让作业处理完所有正在传输的数据。
如果你想永久的停止作业,可以使用
--drain
配置。如果以后你想恢复作业,就不要 drain pipeline 了,因为这会导致作业恢复时产生错误的结果。
最后,你可以选择提供 savepoint 使用那种 二进制格式 。
可以通过 cancel
操作来取消作业:
$ ./bin/flink cancel $JOB_ID
Cancelling job cca7bc1061d61cf15238e92312c2fc20.
Cancelled job cca7bc1061d61cf15238e92312c2fc20.
对应的作业状态将会从 running
变成 cancelled
,所有的计算都会停止。
--withSavepoint
配置允许创建一个 savepoing 为作业取消的一部分,该特性已经过时,请使用 stop 操作来代替。
可以通过 run
和 run-application
操作从 savepoint 启动一个作业。
$ ./bin/flink run \
--detached \
--fromSavepoint /tmp/flink-savepoints/savepoint-cca7bc-bb1e257f0dab \
./examples/streaming/StateMachineExample.jar
Usage with built-in data generator: StateMachineExample [--error-rate ] [--sleep ]
Usage with Kafka: StateMachineExample --kafka-topic [--brokers ]
Options for both the above setups:
[--backend ]
[--checkpoint-dir ]
[--async-checkpoints ]
[--incremental-checkpoints ]
[--output OR null for stdout]
Using standalone source with error rate 0.000000 and sleep delay 1 millis
Job has been submitted with JobID 97b20a0a8ffd5c1d656328b0cd6436a6
该命令和初始 run 命令是一样的,但是额外指定了 --fromSavepoint
参数,该参数用来指定要从哪个之前停止的作业状态进行恢复。之后会生成一个新的 JobID 来维护新的作业。
默认情况下,我们会尝试匹配整个 savepoint 状态到提交的作业,如果你想允许新作业跳过无法从 savepoint 状态恢复的算子,可以使用 --allowNonRestoredState
配置。在你移除了程序中一个算子之后,你还想使用之前触发的 savepoint 来恢复作业时,你需要指定该配置。
$ ./bin/flink run \
--fromSavepoint <savepointPath> \
--allowNonRestoredState ...
如果你的程序删除了属于 savepoint 一部分的某个算子时,这是非常有用的。
你也可以选择对 savepoint 使用哪个 恢复模式,恢复模式会控制谁有指定 savepoint 的获取权限。
下面是 Flink CLI 工具支持的操作:
| 操作 | 作用 |
| :---------------* | :----------------------------------------------------------* |
| run
| 该操作用来执行作业,他要求 jar 文件包含作业,可以通过它来传递 flink 或作业相关的参数。 |
| run-application
| 该操作会使用 Application Mode 执行作业,除此之外,他要求和 run
操作相同的参数。 |
| info
| 该操作可以被用来打印作业优化之后的执行图,同样要求 jar 包含作业。 |
| list
| 该操作会列出所有正在运行或被调度的作业。 |
| savepoint
| 该操作可以被用来根据给定的作业来创建或清除 savepoint,如果没有在 conf/flink-config.yaml
文件中指定 state.savepoints.dir 参数,该操作会要求除了 JobID 外还要指定一个 savepoint 目录。 |
| cancel
| 该操作会基于指定的 JobID 来取消正在运行的作业。 |
| stop
| 该操作合并了 cancel
和 savepoint
操作来停止正在运行的作业,并且会创建一个可以被用来重新启动的 savepoint。 |
可以通过 bin/flink --help
或bin/flink
命令来获取所有操以及他们的参数更加详细的描述。
也可以通过 REST API 来管理 Flink 集群,前面章节中的命令描述是 Flink REST 终端的一部分,因此,可以使用类似 curl
之类的工具从 Flink 中获取更多信息。
Flink 兼容很多集群管理框架,比如 Kubernetes 或 YARN ,作业可以通过不同的 部署模式 提交,基于下面提到的框架和部署模式,作业的提交参数是不同的。
bin/flink
脚本提供了 --target
参数来处理不同的选项,除此之外,作业提交还必须使用 run
(for Session and Per-Job 模式) or run-application
(for Application 模式)。查看下面的参数组合总结:
./bin/flink run --target yarn-session
:提交作业到已经在 YARN 集群上运行的 Flink session 集群./bin/flink run --target yarn-per-job
:使用 Per-job 模式提交作业到 YARN 上./bin/flink run-application --target yarn-application
:使用 application 模式提交作业到 YARN 上./bin/flink run --target kubernetes-session
:提交作业到已经在 Kubernetes 集群上运行的 Flink session 集群./bin/flink run-application --target kubernetes-application
:使用 application 模式提交作业到 kubernetes 上./bin/flink run --target local
:使用 session 模式提交作业到本地 MiniCluster./bin/flink run --target remote
:提交作业到一个已经在运行的 Flink 集群--target
选项会覆盖在 config/flink-config.yaml
中指定的 execution.target 。
更多关于命令和可用选项的细节,请参考“资源提供者”章节。
目前,用户可以通过 CLI 来提交 PyFlink 作业,和 java 作业提交不同,该方式不要求指定 JAR 文件路径或主类完全限定名。
通过
flink run
提交 Python 作业时,Flink 会运行python
命令,请运行下面的命令来确认当前环境中的 python 版本为 3.6+。
$ python --version
# 打印的 python 版本必须是 3.6+
下面的命令展示不同的 PyFlink 作业提交用例:
$ ./bin/flink run --python examples/python/table/batch/word_count.py
--pyFiles
指定的文件将会被添加到 PYTHONPATH
,之后就可以在 Python 代码中使用对应的文件了。$ ./bin/flink run \
--python examples/python/table/batch/word_count.py \
--pyFiles file:///user.txt,hdfs:///$namenode_address/username.txt
--jarfile
指定的 JAR 文件将会被上传到集群。$ ./bin/flink run \
--python examples/python/table/batch/word_count.py \
--jarfile <jarFile>
--pyModule
指定的模块运行 PyFlink 作业:$ ./bin/flink run \
--pyModule batch.word_count \
--pyFiles examples/python/table
主机上运行的 JobManager:$ ./bin/flink run \
--jobmanager <jobmanagerHost>:8081 \
--python examples/python/table/batch/word_count.py
$ ./bin/flink run \
--target yarn-per-job
--python examples/python/table/batch/word_count.py
使用 YARN cluster in Application Mode 运行 PyFlink 作业:
$ ./bin/flink run-application
-t yarn-application \
-Djobmanager.memory.process.size=1024m \
-Dtaskmanager.memory.process.size=1024m \
-Dyarn.application.name=<ApplicationName> \
-Dyarn.ship-files=/path/to/shipfiles \
-pyarch shipfiles/venv.zip \
-pyclientexec venv.zip/venv/bin/python3 \
-pyexec venv.zip/venv/bin/python3 \
-py shipfiles/word_count.py
-pyfs shipfiles \
-pym word_count
注意:这意味着执行作业需要的 Python 依赖已经放到了 /path/to/shipfiles
(上面案例中的目录) 目录下,比如,上例中应该包含 venv.zip 和 word_count.py。
注意:当以 YARN application 模式在 JobManager 上执行作业时,-pyarch
和 -py
中指定的路径是相对于 shipfiles 的路径,shipfiles 是发送文件的目录名。
注意:通过 -pyarch
指定的存档文件将通过 blob 服务器分发到 TaskManager,文件大小限制为 2 GB。如果存档文件的大小超过 2 GB,可以将其上传到分布式文件系统,然后使用命令行选项 -pyarch
指定路径。
使用指定的集群 ID
在原生 Kubernetes 集群上运行 PyFlink 程序,要求安装 PyFlink 的 docker 镜像,请参考 在 docker 中启用 PyFlink。
$ ./bin/flink run-application \
--target kubernetes-application \
--parallelism 8 \
-Dkubernetes.cluster-id=<ClusterId> \
-Dtaskmanager.memory.process.size=4096m \
-Dkubernetes.taskmanager.cpu=2 \
-Dtaskmanager.numberOfTaskSlots=4 \
-Dkubernetes.container.image=<PyFlinkImageName> \
--pyModule word_count \
--pyFiles /opt/flink/examples/python/table/batch/word_count.py
更多可用选项,请参考 Kubernetes 或 YARN,他们描述了更多的细节。
除了上面提到的 --pyFiles
、 --pyModule
和 --python
选项之外,还有一些和 Python 相关的选项。下面 Flink CLI 工具支持的 Python 的 run
和 run-application
操作相关的选项:
| 选项 | 描述 |
| :----------------------------------* | :----------------------------------------------------------* |
| -py,--python
| 程序入口对应的 Python 脚本文件。可以使用 --pyFiles
选项配置依赖的资源。 |
| -pym,--pyModule
| 程序入口对应的 Python 模块。该选项必须和 --pyFiles
组合使用。 |
| -pyfs,--pyFiles
| 给作业附加自定义文件。支持标准的资源文件后缀,比如:.py/.egg/.zip/.whl,也支持目录。这些文件将会被添加到本地客户端和远程 python UDF 工作节点的 PYTHONPATH。以 .zip 为后缀的文件将会被提取出来并添加到 PYTHONPATH。符号“英文逗号”可以被用于多个文件之间的分隔符,比如:–pyFiles file:///tmp/myresource.zip,hdfs:///$namenode_address/myresource2.zip。 |
| -pyarch,--pyArchives
| 给作业添加归档文件。归档文件会被提取到 python UDF 工作节点的工作目录中。目前只支持 zip 格式。对于每个归档文件,都需要指定目标目录。如果指定了目标目录名称,归档文件将会被提取导指定的目录中。否则,归档文件将会被提取导同一个目录中。通过该选项上传的文件可以通过相对目录进行访问。‘#’ 可以用于归档文件路径和目标目录名称的分隔符号。符号“英文逗号”可以被用于多个归档文件的分隔符号。该选项可以被用于上传虚拟环境,即 Python UDF 中使用的数据文件,比如:–pyArchives file:///tmp/py37.zip,file:///tmp/data.zip#data --pyExecutable py37.zip/py37/bin/python。数据文件可以通过 Python UDF 访问,比如:f = open(‘data/data.txt’, ‘r’)。 |
| -pyclientexec,--pyClientExecutable
| 通过 “flink run” 提交 Python 作业,或编译包含 Python UDF 的 Java/Scala 作业时使用的 Python 解释器路径。比如:--pyArchives file:///tmp/py37.zip --pyClientExecutable py37.zip/py37/python
|
| -pyexec,--pyExecutable
| 指定用于执行 Python UDF 工作节点的 python 解释器路径,比如:–pyExecutable /usr/local/bin/python3。python UDF 工作依赖于 Python 3.6+,Apache Beam(version == 2.27.0), Pip (version >= 7.1.0) 和 SetupTools (version >= 37.0.0)。请确保指定的环境满足上面的要求。 |
| -pyreq,--pyRequirements
| 指定定义第三方依赖的 requirements.txt 文件。这些依赖将会被安装并添加到 python UDF 工作节点的 PYTHONPATH。指定包含这些依赖的安装包目录是可选操作。如果可选的参数已经存在,可以使用 ‘#’ 作为分隔符号,比如:–pyRequirements file:///tmp/requirements.txt#file:///tmp/cached_dir。 |
除了提交作业的命令行选项,也支持通过配置或代码中的 python API 来指定依赖。请参考依赖管理来获取更多细节。
Apache Flink 允许你弹性伸缩你的作业。你可以手动停止你的作业,然后配置不同的并行度,并且使用之前停止作业时创建的 savepoint 来重新启动你的作业。
该章节描述 Flink 自动调整并行度的相关选项配置。
响应式模式是一个 MVP(minimum viable product)特性。Flink 社区会积极的查找用户通过邮箱发送的反馈。请参考该章节中的限制列表。
响应式模式会配置作业,以让作业使用集群所有可用的资源。增加一个 TaskManager 将会提升作业性能,移除资源将会降低作业性能。Flink 会管理作业的并行度,通常设置并行度为最大可能的值。
响应式模式会在遇到调整事件时使用最新完成的 checkpoint 重启作业。这意味着不需要花费创建 savepoint 的开销,savepoint 通常被用于手动调整作业。同时,调整后需要处理的数据数量取决于 checkpoint 间隔,恢复时间取决于状态大小。
响应式模式通过服务监控指标,比如消费延迟,平均 CPU 使用率,吞吐量或延迟,允许 Flink 用户实现更强大的自动伸缩机制。只要这些指标高于或低于某个确定的阈值,就可以增加额外的 TaskManager 或从 Flink 集群中移除 TaskManager。可以通过改变 Kubernetges 部署的 replica factor ,或 AWS 的 autoscaling group 来实现。外部服务只需要处理资源收集和释放。Flink 只关心保持作业运行的可用资源。
如果你只想尝试我们的响应式模式,请参考下面的说明。他们假设你在单个机器上部署 Flink。
# 这些说明假设你在 Flink 分布式部署的根目录下
# 将作业 jar 包放到 lib/ 目录下
cp ./examples/streaming/TopSpeedWindowing.jar lib/
# 使用响应模式提交作业
./bin/standalone-job.sh start -Dscheduler-mode=reactive -Dexecution.checkpointing.interval="10s" -j org.apache.flink.streaming.examples.windowing.TopSpeedWindowing
# 启动第一个 TaskManager
./bin/taskmanager.sh start
让我们快速检查一下使用的提交命令:
./bin/standalone-job.sh start
:使用 Application Mode 部署 Flink。-Dscheduler-mode=reactive
:启用响应式模式。-Dexecution.checkpointing.interval="10s"
:配置 checkpoint。现在你已经通过响应式模式启动了一个 Flink 作业。web interface 展示了作业正在一个 TaskManager 上运行。如果你想扩展作业资源,给集群简单的增加一个其他的 TaskManager 即可:
# 启动一个额外的 TaskManager
./bin/taskmanager.sh start
移除一个 TaskManager 实例:
# 移除一个 TaskManager
./bin/taskmanager.sh stop
为了启用响应式模式,你需要配置 scheduler-mode
为 reactive
。
调度器会决定单个算子的并行度,他是不可配置的,通过单独指定单个算子或整个作业的并行度配置将会被忽略。
影响并行度的唯一方式是设置算子的最大并行度,这会被调度器认可。最大并行度上线是 2^15(32768)。如果没有对单个算子或整个作业设置最大并行度,将会采用默认并行度规则,可能会接受下限而不是最大的可能值。使用默认的调度模式,请参考并行度最佳实践。
注意,一个较高的最大并行度值设置,可能会影响作业的性能,因为保持 Flink 一些内部结构需要更多的资源。
当启用响应式模式时,jobmanager.adaptive-scheduler.resource-wait-timeout
配置将会被默认设置为 -1
,这意味着 JobManager 将会永远运行,等待足够的资源。如果你希望 JobManager 在等待有足够的 TaskManager 来运行作业时有一个确定的超时时间以停止 JobManager,请配置 jobmanager.adaptive-scheduler.resource-wait-timeout
.
启用响应式模式后,jobmanager.adaptive-scheduler.resource-stabilization-timeout
配置将会被默认设置为 0
:一旦有足够的可用资源,Flink 将会开始运行作业。如果所有的 TaskManager 没有同时连接,而是一个接着一个缓慢的连接,每次连接上一个 TaskManager,都会导致作业重启。如果你想在调度作业之前等待资源稳定,则可以增加该配置值。另外,还有配置 jobmanager.adaptive-scheduler.min-parallelism-increase
:该配置选项指定在触发资源扩展之前的聚合算子并行度增加的最小值。比如,你的作业配置为:source 并行度为 2,sink 并行度为 2,聚合算子并行度为 4,默认情况下,该配置值为 1,因此增加聚合算子并行度将会触发重启。
给有状态作业配置周期性 checkpoint:响应式模式会在遇到调整事件后从最新完成的 checkpoint 重启作业。如果没有启用周期性的 checkpoint,程序将会丢失他的状态。checkpoint 也需要配置重启策略。响应式模式将会尊重配置的重启策略:如果没有配置重启策略,响应式模式会使作业失败,而不是调整它。
响应式模式下的缩小资源可能会导致很长的暂停时间,因为 Flink 会等待 JobManager 和停止的 TaskManager 之间的心跳超时。你将会看到你的 Flink 作业使用一个较小的并行度重新部署之前卡主大概 50 秒。
默认超时时间配置为 50 秒,可以通过 heartbeat.timeout
配置将其设置为一个更低的值。设置心跳超时为一个更低的值,可能会导致 TaskManager 在恢复心跳时失败,比如由于网络波动或长时间的 GC 暂停。注意:heartbeat.interval
通常需要低于超时时间 heartbeat.timeout
。
因为响应式模式是一个新的实验性的特性,所以默认的调度器支持所有的特性在响应式模式中并不都是可用的。Flink 社区正在努力解决这些限制。
只支持 standalone application 部署。资源提供者,比如原生 Kubernetes、YARN,都不支持。standalone session 集群也不支持。application 部署仅限于单个作业程序。
只支持 Standalone in Application Mode 、Docker in Application Mode 和 Standalone Kubernetes Application Cluster 部署,
自适应调度器的局限性也适用于响应式模式。
直接使用自适应调度器,只可用于高级用户,因为没有定义在一个 session 集群上运行多个作业的 slot 收集。
自使用调度器可以根据可用 slot 数量来调整作业的并行度。如果没有足够可用的 slot 满足原始配置的并行度来运行作业,他将会自动减小作业的并行度,比如提交作业时没有足够的资源可用,或在作业运行期间 TaskManager 停止运行。如果新的 slot 可用,则作业将会再次被调整,以达到配置的并行度。在响应式模式中,配置的并行度将会被忽略,并且认为它为无限大,这会导致作业尽可能的使用更多的资源。你也可以在响应式模式中使用自适应调度器,但有一些限制:
自适应调度器相比于默认调度器的一个好处是它可以优雅的处理 TaskManager 丢失,因为它可以在 TaskManager 丢失时进行资源降级。
需要设置下面的配置参数:
jobmanager.scheduler: adaptive
:将默认调度器改为自适应调度器。cluster.declarative-resource-management.enabled
:启用声明式资源管理,默认启用。通过 adaptive-scheduler
包含的所有配置选项中的名称来配置自适应调度器的行为。
自适应批调度器可以自动决定批作业算子的并行度,如果算子没有设置并行度,调度器会根据它消费的数据集大小来决定他的并行度,这会带来很多好处:
为了让自适应批调度器自动决定算子的并行度,你需要:
-1
。为了使用自适应批调度器,你需要:
jobmanager.scheduler: AdaptiveBatch
。execution.batch-shuffle-mode
,或显式设置它的值为 ALL-EXCHANGES-BLOCKING
(默认值),下面的限制章节中说明了为什么要这么设置。另外,使用自适应批调度器时,也有一些相关的配置参数需要调整:
jobmanager.adaptive-batch-scheduler.min-parallelism
:允许设置的并行度下限。目前,该选项应该被设置为 2 的幂次方,如果不是 2 的幂次方,调度器也会自动取大于对应值的 2 的幂次方的数。jobmanager.adaptive-batch-scheduler.max-parallelism
:允许设置的并行度上限。目前,该选项应该被设置为 2 的幂次方,如果不是 2 的幂次方,调度器也会自动取大于对应值的 2 的幂次方的数。jobmanager.adaptive-batch-scheduler.avg-data-volume-per-task
:期望每个任务示例处理的数据卷平均大小。注意,由于并行度的值会被调整为 2 的幂次方,所以真实的平均值将会是该值的 0.75~1.5 倍。同时也要注意数据倾斜,或者由于太大的数据量而导致并行度达到设置的上限,有些任务的处理的真实数据量会远超过该值。jobmanager.adaptive-batch-scheduler.default-source-parallelism
:source 算子的默认并行度。-1
自适应批调度器只会决定没有被用户特殊指定的算子的并行度,这些算子并行度为 -1,所以,如果你想让调度器自动决定算子的并行度,你需要设置如下配置:
parallelism.default: -1
table.exec.resource.default-parallelism: -1
setParallelism()
方法StreamExecutionEnvironment/ExecutionEnvironment
的 setParallelism()
方法taskmanager.network.memory.buffers-per-channel
为 0
,这可以减少并行实例要求的网络内存,对于大作业来说,“不充足的网络缓存数(Insufficient number of network buffers)”可能会很少发生。jobmanager.adaptive-batch-scheduler.max-parallelism
值设置为在最坏情况下的你期望的并行度,不建议设置高于期望的值,因为过大的值可能会影响性能。该选项会影响上游任务产生的子分区数量,过多的子分区数量可能会降低 hash shuffle 的性能,也会由于过小的数据包影响网络传输性能。ALL-EXCHANGES-BLOCKING
的作业。jobmanager.adaptive-batch-scheduler.max-parallelism
选项应该被设置为 2 的幂次方(2^N),并且决定的并行度也会是 2 的幂次方 (2^M and M <= N)。StreamExecutionEnvironment#readFile(...)
、StreamExecutionEnvironment#readTextFile(...)
和 StreamExecutionEnvironment#createInput(FileInputFormat, ...)
。当使用自适应批调度器时,用户应该使用新的 source(FileSystem DataStream Connector 或FileSystem SQL Connector) 来读取文件。Apache Flink 会努力自动满足所有程序默认的资源需求,对于希望微调他们的资源消耗的用户,基于对于他们特殊的场景,Flink 提供了细粒度资源管理。
该章节描述细粒度资源管理的使用,适用场景,以及他们如何工作。
注:该特性目前是一个 MVP (minimum viable product) 特性,并且是能在 DataStream API 中使用。
可以从细粒度资源管理受益的典型场景如下:
在如何提高资源效率中有关于为什么细粒度资源管理可以提高上述场景的资源效率的深入讨论。
如 Flink Architecture 中的描述,在一个 TaskManager 中任务执行需要的资源会被切分到很多 slot 中。slot 是 Flink 运行时资源调度和资源需求的基本单位。
通过细粒度资源管理,用户可以指定包含特定资源配置文件的 slot 请求。Flink 会根据用户指定的资源需求,动态的从 TaskManager 可用的资源中取出一个完全匹配的 slot。如上图右图所示,对于需要 0.25 Core 和 1GB 内存的需求,Flink 会为它收集 slot1。
之前,资源需求只包含需要的 slot,没有叫做细粒度资源管理的细粒度的资源配置,TaskManager 有固定数量的 slot 来满足这些需求。
对于没有提供特定资源配置文件的资源需求,Flink 会自动确定资源配置文件。目前,资源配置通过 TaskManager 的总资源 和 taskmanager.numberOfTaskSlots 来计算。如上图左图所示,TaskManager 总资源是 1 Core 和 4GB 内存,任务 slot 为 2,如果没有特定的资源配置文件,则两个 slot 资源都是 0.5 Core 和 2GB 内存。
右图中,在分配完 slot1 和 slot2 之后,TaskManager 中将会保留 0.25 Core 和 1GB 内存的空闲资源,这些资源可以进一步分区,以满足之后的资源需求。
请参考 资源分配策略 来获取更多细节。
为了使用细粒度资源管理,你需要:
为了启用细粒度资源管理,你需要配置 cluster.fine-grained-resource-management.enabled 为 true
。
如果没有配置该参数,Flink 运行时将无法根据你特定的资源需求来调度 slot,并且作业会失败,并抛出异常。
细粒度资源管理定义在 slot 共享组上,一个 slot 共享组会告诉 TaskManager 将在 CAN 中的算子/任务放到同一个 slot 中。
为了指定资源需求,你需要:
有两种方式定义 slot 共享组以及它包含的算子:
SlotSharingGroup
实例,提供一个名字和一个可选的 slot 共享组资源配置。 SlotSharingGroup
可以通过 slotSharingGroup(SlotSharingGroup ssg)
来附加到一个算子上。你可以对你的 slot 共享组指定资源配置:
slotSharingGroup(SlotSharingGroup ssg)
来设置 slot 共享组,你可以在 SlotSharingGroup
实例的构造器总指定资源配置。StreamExecutionEnvironment#registerSlotSharingGroup(SlotSharingGroup ssg)
方法来注册他们的资源。Java
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
SlotSharingGroup ssgA = SlotSharingGroup.newBuilder("a")
.setCpuCores(1.0)
.setTaskHeapMemoryMB(100)
.build();
SlotSharingGroup ssgB = SlotSharingGroup.newBuilder("b")
.setCpuCores(0.5)
.setTaskHeapMemoryMB(100)
.build();
someStream.filter(...).slotSharingGroup("a") // 设置 slot 共享组名称为 “a”
.map(...).slotSharingGroup(ssgB); // 直接设置 slot 共享组的名称和资源
env.registerSlotSharingGroup(ssgA); // 然后注册共享组 “a” 的资源
Scala
val env = StreamExecutionEnvironment.getExecutionEnvironment
val ssgA = SlotSharingGroup.newBuilder("a")
.setCpuCores(1.0)
.setTaskHeapMemoryMB(100)
.build()
val ssgB = SlotSharingGroup.newBuilder("b")
.setCpuCores(0.5)
.setTaskHeapMemoryMB(100)
.build()
someStream.filter(...).slotSharingGroup("a") // 设置 slot 共享组名称为 “a”
.map(...).slotSharingGroup(ssgB) // 直接设置 slot 共享组的名称和资源
env.registerSlotSharingGroup(ssgA) // 然后注册共享组 “a” 的资源
Python
env = StreamExecutionEnvironment.get_execution_environment()
ssg_a = SlotSharingGroup.builder('a') \
.set_cpu_cores(1.0) \
.set_task_heap_memory_mb(100) \
.build()
ssg_b = SlotSharingGroup.builder('b') \
.set_cpu_cores(0.5) \
.set_task_heap_memory_mb(100) \
.build()
some_stream.filter(...).slot_sharing_group('a') # 设置 slot 共享组名称为 “a”
.map(...).slot_sharing_group(ssg_b) # 直接设置 slot 共享组的名称和资源
env.register_slot_sharing_group(ssg_a) # 然后注册共享组 “a” 的资源
注:每个 slot 共享组只能附加到一个指定的资源上,任何冲突都会导致你的作业编译失败。
在 SlotSharingGroup
构造器中,你可以对 slot 共享组设置下面的资源组件:
Java
// 直接在构建 slot 共享组时指定资源
SlotSharingGroup ssgWithResource =
SlotSharingGroup.newBuilder("ssg")
.setCpuCores(1.0) // required
.setTaskHeapMemoryMB(100) // required
.setTaskOffHeapMemoryMB(50)
.setManagedMemory(MemorySize.ofMebiBytes(200))
.setExternalResource("gpu", 1.0)
.build();
// 构建 slot 共享组时不指定资源,之后在 StreamExecutionEnvironment 中注册他的资源
SlotSharingGroup ssgWithName = SlotSharingGroup.newBuilder("ssg").build();
env.registerSlotSharingGroup(ssgWithResource);
Scala
// 直接在构建 slot 共享组时指定资源
val ssgWithResource =
SlotSharingGroup.newBuilder("ssg")
.setCpuCores(1.0) // required
.setTaskHeapMemoryMB(100) // required
.setTaskOffHeapMemoryMB(50)
.setManagedMemory(MemorySize.ofMebiBytes(200))
.setExternalResource("gpu", 1.0)
.build()
// 构建 slot 共享组时不指定资源,之后在 StreamExecutionEnvironment 中注册他的资源
val ssgWithName = SlotSharingGroup.newBuilder("ssg").build()
env.registerSlotSharingGroup(ssgWithResource)
Python
# 直接在构建 slot 共享组时指定资源
ssg_with_resource = SlotSharingGroup.builder('ssg') \
.set_cpu_cores(1.0) \
.set_task_heap_memory_mb(100) \
.set_task_off_heap_memory_mb(50) \
.set_managed_memory(MemorySize.of_mebi_bytes(200)) \
.set_external_resource('gpu', 1.0) \
.build()
# 构建 slot 共享组时不指定资源,之后在 StreamExecutionEnvironment 中注册他的资源
ssg_with_name = SlotSharingGroup.builder('ssg').build()
env.register_slot_sharing_group(ssg_with_resource)
注:你可以在构建 SlotSharingGroup 时指定或不指定他的资源配置。如果指定了资源配置,你需要显式设置 CPU 核心数 和 任务堆内内存 为一个正数,其他组件可选。
因为细粒度资源管理是一个新的实验性的特性,默认的调度器并不支持所有的特性,Flink 社区正在定位这些限制。
true
。注:这可能会影响性能,查看 FLINK-20865 来获取更多细节。在该章节中,我们会深度解析细粒度资源管理如何提高资源效率,帮助你理解它是如何让你的作业受益的。
在之前,Flink 采用了一个粗粒度的资源管理方式,任务会被部署到预定义的,完全相同的 slot 中,而且没有每个 slot 应该包含多少资源的概念。对于很多作业来说,使用粗粒度资源管理,简单的将所有任务放到 slot 共享组 ,在资源利用方面已经足够好了。
然而,在某些情况下,粗粒度资源管理效果不太好。
尝试在完全相同的 slot 上执行所有作业会导致不理想的资源利用。相同 slot 的资源必须满足最大的资源请求,这对于其他请求是浪费的。当涉及到昂贵的资源,比如 GPU 时,这些浪费会难以承受。在这些情况下,细粒度资源管理会让 不同的 slot 有不同的资源,从而提升资源利用。
在该章节中,我们会讨论在 Flink 运行时的 slot 分区机制和资源分配策略,包括 Flink 运行时如何选择一个 TaskManager 来切分 slot,以及在 原生 Kubernetes 和 YARN 上如何分配 TaskManager。Flink 运行时的资源分配策略是可插拔的,我们这里介绍的是细粒度资源管理默认实现中的第一步操作。在未来,可能会有多种策略供用户在不同的情况下使用。
如上面“执行细节”章节所述,Flink 会从 TaskManager 中切出一个正好符合指定资源的 slot,内部执行细节如上图所示,TaskManager 启动时会收集所有的资源,但是并没有预定义的 slot。当收到 0.25 Core 和 1GB 内存的 slot 请求时,Flink 会选择一个有足够空闲资源的 TaskManager,并创建一个满足资源请求的 slot。如果某个 slot 被释放,它将会释放它的资源给对应的 TaskManager。
在当前的资源分配策略中,Flink 会查看所有注册的 TaskManager,并查找第一个有足够空闲资源来满足 slot 请求的 TaskManager。当部署到 原生 Kubernetes 或 YARN 时,如果没有 TaskManager 有足够空闲的资源时,Flink 将会尝试分配一个新的 TaskManager。在当前策略中,Flink 会根据 用户的配置 分配完全一致的 TaskManager,TaskManager 的资源规则是预定义好的:
该文档描述预测执行的背景,使用方法,以及如何验证其有效性。
预测执行是一种用于缓解异常机器节点导致作业执行缓慢的机制。机器节点异常包括硬件异常,偶发的输入输出繁忙,高 CPU 负载等问题。这些问题会导致运行在其上的任务比起在其他节点上运行的任务慢很多,从而影响到整个作业的执行时长。
在这种情况下,预测执行会为这些慢任务创建一些新的执行实例并部署在正常的机器节点上。这些新的执行实例和其对应的老执行实例(慢任务) 会消费相同的数据,并产出相同的结果。而那些老执行实例也会被保留继续执行。这些执行实例(包括新实例和老实例)中首先成功结束的执行实例会被认可,其产出的结果会对下游任务可见,其他实例则会被取消掉。
为了实现这个机制,Flink 会通过一个慢任务检测器来检测慢任务。检测到的慢任务对应的机器节点会被识别为异常机器节点,并被加入机器节点黑名单中。调度器则会为这些慢节点创建新的执行实例,并将其部署到未被加黑的机器节点上。
本章节描述如何使用预测执行,包含如何启用,调优,以及开发/改进自定义 source 来支持预测执行。
注意: Flink 尚不支持 sink 的预测执行。这个能力会在后续版本中得到完善。
注意:Flink 不支持 DataSet 作业的预测执行,因为 DataSet API 在不久的将来会被废弃。现在推荐使用 DataStream API 来开发 Flink 批处理作业。
要启用预测执行,你需要设置以下配置项:
jobmanager.scheduler: AdaptiveBatch
jobmanager.adaptive-batch-scheduler.speculative.enabled: true
考虑到不同作业的差异,为了让预测执行获得更好的效果,你可以调优下列调度器配置项:
jobmanager.adaptive-batch-scheduler.speculative.max-concurrent-executions
jobmanager.adaptive-batch-scheduler.speculative.block-slow-node-duration
你还可以调优下列慢任务检测器的配置项:
slow-task-detector.check-interval
slow-task-detector.execution-time.baseline-lower-bound
slow-task-detector.execution-time.baseline-multiplier
slow-task-detector.execution-time.baseline-ratio
如果你的作业有用到自定义 Source , 并且这个 Source 用到了自定义的 SourceEvent , 你需要修改该 Source 的 SplitEnumerator 实现接口 SupportsHandleExecutionAttemptSourceEvent 。
public interface SupportsHandleExecutionAttemptSourceEvent {
void handleSourceEvent(int subtaskId, int attemptNumber, SourceEvent sourceEvent);
}
这意味着 SplitEnumerator 需要知道是哪个执行实例发出了这个事件。否则,JobManager 会在收到 SourceEvent 的时候报错从而导致作业失败。
除此之外的 Source 不需要额外的改动就可以进行预测执行,包括 SourceFunction Source , InputFormat Source , 和 新版 Source . Apache Flink 官方提供的 Source 都支持预测执行。
在启用预测执行后,当出现慢任务触发预测执行时,Web UI 会在作业页面的节点信息的 SubTasks
分页展示预测执行实例。Web UI 还会在 Overview
和 Task Managers
页面展示当前被加黑的 TaskManager。
你还可以通过检查这些 指标
来判断预测执行的有效性。
Apache Flink 使用文件系统来消费和持久化存储数据,以处理应用结果以及容错与恢复。以下是一些最常用的文件系统:本地存储,hadoop-compatible(HDFS),Amazon S3,MapR FS,阿里云 OSS 和 Azure Blob Storage。
文件使用的文件系统通过其 URI Scheme 指定。例如 file:///home/user/text.txt
表示一个在本地文件系统中的文件,hdfs://namenode:50010/data/user/text.txt
表示一个在指定 HDFS 集群中的文件。
文件系统在每个进程中实例化一次,然后进行缓存/池化,从而避免每次创建流时的配置开销,并强制执行特定的约束,如连接/流的限制。
Flink 原生支持本地机器上的文件系统,包括任何挂载到本地文件系统的 NFS 或 SAN 驱动器,默认开箱即用,无需额外配置。本地文件可通过 file://
URI Scheme 引用。
Apache Flink 支持下列文件系统:
flink-s3-fs-presto
和 flink-s3-fs-hadoop
两种替代实现提供支持。这两种实现都是独立的,没有依赖项。flink-oss-fs-hadoop
支持,并通过 oss:// URI scheme 使用。该实现基于 Hadoop Project,但其是独立的,没有依赖项。flink-azure-fs-hadoop
支持,并通过 adfs(s)://URI
schema 使用。该实现基于 Hadoop Project,但其是独立的,没有依赖项。flink-azure-fs-hadoop
支持,并通过 wasb(s)/ URI scheme 使用。该实现基于 Hadoop Project,但其是独立的,没有依赖项。gcs-connector
支持,并通过 gs:// URI scheme 使用。该实现基于 Hadoop Project,但其是独立的,没有依赖项。上述文件系统可以作为插件使用。
使用外部文件系统时,在启动 Flink 之前需将对应的 JAR 文件从 opt
目录复制到 Flink 发行版 plugin
目录下的某一文件夹中,例如:
mkdir ./plugins/s3-fs-hadoop
cp ./opt/flink-s3-fs-hadoop-1.16.0.jar ./plugins/s3-fs-hadoop/
注意 文件系统的插件机制在 Flink 版本 1.9 中引入,以支持每个插件专有的 Java 类加载器,并避免类隐藏机制。您仍然可以通过旧机制使用文件系统,即将对应的 JAR 文件复制到 lib
目录中,或使用您自己的实现方式,但是从版本 1.10 开始,S3 插件必须通过插件机制加载,因为这些插件不再被隐藏(版本 1.10 之后类不再被重定位),旧机制不再可用。
尽可能通过基于插件的加载机制使用支持的文件系统。未来的 Flink 版本将不再支持通过 lib
目录加载文件系统组件。
文件系统由类 org.apache.flink.core.fs.FileSystem
表示,该类定义了访问与修改文件系统中文件与对象的方法。
要添加一个新的文件系统:
org.apache.flink.core.fs.FileSystem
的子类。org.apache.flink.core.fs.FileSystemFactory
的子类。META-INF/services/org.apache.flink.core.fs.FileSystemFactory
,文件中包含文件系统 Factory 类的类名。(更多细节请查看 Java Service Loader docs)在插件检索时,文件系统 Factory 类会由一个专用的 Java 类加载器加载,从而避免与其他类或 Flink 组件冲突。在文件系统实例化和文件系统调用时,应使用该类加载器。
警告 实际上这表示您的实现应避免使用 Thread.currentThread().getContextClassLoader()
类加载器。
所有 Flink 无法找到直接支持的文件系统均将回退为 Hadoop。当 flink-runtime 和 Hadoop 类包含在 classpath 中时,所有的 Hadoop 文件系统将自动可用。
因此,Flink 无缝支持所有实现 org.apache.hadoop.fs.FileSystem
接口的 Hadoop 文件系统和所有兼容 Hadoop 的文件系统 (Hadoop-compatible file system, HDFS):
Hadoop 配置须在 core-site.xml
文件中包含所需文件系统的实现。可查看 Alluxio 的示例。
除非有其他的需要,建议使用 Flink 内置的文件系统。在某些情况下,如通过配置 Hadoop core-site.xml
中的 fs.defaultFS
属性将文件系统作为 YARN 的资源存储时,可能需要直接使用 Hadoop 文件系统。
在 core-site.xml
文件中添加以下条目以支持 Alluxio:
<property>
<name>fs.alluxio.implname>
<value>alluxio.hadoop.FileSystemvalue>
property>
Apache Flink 提供了一些对所有文件系统均适用的基本配置。
如果文件路径未明确指定文件系统的 scheme(和 authority),将会使用默认的 scheme(和 authority):
fs.default-scheme: -fs>
例如默认的文件系统配置为 fs.default-scheme: hdfs://localhost:9000/
,则文件路径 /user/hugo/in.txt
将被处理为 hdfs://localhost:9000/user/hugo/in.txt
。
如果文件系统不能处理大量并发读/写操作或连接,可以为文件系统同时打开的总连接数设置上限。
例如在一个大型 Flink 任务建立 checkpoint 时,具有少量 RPC handler 的小型 HDFS 集群可能会由于建立了过多的连接而过载。
要限制文件系统的连接数,可将下列配置添加至 Flink 配置中。设置限制的文件系统由其 scheme 指定:
fs..limit.total : (数量,0/-1 表示无限制)
fs..limit.input : (数量,0/-1 表示无限制)
fs..limit.output : (数量,0/-1 表示无限制)
fs..limit.timeout : (毫秒,0 表示无穷)
fs..limit.stream-timeout : (毫秒,0 表示无穷)
输入和输出连接(流)的数量可以分别通过 fs.
和 fs.
进行限制,也可以通过 fs.
限制并发流的总数。如果文件系统尝试打开更多的流,操作将被阻塞直至某些流关闭。如果打开流的时间超过 fs.
,则流打开失败。
为避免不活动的流占满整个连接池(阻止新连接的建立),可以在配置中添加无活动超时时间,如果连接至少在 fs.
时间内没有读/写操作,则连接会被强制关闭。
连接数是按每个 TaskManager/文件系统来进行限制的。因为文件系统的创建是按照 scheme 和 authority 进行的,所以不同的 authority 具有独立的连接池,例如 hdfs://myhdfs:50010/
和 hdfs://anotherhdfs:4399/
会有单独的连接池。
Amazon Simple Storage Service (Amazon S3) 提供用于多种场景的云对象存储。S3 可与 Flink 一起使用以读取、写入数据,并可与 流的 State backends 相结合使用。
通过以下格式指定路径,S3 对象可类似于普通文件使用:
s3:///
Endpoint 可以是一个文件或目录,例如:
// 读取 S3 bucket
env.readTextFile("s3:///" );
// 写入 S3 bucket
stream.writeAsText("s3:///" );
// 使用 S3 作为 FsStatebackend
env.setStateBackend(new FsStateBackend("s3:///" ));
注意这些例子并不详尽,S3 同样可以用在其他场景,包括 JobManager 高可用配置 或 EmbeddedRocksDBStateBackend,以及所有 Flink 需要使用文件系统 URI 的位置。
在大部分使用场景下,可使用 flink-s3-fs-hadoop
或 flink-s3-fs-presto
两个独立且易于设置的 S3 文件系统插件。然而在某些情况下,例如使用 S3 作为 YARN 的资源存储目录时,可能需要配置 Hadoop S3 文件系统。
注意: 如果您在使用 Flink on EMR,您无需手动对此进行配置。
Flink 提供了两种文件系统用来与 S3 交互:flink-s3-fs-presto
和 flink-s3-fs-hadoop
。两种实现都是独立的且没有依赖项,因此使用时无需将 Hadoop 添加至 classpath。
flink-s3-fs-presto
,通过 s3:// 和 s3p:// 两种 scheme 使用,基于 Presto project。 可以使用和 Presto 文件系统相同的配置项进行配置,方式为将配置添加到 flink-conf.yaml
文件中。如果要在 S3 中使用 checkpoint,推荐使用 Presto S3 文件系统。
flink-s3-fs-hadoop
,通过 s3:// 和 s3a:// 两种 scheme 使用, 基于 Hadoop Project。 本文件系统可以使用类似 Hadoop S3A 的配置项 进行配置,方式为将配置添加到 flink-conf.yaml
文件中。
例如,Hadoop 有 fs.s3a.connection.maximum
的配置选项。 如果你想在 Flink 程序中改变该配置的值,你需要将配置 s3.connection.maximum: xyz
添加到 flink-conf.yaml
文件中。Flink 会在内部将其转换成 fs.s3a.connection.maximum
,而无需通过 Hadoop 的 XML 配置文件来传递参数。
另外,它是唯一支持 StreamingFileSink 和 FileSink 的 S3 文件系统。
flink-s3-fs-hadoop
和 flink-s3-fs-presto
都为 s3:// scheme 注册了默认的文件系统包装器,flink-s3-fs-hadoop
另外注册了 s3a://,flink-s3-fs-presto
注册了 s3p://,因此二者可以同时使用。 例如某作业使用了 StreamingFileSink,它仅支持 Hadoop,但建立 checkpoint 使用 Presto。在这种情况下,建议明确地使用 s3a:// 作为 sink (Hadoop) 的 scheme,checkpoint (Presto) 使用 s3p://。这一点对于 FileSink 同样成立。
在启动 Flink 之前,将对应的 JAR 文件从 opt
复制到 Flink 发行版的 plugins
目录下,以使用 flink-s3-fs-hadoop
或 flink-s3-fs-presto
。
mkdir ./plugins/s3-fs-presto
cp ./opt/flink-s3-fs-presto-1.16.2.jar ./plugins/s3-fs-presto/
在设置好 S3 文件系统包装器后,您需要确认 Flink 具有访问 S3 Bucket 的权限。
建议通过 Identity and Access Management (IAM) 来配置 AWS 凭据。可使用 IAM 功能为 Flink 实例安全地提供访问 S3 Bucket 所需的凭据。关于配置的细节超出了本文档的范围,请参考 AWS 用户手册中的 IAM Roles 部分。
如果配置正确,则可在 AWS 中管理对 S3 的访问,而无需为 Flink 分发任何访问密钥(Access Key)。
可以通过**访问密钥对(access and secret key)**授予 S3 访问权限。请注意,根据 Introduction of IAM roles,不推荐使用该方法。
s3.access-key
和 s3.secret-key
均需要在 Flink 的 flink-conf.yaml
中进行配置:
s3.access-key: your-access-key
s3.secret-key: your-secret-key
S3 文件系统还支持兼容 S3 的对象存储服务,如 IBM’s Cloud Object Storage 和 Minio。可在 flink-conf.yaml
中配置使用的访问点:
s3.endpoint: your-endpoint-hostname
某些兼容 S3 的对象存储服务可能没有默认启用虚拟主机样式的寻址。这种情况下需要在 flink-conf.yaml
中添加配置以启用路径样式的访问:
s3.path.style.access: true
内置的 S3 文件系统 (flink-s3-fs-presto
and flink-s3-fs-hadoop
) 支持熵注入。熵注入是通过在关键字开头附近添加随机字符,以提高 AWS S3 bucket 可扩展性的技术。
如果熵注入被启用,路径中配置好的字串将会被随机字符所替换。例如路径 s3://my-bucket/_entropy_/checkpoints/dashboard-job/
将会被替换成类似于 s3://my-bucket/gf36ikvg/checkpoints/dashboard-job/
的路径。 这仅在使用熵注入选项创建文件时启用! 否则将完全删除文件路径中的 entropy key。更多细节请参见 FileSystem.create(Path, WriteOption)。
注意: 目前 Flink 运行时仅对 checkpoint 数据文件使用熵注入选项。所有其他文件包括 chekcpoint 元数据与外部 URI 都不使用熵注入,以保证 checkpoint URI 的可预测性。
配置 entropy key 与 entropy length 参数以启用熵注入:
s3.entropy.key: _entropy_
s3.entropy.length: 4 (default)
s3.entropy.key
定义了路径中被随机字符替换掉的字符串。不包含 entropy key 的路径将保持不变。 如果文件系统操作没有经过 “熵注入” 写入,entropy key 字串将被直接移除。 s3.entropy.length
定义了用于熵注入的随机字母/数字字符的数量。
Google Cloud storage (GCS) 提供了多样化的云存储使用案例,你可以使用它进行读写数据,并且在 流任务的 状态后端 中使用 FileSystemCheckpointStorage
时作为 checkpoint 存储。
你可以在配置了下面的格式之后,像访问常规文件一样通过指定的路径使用 GCS 对象:
gs:///
endpoint 可以是单个文件或目录,比如:
// 从 GSC bucket 读取
env.readTextFile("gs:///" );
// 向 GCS bucket 写入
stream.writeAsText("gs:///" );
// 将 GCS 作为 checkpoint 存储
env.getCheckpointConfig().setCheckpointStorage("gs:///" );
注意这些案例并不全,你也可以在其他地方使用 GCS,包括 JobManager 高可用配置 或 EmbeddedRocksDBStateBackend,以及所有 Flink 需要使用文件系统 URI 的位置。
Flink 提供了 flink-gs-fs-hadoop
文件系统来写入 GCS,该实现是自包含的,无需其他的依赖,因此在使用它时无需将 hadoop 加入 classpath。
flink-gs-fs-hadoop
会注册一个文件系统包装器,URI 的 schema 为 gs://
,它也使用了谷歌的 google-cloud-storage 库来提供可回收写入器 RecoverableWriter
的支持。
该文件系统可以作为 文件系统连接器 来使用。
为了使用 flink-gs-fs-hadoop
,在启动 Flink 之前,请将 JAR 文件从 opt
目录拷贝到 plugins
目录。
mkdir ./plugins/gs-fs-hadoop
cp ./opt/flink-gs-fs-hadoop-1.16.0.jar ./plugins/gs-fs-hadoop/
可以在你的 flink-conf.yaml
文件中使用 Hadoop configuration keys 来配置下面的 hadoop 文件系统,将其作为 gcs-connector
使用。
比如, gcs-connector
包含 fs.gs.http.connect-timeout
配置,如果你想更改它的值,你需要在 flink-conf.yaml
中设置 gs.http.connect-timeout: xyz
,Flink 会在内部将其转化为 fs.gs.http.connect-timeout
。
你也可以直接在 hadoop 的 core-site.xml
文件中设置 gcs-connector
选项,只要通过 flink 选项 env.hadoop.conf.dir
或通过 HADOOP_CONF_DIR
环境变量让 Flink 知道 hadoop 的配置目录即可。
flink-gs-fs-hadoop
也可以 flink-conf.yaml
文件中进行配置:
| Key | 描述 |
| ------------------------------* | -----------------------------------------------------------* |
| gs.writer.temporary.bucket.name | 配置该选项,可以让 Flink 通过 RecoverableWriter
来选择一个桶以让正在运行的写入器保存临时产生的二进制大对象。如果没有设置该配置,临时的二进制大对象将会被写入与最终文件相同的同一个桶。其他情况下,临时的二进制大对象的写入会带有前缀 .inprogress/
。
建议选择一个指定的桶,并 给他分配一个 TTL,在从 check/savepoint 恢复重启时,清理不再使用的二进制大对象。
如果你使用了一个指定了 TTL 的桶来存储临时的二进制大对象,在 TTL 过期之后从 check/savepoit 恢复重启的话可能会失败。 |
| gs.writer.chunk.size | 配置该选项,以通过RecoverableWriter
来设置写入器的 数据块大小。
如果没有设置,将会使用谷歌设定的默认大小。 |
GCS 上的大部分操作都需要认证,可以通过以下方式之一来提供认证证书:
在 JobManager 和 TaskManager 运行的机器上,设置环境变量 GOOGLE_APPLICATION_CREDENTIALS
为 JSON 证书文件的路径,如 here 的描述。这是推荐的方式。
在 core-site.xml
中设置 google.cloud.auth.service.account.json.keyfile
配置值为 JSON 证书文件的路径,并且确保指定了 hadoop 配置目录,如 above 所述。
<configuration>
<property>
<name>google.cloud.auth.service.account.json.keyfilename>
<value>PATH TO GOOGLE AUTHENTICATION JSON FILEvalue>
property>
configuration>
为了让 flink-gs-fs-hadoop
使用通过上面两种方式之一提供的证书,必须开启服务账号的认证。默认是开启的,但也可以在 core-site.xml
禁用:
<configuration>
<property>
<name>google.cloud.auth.service.account.enablename>
<value>falsevalue>
property>
configuration>
除了上面描述的
google.cloud.auth.service.account.json.keyfile
之外,gcs-connector
也支持其他选项来提供认证证书。然而,如果你使用了任何其他
RecoverableWriter
支持的选项,提供的证书将不会被google-cloud-storage
库使用,因此 Flink 的 recoverable-write 操作将会失败。为了解决这个问题,建议使用
gcs-connector
认证证书选项,而不是google.cloud.auth.service.account.json.keyfile
。
阿里云对象存储服务 (Aliyun OSS) 使用广泛,尤其在中国云用户中十分流行,能提供多种应用场景下的云对象存储。OSS 可与 Flink 一起使用以读取与存储数据,以及与流状态后端 结合使用。
通过以下格式指定路径,OSS 对象可类似于普通文件使用:
oss:///
以下代码展示了如何在 Flink 作业中使用 OSS:
// 读取 OSS bucket
env.readTextFile("oss:///" );
// 写入 OSS bucket
stream.writeAsText("oss:///" )
// 将 OSS 用作 FsStatebackend
env.setStateBackend(new FsStateBackend("oss:///" ));
为使用 flink-oss-fs-hadoop
,在启动 Flink 之前,将对应的 JAR 文件从 opt
目录复制到 Flink 发行版中的 plugin
目录下的一个文件夹中,例如:
mkdir ./plugins/oss-fs-hadoop
cp ./opt/flink-oss-fs-hadoop-1.16.2.jar ./plugins/oss-fs-hadoop/
flink-oss-fs-hadoop
为使用 oss://
scheme 的 URI 注册了默认的文件系统包装器。
在设置好 OSS 文件系统包装器之后,需要添加一些配置以保证 Flink 有权限访问 OSS buckets。
为了简单使用,可直接在 flink-conf.yaml
中使用与 Hadoop core-site.xml
相同的配置关键字。
可在 Hadoop OSS 文档 中查看配置关键字。
一些配置必须添加至 flink-conf.yaml
(在 Hadoop OSS 文档中定义的其它配置为用作性能调优的高级配置):
fs.oss.endpoint: 连接的 Aliyun OSS endpoint
fs.oss.accessKeyId: Aliyun access key ID
fs.oss.accessKeySecret: Aliyun access key secret
备选的 CredentialsProvider
也可在 flink-conf.yaml
中配置,例如:
# 10. 从 OSS_ACCESS_KEY_ID 和 OSS_ACCESS_KEY_SECRET 读取凭据 (Credentials)
fs.oss.credentials.provider: com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider
其余的凭据提供者(credential providers)可在这里中找到。
Azure Blob 存储 是一项由 Microsoft 管理的服务,能提供多种应用场景下的云存储。 Azure Blob 存储可与 Flink 一起使用以读取和写入数据,以及与流状态后端 结合使用。
Azure 建议使用
abfs://
访问 ADLS Gen2 存储帐户,尽管wasb://
通过向后兼容也可以工作。
abfs://
只能用于访问 ADLS Gen2 存储帐户。请访问 Azure 文档,了解如何识别 ADLS Gen2 存储帐户。
通过以下格式指定路径,Azure Blob 存储对象可类似于普通文件使用:
// WASB 非加密访问
wasb://@$.blob.core.windows.net/
// WASB SSL 加密访问
wasbs://@$.blob.core.windows.net/
// ABFS 非加密访问
abfs://@$.dfs.core.windows.net/
// ABFS SSL 加密访问
abfss://@$.dfs.core.windows.net/
参见以下代码了解如何在 Flink 作业中使用 Azure Blob 存储:
// 读取 Azure Blob 存储
env.readTextFile("wasb://@$.blob.core.windows.net/" );
// 写入 Azure Blob 存储
stream.writeAsText("wasb://@$.blob.core.windows.net/" )
// 将 Azure Blob 存储用作 FsStatebackend
env.setStateBackend(new FsStateBackend("wasb://@$.blob.core.windows.net/" ));
为使用 flink-azure-fs-hadoop,在启动 Flink 之前,将对应的 JAR 文件从 opt 目录复制到 Flink 发行版中的 plugin 目录下的一个文件夹中,例如:
mkdir ./plugins/azure-fs-hadoop
cp ./opt/flink-azure-fs-hadoop-1.16.2.jar ./plugins/azure-fs-hadoop/
flink-azure-fs-hadoop
为使用 wasb://
和 *wasbs://*
(SSL 加密访问) 的 URI 注册了默认的文件系统包装器。
Hadoop 的 Azure 文件系统支持通过 Hadoop 配置来配置凭据,如 Hadoop Azure Blob Storage 文档 所述。 为方便起见,Flink 将所有的 Flink 配置添加 fs.azure
键前缀后转发至文件系统的 Hadoop 配置中。因此,可通过以下方法在 flink-conf.yaml
中配置 Azure Blob 存储密钥:
fs.azure.account.key..blob.core.windows.net : >
或者通过在 flink-conf.yaml
中设置以下配置键,将文件系统配置为从环境变量 AZURE_STORAGE_KEY
读取 Azure Blob 存储密钥:
fs.azure.account.keyprovider..blob.core.windows.net : org.apache.flink.fs.azurefs.EnvironmentVariableKeyProvider
Hadoop 的 ABFS Azure 文件系统支持多种配置身份验证的方法。关于如何配置,请访问Hadoop ABFS文档。
Azure 推荐使用 Azure 托管身份来使用 abfs 访问 ADLS Gen2 存储帐户。关于如何做到这一点的细节超出了本文档的范围,更多细节请参阅 Azure 文档。
请访问 页面 来查看支持的管理身份服务列表。在这些 Azure 服务上部署的 Flink 集群可以获得管理的身份服务的好处。
Azure blob 存储密钥可以通过以下方式在 flink-conf.yaml
中配置:
fs.azure.account.key..dfs.core.windows.net : >
插件机制通过特定的类加载器为代码的严格分离提供了便利。一个插件不能访问其他插件或 Flink 里不在白名单中指定的类。该隔离机制允许插件对于相同的库包含冲突的版本,而不需要移除冲突类或将库转换为相同的版本。目前,文件系统和指标上报是插件化的,但是在将来,连接器、格式甚至是用户代码也应该是插件化的。
插件都放在他们自己的目录中,并且可以放很多个 jar 包,插件目录的名字可以是任意的。
flink-dist
├── conf
├── lib
...
└── plugins
├── s3
│ ├── aws-credential-provider.jar
│ └── flink-s3-fs-hadoop.jar
└── azure
└── flink-azure-fs-hadoop.jar
每个插件都通过他们自己的类加载器进行加载,并且和其他插件完全隔离。因此, flink-s3-fs-hadoop
和 flink-azure-fs-hadoop
可以依赖不同且冲突的库版本,且不需要移除通过 shade 创建创建的 fat jar 中的任何类。
插件可能会访问 Flink lib/
目录下包含在白名单的 jar 包,尤其是通过系统类加载器加载的必要的服务提供接口 (SPI),因此,即使用户在他们的 fat jar 中意外绑定了它,也不能同时存在两个版本的 org.apache.flink.core.fs.FileSystem
。这个单例类是必要的,如此一来,Flink 运行时才能有一个进入插件的入口点。通过 java.util.ServiceLoader
来发现服务类,因此在 shade 打包时需要确保在 META-INF/services
中定义了服务。
注意:目前,在我们具象化了 SPI 系统时,插件仍然可以访问很多 Flink 核心类。
此外,很多公共的日志框架也在白名单中,因此在 Flink 核心类、插件和用户代码中均可访问日志类。
所有文件系统都是插件化的,这意味着他们应该被当做插件使用。为了使用插件化的文件系统,在启动 Flink 之前,需要将对应的 JAR 文件从 Flink 分布式部署中的 opt
目录拷贝到 plugins
目录下的某个目录。比如:
mkdir ./plugins/s3-fs-hadoop
cp ./opt/flink-s3-fs-hadoop-1.16.0.jar ./plugins/s3-fs-hadoop/
s3 文件系统 (
flink-s3-fs-presto
和flink-s3-fs-hadoop
) 只能被当做插件使用,因为我们已经移除了他们,将他们放到lib/
目录下会导致系统失败。
由于严格的隔离,文件系统已经无法访问
lib/
目录下的凭证提供者了。请在各自的插件目录下增加需要的提供者。
Flink 提供的所有指标上报都可以被当做插件使用,查看指标文档来后去更多细节。
JobManager 高可用(HA)用来对 Flink 集群的 JobManager 进行容错,该特性可以保证 Flink 集群一直持续的运行提交的作业。
JobManager 会协调每个 Flink 的部署,它会同时负责调度和资源管理。
默认情况下,每个 Flink 集群只有一个 JobManager 实例。这会导致单点失败(SPOF)问题:如果 JobManager 崩溃,就无法提交新程序了,而且正在运行的程序也会失败。
通过 JobManager 高可用,就可以在 JobManager 失败后恢复,并且解决 SPOF 问题。你可以为每个集群部署配置高可用,查看可用的高可用服务列表来获取更多信息。
JobManager 高可用的通常实现方式为:任何时候都有一个 leader 角色的 JobManager,然后有多个备用的 JobManager 在 leader 失败后接管 leader 角色。这可以保证没有单点失败问题,并且程序可以在备用 JobManager 成为 leader 时继续运行。
如下图,有部署了三个 JobManager 实例:
Flink 的高可用服务封装了必要的服务,以使所有工作都能正常进行:
n
个候选者池中选举一个 leaderFlink 提供了两种该可用该服务实现:
为了恢复提交的作业,Flink 会持久化元数据和作业数据。HA 数据会报一直保存,直到作业要么成功,要么被取消或失败终止。这些情况一旦发生,所有的 HA 数据,包括存储到 HA 服务中的元数据,都会被删除。
JobResultStore 被用于归档作业达到全局终止状态的最终结果,比如:完成、取消或失败。数据会被存储到文件系统,具体可查看 job-result-store.storage-path。只要相应的作业没有正确地清理,这个存储中的条目就被标记为脏的,可以在作业的 high-availability.storageDir 子目录中找到坐标。
脏条目将会被清理,比如:对应的作业要么被 Flink 清理,要么作为恢复的一部分被清理。一旦清理成功,条目将会被删除。查看HA配置选项下的 JobResultStore 配置参数,了解如何调整行为的进一步详细信息。
Flink ZooKeeper HA 服务使用 ZooKeeper 作为高可用服务。
Flink 使用 ZooKeeper 来分布式协调所有正在运行的 JobManager 实例。ZooKeeper 服务和 Flink 单独分开的,ZooKeeper 提供了高可用分布式协调的 leader 选举,以及轻量级的一致性状态存储。通过 ZooKeeper 的入门指南 来获取更多有关 ZooKeeper 信息。Flink 内置了启动一个简单的 ZooKeeper 的安装脚本。
为了启动一个高可用集群,你需要配置下面的这些参数:
high-availability (必选):high-availability
选项必须设置为 zookeeper
。
high-availability: zookeeper
high-availability.storageDir (必选):JobManager 元数据会被持久化到通过 high-availability.storageDir
配置的文件系统,并且只有指向该状态的指针会被存储到 ZooKeeper 中。
high-availability.storageDir: hdfs:///flink/recovery
storageDir
会存储为了恢复失败 JobManager 的所有元数据。
[high-availability.zookeeper.quorum]({%link deployment/config.md %}#high-availability-zookeeper-quorum) (必选):ZooKeeper 服务复制组的 ZooKeeper quorum,用来提供分布式协调服务。
high-availability.zookeeper.quorum: address1:2181[,...],addressX:2181
每个 addressX:port
都指向一个 ZooKeeper 服务,并且 Flink 可以通过它来访问 ZooKeeper。
high-availability.zookeeper.path.root (推荐):ZooKeeper 的根节点,所有的 flink 集群节点都会放到它下面。
high-availability.zookeeper.path.root: /flink
high-availability.cluster-id (推荐):存放到 ZooKeeper 的 Flink 集群 ID,所有 Flink 集群的协调数据都会放到它下面。
high-availability.cluster-id: /default_ns # 重要提示:对每个集群都自定义一个名称
重要:当使用 on YARN、原生 Kubernetes 或其他集群管理者运行 Flink 时不要设置该参数值。在这些情况下,会自动生成 cluster-id。如果你裸机部署了多个 Flink HA 集群,你需要对每个集群手动配置单独的 cluster-id。
在 conf/flink-conf.yaml
中配置高可用模式和 ZooKeeper quorum:
high-availability: zookeeper
high-availability.zookeeper.quorum: localhost:2181
high-availability.zookeeper.path.root: /flink
high-availability.cluster-id: /cluster_one # 重要提示:对每个 flink 集群单独配置 id
high-availability.storageDir: hdfs:///flink/recovery
如果 ZooKeeper 使用 Kerberos 在安全模式下运行,需要在 flink-conf.yaml
中覆盖下面的配置:
# 11. 默认为 zookeeper。如果 ZooKeeper quorum 配置了不同的服务名称,可以在这儿配置对应的名称。
zookeeper.sasl.service-name: zookeeper
# 12. 默认为 Client。配置值应该匹配 "security.kerberos.login.contexts" 中的某个值。
zookeeper.sasl.login-context-name: Client
可以通过参考 Flink 配置的安全部分来获取更多 Flink 的 Kerberos 安全配置,也可以通过 Flink 内部基于 Kerberos 的设置 获取更进一步的细节。
默认情况下,Flink 的 ZooKeeper 客户端会将挂起的 ZooKeeper 连接作为一个错误对待,这意味着,如果一个连接被挂起, Flink 将会任务组件的领导者已经失败,然后触发一个失败恢复。
该行为在有些情况下影响可能会很大,比如不稳定的网络环境。如果你想采用一个更积极的方式,你可以通过配置 high-availability.zookeeper.client.tolerate-suspended-connections 参数为 true
容忍挂起的 ZooKeeper 连接,只将丢失的连接作为一个错误。启用该特性,将会使 Flink 更有弹性,可以容忍临时的连接问题。
获取更多的信息,请参考 Curator 的错误处理。
Flink 单独存放了 3.4 和 3.5 版本的 ZooKeeper 客户端,lib
目录下默认放置的是 3.4 版本客户端,3.5 版本的客户端放在 opt
目录下。
3.5 版本客户端允许通过 SSL 安全访问 ZooKeeper,但可能无法访问 3.4 版本 ZooKeeper。
你可以通过在 lib
目录下存放对应的 jar 包来控制使用哪个版本的 ZooKeeper 客户端。
如果你现在没有一个正在运行的 ZooKeeper 集群,可以使用 Flink 提供的帮助脚本来运行一个简单的 Flink 集群。
conf/zoo.cfg
文件内容为 ZooKeeper 的配置模板。你可以通过 server.X
条目来配置运行 ZooKeeper 的主机,其中 X
为每个服务的唯一 ID:
server.X=addressX:peerPort:leaderPort
[...]
server.Y=addressY:peerPort:leaderPort
脚本 bin/start-zookeeper-quorum.sh
会在配置的每个主机上启动一个 ZooKeeper 服务,通过 Flink 包装器启动的 ZooKeeper 服务进程,会读取 conf/zoo.cfg
中的配置,并且为了方便,会设置一些必要的配置值。在生产环境中,建议管理自己的 ZooKeeper 集群。
Flink 的 Kubernetes HA 服务使用 Kubernetes 来实现高可用。
只有在 Kubernetes 上部署 Flink 时才可使用 Kubernetes 高可用服务,因此,可以通过 standalone Flink on Kubernetes or the 原生 Kubernetes 整合 来配置他们。
为了使用 Flink 的 Kubernetes HA 服务,必须完全满足下面的要求:
为了启动一个 HA 集群,你需要配置下面这些参数:
high-availability
选项必须被设置为:KubernetesHaServicesFactory
。high-availability: org.apache.flink.kubernetes.highavailability.KubernetesHaServicesFactory
high-availability.storageDir
指定的文件系统,并且只会在 Kubernetes 中保存一个指向状态的指针。high-availability.storageDir: s3://flink/recovery
storageDir
会存储所有用来恢复失败 JobManager 的元数据。
kubernetes.cluster-id
。kubernetes.cluster-id: cluster1337
在 conf/flink-conf.yaml
中配置高可用模式:
kubernetes.cluster-id: -id>
high-availability: org.apache.flink.kubernetes.highavailability.KubernetesHaServicesFactory
high-availability.storageDir: hdfs:///flink/recovery
为了在重启 Flink 集群时保存高可用数据,可以通过 kubectl delete deployment
来简单的删除部署。所有 Flink 集群相关资源都将会被删除,比如:JobManager 部署、TaskManager pod、服务、Flink ConfigMap 配置。与 HA 相关的 ConfigMap 将会被保留,因为他们没有设置所有者引用。当重启集群时,所有之前运行的作业将会被恢复,并且使用最新成功的 checkpoint 重启。
Flink 允许指标上报到外部系统,更多有关 Flink 的指标系统信息,请参考指标系统文档。
可以通过在 conf/flink-conf.yaml
中配置一个或多个上报器将指标暴露给外部系统,在他们启动时,这些上报器将会被安装到每个作业和任务管理器。
下面的章节列出了所有支持的上报器。所有属性都可以通过设置 metrics.reporter.
进行配置。上报器也会基于实现提供额外的特殊参数,这些都可以在下面上报器的章节中找到。
| Key | 默认值 | 类型 | 描述 |
| :------------------------* | :-------* | :----------* | :----------------------------------------------------------* |
| factory.class | (none) | String | 名为 的上报器使用的工厂类。 |
| interval | 10 s | Duration | 名为
的上报器上报数据的时间间隔,只适用于基于推送的上报器。 |
| scope.delimiter | “.” | String | 用于组装名为 的上报器指标标识符的分隔符。 |
| scope.variables.additional | | Map | 应该包含到名为 上报器的额外变量 map 表,只适用于基于标签的上报器。 |
| scope.variables.excludes | “.” | String | 名为 上报器应该排除的变量 set 集合,只适用于基于标签的上报器。 |
| filter.includes | "*:*:*"
| List | 名为 的上报器应该包含的指标。过滤器被指定为一个 list 列表,格式为:
。如果范围模式和至少一种名称模式以及至少一种类型匹配,则指标与过滤器匹配。
scope: 基于逻辑 scope 的过滤器。*
匹配任何字符序列,.
分割 scope 组件的模式
比如: “jobmanager.job
” 匹配 JobManager 上与作业相关的指标,“*.job
” 匹配所有和作业相关的指标,“*.job.*
” 匹配所有作业级别以下的指标,比如:任务/算子指标。
name: 基于指标名称的过滤器。指定通过英文逗号分割的 list 列表,*
匹配任何字符序列。
比如:“*Records*,*Bytes*
” 匹配任何名称包含 “Records” 或 “Bytes” 的指标。
type: 基于指标类型的过滤器。指定通过英文逗号分割的 list 列表的指标类型:[counter, meter, gauge, histogram]
。
案例:
“*:numRecords*
” 匹配像 numRecordsIn
的指标
“*.job.task.operator:numRecords*
” 匹配算子级别像 numRecordsIn
的指标。
“*.job.task.operator:numRecords*:meter
” 匹配算子级别像 numRecordsInPerSecond
的指标。
“*:numRecords*,numBytes*:counter,meter
” 匹配所有像 numRecordsInPerSecond
的 counter/meter 指标。 |
| filter.excludes | | List | 名为 的上报器应该排除的指标。格式和 filter.includes
相同。 |
| | (none) | String | 配置名为 的上报器的参数 。 |
所有上报器必须配置factory.class
属性。一些上报器需要指定上报 interval
。
指定多个上报器的配置示例:
metrics.reporters: my_jmx_reporter,my_other_reporter
metrics.reporter.my_jmx_reporter.factory.class: org.apache.flink.metrics.jmx.JMXReporterFactory
metrics.reporter.my_jmx_reporter.port: 9020-9040
metrics.reporter.my_jmx_reporter.scope.variables.excludes:job_id;task_attempt_num
metrics.reporter.my_other_reporter.class: org.apache.flink.metrics.graphite.GraphiteReporter
metrics.reporter.my_other_reporter.host: 192.168.1.1
metrics.reporter.my_other_reporter.port: 10000
重要:包含上报器对应的 jar 包必须在 Flink 启动时可以被访问,支持 factory.class
的上报器可以通过插件形式加载,否则,jar 包必须放到 /lib
目录下。Flink 提供的上报器(该章节中提到的上报器)默认都直接可用。
可以通过实现 org.apache.flink.metrics.reporter.MetricReporter
接口来定制化自己的 Reporter
上报器,如果上报器需要定期发送指标,则还需要实现 Scheduled
接口。通过增加对 MetricReporterFactory
接口的实现,你的上报器也可以作为插件进行加载。
上报器通常通过两种格式来暴露指标。
基于标识符的上报器会将所有范围内的信息和指标名称包含到一个扁平的字符串中,比如 job.MyJobName.numRestarts
。
基于标签的上报器定义了一个由逻辑范围和指标名称(例如 job.numRestarts
)组成的通用指标类,并将所述指标的特定实例报告为一组 key-value
对,即所谓的“标签”或“变量”(例如 “jobName=MyJobName”)。
指标通过推或拉的方式进行暴露。
基于推的上报器通常会实现 Scheduled
接口,并且周期性的发送当前指标的汇总给一个额外的系统。
基于拉的上报器会由一个外部系统进行查询。
下面的章节列出了所有支持的上报器。
org.apache.flink.metrics.jmx.JMXReporter
类型:拉/标签
参数:
port
:可选,连接 JMX 的监听端口。为了在一个主机上运行几个不同的上报器实例,将端口号配置为类似于 9250-9260
的范围是一个很好的选择。当配置为范围时,真正使用的端口号会在相关的作业或 TaskManager 日志中展现。如果设置了该参数,则 Flink 会为给定的 端口号/范围 启动一个额外的 JMX 连接器。默认的本地 JMX 接口的指标都是可用的。配置示例:
metrics.reporter.jmx.factory.class: org.apache.flink.metrics.jmx.JMXReporterFactory
metrics.reporter.jmx.port: 8789
通过 JMX 暴露的指标通过一个域名和 key-properties 列表来标识,他们共同构成对象的名称。
域名通常以 org.apache.flink
为开头,后面跟着生成的指标标识。和常见的标识符不同,他不会受到作用域格式的影响,不包含任何变量,并且在多个作业之间都是一样的。比如: org.apache.flink.job.task.numBytesOut
。
key-property 列表包含了所有变量的值,不管配置的作用域格式是什么,他们都会和一个给定的指标相关。这样一个列表的案例为:host=localhost,job_name=MyJob,task_name=MyTask
。
域名会标识一个指标 class 类,key-property 列表会标识指标对应的一个或多个实例。
org.apache.flink.metrics.graphite.GraphiteReporter
类型:推/标识符
参数:
host
* Graphite 服务主机名port
* Graphite 服务端口号protocol
* 使用的协议:TCP/UDP配置示例:
metrics.reporter.grph.factory.class: org.apache.flink.metrics.graphite.GraphiteReporterFactory
metrics.reporter.grph.host: localhost
metrics.reporter.grph.port: 2003
metrics.reporter.grph.protocol: TCP
metrics.reporter.grph.interval: 60 SECONDS
org.apache.flink.metrics.influxdb.InfluxdbReporter
类型:推/标签
配置:
| Key | 默认值 | 类型 | 描述 |
| :-------------* | :----* | :---------------------------------* | :----------------------------* |
| connectTimeout | 10000 | Integer | 可选,连接 InfluxDB 的超时时间 |
| consistency | ONE | 枚举可用值: [ALL, ANY, ONE, QUORUM] | 可选,InfluxDB 的一致性级别 |
| db | (none) | String | InfluxDB 存储指标的数据库名称 |
| host | (none) | String | InfluxDB 服务的主机名 |
| password | (none) | String | 可选,InfluxDB 密码 |
| port | 8086 | Integer | InfluxDB 服务的端口号 |
| retentionPolicy | (none) | String | 可选,InfluxDB 的数据保留策略 |
| scheme | http | 枚举可用值: [http, https] | InfluxDB schema |
| username | (none) | String | 可选,InfluxDB 用户名 |
| writeTimeout | 10000 | Integer | 可选,写入 InfluxDB 的超时时间 |
配置案例:
metrics.reporter.influxdb.factory.class: org.apache.flink.metrics.influxdb.InfluxdbReporterFactory
metrics.reporter.influxdb.scheme: http
metrics.reporter.influxdb.host: localhost
metrics.reporter.influxdb.port: 8086
metrics.reporter.influxdb.db: flink
metrics.reporter.influxdb.username: flink-metrics
metrics.reporter.influxdb.password: qwerty
metrics.reporter.influxdb.retentionPolicy: one_hour
metrics.reporter.influxdb.consistency: ANY
metrics.reporter.influxdb.connectTimeout: 60000
metrics.reporter.influxdb.writeTimeout: 60000
metrics.reporter.influxdb.interval: 60 SECONDS
上报器会使用 http 协议以及指定的保留策略或 InfluxDB 服务的默认策略发送指标到 InfluxDB 服务。所有的 Flink 指标变量(查看变量列表)会暴露为 InfluxDB 标签。
org.apache.flink.metrics.prometheus.PrometheusReporter
类型:拉/标签
参数:
port
:可选,Prometheus 上报器监听端口号,默认为 9249。为了在一个主机上运行多个上报器实例,可以将端口号配置为范围形式,比如:9250-9260
。filterLabelValueCharacters
:可选,是否过滤标签值字符。如果启用,则所有没有匹配 [a-zA-Z0-9:_] 的字符都会被移除,如果禁用,则不会移除任何字符。在禁用该选项之前,请确保你的标签值符合 Prometheus 要求。配置案例:
metrics.reporter.prom.class: org.apache.flink.metrics.prometheus.PrometheusReporter
下面的 Flink 指标类型匹配了 Prometheus 的指标类型:
| Flink | Prometheus | 备注 |
| :-------* | :--------* | :------------------------------------* |
| Counter | Gauge | Prometheus 计数器不能降低 |
| Gauge | Gauge | 只支持数字和 boolean |
| Histogram | Summary | 分位数:.5, .75, .95, .98, .99 和 .999 |
| Meter | Gauge | 仪表会输出计量的速率 |
所有的 Flink 指标变量(变量列表)会作为标签上报给 Prometheus。
org.apache.flink.metrics.prometheus.PrometheusPushGatewayReporter
类型:推/标签
参数:
| Key | 默认值 | 类型 | 描述 |
| :------------------------* | :----* | :-----* | :----------------------------------------------------------* |
| deleteOnShutdown | true | Boolean | 在停止后是否从 PushGateway 中删除指标。Flink 会尽最大努力去删除指标,但是无法保证一定可以删除掉。查看这儿获取更多细节。 |
| filterLabelValueCharacters | true | Boolean | 是否过滤标签值字符。如果启用,则所有没有匹配 [a-zA-Z0-9:_] 的字符都会被移除,如果禁用,则不会移除任何字符。在禁用该选项之前,请确保你的标签值符合 Prometheus 要求。 |
| groupingKey | (none) | String | 指定分区键,它是所有指标的分组和全局标签。通过 ‘=’ 来分隔标签名和值,通过 ‘;’ 来分隔多个标签,比如:k1=v1;k2=v2
。请确保你的分组键符合 Prometheus 要求。 |
| hostUrl | (none) | String | PushGateway 服务主机名。 |
| jobName | (none) | String | 推送指标的作业名称。 |
| randomJobNameSuffix | true | Boolean | 是否将随机字符串追加到作业名称后面。 |
配置案例:
metrics.reporter.promgateway.class: org.apache.flink.metrics.prometheus.PrometheusPushGatewayReporter
metrics.reporter.promgateway.hostUrl: http://localhost:9091
metrics.reporter.promgateway.jobName: myJob
metrics.reporter.promgateway.randomJobNameSuffix: true
metrics.reporter.promgateway.deleteOnShutdown: false
metrics.reporter.promgateway.groupingKey: k1=v1;k2=v2
metrics.reporter.promgateway.interval: 60 SECONDS
PrometheusPushGatewayReporter 会将指标推送到 Pushgateway,PushGateway 会将数据上报给 Prometheus。
查看 Prometheus 文档来获取用户案例。
org.apache.flink.metrics.statsd.StatsDReporter
类型:推/标识符
参数:
host
* StatsD 服务主机名port
* StatsD 服务端口号配置案例:
metrics.reporter.stsd.factory.class: org.apache.flink.metrics.statsd.StatsDReporterFactory
metrics.reporter.stsd.host: localhost
metrics.reporter.stsd.port: 8125
metrics.reporter.stsd.interval: 60 SECONDS
org.apache.flink.metrics.datadog.DatadogHttpReporter
类型:推/标签
Flink 指标的任何变量,比如:
,
,
,
,
, 和
,将会作为标签发送到 Datadog。标签类似于: host:localhost
和 job_name:myjobname
。
注:直方图会按照 Datadog 直方图的命名习惯(
)暴露一系列度量。默认会上报 min
聚合,然后 sum
聚合不可用。和 Datadog 提供的直方图不同,上报的聚合指标并不是根据指定上报间隔计算的聚合结果。
参数:
apikey
* Datadog API keyproxyHost
:可选,使用的代理主机名proxyPort
:可选,使用的代理端口号,默认为 8080dataCenter
:可选,连接的数据中心,默认为 US
,可用值有:EU
/US
。maxMetricsPerRequest
:可选,每次请求可以包含的最大指标数量,默认为 2000useLogicalIdentifier
:可选,上报器是否使用一个逻辑指标标识符,默认为 false
。配置案例:
metrics.reporter.dghttp.factory.class: org.apache.flink.metrics.datadog.DatadogHttpReporterFactory
metrics.reporter.dghttp.apikey: xxx
metrics.reporter.dghttp.proxyHost: my.web.proxy.com
metrics.reporter.dghttp.proxyPort: 8080
metrics.reporter.dghttp.dataCenter: US
metrics.reporter.dghttp.maxMetricsPerRequest: 2000
metrics.reporter.dghttp.interval: 60 SECONDS
metrics.reporter.dghttp.useLogicalIdentifier: true
org.apache.flink.metrics.slf4j.Slf4jReporter
类型:推/标识符
配置案例:
metrics.reporter.slf4j.factory.class: org.apache.flink.metrics.slf4j.Slf4jReporterFactory
metrics.reporter.slf4j.interval: 60 SECONDS
暂时不做翻译。
本文简要描述了 Flink 如何在各种部署机制(Standalone, native Kubernetes, YARN)、文件系统、connector 以及 state backend 的上下文中安全工作。
Flink Kerberos 安全框架的主要目标如下:
生产部署场景中,流式作业通常会运行很长一段时间(天、周、月级别的时间段),并且需要在作业的整个生命周期中对其进行身份认证以保护数据源。与 Hadoop delegation token 和 ticket 缓存项不同,Kerberos keytab 不会在该时间段内过期。
当前的实现支持使用可配置的 keytab credential 或 Hadoop delegation token 来运行 Flink 集群(JobManager / TaskManager / 作业)。请注意,所有作业都能共享为指定集群配置的凭据。如果想为一个作业使用不同的 keytab,只需单独启动一个具有不同配置的 Flink 集群。多个 Flink 集群可以在 Kubernetes 或 YARN 环境中并行运行。
理论上,Flink 程序可以使用自己的或第三方的连接器(Kafka、HDFS、Cassandra、Flume、Kinesis 等),同时需要支持任意的认证方式(Kerberos、SSL/TLS、用户名/密码等)。满足所有连接器的安全需求还在进行中,不过 Flink 提供了针对 Kerberos 身份认证的一流支持。Kerberos 身份认证支持以下服务和连接器:
请注意,你可以单独为每个服务或连接器启用 Kerberos。例如,用户可以启用 Hadoop security,而无需为 ZooKeeper 开启 Kerberos,反之亦然。Kerberos 凭证是组件之间共享的配置,每个组件会显式地使用它。
Flink 安全内部架构是建立在安全模块(实现 org.apache.flink.runtime.security.modules.SecurityModule
)上的,安全模块在 Flink 启动过程中被安装。后面部分描述了每个安全模块。
该模块使用 Hadoop UserGroupInformation(UGI)类来建立进程范围的 登录用户 上下文。然后,登录用户用于与 Hadoop 组件的所有交互,包括 HDFS、HBase 和 YARN。
如果启用了 Hadoop security(在 core-site.xml
中),登录用户将拥有所有配置的 Kerberos 凭据。否则,登录用户仅继承启动集群的操作系统帐户的用户身份。
该模块为集群提供动态 JAAS 配置,使已配置的 Kerberos 凭证对 ZooKeeper、Kafka 和其他依赖 JAAS 的组件可用。
请注意,用户还可以使用 Java SE 文档中描述的机制提供静态 JAAS 配置文件。静态配置项会覆盖此模块提供的任何动态配置项。
该模块配置某些进程范围内 ZooKeeper 安全相关的设置,即 ZooKeeper 服务名称(默认为:zookeeper
)和 JAAS 登录上下文名称(默认为:Client
)。
以下是针对每种部署模式的一些信息。
在 standalone 模式或集群模式下运行安全 Flink 集群的步骤如下:
security.kerberos.login.keytab
指定的路径上。在原生 Kubernetes 或 YARN 模式下运行安全 Flink 集群的步骤如下:
security.kerberos.login.keytab
指定的路径上。在 YARN 和 原生 Kubernetes 模式下,keytab 文件会被自动从客户端拷贝到 Flink 容器中。
要启用 Kerberos 身份认证,还需要 Kerberos 配置文件。该文件可以从集群环境中获取,也可以由 Flink 上传。针对后者,你需要配置 security.kerberos.krb5-conf.path
来指定 Kerberos 配置文件的路径,Flink 会将此文件复制到相应容器或 pod。
请参阅 YARN security 文档获取更多相关信息。
在 YARN 模式下,可以不需要 keytab 而只使用 ticket 缓存(由 kinit
管理)来部署一个安全的 Flink 集群。这避免了生成 keytab 的复杂性,同时避免了将其委托给集群管理器。在这种情况下,使用 Flink CLI 获取 Hadoop delegation token(用于 HDFS 和 HBase)。主要缺点是集群必须是短暂的,因为生成的 delegation token 将会过期(通常在一周内)。
使用 kinit
运行安全 Flink 集群的步骤如下:
kinit
命令登录。使用 Kerberos 的每个组件都独立负责更新 Kerberos ticket-granting-ticket(TGT)。Hadoop、ZooKeeper 和 Kafka 都在提供 keytab 时自动更新 TGT。在 delegation token 场景中,YARN 本身会更新 token(更新至其最大生命周期)。
包含了 Python 和 Scala 的 REPL 相关介绍和使用方式,暂不做翻译整理。
许多计算任务需要使用除了 CPU 与内存外的资源,比如深度学习需要使用 GPU 来进行加速。为了支持这种扩展资源,Flink 提供了一个扩展资源框架。该框架支持从底层资源管理系统(如 Kubernetes)请求各种类型的资源,并向算子提供使用这些资源所需的信息。该框架以插件形式支持不同的资源类型。目前 Flink 仅内置了支持 GPU 资源的插件,你可以为你想使用的资源类型实现第三方插件。
扩展资源(External Resource)框架主要做了以下两件事:
当 Flink 部署在资源管理系统(Kubernetes、Yarn)上时,扩展资源框架将确保分配的 Pod、Container 包含所需的扩展资源。目前,许多资源管理系统都支持扩展资源。例如,Kubernetes 从 v1.10 开始通过 Device Plugin 机制支持 GPU、FPGA 等资源调度,Yarn 从 2.10 和 3.1 开始支持 GPU 和 FPGA 的调度。在 Standalone 模式下,由用户负责确保扩展资源的可用性。
扩展资源框架向算子提供扩展资源相关信息,这些信息由你配置的扩展资源 Driver 生成,包含了使用扩展资源所需要的基本属性。
为了启用扩展资源框架来使用扩展资源,你需要:
RuntimeContext
来获取扩展资源的信息并使用这些资源你需要为使用的扩展资源准备插件,并将其放入 Flink 发行版的 plugins/
文件夹中, 参看 Flink Plugins。 Flink 提供了的 GPU 资源插件。你同样可以为你所使用的扩展资源实现自定义插件实现自定义插件。
首先,你需要使用分隔符“;”将所有使用的扩展资源类型的资源名称添加到扩展资源列表(配置键“external-resources”)中,例如,“external-resources: gpu;fpga”定义了两个扩展资源“gpu”和“fpga”。 只有此处定义了扩展资源名称(
对于每个扩展资源,有以下配置选项。下面的所有配置选项中的
external..amount
):需要从外部系统请求的扩展资源的数量。external-resource..yarn.config-key
):可选配置。如果配置该项,扩展资源框架将把这个键添加到 Yarn 的容器请求的资源配置中,该键对应的值将被设置为external-resource..amount
。external-resource..kubernetes.config-key
):可选配置。 如果配置该项,扩展资源框架将添加 resources.limits.
和 resources.requests.
到 TaskManager 的主容器配置中,对应的值将被设置为 external-resource..amount
。external-resource..driver-factory.class
):可选配置。定义由 TaskManager
,只是算子在这种情况下无法从 RuntimeContext
中拿到该资源的信息。external-resource..param.
):可选配置。由 示例配置,该配置定义两个扩展资源:
external-resources: gpu;fpga # 定义两个扩展资源,“gpu”和“fpga”。
external-resource.gpu.driver-factory.class: org.apache.flink.externalresource.gpu.GPUDriverFactory # 定义 GPU 资源对应 Driver 的工厂类。
external-resource.gpu.amount: 2 # 定义每个 TaskManager 所需的 GPU 数量。
external-resource.gpu.param.discovery-script.args: --enable-coordination # 自定义参数 discovery-script.args,它将被传递到 GPU 对应的 Driver 中。
external-resource.fpga.driver-factory.class: org.apache.flink.externalresource.fpga.FPGADriverFactory # 定义 FPGA 资源对应 Driver 的工厂类。
external-resource.fpga.amount: 1 # 定义每个 TaskManager 所需的 FPGA 数量。
external-resource.fpga.yarn.config-key: yarn.io/fpga # 定义 FPGA 在 Yarn 中对应的配置键。
为了使用扩展资源,算子需要从 RuntimeContext
获取 ExternalResourceInfo
集合。 ExternalResourceInfo
包含了使用扩展资源所需的信息,可以使用 getProperty
检索这些信息。 其中具体包含哪些属性以及如何使用这些属性取决于特定的扩展资源插件。
算子可以通过 getExternalResourceInfos(String resourceName)
从 RuntimeContext
或 FunctionContext
中获取特定扩展资源的 ExternalResourceInfo
。 此处的 resourceName
应与在扩展资源列表中定义的名称相同。具体用法如下:
Java
public class ExternalResourceMapFunction extends RichMapFunction<String, String> {
private static final String RESOURCE_NAME = "foo";
@Override
public String map(String value) {
Set<ExternalResourceInfo> externalResourceInfos = getRuntimeContext().getExternalResourceInfos(RESOURCE_NAME);
List<String> addresses = new ArrayList<>();
externalResourceInfos.iterator().forEachRemaining(externalResourceInfo ->
addresses.add(externalResourceInfo.getProperty("address").get()));
// map function with addresses.
// ...
}
}
Scala
class ExternalResourceMapFunction extends RichMapFunction[(String, String)] {
var RESOURCE_NAME = "foo"
override def map(value: String): String = {
val externalResourceInfos = getRuntimeContext().getExternalResourceInfos(RESOURCE_NAME)
val addresses = new util.ArrayList[String]
externalResourceInfos.asScala.foreach(
externalResourceInfo => addresses.add(externalResourceInfo.getProperty("address").get()))
// map function with addresses.
// ...
}
}
ExternalResourceInfo
中包含一个或多个键-值对,其键值表示资源的不同维度。你可以通过 ExternalResourceInfo#getKeys
获取所有的键。
**提示:**目前,RuntimeContext#getExternalResourceInfos
返回的信息对所有算子都是可用的。
要为你所使用的扩展资源实现自定义插件,你需要:
org.apache.flink.api.common.externalresource.ExternalResourceDriver
接口。org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory
接口。META-INF/services/org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory
文件,其中包含了 Driver 对应工厂类的类名(更多细节请参看 Java Service Loader)。例如,要为名为“FPGA”的扩展资源实现插件,你首先需要实现 FPGADriver
和 FPGADriverFactory
:
Java
public class FPGADriver implements ExternalResourceDriver {
@Override
public Set<FPGAInfo> retrieveResourceInfo(long amount) {
// 返回 "FPGA" 的信息
}
}
public class FPGADriverFactory implements ExternalResourceDriverFactory {
@Override
public ExternalResourceDriver createExternalResourceDriver(Configuration config) {
return new FPGADriver();
}
}
// 包含 "FPGA" 资源基础配置的实现类 FPGAInfo
public class FPGAInfo implements ExternalResourceInfo {
@Override
public Optional<String> getProperty(String key) {
// 返回给定 key 的属性值
}
@Override
public Collection<String> getKeys() {
// 返回所有 key
}
}
Scala
class FPGADriver extends ExternalResourceDriver {
override def retrieveResourceInfo(amount: Long): Set[FPGAInfo] = {
// 返回 "FPGA" 的信息
}
}
class FPGADriverFactory extends ExternalResourceDriverFactory {
override def createExternalResourceDriver(config: Configuration): ExternalResourceDriver = {
new FPGADriver()
}
}
// 包含 "FPGA" 资源基础配置的实现类 FPGAInfo
class FPGAInfo extends ExternalResourceInfo {
override def getProperty(key: String): Option[String] = {
// 返回给定 key 的属性值
}
override def getKeys(): util.Collection[String] = {
// 返回所有 key
}
}
在 META-INF/services/
中创建名为 org.apache.flink.api.common.externalresource.ExternalResourceDriverFactory
的文件,向其中写入工厂类名,如 your.domain.FPGADriverFactory
。
之后,将 FPGADriver
,FPGADriverFactory
,META-INF/services/
和所有外部依赖打入 jar 包。在你的 Flink 发行版的 plugins/
文件夹中创建一个名为“fpga”的文件夹,将打好的 jar 包放入其中。更多细节请查看 Flink Plugin。
**提示:**扩展资源由运行在同一台机器上的所有算子共享。社区可能会在未来的版本中支持外部资源隔离。
目前,Flink提供了 GPU 资源插件。
我们为 GPU 提供了第一方插件。该插件利用一个脚本来发现 GPU 设备的索引,该索引可通过“index”从 ExternalResourceInfo
中获取。我们提供了一个默认脚本,可以用来发现 NVIDIA GPU。您还可以提供自定义脚本。
我们提供了一个示例程序,展示了如何在 Flink 中使用 GPU 资源来做矩阵-向量乘法。
**提示:**目前,对于所有算子,
RuntimeContext#getExternalResourceInfos
会返回同样的资源信息。也就是说,在同一个 TaskManager 中运行的所有算子都可以访问同一组 GPU 设备。扩展资源目前没有算子级别的隔离。
要使 GPU 资源可访问,根据您的环境,需要满足以下先决条件:
如启用扩展资源框架中所述,要使用 GPU 资源,还需要执行两项操作:
对于 GPU 插件,你需要指定的扩展资源框架配置:
external-resources
:你需要将 GPU 的扩展资源名称(例如“gpu”)加到该列表中。external-resource..amount
:每个 TaskManager 中的 GPU 数量。external-resource..yarn.config-key
:对于 Yarn,GPU 的配置键是 yarn.io/gpu
。请注意,Yarn 目前只支持 NVIDIA GPU。external-resource..kubernetes.config-key
:对于 Kubernetes,GPU 的配置键是 .com/gpu
。 目前,“nvidia”和“amd”是两个支持的 GPU 品牌。请注意,如果你使用 AMD GPU,你需要提供一个自定义的发现脚本。external-resource..driver-factory.class
:需要设置为 org.apache.flink.externalresource.gpu.GPUDriverFactory。此外,GPU 插件还有一些专有配置:
external-resource..param.discovery-script.path
:发现脚本的文件路径。 它既可以是绝对路径,也可以是相对路径,如果定义了“FLINK_HOME”,该路径将相对于“FLINK_HOME”,否则相对于当前目录。如果没有显式配置该项,GPU 插件将使用默认脚本。external-resource..param.discovery-script.args
:传递给发现脚本的参数。对于默认的发现脚本,请参见默认脚本以获取可用参数。GPU 插件示例配置:
external-resources: gpu
external-resource.gpu.driver-factory.class: org.apache.flink.externalresource.gpu.GPUDriverFactory # 定义 GPU 资源的工厂类。
external-resource.gpu.amount: 2 # 定义每个 TaskManager 的 GPU 数量。
external-resource.gpu.param.discovery-script.path: plugins/external-resource-gpu/nvidia-gpu-discovery.sh
external-resource.gpu.param.discovery-script.args: --enable-coordination # 自定义参数,将被传递到 GPU 的 Driver 中。
external-resource.gpu.yarn.config-key: yarn.io/gpu # for Yarn
external-resource.gpu.kubernetes.config-key: nvidia.com/gpu # for Kubernetes
GPUDriver
利用发现脚本来发现 GPU 资源并生成 GPU 资源信息。
我们为 NVIDIA GPU 提供了一个默认脚本,位于 Flink 发行版的 plugins/external-resource-gpu/nvidia-gpu-discovery.sh
。该脚本通过 nvidia-smi
工具获取当前可见 GPU 的索引。它尝试返回一个 GPU 索引列表,其大小由 external-resource.
指定,如果 GPU 数量不足,则以非零退出。
在 Standalone 模式中,多个 TaskManager 可能位于同一台机器上,并且每个 GPU 设备对所有 TaskManager 都是可见的。 默认脚本提供 GPU 协调模式,在这种模式下,脚本利用文件来同步 GPU 的分配情况,并确保每个GPU设备只能由一个TaskManager进程使用。相关参数为:
--enable-coordination-mode
:启用 GPU 协调模式。默认情况下不启用。--coordination-file filePath
:用于同步 GPU 资源分配状态的文件路径。默认路径为 /var/tmp/flink-gpu-coordination
。**提示:**协调模式只确保一个 GPU 设备不会被同一个 Flink 集群的多个 TaskManager 共享。不同 Flink 集群间(具有不同的协调文件)或非 Flink 应用程序仍然可以使用相同的 GPU 设备。
你可以提供一个自定义的发现脚本来满足你的特殊需求,例如使用 AMD GPU。请确保自定义脚本的的路径正确配置(external-resource.
)并且 Flink 可以访问。自定义的发现脚本需要:
GPUDriver
将 GPU 数量(由 external-resource..amount
定义)作为第一个参数传递到脚本中。external-resource..param.discovery-script.args
中自定义的参数会被附加在后面。Flink 提供了 history server,可以在相应的 Flink 集群关闭之后查询已完成作业的统计信息。
此外,它暴露了一套 REST API,该 API 接受 HTTP 请求并返回 JSON 格式的数据。
HistoryServer 允许查询 JobManager 存档的已完成作业的状态和统计信息。
在配置了 HistoryServer 和 JobManager 之后,你可以使用相应的脚本来启动和停止 HistoryServer:
# 13. 启动或者停止 HistoryServer
bin/historyserver.sh (start|start-foreground|stop)
默认情况下,此服务器绑定到 localhost
的 8082
端口。
目前,只能将 HistoryServer 作为独立的进程运行。
配置项 jobmanager.archive.fs.dir
和 historyserver.archive.fs.refresh-interval
需要根据 作业存档目录
和 刷新作业存档目录的时间间隔
进行调整。
JobManager
已完成作业的存档在 JobManager 上进行,将已存档的作业信息上传到文件系统目录中。你可以在 flink-conf.yaml
文件中通过 jobmanager.archive.fs.dir
设置一个目录存档已完成的作业。
# 14. 上传已完成作业信息的目录
jobmanager.archive.fs.dir: hdfs:///completed-jobs
HistoryServer
可以通过 historyserver.archive.fs.dir
设置 HistoryServer 监视以逗号分隔的目录列表。定期轮询已配置的目录以查找新的存档;轮询间隔可以通过 historyserver.archive.fs.refresh-interval
来配置。
# 15. 监视以下目录中已完成的作业
historyserver.archive.fs.dir: hdfs:///completed-jobs
# 16. 每 10 秒刷新一次
historyserver.archive.fs.refresh-interval: 10000
所包含的存档被下载缓存在本地文件系统中。本地目录通过 historyserver.web.tmpdir
配置。
请查看配置页面以获取配置选项的完整列表。
Flink 没有提供归档已完成作业日志的内置方法。但是,如果你已经有日志归档和浏览器服务,则可以通过 historyserver.log.jobmanager.url-pattern
和 historyserver.log.taskmanager.url-pattern
配置 HistoryServer 来整合他们。在这种方式下,你可以直接在 HistoryServer WebUI 上连接到相关的 JobManager/TaskManager 。
# 17. HistoryServer 会使用相关的作业 id 替换
historyserver.log.jobmanager.url-pattern: http://my.log-browsing.url/>
# 18. HistoryServer 会使用相关的作业 id 替换 ,使用 taskmanager id 替换
historyserver.log.taskmanager.url-pattern: http://my.log-browsing.url/>/>
以下是可用且带有示例 JSON 响应的请求列表。所有请求格式样例均为 http://hostname:8082/jobs
,下面我们仅列出了 URLs 的 path 部分。尖括号中的值为变量,例如作业 7684be6004e4e955c2a558a9bc463f65
的 http://hostname:port/jobs/
请求须写为 http://hostname:port/jobs/7684be6004e4e955c2a558a9bc463f65/exceptions
。
/config
/jobs/overview
/jobs/
/jobs//vertices
/jobs//config
/jobs//exceptions
/jobs//accumulators
/jobs//vertices/
/jobs//vertices//subtasktimes
/jobs//vertices//taskmanagers
/jobs//vertices//accumulators
/jobs//vertices//subtasks/accumulators
/jobs//vertices//subtasks/
/jobs//vertices//subtasks//attempts/
/jobs//vertices//subtasks//attempts//accumulators
/jobs//plan
/jobs//jobmanager/config
/jobs//jobmanager/environment
/jobs//jobmanager/log-url
/jobs//taskmanagers//log-url
所有的 Flink 进程都会创建一个进程中包含各种事件发生信息的日志文本文件,这些日志提供了对 Flink 内部工作原理的深度信息,并且可以被用来定位问题以及进行 debug。
log 文件可以通过 WebUI 界面的 Job/TaskManager 页面来访问。使用 资源框架 ,比如 YARN,可能会提供访问它们的其他方法。
Flink 的日志使用 SLF4J 日志接口,这允许你使用任何支持 SLF4J 的日志框架,而无需改变 Flink 的源码。
默认情况下,底层使用 Log4j 2 日志框架。
Log4j2 是使用配置文件指定的。
Flink发行版在 conf 目录中附带了以下 log4j 属性文件,如果启用 log4j 2,将自动使用这些文件:
log4j-cli.properties
:由 Flink 命令行客户端使用(例如 flink run
)log4j-session.properties
:Flink 命令行客户端在启动 YARN 或 Kubernetes session 时使用(yarn-session.sh
,kubernetes-session.sh
)log4j-console.properties
:由前台运行的 Job/TaskManager 使用,比如:Kubernetes。log4j.properties
:作为 JobManager/TaskManager 默认日志配置使用。Log4j 会定期浏览这些文件的更改,如果有需要,也会调整日志的行为。默认情况下,会定时 30 秒检查一次,可以通过 Log4j 配置文件中的 monitorInterval
来控制。
Flink 附带了 Log4j API bridge,使得现有作业能够继续使用 log4j1 的接口。
如果你有基于 Log4j 的自定义配置文件或代码,请查看官方 Log4j 兼容性和迁移指南。
要将 Flink 与 Log4j1 一起使用,必须确保:
org.apache.logging.log4j:log4j-core
,org.apache.logging.log4j:log4j-slf4j-impl
和 org.apache.logging.log4j:log4j-1.2-api
,log4j:log4j
,org.slf4j:slf4j-log4j12
,org.apache.logging.log4j:log4j-to-slf4j
和 org.apache.logging.log4j:log4j-api
。在 IDE 中使用 log4j1,你必须在 pom 文件中使用上述 Classpath 中存在的 jars
依赖项替换 Classpath 中不存在的 jars
依赖项,并尽可能在传递依赖于 Classpath 中不存在的 jars
的依赖项上添加排除 Classpath 中不存在的 jars
配置。
对于 Flink 发行版,这意味着你必须
lib
目录中移除 log4j-core
,log4j-slf4j-impl
和 log4j-1.2-api
jars,lib
目录中添加 log4j
,slf4j-log4j12
和 log4j-to-slf4j
jars,conf
目录中的所有 log4j 配置文件。为了让 Flink 使用 logback 你必须确保:
org.apache.logging.log4j:log4j-slf4j-impl
ch.qos.logback:logback-core
和 ch.qos.logback:logback-classic
在 IDE 中,这意味着你必须在你的 pom 文件中替换这些依赖定义,而且有可能还需要增加一些必要的额外依赖。
对于 Flink 部署,这意味着你必须:
lib
目录下移除 log4j-slf4j-impl
jar 包logback-core
和 logback-classic
jar 包到 lib
目录如果启用了 logback,Flink 会自动传递 conf
目录中下面的这些 logback 配置文件:
logback-session.properties
:启动 Kubernetes/Yarn session 集群时使用的命令行接口,比如:kubernetes-session.sh
/yarn-session.sh
。logback-console.properties
:在前台运行 Job/TaskManager 时使用,比如:Kubernets。logback.xml
:命令行接口和 Job/TaskManager 默认使用。Logback 1.3.+ 要求 SLF4J 2,目前还不支持。
你可以通过调用 org.slf4j.LoggerFactory#LoggerFactory.getLogger
,并将你的 class 类作为参数来创建一个 SLF4J 日志对象。
我们强烈建议将该对象存储为一个 private static final
属性。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Foobar {
private static final Logger LOG = LoggerFactory.getLogger(Foobar.class);
public static void main(String[] args) {
LOG.info("Hello world!");
}
}
为了最大限度地利用 slf4j,建议使用其占位符机制。使用占位符可以避免不必要的字符串构造,以防日志级别设置得太高而不会记录消息。
占位符的语法如下:
LOG.info("This message contains {} placeholders. {}", 2, "Yippie");
占位符也可以和要记录的异常一起使用。
catch(Exception exception){
LOG.error("An {} occurred.", "error", exception);
}