docker-compose how to pass env variables into file - docker-compose

Is there any chance to pass variables from docker-compose into apache.conf file?
I have Dockerfile with variables
ENV APACHE_SERVER_NAME localhost
ENV APACHE_DOCUMENT_ROOT /var/www/html
I have apache.conf which I copy into /etc/apache2/sites-available/ while building image
ServerName ${APACHE_SERVER_NAME}
DocumentRoot ${APACHE_DOCUMENT_ROOT}
I have docker-compose.yml
environment:
- APACHE_SERVER_NAME=cms
- APACHE_DOCUMENT_ROOT=/var/www/html/public
When I run docker-compose, nothing happened and apache.conf in container is unchanged.
Am I completely wrong and is this imposible or am I missing any step or point?
Thank you

Let me explain some little differences among ways to pass environment variables:
Environment variables for building time and for entrypoint in running time
ENV (Dockerfile): Once specified in Dockerfile before building, containers, will have environment variables for entrypoints.
environment: The same than ENV but for docker-compose.
docker run -e VAR=value... The same than before but for cli.
Environment variables only for building time
ARG (Dockerfile): They won't appear in deployed containers.
Environment variables accesibled for every container and builds
.env file defined in your working dir where executes docker run or docker-compose.
env_file: section in docker-compose.yml file to define another .env file.
As you're trying to define variables for apache conf file, maybe you should try to use apache as entrypoint, or just define them in .env file.

Related

docker-compose environment variable loading .env, but not env_file from compose file

I don't really make sense of docker-compose's behavior with regards to environment variables files.
I've defined a few variables for an simple echo server setup with 2 flask application running.
In .env:
FLASK_RUN_PORT=5000
WEB_1_PORT=80
WEB_2_PORT=8001
Then in docker-compose.yml:
version: '3.8'
x-common-variables: &shared_envvars
FLASK_ENV: development
FLASK_APP: main.py
FLASK_RUN_HOST: 0.0.0.0
COMPOSE_PROJECT_NAME: DOCKER_ECHOES
x-volumes: &com_volumes
- .:/project # maps the current directory, e.g. project root that is gitted, to /proj in the container so we can live-reload
services:
web_1:
env_file: .env
build:
dockerfile: dockerfile_flask
context: .
ports:
- "${WEB_1_PORT}:${FLASK_RUN_PORT}" # flask runs on 5000 (default). docker-compose --env-file .env up loads whatever env vars specified & allows them to be used this way here.
volumes: *com_volumes
environment:
<<: *shared_envvars # DRY: defined common stuff in a shared section above, & use YAML merge language syntaxe to include that k-v mapping here. pretty neat.
FLASK_NAME: web_1
web_2:
env_file: .env
build:
dockerfile: dockerfile_flask
context: .
ports:
- "${WEB_2_PORT}:${FLASK_RUN_PORT}" # flask by default runs on 5000 so keep it on container, and :8001 on host
volumes: *com_volumes
environment:
<<: *shared_envvars
FLASK_NAME: web_2
If I run docker-compose up with the above, everything works as expected.
However, if I simply change the name of the file .env for, say, flask.env, and then accordingly change both env_file: .env to env_file: flask.env, then I get:
(venv) [fv#fv-hpz420workstation flask_echo_docker]$ docker-compose up
WARNING: The WEB_1_PORT variable is not set. Defaulting to a blank string.
WARNING: The FLASK_RUN_PORT variable is not set. Defaulting to a blank string.
WARNING: The WEB_2_PORT variable is not set. Defaulting to a blank string.
ERROR: The Compose file './docker-compose.yml' is invalid because:
So obviously the envvars defined in the file were not loaded in that case. I know that according to the documentation, the section environement:, which I am using, overrides what is loaded in the env_file:. But those aren't the same variables. And at any rate, if that was the issue, it shouldn't work either with the first way, right?
What's wrong with the above?
Actually, the env_file is loaded AFTER the images have been built. We can verify this. With the code I have posted above, I can see that env_file.env has not been loaded at build time, because of the error message that I get (telling me WEB_PORT_1 is not set etc.).
But this could simply be that the file is never loaded. To rule that out, we build the image (say by providing the missing arguments with docker-compose build -e (...), then we can verify that it is indeed loaded (by logging its value in the flask application in my case, or a simple print to screen etc.).
This means the the content of env_file is available to the running container, but not before (such as when building the image).
If those variables are to be used within the docker-compose.yml file at BUILD time this file MUST be named .env (unless there is a way to provide a name other than the default, but if so I haven't found any). This is why changing env_file: flask.env to env_file: .env SEEMED to make it work - but the real reason why it worked then was because my ports were specified in a .env with the default name that docker-compose parses anyways. It didn't care that I specified it in docker-compose.yml file or not.
To summarize - TL;DR
If you need to feed environment variables to docker-compose for build-time, you must store them in a .env. No further actions needed, other than ensure this file is in the same directory as the docker-compose.yml. You can't change the default name .env
To provide envars at container run-time, you can put them in foo.env and then specify env_file:foo.env.
For run-time variable, another option is to specify them environment: [vars], if just hard-coding them in the docker-compose.yml is acceptable.. According to doc (not tested) those will override any variables also defined by the env_file

Copy files to host from project folder and use them inside my container

I have a docker-compose file to build three different images from 3 different Dockerfiles.
In my project structure, I have some files that I want to copy to a specific folder in my host machine, because those files will be used by one of the containers.
I donĀ“t want those folders to be inside of my container, because anytime I change something on one of the files, I need to build the image again.
Instead of using command line to copy the files from my project to the host machine, how how can it be done automatically when I run docker-compose build?
Unfortunately docker-compose has no such concept as pre- or post-scripts. The simplest way of achieving your goal would be creating a shell script which would copy the files and call docker-compose. If for some reason you are limited to calling docker-compose itself, you can create additional "setup" container to copy the files before starting other containers:
version: "3"
services:
# setup container copies files from the project directory PROJECT_DIR
# to another directory HOST_DIR on the host machine
setup:
image: alpine:latest
volumes:
- ${PROJECT_DIR}:/project
- ${HOST_DIR}:/host
command: >
sh -c "cp -R /project/* /host/"
# service container depends on the setup container and uses files from
# the host machine directory HOST_DIR mounted to CONTAINER_DIR
service:
image: alpine:latest
depends_on:
- setup
volumes:
- ${HOST_DIR}:${CONTAINER_DIR}
command: >
sh -c "ls ${CONTAINER_DIR}"

Is it possible to have environment and env_file in Docker Compose?

I am new to Gitlab CI/CD and I have the following issue.
Suppose I have some environment variables in my local setup in an .env file. Something like this:
SOME_URL=https://someurl.com/
SECRET_KEY=verysecretkey
For my setup to work, I need both of these environment variables. The SECRET_KEY is not in the .env for the deployment. It is in the "secrets" in GitLab. If I have something like this in my Docker Compose file:
environment:
SECRET_KEY: ${SECRET_KEY}
env_file:
- .env
My two questions are:
In my local setup, will I have in my environment variables both SECRET_KEY and SOME_URL?
In GitLab, am I going to be able to replace SECRET_KEY with secrets?
Thanks for your answers in advance!
The variables defined in Gitlab CI/CD are available inside the pipeline (.gitlab-ci.yml). In the step, you are executing the docker-compose command, the variable will be replaced then set in your container thanks to environment: SECRET_KEY: ${SECRET_KEY}. That should be fine.
https://docs.gitlab.com/ee/ci/variables/
https://docs.docker.com/compose/compose-file/#variable-substitution

Apply label to docker compose service depending on environment configuration?

Say I have a docker-compose.yml like so:
version: "2.1"
services:
web:
image: foo
cli:
image: bar
Upon docker-compose up, depending on the value of an environment variable, I would like to add a specific label to either the web service or the cli service, but never both.
What are some solutions for this?
EDIT: An additional stipulation is that the compose file can have an arbitrary set of services in it (i.e. the set of services is not constant, it is variable).
You might want to split your compose.yml file and add some shell scripting around docker to achieve this.
So you could create a bash script that checks your environment variable, and switches the appropriate yml files into the 'docker compose up' command it calls.

docker-compose run does not set environment variables

I do use docker-compose and have a .env file containing a environment variable:
KEY=VAL
Additionally I have the following in my docker-compose.yml:
version: '3'
services:
webapp:
build: ./dir
environment:
- KEY={$KEY}
If I run docker-compose build as well as docker-compose up the environment variable KEY is accessable in the container.
If I now run some command: docker-compose run webapp echo $KEY
It prints nothing, so so guess it is not set. Is that normal behaviour, or do I miss something substantial?
Thanks in advance