Reactive Containers: How Kitematic works hand-in-hand with the Docker CLI

2PdxbjBKwritten by Jeff Morgan, Software Engineer at Docker, Inc. 


Introduction

Kitematic is a desktop app for Mac & Windows with the purpose of helping new Docker users run their first container in minutes. Kitematic streamlines the install process to setup a local Docker environment and provides a GUI to pull, build and run containers. If you haven’t tried it yet, you can download Kitematic at kitematic.com/download.

A primary goal of Kitematic is to be interoperable with the Docker CLI and provide a seamless experience between the two. For example, a user should be able to start a container by running the command docker run hello-world in the Docker Client and see the result reflected in Kitematic and vice-versa.  Further, our aim is to have Kitematic work out-of-box with other Docker tools such as Docker Compose.

As another example, if we delete a container via the command line, Kitematic should immediately reflect this change:

2015-08-06_22_24_34

 

Application Architecture: React and Flux

Kitematic is written entirely in JavaScript. After considering different architecture designs & frameworks with which to build it, we landed on the following architecture inspired by Facebook’s Flux.  The general architecture of Kitematic is shown in the diagram below:

kitematic_arch

Stores: Sources of truth for data in the application and required logic to manage this data.

Components: Implemented in React, responsible for rendering the data in stores and capturing user input.

Actions: Intermediate objects that are created by interacting with components or via events in the Docker Remote API. Actions are handled by stores to cause changes in the application’s data.

Utils:  These are not a part of a standard Flux implementation, but are used by actions to interact with external services. For example, Kitematic has a DockerUtil object for interacting with the Docker Engine Remote API.

In the case where a container is deleted via the Docker Client, the following happens:

• The Docker Engine produces a destroy event

• This event, in turn, triggers a container destroyed action

• Subscribing to container actions, the ContainerStore, responsible for storing container data, updates itself to remove the destroyed container’s data from its internal list of containers.

• The UI component responsible for listing containers, ContainerList component is re-rendered appropriately.

 

The Code: Propagating Docker Engine Events to Kitematic GUI

Let’s dive into the different sections of the codebase that makes this all work.  We have simplified it a little for this example.

 

1. Triggering Actions from Docker Remote API Events

Using the dockerode library, listening to events from the Docker Remote API is simple. In this case, if an event is received with the status destroyed, an action is created to notify relevant stores to update their internal data:

In DockerUtil.js

dockerodeClient.getEvents((error, stream) => {
      ...
      stream.on('data', json => {.
            // If the container was destroyed, create a destroyed action
            if (data.status === 'destroy') {
                  containerServerActions.destroyed({id: data.id});
            }
            ...
      }); 
});            

 

2. Updating The Container Store

The ContainerStore object implements a handler any destroyed container actions, updating its internal data and notifying all listening components to re-render.

In ContainerStore.js

class ContainerStore {
      constructor () {
            ...
            this.bindActions(containerServerActions);
            this.containers = {};
            ... 
      }
      ...
      destroyed ({lid}) {
            if (this.containers[id]) {
                  delete.containers[id];
                  this.emitChange();
            }
      }
      ...
}             

 

3. Triggering an Update in the ContainerList React Component

The high-level Containers React component in Kitematic listens to updates emitted by the ContainerStore, and re-renders any children components that depend on the container data by passing them the updated list of containers as a React prop. In this case, when ContainerStore emits an update, ContainerList, which is responsible for rendering a simple list of containers, is also rendered with updated container data.

In Containers.react.js

import containerStore from '../stores/ContainerStore';
var Containers = React.createClass({

      componentDidMount: function () {
            containerStore.listen(this.update);
      }

      update: function () {
            this.setState({
                  containers: containerStore.getState().containers,
            });
      }

      render: function () {
            return (
                  ...
                  <ContainerList containers={this.state.containers}
            .../>

                  ...
            );
      }
});

 

and in ContainerList.react.js, one container item is rendered for each container in the list:

var ContainerList = React.createClass({
  ...
  render: function () {
    var containers = this.props.containers.map(container => {
      return (
        <ContainerListItem key={container.Id} container={container} ... />
      );
    });
    return (
      <ul>
        {containers}
      </ul>
    );
  }
});

 

Conclusion

To recap, this post explained how Kitematic makes use of the Docker Remote API, React and Flux to provide real-time updates as changes occur via the Docker CLI or any other tool interactive with the Docker Engine. Download Kitematic today and remember to send us comments and feedback.

Here are some additional resources:

• Kitematic on Github: https://github.com/kitematic/kitematic

• Dockerode on Github: https://github.com/apocas/dockerode

• Alt, a Flux implementation on GitHub: https://github.com/goatslacker/alt

• Flux: https://facebook.github.io/flux/

• React: http://facebook.github.io/react/

• Docker Remote API: https://docs.docker.com/reference/api/docker_remote_api_v1.19/

 


 

 Learn More about Docker


Reactive Containers: How Kitematic works hand-in-hand with the Docker CLI


One Response to “Reactive Containers: How Kitematic works hand-in-hand with the Docker CLI”

  1. Laurent Kempé

    In your code snippet DockerUtil.js you miss the line
    let data = JSON.parse(json);
    to make it understandable

    Reply

Leave a Reply to Laurent Kempé

Click here to cancel reply.

Get the Latest Docker News by Email

Docker Weekly is a newsletter with the latest content on Docker and the agenda for the upcoming weeks.