Accelerate your local development environment with Tilt

Elton Minetto - Sep 1 '22 - - Dev Community

We spend hours and hours developing applications on our machines, with more and more requirements and complexity. In addition, any modern application has multiple containers, microservices, deployments in different environments, various stacks, etc. So any tool that can make our flow more agile is handy.

In this post, I want to introduce a powerful tool that can save you a lot of time in your development process. This is Tilt, which was recently acquired by Docker.

To demonstrate a little bit of what you can do with Tilt, I will use this repository I used in a talk about microservices (in Portuguese). The examples will be made in Go, but in the official documentation, you can see how to use it with other technologies and scenarios.

Installation

The first step is to install the application from the command line. For that, on my macOS, I ran:

curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
Enter fullscreen mode Exit fullscreen mode

The documentation shows how to install it on other operating systems.

First steps

Tilt works by reading a file called Tiltfile at the root of your project. It has a syntax resembling Python, and the documentation is very detailed, showing all the options we can use.

The contents of the Tiltfile file looked like this:

local_resource('auth', cmd='cd auth; go build -o bin/auth main.go',
               serve_cmd='auth/bin/auth', deps=['auth/main.go', 'auth/security', 'auth/user', 'pkg'])

local_resource('feedbacks', cmd='cd feedbacks; go build -o bin/feedbacks main.go',
               serve_cmd='feedbacks/bin/feedbacks', deps=['feedbacks/main.go', 'feedbacks/feedback', 'pkg'])


local_resource('votes', cmd='cd votes; go build -o bin/votes main.go',
               serve_cmd='votes/bin/votes', deps=['votes/main.go', 'votes/vote', 'pkg'])

Enter fullscreen mode Exit fullscreen mode

The local_resource function configures actions that will be executed on your local machine, and the first parameter is the name we are giving the resource, which must be unique within the Tiltfile.

The cmd parameter contains the command to be executed. The information contained within the serve_cmd parameter will be performed by Tilt and is expected not to terminate. That is, it is the command that will run our service.

The last parameter, deps, is one of the most interesting. It indicates which project directories Tilt will watch; if changes are made, it will automatically run the process. So, for example, if any changes happen to auth/main.go, auth/security, auth/user, or pkg, the auth service will be recompiled and run again. As it is a compiled language like Go, this is a great help because changing the file will automatically generate it, saving us developers precious time.

As our project consists of three microservices, the rest of the Tiltfile configures the same behavior for all.

To run Tilt, just open a terminal and type:

tilt up
Enter fullscreen mode Exit fullscreen mode

The following is presented:

tilt

Pressing the spacebar brings us to Tilt's graphical interface, where we'll spend a lot of time:

tilt

We can check each application's compilation log on this interface and execute the desired step again. It also aggregates the application logs and allows us to perform searches on them:

tilt

Compilation errors also appear on this screen:

tilt

These features alone that I've presented so far should be enough to put Tilt on your list of tools to test, right? But let's delve a little deeper.

Containers

Let's now improve our environment. Instead of running the binaries locally, we will add the ability to automatically create and update containers for our microservices. After all, they should run this way in the production environment.

The new version of Tiltfile looks like this:

local_resource(
    'auth-compile',
    cmd='cd auth; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/auth main.go',
    deps=['auth/main.go', 'auth/security', 'auth/user', 'pkg'],
)

docker_build(
    'auth-image',
    './auth',
    dockerfile='auth/Dockerfile',
)

local_resource(
    'feedbacks-compile',
    cmd='cd feedbacks; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/feedbacks main.go',
    deps=['feedbacks/main.go', 'feedbacks/feedback', 'pkg'],
)

docker_build(
    'feedbacks-image',
    './feedbacks',
    dockerfile='feedbacks/Dockerfile',
)

local_resource(
    'votes-compile',
    cmd='cd votes; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/votes main.go',
    deps=['votes/main.go', 'votes/vote', 'pkg'],
)

docker_build(
    'votes-image',
    './votes',
    dockerfile='votes/Dockerfile',
)

docker_compose('./docker-compose.yml')
Enter fullscreen mode Exit fullscreen mode

I added the docker_build function. As the name suggests, it generates the container image. For that, creating a Dockerfile was necessary for each microservice. For example, the one for the auth service looks like this:

FROM alpine
ADD bin/auth /
EXPOSE 8081
CMD ["/auth"]
Enter fullscreen mode Exit fullscreen mode

The other services were very similar, just changing the name of the executable and the port: feedbacks runs on port 8082 and votes on 8083.

When making this change, Tilt will warn that it is necessary to have some way of deploying containers; otherwise, it will not work. One way to do this is to create a docker-compose.yml and use it in the docker_compose function. Your content looks like this:

version: "3"
services:
  auth:
    image: auth-image
    ports:
      - "8081:8081"
    container_name: auth
  feedbacks:
    image: feedbacks-image
    ports:
      - "8082:8082"
    container_name: feedbacks
  votes:
    image: votes-image
    ports:
      - "8083:8083"
    container_name: votes
Enter fullscreen mode Exit fullscreen mode

With these changes, Tilt now observes modifications in the project's codes, and if they happen, it does the compilation, generation of containers, and updating of the environment!

tilt

Kubernetes!!

Now let's make it a little more serious! Let's have Tilt deploy our application to a Kubernetes cluster. For this, I will use minikube, a solution that installs a local environment for development.

On macOS, just run:

brew install minikube
minikube start
Enter fullscreen mode Exit fullscreen mode

Now that we have our cluster set up, let's change our Tiltfile to reflect the new environment:

load('ext://restart_process', 'docker_build_with_restart')
local_resource(
    'auth-compile',
    cmd='cd auth; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/auth main.go',
    deps=['auth/main.go', 'auth/security', 'auth/user', 'pkg'],
)

docker_build_with_restart(
    'auth-image',
    './auth',
    dockerfile='auth/Dockerfile',
    entrypoint=['/auth'],
    live_update=[
        sync('./auth/bin/auth', '/auth'),
    ],
)

k8s_yaml('auth/kubernetes.yaml')
k8s_resource('ms-auth', port_forwards=8081,
             resource_deps=['auth-compile'])


local_resource(
    'feedbacks-compile',
    cmd='cd feedbacks; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/feedbacks main.go',
    deps=['feedbacks/main.go', 'feedbacks/feedback', 'pkg'],
)

docker_build_with_restart(
    'feedbacks-image',
    './feedbacks',
    dockerfile='feedbacks/Dockerfile',
    entrypoint=['/feedbacks'],
    live_update=[
        sync('./feedbacks/bin/feedbacks', '/feedbacks'),
    ],
)

k8s_yaml('feedbacks/kubernetes.yaml')
k8s_resource('ms-feedbacks', port_forwards=8082,
             resource_deps=['feedbacks-compile'])


local_resource(
    'votes-compile',
    cmd='cd votes; CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/votes main.go',
    deps=['votes/main.go', 'votes/vote', 'pkg'],
)

docker_build_with_restart(
    'votes-image',
    './votes',
    dockerfile='votes/Dockerfile',
    entrypoint=['/votes'],
    live_update=[
        sync('./votes/bin/votes', '/votes'),
    ],
)

k8s_yaml('votes/kubernetes.yaml')
k8s_resource('ms-votes', port_forwards=8083,
             resource_deps=['votes-compile'])

Enter fullscreen mode Exit fullscreen mode

There are a lot of new things where!

The first is the load function which loads Tilt extensions. It's a way to expand the tool's features, and several are available. Here we are using docker_build_with_restart, which will update the container running inside our Kubernetes cluster.

Another change is related to the application deployment settings within Kubernetes. The k8s_yaml function indicates which file contains the "recipe" used for the deployment. And the k8s_resource function is used here to forward the cluster port to our local environment, making testing more accessible.

The content of the auth/kubernetes.yaml file is:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: ms-auth
  name: ms-auth
spec:
  ports:
    - port: 8081
      name: http
      protocol: TCP
      targetPort: 8081
  selector:
    app: ms-auth
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ms-auth
  labels:
    app: ms-auth
spec:
  selector:
    matchLabels:
      app: ms-auth
  template:
    metadata:
      labels:
        app: ms-auth
    spec:
      containers:
        - name: ms-auth
          image: auth-image
          ports:
            - containerPort: 8081
Enter fullscreen mode Exit fullscreen mode

The other files are practically the same, just changing the name of the binary and the port.

Now Tilt does all the heavy lifting for us:

tilt

To check if our microservices are running on the cluster, we can use the command:

kubectl get pods -n default
NAME                          READY   STATUS    RESTARTS   AGE
ms-auth-7446897869-89r2j      1/1     Running   0          81s
ms-feedbacks-b5df67d6-wzbj2   1/1     Running   0          81s
ms-votes-76565ddc9c-nkkt7     1/1     Running   0          81s
Enter fullscreen mode Exit fullscreen mode

Conclusions

I don't know if I managed to demonstrate how excited I am about this tool!

I've used Tilt for a few weeks on a complex project, creating a Kubernetes Controller. Thanks to all this automation, I can focus on the application logic while the rest is done automatically. And that saves a lot of time.

Thanks to my colleague Felipe Paes de Oliveira, who introduced me to this fantastic tool. And if you want to see Tilt being demonstrated by the fabulous Ellen Korbes, who works at Tilt, check out this video.

Originally published at https://eltonminetto.dev on August 31, 2022.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .