Garden
I’ve been playing with a lot of development work flow tools over the last few weeks. I was an early adopter of Docker and have always been passionate about reducing friction in the development process, especially locally. Quick performance wins for individual developers add up to substantial productivity improvements across a whole team. I’ve been interested in how these tools are evolving, as we expand beyond the basic tools Docker provided with containers. As an outcome of this exploration I’m going to write a few posts on these tools.
The posts:
Garden
Garden …
orchestrates your development work flows to make developing microservices and functions both faster and easier.
Garden manages your app from source code, through development, testing, and then make deployment repeatable and consistent. With Garden you can model your services locally, test them, and then deploy them to production (or the next stop on their journey - development, testing, QA, etc). This not only allows you to model what’s happening locally, with Garden you develop with a “hot reload” capability, but it also allows you to test services without needing to locally mock or stub them out. Instead of discovering a bug in CI because of an integration issue, find it locally first and fix it before your CI run.
Garden architecture
Garden is graph-based, each component of your application is defined independently and can be explictly or implictly dependent on other components. This allows you to locally model applications as they appear in production. This also means that when you change something the graph ensures only what needs to be changed will be, reducing the time needed to build or re-deploy applications.
The root of the graph is a project. Projects contain providers and modules. Providers are the deployment environments for Garden, for example a local Kubernetes cluster. They provide the “how” of implementing your project. Modules contain your services and two other primitives: tasks and tests. Each modules has a type, like a container or a Helm chart. Each type of module can contain zero or more services, the applications you want to write. You can build dependencies between these services that will be reflected in your graph.
There are two other primitives: tasks and tests. Tasks are activities you can run and wait for them to finish. Tests are either unit tests for your specific services or integration tests that you can make aware of Garden’s graph to allow you to do inter-service integration testing. Both tasks and tests can also be dependent on services or other primitives.
Let’s install Garden locally and run it.
Installing Garden
We’re going to install on macOS but the documentation has installation instructions for Linux and Windows too. We will need to install Docker and a local Kubernetes environment (you could also use an external Kubernetes cluster). One of the easiest ways to do this locally is to use Docker for Mac, which installs both. You could also install something like Minikube.
Install one or the other and initiate a local Kubernetes cluster, either enabling it in Docker for Mac, or initiating and building a Minikube cluster.
Then we can install Garden itself is via Homebrew. Garden has its own tap you can add and a recipe called garden-cli
.
|
|
You can later upgrade Garden with brew update; brew upgrade garden-cli
.
We can now confirm Garden is working.
|
|
Building a Garden project
To test Garden, let’s create our first project. We’re going to use a Garden example we’ve pre-built to take you through the process. You can find it on Github.
Let’s checkout our project.
|
|
Here we’ve checked out the project, changed into the resulting directory, and listed its contents. We have a README for the project, a garden.yml
file, and a directory called modules
. You’ll see several garden.yml
files in our project, each defining some element of the project. The top-level garden.yml
file is the heart of our project. It contains the definition of it. Let’s look at it now.
|
|
We can see our project is named garden-example
and we’ve specified an environment. Environments can be things like development, testing, etc. Ours is called local
. It contains a single provider: local-kubernetes
. Which indicates to Garden that we’ll run any services in the environment in a local Kubernetes cluster. Other providers include external Kubernetes clusters and clusters running on Cloud platforms like Azure, AWS, or GCP.
We’ve also created a directory called modules
to hold our modules. Let’s look inside.
|
|
We can see two modules, one called yo
and one called mate
. Let’s look at the yo
module.
|
|
We can see a Dockerfile
, another garden.yml
file, and a directory called yoservice
, that contains the source code for a service in this module. The Dockerfile
is a standard Docker Dockerfile
and tells Garden how to build our service.
|
|
The real magic is inside our second garden.yml
file, which defines the configuration for our yo
module.
|
|
This garden.yml
file contains our yo
module configuration. We give the module a name, yo
, and a description. We also give it a type, this is going to tell Garden how to handle and deploy the module. In our case, our type is container
, a Docker container. You can also create a helm
chart or an exec
module, which allows you to run commands locally.
We then define any services inside the module. Services are the individual components that Garden deploys. We’ve defined one service: yo-service
. The service definition will look pretty familiar, it’s very similar to a Kubernetes service definition. We expose port 8080
as port 80
, define a HTTP GET health check on the path /yo
on our HTTP port. We also define an ingress to allow access to that path and port.
Lastly, we define a dependency. Garden service dependencies specify other services or tasks that this service depends on, i.e. things it’ll start or run first. You can also create dependencies between modules, allowing you to create whole sets of services that are dependent on one another.
When we deploy this module with Garden, it’ll first build the mate-service
and then the yo
service inside this module. To build the service, it will create a Docker image, using our Dockerfile
, for the yo
service and then deploy it to our local Kubernetes cluster.
We’ll skip over looking at the actual source code of the yo
service, you can find it here on Github, it’s a very simple Golang web service. Instead let’s quickly look at the mate
module and its service. If we change into the mate
directory we’ll find another garden.yml
file and a matching Dockerfile
.
|
|
This configuration is near identical to our yo
module, barring it’s not dependent on anything else. The underlying service is written in Ruby (also available on Github) and is exposed via HTTP on the path /mate
.
Now we have our project, modules, and services defined, let’s see about deploying it.
Running Garden
We’ve assumed you’ve installed and got running some local Kubernetes cluster, via Docker or Minikube, etc. We then tell Garden to initialize our environment to ready it for deployment. We do this using the garden init
command.
|
|
During this process, Garden creates the scaffolding neded to run our project on top of our local Kubernetes cluster. Garden will be deploying our modules and services into this scaffold.
Now, again from the root of our project, we can then deploy our modules and services using the garden deploy
command. There’s two stages to a Garden deploy, the first builds everything in our modules and the second deploys what we’ve built. Let’s build and deploy now.
|
|
garden build
command.We can see that we’ve built and deployed our mate
and yo
modules and the services within them. Each has returned a local URL that they are deployed on. Let’s visit the yo
’s services URL: http://garden-example.local.app.garden/yo
.
Cool. We can do the same with the mate
service, let’s do that via curl
.
|
|
Developing with Garden
Now we have Garden running our services, we can develop them further, interactively in much the same way you can build and test a static site with Hugo or Jekyll. To do this we start the Garden development server.
|
|
This launches a developer console and API server, and then that waits for changes to our code.
We can also see the console in our browser.
The console shows us our running services and the Garden Stack graph.
If we make a change to our project, let’s say add another endpoint to one of our services, we’ll see Garden rebuilding and redeploying our service. Let’s add a /folks
endpoint to our mate
service. We’ll add code to our service,
|
|
And then a new ingress in the garden.yml
file for the module.
|
|
Once we’ve saved this code take a look at the Garden dev console we’ve started on the command line and you’ll see that the service has been rebuilt and redeployed.
|
|
If we now query, we’ll see our new endpoint.
|
|
Woot! Local services development, updated real time.
Summary
I hope this was useful as a walk through. This just scratches the surface of Garden’s capabilities, it’s also possible to use Garden with shared, external dev/test environments, making use of private namespaces, and integrate it with your whole CI life cycle. You can explore this, and other uses cases in the Garden documentation.