ノート:

Oracle Cloud Infrastructure Functionsでの複数層バックエンド・アプリケーションのホスト

イントロダクション

Oracle Cloud Infrastructure Functions (OCI Functions)は、完全に管理された、マルチテナントでスケーラビリティが高いオンデマンドのFunctions-as-a-Serviceプラットフォームです。エンタープライズグレードのOracle Cloud Infrastructure(OCI)上に構築されており、Fn Projectオープン・ソース・エンジンによって強化されています。ビジネス・ニーズを満たすためのコードの記述に集中する場合は、OCI Functionsを使用します。OCI Functionsのサーバーレスおよびエラスティック・アーキテクチャは、実行する必要があるインフラストラクチャ管理やソフトウェア管理がないことを意味します。コンピュート・インスタンスをプロビジョニングまたはメンテナンスすることはなく、オペレーティング・システムのソフトウェアのパッチやアップグレードは自動的に適用されます。OCI Functionsは、アプリケーションの可用性を高め、スケーラブルかつセキュアで、モニターするだけで済みます。OCI Functionsを使用すると、Java、Python、Node、Go、RubyおよびC#でコードを作成できます(また、高度なユース・ケースの場合は、独自のDockerファイルおよびGraal VMを使用できます)。その後、コードのデプロイ、そのコードの直接コール、イベントへのレスポンスによるトリガー、および実行中に消費したリソースに対してのみ請求されるようにすることができます。

OCIまたはその他のクラウドで作業している間、アプリケーション・バックエンド・コードをサーバーレス・アーキテクチャでホストするための要件があり、OCI Functionsを使用できます。たとえば、アプリケーションに複数のチームによって開発された複数のバックエンド・コンポーネントがあるとします。あるチームはJavaでコンポーネントを開発し、別のチームはPythonを使用しています。これらの複数のアプリケーション・コンポーネントをOCI Functionsに移動し、APIエンドポイントを使用してコールできます。

目的

前提条件

タスク1: 必要な動的グループおよびポリシーの作成

  1. OCIコンソールに移動し、「アイデンティティとセキュリティ」「アイデンティティ」「動的グループ」に移動して、「動的グループの作成」をクリックします。

    動的グループの作成

  2. 動的グループの「名前」「説明」「一致ルール」などの詳細を入力します。どのリソースがどのコンパートメントでアクセスを取得するかを定義するため、ルールの詳細を慎重に追加します。

    動的グループの詳細

  3. 動的グループの詳細を保存し、OCIコンソールの詳細とともに動的グループが作成されるまでお待ちください。動的グループが作成されたら、この新しく作成された動的グループのIAMポリシーを作成して進みます。

  4. 「アイデンティティとセキュリティ」「アイデンティティ」「ポリシー」にナビゲートし、「ポリシーの作成」をクリックします。

    ポリシーの作成

  5. 動的グループの「名前」「摘要」「コンパートメント」などの詳細を入力します。ポリシー詳細は、ファンクションがどのコンパートメントでアクセスできる他のリソースを定義するため、慎重に追加してください。

    ポリシーの詳細

    ポリシー作成済

  6. ポリシーの詳細を保存し、OCIコンソールでポリシーが作成されるまでお待ちください。ポリシーが作成されたら、先に進んでアプリケーションおよびファンクションを作成します。

タスク2: 複数のプログラミング言語を使用したアプリケーションおよびファンクションの作成

  1. OCIコンソールに移動し、「開発者サービス」「ファンクション」「アプリケーション」に移動して、「アプリケーションの作成」をクリックします。

    アプリケーションの作成

  2. アプリケーションの名前VCNサブネットシェイプなどの詳細を入力します。許可されたネットワーク内でアプリケーションを起動できるようにするため、ネットワーキングを考慮してVCNおよびサブネットを選択します。

    アプリケーションの詳細

  3. アプリケーションの詳細を保存し、OCIコンソールの詳細とともにアプリケーションが作成されるまでお待ちください。アプリケーションが作成されたら、先に進んでファンクションを作成します。

    作成されたアプリケーション

  4. 「リソース」にナビゲートし、「アプリケーション」をクリックします。作成されたアプリケーションに接続してファンクションを作成するために必要なすべてのコマンドが表示されます。「Cloud Shell設定」または「ローカル設定」から任意のオプションを選択し、このチュートリアルで「Cloud Shell設定」を選択します。

    機能の開始

  5. OCI Functionsに接続したことを確認するには、次のコマンドを接続してトリガーできる必要があります。

    fn list apps
    

    関数リスト

  6. JavaやPythonなどの複数のプログラミング言語でいくつかの関数を作成し、ビジネス要件に従ってコードを追加します。

    fn init --runtime python getStreamData
    fn init --runtime java pushObjectStorageFile
    fn init --runtime java getADWSecret
    fn init --runtime python pushADWData
    
  7. 4つの異なる関数(Javaでは2つ、Pythonでは2つ)を作成しました。コードをデプロイし、コードが想定どおりに動作していることを確認するには、手動で呼び出します。

    cd getStreamData
    fn -v deploy --app oci-app-demo
    fn invoke oci-app-demo getStreamData
    
    cd pushObjectStorageFile
    fn -v deploy --app oci-app-demo
    fn invoke oci-app-demo pushObjectStorageFile
    
    cd getADWSecret
    fn -v deploy --app oci-app-demo
    fn invoke oci-app-demo getADWSecret
    
    cd pushADWData
    fn -v deploy --app oci-app-demo
    fn invoke oci-app-demo pushADWData
    

このバックエンド・アプリケーションのフローは、ストリーミングでデータを生成するプライマリ・アプリケーションであり、データはPythonで記述されたgetStreamData関数によって消費されます。次に、データをファイルとしてOCI Object Storageにプッシュし、Autonomous Data Warehouseのシークレット・パスワードを取得します。その後、データをAutonomous Data Warehouseにプッシュして、さらなる処理と分析を行います。

タスク3では、フローを作成できるように、JavaおよびPythonベースの関数呼出しをアプリケーションおよび関数コードに追加する方法を確認します。

タスク3: 別の関数から関数を呼び出すコードの追加

OCI SDKを使用して、1つのファンクションをコールまたは別のファンクションから呼び出すことができます。スニペットの例は、JavaおよびPythonで記述され、ビジネス・ロジックに従って使用できます。

  1. Java Software Development Kit (SDK)の次のコード・スニペットを追加します。

    package com.example.fn;
    
    import com.oracle.bmc.auth.ResourcePrincipalAuthenticationDetailsProvider;
    import com.oracle.bmc.objectstorage.ObjectStorage;
    import com.oracle.bmc.objectstorage.ObjectStorageClient;
    import com.oracle.bmc.objectstorage.requests.GetObjectRequest;
    import com.oracle.bmc.objectstorage.responses.GetObjectResponse;
    import com.oracle.bmc.Region;
    import com.oracle.bmc.functions.FunctionsInvokeClient;
    import com.oracle.bmc.functions.FunctionsManagementClient;
    import com.oracle.bmc.functions.model.FunctionSummary;
    import com.oracle.bmc.functions.model.ApplicationSummary;
    import com.oracle.bmc.functions.requests.ListApplicationsRequest;
    import com.oracle.bmc.functions.responses.ListApplicationsResponse;
    import com.oracle.bmc.functions.requests.ListFunctionsRequest;
    import com.oracle.bmc.functions.responses.ListFunctionsResponse;
    import com.oracle.bmc.functions.requests.InvokeFunctionRequest;
    import com.oracle.bmc.functions.responses.InvokeFunctionResponse;
    import com.oracle.bmc.util.StreamUtils;
    
    import java.nio.charset.StandardCharsets;
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.util.stream.Collectors;
    
    public class HelloFunction {
    
       private ObjectStorage objStoreClient = null;
       final ResourcePrincipalAuthenticationDetailsProvider provider
                = ResourcePrincipalAuthenticationDetailsProvider.builder().build();
    
       public HelloFunction() {
          try {
                objStoreClient = new ObjectStorageClient(provider);
          } catch (Throwable ex) {
                System.err.println("Failed to instantiate ObjectStorage client - " + ex.getMessage());
          }
       }
       public static class GetObjectInfo {
    
          private String bucketName;
          private String name;
    
          public String getBucketName() {
                return bucketName;
          }
    
          public void setBucketName(String bucketName) {
                this.bucketName = bucketName;
          }
    
          public String getName() {
                return name;
          }
    
          public void setName(String name) {
                this.name = name;
          }
    
       }
       public String handleRequest(GetObjectInfo objectInfo) {
    
          String result = "FAILED";
          final ResourcePrincipalAuthenticationDetailsProvider provider = ResourcePrincipalAuthenticationDetailsProvider.builder().build();
          final Region region = Region.US_PHOENIX_1;
          final String compartmentId = "ocid1.compartment.oc1..<your_compartment_ocid>";
          final String name = "oci-java-sdk-function";
          final String payload = "Hii";
    
          if (objStoreClient == null) {
                System.err.println("There was a problem creating the ObjectStorage Client object. Please check logs");
                return result;
          }
          try {
    
                String nameSpace = System.getenv().get("NAMESPACE");
    
                GetObjectRequest gor = GetObjectRequest.builder()
                      .namespaceName(nameSpace)
                      .bucketName(objectInfo.getBucketName())
                      .objectName(objectInfo.getName())
                      .build();
                System.err.println("Getting content for object " + objectInfo.getName() + " from bucket " + objectInfo.getBucketName());
    
                GetObjectResponse response = objStoreClient.getObject(gor);
                result = new BufferedReader(new InputStreamReader(response.getInputStream()))
                      .lines().collect(Collectors.joining("\n"));
    
                System.err.println("Finished reading content for object " + objectInfo.getName());
                invokeFunction(provider, region, compartmentId, name, payload);
    
          } catch (Throwable e) {
                System.err.println("Error fetching object " + e.getMessage());
                result = "Error fetching object " + e.getMessage();
          }
    
          //invokeFunction(provider, region, compartmentId, name, payload);
    
          return result;
    
       }
    
       public static FunctionSummary getUniqueFunctionByName(
                final FunctionsManagementClient fnManagementClient,
                final String compartmentId,
                final String applicationDisplayName,
                final String functionDisplayName)
                throws Exception {
          final ApplicationSummary application =
                   getUniqueApplicationByName(
                            fnManagementClient, compartmentId, applicationDisplayName);
          return getUniqueFunctionByName(
                   fnManagementClient, application.getId(), functionDisplayName);
       }
    
       public static FunctionSummary getUniqueFunctionByName(
                final FunctionsManagementClient fnManagementClient,
                final String applicationId,
                final String functionDisplayName)
                throws Exception {
    
          final ListFunctionsRequest listFunctionsRequest =
                   ListFunctionsRequest.builder()
                            .applicationId(applicationId)
                            .displayName(functionDisplayName)
                            .build();
    
          final ListFunctionsResponse listFunctionsResponse =
                   fnManagementClient.listFunctions(listFunctionsRequest);
    
          if (listFunctionsResponse.getItems().size() != 1) {
                throw new Exception(
                      "Could not find function with name "
                               + functionDisplayName
                               + " in application "
                               + applicationId);
          }
    
          return listFunctionsResponse.getItems().get(0);
       }
    
       public static ApplicationSummary getUniqueApplicationByName(
                final FunctionsManagementClient fnManagementClient,
                final String compartmentId,
                final String applicationDisplayName)
                throws Exception {
          final ListApplicationsRequest listApplicationsRequest =
                   ListApplicationsRequest.builder()
                            .displayName(applicationDisplayName)
                            .compartmentId(compartmentId)
                            .build();
    
          final ListApplicationsResponse resp =
                   fnManagementClient.listApplications(listApplicationsRequest);
    
          if (resp.getItems().size() != 1) {
                throw new Exception(
                      "Could not find unique application with name "
                               + applicationDisplayName
                               + " in compartment "
                               + compartmentId);
          }
    
          final ApplicationSummary application = resp.getItems().get(0);
          return application;
       }
    
       private static String invokeFunction(
                final FunctionsInvokeClient fnInvokeClient,
                final FunctionSummary function,
                final String payload)
                throws Exception {
          String response;
          try {
                System.err.println("Invoking function endpoint - " + function.getInvokeEndpoint());
    
                fnInvokeClient.setEndpoint(function.getInvokeEndpoint());
                final InvokeFunctionRequest invokeFunctionRequest =
                      InvokeFunctionRequest.builder()
                               .functionId(function.getId())
                               .invokeFunctionBody(
                                        StreamUtils.createByteArrayInputStream(payload.getBytes()))
                               .build();
    
                final InvokeFunctionResponse invokeFunctionResponse =
                      fnInvokeClient.invokeFunction(invokeFunctionRequest);
    
                response = "Done executing func1...";
                      //StreamUtils.toString(
                               //invokeFunctionResponse.getInputStream(), StandardCharsets.UTF_8);
    
          } catch (final Exception e) {
                e.printStackTrace();
                System.err.println("Failed to invoke function: " + e);
                throw e;
          }
    
          return response;
       }
    
       public static void invokeFunction(
          final ResourcePrincipalAuthenticationDetailsProvider provider,
          final Region region,
          final String compartmentId,
          final String name,
          final String payload)
          throws Exception {
    
          final FunctionsManagementClient fnManagementClient =
                   FunctionsManagementClient.builder().region(region).build(provider);
    
          final FunctionsInvokeClient fnInvokeClient =
                   FunctionsInvokeClient.builder().build(provider);
    
          try {
             final String appName = "e2e-function-demo";
             final String fnName = "java_func2";
             final FunctionSummary fn =
                      getUniqueFunctionByName(fnManagementClient, compartmentId, appName, fnName);
    
             final String response = invokeFunction(fnInvokeClient, fn, payload);
             if (response != null) {
                   System.out.println("Response from function:  " + response);
             }
          } finally {
             fnInvokeClient.close();
             fnManagementClient.close();
          }
       }
    }
    
    
  2. Python SDKの次のコード・スニペットを追加します。

    def trigger_function():
       logging.getLogger().info("Reached python function func1...")
       function_endpoint = "https://932jksmn9.us-ashburn-1.functions.oci.oraclecloud.com"
       function_ocid = "ocid1.fnfunc.oc1.iad.your_function_ocid"
       function_body = "Hii"
       signer = oci.auth.signers.get_resource_principals_signer()
       client = oci.functions.FunctionsInvokeClient(config={}, signer=signer, service_endpoint=function_endpoint)
       resp = client.invoke_function(function_id=function_ocid, invoke_function_body=function_body)
       logging.getLogger().info("Completed calling func2")
    

確認

その他の学習リソース

docs.oracle.com/learnの他のラボをご覧いただくか、Oracle Learning YouTubeチャネルで無料のラーニング・コンテンツにアクセスしてください。また、education.oracle.com/learning-explorerにアクセスしてOracle Learning Explorerになります。

製品ドキュメントは、Oracle Help Centerを参照してください。