![Thumbnail artcile resources/blog](https://grateful-confidence-e8d3628efb.media.strapiapp.com/Live_Stream_introducing_clio_502152da05.png)
Hi, I'm Clio, your slightly grumpy but friendly assistant, designed to help you with all your DevOps-related tasks using CLI programs.
Building and deploying applications to the cloud is more challenging than it looks. Multiple steps are involved in deploying an application on the cloud, from configurations and resource allocations to defining workloads. While these may look simple, they can sometimes challenge some of the most adept developers.
That’s where Acorn comes to the rescue. A cloud platform that brings back the simplicity of hosting applications. Acorn makes it super easy to work with applications, configurations, and workload specifications and even share your application with others.
At the heart of this is an Acornfile that helps you define your applications and workloads. In this blog post, we will show you how to write your first Acornfile and share some of the best practices you should follow while authoring Acornfiles for an optimized and efficient workflow.
What a Docker compose file is to Docker, Acornfile is to Acorn. A docker compose file has all the instructions to deploy and execute a complete application - services, images, network, volumes, secrets, etc.
An Acornfile is similar. It tells the Acorn runtime what the application comprises and how to deploy it to Acorn. You can start writing your Acornfile based on a docker file or a docker compose file.
For instance, here’s what an NGINX docker compose file looks like:
version: '3' services: nginx: image: nginx:latest ports: - "80:80"
Below is the corresponding Acornfile:
containers: web: { image: "nginx" ports: publish: "80/http” }
Like a docker compose file, an Acornfile has many fields that help you define your application. Let us see all the fields that make up an Acorn file.
Many fields can be defined in an Acornfile to help you describe how to deploy your applications. Below is a brief overview of each of them:
These Acornfiles also help you build Acorn images that are OCI compliant and contain all the configurations along with Docker images required to run your application. This image can be easily pushed to any container registry and deployed to Acorn.
Read more about authoring Acorn files for a more in-depth understanding.
It’s said the best way to learn something is by doing it, so why not learn more about Acornfiles by creating one?
Before you start with creating an Acornfile, make sure to sign up on Acorn as well as install the Acorn CLI on your system.
The first and foremost step is to build your application. It can be as simple as a “Hello World” app or as complex as a shopping cart.
We’ll create a simple student entry application allowing you to enter and store student details. The front end has a basic form to enter the details built using express.js, while the database is an instance of MariaDB.
You can check out the complete application code in this Git repo. Clone this repository, as we’ll be using it throughout this blog post.
Clone the repo to your local machine. Note that the repo already has an Acornfile. For this blog post, rename it to something else, create a new Acornfile using your favorite editor, and follow along.
The core of an Acornfile is the app container. It defines everything that an application requires to execute. It includes references to the images it uses, services it consumes and depends on, storage volume, the environment variables it needs to run, permission for various resources, etc.
This application is built from the Dockerfile in the
app
# Use the official Node.js image as the base image FROM node:14 # Set the working directory in the container WORKDIR /usr/src/app # Copy package.json and package-lock.json to the working directory COPY package*.json ./ # Install the application dependencies RUN npm install # Copy the entire application code to the working directory COPY . . # Expose the application port (e.g. 3000) EXPOSE 3000 # Define the command to run the application CMD ["npm", "start"]
We will use this Dockerfile as the basis for creating our Acornfile. We will add a new
containers
name: “Student Entry App” containers: { app: { build: "./app" ports: "3000/http" } }
This defines the app container and instructs Acorn to build the image, and the port the app will be available on. With this basic definition the port 3000 is defined to only be accessible by other Acorns running in the same Acorn project.
In order to access the application from our web browser, we will “publish” the port. Publishing instructs Acorn to expose the port through a load balancer so end users can access the application.
Let's update the code to reflect our desired state:
name: “Student Entry App” containers: { app: { build: "./app" ports: publish: "3000/http" } }
Save this as an Acornfile and deploy it to Acorn using
acorn run -n school
If everything is successful, you’ll see Acorn return a URL for the application. When you access the application, you’ll see the form. However, nothing happens when you hit the Submit button. Why?
This is because we’ve not attached a database to our application. Let us see how to do that in the next step.
Our application requires a database to store the students' records. The simplest approach is to use a MariaDB container. From the official Docker hub image, the MariaDB container requires the following environment variables.
MARIADB_ROOT_PASSWORD
Optionally, though recommended, a username, password, and database name.
MARIADB_USER MARIADB_PASSWORD MARIADB_DATABASE
Let's add the MariaDB container to our Acornfile:
name: "Student Entry App" containers: { app: { build: "./app" ports: publish: "3000/http" } mariadb: { image: “maridb:10.11.5” env: { MARIADB_ROOT_PASSWORD: "" MARIADB_USER: "" MARIADB_PASSWORD: "" MARIADB_DATABASE: "" } } }
In the above Acornfile, we have added a ‘mariadb’ container, and in this case, we are using the official Docker image for MariaDB.
When using a pre-built image, it is best to use a specific version vs. the “latest” or other moving tag. The reasoning is that later on, you will want to know which version you are running should something go wrong and you need to troubleshoot.
If you use a moving tag, the version will be baked into the Acorn image, so it will never change when using that Acorn image. Still, determining which version of the software was used at the time of the build later on can provide an extra step during a production outage.
Also, notice the use of the Environment variables in the Acornfile. These values need to contain passwords, so we want to handle them with care that we aren’t passing secret values around in plain text.
The best way to handle passwords is the use of Acorn secrets. The benefit here is that if you just need a password for a development database, as we will in this Acornfile, Acorn will just generate one for you. Let us modify our Acornfile so that secret values are created for our database container. We will use a secret type “basic” for both the user and root password for the image. Adding the secrets, we will get:
name: "Student Entry App" containers: { app: { build: "./app" ports: publish: "3000/http" } mariadb: { image: “maridb:11.2.2” env: { MARIADB_ROOT_PASSWORD: "secret://admin/password" MARIADB_USER: "secret://user/username" MARIADB_PASSWORD: "secret://user/password" MARIADB_DATABASE: "app" } } } secrets: admin: { type: “basic” params: { usernameLength: 11 usernameCharacters: "a-z" passwordCharacters: "A-Za-z0-9^_-" } data: { username: “” password: “” } } secrets: user: { type: “basic” params: { usernameLength: 11 usernameCharacters: "a-z" passwordCharacters: "A-Za-z0-9^_-" } data: { username: “” password: “” } }
Looking at the Acornfile, we have two secrets that house the root and user credentials. When the values for the fields ‘username’ and ‘password’ are left as empty strings
””
If you have credentials for the database already that you would like to use, you can create those before running your Acorn and then tell Acorn to use those credentials when you run. By having these secrets defined, we never have to pass “secret” data to the Acorn in clear text.
So, if we were to start this Acorn, we would see the two containers startup, but the app container is still not configured to talk to the database. We know that the application will need the following variables to connect to the database:
DB_NAME DB_USER DB_PASS DB_HOST
Now, we could do something like:
DB_USER: “secret://user/username”
While this is valid, it would create a tight coupling within this Acorn. Say you wanted to use an external database when you go to production. You would have a tough time updating the DB_HOST settings. To help make this acorn more modular and composable, we will use a service to connect the “app” container to the database. Using a service, we can later use any Acorn that presents the same service by swapping it at runtime.
Let us modify our Acornfile to use a service and wire up our application container to the database.
name: "Student Entry App" services: db: { default: true container: "mariadb" secrets: ["admin", "user"] ports: "3306" data: dbName: “app” } containers: { app: { build: "./app" dependsOn: [“db”] env: { DB_HOST: “@{service.db.address}” DB_USER: “@{service.db.secrets.user.username}” DB_PASS: “@{service.db.secrets.user.password}” DB_NAME: “@{service.db.data.dbName}” ports: publish: "3000/http" } mariadb: { image: “maridb:11.2.2” env: { MARIADB_ROOT_PASSWORD: "secret://root/password" MARIADB_USER: "secret://user/username" MARIADB_PASSWORD: "secret://user/password" MARIADB_DATABASE: "app" } } } secrets: admin: { type: “basic” params: { usernameLength: 11 usernameCharacters: "a-z" passwordCharacters: "A-Za-z0-9^_-" } data: { username: “” password: “” } } secrets: user: { type: “basic” params: { usernameLength: 11 usernameCharacters: "a-z" passwordCharacters: "A-Za-z0-9^_-" } data: { username: “” password: “” } }
At this point, we tell the Acorn runtime to create an Acorn service using the MariaDB container provided, which will be referenced as db. We also added a “consumes: [db]” field to our application. This creates dependency for the app container on the service DB becoming available first.
We use “consumes” instead of “dependsOn” because, in some situations, permissions are passed along with the credentials. If we were only using containers without the service, we could use “dependsOn: [“db”].”
When you start this Acornfile it will run both the Mariadb container and the application container.
However, there will be no data or schema in our new database instance. Let us look at Jobs to see how we can address DB migrations.
Jobs in Acorn are used to perform one-time tasks. These can be configured to execute based on a schedule or some events. Jobs are perfect for tasks like initializing a database, cleaning up files, etc.
In our case, let us add a “jobs” block and create a db-init job to initialize our database.
name: "Student Entry App" services: … jobs: { dbinit: { build: "./db-init" env: { "DB_NAME": "@{service.db.data.dbName}" "DB_USER": "@{service.db.secrets.user.username}" "DB_PASS": "@{service.db.secrets.user.password}" "DB_HOST": "@{service.db.address}" } consumes: ["db"] } } containers: { app: { ...
We’ve added a job to initialize our database using the Dockerfile in the db-init directory. It will create the tables and enter some dummy data. Note that we’ve used the service mechanism to pass the credentials and connection information to the job, just as we have done for the application.
This job will respond to all create, update, and delete events. You can customize this by using the
events: [“create”, “update”, “delete”]
schedule
Now, when you execute the Acorn, you’ll see some dummy data, and you’ll also be able to make an entry to the application.
Congratulations! You’ve successfully created an Acornfile for your application.
You can follow similar steps to create Acornfiles for your application as well. Do remember to refer to the best practices that we’ve shared for each of the fields.
Apart from the best practices that we’ve listed above, there are a few general handy tips that you can use while authoring your Acorn files.
Deploying applications to the cloud is made easier with Acorn. You can get more control over the deployments when you work directly with Acornfiles. By leveraging the best practices listed here, you can write your first Acornfile and optimize your applications just like what we saw in our application.
We invite you to try the Acorn playground to explore and experiment with these best practices. Once comfortable, you can deploy your application to the Acorn sandbox environment or try one of our pre-built applications from our Acorn library. Feel free to contact the Acorn Slack community if you have any issues.