Consider the following: You’re migrating some microservices to Kubernetes with certain API dependencies, which are not yet reachable. There can be plenty of reasons for that: Some dependent services run in a highly secured data center on-prem and are not yet migrated, some APIs may not be fully developed yet.

In this post we explore the use of Mockoon in Kubernetes: We’ll expose Mockoon via internal DNS and make it reachable for a simple test web application.

Exploring the Mockoon GUI

Image.png

I highly recommend checking out the desktop app over here: https://mockoon.com/download/. If you’re following on macOS, just use brew for convenience:

brew install --cask mockoon

The Mockoon UI enables you a postman-y overview of all your mock routes. It’s quite intuitive, feel free to play around with it. If you find yourself struggling with the navigation, check out their great Mockoon GUI cheat sheet.

In our demo app we’ll be using the plain demo API with faker.js in the background generating some random userdate on the route /users. Right-click the “Demo API” in the left sidebar and grab the json file containing the config. That’s what we’ll need in the next step.

Mockoon + Docker = ♥️

We’ll be using the actively maintained Docker image Mockoon CLI. To try out the image locally, grab your exported json file and give it a spin:

docker run -d --mount type=bind,source=/home/username/your-data-file.json,target=/data,readonly -p 3000:3000 mockoon/cli:latest -d data -i 0 -p 3000

Now curl your mock API:

$ curl localhost:3000/users

# Response
{
  "Templating example": "For more information about templating, click the blue 'i' above this editor",
  "users": [
      {
        "userId": "90741",
        "firstname": "Shany",
        "lastname": "Dach",
        "friends": [
        ]
      },
...
}

Great! Your Mockoon server is exposed correctly. Now then, let’s build something in Go requesting our mock API.

Setting up the Go microservice

For my exemplary microservice I’m using a small Golang server handling three routes, /, /healthz and /mockoon. /mockoon will HTTP GET the mock API from mockoon’s /users path and print it as plain text. If you want to follow along, grab the code from this GitHub Gist.

To reproduce a common DevOps practice our server will pull the mockoon route /users from the environmental variable MOCKURL. Once we deploy our k8s manifests, we’ll set the variable to http://mockoon.mockoon.svc.cluster.local/users . Let’s check out the relevant function in go:

func mockoon() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path != "/mockoon" {
			http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
			return
		}

		resp, err := http.Get(os.Getenv("MOCKURL"))
		if err != nil {
			log.Fatal(err)
		}
		defer resp.Body.Close()

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Fatal(err)
		}

		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.Header().Set("X-Content-Type-Options", "nosniff")
		w.WriteHeader(http.StatusOK)
		fmt.Fprintln(w, "Querying Mockoon API from ENV VAR:", os.Getenv("MOCKURL"))
		fmt.Fprintln(w, string(body))
	})
}

Dockerizing our Go Microservice

I highly encourage you to check out Google’s distroless container images.

Restricting what’s in your runtime container to precisely what’s necessary for your app is a best practice employed by Google and other tech giants that have used containers in production for many years. It improves the signal to noise of scanners (e.g. CVE) and reduces the burden of establishing provenance to just what you need.

The Dockerfile of our Go server will look like that:

FROM golang:1.17-bullseye as build

WORKDIR /go/src/app
ADD . /go/src/app

RUN go get -d -v ./...
RUN go build -o /go/bin/app

FROM gcr.io/distroless/base-debian11
COPY --from=build /go/bin/app /

EXPOSE 8090
CMD ["/app"]

Dont forget to build, tag and push your image to the repository of your liking. Also make sure your Kubernetes Cluster has the ability to pull your image from your desired registry.

Exposing Mockoon in Kubernetes

The basic infrastructure will look like this:

Image

The Go server pod (go-mockoon) will be requesting the Mockoon pod (mockoon) by its internal DNS. The Mockoon pod mounts /data/data.json from the ConfigMap (mockoon-data). Check out the manifests:

---
apiVersion: v1
kind: Namespace
metadata:
  name: mockoon
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: go-mockoon
  namespace: mockoon
spec:
  replicas: 1
  selector:
    matchLabels:
      app: go-mockoon
  template:
    metadata:
      labels:
        app: go-mockoon
    spec:
      containers:
        - name: go-mockoon
          image: yourrepo/go-mockoon
          env:
            - name: MOCKURL
              value: http://mockoon.mockoon.svc.cluster.local/users
          ports:
            - containerPort: 8090
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8090
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: go-mockoon
  namespace: mockoon
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8090
  selector:
    app: go-mockoon
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: go-mockoon
  namespace: mockoon
spec:
  rules:
    - host: go-mockoon.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: go-mockoon
                port:
                  number: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mockoon
  namespace: mockoon
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mockoon
  template:
    metadata:
      labels:
        app: mockoon
    spec:
      containers:
        - name: mockoon
          image: mockoon/cli:latest
          args: ["-d", "/data/data.json"]
          ports:
            - containerPort: 3000
          volumeMounts:
            - name: data
              mountPath: "/data"
      volumes:
        - name: data
          configMap:
            name: mockoon-data
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: mockoon
  namespace: mockoon
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 3000
  type: NodePort
  selector:
    app: mockoon
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mockoon-data
  namespace: mockoon
data:
  data.json: |
        {
	...
	}

Now check your pods:

$ kubectl get pods -n mockoon
NAME                         READY   STATUS    RESTARTS   AGE
go-mockoon-98cd57695-b6mwn   1/1     Running   0          2d14h
mockoon-69bb77755d-c96dd     1/1     Running   0          2d14h

Now test your Go server by requesting the host you set in your ingress resource, i.e. go-mockoon.example.com. Tail the logs of your go-mockoon pod:

$ kubectl logs -f go-mockoon-98cd57695-b6mwn -n mockoon
...
http: 2022/02/13 23:12:43 1644793963376041701 GET /healthz 10.0.22.15:45010 kube-probe/1.21+
http: 2022/02/13 23:12:46 1644793966405444999 GET /mockoon 10.0.5.247:6206 Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:96.0) Gecko/20100101 Firefox/96.0

Requesting go-mockoon.example.com/mockoon will redirect to the internal Mockoon pod /users route, located at http://mockoon.mockoon.svc.cluster.local/users. You should see the faker.js generated user data originating from the Mockoon pod 🦝 🎉.

I want the true Dev Experience!

Me too. In part 2 we’ll explore Helm charts and an introduction to Skaffold. Stay tuned!