Requirements
I was deep-diving on creating a decent-sized docker-compose
stack with 5+ services in them.
I was also on the look out for some Deployment Pattern where I didn't want to dump all my
Environment Variables into a giant .env
file, neither in Production nor in Development.
I decided to create a structure where each service aka. component would have their own dedicated
directory with their respective secrets (credentials) and their configuration files all bundled
together.
I decided upon the following structure:
ComponentName
├── config.component
└── secrets
└── .env.component
Here the ComponentName
could your favourite database, the secrets
directory within this directory
would contain a .env.database
file with all necessary admin credentials for the initial stack configuration.
The config.component
file is a dedicated configuration file (if needed)
I think the structure would work Okay since changes to a component's configuration would be reflected
a bit better in the Git History / Logs
Problem
If you end up having a lot of containers in the stack, docker-compose logs
only takes ever so far! You might
decide upon Portainer to make the container management a bit easier through it's UI.
One problem, Portainer doesn't support the Environment Variables Configuration pattern as by many container
based software applications.
It relies on executing command
in your docker-compose
file to introduce an password into your Portainer's service.
If you have a bit of Software OCD, you are going to hate it! One service not conforming to your dream stack deployment!
Approach
Here comes docker secret
to the rescue!
One massive Caveat, according to the documentation, docker secret
is only available for Swarm Setting, or it ?!
A blog post from Michael Irwin about using
docker secrets in Development provides a way to trick docker into accepting files as a docker secret and use it into your
service.
Another thing when going through Portainer's Documentation is that, it accepts admin password also through a
password file
We could see some Puzzle Pieces falling into place here!
Solution
Let's start with our structure mentioned above:
portainer
└── secrets
└── portainer-admin-password
|- docker-compose.yml
Instead of an .env
we rename the file to portainer-admin-password
where you can set your password.
Your docker-compose.yml
will then look as follows:
version: '3.7'
services:
portainer:
image: portainer/portainer-ce:latest
container_name: sys_portainer
restart: always
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command:
- '--admin-password-file=/run/secrets/portainer_admin_password'
- '--no-analytics'
networks:
- some_network
ports:
- "9000:9000"
secrets:
- portainer_admin_password
networks:
some_network:
external: true
secrets:
portainer_admin_password:
file: ./portainer/secrets/portainer_admin_password
Breaking it down functionally!
secrets:
portainer_admin_password:
file: ./portainer/secrets/portainer_admin_password
mounts the file as a docker secret into the compose file. Since it is a file mount, it will be
available in the following directory /run/secrets/<KEY_NAME_IN_SECRETS_section>
since our key
is the same name as that of the file (in order to avoid any discrepancy), the content of the file
will be consumed by the --admin-password-file
flag during the stack initialization.
Voilá! You now have a similar structure without having to hardcode passwords or sensitive information
into your docker-compose.yml
file
Top comments (0)