Docker compose - GradedJestRisk/cicd-training GitHub Wiki
https://docs.docker.com/compose/install/
Overview:
- all services share same network (log is
Creating network (..) with the default driver) - network aliases are created based on service's names and can be used anywhere in application source code
- commands
** start containers:
docker-compose up** start containers:docker-compose up -d** check docker-compose.ymlk is validdocker-compose config** build image, start container:docker-compose up --build** list running containersdocker-compose ps** execute a commanddocker-compose exec <SERVICE_NAME>** stop containers:docker-compose down
docker-compose.yml
<SERVICE_NAME>
container_name : <CONTAINER_NAME> (optionnal)
hostname: <HOST_NAME> (optional)
build:
context: <BUILD_CONTEXT>
dockerfile: <DOCKER_FILE>
ports:
- "<LOCAL_PORT>:<CONTAINER_PORT>"
volumes:
- <PRESERVE_CONTAINER_FOLDER>
- "<LOCAL_FOLDER>:<CONTAINER_FOLDER>"
environment:
-<ENVIRONMENT_VARIABLE_NAME>=<<ENVIRONMENT_VARIABLE_VALUE>
command: ["<COMMAND", "<PARAMETER_1>", "<PARAMETER_2>"]
See here to get working example.
Syntax :
$VARIABLE${VARIABLE}-
"$$VARIABLE"to prevent interpolation and get$VARIABLEso it will be interpolated somewhere else
Default :
-
${VARIABLE:-default}evaluates todefaultif VARIABLE is unset or empty -
${VARIABLE-default}evaluates todefaultonly if VARIABLE is unset -
${VARIABLE:-${FOO}}evaluate to$FOOif $VARIABLE is unset Mandatory : -
${VARIABLE:?message}: if VARIABLE is unset or empty, exits withmessage -
${VARIABLE?err}: if VARIABLE is unset, exits withmessage
https://docs.docker.com/compose/compose-file/12-interpolation/
Mind the precedence ! Having any ARG or ENV setting in a Dockerfile evaluates only if there is no Docker Compose entry. https://docs.docker.com/compose/environment-variables/envvars-precedence/
The order of precedence (highest to lowest) is as follows:
docker compose run --env FOO=bar- Substituted from your shell
<SERVICE_NAME>
environment:
- FOO=${FOO}- Set using just the environment attribute in the Compose file
<SERVICE_NAME>
environment:
- FOO=BAR- Use of the --env-file argument in the CLI
`docker compose --env-file .env.env
FOO=bar- Use of the
env_fileattribute in the Compose file
<SERVICE_NAME>
env_file:
- local.envlocal.env
FOO=bar- Set using an .env file placed at base of your project directory
.env
FOO=bar- Set in a container image in the ENV directive
ENV FOO=BAR
FROM alpinehttps://docs.docker.com/compose/environment-variables/set-environment-variables/
You can read them from .env file
POSTGRES_DB=somedbReference them with ${} as regular environment variable
docker-compose
environment:
POSTGRES_DB: "MY-${POSTGRES_DB}"Check result
docker compose configIf you want to use
- more than one
.envfile - name it differently or store it elsewhere Then you can supply their name and mandatory status in the service
<SERVICE_NAME>
env_file:
- path: ./default.env
required: true # default
- path: ./override.env
required: falseYou can always override these settings using CLI --env-file
If no name is given, the name will be $PROJECT_$SERVICE_1.
The project name comes from:
-
COMPOSE_NAMEenvironment variable; -
--project-name <PROJECT_NAME>parameter; - folder name.
You can override this name by supplying a container_name element, but beware it cannot be scaled.
You can express dependencies: container A needs container B to start successfully.
Short form : service-a will start as soon as service b is started
service-a :
depends_on:
- service-bLong form : service-a will start as soon as s
- service b has started and exist successfully
- service c has started and healthcheck has completed successfully
service-a:
depends_on:
service-b:
condition: service_completed_successfully
service-c:
condition: service_healthyReuse a service definition from another file
<SERVICE_NAME>
extends:
file: <COMPOSE_FILE_PATH>
service: <SERVICE_NAME>While you can override some attributes, if something is not allowed you'll get a nice Go compilation error DOC
docker compose pullIn docker-compose.yml
Policy:
- always
- missing
- never https://github.com/compose-spec/compose-spec/blob/master/spec.md#pull_policy
!! The spec exists but does not seem to be implemented https://stackoverflow.com/questions/37685581/how-to-get-docker-compose-to-use-the-latest-image-from-repository
docker compose up --detach <OPTIONAL_SERVICES_NAME_LIST>Pull (if needed - and this probably not work), build, start
docker compose up --pull missing --build <SERVICE>--wait option: wait for ALL services in file to be healthy before returning.
You'll need an healthcheck entry for each service.
Stateless: if an anonymous container is used, you can drop it using --renew-anon-volumes
Anonymous volumes are :
- reused if the service is stopped using
docker compose stop <SERVICE>; - discarded if the service is stopped using
docker compose down.
See https://github.com/docker/compose/issues/7444
Containers lifecycle is influence by compose file.
If it has changed, new containers will be created unless you provide --no-recreate
If it has not changed, but you want fresh containers, use --force-recreate
Define healthcheck command: it will be run in the container
- postgresql :
PGPASSWORD=<PASSWORD> psql -U <USER_NAME> -d <DATABASE_NAME> - http (if wget is available) :
sh -c "wget -q -O- http://localhost:8080/health" - keycloak : ``
Test it: it should return 0 when the service is healthy
docker exec --tty --interactive <CONTAINER_NAME> bash
<COMMAND>
echo $?Check how much time it take usually and enter it as start_period
In docker-compose.yml
- add
healthcheckproperty to the service you want to wait for
healthcheck:
test: "PGPASSWORD=permissions psql -U permissions -d permissions_db"
start_period: 1min
interval: 10s
timeout: 10s
retries: 5
- add
depends_onproperty to the service that will wait
depends_on:
<SERVICE_NAME>:
condition: service_healthy
If no service is to wait, you can use hello-world image.
services:
wait:
image: hello-world
container_name: wait
depends_on:
<SERVICE_NAME>:
condition: service_healthy
Then test
docker compose up --detachIf your service does not respond, you'll get an timeout error
✘ Container <CONTAINER_NAME> Error 162.8s
⠦ Container wait Recreated 152.5s
dependency failed to start: container <CONTAINER_NAME> is unhealthyIf the logs show that it has started successfully, you can debug healthcheck
docker inspect --format "{{json .State.Health }}" <CONTAINER_NAME> | jqYou'll get
>
{
"Status": "starting",
"FailingStreak": 1,
"Log": [
{
"Start": "2024-02-28T15:58:54.948143982+01:00",
"End": "2024-02-28T15:58:55.420688109+01:00",
"ExitCode": 1,
"Output": ""
}
]
} deploy:
restart_policy:
condition: on-failure
delay: 30s
max_attempts: 10
window: 5scondition:
- "no"
- on-failure
- unless-stopped
- always
docker compose down Use stop.
Then you can :
- start it again
start; - restart it
restart; - delete if
rm.
https://docs.docker.com/compose/compose-file/deploy/#resources
You can restrict memory and RAM, but not I/O https://superuser.com/questions/1306172/limit-usage-of-disk-i-o-by-docker-container-using-compose
Use deploy/resources/limits property
deploy:
resources:
limits:
cpus: '1'
memory: 128mIn brief:
- Compose sets up a single (docker) network, each container join it.
- each container is discoverable on this network, by the swarm's members, using the service's name.
- containers are NOT discoverable outside the swarm (by the host, running dockerd) - except if exposing ports using
portsmapping.
So you shouldn't use container_name or hostname property to pin down host name.
Long version
By default Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by the service's name.
Your app's network is given a name based on the "project name", which is based on the name of the directory it lives in. You can override the project name with either the --project-name flag or the COMPOSE_PROJECT_NAME environment variable.
https://docs.docker.com/compose/how-tos/networking/
You can also give a custom host name in hostname property, so you can avoid using localhost to refer to it.
This will enforce 12-factor app rules.
This will NOT resolve name from your OS, only from containers. To do it from your OS, you can use /etc/hosts
127.0.01 $HOST_NAME
Name services using kebab-case: my-service.
Publish as few ports as possible in ports, prefer not exposing them on host unless mandatory, as this impose that all ports are unique. But always declare ports used by your application using expose.
Use .env file to store common values.
Do NOT use container_name or hostname property (display will be less readable in docker ps, but you will not be annoyed when using the same name between project, as docker will refuse to start a service with the same name, unless you remove the container first).
Add healthchecks and use --wait on up.
If you need to start an application not in swarm, use hello-world with dependencies.
services:
dummy-service-to-get-all-working-properly:
image: hello-world
depends_on:
service-with-healthcheck:
condition: service_healthy
service-without-healthcheck:
condition: service_startedThen start.
docker compose up dummy-service-to-get-all-working-properlyYou will soon need these options also.
--detach --remove-orphans --force-recreate