I've been playing with OpenFaaS recently and it's a very accessable way to starting building cloud first services. I wanted to see what I could cram in there, so I built a few templates that would let me host a static site. One that is just html, and another than can be built with something like create-react-app.

Static

Create the template directory:

1
mkdir -p template/static

Then add a template/static/template.yml file:

1
2
language: static
handler_folder: public

Then add a template/static/Dockerfile to do the build:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
FROM --platform=${TARGETPLATFORM:-linux/amd64} openfaas/of-watchdog:0.7.2 as watchdog
FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine:3.10 AS runtime

WORKDIR /home/app

COPY --from=watchdog /fwatchdog .
COPY . .

ENV mode="static"
ENV static_path="/home/app/public"

HEALTHCHECK --interval=3s CMD [ -e /tmp/.lock ] || exit 1

CMD ["./fwatchdog"]

Serving at root

Setting up the Caddyfile:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  email "email@example.com"
}

hostname.example.com {
  @proxy path /ui/* /system/* /function/*
  handle @proxy {
    reverse_proxy localhost:8080
  }

  handle {
    uri replace / /function/static/ 1
    reverse_proxy localhost:8080
  }
}

This will proxy /ui/*, /system/*, and /function/* to the OpenFaaS gateway, and everything else it will (internally) rewrite to /function/static/, which will get forwarded our static handler. So when you deploy the function make sure you name it that!

React

First we need to create our template directory:

1
mkdir -p template/react

First we need to create a template/react/template.yml file

1
2
language: react
handler_folder: react

node_modules, my least favorite thing in the world, needs to get excluded, so lets create template/react/.dockerignore. The react here must be the same as the handler_folder above.

1
2
react/node_modules
react/build

Then we add the Dockerfile to build everything:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
FROM --platform=${TARGETPLATFORM:-linux/amd64} node:12-alpine as build

ARG TARGETPLATFORM
ARG BUILDPLATFORM

RUN apk --no-cache add curl ca-certificates \
    && addgroup -S app && adduser -S -g app app

# Turn down the verbosity to default level.
ENV NPM_CONFIG_LOGLEVEL warn

RUN mkdir -p /home/app

# Build the react production build

WORKDIR /home/app

COPY react/package.json react/yarn.lock ./

# This ordering means the yarn installation is cached for the outer
# react handler.

RUN yarn

COPY react .

RUN yarn build

FROM --platform=${TARGETPLATFORM:-linux/amd64} openfaas/of-watchdog:0.7.2 as watchdog
FROM alpine:3.10 AS runtime

WORKDIR /home/app

COPY --from=build /home/app/build public

COPY --from=watchdog /fwatchdog .

ENV mode="static"
ENV static_path="/home/app/public"

HEALTHCHECK --interval=3s CMD [ -e /tmp/.lock ] || exit 1

CMD ["./fwatchdog"]

Testing React

First we will create the function itself, and then populate it using npx create-react-app testappreact. This will write it into the "handler" directory.

We will also remove the .git repo that create-react-app sets up.

1
2
3
  faas-cli new --lang react testappreact
  npx create-react-app testappreact
  rm -rf testappreact/.git

I recommend using relative links, by adding "homepage": "./" in the generated package.json file.

Then we can build and deploy using

1
faas-cli up -f testappreact.yml

And visit your server to see what you see!

Testing Vue app

Just for fun, lets make it build a vue app.

1
2
3
  faas-cli new --lang react testappvue
  vue create testappvue
  rm -rf testappvue/.git

In order to get vue apps to have relative paths, you need to create a vue.config.js file to set it:

1
2
3
  module.exports = {
      publicPath: ''
  };

And since vue builds in the dist directory, we need to modify the build script inside of package.json to rename the file after it's built:

1
    "build": "vue-cli-service build && mv dist build",

Then, we can build and deploy:

1
  faas-cli up -f testappvue.yml

It's a little hacky but it seems to work alright!

Previously

Docker One Liners Why install

2021-02-12

Next

Uploading Blobs Simple datastore

2021-02-17

howto

Previously

Interacting With Git via HTTP Looking at git http traffic

2021-02-11

Next

Uploading Blobs Simple datastore

2021-02-17