今日もプログラミング

IT技術とかプログラミングのこととか特にJavaを中心に書いていきます

Hadoopでプロキシ経由でAmazon S3にアクセスする

Hadoopと言うか、HadoopのFileSystemクラスを利用してS3にアクセスしたいのである。

AWS SDKがあるのに、なんで?かと言うと、FileSystemを使うとローカルファイルとかHDFSとかと同じインターフェイスでアクセスできるので、汎用的なツールを作るのに便利だからだ。

 

Hadoop 2.6.0のhadoop-tools/hadoop-awsの下を探してみると、S3FileSystem(s3:)、NativeS3FileSystem(s3n:)、S3AFileSystem(s3a:)の3つのFileSystemクラスがあった。

このうちs3は特殊な用途らしいので、s3n:とs3a:について調べてみる。

 

NativeS3FileSystem(s3n:)の場合

まず、何も考えずに

Configuration configuration = new Configuration();
Path path = new Path("s3n://xxx/yyy/zzz.txt");
FileSystem fs = path.getFileSystem(configuration);
System.out.println(fs.getFileStatus(path));

とすると、

Exception in thread "main" java.lang.IllegalArgumentException: AWS Access Key ID and Secret Access Key must be specified as the username or password (respectively) of a s3n URL, or by setting the fs.s3n.awsAccessKeyId or fs.s3n.awsSecretAccessKey properties (respectively).
    at org.apache.hadoop.fs.s3.S3Credentials.initialize(S3Credentials.java:70)
    at org.apache.hadoop.fs.s3native.Jets3tNativeFileSystemStore.initialize(Jets3tNativeFileSystemStore.java:80)

というエラーが出た。

当然だが、AWSにアクセスするには、キーが必要である。

ので、キーを設定して再度実行してみる。

Configuration configuration = new Configuration();
configuration.set("fs.s3n.awsAccessKeyId", "XXXXXXXXXXXXXX");
configuration.set("fs.s3n.awsSecretAccessKey", "XXXXXXXXXXXXXX");
Path path = new Path("s3n://xxx/yyy/zzz.txt");
FileSystem fs = path.getFileSystem(configuration);
System.out.println(fs.getFileStatus(path));

今度は、

Exception in thread "main" java.net.UnknownHostException: xxx.s3.amazonaws.com

というエラーだ。プロキシの設定をしていないので、当然アクセスできない。

s3n:ではjets3tというライブラリを使っているので、そいつのプロキシ設定について調べてみる。

ググってみたところ、このサイトに説明があった。

jets3t.propertiesというファイルを作って、

httpclient.proxy-autodetect=false
httpclient.proxy-host=プロキシサーバのアドレス
httpclient.proxy-port=プロキシのポート番号
httpclient.proxy-user=プロキシのユーザ名
httpclient.proxy-password=プロキシのパスワード

のよう設定すればよい。

再度試したら、無事成功した!

(実は以前はhttpclient.proxy-autodetect=falseが無くてもOKだったのだが、変わっていたようで、はまった…)

 

S3AFileSystem(s3a:)の場合

同じように

Configuration configuration = new Configuration();
configuration.set("fs.s3a.awsAccessKeyId", "XXXXXXXXXXXXXX");
configuration.set("fs.s3a.awsSecretAccessKey", "XXXXXXXXXXXXXX");
Path path = new Path("s3a://xxx/yyy/zzz.txt");
FileSystem fs = path.getFileSystem(configuration);
System.out.println(fs.getFileStatus(path));

として実行してみると、

Exception in thread "main" com.amazonaws.AmazonClientException: Unable to load AWS credentials from any provider in the chain
    at com.amazonaws.auth.AWSCredentialsProviderChain.getCredentials(AWSCredentialsProviderChain.java:117)
    at com.amazonaws.services.s3.AmazonS3Client.invoke(AmazonS3Client.java:3521)
    at com.amazonaws.services.s3.AmazonS3Client.headBucket(AmazonS3Client.java:1031)
    at com.amazonaws.services.s3.AmazonS3Client.doesBucketExist(AmazonS3Client.java:994)
    at org.apache.hadoop.fs.s3a.S3AFileSystem.initialize(S3AFileSystem.java:154)

というようなエラーが出た。

調べてみたところ、どうもキーを設定する場所が変わったようで、

configuration.set("fs.s3a.access.key", "XXXXXXXXXXXXXX");
configuration.set("fs.s3a.secret.key", "XXXXXXXXXXXXXX");

のようにするようだ。

修正して実行すると、………帰ってこないな。

まあ、プロキシの設定をしていないから、アクセスできないのは当然なんだけど。

 

ソースを見ると、s3a:ではAWS SDKを使ってアクセスするようだ。

初期化部はこんな感じ。

public void initialize(URI name, Configuration conf) throws IOException {
    super.initialize(name, conf);
    ...

    ClientConfiguration awsConf = new ClientConfiguration();
    awsConf.setMaxConnections(conf.getInt(MAXIMUM_CONNECTIONS, 
      DEFAULT_MAXIMUM_CONNECTIONS));
    awsConf.setProtocol(conf.getBoolean(SECURE_CONNECTIONS, 
      DEFAULT_SECURE_CONNECTIONS) ?  Protocol.HTTPS : Protocol.HTTP);
    awsConf.setMaxErrorRetry(conf.getInt(MAX_ERROR_RETRIES, 
      DEFAULT_MAX_ERROR_RETRIES));
    awsConf.setSocketTimeout(conf.getInt(SOCKET_TIMEOUT, 
      DEFAULT_SOCKET_TIMEOUT));

    s3 = new AmazonS3Client(credentials, awsConf);

AWS SDK自体はプロキシに対応していて、

    awsConf.setProxyHost(プロキシサーバのアドレス);
    awsConf.setProxyPort(プロキシのポート番号);
    awsConf.setProxyUsername(プロキシのユーザ名);
    awsConf.setProxyPassword(プロキシのパスワード);

とすればよいのだが…、この処理が入っていない…、ということは、プロキシには対応していないということだな。

 

まとめ

  • s3n:は、jets3t.propertiesというファイルにプロキシ設定を書くとアクセスできる
  • s3a:は、プロキシに対応していないようだ