5.8 KiB

docker-compose template

Run it

Execute this template like so:

cookiecutter https://quico.space/Quico/py-cookiecutter-templates.git --directory 'docker-compose'

Cookiecutter interactively prompts you for the following info, here with example answers:

project_slug [dir-name]: grafana
service [grafana]:
component_list [grafana]: grafana,nginx
context [ctx]: cncf

Done, directory structure and files for your next docker-compose project are ready for you to hit the ground running.

Explanation and terminology

Your four answers translate as follows into rendered files.

  1. The project_slug is used only as directory name. A container named vault may be fine but the project directory name hashicorpvault might be more descriptive.

    .
    └── hashicorpvault                                        <--- Here
        ├── build-context
        │   ├── docker-data
        │   │   └── .gitkeep
        │   ├── Dockerfile
        ...
    
  2. The service variable by default copies your project_slug answer. It's a style decision whether you leave it at that and just hit Enter. The service name will come up in rendered docker-compose.yml at purely cosmetic locations such as the networks: key, container_name: and /opt/docker-data volume mount presets, here with ftp as the service name:

    services:
        mysql:
            image: "mysql:${MYSQL_VERSION}"
            container_name: "ftp-mysql-${CONTEXT}"            <---
            networks:
                ftp-default:                                  <---
            ...
            volumes:
                # - /opt/docker-data/ftp-mysql-${CONTEXT}/... <---
                # - /opt/docker-data/ftp-mysql-${CONTEXT}/... <---
                # - /opt/docker-data/ftp-mysql-${CONTEXT}/... <---
        ...
    
  3. Treat component_list as the list of Docker images that make up your service. Each docker-compose project forms a service - see above - that consists of either a single or multiple components. They're your services:, your container, volume, variable names etc.:

    services:
        grafana:                                              <---
            image: "grafana:${GRAFANA_VERSION}"               <---
            container_name: "grafana-grafana-${CONTEXT}"      <---
            ...
            environment:
                # GRAFANA_USER: ${GRAFANA_USER}               <---
                # GRAFANA_PASSWORD: ${GRAFANA_PASSWORD}       <---
        ...
    
  4. The last prompt for a context is a generic string to help you distinguish deployments. It can be whatever you want such as for example a team name, here cncf which shows up as a preset in an example env file. It defaults to ctx just so it can't be empty.

    .
    └── grafana
        ...
        └── env
            └── fqdn_context.env.example                      <---
    

    Which then looks like:

    CONTEXT=cncf
    ...
    

Also check out the Caveats section at the end to learn what this template does not do well.

Result

Multi-component service

Above example of a multi-component (two in this case) grafana service will give you this directory structure:

.
└── grafana
    ├── build-context
    │   ├── grafana
    │   │   ├── docker-data
    │   │   │   └── .gitkeep
    │   │   ├── Dockerfile
    │   │   └── extras
    │   │       └── .gitkeep
    │   └── nginx
    │       ├── docker-data
    │       │   └── .gitkeep
    │       ├── Dockerfile
    │       └── extras
    │           └── .gitkeep
    ├── common-settings.yml
    ├── docker-compose.override.yml
    ├── docker-compose.yml
    └── env
        └── fully.qualified.domain.name.example

Check out file contents over in the examples/grafana subdir.

Single-component service

With an alternative single-component hashicorpvault service the result may look like this:

.
└── hashicorpvault
    ├── build-context
    │   ├── docker-data
    │   │   └── .gitkeep
    │   ├── Dockerfile
    │   └── extras
    │       └── .gitkeep
    ├── common-settings.yml
    ├── docker-compose.override.yml
    ├── docker-compose.yml
    └── env
        └── fully.qualified.domain.name.example

Check out file contents over in the examples/hashicorpvault subdir.

Caveats

Consider Cookiecutter's project directory and rendered files a starting point. It won't do everything perfectly.

Imagine if you will a service that consists of Infinispan among other things. In Docker Hub's content-addressable image store Infinispan's location is at infinispan/server so you obviously want that exact string with a forward slash to show up in your docker-compose.yml as the image: key's value, same with your Dockerfile. The image: key's value comes from what you enter in Cookiecutter's component_list prompt. Component strings are then used to also pre-fill the volumes: key.

This will cause obvious issues (but the image: key is kinda correct):

services:
    infinispan/server:
        image: "infinispan/server:${INFINISPAN/SERVER_VERSION}"
        container_name: "cacheman-infinispan/server-${CONTEXT}"

This won't cause issues (but you'll have to then go in and manually change the image: key to use infinispan/server):

services:
    infinispan:
        image: "infinispan:${INFINISPAN_VERSION}"
        container_name: "cacheman-infinispan-${CONTEXT}"

You're going to want to keep it simple and go with option 2.