
目次
はじめに
こんにちは、クラウド事業部の高橋です。
本投稿は、OCI Functions + API Gatewayを利用し、シンプルなブログ投稿APIを作成したハンズオン記事です。
私自身、OCI Functionsの実運用経験は多くなく、APIについても学習を進めている段階のため、そのどちらも利用できるハンズオンとして構成してみました。
これからOCI Functionsを利用してみたいと思った方のきっかけとなれば幸いです。
構成図
このハンズオン記事では、以下の構成図をもとにリソースを作成していきます。
通常、ブログシステムには記事の投稿・保存・一覧取得・削除などたくさんの機能が必要ですが、今回のハンズオンは記事の保存(POST)に絞って構築していきます。

- 記事の保存(POST) : 送られてきたタイトルや本文を、JSON形式ファイルとしてクラウド上のObject Storageに保存します。
本来であれば、ブログを作成するにはWordPressのような大きなシステムと、MySQLのようなデータベースサーバーが必要です。しかし、今回の構成はサーバーレス(OCI Functions)を利用しているため、疎結合であり、リクエストが来た際にだけ起動するため、待機コストがゼロになる(リクエストがない間は課金されない)というメリットがあります。
OCI Functions とは
Oracle Cloud Infrastructure Functions は、完全に管理されている、マルチテナントでスケーラビリティが高いオンデマンドのFunctions-as-a-Serviceプラットフォームである。エンタープライズグレードのOracle Cloud Infrastructure上に構築されており、Fn Projectオープン・ソース・エンジンによって強化されています。ビジネス・ニーズを満たすためのコードの記述に焦点を当てる場合は、OCI Functions (Functionsのみに省略される場合もあります。以前はOracle Functionsと呼ばれていました)を使用します。 docs.oracle.com
OCI API Gateway とは
APIゲートウェイ・サービスでは、ネットワーク内からアクセス可能なプライベート・エンドポイントとともに、インターネット・トラフィックを受け入れる場合にパブリックIPアドレスで公開できるAPIを公開できます。エンドポイントは、API検証、リクエストとレスポンスの変換、CORS、認証と認可およびリクエスト制限をサポートします。 APIゲートウェイ・サービスを使用して、APIクライアントからのトラフィックを処理し、バックエンド・サービスにルーティングするために、リージョナル・サブネットに1つ以上のAPIゲートウェイを作成します。単一のAPIゲートウェイを使用して、複数のバックエンド・サービス(ロード・バランサ、コンピュート・インスタンス、OCI関数など)を単一の統合APIエンドポイントにリンクできます。 docs.oracle.com
料金
OCI Functions
OCI Functionsは以下の価格で提供されます。
呼び出し - 1ヶ月あたり200万件以下の場合 : 無料
呼び出し - 1ヶ月あたり200万件を超過する場合 : $0.0000002
実行時間 - 1ヶ月あたり40万時間以下の場合 : 無料
実行時間 - 1ヶ月あたり40万時間を超過する場合 : $0.00001417
www.oracle.com
API Gateway
API Gatewayは以下の価格で提供されます。従量課金制のため、1ヶ月あたり100万回のAPI呼び出しが$3.00となり、それ以上は加算されます。
API Gateway - 1ヶ月あたり100万回のAPI呼び出し : $3.00 www.oracle.com
実際にやってみた
- 前提条件
・OCIアカウントの作成が完了していること。
・テナンシ / コンパートメントの作成が完了していること。
・作業するユーザーが権限を保持していること。具体的にはFunctionsとObject Storageを管理できる権限です。
・ローカル環境にHomeBrew / Docker / FnCLI / OCI CLI / Python3.xがインストールされていること。
1. Object Storage バケット作成
DBの代替となる保存先バケットを作成します。
左側ハンバーガーボタンより、[ストレージ] -> [オブジェクト・ストレージとアーカイブ・ストレージ] -> [バケット] -> [バケットの作成]で新規バケットを作成しましょう。
名前以外は特に変更せず、デフォルトで問題ありません。

2. VCN / サブネット作成
今回の検証に用いるVCN / サブネットを作成します。
左側ハンバーガーボタンより、[ネットワーキング] -> [仮想クラウド・ネットワーク] -> [アクション] -> [VCNウィザードの起動]でインターネット接続性を持つVCNの作成をクリックします。
VCN名、コンパートメントを選択し、IPv4 CIDRはデフォルトのままで問題ありません(あるいは任意の値を入力してください)。
今回の構成はあくまでも外からのAPI Gateway経由での呼び出し(Invoke)を想定しているため、パブリックサブネット・プライベートサブネットを構成します。
次に、パブリックサブネットに自グローバルIPのHTTPS:443の穴あけをしておきましょう。
[ネットワーキング] -> [仮想クラウド・ネットワーク] -> [<作成したVCN>] -> [サブネット] -> [<自動作成されたパブリックサブネット>] -> [セキュリティ] -> [Default Security List for
プロトコルはTCPで、ソース・ポート範囲は空白のまま、宛先ポート範囲に443を入れます。

3. 動的グループ・ポリシー設定(Functions to Object Storage)
FunctionsからObject Storageにファイルを書き込むために、Functionsに権限を付与する必要があります。
左側ハンバーガーボタンより、[アイデンティティとセキュリティ] -> [ドメイン]をクリックします。
現在使用しているドメイン(通常はDefault)を選択します。もし作業コンパートメントにDefaultドメインがない場合は、ルートコンパートメントに存在する可能性があります。
[動的グループタブ] -> [動的グループの作成]をクリックします。
名前と説明は任意で構いません。一致ルールには以下を記載しましょう。
作業コンパートメントのOCIDを調べるには、左側ハンバーガーボタンより、[アイデンティティとセキュリティ] -> [コンパートメント]をクリックし、詳細タブのOCIDをコピーしましょう。

ALL {resource.type = 'fnfunc', resource.compartment.id = '<作業コンパートメントのOCID>'}
再びメニューから、[アイデンティティとセキュリティ] -> [ポリシー] -> [ポリシーの作成]をクリックします。
名前は任意の値で構いません。コンパートメントは作業コンパートメントを設定します。ポリシービルダーで以下をペーストしましょう。

Allow dynamic-group <動的グループ名> to manage objects in compartment <コンパートメント名> where target.bucket.name = '<バケット名>'
4. アプリケーション作成
Functionsを入れるためのアプリケーションと呼ばれるものをコンソールで作成します。
左側ハンバーガーボタンより、[開発者サービス] -> [アプリケーション] -> [アプリケーションの作成]をクリックします。
名前は任意の値で構いません。VCNとサブネットは、先ほど作成したコンパートメントにあるものを選択し、VCNは作成したものを使いましょう。
サブネットはVCNウィザードで作成していればパブリック・プライベートの2つがあります。ここでは商用環境をイメージしてプライベートサブネットを選択しました。

5. ローカル環境でのPython開発準備
前提条件にも記載しましたが、ローカル環境にDocker / Fn CLI / OCI CLIのインストールが必要となります。簡単な手順を記載します。
なお、私の環境はMacOS Tahoe 26.2で実施しています。
Docker
公式サイトからインストールし、起動します。
www.docker.com
Fn CLI
curlコマンド(curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh)でインストールします。
OCI CLI
brew install oci-cliでインストールします。
次に、OCIコンソールでAPIキーの発行をしましょう。
OCIコンソールの画面右上の人型アイコンをクリックし、自分のメールアドレスをクリックします。
[トークンおよびキー] -> [APIキー] -> [APIキーの追加] -> [APIキー・ペアの生成] -> [秘密キーのダウンロード]で作成できます。
構成ファイルのプレビューで表示される内容をコピーし、メモ帳に貼り付けておきましょう。
次に以下コマンドを順番に実行し、設定を済ませておきます。
# 1. 設定用フォルダの作成 mkdir -p ~/.oci # 2. キー・ペアの移動とリネーム mv ~/Downloads/<ダウンロードしたファイル名>.pem ~/.oci/oci_api_key.pem # 3. キー・ペアの権限変更 chmod 600 ~/.oci/oci_api_key.pem # 4. Configファイルの作成 vi ~/.oci/config # 5. 先ほどコピーした構成テキストをペーストします。 # 6. 構成テキストのkey_fileを修正して、Configファイルに上書き保存します。 key_file=/Users/<ユーザー名>/.oci/oci_api_key.pem
認証トークンの作成
Dockerログインをするための認証トークンを作成します。
OCIコンソールの画面右上の人型アイコンをクリックし、表示されたメニューから自身のメールアドレスを選択します。
[トークンおよびキー] -> [認証トークン] -> [トークンの生成] で説明を記載し、トークンを生成します。一度限りの表示のため、メモ帳に貼り付けておきましょう。
このトークンは後ほど、Dockerログインする際に利用します。
Fn Context の設定
ローカルの Fn CLI と OCI 環境を紐付けます。以下のコマンドを順に実行してください。
ネームスペース名の確認は、OCIコンソールの画面右上の人型アイコンをクリック -> [テナンシ名] -> [オブジェクト・ストレージ・ネームスペース]で確認できます。
# 1. コンテキストの作成 (レジストリURLなどは自分の環境に合わせて変更) fn create context <コンテキスト名(任意の値でOK)> --provider oracle # 2. コンテキストの選択 fn use context <コンテキスト名(上記で決めたもの)> # 3. コンパートメントIDの設定 fn update context oracle.compartment-id <作業コンパートメントのOCID> # 4. APIエンドポイントの設定 (東京リージョンの場合) fn update context api-url https://functions.ap-tokyo-1.oraclecloud.com # 5. コンテナレジストリの登録(東京リージョンの場合) fn update context registry nrt.ocir.io/<ネームスペース名>/<リポジトリ名(任意の値でOK)>
OCIR(Docker)へのログイン
Docker がイメージをプッシュできるようにログインします。
パスワードには OCI ユーザー設定で発行した認証トークンを使用します。
docker login nrt.ocir.io # Username: <ネームスペース名>/<ユーザー名(メールアドレス)> # Password: <認証トークン>
6. 関数の作成準備
開発環境が整ったので、いよいよ関数の作成準備を実施していきます。
以下の作業を上から順に流れで実施していけば準備は完了です。
# 1. 任意のプロジェクトディレクトリを作成し、移動 mkdir <project directory> cd <project directory> # 2. Fn CLIをPythonランタイムで初期化 fn init --runtime python post-func # 3. 作成されたpost-funcディレクトリに移動 cd post-func # 4. ライブラリの定義をするためにrequirements.txtに以下上書き fdk>=0.1.71 oci>=2.116.0 # 5. 設定ファイルであるfunc.yamlに以下上書き(memoryを256MBに変更) schema_version: 20180708 name: post-func version: 0.0.1 runtime: python memory: 256 entrypoint: /python/bin/fdk /function/func.py handler
7. 関数func.pyの作成
post-funcディレクトリにあるfunc.pyに以下の関数を上書き保存します。
途中、ご自身の環境に対応するNAMESPACE_NAMEと、BUCKET_NAMEの書き換えを忘れずに実施してください。
import io
import json
import logging
import uuid
import datetime
import oci
from fdk import response
NAMESPACE_NAME = "<OCIテナンシのオブジェクト・ストレージ・ネームスペース>"
BUCKET_NAME = "<作成したバケット名>"
def handler(ctx, data: io.BytesIO = None):
logging.getLogger().info("Function invoked")
try:
#1. 入力データの取得
input_data = {}
try:
body = data.getvalue()
if body:
input_data = json.loads(body)
except Exception:
pass
#2. 認証プロバイダーの初期化
#Resource Principalを使って、Function自身を認証します
signer = oci.auth.signers.get_resource_principals_signer()
#3. Object Storage クライアントの作成
object_storage = oci.object_storage.ObjectStorageClient(config={}, signer=signer)
#4. 保存データの作成
post_id = str(uuid.uuid4())
blog_post = {
"id": post_id,
"title": input_data.get("title", "No Title"),
"content": input_data.get("content", "No Content"),
"createdAt": datetime.datetime.now().isoformat()
}
content_str = json.dumps(blog_post)
object_name = f"posts/{post_id}.json"
logging.getLogger().info(f"Uploading to {BUCKET_NAME}/{object_name}")
#5. Object Storageへアップロード
object_storage.put_object(
namespace_name=NAMESPACE_NAME,
bucket_name=BUCKET_NAME,
object_name=object_name,
put_object_body=content_str.encode('utf-8')
)
return response.Response(
ctx,
response_data=json.dumps({"message": "Success", "postId": post_id}),
headers={"Content-Type": "application/json"}
)
except (Exception, ValueError) as ex:
logging.getLogger().error(f"Error: {str(ex)}")
return response.Response(
ctx,
response_data=json.dumps({"error": str(ex)}),
headers={"Content-Type": "application/json"},
status_code=500
)
8. 関数のデプロイ
ここまで来たらOCI Functionsの作業はもう終わりです!
デプロイし、実際にObject StorageにJSONが書き込まれているか、テストしてみましょう。
# 1. Fn CLIデプロイコマンド
fn -v deploy --app <app name> --no-cache
# 2. テスト実行コマンド
echo -n '{"title":"Python Test", "content":"Hello OCI Functions"}' | fn invoke <アプリケーション名> post-func
# 3. 成功時のレスポンス
{"message": "Success", "postId": "787acfd2-3ada-4d01-9785-3c5917fe48fc"}
無事に成功した場合、Object StorageにpostIdでアップロードされたことを確認できます。

9. API Gatewayの作成
次に、外部から呼び出すためのAPI Gatewayを作成しましょう。
左側ハンバーガーボタンより、[開発者サービス] -> [ゲートウェイ] -> [ゲートウェイの作成]をクリックします。
名前は任意の値で、タイプはパブリックにしましょう。コンパートメントは作業コンパートメントを指定します。
ネットワーク部分はこれまでと同じように、作業用VCNを選択し、サブネットはパブリックとします。
作成後、しばらく待つとアクティブとなります。

10. デプロイメント作成とルート設定
次に、ゲートウェイにデプロイメントを作成し、URLとFunctionsを紐付けます。
作成したAPI Gatewayの左メニューから[デプロイメント] -> [デプロイメントの作成]をクリックします。
名前は任意の値で、パス接頭辞は/v1とし、作業コンパートメントを設定して次をクリックします。
認証は一旦そのまま認証なしを選択し、次をクリックします。(商用環境は認証必須だと思いますが...一旦。)
ルート設定はパスを/postsに、メソッドをPOSTに、バックエンド・タイプをOracle ファンクションに変更します。
アプリケーションは事前に作成していたOCI Functionsアプリケーションを選択し、関数名はpost-funcを設定します。
最後に、設定値の確認をして問題がなければ、作成でOKです。
11. 動的グループ・ポリシーの設定(API Gateway to Functions)
API GatewayからFunctionsを呼び出するために、権限を付与する必要があります。
左側ハンバーガーボタンより、[アイデンティティとセキュリティ] -> [ドメイン]をクリックします。
現在使用しているドメイン(通常はDefault)を選択します。もし作業コンパートメントにDefaultドメインがない場合は、ルートコンパートメントに存在する可能性があります。
[動的グループタブ] -> [動的グループの作成]をクリックします。
名前と説明は任意で構いません。一致ルールには以下を記載しましょう。
作業コンパートメントのOCIDを調べるには、左側ハンバーガーボタンより、[アイデンティティとセキュリティ] -> [コンパートメント]をクリックし、詳細タブのOCIDをコピーしましょう。

ALL {resource.type = 'apigateway', resource.compartment.id = '<作業コンパートメントのOCID>'}
再びメニューから、[アイデンティティとセキュリティ] -> [ポリシー] -> [ポリシーの作成]をクリックします。
名前は任意の値で構いません。コンパートメントは作業コンパートメントを設定します。ポリシービルダーで以下をペーストしましょう。

Allow dynamic-group <動的グループ名> to use functions-family in compartment <コンパートメント名>
12. API Gatewayからの呼び出し
ここまでの作業を終えたら、実際にcurlコマンドを実行してみましょう!
# curlコマンド
curl -i -X POST -H "Content-Type: application/json" \
-d '{"title":"Final Test", "content":"From API Gateway"}' \
https://<API Gatewayのホスト名>/v1/posts
# 成功レスポンス
HTTP/2 200
strict-transport-security: max-age=31536000
server: Oracle API Gateway
x-frame-options: sameorigin
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
opc-request-id: /30C01BDB57E44B0EA1CDF3DCD07021C6/82F2E4B2F05349C395F2439B06C09E50
content-type: application/json
date: Thu, 08 Jan 2026 04:42:03 GMT
content-length: 72
{"message": "Success", "postId": "43bd2196-fd7e-43a1-a83f-10673250bf52"}%
おめでとうございます、無事にPOSTリクエストが成功し、記事の投稿が完了しました!
13. 検証中のトラブルについて
今回のハンズオン検証中に遭遇したトラブルについて、まとめてみます。
- API Gatewayを呼び出した時にCouldn't connect to serverが発生する。
API Gatewayを構築した後、curlコマンドでの呼び出しを試した際に以下のエラーが発生しました。
curl: (28) Failed to connect to <API Gateway ホスト名> port 443 after 75136 ms: Couldn't connect to server
原因はAPI Gatewayを配置したパブリックサブネットのセキュリティ・リストのイングレス・ルールに自グローバルIPのルールが存在しないためでした。
API GatewayのメトリックにあるAPIリクエストの数値が変化しなかったため、API Gatewayまで到達していないと考え、セキュリティ・リストの確認をして気づくことができました。
※ ハンズオン部分には手順として追記済みです。
- API Gatewayを呼び出した時に504 Gateway Timeoutが発生する。
上記のCouldn't connect to serverを解消した際に、再度打鍵をしたところ以下のエラーが発生しました。
{"code":504,"message":"Gateway Timeout"}%
原因はAPI Gatewayを配置したパブリックサブネットのセキュリティ・リストのエグレス・ルール(0.0.0.0/0)にステートレスのチェックがついていたことでした。
0.0.0.0/0ですべてのプロトコルを許可しているのに、API Gateway(パブリックサブネット) <-> OCI Functions(プライベートサブネット)への疎通がNGとなってしまい、Timeoutとなりました。
検証のタイミングでステートレスにチェックを入れてしまったようです...。切り分けのため、OCI Functionsを単体で呼び出して、正常に呼び出されることを確認できたため、API Gateway <-> OCI Functionsのルートに異常があるのでは?と疑うことができ、原因に気がつくことができました。

おわりに
かなり長くなってしまいましたが、OCI Functions + API Gatewayを利用し、外部からブログ投稿APIを呼び出すことに成功しました。
普段はTerraformなどのIaCでAWSリソースを触ることの多い私ですが、コンソールでのイチからの構築は非常に新鮮で、かつOCI特有の動的グループ・ポリシーの書き方に難儀しました...。
ただ、API Gatewayの部分は非常に分かりやすく、AWSに比べてシンプルだなと感じました。
今回はPOST APIのみの実装となりましたが、時間があればGET APIの実装もしてみたいと思います。
次回は構築したOCI Functions + API Gatewayの監視をDatadogで検証しようと思います。
検証しました↓
techblog.ap-com.co.jp
関連記事
APC技術ブログでOCI Functionsを利用してみた記事は他にもありますので、ぜひ参考にしてみてください。
techblog.ap-com.co.jp techblog.ap-com.co.jp
お知らせ
私達クラウド事業部はクラウド技術を活用したSI/SESのご支援をしております。
また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。