Skip to main content

Gatsby Blog with a MeiLi Search Backend

Shenzhen, China

I already looked into how to:

The next step is to wrap the pre-rendered build inside a Docker container that uses goFiber to serve the generated static HTML/CSS/JS/JSON code.

Prepare the goFiber Webserver

The code for the webserver is fairly simple - very similar to Express.js:

package main

import (
"flag"
"log"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/recover"
)

var (
port = flag.String("port", ":8888", "Port to listen on")
prod = flag.Bool("prod", false, "Enable prefork in Production")
)

func main() {

// Create fiber app

// Development
// app := fiber.New(fiber.Config{
// Prefork: *prod, // go run app.go -prod
// })

// Production
app := fiber.New(fiber.Config{
Prefork: true,
})

// Middleware
app.Use(recover.New())
app.Use(logger.New())

// Setup static files
app.Static("/", "./data/public")

// Listen on port 8888
log.Fatal(app.Listen(*port)) // go run app.go -port=:8888
}

The webserver expects our static code - generated from our React.js MeiLi search interface and pre-rendered by Gatsby.js - in the public folder. It will then serve it on / with port 8888.

Building the Go App

We can now use Docker to build the webserver inside a Golang Container and then transfer our website code and the build binary into a tiny Alpine Container:

# Building the binary of the App
FROM golang:alpine AS build

# Project labels
LABEL maintainer="m.polinowski@gmail.com"

# `build` can be replaced with your project name
WORKDIR /go/src/build

# Copy all the Code and stuff to compile everything
COPY ./container/* ./

# Downloads all the dependencies in advance (could be left out, but it's more clear this way)
RUN go mod download

# Builds the application as a staticly linked one, to allow it to run on alpine
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o app .

# Moving the binary to the 'final Image' to make it smaller
FROM alpine:latest

WORKDIR /app

# Create the `public` dir and copy all the assets into it
RUN mkdir ./data
COPY ./container/data ./data

# `build` can be replaced here as well
COPY --from=build /go/src/build/app .

# Exposes port 8888 because our program listens on that port
EXPOSE 8888

# CMD ["./app"]
RUN chmod +x ./data/run.sh
CMD ["ash", "./data/run.sh"]

CI Pipeline

Now I would use a Gitlab CI pipeline to do my Gatsby.js build as well as the build described above. But this also works locally with a npm script that we can add to the package.json file of our Gatsby.js app:

"scripts": {
"build": "node --max-old-space-size=8192 node_modules/gatsby/dist/bin/gatsby build",
"develop": "node --max-old-space-size=8192 node_modules/gatsby/dist/bin/gatsby develop",
"start": "npm run develop",
"serve": "gatsby serve",
"clean": "gatsby clean",
"docker": "mv public/* docker/container/data/public && docker build -t my_blog docker/. && mv docker/container/data/public/* public"
}

So now we can enter the root dir of our Gatsby.js app and execute:

npm run build
npm run docker

Now we have both container images ready to be served:

REPOSITORY             TAG      SIZE
my_blog latest 23.4MB
getmeili/meilisearch latest 258MB

Docker Compose

Now we can write a docker-compose.yml that brings it all together for us - Note that I ran into an issue here. I am building the Gatsby.js page while starting the MeiLi Search container manually (without Compose). I am connecting to the service on localhost. If I now use a virtual network for those container I will have to change the connection URL from localhost:7700 to meilisearch:7700. But to keep this simple I just run the container on the host network stack here - so nothing needs to be changed:

version: '3'

services:
meilisearch:
container_name: meilisearch
image: getmeili/meilisearch:latest
environment:
- MEILI_MASTER_KEY=RhTX1pLPSKSn7KW9yf9u_MNKC0v1YKkmx2Sc6qSwbLQ
- MEILI_NO_ANALYTICS=true
# - MEILI_ENV=development
- MEILI_ENV=production
network_mode: "host"
# ports:
# - ${MEILI_PORT:-7700}:7700
# networks:
# - meilisearch
volumes:
- /opt/meili_data:/meili_data
restart: unless-stopped

gatsby_frontend:
container_name: my_blog
image: my_blog:latest
network_mode: "host"
# ports:
# - 8888:8888
# networks:
# - meilisearch
restart: unless-stopped

# networks:
# meilisearch:
# driver: bridge

You can now bring the application up with:

docker-compose up

and visit http://localhost:8888 and should see the Gatsby.js frontend being served by your goFiber webserver connecting to your MeiLi Search backend:

Gatsby Blog with a MeiLi Search Backend

👍