LoginSignup
3
7

More than 5 years have passed since last update.

AWS S3ファイルのバケット間転送

Posted at

概要

AWS Batchを用いて、AWS S3のファイル処理を行う方法を試行錯誤したので、自分の備忘録を兼ねて紹介する。

課題

AWS S3のバケットにある動画ファイルを、他のシステムからの指示により別のバケットにコピーしたい。

前提

  • 転送元と転送先のバケットは固定である。
  • 動画ファイルは、小さいもので数百 MBytes、大きいものでは1つあたり100 GBytes 以上も含む。
  • このシステムは24h365dayの稼働を期待されているが、運用チームにシステムエンジニアは一人しかいない(つまり自分だ)。24時間張り付きは嫌だから、極力、システム運用が発生しない方法を選択しないといけない
  • 自分は通常Windows PCで仕事をしているので、以下はWindowsの手順である。Macの人は適宜、読み替えてほしい。

方針

  • ファイルの転送は5分以上かかる可能性があるので、Lambda は使えない。またS3バケットに過剰な転送負荷をかけるべきでないので、同時処理数を制御を行うには Batch が良さそうである。
  • S3で大きな大きなファイル転送の場合、通常multi part転送を使う。がっつりとプログラムを組む方法もあるが、AWS CLIでもmulti partをサポートしていて、メンテナンスもAWSがやってくれるので、手間が省ける。CLIを利用する。
    参考: AWS SDK for .NET マルチパートアップロード API を使用した Amazon S3 オブジェクトのコピー

構築手順

AWS環境構築

  • AWS CLIをインストールして、接続設定を行う。Windows PowerShell を起動し(コマンドプロンプトでもよい。以下シェルと略す)
> aws configure

参考: AWS CLI のインストールと設定

Docker環境構築

  • Docker HUBでアカウントを作成し、ログインして docker 最新版ダウンロード1し、インストールする。(再起動を求められる) 再起動時、PCのBIOSで仮想対応をONにして Docker Desctop サービスを開始する。

Dockerコンテナ作成

  • PCのローカルに空のフォルダを作成し、以下を作成する。テキストエディタでUTF-8で保存するとBOMを解釈できず動きが怪しいので、日本語を使わないほうが無難だ。
run.sh
#!/bin/bash
BUCKET_SRC=(転送元のバケットを指定)
BUCKET_DST=(転送先のバケットを指定)
if [ $# -ne 2 ]; then
  echo "usage $0 source_path/filename destination_path/filename" 1>&2
  exit 1
fi

echo aws s3 cp s3://$BUCKET_SRC/$1 s3://$BUCKET_DST/$2
aws s3 cp s3://$BUCKET_SRC/$1 s3://$BUCKET_DST/$2
Dockerfile
FROM amazonlinux:latest
RUN curl -kl https://bootstrap.pypa.io/get-pip.py | python
RUN pip install awscli
RUN aws configure set default.s3.max_concurrent_requests 20
RUN aws configure set default.s3.multipart_threshold 64MB
RUN aws configure set default.s3.multipart_chunksize 16MB
ADD run.sh /usr/local/
  • シェルを起動し、先ほどのフォルダにcdしてから、コンテナ"aws-s3-objectcopy"を作成する。"SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host..."のようなワーニングが出るが、今回は対処しない2
> docker build -t aws-s3-objectcopy .
(中略)
Successfully built xxxxxxxxxxx
Successfully tagged aws-s3-objectcopy:latest

念のため、意図したものができているか確認する3

> docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
aws-s3-objectcopy   latest              dcdcc19a1ebf        2 hours ago         231MB

> docker run -it aws-s3-objectcopy /bin/bash
bash-4.2## ls -l /usr/local/run.sh
-rwxr-xr-x 1 root root 226 Jan  2 06:06 /usr/local/run.sh
bash-4.2# exit
exit

ECRにレポジトリを登録する

  • ブラウザでマネジメントコンソールより、ECR(Amazon Elastic Container Registry)を開く。 新規にレポジトリを作成する。ここではレポジトリ名は"aws-s3-objectcopy"とした。 シェルを起動し、ECRログイン文字列を取得し、レポジトリに登録する。
// ログイン文字列を取得
> aws ecr get-login --no-include-email --region ap-northeast-1
docker login -u AWS -p (長い文字が出力される)

> docker login -u AWS -p .....(上記表示をコピー)
Login Succeeded

// タグを指定
docker tag aws-s3-objectcopy:latest xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/aws-s3-objectcopy:latest

//リポジトリにpush
docker push xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/aws-s3-objectcopy:latest

AWS Batch起動用のネットワーク設定

  • あとでセキュリティをカスタマイズするかもしれないので、専用のVPC/サブネットを作成して、セキュリティグループを作成する。(解説は省略)
  • Batchコンテナを起動したサブネットからS3にアクセスするため、NAT Gateway経由でアクセスできるようにNATおよびルートテーブルを構築する。

AWS Batchの設定を行う(コンピューティング環境とジョブキューの作成)

  • マネジメントコンソールでAWS Natchを開く。初回、初期ウィザードが起動したら「ジョブをどのように実行しますか?」で「ジョブの送信なし」を指定して「次へ」
  • コンピューティング環境とジョブキューの設定

    コンピューティング環境の名前=batch-s3-objectcopy(任意)
    サービスロール=新しいロールの作成
    EC2 インスタンスロール=新しいロールの作成
    プロビジョニングモデル=オンデマンド
    最小 vCPU=0
    必要な vCPU=0
    最大 vCPU=2
    ジョブキュー名=jq-batch-s3-objectcopy ロールはあとで調整する。vCPUは間違ってコストを浪費しないように、最初は最小設定で。
    ネットワーキングの設定は先ほど作成したVPC/サブネット/セキュリティグループを指定

ジョブ実行ロールの設定を行う

  • Batchの初期ウィザードにより、AWSBatchServiceRole と ecsInstanceRole が作成される。
    マネジメントコンソールでIAMの画面より、ロールの画面を開き、該当のロールを確認する。
  • ecsInstanceRole にS3の該当バケットにアクセスするためのカスタムポリシーを作成して割り当てる。
policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::(転送元バケット)/*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
            "Resource": "arn:aws:s3:::(転送先バケット)/*"
        }
    ]
}

AWS Batchの設定を行う(ジョブ定義の作成)

  • ジョブ定義を新規作成する

    Job definition name=jd-s3-objectcopy(任意)
    Job attempts(ジョブ試行)=1
    Execution timeout(実行のタイムアウト)=600
    Container image=(先ほど作成したコンテナ)
    Command="/usr/local/run.sh 転送元パス/ファイル名 転送先パス/ファイル名"
    vCPU=1
    メモリ=1024(適当な値で、後で調節)

AWS Batchの設定を行う(ジョブ定義の作成)

  • いよいよジョブを登録する。
    マネジメントコンソールでジョブの画面を開き、「ジョブの送信」
  • Submit an AWS Batch Job

    Job name=適当に
    Job definition=上記で作成したものを指定
    Job queue=上記で作成したものを指定
    Commandでジョブ定義で指定したものがデフォルトで設定されているので、必要あれば変更する。
    「submit job」を実行。
  • ジョブが正常に作成されたらダッシュボードを開く
    job_dashboard.png
    RUNNABLEに登録したジョブが表示され、しばらく1~2分くらい待つとSTARTになり、正常に完了するとSUCCEEDEDになる。
    マネジメントコンソールのS3から、転送先のバケットを見るとファイルが作成されていれば成功。
  • CloudWatch で実行したログを確認する。
    job_log.png
    起動した実行ファイルの出力がそのままCloudWatchに出力されるらしい。

トラブルシュート

まとめ

  • AWS Batch でバケット間ファイルコピーができた。
  • この方法で600MBの動画ファイルをオレゴンリージョンのS3バケットから同一リージョンの別のバケットに転送してみたが、バッチの実行時間は8秒であった。こんなに短いとBatchでつくる必要があるか、というのは悩ましいが、AWSのドキュメントではs3のgetObject/putObjectの速度を保証していないので、深く考えないことにしよう5

今後の課題

  • バッチジョブを起動する仕組みの構築を行う
  • ファイル転送の失敗、ジョブがタイムアウトした場合の検知とエラー処理

参考文献


  1. 今回用いたDockerバージョンは 18.09.0である 

  2. 開発段階では動作上支障がないのでエラーは無視する。Windows特有の現象なのか? 実際使うときになったらMacかLinux上でビルドしよう。 

  3. 作成したコンテナは"docker ps -a -n=5" で確認。"docker rm (コンテナID)"で不要コンテナを整理。不要になったイメージは docker rmiで削除する。 

  4. 自分でハマったときは、ルートテーブルのデフォルトゲートウェイのCIDRネットマスク部分を間違えて "0.0.0.0/24" と指定していた。正しくは "0.0.0.0/0"。サブネットにVPCを起動してパブリックサブネットのVPCを経由してログインできるのに、外部ネットワークにリーチャブルでなく、routeを確認して気づいた。 

  5. もしやりたい処理が数秒で完了することが保証されるなら、わざわざBatchにしなくても、呼び出し側のプログラムで実装したほうが簡単であるし、コストが小さい。 

3
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
7