It has been requested that extending services should work with multiple services, where each one may add additional bits of configuration.
Something along these lines:
services:
base-has-term:
environment:
TERM: xterm-256color
base-has-code:
volumes:
- .:/code
web:
extends:
- base-has-term
- base-has-code
...
This is related to #1988, because the idea of an "abstract" or "base" service will make this much neater (in fact, the example above assumes there is this sort of concept).
I requested this. Here is why.
Containers share configuration; usually when there is a one-to-many relationship going on. If you want to define a database and have several containers access it then all of those containers are going to need to know the DSN, along with some other settings that form a little collection. I think that using env_file declarations is a non-solution to this, as it only address environment variables and not other declaration types. I might want this little collection of settings to include a depends_on
, or if I have put this database on a separate network I would want to include that network in my networks
. Perhaps this database lives externally and requires an external_links
. You get the point.
I think that a mixin-style approach would be a very neat solution. Through it we would be able to cleanly group all configuration relating to the access of a particular container, right there in the same docker-compose.yaml. And, if you think about it, we are half-way there already. We have the extends
declaration. The demand for extends
proves that people need to share configuration between containers - I'm just suggesting that we take it one logical step further and allow people to extend from many. Inheritance has majorly fallen out of favour recently in the OO space, and for good reason - it only allows you to choose once. As soon as you want to chop things up differently you have to hack around your existing structure. "prefer _composition_ over inheritance", right? Should we not follow the same rubric here? I know that some will raise concerns over the need for simplicity, but I think that is wrong-headed. While I agree that at the Dockerfile level we don't want to have, for example, a bind-mount because then the image is inherently non-shareable, I think that in the case of docker-compose we are already talking about a collection of interconnected containers which make up a whole system - the parts of which are implicitly not extractable in the way that an isolated Dockerfile is. Remember, for every complex problem there is an answer that is clear, simple, and wrong. I think that mixins would walk the correct line between (marginal) additional complexity and beneficial utility.
@dnephin I saw that you downvoted the original issue. I have added some reasoning and would be interested in your opinion.
Thanks for adding more information.
There is always a trade-off between being more explicit (adding the options to every service) and trying to reduce duplication (supporting ways to share options).
It almost sounds like what is desired here is something like "roles" (as it would be called in puppet), were you can assign a block of options because the node has some role (db client).
So far in my experience I've always opted to try and keep the options specified in the compose file to a minimum. Once it starts to grow, I'll move it to a file and use volumes_from
, or an env_file
. As you've mentioned the issue is that it's not always application config, sometimes it's actually container configuration that is shared.
I would agree with "prefer composition over inheritance", however I don't see mixins as composition, mixins are multiple-inheritance. If we wanted to use composition, maybe one way to do that would be to support an idea of a "role".
@dnephin Indeed, I also think this is related to config management. I imagine chef, salt, ansible, et al. have an equivalent to puppet's roles. I think that multiple inheritance, mixins, and this concept are in a similar space, especially because we are limiting ourselves to data representation (there's no polymorphism, method overloading, etc. when you're talking about a config file). However, I think that this is a bit different to how roles operate, because with roles you are composing modules together and I don't think that they interact all that much (?). Whereas here we want to be able to interleave the definitions.
Stripping it down, the concrete behaviour needed is to define the shared "atoms" once, usually as part of aggregate sets, and be able to create aggregates of those aggregate sets. I can think of three classes of atoms present in docker-compose.yml files:
dockerfile
, image
, depends_on
, ...)volumes
, ports
, networks
, ...)environment
, logging
options
, driver_opts
, ...)For each some form of aggregating behaviour must be defined:
I think that there are two main use-cases. Firstly where the service "is-a" type of a common base service, and secondly where the service "has-a" common collection of settings (OO terms only used here for the purpose of analogy).
Applications (or some subset of services) which look like:
It seems that there have been a number of efforts to solve this problem that have each provided a part-solution. The extends definition, multiple files passed on the command line, env_file declarations, and variable substitution are all different ways to switch out parts of the docker-compose.yml file under different circumstances. I think that the approach outlined here could obviate much of the need for the first three, and potentially provide a better way to switch out settings that differ between dev/test/prod (which is I think the main utility of variable substitution).
I think that ultimately the most intuitive way to represent this is all in one place - in the docker-compose.yml file - rather than being woven through an assemblage of discordant files which are awkwardly, and confusingly, tangled up together.
I'm currently using the following code to achieve this, though I'd much _much_ rather use something native https://gist.github.com/dunk/8d258be750c7da048f52
The extends definition, multiple files passed on the command line, env_file declarations, and variable substitution are all different ways to switch out parts of the docker-compose.yml file under different circumstances. I think that the approach outlined here could obviate much of the need for the first three...
I can understand how mixin-like behaviour would replace/enhance extends
(essentially by providing a superset of its functionality), but I don't see how it would do the job of multiple -f
arguments or the env_file
option. In both of those cases, the presence of a separate file seems key, as it allows the core configuration to remain a static, version-controlled part of the app, while the file containing environment-specific configuration is supplied at runtime.
Could you clarify how you see mixins working to serve the same use cases?
@aanand Sure thing. On closer examination I was wrong about multiple -f
arguments, but I think I am right about env_file
. By including a mixin declaration containing the env vars in the file passed to -f
, we can apply these env vars to multiple services but still have them be dependent on which environment we are in. No external file needed.
There are two aspects at play - env-dependant composition and reusable grouping.
| feature | env-dependant composition | reusable grouping |
| --- | --- | --- |
| -f
| yes | no |
| env_file
| yes | yes |
| mixins | no | yes |
Of course, -f
and mixins are applicable much more generally than env_file
. I think it feels analogous to factoring depends_on
and networks
out from links
.
Is there any chance of this issue being on the roadmap at the moment?
As for shared configuration inside the docker-compose.yml -- I would look into yaml anchors ('variables' for sharing structure/values).
As for true mix-ins (what you are asking for)-- I believe (and please correct me if I'm wrong), but the role of docker-compose, specifically, is to handle already built images and putting them together in a cohesive set of inter-working containers. Mix-ins seem to me hinting towards the image creation process sharing functionality, not containers.
On the image building side-- while it may be a bit hackish-- I am currently working on a project that involves packaging up functionality (such as loading zsh, oh-my-zsh, vim, etc.) into a shell script that are executed (and therefore added as an image layer) given a specific --build-arg such as
docker build -f shell.Dockerfile --build-arg MIXINS='vim zsh'.
@colinross Anchors don't solve this problem - only a subset. You cannot, for instance, interleave items from dictionaries or lists - you can only reference a static predefined lump of stuff.
I think that the mix-ins idea is actually inevitable. There are many things that are going to apply to multiple containers - a database username / password combo, for instance, for every container which accesses that container. While this particular case can be solved by an env var file, other cases can't (particularly when groups of directives cluster - say a volumes_from
+depends_on
or particular environment
+volumes
). Without something isomorphic to mix-ins you must repeat these declarations in every container's definition in the yaml.
Indeed that is a hack. Wouldn't it be nicer if there was a way to have it built in?
Hello,
Any news for this function ?
I would still love it too, fwiw.
Almost 1 year passed - any news? I would also love that feature
Almost 1 year passed - any news? I would also love that feature
True love never dies :)
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically marked as not stale anymore due to the recent activity.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically closed because it had not recent activity during the stale period.
Most helpful comment
I would still love it too, fwiw.