forked from DSpace/DSpace
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
267 lines (267 loc) · 13 KB
/
Copy pathdocker-compose.yml
File metadata and controls
267 lines (267 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
version: '3.7'
# IMAGE VERSION - build it, do not pull it
#
# The dspace image MUST be built from this repository:
#
# docker compose build dspace
#
# This branch carries customisations that no published image contains. For example
# dspace/config/ehcache.xml references org.dspace.external.provider.orcid.xml.CacheLogger,
# which exists in dspace-api here but in neither upstream 7.6.5 nor 7.6.8. Since this
# compose file also mounts ./dspace/config over the image config, a pulled image gives
# you a newer/other webapp reading this tree config, and the DSpace kernel dies with
# "Error parsing XML configuration at file:/dspace/config/ehcache.xml".
#
# `docker compose up` builds a missing image on its own, so a clean machine is fine.
# The trap is a STALE image left over from an earlier pull: it shadows the build and
# the failure looks unrelated to images. `docker compose build` is the fix.
#
# DSPACE_VER is pinned to the pom.xml version rather than the floating dspace-7_x tag
# so the local build cannot be silently shadowed by a newer upstream one. Bump it
# together with pom.xml.
networks:
dspacenet:
ipam:
config:
# Define a custom subnet for our DSpace network, so that we can easily trust requests from host to container.
# If you customize this value, be sure to customize the 'proxies.trusted.ipranges' env variable below.
- subnet: 172.23.0.0/16
services:
# DSpace (backend) webapp container
dspace:
container_name: dspace
# Required for the ExitOnOutOfMemoryError in JAVA_OPTS below to be useful: it makes Docker
# bring the container back after the JVM kills itself. Note this policy reacts
# ONLY to the process exiting - an unhealthy-but-running container is the
# autoheal sidecar's job, not this.
restart: unless-stopped
environment:
# Below syntax may look odd, but it is how to override dspace.cfg settings via env variables.
# See https://github.com/DSpace/DSpace/blob/main/dspace/config/config-definition.xml
# __P__ => "." (e.g. dspace__P__dir => dspace.dir)
# __D__ => "-" (e.g. google__D__metadata => google-metadata)
# dspace.dir: Must match with Dockerfile's DSPACE_INSTALL directory.
dspace__P__dir: /dspace
# Uncomment to set a non-default value for dspace.server.url or dspace.ui.url
# dspace__P__server__P__url: http://localhost:8080/server
# dspace__P__ui__P__url: http://localhost:4000
dspace__P__name: 'DSpace Started with Docker Compose'
# db.url: Ensure we are using the 'dspacedb' image for our database
db__P__url: 'jdbc:postgresql://dspacedb:5432/dspace'
# solr.server: Ensure we are using the 'dspacesolr' image for Solr
solr__P__server: http://dspacesolr:8983/solr
# proxies.trusted.ipranges: This setting is required for a REST API running in Docker to trust requests
# from the host machine. This IP range MUST correspond to the 'dspacenet' subnet defined above.
proxies__P__trusted__P__ipranges: '172.23.0'
LOGGING_CONFIG: /dspace/config/log4j2-container.xml
# JVM options. -Xmx2000m is the image default and is kept as-is.
#
# ExitOnOutOfMemoryError is the piece that gives OOM coverage. A healthcheck is
# the wrong tool for OOM: after an OutOfMemoryError the JVM is often still able
# to answer a trivial health request while being unable to serve real work, so a
# probe can report healthy on a process that is effectively dead. Letting the JVM
# exit turns an invisible internal failure into a container exit, which the
# restart policy below then handles - no sidecar involved.
#
# HeapDumpOnOutOfMemoryError writes the dump to the mounted log volume so the
# cause survives the restart. Without it the restart destroys the evidence.
JAVA_OPTS: '-Xmx2000m -XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dspace/log'
image: "${DOCKER_OWNER:-dspace}/dspace:${DSPACE_VER:-dspace-7.6.5}-test"
build:
context: .
dockerfile: Dockerfile.test
depends_on:
dspacedb:
condition: service_healthy
dspacesolr:
condition: service_healthy
networks:
- dspacenet
ports:
- published: 8080
target: 8080
- published: 8009
target: 8009
- published: 8000
target: 8000
stdin_open: true
tty: true
volumes:
# Keep DSpace assetstore directory between reboots
- assetstore:/dspace/assetstore
# Keep the log directory between container recreates. Without this, the heap dump
# written by HeapDumpOnOutOfMemoryError (see JAVA_OPTS) lives only in the writable
# layer and is destroyed by `docker compose down` - i.e. exactly when someone is
# trying to find out why the container died. A named volume is used on purpose: it
# inherits ownership of /dspace/log from the image, whereas a bind mount to a fresh
# host directory would be owned by root and silently unwritable.
- dspacelogs:/dspace/log
# Mount local [src]/dspace/config/ to container. This syncs your local configs with container
# NOTE: Environment variables specified above will OVERRIDE any configs in local.cfg or dspace.cfg
- ./dspace/config:/dspace/config
# The database is guaranteed to be ready by depends_on: condition: service_healthy
# above, so this no longer waits for it. The TCP poll that used to live here was
# weaker anyway: the port accepts connections before Postgres accepts queries.
# 1. Run database migration to init database tables
# 2. Start Tomcat
entrypoint:
- /bin/bash
- '-c'
- |
/dspace/bin/dspace database migrate
catalina.sh run
# This probe targets the liveness GROUP, not the aggregated /actuator/health.
# The aggregate is DOWN even on a clean install (SEOHealthIndicator reports a
# missing robots.txt/sitemap/SSR) and a restart cannot fix that, so wiring a
# healthcheck to it would restart-loop forever. The group also maps DOWN to a
# real 503, so a plain `curl -f` suffices - no fragile JSON string matching.
#
# What this probe covers, and what it deliberately does not:
#
# DB unreachable / dead connection pool -> the `db` component in the liveness
# group goes DOWN, the group maps that to 503, curl -f fails.
#
# Tomcat thread pool exhausted, or the JVM wedged in a GC spiral -> actuator is
# served by the SAME Tomcat connector and thread pool as the REST API, so a
# wedged server cannot answer this request either. --max-time 5 turns that into
# a failure instead of a hang. (Measured: with the database frozen, the health
# endpoint returned nothing for 25s+.) This is why the timeout matters as much
# as the endpoint choice.
#
# OutOfMemoryError -> NOT covered here on purpose. After an OOM the JVM can often
# still answer a trivial health request while being unable to do real work, so a
# probe would report healthy on a dead process. That case is handled by
# -XX:+ExitOnOutOfMemoryError in JAVA_OPTS plus `restart: unless-stopped`.
#
# Disk full, missing robots.txt, misconfiguration -> deliberately NOT covered.
# A restart cannot fix any of them, so making them fail this probe would only
# produce a restart loop.
healthcheck:
# The leading chaos-flag check is a deliberate test hook: it lets you prove the
# whole unhealthy -> restart chain end to end without taking down Postgres or
# Solr, and without waiting for a real outage.
# docker exec dspace touch /tmp/chaos-fail -> report a fault artificially
# docker exec dspace rm /tmp/chaos-fail -> clear it
# NOTE: the flag survives a restart (it lives in the container's writable
# layer, which `docker restart` does not reset), so remove it once the restart
# has been observed or autoheal will keep restarting the container.
test: ["CMD-SHELL", "[ -f /tmp/chaos-fail ] && exit 1; curl -fsS --max-time 5 http://localhost:8080/server/actuator/health/liveness > /dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 3
# DSpace needs several minutes to boot; too short a start_period restart-loops.
start_period: 300s
labels:
# Consumed by the optional autoheal sidecar, see docker-compose-autoheal.yml
autoheal: "true"
# DSpace PostgreSQL database container
dspacedb:
container_name: dspacedb
# Two independent recovery paths, both needed:
# restart policy -> the postmaster died and the container exited
# autoheal label -> the container still runs but pg_isready keeps failing
# Restarting a database is not free, which is why the sidecar enforces a restart
# budget (3 per hour by default) and then stops and asks for a human.
restart: unless-stopped
labels:
autoheal: "true"
# Uses a custom Postgres image with pgcrypto installed
image: "${DOCKER_OWNER:-dspace}/dspace-postgres-pgcrypto:${DSPACE_VER:-dspace-7.6.5}"
build:
# Must build out of subdirectory to have access to install script for pgcrypto
context: ./dspace/src/main/docker/dspace-postgres-pgcrypto/
environment:
PGDATA: /pgdata
POSTGRES_PASSWORD: dspace
networks:
dspacenet:
ports:
- published: 5432
target: 5432
stdin_open: true
tty: true
volumes:
# Keep Postgres data directory between reboots
- pgdata:/pgdata
# pg_isready is the purpose-built readiness probe: unlike a TCP port check it
# only succeeds once the server actually accepts queries (not during initdb
# or crash recovery, when the port is already listening).
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dspace -d dspace"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# DSpace Solr container
dspacesolr:
container_name: dspacesolr
# Two independent recovery paths, both needed:
# restart policy -> the Solr process died and the container exited
# autoheal label -> the container still runs but the search core is gone
#
# Note what a restart does NOT fix: precreate-core only checks whether the core
# DIRECTORY exists, so once a core has been unloaded (its core.properties is gone
# but the directory stays) a restart logs "Core search already exists" and moves on,
# leaving the core unregistered. That is intentional on the sidecar side - it burns
# its restart budget, gives up, and asks for a human, which beats looping forever.
restart: unless-stopped
labels:
autoheal: "true"
image: "${DOCKER_OWNER:-dspace}/dspace-solr:${DSPACE_VER:-dspace-7.6.5}"
build:
# Context is the repository root, matching .github/workflows/docker.yml. The
# Dockerfile does `COPY scripts/log4j2.solr.xml`, which lives at the repo root,
# so the narrower ./dspace/src/main/docker/dspace-solr/ context used before made
# `docker compose build dspacesolr` fail with "/scripts/log4j2.solr.xml: not found"
# while CI built the same image fine.
context: .
dockerfile: ./dspace/src/main/docker/dspace-solr/Dockerfile
# Provide path to Solr configs necessary to build Docker image
additional_contexts:
solrconfigs: ./dspace/solr/
args:
SOLR_VERSION: "${SOLR_VER:-8.11}"
networks:
dspacenet:
ports:
- published: 8983
target: 8983
stdin_open: true
tty: true
working_dir: /var/solr/data
volumes:
# Keep Solr data directory between reboots
- solr_data:/var/solr/data
# Initialize all DSpace Solr cores then start Solr:
# * First, run precreate-core to create the core (if it doesn't yet exist). If exists already, this is a no-op
# * Second, copy configsets to this core:
# Updates to Solr configs require the container to be rebuilt/restarted: `docker compose -p d7 up -d --build dspacesolr`
entrypoint:
- /bin/bash
- '-c'
- |
init-var-solr
precreate-core authority /opt/solr/server/solr/configsets/authority
cp -r /opt/solr/server/solr/configsets/authority/* authority
precreate-core oai /opt/solr/server/solr/configsets/oai
cp -r /opt/solr/server/solr/configsets/oai/* oai
precreate-core search /opt/solr/server/solr/configsets/search
cp -r /opt/solr/server/solr/configsets/search/* search
precreate-core statistics /opt/solr/server/solr/configsets/statistics
cp -r /opt/solr/server/solr/configsets/statistics/* statistics
exec solr -f
# A port check is not enough: precreate-core runs AFTER Solr starts listening,
# so a port probe goes green while DSpace would still fail every query.
# Asking for a specific core discriminates - a missing core returns
# {"status":{"search":{}}} with no "name", so the grep fails.
healthcheck:
test: ["CMD-SHELL", "curl -fsS 'http://localhost:8983/solr/admin/cores?action=STATUS&core=search' | grep -q '\"name\":\"search\"'"]
interval: 10s
timeout: 5s
retries: 5
start_period: 60s
volumes:
assetstore:
dspacelogs:
pgdata:
solr_data: