Adding extra npm modules to IBM Cloud Functions with Docker

IBM Cloud functions comes with a default set of npm modules — if you need to add additional packages, especially those with compiled components, you’re going to need to master Docker-based deployment. This article tells you how.

Glynn Bird
Webtips
3 min readJun 7, 2020

--

IBM Cloud Functions is a serverless platform based on Apache OpenWhisk. It allows you to create actions, tiny bundles of custom code, which can be executed for you without owning fixed computing assets — you only pay for the executions you use, and pay nothing if your actions don’t run.

For a Node.js developer, IBM Cloud Functions deployments can be as simple as sending your JavaScript source to the service where it is packaged up and hosted for you:

# create an action called "myaction" using by Node.js code
# from mysourcecode.js
ibmcloud fn action create myaction mysourcecode.js

You have access a number of pre-installed npm packages, but if you need extra npm modules your deployment procedure gets a bit more complicated. There are two options:

  1. Zip up your package.json, source files & node_modules directories and upload the zip file instead of a single JavaScript file.
  2. Create a custom Docker image containing your extra dependencies. Deploy that Docker image with your source code.

The first option is limited by the size of the upload (e.g. node_modules can get large npm module and will eventually be too big for IBM Cloud Functions). It will also fail for dependencies that have binary components — if you upload modules that compile themselves from source, they may fail on the IBM Cloud Functions hardware which is likely to be a different architecture from your local machine.

Customised containers — Middlesbrough, UK

Adding your own npm modules to the IBM Cloud Functions base image

The base docker image that runs your action code is made with this repository. The base image contains the bare-bones operating system, the Node.js installation, the default npm packages, and the OpenWhisk “runner” — an Express server that exposes an API allowing your custom code to be executed in containers built from this image.

We can take a base image such ibmfunctions/action-nodejs-v10 and use it as the basis of a new image, but with extra npm modules built-in. As an example, I’m going to create a new Docker image that has an additional npm module — a random id generation library. To follow along at home you’ll need Docker installed with a functioning DockerHub account, an IBM Cloud account and the IBM Cloud Functions CLI installed.

Create a file called Dockerfile containing the following text:

FROM ibmfunctions/action-nodejs-v10RUN npm install kuuid

The Dockerfile is the recipe which your local Docker app will use to build the new image, by starting with the IBM Cloud Functions base image and additionally adding extra npm modules with “npm install”, in this case, “kuuid”, our id generation library.

Build the image with your local Docker installation (where glynnbird is your DockerHub username):

docker build -t glynnbird/kuuid .

We can now tag this with a version number:

docker tag glynnbird/kuuid:latest glynnbird/kuuid:0.0.1

and push it to DockerHub with:

docker push glynnbird/kuuid:0.0.1

So now we have an image on DockerHub that is compatible with IBM Cloud Functions, but has extra npm modules ready for our actions to use.

We can create some source code that uses the kuuid module to generate unique identifiers:

const kuuid = require('kuuid')const main = () => {
return { id: kuuid.id() }
}

We can deploy this code as a serverless action with :

ibmcloud fn action update kuuid --docker glynnbird/kuuid:0.0.1 index.js

Note that we we are supplying our source file “index.js”, but instead of using the default base image to execute the code, it will use the image from DockerHub we referenced with the --docker parameter.

We can then execute the action with:

ibmcloud fn action invoke kuuid --result
{
"id": "001jhvxU1dDLJ63hNMK90vyrU91bTybI"
}

This approach has several benefits:

  • If your dependencies are static, we need only do the Docker build/tag/push procedure once.
  • Although DockerHub is in the public domain, your Docker image only contains the IBM Cloud Functions base image and extra npm modules — none of your custom code.
  • Deployment of your custom code to IBM Cloud Functions is very quick — no uploading of multi-megabyte zip files containing thousands of node_modules files.

--

--