【OCI クラウド移行ガイド】 AWS Lambda を OCI Functionsへ移行してみた（その3:OCIからAWSサービスを扱う編）


(その1:Hello World編はこちら）
https://qiita.com/araidon/items/6ae7067efe92ecaec776

(その2:S3 put編はこちら)
https://qiita.com/araidon/items/56d7942dbd19ec1c1e87

# OCIクラウド移行ガイドとは

オンプレミスやAWSなど、複数のプラットフォームからOracle Cloud Infrastructureへの移行プロジェクトに取り組んでいるクラウドエンジニア（@araidon,@kazunishi,@yama6）による、OCI移行手順をまとめたシリーズ記事です。
各回、サンプルワークロードから対象サービスを取り上げ、移行手順をガイドいたします。
まとめ記事は以下になります。

https://qiita.com/yama6/items/b197c0fe3ec75eb02637

# 移行するサービス：AWS Lambda

今回、移行対象とするのはAWS Lambdaです。
例えばOCIに移行することが難しいAWSサービスに対して、AWS Lambdaからではなく、
OCI Functionsから利用します。

今回は、OCIのObjectの画像を元に、AWS Rekognition で画像解析し、結果のJSONをOCIのObject Storageに保存します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/463e96c7-9959-29dd-c01c-487ed3825546.png)


# OCI Functionsとは
一言でいうと、OCIでのFaaSサービスです。
いわゆるサーバレスサービスで、AWS Lambda、Azure Functions、Google Cloud Functions等と同等のサービスです。

AWS Lambdaと比較して、価格面で優位となっているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/17439df8-faea-79f7-bcba-f2b6b1d207aa.png)

https://speakerdeck.com/oracle4engineer/functions-overview


# 前提条件
- 言語はPythonとします
- AWSのアカウントは作成済みとします
- AWS LambdaとOCI Functionsの環境構築について、今回は触れません
- Functionsの環境構築手順は以下を参照願います（公式チュートリアル）

https://oracle-japan.github.io/ocitutorials/cloud-native/functions-for-beginners/#%E4%BA%8B%E5%89%8D%E6%BA%96%E5%82%99

# 1. 準備
## 1-1. (AWS) アクセスキーとシークレットキーの作成
AWSにて、サービスに権限があるIAMユーザのアクセスキーとシークレットキーを作成します。
コンソールにて
IAM > ユーザー > アクセスキーを作成
![CleanShot 2024-08-27 at 01.37.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/42af7f4e-2153-8e8b-5160-71d65639cc3b.png)

## 1-2. (OCI) Functionsにアクセスキーとシークレットキーを登録
OCIにて、作成したFunctionsの構成に、アクセスキーとシークレットキーを登録します。
|キー|値|
|-|-|
|aws_secret_access_key|(AWSで作成したシークレットアクセスキー)|
|aws_access_key|(AWSで作成したアクセスキー)|
|region|(アクセスするAWSリージョン)|

![CleanShot 2024-08-27 at 01.44.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/02d95f40-a011-4a18-58c6-cbfd17fdb6f3.png)

## 1-3. (OCI) 画像保管用のObject Storageと、結果保管用のObject Storageを作成
下記画像では、以下の2バケットを作成
- 画像保管用 - Image-for-aws-rekognition
- 結果保管用 - Image-for-aws-rekognition-json
![CleanShot 2024-08-27 at 01.57.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/3f64f46f-cab0-aade-5b5f-b8c19d01f458.png)

## 1-4. (OCI) Object Storageにポリシーを付与
Functions実行ユーザーに、ポリシーを適用します。

```bash
# 例1. 特定のグループがテナンシ内の全バケットへのポリシー適用
Allow group (ユーザーの所属グループ) to manage buckets in tenancy

# 例2. 特定のグループが特定のコンパートメントにのみポリシー適用
 Allow group (ユーザーの所属グループ) to manage buckets in compartment (コンパートメント名)  
```

ポリシー設定の詳細は以下を参照

https://docs.oracle.com/ja-jp/iaas/Content/Security/Reference/objectstorage_security.htm

# 2. 実装
OCIからAWSのサービスを扱うために、AWS SDK for PythonことBoto3を使用します。

https://aws.amazon.com/jp/sdk-for-python/

```python: OCIからBoto3でRekognitionを利用
import json
import io
import os

import oci
import boto3


def handler(ctx, data: io.BytesIO = None):
    # Functionsの環境変数から   変数取得
    aws_access_key        = os.getenv('aws_access_key')
    aws_secret_access_key = os.getenv('aws_secret_access_key')
    region                = os.getenv('region')

    # OCI Object Storage用の変数
    namespace      = "" # OCIテナントのnamespace
    bucketName     = "" # 画像作成用のバケット名
    objectName     = "" # アップロードしたオブジェクト名
    bucketNameJson = "" # Jsonファイル格納用バケット名


    # OCI SDKを利用して、OCI Object Storageを扱う
    signer = oci.auth.signers.get_resource_principals_signer()
    object_storage = oci.object_storage.ObjectStorageClient(config={}, signer=signer)
    # 画像ファイルを取得
    get_object = object_storage.get_object(namespace, bucketName, objectName)



    # boto3を利用して、Rekognitionを使う
    rekoclient = boto3.client('rekognition',
        aws_access_key_id=aws_access_key,
        aws_secret_access_key=aws_secret_access_key,
        region_name=region
        )

    # Rekognitionのcelebritiesを利用して、有名人の顔を検出
    response = rekoclient.recognize_celebrities(
        Image = {"Bytes": get_object.data.content}
    )

    s = json.dumps(response, ensure_ascii=False, indent=2)	# JSON形式の文字列に変換
    # JSONファイルをObject Storageにアップロード
    object_storage.put_object( namespace, bucketNameJson, objectName + "-celebrities.json", s) 
```

# 3. 検証

Oracleの会長兼CTOのラリー・エリソンの画像を使って試してみます。
画像は以下の公式サイトから取得
https://www.oracle.com/jp/artificial-intelligence/larry-ellison-cloudworld-genai-strategy/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/23c9db91-ed8a-7b04-f0ac-f54dddb8a69c.png)

## 3-1. 画像アップロード
画像保管用のObject Storageに先程のファイルをアップロード
![CleanShot 2024-08-27 at 03.09.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/967d2fc4-64d8-e0e8-7178-4834026fa591.png)

## 3-2. Functionsの実行

今回はCLIで実行
```bash
$ fn invoke toaws-app to-aws-rekognition
$ 
```

ログを見て、正常に終了したことを確認
![CleanShot 2024-08-27 at 03.11.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/585cd3ea-c882-d9fe-8997-0e249eca8d2a.png)

AWSのRekognitionのメトリクスで、Callされていることを確認
![CleanShot 2024-08-27 at 03.15.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/ed22ef7c-143e-714c-7058-a3d65e6910fd.png)

## 3-3 Outputを確認
結果確認用バケットにjsonファイルがアップロードされていることを確認
![CleanShot 2024-08-27 at 03.17.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276568/94902263-9d75-fd05-5ca1-2b9e7a49445a.png)

jsonの中身抜粋、 "Name": "Larry Ellison"となっていることを確認
```json: 
{
  "CelebrityFaces": [
    {
      "Urls": [
        "www.wikidata.org/wiki/Q92759",
        "www.imdb.com/name/nm0255213"
      ],
      "Name": "Larry Ellison",
      "Id": "3d1Jw1z",
      "Face": {
        "BoundingBox": {
          "Width": 0.07612882554531097,
          "Height": 0.24945056438446045,
          "Left": 0.5756229162216187,
          "Top": 0.10707202553749084

 (中略)
 
  "UnrecognizedFaces": [],
  "ResponseMetadata": {
    "RequestId": "4c440606-38f8-43a3-96f5-3f8ed2c41ed9",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "x-amzn-requestid": "4c440606-38f8-43a3-96f5-3f8ed2c41ed9",
      "content-type": "application/x-amz-json-1.1",
      "content-length": "1349",
      "date": "Mon, 26 Aug 2024 18:11:27 GMT"
    },
    "RetryAttempts": 0
  }
}
```

# まとめ
意外と簡単に実装できました。
AWS側でシークレットキーとアクセスキーを発行して、OCIでBoto3を利用するだけです。
それでは、より良いマルチクラウドライフを〜。

# 参考

https://speakerdeck.com/oracle4engineer/functions-overview

https://docs.oracle.com/ja-jp/iaas/Content/Functions/home.htm

https://docs.aws.amazon.com/ja_jp/pythonsdk/