Add pgAdmin to the peo4c image
Install pgAdmin 9.18 in its own virtualenv and run it under gunicorn on port 5050 next to PostgreSQL and the bridge. On first start the entrypoint creates the pgAdmin login (PGADMIN_DEFAULT_EMAIL, default admin@example.com; PGADMIN_DEFAULT_PASSWORD, default POSTGRES_PASSWORD) and registers the bundled PostgreSQL server. pgAdmin is optional (PEO4C_PGADMIN=false) and a failure in it does not stop the container. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
47b8f5f746
commit
e72af59401
4 files changed
+98
-20
No files matched your search
+19
-5
@@ -3,9 +3,10 @@
|
||||
# Stage 1 builds the neo4j-browser static frontend from source.
|
||||
# Stage 2 is the Apache AGE image (PostgreSQL with the AGE extension) plus the
|
||||
# peo4c bridge, which speaks the Bolt protocol to the browser and translates
|
||||
# Cypher into AGE `cypher()` calls.
|
||||
# Cypher into AGE `cypher()` calls, and pgAdmin as a SQL interface.
|
||||
|
||||
ARG AGE_IMAGE=docker.io/apache/age:release_PG18_1.8.0
|
||||
ARG PGADMIN_VERSION=9.18
|
||||
|
||||
FROM docker.io/library/node:16.20.2-bullseye AS browser
|
||||
|
||||
@@ -23,13 +24,23 @@ RUN yarn build && test -f dist/index.html
|
||||
|
||||
FROM ${AGE_IMAGE}
|
||||
|
||||
ARG PGADMIN_VERSION
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends python3 python3-psycopg \
|
||||
&& apt-get install -y --no-install-recommends python3 python3-psycopg python3-venv \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN python3 -m venv /opt/pgadmin \
|
||||
&& /opt/pgadmin/bin/pip install --no-cache-dir "pgadmin4==${PGADMIN_VERSION}" gunicorn \
|
||||
&& ln -s "$(/opt/pgadmin/bin/python -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])')/pgadmin4" \
|
||||
/opt/pgadmin/app \
|
||||
&& mkdir -p /var/lib/pgadmin \
|
||||
&& chown postgres:postgres /var/lib/pgadmin
|
||||
|
||||
COPY --from=browser /src/dist /opt/peo4c/browser
|
||||
COPY bridge /opt/peo4c/bridge
|
||||
COPY docker/initdb/ /docker-entrypoint-initdb.d/
|
||||
COPY docker/pgadmin/config_distro.py /opt/peo4c/pgadmin/config_distro.py
|
||||
COPY docker/peo4c-entrypoint.sh /usr/local/bin/peo4c-entrypoint.sh
|
||||
|
||||
ENV PEO4C_DEFAULT_GRAPH=neo4j \
|
||||
@@ -37,10 +48,13 @@ ENV PEO4C_DEFAULT_GRAPH=neo4j \
|
||||
PEO4C_HTTP_PORT=7474 \
|
||||
PEO4C_BOLT_PORT=7687 \
|
||||
PEO4C_PG_HOST=127.0.0.1 \
|
||||
PEO4C_PG_PORT=5432
|
||||
PEO4C_PG_PORT=5432 \
|
||||
PEO4C_PGADMIN=true \
|
||||
PEO4C_PGADMIN_PORT=5050 \
|
||||
CONFIG_DISTRO_FILE_PATH=/opt/peo4c/pgadmin/config_distro.py
|
||||
|
||||
# 7474: neo4j-browser (HTTP), 7687: Bolt, 5432: PostgreSQL
|
||||
EXPOSE 7474 7687 5432
|
||||
# 7474: neo4j-browser (HTTP), 7687: Bolt, 5050: pgAdmin, 5432: PostgreSQL
|
||||
EXPOSE 7474 7687 5050 5432
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/peo4c-entrypoint.sh"]
|
||||
CMD ["postgres", "-c", "shared_preload_libraries=age"]
|
||||
@@ -3,7 +3,8 @@
|
||||
[neo4j-browser](https://github.com/neo4j/neo4j-browser) running on PostgreSQL, using
|
||||
[Apache AGE](https://github.com/apache/age) as the graph engine.
|
||||
|
||||
Everything ships in one container image:
|
||||
Everything ships in one container image, together with
|
||||
[pgAdmin](https://www.pgadmin.org/) for working with the same data in SQL:
|
||||
|
||||
```
|
||||
neo4j-browser (static files) peo4c bridge (Python) PostgreSQL + AGE
|
||||
@@ -19,14 +20,20 @@ Everything ships in one container image:
|
||||
```sh
|
||||
podman build -t peo4c .
|
||||
podman run -d --name peo4c -e POSTGRES_PASSWORD=secret \
|
||||
-p 7474:7474 -p 7687:7687 -p 5432:5432 \
|
||||
-v peo4c-data:/var/lib/postgresql peo4c
|
||||
-p 7474:7474 -p 7687:7687 -p 5050:5050 -p 5432:5432 \
|
||||
-v peo4c-data:/var/lib/postgresql -v peo4c-pgadmin:/var/lib/pgadmin peo4c
|
||||
```
|
||||
|
||||
Open <http://localhost:7474/> and log in with your **PostgreSQL** credentials:
|
||||
user `postgres`, password the value of `POSTGRES_PASSWORD`. Bolt clients (the Neo4j
|
||||
drivers, cypher-shell) can connect to `neo4j://localhost:7687` the same way, and
|
||||
`psql` works on port 5432.
|
||||
| Port | Service | Log in with |
|
||||
| --- | --- | --- |
|
||||
| 7474 | neo4j-browser: <http://localhost:7474/> | PostgreSQL user `postgres` and `POSTGRES_PASSWORD` |
|
||||
| 7687 | Bolt, for Neo4j drivers and cypher-shell (`neo4j://localhost:7687`) | same as above |
|
||||
| 5050 | pgAdmin: <http://localhost:5050/> | `admin@example.com` and `POSTGRES_PASSWORD` |
|
||||
| 5432 | PostgreSQL, for `psql` and other SQL clients | same as neo4j-browser |
|
||||
|
||||
pgAdmin comes with the bundled PostgreSQL server already registered as "peo4c";
|
||||
it asks for the PostgreSQL password the first time you open it. AGE stores each
|
||||
graph as a schema of the same name, with one table per label.
|
||||
|
||||
`docker` works as a drop-in replacement for `podman` in the commands above.
|
||||
|
||||
@@ -59,9 +66,16 @@ drivers, cypher-shell) can connect to `neo4j://localhost:7687` the same way, and
|
||||
| `PEO4C_ADVERTISED_BOLT_PORT` | `7687` | Bolt port advertised to the browser; set it when you publish 7687 on another host port. |
|
||||
| `PEO4C_ADVERTISED_HOST` | `localhost` | Host reported in `SHOW DATABASES` and to clients that do not send a routing address. |
|
||||
| `PEO4C_LOG_LEVEL` | `INFO` | `DEBUG` logs every generated SQL statement. |
|
||||
| `PGADMIN_DEFAULT_EMAIL` | `admin@example.com` | pgAdmin login, created on first start. |
|
||||
| `PGADMIN_DEFAULT_PASSWORD` | `$POSTGRES_PASSWORD` | pgAdmin login password, set on first start. |
|
||||
| `PEO4C_PGADMIN` | `true` | Set to `false` to not start pgAdmin. |
|
||||
| `PEO4C_PGADMIN_PORT` | `5050` | Port pgAdmin listens on inside the container. |
|
||||
|
||||
Build arguments: `AGE_IMAGE` (base image, default `apache/age:release_PG18_1.8.0`) and
|
||||
`NEO4J_BROWSER_REF` (neo4j-browser commit to build).
|
||||
Build arguments: `AGE_IMAGE` (base image, default `apache/age:release_PG18_1.8.0`),
|
||||
`NEO4J_BROWSER_REF` (neo4j-browser commit to build) and `PGADMIN_VERSION` (default `9.18`).
|
||||
|
||||
pgAdmin keeps its users and settings in `/var/lib/pgadmin`; changing
|
||||
`PGADMIN_DEFAULT_*` after the first start has no effect unless that directory is empty.
|
||||
|
||||
## Limitations
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Runs PostgreSQL (through the stock postgres entrypoint) and the peo4c bridge
|
||||
# side by side, and stops the container when either of them exits.
|
||||
# Runs PostgreSQL (through the stock postgres entrypoint), the peo4c bridge and
|
||||
# pgAdmin side by side. The container stops when PostgreSQL or the bridge exits;
|
||||
# pgAdmin is optional and its failure does not take the others down.
|
||||
set -euo pipefail
|
||||
|
||||
if [ "${1:-}" != "postgres" ]; then
|
||||
@@ -9,6 +10,31 @@ fi
|
||||
|
||||
export PEO4C_PG_DATABASE="${PEO4C_PG_DATABASE:-${POSTGRES_DB:-${POSTGRES_USER:-postgres}}}"
|
||||
|
||||
start_pgadmin() {
|
||||
local app=/opt/pgadmin/app
|
||||
local email="${PGADMIN_DEFAULT_EMAIL:-admin@example.com}"
|
||||
|
||||
if [ ! -f /var/lib/pgadmin/pgadmin4.db ]; then
|
||||
# First start: create the login user and register the bundled PostgreSQL.
|
||||
cat > /var/lib/pgadmin/servers.json <<EOF
|
||||
{"Servers": {"1": {"Name": "peo4c", "Group": "Servers", "Host": "127.0.0.1", "Port": 5432,
|
||||
"MaintenanceDB": "${PEO4C_PG_DATABASE}", "Username": "${POSTGRES_USER:-postgres}", "SSLMode": "prefer"}}}
|
||||
EOF
|
||||
chown postgres:postgres /var/lib/pgadmin/servers.json
|
||||
PGADMIN_SETUP_EMAIL="$email" \
|
||||
PGADMIN_SETUP_PASSWORD="${PGADMIN_DEFAULT_PASSWORD:-${POSTGRES_PASSWORD:-}}" \
|
||||
gosu postgres /opt/pgadmin/bin/python "$app/setup.py" setup-db \
|
||||
&& gosu postgres /opt/pgadmin/bin/python "$app/setup.py" load-servers \
|
||||
/var/lib/pgadmin/servers.json --user "$email" \
|
||||
|| { rm -f /var/lib/pgadmin/pgadmin4.db; return 1; }
|
||||
fi
|
||||
|
||||
gosu postgres /opt/pgadmin/bin/gunicorn --bind "0.0.0.0:${PEO4C_PGADMIN_PORT}" \
|
||||
--workers 1 --threads 25 --timeout 0 --chdir "$app" --log-level warning pgAdmin4:app &
|
||||
pgadmin_pid=$!
|
||||
echo "peo4c: pgAdmin on http://localhost:${PEO4C_PGADMIN_PORT}/ (login: $email)"
|
||||
}
|
||||
|
||||
docker-entrypoint.sh "$@" &
|
||||
pg_pid=$!
|
||||
|
||||
@@ -16,16 +42,22 @@ cd /opt/peo4c/bridge
|
||||
gosu postgres python3 -m peo4c &
|
||||
bridge_pid=$!
|
||||
|
||||
pgadmin_pid=
|
||||
if [ "${PEO4C_PGADMIN}" = "true" ]; then
|
||||
start_pgadmin || echo "peo4c: WARNING: pgAdmin setup failed, continuing without it" >&2
|
||||
fi
|
||||
|
||||
shutdown() {
|
||||
kill -TERM "$bridge_pid" 2>/dev/null || true
|
||||
kill -TERM "$pg_pid" 2>/dev/null || true
|
||||
for pid in "$bridge_pid" $pgadmin_pid "$pg_pid"; do
|
||||
kill -TERM "$pid" 2>/dev/null || true
|
||||
done
|
||||
}
|
||||
trap shutdown TERM INT
|
||||
|
||||
# Wait until one of the two processes exits, then stop the other one too.
|
||||
# Wait until PostgreSQL or the bridge exits, then stop everything.
|
||||
set +e
|
||||
wait -n "$pg_pid" "$bridge_pid"
|
||||
status=$?
|
||||
shutdown
|
||||
wait "$pg_pid" 2>/dev/null
|
||||
wait 2>/dev/null
|
||||
exit "$status"
|
||||
@@ -0,0 +1,18 @@
|
||||
# pgAdmin settings for the peo4c image, loaded via CONFIG_DISTRO_FILE_PATH.
|
||||
# Paths derived from DATA_DIR in pgAdmin's config.py are computed before this
|
||||
# file is applied, so they are all set explicitly here.
|
||||
|
||||
DATA_DIR = '/var/lib/pgadmin'
|
||||
SQLITE_PATH = DATA_DIR + '/pgadmin4.db'
|
||||
SESSION_DB_PATH = DATA_DIR + '/sessions'
|
||||
STORAGE_DIR = DATA_DIR + '/storage'
|
||||
KERBEROS_CCACHE_DIR = DATA_DIR + '/krbccache'
|
||||
AZURE_CREDENTIAL_CACHE_DIR = DATA_DIR + '/azurecredentialcache'
|
||||
LOG_FILE = '/dev/null'
|
||||
|
||||
SERVER_MODE = True
|
||||
UPGRADE_CHECK_ENABLED = False
|
||||
|
||||
# From the config_distro.py shipped in the pgadmin4 wheel, which this replaces.
|
||||
HELP_PATH = '../../docs/en_US/_build/html/'
|
||||
MINIFY_HTML = False
|
||||
Reference in new issue
Block a user