Permalink
Fetching contributors…
Cannot retrieve contributors at this time
311 lines (238 sloc) 9.22 KB

Simple Project

In this guide, we'll walk you through configuring a simple project to run on Garden. The project will consist of two Dockerized web services that communicate with one another, along with unit and integration tests.

In what follows you'll learn how to:

Before you get started

This tutorial assumes that you have already have a running installation of Garden.

Clone the example repo

The code for this tutorial can be found in our Github repository under the examples directory. We'll use the simple-project-start example and work our way from there. The final version is under simple-project.

First, let's clone the examples repo, change into the directory, and take a look inside:

$ git clone https://github.com/garden-io/garden.git
$ cd garden/examples/simple-project-start
$ tree .
.
└── services
    ├── go-service
    │   ├── Dockerfile
    │   └── webserver
    │       └── main.go
    └── node-service
        ├── Dockerfile
        ├── app.js
        ├── main.js
        ├── package.json
        └── test
            └── integ.js

5 directories, 7 files

As you can see, the project consists of two super simple services and their accompanying Dockerfiles. One of the core tenets of multi-service backends is being able to pick the right tool for the job, and therefore we have a Node.js service and a Go service, that we can pretend have different responsibilities.

The task at hand is to configure these services so that they can run on the Garden framework.

Project-wide configuration

To begin with, every project needs a project-wide garden.yml configuration file at the root level. There we define, among other things, the name of the project, and the providers the project requires.

Let's go ahead and create one:

$ touch garden.yml

and add the following configuration:

project:
  name: simple-project
  environments:
    - name: local
      providers:
        - name: local-kubernetes

Above, we've specified the name of our project and configured it to use the local-kubernetes provider for local development. Note that this file must be located in the project root directory.

Module configuration

Now, let's turn to our services. Services live inside modules, and each module has its own garden.yml configuration file.

We'll start with the module for the node-service:

$ touch services/node-service/garden.yml

and add the following:

module:
  description: Node service container
  type: container

By running the scan command we can see that Garden detects our module config:

$ garden scan
- name: node-service
  type: container
  path: /Users/username/code/simple-project/services/node-service
  description: Node service container
  version:
    versionString: 2c8818986d-1528373640
    latestCommit: 2c8818986d
    dirtyTimestamp: 1528373640

Under the module directive of our services/node-service/garden.yml file we can now specify how to run our service:

module:
  description: Node service container
  type: container
  services:
    - name: node-service
      command: [npm, start]
      ports:
        - name: http
          containerPort: 8080
      ingresses:
        - path: /hello-node
          port: http

The services field is specific to container modules, and defines the services exposed by the module. In this case, our containerized Node.js server. The sub-directives tell Garden how to start the service and which ingress endpoints to expose.

Deploying

With this configuration we're almost ready to deploy. First, we'll need to make sure the environment is ready, by running the init command:

$ garden init

Garden can now deploy our service to a local Kubernetes cluster:

$ garden deploy

To verify that everything is working, we can call the service at the /hello-node ingress defined in /services/node-service/app.js:

$ garden call node-service/hello-node
✔ Sending HTTP GET request to http://simple-project.local.app.garden/hello-node

200 OK

Hello from Node server!

In a similar manner, we create a config file for our go-service with

$ touch services/go-service/garden.yml

and add the following:

module:
  description: Go service container
  type: container
  services:
    - name: go-service
      ports:
        - name: http
          containerPort: 80
      ingresses:
        - path: /hello-go
          port: http

Run the deploy command again, this time only for the go-service:

$ garden deploy go-service

Another way to verify that our services are up and running is to have a look at the service logs. We can either get an aggregate from all our services, by running garden logs, or we can specify a list of services. This time we're only interested in our go-service:

$ garden logs go-service
go-service         → 2018-06-07T12:52:41.075Z → Server running...

Looks good! Let's take stock:

  • We started out with a project consisting of multiple containerized services (really just two, but hey, it's a simple project).
  • We added a project wide configuration at the root level, and a module configuration for each service.
  • We deployed our entire project with the garden deploy command
  • We saw how we could call our services and read their logs with the garden call and garden logs commands.

Inter-service communication

Calling the go-service from the node-service is straightforward from within the application code. Open services/node-service/app.js with your favorite editor and add the following:

const request = require('request-promise')

// Unless configured otherwise, the hostname is simply the service name
const goServiceEndpoint = `http://go-service/hello-go`;

app.get('/call-go-service', (req, res) => {
  // Query the go-service and return the response
  request.get(goServiceEndpoint)
    .then(message => {
      res.json({
        message,
      })
    })
    .catch((err) => {
      res.statusCode = 500
      res.json({
        error: err,
        message: "Unable to reach service at " + goServiceEndpoint,
      })
    })
})

Now let's re-deploy the node-service and try out our new endpoint:

$ garden deploy node-service
$ garden call node-service/call-go-service
✔ Sending HTTP GET request to http://simple-project.local.app.garden/call-go-service

200 OK

{
    "message": "Hello from Go!"
}

Nice!

So far, we've seen how to configure a simple project and it's modules, how to deploy our services, and how these services can communicate. Next, let's take a look at how we can define dependencies and set up testing.

Dependencies

An attentive reader will no doubt have noticed that our node-service depends on the go-service for it's call-go-service endpoint. We can express this in the node-service module configuration by adding dependencies under the services directive:

module:
  description: Node service container
  ...
  services:
    - name: node-service
      command: [npm, start]
      ...
      dependencies:
        - go-service

This will ensure that our go-service will be deployed before the node-service.

Testing

Finally, we'll update our node-service module configuration to tell Garden how to run our tests. Add the following test config under the module directive in services/node-service/garden.yml:

module:
  description: Node service container
  ...
  services:
    - name: node-service
      command: [npm, start]
    ...
  tests:
    - name: unit
      command: [npm, test]
    - name: integ
      command: [npm, run, integ]
      dependencies:
        - go-service

This allows us to run individual test groups by name or all of them at once with the test command:

$ garden test

Notice also that the integration test depends on the go-service being deployed.

The entire module config should now look like this:

module:
  description: Node service container
  type: container
  services:
    - name: node-service
      command: [npm, start]
      ports:
        - name: http
          containerPort: 8080
      ingresses:
        - path: /
          port: http
      dependencies:
        - go-service
  tests:
    - name: unit
      command: [npm, test]
    - name: integ
      command: [npm, run, integ]
      dependencies:
        - go-service

And that's it! Our services are up and running locally, dependencies are resolved, and tests are ready to run.

Check out some of our other Example projects for more of an in-depth look.