创建并运行 EMR on EKS 集群

Posted bluishglc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建并运行 EMR on EKS 集群相关的知识,希望对你有一定的参考价值。


EMR on EKS的创建工作完全是命令行驱动的,目前尚无对应的UI界面来完成相关操作。本文将通过命令行演示如何创建并运行一个EMR on EKS集群。创建EMR on EKS的过程可以分为两个阶段:第一阶段是先创建出一个EKS集群,第二阶段是在这个EKS集群之上创建EMR的虚拟集群,以下是具体操作步骤。

注:在操作过程中,我们将会陆续得到一些值,例如EKS集群的名称,虚拟集群的ID,这些变量在后续的操作中会再次使用,为了便于提升文中脚本的可复用性,我们会单独将这些值抽取出来,赋给一个变量,同时用export导出,便于后续的引用。以下是操作过程中将会生成并被引用到的一些变量,以及本例我们将采用的值:

变量名称本例取值描述
REGIONus-east-1当前所处的AWS REGION
ZONESus-east-1a,us-east-1b,us-east-1c分配给将要创建的EKS集群的可用区
EKS_CLUSTER_NAMEit-infrastructure将要创建的EKS集群的名称
DATALAKE_NAMESPACEdatalake将要在EKS上创建的面向数据系统的Kubenetes命名空间,将要创建的EMR on EKS虚拟集群会被置于该空间下
VIRTUAL_CLUSTER_NAMEemr-cluster-1将要创建的EMR on EKS虚拟集群的名字
SSH_PUBLIC_KEY<从EC2->Kye Pairs处查找>将要创建的EKS集群需要指定公钥
EXECUTION_ROLE_ARN<从IAM的Admin Role处查找>用于运行EMR on EKS的IAM Role
VIRTUAL_CLUSTER_ID<过程中产生>将要创建的EMR on EKS虚拟集群的ID

以下是为上述全局变量赋值的命令(VIRTUAL_CLUSTER_ID将在后续操作中产生,暂不赋值):

export REGION="us-east-1"
export ZONES="us-east-1a,us-east-1b,us-east-1c"
export EKS_CLUSTER_NAME="it-infrastructure"
export DATALAKE_NAMESPACE="datalake"
export VIRTUAL_CLUSTER_NAME="emr-cluster-1"
export SSH_PUBLIC_KEY="<your-pub-key-name>"
export EXECUTION_ROLE_ARN="<your-admin-role-arn>"

0. 前置条件

  • 确保有你有一台Linux主机,并已安装awscli命令行
  • 确保配置给awscli的access_key属于一个Admin账号

1. 安装ekscli

ekscli是用于操作eks的命令行工具,我们需要使用到该工具,须先行安装,安装命令如下:

curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin

2. 安装kubectl

kubectl是用于管理kubenetes集群的命令行工具,我们需要使用到该工具,须先行安装,安装命令如下:

curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.20.4/2021-04-12/bin/linux/amd64/kubectl
chmod +x ./kubectl
mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$PATH:$HOME/bin
echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc

3. 创建EKS集群

接下来,我们要在美东1创建名为ABC_IT_INFRASTRUCTURE的EKS集群,命令如下:

eksctl create cluster \\
    --region $REGION \\
    --name $EKS_CLUSTER_NAME \\
    --zones $ZONES \\
    --node-type m5.xlarge \\
    --nodes 5 \\
    --with-oidc \\
    --ssh-access \\
    --ssh-public-key $SSH_PUBLIC_KEY \\
    --managed

上述命令行需要注意如下几点:

  • $SSH_PUBLIC_KEY为你在AWS上的公钥key的ID,这个字符串可在EC2控制台->Key Pairs处查找,name列即是;
  • --zones并不是必选项,如不指定,会随机选择AZ,但是有时随机选择的AZ在创建时并没有足够的资源支撑请求创建的EKS集群,这时就需要显式地指定zone来避开不可用的zone;
  • --node-type--nodes也不是必选项,如不指定,集群默认部署在2个m5.large节点上,对于EMR来说,这个集群的配置太低了,所以必须显式配置这两项,赋予集群更大的资源;

上述命令行需要执行较长时间(约20分钟左右),当最后出现:

EKS cluster "ABC_IT_INFRASTRUCTURE" in "us-east-1" region is ready

表明EKS集群已经建好。需要注意的是,该命令在执行过程中会通过Cloud Formation创建大量的基础设施,包括IAM Role,VPC,EC2等等,中途发生错误的可能性较大,且很多操作是不能自动回滚的,所以需要打开Cloud Formation的控制台并持续关注,如发现未清理的Stack,须手动删除Stack后再重新执行上述命令。

eksctl create cluster还有很多可配置的选项,可以通过如下命令查看详细说明:

eksctl create cluster -h

4. 查看EKS集群状态

EKS集群创建完成后,为确保集群是否健康,可通过命令行查看一下集群状况(该步骤非必须,可跳过)。

  • 查看集群各物理节点状况
kubectl get nodes -o wide
  • 查看集群POD的状况
kubectl get pods --all-namespaces -o wide

5. 创建Namespace

为便于对资源进行管理,我们可以在Kubenetes集群上为数据相关的系统创建单独的namespace,取名ABC_DATALAKE,后续创建的EMR虚拟集群将被置于该namespace下:

kubectl create namespace $DATALAKE_NAMESPACE

6. 授权访问Namespace

默认情况下,EMR on EKS是无权直接访问和使用EKS上的namespace的,需要我们创建一个Kubernetes role,然后将该Role绑定到一个Kubernetes user上,同时将一个服务角色AWSServiceRoleForAmazonEMRContainers映射到这个user上,这样才能桥接Kubenetes端和EMR on EKS服务端之间的权限认证,幸运的是我们不需要手动逐一完成这些操作,通过一条eksctl命令可以直接实现:

eksctl create iamidentitymapping \\
    --region $REGION \\
    --cluster $EKS_CLUSTER_NAME \\
    --namespace $DATALAKE_NAMESPACE \\
    --service-name "emr-containers"

控制台的输出也会印证上面的论述:

2021-06-02 12:39:49 [ℹ]  created "datalake:Role.rbac.authorization.k8s.io/emr-containers"
2021-06-02 12:39:49 [ℹ]  created "datalake:RoleBinding.rbac.authorization.k8s.io/emr-containers"
2021-06-02 12:39:49 [ℹ]  adding identity "arn:aws:iam::1234567898765:role/AWSServiceRoleForAmazonEMRContainers" to auth ConfigMap

7. 创建Job Execution Role

运行EMR on EKS需要一个IAM Role,在这个Role中要配置授权EMR on EKS可以使用的资源有哪些,例如s3上某些桶,cloudwatch等服务,这些被称之为Role Policies,官方文档给出过一份Role Policies的参考配置,可参见:https://docs.aws.amazon.com/emr/latest/EMR-on-EKS-DevelopmentGuide/creating-job-execution-role.html

为方便起见,本文将直接使用Admin角色作为job execution role

8. 创建Role的Trust Relationship

如果通过第7步创建了一个role,还需要对这个role进行编辑,添加这个role和EMR服务账号(EMR managed service account)之间的互信。这里所谓的EMR服务账号(EMR managed service account)是在job提交时自动创建的,所以在配置中在EMR服务账号部分会使用统配符。

不过幸运的是,我们不需要手动编辑Role的Trust Relationships部分,我们可以如下命令行自动添加这个Trust Relationship:

aws emr-containers update-role-trust-policy \\
   --cluster-name $EKS_CLUSTER_NAME \\
   --namespace $DATALAKE_NAMESPACE \\
   --role-name <Admin or the-job-excution-role-name-you-created>

其中,你需要将<Admin or the-job-excution-role-name-you-created>替换为Admin或者是在第7步中创建的role的名称。当创建成功之后,可以在Role的Trust Relationships页面看到生成的类似下面的相关配置:

{
  "Effect": "Allow",
  "Principal": {
    "Federated": "arn:aws:iam::1234567898765:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/1C2DF227CD8E011A693BCF03D7EBD581"
  },
  "Action": "sts:AssumeRoleWithWebIdentity",
  "Condition": {
    "StringLike": {
      "oidc.eks.us-east-1.amazonaws.com/id/1C2DF227CD8E011A693BCF03D7EBD581:sub": "system:serviceaccount:kube-system:emr-containers-sa-*-*-1234567898765-3l0vgne6"
    }
  }
}

即使我们在第7步选择使用Admin角色作为job execution role,该步操作依然需要执行,–role-name取值Admin, 否则在作业执行过程中无权完成创建Log Group以及在s3上存储日志等操作

9. 在EKS上创建EMR虚拟集群

接下来我们就将创建EMR集群了,其实更准确的叫法应该是“注册”,因为这一步执行完成后并不会在EKS上生成一个EMR集群,这里创建的是一个虚拟的集群,集群要在第一次提交作业时才会创建。创建集群的命令如下:

# create virtual cluster description file
tee $VIRTUAL_CLUSTER_NAME.json <<EOF
{
  "name": "$VIRTUAL_CLUSTER_NAME",
  "containerProvider": {
    "type": "EKS",
    "id": "$EKS_CLUSTER_NAME",
    "info": {
      "eksInfo": {
        "namespace": "$DATALAKE_NAMESPACE"
      }
    }
  }
}
EOF

# create virtual cluster
aws emr-containers create-virtual-cluster --cli-input-json file://./$VIRTUAL_CLUSTER_NAME.json

上述命令先创建一个集群描述文件$VIRTUAL_CLUSTER_NAME.json, 这个文件描述了EMR集群的名称以及要建在哪个EKS集群的哪个Namespace上,然后通过aws emr-containers create-virtual-cluster创建出这个文件描述的虚拟集群。

上述命令如果执行成功,会在控制台输出一份描述集群的json数据,其中的id字段较为重要,后续提交作业时都会使用到这个id,如果没有保存下来,也可以通过如下命令随时查询:

aws emr-containers list-virtual-clusters

将获得的id付给全局变量VIRTUAL_CLUSTER_ID,后续操作将会多次引用到该ID:

export VIRTUAL_CLUSTER_ID='<cluster-id>'

10. 向EMR on EKS提交作业

虚拟集群建好之后,就可以提交大数据作业了,EMR on EKS是基于容器的,不同于EMR通过shell登录进行操作(可以但不方便),常规的使用方式是将其视为一个计算资源的黑盒,向其提交作业即可。以下是一条向EMR on EKS提交作业的示例命令,它执行的是spark自带的example程序pi.py

aws emr-containers start-job-run \\
--virtual-cluster-id $VIRTUAL_CLUSTER_ID \\
--name sample-job-name \\
--execution-role-arn $EXECUTION_ROLE_ARN \\
--release-label emr-6.2.0-latest \\
--job-driver '{"sparkSubmitJobDriver": {"entryPoint": "local:///usr/lib/spark/examples/src/main/python/pi.py","sparkSubmitParameters": "--conf spark.executor.instances=2 --conf spark.executor.memory=2G --conf spark.executor.cores=2 --conf spark.driver.cores=1"}}' \\
--configuration-overrides '{"monitoringConfiguration": {"cloudWatchMonitoringConfiguration": {"logGroupName": "/emr-on-eks/$VIRTUAL_CLUSTER_NAME", "logStreamNamePrefix": "pi"}}}'

start-job-run这条命令最需要关注的是--job-driver这个参数,所有关于作业本身的相关信息都在这个参数里了。基于文档可知,目前的EMR on EKS仅支持sparkSubmitJobDriver一种形式的作业提交,即只能是以spark-submit可接受的形式提交作业,也就是通过jar包+class或pyspark脚本的形式提交作业。jar包及其依赖jar文件可部署在s3上。

一种更加优雅的作业提交方式是提供一份job run的json描述文件,把所有集群、作业和作业配置相关的信息集中配置在这份json文件中,然后通过命令执行,如下所示:

# create job description file
tee start-job-run-request.json <<EOF
{
  "name": "sample-job-name", 
  "virtualClusterId": "$VIRTUAL_CLUSTER_ID",  
  "executionRoleArn": "$EXECUTION_ROLE_ARN", 
  "releaseLabel": "emr-6.2.0-latest", 
  "jobDriver": {
    "sparkSubmitJobDriver": {
      "entryPoint": "local:///usr/lib/spark/examples/src/main/python/pi.py",
      "sparkSubmitParameters": "--conf spark.executor.instances=2 --conf spark.executor.memory=2G --conf spark.executor.cores=2 --conf spark.driver.cores=1"
    }
  }, 
  "configurationOverrides": {
    "applicationConfiguration": [
      {
        "classification": "spark-defaults", 
        "properties": {
          "spark.driver.memory":"2G"
         }
      }
    ], 
    "monitoringConfiguration": {
      "persistentAppUI": "ENABLED", 
      "cloudWatchMonitoringConfiguration": {
    "logGroupName": "/emr-on-eks/$VIRTUAL_CLUSTER_NAME", 
        "logStreamNamePrefix": "pi"
      }, 
      "s3MonitoringConfiguration": {
        "logUri": "s3://glc-emr-on-eks-logs/"
      }
    }
  }
}
EOF
# start job
aws emr-containers start-job-run --cli-input-json file://./start-job-run-request.json

关于json文件的编写,可以参考:https://docs.aws.amazon.com/emr/latest/EMR-on-EKS-DevelopmentGuide/emr-eks-jobs-CLI.html#emr-eks-jobs-submit

最后是关于EMR集群的配置,与纯EMR集群类似,集群配置也是通过json文件提交的,写到applicationConfiguration里面,例如上述配置中的"classification": "spark-defaults"部分。由于EMR on EKS目前仅支持Spark,所以也只有如下几类classification可配置

ClassificationsDescriptions
core-siteChange values in Hadoop’s core-site.xml file.
emrfs-siteChange EMRFS settings.
spark-metricsChange values in Spark’s metrics.properties file.
spark-defaultsChange values in Spark’s spark-defaults.conf file.
spark-envChange values in the Spark environment.
spark-hive-siteChange values in Spark’s hive-site.xml file.
spark-log4jChange values in Spark’s log4j.properties file.

11. 删除与清理

删除与清理集群的顺序应与创建过程相反,先删除ERM虚拟集群,然后再删除EKS集群:

# 1. list all jobs
aws emr-containers list-job-runs --virtual-cluster-id $VIRTUAL_CLUSTER_ID

# 2. cancel running jobs
aws emr-containers cancel-job-run --id <job-run-id> --virtual-cluster-id $VIRTUAL_CLUSTER_ID

# 3. delete virtual cluster
aws emr-containers delete-virtual-cluster --id $VIRTUAL_CLUSTER_ID

# 4. delete eks cluster
eksctl delete cluster --region $REGION --name $EKS_CLUSTER_NAME

注意:第4步在删除EKS集群时,须找到对应Cloud Formation模板里的一项资源NodeInstanceRole,手动dettach 该Role上的所有policies,命令才能执行成功。

12. 常见错误

  • 通过eksctl create cluster创建的eks集群默认是两个m5.large节点,这个配置很难支撑一个EMR集群,所以务必要指定一下节点数量和节点类型:

  • 如果在第3步创建EKS集群遇到类似如下的错误:

AWS::EKS::Cluster/ControlPlane: CREATE_FAILED – "Cannot create cluster 'my-bigdata-infra-cluster' because us-east-1e, the targeted availability zone, does not currently have sufficient capacity to support the cluster. Retry and choose from these availability zones: us-east-1a, us-east-1b, us-east-1c, us-east-1d, us-east-1f (Service: AmazonEKS; Status Code: 400; Error Code: UnsupportedAvailabilityZoneException; Request ID: 61028748-0cc1-4100-9152-aab79a475fe6; Proxy: null)"

说明自动分配或指定的某一个AZ目前不可用,可在--zones参数列表中取其他AZ替换。

关于作者:架构师,15年IT系统开发和架构经验,对大数据、企业级应用架构、SaaS、分布式存储和领域驱动设计有丰富的实践经验,热衷函数式编程。对Hadoop/Spark 生态系统有深入和广泛的了解,参与过Hadoop商业发行版的开发,曾带领团队建设过数个完备的企业数据平台,个人技术博客:https://laurence.blog.csdn.net/ 作者著有《大数据平台架构与原型实现:数据中台建设实战》一书,该书已在京东和当当上线。
在这里插入图片描述

以上是关于创建并运行 EMR on EKS 集群的主要内容,如果未能解决你的问题,请参考以下文章

EMR EKS 无法启动驱动程序 pod

Terraform EKS 配置图被禁止

Terraform EKS 标记

创建 EMR 集群时出错,EMR 服务角色无效

用于创建 EMR 集群的 Lambda 不会触发集群创建

我在 AWS 中有一个现有的 EMR 集群。我想从气流运行 dag 到现有的 aws 集群