これは、GoとOracle Cloud Infrastructureに関する5つのパート・シリーズの第3の分割です。このシリーズでは、コンピュート・インスタンス(VM)のOracle Cloud Infrastructure (OCI)、Kubernetesでコンテナ化、またはサーバーレス・ファンクションとしてGoアプリケーションを作成および実行する方法について説明します。この記事では、OCI DevOpsを使用して、これらのGoアプリケーションのビルドとデプロイメントを自動化する方法を紹介しています。重要なトピックは、GoアプリケーションのOCIサービス(OCIで実行されているものと、他の場所で実行されているGoコードの両方)を使用する方法です。ここで説明するOCIサービスには、オブジェクト・ストレージ、ストリーミング、Key Vault、Autonomous Databaseなどがあります。
これらの記事をフォローするには、読者は少なくともGoアプリケーションの作成方法に関する基本的な知識を持っている必要があります。読者は、独自のGo開発環境にアクセスできるものとします。例やスクリーンショットの中には、特に開発ツールとしてVS Codeについて言及するものもあります。ただし、他のエディタやIDEも使用できます。これらの記事で示されているGoコードは、最も単純な形式で、依存関係が最小限であることを最大限にわかりやすくするために多くのメカニズムを示しています。読者は、意味のある機能や本番対応のコードを期待してはいけません。
この記事では、OCIへの移行方法について説明します。この例を試すには、読者がOCIテナンシへのアクセス権を持ち、これらの記事で説明するOCIリソースを作成する権限を持っている必要があります。使用されるリソースのほとんどは、Aways Free Tier (コンピュート・インスタンス、VCN、Autonomous Database、オブジェクト・ストレージ、ロギング、リソース・マネージャ)で使用可能であるか、月次使用制限(ファンクション、APIゲートウェイ、ストリーミング、Vault、DevOps)の空き割当て層があります。
このシリーズの最初の部分では、Oracle Linux Cloud Developerイメージに基づくコンピュート・インスタンスのプロビジョニング、インバウンドおよびアウトバウンドのネットワーク・アクティビティ用のオープン、HTTPリクエストを処理するGoアプリケーションの作成と実行、アプリケーションによって生成されたロギングのOCIロギングへの接続について説明します。第2部では、OCI DevOpsサービスによるソフトウェア・エンジニアリング、ビルドの自動化、およびアプリケーションのデプロイメントについて説明します。このサービスは、Goソース・コードの格納、アプリケーション実行可能ファイルの構築、デプロイ可能なアーティファクトとしての格納、およびコンピュート・インスタンスへのそのアーティファクトのデプロイに使用されます。2番目の記事では、OCI API Gatewayを介してアプリケーションのHTTPエンドポイントを公開する方法も示しています。
この3つ目の部分は、Goでサーバーレス関数を作成し、OCIにデプロイする方法を示しています。Go SDK for OCIが導入されました。まず、ローカル、スタンドアロンGoアプリケーション、その後ファンクションから使用するために、リソース・プリンシパル認証を利用します。このSDKは、バケットの作成およびファイルの書込みおよび読取りのためにOCI Object Storageサービスと対話するために使用されます。
最初は、ファンクションが手動で構築およびデプロイされます。APIゲートウェイのデプロイメントに、OCI外部のクライアントからファンクションを起動するためのルートが追加されます。次に、コンテナ・イメージ・レジストリ内のイメージからファンクションをデプロイするためのOCI DevOpsデプロイメント・パイプラインが作成されます。最後に、ビルド・パイプラインは、コード・リポジトリ内のソースを取得し、コンテナ・イメージを構築および公開してから、エンドツーエンドのビルドおよびデプロイのデプロイメント・パイプラインをトリガーするように設定されています。
OCIのサーバーレス機能は、オープン・ソースのProject Fnの技術に基づいています。ファンクションのビジネス・ロジックは、お気に入りの言語(この場合はGo)で記述され、ファンクションのライフサイクルおよびファンクションとの相互作用を処理するFnフレームワークに埋め込まれます。Fnフレームワークは、ローカル・マシン上、任意のクラウド上のVM内、またはオンプレミス上の任意の場所で実行できます。Oracle Cloud Infrastructureは、同じテクノロジに基づいたサーバーレス機能用のフルマネージドのPaaSサービスOCI Functionsを提供しています。
関数はコンテナ・イメージに組み込まれています。このイメージはコンテナ・イメージ・レジストリにプッシュされます。ファンクションを公開するために、このイメージはFnサーバーに転送されます。ファンクションが呼び出されるたびに、コンテナがイメージから起動され、リクエストが処理されます。コンテナは、起動の処理後、追加のリクエストを処理する準備ができたホット状態で、しばらくの間実行し続けます。同時リクエストの数が増加すると、すべてのリクエストを処理できるように、同じファンクションの追加コンテナがFnサーバーによって起動されます。
開発者やアプリケーション・オペレータのための機能の魅力は、ビジネス・ロジックを実行するプラットフォームの設計、作成および管理にエネルギーを注ぐ必要がないという事実です。全てはその論理を書くことになる。
ここでは、Goでファンクションを作成し、それをコンテナ・イメージに構築し、ローカルでデプロイして実行します。次に、この関数をOCI Functionsサービスに移動し、クラウド側で実行します。
関数を開発するには、Project Fnをサポートする環境が必要です。Fnは、ラップトップ、サーバーまたはクラウドで実行できる軽量のDockerベースのサーバーレス関数プラットフォームです。Fn Projectのチュートリアル –Fnのインストールの手順に従って、LinuxまたはMacOSにFnを簡単にインストールできます。
最初に作成し、このシリーズの2回目のインストールでも使用したgo-app-vmコンピュート・インスタンスで作業することを選択できます。このOracle Linux環境にはFnが設定されていませんが、インストールはかなり簡単です。
または、OCI Cloud Shellで作業することもできます。このブラウザでアクセス可能な環境は、Fnで設定されます。OCI Cloud ShellでのFnの操作については、OCIドキュメント関数: クラウド・シェルを使い始めるを参照してください。
Fn CLIがインストールされている開発環境で、ファンクションのサブディレクトリを作成するディレクトリに移動します。コマンド行で、次のコマンドを入力して実行します。
fn init --runtime go --trigger http greeter
greeterというサブディレクトリが作成されます。ナビゲートして内容を確認します。
cd greeter ls -l
ファイルfunc.yamlには、ファンクションに関するメタデータが含まれており、ビルド時にFnフレームワークによって解釈され、その後ファンクションの実行時に解釈されます。ファイル go.modには、関数がfdk-goパッケージに対して持つ依存関係が含まれています。実際の関数自体はfunc.goにあります。ファンクションの構造とFnランタイムとの相互作用は、ここで確認できます。ファンクションmainは、ファンクションmyHandlerをFnランタイムに登録します。このランタイムは、受信したHTTPリクエストに対して、このファンクションを実行時に起動するように指示し、有効にします。この関数は、io.Readerパラメータでリクエストの本文を受け取ります。また、レスポンス本文を書き込むことができるio.Writerの出力も受信します。context.Contextパラメータctxには、HTTPヘッダー、完全URL、リクエスト・メソッド、関数自体など、元のリクエストのメタデータが含まれます。これには、そのリクエストに定義されているすべての構成変数も含まれます。
現在、myHandler関数は、nameというフィールドを持つJSONペイロードを含むことを想定して、リクエスト本文をデコードします。このフィールドの値に名前が設定された個人が作成され、その名前が設定されていない場合はデフォルトで「世界」に設定されます。次に、予期されるレスポンス(messageという単一のフィールドを持つJSONオブジェクト)を作成します。このオブジェクトには、Helloから構成された文字列と名前の値が含まれます。
本当に素晴らしいことは何もしませんが、機能は健全で完全であり、ローカルにデプロイして呼び出すことができます。そのためには、ローカル・コンテキストとローカルFnサーバーを起動して実行する必要があります。次のものを使用してコンテキストを確認します。
fn list contexts
これは、少なくとも1つのコンテキスト(場合によっては複数のコンテキスト)のリストを示します。ローカルFnサーバーを使用するには、デフォルトのコンテキストがアクティブなコンテキストであることを確認します。現在のコンテキストをデフォルトに設定する必要がある場合は、次を使用します。
fn use context default
次に、ファンクションのホストとしてアプリケーションを作成します。
fn create app go-oci-app fn list apps
これらの文の最初の文が接続が拒否されて失敗した場合、サーバーはおそらくまだ実行されていません。次のコマンドを使用してサーバーを起動し、アプリケーションの作成を再試行してください。
fn start
アプリケーションが正常に作成されると、ファンクションをそのアプリケーションにデプロイできるようになります。次のコマンドは、このデプロイメントを処理します。その前にコンテナ・イメージ構築プロセスがあります。
fn --verbose deploy --app go-oci-app --local
--localを指定すると、ローカル・サーバーにデプロイされますが、ファンクション・イメージはDockerレジストリにプッシュされません。これは、リモートFnサーバーにデプロイする場合に必要になります。
非常に多くのログ・メッセージが出力されるため、--verboseフラグは常時使用するフラグではありません。しかし、それはあなたが何が起こっているのかについてかなり良い洞察を与えます。複数のコンテナ・イメージがプルされ、2ステージのコンテナ構築プロセスが実行されて、greeter関数のコンテナ・イメージが作成されます。事前定義済のFnプロジェクト・イメージは、ビルド・ステージ(書込み時のfnproject/go:1.15-dev)およびランタイム・イメージの基盤として使用されます(fnproject/go:1.15)。
最終出力は次のようになります。
Updating function greeter using image greeter:0.0.2... Successfully created function: greeter with greeter:0.0.2 Successfully created trigger: greeter Trigger Endpoint: http://localhost:8080/t/go-oci-app/greeter
関数イメージはgreeter:0.0.2と呼ばれます。このイメージは、次を使用してローカル・コンテナ・レジストリにあります。
docker images | grep greeter
このファンクションは、次のように名前とアプリケーションを使用してFn CLIを介して起動できます。
fn invoke go-oci-app greeter
この関数は、名前フィールドを含むJSONペイロードを想定しているため、次のことを正確に提供します。
echo -n '{"name":"Clark Kent"}' | fn invoke go-oci-app greeter --content-type application/json
ファンクション・デプロイメントからの出力では、ファンクションのトリガー・エンドポイントも示されました。これは、HTTPリクエストを送信してファンクションをトリガーできるHTTPエンドポイントです。Fnとの(表示可能な)相互作用はありませんが、起動するエンドポイントは実際にはFn Serverエンドポイントです。URLパスは、トリガーするアプリケーションおよび特定の関数をFnに指示します。
curl -X "POST" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' http://localhost:8080/t/go-oci-app/greeter
ここでは、開発環境のみでなく、OCI上にこのファンクションを作成しましょう。ステップは、関数をローカルで作成するために使用したステップと非常によく似ています。別のコンテキストのみを使用する必要があります。ローカル・コンテキストではなく、OCI用です。
申込を作成
まず、OCIコンソールを使用してアプリケーションを作成します。検索ボックスにappと入力し、サービス領域のApplications Functionsをクリックします。
「アプリケーションの作成」ボタンをクリックします。アプリケーションの名前を入力します: go-on-oci-app。記事シリーズおよびその1つのパブリック・サブネットの一部として作成されたVCNを選択します。次に、「Create」をクリックしてアプリケーションを作成します。
OCIインタラクションおよびファンクション・イメージ・プッシュ用のローカル環境の準備
アプリケーションが作成されると、アプリケーションの一般情報が表示されます。このページには、OCI Cloud Shellまたはローカル設定(もちろん、go-app-vmコンピュート・インスタンスも可能)で最初の関数を作成する手順も含まれています。
OCI Cloud Shellを使用している場合、このコンテキストの作成ステップは、通常の開発環境で作業する場合と若干異なります(さらに単純です)。OCIシェルの設定に従ってください。この記事では、すべてのローカル開発環境に使用されるもう1つのパスを取ります。
ローカル環境(Fn CLIが以前にインストールされた環境)で実行するステップはいくつかあります。
これらのステップの後、このコマンドを使用してステップ1、2および4の成功を試すことができます。これにより、テナンシ内のコンパートメントのリストが返されます:
oci iam compartment list
オプション: コンテナ・イメージ・レジストリ・リポジトリの作成
ファンクションのデプロイに使用されるユーザー・アカウントに必要なIAM権限がある場合、デプロイメントによって、コンテナ・イメージ・レジストリ内のファンクション・イメージのリポジトリが作成されます。これらの権限が使用できない場合、またはリポジトリを準備する場合は、次のように実行できます。
Fn CLIでのOCIのコンテキストの作成
ローカル環境のコマンドラインに戻り、OCI上の現在のコンパートメントのFnコンテキストを作成し、その後Fn操作で使用するFnコンテキストを選択する必要があります。次のコマンドを実行します(これは、go-on-oci-appページの「スタート・ガイド」タブからコピーできます)。
fn create context go-on-oci --provider oracle fn use context go-on-oci
ステップ4のコマンドをコピーして、コンパートメントOCIDおよびOracle Functions API URLでコンテキストを更新します。私の場合:
fn update context oracle.compartment-id ocid1.compartment.oc1..aaaaaaaaqb4vxvxuho5h7eewd3fl6dmlh4xg5qaqmtlcmzjtpxszfc7nzbyq fn update context api-url https://functions.us-ashburn-1.oraclecloud.com
コマンドは似ていますが、あなたには異なります。
一意のリポジトリ名接頭辞を指定します。go-on-ociを使用して、ファンクション・イメージを公開する必要があるイメージ・レジストリ・リポジトリを含むコンパートメントを指定します:
fn update context registry iad.ocir.io/idtwlqf2hanz/go-on-oci fn update context oracle.image-compartment-id
パスワードとして認証トークンを使用してレジストリにログインします:
docker login iad.ocir.io
私の場合、私が作業するリージョンは、リージョン・キーiad.ocir.ioで識別されるアッシュバーンです。ユーザー名の入力を求められます。これは、コンテナ・イメージ・レジストリの名前および各リポジトリに含まれるネームスペース接頭辞を含む文字列です。その後、パスワードが要求されます。ここでは、ユーザーに設定された認証トークンを指定します。この認証トークンは、コード・リポジトリでログインが実行された前の記事で使用していました。
次のコマンドは、現在のFnコンテキスト内のアプリケーションのリストを表示します。
fn list apps
このリストには、go-on-oci-appという1つのアプリケーションが含まれています。
以前に作成、ローカルにデプロイおよび起動されたファンクション・グリータをOCIアプリケーションにデプロイして、クラウドネイティブのサーバーレス・ファンクションになることができるようになりました。デプロイに使用するコマンドは、以前使用したコマンドと同じです。その影響は、コンテキストが変更されたために劇的に異なります。ローカル・コンテキストのかわりに、OCIプロバイダに基づくコンテキストがあり、OCIテナンシおよびコンパートメントにリンクされるようになりました。コンテナ・イメージがOCIコンテナ・イメージ・レジストリにプッシュされ、ファンクションがOCIファンクション・サービスに作成されます。
fn -v deploy --app go-on-oci-app
出力は以前に生成されたものと似ていますが、作成プロセスはまったく同じです。ファンクション・コンテナ・イメージの準備ができたら、物事が偏り始めます。イメージがOCIコンテナ・イメージ・レジストリにプッシュされ、ファンクションがクラウドにデプロイされます。出力の関連行は次のとおりです。
=> exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:008dc3b990f1e69d67a7dd8649fbd63649d72f0bf1a161b2c2e073064f16c918 0.0s => => naming to iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 0.0s Parts: [iad.ocir.io idtwlqf2hanz go-on-oci greeter:0.0.3] Using Container engine docker to push Pushing iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 to docker registry...The push refers to repository [iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter] ... e57f007acf74: Pushed 0.0.3: digest: sha256:bb4f2abde44d97517520571a21c407e349ddfc6572583a8ba53717436fd0b7f5 size: 1155 Updating function greeter using image iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3... Successfully created function: greeter with iad.ocir.io/idtwlqf2hanz/go-on-oci/greeter:0.0.3 Fn: HTTP Triggers are not supported on Oracle Functions
この時点で、ファンクションはクラウドにあり、(Fn CLIを使用して)起動できます:
fn invoke go-on-oci-app greeter
ファンクションがコールド・アウトを開始し、基礎となるコンテナ・イメージを実行中のコンテナにインスタンス化する必要があるため、最初のコールにはかなり時間がかかります。関数の後続の呼び出しはすべて、はるかに高速になります。10分待ってからファンクションがコールドになった場合、コンテナは停止します。
このイメージは、到着した状況を示しています。
OCIコンソールで、今起こったことの証拠を確認できます。コンソールの検索ボックスにgreeterと入力します。「リソース」の下に、「greeter」→「Functions」というエントリがあります。リンクをクリックすると、機能の詳細を示すページに移動します。ファンクション・イメージ、メモリー設定およびファンクションを呼び出すエンドポイントへの参照があります。メトリックの下に、Fn CLIを使用して行われたファンクションのコールに関する証拠があります。
greeterの検索結果には、コンテナ・リポジトリのgo-on-oci/greeterも表示されます。リポジトリに移動すると、リポジトリに公開されているイメージの詳細が表示されます。
OCI Functionsは単なる起動もできません。HTTPエンドポイントがあり、ブラウザまたはコマンドラインでcurlから呼び出すことができることを示唆しているように見えますが、実際にはそれほど単純ではありません。ファンクションへのHTTPコールは署名する必要があり、この署名プロセスは単純で単純ではありません。
コンシューマがファンクションを起動できるようにするには、APIゲートウェイを使用することをお薦めします。前の記事でAPI Gatewayを使用して、(潜在的に)プライベート・コンピュート・インスタンスで実行されているmyserverアプリケーションへのパブリック・ルートを開きました。ここでは、API Gateway the-API-gatewayの追加ルートと、前の記事で作成したデプロイメントmyserver-APIを使用して、greeter関数についても同じことを行います。
APIゲートウェイのIAMアクセスの設定
APIゲートウェイがファンクションを呼び出すための権限を提供するポリシーを使用して、APIゲートウェイがファンクションを呼び出すことを許可する必要があります。
ファンクションを呼び出すAPIゲートウェイのポリシーを作成します。コンソールでポリシーを作成するには、検索バーにpoliと入力し、検索結果ポップアップの「サービス」領域で「ポリシー」→「アイデンティティ」をクリックします。これにより、現在のコンパートメントの「ポリシー」概要ページが表示されます。
ポリシーは、コンパートメント内のリソースにアクセスするためのAPIゲートウェイの権限を定義します。新しいポリシーを作成し、名前(invoke-function-for-api-gateway)、説明および次の文を入力します。
ALLOW any-user to use functions-family in compartment where ALL {request.principal.type= 'ApiGateway', request.resource.compartment.id = ' '}
APIゲートウェイでのデプロイメントでのファンクションのルートの定義
対処された権限を使用して、APIゲートウェイでルートを定義できるようになりました。コンソールの検索バーにgatと入力します。「ゲートウェイ」→「API管理」をクリックします。*the-api-gatewayのリンクをクリックします。「Deployments」をクリックします。デプロイメントのリスト(単一のデプロイメントを含む)で、リンクmyserver-apiをクリックします。
「編集」ボタンをクリックして、デプロイメント仕様を開きます。2番目のステップの「ルート」のリンクをクリックします。下にスクロールして、「+別のルート」ボタンをクリックします。
この新しいルートのパスとして/greetingと入力します。メソッドとしてGETを選択し、バックエンドの「タイプ」としてOracle Functionsを選択します。アプリケーションgo-on-oci-appを選択し、「関数名」をgreeterに設定します。
「次へ」を押します。その後、「Save Changes」を押して変更を適用し、新しいルートをリアルにします。
APIゲートウェイを介したファンクションの起動
新しいルートが設定され、デプロイメントがAPI Gatewayでリフレッシュされたことで、API Gatewayのパブリック・エンドポイントに対してシンプルで簡単なHTTPリクエストを作成し、関数greeterを間接的にトリガーしてレスポンスを受信できるようになりました。
ブラウザでこのURLを使用すると、ファンクションのレスポンスを取得できます。
https:///my-api/greeting
応答は少し圧倒的ですが、このような単純な機能で期待されます。
curlを使用すると、JSONペイロードを関数に送信し、より興味深いレスポンスを受信できます。
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting
応答は{"message":"Hello Mickey Mouse"}です。
そのため、API Gatewayからサーバーレス関数へのエンド・ツー・エンドのフローを確立しました。また、ローカル開発環境のソースに基づいてファンクションを手動でデプロイする方法もあります。作業を活用するために、func.goのソース・コードにいくつか変更を加えてから、Fn CLIを使用した単一のコマンドであるファンクションを再度デプロイし、APIゲートウェイでグリーティング・ルートを起動して、変更がライブであることを確認できます。
例: Msgの値を設定する行を次のように変更します。
Msg: fmt.Sprintf("Warmest greetings from your function dear %s", p.Name)
更新されたfunc.goソースを保存します。次に、これらのコマンドを実行して、更新された関数をデプロイし、その後起動します。
fn -v deploy --app go-on-oci-app
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting
その結果、応答が向上します。ビルドおよびデプロイ・プロセスは、準備された環境で単一の手動コマンドにまとめられます。次に、OCI DevOpsを使用するファンクションの自動デプロイメント・プロセスと、コード・リポジトリ内のソースに基づく前述の自動ビルド・プロセスについて説明します。次に、単純な挨拶を返すよりも少し多く行う関数に移動します。
このシリーズの前回のインストールでは、アプリケーションをコンピュート・インスタンスにデプロイするためのOCI DevOpsデプロイメント・パイプラインの使用を確認しました。ここでは、ファンクションの自動デプロイメントにパイプラインを使用します。全体的なアプローチと成分は似ています。アーティファクト、(ターゲット)環境、およびファンクション・デプロイメント・ステージを持つデプロイメント・パイプライン、およびパイプラインがアーティファクトを読み取ってファンクションをデプロイするためのIAM権限が必要です。
これらの成分の詳細:
OCIコンソールで、DevOps Project go-on-OCIのホーム・ページに移動します。「アーティファクト」タブを開きます。「アーティファクトの追加」ボタンをクリックします。ここで定義する内容は、アーティファクト自体ではなく、DevOpsプロジェクトから実際のアーティファクトへのリンクまたはプロキシです。
DevOpsアーティファクトの名前としてgreeter-functionと入力します。タイプはコンテナ・イメージ・リポジトリに設定する必要があります。イメージへの完全修飾パスは、リージョン・キー、リポジトリ・ネームスペースおよび接頭辞、ファンクション名およびファンクション・バージョン・タグで構成されます。この場合、バージョン・タグのプレースホルダを使用します。パスは次のように定義されます。
///greeter:${imageVersion}
ドロップダウン・フィールド「このアーティファクトで使用されるパラメータの置換」を「はい」に設定し、プレースホルダを置換します。
「追加」ボタンをクリックしてアーティファクトの定義を完了し、保存します。
DevOpsプロジェクトの「環境」タブを開きます。ここには、コンピュート・インスタンスへのmyserverのデプロイ用に作成されたgo-on-oci-vm環境が含まれています(前の記事を参照)。「環境の作成」ボタンをクリックします。
最初のステップの「基本情報」で、「関数- 関数の環境の作成」タイルをクリックします。環境の名前としてgreeter-function-in-app-go-on-oci-appと入力します。「次へ」を押して、環境の詳細を含む2番目のステップに進みます。リージョン、コンパートメント、アプリケーションおよびファンクションを確認します。これらの設定を変更する必要はありません。その場合は、アプリケーションgo-on-oci-appの関数greeterが選択されていることを確認します。
「環境の作成」をクリックして定義を保存します。
DevOpsプロジェクトの概要ページで、「パイプラインの作成」をクリックします。「パイプラインの作成」フォームが表示されます。名前(deploy-greeter-function-to-go-on-oci-app)およびオプションで説明を入力します。次に、「Create pipeline」をクリックします。デプロイメント・パイプラインは作成されますが、非常に空です。デプロイ先の環境ではなく、デプロイするアーティファクトも、実行するステップを定義する構成ファイルもありません。
表示されるパイプライン・エディタで、「ステージの追加」タイル(またはプラス・アイコン)をクリックします。次のページには、ステージ・タイプのリストが表示されます。「組込み関数の更新戦略を使用します」というラベルのタイルをクリックします。
「次へ」ボタンを押します。
ステージ名(update-function-greeterなど)を入力します。関数に対して以前に定義した環境(greeter-function-in-app-go-on-oci-app)を選択します。
「アーティファクト」という見出しの下で、「アーティファクトの選択」をクリックします。Dockerイメージ・タイプのDevOpsプロジェクト内のすべてのアーティファクトのリストが表示されます。ファンクション・コンテナ・イメージに対して以前に作成したエントリのみを選択します。
「Select Artifact(アーティファクトの選択)」ボタンは使用できなくなりました。このステージに関連付けることができるコンテナ・イメージは1つのみです。
[追加]をクリックします。パイプライン・ステージがパイプラインに作成されます。パイプラインの実行準備が整い、定義が完了しました。それとも?このパイプラインが使用するアーティファクトは明確に定義されていません。コンテナ・イメージのパスのバージョン・ラベルにはプレースホルダ${imageVersion}が含まれます。デプロイメントに適切なバージョンが使用されるようにするには、このプレースホルダを適切な値に置き換える必要があります。これは、パイプラインでimageVersionと呼ばれるパラメータを定義し、既存のバージョン・ラベルに設定することで配置されます。
パイプラインの「パラメータ」タブをクリックします。imageVersionという新しいパラメータを定義します。デフォルト値は任意ですが、グリーター関数コンテナイメージの既存のバージョンラベルにも対応している可能性があります。パラメータ定義を保存します。
パイプラインの実行準備は整っているように見えますが、ジョブの実行が許可されていることを確認する必要があります。何かを試す前に、次のセクションを読んでください。
前の記事では、コンパートメント内のすべてのデプロイメント・パイプラインに対して動的グループが定義されていました。新しいパイプラインは、自動的にそのグループのメンバーになります。また、すべてのアーティファクト(コンパートメントのコンテナ・イメージ・レジストリ・リポジトリ内の(ファンクション)コンテナ・イメージを含む)を読み取る権限を動的グループに付与するポリシーも定義しました。また、すでに作成された別のポリシーによって、動的グループにコンパートメント内のすべてのリソースを管理するための非常に広範な権限が付与されます。また、機能の作成および更新についても取り上げるため、このポリシーの範囲を広げることができます。
「実行パイプライン」を押して、デプロイメント・パイプラインを実行します。
デプロイメントが完了すると、成功を宣言する緑色のマーカーが表示されます。ただし、Fn CLIコマンドラインからファンクションを手動でデプロイすることで達成した状況が最終結果であるため、この成功を示す他の明らかな兆候はありません。
もう少し面白くするために、関数のコードを変更します。次に、ファンクションのコンテナ・イメージを(ローカルで)構築し、新しいファンクション・イメージをコンテナ・イメージ・レジストリにプッシュします。次に、デプロイメント・パイプラインを再度開始します。今回は、成功すると、APIゲートウェイでmy-API/greetingルートを起動することで、新しい状況が発生します。
変更機能実装
ローカル環境でfunc.goを小さく見やすく変更します。新しいバージョンのファンクションからのレスポンスが現在のバージョンと大きく異なることを確認します。変更を保存します。
次の項では、変更されたソースから新しいバージョンのファンクション・コンテナ・イメージを構築し、最終的にOCIファンクションで実行します。
新しいファンクション・コンテナ・イメージの作成(ローカル)
これらの次のコマンドでは、最初に、関数にタグを付けるために使用するバージョン・ラベルを3桁目に増やします(bmはbumpの略です)。次に、変更されたソースを使用してファンクション・コンテナ・イメージを構築します。3番目のコマンドは、ローカル・コンテナ・イメージをリストし、名前にグリーターを付けてイメージをフィルタします。コマンドを実行してください。
fn bm
fn build
docker images | grep greeter
新しく作成されたイメージは、OCIリージョン・キー、ネームスペース、リポジトリ接頭辞、関数名グリータなど、完全修飾名でバージョン・ラベルが付加されたものであることがわかります。
新しいバージョンのラベルでコンテナ・イメージをタグ付けし、レジストリにプッシュ
バージョン・ラベルを0.1.0に設定する次のコマンドを使用して、イメージの新しい識別子を定義します。
docker tag : :0.1.0
次に、次を使用して、新しいファンクション・コンテナ・イメージをOCIコンテナ・イメージ・レジストリ・リポジトリにプッシュします:
docker push :0.1.0
この時点では、この新しいバージョンのコンテナ・イメージに基づいてファンクションを再デプロイしていません。イメージを構築し、OCI上のレジストリにプッシュするだけです。OCI関数の起動に違いはありません。
デプロイメント・パイプラインの実行(新しいファンクション・イメージの場合)
デプロイメント・パイプラインをもう1回実行します。パラメータimageVersionの値を0.1.0に設定します。
パイプラインが正常に完了すると、ファンクションに適用したすべてのエキサイティングな変更を含む新しいバージョンのファンクションがライブになります。
新しくデプロイされたファンクションの起動
新しいファンクションのバージョンは、Fn CLIを使用してコマンドラインで起動することで確認できます。
fn invoke go-on-oci-app greeter
(Fn CLIのコンテキストはOracleプロバイダからまだgo-on-OCIであり、greeter関数を含むgo-on-OCIコンパートメント用に構成されているため、このコールはOCI関数に送信され、この時点でコンテナ・イメージの新しいバージョンに基づきます。)
または、ファンクションを起動するAPIゲートウェイのルートにcurlできます:
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting
これまで、Fn CLIを使用してローカル開発環境でファンクション・コンテナ・イメージを手動で構築してきました。ただし、ビルド・パイプラインによって実行可能ファイルとして生成されたGoアプリケーションの前の記事で行ったように、関数の構築を自動プロセスに変えます。OCI DevOpsビルド・パイプラインは、コード・リポジトリからソースを取得し、ローカル・コンテナ・イメージを生成するマネージド・ビルド・ステージを実行し、このイメージをアーティファクトとしてコンテナ・イメージ・レジストリ・リポジトリに公開するために作成されます。最後のステップとして、ビルド・パイプラインはデプロイメント・パイプラインをトリガーして、ファンクションの最新の定義を実行時のOCIファンクション環境に公開します。
すべての要素が配置されている場合、相互接続されたOCIコンポーネントの合計セットは、次の図で視覚化されているように見えます。
この図に示すアーティファクトとデプロイメント・パイプラインは、ファンクションのイメージのアプリケーション、ファンクションおよびコンテナ・イメージ・レジストリ・リポジトリと同様に、DevOpsプロジェクトですでに定義されています。前の記事で設定したコード・リポジトリを使用します。作成する必要があるのは、ビルド・パイプラインのビルド・グレーター関数と3つのステージのみです。
ビルド・パイプラインの作成
DevOps Project go-on-ociの概要ページで、「ビルド・パイプラインの作成」ボタンをクリックします。名前(build-greeter-functionなど)と説明を指定するページが表示されます。ビルド・パイプラインをDevOpsプロジェクトに追加するには、「作成」を押します。
リストのbuild-greeter-functionリンクをクリックして、詳細ページに移動します。
第1ステージ: マネージド・ビルド
ビルド・パイプラインの最初のステージは、マネージド・ビルド・ステージです。このステージでは、パイプラインがビルド・サーバーを保持し、コード・リポジトリからサーバーに指定されたソースをコピーして、そのサーバー上で多数のアクションを実行する手順を示します。この書き込みの時点では、ビルドサーバーに単一のイメージを使用できます。Oracle Linuxイメージ(8 GBメモリー、1 OCPU)で、ツールと言語の実行時間が事前にインストールされています。ファンクション・コンテナ・イメージの構築には、ビルド・サーバーにDockerとFn CLIの両方が装備されていることが関係します。
プラス・アイコンまたはステージの追加カードのいずれかをクリックします。2ステップのステージの追加ウィザードが表示されます。ウィザードのステップ1で、ステージのタイプに「マネージド・ビルド」カードが選択されていることを確認します。「次へ」を押します。
2番目のページが表示されます。ビルド・ステージの名前(build-go-source-to-function-container-image)を定義します。オプションで、摘要を追加します。
現在、別のビルド・イメージは選択できないため、使用可能なイメージに定着しています。これは目的に応じて問題ありません。
ビルド仕様ファイル・パスを/functions/greeter/go-function-build-spec.yamlに設定します。このファイルには、Greeter関数(または他のGo関数)でGoソースを構築し、最後にファンクション・コンテナ・イメージを構築する手順が含まれています。
「プライマリ・コード・リポジトリ」の下の「選択」ボタンをクリックします。ビルドがソースを取得するコード・リポジトリを指定できるようになりました。「ソース接続タイプ」として「OCIコード・リポジトリ」を選択します。次に、go-on-oci-repoリポジトリを選択します。メイン・ブランチでソースを操作するため、そのデフォルトを変更しないでください。ビルド・ソース名の値としてgo-on-oci-sourcesと入力します。管理対象ビルド・ステージでは、複数のリポジトリのソースを使用できます。ビルド仕様では、ビルド・ソース名として定義されているラベルを使用して、これらの各ソースのルートの場所を参照できます。「保存」をクリックします。
[追加]ボタンを押します。これで、管理対象ビルド・ステージの定義が完了します。ソースを取得し、アーティファクトに処理するために必要なのはこれだけです。この管理対象ビルド・ステージおよびビルド・サーバーで実行される詳細な手順は、go-function-build-spec.yamlファイルで定義されます。このファイルには、ビルド・サーバーで実行される実際の詳細なステップの指示が含まれています。
version: 0.1
component: build
timeoutInSeconds: 6000
runAs: root
shell: bash
env:
# these are local variables to the build config
variables:
SOURCE_DIRECTORY: "go-on-oci-sources/functions/greeter"
FUNCTION_NAME: "greeter"
# # the value of a vaultVariable is the secret-id (in OCI ID format) stored in the OCI Vault service
# you can then access the value of that secret in your build_spec.yaml commands
vaultVariables:
# exportedVariables are made available to use in sucessor stages in this Build Pipeline
# For this Build to run, the Build Pipeline needs to have a BUILDRUN_HASH parameter set
exportedVariables:
- BUILDRUN_HASH
steps:
- type: Command
name: "Export variables"
timeoutInSeconds: 40
command: |
export BUILDRUN_HASH=`echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7`
echo "BUILDRUN_HASH: " $BUILDRUN_HASH
echo "fully qual sources" ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
echo "container image version from build pipeline parameter" ${imageVersion}
go version
- type: Command
timeoutInSeconds: 600
name: "Install golangci-lint"
command: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.37.1
- type: Command
timeoutInSeconds: 600
name: "Verify golangci-lint version"
command: |
/root/go/bin/golangci-lint version
- type: Command
timeoutInSeconds: 600
name: "Run go mod tidy for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go mod tidy
- type: Command
timeoutInSeconds: 600
name: "Run go vet for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go vet .
- type: Command
timeoutInSeconds: 600
name: "Run gofmt for Go Application"
command: |
gofmt -w ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
- type: Command
timeoutInSeconds: 600
name: "Run Lint for Go Application"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
/root/go/bin/golangci-lint run .
- type: Command
timeoutInSeconds: 600
name: "Run Unit Tests for Go Application (with verbose output)"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
go test -v
- type: Command
timeoutInSeconds: 600
name: "Build Go Function into Function Container Image"
command: |
cd ${OCI_WORKSPACE_DIR}/${SOURCE_DIRECTORY}
pwd
fn build --verbose
image=$(docker images | grep $FUNCTION_NAME | awk -F ' ' '{print $3}') ; docker tag $image go-function-container-image
outputArtifacts:
- name: go-function-container-image
type: DOCKER_IMAGE
# this location tag doesn't effect the tag used to deliver the container image
# to the Container Registry
location: go-function-container-image:latest
ビルド仕様は次の3つの部分で構成されます。
ビルド・ステップは、次のように要約できます。
これらのステップは、VMにデプロイされたバイナリ実行可能ファイルに最終的に変換されたGoアプリケーションの前の記事で定義したマネージド・ビルドとほぼ同じです。ステップ9と10は異なります。これらは、Goアプリケーションをビルドの最終製品であるファンクション・コンテナ・イメージに変換します。
第2ステージ- アーティファクトの公開
ビルド・パイプラインの概要ページで、現在管理されているビルド・ステージの下部にあるプラス・アイコンをクリックします。ポップアップするコンテキストメニューで、「Add stage」をクリックします。ステージ・ウィザードが表示されます。
「アーティファクトの配信」をクリックします。「Next」をクリックします。
このステージの名前を入力します: publish-greeter-function-container-image。公開するアーティファクトをDevOpsプロジェクトで選択する必要があります。このアーティファクトは、コンテナ・イメージgo-on-oci/greeter:${imageVersion}です。「アーティファクトの選択」をクリックし、コンテナ・イメージを選択します。
「アーティファクトをビルド結果に関連付けます」領域では、管理対象ビルド・ステージの結果のうち、アーティファクトを公開するためのソースとして選択した各アーティファクトについて示す必要があります。ビルド仕様では、go-function-container-imageというラベルの出力を定義します。この出力は、関数構築プロセスによって構築サーバー上で生成されるコンテナイメージを示します。「ビルド構成/結果アーティファクト名」フィールドにラベルgo-function-container-imageを入力します。「追加」ボタンを押して、「アーティファクトの配信」ステージを作成します。
第3ステージ: デプロイメント・パイプラインのトリガー
ビルド・パイプラインの概要ページで、「アーティファクトの配信」ステージの下部にあるプラス・アイコンをクリックします。ポップアップするコンテキストメニューで、「Add stage」をクリックします。ステージ・ウィザードが表示されます。
「デプロイメントのトリガー」をクリックします。「Next」をクリックします。
ステージの名前を入力します: trigger-deployment-of-greeter-function-to-go-on-oci-app、およびオプションで説明。「デプロイメント・パイプラインの選択」ボタンをクリックします。パイプラインdeploy-greeter-function-to-go-on-oci-appを選択します。パラメータ(imageVersion)やデプロイメントで使用されるアーティファクトなど、パイプラインの詳細が表示されます。
「追加」をクリックしてステージ定義を完了し、ビルド・パイプラインに追加します。
これでビルド・パイプラインが完了します。ソースを取得し、デプロイ可能なアーティファクトに処理し、アーティファクトをイメージ・リポジトリに公開し、そこからデプロイメント・パイプラインを取得するようにトリガーします。
「手動実行の開始」をクリックします。パラメータimageVersionの値を定義します(例: 0.2.0)。ボタンをクリックしてビルド・パイプラインを起動します。
これで、ビルド・パイプラインの完了に数分かかり、新しく構築されたファンクション・イメージの後続のデプロイメントがトリガーされます。
すべてが完了し、成功がレポートされたら、APIゲートウェイでルートを起動して、応答が実際に予期された新しいものかどうかを確認するグリーター・ファンクションにつながります。
curl -X "GET" -H "Content-Type: application/json" -d '{"name":"Mickey Mouse"}' https:///my-api/greeting
{"message":"Extremely hot greetings from your automatically built and deployed function dear Mickey Mouse"}
ちょっとしたお祝いの時間です。Goアプリケーション・ソースをコード・リポジトリから取得し、サーバーレスOCI関数の稼働中の新しいバージョンであるlint、vetting、test、およびbuild後に提供する、自動化されたエンドツーエンドのプロセスを達成しました。ファンクションをさらに更新するには、変更をコミットしてビルド・パイプラインをトリガーするだけです。また、次のステップとして、コード・リポジトリで(特定の)コミットが発生したときにビルド・パイプラインを自動的にトリガーすることもできます。
この記事の2番目の部分では、一般的にGoアプリケーションのOCIサービスの使用、および特にGo関数の使用について説明します。Go SDK for OCIの使用が導入され、OCI Object Storage Serviceとの相互作用が実証されています。
Oracle Cloud Infrastructureは、Goで開発されたアプリケーションを構築、実行できるプラットフォームです。コンピュート・インスタンスまたはサーバーレス機能のいずれかで、管理対象Kubernetesクラスタにもコンテナ化されます(このシリーズの一部5で説明します)。OCIはランタイム・プラットフォームよりもGo開発チームにとってはるかに重要であることを認識しておくことをお薦めします。OCIは、アプリケーションから利用できる多くのサービスを提供しています。メッセージの公開および消費を処理するための、ファイル、リレーショナルまたはNoSQLデータを格納するためのサービス。データを転送および分析するためのサービス。監視などのアプリケーションに対する操作をサポートするサービス。
OCIサービスとの対話は、すべてのサービスのあらゆる側面で使用可能なREST APIを介して実行できます。HTTPを介したJSONペイロードを使用したRESTサービスのコールは、Goアプリケーションから十分に簡単です。1つの複雑な要素があります。これらのAPIコールは署名する必要があります。Oracle Cloud Infrastructureの署名では、「署名」認証スキーム(認可ヘッダー付き)が使用され、署名プロセスはまったく簡単ではありません。この署名プロセスの詳細は、OCIドキュメント –OCI REST APIリクエスト署名を参照してください。OCIサービスにコールアウトするGoアプリケーションの開発に幸いなことに、Go SDK for OCIを利用できます。このオープン・ソース開発キットは、OCI APIリクエストへの署名を容易にします。SDKを使用して、OCIサービスへのコールは、JSONリクエストおよびレスポンス本文を処理するのではなく、厳密に型指定された事前定義済構造体を使用してローカル・コールとして行われます。OCI用のGo SDKでは、以前にFn CLIおよびOCI CLIに使用したものと同じ構成ファイルが使用されます。このファイルのデフォルトの場所は$HOME/.ociです。このファイルは、ユーザーに設定されたペアの秘密キーの半分を使用して、ユーザー・アカウントの特定のテナンシおよびリージョンにGo SDKを指示します。Go SDK for OCIを使用するGoアプリケーションは、詳細を処理しなくても、この構成を単純に構築できます。
Go SDK for OCIのドキュメントは、OCIドキュメント – SDK for Goにあります。
この項では、ファイルの作成および読取りにOCI Object Storage Serviceを使用する単純なGoアプリケーションを開発します。最初は、これはコンパイルしてどこでも実行できるスタンドアロン・アプリケーションです(OCI構成ファイルが使用可能であるかぎり)。次に、OCI内(VM上またはファンクション)でのGoアプリケーションの実行について説明します。Go SDKをOCI内で使用すると、アプリケーションを実行しているコンポーネントのIDと権限を利用できるため、この特別な焦点が当てられます。つまり、OCIで実行されるコードは、独自のOCI構成ファイルを持ち込む必要がないため、さらにシンプルになります。
まず、SDKを介してOCIに接続できる非常にシンプルなGoアプリケーションを作成します。次に、この基盤を使用して、Object Storage Serviceとの対話を追加します。
OCIの最もシンプルなGoクライアント
Go SDK for OCIを使用してOCIと通信する最も単純なGoアプリケーションは、次のように作成されます。
この記事で前述したOCI構成ファイルは、デフォルト名$HOME/.oci/configのデフォルトの場所にあります。ファイルが別の場所にある場合は、構成ファイルのカスタムの場所を受け入れるgithub.com/oracle/oci-go-sdk/v65/commonパッケージのConfigurationProviderFromFileファンクションを使用できます。
go.modファイルには、次の内容が含まれます。
module oci-client
go 1.16
require github.com/oracle/oci-go-sdk/v65 v65.2.0
github.com/oracle/oci-go-sdk/v65ビットは、Go SDKの最新(書込み時)バージョンを参照します。ソースはこの参照に基づいてダウンロードされます。Goアプリケーションでは、SDKのパッケージを活用してOCIポートフォリオの様々なサービスにアクセスできることを意味します。
ファイルoci-client.goには、共通およびアイデンティティ・パッケージを使用する次のコードが含まれています。
package main
import (
"context"
"fmt"
"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/identity"
)
func main() {
c, _ := identity.NewIdentityClientWithConfigurationProvider(common.DefaultConfigProvider())
tenancyID, _ := common.DefaultConfigProvider().TenancyOCID()
request := identity.GetCompartmentRequest{
CompartmentId: &tenancyID,
}
response, _ := c.GetCompartment(context.Background(), request)
fmt.Printf("Name of Root Compartment: %v", *response.Compartment.Name)
}
ファンクションの最初の行は、その後、ルート・コンパートメントの詳細を返すGetCompartmentコールなど、OCIとのほとんどの相互作用に使用できるクライアントを取得します。
アプリケーションは、go run oci-client.goを使用して実行でき、非常に単純な出力が生成されます。
Name of Root Compartment:
結果は特に目立たないものの、出力があるという事実は、Go SDK for OCIが機能しており、より高価な活動に活用する準備ができているという証拠です。
コードは、発生する可能性のあるエラーを完全に無視します。エラーが発生した場合は、アンダースコアを変数に置き換えて、そのエラーを取得して処理します。
GoアプリケーションOCIオブジェクト・ストレージ・サービス内およびOCIオブジェクト・ストレージ・サービスからのオブジェクトの作成および取得
OCI Object Storage Serviceは、さまざまなタイプのデータに対して安価で永続的なストレージを提供します。大きいオブジェクトと小さいオブジェクトは、プログラムによってこのサービスに格納され、短期間または長期間保持され、プログラムによって、または直接URLを介してアクセスできます。Object Storageは、バージョニング、異なる保存ルール、格納データの暗号化、オブジェクト・ライフサイクル管理、時間ベースの自動削除および様々なストレージ層を提供します。
Object Storage Service上のオブジェクトはバケットに編成されています。バケットは、特定のコンパートメントに存在するオブジェクトの論理コンテナです。バケット内のすべてのオブジェクトに対して一部の操作を実行できます。
Go SDK for OCIには、Object Storage Service from Goアプリケーションを簡単かつ簡単に操作できる機能とタイプが用意されています。バケットを操作し、オブジェクトを作成、削除および取得する操作はすぐに使用できます。
Go SDK for OCIのObject Storageパッケージの詳細は、Go SDK for OCIのObject Storageパッケージのドキュメントを参照してください。
この記事のソース・リポジトリには、フォルダapplication/store-n-retrieveが含まれています。このフォルダには、OCIテナンシに接続してバケットを作成し、その後にオブジェクトを作成し、その同じオブジェクトを取得する単純なGoアプリケーションがあります。アプリケーションは、前の項で$HOME/.oci/configファイルを使用してOCI APIにリクエストに署名するために使用したものと同じDefaultConfigProviderを使用します。
OCI用のGo SDKに対するこのアプリケーションの依存関係は、go.modで定義されます。
コードの最初の部分では、ObjectStorageClientインスタンスが作成されます。基礎となるインタフェースには多くの関数が定義されており、すべてObject Storage Serviceとのなんらかの形式の対話をサポートしています。ObjectStorageClientは、秘密キーを含むファイルへの参照を含むデフォルトのOCI構成ファイルを使用して、common.DefaultConfigProvider()を使用して(前述のように)作成されます。
関数getNamespaceは、現在のテナンシのネームスペースを取得するためにコールされます。次に、バケットの名前でensureBucketExistsがコールされ、バケットが存在しない場合はバケットが作成されます。
package main
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"github.com/oracle/oci-go-sdk/v65/common"
"github.com/oracle/oci-go-sdk/v65/objectstorage"
)
const (
bucketName = "go-bucket" // feel free to use a different name for the bucket
compartmentOCID = " " // replace with the OCID of the go-on-oci compartment in your tenancy
objectName = "welcome.txt" // feel free to use a different name for the object
)
func main() {
objectStorageClient, cerr := objectstorage.NewObjectStorageClientWithConfigurationProvider(common.DefaultConfigProvider())
if cerr != nil {
fmt.Printf("failed to create ObjectStorageClient : %s", cerr)
}
ctx := context.Background()
namespace, cerr := getNamespace(ctx, objectStorageClient)
if cerr != nil {
fmt.Printf("failed to get namespace : %s", cerr)
} else {
fmt.Printf("Namespace : %s", namespace)
}
err := ensureBucketExists(ctx, objectStorageClient, namespace, bucketName, compartmentOCID)
if err != nil {
fmt.Printf("failed to read or create bucket : %s", err)
}
.........................
このスニペットで呼び出されたファンクションは、ネームスペースを取得し、バケットの存在を確認(存在しない場合は作成)するために非常に簡単です。これらは、次のように定義されます。
func getNamespace(ctx context.Context, client objectstorage.ObjectStorageClient) (string, error) {
request := objectstorage.GetNamespaceRequest{}
response, err := client.GetNamespace(ctx, request)
if err != nil {
return *response.Value, fmt.Errorf("failed to retrieve tenancy namespace : %w", err)
}
return *response.Value, nil
}
func ensureBucketExists(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, name string, compartmentOCID string) error {
req := objectstorage.GetBucketRequest{
NamespaceName: &namespace,
BucketName: &name,
}
// verify if bucket exists.
response, err := client.GetBucket(ctx, req)
if err != nil {
if response.RawResponse.StatusCode == 404 {
err = createBucket(ctx, client, namespace, name, compartmentOCID)
return err
}
return err
}
fmt.Printf("bucket %s already exists", bucketName)
return nil
}
// bucketname needs to be unique within compartment. there is no concept of "child" buckets. using "/" separator characters in the name, the suggestion of nested bucket can be created
func createBucket(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, name string, compartmentOCID string) error {
request := objectstorage.CreateBucketRequest{
NamespaceName: &namespace,
}
request.CompartmentId = &compartmentOCID
request.Name = &name
request.Metadata = make(map[string]string)
request.PublicAccessType = objectstorage.CreateBucketDetailsPublicAccessTypeNopublicaccess
_, err := client.CreateBucket(ctx, request)
if err != nil {
return fmt.Errorf("failed to create bucket on OCI : %w", err)
} else {
fmt.Printf("created bucket : %s", bucketName)
}
return nil
}
このアプリケーションの2番目の部分では、バケットにオブジェクトが作成され、その後そのオブジェクトが取得されます。アプリケーションの実行が完了すると、バケットが作成され(まだ存在していない場合)、オブジェクトが作成または更新された(アプリケーションが以前に実行されている場合)という永続的な効果があります。OCIコンソールのバケットgo-bucketの詳細ページで、作成されたバケットとオブジェクトの両方を表示できます。
..................
contentToWrite := []byte("We would like to welcome you in our humble dwellings. /n We consider it a great honor. Bla, bla.")
objectLength := int64(len(contentToWrite))
err = putObject(ctx, objectStorageClient, namespace, bucketName, objectName, objectLength, ioutil.NopCloser(bytes.NewReader(contentToWrite)))
if err != nil {
fmt.Printf("failed to write object to OCI Object storage : %s", err)
}
var contentRead []byte
contentRead, err = getObject(ctx, objectStorageClient, namespace, bucketName, objectName)
if err != nil {
fmt.Printf("failed to get object %s from OCI Object storage : %s", objectName, err)
}
fmt.Printf("Object read from OCI Object Storage contains this content: %s", contentRead)
}
func putObject(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, bucketName string, objectname string, contentLen int64, content io.ReadCloser) error {
request := objectstorage.PutObjectRequest{
NamespaceName: &namespace,
BucketName: &bucketName,
ObjectName: &objectname,
ContentLength: &contentLen,
PutObjectBody: content,
}
_, err := client.PutObject(ctx, request)
fmt.Printf("Put object %s in bucket %s", objectname, bucketName)
if err != nil {
return fmt.Errorf("failed to put object on OCI : %w", err)
}
return nil
}
func getObject(ctx context.Context, client objectstorage.ObjectStorageClient, namespace string, bucketName string, objectname string) (content []byte, err error) {
request := objectstorage.GetObjectRequest{
NamespaceName: &namespace,
BucketName: &bucketName,
ObjectName: &objectname,
}
response, err := client.GetObject(ctx, request)
if err != nil {
return nil, fmt.Errorf("failed to retrieve object : %w", err)
}
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(response.Content)
if err != nil {
return nil, fmt.Errorf("failed to read content from object on OCI : %w", err)
}
return buf.Bytes(), nil
}
このアプリケーションの実行 –go run object-organizer.go– 次の出力になります。
Namespace : idtwlqf2hanz
created bucket : go-bucket
Put object welcome.txt in bucket go-bucket
Object read from OCI Object Storage contains this content: We would like to welcome you in our humble dwellings. /n We consider it a great honor. Bla, bla.
前の項では、SDKを介してOCIサービスと対話するGoアプリケーションについて説明しました。では、OCI Functions in Goについて、他に何を言うべきでしょうか。開発したGoコードおよびGo SDKを使用するGoコードがOCI関数としてデプロイされるか、OCIのコンピュート・インスタンスで実行される場合は、ライフをよりシンプルにできることがわかりましたので、お読みください。このような場合、リソース・プリンシパル認証(ファンクションの場合)およびInstance Principal認証(コンピュート・インスタンスで実行されるコードの場合)を利用できます。つまり、OCI構成ファイルを含める必要がなく、SDKと通信するコードが少し単純になる可能性があります。
リソース・プリンシパル認証の詳細は、ファンクションのリソース・プリンシパル認証に関するOCIドキュメントを参照してください。
この項では、オブジェクト・ブローカと呼ばれるOCI関数について説明します。この記事のソース・リポジトリ内のソースは、パス関数/オブジェクト・ブローカにあります。前述のとおり、この関数はメタデータを含むfunc.yamlファイルと、関数とFnフレームワーク間のリンクを含むfunc.goファイルを使用して定義されます。ファイルgo.modは、ファンクションの依存関係を定義します。OCI Object Storage Serviceと対話するためのロジックは、ファイルobject-organizer.goにあります。これは、func.goからコールされるパブリックfunc CreateObjectを定義します。
CreateObjectは、指定された名前のオブジェクトを指定されたバケットに作成します。オブジェクトorganizer.goのファンクションCreateObjectの最初の行は、このリソース・プリンシパル認証を操作するために変更を加えました。現在使用されているauth.ResourcePrincipalConfigurationProvider()では、OCI構成ファイルおよび秘密キーをアプリケーションに含める必要はありません。コードは、OCI内で、さらに具体的にはリソース・プリンシパルと呼ばれるリソース(ファンクションまたはDevOpsビルド・サーバーなど)内で実行され、動的グループに含まれ、そのグループ・メンバーシップから権限を継承することを前提としています。あまりにも長い間、あなたはこれに必要な措置を取るための指示に到達します。
func CreateObject(objectName string, bucketName string, compartmentOCID string) (string, err) {
configurationProvider, err := auth.ResourcePrincipalConfigurationProvider()
if err != nil {
fmt.Printf("failed to get oci configurationprovider based on resource principal authentication : %s", err)
}
objectStorageClient, cerr := objectstorage.NewObjectStorageClientWithConfigurationProvider(configurationProvider)
func.goの横に注目しましょう。Func(tion) myHandlerはファンクション・トリガーを処理します。CreateObjectを呼び出しますが、リクエストで生成されるオブジェクトの名前と、オブジェクトを含むバケットのバケット名を決定するまでは呼び出されません。これらの名前にはデフォルト値がありますが、この関数は特定の値を提供するHTTPリクエスト問合せパラメータ値の検索を試みます。これは、ファンクションに対するHTTPトリガーでのみ機能し、fn invokeを使用して行われたコールでは機能しません。
func myHandler(ctx context.Context, in io.Reader, out io.Writer) {
objectName := "defaultObjectName.txt"
bucketName := "the-bucket"
fnctx := fdk.GetContext(ctx) // fnctx contains relevant elements about the Function itself
fnhttpctx, ok := fnctx.(fdk.HTTPContext) // fnhttpctx contains relevant elements about the HTTP Request that triggered it
if ok { // an HTTPContent was found which means that this was an HTTP request (not an fn invoke) that triggered the function
u, err := url.Parse(fnhttpctx.RequestURL())
......
特にこの例では、「Project Fn Go FDKのドキュメント」でFnコンテキストの詳細を調べることができます。
ファンクションは、バケットが配置されるOCIコンパートメントを認識する必要があります。このような実行時設定は、通常、構成を介してアプリケーションの機能のいずれかで定義されます。構成の値は、次の行に示すように、コンテキスト内のマップから関数で読み取ることができます。
if compartmentOCID, ok := fnctx.Config()["compartmentOCID"]; ok {
Fn CLIを使用したファンクションの構築およびデプロイ
Fn CLIがインストールされているローカル開発環境で、現在のディレクトリがfunctions/object-brokerの場合、詳細出力を使用してファンクションのローカル・ビルドを実行できます。
fn -v build
ビルドが正常に見える場合、次のステップはファンクションのデプロイです。Fnコンテキストがgo-on-OCIコンテキストを使用するように設定されている場合(fnリスト・コンテキストで確認)、これにより、OCI上のgo-on-OCI-appアプリケーションにファンクションがデプロイされます:
fn -v deploy --app go-on-oci-app
このファンクションは、コンパートメントOCID値に対して構成が定義されている場合にのみ、意味のあるジョブを実行できます。これを行うには、コンソールを使用するか、次の文をFn CLIとともに使用します。
fn cf f go-on-oci-app object-broker compartmentOCID
これで、ファンクションがデプロイされ、その構成になります。このコマンドを使用した関数の呼び出しが成功すると予想されます。
fn invoke go-on-oci-app object-broker
ただし、処理する最終的な側面は1つあります。このファンクションは、OCI Object Storage Service APIを使用してバケットおよびオブジェクトを操作しますが、これを行うには権限を明示的に付与されている必要があります。これを実現するには、動的グループと2つのポリシーを使用します。
オブジェクトおよびバケットを操作するためのファンクションの権限
動的グループを使用してデプロイメント・パイプラインおよびビルド・パイプラインを表す権限受領者を作成したのと同様に、権限を付与する関数を含む動的グループも作成する必要があります。動的グループを作成するには、検索バーにdynと入力します。検索結果ペインで「Dynamic Groups」リンクをクリックします。
動的グループの概要ページで、「動的グループの作成」をクリックします。
デプロイメント・パイプラインの動的グループの名前(functions-in-go-on-ociなど)を入力し、オプションで説明を入力します。コンパートメントの一部であるすべてのファンクションを選択する次のルールを定義します:
All {resource.type = 'fnfunc', resource.compartment.id = ''}
もちろん、
コンソールでポリシーを作成するには: 検索バーにpoliと入力し、検索結果ポップアップの「サービス」領域で「ポリシー」→「アイデンティティ」をクリックします。これにより、現在のコンパートメントの「ポリシー」概要ページが表示されます。
最初のポリシー・ステートメントは、コンパートメント内のオブジェクトを管理するためのファンクションの権限を定義します。2番目の文は、バケットを管理するための権限を追加します。名前、説明および次の文を定義します。
allow dynamic-group functions-in-go-on-oci to manage objects in compartment go-on-oci
allow dynamic-group functions-in-go-on-oci to manage buckets in compartment go-on-oci
次の図は、これらのステートメントを含むポリシーが保存されたときに関数に適用されるアクセス権を示しています。
これで、ファンクションを起動でき、バケットおよびオブジェクトのデフォルト名を使用してその操作を実行できるようになります。
fn invoke go-on-oci-app object-broker
OCI URLからバケット・ページへのコンソールを使用して、バケットが作成され、新しく作成されたオブジェクトが含まれていることを確認します。
トリガー関数へのAPIゲートウェイでのルートの追加
HTTPを介してオブジェクト・ブローカ・ファンクションをどこからでも起動できるように、APIゲートウェイを再度使用します。コンソールの検索バーにgatと入力します。「ゲートウェイ」→「API管理」をクリックします。the-api-gatewayのリンクをクリックします。「Deployments」をクリックします。デプロイメントを含むリスト(単一のデプロイメントを含む)で、リンクmyserver-apiをクリックします。
「編集」をクリックしてデプロイメント仕様を開きます。2番目のステップの「ルート」のリンクをクリックします。下にスクロールして「+ Another Route」をクリックします。
この新しいルートのパスとして/object-brokerと入力します。メソッドとしてGETを選択し、バックエンドの「タイプ」としてOracle Functionsを選択します。アプリケーションgo-on-oci-appを選択し、「関数名」をobject-brokerに設定します。[次へ]を押してから[変更を保存]を押して変更を適用し、新しいルートをリアルにします。
HTTPコンシューマからAPI GatewayからFunctionまで、最後にバケットとオブジェクトで構成されるエンドツーエンドの画像は次のようになります。
次のコマンドを使用して、ブラウザから関数を起動するか、コマンドラインでcurlを使用します。
curl -X "GET" "http:///my-api/object-broker?objectName=new-exciting-object.txt&bucketName=the-ultimate-collection"
自動ビルドおよびデプロイメント
この関数オブジェクト・ブローカは、Fn CLIを使用してコマンドラインに手動でデプロイされました。それは当然うまくいった。ただし、この関数で開発を開始し、複数の開発サイクルを経る場合は、ビルド・アンド・デプロイ・プロセスで自動化を導入することをお薦めします。
これまでと同様に、OCI DevOpsで必要な要素を簡単に設定して、デプロイメント(コンテナ・イメージ・レジストリ内のファンクション・コンテナ・イメージ)およびビルド(コード・リポジトリから開始して、ファンクションのコンテナ・イメージを新たにベイク処理)の自動パイプラインを実現できます。ファンクションに固有のメイン要素は、ビルド・パイプラインの管理対象ビルド・ステージのビルド仕様ファイルです。このファイルは、func.goおよびfunc.yamlと同じディレクトリにgo-function-build-spec.yamlとして提供されます。
ファンクション・コンテナ・イメージのDevOpsアーティファクト、ファンクションの環境、およびビルドおよびデプロイメント用の2つのパイプラインを作成した後、自動化されたDevOpsプロセスの設定は次のようになります。
この記事では、Oracle Cloud InfrastructureでGoで記述され、実行されているサーバーレス機能に重点を置いています。APIゲートウェイを使用して外部HTTPコンシューマにファンクションへのアクセスを提供する場合と同様に、これらのファンクションの自動ビルドおよびデプロイメントについて説明しました。
2つ目の主なトピックは、GoアプリケーションのOCIサービスと対話するためのGo SDK for OCIでした。この記事では、Goコードを使用してObject Storageサービスにアクセスし、ファイルを格納および取得する方法を示します。
この2つのトピックは、関数オブジェクト・ブローカで結合されました。このOCI関数は、動的グループを介して付与されたリソース・プリンシパル認証および権限を利用します。実行時構成を介して、この関数は現在の環境固有の設定について学習します。
次の記事では、Oracle Databaseとの対話がメイン・トピックになります。GoアプリケーションからローカルOracle Databaseへの接続、およびOCIで実行されているAutonomous Databaseへの接続を作成し、Goアプリケーションの快適さからこれらのデータベースでSQL操作を実行します。さらに、デプロイメント・プロセスのウォレットを含むデータベース資格証明を適切に管理するためのOracle Walletの操作や、単一のアプリケーションでのOCI Object StorageおよびAutonomous Databaseサービスとの対話の結合などについても説明します。