At Virginia Tech, I work on a product called Summit, which is a tool to help manage the process around sponsored research (validating compliance, building budgets, obtaining approvals from colleges/departments, and more). And, we've been doing some pretty cool stuff with Docker App and it has completely changed our development environments. But, first… some background.
Summit-in-a-Box with Docker App
Summit is composed of many components, most of which are different front-end clients to serve various purposes or audiences. Here's the breakdown…
- Single/monolithic backend exposing REST API endpoints - our backend is built using JavaEE and deployed into a Wildfly container, served in containers! Gah! Container overload!
- Several static web-based frontends - using a variety of JS frameworks (AngularJS, Angular4+, and React), our clients use data from the API to drive the application
- Docs/user guide - a MkDocs site that is converted into static HTML/CSS
With all of these components, we built a single docker-compose.yml
file that is using the latest image for each service. It's kept up-to-date using automated pipelines and is done on a per-feature-branch capability. This makes it easy to switch all components when changing feature branches. The automation setup will be the topic for (another blog post)[/2019/03/keeping-docker-app-updated/]. (nice teaser, huh?)
With this, if any developer on our team were to push an update to the API, the compose file is updated to have the api service use the new image (we tag images using git commit hashes). The Docker App is then published.
version: "3.7"
services:
api:
image: summit/api:fbc6bd9...
...
desktop:
image: summit/desktop:e004...
...
docs:
image: summit/docs:da38a7a...
...
A change to the API causes update to compose file to use the newest image tag
version: "3.7"
services:
api:
image: summit/api:02f8125...
...
desktop:
image: summit/desktop:e004...
...
docs:
image: summit/docs:da38a7a...
...
What this means is I can simply run docker-app deploy our-registry/summit.dockerapp:master
and the latest images for each service will be deployed locally on my machine. We added Traefik as a reverse proxy for host-based routing (each service has its own hostname) and boom! Summit-in-a-Box!
Summit-in-a-Box
Excluding Services from Docker App
With Docker App, a service can define an optional x-enabled
attribute. When set to anything "falsey", that service is completely ignored when deploying or rendering the Compose file. Docker App also let's us use settings (being renamed to parameters) to allow a value to be changed. Let's extend the example from before to add the attributes…
version: "3.7"
services:
api:
image: summit/api:69decc011c00bca57fb15476a0f5e348aab2fb68
x-enabled: "${enable-api}"
...
desktop:
image: summit/desktop:e0044d2ef0a5b3495ca57052c248516f7492fde4
x-enabled: "${enable-desktop}"
...
docs:
image: summit/docs:da38a7ab6e4e99e75b46aa49d3b3cfd102385b23
x-enabled: "${enable-docs}"
...
And then set the default values for the settings:
enable-api: true
enable-desktop: true
enable-docs: true
Now, with my updated docker app, I can override the setting values (using -s [var name]=[value]
) to selectively disable services. Running this command:
docker-app render -s enable-desktop=false our-registry/summit.dockerapp:master
will generate the following Docker Compose file:
version: "3.7"
services:
api:
image: summit/api:69decc011c00bca57fb15476a0f5e348aab2fb68
x-enabled: true
...
docs:
image: summit/docs:da38a7ab6e4e99e75b46aa49d3b3cfd102385b23
x-enabled: true
...
Look! No desktop
service!
Swapping in Another Container
Even though the desktop service is not included in our Summit-in-a-Box deploy, there's nothing stopping us from running another container in its place! In the repository for our desktop client component, we might have a compose file that looks something like this:
version: "3.7"
services:
desktop:
image: node
command: yarn run dev
working_dir: /app
volumes:
- ./:/app
labels:
traefik.backend: desktop-client
traefik.frontend.rule: Host:app.localhost
traefik.port: 3000
networks:
siab-frontend:
networks:
siab-frontend:
external: true
A couple of things to note about this…
- Use a dev-focused container - use a container image that makes sense for your dev environment. For us, since we're developing various JS frontends, it makes sense to use a Node image, even though our production image is simply nginx serving static content. Mount in source code, watch for changes and rebuild code automatically, etc. Make it easy for your devs!
- Hook in using Traefik labels - in order for Traefik to send requests to the container, we need to add labels. Most likely, you'll want the same hostname being used by the app-provided service, so copy that over.
- Connect to the Docker App networks - in order for Traefik to actually send the traffic, we need to be on the same network. Since Summit-in-a-Box (SiaB) is defining that network, it's an "external" network to this compose file.
Now, if I run docker-compose up
in the desktop repo, I'll start this dev-ready container, yet hook into the application stack coming from Docker App. Cool, huh?
Recap
By adopting and taking advantage of Docker App, we're able keep our environments even more consistent by doing the following:
- Upstream components build container images
- Updated images are reflected in the deployed Summit Docker App ((blog post on how we do that here)[/2019/03/keeping-docker-app-updated/]).
- Developers can pull the latest Docker-app for any feature branch and determine services to disable.
- Developers can then spin up dev-focused containers to replace any upstream service.
We've made tools to help with #3 and #4, which will have its own blog post soon too! In the mean time, feel free to look at DevDock.