Oracle Cloud's answer to serverless architecture--an overview of how to use it in Python including with configuration variables and input request bodies
What is OCI Functions
Oracle Cloud's Functions is equivalent of AWS Lambda--a scalable, on-demand, multi-tenant Functions-as-a-Service platform solution which can be used to perform 5-minute-max short functions on the cloud. It is built on and powered by the Fn Project open source engine and can be deployed by the OCI API, CLI, and UI Console. Deployed Functions can be invoked by using the CLI, signed HTTP requests, or automatically on a schedule using the Resource Scheduler.
You can write code in Java, Python, Node, Go, Ruby, and C# (and for advanced use cases, bring your own Dockerfile, and Graal VM) to use in OCI Functions.
How to create a Function in OCI (Python)
1️⃣ Create an Application
Within the UI console on https://cloud.oracle.com, head to Developer Services > Applications. Hit Create application.
A shape of GENERIC_X86 is appropriate in most use cases.
2️⃣ Install prerequisites
To continue, we need to make sure you've installed certain requirements.
🔹 1. Ensure you have your OCI API config profile set up. See Setting up the OCI Configuration File using API Keys .
🔹 2. Install the fn
CLI:
MacOS using Brew:
brew update && brew install fn
Linux or MacOS:
curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
Windows:
In a Windows environment, install the Fn Project CLI by following the Install Fn Client instructions in the How-to: Run Fn client on Windows and connect to a remote Fn server topic on GitHub.
More info: Installing the Fn Project CLI
3️⃣ Create and Deploy a sample Function in Python
These instructions can be found on the Console UI of the new Application page under Details > Getting Started > Local setup.
Note that since we are creating an AMD image as opposed to an ARM one, you cannot deploy this Function from a Mac or any ARM machines.
Making sure to substitute the appropriate values like your tenancy namespace (idds2omv6oq), compartment OCID, compartment name (fdurrani-lab), and Docker login info, run the following to deploy and invoke a new Function from your local Linux/AMD machine:
# 1. Initialize your function
fn init --runtime python my-func
# 2. Switch into the generated directory
cd my-func
# 3. Create a context for this compartment and select it for
fn create context fdurrani-lab --provider oracle
fn use context fdurrani-lab
# 4. Update the context with the compartment ID and the Oracle Functions API URL
fn update context oracle.compartment-id ocid1.compartment.oc1..aaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
fn update context api-url https://functions.us-ashburn-1.oraclecloud.com
# 5. Provide a unique repository name prefix to distinguish your function images from other people's.
fn update context registry iad.ocir.io/idds2omv6oq/test-app-func-repo
# 6. Log into the Registry using the Auth Token as your password
docker login -u 'idds2omv6oq/[email protected]' iad.ocir.io
# 7. (Optional) Switch to the right OCI profile
fn update context oracle.profile DEFAULT
# 8. Deploy your function
fn deploy --app test-app
# 9. Invoke your function
fn invoke test-app my-func
The Python template will have the following files:
func.py
import io
import json
import logging
from fdk import response
def handler(ctx, data: io.BytesIO = None):
name = "World"
try:
body = json.loads(data.getvalue())
name = body.get("name")
except (Exception, ValueError) as ex:
logging.getLogger().info('error parsing json payload: ' + str(ex))
logging.getLogger().info("Inside Python Hello World function")
return response.Response(
ctx, response_data=json.dumps(
{"message": "Hello {0}".format(name)}),
headers={"Content-Type": "application/json"}
)
func.yaml
schema_version: 20180708
name: my-func
version: 0.0.7
runtime: python
build_image: fnproject/python:3.11-dev
run_image: fnproject/python:3.11
entrypoint: /python/bin/fdk /function/func.py handler
memory: 256
requirements.txt
fdk>=0.1.93
4️⃣ Invoke the Function
🔹 1. Using Fn CLI
We've seen above how to invoke the Function using the Fn CLI in Step #9.
After deploying the Function and invoking it with Step #9 above, we get a simple Hello World reply:
🔹 2. Using the OCI CLI (Signed HTTP)
We also can copy the Invoke endpoint of the Function and run a raw-request call using the OCI CLI to make a signed HTTP POST request to the endpoint:
oci raw-request --http-method POST --target-uri https://oofozydkb5a.us-ashburn-1.functions.oci.oraclecloud.com/20181201/functions/ocid1.fnfunc.oc1.iad.aaaaaaaaxxxxxxxxxxx/actions/invoke --profile DEFAULT
🔹 3. Periodic scheduling using Resource Scheduler
You can also schedule the Function to run periodically based on a Cron schedule using OCI's Resource Scheduler.
First, you need to give the Resource Scheduler permission to use the Function with the OCI IAM Policies:
allow any-user to use fn-function in compartment compA where all {request.principal.type='resourceschedule', request.principal.id='ocid1.resourceschedule.oc1.iad.aaaaaxxxxxxxxxxx'}
Of course, filtering by ID is optional if you want to allow the Resource Scheduler to invoke all Functions in the tenancy.
Include as well any policies the Function may need to read or access your OCI resources like below:
allow dynamic-group 'lb-fn-dynamic-grp' to inspect load-balancers in tenancy
where lb-fn-dynamic-grp
is a Dynamic Group defined as:
resource.id = 'ocid1.fnfunc.oc1.iad.aaaaaxxxxxxxx'
Then, create a Schedule to Start the Function:
5️⃣ Insert inputs
You can pass arguments and values to a Function.
Looking at the initial Python Function template above, we see the handler()
function retrieving the POST API request body from the data:
func.yaml
...
def handler(ctx, data: io.BytesIO = None):
name = "World"
try:
body = json.loads(data.getvalue())
name = body.get("name")
...
🔹 Prefixing in fn invoke
We can prefix the JSON body by prefixing the JSON to the fn invoke
command:
echo -n '{"name":"John"}' | fn invoke test-app my-func
🔹 Add in HTTP request
You can also add the JSON value into the body of the HTTP POST request when invoking the API:
oci raw-request --http-method POST --target-uri https://oofozydkb5a.us-ashburn-1.functions.oci.oraclecloud.com/20181201/functions/ocid1.fnfunc.oc1.iad.aaaaaaaaxxxxxxxxxxx/actions/invoke --request-body '{"name":"John"}' --profile DEFAULT
6️⃣ Use environment variables
You can insert configuration environment variables onto the Function or the parent Application page on the Console to be fed into the Function. This can be helpful if you want to allow the end user to modify certain variables without modifying the Function's code and redeploying it.
🔹 1. Add configuration variables
You can add the configuration variables either in the parent Application:
or in the Function:
As you can see, the parent Application's configuration variables are inherited to the Function.
🔹 2. Modify the Python code
Now, we modify the Python code to read in the configuration variables and return them:
func.yaml
import io
import json
import logging
from fdk import response
def handler(ctx, data: io.BytesIO = None):
name = "World"
allKeyValuesString = ""
try:
cfg = ctx.Config()
allKeyValuesString += cfg.get("key1", "missing") + " "
allKeyValuesString += cfg.get("key2", "missing") + " "
allKeyValuesString += cfg.get("key3", "missing") + " "
allKeyValuesString += cfg.get("keyParent", "missing") + " "
body = json.loads(data.getvalue())
name = body.get("name")
except (Exception, ValueError) as ex:
logging.getLogger().info('error parsing json payload: ' + str(ex))
logging.getLogger().info("Inside Python Hello World function")
return response.Response(
ctx, response_data=json.dumps(
{"message": "Hello {0}. {1}".format(name, allKeyValuesString)}),
headers={"Content-Type": "application/json"}
)
🔹 3. Re-deploy and invoke the Function
Deploying and invoking the Function again, and we get a printout of all the available values from the configuration:
fn deploy --app test-app
fn invoke test-app my-func
References
Safe harbor statement
The information provided on this channel/article/story is solely intended for informational purposes and cannot be used as a part of any contractual agreement. The content does not guarantee the delivery of any material, code, or functionality, and should not be the sole basis for making purchasing decisions. The postings on this site are my own and do not necessarily reflect the views or work of Oracle or Mythics, LLC.
This work is licensed under a Creative Commons Attribution 4.0 International License.
Top comments (0)