When migrating off IBM Cloud Functions, IBM Cloud Code Engine is one of the possible deployment targets. Code Engine offers apps, jobs and (recently function) that you can (or need) to pick from. In this post, we provide some discussion points and share tips and tricks on how to work with Code Engine functions.
IBM Cloud Code Engine is a fully managed, serverless platform to (not only) run your containerized workloads. It has evolved a lot since March 2021, when I published the blog post “Migrating Cloud Functions Code to Code Engine.” In 2021, there were only Code Engine apps and jobs. Earlier this year, Code Engine added support for functions (Functions-as-a-Service, or FaaS).
In this post, I am going to take a fresh look at that topic and discuss the options on how to move from IBM Cloud Functions to Code Engine.
Utilize Code Engine environment variables.
Apps, jobs and functions
IBM Cloud Code Engine features three different ways to run your business logic:
An app is a continuously run process that answers to HTTP requests.
A job runs to handle a task and then terminates.
A function is a stateless code snippet that is invoked by an HTTP request and, after producing a response, terminates. Moreover, jobs usually run significantly longer than functions (“batch processing”).
There are many more characteristics that help distinguish between apps, jobs and functions. In short, apps are a good fit if you want to craft a REST API or deploy a web application with backend/frontend functionality. You have full control over the HTTP server and its resources.
Jobs, on the other hand, are long-running processes that do not require any user interaction. They could be typical batch activities, analytics processing or even AI model training.
Lastly, functions can react to incoming HTTP requests very quickly. They serve low-latency use cases well, like chatbot integrations or webhooks. In contrast to apps, you do not and cannot define and configure the HTTP server.
When coming from Cloud Functions, you may have experienced that there are many use cases supported by Cloud Functions. Similarly, a function may have different attributes that are important depending on the case:
The invocation or start-up time (cold start) might be important, leading to an overall short response time.
In other cases, the cost (billing) might have been the competitive factor.
The simplicity and agility, caused by a function as unit for development and deployment in a DevSecOps process leads some projects to opt for functions.
Often, it is a combination of the above that leads to people preferring functions (FaaS) over other runtime or compute options.
From Cloud Functions to Code Engine
When moving from Cloud Functions to Code Engine, the following function characteristics need to be taken into account when deciding to on an app, a job or a Code Engine function:
Is an http endpoint needed to invoke the code?
Is the processing triggered by an event?
What programming language is used for the existing function and how big are the required libraries?
How long does the processing take, what compute resources are needed, is parallel processing desired?
The guide Migrating IBM Cloud Functions to Code Engine has a detailed overview with Code Engine app, job and function characteristics. They help you to select the best entity for your existing workload. Additionally, the current Code Engine function limitations and the general limits and quotas for Code Engine need to be taken into account. The section Migrating IBM Cloud Functions Actions to Code Engine Functions FAQ might help you decide how to migrate.
Tips and tricks for Code Engine functions
The following tips and tricks are based on my experiences with moving existing code from Cloud Functions to Code Engine functions. They help in cutting down deployment cycles by first utilizing local tests to implement similar functionality in combining Code Engine functions and jobs and designing integrated APIs by making use of Code Engine system variables.
Local testing of functions
Apps are regular web applications, jobs are like scripts, and both can be tested locally in several ways. Because functions are code snippets, some wrapper is needed to turn them into programs. The following approach has served me well so far.
With the function code in a subdirectory “func,” I utilize either the Python or Node.js wrapper code shown below and place it in the parent directory. There, I also maintain files with test configurations as JSON objects, similar to what is passed by Code Engine to the function on invocation. For testing, I run the wrapper along with the configuration file as parameter. The wrappers for Python and Node.js are shown below:
# syntax: python wrapper.py params.json
# import the Code Engine function: func/__main__.py
from func.__main__ import main
import sys, json
if __name__ == “__main__”:
# open file, read JSON config
with open(str(sys.argv)) as confFile:
# invoke the CE function and print the result
// syntax: node wrapper.js params.json
// require the Code Engine function: func/main.js
// read the file with function parameters
const fs = require(“fs”);
const data = fs.readFileSync(process.argv);
// invoke the CE function and log the result
Sometimes, you might need the HTTP endpoint of a function and the possibly longer execution time of a job. In that case, create both a function and a job. Then, utilize the Code Engine API to create a job run from within the function. In this hybrid approach, the function can get called via its HTTP endpoint and it terminates after kicking off the job run. A job could then run up to 24 hours and benefit from the parallel job processing capabilities in Code Engine. You can find a sample implementation of this pattern in the Code Engine code examples.
Environment variables and API design
For designing your API and functions namespace, you can utilize Code Engine-injected environment variables like __ce_path and __ce_method. The former holds the path component of the requested URL like “/object”, and the latter has the HTTP method like GET or POST. By switching on the supplied values for these variables, you can serve multiple API functions from the same Code Engine function. The benefit is a single base URL.
Depending on your project and code management, you might even want to combine this approach with separating each API function implementation into its own file—similar to the wrapper approach shown above.
IBM Cloud Functions have many use cases and properties, so there is no straightforward mapping to a specific Code Engine entity (i.e., app, job or function). By comparing an existing (Cloud Functions) function’s attribute to those of the Code Engine entities, you can pick the best fit. In many cases, a Code Engine function might be a good choice. For these cases, we shared tips and tricks that you can use for your Functions-as-a-Service project with Code Engine.
Use the following IBM Cloud Code Engine documentation to get started: