IBM Cloud Functions を使って IBM Cloud Object Storage (ICOS) バケットをリージョン間でレプリケーションする


# 概要
こんなイメージです。
`WRITE` オペレーションには、 `create`, `update` が含まれます。
また、Functions からは ICOS プライベートエンドポイントを経由してオペレーションがおこなえるため、データ転送料金（ICOS からのパブリックアウトバウンド）については発生しません。
リージョン間のコピーは「[Object operations - Copy an object](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-object-operations#object-operations-copy)」（ICOS サーバーサイドでのコピー処理）を活用し、Functions （クライアント）に一度ダウンロードして再アップロードするといった動作はさせません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/84dda744-f883-e0d9-e206-ab2c321331c4.png)

# 事前準備

## CLI プラグインインストール

```shell:

ibmcloud plugin install cloud-functions
ibmcloud plugin show cloud-functions
```

## CLI ログイン

```shell:

export SOURCE_REGION="jp-tok"
ibmcloud login -a cloud.ibm.com -r $SOURCE_REGION
```
## 環境設定

```shell:

export FN_NAMESPACE="khayama-fn"
export COS_INSTANCE_NAME="khayama-cos" # 既存の ICOS インスタンスを使います
```

## Functions Namespace 設定
`namespace` を作成してプロパティをセットします。

```shell:

ibmcloud fn namespace create $FN_NAMESPACE
ibmcloud fn namespace target $FN_NAMESPACE
```

## Functions から ICOS へのアクセスを許可
ICOS の変更通知を受信してトリガーできるように以下の許可設定をおこないます。

- [Using authorizations to grant access between services](https://cloud.ibm.com/docs/iam?topic=iam-serviceauth#auth-cli)

```shell:

ibmcloud iam authorization-policy-create \
functions \
--source-service-instance-name $FN_NAMESPACE \
cloud-object-storage \
--target-service-instance-name $COS_INSTANCE_NAME \
"Notifications Manager" 
```

## Functions ServiceID に Writer 権限を付与
`namespace` を作成すると、ServiceID (API Key) が自動で発行されます。
Functions では、`__OW_IAM_NAMESPACE_API_KEY` の環境変数にセットされるので、これを使って `IAM_TOKEN` を取得し他のサービス認証をおこなうことができます。 

- [Managing namespaces - What happens when I create a namespace?](https://cloud.ibm.com/docs/openwhisk?topic=openwhisk-namespaces#what-happens-when-i-create-a-namespace-)

その ServiceID (API Key) に Writer 権限を付与して Functions からオブジェクトを書き込めるようにします。

- [Bucket permissions - Granting access to a service ID](https://cloud.ibm.com/docs/cloud-object-storage/iam?topic=cloud-object-storage-iam-bucket-permissions&locale=en#iam-service-id)

```shell:

export COS_INSTANCE_ID=`ibmcloud resource service-instance $COS_INSTANCE_NAME | grep GUID | awk '{print $2}'`
```

```shell:

ibmcloud iam service-id-unlock $FN_NAMESPACE -f

ibmcloud iam service-policy-create $FN_NAMESPACE \
      --roles "Writer" \
      --service-name cloud-object-storage \
      --service-instance $COS_INSTANCE_ID 

ibmcloud iam service-id-lock $FN_NAMESPACE -f
```

## GitHub からダウンロード
GitHub にあるファイルをダウンロードします。

- [khayama/fn-icosReplication](https://github.com/khayama/fn-icosReplication)

```shell:

git clone https://github.com/khayama/fn-icosReplication.git
cd fn-icosReplication
```

今回の Functions デプロイ用 yaml では以下のように定義しています。
Functions 実行パラメータは、環境変数の値を取得して埋め込む形で定義しています。

```yaml:replicate-bucket.yml
packages:
  my-cloud-object-storage:
    version: 1.0
    license: Apache-2.0
    actions:
      replicate-object-write:
        version: 1.0
        description: replicate object-write between buckets
        docker: openwhisk/dockerskeleton
        function: replicate-object-write.sh
        inputs:
          TARGET_REGION: $TARGET_REGION
          TARGET_BUCKET: $TARGET_BUCKET
      replicate-object-delete:
        version: 1.0
        description: replicate object-delete between buckets
        docker: openwhisk/dockerskeleton
        function: replicate-object-delete.sh
        inputs:
          TARGET_REGION: $TARGET_REGION
          TARGET_BUCKET: $TARGET_BUCKET

    triggers:
      object-write:
        feed: /whisk.system/cos/changes
        inputs:
          bucket: $SOURCE_BUCKET # The COS bucket must be regional and exist in the same location as the namespace
          event_types: write # https://cloud.ibm.com/docs/openwhisk?topic=openwhisk-pkg_obstorage#pkg_obstorage_ev_ch_ref_trig
      object-delete:
        feed: /whisk.system/cos/changes
        inputs:
          bucket: $SOURCE_BUCKET # The COS bucket must be regional and exist in the same location as the namespace
          event_types: delete # https://cloud.ibm.com/docs/openwhisk?topic=openwhisk-pkg_obstorage#pkg_obstorage_ev_ch_ref_trig

    rules:
      replicate-object-write:
        description: Action is triggered by object-write operation
        action: replicate-object-write
        trigger: object-write
      replicate-object-delete:
        description: Action is triggered by object-delete operation
        action: replicate-object-delete
        trigger: object-delete
```

# Functions デプロイ

## 環境変数セット
以下のように環境変数をセットします。

```shell:

export SOURCE_BUCKET="khayama-source"
export TARGET_REGION="us-south"
export TARGET_BUCKET="khayama-target"
```

## yaml ファイルを指定してデプロイ

```shell:

ibmcloud fn deploy --manifest replicate-bucket.yml
# 削除するには ibmcloud fn undeploy --manifest replicate-bucket.yml
```
# 動作確認
実際に ICOS にファイルアップロードをおこない、Functions がトリガーされたかは以下の `Monitor` 画面で確認できます。

https://cloud.ibm.com/functions/dashboard
![貼り付けた画像_2020_07_02_1_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/ee054f72-6de7-210a-b870-f30fbb1b32cd.png "貼り付けた画像_2020_07_02_1_44.png")

サイズの小さなオブジェクトであれば、時間を見るとすぐにレプリケーションされていることが確認できます。

![Kobito.N0Cre7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/fc9fd134-331e-4584-fad7-6893ab7b7968.png "Kobito.N0Cre7.png")

![Kobito.O7XiZH.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/f35dc88f-5d75-e825-0b24-3024943a82fa.png "Kobito.O7XiZH.png")


## WRITE 時のレスポンス
```json:args.json
{
    "args": {
        "TARGET_BUCKET": "khayama-target",
        "TARGET_REGION": "us-south",
        "bucket": "khayama-source",
        "endpoint": "s3.private.jp-tok.cloud-object-storage.appdomain.cloud",
        "key": "5MB.zip",
        "notification": {
            "bucket_name": "khayama-source",
            "content_type": "application/octet-stream",
            "event_type": "Object:Write",
            "format": "2.0",
            "object_etag": "b3215c06647bc550406a9c8ccc378756",
            "object_length": "5242880",
            "object_name": "5MB.zip",
            "request_id": "829a1167-e547-45a0-9b87-7d7beb696054",
            "request_time": "2020-07-01T15:03:19.341Z"
        },
        "operation": "Object:Write"
    }
}
```
```http:result

HTTP/1.1 200 OK
Date: Wed, 01 Jul 2020 09:34:08 GMT
X-Clv-Request-Id: 682cf9d0-80dd-43e8-9aef-917a03d65f95
Server: Cleversafe/3.14.12.36
X-Clv-S3-Version: 2.5
x-amz-request-id: 682cf9d0-80dd-43e8-9aef-917a03d65f95
ETag: "a4c8d9261cdf437ba475bdcb637da93f"
Content-Type: application/xml
Content-Length: 240

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><CopyObjectResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><LastModified>2020-07-01T09:34:08.180Z</LastModified><ETag>"a4c8d9261cdf437ba475bdcb637da93f"</ETag></CopyObjectResult>
```

## DELETE 時のレスポンス
```json:args.json
{
    "args": {
        "TARGET_BUCKET": "khayama-target",
        "TARGET_REGION": "us-south",
        "bucket": "khayama-source",
        "endpoint": "s3.private.jp-tok.cloud-object-storage.appdomain.cloud",
        "key": "5MB.zip",
        "notification": {
            "bucket_name": "khayama-source",
            "content_type": "application/octet-stream",
            "event_type": "Object:Delete",
            "format": "2.0",
            "object_length": "5242880",
            "object_name": "5MB.zip",
            "request_id": "d7773487-430a-408f-bac4-795f08126194",
            "request_time": "2020-07-01T15:03:52.978Z"
        },
        "operation": "Object:Delete"
    }
}
```
```http:result

HTTP/1.1 204 No Content
Date: Wed, 01 Jul 2020 15:04:05 GMT
X-Clv-Request-Id: 45d82f2e-e78c-4532-aeda-af66aa37f8a0
Server: Cleversafe/3.14.12.36
X-Clv-S3-Version: 2.5
x-amz-request-id: 45d82f2e-e78c-4532-aeda-af66aa37f8a0
```

# さいごに
これでリージョナルバケットを他のリージョンに自動でレプリケーションできたことが確認できました。
クロスリージョンバケットを活用する場合はどうしてもパフォーマンスが落ちてしまうので、リージョナルのパフォーマンスを維持した上で、他リージョンにもデータをとっておきたい場合に有効な手段になると考えます。

# 参考
- [Generating an IBM Cloud IAM token by using an API key](https://cloud.ibm.com/docs/iam?topic=iam-iamtoken_from_apikey&locale=en)
- [IBM Cloud Object Storage trigger parameters](https://cloud.ibm.com/docs/openwhisk?topic=openwhisk-pkg_obstorage#pkg_obstorage_ev_ch_ref_trig)
- [Using cURL - Delete an object](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-curl#curl-delete-object)