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);
awsConf.setProxyHost(プロキシサーバのアドレス); awsConf.setProxyPort(プロキシのポート番号); awsConf.setProxyUsername(プロキシのユーザ名); awsConf.setProxyPassword(プロキシのパスワード);
とすればよいのだが…、この処理が入っていない…、ということは、プロキシには対応していないということだな。
まとめ
- s3n:は、jets3t.propertiesというファイルにプロキシ設定を書くとアクセスできる
- s3a:は、プロキシに対応していないようだ