Blogical

AWS/Salesforceを中心に様々な情報を配信していきます(/・ω・)/

AWS EMRを使ってみた!

こんにちは、ロジカル・アーツの井川です。

今日は、AWS EMR について紹介していきたいと思います。

AWS EMRとは?

EMRドキュメント によると、EMRは以下のように説明されています。 要するに、機械学習等で用いられる分散フレームワークApache Spark や Apache Map Reduce等が使える、AWSのマネージドサービスになります。

Amazon EMR は、AWSビッグデータフレームワーク (Apache HadoopApache Spark など) の実行を簡素化して、大量のデータを処理および分析するマネージド型>クラスタープラットフォームです。これらのフレームワークと関連するオープンソースプロジェクト (Apache Hive や Apache Pig など) を使用することで、分析用のデ>ータやビジネスインテリジェンスワークロードを処理できます。さらに、Amazon EMR を使用して大量のデータを変換し、Amazon Simple Storage Service (Amazon S3) >や Amazon DynamoDB などの他の AWS データストアおよびデータベースとの間で移動することもできます。

EMR 6.0.0の紹介

EMRはApache Spark等の分散フレームワークを使うことができるのですが、チームで Pythonや R ライブラリの依存関係を管理するのが難しいことがあります。 そのような課題を解決してくれるのが、EMR 6.0.0です。EMR 6.0.0はDockerを用いてライブラリの依存関係を管理することができるため、環境の違いを気にする必要がありません。 以下では、EMR 6.0.0 を用いたハンズオンを行います。

実際に触ってみる

今回は AWSの公式ブログを参考にハンズオンを進めていきたいと思います。

EMR 6.0.0 の Docker を使用して、Spark の依存関係の管理を簡素化する を参考に進めていきたいと思います。

使用するフレームワークApache Spark、使用言語は Pythonです。

作業環境

私が行う環境は以下の通りです。

項目 説明
PC OS Windows 10 Professional
ブラウザ Chrome
作業環境(sshでログイン) Amazon Linux 2
リージョン Tokyo (ap-northeast-1)
コマンドラインツール aws-cli, シェルスクリプト(bash)
分散フレームワーク Apache Spark
使用言語 Python

留意事項

私の環境では、下記を前提として進めていきます。

  • AWS アカウントがある

ステップ 1: 環境を整える

今回はEC2を使って構築するため、EC2をまず最初に準備します。

  1. Amazon Linux 2
  2. インスタンスタイプは t2.micro
  3. "自動割り当てパブリックIP" を有効にする
  4. IAMロールに AmazonElasticMapReduceFullAccess を割り当てる。
  5. sshログインを有効にする

ステップ 1.1: EC2にログインする

ssh -i EC2に接続するための秘密鍵ファイルへのパス ec2-user@パブリックIPアドレス

ステップ 1.2: Dockerをインストールする

以下の手順にしたがって、EC2 に Dockerをインストールします。

#   Dockerインストール
$ sudo yum update -y
$ sudo amazon-linux-extras install docker
$ sudo service docker start
$ sudo usermod -a -G docker ec2-user

# ログアウト
$ exit

# 再ログイン
$ ssh -i EC2に接続するための秘密鍵ファイルへのパス ec2-user@パブリックIPアドレス

# Dockerがインストールされていることを確認する
$ docker info

ステップ 2: EMR環境構築のための準備

ステップ 2.1: 作業ディレクトリを作成

以下のようなディレクトリ、ファイルを作成します。

EMR/
├── deploy.sh
├── emr-configuration.json
├── pyspark
│   └── Dockerfile

ステップ 2.2: pyspark/Dockerfileを作成

サイトに掲載されているDockerfileをそのまま活用します。

FROM amazoncorretto:8

RUN yum -y update
RUN yum -y install yum-utils
RUN yum -y groupinstall development

RUN yum list python3*
RUN yum -y install python3 python3-dev python3-pip python3-virtualenv

RUN python -V
RUN python3 -V

ENV PYSPARK_DRIVER_PYTHON python3
ENV PYSPARK_PYTHON python3

RUN pip3 install --upgrade pip
RUN pip3 install numpy

RUN python3 -c "import numpy as np"

ステップ 2.3: deploy.shを作成

Dockerイメージ及びEMRクラスターの構築作成用シェルスクリプト

#!/bin/bash

readonly region="ap-northeast-1"
readonly account="AWS Account ID"
readonly repository="pyspark"
readonly image="pyspark:latest"
readonly EC2KeyPair="EC2 に接続するためのキーペア"
readonly public_subnet_id="Public Subnet ID"

echo "region           : ${region}"
echo "account          : ${account}"
echo "repository       : ${repository}"
echo "image            : ${image}"
echo "EC2KeyPair       : ${EC2KeyPair}"
echo "public_subnet_id : ${public_subnet_id}"

##################
# Docker setting #
##################
# build
docker build -t ${image} pyspark/
docker tag ${image} ${account}.dkr.ecr.${region}.amazonaws.com/${repository}:latest

# push
aws ecr describe-repositories --repository-names ${repository} --region ${region} > /dev/null 2>&1
if [ $? -ne 0 ] ; then
    aws ecr create-repository --repository-name ${repository} --region ${region} > /dev/null
fi
$(aws ecr get-login --region ${region} --no-include-email)
docker image push ${account}.dkr.ecr.${region}.amazonaws.com/${repository}:latest

######################
# create EMR cluster #
######################
sed -e "s/%Region%/${region}/g" \
    -e "s/%Account%/${account}/g" \
    -e "s/%Repositry%/${repository}/g" \
    emr-configuration.json > after.json

aws emr create-cluster \
    --name 'EMR 6.0.0 with Docker' \
    --release-label emr-6.0.0 \
    --applications Name=Livy Name=Spark \
    --ec2-attributes "KeyName=${EC2KeyPair},SubnetId=${public_subnet_id}" \
    --instance-type m5.xlarge \
    --instance-count 3 \
    --use-default-roles \
    --configurations file://./after.json

# success
echo -e "\033[34msuccessfully deployed!!\033[0m"

ステップ 2.4: emr-configuration.jsonを作成

EMRクラスターの設定ファイル

[
    {
        "Classification":"container-executor",
        "Properties":{
 
        },
        "Configurations":[
            {
                "Classification":"docker",
                "Properties":{
                    "docker.privileged-containers.registries":"local,centos,%Account%.dkr.ecr.%Region%.amazonaws.com",
                    "docker.trusted.registries":"local,centos,%Account%.dkr.ecr.%Region%.amazonaws.com"
                }
            }
        ]
    },
    {
        "Classification":"livy-conf",
        "Properties":{
            "livy.spark.master":"yarn",
            "livy.spark.deploy-mode":"cluster",
            "livy.server.session.timeout":"16h"
       }
    },
    {
        "Classification":"hive-site",
        "Properties":{
            "hive.execution.mode":"container"
       }
    },
    {
        "Classification":"spark-defaults",
        "Properties":{
            "spark.executorEnv.YARN_CONTAINER_RUNTIME_TYPE":"docker",
            "spark.yarn.am.waitTime":"300s",
            "spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_TYPE":"docker",
            "spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG":"hdfs:///user/hadoop/config.json",
            "spark.executorEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE":"%Account%.dkr.ecr.%Region%.amazonaws.com/%Repositry%:latest",
            "spark.executor.instances":"2",
            "spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_CLIENT_CONFIG":"hdfs:///user/hadoop/config.json",
            "spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_DOCKER_IMAGE":"%Account%.dkr.ecr.%Region%.amazonaws.com/%Repositry%:latest"
        }
    }
]

ステップ 3: EMRリソースを構築する

ステップ 3.1: IAM Roleを作成する

以下のようにして、必要なIAM Roleを作成します。

~ $ aws emr create-default-roles
~ $ aws iam attach-role-policy --role-name EMR_EC2_DefaultRole --policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly

作成されたIAM Roleの内容は以下のようになっています。

EMR_EC2_DefaultRole

クラスタインスタンスに対して Hadoop エコシステム上で実行されるアプリケーションプロセスは、このロールを使用して AWS の他のサービスを呼び出します。EMRFS を使用して Amazon S3 のデータにアクセスする場合は、リクエスト元のユーザーやグループ、または Amazon S3 のデータの場所に応じて引き受けるロールを指定できます。

  • ポリシーARN

    arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforEC2Role

    arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly

  • 信頼されたエンティティ(EC2に権限を付与)

{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

EMR_DefaultRole

リソースをプロビジョニングしてサービスレベルのアクションを実行する際にユーザーに代わって AWS の他のサービスを呼び出すことを Amazon EMR に許可します。このロールは、すべてのクラスターに必須です。

  • ポリシーARN

    arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceRole

  • 信頼されたエンティティ(EMRに権限を付与)

{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "elasticmapreduce.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

EMR_AutoScaling_DefaultRole

以下ポリシーによって、動的なスケーリング環境用の追加のアクションを許可します。Amazon EMR で Auto Scaling を使用するクラスターでのみ必須です。

  • ポリシーARN

    arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforAutoScalingRole

  • 信頼されたエンティティ(EMRとApplication AutoScalingに権限を付与)

{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "elasticmapreduce.amazonaws.com",
          "application-autoscaling.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

ステップ 3.2: deploy.sh を実行する

deploy.shを実行し、EMRクラスターを構築します。

~ $ cd ~/EMR
EMR $ source deploy.sh

ステップ 3.3: AWS EMRへログイン

クラスターが起動し、待機状態になったら、クラスターホストがAmazon ECRに対して自身を認証し、Dockerイメージをダウンロードできることを確認します。 クラスターのコアノードの 1 つに SSH を接続します。

# このコマンドは、config.json ファイルを /root/.docker フォルダ内に作成します。
~ $ ssh -i deploy.shで指定したEMRログイン用の秘密鍵ファイルパス ec2-user@パブリックIPアドレス
~ $ sudo usermod -a -G docker ec2-user
~ $ exit
~ $ ssh -i deploy.shで指定したEMRログイン用の秘密鍵ファイルパス ec2-user@パブリックIPアドレス
~ $ $(aws ecr get-login --region ap-northeast-1 --no-include-email)


# 次のコマンドを使用して、作成した config.json ファイルを HDFS がある場所の /user/hadoop/ に配置します。
~ $ sudo hdfs dfs -put ~/.docker/config.json /user/hadoop/

ステップ 3.4: ノートブックを作成する

AWS EMRコンソールにて、以下のようにしてノートブックを作成します。

f:id:logicalarts:20201016104427p:plain

ステップ 4: 動作確認

PySparkが実行できるかどうかを確認してみましょう。

ノートブックが準備完了ステータスになったら、JupyterLabで開く ボタンをクリックすると、新しいブラウザータブで開くことができます。ご自分の EMR Notebook の名前が付いた既定のノートブックが、デフォルトで作成されます。そのノートブックをクリックすると、カーネルを選択するように求められます。PySpark をクリックします。

ノートブックの最初のセルに以下を書き込みます。

%%configure -f
{"conf": { "spark.pyspark.virtualenv.enabled": "false" }}

ノートブックの2つめのセルに以下を書き込みます。

from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("docker-numpy").getOrCreate()
sc = spark.sparkContext

import numpy as np
a = np.arange(15).reshape(3, 5)
print(a)
print(np.__version__)

実行結果が以下のようになれば成功です。このPySparkコードは、YARN、Docker、作成した pysparkイメージを使用して、EMR6.0.0 クラスターで実行したものです。 そして、EMR Notebooksは、Apache Livyを利用してEMRクラスターに接続します。

f:id:logicalarts:20201016104432p:plain

まとめ

長くなりましたが、いかがでしたでしょうか。私も AWS EMR を検証の段階でしたので、まとめるのに苦慮しました。説明に不備や誤りがありましたらご指摘ください。また、不明点があれば、元サイト もおすすめです。とても丁寧にまとめられていて理解が浅い私でも進めやすかったです。

所感として、機械学習等で使うデータを用意するための前段階処理として、使われることの多いApache SparkやMap Reduceが、手軽に使えるようにマネージドされたサービスという印象です。

参考サイト

https://aws.amazon.com/jp/blogs/news/simplify-your-spark-dependency-management-with-docker-in-emr-6-0-0/

https://d1.awsstatic.com/webinars/jp/pdf/services/20191023_AWS-Blackbelt_EMR.pdf

https://docs.aws.amazon.com/ja_jp/emr/latest/ManagementGuide/emr-what-is-emr.html

https://www.atmarkit.co.jp/ait/articles/1608/24/news014.html