NAV Navbar

Quilt

Quilt enables anyone to deploy complex applications to the cloud by encoding expertise about how to run those applications in JavaScript blueprints.

This site describes how to use Quilt. It describes how to install Quilt to run a blueprint, and also how to write your own blueprint for a new application.

We hope you will find this documentation to be a helpful guide to using Quilt. If you run into any issues using Quilt, don’t hesitate to contact us. If you notice issues in the documentation, please submit a Github issue, or feel free to fix it and submit a pull request!

Getting Started

How Quilt Works

This section describes what happens when you run an application using Quilt; feel free to skip this section and head straight to Installing Quilt if you’d like to quickly get up and running with Quilt.

The key idea behind Quilt is a blueprint: a blueprint describes every aspect of running a particular application in the cloud, and is written in JavaScript. Quilt blueprints exist for many common applications. Using Quilt, you can run one of those applications by executing just two commands on your laptop:

Quilt Diagram

The first command,quilt daemon, starts a long-running process that handles launching machines in the cloud (on your choice of cloud provider), configuring those machines, and managing them (e.g., detecting when they’ve failed so need to be re-started). The quilt daemon command starts the daemon, but doesn’t yet launch any machines. To launch an application, call quilt run with a JavaScript blueprint (in this example, the blueprint is called my_app.js). The run command passes the parsed blueprint to the daemon, and the daemon sets up the infrastructure described in the blueprint.

Quilt runs applications using Docker containers. You can think of a container as being like a process: as a coarse rule-of-thumb, anything that you’d launch as its own process should have it’s own container with Quilt. While containers are lightweight (like processes), they each have their own environment (including their own filesystem and their own software installed) and are isolated from other containers running on the same machine (unlike processes). If you’ve never used containers before, it may be helpful to review the Docker getting started guide.

In this example, my_app.js described an application made up of three containers, and it described a cluster with one master machine and two worker machines. The master is responsible for managing the worker machines, and no application containers run on the master. The application containers are run on the workers; in this case, Quilt ran two containers on one worker machine and one container on the other.

Installing Quilt

Quilt relies on Node.js. Start by downloading Node.js from the Node.js download page. We have only tested Quilt with Node version v7.10.0 and above.

Next, use Node.js’s package manager, npm, to install Quilt:

$ npm install -g @quilt/install

Note that if installing as root, the --unsafe-perm flag is required:

$ sudo npm install -g @quilt/install --unsafe-perm

To check that this worked, try launching the Quilt daemon. This is a long-running process, so it will not return (you’ll need to use a new shell window to edit and run blueprints).

$ quilt daemon

Configure A Cloud Provider

In order to run any applications with Quilt, you’ll need to setup a cloud provider that Quilt will use to launch machines. Quilt currently supports Amazon EC2, Digital Ocean, and Google Compute Engine; support for running locally with Vagrant is currently experimental. Contact us if you’re interested in a cloud provider that we don’t yet support.

Below, we describe how to setup Amazon EC2; refer to the Cloud Providers section if you’d like to setup a different cloud provider.

Amazon EC2

For Amazon EC2, you’ll first need to create an account with Amazon Web Services and then find your access credentials from the Security Credentials page in the AWS Management Console. Once you’ve done that, put your Amazon credentials in a file called ~/.aws/credentials:

[default]
aws_access_key_id = <YOUR_ID>
aws_secret_access_key = <YOUR_SECRET_KEY>

The file needs to appear exactly as above (including the [default] at the top), except with <YOUR_ID> and <YOUR_SECRET_KEY> filled in appropriately.

Running Your First Quilt Blueprint

This section will walk you through using Quilt to run Nginx, which is an open-source HTTP server that. In the example, we’ll use Nginx to serve a simple webpage. Start by downloading the blueprint using git:

$ git clone https://github.com/quilt/nginx.git

The blueprint is the main.js file in the nginx directory; take a look at this file if you’d like an to see an example of what blueprints look like. This blueprint will start one master and one worker machine on Amazon AWS, using t2.micro instances (which are in Amazon’s free tier, meaning that you can run them for a few hours for free if you’re a new Amazon user). Recall from How Quilt Works that the master is responsible for managing the worker machines, and worker machines are used to run application containers. In this case, the worker machine will serve the webpage in index.html.

Note that if you decided above to setup a different cloud provider, you’ll need to update the Machine in main.js to use the corresponding cloud provider (e.g., by changing "Amazon" to "Google").

Before running anything, you’ll need to download the JavaScript dependencies of the blueprint. The Nginx blueprint depends on the @quilt/quilt module; more complicated blueprints may have other dependencies that need to be installed. Use npm, the Node.js package manager, to install all dependencies in the nginx folder:

$ npm install .

To run a blueprint, you first need to have a Quilt daemon running. If you haven’t already started one, open a new terminal window and start it:

$ quilt daemon

The daemon is a long running process that periodically prints some log messages. Leave this running, and use a new terminal window to run the blueprint:

$ quilt run ./main.js

This command tells the daemon to launch the machines and containers described in main.js. It will return immediately, but if you return to the daemon window, you’ll see some things starting to happen. The best way to see what’s happening is to return to the window where you typed quilt run, and now use Quilt’s ps command (ps stands for “processes”, and lists everything that’s running in Quilt):

$ quilt ps
MACHINE         ROLE      PROVIDER    REGION       SIZE     PUBLIC IP    STATUS e5b1839d2bea    Master    Amazon      us-west-1    t2.micro              disconnected
e2401c348c78    Worker    Amazon      us-west-1    t2.micro              disconnected

Your output will look similar to the output above. This output means that Quilt has launched two machines, one as a master and one as a worker, in Amazon. Both machines are disconnected, because they’re still being initialized. When a machine is fully booted and configured, it will be marked as connected. Launching machines on AWS takes a few minutes, and eventually the output of ps will look like:

$ quilt ps
MACHINE         ROLE      PROVIDER    REGION       SIZE        PUBLIC IP         STATUS
e5b1839d2bea    Master    Amazon      us-west-1    t2.micro    54.183.98.15      connected
e2401c348c78    Worker    Amazon      us-west-1    t2.micro    54.241.251.192    connected

CONTAINER       MACHINE         COMMAND        LABELS           STATUS     CREATED               PUBLIC IP
bd681b3d3af7    e2401c348c78    nginx:1.13     nginx_example    running    About a minute ago    54.241.251.192:80

The bottom row lists the container that’s running nginx. The nginx deployment is relatively simple and has just one container, but a typical application running in Quilt will have many containers running (one for each part of the application; for example, your website application might require a second container that runs a database). The last column in that row, PUBLIC IP, says the address you can use to access your website.

By default, Quilt-managed containers are disconnected from the public internet and isolated from one another. This helps to keep your application secure by preventing all access except for what you explicitly specify. In order to make the Nginx container accessible from the public internet, nginx/main.js explicitly opens port 80 on the Nginx service to the outside world:

webTier.allowFrom(publicInternet, 80);

This means you can access the webpage you launched by copy-pasting the IP address from quilt ps into a browser window. A site with “Hello, world!” text should appear.

Once you’ve launched a container, you’ll often need to login to change something or debug an issue. The quilt ssh command makes this easy. Use the container ID in the quilt show output as the argument to quilt ssh to login to that container. For instance, to ssh into a container or VM whose ID starts with bd68:

$ quilt ssh bd68

Note that you don’t need to type the whole ID; as long as you use a unique subset of it, Quilt will log in to the correct machine.

You may later decide that you’d like to change the contents of the simple website. You could do this by logging into the container, but for the sake of example, let’s do it using Quilt. On your laptop, open the nginx/index.html that was downloaded from github and change the contents of the page (e.g., you can change the “Hello, World!” message). Now, with the daemon still running, re-deploy the webpage with Quilt:

$ quilt run ./main.js

Quilt automatically detects the changes to your deployment, and will update the cluster to implement your changes. Note that we didn’t need to tell Quilt to stop the nginx container and start a new one; we just updated the view of what the deployment should look like (in this case, by changing index.html), and Quilt automatically detects this and updates the cluster accordingly. Quilt will prompt you to accept the changes that you’re making to your deployment; type y. If you run quilt ps, you’ll notice that Quilt has stopped the old container and is starting a new one. If you navigate to the new IP address, you’ll notice your new page is up.

When you’re done experimenting with Quilt, make sure to stop the machines you’ve started!. Otherwise, they will continue running on Amazon and you will be charged for the unused time. You can stop everything with Quilt’s stop command:

$ quilt stop

You can use quilt ps to ensure nothing is still running. At this point, you can kill the Quilt daemon.

Blueprint Writers Guide

The previous section described how to use Quilt to run an application that already had a blueprint. This guide describes how to write the Quilt blueprint for a new application, using the lobste.rs application as an example. lobste.rs is an open source project that implements a reddit-like web page, where users can post content and vote up or down other content.

Decomposing the application into containers

The first question you should ask yourself is “how should this application be decomposed into different containers?” Be sure you’ve read the How Quilt Works section, which gives a brief overview of containers. If you’ve already figured out the containers that are needed for your application (e.g., if you’re already using Docker), you can skip the rest of this section.

Specifying the containers for your application

As an example of how to specify the containers for your application, let’s use the lobste.rs example. lobste.rs requires mysql to run, so we’ll use one container for mysql. We’ll use a second container for the lobste.rs program to run in.

For each container that your application uses, you’ll need a container image. The container image describes the filesystem that will be on the container when it’s started. For mysql, for exampe, the container image includes all of the dependencies that mysql needs to run, so that after starting a new mysql container, you can simply launch mysql (no more installation is needed). Most popular applications already have containers that you can use, and a quick google search yields an existing mysql image that we can use for lobste.rs.

For the container that runs lobste.rs, we’ll need to create a new image by writing our own Dockerfile, which describes how the Docker image should be created. In this case, the Dockerfile is relatively simple:

# This container is based on the Ruby image, which means that it
# automatically inherits the Ruby installation defined in that image.
FROM ruby:2.3.1

# Install NodeJS, which is required by lobste.rs.
RUN apt-get update && apt-get install nodejs -y

# Download and build the lobste.rs code.
RUN git clone git://github.com/jcs/lobsters.git
WORKDIR lobsters
RUN bundle

# Add a file to the container that contains startup code for lobste.rs. This
# command assumes that start-lobsters.sh is in the same directory as this
# Dockerfile.
COPY start-lobsters.sh /lobsters/

# When the container starts, it should run the lobste.rs server using the
# start-lobsters.sh bash file that we copied above.  This is a common
# "gotcha" to people new to containers: unlike VMs, each container is based
# on a process (in this case, rails, which is started at the end of
# start-lobsters.sh) and will be shutdown when that process stops.
ENTRYPOINT ["/bin/sh", "/lobsters/start-lobsters.sh"]

In this case, we wrote an additional bash script, start-lobsters.sh, to help start the application. The important thing about that script is that it does some setup that needed to be done after the container was started, so it couldn’t be done in the Dockerfile. For example, the first piece of setup it does it to initialize the SQL database. Because that requires a connection to mysql, it needs to be done after the container is launched (and configured to access the mysql container, as discussed below). After initializing the database, the start-lobsters.sh script launches the rails server, which is the main process run by the container.

To create a docker image using this file, run docker build in the directory with the Dockerfile (don’t forget the period at the end!):

$ docker build -t kayousterhout/lobsters .

In this case, we called the resulting image kayousterhout/lobsters, because we’ll push it to the Dockerhub for kayousterhout; you’ll want to use your own Dockerhub id to name your images.

This will take a few minutes, and creates a new image with the name kayousterhout/lobsters. If you want to play around with the new container, you can use Docker to launch it locally:

$ docker run -n lobsters-test kayousterhout/lobsters

To use a shell on your new container to poke around (while the rails server is running), use:

$ docker exec -it lobsters-test /bin/bash

This can be helpful for making sure everything was installed and is running as expected (although in this case, lobste.rs won’t work when you start it with Docker, because it’s not yet connected to a mysql container).

Deploying the containers with Quilt

So far we have a mysql container image (we’re using an existing one hosted on Dockerhub) and a lobste.rs container image that we just made. You should similarly have the containers ready for your application. Up until now, we haven’t done anything Quilt-specific: if you were using another container management service like Kubernetes, you would have had to create the container images like we did above. These containers aren’t yet configured to communicate with each other, which is what we’ll set up with Quilt. We’ll also use Quilt to descrbie the machines to launch for the containers to run on.

To run the containers for your application with Quilt, you’ll need to write a Quilt blueprint. Quilt blueprints are written in Javascript, and the Quilt Javascript API is described here. In this guide, we’ll walk through how to write a Quilt blueprint for lobste.rs, but the Quilt API has more functionality than we could describe here. See the API guide for more usage information.

Writing the Quilt blueprint for MySQL

First, let’s write the Quilt blueprint to get the MySQL container up and running. We need to create a container based on the mysql image:

var sqlContainer = new Container("mysql:5.6.32");

Here, the argument to Container is the name of an image. You can also pass in a Dockerfile to use to create a new image, as described in the Javascript API documentation.

Next, the SQL container requires some environment variables to be set. In particular, we need to specify a root password for SQL. We can set the root password to foo with the setEnv function:

sqlContainer.setEnv("MYSQL_ROOT_PASSWORD", "foo");

All containers need to be part of a service in order to be executed. In this case, the service just has our single mysql container. Each service is created using a name and a list of containers:

var sqlService = new Service("sql", [sqlContainer]);

The SQL service is now initialized.

Writing the Quilt blueprint for lobste.rs

Next, we can similarly initialize the lobsters service. The lobsters service is a little trickier to initialize because it requires an environment variable (DATABASE_URL) to be set to the URL of the SQL container. Quilt containers are each assigned unique hostnames when they’re initialized, so we can create the lobsters container and initialize the URL as follows:

var lobstersContainer = new Container("kayousterhout/lobsters");
var sqlDatabaseUrl = "mysql2://root:" + mysqlOpts.rootPassword + "@" + sqlService.hostname() + ":3306/lobsters";
lobstersContainer.setEnv("DATABASE_URL", sqlDatabaseUrl);
var lobstersService = new Service("lobsters", [lobstersContainer]);

Allowing network connections

At this point, we’ve written code to create a mysql service and a lobsters service. With Quilt, by default, all network connections are blocked. To allow lobsters to talk to mysql, we need to explicitly open the mysql port (3306):

lobstersService.connect(3306, sqlService);

Because lobsters is a web application, the relevant port should also be open to the public internet on the lobsters service. Quilt has a publicInternet variable that can be used to connect services to any IP address:

publicInternet.connect(3000, lobstersService);

Deploying the application on infrastructure

Finally, we’ll use Quilt to launch some machines, and then start our services on those machines. First, we’ll define a “base machine.” We’ll deploy a few machines, and creating the base machine is a useful way to create one machine that all of the machines in our deployment will be based off of. In this case, the base machine will be an Amazon instance that allows ssh access from the public key “bar”:

var baseMachine = new Machine({provider: "Amazon", sshKeys: ["ssh-rsa bar"]});

Now, using that base machine, we can deploy a master and a worker machine. All quilt deployments must have one master, which keeps track of state for all of the machines in the cluster, and 0 or more workers. To deploy machines and services, you must create a deployment object, which maintains state about the deployment.

var deployment = createDeployment();
deployment.deploy(baseMachine.asMaster());
deployment.deploy(baseMachine.asWorker());

We’ve now defined a deployment with a master and worker machine. Let’s finally deploy the two services on that infrastructure:

deployment.deploy(sqlService);
deployment.deploy(lobstersService);

We’re done! Running the blueprint is now trivial. With a quilt daemon running, run your new blueprint (which, in this case, is called lobsters.js):

$ quilt run ./lobsters.js

Now users of lobsters, for example, can deploy it without needing to worry about the details of how different services are connected with each other. All they need to do is to quilt run the existing blueprint.

Quilt.js API Documentation

This section documents use of the Quilt JavaScript library, which is used to write blueprints.

Container

The Container object represents a container to be deployed.

Specifying the Image

The first argument of the Container constructor is the image that container should run.

If a string is supplied, the image at that repository is used.

Dockerfile

Instead of supplying a link to a pre-built image, Quilt also support building images in the cluster. When specifying a Dockerfile to be built, an Image object must be passed to the Container constructor.

For example,

var container = new Container(new Image("my-image-name",
  "FROM nginx\n" +
  "RUN cd /web_root && git clone github.com/my/web_repo"
));

would deploy an image called my-image-name built on top of the nginx image, with the github.com/my/web_repo repository cloned into /web_root.

If the Dockerfile is saved as a file, it can simply be read in:

var container = new Container(new Image("my-image-name", fs.readFileSync("./Dockerfile")));

If a user runs a blueprint that uses a custom image, then runs another blueprint that changes the contents of that image’s Dockerfile, the image is re-built and all containers referencing that Dockerfile are restarted with the new image.

If multiple containers specify the same Dockerfile, the same image is reused for all containers.

If two images with the same name but different Dockerfiles are referenced, an error is thrown.

Container.filepathToContent

Container.filepathToContent defines text files to be installed on the container before the container starts. Both the key and value are strings.

For example,

{
  "/etc/myconf": "foo"
}

would create a file at /etc/myconf containing the text foo.

new Container("haproxy").withFiles({
  "/etc/myconf": "foo"
});

would create a haproxy instance with a text file /etc/myconf containing foo.

If the files change after the container boots, Quilt does not restart the container. However, if the file content specified by filepathToContent changes, Quilt will destroy the old container and boot a new one with the proper files.

The files are installed with permissions 0644. Parent directories are automatically created.

Container.hostname()

Container.hostname gets the container’s hostname. If the container has no hostname, an error is thrown.

Container.setHostname()

Container.setHostname gives the container a hostname at which the container can be reached.

If multiple containers have the same hostname, an error is thrown during the vetting process.

Machine

The Machine object represents a machine to be deployed.

Its attributes are:

Security

TLS

Quilt uses grpc for communication with the daemon and deployed clusters. Functionality exposed through grpc include deploying new Stitches and querying deployment information. Thus, TLS should be enabled for all non-experimental deployments. It is currently disabled by default.

Quickstart

Generate the necessary TLS files.

$ quilt setup-tls ~/.quilt/tls

Start the daemon with TLS enabled.

$ quilt daemon -tls-dir ~/.quilt/tls

Use the other Quilt commands as normal.

$ quilt run ./example.js
$ quilt show
MACHINE         ROLE      PROVIDER    REGION       SIZE         PUBLIC IP         STATUS
8a0d2198229c    Master    Amazon      us-west-1    m3.medium    54.153.11.92      connected
b92d625c6847    Worker    Amazon      us-west-1    m3.medium    52.53.170.129     connected

CONTAINER       MACHINE         COMMAND                     LABELS    STATUS     CREATED           PUBLIC IP
1daa461f0805    b92d625c6847    alpine tail -f /dev/null    alpine    running    24 seconds ago    52.53.170.129:8000

However, trying to connect to a cluster with different credentials fails. This can be simulated by restarting the daemon and running the same blueprint, but with different TLS credentials. Note that the machines never connect.

$ quilt daemon -tls-dir ~/.quilt/other-credentials
$ quilt run ./example.js
$ quilt show
MACHINE         ROLE      PROVIDER    REGION       SIZE         PUBLIC IP        STATUS
8a0d2198229c    Master    Amazon      us-west-1    m3.medium    54.153.11.92     connecting
b92d625c6847    Worker    Amazon      us-west-1    m3.medium    52.53.170.129    connecting

Trying to connect in Insecure mode also fails. Note that the machines never connect.

$ quilt daemon
$ quilt run ./example.js
$ quilt show
MACHINE         ROLE      PROVIDER    REGION       SIZE         PUBLIC IP        STATUS
8a0d2198229c    Master    Amazon      us-west-1    m3.medium    52.53.170.129    connecting
b92d625c6847    Worker    Amazon      us-west-1    m3.medium    54.153.11.92     connecting

Setup

The certificate hierarchy can be easily created using the setup-tls subcommand. For example,

$ quilt setup-tls ~/.quilt/tls

Will create the file structure described in tls-dir. No additional setup is necessary – the -tls-dir flag can now be set to your chosen TLS directory.

tls-dir

TLS is enabled with the -tls-dir option. The TLS directory should have the following structure when passed to quilt daemon:

$ tree ~/.quilt/tls
├── certificate_authority.crt
├── certificate_authority.key
├── quilt.crt
├── quilt.key

Other files in the directory are ignored by Quilt.

admin-ssh-private-key

The daemon installs keys using SFTP, so the daemon requires SSH access to the machines. By default, the daemon generates an in-memory key to use for distributing keys. A key can be specified from the filesystem using the admin-ssh-private-key flag.

For example,

$ quilt daemon -admin-ssh-private-key ~/.quilt/id_rsa -tls-dir ~/.quilt/tls

Quilt CLI

Quilt’s CLI, quilt, is a handy command line tool for starting, stopping, and managing deployments. Quilt CLI commands have the following format:

$ quilt [OPTIONS] COMMAND

To see the help text for a specific command, run:

$ quilt COMMAND --help

Options

Name, shorthand Default Description
--log-level, -l info Logging level (debug, info, warn, error, fatal, or panic)
--verbose, -v false Turn on debug logging
-log-file Log output file (will be overwritten)

Commands

Name Description
counters Display internal counters tracked for debugging purposes. Most users will not need this command.
daemon Start the quilt daemon, which listens for quilt API requests.
debug-logs Fetch logs for a set of machines or containers.
inspect Visualize a blueprint.
logs Fetch the logs of a container or machine minion.
minion Run the quilt minion.
ps Display the status of quilt-managed machines and containers.
run Compile a blueprint, and deploy the system it describes.
ssh SSH into or execute a command in a machine or container.
stop Stop a deployment.
version Show the Quilt version information.
setup-tls Create the files necessary for TLS-encrypted communication with Quilt.

Cloud Provider Configuration

This section describes the basic configuration of the cloud providers supported by Quilt, and gives some details about how to enable extra features (e.g., floating IP addresses) on each cloud provider.

Amazon EC2

For Amazon EC2, you’ll first need to create an account with Amazon Web Services and then find your access credentials from the Security Credentials page in the AWS Management Console. Once you’ve done that, put your Amazon credentials in a file called ~/.aws/credentials:

[default]
aws_access_key_id = <YOUR_ID>
aws_secret_access_key = <YOUR_SECRET_KEY>

The file needs to appear exactly as above (including the [default] at the top), except with <YOUR_ID> and <YOUR_SECRET_KEY> filled in appropriately.

To deploy an m3.medium instance on Amazon EC2’s us-west-2 region as a Worker:

deployment.deploy(new Machine({
    provider: "Amazon",
    region: "us-west-2",
    size: "m3.medium",
    role: "Worker" }));

DigitalOcean

Basic Setup

Quilt needs access to a DigitalOcean account token in order to make the API calls needed to boot your deployment. To get and set up this token:

  1. Create a new token here. The token must have both read and write permissions.

  2. Save the token in ~/.digitalocean/key on the machine that will be running the Quilt daemon.

Now, to deploy a DigitalOcean droplet in the sfo1 zone of size 512mb as a Worker:

deployment.deploy(new Machine({
    provider: "DigitalOcean",
    region: "sfo1",
    size: "512mb",
    role: "Worker" }));

Floating IPs

Unless there are already droplets running, DigitalOcean doesn’t allow users to create floating IPs under the “Networking” tab on their website. Instead, this link can be used to reserve IPs that Quilt can then assign to droplets.

Google Compute Engine

Basic Setup

  1. Create a Google Cloud Platform Project: All instances are booted under a Cloud Platform project. To setup a project for use with Quilt, go to the console page, then click the project dropdown at the top of page, and hit the plus icon. Pick a name, and create your project.

  2. Enable the Compute API: Select your newly created project from the project selector at the top of the console page, and then select API Manager -> Library from the navbar on the left. Search for and enable the Google Compute Engine API.

  3. Save the Credentials File: Go to Credentials on the left navbar (under API Manager), and create credentials for a Service account key. Create a new service account with the Project -> Editor role, and select the JSON output option. Copy the downloaded file to ~/.gce/quilt.json on the machine from which you will be running the Quilt daemon.

Now, to deploy a GCE machine in the us-east1-b zone of size n1-standard-1 as a Worker:

deployment.deploy(new Machine({
    provider: "Google",
    region: "us-east1-b",
    size: "n1-standard-1",
    role: "Worker" }));

Developing Quilt

Developer Setup

Install Go

The project is written in Go and supports Go version 1.8 or later. Install Go using your package manager (Go is commonly referred to as “golang” in package managers and elsewhere) or via the Go website.

If you’ve never used Go before, we recommend reading the overview to Go workspaces here. In short, you’ll need to configure the GOPATH environment variable to be the location where you’ll keep all Go code. For example, if you’d like your Go workspace to be $HOME/gowork:

export GOPATH="$HOME/gowork"
export PATH="$GOPATH/bin:$PATH"

Add these commands to your .bashrc so that they’ll be run automatically each time you open a new shell.

Download Quilt

Clone the Quilt repository into your Go workspace using go get:

$ go get github.com/quilt/quilt

This will install Quilt in your Go workspace at $GOPATH/src/github.com/quilt/quilt, and compile Quilt. After running installing Quilt, the quilt command should execute successfully in your shell.

Note that if you’ve previously installed Quilt with npm, there will be another Quilt binary installed on your machine (that was downloaded during the npm installation). If you want to develop Quilt, you probably want to make sure that when you run quilt, the version you’re developing (that was compiled from the Go code) gets run, and not the Quilt release that was downloaded from npm. Check that this is the case:

$ which quilt
/Users/kay/gowork/bin/quilt

If running which quilt results in a path that includes your $GOPATH$, like the one above, you’re all set. If it instead returns someplace else, e.g., /usr/local/bin/quilt, you’ll need to fix your $PATH variable so that $GOPATH/bin comes first.

Building Quilt

To build quilt, run go install in the Quilt directory. To do things beyond basic build and install, several additional build tools are required. These can be installed with the make go-get target.

Protobufs

If you change any of the proto files, you’ll need to regenerate the protobuf code. We currently use protoc v3. On a Mac with homebrew, you can install protoc v3 using:

$ brew install --devel protobuf

On other operating systems you can directly download the protoc binary here, and then add it to your $PATH.

You’ll also need to install protobuf go bindings:

$ go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

To generate the protobufs simply call:

$ make generate

Dependencies

We use govendor for dependency management. If you are using Go 1.5 make sure GO15VENDOREXPERIMENT is set to 1.

To add a new dependency:

  1. Run go get foo/bar
  2. Edit your code to import foo/bar
  3. Run govendor add +external

To update a dependency:

$ govendor update +vendor

Developing the Minion

Whenever you develop code in minion, make sure you run your personal minion image, and not the default Quilt minion image. To do that, follow these steps:

  1. Create a new empty repository on your favorite registry - docker hub for example.
  2. Modify quiltImage in cloudcfg.go to point to your repo.
  3. Modify Version in version.go to be “latest”. This ensures that you will be using the most recent version of the minion image that you are pushing up to your registry.
  4. Create a .mk file (for example: local.mk) to override variables defined in Makefile. Set REPO to your own repository (for example: REPO = sample_repo) inside the .mk file you created.
  5. Create the docker image: make docker-build-quilt
    • Docker for Mac and Windows is in beta. See the docs for install instructions.
  6. Sign in to your image registry using docker login.
  7. Push your image: make docker-push-quilt.

After the above setup, you’re good to go - just remember to build and push your image first, whenever you want to run the minion with your latest changes.

Contributing Code

We highly encourage contributions to Quilt from the Open Source community! Everything from fixing spelling errors to major contributions to the architecture is welcome. If you’d like to contribute but don’t know where to get started, feel free to reach out to us for some guidance.

The project is organized using a hybrid of the Github and Linux Kernel development workflows. Changes are submitted using the Github Pull Request System and, after appropriate review, fast-forwarded into master. See Submitting Patches for details.

Coding Style

The coding style is as defined by the gofmt tool: whatever transformations it makes on a piece of code are considered, by definition, the correct style. In addition, golint, go vet, and go test should pass without warning on all changes. An easy way to check these requirements is to run make lint check on each patch before submitting a pull request. Running make format will fix many (but not all) formatting errors.

Unlike official go style, in Quilt lines should be wrapped to 89 characters. This requirement is checked by make lint.

The fundamental unit of work in the Quilt project is the git commit. Each commit should be a coherent whole that implements one idea completely and correctly. No commits should break the code, even if they “fix it” later. Commit messages should be wrapped to 80 characters and begin with a title of the form <Area>: <Title>. The title should be capitalized, but not end with a period. For example, provider: Move the provider interfaces into the cluster directory is a good title. When possible, the title should fit in 50 characters.

All but the most trivial of commits should have a brief paragraph below the title (separated by an empty line), explaining the context of the commit. Why the patch was written, what problem it solves, why the approach was taken, what the future implications of the patch are, etc.

Commits should have proper author attribution, with the full name of the commit author, capitalized properly, with their email at the time of authorship. Commits authored by more than one person should have a Co-Authored-By: tag at the end of the commit message.

Submitting Patches

Patches are submitted for inclusion in Quilt using a Github Pull Request.

A pull request is a collection of well formed commits that tie together in some theme, usually the larger goal they’re trying to achieve. Completely unrelated patches should be included in separate pull requests.

Pull requests are reviewed by one person: either by a committer, if the code was submitted by a non-committer, or by a non-committer otherwise. You do not need to choose a reviewer yourself; quilt-bot will randomly select a reviewer from the appropriate group. Once the reviewer has approved the pull request, a committer will merge it.

Once the patch has been approved by the first reviewer, quilt-bot will assign a committer to do a second (sometimes cursory) review. The committer will either merge the patch, provide feedback, or if a great deal of work is still needed, punt the patch back to the original reviewer.

It should be noted that the code review assignment is just a suggestion. If a another contributor, or member of the public for that matter, happens to do a detailed review and provide a +1 then the assigned reviewer is relieved of their responsibility. If you’re not the assigned reviewer, but would like to do the code review, it may be polite to comment in the PR to that effect so the assigned reviewer knows they need not review the patch.

We expect patches to go through multiple rounds of code review, each involving multiple changes to the code. After each round of review, the original author is expected to update the pull request with appropriate changes. These changes should be incorporated into the patches in their most logical places. I.E. they should be folded into the original patches or, if appropriate inserted as a new patch in the series. Changes should not be simply tacked on to the end of the series as tweaks to be squashed in later – at all stages the PRs should be ready to merge without reorganizing commits.

The Quilt Daemon

Two processes need to be running for blueprints to be enforced: quilt daemon and quilt run. quilt daemon does the heavy lifting – it’s responsible for enforcing blueprints. quilt run is responsible for compiling blueprints and sending them to the daemon to be enforced.

Code Structure

Quilt is structured around a central database (db) that stores information about the current state of the system. This information is used both by the global controller (Quilt Global) that runs locally on your machine, and by the minion containers on the remote machines.

Database

Quilt uses the basic db database implemented in db.go. This database supports insertions, deletions, transactions, triggers and querying.

The db holds the tables defined in table.go, and each table is simply a collection of rows. Each row is in turn an instance of one of the types defined in the db directory - e.g. Cluster or Machine. Note that a table holds instances of exactly one type. For instance, in ClusterTable, each row is an instance of Cluster; in ConnectionTable, each row is an instance of Connection, and so on. Because of this structure, a given row can only appear in exactly one table, and the developer therefore performs insertions, deletions and transactions on the db, rather than on specific tables. Because there is only one possible table for any given row, this is safe.

The canonical way to query the database is by calling a SelectFromX function on the db. There is a SelectFromX function for each type X that is stored in the database. For instance, to query for Connections in the ConnectionTable, one should use SelectFromConnection.

Quilt Global

The first thing that happens when Quilt starts is that your blueprint is parsed by Quilt’s JavaScript library, quilt.js. quilt.js then puts the connection and container specifications into a sensible format and forwards them to the engine.

The engine is responsible for keeping the db updated so it always reflects the desired state of the system. It does so by computing a diff of the config and the current state stored in the database. After identifying the differences, engine determines the least disruptive way to update the database to the correct state, and then performs these updates. Notice that the engine only updates the database, not the actual remote system - cluster takes care of that.

The cluster takes care of making the state of your system equal to the state of the database. cluster continuously checks for updates to the database, and whenever the state changes, cluster boots or terminates VMs in you system to reflect the changes in the db.

Now that VMs are running, the minion container will take care of starting the necessary system containers on its host VM. The foreman acts like the middle man between your locally run Quilt Global, and the minion on the VMs. Namely, the foreman configures the minion, notifies it of its (the minion‘s) role, and passes it the policies from Quilt Global.

All of these steps are done continuously so the blueprint, database and remote system always agree on the state of the system.

Quilt Remote

As described above, cluster is responsible for booting VMs. On boot, each VM runs docker and a minion. The VM is furthermore assigned a role - either worker or master - which determines what tasks it will carry out. The master minion is responsible for control related tasks, whereas the worker VMs do “the actual work” - that is, they run containers. When the user specifies a new container the config file, the scheduler will choose a worker VM to boot this container on. The minion on the chosen VM is then notified, and will boot the new container on its host. The minion is similarly responsible for tearing down containers on its host VM.

While it is possible to boot multiple master VMs, there is only one effective master at any given time. The remaining master VMs simply perform as backups in case the leading master fails.

Frequently Asked Questions

This section includes answers to common questions about Quilt, and solutions to various issues. If you run into an issue and can’t find the answer here, don’t hesitate to email us at discuss@quilt.io.

I tried to quilt run a blueprint on Amazon and nothing seems to be working.

If you’re running a blueprint on AWS and the containers are not getting properly created, you may have an issue with your VPC (Virtual Private Cloud) settings on Amazon. When this issue occurs, if you run quilt show, the machines will all have status connected, but the containers will never progress to the scheduled state (either the status will be empty, or for Dockerfiles that are built in the cluster, the status will say built). This issue only occurs if you’ve changed your default VPC on Amazon, so if you don’t know what a VPC is or you haven’t used one before on Amazon, this is probably not the issue you’re experiencing.

If you previously changed your default VPC on Amazon, it may be configured in a way that prevents Quilt containers from properly communicating with each other. This happens if the subnet configured for your VPC overlaps with 10.0.0.0/8, which is the subnet that Quilt uses. This problem can manifest in many ways; typically it looks like nothing seems to be working correctly. For example, a recent user who experienced this problem saw the following logs on the etcd container on a Quilt worker:

2017-06-05 21:14:40.823760 W | etcdserver: could not get cluster response from http://10.201.160.199:2380: Get http://10.201.160.199:2380/members: dial tcp 10.201.160.199:2380: getsockopt: no route to host
2017-06-05 21:14:40.823787 W | etcdmain: proxy: could not retrieve cluster information from the given urls

You can check (and fix) your VPC settings in the VPC section of the online AWS console.