GCS to S3を極力マネージドサービスで実現してみる

2023.07.31

はじめに

データアナリティクス事業本部のkobayashiです。

Amazon S3からGoogle Cloud Storage(GCS)へオブジェクトを転送するサービスとしてはStorage Transfer Serviceを使うことでOne-timeの転送やスケジューリングで定期転送を行うことができます。 一方、GCSからS3への転送となるとフルマネージドなサービスがなかなかありません。今回極力マネージドなサービスを使ってGCSからS3へ転送する仕組みを作ってみたのでまとめます。

Storage Transfer Service  |  Google Cloud

Cloud Run JobsのシェルジョブでGCSからS3へ転送する

GCSからS3への転送を極力マネージドな仕組みで実現するために今回作ってみるのは以下のようなものです。

  1. gsutilrsyncを使ってGCSからS3への転送を行う
  2. Cloud Run Jobsのシェルジョブでgsutilを実行する
    1. .botoにはシークレット情報を保存しない
    2. シークレット(AWS接続情報)はSecret Managerで管理する
    3. 環境変数で転送元、転送先、シークレットを与える

それでは早速GCSからS3へ転送を行うCloud Run Jobsを作ってみます。Cloud Run Jobsの作成方法は公式ドキュメントの「 クイックスタート: Cloud Run でシェルジョブをビルドして作成する  |  Cloud Run のドキュメント  |  Google Cloud 」に詳しい手順が書かれていますのでこれを参考に作成してみます。

ジョブで実行するシェルスクリプトの作成

Cloud Run Jobsのジョブではgsutil rsyncコマンドを使ってでGCSからS3へ転送を行います。今回使うgsutilコマンドは以下のような使い方をします。

gsutil -m rsync -rdC gs://${SRC_PATH} s3://${DIST_PATH}

このコマンドを使ってGCSからS3への転送を行いますので、Cloud Run Jobsで実行するシェルスクリプトファイルは以下になります。

script.sh

#!/bin/bash

export BOTO_CONFIG=.boto

echo ${SRC_PATH} # 転送元
echo ${DIST_PATH} # 転送先
echo "Credentials:aws_access_key_id=${AWS_ACCESS_KEY_ID}" # AWSアクセスキー

gsutil -m -o "Credentials:aws_access_key_id=${AWS_ACCESS_KEY_ID}" -o "Credentials:aws_secret_access_key=${AWS_SECRET_ACCESS_KEY}" rsync -rdC gs://${SRC_PATH} s3://${DIST_PATH}

Cloud Run Jobsで実行するためGCSへのアクセスについては作成するジョブで使うサービスアカウントに権限を与えれば良いですが、S3へのアクセス情報となるアクセスキー・シークレットキーは通常.boto[Credentials]ブロックに記述します。今回は.botoに記述してしまうとビルドしたイメージ中にシークレットが含まれてしまうので、Secret Managerにそれらの情報は保存してジョブ実行時に環境変数として与えるようにします。

したがって合わせて使う.botoは以下になります。

.boto

[s3]
use-signv4=True
host=s3.ap-northeast-1.amazonaws.com

gsutilで使っている上記以外のオプション等細かい設定は以下の公式ドキュメントを参考してにしてください。

ジョブ用のイメージ作成

上記のファイルを使いCloud Run Jobsで使うイメージをCloud Buildで作成します。以下のようなディレクトリ構成でファイルを配置します。

└── jobs
   ├── .boto
   ├── Dockerfile
   └── script.sh

.botoscript.shは前述したものを使います。

Dockerfileは以下になります。

Dockerfile

FROM google/cloud-sdk:alpine

RUN apk update && \
    apk upgrade && \
    apk add --no-cache bash

WORKDIR /workspace

COPY .boto .

COPY script.sh .
RUN chmod +x ./script.sh

CMD [ "./script.sh" ]

gsutilを使いたいのでGoogle Cloud SDKがインストールされているgoogle/cloud-sdkイメージをベースイメージとして使います。

それではCloud Buildでコンテナイメージを作成します。

gcloud builds submit -t "gcr.io/{プロジェクトID}/gcs-to-s3-job"

これでCloud Run Jobsでジョブとして使うイメージが作成できました。

Secret Managerとサービスアカウントの作成

次にCloud Run Jobsで使うSecret Managerの作成と実行時に使うサービスアカウントの作成を行います。

はじめにSecret ManagerにAWS認証情報のアクセスキーとシークレットキー以下のコマンドで作成します。

gcloud secrets create aws_access_key_id --data-file=/tmp/aws_access_key_id
gcloud secrets create aws_secret_access_key --data-file=/tmp/aws_secret_access_key

次にサービスアカウントを作ります。サービスアカウトに与える権限はSecret Managerの読み取り権限とGCSの管理者権限を与えます。

gcloud iam service-accounts create cloud-run-sa
gcloud projects add-iam-policy-binding {プロジェクトID} \
 --member serviceAccount:cloud-run-sa@{プロジェクトID}.iam.gserviceaccount.com\
 --role="roles/storage.admin"
gcloud projects add-iam-policy-binding {プロジェクトID} \
 --member serviceAccount:cloud-run-sa@{プロジェクトID}.iam.gserviceaccount.com\
 --role "roles/secretmanager.secretAccessor"

Cloud Run Jobsのジョブを作成

イメージを作成しSecret Managerとサービスアカウントを作成したのでこれらを使ってCloud Run Jobsのジョブを以下のコマンドで作成します。

gcloud beta run jobs create gcs-to-s3-job \
    --image gcr.io/{プロジェクトID}/gcs-to-s3-job \
    --set-env-vars SRC_PATH={転送元バケット}/s3_sample,DIST_PATH={転送先バケット}/s3_sample \
    --set-secrets AWS_ACCESS_KEY_ID=aws_access_key_id:latest,AWS_SECRET_ACCESS_KEY=aws_secret_access_key:latest \
    --max-retries 3 \
    --region asia-northeast1 \
    --project {プロジェクトID} \
    --service-account cloud-run-sa@{プロジェクトID}.iam.gserviceaccount.com

これでジョブが作成できたので以下のコマンドでCloud Run Jobsを実行します。

gcloud beta run jobs execute gcs-to-s3-job --region asia-northeast1

するとジョブが実行されGCSで転送元として指定したパス配下のオブジェクトがS3の転送先として指定したパスに作成されます。

$ gsutil ls -l gs://{転送元バケット}/s3_sample/    
         0  2023-05-20T01:25:29Z  gs://{転送元バケット}/s3_sample/
   1045260  2023-05-20T01:25:30Z  gs://{転送元バケット}/s3_sample/streaming_test1.csv
   1045260  2023-05-20T01:25:30Z  gs://{転送元バケット}/s3_sample/streaming_test2.csv
   1045260  2023-05-20T01:25:30Z  gs://{転送元バケット}/s3_sample/streaming_test3.csv
   1045260  2023-05-20T01:25:30Z  gs://{転送元バケット}/s3_sample/streaming_test4.csv
   1045260  2023-05-20T01:25:30Z  gs://{転送元バケット}/s3_sample/streaming_test5.csv
   1045260  2023-05-20T01:25:30Z  gs://{転送元バケット}/s3_sample/streaming_test6.csv
   1045260  2023-05-20T01:25:30Z  gs://{転送元バケット}/s3_sample/streaming_test7.csv
   1045260  2023-05-20T01:25:30Z  gs://{転送元バケット}/s3_sample/streaming_test8.csv
   1045260  2023-05-20T01:28:54Z  gs://{転送元バケット}/s3_sample/streaming_test9.csv

$ aws s3 --profile cm_sspg-user ls s3://{転送先バケット}/s3_sample/
    2023-05-20 10:28:18    1045260 streaming_test1.csv
    2023-05-20 10:28:22    1045260 streaming_test2.csv
    2023-05-20 11:33:30    1045260 streaming_test3.csv
    2023-05-20 11:33:29    1045260 streaming_test4.csv
    2023-05-20 11:33:31    1045260 streaming_test5.csv
    2023-05-20 11:33:29    1045260 streaming_test6.csv
    2023-05-20 11:33:30    1045260 streaming_test7.csv
    2023-05-20 11:33:30    1045260 streaming_test8.csv
    2023-05-20 11:33:30    1045260 streaming_test9.csv

これでCloud Run Jobsを使っているのでインスタンスの管理などは必要なく、極力マネージドなサービを使ってGCSからS3への転送を行えました。

転送元と転送先は環境変数としてジョブ作成時に与えられるように作成したので新しい転送元と転送先がある場合はgcloud beta run jobs createで都度新しいジョブを作成すれば簡単に新しいGCSからS3へを転送ジョブを作る事ができます。

今回は手動でジョブを実行しましたが、Cloud Run Jobsを使っているのでCloud Schedulerでスケジュール実行をおこなったり、Eventarcを使ってGCSへのオブジェクトアップロードをトリガーに転送ジョブを実行することもできます。

gsutil rsyncの注意点

今回作成したジョブで1点注意点があります。サイズが5GB以上のオブジェクトを扱う場合は以下のエラーが出ます。エラー内容の通り、5GB以上のオブジェクトをS3にアップロードする際にはマルチパートでアップロードする必要がありますが、gsutilでは対応していないため転送元のGCSには5GB未満のオブジェクトに分割してアップロードしておく必要があります。

CommandException: "gs://{ソースバケット}/s3_sample/streaming_test_5.csv" exceeds the maximum gsutil-supported size for an S3 upload. S3 objects greater than 5 GiB in size require multipart uploads, which gsutil does not support.

まとめ

GCSからS3へのオブジェクト転送を極力マネージドなサービスで実現する目的でCloud Run Jobsを使ってジョブを実装してみました。処理の中身もgsutilを使っているため実行するスクリプトも数行となり非常にシンプルになりました。

GCS to S3の仕組みをお手軽に実装できるので是非試してみてください。

最後まで読んで頂いてありがとうございました。