EMR Core 节点部署 Flink Client 实战:Bootstrap Action 一次打包多次复用,解决调度系统提交任务的痛点

在 EMR 上跑 Flink 的同学应该都碰到过一个问题:Flink Client 只装在 Master 节点上,Core 节点默认没有。

这在大多数场景下没问题------反正任务从 Master 提交就行了。但如果你在 Core 节点上跑 DolphinScheduler、Airflow 这类调度系统的 Worker 组件,问题就来了:Worker 想直接调用 flinkflink-sql-clientyarn-session.sh 提交任务,发现命令不存在。

只能绕路 SSH 到 Master 节点执行,架构复杂度和维护成本都上去了。

这篇文章记录我怎么用 Bootstrap Action 解决这个问题:从 Master 打包 Flink 二进制 → 上传 S3 → 新集群启动时自动分发到 Core/Task 节点。整个方案遵循"一次打包、多次复用"的原则,只要 EMR 版本不变,打包产物可以反复用。

环境说明

  • Amazon EMR 7.10.0
  • Flink 1.20.0-amzn-4(AWS 基于开源版本的定制构建,包含安全补丁和服务集成优化)
  • EMR 中的 Flink 版本与发行版绑定,选 emr-7.10.0 自动获得 Flink 1.20.0-amzn-4

第一步:创建打包用集群

先起一个临时集群用来打包 Flink 二进制文件:

bash 复制代码
aws emr create-cluster \
  --release-label emr-7.10.0 \
  --applications Name=Flink Name=Hadoop \
  --instance-type m5.xlarge \
  --instance-count 3 \
  --service-role EMR_DefaultRole \
  --ec2-attributes InstanceProfile=EMR_EC2_DefaultRole \
  --name "Flink-Source-Cluster"

等集群进入 WAITING 状态(约 5-10 分钟),记下 ClusterId

SSH 或 SSM 连接到 Master 节点:

bash 复制代码
# 验证 Flink 版本
flink --version

# 验证 Flink 在 YARN 上能跑
sudo -u hadoop /usr/lib/flink/bin/yarn-session.sh -d -jm 1024 -tm 2048
yarn application -list

踩坑提醒:在 Master 节点上必须用完整路径 /usr/lib/flink/bin/yarn-session.sh,否则会因 $bin 路径解析问题报错。同时需要用 hadoop 用户才有 HDFS 写权限。

打包并上传 S3:

bash 复制代码
# 打包二进制文件(bin、lib、opt、plugins、examples)
tar -czf flink-client-1.20.0-amzn-4.tar.gz -C /usr/lib flink

# 打包配置文件
tar -czf flink-conf.tar.gz -C /etc flink

# 上传到 S3
aws s3 cp flink-client-1.20.0-amzn-4.tar.gz s3://<your-bucket>/emr/bootstrap/
aws s3 cp flink-conf.tar.gz s3://<your-bucket>/emr/bootstrap/

这两个文件就是以后反复用的产物,只要 EMR 版本不变就不用重新打包。

第三步:编写 Bootstrap Action 脚本

这是核心部分。脚本会检测节点类型,只在 Core/Task 节点上执行安装:

bash 复制代码
#!/bin/bash
# install_flink_client.sh
set -e

S3_BUCKET="<your-bucket>"
FLINK_TARBALL="flink-client-1.20.0-amzn-4.tar.gz"
FLINK_CONF_TARBALL="flink-conf.tar.gz"
S3_PREFIX="s3://${S3_BUCKET}/emr/bootstrap"

IS_MASTER=$(cat /emr/instance-controller/lib/info/instance.json | jq -r '.isMaster')

if [ "$IS_MASTER" = "false" ]; then
    echo "Core/Task node detected, installing Flink client..."

    # 1. 下载并解压 Flink 二进制
    aws s3 cp "${S3_PREFIX}/${FLINK_TARBALL}" /tmp/
    sudo tar -xzf "/tmp/${FLINK_TARBALL}" -C /usr/lib/

    # 2. 下载并解压配置文件
    aws s3 cp "${S3_PREFIX}/${FLINK_CONF_TARBALL}" /tmp/
    sudo tar -xzf "/tmp/${FLINK_CONF_TARBALL}" -C /etc/

    # 3. 修复 /etc/flink/conf 的 broken symlink
    if [ -L "/etc/flink/conf" ] && [ ! -e "/etc/flink/conf" ]; then
        echo "Fixing broken symlink /etc/flink/conf..."
        sudo rm -f /etc/flink/conf
        if [ -d "/etc/flink/conf.dist" ]; then
            sudo cp -r /etc/flink/conf.dist /etc/flink/conf
        else
            sudo mkdir -p /etc/flink/conf
        fi
    fi

    # 4. 修复 /usr/lib/flink/conf 的 broken symlink
    if [ -L "/usr/lib/flink/conf" ] && [ ! -e "/usr/lib/flink/conf" ]; then
        sudo rm -f /usr/lib/flink/conf
        sudo ln -sf /etc/flink/conf /usr/lib/flink/conf
    fi

    # 5. 创建 wrapper scripts(不用 symlink!)
    sudo tee /usr/bin/flink > /dev/null << 'WRAPPER'
#!/bin/bash
exec /usr/lib/flink/bin/flink "$@"
WRAPPER
    sudo chmod +x /usr/bin/flink

    sudo tee /usr/bin/flink-sql-client > /dev/null << 'WRAPPER'
#!/bin/bash
exec /usr/lib/flink/bin/sql-client.sh "$@"
WRAPPER
    sudo chmod +x /usr/bin/flink-sql-client

    sudo tee /usr/bin/yarn-session.sh > /dev/null << 'WRAPPER'
#!/bin/bash
exec /usr/lib/flink/bin/yarn-session.sh "$@"
WRAPPER
    sudo chmod +x /usr/bin/yarn-session.sh

    # 6. 环境变量
    cat << 'ENVEOF' | sudo tee /etc/profile.d/flink.sh
export FLINK_HOME=/usr/lib/flink
export PATH=$FLINK_HOME/bin:$PATH
ENVEOF

    # 7. 清理
    rm -f "/tmp/${FLINK_TARBALL}" "/tmp/${FLINK_CONF_TARBALL}"
    echo "Flink client installation completed."
else
    echo "Master node detected, skipping."
fi

上传脚本:

bash 复制代码
aws s3 cp install_flink_client.sh s3://<your-bucket>/emr/bootstrap/

这是个踩过的坑。Flink 的 bin 目录下的脚本内部会用 . "$bin"/config.sh 这样的相对路径引用。如果用 symlink(ln -s /usr/lib/flink/bin/flink /usr/bin/flink),$bin 会解析为 /usr/bin/ 而不是 /usr/lib/flink/bin/,导致 config.sh 找不到。

用 wrapper scripts 的 exec 命令则不会有这个问题------它直接调用原始路径的脚本,$bin 自然解析为 /usr/lib/flink/bin/

EMR 在 Master 节点上会创建 /etc/flink/conf → /etc/alternatives/flink-conf 这个 symlink,但 Core 节点上 /etc/alternatives/flink-conf 不存在。打包配置文件后解压到 Core,这个 symlink 就是 broken 的。

脚本的处理逻辑:如果 /etc/flink/conf.dist 存在就复制一份,否则创建空目录。

第四步:创建正式集群

bash 复制代码
aws emr create-cluster \
  --release-label emr-7.10.0 \
  --applications Name=Flink Name=Hadoop \
  --bootstrap-actions Path=s3://<your-bucket>/emr/bootstrap/install_flink_client.sh,Name="Install Flink Client on Core Nodes" \
  --instance-type m5.xlarge \
  --instance-count 3 \
  --service-role EMR_DefaultRole \
  --ec2-attributes InstanceProfile=EMR_EC2_DefaultRole \
  --name "Flink-Production-Cluster"

第五步:验证

Core 节点没有公网 IP,需要通过 Master 跳转。SSH 到 Master 后跳到任意 Core 节点:

bash 复制代码
flink --version
flink-sql-client --help
yarn-session.sh --help

排查问题看 Bootstrap Action 日志:

bash 复制代码
sudo cat /var/log/bootstrap-actions/1/stdout
sudo cat /var/log/bootstrap-actions/1/stderr

方案总结

维度 说明
适用版本 EMR 7.10.0 + Flink 1.20.0-amzn-4
复用性 同版本 EMR 可直接复用 S3 上的打包文件
执行时机 集群启动时自动执行,无需手动干预
影响范围 仅 Core/Task 节点,Master 节点跳过
维护成本 EMR 升级时需重新打包

整个方案的核心思路:把 Master 节点上已有的东西打包搬运到 Core 节点,而不是从头装一遍。简单直接,没有魔法。

生产环境建议:上线前通过 Bootstrap Action 日志和节点级命令验证确认安装结果,避免调度系统在生产中因环境缺失而静默失败。

本文参考亚马逊云科技官方博客:在 AWS EMR Core 节点部署 Flink Client 的实战指南

相关推荐
学掌门1 小时前
数据分析师职业规划——数据分析师的职业焦虑与未来发展
大数据·信息可视化
盘古信息IMS2 小时前
九宸纳百川,数智启新程|盘古信息与合肥昊邦科技合资成立合肥九宸智能,共筑智造新生态
大数据·人工智能
Irene19912 小时前
大数据开发语境下,SQL 模式名,映射关系 - - 概念理解
大数据·数据库·sql
小熊美家熊猫系统2 小时前
社区家政与平台家政:两种创业模式的深度对比分析
大数据·家政行业·社区家政·平台家政·家政管理软件
互联网志2 小时前
打通转化通道 赋能产业发展——高校科技成果转化的现状与破局
大数据·人工智能·物联网
绿虫光伏运维2 小时前
一文理清光伏运维的内容、常见问题与重要措施
大数据·运维·光伏业务
zandy10112 小时前
HENGSHI SENSE 6.2 架构全景解析:Data Agent、指标引擎与Headless语义层的工程实现
大数据·人工智能·架构
我是发哥哈2 小时前
主流AI框架生产环境性能对比:5大关键维度深度评测
大数据·人工智能·学习·机器学习·ai·chatgpt·ai-native
无忧智库3 小时前
数据驱动精准农业:高标准农田地力与微环境多维异构数据集建设深度解析(WORD)
大数据