26
Developing a NextJS app on OpenFaaS
The instructions covered in this article require the following tools to be installed before proceeding.
- faas-cli - https://github.com/openfaas/faas-cli
- Terraform - https://terraform.io
- kubectl - https://kubernetes.io/docs/tasks/tools/
- Docker - https://www.docker.com/get-started
- Helm - https://helm.sh
Personally, I prefer to leverage VS Code Remote Containers to create portable development environments. Below you'll find the devcontainer.json
and the Dockerfile
to put inside your project's .devcontainer
folder.
{
"name": "<appname>",
"build": {
"dockerfile": "Dockerfile",
// Update 'VARIANT' to pick an Alpine version: 3.11, 3.12, 3.13, 3.14
"args": {
"VARIANT": "3.14",
"DOCKER_GID": "1001",
"NODE_VERSION": "14"
}
},
// Set *default* container specific settings.json values on container create.
"settings": {},
"mounts": [
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind",
"source=${env:HOME}${env:USERPROFILE}/.kube,target=/home/vscode/.kube,type=bind"
],
// Add the IDs of extensions you want installed when the container is created.
// Note that some extensions may not work in Alpine Linux. See https://aka.ms/vscode-remote/linux.
"extensions": [
"ms-kubernetes-tools.vscode-kubernetes-tools",
"ms-azuretools.vscode-docker"
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "uname -a",
// Replace when using a ptrace-based debugger like C++, Go, and Rust
// "runArgs": [ "--init", "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ],
"runArgs": ["--init"],
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"remoteUser": "vscode"
}
The first step in deploying an application to OpenFaas is to deploy the OpenFaaS platform to Kubernetes. I use Helm and Terraform to create the OpenFaaS deployment.
OpenFaaS provides a helm chart
provider "kubernetes" {
config_context = "docker-desktop"
config_path = "~/.kube/config"
}
provider "helm" {
kubernetes {
config_context = "docker-desktop"
config_path = "~/.kube/config"
}
}
variable "openfaas_password" {
type = string
description = "OpenFaaS admin password"
}
resource "kubernetes_namespace" "ns_openfaas_fn" {
metadata {
name = "openfaas-fn"
}
}
resource "kubernetes_namespace" "ns_openfaas" {
metadata {
name = "openfaas"
}
}
resource "kubernetes_secret" "sec_openfaas_creds" {
metadata {
name = "basic-auth"
namespace = "openfaas"
}
data = {
"basic-auth-user: "admin",
"basic-auth-password": var.openfaas_password
}
}
resource "helm_release" "rel_openfaas" {
name = "openfaas"
namespace = "openfaas"
chart = "openfaas"
repository = "https://openfaas.github.io/faas-netes/"
set {
name = "functionNamespace"
value = "openfaas-fn"
}
set {
name = "generateBasicAuth"
value = "false"
}
set {
name = "basic_auth"
value = "true"
}
set {
name = "serviceType"
value = "ClusterIP"
}
set {
name = "ingressOperator.create"
value = "true"
}
}
The terraform script can be deployed with the following commands:
terraform init
terraform plan -var openfaas_password='<openfaas_password>' --out out.plan
terraform apply out.plan
The terraform script performs the following operations:
- Creates the
openfaas
namespace - Creates the
openfaas-fn
namespace - Creates a Kubernetes secret with the
basic-auth
credentials - Deploys the OpenFaaS helm template
- Creates the OpenFaaS stack
- Disables the generation of a randomized admin password -- instead preferring the
basic-auth
secret we created earlier - Deploys the OpenFaaS ingress operator which enables us to ingress our functions using a Custom Resource Definition (CRD)
To create the function which will serve NextJS once we deploy it to OpenFaaS, the Docker template will need to be created.
faas-cli template store pull dockerfile
faas-cli new <appname> --lang dockerfile
The dockerfile
template is created in a new folder which will be named the same value that was used for <appname>
in the snippet above.
Next, the NextJS app will be initialized
npx create-next-app tmp-<appname> --ts # ts is optional. I like Typescript
mv tmp-<appname>/* <appname>/* # Relocate all files into the openfaas function folder
rm -rf tmp-<appname> # temporary folder is no longer needed
We have the basis for our NextJS OpenFaas function. The container template files need to be tweaked to work properly.
Update .dockerignore
to exclude all unnecessary files from the Docker build
node_modules
.next
__tests__
coverage
docs
Update Dockerfile
to properly build the NextJS application into an OpenFaaS function
# This template was adapted from the original node-express template
# https://github.com/openfaas-incubator/node10-express-template
FROM openfaas/of-watchdog:0.8.2 as watchdog
FROM node:14-alpine as ship
COPY /fwatchdog /usr/bin/fwatchdog
RUN chmod +x /usr/bin/fwatchdog
RUN addgroup -S app && adduser -S -g app app
ENV NPM_CONFIG_LOGLEVEL warn
RUN mkdir -p /home/app
WORKDIR /home/app
RUN yarn
COPY . /home/app/
# Build the server
# remove the dev dependencies
RUN yarn && yarn build \
&& npm prune --production
RUN chown -R app:app /home/app && chmod 777 /tmp
USER app
ENV cgi_headers="true"
ENV fprocess="yarn start"
ENV mode="http"
ENV upstream_url="http://127.0.0.1:3000"
ENV exec_timeout="10s"
ENV write_timeout="15s"
ENV read_timeout="15s"
EXPOSE 8080
HEALTHCHECK CMD [ -e /tmp/.lock ] || exit 1
CMD ["fwatchdog"]
With all the configuration completed, you should be able to deploy the function to OpenFaaS
faas-cli login # Prompt for username and password
faas-cli up -f <appname>.yml # Deploy he function
- OpenFaaS Helm Chart - https://github.com/openfaas/faas-netes/tree/master/chart/openfaas
- OpenFaaS Ingress - https://github.com/openfaas/ingress-operator
26