_From @dhanvi on March 10, 2018 18:54_
Few people might have a use case to pass environment variables with a flag like -e.
docker stack deploy -c file.yml -e name=value stack_name
Labels: area/cli kind/feature
_Copied from original issue: moby/moby#36554_
_From @AkihiroSuda on March 11, 2018 14:16_
CLI is out of the scope of this repo.
Please refer to https://github.com/docker/cli
+1
+1
@dhanvi Thanks for the issues. That said, I have a small question about the feature request
Few people might have a use case to pass environment variables with a flag like -e.
I can see two different features requested here :
${name}), this is then equivalent to env name=value docker stack deploy@marcel-steinbach-mpf @jeanadrien @mallchin it would be helpful for us to know which one is it :angel: :pray:
@vdemeester I am just looking for the option docker stack deploy -e name=value stack_name, I gave an example of -c for reference here. (equivalent to env name=value docker stack deploy is what I have in mind.)
I am assuming that the variable in here docker stack deploy -c file.yml -e name=value stack_name will only be applicable to the service in the compose file. (It doesn't makes sense for to add a variable for the whole stack, my assumption is that variables are scoped to the service only, can they be scoped for the whole stack ?)
@vdemeester's question was because there are two "levels" of environment variables:
For example, taking this docker-compose.yml:
version: "3.6"
services:
one:
image: "nginx:alpine"
environment:
foo: "bar"
SOME_VAR:
baz: "${OTHER_VAR}"
labels:
some-label: "$SOME_VAR"
two:
image: "nginx:alpine"
environment:
hello: "world"
world: "${SOME_VAR}"
labels:
some-label: "$OTHER_VAR"
env SOME_VAR="I am some var" OTHER_VAR="I am other var" docker stack deploy -c docker-compose.yml envie
Inspecting environment variables on service one (envie_one):
docker service inspect --format='{{ json .Spec.TaskTemplate.ContainerSpec.Env}}' envie_one
Shows:
[
"SOME_VAR=I am some var",
"baz=I am other var",
"foo=bar"
]
And its labels:
docker service inspect --format='{{ json .Spec.TaskTemplate.ContainerSpec.Labels}}' envie_one
Shows:
{
"com.docker.stack.namespace": "envie",
"some-label": "I am some var"
}
Inspecting environment variables on service two (envie_two)
docker service inspect --format='{{ json .Spec.TaskTemplate.ContainerSpec.Env}}' envie_two
[
"hello=world",
"world=I am some var"
]
And its labels:
docker service inspect --format='{{ json .Spec.TaskTemplate.ContainerSpec.Labels}}' envie_two
Shows:
{
"com.docker.stack.namespace": "envie",
"some-label": "I am other var"
}
Environment variables that are _substituted_ while processing the docker compose file are already possible;
export MYVAR=myvalue)env MYVAR=myvalue)What's _not_ supported (yet?) is the use of an .env file when deploying stacks: https://github.com/moby/moby/issues/29133. I'd personally be good with adding support for .env files, so that "environment" variables are set when deploying a stack, without the requirement to export those variables up-front (or set them manually on each run).
What I _don't_ think should be done is add an --env foo=bar option that would set the foo=bar environment variable on each service in the stack. Reasons I think we should not be adding that:
Use variable substitusion, and set the environment-variable in your shell (having support for an .env file would help with this):
version: "3.6"
services:
one:
image: "nginx:alpine"
environment:
SOME_VAR:
Now, deploy the stack using either:
export SOME_VAR=some-value
docker stack deploy -c docker-compose.yml foobar
Or
env SOME_VAR=some-value docker stack deploy -c docker-compose.yml foobar
For example: during development, I want to set some additional environment variables, but in production, I don't want those set (or vice-versa).
Use an "override" file, by deploying multiple docker compose files:
docker-compose.yml:
version: "3.6"
services:
one:
image: "nginx:alpine"
environment:
somevar: "somevalue"
two:
image: "nginx:alpine"
docker-compose-dev.yml:
version: "3.6"
services:
one:
environment:
debug: "1"
two:
environment:
development: "yes"
Specify both files when deploying the stack:
docker stack deploy -c docker-compose.yml -c docker-compose-dev.yml mystack
And see that the additional environment variables are set;
docker service inspect --format='{{ json .Spec.TaskTemplate.ContainerSpec.Env}}' mystack_one
["debug=1","somevar=somevalue"]
docker service inspect --format='{{ json .Spec.TaskTemplate.ContainerSpec.Env}}' mystack_two
["development=yes"]
Having said the above; I'm interested to hear what the exact use-case is that people here are looking for. Perhaps you can explain what you want to use this for, and show examples how it will be used.
My use case is the first one you describe:
- Environment variables that are substituted when processing the docker compose file
Specifically, I have two files dev.env and prod.env for development and production that each export a bunch of variables. And when I run docker stack I do it like this:
$ . ./dev.env && docker stack deploy -c run.yml torro-run
Or,
$ . ./prod.env && docker stack deploy -c run.yml torro-run
Within those env files I also have the following for variables in common across prod and dev:
. ./shared.env
As long as the user remembers to source the file, it works great. It would be a little nicer to have support for multiple .env files, but the approach we have now seems to work.
Thanks for the additional information!
Okay, so looks like the desired feature would be to (for a start) make docker stack deploy read an .envfile from the current directory (same as docker-compose up does).
From https://github.com/moby/moby/issues/29133#issuecomment-264890488, I see that @dnephin was open to discuss that option, but looks like he had some reservations;
This is by design. The
.envsupport is a feature of Compose, not of the file format.
We can discuss adding this for a future release, but I don't know if it's really the best option.
Some questions, as I know there are various limitations to the current .env files;
key=value syntax (no special treatment of quotes (", '") in values)?.env file thus will not be useful for Bash/shell (i.e., export foo=bar won't work)FOO=${BAR}-something will literally use ${BAR}-something as value for the FOO env-var)In addition to the above, I think it would be useful to print an informational message that docker read variables from that file (so that it doesn't catch a user by surprise).
I'm busy with GDPR right now but will revisit this next week. I had a specific use case and will see how it fits in with the above. I expect setting environment variables on the command line might work for me but will confirm shortly.
I have a slightly different scenario:-
I am using a standard docker-compose.yml file to define my stack. I wish to build the containers via "docker-compose build" and then deploy into my swarm via "docker stack deploy".
Due to way our CI/CD pipeline is setup, we explicitly tag all of our Docker images with the product release name and version. As Docker Compose supports environment variable substitution, I can define the "image" tags within the "build" section as follows:-
my-service-name:
image: my-service-name:${CODENAME}-${TAG}
build:
context: ${GIT_ROOT}/my-service-name-repo
The result of the build is a suitably tagged image.
What I would like to happen is for the same docker-compose.yml file to support environment variable substitution via the call to "docker stack deploy" when deploying my stack such that the correct images are deployed. My workaround for now is to set those environment variables via the shell, but it would be good to see a way to set these on the command line call.
Happy to take advice if there is a better or more Docker compliant way of achieving this.
I'm deploying from CircleCI and have the same need as @gbjbaaha. Unfortunately it not as easy as exporting the variable to the environment because in CircleCI the docker environment is remote, so I'd have to ssh in and export them there. This requires a lot of setup because again the machine where the docker commands are executed is remote, see setup_remote_docker
+1
I'm also using a tag like @gbjbaaha , in the .env file: VERSION_TAG=v1.2.3 and in docker-compose: image: organization-name/image-name:$VERSION_TAG. And, to upgrade all images, a cron job updates the version in .env, then does docker-compose down and then up, meaning, all services restart with the new image versions (so they don't need to be compatible with old versions of the other services API:s — since everything upgrade at once to the same new version).
@gbjbaaha if I understood you correctly, replacing a $TAG variable, works, if one does this: ?
VERSION_TAG=v1.2.3 docker stack deploy -c stack-file.yml
So, this: VERSION_TAG=v1.2.3 docker stack ...
is "the same as" an .env file with VERSION_TAG=v1.2.3, and docker-compose ...?
Hmm maybe the script could update the version number directly in the stack-file.yml as well, instead of indirectly via the .env file. The version number would then be hardcoded at many places in the stack-file.yml ... meaning, a little bit duplication, and maybe that's fine.
Yes, variable substitution via environment variables set in the shell as suggested by @kajmagnus works correctly.
Ok thanks @gbjbaaha for the info :- )
+1
+1
There is this work around docker-compose config | docker stack deploy --compose-file - test That will create your docker-compose.yaml but replace with the variables from .env
Another use case:
How to force a container's (task) environment variables to receive its values from the environment variables of the host in which it is running? (This is for each task in a "docker stack deploy" running in several swarm nodes)
I have a service defined in a "docker-compose.yml" file, like:
openlegacy-config-server:
image: credicorpbankinnovacion/config-server:4.2.4.b1914
environment:
- SPRING_PROFILES_ACTIVE=docker,elk-logs
- OL_ENCRYPTION_KEY=changeme
- OL_ECOSYS_DB_SRV=${OL_ECOSYS_DB_SRV}
- OL_ECOSYS_SRV=${OL_ECOSYS_SRV}
build: openlegacy-config-server/
networks:
- ol-network
# This deploy config is for QA & PROD (those envs use Docker Swarm)
deploy:
mode: replicated
replicas: 2
placement:
constraints:
- node.role == manager
depends_on:
- openlegacy-service-discovery
restart: always
This service is to be deployed unto two nodes of a docker swarm using docker stack deploy
For each node I have defined the environment variables:
NODE A:
OL_ECOSYS_SRV=192.168.10.149
OL_ECOSYS_DB_SRV=192.168.10.123
NODE B:
OL_ECOSYS_SRV=192.168.10.143
OL_ECOSYS_DB_SRV=192.168.10.117
But the expressions ${OL_ECOSYS_SRV} and ${OL_ECOSYS_DB_SRV} are evaluated at the moment of entering the command "docker stack deploy" in node A, so the different IP address effect depending on the node if not achieved, and all containers (tasks) in both nodes are created with the same IPs of node A because the variables are evaluated at the service creation moment and not at the task creation moment.
How could I pass to the containers in node A and B a different local dns configuration (/etc/hosts) depending on the node itself? Is there another approach to solve this problem?
This must work for a single but replicated docker swarm service.
Which approach would you use?
There is this work around
docker-compose config | docker stack deploy --compose-file - testThat will create yourdocker-compose.yamlbut replace with the variables from.env
Good workaround, but will not work in cases docker-compose is not installed, only docker is
@ejyanezp
Hey man, did you manage to find an approach for your use case?
There is this work around
docker-compose config | docker stack deploy --compose-file - testThat will create yourdocker-compose.yamlbut replace with the variables from.envGood workaround, but will not work in cases docker-compose is not installed, only docker is
My entire workflow is failing at this point with external networks defined on the docker-compose giving an error only if this method is used instead hardcoding configs. So this is failing parsing external networks or in any other way:
docker-compose config -> the parsed definition contains your external-network-name: null and then docker deploy prints this error as a consecuence.
Additional property name is not allowed
I really don't know how to transition from docker-compose to Swarm, where you set a development and production scenarios.
How can you make this possible, when your docker-compose workflow is meant to reading ${VARIABLES} from .env file but you are not intended to use this approach when you are full on docker Swarm?
I just read about secrets but I want to define not secret configs from .env like hostnames and use secrets like passwords etc.. without the need of create secret manually for every ${VARIABLE} i was used to put in .env file when docker-compose times.
There is this work around
docker-compose config | docker stack deploy --compose-file - testThat will create yourdocker-compose.yamlbut replace with the variables from.envGood workaround, but will not work in cases docker-compose is not installed, only docker is
My entire workflow is failing at this point with external networks defined on the
docker-composegiving an error only if this method is used instead hardcoding configs. So this is failing parsing external networks or in any other way:
docker-compose config-> the parsed definition contains yourexternal-network-name: nulland thendocker deployprints this error as a consecuence.Additional property name is not allowedI really don't know how to transition from docker-compose to Swarm, where you set a development and production scenarios.
How can you make this possible, when your docker-compose workflow is meant to reading ${VARIABLES} from.envfile but you are not intended to use this approach when you are full on docker Swarm?
My workaround:
deploy.sh:
#!/bin/sh
export $(cat .env) > /dev/null 2>&1;
docker stack deploy -c docker-compose.yml ${1:-STACK_NAME}
. deploy.sh <stack_name>
(stack_name optional if previously defined in .env)
@ejyanezp
Hey man, did you manage to find an approach for your use case?
Nope :(
Most helpful comment
There is this work around
docker-compose config | docker stack deploy --compose-file - testThat will create yourdocker-compose.yamlbut replace with the variables from.env