woshidan's blog

そんなことよりコードにダイブ。

Hadoop3.1.1でs3a://ファイルシステムを利用する場合の環境構築をしてみる

今日はHadoopファイルシステム(特に、s3を利用するもの)についてすこしおさらいしてから表題の環境構築をしてみます。

Hadoopファイルシステム

Hadoopは抽象化されたファイルシステムの概念を持っていて、その実装に

など、さまざまなストレージをバックエンドとした実装があり、今日扱うS3をバックエンドとするファイルシステムもこの中に含まれます。

ただ、Hadoopで扱えるファイルシステムのうち、S3をバックエンドとするものにはいくつか種類があり、ややこしいので動かすための設定を見ていく前にかんたんに整理しておきます。

Hadoopのs3をバックエンドとしたファイルシステム

詳しい歴史的経緯はこちらのドキュメントの冒頭やこちらの記事が参考になりますが、Hadoopのs3用のファイルシステムを表にすると以下のようになります。

ファイルシステムURIスキーマ*1 備考
s3 最初に作成されたs3をバックエンドとするファイルシステム。このファイルシステムで作成されたファイルはHadoopのs3ファイルシステムを通してしか読み書きできなかった。現在はもうApacheによる開発は終了しており、ソースコードにも含まれていない。下の方で述べるEMRのs3ファイルシステムとは別物。
s3n 上述のs3ファイルシステムの問題点を解決し、アプリケーションのコードとHadoopの間でS3のオブジェクトを共用できるようにしたもの。Hadoopの2.x系で利用することができた
s3a s3nより、より大きいファイルをより高いパフォーマンスで扱えることを目的に開発された第三世代。改善内容は、たとえば、部分的なシークの追加など。Hadoop 3.x系で利用できる
s3(Amazon版) s3nが開発されている頃からAmazonが独自にEMR用に改良したファイルシステム。現在s3のファイルシステムで開発されているものはこちらであり、Hadoopコミュニティが開発していたs3ファイルシステムとは別物

それでは、今日はs3aプロトコルを利用する場合の環境構築をしていきます。

Hadoopでs3a://ファイルシステムをPseudo-Distributed Modeで動かしてみる場合の環境構築について

s3にHadoop用のファイルを置くバケットを作成

f:id:woshidan:20180920082701p:plain

なお、バケットには管理者のみがアクセスできるようにします。

バケットを作成したら、テスト用のCSVもアップロードしておきます。

そのCSVの中身は以下です。

dog
dog
dog
cat
cat
dog
dog
cat

Hadoopの設定

Hadoopの認証情報とファイルシステムの実装クラスを設定する

次にHadoop側の設定をしていきます。まず、 core-site.xml の変更内容ですが以下になります。

<!-- etc/hadoop/core-site.xml -->
<property>
  <name>fs.s3a.access.key</name>
  <value>MY_ACCESS_KEY</value>
</property>

<property>
  <name>fs.s3a.secret.key</name>
  <value>MY_SECRET_KEY</value>
</property>

etc/hadoop/core-site.xml の変更はずいぶん少ないですが、これは今回s3a系のファイルシステムを利用する際の設定のうち、

についてHadoopのデフォルトの設定を利用しているためです。

まず、Hadoopのデフォルトの設定については、 https://hadoop.apache.org/docs/r3.1.1/hadoop-project-dist/hadoop-common/core-default.xml に記載されていて、fs.s3a.impl の値は org.apache.hadoop.fs.s3a.S3AFileSystem になっています。

また、 fs.s3a.aws.credentials.provider の値の設定に関しては https://hadoop.apache.org/docs/r3.1.1/hadoop-project-dist/hadoop-common/core-default.xml にも記載されていますが、s3のファイルシステムの設定について説明しているHadoop-AWS module: Integration with Amazon Web Servicesのページの方がわかりやすいです。

Hadoop-AWS module: Integration with Amazon Web Servicesによると、s3a://ファイルシステムを利用する場合、

  • 認証方法の指定は credential provider クラスの指定という形で行い、複数指定することもできる
  • 特に認証方法を指定しない場合、デフォルトで指定されている認証方法が用いられる
    • デフォルトで指定されている credential provider は以下の順で試される
      • org.apache.hadoop.fs.s3a.BasicAWSCredentialsProvider ... core-site.xml に記載した fs.s3a.access.keyfs.s3a.secret.key を利用して認証を行う
      • com.amazonaws.auth.EnvironmentVariableCredentialsProvider ... 環境変数 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY を利用
      • com.amazonaws.auth.InstanceProfileCredentialsProvider ... EC2のインスタンスプロファイルの認証情報を利用

となっています。

そして、今回はデフォルトの認証方法のうち、org.apache.hadoop.fs.s3a.BasicAWSCredentialsProvider を利用する前提の設定となっているので、 core-site.xmlfs.s3a.access.keyfs.s3a.secret.key だけ記載しています。

HadoopからAWSを利用するためのクラスのパスをクラスパスに追加

hadoop から s3 を利用するためには $HADOOP_HOME/share/hadoop/tools/lib/ 以下にある hadoop-aws-3.1.1.jar が必要なので、 etc/hadoop/hadoop-env.sh に以下を追記します。

# etc/hadoop/hadoop-env.sh
export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:$HADOOP_HOME/share/hadoop/tools/lib/*

その他

また、 s3a:// プロトコルを利用することとは関係ないですが、以下の設定もしていました。

<!-- etc/hadoop/hdfs-site.xml -->
<!-- 今回もおためしなのでレプリケーションは作りません -->
<configuration>
  <property>
    <name>dfs.replication</name>
    <value>1</value>
  </property>
</configuration>

コマンドを実行してみる

$ $HADOOP_HOME/bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-3.1.1.jar wordcount s3a://woshidan-hadoop-test/word_count_test.csv wordcount-output-s3
$ $HADOOP_HOME/bin/hdfs dfs -cat wordcount-output-s3/*
cat 3
dog 5

よさそうです。

トラブルシューティング

bin/hadoop fs -ls s3a://bucket_name を実行したら ls: `s3a://bucket_name': No such file or directory というエラーメッセージが返ってきた

https://hortonworks.github.io/hdp-aws/s3-s3aclient/index.html のコマンドを眺めていたら s3a://bucket_name/ のように末尾に / が必要なようでした。

[ec2-user@ip-172-31-16-244 hadoop]$ bin/hadoop fs -ls s3a://woshidan-hadoop-test
2018-09-20 23:14:46,289 INFO impl.MetricsConfig: loaded properties from hadoop-metrics2.properties
2018-09-20 23:14:46,384 INFO impl.MetricsSystemImpl: Scheduled Metric snapshot period at 10 second(s).
2018-09-20 23:14:46,384 INFO impl.MetricsSystemImpl: s3a-file-system metrics system started
2018-09-20 23:14:48,278 INFO Configuration.deprecation: fs.s3a.server-side-encryption-key is deprecated. Instead, use fs.s3a.server-side-encryption.key
ls: `s3a://woshidan-hadoop-test': No such file or directory
2018-09-20 23:14:48,543 INFO impl.MetricsSystemImpl: Stopping s3a-file-system metrics system...
2018-09-20 23:14:48,544 INFO impl.MetricsSystemImpl: s3a-file-system metrics system stopped.
2018-09-20 23:14:48,544 INFO impl.MetricsSystemImpl: s3a-file-system metrics system shutdown complete.

[ec2-user@ip-172-31-16-244 hadoop]$ bin/hadoop fs -ls s3a://woshidan-hadoop-test/
2018-09-20 23:20:24,680 INFO impl.MetricsConfig: loaded properties from hadoop-metrics2.properties
2018-09-20 23:20:24,798 INFO impl.MetricsSystemImpl: Scheduled Metric snapshot period at 10 second(s).
2018-09-20 23:20:24,798 INFO impl.MetricsSystemImpl: s3a-file-system metrics system started
2018-09-20 23:20:26,691 INFO Configuration.deprecation: fs.s3a.server-side-encryption-key is deprecated. Instead, use fs.s3a.server-side-encryption.key
Found 1 items
-rw-rw-rw-   1 ec2-user ec2-user         31 2018-09-19 23:26 s3a://woshidan-hadoop-test/word_count_test.csv
2018-09-20 23:20:26,924 INFO impl.MetricsSystemImpl: Stopping s3a-file-system metrics system...
2018-09-20 23:20:26,925 INFO impl.MetricsSystemImpl: s3a-file-system metrics system stopped.
2018-09-20 23:20:26,925 INFO impl.MetricsSystemImpl: s3a-file-system metrics system shutdown complete.

org.apache.hadoop.fs.s3a.BasicAWSCredentialsProvider を fs.s3a.aws.credentials.provider に指定して fs -ls したらエラーになった

[ec2-user@ip-172-31-16-244 hadoop]$ bin/hadoop fs -ls s3a://woshidan-hadoop-test
2018-09-20 23:05:52,547 INFO impl.MetricsConfig: loaded properties from hadoop-metrics2.properties
2018-09-20 23:05:52,670 INFO impl.MetricsSystemImpl: Scheduled Metric snapshot period at 10 second(s).
2018-09-20 23:05:52,670 INFO impl.MetricsSystemImpl: s3a-file-system metrics system started
ls: org.apache.hadoop.fs.s3a.BasicAWSCredentialsProvider constructor exception.  A class specified in fs.s3a.aws.credentials.provider must provide a public constructor accepting Configuration, or a public factory method named getInstance that accepts no arguments, or a public default constructor.

fs.s3a.aws.credentials.provider に指定できる credential provider のクラスは

  • org.apache.hadoop.conf.Configurationインスタンスを引数にとるpublicコンストラクタを持つ
  • 何も引数を取らないgetInstance()メソッドを持つ
  • publicなデフォルトコンストラクタを持つ

という条件を満たす必要があります。

たとえば、他の credential provider のクラスを見てみると TemporaryAWSCredentialsProvider では

https://github.com/apache/hadoop/blob/da9a39eed138210de29b59b90c449b28da1c04f9/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/TemporaryAWSCredentialsProvider.java#L55-L57

  public TemporaryAWSCredentialsProvider(Configuration conf) {
    this(null, conf);
  }

のように、 Configurationインスタンスを引数にとるpublicコンストラクタを持っており、 AnonymousAWSCredentialsProvider では

https://github.com/apache/hadoop/blob/da9a39eed138210de29b59b90c449b28da1c04f9/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/AnonymousAWSCredentialsProvider.java#L40-L56

public class AnonymousAWSCredentialsProvider implements AWSCredentialsProvider {

  public static final String NAME
      = "org.apache.hadoop.fs.s3a.AnonymousAWSCredentialsProvider";

  public AWSCredentials getCredentials() {
    return new AnonymousAWSCredentials();
  }

  public void refresh() {}

  @Override
  public String toString() {
    return getClass().getSimpleName();
  }

コンストラクタが宣言されていないので、publicなデフォルトコンストラクタを使えます。一方、 BasicAWSCredentialsProvider クラスは

https://github.com/apache/hadoop/blob/da9a39eed138210de29b59b90c449b28da1c04f9/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/BasicAWSCredentialsProvider.java#L42-L45

  public BasicAWSCredentialsProvider(String accessKey, String secretKey) {
    this.accessKey = accessKey;
    this.secretKey = secretKey;
  }

のようにコンストラクタが定義されていますが、これは fs.s3a.aws.credentials.provider に指定できる credential provider のクラスの条件

  • org.apache.hadoop.conf.Configurationインスタンスを引数にとるpublicコンストラクタを持つ
  • 何も引数を取らないgetInstance()メソッドを持つ
  • publicなデフォルトコンストラクタを持つ

を満たしていないので、Basic と接頭辞がついているので試しに使ってみたい感じなのですが、 fs.s3a.aws.credentials.provider に指定することはできません。ですが、 fs.s3a.aws.credentials.provider に何も指定しなければ、デフォルトで利用されるため、今回はそうしています。

現場からは以上です。

*1:HadoopではUIスキーマで利用するファイルシステムの実装を指定する