Quick Links

Docker App is an experimental Docker feature which lets you build and publish application stacks consisting of multiple containers. It aims to let you share Docker Compose stacks with the same ease of use as regular Docker containers.

Why Use Docker App?

Docker App brings the Docker container experience to entire application stacks. Many popular software projects now have official Docker images. They might not necessarily work as-is though: WordPress, for example, needs a database connection to run.

Containerisation advocates the separation of each service in an app stack into its own container. A Dockerised WordPress installation should consist of a WordPress container running a web server, PHP and WordPress, and a dedicated MySQL database container.

Running

        docker run -d -p 80:80 wordpress:latest
    

isn't sufficient to get this up-and-running. You'll have WordPress live but no database connection. You can use Docker Compose to setup a stack with a WordPress container and a MySQL container. The onus is on you to create and maintain the Compose file, even though every user will need to do something similar.

Docker App attempts to solve this problem. It lets you create and run "app packages" of multiple containers. These can be pushed to Docker registries in the same way as individual containers. Other users can then pull down and run your app stack without having to write their own Docker Compose file.

Internally Docker App implements the Cloud Native Application Bundle (CNAB) specification. This is a cross-industry effort to make it simpler to define, share and install multi-container cloud native apps. It includes support for peripheral resources such as volumes and networks.

CNAB clients like Docker App create CNAB app bundles which can be published to supporting registries. A CNAB bundle is an abstraction which encapsulates multiple containers but is treated similarly to a single container when pushed to a registry.

Docker App is currently experimental. It's been included with the Docker CLI since version 19.03 but may change or be removed in the future. The project has changed direction in the past and its current development status is uncertain. Nonetheless, the version included with Docker today can already be used to distribute multi-container app stacks.

Creating a Docker App Stack

You can use the Docker CLI to initialise a new app stack.

docker app init --single-file example-app

This will create a new file called example-app.dockerapp in your working directory. The --single-file argument instructed Docker App to merge all the project files into the one example-app.dockerapp file. Without this argument, you'd see three separate files: docker-compose.yml, metadata.yml and parameters.yml.

Each constituent file is represented in the merged single file as a YAML section. We're using a single file to aid clarity in this article's code snippets; if you're planning a complex app stack, using multiple files will aid maintainability.

Here's what each of the three sections do:

  • docker-compose.yml - This is a regular Docker Compose file defining the containers which make up your app.
  • metadata.yml - This sets basic metadata about your app, such its name, version and description. You use this to describe your entire app stack, rather than any one container.
  • parameters.yml - This sets up default parameters for variables used in your Docker Compose file. Parameters are defined as simple key-value pairs. Your stack will use these default parameters unless the user overrides them with custom values.

The only additional configuration needed beyond a regular Docker Compose file is the metadata section. If you already have a Docker Compose file, you can use docker app init with the --compose-file flag to import it:

docker app init --compose-file docker-compose.yml example-app

Update the manifest section with your app's name, version and description. You can then proceed to run and publish your app!

If you've not got a Docker Compose file ready, add some container definitions to the Compose section of the configuration now.

# Application services - equivalent to docker-compose.yml.

---

services:

apache:

image: httpd:latest

ports:

- ${apache_port}:80

mysql:

image: mysql:latest

ports:

- ${mysql_port}:3306

environment:

- MYSQL_ROOT_PASSWORD=${mysql_root_password}

---

# Default application parameters - equivalent to parameters.yml.

apache_port: 80

mysql_port: 3306

mysql_root_password: mysql

This simple stack creates Apache and MySQL containers. Each container needs to be configured with the host port to bind to. The MySQL container also needs a value for the MYSQL_ROOT_PASSWORD variable. This is used by MySQL during first-run to set the password for the initial root user.

Default values for these variables are provided in the parameters section of the Docker App file. These values will be used unless you override them when spinning up your app stack.

Building App Images

The next step is to build your Docker App stack into a CNAB-compliant bundle.

docker app build . -f example-app.dockerapp -t example-user/example-app:latest

This command functions similarly to docker build. It'll create an app bundle tagged as example-user/example-app:latest. You can now push this bundle to Docker Hub!

docker app push example-user/example-app:latest

You've successfully published your first multi-container app stack, without having to manually share a Docker Compose file.

Running App Images

The final piece is to run an instance of your stack:

docker app run example-user/example-app:latest --name my-app

Docker App will start new containers based on the definition in your app bundle. The app bundle itself will be pulled from Docker Hub if it doesn't already exist on your machine.

If your Compose file included resources like volumes and networks, they'll be created and linked to the containers. The whole operation is akin to using docker-compose up without having to download the docker-compose.yml file first.

The app will be launched with the default variables defined in its parameters.yml file. You can override any variables you need with the --set flag:

docker app run example-user/example-app:latest --name my-app --set mysql_root_password=mysql

You can change the variables of a running app using docker app update:

docker app update my-app --set my_variable=new_value

Once an app's running, you can use docker app ls to inspect its details. Use docker app rm my-app to destroy a specific app by its name. This will remove all the resources associated with the app.

Conclusion

Docker App is a promising approach to defining, sharing and running multi-container application stacks with the Docker CLI. The project implements the Cloud Native Application Bundle specification while providing a friendly abstracted interface to users.

Adoption of Docker App is likely to remain low until its status in the Docker ecosystem has been confirmed. If it graduates from its experimental position, Docker App should fill the gap between single-container registries and complex cloud native deployment solutions such as Helm.