Acorn was created and launched by the previous founding team of Rancher on the 3rd of August 2022. I actually got to see the product, demo, and deep dive when it was a private project, big thanks to Darren. Acorn is a framework to make the deployment of applications to Kubernetes seamless and easy. In this article, I will share my first impressions, spin it up on Civo Kubernetes and see the benefits.

The problem statement

Have you felt frustrated while deploying a simple app? We all get that feeling when we are writing the YAML files for deployment, services, persistent volume and other K8s components to actually get a simple app deployed to Kubernetes. Now tools like K3s and services like Civo Kubernetes give you cluster with ease, but the frustration of building, deploying and getting the URL (with ingress or svc) is still a pain. This is where the team at Acorn Labs decided to create something to help solves this and make things simpler for the teams deploying applications to Kubernetes.

What is Acorn?

Acorn is a tool/CLI that you can use to simply deploy your application to Kubernetes for different environments (dev, staging, prod). Here you need to write an Acornfile which takes huge inspiration from Dockerfile and Docker Compose, its syntax is simple (derived from CUE) and there is an Acornfile reference as well in the docs so you don't need to learn CUE lang to write Acornfile.

Sample Acornfile

containers: {
  web: {
      build: "."
      ports: publish: "8080/http"
  }
}

This example shows how to create a container web and build from the current directory with the ports to be published. Acorn Images can actually have multiple Docker images and even the metadata. This artefact can be published to any OCI registry. I will show you a couple of Acornfile examples so that it becomes clear how simple it is to create and use them for application deployment, as well as customize them for your needs.

Acorn Architecture

There is a high-level architecture given in the docs so I will not reinvent the wheel by creating a new one. The below example is taken directly from the docs.

Your Alt Text

Now when you run acorn install against the Kubernetes cluster, you can see that three components will be installed:

  • acorn-api: this is the one that is used to perform all the actions. After the kubernetes apiserver authentication is checked and passed, it goes to acorn-apiserver via the Kubernetes API aggregation layer. When the request comes, acorn-apiserver will perform different operations like it will creates the CRD appinstances.internal.acorn.io on app creation, build image using buildkit and push to external or internal registry depending on the passed arguments.
  • buildkit + Internal registry: it is a service used for image building and has 2 containers.
  • acorn-controller: this is the one actually converting acorn apps to Kubernetes objects.

Cluster creation

As Civo is super fast with <2 minute launch time, I also added Acorn as a marketplace app for installing the server-side components as soon as the project was announced (a couple of hours later to be precise). This means that you need not run acorn install.

I will be using Civo CLI to create the cluster, you can install the cli from here. You can create the cluster from the UI or from the Civo CLI. For this tutorial, let's create using the CLI.

civo k3s create acorn-civo --applications Acorn
The cluster acorn-civo (13188e49-a39c-4bcb-8c93-f3d001c3f361) has been created

The above example will create a 3 node cluster named acorn-civo. Once the cluster is up and running in a couple of minutes, we can proceed.

We will then need to get the Kubeconfig for the cluster and save to our desired location. If you do not specify a path, it will save it to the default location of ~/.kube/config.

civo k3s config acorn-civo --save --local-path /Users/saiyam/Downloads/acorn.config
Merged with main kubernetes config: /Users/saiyam/Downloads/acorn.config

Let's make sure that kubectl knows to use our cluster's configuration file:

export KUBECONFIG=/Users/saiyam/Downloads/acorn.config

kubectl get nodes
NAME                                              STATUS   ROLES    AGE     VERSION
k3s-acorn-civo-b77c-409c89-node-pool-d6a4-hnd5l   Ready    <none>   2m1s    v1.22.11+k3s1
k3s-acorn-civo-b77c-409c89-node-pool-d6a4-hwku7   Ready    <none>   2m25s   v1.22.11+k3s1
k3s-acorn-civo-b77c-409c89-node-pool-d6a4-stiv6   Ready    <none>   2m25s   v1.22.11+k3s1

We can also check if the acorn components are installed

kubectl get all -n acorn-system
NAME                                    READY   STATUS    RESTARTS   AGE
pod/acorn-api-57db7cc74b-wm6tb          1/1     Running   0          2m21s
pod/acorn-controller-6c465fbf65-x2ct8   1/1     Running   0          2m21s

NAME                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/acorn-api   ClusterIP   10.43.241.163   <none>        7443/TCP   2m22s

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/acorn-api          1/1     1            1           2m22s
deployment.apps/acorn-controller   1/1     1            1           2m22s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/acorn-api-57db7cc74b          1         1         1       2m22s
replicaset.apps/acorn-controller-6c465fbf65   1         1         1       2m22s

Acorn in Action

Now would be the time to install the acorn cli locally on your systems. You can do that by running curl https://get.acorn.io | sh

[INFO]  Finding release for channel latest
[INFO]  Using v0.1.0 as release
[INFO]  Downloading hash https://github.com/acorn-io/acorn/releases/download/v0.1.0/checksums.txt
[INFO]  Downloading archive https://github.com/acorn-io/acorn/releases/download/v0.1.0/acorn-v0.1.0-macOS-universal.tar.gz
[INFO]  Verifying binary download
[INFO]  Installing acorn to /usr/local/bin/acorn
acorn -v
acorn version v0.1.0+80d6e93d

After installing acorn successfully n your system, clone this repository and cd into it.

git clone https://github.com/saiyam1814/acorn-demo.git
cd acorn-demo

Now, this has the source code and a Dockerfile to run a simple flask app and in the Acornfile we are telling it to run a web container and build the Dockerfile present in the current directory.

containers: {
  web: {
      build: "."
      ports: publish: "8080/http"
  }
}

Now we run the acorn build

acorn build . 
acorn build .
Waiting for builder to start... Ready

it creates the third pod buildkit with two containers as explained in the architecture. buildkitd-5f77fb596c-q86r5 2/2 Running 0 4m46s

Once its ready it starts building the image from the Dockerfile and pushes it to the internal registry.

acorn build .
[+] Building 2.3s (7/7) FINISHED                                                        
 => [internal] load .dockerignore                                                  0.4s
 => => transferring context: 2B                                                    0.3s
 => [internal] load build definition from Dockerfile                               0.5s
 => => transferring dockerfile: 131B                                               0.5s
 => [internal] load metadata for docker.io/tiangolo/uwsgi-nginx-flask:latest       0.7s
 => [internal] load build context                                                  0.7s
 => => transferring context: 39.41kB                                               0.7s
 => [1/2] FROM docker.io/tiangolo/uwsgi-nginx-flask:latest@sha256:d588b419b8c6922  0.0s
 => => resolve docker.io/tiangolo/uwsgi-nginx-flask:latest@sha256:d588b419b8c6922  0.0s
 => CACHED [2/2] COPY . ./                                                         0.0s
 => exporting to image                                                             0.3s
 => => exporting layers                                                            0.0s
 => => exporting manifest sha256:818583281395c9c16206151960414efee95ff6a6aea6fa99  0.0s
 => => exporting config sha256:06801160ae6868056b39b78d6e11b460b15fe92601a4d475e3  0.0s
 => => pushing layers                                                              0.2s
 => => pushing manifest for 127.0.0.1:5000/acorn/acorn:latest@sha256:818583281395  0.0s
[+] Building 1.4s (5/5) FINISHED                                                        
 => [internal] load .dockerignore                                                  0.5s
 => => transferring context: 64B                                                   0.5s
 => [internal] load build definition from Dockerfile                               0.5s
 => => transferring dockerfile: 58B                                                0.5s
 => [internal] load build context                                                  0.5s
 => => transferring context: 262B                                                  0.5s
 => CACHED [1/1] COPY . /                                                          0.0s
 => exporting to image                                                             0.1s
 => => exporting layers                                                            0.0s
 => => exporting manifest sha256:891231607fff23bfdf6ddd1da12d8d32e8259e0fc0036004  0.0s
 => => exporting config sha256:8b606335d8ca7c36bafb77260bbbc3abe9cc8051c571533acf  0.0s
 => => pushing layers                                                              0.0s
 => => pushing manifest for 127.0.0.1:5000/acorn/acorn:latest@sha256:891231607fff  0.0s
4425dd246abcc566dfaca7c81e29acb7aafb6838b67bc617680c2efa332cc272

Now let's run the app

acorn run 4425dd246abcc566dfaca7c81e29acb7aafb6838b67bc617680c2efa332cc272
small-cloud
acorn apps
NAME          IMAGE          HEALTHY   UP-TO-DATE   CREATED   ENDPOINTS                      MESSAGE
small-cloud   4425dd246abc   1         1            21s ago   http://<pending> => web:8080   OK

This should be populated with the ingress created (if not then you can check ing)

kubectl get ing -A
NAMESPACE                  NAME   CLASS    HOSTS                                                               ADDRESS   PORTS   AGE
small-cloud-b67bd297-fb3   web    <none>   web.small-cloud.13188e49-a39c-4bcb-8c93-f3d001c3f361.k8s.civo.com             80      9m26s

Now when you open the URL in the host, boom!!!...you will see your application running! How cool and fancy is that? You didn't have to create any Kubernetes resources and everything was created automatically for you.

Your Alt Text

Let us try another example, this time from a different Acornfile which is present in the same repo.

containers: {
    frontend: {
        image: "nginx"
        ports: publish: "80/tcp"
        dirs: {
            "/var/www/html": "volume://web-content"
        }
        // ...
    }
}
// ...
volumes: {
    "web-content": {}
}

The above is basically to show that you can create persistent volume as well using Acornfile. Adding tcp instead of http will create a service type LoadBalancer.

acorn run -f Acornfile2    
[+] Building 5.8s (5/5) FINISHED                                                                    
 => [internal] load build definition from acorn-dockerfile-302118934                           0.6s
 => => transferring dockerfile: 64B                                                            0.5s
 => [internal] load .dockerignore                                                              0.4s
 => => transferring context: 2B                                                                0.4s
 => [internal] load metadata for docker.io/library/nginx:latest                                2.8s
 => [1/1] FROM docker.io/library/nginx@sha256:ecc068890de55a75f1a32cc8063e79f90f0b043d70c5fcf  1.6s
 => => resolve docker.io/library/nginx@sha256:ecc068890de55a75f1a32cc8063e79f90f0b043d70c5fcf  0.0s
 => => sha256:b1349eea8fc5b5eebb633c2cd79fc24a915fcb00279de24684bb07e349e8eab 1.39kB / 1.39kB  0.1s
 => => sha256:27e0d286aeab484653fdf0f736d5f7a2fbcc572e387ec8a1d6ccf0e74b6bfefc 664B / 664B     0.1s
 => => sha256:6a17c8e7063d97ef72e89f7d4673935ff9a2b7c179bea1726852399219118f65 891B / 891B     0.2s
 => => sha256:05396a986fd3f3739cc890e30a2ed78e377c6a2b24d9f0ebe99ff3349aedc603 603B / 603B     0.2s
 => => sha256:1efc276f4ff952c055dea726cfc96ec6a4fdb8b62d9eed816bd2b788f2860 31.37MB / 31.37MB  0.6s
 => => sha256:baf2da91597d101646b307b706d06b048862192b127f74b1079d374d902e3 25.35MB / 25.35MB  0.8s
 => exporting to image                                                                         1.9s
 => => exporting layers                                                                        0.0s
 => => exporting manifest sha256:77c9bb357067366f2629112b4af9e008bd9ff1ebcaa97f99420b2dfbc626  0.0s
 => => exporting config sha256:f53af215ede0ee11e25dcfdd813625fa1564d3053721493a1cc5553317c876  0.0s
 => => pushing layers                                                                          1.8s
 => => pushing manifest for 127.0.0.1:5000/acorn/acorn:latest@sha256:77c9bb357067366f2629112b  0.0s
[+] Building 1.7s (5/5) FINISHED                                                                    
 => [internal] load build definition from Dockerfile                                           0.6s
 => => transferring dockerfile: 58B                                                            0.5s
 => [internal] load .dockerignore                                                              0.5s
 => => transferring context: 64B                                                               0.5s
 => [internal] load build context                                                              0.5s
 => => transferring context: 418B                                                              0.5s
 => [1/1] COPY . /                                                                             0.1s
 => exporting to image                                                                         0.3s
 => => exporting layers                                                                        0.1s
 => => exporting manifest sha256:012dc34ea34ee765cb040392ffa1a15ac13dee9d09c4311c3026482b49aa  0.0s
 => => exporting config sha256:e3a86ddb9208a5d2b9868b7508490bbdb7293cc1f8a31c83b32d123e04ce9b  0.0s
 => => pushing layers                                                                          0.1s
 => => pushing manifest for 127.0.0.1:5000/acorn/acorn:latest@sha256:012dc34ea34ee765cb040392  0.0s
wandering-tree
acorn apps list 
acorn apps
NAME             IMAGE          HEALTHY   UP-TO-DATE   CREATED    ENDPOINTS                                                                  MESSAGE
wandering-tree   3a6183bacf50   1         1            102s ago   tcp://534320fc-56cf-4692-931d-cabcd9daa36b.lb.civo.com:80 => frontend:80   OK

You can see the PV/PVC created using civo-volume storage class

kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                     STORAGECLASS   REASON   AGE
pvc-a86abaa1-86cd-4893-b2fe-755703b9cec4   10Gi       RWO            Retain           Bound    wandering-tree-7d2bbad7-59d/web-content   civo-volume             23m

kubectl get pvc -A
NAMESPACE                     NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
wandering-tree-7d2bbad7-59d   web-content   Bound    pvc-a86abaa1-86cd-4893-b2fe-755703b9cec4   10Gi       RWO            civo-volume    23m
kubectl get svc -A | grep LoadBalancer

wandering-tree-7d2bbad7-59d   frontend-publish-7d2bbad7-59d   LoadBalancer   10.43.131.13      80:30072/TCP             6m59s

Your Alt Text

Again you can see how easy it is to deploy your application to the Kubernetes cluster.

Conclusion

Overall the first impressions are really good, where acorn is trying to solve the problem of deploying the apps across environments easily. Right now the apps do not do much and there are endpoint issues where sometimes it doesn't get populated, which I am sure is in the works already (I reported one issue on DNS as well). It would be good if acorn apps can fetch the logs, and can add a debug container in future. I would also like to see a GitOps approach, where you can connect to a repo and deploy directly from git. Something like an acorn monitor for the health of apps and then a fancy dashboard where I can monitor acorn, its components and apps. I will be keeping an eye on this project as I love the people behind it and will keep you all updated with the latest acorn features.