這是在去年 AWS re:Invent 2020 發佈的新功能之一「New – Amazon EMR on Amazon Elastic Kubernetes Service (EKS)」讓 Amazon EMR 可以跑在 Amazon EKS 上,直到現在才有空來把文章補齊,順手紀錄這中間的差異。
在這之前 Amazon EMR 都是基於 Amazon EC2 跑一組 Hadoop MapReduce Cluster 這讓整個 bootstrap 時間拉的很長,光是等待時間就很惱人。而 Amazon EMR on EKS 大幅減少了 bootstrap 的時間以外,跑在 Amazon EKS 上就意味著 Data engineer 只需專注在 Jobs 上的運算。
Architecture
從左右明顯的比較當需要執行不同的 MapReduce / Spark 版本時 Amazon EMR on EKS 能以更好的空間利用率跑在 Amazon EKS 上,而 Amazon EMR on EC2 則會有許多冗余的資源沒有利用到以外 Bootstrap 等待時間更是問題。
Amazon EMR on EKS 提供標準的 API interface 也因為底層由 Kubernetes 實作,Amazon EMR 基本上不需管理底層而是由 Kubernetes 負責 HA 和調度的工作。
Prerequisites
Amazon EMR on EKS 會用到 eksctl、awscli、kubectl 三個不同的 Command line Tools 以及預先準備好的 Amazon EKS Cluster。除此之外 IRSA 和 RBAC 也是在這個實作過程中必須知道的實作概念。
How to submit Spark job on Amazon EMR on EKS
在 step-by-step 的部分我就不提太多細節,詳細步驟可以參考 EMR on EKS Setting up、EMR on EKS Workshop 或是「一小時內搭建好 Amazon EMR on EKS 運行 Spark on Kuberentes」,在這篇我主要提起幾個步驟中需要注意的部分。
設定 RBAC configuration 授與 Amazon EMR submit jobs 到 Amazon EKS
$ eksctl create iamidentitymapping --cluster eks-sample --namespace <kubernetes-namespace>--service-name "emr-containers"
eksctl create iamidentitymapping
這個動作會完成幾件事:
- 在
aws-auth
加入一個mapRoles
允 AWSServiceRoleForAmazonEMRContainers 這個 IAM Role 訪問 Amazon EKS - 在 Kubernetes 建立 Role, RoleBinding
emr-containers
apiVersion: v1
data:
mapRoles: |
- rolearn: arn:aws:iam::${accountId}:role/AWSServiceRoleForAmazonEMRContainers
username: emr-containers
...
而在 AWSServiceRoleForAmazonEMRContainers 這個 ServiceRole 必須 trust emr-containers.amazonaws.com
這個服務的授權以及掛上 AmazonEMRContainersServiceRolePolicy Managed Policy。
設定 IRSA 授與 Spark jobs 擁有 AWS 服務訪問權限
AmazonEMRContainersJobExecutionRole 這個 IAM Role 通常至少會有 Amazon S3 和 CloudWatch 寫入 Logs 的權限
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents",
"logs:CreateLogStream",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
],
"Resource": [
"arn:aws:logs:*:*:*"
]
}
]
}
IRSA 則是可以用 eksctl create iamserviceaccount
和 aws emr-containers
來建立
$ eksctl create iamserviceaccount \
--name aws-emr-on-eks-job-execution \
--namespace amazon-emr \
--cluster eks-sample \
--attach-policy-arn arn:aws:iam::<accountId>:policy/AmazonEMRContainersJobExecutionRolePolicy \
--approve \
--override-existing-serviceaccounts
$ aws emr-containers update-role-trust-policy --cluster-name eks-sample --namespace amazon-emr --role-name AmazonEMRContainersJobExecutionRole
aws emr-containers
會更新 trust policy 加上第二個 sts:AssumeRoleWithWebIdentity
允許來自 Spark job 的 ServiceAccount
Service Account
emr-containers-sa-–-${accountId}-*
IRSA 可以檢查幾個資源:
- Kubernetes ServiceAccount:
aws-emr-on-eks-job-execution
並且 mapping 到 IAM Role - IAM Role
AmazonEMRContainersJobExecutionRole
將信任來自 Kubernetes ServiceAccount 的sts:AssumeRoleWithWebIdentity
若由 eksctl 建立則 IAM Role name 為 eksctl-*,但在此篇姑且稱為 AmazonEMRContainersJobExecutionRole
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::${accountId}:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/0B05CA2EC6D6EC592EB9900DB42673DA"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.us-east-1.amazonaws.com/id/0B05CA2EC6D6EC592EB9900DB42673DA:sub": "system:serviceaccount:amazon-emr:aws-emr-on-eks-job-execution",
"oidc.eks.us-east-1.amazonaws.com/id/0B05CA2EC6D6EC592EB9900DB42673DA:aud": "sts.amazonaws.com"
}
}
},
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::${accountId}:oidc-provider/oidc.eks.us-east-1.amazonaws.com/id/0B05CA2EC6D6EC592EB9900DB42673DA"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"oidc.eks.us-east-1.amazonaws.com/id/0B05CA2EC6D6EC592EB9900DB42673DA:sub": "system:serviceaccount:amazon-emr:emr-containers-sa-*-*-${accountId}-gae37tpvfjmnnjvlpdmfzgzvzxeu1sdge2kmsimjra0hmaa6tam0yt"
}
}
}
]
}
Create Virtual Cluster for Amazon EMR
在撰文的當下 Amazon EMR on EKS 還沒有支援 Management Console 的方法,所以多數操作必須仰賴 aws emr-containers
來建立 Virtual cluster。
$ aws emr-containers create-virtual-cluster \
--name eks-sample \
--container-provider '{
"id": "eks-sample",
"type": "EKS",
"info": {
"eksInfo": {
"namespace": "amazon-emr"
}
}
}'
--container-provider
指定的是 Amazon EKS 顆粒度可以到 namespace,假設有 Multitenancy 需求可以多利用 namespace 來切割不同 EMR Virtual cluster
Running a Map Reduce Job
當 Virtual cluster 建立完成後會拿到 virtual cluster id,如果不小心洗掉的話可以用 describe-virtual-cluster 查到
$ aws emr-containers start-job-run \
--virtual-cluster-id <emr-virtual-cluster-id> \
--name spark-sample-job \
--execution-role-arn arn:aws:iam::381354187112:role/AmazonEMRContainersJobExecutionRole \
--release-label emr-6.3.0-latest \
--job-driver '{
"sparkSubmitJobDriver": {
"entryPoint": "local:///usr/lib/spark/examples/src/main/python/pi.py",
"sparkSubmitParameters": "--conf spark.executor.instances=1 --conf spark.executor.memory=2G --conf spark.executor.cores=1 --conf spark.driver.cores=1"
}}' \
--configuration-overrides '{
"applicationConfiguration": [{
"classification": "spark-defaults",
"properties": {
"spark.driver.memory": "2G"
}
}],
"monitoringConfiguration": {
"cloudWatchMonitoringConfiguration": {
"logGroupName": "/aws/emr-eks/sample",
"logStreamNamePrefix": "pi"
},
"persistentAppUI":"ENABLED",
"s3MonitoringConfiguration": {
"logUri": "s3://aws-emr-on-eks-us-east-1-381354187112"
}}}'
Submit Job 在 Amazon EKS 上可以找到在 amazon-emr
namespace 開始連續跑三個 Pods 分別是 job-runner, spark-driver 以及 execute job,運行資訊也可以在 Management Console 查到。
Summary tips for Amazon EMR on EKS
- Amazon EMR 以 IAM Role
AWSServiceRoleForAmazonEMRContainers
透過RBAC
方法授權 Submit job 到 Amazon EKS - Amazon EKS Spark job 透過
IRSA
方法以 IAMAmazonEMRContainersJobExecutionRole
訪問 AWS 服務 - 一個 Amazon EMR Spark job 會運行 Amazon EKS pods:
job-runner
包含 job-runner, emr-container-fluentd 以及 fluentd-data-collectorspark-driver
包含 spark-kubernetes-driver, emr-container-fluentdexecute job
包含 spark-kubernetes-executor, emr-container-fluentd
Performance of Amazon EMR with EC2 and Fargate
在最後我做了一個簡單的效能測試,用上述的 Spark job 跑在 Amazon EC2 和 AWS Fargate 時 bootstrap 的時間:
- 跑在 Amazon EC2 有 pre-warm worker node 時 bootstrap 大約 1 分鐘
- 跑在 Amazon EC2 沒有 pre-warm worker node 時 bootstrap 大約 7 分鐘
- 跑在 AWS Fargate 沒有 pre-warm worker node 時 bootstrap 大約 5 分鐘
以上資訊提供參考,以原本 Amazon EMR 需要 15 分鐘以上 bootstrap 時間來說,大幅減少啟動時間。