Building a Sample Microservices App with the Acorn Cloud Platform

Oct 13, 2023 by Luc Juggery
Building a Sample Microservices App with the Acorn Cloud Platform

As a developer you probably know your application will run on Kubernetes when deployed to production. But… maybe you don’t know (and don’t want to know) Kubernetes resources. In that case, from where can you start to make sure your app is ready for Kubernetes ?

You could use Docker Compose to develop and build your application’s images. But Docker Compose will not bring you really far as Kubernetes is concerned. I mean, Compose is great on your local machine but it’s limited to the development environment and it will not help to define your application in a way it can be understood by Kubernetes. Compose does not know anything about Kubernetes. Kompose (https://kompose.io/) could help building yaml manifests of Kubernetes resources, but it will not hide the complexity of those Deployments, Services, ConfigMaps, etc.

Acorn offers an alternative to Docker Compose. It can be used across the whole lifecycle of the application. Acorn uses the Acornfile format to define an application: the containers it is made of, how they interact with each other, the storage your application needs and much more. With this Acornfile we can run an application in development mode thus iterating on the microservices code. It’s very handy to modify code in the IDE and see those changes directly taken into account in the running application which is automatically reloaded. Ok, Docker Compose also knows how to do that, but Acorn goes much further as it can create an OCI package of the whole application, distribute it in an OCI registry and use that same package to deploy the application into a production Kubernetes cluster.

Acorn provides an online platform where we can use the sandbox environment to start right away the development of an application. It also makes it easy to test and package the application. Going one step further, the Acorn platform also allows us to use our own AWS credentials to setup an AWS region and deploy your newly created application there.

A sample project

To illustrate the whole flow we’ll consider a simple microservice application made up of a web frontend and an api. This application simply allows a user to send dummy data to a backend of his choice. This can basically be used to simulate data generated by IoT devices. In a nutshell the web frontend allows an end user to specify the type of data he wants to send and the target they should be sent to. The backend is responsible for sending those data and to report the status to the frontend through a websocket connection.

The screenshot below shows the resulting frontend.

fakely.app.png

In the following we’ll describe the steps involved from the project creation to the deployment.

Creation of the project

First we create a folder containing the whole project and switch into that one

mkdir fakely && cd fakely

Within this folder we will create a basic structure of each microservice of the application (only 2 microservices at that stage but additional ones might be added later on)

Web frontend

The web UI is based on Nextjs (https://nextjs.org/) a great NodeJs framework. The following command creates the scaffold of this application

npx create-next-app@latest

It can be run locally in development mode with the following command

npm run dev

We can see it is running fine accessing the default page when launching a web browser on localhost:3000

NextJS-base.png

We can then stop the app as we will not need to run it locally.

First thing is to add a Dockerfile for this microservice so it can run into a container. Right from the start it’s a good idea to make sure your Dockerfile uses the multistage build and thus allows to build a development and a production version. Below is a sample Dockerfile which defines a base image from which a development and a production image could be built

FROM node:16.19 as base WORKDIR /app COPY . . EXPOSE 3000 FROM base as dev ENV NODE_ENV=development RUN npm ci RUN mv /app/node_modules / CMD ["npm", "run", "dev"] FROM base as production RUN addgroup --system --gid 10000 nodejs RUN adduser --system --uid 10000 nextjs ARG APP_VERSION ENV APP_VERSION $APP_VERSION ENV NODE_ENV=production RUN npm ci --production RUN npm run build USER nextjs CMD ["npm", "start"]

Note: this Dockerfile is quite simple, it would need to be enhanced before it is used in a production environment.

API

The backend is a simple API running on FastAPI (https://github.com/tiangolo/fastapi), It requires python / pip to run locally.

In a new api folder we first we add a requirements.txt file:

fastapi == 0.101.0 uvicorn[standard] == 0.23.2

Next we install those dependencies:

pip install -r requirements.txt

Next we create a very basic main.py:

from fastapi import FastAPI app = FastAPI() @app.get("/") async def root(): return {"message": "Hello World"}

Then we make sure it runs locally

uvicorn main:app --reload

We can see it is running fine when launching a web browser on localhost:8000

api-hello-world.png

We can then stop the app as we will not need to run it locally.

As we’ve done for the front part, we add a Dockerfile for that microservice. It also uses the multistage build and defines both dev and production targets

FROM python:3.9 as base WORKDIR /app FROM base as build COPY requirements.txt requirements.txt RUN pip install --no-cache-dir --upgrade -r requirements.txt COPY . . FROM build as dev EXPOSE 8000 CMD ["uvicorn", "main:app", "--reload", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"] FROM build as production EXPOSE 8000 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Note: this Dockerfile is quite simple, it would need to be enhanced before it is used in a production environment.

In the next parts we will see how we can use Acorn to develop, build, distribute and run the application.

Create an Acornfile

First we create an Acornfile, at the root of the project, defining the application. It specifies both api and www containers as well as a router redirecting incoming requests to the correct container. The frontend is served on the default route and the requests towards /api are redirected to the api container..

containers: { api: { build: { target: std.ifelse(args.dev, "dev", "production") context: "./api" } if args.dev { dirs: { "/app/main.py": "./api/main.py" } } ports: "8000/http" } www: { build: { target: std.ifelse(args.dev, "dev", "production") context: "./www" } if args.dev { dirs: "/app": "./www/" } ports: "3000/http" } } routers: { default: { routes: { "/": "www:3000" "/api": "api:8000" } } }

With all those elements created, the folder structure of the application should be like the following:

% tree -L 2 . ├── Acornfile ├── api ├── __pycache__ └── main.py └── www ├── README.md ├── next-env.d.ts ├── next.config.js ├── node_modules ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── src ├── tailwind.config.ts └── tsconfig.json

Start developing against the Acorn platform

In this step we will run the application in dev mode in the Acorn Cloud sandbox.

First we go to https://acorn.io

acorn.io.png

Clicking the login button leads to the authentication page (a GitHub account is the only requirement for the login step).

acorn-login.png

Once logged in we land on the Acorn Cloud dashboard

acorn-dashboard.png

Before we can run the app, we need to set up the local acorn cli so it can connect to the acorn controller running on Acorn Cloud. Clicking the profile icon / “Setup CLI” to get the detailed instructions.

cli-setup-1.png

From there we execute the commands provided in the UI:

% acorn login -p cz8x8xw77wlm6hsj acorn project use acorn.io/lucj/acorn Run "acorn projects" to list available projects Run "acorn project use acorn.io/lucj/acorn" to set default project Login to acorn.io as lucj succeeded

After a couple of seconds the connection is successful.

cli-setup-2.png

We are now ready to run our application in the Acorn Cloud’s sandbox.

Running the app in dev mode

From the root folder of the project (the folder containing the Acornfile), we run the application in development mode.

Note: the development mode allows to develop the application in an interactive way as changes of the source code is automatically taken into account by the running application

acorn run -n demo -i

After a few tens of seconds we are returned a https endpoint allowing to access the application.)

NextJS-base-sandbox.png

There are not many things here as our application code is still very simple.

Using Acorn Cloud platform we can see the application has been deployed.

app-running.png

It contains 2 containers, each one is currently in the Running state.

app-containers.png

Clicking on a container’s name we can get detailed information such as:

  • the resources used by the container
  • the number of replicas
  • the multistage target used to build it
  • the mounted directories

container-details.png

We can get the logs of a the container’s replica

replica-logs-1.png

replica-logs-2.png

The bottom menu allows to select the replica we want to see the logs from and to filter the logs.

Now that the application runs as an acorn in development mode we can change the code from our IDE and see those changes taken into account in real time. Let’s now jump to the code, modify it and forget about everything else for some time.

… a couple of hours later …

We are happy with the development we’ve done, the application is working as expected in dev mode. The last version of the code has been pushed to the project’s GitLab repository https://gitlab.com/fakely.

We can then stop the app and run it in production mode to make sure everything is fine with this configuration too.

// Stop the application from running in development mode CTRL-C // Start the mode in “normal” mode acorn run -n fakely

The web frontend is then accessible through the https endpoint returned by Acorn.

We can then stop the application

acorn rm -af fakely

Next we will package the application and distribute it in a registry.

Push an artifact to an OCI registry

Now the application is running fine we can build an OCI artifact and push it to an OCI compatible registry. We use DockerHub in this example.

First we log into the registry

acorn login docker.io

Next we build and push the image

acorn build -t docker.io/lucj/fakely:v1.0.0 --push

Run the artifact from the Acorn image

From the UI we can run the application from an existing image

deploy-from-image-1.png

The Create Acorn button allows to create an Acorn from an image or from a Service catalog (this will be the subject of another blog in the series).

deploy-from-image-2.png

When selecting an image we provide a name, this one will be used to name the application, and the OCI image to be used. In the example below we use the following image docker.io/lucj/fakely:v#.#.# The tag is special in that it configure the auto-upgrade functionality: each time a newer image is published to the registry the application will be automatically upgraded with that one.

deploy-from-image-3.png

It only takes a few tens of seconds for the application to be deployed.

app-runningapp-running.png

We can access the application by clicking the endpoint icon.

app-endpoint.png

This opens up the application in the browser

app-ui.png

From the UI we could test the app making sure it is working fine. we can do it sending dummy data to a demo backend created on https://webhooks.app

fakely-send.png

webhooks-receive.png

Summary

In this article we saw how we can use Acorn right from the beginning of a project. Acorn allows to develop an application in an interactive way, package and distribute the app in a single OCI artifact, run it in a production environment. Acorn cloud platform really eases the onboarding process for developers as it provides a ready to use environment out of the box. By the way, we were able to build and deploy an app onto a Kubernetes cluster but thanks to Acorn we didn’t even notice it as it hides all the Kubernetes internals for a much better user experience.