-
Notifications
You must be signed in to change notification settings - Fork 0
Docker 3 Docker structure
Our Docker setup is defined in aikon/front/docker/docker-compose.yml.
Good to know:
- all paths provided in
docker-compose.ymlare relative to the directory where the file is located, (aikon/frot/docker/). -
when several values are defined for a directive (e.g.
volumes,env_file) and some enter in conflict, the last one defined will be used.
In many cases, this section will be useless, but if your organization uses a proxy, this is very critical for your application to work. Docker requests to internet will be proxied using the proxy server, but internal requests (localhost and requests between containers) will not be proxied.
x-proxy-settings: &proxy-settings
HTTP_PROXY: ${HTTP_PROXY:-}
HTTPS_PROXY: ${HTTPS_PROXY:-}
NO_PROXY: "localhost,127.0.0.1,aiiinotate,mirador,cantaloupe,web,mongo,nginx,redis,db,.aikon_network"For further troubleshooting, you can check the Proxy nightmare section in Troubleshooting.
The web container contains the Django application and encapsulates several other processes (gunicorn and celery) managed by supervisord.
web:
build:
context: .. # we build the image from the parent directory of the current one.
dockerfile: docker/Dockerfile # we use the Dockerfile located in the `docker` directory (relative to the context)
args: # arguments passed to the Dockerfile (extracted from .env files in the env_file section)
USERID: ${USERID} # allow docker/Dockerfile to use `ARG USERID`
PORT: ${DJANGO_PORT} # allow docker/Dockerfile to use `ARG PORT`
volumes:
- ${DATA_FOLDER}:/data # bind the $DATA_FOLDER (absolute path on the host machine) to /data inside the container
- ../app:/home/aikon/app # the content of app is copied in Dockerfile but with this line, it will be updated if content is altered in the container
env_file:
- ../app/config/.env # load environment variables from the django .env file
- ./.env # load environment variables from the docker/.env file (⚠️ if any variable is defined in both files, the one defined in the second file will be used)
environment:
<<: *proxy-settings # use the proxy settings defined above
ports:
- "${DJANGO_PORT}:8000" # bind the port defined in the .env file to port 8000 inside the container
depends_on: # the web container will be started only after the db and redis containers are started
- db
- redis
networks:
- aikon_network # the container has access to the aikon_network
restart: unless-stopped # the container will restart if the machine rebootsDockerfile: this file is the set of instructions to build the image of the web container. It contains the following steps:
- Compile Svelte code
-
Base image: the image is based on
ubuntu:22.04 -
Set variables: the
USERIDandPORTvariables are passed as build arguments to the Dockerfile - Install dependencies: install necessary system packages
-
Copy/Create files and set permissions: create necessary directories and copy content of
app/folder - Install Python deps
- Expose port: expose the port on which the application will run
-
Run: the
RUNcommands contains- `manage.sh: the final configs that require other containers to run
-
supervisord: run the application usingsupervisord(configured indocker/supervisord.conf)
The db container contains the PostgreSQL database. It is configured to use a volume to persist data.
It uses an official docker image so configuration is easy.
db:
image: postgres:14 # official postgres image (when upgrading, look at #Postgres-version section for instructions)
command: postgres -p ${DB_PORT:-5432} # defining this command allows us to use a custom port
volumes:
- pgdata:/var/lib/postgresql/data # bind pgdata volume to /var/lib/postgresql/data inside the container
env_file:
- ../app/config/.env # to get $POSTGRES_DB, POSTGRES_USER, $POSTGRES_PASSWORD, $DB_PORT
networks:
- aikon_network # the container has access to the aikon_network
ports:
- "${DB_PORT:-5432}:${DB_PORT:-5432}" # <port_outside_of_container>:<port_inside_of_container> => $DB_PORT or 5432 if not defined
restart: unless-stopped # the container will restart if the machine reboots
healthcheck: # command that is run to check whether the container runs correctly
test: [ "CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER} -p $${DB_PORT:-5432}" ]
interval: 10s
timeout: 5s
retries: 5The cantaloupe container contains the IIIF image server: it serves images to the web application.
The image is built from the cantaloupe folder, which contains a Dockerfile and a cantaloupe.properties file.
cantaloupe:
build:
context: .. # The container is built in the front/ directory
dockerfile: cantaloupe/Dockerfile # The Dockerfile is thus located in the front/cantaloupe/ directory
env_file:
- ./.env # Get $CANTALOUPE_PORT
ports:
- "${CANTALOUPE_PORT:-8182}:${CANTALOUPE_PORT:-8182}" # <port_outside_of_container>:<port_inside_of_container> => $CANTALOUPE_PORT or 8182 if not defined
volumes:
- ${DATA_FOLDER}:/data # bind the $DATA_FOLDER (path on the host machine) to /data inside the container
networks:
- aikon_network # the container has access to the aikon_network
environment:
<<: *proxy-settings # use the proxy settings defined above
user: root # run the container as root userCantaloupe is configured using the cantaloupe.properties file, which is generated by the init.sh script.
The cantaloupe.properties tells Cantaloupe 🍈 to use /data/mediafiles/img as the root directory for images.
By setting volumes to ${DATA_FOLDER}:/data, Cantaloupe will directly access the images stored in ${DATA_FOLDER}/mediafiles/img folder on the host machine.
The Cantaloupe Dockerfile is the set of instructions to build the image for the cantaloupe container.
It contains the following steps:
-
Base image: the image is based on
openjdk:11-jdk -
Working directory: the working directory is set to
/cantaloupe -
Copy files: the content of
cantaloupe/folder is copied to the working directory -
Start script: the
start.shpermissions are updated and then is executed
The command inside the start.sh script must be run with sudo or as root user.
It launched the Cantaloupe server with the cantaloupe.properties file as argument.
Mirador 4 is used to visualize IIIF manifests, view them, and view, create, modify and delete IIIF annotations. Those annotations are stored in an aiiinotate instance.
mirador:
build:
context: ..
dockerfile: annotations/Dockerfile-mirador # we create a Mirador Dockerfile ourselvers. It bundles together Mirador 4 and the Mirador-Annotation-Editor plugin.
args:
PORT: ${MIRADOR_PORT} # port Mirador works on
ENV_PATH: ../app/config/.env # our config file
env_file:
- ../app/config/.env
depends_on:
- aiiinotate # is dependent on aiiinotate to store annos
ports:
- "${MIRADOR_PORT}:${MIRADOR_PORT}" # external and internal Docker ports are the same.
networks:
- aikon_network
environment:
<<: *proxy-settings
restart: alwaysThe Dockerfile-mirador contains the following steps:
- Pull a NodeJS image
- Setup environment (read arguments, copy .env file ocally)
- Pull system packages
- Setup your NPM project (create it and install packages, mostly Mirador and Mirador-annotation-editor)
- Chmod run script so that is can be executed
- Run: this will serve an HTML file with JS that bundles together a Mirador instance with MAE connected to the Dockerized aiiinotate instance.
aiiinotate is a IIIF annotation server used to store results of Region extractions as IIIF annotations. It relies on the mongo container.
aiiinotate: # container name
build:
context: ..
dockerfile: annotations/Dockerfile-aiiinotate # the dockerfile, stored in `aikon/frot/annotations`
args: # build arguments
PORT: ${AIIINOTATE_PORT} # the port aiiinotate runs on
ENV_PATH: ../app/config/.env # path to our .env
env_file:
- ../app/config/.env # path to our .env
ports:
- "${AIIINOTATE_PORT}:${AIIINOTATE_PORT}" # port aiiinotate will run on
extra_hosts:
# fixes aiiinotate-to-web DNS resolution failures: adds an /etc/hosts entry
# that maps "${PROD_URL}" to the host machine's Docker bridge IP (host-gateway),
# bypassing Docker's internal DNS entirely. Requests then reach NGINX directly
# without a DNS lookup.
- "${PROD_URL}:host-gateway"
depends_on:
mongo: # mongo DB where annos. are stored
condition: service_healthy # healthcheck
networks:
- aikon_network
environment:
<<: *proxy-settings
restart: alwaysThe Dockerfile-aiiinotate runs the following steps:
- Pull a NodeJS image
- Setup environment (read arguments, copy .env file ocally)
- Pull system packages
- Setup your NPM project (create it and install aiiinotate in int)
- Expose port
- Run: create the daabase and start the aiiinotate instance
MongoDB is aiiinotate's database backend. It stores IIIF annotations.
It uses an official docker image so configuration is easy.
mongo:
image: mongo:8 # official mongo image
restart: always
networks:
- aikon_network
volumes:
- mongodata:/data/db # shared volume for data persistency
environment:
<<: *proxy-settings
healthcheck: # make sure mongo is healthy
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5The redis container contains the Redis database, which is used as broker for Celery tasks. Data is persisted in redisdata volume.
It uses an official docker image so configuration is easy. Note that Redis passwords are not supported.
redis:
image: redis:6 # official redis image
command: redis-server --port ${REDIS_PORT:-6379} --loglevel warning # defining this command allows us to use a custom port
volumes:
- redisdata:/data # bind redisdata volume to /data inside the container
env_file:
- ../app/config/.env # get $REDIS_PORT and $REDIS_PASSWORD
networks:
- aikon_network # the container has access to the aikon_network
ports:
- "${REDIS_PORT:-6379}:${REDIS_PORT:-6379}" # <port_outside_of_container>:<port_inside_of_container> => $REDIS_PORT or 6379 if not defined
restart: unless-stopped # the container will restart if the machine reboots
healthcheck: # command that is run to check whether the container runs correctly
test: [ "CMD", "redis-cli", "-p", "${REDIS_PORT:-6379}", "ping" ]
interval: 10s
timeout: 5s
retries: 5 nginx:
image: nginx:latest
env_file:
- ./.env
- ../app/config/.env
ports:
- "${NGINX_PORT:-8080}:${NGINX_PORT:-8080}"
environment:
<<: *proxy-settings
PROD_URL: ${PROD_URL:-localhost}
NGINX_PORT: ${NGINX_PORT:-8080}
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ../app/staticfiles:/home/aikon/app/staticfiles:ro
- ${DATA_FOLDER}:/data:ro
- /etc/ssl:/etc/ssl:ro
depends_on:
- web
- cantaloupe
- sas
networks:
- aikon_network
restart: unless-stoppedVolumes are used to persist data in between builds and to share data between containers.
Networks are used to allow communication between containers: aikon_network is the bridge network that connects all containers together.
volumes:
pgdata:
redisdata:
mongodata:
staticfiles:
driver: local
driver_opts:
type: none
o: bind
device: ../app/staticfiles
networks:
aikon_network:
driver: bridge