原文はこちら
Oracle Cloud InfrastructureのFunction-as-a-Service(FaaS)プラットフォームであるOracle Functionsがすべての商用リージョンでGAになりました。
このポストではOracle Functionsを用いてOracle Cloud Infrastructure Object Storage上のバケットにあるオブジェクトの一覧を、セキュアで細かくアクセス制御されたやり方で取得するサーバレスJavaファンクションを構築、デプロイする方法をご紹介します。ここでは以下をご説明します:
- コードとコンセプト――背景とコードの概要
- ファンクションの設定とデプロイ
このファンクションはOracle Cloud Infrastructure Java SDKにあるObject Storage APIを利用してObject Storageバケットにアクセスします。
始める前に、ここで使うことになるOracle Cloud Infrastructureプラットフォームの特長を見ておきましょう。
Oracle Functions
Oracle Functionsはフルマネージドで高いスケーラビリティを備えたオンデマンドのFaaSプラットフォームです。それはサーバレスサービスであり、下回りのインフラを気にすることなくビジネス上の要求を満たすためのコードの開発に集中することができ、また、実行時に消費されたリソースの分だけが課金されます。
Oracle Functionsはどのクラウドでもオンプレミスでも稼働することができるオープンソースのコンテナネイティブ、サーバレスプラットフォームであるFn Projectをベースとしています。あなたはFn Projectのオープンソースディストリビューションをローカルにダウンロード、インストールして、ファンクションを開発してテストすることができます。そしてそれと同じツールをOracle Functionsへのデプロイに利用することができるのです。
Object Storage
Object Storageサービスはインターネットスケールのハイパフォーマンスなストレージプラットフォームであり信頼性が高くコストパフォーマンスの良いデータ永続化を提供します。Object Storageは分析データから画像や動画などのリッチなコンテンツまで、内容の種類を問わず非構造化データを容量無制限で格納します。
コード・ウォークスルー
では、ファンクションの設定とデプロイを行う前に、イメージをつけるために前述したことを行うコードを見ていきましょう。
認証
Oracle Cloud Infrastructure SDKに含まれるResource Principal認証プロバイダがObject Storageサービスへのファンクションのアクセス許可に使われています。ResourcePrincipalAuthenticationDetailsProvider オブジェクトがObject Storageを呼び出すために使われることになるObjectStorageClientのインスタンスを作成するのに使われます。
private ObjectStorage objStoreClient = null;
final ResourcePrincipalAuthenticationDetailsProvider provider
= ResourcePrincipalAuthenticationDetailsProvider.builder().build();
public ObjectStoreListFunction() {
try {
objStoreClient = new ObjectStorageClient(provider);
} catch (Throwable ex) {
System.err.println("Failed to instantiate ObjectStorage client - " + ex.getMessage());
}
}
これを動かすためには、Oracle Cloud Infrastructureのダイナミックグループにそのファンクションを含めてやる必要があります。その後、Identity and Access Management(IAM)ポリシーでそのダイナミックグループのObject Storageへのアクセスを許可しておきます。このステップは後のセクションで説明します。詳細はドキュメントをご覧ください。
Listファンクション
The handle method for the list objects function needs just the bucket name as the input. Then, it uses the API handle (listObjects method) to get all the bucket object names.
オブジェクトの一覧を取得するhandleメソッドに必要なのはバケット名の引数だけです。そして、このメソッドはAPI(listObjects メソッド)を使ってバケットの全オブジェクト名を取得します。
public List<String> handle(String bucketName) {
if (objStoreClient == null) {
System.err.println("There was a problem creating the ObjectStorageClient object. Please check logs");
return Collections.emptyList();
}
List<String> objNames = null;
try {
String nameSpace = System.getenv().get("NAMESPACE");
ListObjectsRequest lor = ListObjectsRequest.builder()
.namespaceName(nameSpace)
.bucketName(bucketName)
.build();
System.err.println("Listing objects from bucket " + bucketName);
ListObjectsResponse response = objStoreClient.listObjects(lor);
objNames = response.getListObjects().getObjects().stream()
.map((objSummary) -> objSummary.getName())
.collect(Collectors.toList());
System.err.println("Got list of objects in bucket " + bucketName);
} catch (Throwable e) {
System.err.println("Error fetching object list from bucket " + e.getMessage());
}
return objNames;
}
前提
ファンクションを設定、デプロイする前に、以下の準備をしておいてください。
Object Storage
Create an Object Storage bucket. For details, see the documentation. Then, upload a few objects to the bucket (documentation).
Oracle Functions
Set up and configure Oracle Functions in your tenancy as described in the documentation.
このドキュメントを参照して、Oracle Functionsをあなたのテナンシーでセットアップしておいてください。
ソースコード
ソースコードを格納するためのディレクトリ構造を作りましょう:
mkdir -p function-object-store-example/src/main/java/io/fnproject/example
cd function-object-store-example
以下の内容を持ったfunc.yamlという名前のファイルを作りましょう:
schema_version: 20180708
name: listobjects
version: 0.0.1
runtime: java
build_image: fnproject/fn-java-fdk-build:jdk11-1.0.98
run_image: fnproject/fn-java-fdk:jre11-1.0.98
cmd: io.fnproject.example.ObjectStoreListFunction::handle
timeout: 120
以下の内容を持ったpom.xmlという名前のファイルを作りましょう:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<groupId>io.fnproject.example</groupId>
<artifactId>list-objects</artifactId>
<version>1.0.0</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.oracle.oci.sdk</groupId>
<artifactId>oci-java-sdk-bom</artifactId>
<version>1.5.14</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.oracle.oci.sdk</groupId>
<artifactId>oci-java-sdk-objectstorage</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
</plugins>
</build>
<name>list-objects</name>
</project>
別のディレクトリに移ります:
cd src/main/java/io/fnproject/example/
以下の内容を持ったObjectStoreListFunction.java という名前のファイルを作りましょう:
package io.fnproject.example;
import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider;
import com.oracle.bmc.objectstorage.ObjectStorage;
import com.oracle.bmc.objectstorage.ObjectStorageClient;
import com.oracle.bmc.objectstorage.requests.ListObjectsRequest;
import com.oracle.bmc.objectstorage.responses.ListObjectsResponse;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class ObjectStoreListFunction {
private ObjectStorage objStoreClient = null;
final ResourcePrincipalAuthenticationDetailsProvider provider
= ResourcePrincipalAuthenticationDetailsProvider.builder().build();
public ObjectStoreListFunction() {
try {
//print env vars in Functions container
System.err.println("OCI_RESOURCE_PRINCIPAL_VERSION " + System.getenv("OCI_RESOURCE_PRINCIPAL_VERSION"));
System.err.println("OCI_RESOURCE_PRINCIPAL_REGION " + System.getenv("OCI_RESOURCE_PRINCIPAL_REGION"));
System.err.println("OCI_RESOURCE_PRINCIPAL_RPST " + System.getenv("OCI_RESOURCE_PRINCIPAL_RPST"));
System.err.println("OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM " + System.getenv("OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM"));
objStoreClient = new ObjectStorageClient(provider);
} catch (Throwable ex) {
System.err.println("Failed to instantiate ObjectStorage client - " + ex.getMessage());
}
}
public List<String> handle(String bucketName) {
if (objStoreClient == null) {
System.err.println("There was a problem creating the ObjectStorage Client object. Please check logs");
return Collections.emptyList();
}
List<String> objNames = null;
try {
String nameSpace = System.getenv().get("NAMESPACE");
ListObjectsRequest lor = ListObjectsRequest.builder()
.namespaceName(nameSpace)
.bucketName(bucketName)
.build();
System.err.println("Listing objects from bucket " + bucketName);
ListObjectsResponse response = objStoreClient.listObjects(lor);
objNames = response.getListObjects().getObjects().stream()
.map((objSummary) -> objSummary.getName())
.collect(Collectors.toList());
System.err.println("Got " + objNames.size() + " objects in bucket " + bucketName);
} catch (Throwable e) {
System.err.println("Error fetching object list from bucket " + e.getMessage());
}
return objNames;
}
}
Oracle Functionsへのデプロイ
ディレクトリのルートに移動します:
cd ../../../../../..
アプリケーションの作成
アプリケーションを作成して必要な設定を行います。あなたの全てのファンクションはこのアプリケーションの一部となります。
fn create app --annotation oracle.com/oci/subnetIds='["<OCI_SUBNET_OCIDs>"] --config NAMESPACE=<NAMESPACE> fn-object-store-app
ここで設定パラメータは以下のように定義します:
- OCI_SUBNET_OCIDs: Oracle Functions用に設定したコンパートメント内のサブネットをひとつ、あるいは複数指定します。
- NAMESPACE: Object Storageのネームスペース(ドキュメント)を指定してください。
例:
fn create app --annotation oracle.com/oci/subnetIds='["ocid1.subnet.oc1.phx.exampleuniqueid"]' --config NAMESPACE=foobar fn-object-store-app
ファンクションをデプロイ
以下のコマンドを実行してファンクションをデプロイします:
fn -v deploy --app fn-object-store-app
プロセスが完了したら、次のコマンドを実行してデプロイが成功したことを確認しましょう:
//to check your app (and its config)
fn inspect app fn-object-store-app
//to check associated function
fn list functions fn-object-store-app
Oracle Functions Resource Principalsの設定
ファンクションがデプロイされたので、Object Storageにアクセスできるようにテナンシーの設定を行っていきましょう。
ファンクションのOCIDを以下のコマンドで取得します:
fn inspect fn fn-object-store-app listobjects id
以下のマッチングルールでfn-obj-store-listという名前のダイナミックグループを作成してください:
resource.id = '<FUNCTION_OCID>'
以下のステートメント文でfn-object-store-list-policyという名前のIAMポリシーを作成してください。<COMPARTMENT_NAME>と<BUCKET_NAME>はご自身のテナンシーの値で置き換えてください:
allow dynamic-group fn-obj-store-list-dg to read objects in compartment <COMPARTMENT_NAME> where all{target.bucket.name=’<BUCKET_NAME>’}
ファンクションのテスト
いよいよデプロイしたばかりのファンクションを試してみる時がきました。
バケット内の全てのオブジェクト名を一覧:
echo -n '<object store bucket name>' | fn invoke fn-object-store-app listobjects
例:
echo -n 'test' | fn invoke fn-object-store-app listobjects
全オブジェクト名の一覧がレスポンスとして返ってくるはずです。以下が例です:
[
"file1.txt,
"file2.txt"
]
まとめ
ここまででOracle Cloud Infrastructure Object Storageとやり取りするファンクションをどのように開発するかのイメージを掴んでいただけたかと思います。Java SDKをファンクションの構築、設定、Oracle Functionsへのデプロイの中でどのように使うのかもおわかりになったでしょう。
ここでの例はごくシンプルなものでしたが、このコンセプトはどこでも応用できるものです。