From 11be1aafaf87038b6dd55c2c5333b7e9c0df912f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Sat, 12 Sep 2026 14:27:17 -0300 Subject: [PATCH 1/5] Add Aurora and historical container image pages - Add pages for the `plone/aurora` and historical `plone/plone` container images. - Split the container images index into official images for Plone 6 and other images. - Point links to the renamed GitHub repositories: `plone/container-backend`, `plone/container-frontend`, `plone/container-zeo`, and `plone/container-historical`. - Fix Vale errors in the backend image page and the containers overview. - Add Aurora and Podman to the Vale vocabulary. Closes #2103 --- docs/install/containers/images/aurora.md | 160 +++++++++ docs/install/containers/images/backend.md | 14 +- docs/install/containers/images/frontend.md | 8 +- docs/install/containers/images/historical.md | 353 +++++++++++++++++++ docs/install/containers/images/index.md | 24 +- docs/install/containers/images/zeo.md | 6 +- docs/install/containers/index.md | 6 +- docs/install/containers/recipes/index.md | 4 +- styles/config/vocabularies/Plone/accept.txt | 2 + 9 files changed, 554 insertions(+), 23 deletions(-) create mode 100644 docs/install/containers/images/aurora.md create mode 100644 docs/install/containers/images/historical.md diff --git a/docs/install/containers/images/aurora.md b/docs/install/containers/images/aurora.md new file mode 100644 index 0000000000..4103a4cbca --- /dev/null +++ b/docs/install/containers/images/aurora.md @@ -0,0 +1,160 @@ +--- +myst: + html_meta: + "description": "Using the plone/aurora image" + "property=og:description": "Using the plone/aurora image" + "property=og:title": "Plone Aurora image" + "keywords": "Plone 6, install, installation, docker, containers, Aurora, frontend, plone/aurora" +--- + +# `plone/aurora` + +This chapter covers the container images for [Aurora](https://github.com/plone/aurora), the future React frontend of Plone. +Aurora requires a Plone backend to be running and accessible. + +% TODO: Remove this admonition when Aurora has a final release. + +```{important} +Aurora has not reached a final release yet. +Evaluate it before you use it in production. +``` + + +## Using this image + + +### Simple usage + +Create a network, and start a Plone backend with a site named `Plone` on it. + +```shell +docker network create plone +docker run -d --name backend --network plone -e SITE=Plone plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} +``` + +Start Aurora on the same network, and point it at the backend. + +```shell +docker run -d --name aurora --network plone -p 3000:3000 -e PLONE_API_PATH=http://backend:8080/Plone plone/aurora:latest +``` + +Then point your browser at `http://localhost:3000`. + +If your Plone backend runs on your computer instead of in a container, use `host.docker.internal` to reach it from the Aurora container. + +```shell +docker run -d --name aurora -p 3000:3000 -e PLONE_API_PATH=http://host.docker.internal:8080/Plone plone/aurora:latest +``` + +% TODO: Confirm whether Linux hosts need `--add-host=host.docker.internal:host-gateway` for this example. + + +### Service configuration with Docker Compose + +Create a directory for your project, and inside it create a `docker-compose.yml` file with the following content. + +```yaml +services: + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + volumes: + - data:/data + + frontend: + image: plone/aurora:latest + environment: + PLONE_API_PATH: http://backend:8080/Plone + ports: + - "3000:3000" + depends_on: + - backend + +volumes: + data: {} +``` + +Now run `docker compose up -d` from your project directory. + +Point your browser at `http://localhost:3000`, and you should see your Plone site. +Until the backend finishes starting, Aurora returns an error page. + +The [`examples` directory of the `plone/container-aurora` repository](https://github.com/plone/container-aurora/tree/main/examples) contains more complete configurations, with a web server in front of Aurora, and with ZEO or PostgreSQL as the database. + + +## Configuration variables + +| Environment variable | Description | Default value | +| --- | --- | --- | +| `PLONE_API_PATH` | Address of the Plone site that Aurora uses as its backend | `http://localhost:8080/Plone` | +| `COOKIE_SECRET` | Secret that signs the authentication cookie | `default` | +| `PORT` | Port where Aurora listens | `3000` | +| `HOST` | Network address where Aurora listens | All network interfaces | + +Always set `COOKIE_SECRET` to a long, random value in production. +Without it, Aurora signs the authentication cookie with a publicly known value, and logs a warning at startup. + +Aurora runs in production mode, and marks its authentication cookie as secure. +Serve Aurora over HTTPS, so browsers keep the login session. + + +## Images + +Each Aurora release publishes the following images, for the `linux/amd64` and `linux/arm64` platforms. + +| Image | Description | +| --- | --- | +| `plone/aurora` | Aurora, ready to run in production | +| `plone/aurora-builder` | An Aurora project with its dependencies installed, used to build images | +| `plone/aurora-dev` | The Aurora development server, which also exposes Storybook on port 6006 | +| `plone/aurora-prod-config` | A minimal Node.js runtime, used as the base of production images | + + +## Extending from this image + +The `plone/aurora` image is built in two stages. +The first stage builds Aurora in `plone/aurora-builder`, and the second copies the result onto `plone/aurora-prod-config`. +Use the same structure to build your own image. + +In a directory, create a `Dockerfile` file. + +```Dockerfile +# syntax=docker/dockerfile:1 +FROM plone/aurora-builder:latest AS builder + +RUN < Date: Sat, 12 Sep 2026 15:17:39 -0300 Subject: [PATCH 2/5] Add .venv to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a825459902..452db68f49 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Dependencies +/.venv /venv # Generated files From e1494a5b4ce329b1ffa6f271ec0e2f0883f996b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Sat, 12 Sep 2026 17:54:44 -0300 Subject: [PATCH 3/5] Update container examples and add Docker Swarm stacks - Move the Docker Compose examples to examples/compose, and add Traefik versions of every nginx example: traefik-volto-plone, traefik-volto-plone-zeo, traefik-volto-plone-postgresql, and traefik-plone. - Add Docker Swarm stacks in examples/swarm for every Traefik example, aligned with cookieplone-templates: Let's Encrypt certificates, a Docker socket proxy, a table of environment variables, and HTTPS-only access. - Use source replacements for image versions: PLONE_FRONTEND_VERSION, PLONE_ZEO_VERSION, TRAEFIK_VERSION, and POSTGRES_VERSION. - Name the data volumes vol-site-data, stop publishing database ports, and remove duplicate keys from the Traefik and Varnish example. - Add HAProxy, Let's Encrypt, nginx, and Traefik to the Vale vocabulary. Closes #2104 --- docs/conf.py | 4 + .../{ => compose}/haproxy-plone-zeo.md | 9 +- .../containers/examples/compose/index.md | 53 ++ .../examples/{ => compose}/nginx-plone.md | 4 +- .../nginx-volto-plone-postgresql.md | 10 +- .../{ => compose}/nginx-volto-plone-zeo.md | 12 +- .../{ => compose}/nginx-volto-plone.md | 6 +- .../examples/compose/traefik-plone.md | 105 +++ .../compose/traefik-volto-plone-postgresql.md | 137 ++++ .../traefik-volto-plone-varnish.md | 17 +- .../compose/traefik-volto-plone-zeo.md | 137 ++++ .../examples/compose/traefik-volto-plone.md | 116 +++ docs/install/containers/examples/index.md | 51 +- .../containers/examples/swarm/index.md | 31 + .../examples/swarm/traefik-plone.md | 272 +++++++ .../swarm/traefik-volto-plone-postgresql.md | 357 +++++++++ .../swarm/traefik-volto-plone-varnish.md | 710 ++++++++++++++++++ .../examples/swarm/traefik-volto-plone-zeo.md | 352 +++++++++ .../examples/swarm/traefik-volto-plone.md | 326 ++++++++ styles/config/vocabularies/Plone/accept.txt | 4 + 20 files changed, 2666 insertions(+), 47 deletions(-) rename docs/install/containers/examples/{ => compose}/haproxy-plone-zeo.md (95%) create mode 100644 docs/install/containers/examples/compose/index.md rename docs/install/containers/examples/{ => compose}/nginx-plone.md (98%) rename docs/install/containers/examples/{ => compose}/nginx-volto-plone-postgresql.md (96%) rename docs/install/containers/examples/{ => compose}/nginx-volto-plone-zeo.md (94%) rename docs/install/containers/examples/{ => compose}/nginx-volto-plone.md (96%) create mode 100644 docs/install/containers/examples/compose/traefik-plone.md create mode 100644 docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md rename docs/install/containers/examples/{ => compose}/traefik-volto-plone-varnish.md (98%) create mode 100644 docs/install/containers/examples/compose/traefik-volto-plone-zeo.md create mode 100644 docs/install/containers/examples/compose/traefik-volto-plone.md create mode 100644 docs/install/containers/examples/swarm/index.md create mode 100644 docs/install/containers/examples/swarm/traefik-plone.md create mode 100644 docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md create mode 100644 docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md create mode 100644 docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md create mode 100644 docs/install/containers/examples/swarm/traefik-volto-plone.md diff --git a/docs/conf.py b/docs/conf.py index ce9fd132d1..a13bd7816b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -475,6 +475,10 @@ def source_replace(app, docname, source): # Dict of replacements. source_replacements = { "{PLONE_BACKEND_MINOR_VERSION}": "6.2", + "{PLONE_FRONTEND_VERSION}": "19", + "{PLONE_ZEO_VERSION}": "6", + "{TRAEFIK_VERSION}": "v3.7", + "{POSTGRES_VERSION}": "18", } diff --git a/docs/install/containers/examples/haproxy-plone-zeo.md b/docs/install/containers/examples/compose/haproxy-plone-zeo.md similarity index 95% rename from docs/install/containers/examples/haproxy-plone-zeo.md rename to docs/install/containers/examples/compose/haproxy-plone-zeo.md index 134777aea1..b93c6f4e32 100644 --- a/docs/install/containers/examples/haproxy-plone-zeo.md +++ b/docs/install/containers/examples/compose/haproxy-plone-zeo.md @@ -20,7 +20,6 @@ We will use the image [`plone/plone-haproxy`](https://github.com/plone/plone-hap Create a directory for your project, and inside it create a `docker-compose.yml` file that starts your Plone instance and the ZEO instance with volume mounts for data persistence. ```yaml -version: "3" services: lb: @@ -50,15 +49,13 @@ services: - zeo zeo: - image: plone/plone-zeo:latest + image: plone/plone-zeo:{PLONE_ZEO_VERSION} restart: always volumes: - - data:/data - ports: - - "8100" + - vol-site-data:/data volumes: - data: {} + vol-site-data: {} ``` diff --git a/docs/install/containers/examples/compose/index.md b/docs/install/containers/examples/compose/index.md new file mode 100644 index 0000000000..e70015af26 --- /dev/null +++ b/docs/install/containers/examples/compose/index.md @@ -0,0 +1,53 @@ +--- +myst: + html_meta: + "description": "Examples of Plone 6 setup with containers" + "property=og:description": "Examples of Plone 6 setup with containers" + "property=og:title": "Examples of Plone 6 using containers" + "keywords": "Plone 6, install, installation, docker, containers" +--- + +# Examples of Plone 6 using containers + +```{toctree} +:maxdepth: 2 +:hidden: true + +traefik-volto-plone +traefik-volto-plone-zeo +traefik-volto-plone-postgresql +traefik-plone +traefik-volto-plone-varnish +nginx-volto-plone +nginx-volto-plone-zeo +nginx-volto-plone-postgresql +nginx-plone +haproxy-plone-zeo +``` + +Examples of projects running Plone using `docker compose`. + +## Traefik + +| Project example | Description | +| --- | --- | +| [`traefik-volto-plone`](traefik-volto-plone) | Stack with Traefik, Frontend, and Backend | +| [`traefik-volto-plone-zeo`](traefik-volto-plone-zeo) | Stack with Traefik, Frontend, Backend, and ZEO server | +| [`traefik-volto-plone-postgresql`](traefik-volto-plone-postgresql) | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | +| [`traefik-plone`](traefik-plone) | Stack with Traefik and Backend (Plone Classic) | +| [`traefik-volto-plone-varnish`](traefik-volto-plone-varnish) | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | + +## nginx + +| Project example | Description | +| --- | --- | +| [`nginx-volto-plone`](nginx-volto-plone) | Stack with nginx, Frontend, and Backend | +| [`nginx-volto-plone-zeo`](nginx-volto-plone-zeo) | Stack with nginx, Frontend, Backend, and ZEO server | +| [`nginx-volto-plone-postgresql`](nginx-volto-plone-postgresql) | Stack with nginx, Frontend, Backend, and PostgreSQL DB | +| [`nginx-plone`](nginx-plone) | Stack with nginx and Backend (Plone Classic) | + +## HAProxy + +| Project example | Description | +| --- | --- | +| [`haproxy-plone-zeo`](haproxy-plone-zeo) | Stack with HAProxy, Backend, and ZEO server | diff --git a/docs/install/containers/examples/nginx-plone.md b/docs/install/containers/examples/compose/nginx-plone.md similarity index 98% rename from docs/install/containers/examples/nginx-plone.md rename to docs/install/containers/examples/compose/nginx-plone.md index 9cac06c9a8..6f5b778a62 100644 --- a/docs/install/containers/examples/nginx-plone.md +++ b/docs/install/containers/examples/compose/nginx-plone.md @@ -86,12 +86,12 @@ services: SITE: Plone TYPE: classic volumes: - - data:/data + - vol-site-data:/data ports: - "8080:8080" volumes: - data: {} + vol-site-data: {} ``` diff --git a/docs/install/containers/examples/nginx-volto-plone-postgresql.md b/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md similarity index 96% rename from docs/install/containers/examples/nginx-volto-plone-postgresql.md rename to docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md index c568ed4917..6e9e988aa4 100644 --- a/docs/install/containers/examples/nginx-volto-plone-postgresql.md +++ b/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md @@ -96,7 +96,7 @@ services: - "80:80" frontend: - image: plone/plone-frontend:latest + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone ports: @@ -115,18 +115,16 @@ services: - db db: - image: postgres + image: postgres:{POSTGRES_VERSION} environment: POSTGRES_USER: plone POSTGRES_PASSWORD: plone POSTGRES_DB: plone volumes: - - data:/var/lib/postgresql/data - ports: - - "5432:5432" + - vol-site-data:/var/lib/postgresql volumes: - data: {} + vol-site-data: {} ``` diff --git a/docs/install/containers/examples/nginx-volto-plone-zeo.md b/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md similarity index 94% rename from docs/install/containers/examples/nginx-volto-plone-zeo.md rename to docs/install/containers/examples/compose/nginx-volto-plone-zeo.md index 06102d25a6..3cfc3d67bf 100644 --- a/docs/install/containers/examples/nginx-volto-plone-zeo.md +++ b/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md @@ -96,7 +96,7 @@ services: - "80:80" frontend: - image: plone/plone-frontend:latest + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone ports: @@ -111,22 +111,20 @@ services: ZEO_ADDRESS: db:8100 ZEO_SHARED_BLOB_DIR: on # otherwise the backend will create its own blob storage volumes: - - data:/data # the backend and database need access to the same volume + - vol-site-data:/data # the backend and database need access to the same volume ports: - "8080:8080" depends_on: - db db: - image: plone/plone-zeo:latest + image: plone/plone-zeo:{PLONE_ZEO_VERSION} restart: always volumes: - - data:/data - ports: - - "8100:8100" + - vol-site-data:/data volumes: - data: {} + vol-site-data: {} ``` diff --git a/docs/install/containers/examples/nginx-volto-plone.md b/docs/install/containers/examples/compose/nginx-volto-plone.md similarity index 96% rename from docs/install/containers/examples/nginx-volto-plone.md rename to docs/install/containers/examples/compose/nginx-volto-plone.md index 4955d6119b..f40207dded 100644 --- a/docs/install/containers/examples/nginx-volto-plone.md +++ b/docs/install/containers/examples/compose/nginx-volto-plone.md @@ -94,7 +94,7 @@ services: - "80:80" frontend: - image: plone/plone-frontend:latest + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone ports: @@ -107,12 +107,12 @@ services: environment: SITE: Plone volumes: - - data:/data + - vol-site-data:/data ports: - "8080:8080" volumes: - data: {} + vol-site-data: {} ``` diff --git a/docs/install/containers/examples/compose/traefik-plone.md b/docs/install/containers/examples/compose/traefik-plone.md new file mode 100644 index 0000000000..4186e61017 --- /dev/null +++ b/docs/install/containers/examples/compose/traefik-plone.md @@ -0,0 +1,105 @@ +--- +myst: + html_meta: + "description": "Simple Plone 6 Classic UI setup with Traefik and one backend, with data persisted in a Docker volume." + "property=og:description": "Simple Plone 6 Classic UI setup with Traefik and one backend, with data persisted in a Docker volume." + "property=og:title": "Traefik, Plone Classic container example" + "keywords": "Plone 6, Container, Docker, Traefik, Plone Classic" +--- + +# Traefik, Plone Classic container example + +This example is a simple setup with one backend, with data persisted in a Docker volume. + +In this example, {term}`Traefik Proxy` routes requests to the backend. + + +## Setup + +Create an empty project directory named `traefik-plone`. + +```shell +mkdir traefik-plone +``` + +Change into your project directory. + +```shell +cd traefik-plone +``` + + +### Service configuration with Docker Compose + +Create a `docker-compose.yml` file with the following content. +Traefik reads its routing configuration from the labels of the `backend` service, so this example doesn't need a separate proxy configuration file. + +```yaml +services: + + traefik: + image: traefik:{TRAEFIK_VERSION} + ports: + - "80:80" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + command: + - --providers.docker + - --providers.docker.exposedbydefault=false + - --entrypoints.http.address=:80 + - --accesslog + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + TYPE: classic + volumes: + - vol-site-data:/data + ports: + - "8080:8080" + labels: + - traefik.enable=true + # Service + - traefik.http.services.svc-backend.loadbalancer.server.port=8080 + # Middleware: Virtual Host Monster rewrite for the site + - "traefik.http.middlewares.mw-backend-vhm.replacepathregex.regex=^/(.*)" + - "traefik.http.middlewares.mw-backend-vhm.replacepathregex.replacement=/VirtualHostBase/http/plone.localhost/Plone/VirtualHostRoot/$$1" + # Router + - traefik.http.routers.rt-backend.rule=Host(`plone.localhost`) + - traefik.http.routers.rt-backend.entrypoints=http + - traefik.http.routers.rt-backend.service=svc-backend + - traefik.http.routers.rt-backend.middlewares=mw-backend-vhm + +volumes: + vol-site-data: {} +``` + +```{note} +Use `http://plone.localhost/` to access the website. +If `plone.localhost` doesn't resolve on your computer, add it to your `/etc/hosts` file, pointing to the IP address of the Docker host. +``` + + +## Build the project + +Start the stack with `docker compose`. + +```shell +docker compose up -d +``` + +This pulls the needed images and starts Plone. + + +## Access Plone in a browser + +After startup, go to `http://plone.localhost/` and you should see the site. +You can also open the main Plone control page, where you can create more Plone sites, at `http://localhost:8080`. + + +## Shutdown and cleanup + +The command `docker compose down` removes the containers and default network, but preserves the Plone database. + +The command `docker compose down --volumes` removes the containers, default network, and the Plone database. diff --git a/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md b/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md new file mode 100644 index 0000000000..5b9af7b52d --- /dev/null +++ b/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md @@ -0,0 +1,137 @@ +--- +myst: + html_meta: + "description": "Simple Plone 6 setup with Traefik and one or more backend instances accessing a PostgreSQL server, with data persisted in a Docker volume." + "property=og:description": "Simple Plone 6 setup with Traefik and one or more backend instances accessing a PostgreSQL server, with data persisted in a Docker volume." + "property=og:title": "Traefik, Frontend, Backend, PostgreSQL container example" + "keywords": "Plone 6, Container, Docker, Traefik, Frontend, Backend, PostgreSQL" +--- + +# Traefik, Frontend, Backend, PostgreSQL container example + +This example is a simple setup with one or more backend instances accessing a PostgreSQL server, with data persisted in a Docker volume. + +In this example, {term}`Traefik Proxy` routes requests to the frontend and the backend. + + +## Setup + +Create an empty project directory named `traefik-volto-plone-postgresql`. + +```shell +mkdir traefik-volto-plone-postgresql +``` + +Change into your project directory. + +```shell +cd traefik-volto-plone-postgresql +``` + + +### Service configuration with Docker Compose + +Create a `docker-compose.yml` file with the following content. +Traefik reads its routing configuration from the labels of the `frontend` and `backend` services, so this example doesn't need a separate proxy configuration file. + +```yaml +services: + + traefik: + image: traefik:{TRAEFIK_VERSION} + ports: + - "80:80" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + command: + - --providers.docker + - --providers.docker.exposedbydefault=false + - --entrypoints.http.address=:80 + - --accesslog + + frontend: + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + environment: + RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone + depends_on: + - backend + labels: + - traefik.enable=true + # Service + - traefik.http.services.svc-frontend.loadbalancer.server.port=3000 + # Router + - traefik.http.routers.rt-frontend.rule=Host(`plone.localhost`) + - traefik.http.routers.rt-frontend.entrypoints=http + - traefik.http.routers.rt-frontend.service=svc-frontend + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + RELSTORAGE_DSN: "dbname='plone' user='plone' host='db' password='plone'" + depends_on: + - db + labels: + - traefik.enable=true + # Service + - traefik.http.services.svc-backend.loadbalancer.server.port=8080 + # Middleware: Virtual Host Monster rewrite for /++api++/ + - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/\\+\\+api\\+\\+($$|/.*)" + - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=/VirtualHostBase/http/plone.localhost/Plone/++api++/VirtualHostRoot$$1" + # Router + - traefik.http.routers.rt-backend-api.rule=Host(`plone.localhost`) && PathPrefix(`/++api++`) + - traefik.http.routers.rt-backend-api.entrypoints=http + - traefik.http.routers.rt-backend-api.service=svc-backend + - traefik.http.routers.rt-backend-api.middlewares=mw-backend-vhm-api + + db: + image: postgres:{POSTGRES_VERSION} + environment: + POSTGRES_USER: plone + POSTGRES_PASSWORD: plone + POSTGRES_DB: plone + volumes: + - vol-site-data:/var/lib/postgresql + +volumes: + vol-site-data: {} +``` + +```{note} +Use `http://plone.localhost/` to access the website. +If `plone.localhost` doesn't resolve on your computer, add it to your `/etc/hosts` file, pointing to the IP address of the Docker host. +``` + + +## Build the project + +Start the stack with `docker compose`. + +```shell +docker compose up -d +``` + +This pulls the needed images and starts Plone. + + +## Access Plone in a browser + +After startup, go to `http://plone.localhost/` and you should see the site. + + +## Increase the number of backends + +To use two containers for the backend, run `docker compose` with `--scale`. + +```shell +docker compose up -d --scale backend=2 +``` + +Traefik distributes the requests to the backend among both containers. + + +## Shutdown and cleanup + +The command `docker compose down` removes the containers and default network, but preserves the Plone database. + +The command `docker compose down --volumes` removes the containers, default network, and the Plone database. diff --git a/docs/install/containers/examples/traefik-volto-plone-varnish.md b/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md similarity index 98% rename from docs/install/containers/examples/traefik-volto-plone-varnish.md rename to docs/install/containers/examples/compose/traefik-volto-plone-varnish.md index 24d490115c..3160bb91c8 100644 --- a/docs/install/containers/examples/traefik-volto-plone-varnish.md +++ b/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md @@ -325,7 +325,7 @@ Now let's create a {file}`docker-compose.yml` file: ```yaml services: webserver: - image: traefik + image: traefik:{TRAEFIK_VERSION} ports: - 80:80 @@ -364,7 +364,7 @@ services: - --api frontend: - image: plone/plone-frontend:latest + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone RAZZLE_API_PATH: http://plone.localhost @@ -393,13 +393,10 @@ services: environment: SITE: Plone PROFILES: "plone.app.caching:with-caching-proxy" - environment: ZEO_ADDRESS: db:8100 ZEO_SHARED_BLOB_DIR: on # otherwise the backend will create its own blob storage volumes: - - data:/data # the backend and database need access to the same volume - ports: - - 8080:8080 + - vol-site-data:/data # the backend and database need access to the same volume depends_on: - db # If the Docker container is run with a UID other than the UID which owns the local file system persistent storage, @@ -468,14 +465,12 @@ services: - backend db: - image: plone/plone-zeo:latest + image: plone/plone-zeo:{PLONE_ZEO_VERSION} volumes: - - data:/data - ports: - - "8100:8100" + - vol-site-data:/data volumes: - data: {} + vol-site-data: {} ``` diff --git a/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md b/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md new file mode 100644 index 0000000000..5b1f6dc352 --- /dev/null +++ b/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md @@ -0,0 +1,137 @@ +--- +myst: + html_meta: + "description": "Simple Plone 6 setup with Traefik and one or more backend instances accessing a ZEO server, with data persisted in a Docker volume." + "property=og:description": "Simple Plone 6 setup with Traefik and one or more backend instances accessing a ZEO server, with data persisted in a Docker volume." + "property=og:title": "Traefik, Frontend, Backend, ZEO container example" + "keywords": "Plone 6, Container, Docker, Traefik, Frontend, Backend, ZEO" +--- + +# Traefik, Frontend, Backend, ZEO container example + +This example is a simple setup with one or more backend instances accessing a ZEO server, with data persisted in a Docker volume. + +In this example, {term}`Traefik Proxy` routes requests to the frontend and the backend. + + +## Setup + +Create an empty project directory named `traefik-volto-plone-zeo`. + +```shell +mkdir traefik-volto-plone-zeo +``` + +Change into your project directory. + +```shell +cd traefik-volto-plone-zeo +``` + + +### Service configuration with Docker Compose + +Create a `docker-compose.yml` file with the following content. +Traefik reads its routing configuration from the labels of the `frontend` and `backend` services, so this example doesn't need a separate proxy configuration file. + +```yaml +services: + + traefik: + image: traefik:{TRAEFIK_VERSION} + ports: + - "80:80" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + command: + - --providers.docker + - --providers.docker.exposedbydefault=false + - --entrypoints.http.address=:80 + - --accesslog + + frontend: + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + environment: + RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone + depends_on: + - backend + labels: + - traefik.enable=true + # Service + - traefik.http.services.svc-frontend.loadbalancer.server.port=3000 + # Router + - traefik.http.routers.rt-frontend.rule=Host(`plone.localhost`) + - traefik.http.routers.rt-frontend.entrypoints=http + - traefik.http.routers.rt-frontend.service=svc-frontend + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + ZEO_ADDRESS: db:8100 + ZEO_SHARED_BLOB_DIR: on # otherwise the backend will create its own blob storage + volumes: + - vol-site-data:/data # the backend and database need access to the same volume + depends_on: + - db + labels: + - traefik.enable=true + # Service + - traefik.http.services.svc-backend.loadbalancer.server.port=8080 + # Middleware: Virtual Host Monster rewrite for /++api++/ + - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/\\+\\+api\\+\\+($$|/.*)" + - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=/VirtualHostBase/http/plone.localhost/Plone/++api++/VirtualHostRoot$$1" + # Router + - traefik.http.routers.rt-backend-api.rule=Host(`plone.localhost`) && PathPrefix(`/++api++`) + - traefik.http.routers.rt-backend-api.entrypoints=http + - traefik.http.routers.rt-backend-api.service=svc-backend + - traefik.http.routers.rt-backend-api.middlewares=mw-backend-vhm-api + + db: + image: plone/plone-zeo:{PLONE_ZEO_VERSION} + restart: always + volumes: + - vol-site-data:/data + +volumes: + vol-site-data: {} +``` + +```{note} +Use `http://plone.localhost/` to access the website. +If `plone.localhost` doesn't resolve on your computer, add it to your `/etc/hosts` file, pointing to the IP address of the Docker host. +``` + + +## Build the project + +Start the stack with `docker compose`. + +```shell +docker compose up -d +``` + +This pulls the needed images and starts Plone. + + +## Access Plone in a browser + +After startup, go to `http://plone.localhost/` and you should see the site. + + +## Increase the number of backends + +To use two containers for the backend, run `docker compose` with `--scale`. + +```shell +docker compose up -d --scale backend=2 +``` + +Traefik distributes the requests to the backend among both containers. + + +## Shutdown and cleanup + +The command `docker compose down` removes the containers and default network, but preserves the Plone database. + +The command `docker compose down --volumes` removes the containers, default network, and the Plone database. diff --git a/docs/install/containers/examples/compose/traefik-volto-plone.md b/docs/install/containers/examples/compose/traefik-volto-plone.md new file mode 100644 index 0000000000..d719b459cb --- /dev/null +++ b/docs/install/containers/examples/compose/traefik-volto-plone.md @@ -0,0 +1,116 @@ +--- +myst: + html_meta: + "description": "Simple Plone 6 setup with Traefik, one frontend, and one backend, with data persisted in a Docker volume." + "property=og:description": "Simple Plone 6 setup with Traefik, one frontend, and one backend, with data persisted in a Docker volume." + "property=og:title": "Traefik, Frontend, Backend container example" + "keywords": "Plone 6, Container, Docker, Traefik, Frontend, Backend" +--- + +# Traefik, Frontend, Backend container example + +This example is a simple setup with one frontend and one backend, with data persisted in a Docker volume. + +In this example, {term}`Traefik Proxy` routes requests to the frontend and the backend. + + +## Setup + +Create an empty project directory named `traefik-volto-plone`. + +```shell +mkdir traefik-volto-plone +``` + +Change into your project directory. + +```shell +cd traefik-volto-plone +``` + + +### Service configuration with Docker Compose + +Create a `docker-compose.yml` file with the following content. +Traefik reads its routing configuration from the labels of the `frontend` and `backend` services, so this example doesn't need a separate proxy configuration file. + +```yaml +services: + + traefik: + image: traefik:{TRAEFIK_VERSION} + ports: + - "80:80" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + command: + - --providers.docker + - --providers.docker.exposedbydefault=false + - --entrypoints.http.address=:80 + - --accesslog + + frontend: + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + environment: + RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone + depends_on: + - backend + labels: + - traefik.enable=true + # Service + - traefik.http.services.svc-frontend.loadbalancer.server.port=3000 + # Router + - traefik.http.routers.rt-frontend.rule=Host(`plone.localhost`) + - traefik.http.routers.rt-frontend.entrypoints=http + - traefik.http.routers.rt-frontend.service=svc-frontend + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + volumes: + - vol-site-data:/data + labels: + - traefik.enable=true + # Service + - traefik.http.services.svc-backend.loadbalancer.server.port=8080 + # Middleware: Virtual Host Monster rewrite for /++api++/ + - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.regex=^/\\+\\+api\\+\\+($$|/.*)" + - "traefik.http.middlewares.mw-backend-vhm-api.replacepathregex.replacement=/VirtualHostBase/http/plone.localhost/Plone/++api++/VirtualHostRoot$$1" + # Router + - traefik.http.routers.rt-backend-api.rule=Host(`plone.localhost`) && PathPrefix(`/++api++`) + - traefik.http.routers.rt-backend-api.entrypoints=http + - traefik.http.routers.rt-backend-api.service=svc-backend + - traefik.http.routers.rt-backend-api.middlewares=mw-backend-vhm-api + +volumes: + vol-site-data: {} +``` + +```{note} +Use `http://plone.localhost/` to access the website. +If `plone.localhost` doesn't resolve on your computer, add it to your `/etc/hosts` file, pointing to the IP address of the Docker host. +``` + + +## Build the project + +Start the stack with `docker compose`. + +```shell +docker compose up -d +``` + +This pulls the needed images and starts Plone. + + +## Access Plone in a browser + +After startup, go to `http://plone.localhost/` and you should see the site. + + +## Shutdown and cleanup + +The command `docker compose down` removes the containers and default network, but preserves the Plone database. + +The command `docker compose down --volumes` removes the containers, default network, and the Plone database. diff --git a/docs/install/containers/examples/index.md b/docs/install/containers/examples/index.md index 5af7f6315b..d08fe83b89 100644 --- a/docs/install/containers/examples/index.md +++ b/docs/install/containers/examples/index.md @@ -13,21 +13,48 @@ myst: :maxdepth: 2 :hidden: true -nginx-volto-plone -nginx-volto-plone-zeo -nginx-volto-plone-postgresql -nginx-plone -haproxy-plone-zeo -traefik-volto-plone-varnish +compose/index +swarm/index ``` +## Docker Compose + Examples of projects running Plone using `docker compose`. +### Traefik + +| Project example | Description | +| --- | --- | +| [`traefik-volto-plone`](compose/traefik-volto-plone) | Stack with Traefik, Frontend, and Backend | +| [`traefik-volto-plone-zeo`](compose/traefik-volto-plone-zeo) | Stack with Traefik, Frontend, Backend, and ZEO server | +| [`traefik-volto-plone-postgresql`](compose/traefik-volto-plone-postgresql) | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | +| [`traefik-plone`](compose/traefik-plone) | Stack with Traefik and Backend (Plone Classic) | +| [`traefik-volto-plone-varnish`](compose/traefik-volto-plone-varnish) | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | + +### nginx + | Project example | Description | | --- | --- | -| [nginx-volto-plone](nginx-volto-plone) | Stack with nginx, Frontend, and Backend | -| [nginx-volto-plone-zeo](nginx-volto-plone-zeo) | Stack with nginx, Frontend, Backend, and ZEO server | -| [nginx-volto-plone-postgresql](nginx-volto-plone-postgresql) | Stack with nginx, Frontend, Backend, and PostgreSQL DB | -| [nginx-plone](nginx-plone) | Stack with nginx and Backend (Plone Classic) | -| [haproxy-plone-zeo](haproxy-plone-zeo) | Stack with HAProxy, Backend, and ZEO server | -| [traefik-volto-plone-varnish](traefik-volto-plone-varnish) | Stack with traefik, Frontend, Backend, Varnish | +| [`nginx-volto-plone`](compose/nginx-volto-plone) | Stack with nginx, Frontend, and Backend | +| [`nginx-volto-plone-zeo`](compose/nginx-volto-plone-zeo) | Stack with nginx, Frontend, Backend, and ZEO server | +| [`nginx-volto-plone-postgresql`](compose/nginx-volto-plone-postgresql) | Stack with nginx, Frontend, Backend, and PostgreSQL DB | +| [`nginx-plone`](compose/nginx-plone) | Stack with nginx and Backend (Plone Classic) | + +### HAProxy + +| Project example | Description | +| --- | --- | +| [`haproxy-plone-zeo`](compose/haproxy-plone-zeo) | Stack with HAProxy, Backend, and ZEO server | + + +## Docker Swarm + +Examples of stacks running Plone on a Docker Swarm cluster. + +| Stack example | Description | +| --- | --- | +| [`traefik-volto-plone`](swarm/traefik-volto-plone) | Stack with Traefik, Frontend, and Backend | +| [`traefik-volto-plone-zeo`](swarm/traefik-volto-plone-zeo) | Stack with Traefik, Frontend, Backend, and ZEO server | +| [`traefik-volto-plone-postgresql`](swarm/traefik-volto-plone-postgresql) | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | +| [`traefik-plone`](swarm/traefik-plone) | Stack with Traefik and Backend (Plone Classic) | +| [`traefik-volto-plone-varnish`](swarm/traefik-volto-plone-varnish) | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | diff --git a/docs/install/containers/examples/swarm/index.md b/docs/install/containers/examples/swarm/index.md new file mode 100644 index 0000000000..827e535b39 --- /dev/null +++ b/docs/install/containers/examples/swarm/index.md @@ -0,0 +1,31 @@ +--- +myst: + html_meta: + "description": "Examples of Plone 6 setup with containers" + "property=og:description": "Examples of Plone 6 setup with containers" + "property=og:title": "Examples of Plone 6 using containers" + "keywords": "Plone 6, install, installation, docker, containers" +--- + +# Examples of Plone 6 using containers + +```{toctree} +:maxdepth: 2 +:hidden: true + +traefik-volto-plone +traefik-volto-plone-zeo +traefik-volto-plone-postgresql +traefik-plone +traefik-volto-plone-varnish +``` + +Examples of stacks for running Plone with `docker swarm`. + +| Stack example | Description | +| --- | --- | +| [`traefik-volto-plone`](traefik-volto-plone) | Stack with Traefik, Frontend, and Backend | +| [`traefik-volto-plone-zeo`](traefik-volto-plone-zeo) | Stack with Traefik, Frontend, Backend, and ZEO server | +| [`traefik-volto-plone-postgresql`](traefik-volto-plone-postgresql) | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | +| [`traefik-plone`](traefik-plone) | Stack with Traefik and Backend (Plone Classic) | +| [`traefik-volto-plone-varnish`](traefik-volto-plone-varnish) | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | diff --git a/docs/install/containers/examples/swarm/traefik-plone.md b/docs/install/containers/examples/swarm/traefik-plone.md new file mode 100644 index 0000000000..6f85ad29cc --- /dev/null +++ b/docs/install/containers/examples/swarm/traefik-plone.md @@ -0,0 +1,272 @@ +--- +myst: + html_meta: + "description": "Plone 6 Classic UI stack for Docker Swarm with Traefik and a backend that stores its data in a Docker volume." + "property=og:description": "Plone 6 Classic UI stack for Docker Swarm with Traefik and a backend that stores its data in a Docker volume." + "property=og:title": "Docker Swarm: Traefik, Plone Classic example" + "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Plone Classic" +--- + +# Docker Swarm: Traefik, Plone Classic example + +This example deploys a Plone 6 site with the Classic UI as a stack on a Docker Swarm cluster. +The stack runs the following services. + +`traefik` +: {term}`Traefik Proxy` routes requests to the backend, and gets TLS certificates from Let's Encrypt. + +`socket-proxy` +: Gives Traefik access to the parts of the Docker API it needs, instead of the Docker socket itself. + +`backend` +: The Plone backend with the Classic UI, with its data persisted in a Docker volume. + + +## Prerequisites + +- A Docker Swarm cluster. + To create a cluster with a single node, run `docker swarm init` on that node. +- DNS records that point the host names of `STACK_HOSTNAME`, `STACK_HOSTNAME_REDIRECT`, and `TRAEFIK_HOSTNAME` to your cluster. +- Ports 80 and 443 open to the internet, so Let's Encrypt can validate your host names. + + +## Setup + +Create an empty project directory named `swarm-traefik-plone`. + +```shell +mkdir swarm-traefik-plone +``` + +Change into your project directory. + +```shell +cd swarm-traefik-plone +``` + + +### Create the public network + +Traefik and the services it routes requests to share an overlay network named `nw-public`. +Create it once on a manager node, before you deploy the stack. + +```shell +docker network create --driver overlay nw-public +``` + + +### Stack file + +Create a `stack.yml` file with the following content. + +```yaml +services: + + socket-proxy: + image: tecnativa/docker-socket-proxy + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + NODES: 1 + SERVICES: 1 + TASKS: 1 + NETWORKS: 1 + networks: + - nw-traefik + deploy: + placement: + constraints: + - node.role == manager + + traefik: + image: traefik:{TRAEFIK_VERSION} + ports: + - "80:80" + - "443:443" + volumes: + - vol-traefik-certs:/certificates + command: + - --providers.swarm + - --providers.swarm.endpoint=tcp://socket-proxy:2375 + - --providers.swarm.exposedbydefault=false + - --providers.swarm.network=nw-public + - --providers.swarm.constraints=Label(`traefik.constraint-label`, `public`) + - --entrypoints.http.address=:80 + - --entrypoints.https.address=:443 + - --certificatesresolvers.le.acme.email=${TRAEFIK_EMAIL:?Set TRAEFIK_EMAIL} + - --certificatesresolvers.le.acme.storage=/certificates/acme.json + - --certificatesresolvers.le.acme.tlschallenge=true + - --accesslog + - --accesslog.format=json + - --log.level=INFO + - --log.format=json + - --api + networks: + - nw-public + - nw-traefik + deploy: + replicas: 1 + placement: + constraints: + - node.role == manager + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + - traefik.http.services.traefik-public.loadbalancer.server.port=8000 + + # Dashboard + - traefik.http.middlewares.admin-auth.basicauth.users=${TRAEFIK_BASIC_AUTH:?Set TRAEFIK_BASIC_AUTH} + - traefik.http.routers.traefik-dashboard.rule=Host(`${TRAEFIK_HOSTNAME:?Set TRAEFIK_HOSTNAME}`) + - traefik.http.routers.traefik-dashboard.entrypoints=https + - traefik.http.routers.traefik-dashboard.tls=true + - traefik.http.routers.traefik-dashboard.tls.certresolver=le + - traefik.http.routers.traefik-dashboard.service=api@internal + - traefik.http.routers.traefik-dashboard.middlewares=admin-auth + + # Generic middlewares + - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https + - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true + - traefik.http.middlewares.gzip.compress=true + - traefik.http.middlewares.gzip.compress.excludedcontenttypes=image/png, image/jpeg, font/woff2 + + # Redirect every HTTP request to HTTPS + - traefik.http.routers.generic-https-redirect.entrypoints=http + - traefik.http.routers.generic-https-redirect.rule=HostRegexp(`^.+$$`) + - traefik.http.routers.generic-https-redirect.priority=1 + - traefik.http.routers.generic-https-redirect.middlewares=https-redirect + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + TYPE: classic + volumes: + - vol-site-data:/data + networks: + - nw-public + deploy: + replicas: 1 + update_config: + parallelism: 1 + delay: 5s + order: stop-first # only one process can open the database + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME:?Set STACK_NAME}-backend.loadbalancer.server.port=8080 + + # Middlewares + ## Redirect + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.permanent=true + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.regex=^https://${STACK_HOSTNAME_REDIRECT:?Set STACK_HOSTNAME_REDIRECT}/(.*) + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.replacement=https://${STACK_HOSTNAME:?Set STACK_HOSTNAME}/$${1} + ## Virtual Host Monster rewrite for the site + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm.replacepathregex.regex=^/(.*)" + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm.replacepathregex.replacement=/VirtualHostBase/https/${STACK_HOSTNAME}/Plone/VirtualHostRoot/$$1" + + # Routers + ## Redirect + - traefik.http.routers.rt-${STACK_NAME}-backend-redirect.rule=Host(`${STACK_HOSTNAME_REDIRECT}`) + - traefik.http.routers.rt-${STACK_NAME}-backend-redirect.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-backend-redirect.tls=true + - traefik.http.routers.rt-${STACK_NAME}-backend-redirect.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-backend-redirect.middlewares=mw-${STACK_NAME}-redirect + ## / + - traefik.http.routers.rt-${STACK_NAME}-backend.rule=Host(`${STACK_HOSTNAME}`) + - traefik.http.routers.rt-${STACK_NAME}-backend.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-backend.tls=true + - traefik.http.routers.rt-${STACK_NAME}-backend.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-backend.service=svc-${STACK_NAME}-backend + - traefik.http.routers.rt-${STACK_NAME}-backend.middlewares=gzip,mw-${STACK_NAME}-backend-vhm + +volumes: + vol-traefik-certs: {} + vol-site-data: {} + +networks: + nw-public: + external: true + nw-traefik: + driver: overlay + internal: true +``` + +```{warning} +The backend stores its data in a Docker volume, and only one backend process can open that data at a time. +Keep the `backend` service at one replica. + +Docker volumes are also local to the node where they're created. +In a cluster with more than one node, add a placement constraint to the `backend` service, so it always runs on the node that holds its data. +``` + + +### Environment variables + +The stack reads its configuration from the following environment variables. +All of them are required, and `docker stack deploy` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_NAME` | Name of the stack, which must match the name you pass to `docker stack deploy`. Traefik uses it to name routers, services, and middleware. | | `plone` | +| `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | +| `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | +| `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | +| `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | +| `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | + +To create a password hash, run the following command, with your password instead of `secret`. + +```shell +openssl passwd -apr1 secret +``` + +Create a `.env` file with your values. +Wrap values that contain a dollar sign, such as password hashes, in single quotes. + +```shell +STACK_NAME=plone +STACK_HOSTNAME=www.example.com +STACK_HOSTNAME_REDIRECT=example.com +TRAEFIK_HOSTNAME=traefik.example.com +TRAEFIK_EMAIL=admin@example.com +TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' +``` + +`docker stack deploy` doesn't read `.env` files. +Load the variables into your shell before you deploy. + +```shell +set -a +. ./.env +set +a +``` + + +## Deploy the stack + +Deploy the stack with the name from `STACK_NAME`. + +```shell +docker stack deploy -c stack.yml "$STACK_NAME" +``` + +This pulls the needed images and starts Plone. + + +## Access Plone + +After startup, go to `https://www.example.com`, using your value of `STACK_HOSTNAME`, and you should see the site. + +Unlike the {doc}`Docker Compose example <../compose/traefik-plone>`, this stack doesn't publish port 8080 of the backend, so the page to create more Plone sites isn't reachable. + +The Traefik dashboard is available at the host name from `TRAEFIK_HOSTNAME`, behind the user name and password from `TRAEFIK_BASIC_AUTH`. + + +## Shutdown and cleanup + +The command `docker stack rm "$STACK_NAME"` removes the services and the stack networks, but preserves the Plone database and the TLS certificates in their volumes. diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md b/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md new file mode 100644 index 0000000000..7bc3dadb55 --- /dev/null +++ b/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md @@ -0,0 +1,357 @@ +--- +myst: + html_meta: + "description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend and backend, and a PostgreSQL database." + "property=og:description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend and backend, and a PostgreSQL database." + "property=og:title": "Docker Swarm: Traefik, Frontend, Backend, PostgreSQL example" + "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Frontend, Backend, PostgreSQL" +--- + +# Docker Swarm: Traefik, Frontend, Backend, PostgreSQL example + +This example deploys Plone 6 as a stack on a Docker Swarm cluster. +The stack runs the following services. + +`traefik` +: {term}`Traefik Proxy` routes requests to the other services, and gets TLS certificates from Let's Encrypt. + +`socket-proxy` +: Gives Traefik access to the parts of the Docker API it needs, instead of the Docker socket itself. + +`frontend` +: The Plone frontend, with two replicas by default. + +`backend` +: The Plone backend, with two replicas by default, which stores its data in PostgreSQL. + +`db` +: The PostgreSQL database, with its data persisted in a Docker volume. + + +## Prerequisites + +- A Docker Swarm cluster. + To create a cluster with a single node, run `docker swarm init` on that node. +- DNS records that point the host names of `STACK_HOSTNAME`, `STACK_HOSTNAME_REDIRECT`, and `TRAEFIK_HOSTNAME` to your cluster. +- Ports 80 and 443 open to the internet, so Let's Encrypt can validate your host names. + + +## Setup + +Create an empty project directory named `swarm-traefik-volto-plone-postgresql`. + +```shell +mkdir swarm-traefik-volto-plone-postgresql +``` + +Change into your project directory. + +```shell +cd swarm-traefik-volto-plone-postgresql +``` + + +### Create the public network + +Traefik and the services it routes requests to share an overlay network named `nw-public`. +Create it once on a manager node, before you deploy the stack. + +```shell +docker network create --driver overlay nw-public +``` + + +### Stack file + +Create a `stack.yml` file with the following content. + +```yaml +services: + + socket-proxy: + image: tecnativa/docker-socket-proxy + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + NODES: 1 + SERVICES: 1 + TASKS: 1 + NETWORKS: 1 + networks: + - nw-traefik + deploy: + placement: + constraints: + - node.role == manager + + traefik: + image: traefik:{TRAEFIK_VERSION} + ports: + - "80:80" + - "443:443" + volumes: + - vol-traefik-certs:/certificates + command: + - --providers.swarm + - --providers.swarm.endpoint=tcp://socket-proxy:2375 + - --providers.swarm.exposedbydefault=false + - --providers.swarm.network=nw-public + - --providers.swarm.constraints=Label(`traefik.constraint-label`, `public`) + - --entrypoints.http.address=:80 + - --entrypoints.https.address=:443 + - --certificatesresolvers.le.acme.email=${TRAEFIK_EMAIL:?Set TRAEFIK_EMAIL} + - --certificatesresolvers.le.acme.storage=/certificates/acme.json + - --certificatesresolvers.le.acme.tlschallenge=true + - --accesslog + - --accesslog.format=json + - --log.level=INFO + - --log.format=json + - --api + networks: + - nw-public + - nw-traefik + deploy: + replicas: 1 + placement: + constraints: + - node.role == manager + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + - traefik.http.services.traefik-public.loadbalancer.server.port=8000 + + # Dashboard + - traefik.http.middlewares.admin-auth.basicauth.users=${TRAEFIK_BASIC_AUTH:?Set TRAEFIK_BASIC_AUTH} + - traefik.http.routers.traefik-dashboard.rule=Host(`${TRAEFIK_HOSTNAME:?Set TRAEFIK_HOSTNAME}`) + - traefik.http.routers.traefik-dashboard.entrypoints=https + - traefik.http.routers.traefik-dashboard.tls=true + - traefik.http.routers.traefik-dashboard.tls.certresolver=le + - traefik.http.routers.traefik-dashboard.service=api@internal + - traefik.http.routers.traefik-dashboard.middlewares=admin-auth + + # Generic middlewares + - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https + - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true + - traefik.http.middlewares.gzip.compress=true + - traefik.http.middlewares.gzip.compress.excludedcontenttypes=image/png, image/jpeg, font/woff2 + + # Redirect every HTTP request to HTTPS + - traefik.http.routers.generic-https-redirect.entrypoints=http + - traefik.http.routers.generic-https-redirect.rule=HostRegexp(`^.+$$`) + - traefik.http.routers.generic-https-redirect.priority=1 + - traefik.http.routers.generic-https-redirect.middlewares=https-redirect + + frontend: + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + environment: + RAZZLE_INTERNAL_API_PATH: http://${STACK_NAME:?Set STACK_NAME}_backend:8080/Plone + RAZZLE_API_PATH: https://${STACK_HOSTNAME:?Set STACK_HOSTNAME} + networks: + - nw-public + - nw-internal + deploy: + replicas: ${STACK_FRONT_REPLICAS:-2} + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME}-frontend.loadbalancer.server.port=3000 + + # Middleware + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.permanent=true + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.regex=^https://${STACK_HOSTNAME_REDIRECT:?Set STACK_HOSTNAME_REDIRECT}/(.*) + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.replacement=https://${STACK_HOSTNAME}/$${1} + + # Routers + ## Redirect + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.rule=Host(`${STACK_HOSTNAME_REDIRECT}`) + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.tls=true + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.middlewares=mw-${STACK_NAME}-redirect + ## / + - traefik.http.routers.rt-${STACK_NAME}-frontend.rule=Host(`${STACK_HOSTNAME}`) + - traefik.http.routers.rt-${STACK_NAME}-frontend.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-frontend.tls=true + - traefik.http.routers.rt-${STACK_NAME}-frontend.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-frontend.service=svc-${STACK_NAME}-frontend + - traefik.http.routers.rt-${STACK_NAME}-frontend.middlewares=gzip + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + RELSTORAGE_DSN: "dbname='${DB_NAME:-plone}' user='${DB_USER:-plone}' host='${STACK_NAME}_db' password='${DB_PASSWORD:?Set DB_PASSWORD}'" + networks: + - nw-public + - nw-internal + deploy: + replicas: ${STACK_BACK_REPLICAS:-2} + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME}-backend.loadbalancer.server.port=8080 + + # Middlewares + ## Virtual Host Monster rewrite for /++api++/ + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-api.replacepathregex.regex=^/\\+\\+api\\+\\+($$|/.*)" + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-api.replacepathregex.replacement=/VirtualHostBase/https/${STACK_HOSTNAME}/Plone/++api++/VirtualHostRoot$$1" + ## Virtual Host Monster rewrite for /ClassicUI/ + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-classic.replacepathregex.regex=^/ClassicUI($$|/.*)" + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-classic.replacepathregex.replacement=/VirtualHostBase/https/${STACK_HOSTNAME}/Plone/VirtualHostRoot/_vh_ClassicUI$$1" + ## Basic authentication for /ClassicUI/ + - traefik.http.middlewares.mw-${STACK_NAME}-backend-auth.basicauth.users=${BASIC_AUTH_USER:?Set BASIC_AUTH_USER}:${BASIC_AUTH_PASSWORD_HASH:?Set BASIC_AUTH_PASSWORD_HASH} + + # Routers + ## /++api++ + - traefik.http.routers.rt-${STACK_NAME}-backend-api.rule=Host(`${STACK_HOSTNAME}`) && PathPrefix(`/++api++`) + - traefik.http.routers.rt-${STACK_NAME}-backend-api.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-backend-api.tls=true + - traefik.http.routers.rt-${STACK_NAME}-backend-api.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-backend-api.service=svc-${STACK_NAME}-backend + - traefik.http.routers.rt-${STACK_NAME}-backend-api.middlewares=gzip,mw-${STACK_NAME}-backend-vhm-api + ## /ClassicUI + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.rule=Host(`${STACK_HOSTNAME}`) && PathPrefix(`/ClassicUI`) + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.tls=true + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.service=svc-${STACK_NAME}-backend + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.middlewares=gzip,mw-${STACK_NAME}-backend-auth,mw-${STACK_NAME}-backend-vhm-classic + + db: + image: postgres:{POSTGRES_VERSION} + environment: + POSTGRES_USER: ${DB_USER:-plone} + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_DB: ${DB_NAME:-plone} + volumes: + - vol-site-data:/var/lib/postgresql + networks: + - nw-internal + deploy: + replicas: 1 + update_config: + parallelism: 1 + delay: 1s + order: stop-first + +volumes: + vol-traefik-certs: {} + vol-site-data: {} + +networks: + nw-public: + external: true + nw-traefik: + driver: overlay + internal: true + nw-internal: + driver: overlay + internal: true +``` + +```{warning} +Docker volumes are local to the node where they're created. +In a cluster with more than one node, add a placement constraint to the `db` service, so the database always runs on the node that holds its data. +``` + + +### Environment variables + +The stack reads its configuration from the following environment variables. +Variables without a default value are required, and `docker stack deploy` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_NAME` | Name of the stack, which must match the name you pass to `docker stack deploy`. The services use it to reach each other, and Traefik uses it to name routers, services, and middleware. | | `plone` | +| `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | +| `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | +| `STACK_FRONT_REPLICAS` | Number of frontend replicas | `2` | `3` | +| `STACK_BACK_REPLICAS` | Number of backend replicas | `2` | `4` | +| `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | +| `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | +| `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | +| `BASIC_AUTH_USER` | User name for the Classic UI at `/ClassicUI` | | `admin` | +| `BASIC_AUTH_PASSWORD_HASH` | Password hash for the Classic UI at `/ClassicUI` | | `$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | +| `DB_NAME` | Name of the PostgreSQL database | `plone` | `plone` | +| `DB_USER` | Name of the PostgreSQL user | `plone` | `plone` | +| `DB_PASSWORD` | Password of the PostgreSQL user | | `Correct-Horse-Battery-Staple` | + +To create a password hash, run the following command, with your password instead of `secret`. + +```shell +openssl passwd -apr1 secret +``` + +Create a `.env` file with your values. +Wrap values that contain a dollar sign, such as password hashes, in single quotes. + +```shell +STACK_NAME=plone +STACK_HOSTNAME=www.example.com +STACK_HOSTNAME_REDIRECT=example.com +TRAEFIK_HOSTNAME=traefik.example.com +TRAEFIK_EMAIL=admin@example.com +TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' +BASIC_AUTH_USER=admin +BASIC_AUTH_PASSWORD_HASH='$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' +DB_PASSWORD=Correct-Horse-Battery-Staple +``` + +`docker stack deploy` doesn't read `.env` files. +Load the variables into your shell before you deploy. + +```shell +set -a +. ./.env +set +a +``` + + +## Deploy the stack + +Deploy the stack with the name from `STACK_NAME`. + +```shell +docker stack deploy -c stack.yml "$STACK_NAME" +``` + +This pulls the needed images and starts Plone. + + +## Access Plone + +After startup, go to `https://www.example.com`, using your value of `STACK_HOSTNAME`, and you should see the site. + +The Classic UI is available at `https://www.example.com/ClassicUI`, behind the user name and password from `BASIC_AUTH_USER` and `BASIC_AUTH_PASSWORD_HASH`. + +The Traefik dashboard is available at the host name from `TRAEFIK_HOSTNAME`, behind the user name and password from `TRAEFIK_BASIC_AUTH`. + + +## Increase the number of backends + +To run four backend replicas, scale the backend service. + +```shell +docker service scale "${STACK_NAME}_backend=4" +``` + +The next `docker stack deploy` sets the number of replicas back to the value of `STACK_BACK_REPLICAS`. + + +## Shutdown and cleanup + +The command `docker stack rm "$STACK_NAME"` removes the services and the stack networks, but preserves the Plone database and the TLS certificates in their volumes. diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md b/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md new file mode 100644 index 0000000000..e329797647 --- /dev/null +++ b/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md @@ -0,0 +1,710 @@ +--- +myst: + html_meta: + "description": "Plone 6 stack for Docker Swarm with Traefik, Varnish, a scalable frontend and backend, and a ZEO server." + "property=og:description": "Plone 6 stack for Docker Swarm with Traefik, Varnish, a scalable frontend and backend, and a ZEO server." + "property=og:title": "Docker Swarm: Traefik, Frontend, Backend, ZEO, Varnish example" + "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Frontend, Backend, ZEO, Varnish" +--- + +# Docker Swarm: Traefik, Frontend, Backend, ZEO, Varnish example + +This example deploys Plone 6 as a stack on a Docker Swarm cluster, with {term}`Varnish` caching the responses of the frontend and the backend. +The stack runs the following services. + +`traefik` +: {term}`Traefik Proxy` routes requests to the other services, and gets TLS certificates from Let's Encrypt. + +`socket-proxy` +: Gives Traefik access to the parts of the Docker API it needs, instead of the Docker socket itself. + +`varnish` +: Caches responses, with two replicas by default. + +`purger` +: Receives purge requests from the backend, and forwards them to every Varnish replica. + +`frontend` +: The Plone frontend, with two replicas by default. + +`backend` +: The Plone backend, with two replicas by default, which stores its data in the ZEO server. + +`db` +: The ZEO server, with its data persisted in a Docker volume. + +Traefik receives public requests over HTTPS, and sends them to Varnish. +Varnish adds the `X-Varnish-Routed: 1` header to each request that it doesn't serve from its cache, and sends the request back to Traefik on port 8081, which routes it to the frontend or the backend. +The stack doesn't publish port 8081, so clients can only reach the site over HTTPS. +Requests to the Classic UI at `/ClassicUI` go straight from Traefik to the backend. + + +## Prerequisites + +- A Docker Swarm cluster. + To create a cluster with a single node, run `docker swarm init` on that node. +- DNS records that point the host names of `STACK_HOSTNAME`, `STACK_HOSTNAME_REDIRECT`, and `TRAEFIK_HOSTNAME` to your cluster. +- Ports 80 and 443 open to the internet, so Let's Encrypt can validate your host names. + + +## Setup + +Create an empty project directory named `swarm-traefik-volto-plone-varnish`. + +```shell +mkdir swarm-traefik-volto-plone-varnish +``` + +Change into your project directory. + +```shell +cd swarm-traefik-volto-plone-varnish +``` + + +### Create the public network + +Traefik and the services it routes requests to share an overlay network named `nw-public`. +Create it once on a manager node, before you deploy the stack. + +```shell +docker network create --driver overlay nw-public +``` + + +### Varnish configuration + +Create a directory named `etc`. + +```shell +mkdir etc +``` + +Create a file named `etc/varnish.vcl` with the following content. +It differs from the file in the {doc}`Docker Compose example <../compose/traefik-volto-plone-varnish>` in two places. +The backend is port 8081 of `traefik`, the name of the Traefik service in the stack, and the `purge` access control list doesn't include the `backend` host name. + +```vcl +vcl 4.0; + +import std; +import directors; + +backend traefik_loadbalancer { + .host = "traefik"; + .port = "8081"; + .connect_timeout = 2s; + .first_byte_timeout = 300s; + .between_bytes_timeout = 60s; +} + +/* Only allow PURGE from localhost and API-Server */ +acl purge { + "localhost"; + "127.0.0.1"; + "172.16.0.0/12"; + "10.0.0.0/8"; + "192.168.0.0/16"; +} + +sub detect_protocol{ + unset req.http.X-Forwarded-Proto; + set req.http.X-Forwarded-Proto = "http"; +} + +sub detect_debug{ + # Requests with X-Varnish-Debug will display additional + # information about requests + unset req.http.x-vcl-debug; + # Should be changed after switch to live + if (req.http.x-varnish-debug) { + set req.http.x-vcl-debug = false; + } +} + +sub detect_auth{ + unset req.http.x-auth; + if ( + (req.http.Cookie && ( + req.http.Cookie ~ "__ac(_(name|password|persistent))?=" || req.http.Cookie ~ "_ZopeId" || req.http.Cookie ~ "auth_token")) || + (req.http.Authenticate) || + (req.http.Authorization) + ) { + set req.http.x-auth = true; + } +} + +sub detect_requesttype{ + unset req.http.x-varnish-reqtype; + set req.http.x-varnish-reqtype = "Default"; + if (req.http.x-auth){ + set req.http.x-varnish-reqtype = "auth"; + } elseif (req.url ~ "\/@@(images|download|)\/?(.*)?$"){ + set req.http.x-varnish-reqtype = "blob"; + } elseif (req.url ~ "\/\+\+api\+\+/?(.*)?$") { + set req.http.x-varnish-reqtype = "api"; + } else { + set req.http.x-varnish-reqtype = "express"; + } +} + +sub process_redirects{ + // Add manual redurect + if (req.url ~ "^/old-folder/(.*)") { + set req.http.x-redirect-to = regsub(req.url, "^/old-folder/(.*)", "^/new-folder/\1"); + } + + if (req.http.x-redirect-to) { + return (synth(301, req.http.x-redirect-to)); + } +} + +sub vcl_init { + new cluster_loadbalancer = directors.round_robin(); + cluster_loadbalancer.add_backend(traefik_loadbalancer); +} + +sub vcl_recv { + set req.backend_hint = cluster_loadbalancer.backend(); + set req.http.X-Varnish-Routed = "1"; + + # Annotate request with x-forwarded-proto + # We always serve requests over https, but talk to Traefik + # and then to Volto and Plone using http. + call detect_protocol; + + # Annotate request with x-vcl-debug + call detect_debug; + + # Annotate request with x-auth indicating if request is authenticated or not + call detect_auth; + + # Annotate request with x-varnish-reqtype with a classification for the request + call detect_requesttype; + + # Process redirects + call process_redirects; + + # Sanitize cookies so they do not needlessly destroy cacheability for anonymous pages + if (req.http.Cookie) { + set req.http.Cookie = ";" + req.http.Cookie; + set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";"); + set req.http.Cookie = regsuball(req.http.Cookie, ";(sticky|I18N_LANGUAGE|statusmessages|__ac|_ZopeId|__cp|beaker\.session|authomatic|serverid|__rf|auth_token)=", "; \1="); + set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", ""); + set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", ""); + + if (req.http.Cookie == "") { + unset req.http.Cookie; + } + } + + if (req.http.x-auth) { + return(pass); + } + + if (req.method == "PURGE") { + if (!client.ip ~ purge) { + return (synth(405, "Not allowed.")); + } else { + ban("req.url == " + req.url); + return (synth(200, "Purged.")); + } + + } elseif (req.method == "BAN") { + # Same ACL check as above: + if (!client.ip ~ purge) { + return (synth(405, "Not allowed.")); + } + ban("req.http.host == " + req.http.host + "&& req.url == " + req.url); + # Throw a synthetic page so the + # request won't go to the backend. + return (synth(200, "Ban added")); + + } elseif (req.method != "GET" && + req.method != "HEAD" && + req.method != "PUT" && + req.method != "POST" && + req.method != "PATCH" && + req.method != "TRACE" && + req.method != "OPTIONS" && + req.method != "DELETE") { + /* Non-RFC2616 or CONNECT which is weird. */ + return (pipe); + } elseif (req.method != "GET" && + req.method != "HEAD" && + req.method != "OPTIONS") { + /* POST, PUT, PATCH will pass, always */ + return(pass); + } + + return(hash); +} + +sub vcl_pipe { + /* This is not necessary if you do not do any request rewriting. */ + set req.http.connection = "close"; +} + +sub vcl_purge { + return (synth(200, "PURGE: " + req.url + " - " + req.hash)); +} + +sub vcl_synth { + if (resp.status == 301) { + set resp.http.location = resp.reason; + set resp.reason = "Moved"; + return (deliver); + } +} + +sub vcl_hit { + if (obj.ttl >= 0s) { + // A pure unadulterated hit, deliver it + return (deliver); + } elsif (obj.ttl + obj.grace > 0s) { + // Object is in grace, deliver it + // Automatically triggers a background fetch + return (deliver); + } else { + return (restart); + } +} + + +sub vcl_backend_response { + + # Don't allow static files to set cookies. + # (?i) denotes case insensitive in PCRE (perl compatible regular expressions). + # make sure you edit both and keep them equal. + if (bereq.url ~ "(?i)\.(pdf|asc|dat|txt|doc|xls|ppt|tgz|png|gif|jpeg|jpg|ico|swf|css|js)(\?.*)?$") { + unset beresp.http.set-cookie; + } + if (beresp.http.Set-Cookie) { + set beresp.http.x-varnish-action = "FETCH (pass - response sets cookie)"; + set beresp.uncacheable = true; + set beresp.ttl = 120s; + return(deliver); + } + if (beresp.http.Cache-Control ~ "(private|no-cache|no-store)") { + set beresp.http.x-varnish-action = "FETCH (pass - cache control disallows)"; + set beresp.uncacheable = true; + set beresp.ttl = 120s; + return(deliver); + } + + # if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") { + # Do NOT cache if there is an "Authorization" header + # beresp never has an Authorization header in beresp, right? + if (beresp.http.Authorization) { + set beresp.http.x-varnish-action = "FETCH (pass - authorized and no public cache control)"; + set beresp.uncacheable = true; + set beresp.ttl = 120s; + return(deliver); + } + + # Use this rule IF no cache-control (SSR content) + if ((bereq.http.x-varnish-reqtype ~ "express") && (!beresp.http.Cache-Control)) { + set beresp.http.x-varnish-action = "INSERT (30s caching / 60s grace)"; + set beresp.uncacheable = false; + set beresp.ttl = 30s; + set beresp.grace = 60s; + return(deliver); + } + + if (!beresp.http.Cache-Control) { + set beresp.http.x-varnish-action = "FETCH (override - backend not setting cache control)"; + set beresp.uncacheable = true; + set beresp.ttl = 120s; + return (deliver); + } + + if (beresp.http.X-Anonymous && !beresp.http.Cache-Control) { + set beresp.http.x-varnish-action = "FETCH (override - anonymous backend not setting cache control)"; + set beresp.ttl = 600s; + return (deliver); + } + + set beresp.http.x-varnish-action = "FETCH (insert)"; + return (deliver); +} + +sub vcl_deliver { + + if (req.http.x-vcl-debug) { + set resp.http.x-varnish-ttl = obj.ttl; + set resp.http.x-varnish-grace = obj.grace; + set resp.http.x-hits = obj.hits; + set resp.http.x-varnish-reqtype = req.http.x-varnish-reqtype; + if (req.http.x-auth) { + set resp.http.x-auth = "Logged-in"; + } else { + set resp.http.x-auth = "Anon"; + } + if (obj.hits > 0) { + set resp.http.x-cache = "HIT"; + } else { + set resp.http.x-cache = "MISS"; + } + } else { + unset resp.http.x-varnish-action; + unset resp.http.x-cache-operation; + unset resp.http.x-cache-rule; + unset resp.http.x-powered-by; + } +} +``` + + +### Stack file + +Create a `stack.yml` file with the following content. + +```yaml +services: + + socket-proxy: + image: tecnativa/docker-socket-proxy + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + NODES: 1 + SERVICES: 1 + TASKS: 1 + NETWORKS: 1 + networks: + - nw-traefik + deploy: + placement: + constraints: + - node.role == manager + + traefik: + image: traefik:{TRAEFIK_VERSION} + ports: + - "80:80" + - "443:443" + volumes: + - vol-traefik-certs:/certificates + command: + - --providers.swarm + - --providers.swarm.endpoint=tcp://socket-proxy:2375 + - --providers.swarm.exposedbydefault=false + - --providers.swarm.network=nw-public + - --providers.swarm.constraints=Label(`traefik.constraint-label`, `public`) + - --entrypoints.http.address=:80 + - --entrypoints.https.address=:443 + - --entrypoints.varnish.address=:8081 # requests from Varnish, not published + - --certificatesresolvers.le.acme.email=${TRAEFIK_EMAIL:?Set TRAEFIK_EMAIL} + - --certificatesresolvers.le.acme.storage=/certificates/acme.json + - --certificatesresolvers.le.acme.tlschallenge=true + - --accesslog + - --accesslog.format=json + - --log.level=INFO + - --log.format=json + - --api + networks: + - nw-public + - nw-traefik + deploy: + replicas: 1 + placement: + constraints: + - node.role == manager + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + - traefik.http.services.traefik-public.loadbalancer.server.port=8000 + + # Dashboard + - traefik.http.middlewares.admin-auth.basicauth.users=${TRAEFIK_BASIC_AUTH:?Set TRAEFIK_BASIC_AUTH} + - traefik.http.routers.traefik-dashboard.rule=Host(`${TRAEFIK_HOSTNAME:?Set TRAEFIK_HOSTNAME}`) + - traefik.http.routers.traefik-dashboard.entrypoints=https + - traefik.http.routers.traefik-dashboard.tls=true + - traefik.http.routers.traefik-dashboard.tls.certresolver=le + - traefik.http.routers.traefik-dashboard.service=api@internal + - traefik.http.routers.traefik-dashboard.middlewares=admin-auth + + # Generic middlewares + - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https + - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true + - traefik.http.middlewares.gzip.compress=true + - traefik.http.middlewares.gzip.compress.excludedcontenttypes=image/png, image/jpeg, font/woff2 + + # Redirect every HTTP request to HTTPS + - traefik.http.routers.generic-https-redirect.entrypoints=http + - traefik.http.routers.generic-https-redirect.rule=HostRegexp(`^.+$$`) + - traefik.http.routers.generic-https-redirect.priority=1 + - traefik.http.routers.generic-https-redirect.middlewares=https-redirect + + varnish: + image: varnish + configs: + - source: varnish-vcl + target: /etc/varnish/default.vcl + networks: + - nw-public + - nw-internal + deploy: + replicas: ${STACK_VARNISH_REPLICAS:-2} + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME:?Set STACK_NAME}-varnish.loadbalancer.server.port=80 + + # Middleware + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.permanent=true + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.regex=^https://${STACK_HOSTNAME_REDIRECT:?Set STACK_HOSTNAME_REDIRECT}/(.*) + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.replacement=https://${STACK_HOSTNAME:?Set STACK_HOSTNAME}/$${1} + + # Routers + ## Redirect + - traefik.http.routers.rt-${STACK_NAME}-varnish-redirect.rule=Host(`${STACK_HOSTNAME_REDIRECT}`) + - traefik.http.routers.rt-${STACK_NAME}-varnish-redirect.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-varnish-redirect.tls=true + - traefik.http.routers.rt-${STACK_NAME}-varnish-redirect.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-varnish-redirect.middlewares=mw-${STACK_NAME}-redirect + ## Public requests + - traefik.http.routers.rt-${STACK_NAME}-varnish.rule=Host(`${STACK_HOSTNAME}`) + - traefik.http.routers.rt-${STACK_NAME}-varnish.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-varnish.tls=true + - traefik.http.routers.rt-${STACK_NAME}-varnish.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-varnish.service=svc-${STACK_NAME}-varnish + - traefik.http.routers.rt-${STACK_NAME}-varnish.middlewares=gzip + + purger: + image: ghcr.io/kitconcept/cluster-purger:latest + environment: + PURGER_MODE: swarm + PURGER_SERVICE_NAME: ${STACK_NAME}_varnish + PURGER_SERVICE_PORT: 80 + PURGER_PUBLIC_SITES: "['${STACK_HOSTNAME}']" + networks: + - nw-internal + deploy: + replicas: 2 + update_config: + parallelism: 1 + delay: 5s + order: start-first + + frontend: + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + environment: + RAZZLE_INTERNAL_API_PATH: http://${STACK_NAME}_backend:8080/Plone + RAZZLE_API_PATH: https://${STACK_HOSTNAME} + networks: + - nw-public + - nw-internal + deploy: + replicas: ${STACK_FRONT_REPLICAS:-2} + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME}-frontend.loadbalancer.server.port=3000 + + # Routers + ## / (requests from Varnish) + - traefik.http.routers.rt-${STACK_NAME}-frontend.rule=Host(`${STACK_HOSTNAME}`) && Header(`X-Varnish-Routed`, `1`) + - traefik.http.routers.rt-${STACK_NAME}-frontend.entrypoints=varnish + - traefik.http.routers.rt-${STACK_NAME}-frontend.service=svc-${STACK_NAME}-frontend + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + PROFILES: "plone.app.caching:with-caching-proxy" + ZEO_ADDRESS: "${STACK_NAME}_db:8100" + networks: + - nw-public + - nw-internal + deploy: + replicas: ${STACK_BACK_REPLICAS:-2} + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME}-backend.loadbalancer.server.port=8080 + + # Middlewares + ## Virtual Host Monster rewrite for /++api++/ + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-api.replacepathregex.regex=^/\\+\\+api\\+\\+($$|/.*)" + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-api.replacepathregex.replacement=/VirtualHostBase/https/${STACK_HOSTNAME}/Plone/++api++/VirtualHostRoot$$1" + ## Virtual Host Monster rewrite for /ClassicUI/ + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-classic.replacepathregex.regex=^/ClassicUI($$|/.*)" + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-classic.replacepathregex.replacement=/VirtualHostBase/https/${STACK_HOSTNAME}/Plone/VirtualHostRoot/_vh_ClassicUI$$1" + ## Basic authentication for /ClassicUI/ + - traefik.http.middlewares.mw-${STACK_NAME}-backend-auth.basicauth.users=${BASIC_AUTH_USER:?Set BASIC_AUTH_USER}:${BASIC_AUTH_PASSWORD_HASH:?Set BASIC_AUTH_PASSWORD_HASH} + + # Routers + ## /++api++ (requests from Varnish) + - traefik.http.routers.rt-${STACK_NAME}-backend-api.rule=Host(`${STACK_HOSTNAME}`) && PathPrefix(`/++api++`) && Header(`X-Varnish-Routed`, `1`) + - traefik.http.routers.rt-${STACK_NAME}-backend-api.entrypoints=varnish + - traefik.http.routers.rt-${STACK_NAME}-backend-api.service=svc-${STACK_NAME}-backend + - traefik.http.routers.rt-${STACK_NAME}-backend-api.middlewares=mw-${STACK_NAME}-backend-vhm-api + ## /ClassicUI + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.rule=Host(`${STACK_HOSTNAME}`) && PathPrefix(`/ClassicUI`) + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.tls=true + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.service=svc-${STACK_NAME}-backend + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.middlewares=gzip,mw-${STACK_NAME}-backend-auth,mw-${STACK_NAME}-backend-vhm-classic + + db: + image: plone/plone-zeo:{PLONE_ZEO_VERSION} + volumes: + - vol-site-data:/data + networks: + - nw-internal + deploy: + replicas: 1 + update_config: + parallelism: 1 + delay: 1s + order: stop-first + +configs: + varnish-vcl: + file: ./etc/varnish.vcl + +volumes: + vol-traefik-certs: {} + vol-site-data: {} + +networks: + nw-public: + external: true + nw-traefik: + driver: overlay + internal: true + nw-internal: + driver: overlay + internal: true +``` + +Docker Swarm stores the content of `etc/varnish.vcl` under the name `varnish-vcl`, and you can't change it after you deploy the stack. +To change the Varnish configuration later, rename `varnish-vcl` in both places in `stack.yml`, for example to `varnish-vcl-2`, and deploy the stack again. + +```{warning} +Docker volumes are local to the node where they're created. +In a cluster with more than one node, add a placement constraint to the `db` service, so the ZEO server always runs on the node that holds its data. +``` + + +### Environment variables + +The stack reads its configuration from the following environment variables. +Variables without a default value are required, and `docker stack deploy` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_NAME` | Name of the stack, which must match the name you pass to `docker stack deploy`. The services use it to reach each other, and Traefik uses it to name routers, services, and middleware. | | `plone` | +| `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | +| `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | +| `STACK_VARNISH_REPLICAS` | Number of Varnish replicas | `2` | `3` | +| `STACK_FRONT_REPLICAS` | Number of frontend replicas | `2` | `3` | +| `STACK_BACK_REPLICAS` | Number of backend replicas | `2` | `4` | +| `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | +| `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | +| `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | +| `BASIC_AUTH_USER` | User name for the Classic UI at `/ClassicUI` | | `admin` | +| `BASIC_AUTH_PASSWORD_HASH` | Password hash for the Classic UI at `/ClassicUI` | | `$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | + +To create a password hash, run the following command, with your password instead of `secret`. + +```shell +openssl passwd -apr1 secret +``` + +Create a `.env` file with your values. +Wrap values that contain a dollar sign, such as password hashes, in single quotes. + +```shell +STACK_NAME=plone +STACK_HOSTNAME=www.example.com +STACK_HOSTNAME_REDIRECT=example.com +TRAEFIK_HOSTNAME=traefik.example.com +TRAEFIK_EMAIL=admin@example.com +TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' +BASIC_AUTH_USER=admin +BASIC_AUTH_PASSWORD_HASH='$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' +``` + +`docker stack deploy` doesn't read `.env` files. +Load the variables into your shell before you deploy. + +```shell +set -a +. ./.env +set +a +``` + + +## Deploy the stack + +Deploy the stack with the name from `STACK_NAME`. + +```shell +docker stack deploy -c stack.yml "$STACK_NAME" +``` + +This pulls the needed images and starts Plone. + + +## Configure cache purging + +The `plone.app.caching:with-caching-proxy` profile sets up caching rules for the site, but it doesn't set where the backend sends purge requests. +Go to the caching control panel at `https://www.example.com/ClassicUI/@@caching-controlpanel`, using your value of `STACK_HOSTNAME`, and set the following options. + +{guilabel}`Enable purging` +: Selected + +{guilabel}`Caching proxies` +: `http://purger` + +{guilabel}`Send PURGE requests with virtual hosting paths` +: Cleared + +The backend then sends purge requests to the `purger` service, which forwards them to every Varnish replica. + + +## Access Plone + +After startup, go to `https://www.example.com`, using your value of `STACK_HOSTNAME`, and you should see the site. + +The Classic UI is available at `https://www.example.com/ClassicUI`, behind the user name and password from `BASIC_AUTH_USER` and `BASIC_AUTH_PASSWORD_HASH`. + +The Traefik dashboard is available at the host name from `TRAEFIK_HOSTNAME`, behind the user name and password from `TRAEFIK_BASIC_AUTH`. + + +## Increase the number of backends + +To run four backend replicas, scale the backend service. + +```shell +docker service scale "${STACK_NAME}_backend=4" +``` + +The next `docker stack deploy` sets the number of replicas back to the value of `STACK_BACK_REPLICAS`. + + +## Shutdown and cleanup + +The command `docker stack rm "$STACK_NAME"` removes the services and the stack networks, but preserves the Plone database and the TLS certificates in their volumes. diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md b/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md new file mode 100644 index 0000000000..f859e3810b --- /dev/null +++ b/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md @@ -0,0 +1,352 @@ +--- +myst: + html_meta: + "description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend and backend, and a ZEO server." + "property=og:description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend and backend, and a ZEO server." + "property=og:title": "Docker Swarm: Traefik, Frontend, Backend, ZEO example" + "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Frontend, Backend, ZEO" +--- + +# Docker Swarm: Traefik, Frontend, Backend, ZEO example + +This example deploys Plone 6 as a stack on a Docker Swarm cluster. +The stack runs the following services. + +`traefik` +: {term}`Traefik Proxy` routes requests to the other services, and gets TLS certificates from Let's Encrypt. + +`socket-proxy` +: Gives Traefik access to the parts of the Docker API it needs, instead of the Docker socket itself. + +`frontend` +: The Plone frontend, with two replicas by default. + +`backend` +: The Plone backend, with two replicas by default, which stores its data in the ZEO server. + +`db` +: The ZEO server, with its data persisted in a Docker volume. + + +## Prerequisites + +- A Docker Swarm cluster. + To create a cluster with a single node, run `docker swarm init` on that node. +- DNS records that point the host names of `STACK_HOSTNAME`, `STACK_HOSTNAME_REDIRECT`, and `TRAEFIK_HOSTNAME` to your cluster. +- Ports 80 and 443 open to the internet, so Let's Encrypt can validate your host names. + + +## Setup + +Create an empty project directory named `swarm-traefik-volto-plone-zeo`. + +```shell +mkdir swarm-traefik-volto-plone-zeo +``` + +Change into your project directory. + +```shell +cd swarm-traefik-volto-plone-zeo +``` + + +### Create the public network + +Traefik and the services it routes requests to share an overlay network named `nw-public`. +Create it once on a manager node, before you deploy the stack. + +```shell +docker network create --driver overlay nw-public +``` + + +### Stack file + +Create a `stack.yml` file with the following content. + +```yaml +services: + + socket-proxy: + image: tecnativa/docker-socket-proxy + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + NODES: 1 + SERVICES: 1 + TASKS: 1 + NETWORKS: 1 + networks: + - nw-traefik + deploy: + placement: + constraints: + - node.role == manager + + traefik: + image: traefik:{TRAEFIK_VERSION} + ports: + - "80:80" + - "443:443" + volumes: + - vol-traefik-certs:/certificates + command: + - --providers.swarm + - --providers.swarm.endpoint=tcp://socket-proxy:2375 + - --providers.swarm.exposedbydefault=false + - --providers.swarm.network=nw-public + - --providers.swarm.constraints=Label(`traefik.constraint-label`, `public`) + - --entrypoints.http.address=:80 + - --entrypoints.https.address=:443 + - --certificatesresolvers.le.acme.email=${TRAEFIK_EMAIL:?Set TRAEFIK_EMAIL} + - --certificatesresolvers.le.acme.storage=/certificates/acme.json + - --certificatesresolvers.le.acme.tlschallenge=true + - --accesslog + - --accesslog.format=json + - --log.level=INFO + - --log.format=json + - --api + networks: + - nw-public + - nw-traefik + deploy: + replicas: 1 + placement: + constraints: + - node.role == manager + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + - traefik.http.services.traefik-public.loadbalancer.server.port=8000 + + # Dashboard + - traefik.http.middlewares.admin-auth.basicauth.users=${TRAEFIK_BASIC_AUTH:?Set TRAEFIK_BASIC_AUTH} + - traefik.http.routers.traefik-dashboard.rule=Host(`${TRAEFIK_HOSTNAME:?Set TRAEFIK_HOSTNAME}`) + - traefik.http.routers.traefik-dashboard.entrypoints=https + - traefik.http.routers.traefik-dashboard.tls=true + - traefik.http.routers.traefik-dashboard.tls.certresolver=le + - traefik.http.routers.traefik-dashboard.service=api@internal + - traefik.http.routers.traefik-dashboard.middlewares=admin-auth + + # Generic middlewares + - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https + - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true + - traefik.http.middlewares.gzip.compress=true + - traefik.http.middlewares.gzip.compress.excludedcontenttypes=image/png, image/jpeg, font/woff2 + + # Redirect every HTTP request to HTTPS + - traefik.http.routers.generic-https-redirect.entrypoints=http + - traefik.http.routers.generic-https-redirect.rule=HostRegexp(`^.+$$`) + - traefik.http.routers.generic-https-redirect.priority=1 + - traefik.http.routers.generic-https-redirect.middlewares=https-redirect + + frontend: + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + environment: + RAZZLE_INTERNAL_API_PATH: http://${STACK_NAME:?Set STACK_NAME}_backend:8080/Plone + RAZZLE_API_PATH: https://${STACK_HOSTNAME:?Set STACK_HOSTNAME} + networks: + - nw-public + - nw-internal + deploy: + replicas: ${STACK_FRONT_REPLICAS:-2} + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME}-frontend.loadbalancer.server.port=3000 + + # Middleware + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.permanent=true + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.regex=^https://${STACK_HOSTNAME_REDIRECT:?Set STACK_HOSTNAME_REDIRECT}/(.*) + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.replacement=https://${STACK_HOSTNAME}/$${1} + + # Routers + ## Redirect + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.rule=Host(`${STACK_HOSTNAME_REDIRECT}`) + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.tls=true + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.middlewares=mw-${STACK_NAME}-redirect + ## / + - traefik.http.routers.rt-${STACK_NAME}-frontend.rule=Host(`${STACK_HOSTNAME}`) + - traefik.http.routers.rt-${STACK_NAME}-frontend.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-frontend.tls=true + - traefik.http.routers.rt-${STACK_NAME}-frontend.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-frontend.service=svc-${STACK_NAME}-frontend + - traefik.http.routers.rt-${STACK_NAME}-frontend.middlewares=gzip + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + ZEO_ADDRESS: "${STACK_NAME}_db:8100" + networks: + - nw-public + - nw-internal + deploy: + replicas: ${STACK_BACK_REPLICAS:-2} + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME}-backend.loadbalancer.server.port=8080 + + # Middlewares + ## Virtual Host Monster rewrite for /++api++/ + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-api.replacepathregex.regex=^/\\+\\+api\\+\\+($$|/.*)" + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-api.replacepathregex.replacement=/VirtualHostBase/https/${STACK_HOSTNAME}/Plone/++api++/VirtualHostRoot$$1" + ## Virtual Host Monster rewrite for /ClassicUI/ + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-classic.replacepathregex.regex=^/ClassicUI($$|/.*)" + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-classic.replacepathregex.replacement=/VirtualHostBase/https/${STACK_HOSTNAME}/Plone/VirtualHostRoot/_vh_ClassicUI$$1" + ## Basic authentication for /ClassicUI/ + - traefik.http.middlewares.mw-${STACK_NAME}-backend-auth.basicauth.users=${BASIC_AUTH_USER:?Set BASIC_AUTH_USER}:${BASIC_AUTH_PASSWORD_HASH:?Set BASIC_AUTH_PASSWORD_HASH} + + # Routers + ## /++api++ + - traefik.http.routers.rt-${STACK_NAME}-backend-api.rule=Host(`${STACK_HOSTNAME}`) && PathPrefix(`/++api++`) + - traefik.http.routers.rt-${STACK_NAME}-backend-api.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-backend-api.tls=true + - traefik.http.routers.rt-${STACK_NAME}-backend-api.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-backend-api.service=svc-${STACK_NAME}-backend + - traefik.http.routers.rt-${STACK_NAME}-backend-api.middlewares=gzip,mw-${STACK_NAME}-backend-vhm-api + ## /ClassicUI + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.rule=Host(`${STACK_HOSTNAME}`) && PathPrefix(`/ClassicUI`) + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.tls=true + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.service=svc-${STACK_NAME}-backend + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.middlewares=gzip,mw-${STACK_NAME}-backend-auth,mw-${STACK_NAME}-backend-vhm-classic + + db: + image: plone/plone-zeo:{PLONE_ZEO_VERSION} + volumes: + - vol-site-data:/data + networks: + - nw-internal + deploy: + replicas: 1 + update_config: + parallelism: 1 + delay: 1s + order: stop-first + +volumes: + vol-traefik-certs: {} + vol-site-data: {} + +networks: + nw-public: + external: true + nw-traefik: + driver: overlay + internal: true + nw-internal: + driver: overlay + internal: true +``` + +Only the `db` service mounts the data volume. +The backends don't share the blob directory of the ZEO server, because `ZEO_SHARED_BLOB_DIR` is `off` by default, so they get blobs from the ZEO server through the network. + +```{warning} +Docker volumes are local to the node where they're created. +In a cluster with more than one node, add a placement constraint to the `db` service, so the ZEO server always runs on the node that holds its data. +``` + + +### Environment variables + +The stack reads its configuration from the following environment variables. +Variables without a default value are required, and `docker stack deploy` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_NAME` | Name of the stack, which must match the name you pass to `docker stack deploy`. The services use it to reach each other, and Traefik uses it to name routers, services, and middleware. | | `plone` | +| `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | +| `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | +| `STACK_FRONT_REPLICAS` | Number of frontend replicas | `2` | `3` | +| `STACK_BACK_REPLICAS` | Number of backend replicas | `2` | `4` | +| `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | +| `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | +| `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | +| `BASIC_AUTH_USER` | User name for the Classic UI at `/ClassicUI` | | `admin` | +| `BASIC_AUTH_PASSWORD_HASH` | Password hash for the Classic UI at `/ClassicUI` | | `$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | + +To create a password hash, run the following command, with your password instead of `secret`. + +```shell +openssl passwd -apr1 secret +``` + +Create a `.env` file with your values. +Wrap values that contain a dollar sign, such as password hashes, in single quotes. + +```shell +STACK_NAME=plone +STACK_HOSTNAME=www.example.com +STACK_HOSTNAME_REDIRECT=example.com +TRAEFIK_HOSTNAME=traefik.example.com +TRAEFIK_EMAIL=admin@example.com +TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' +BASIC_AUTH_USER=admin +BASIC_AUTH_PASSWORD_HASH='$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' +``` + +`docker stack deploy` doesn't read `.env` files. +Load the variables into your shell before you deploy. + +```shell +set -a +. ./.env +set +a +``` + + +## Deploy the stack + +Deploy the stack with the name from `STACK_NAME`. + +```shell +docker stack deploy -c stack.yml "$STACK_NAME" +``` + +This pulls the needed images and starts Plone. + + +## Access Plone + +After startup, go to `https://www.example.com`, using your value of `STACK_HOSTNAME`, and you should see the site. + +The Classic UI is available at `https://www.example.com/ClassicUI`, behind the user name and password from `BASIC_AUTH_USER` and `BASIC_AUTH_PASSWORD_HASH`. + +The Traefik dashboard is available at the host name from `TRAEFIK_HOSTNAME`, behind the user name and password from `TRAEFIK_BASIC_AUTH`. + + +## Increase the number of backends + +To run four backend replicas, scale the backend service. + +```shell +docker service scale "${STACK_NAME}_backend=4" +``` + +The next `docker stack deploy` sets the number of replicas back to the value of `STACK_BACK_REPLICAS`. + + +## Shutdown and cleanup + +The command `docker stack rm "$STACK_NAME"` removes the services and the stack networks, but preserves the Plone database and the TLS certificates in their volumes. diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone.md b/docs/install/containers/examples/swarm/traefik-volto-plone.md new file mode 100644 index 0000000000..fbc758fe92 --- /dev/null +++ b/docs/install/containers/examples/swarm/traefik-volto-plone.md @@ -0,0 +1,326 @@ +--- +myst: + html_meta: + "description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend, and a backend that stores its data in a Docker volume." + "property=og:description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend, and a backend that stores its data in a Docker volume." + "property=og:title": "Docker Swarm: Traefik, Frontend, Backend example" + "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Frontend, Backend" +--- + +# Docker Swarm: Traefik, Frontend, Backend example + +This example deploys Plone 6 as a stack on a Docker Swarm cluster. +The stack runs the following services. + +`traefik` +: {term}`Traefik Proxy` routes requests to the other services, and gets TLS certificates from Let's Encrypt. + +`socket-proxy` +: Gives Traefik access to the parts of the Docker API it needs, instead of the Docker socket itself. + +`frontend` +: The Plone frontend, with two replicas by default. + +`backend` +: The Plone backend, with its data persisted in a Docker volume. + + +## Prerequisites + +- A Docker Swarm cluster. + To create a cluster with a single node, run `docker swarm init` on that node. +- DNS records that point the host names of `STACK_HOSTNAME`, `STACK_HOSTNAME_REDIRECT`, and `TRAEFIK_HOSTNAME` to your cluster. +- Ports 80 and 443 open to the internet, so Let's Encrypt can validate your host names. + + +## Setup + +Create an empty project directory named `swarm-traefik-volto-plone`. + +```shell +mkdir swarm-traefik-volto-plone +``` + +Change into your project directory. + +```shell +cd swarm-traefik-volto-plone +``` + + +### Create the public network + +Traefik and the services it routes requests to share an overlay network named `nw-public`. +Create it once on a manager node, before you deploy the stack. + +```shell +docker network create --driver overlay nw-public +``` + + +### Stack file + +Create a `stack.yml` file with the following content. + +```yaml +services: + + socket-proxy: + image: tecnativa/docker-socket-proxy + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + NODES: 1 + SERVICES: 1 + TASKS: 1 + NETWORKS: 1 + networks: + - nw-traefik + deploy: + placement: + constraints: + - node.role == manager + + traefik: + image: traefik:{TRAEFIK_VERSION} + ports: + - "80:80" + - "443:443" + volumes: + - vol-traefik-certs:/certificates + command: + - --providers.swarm + - --providers.swarm.endpoint=tcp://socket-proxy:2375 + - --providers.swarm.exposedbydefault=false + - --providers.swarm.network=nw-public + - --providers.swarm.constraints=Label(`traefik.constraint-label`, `public`) + - --entrypoints.http.address=:80 + - --entrypoints.https.address=:443 + - --certificatesresolvers.le.acme.email=${TRAEFIK_EMAIL:?Set TRAEFIK_EMAIL} + - --certificatesresolvers.le.acme.storage=/certificates/acme.json + - --certificatesresolvers.le.acme.tlschallenge=true + - --accesslog + - --accesslog.format=json + - --log.level=INFO + - --log.format=json + - --api + networks: + - nw-public + - nw-traefik + deploy: + replicas: 1 + placement: + constraints: + - node.role == manager + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + - traefik.http.services.traefik-public.loadbalancer.server.port=8000 + + # Dashboard + - traefik.http.middlewares.admin-auth.basicauth.users=${TRAEFIK_BASIC_AUTH:?Set TRAEFIK_BASIC_AUTH} + - traefik.http.routers.traefik-dashboard.rule=Host(`${TRAEFIK_HOSTNAME:?Set TRAEFIK_HOSTNAME}`) + - traefik.http.routers.traefik-dashboard.entrypoints=https + - traefik.http.routers.traefik-dashboard.tls=true + - traefik.http.routers.traefik-dashboard.tls.certresolver=le + - traefik.http.routers.traefik-dashboard.service=api@internal + - traefik.http.routers.traefik-dashboard.middlewares=admin-auth + + # Generic middlewares + - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https + - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true + - traefik.http.middlewares.gzip.compress=true + - traefik.http.middlewares.gzip.compress.excludedcontenttypes=image/png, image/jpeg, font/woff2 + + # Redirect every HTTP request to HTTPS + - traefik.http.routers.generic-https-redirect.entrypoints=http + - traefik.http.routers.generic-https-redirect.rule=HostRegexp(`^.+$$`) + - traefik.http.routers.generic-https-redirect.priority=1 + - traefik.http.routers.generic-https-redirect.middlewares=https-redirect + + frontend: + image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + environment: + RAZZLE_INTERNAL_API_PATH: http://${STACK_NAME:?Set STACK_NAME}_backend:8080/Plone + RAZZLE_API_PATH: https://${STACK_HOSTNAME:?Set STACK_HOSTNAME} + networks: + - nw-public + - nw-internal + deploy: + replicas: ${STACK_FRONT_REPLICAS:-2} + update_config: + parallelism: 1 + delay: 5s + order: start-first + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME}-frontend.loadbalancer.server.port=3000 + + # Middleware + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.permanent=true + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.regex=^https://${STACK_HOSTNAME_REDIRECT:?Set STACK_HOSTNAME_REDIRECT}/(.*) + - traefik.http.middlewares.mw-${STACK_NAME}-redirect.redirectregex.replacement=https://${STACK_HOSTNAME}/$${1} + + # Routers + ## Redirect + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.rule=Host(`${STACK_HOSTNAME_REDIRECT}`) + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.tls=true + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-frontend-redirect.middlewares=mw-${STACK_NAME}-redirect + ## / + - traefik.http.routers.rt-${STACK_NAME}-frontend.rule=Host(`${STACK_HOSTNAME}`) + - traefik.http.routers.rt-${STACK_NAME}-frontend.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-frontend.tls=true + - traefik.http.routers.rt-${STACK_NAME}-frontend.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-frontend.service=svc-${STACK_NAME}-frontend + - traefik.http.routers.rt-${STACK_NAME}-frontend.middlewares=gzip + + backend: + image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + environment: + SITE: Plone + volumes: + - vol-site-data:/data + networks: + - nw-public + - nw-internal + deploy: + replicas: 1 + update_config: + parallelism: 1 + delay: 5s + order: stop-first # only one process can open the database + labels: + - traefik.enable=true + - traefik.constraint-label=public + # Service + - traefik.http.services.svc-${STACK_NAME}-backend.loadbalancer.server.port=8080 + + # Middlewares + ## Virtual Host Monster rewrite for /++api++/ + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-api.replacepathregex.regex=^/\\+\\+api\\+\\+($$|/.*)" + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-api.replacepathregex.replacement=/VirtualHostBase/https/${STACK_HOSTNAME}/Plone/++api++/VirtualHostRoot$$1" + ## Virtual Host Monster rewrite for /ClassicUI/ + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-classic.replacepathregex.regex=^/ClassicUI($$|/.*)" + - "traefik.http.middlewares.mw-${STACK_NAME}-backend-vhm-classic.replacepathregex.replacement=/VirtualHostBase/https/${STACK_HOSTNAME}/Plone/VirtualHostRoot/_vh_ClassicUI$$1" + ## Basic authentication for /ClassicUI/ + - traefik.http.middlewares.mw-${STACK_NAME}-backend-auth.basicauth.users=${BASIC_AUTH_USER:?Set BASIC_AUTH_USER}:${BASIC_AUTH_PASSWORD_HASH:?Set BASIC_AUTH_PASSWORD_HASH} + + # Routers + ## /++api++ + - traefik.http.routers.rt-${STACK_NAME}-backend-api.rule=Host(`${STACK_HOSTNAME}`) && PathPrefix(`/++api++`) + - traefik.http.routers.rt-${STACK_NAME}-backend-api.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-backend-api.tls=true + - traefik.http.routers.rt-${STACK_NAME}-backend-api.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-backend-api.service=svc-${STACK_NAME}-backend + - traefik.http.routers.rt-${STACK_NAME}-backend-api.middlewares=gzip,mw-${STACK_NAME}-backend-vhm-api + ## /ClassicUI + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.rule=Host(`${STACK_HOSTNAME}`) && PathPrefix(`/ClassicUI`) + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.entrypoints=https + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.tls=true + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.tls.certresolver=le + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.service=svc-${STACK_NAME}-backend + - traefik.http.routers.rt-${STACK_NAME}-backend-classic.middlewares=gzip,mw-${STACK_NAME}-backend-auth,mw-${STACK_NAME}-backend-vhm-classic + +volumes: + vol-traefik-certs: {} + vol-site-data: {} + +networks: + nw-public: + external: true + nw-traefik: + driver: overlay + internal: true + nw-internal: + driver: overlay + internal: true +``` + +```{warning} +The backend stores its data in a Docker volume, and only one backend process can open that data at a time. +Keep the `backend` service at one replica. +To run more than one backend, use the {doc}`ZEO ` or {doc}`PostgreSQL ` example instead. + +Docker volumes are also local to the node where they're created. +In a cluster with more than one node, add a placement constraint to the `backend` service, so it always runs on the node that holds its data. +``` + + +### Environment variables + +The stack reads its configuration from the following environment variables. +Variables without a default value are required, and `docker stack deploy` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_NAME` | Name of the stack, which must match the name you pass to `docker stack deploy`. The services use it to reach each other, and Traefik uses it to name routers, services, and middleware. | | `plone` | +| `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | +| `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | +| `STACK_FRONT_REPLICAS` | Number of frontend replicas | `2` | `3` | +| `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | +| `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | +| `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | +| `BASIC_AUTH_USER` | User name for the Classic UI at `/ClassicUI` | | `admin` | +| `BASIC_AUTH_PASSWORD_HASH` | Password hash for the Classic UI at `/ClassicUI` | | `$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | + +To create a password hash, run the following command, with your password instead of `secret`. + +```shell +openssl passwd -apr1 secret +``` + +Create a `.env` file with your values. +Wrap values that contain a dollar sign, such as password hashes, in single quotes. + +```shell +STACK_NAME=plone +STACK_HOSTNAME=www.example.com +STACK_HOSTNAME_REDIRECT=example.com +TRAEFIK_HOSTNAME=traefik.example.com +TRAEFIK_EMAIL=admin@example.com +TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' +BASIC_AUTH_USER=admin +BASIC_AUTH_PASSWORD_HASH='$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' +``` + +`docker stack deploy` doesn't read `.env` files. +Load the variables into your shell before you deploy. + +```shell +set -a +. ./.env +set +a +``` + + +## Deploy the stack + +Deploy the stack with the name from `STACK_NAME`. + +```shell +docker stack deploy -c stack.yml "$STACK_NAME" +``` + +This pulls the needed images and starts Plone. + + +## Access Plone + +After startup, go to `https://www.example.com`, using your value of `STACK_HOSTNAME`, and you should see the site. + +The Classic UI is available at `https://www.example.com/ClassicUI`, behind the user name and password from `BASIC_AUTH_USER` and `BASIC_AUTH_PASSWORD_HASH`. + +The Traefik dashboard is available at the host name from `TRAEFIK_HOSTNAME`, behind the user name and password from `TRAEFIK_BASIC_AUTH`. + + +## Shutdown and cleanup + +The command `docker stack rm "$STACK_NAME"` removes the services and the stack networks, but preserves the Plone database and the TLS certificates in their volumes. diff --git a/styles/config/vocabularies/Plone/accept.txt b/styles/config/vocabularies/Plone/accept.txt index 328c2c4d3c..80345051e2 100644 --- a/styles/config/vocabularies/Plone/accept.txt +++ b/styles/config/vocabularies/Plone/accept.txt @@ -25,15 +25,18 @@ extranets folderish fieldset getter +HAProxy interoperate JavaScript [Jj]enkins jQuery +Let's Encrypt libxslt middleware Mockup mxdev namespaces? +nginx npm nvm Pastanaga @@ -59,6 +62,7 @@ Schuko subfolder toggler [Tt]owncrier +Traefik transpilation transpile[drs]{0,1} [Uu]ncomment From ec15a37ba07c69ce3a7a0a795d3cbcbb5536832c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Sat, 12 Sep 2026 19:51:30 -0300 Subject: [PATCH 4/5] Address review of container images and examples documentation - Use the {doc} role for links to documents under docs/install/containers. - Use the {file} role for files and directories. - Read image tags from required STACK_*_TAG environment variables in the Docker Compose and Docker Swarm examples, and document them in an "Environment variables" table on each example. - Rename STACK_FRONT_REPLICAS and STACK_BACK_REPLICAS to STACK_FRONTEND_REPLICAS and STACK_BACKEND_REPLICAS. - Define the container image versions once in conf.py, and use them in both myst_substitutions and source_replacements. source_replace now skips {{KEY}}, so it doesn't break MyST substitutions. --- docs/conf.py | 27 ++++++++---- .../examples/compose/haproxy-plone-zeo.md | 25 +++++++++-- .../containers/examples/compose/index.md | 20 ++++----- .../examples/compose/nginx-plone.md | 27 +++++++++--- .../compose/nginx-volto-plone-postgresql.md | 35 ++++++++++++---- .../examples/compose/nginx-volto-plone-zeo.md | 35 ++++++++++++---- .../examples/compose/nginx-volto-plone.md | 31 +++++++++++--- .../examples/compose/traefik-plone.md | 29 ++++++++++--- .../compose/traefik-volto-plone-postgresql.md | 37 ++++++++++++---- .../compose/traefik-volto-plone-varnish.md | 37 ++++++++++++---- .../compose/traefik-volto-plone-zeo.md | 37 ++++++++++++---- .../examples/compose/traefik-volto-plone.md | 33 ++++++++++++--- docs/install/containers/examples/index.md | 30 ++++++------- .../containers/examples/swarm/index.md | 10 ++--- .../examples/swarm/traefik-plone.md | 16 ++++--- .../swarm/traefik-volto-plone-postgresql.md | 34 +++++++++------ .../swarm/traefik-volto-plone-varnish.md | 42 +++++++++++-------- .../examples/swarm/traefik-volto-plone-zeo.md | 34 +++++++++------ .../examples/swarm/traefik-volto-plone.md | 24 +++++++---- docs/install/containers/images/aurora.md | 6 +-- docs/install/containers/images/backend.md | 34 +++++++-------- docs/install/containers/images/frontend.md | 18 ++++---- docs/install/containers/images/historical.md | 6 +-- docs/install/containers/images/zeo.md | 2 +- docs/install/containers/index.md | 4 +- docs/install/containers/recipes/index.md | 2 +- 26 files changed, 443 insertions(+), 192 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a13bd7816b..042c8e4da4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,6 +4,7 @@ # -- Path setup -------------------------------------------------------------- +import re from datetime import datetime # If extensions (or modules to document with autodoc) are in another directory, @@ -316,6 +317,17 @@ "substitution", # Use Jinja2 for substitutions. https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#substitutions-with-jinja2 ] +# Versions of the container images used in the documentation. +# Use them as MyST substitutions in text, such as {{PLONE_BACKEND_MINOR_VERSION}}, +# and as source replacements in code blocks, such as {PLONE_BACKEND_MINOR_VERSION}. +container_image_versions = { + "PLONE_BACKEND_MINOR_VERSION": "6.2", + "PLONE_FRONTEND_VERSION": "19", + "PLONE_ZEO_VERSION": "6", + "TRAEFIK_VERSION": "v3.7", + "POSTGRES_VERSION": "18", +} + myst_substitutions = { "postman_basic_auth": "![](../_static/img/postman_basic_auth.png)", "postman_headers": "![](../_static/img/postman_headers.png)", @@ -326,6 +338,7 @@ "SUPPORTED_PYTHON_VERSIONS_PLONE60": "3.9, 3.10, 3.11, 3.12, or 3.13", "SUPPORTED_PYTHON_VERSIONS_PLONE61": "3.10, 3.11, 3.12, or 3.13", "SUPPORTED_PYTHON_VERSIONS_PLONE62": "3.10, 3.11, 3.12, 3.13, or 3.14", + **container_image_versions, } @@ -467,18 +480,16 @@ # https://stackoverflow.com/a/56328457/2214933 def source_replace(app, docname, source): result = source[0] - for key in app.config.source_replacements: - result = result.replace(key, app.config.source_replacements[key]) + for key, value in app.config.source_replacements.items(): + # Skip MyST substitutions, such as {{KEY}}, which contain the key {KEY}. + pattern = rf"(?` | Stack with Traefik, Frontend, and Backend | +| {doc}`traefik-volto-plone-zeo ` | Stack with Traefik, Frontend, Backend, and ZEO server | +| {doc}`traefik-volto-plone-postgresql ` | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | +| {doc}`traefik-plone ` | Stack with Traefik and Backend (Plone Classic) | +| {doc}`traefik-volto-plone-varnish ` | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | ## nginx | Project example | Description | | --- | --- | -| [`nginx-volto-plone`](nginx-volto-plone) | Stack with nginx, Frontend, and Backend | -| [`nginx-volto-plone-zeo`](nginx-volto-plone-zeo) | Stack with nginx, Frontend, Backend, and ZEO server | -| [`nginx-volto-plone-postgresql`](nginx-volto-plone-postgresql) | Stack with nginx, Frontend, Backend, and PostgreSQL DB | -| [`nginx-plone`](nginx-plone) | Stack with nginx and Backend (Plone Classic) | +| {doc}`nginx-volto-plone ` | Stack with nginx, Frontend, and Backend | +| {doc}`nginx-volto-plone-zeo ` | Stack with nginx, Frontend, Backend, and ZEO server | +| {doc}`nginx-volto-plone-postgresql ` | Stack with nginx, Frontend, Backend, and PostgreSQL DB | +| {doc}`nginx-plone ` | Stack with nginx and Backend (Plone Classic) | ## HAProxy | Project example | Description | | --- | --- | -| [`haproxy-plone-zeo`](haproxy-plone-zeo) | Stack with HAProxy, Backend, and ZEO server | +| {doc}`haproxy-plone-zeo ` | Stack with HAProxy, Backend, and ZEO server | diff --git a/docs/install/containers/examples/compose/nginx-plone.md b/docs/install/containers/examples/compose/nginx-plone.md index 6f5b778a62..670913960c 100644 --- a/docs/install/containers/examples/compose/nginx-plone.md +++ b/docs/install/containers/examples/compose/nginx-plone.md @@ -16,7 +16,7 @@ This example is a simple setup with one backend and data being persisted in a Do ## Setup -Create an empty project directory named `nginx-plone`. +Create an empty project directory named {file}`nginx-plone`. ```shell mkdir nginx-plone @@ -31,7 +31,7 @@ cd nginx-plone ### nginx configuration -Add a `default.conf` that will be used by the nginx image: +Add a {file}`default.conf` that will be used by the nginx image: ```nginx upstream backend { @@ -61,12 +61,12 @@ server { ```{note} `http://plone.localhost/` is the URL you will be using to access the website. -You can either use `plone.localhost`, or add it in your `/etc/hosts` file or DNS, to point to the Docker host IP. +You can either use `plone.localhost`, or add it in your {file}`/etc/hosts` file or DNS, to point to the Docker host IP. ``` ### Service configuration with Docker Compose -Now let's create a `docker-compose.yml` file: +Now let's create a {file}`docker-compose.yml` file: ```yaml services: @@ -81,7 +81,7 @@ services: - "80:80" backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone TYPE: classic @@ -95,6 +95,23 @@ volumes: ``` +### Environment variables + +The {file}`docker-compose.yml` file reads the tags of its images from the following environment variables. +All of them are required, and `docker compose` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | + +Create a {file}`.env` file in your project directory with your values. +Docker Compose reads it automatically when you run `docker compose` from that directory. + +```shell +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +``` + + ## Build the project Start the stack with `docker compose`. diff --git a/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md b/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md index 6e9e988aa4..b0b03d8781 100644 --- a/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md +++ b/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md @@ -16,7 +16,7 @@ This example is a very simple setup with one or more backend instances accessing ## Setup -Create an empty project directory named `nginx-volto-plone-postgresql`. +Create an empty project directory named {file}`nginx-volto-plone-postgresql`. ```shell mkdir nginx-volto-plone-postgresql @@ -31,7 +31,7 @@ cd nginx-volto-plone-postgresql ### nginx configuration -Add a `default.conf` that will be used by the nginx image: +Add a {file}`default.conf` that will be used by the nginx image: ```nginx upstream backend { @@ -74,13 +74,13 @@ server { ```{note} `http://plone.localhost/` is the URL you will be using to access the website. -You can either use `localhost`, or add it in your `/etc/hosts` file or DNS to point to the Docker host IP. +You can either use `localhost`, or add it in your {file}`/etc/hosts` file or DNS to point to the Docker host IP. ``` ### Service configuration with Docker Compose -Now let's create a `docker-compose.yml` file: +Now let's create a {file}`docker-compose.yml` file: ```yaml services: @@ -96,7 +96,7 @@ services: - "80:80" frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone ports: @@ -105,7 +105,7 @@ services: - backend backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone RELSTORAGE_DSN: "dbname='plone' user='plone' host='db' password='plone'" @@ -115,7 +115,7 @@ services: - db db: - image: postgres:{POSTGRES_VERSION} + image: postgres:${STACK_POSTGRES_TAG:?Set STACK_POSTGRES_TAG} environment: POSTGRES_USER: plone POSTGRES_PASSWORD: plone @@ -128,6 +128,27 @@ volumes: ``` +### Environment variables + +The {file}`docker-compose.yml` file reads the tags of its images from the following environment variables. +All of them are required, and `docker compose` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_POSTGRES_TAG` | Tag (version) of the image for PostgreSQL | | {{POSTGRES_VERSION}} | + +Create a {file}`.env` file in your project directory with your values. +Docker Compose reads it automatically when you run `docker compose` from that directory. + +```shell +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_POSTGRES_TAG={POSTGRES_VERSION} +``` + + ## Build the project Start the stack with `docker compose`. diff --git a/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md b/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md index 3cfc3d67bf..0589994fc4 100644 --- a/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md +++ b/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md @@ -16,7 +16,7 @@ This example is a very simple setup with one or more backend instances accessing ## Setup -Create an empty project directory named `nginx-volto-plone-zeo`. +Create an empty project directory named {file}`nginx-volto-plone-zeo`. ```shell mkdir nginx-volto-plone-zeo @@ -31,7 +31,7 @@ cd nginx-volto-plone-zeo ### nginx configuration -Add a `default.conf` that will be used by the nginx image: +Add a {file}`default.conf` that will be used by the nginx image: ```nginx upstream backend { @@ -74,13 +74,13 @@ server { ```{note} `http://plone.localhost/` is the URL you will be using to access the website. -You can either use `localhost`, or add it in your `/etc/hosts` file or DNS to point to the Docker host IP. +You can either use `localhost`, or add it in your {file}`/etc/hosts` file or DNS to point to the Docker host IP. ``` ### Service configuration with Docker Compose -Now let's create a `docker-compose.yml` file: +Now let's create a {file}`docker-compose.yml` file: ```yaml services: @@ -96,7 +96,7 @@ services: - "80:80" frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone ports: @@ -105,7 +105,7 @@ services: - backend backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone ZEO_ADDRESS: db:8100 @@ -118,7 +118,7 @@ services: - db db: - image: plone/plone-zeo:{PLONE_ZEO_VERSION} + image: plone/plone-zeo:${STACK_ZEO_TAG:?Set STACK_ZEO_TAG} restart: always volumes: - vol-site-data:/data @@ -128,6 +128,27 @@ volumes: ``` +### Environment variables + +The {file}`docker-compose.yml` file reads the tags of its images from the following environment variables. +All of them are required, and `docker compose` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_ZEO_TAG` | Tag (version) of the image for the ZEO server | | {{PLONE_ZEO_VERSION}} | + +Create a {file}`.env` file in your project directory with your values. +Docker Compose reads it automatically when you run `docker compose` from that directory. + +```shell +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_ZEO_TAG={PLONE_ZEO_VERSION} +``` + + ## Build the project Start the stack with `docker compose`. diff --git a/docs/install/containers/examples/compose/nginx-volto-plone.md b/docs/install/containers/examples/compose/nginx-volto-plone.md index f40207dded..265c193f24 100644 --- a/docs/install/containers/examples/compose/nginx-volto-plone.md +++ b/docs/install/containers/examples/compose/nginx-volto-plone.md @@ -15,7 +15,7 @@ This example is a very simple setup with one backend and data being persisted in ## Setup -Create an empty project directory named `nginx-volto-plone` +Create an empty project directory named {file}`nginx-volto-plone` ```shell mkdir nginx-volto-plone @@ -30,7 +30,7 @@ cd nginx-volto-plone ### nginx configuration -Add a `default.conf` that will be used by the nginx image: +Add a {file}`default.conf` that will be used by the nginx image: ```nginx upstream backend { @@ -73,12 +73,12 @@ server { ```{note} `http://plone.localhost/` is the URL you will be using to access the website. -You can either use `localhost`, or add it in your `/etc/hosts` file or DNS to point to the Docker host IP. +You can either use `localhost`, or add it in your {file}`/etc/hosts` file or DNS to point to the Docker host IP. ``` ### Service configuration with Docker Compose -Now let's create a `docker-compose.yml` file: +Now let's create a {file}`docker-compose.yml` file: ```yaml services: @@ -94,7 +94,7 @@ services: - "80:80" frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone ports: @@ -103,7 +103,7 @@ services: - backend backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone volumes: @@ -116,6 +116,25 @@ volumes: ``` +### Environment variables + +The {file}`docker-compose.yml` file reads the tags of its images from the following environment variables. +All of them are required, and `docker compose` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | + +Create a {file}`.env` file in your project directory with your values. +Docker Compose reads it automatically when you run `docker compose` from that directory. + +```shell +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +``` + + ## Build the project Start the stack with `docker compose`. diff --git a/docs/install/containers/examples/compose/traefik-plone.md b/docs/install/containers/examples/compose/traefik-plone.md index 4186e61017..ea183e53d2 100644 --- a/docs/install/containers/examples/compose/traefik-plone.md +++ b/docs/install/containers/examples/compose/traefik-plone.md @@ -16,7 +16,7 @@ In this example, {term}`Traefik Proxy` routes requests to the backend. ## Setup -Create an empty project directory named `traefik-plone`. +Create an empty project directory named {file}`traefik-plone`. ```shell mkdir traefik-plone @@ -31,14 +31,14 @@ cd traefik-plone ### Service configuration with Docker Compose -Create a `docker-compose.yml` file with the following content. +Create a {file}`docker-compose.yml` file with the following content. Traefik reads its routing configuration from the labels of the `backend` service, so this example doesn't need a separate proxy configuration file. ```yaml services: traefik: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - "80:80" volumes: @@ -50,7 +50,7 @@ services: - --accesslog backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone TYPE: classic @@ -77,7 +77,26 @@ volumes: ```{note} Use `http://plone.localhost/` to access the website. -If `plone.localhost` doesn't resolve on your computer, add it to your `/etc/hosts` file, pointing to the IP address of the Docker host. +If `plone.localhost` doesn't resolve on your computer, add it to your {file}`/etc/hosts` file, pointing to the IP address of the Docker host. +``` + + +### Environment variables + +The {file}`docker-compose.yml` file reads the tags of its images from the following environment variables. +All of them are required, and `docker compose` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | + +Create a {file}`.env` file in your project directory with your values. +Docker Compose reads it automatically when you run `docker compose` from that directory. + +```shell +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} ``` diff --git a/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md b/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md index 5b9af7b52d..d5228f1a0b 100644 --- a/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md +++ b/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md @@ -16,7 +16,7 @@ In this example, {term}`Traefik Proxy` routes requests to the frontend and the b ## Setup -Create an empty project directory named `traefik-volto-plone-postgresql`. +Create an empty project directory named {file}`traefik-volto-plone-postgresql`. ```shell mkdir traefik-volto-plone-postgresql @@ -31,14 +31,14 @@ cd traefik-volto-plone-postgresql ### Service configuration with Docker Compose -Create a `docker-compose.yml` file with the following content. +Create a {file}`docker-compose.yml` file with the following content. Traefik reads its routing configuration from the labels of the `frontend` and `backend` services, so this example doesn't need a separate proxy configuration file. ```yaml services: traefik: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - "80:80" volumes: @@ -50,7 +50,7 @@ services: - --accesslog frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone depends_on: @@ -65,7 +65,7 @@ services: - traefik.http.routers.rt-frontend.service=svc-frontend backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone RELSTORAGE_DSN: "dbname='plone' user='plone' host='db' password='plone'" @@ -85,7 +85,7 @@ services: - traefik.http.routers.rt-backend-api.middlewares=mw-backend-vhm-api db: - image: postgres:{POSTGRES_VERSION} + image: postgres:${STACK_POSTGRES_TAG:?Set STACK_POSTGRES_TAG} environment: POSTGRES_USER: plone POSTGRES_PASSWORD: plone @@ -99,7 +99,30 @@ volumes: ```{note} Use `http://plone.localhost/` to access the website. -If `plone.localhost` doesn't resolve on your computer, add it to your `/etc/hosts` file, pointing to the IP address of the Docker host. +If `plone.localhost` doesn't resolve on your computer, add it to your {file}`/etc/hosts` file, pointing to the IP address of the Docker host. +``` + + +### Environment variables + +The {file}`docker-compose.yml` file reads the tags of its images from the following environment variables. +All of them are required, and `docker compose` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_POSTGRES_TAG` | Tag (version) of the image for PostgreSQL | | {{POSTGRES_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | + +Create a {file}`.env` file in your project directory with your values. +Docker Compose reads it automatically when you run `docker compose` from that directory. + +```shell +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_POSTGRES_TAG={POSTGRES_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} ``` diff --git a/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md b/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md index 3160bb91c8..5cd2ed2432 100644 --- a/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md +++ b/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md @@ -19,7 +19,7 @@ A purger component is also used. This solves the problem of invalidating the cac ## Create a project space -Create an empty project directory named `traefik-volto-plone-varnish`. +Create an empty project directory named {file}`traefik-volto-plone-varnish`. ```shell mkdir traefik-volto-plone-varnish @@ -34,7 +34,7 @@ cd traefik-volto-plone-varnish ## Varnish configuration -Create an empty directory named `etc`. +Create an empty directory named {file}`etc`. ```shell mkdir etc @@ -315,7 +315,7 @@ sub vcl_deliver { ```{note} `http://plone.localhost/` is the URL you will be using to access the website. -You can either use `localhost`, or add it in your `/etc/hosts` file or DNS to point to the Docker host IP. +You can either use `localhost`, or add it in your {file}`/etc/hosts` file or DNS to point to the Docker host IP. ``` ## Service configuration with Docker Compose @@ -325,7 +325,7 @@ Now let's create a {file}`docker-compose.yml` file: ```yaml services: webserver: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - 80:80 @@ -364,7 +364,7 @@ services: - --api frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone RAZZLE_API_PATH: http://plone.localhost @@ -389,7 +389,7 @@ services: - "3000:3000" backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone PROFILES: "plone.app.caching:with-caching-proxy" @@ -465,7 +465,7 @@ services: - backend db: - image: plone/plone-zeo:{PLONE_ZEO_VERSION} + image: plone/plone-zeo:${STACK_ZEO_TAG:?Set STACK_ZEO_TAG} volumes: - vol-site-data:/data @@ -474,6 +474,29 @@ volumes: ``` +### Environment variables + +The {file}`docker-compose.yml` file reads the tags of its images from the following environment variables. +All of them are required, and `docker compose` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_ZEO_TAG` | Tag (version) of the image for the ZEO server | | {{PLONE_ZEO_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | + +Create a {file}`.env` file in your project directory with your values. +Docker Compose reads it automatically when you run `docker compose` from that directory. + +```shell +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_ZEO_TAG={PLONE_ZEO_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} +``` + + ## Build the project Start the stack with `docker compose`. diff --git a/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md b/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md index 5b1f6dc352..1e98269800 100644 --- a/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md +++ b/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md @@ -16,7 +16,7 @@ In this example, {term}`Traefik Proxy` routes requests to the frontend and the b ## Setup -Create an empty project directory named `traefik-volto-plone-zeo`. +Create an empty project directory named {file}`traefik-volto-plone-zeo`. ```shell mkdir traefik-volto-plone-zeo @@ -31,14 +31,14 @@ cd traefik-volto-plone-zeo ### Service configuration with Docker Compose -Create a `docker-compose.yml` file with the following content. +Create a {file}`docker-compose.yml` file with the following content. Traefik reads its routing configuration from the labels of the `frontend` and `backend` services, so this example doesn't need a separate proxy configuration file. ```yaml services: traefik: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - "80:80" volumes: @@ -50,7 +50,7 @@ services: - --accesslog frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone depends_on: @@ -65,7 +65,7 @@ services: - traefik.http.routers.rt-frontend.service=svc-frontend backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone ZEO_ADDRESS: db:8100 @@ -88,7 +88,7 @@ services: - traefik.http.routers.rt-backend-api.middlewares=mw-backend-vhm-api db: - image: plone/plone-zeo:{PLONE_ZEO_VERSION} + image: plone/plone-zeo:${STACK_ZEO_TAG:?Set STACK_ZEO_TAG} restart: always volumes: - vol-site-data:/data @@ -99,7 +99,30 @@ volumes: ```{note} Use `http://plone.localhost/` to access the website. -If `plone.localhost` doesn't resolve on your computer, add it to your `/etc/hosts` file, pointing to the IP address of the Docker host. +If `plone.localhost` doesn't resolve on your computer, add it to your {file}`/etc/hosts` file, pointing to the IP address of the Docker host. +``` + + +### Environment variables + +The {file}`docker-compose.yml` file reads the tags of its images from the following environment variables. +All of them are required, and `docker compose` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_ZEO_TAG` | Tag (version) of the image for the ZEO server | | {{PLONE_ZEO_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | + +Create a {file}`.env` file in your project directory with your values. +Docker Compose reads it automatically when you run `docker compose` from that directory. + +```shell +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_ZEO_TAG={PLONE_ZEO_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} ``` diff --git a/docs/install/containers/examples/compose/traefik-volto-plone.md b/docs/install/containers/examples/compose/traefik-volto-plone.md index d719b459cb..bf9623759b 100644 --- a/docs/install/containers/examples/compose/traefik-volto-plone.md +++ b/docs/install/containers/examples/compose/traefik-volto-plone.md @@ -16,7 +16,7 @@ In this example, {term}`Traefik Proxy` routes requests to the frontend and the b ## Setup -Create an empty project directory named `traefik-volto-plone`. +Create an empty project directory named {file}`traefik-volto-plone`. ```shell mkdir traefik-volto-plone @@ -31,14 +31,14 @@ cd traefik-volto-plone ### Service configuration with Docker Compose -Create a `docker-compose.yml` file with the following content. +Create a {file}`docker-compose.yml` file with the following content. Traefik reads its routing configuration from the labels of the `frontend` and `backend` services, so this example doesn't need a separate proxy configuration file. ```yaml services: traefik: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - "80:80" volumes: @@ -50,7 +50,7 @@ services: - --accesslog frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://backend:8080/Plone depends_on: @@ -65,7 +65,7 @@ services: - traefik.http.routers.rt-frontend.service=svc-frontend backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone volumes: @@ -89,7 +89,28 @@ volumes: ```{note} Use `http://plone.localhost/` to access the website. -If `plone.localhost` doesn't resolve on your computer, add it to your `/etc/hosts` file, pointing to the IP address of the Docker host. +If `plone.localhost` doesn't resolve on your computer, add it to your {file}`/etc/hosts` file, pointing to the IP address of the Docker host. +``` + + +### Environment variables + +The {file}`docker-compose.yml` file reads the tags of its images from the following environment variables. +All of them are required, and `docker compose` stops with an error if one of them is missing. + +| Variable | Description | Default value | Example | +| --- | --- | --- | --- | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | + +Create a {file}`.env` file in your project directory with your values. +Docker Compose reads it automatically when you run `docker compose` from that directory. + +```shell +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} ``` diff --git a/docs/install/containers/examples/index.md b/docs/install/containers/examples/index.md index d08fe83b89..b2bc27e35b 100644 --- a/docs/install/containers/examples/index.md +++ b/docs/install/containers/examples/index.md @@ -25,26 +25,26 @@ Examples of projects running Plone using `docker compose`. | Project example | Description | | --- | --- | -| [`traefik-volto-plone`](compose/traefik-volto-plone) | Stack with Traefik, Frontend, and Backend | -| [`traefik-volto-plone-zeo`](compose/traefik-volto-plone-zeo) | Stack with Traefik, Frontend, Backend, and ZEO server | -| [`traefik-volto-plone-postgresql`](compose/traefik-volto-plone-postgresql) | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | -| [`traefik-plone`](compose/traefik-plone) | Stack with Traefik and Backend (Plone Classic) | -| [`traefik-volto-plone-varnish`](compose/traefik-volto-plone-varnish) | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | +| {doc}`traefik-volto-plone ` | Stack with Traefik, Frontend, and Backend | +| {doc}`traefik-volto-plone-zeo ` | Stack with Traefik, Frontend, Backend, and ZEO server | +| {doc}`traefik-volto-plone-postgresql ` | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | +| {doc}`traefik-plone ` | Stack with Traefik and Backend (Plone Classic) | +| {doc}`traefik-volto-plone-varnish ` | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | ### nginx | Project example | Description | | --- | --- | -| [`nginx-volto-plone`](compose/nginx-volto-plone) | Stack with nginx, Frontend, and Backend | -| [`nginx-volto-plone-zeo`](compose/nginx-volto-plone-zeo) | Stack with nginx, Frontend, Backend, and ZEO server | -| [`nginx-volto-plone-postgresql`](compose/nginx-volto-plone-postgresql) | Stack with nginx, Frontend, Backend, and PostgreSQL DB | -| [`nginx-plone`](compose/nginx-plone) | Stack with nginx and Backend (Plone Classic) | +| {doc}`nginx-volto-plone ` | Stack with nginx, Frontend, and Backend | +| {doc}`nginx-volto-plone-zeo ` | Stack with nginx, Frontend, Backend, and ZEO server | +| {doc}`nginx-volto-plone-postgresql ` | Stack with nginx, Frontend, Backend, and PostgreSQL DB | +| {doc}`nginx-plone ` | Stack with nginx and Backend (Plone Classic) | ### HAProxy | Project example | Description | | --- | --- | -| [`haproxy-plone-zeo`](compose/haproxy-plone-zeo) | Stack with HAProxy, Backend, and ZEO server | +| {doc}`haproxy-plone-zeo ` | Stack with HAProxy, Backend, and ZEO server | ## Docker Swarm @@ -53,8 +53,8 @@ Examples of stacks running Plone on a Docker Swarm cluster. | Stack example | Description | | --- | --- | -| [`traefik-volto-plone`](swarm/traefik-volto-plone) | Stack with Traefik, Frontend, and Backend | -| [`traefik-volto-plone-zeo`](swarm/traefik-volto-plone-zeo) | Stack with Traefik, Frontend, Backend, and ZEO server | -| [`traefik-volto-plone-postgresql`](swarm/traefik-volto-plone-postgresql) | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | -| [`traefik-plone`](swarm/traefik-plone) | Stack with Traefik and Backend (Plone Classic) | -| [`traefik-volto-plone-varnish`](swarm/traefik-volto-plone-varnish) | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | +| {doc}`traefik-volto-plone ` | Stack with Traefik, Frontend, and Backend | +| {doc}`traefik-volto-plone-zeo ` | Stack with Traefik, Frontend, Backend, and ZEO server | +| {doc}`traefik-volto-plone-postgresql ` | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | +| {doc}`traefik-plone ` | Stack with Traefik and Backend (Plone Classic) | +| {doc}`traefik-volto-plone-varnish ` | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | diff --git a/docs/install/containers/examples/swarm/index.md b/docs/install/containers/examples/swarm/index.md index 827e535b39..87a589b965 100644 --- a/docs/install/containers/examples/swarm/index.md +++ b/docs/install/containers/examples/swarm/index.md @@ -24,8 +24,8 @@ Examples of stacks for running Plone with `docker swarm`. | Stack example | Description | | --- | --- | -| [`traefik-volto-plone`](traefik-volto-plone) | Stack with Traefik, Frontend, and Backend | -| [`traefik-volto-plone-zeo`](traefik-volto-plone-zeo) | Stack with Traefik, Frontend, Backend, and ZEO server | -| [`traefik-volto-plone-postgresql`](traefik-volto-plone-postgresql) | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | -| [`traefik-plone`](traefik-plone) | Stack with Traefik and Backend (Plone Classic) | -| [`traefik-volto-plone-varnish`](traefik-volto-plone-varnish) | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | +| {doc}`traefik-volto-plone ` | Stack with Traefik, Frontend, and Backend | +| {doc}`traefik-volto-plone-zeo ` | Stack with Traefik, Frontend, Backend, and ZEO server | +| {doc}`traefik-volto-plone-postgresql ` | Stack with Traefik, Frontend, Backend, and PostgreSQL DB | +| {doc}`traefik-plone ` | Stack with Traefik and Backend (Plone Classic) | +| {doc}`traefik-volto-plone-varnish ` | Stack with Traefik, Frontend, Backend, ZEO server, and Varnish | diff --git a/docs/install/containers/examples/swarm/traefik-plone.md b/docs/install/containers/examples/swarm/traefik-plone.md index 6f85ad29cc..a510f361ec 100644 --- a/docs/install/containers/examples/swarm/traefik-plone.md +++ b/docs/install/containers/examples/swarm/traefik-plone.md @@ -32,7 +32,7 @@ The stack runs the following services. ## Setup -Create an empty project directory named `swarm-traefik-plone`. +Create an empty project directory named {file}`swarm-traefik-plone`. ```shell mkdir swarm-traefik-plone @@ -57,7 +57,7 @@ docker network create --driver overlay nw-public ### Stack file -Create a `stack.yml` file with the following content. +Create a {file}`stack.yml` file with the following content. ```yaml services: @@ -79,7 +79,7 @@ services: - node.role == manager traefik: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - "80:80" - "443:443" @@ -140,7 +140,7 @@ services: - traefik.http.routers.generic-https-redirect.middlewares=https-redirect backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone TYPE: classic @@ -215,6 +215,8 @@ All of them are required, and `docker stack deploy` stops with an error if one o | `STACK_NAME` | Name of the stack, which must match the name you pass to `docker stack deploy`. Traefik uses it to name routers, services, and middleware. | | `plone` | | `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | | `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | | `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | | `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | | `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | @@ -225,19 +227,21 @@ To create a password hash, run the following command, with your password instead openssl passwd -apr1 secret ``` -Create a `.env` file with your values. +Create a {file}`.env` file with your values. Wrap values that contain a dollar sign, such as password hashes, in single quotes. ```shell STACK_NAME=plone STACK_HOSTNAME=www.example.com STACK_HOSTNAME_REDIRECT=example.com +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} TRAEFIK_HOSTNAME=traefik.example.com TRAEFIK_EMAIL=admin@example.com TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' ``` -`docker stack deploy` doesn't read `.env` files. +`docker stack deploy` doesn't read {file}`.env` files. Load the variables into your shell before you deploy. ```shell diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md b/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md index 7bc3dadb55..21697959b1 100644 --- a/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md +++ b/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md @@ -38,7 +38,7 @@ The stack runs the following services. ## Setup -Create an empty project directory named `swarm-traefik-volto-plone-postgresql`. +Create an empty project directory named {file}`swarm-traefik-volto-plone-postgresql`. ```shell mkdir swarm-traefik-volto-plone-postgresql @@ -63,7 +63,7 @@ docker network create --driver overlay nw-public ### Stack file -Create a `stack.yml` file with the following content. +Create a {file}`stack.yml` file with the following content. ```yaml services: @@ -85,7 +85,7 @@ services: - node.role == manager traefik: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - "80:80" - "443:443" @@ -146,7 +146,7 @@ services: - traefik.http.routers.generic-https-redirect.middlewares=https-redirect frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://${STACK_NAME:?Set STACK_NAME}_backend:8080/Plone RAZZLE_API_PATH: https://${STACK_HOSTNAME:?Set STACK_HOSTNAME} @@ -154,7 +154,7 @@ services: - nw-public - nw-internal deploy: - replicas: ${STACK_FRONT_REPLICAS:-2} + replicas: ${STACK_FRONTEND_REPLICAS:-2} update_config: parallelism: 1 delay: 5s @@ -186,7 +186,7 @@ services: - traefik.http.routers.rt-${STACK_NAME}-frontend.middlewares=gzip backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone RELSTORAGE_DSN: "dbname='${DB_NAME:-plone}' user='${DB_USER:-plone}' host='${STACK_NAME}_db' password='${DB_PASSWORD:?Set DB_PASSWORD}'" @@ -194,7 +194,7 @@ services: - nw-public - nw-internal deploy: - replicas: ${STACK_BACK_REPLICAS:-2} + replicas: ${STACK_BACKEND_REPLICAS:-2} update_config: parallelism: 1 delay: 5s @@ -232,7 +232,7 @@ services: - traefik.http.routers.rt-${STACK_NAME}-backend-classic.middlewares=gzip,mw-${STACK_NAME}-backend-auth,mw-${STACK_NAME}-backend-vhm-classic db: - image: postgres:{POSTGRES_VERSION} + image: postgres:${STACK_POSTGRES_TAG:?Set STACK_POSTGRES_TAG} environment: POSTGRES_USER: ${DB_USER:-plone} POSTGRES_PASSWORD: ${DB_PASSWORD} @@ -279,8 +279,12 @@ Variables without a default value are required, and `docker stack deploy` stops | `STACK_NAME` | Name of the stack, which must match the name you pass to `docker stack deploy`. The services use it to reach each other, and Traefik uses it to name routers, services, and middleware. | | `plone` | | `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | | `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | -| `STACK_FRONT_REPLICAS` | Number of frontend replicas | `2` | `3` | -| `STACK_BACK_REPLICAS` | Number of backend replicas | `2` | `4` | +| `STACK_FRONTEND_REPLICAS` | Number of frontend replicas | `2` | `3` | +| `STACK_BACKEND_REPLICAS` | Number of backend replicas | `2` | `4` | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_POSTGRES_TAG` | Tag (version) of the image for PostgreSQL | | {{POSTGRES_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | | `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | | `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | | `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | @@ -296,13 +300,17 @@ To create a password hash, run the following command, with your password instead openssl passwd -apr1 secret ``` -Create a `.env` file with your values. +Create a {file}`.env` file with your values. Wrap values that contain a dollar sign, such as password hashes, in single quotes. ```shell STACK_NAME=plone STACK_HOSTNAME=www.example.com STACK_HOSTNAME_REDIRECT=example.com +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_POSTGRES_TAG={POSTGRES_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} TRAEFIK_HOSTNAME=traefik.example.com TRAEFIK_EMAIL=admin@example.com TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' @@ -311,7 +319,7 @@ BASIC_AUTH_PASSWORD_HASH='$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' DB_PASSWORD=Correct-Horse-Battery-Staple ``` -`docker stack deploy` doesn't read `.env` files. +`docker stack deploy` doesn't read {file}`.env` files. Load the variables into your shell before you deploy. ```shell @@ -349,7 +357,7 @@ To run four backend replicas, scale the backend service. docker service scale "${STACK_NAME}_backend=4" ``` -The next `docker stack deploy` sets the number of replicas back to the value of `STACK_BACK_REPLICAS`. +The next `docker stack deploy` sets the number of replicas back to the value of `STACK_BACKEND_REPLICAS`. ## Shutdown and cleanup diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md b/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md index e329797647..b1d0f90f6f 100644 --- a/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md +++ b/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md @@ -49,7 +49,7 @@ Requests to the Classic UI at `/ClassicUI` go straight from Traefik to the backe ## Setup -Create an empty project directory named `swarm-traefik-volto-plone-varnish`. +Create an empty project directory named {file}`swarm-traefik-volto-plone-varnish`. ```shell mkdir swarm-traefik-volto-plone-varnish @@ -74,13 +74,13 @@ docker network create --driver overlay nw-public ### Varnish configuration -Create a directory named `etc`. +Create a directory named {file}`etc`. ```shell mkdir etc ``` -Create a file named `etc/varnish.vcl` with the following content. +Create a file named {file}`etc/varnish.vcl` with the following content. It differs from the file in the {doc}`Docker Compose example <../compose/traefik-volto-plone-varnish>` in two places. The backend is port 8081 of `traefik`, the name of the Traefik service in the stack, and the `purge` access control list doesn't include the `backend` host name. @@ -357,7 +357,7 @@ sub vcl_deliver { ### Stack file -Create a `stack.yml` file with the following content. +Create a {file}`stack.yml` file with the following content. ```yaml services: @@ -379,7 +379,7 @@ services: - node.role == manager traefik: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - "80:80" - "443:443" @@ -497,7 +497,7 @@ services: order: start-first frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://${STACK_NAME}_backend:8080/Plone RAZZLE_API_PATH: https://${STACK_HOSTNAME} @@ -505,7 +505,7 @@ services: - nw-public - nw-internal deploy: - replicas: ${STACK_FRONT_REPLICAS:-2} + replicas: ${STACK_FRONTEND_REPLICAS:-2} update_config: parallelism: 1 delay: 5s @@ -523,7 +523,7 @@ services: - traefik.http.routers.rt-${STACK_NAME}-frontend.service=svc-${STACK_NAME}-frontend backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone PROFILES: "plone.app.caching:with-caching-proxy" @@ -532,7 +532,7 @@ services: - nw-public - nw-internal deploy: - replicas: ${STACK_BACK_REPLICAS:-2} + replicas: ${STACK_BACKEND_REPLICAS:-2} update_config: parallelism: 1 delay: 5s @@ -568,7 +568,7 @@ services: - traefik.http.routers.rt-${STACK_NAME}-backend-classic.middlewares=gzip,mw-${STACK_NAME}-backend-auth,mw-${STACK_NAME}-backend-vhm-classic db: - image: plone/plone-zeo:{PLONE_ZEO_VERSION} + image: plone/plone-zeo:${STACK_ZEO_TAG:?Set STACK_ZEO_TAG} volumes: - vol-site-data:/data networks: @@ -599,8 +599,8 @@ networks: internal: true ``` -Docker Swarm stores the content of `etc/varnish.vcl` under the name `varnish-vcl`, and you can't change it after you deploy the stack. -To change the Varnish configuration later, rename `varnish-vcl` in both places in `stack.yml`, for example to `varnish-vcl-2`, and deploy the stack again. +Docker Swarm stores the content of {file}`etc/varnish.vcl` under the name `varnish-vcl`, and you can't change it after you deploy the stack. +To change the Varnish configuration later, rename `varnish-vcl` in both places in {file}`stack.yml`, for example to `varnish-vcl-2`, and deploy the stack again. ```{warning} Docker volumes are local to the node where they're created. @@ -619,8 +619,12 @@ Variables without a default value are required, and `docker stack deploy` stops | `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | | `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | | `STACK_VARNISH_REPLICAS` | Number of Varnish replicas | `2` | `3` | -| `STACK_FRONT_REPLICAS` | Number of frontend replicas | `2` | `3` | -| `STACK_BACK_REPLICAS` | Number of backend replicas | `2` | `4` | +| `STACK_FRONTEND_REPLICAS` | Number of frontend replicas | `2` | `3` | +| `STACK_BACKEND_REPLICAS` | Number of backend replicas | `2` | `4` | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_ZEO_TAG` | Tag (version) of the image for the ZEO server | | {{PLONE_ZEO_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | | `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | | `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | | `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | @@ -633,13 +637,17 @@ To create a password hash, run the following command, with your password instead openssl passwd -apr1 secret ``` -Create a `.env` file with your values. +Create a {file}`.env` file with your values. Wrap values that contain a dollar sign, such as password hashes, in single quotes. ```shell STACK_NAME=plone STACK_HOSTNAME=www.example.com STACK_HOSTNAME_REDIRECT=example.com +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_ZEO_TAG={PLONE_ZEO_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} TRAEFIK_HOSTNAME=traefik.example.com TRAEFIK_EMAIL=admin@example.com TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' @@ -647,7 +655,7 @@ BASIC_AUTH_USER=admin BASIC_AUTH_PASSWORD_HASH='$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' ``` -`docker stack deploy` doesn't read `.env` files. +`docker stack deploy` doesn't read {file}`.env` files. Load the variables into your shell before you deploy. ```shell @@ -702,7 +710,7 @@ To run four backend replicas, scale the backend service. docker service scale "${STACK_NAME}_backend=4" ``` -The next `docker stack deploy` sets the number of replicas back to the value of `STACK_BACK_REPLICAS`. +The next `docker stack deploy` sets the number of replicas back to the value of `STACK_BACKEND_REPLICAS`. ## Shutdown and cleanup diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md b/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md index f859e3810b..6d03e1b9f4 100644 --- a/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md +++ b/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md @@ -38,7 +38,7 @@ The stack runs the following services. ## Setup -Create an empty project directory named `swarm-traefik-volto-plone-zeo`. +Create an empty project directory named {file}`swarm-traefik-volto-plone-zeo`. ```shell mkdir swarm-traefik-volto-plone-zeo @@ -63,7 +63,7 @@ docker network create --driver overlay nw-public ### Stack file -Create a `stack.yml` file with the following content. +Create a {file}`stack.yml` file with the following content. ```yaml services: @@ -85,7 +85,7 @@ services: - node.role == manager traefik: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - "80:80" - "443:443" @@ -146,7 +146,7 @@ services: - traefik.http.routers.generic-https-redirect.middlewares=https-redirect frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://${STACK_NAME:?Set STACK_NAME}_backend:8080/Plone RAZZLE_API_PATH: https://${STACK_HOSTNAME:?Set STACK_HOSTNAME} @@ -154,7 +154,7 @@ services: - nw-public - nw-internal deploy: - replicas: ${STACK_FRONT_REPLICAS:-2} + replicas: ${STACK_FRONTEND_REPLICAS:-2} update_config: parallelism: 1 delay: 5s @@ -186,7 +186,7 @@ services: - traefik.http.routers.rt-${STACK_NAME}-frontend.middlewares=gzip backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone ZEO_ADDRESS: "${STACK_NAME}_db:8100" @@ -194,7 +194,7 @@ services: - nw-public - nw-internal deploy: - replicas: ${STACK_BACK_REPLICAS:-2} + replicas: ${STACK_BACKEND_REPLICAS:-2} update_config: parallelism: 1 delay: 5s @@ -232,7 +232,7 @@ services: - traefik.http.routers.rt-${STACK_NAME}-backend-classic.middlewares=gzip,mw-${STACK_NAME}-backend-auth,mw-${STACK_NAME}-backend-vhm-classic db: - image: plone/plone-zeo:{PLONE_ZEO_VERSION} + image: plone/plone-zeo:${STACK_ZEO_TAG:?Set STACK_ZEO_TAG} volumes: - vol-site-data:/data networks: @@ -278,8 +278,12 @@ Variables without a default value are required, and `docker stack deploy` stops | `STACK_NAME` | Name of the stack, which must match the name you pass to `docker stack deploy`. The services use it to reach each other, and Traefik uses it to name routers, services, and middleware. | | `plone` | | `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | | `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | -| `STACK_FRONT_REPLICAS` | Number of frontend replicas | `2` | `3` | -| `STACK_BACK_REPLICAS` | Number of backend replicas | `2` | `4` | +| `STACK_FRONTEND_REPLICAS` | Number of frontend replicas | `2` | `3` | +| `STACK_BACKEND_REPLICAS` | Number of backend replicas | `2` | `4` | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_ZEO_TAG` | Tag (version) of the image for the ZEO server | | {{PLONE_ZEO_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | | `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | | `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | | `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | @@ -292,13 +296,17 @@ To create a password hash, run the following command, with your password instead openssl passwd -apr1 secret ``` -Create a `.env` file with your values. +Create a {file}`.env` file with your values. Wrap values that contain a dollar sign, such as password hashes, in single quotes. ```shell STACK_NAME=plone STACK_HOSTNAME=www.example.com STACK_HOSTNAME_REDIRECT=example.com +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_ZEO_TAG={PLONE_ZEO_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} TRAEFIK_HOSTNAME=traefik.example.com TRAEFIK_EMAIL=admin@example.com TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' @@ -306,7 +314,7 @@ BASIC_AUTH_USER=admin BASIC_AUTH_PASSWORD_HASH='$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' ``` -`docker stack deploy` doesn't read `.env` files. +`docker stack deploy` doesn't read {file}`.env` files. Load the variables into your shell before you deploy. ```shell @@ -344,7 +352,7 @@ To run four backend replicas, scale the backend service. docker service scale "${STACK_NAME}_backend=4" ``` -The next `docker stack deploy` sets the number of replicas back to the value of `STACK_BACK_REPLICAS`. +The next `docker stack deploy` sets the number of replicas back to the value of `STACK_BACKEND_REPLICAS`. ## Shutdown and cleanup diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone.md b/docs/install/containers/examples/swarm/traefik-volto-plone.md index fbc758fe92..f4d25ac2f9 100644 --- a/docs/install/containers/examples/swarm/traefik-volto-plone.md +++ b/docs/install/containers/examples/swarm/traefik-volto-plone.md @@ -35,7 +35,7 @@ The stack runs the following services. ## Setup -Create an empty project directory named `swarm-traefik-volto-plone`. +Create an empty project directory named {file}`swarm-traefik-volto-plone`. ```shell mkdir swarm-traefik-volto-plone @@ -60,7 +60,7 @@ docker network create --driver overlay nw-public ### Stack file -Create a `stack.yml` file with the following content. +Create a {file}`stack.yml` file with the following content. ```yaml services: @@ -82,7 +82,7 @@ services: - node.role == manager traefik: - image: traefik:{TRAEFIK_VERSION} + image: traefik:${STACK_TRAEFIK_TAG:?Set STACK_TRAEFIK_TAG} ports: - "80:80" - "443:443" @@ -143,7 +143,7 @@ services: - traefik.http.routers.generic-https-redirect.middlewares=https-redirect frontend: - image: plone/plone-frontend:{PLONE_FRONTEND_VERSION} + image: plone/plone-frontend:${STACK_FRONTEND_TAG:?Set STACK_FRONTEND_TAG} environment: RAZZLE_INTERNAL_API_PATH: http://${STACK_NAME:?Set STACK_NAME}_backend:8080/Plone RAZZLE_API_PATH: https://${STACK_HOSTNAME:?Set STACK_HOSTNAME} @@ -151,7 +151,7 @@ services: - nw-public - nw-internal deploy: - replicas: ${STACK_FRONT_REPLICAS:-2} + replicas: ${STACK_FRONTEND_REPLICAS:-2} update_config: parallelism: 1 delay: 5s @@ -183,7 +183,7 @@ services: - traefik.http.routers.rt-${STACK_NAME}-frontend.middlewares=gzip backend: - image: plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} + image: plone/plone-backend:${STACK_BACKEND_TAG:?Set STACK_BACKEND_TAG} environment: SITE: Plone volumes: @@ -264,7 +264,10 @@ Variables without a default value are required, and `docker stack deploy` stops | `STACK_NAME` | Name of the stack, which must match the name you pass to `docker stack deploy`. The services use it to reach each other, and Traefik uses it to name routers, services, and middleware. | | `plone` | | `STACK_HOSTNAME` | Public host name of the site | | `www.example.com` | | `STACK_HOSTNAME_REDIRECT` | Host name that permanently redirects to `STACK_HOSTNAME` | | `example.com` | -| `STACK_FRONT_REPLICAS` | Number of frontend replicas | `2` | `3` | +| `STACK_FRONTEND_REPLICAS` | Number of frontend replicas | `2` | `3` | +| `STACK_FRONTEND_TAG` | Tag (version) of the image for the frontend | | {{PLONE_FRONTEND_VERSION}} | +| `STACK_BACKEND_TAG` | Tag (version) of the image for the backend | | {{PLONE_BACKEND_MINOR_VERSION}} | +| `STACK_TRAEFIK_TAG` | Tag (version) of the image for Traefik | | {{TRAEFIK_VERSION}} | | `TRAEFIK_HOSTNAME` | Host name of the Traefik dashboard | | `traefik.example.com` | | `TRAEFIK_EMAIL` | Email address of your Let's Encrypt account | | `admin@example.com` | | `TRAEFIK_BASIC_AUTH` | User name and password hash for the Traefik dashboard, in the format `user:hash` | | `admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0` | @@ -277,13 +280,16 @@ To create a password hash, run the following command, with your password instead openssl passwd -apr1 secret ``` -Create a `.env` file with your values. +Create a {file}`.env` file with your values. Wrap values that contain a dollar sign, such as password hashes, in single quotes. ```shell STACK_NAME=plone STACK_HOSTNAME=www.example.com STACK_HOSTNAME_REDIRECT=example.com +STACK_FRONTEND_TAG={PLONE_FRONTEND_VERSION} +STACK_BACKEND_TAG={PLONE_BACKEND_MINOR_VERSION} +STACK_TRAEFIK_TAG={TRAEFIK_VERSION} TRAEFIK_HOSTNAME=traefik.example.com TRAEFIK_EMAIL=admin@example.com TRAEFIK_BASIC_AUTH='admin:$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' @@ -291,7 +297,7 @@ BASIC_AUTH_USER=admin BASIC_AUTH_PASSWORD_HASH='$apr1$Zq3mQ2vH$IX.Ug0PreO6h.m494Bn9L0' ``` -`docker stack deploy` doesn't read `.env` files. +`docker stack deploy` doesn't read {file}`.env` files. Load the variables into your shell before you deploy. ```shell diff --git a/docs/install/containers/images/aurora.md b/docs/install/containers/images/aurora.md index 4103a4cbca..bbe2be681d 100644 --- a/docs/install/containers/images/aurora.md +++ b/docs/install/containers/images/aurora.md @@ -51,7 +51,7 @@ docker run -d --name aurora -p 3000:3000 -e PLONE_API_PATH=http://host.docker.in ### Service configuration with Docker Compose -Create a directory for your project, and inside it create a `docker-compose.yml` file with the following content. +Create a directory for your project, and inside it create a {file}`docker-compose.yml` file with the following content. ```yaml services: @@ -81,7 +81,7 @@ Now run `docker compose up -d` from your project directory. Point your browser at `http://localhost:3000`, and you should see your Plone site. Until the backend finishes starting, Aurora returns an error page. -The [`examples` directory of the `plone/container-aurora` repository](https://github.com/plone/container-aurora/tree/main/examples) contains more complete configurations, with a web server in front of Aurora, and with ZEO or PostgreSQL as the database. +The [{file}`examples` directory of the `plone/container-aurora` repository](https://github.com/plone/container-aurora/tree/main/examples) contains more complete configurations, with a web server in front of Aurora, and with ZEO or PostgreSQL as the database. ## Configuration variables @@ -118,7 +118,7 @@ The `plone/aurora` image is built in two stages. The first stage builds Aurora in `plone/aurora-builder`, and the second copies the result onto `plone/aurora-prod-config`. Use the same structure to build your own image. -In a directory, create a `Dockerfile` file. +In a directory, create a {file}`Dockerfile` file. ```Dockerfile # syntax=docker/dockerfile:1 diff --git a/docs/install/containers/images/backend.md b/docs/install/containers/images/backend.md index b95601f160..a58a5bc331 100644 --- a/docs/install/containers/images/backend.md +++ b/docs/install/containers/images/backend.md @@ -169,7 +169,7 @@ volumes: ```{note} Currently this image supports only the configuration of a PostgreSQL backend via configuration variables. -If you need to use MySQL or Oracle, we recommend that you extend this image and overwrite the `/app/etc/relstorage.conf` file. +If you need to use MySQL or Oracle, we recommend that you extend this image and overwrite the {file}`/app/etc/relstorage.conf` file. ``` @@ -261,12 +261,12 @@ docker run -p 8080:8080 -e ADDONS="plone.volto==3.1.0a3" plone/plone-backend:{PL | `DEVELOP` | A space separated list of python libraries to install in editable mode | {ref}`containers-images-backend-developing-packages-label` | | `PIP_PARAMS` | Parameters used in `pip` installation commands | [`pip install`](https://pip.pypa.io/en/stable/cli/pip_install/) | -#### Adding configuration to `zope.conf` or additional ZCML +#### Adding configuration to {file}`zope.conf` or additional ZCML -Some Plone add-ons require changes to `zope.conf` or extra ZCML. +Some Plone add-ons require changes to {file}`zope.conf` or extra ZCML. With the standard container, it is not possible to add configuration fragments to -`zope.conf` directly or add extra ZCML, like it is with the `buildout` deployment +{file}`zope.conf` directly or add extra ZCML, like it is with the `buildout` deployment method. However, you can derive your own container image, and drop in configuration @@ -299,7 +299,7 @@ docker run -p 8080:8080 -e DEVELOP="/app/src/mysite.policy" -v /path/to/mysite.p ## Extending from this image -In a directory create a `Dockerfile` file: +In a directory create a {file}`Dockerfile` file: ```Dockerfile FROM plone/plone-backend:{PLONE_BACKEND_MINOR_VERSION} @@ -324,7 +324,7 @@ docker run -p 8080:8080 myproject:latest start ### Changing default values of environment variables All the environment variables documented above are supported in your -derived container's `Dockerfile`. You can override the default values +derived container's {file}`Dockerfile`. You can override the default values of variables as follows: ```Dockerfile @@ -341,28 +341,28 @@ Check the respective variable documentation above to determine whether you should use it, or use a different method to get the desired result in production. -### Adding `zope.conf` configuration fragments +### Adding {file}`zope.conf` configuration fragments -In the directory containing your `Dockerfile`, create a folder `etc/zope.conf.d`. -Add your `zope.conf` configuration fragments there. +In the directory containing your {file}`Dockerfile`, create a folder {file}`etc/zope.conf.d`. +Add your {file}`zope.conf` configuration fragments there. -Now add the following to your `Dockerfile`, before any `CMD` or `ENTRYPOINT` +Now add the following to your {file}`Dockerfile`, before any `CMD` or `ENTRYPOINT` stanzas it may have, and after the `FROM` and any `RUN` stanzas: ```Dockerfile COPY /etc/zope.conf.d/*.conf /app/etc/zope.conf.d/ ``` -This ensures your fragments are deployed in the `zope.conf.d` folder, which then -will be used to amend the `zope.conf` file prior to starting Plone. +This ensures your fragments are deployed in the {file}`zope.conf.d` folder, which then +will be used to amend the {file}`zope.conf` file prior to starting Plone. ### Adding ZCML fragments -In the directory containing your `Dockerfile`, create a folder `etc/package-includes`. -Add your ZCML configuration fragments (named `*-meta.zcml`, `*-configure.zcml`, -`*-overrides.zcml`) as files in that folder. +In the directory containing your {file}`Dockerfile`, create a folder {file}`etc/package-includes`. +Add your ZCML configuration fragments (named {file}`*-meta.zcml`, {file}`*-configure.zcml`, +{file}`*-overrides.zcml`) as files in that folder. -Now add the following to your `Dockerfile`, before any `CMD` or `ENTRYPOINT` +Now add the following to your {file}`Dockerfile`, before any `CMD` or `ENTRYPOINT` stanzas it may have, and after the `FROM` and any `RUN` stanzas: ```Dockerfile @@ -383,7 +383,7 @@ You can run Docker as an arbitrary user with the `--user` option. To persist backend data between restarts of Docker, use both options of `--user` and `-v`. -The following command will run the Plone backend container as an arbitrary user, and persist the backend data in a volume, provided that the owner of the directory `/data` is the same as the `--user` option. +The following command will run the Plone backend container as an arbitrary user, and persist the backend data in a volume, provided that the owner of the directory {file}`/data` is the same as the `--user` option. ```shell docker run --user="$(id -u)" -v $(pwd)/data:/data plone/plone-backend diff --git a/docs/install/containers/images/frontend.md b/docs/install/containers/images/frontend.md index 5ac7fe7f1a..c3905f0a13 100644 --- a/docs/install/containers/images/frontend.md +++ b/docs/install/containers/images/frontend.md @@ -25,7 +25,7 @@ This image is **not a base image** to be extended in your projects, but an examp | --- | --- | --- | | `RAZZLE_API_PATH` | Used to generate frontend calls to the backend. Needs to be a public URL accessible by client browser. | `http://api.site.org/++api++/` | | `RAZZLE_INTERNAL_API_PATH` | Used by the middleware to construct requests to the backend. It can be a non-public address. | `http://backend:8080/Plone` | -| `VOLTO_ROBOTSTXT` | Override the `robots.txt` file. | `"User-agent: *\nDisallow: "` | +| `VOLTO_ROBOTSTXT` | Override the {file}`robots.txt` file. | `"User-agent: *\nDisallow: "` | ```{note} For an extensive list of environment variables used by the frontend, visit {doc}`/volto/configuration/environmentvariables`. @@ -34,15 +34,15 @@ For an extensive list of environment variables used by the frontend, visit {doc} ## Using as an example for your Volto project -To use this image as an example of a Docker image for your own Volto project, you will need to edit the file `Dockerfile` in your project. -`Dockerfile` is pulled from the root of the [`plone/container-frontend`](https://github.com/plone/container-frontend/) repository. +To use this image as an example of a Docker image for your own Volto project, you will need to edit the file {file}`Dockerfile` in your project. +{file}`Dockerfile` is pulled from the root of the [`plone/container-frontend`](https://github.com/plone/container-frontend/) repository. ```{note} -The examples for `Dockerfile` in this documentation use Volto 15.x. +The examples for {file}`Dockerfile` in this documentation use Volto 15.x. You might need to adapt the examples for more recent releases. ``` -In `Dockerfile` replace the `yo @plone/volto` command with the `COPY . /build/plone-frontend` command. +In {file}`Dockerfile` replace the `yo @plone/volto` command with the `COPY . /build/plone-frontend` command. ```diff # Generate new volto app @@ -56,17 +56,17 @@ In `Dockerfile` replace the `yo @plone/volto` command with the `COPY . /build/pl The `plone-frontend` Docker image does not have a custom entry point file. For any commands you need to run when starting your Docker container, you will need to create it. -After creating the `entrypoint.sh` file, make sure it has the execute permission: +After creating the {file}`entrypoint.sh` file, make sure it has the execute permission: ```shell chmod 755 entrypoint.sh ``` ```{note} -Do not forget to add the `exec "$@"` command at the end of the `entrypoint.sh` file to run the default `pnpm start` command. +Do not forget to add the `exec "$@"` command at the end of the {file}`entrypoint.sh` file to run the default `pnpm start` command. ``` -In the `Dockerfile` you will need to add two commands to make the Docker container run `entrypoint.sh` on start: +In the {file}`Dockerfile` you will need to add two commands to make the Docker container run {file}`entrypoint.sh` on start: ```diff --no-interactive @@ -93,7 +93,7 @@ docker build . -t myfrontend:latest -f Dockerfile ### Start it -You can use it in the following `docker-compose.yml` file. +You can use it in the following {file}`docker-compose.yml` file. ```yaml version: "3" diff --git a/docs/install/containers/images/historical.md b/docs/install/containers/images/historical.md index 5ed181c4cc..6d8294423d 100644 --- a/docs/install/containers/images/historical.md +++ b/docs/install/containers/images/historical.md @@ -55,8 +55,8 @@ Each release lists only its last version. % TODO: List the variants of the Plone 4.3 to 5.2 images, such as `-alpine`, `-python2`, `-python36`, and `-python37`. -All images listen on port 8080, and store their data in a `/data` volume. -To use an existing database, place its `Data.fs` file at `/data/filestorage/Data.fs` before the first start. +All images listen on port 8080, and store their data in a {file}`/data` volume. +To use an existing database, place its {file}`Data.fs` file at {file}`/data/filestorage/Data.fs` before the first start. ## Plone 5.2 @@ -317,7 +317,7 @@ Then point your browser at `http://localhost:8080/Plone`, and log in with the us ## Upgrade a database -The images for Plone 1.0 to 4.2 carry an `upgrade` command that runs the Plone migration against the database in `/data`. +The images for Plone 1.0 to 4.2 carry an `upgrade` command that runs the Plone migration against the database in {file}`/data`. To upgrade a database across several releases, mount the same volume into each next release, one at a time. Stop any container that uses the database, then run the following command. diff --git a/docs/install/containers/images/zeo.md b/docs/install/containers/images/zeo.md index 3a3e369885..9a3cc77bb8 100644 --- a/docs/install/containers/images/zeo.md +++ b/docs/install/containers/images/zeo.md @@ -26,7 +26,7 @@ docker run -p 8100:8100 plone/plone-zeo:latest ### Service configuration with Docker Compose -Create a directory for your project, and inside it create a `docker-compose.yml` file that starts your Plone instance and the ZEO instance with volume mounts for data persistence: +Create a directory for your project, and inside it create a {file}`docker-compose.yml` file that starts your Plone instance and the ZEO instance with volume mounts for data persistence: ```yaml version: "3" diff --git a/docs/install/containers/index.md b/docs/install/containers/index.md index 37a32d3da3..bb5f89ae24 100644 --- a/docs/install/containers/index.md +++ b/docs/install/containers/index.md @@ -102,6 +102,6 @@ docker stop plone6-backend && docker rm plone6-backend ## Next steps -Get to know the [container images](images/index) maintained by the Plone community. +Get to know the {doc}`container images ` maintained by the Plone community. -Also see some [examples](examples/index) of how to use the container images to bootstrap your projects. +Also see some {doc}`examples ` of how to use the container images to bootstrap your projects. diff --git a/docs/install/containers/recipes/index.md b/docs/install/containers/recipes/index.md index f4d91a4d85..35401e1324 100644 --- a/docs/install/containers/recipes/index.md +++ b/docs/install/containers/recipes/index.md @@ -97,7 +97,7 @@ level = INFO formatter = generic ``` -Comparing this file with the [original `zope.ini` file](https://github.com/plone/container-backend/blob/6.1.x/skeleton/etc/zope.ini) that comes with the `plone/plone-backend` container, you may realize that the only change is the `translogger` configuration was removed from the `pipeline` section. +Comparing this file with the [original {file}`zope.ini` file](https://github.com/plone/container-backend/blob/6.1.x/skeleton/etc/zope.ini) that comes with the `plone/plone-backend` container, you may realize that the only change is the `translogger` configuration was removed from the `pipeline` section. This [`translogger` middleware produces logs in the Apache Combined Log Format](https://docs.pylonsproject.org/projects/waitress/en/latest/logging.html). The above configuration removes it from the setup. From 4eec50b752e21ccb92e43d56c9c826ddef88e2cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Tue, 15 Sep 2026 17:40:13 -0300 Subject: [PATCH 5/5] Differentiate titles and descriptions of container examples - Give the Docker Compose and Docker Swarm index pages their own headings and metadata instead of repeating the parent page's. - Move "Docker Swarm" from the start to the end of the swarm example headings and titles. - Start each example description with what distinguishes its stack, and keep description and og:description identical. - Fix the Varnish compose example metadata (missing ZEO, "Traefik Proxy" naming), a trailing comma in keywords, and a "Postgres" spelling error. - List the Aurora image last in the container images table. --- .../containers/examples/compose/haproxy-plone-zeo.md | 4 ++-- docs/install/containers/examples/compose/index.md | 10 +++++----- .../containers/examples/compose/nginx-plone.md | 4 ++-- .../examples/compose/nginx-volto-plone-postgresql.md | 8 ++++---- .../examples/compose/nginx-volto-plone-zeo.md | 4 ++-- .../containers/examples/compose/nginx-volto-plone.md | 4 ++-- .../containers/examples/compose/traefik-plone.md | 4 ++-- .../compose/traefik-volto-plone-postgresql.md | 4 ++-- .../examples/compose/traefik-volto-plone-varnish.md | 10 +++++----- .../examples/compose/traefik-volto-plone-zeo.md | 4 ++-- .../examples/compose/traefik-volto-plone.md | 4 ++-- docs/install/containers/examples/swarm/index.md | 12 ++++++------ .../containers/examples/swarm/traefik-plone.md | 8 ++++---- .../examples/swarm/traefik-volto-plone-postgresql.md | 8 ++++---- .../examples/swarm/traefik-volto-plone-varnish.md | 8 ++++---- .../examples/swarm/traefik-volto-plone-zeo.md | 8 ++++---- .../containers/examples/swarm/traefik-volto-plone.md | 8 ++++---- docs/install/containers/images/index.md | 2 +- 18 files changed, 57 insertions(+), 57 deletions(-) diff --git a/docs/install/containers/examples/compose/haproxy-plone-zeo.md b/docs/install/containers/examples/compose/haproxy-plone-zeo.md index d692fb688b..b8adcd4ec1 100644 --- a/docs/install/containers/examples/compose/haproxy-plone-zeo.md +++ b/docs/install/containers/examples/compose/haproxy-plone-zeo.md @@ -1,8 +1,8 @@ --- myst: html_meta: - "description": "Simple Plone 6 setup with scalable backend and data being persisted in a ZEO volume." - "property=og:description": "Simple Plone 6 setup with scalable backend and data being persisted in a ZEO volume." + "description": "HAProxy, a ZEO server, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "HAProxy, a ZEO server, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." "property=og:title": "HAProxy, Backend, ZEO container example" "keywords": "Plone 6, Container, Docker, HAProxy, ZEO" --- diff --git a/docs/install/containers/examples/compose/index.md b/docs/install/containers/examples/compose/index.md index ece68b4836..bf8e65fcb0 100644 --- a/docs/install/containers/examples/compose/index.md +++ b/docs/install/containers/examples/compose/index.md @@ -1,13 +1,13 @@ --- myst: html_meta: - "description": "Examples of Plone 6 setup with containers" - "property=og:description": "Examples of Plone 6 setup with containers" - "property=og:title": "Examples of Plone 6 using containers" - "keywords": "Plone 6, install, installation, docker, containers" + "description": "Docker Compose projects that run Plone 6 behind Traefik, nginx, or HAProxy." + "property=og:description": "Docker Compose projects that run Plone 6 behind Traefik, nginx, or HAProxy." + "property=og:title": "Docker Compose examples" + "keywords": "Plone 6, install, installation, Docker, Docker Compose, containers" --- -# Examples of Plone 6 using containers +# Docker Compose examples ```{toctree} :maxdepth: 2 diff --git a/docs/install/containers/examples/compose/nginx-plone.md b/docs/install/containers/examples/compose/nginx-plone.md index 670913960c..4259e70f1b 100644 --- a/docs/install/containers/examples/compose/nginx-plone.md +++ b/docs/install/containers/examples/compose/nginx-plone.md @@ -1,8 +1,8 @@ --- myst: html_meta: - "description": "Simple Plone 6 setup with one backend and data being persisted in a Docker volume." - "property=og:description": "Simple Plone 6 setup with one backend and data being persisted in a Docker volume." + "description": "nginx and a Classic UI backend in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "nginx and a Classic UI backend in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." "property=og:title": "nginx, Plone Classic container example" "keywords": "Plone 6, Container, Docker, nginx, Plone Classic" --- diff --git a/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md b/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md index b0b03d8781..ef739140bf 100644 --- a/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md +++ b/docs/install/containers/examples/compose/nginx-volto-plone-postgresql.md @@ -1,15 +1,15 @@ --- myst: html_meta: - "description": "Very simple Plone 6 setup with only one or more backend instances accessing a PostgreSQL server and data being persisted in a Docker volume." - "property=og:description": "Very simple Plone 6 setup with only one or more backend instances accessing a PostgreSQL server and data being persisted in a Docker volume." + "description": "PostgreSQL server, nginx, a frontend, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "PostgreSQL server, nginx, a frontend, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." "property=og:title": "nginx, Frontend, Backend, PostgreSQL container example" - "keywords": "Plone 6, Container, Docker, nginx, Frontend, Backend, PostgreSQL, " + "keywords": "Plone 6, Container, Docker, nginx, Frontend, Backend, PostgreSQL" --- # nginx, Frontend, Backend, PostgreSQL container example -This example is a very simple setup with one or more backend instances accessing a Postgres server and data being persisted in a Docker volume. +This example is a very simple setup with one or more backend instances accessing a PostgreSQL server and data being persisted in a Docker volume. {term}`nginx` in this example is used as a [reverse proxy](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/). diff --git a/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md b/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md index 0589994fc4..fdb58eac76 100644 --- a/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md +++ b/docs/install/containers/examples/compose/nginx-volto-plone-zeo.md @@ -1,8 +1,8 @@ --- myst: html_meta: - "description": "Very simple Plone 6 setup with only one or more backend instances accessing a ZEO server and data being persisted in a Docker volume." - "property=og:description": "Very simple Plone 6 setup with only one or more backend instances accessing a ZEO server and data being persisted in a Docker volume." + "description": "ZEO server, nginx, a frontend, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "ZEO server, nginx, a frontend, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." "property=og:title": "nginx, Frontend, Backend, ZEO container example" "keywords": "Plone 6, Container, Docker, nginx, Frontend, Backend, ZEO" --- diff --git a/docs/install/containers/examples/compose/nginx-volto-plone.md b/docs/install/containers/examples/compose/nginx-volto-plone.md index 265c193f24..cec2cd7861 100644 --- a/docs/install/containers/examples/compose/nginx-volto-plone.md +++ b/docs/install/containers/examples/compose/nginx-volto-plone.md @@ -1,8 +1,8 @@ --- myst: html_meta: - "description": "Very simple Plone 6 setup with only one backend and data being persisted in a Docker volume." - "property=og:description": "Very simple Plone 6 setup with only one backend and data being persisted in a Docker volume." + "description": "nginx, a frontend, and a single backend in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "nginx, a frontend, and a single backend in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." "property=og:title": "nginx, Frontend, Backend container example" "keywords": "Plone 6, Container, Docker, nginx, Frontend, Backend" --- diff --git a/docs/install/containers/examples/compose/traefik-plone.md b/docs/install/containers/examples/compose/traefik-plone.md index ea183e53d2..9d2b037d92 100644 --- a/docs/install/containers/examples/compose/traefik-plone.md +++ b/docs/install/containers/examples/compose/traefik-plone.md @@ -1,8 +1,8 @@ --- myst: html_meta: - "description": "Simple Plone 6 Classic UI setup with Traefik and one backend, with data persisted in a Docker volume." - "property=og:description": "Simple Plone 6 Classic UI setup with Traefik and one backend, with data persisted in a Docker volume." + "description": "Traefik and a Classic UI backend in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "Traefik and a Classic UI backend in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." "property=og:title": "Traefik, Plone Classic container example" "keywords": "Plone 6, Container, Docker, Traefik, Plone Classic" --- diff --git a/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md b/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md index d5228f1a0b..3fab9e710f 100644 --- a/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md +++ b/docs/install/containers/examples/compose/traefik-volto-plone-postgresql.md @@ -1,8 +1,8 @@ --- myst: html_meta: - "description": "Simple Plone 6 setup with Traefik and one or more backend instances accessing a PostgreSQL server, with data persisted in a Docker volume." - "property=og:description": "Simple Plone 6 setup with Traefik and one or more backend instances accessing a PostgreSQL server, with data persisted in a Docker volume." + "description": "PostgreSQL server, Traefik, a frontend, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "PostgreSQL server, Traefik, a frontend, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." "property=og:title": "Traefik, Frontend, Backend, PostgreSQL container example" "keywords": "Plone 6, Container, Docker, Traefik, Frontend, Backend, PostgreSQL" --- diff --git a/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md b/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md index 5cd2ed2432..46b489108a 100644 --- a/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md +++ b/docs/install/containers/examples/compose/traefik-volto-plone-varnish.md @@ -1,13 +1,13 @@ --- myst: html_meta: - "description": "Basic Plone 6 setup with only one backend, a ZEO server, and data being persisted in a Docker volume." - "property=og:description": "Basic Plone 6 setup with only one backend, a ZEO server, and data being persisted in a Docker volume." - "property=og:title": "Traefik Proxy, Frontend, Backend, Varnish container example" - "keywords": "Plone 6, Container, Docker, Traefik Proxy, Frontend, Backend, Varnish" + "description": "Varnish cache with a purger, Traefik, a frontend, a backend, and a ZEO server in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "Varnish cache with a purger, Traefik, a frontend, a backend, and a ZEO server in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:title": "Traefik, Frontend, Backend, ZEO, Varnish container example" + "keywords": "Plone 6, Container, Docker, Traefik, Frontend, Backend, ZEO, Varnish" --- -# Traefik Proxy, Frontend, Backend, ZEO, Varnish container example +# Traefik, Frontend, Backend, ZEO, Varnish container example This example is a basic setup with one backend accessing a ZEO server and data being persisted in a Docker volume. diff --git a/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md b/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md index 1e98269800..40a3e352e0 100644 --- a/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md +++ b/docs/install/containers/examples/compose/traefik-volto-plone-zeo.md @@ -1,8 +1,8 @@ --- myst: html_meta: - "description": "Simple Plone 6 setup with Traefik and one or more backend instances accessing a ZEO server, with data persisted in a Docker volume." - "property=og:description": "Simple Plone 6 setup with Traefik and one or more backend instances accessing a ZEO server, with data persisted in a Docker volume." + "description": "ZEO server, Traefik, a frontend, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "ZEO server, Traefik, a frontend, and one or more backend instances in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." "property=og:title": "Traefik, Frontend, Backend, ZEO container example" "keywords": "Plone 6, Container, Docker, Traefik, Frontend, Backend, ZEO" --- diff --git a/docs/install/containers/examples/compose/traefik-volto-plone.md b/docs/install/containers/examples/compose/traefik-volto-plone.md index bf9623759b..ea4001cb72 100644 --- a/docs/install/containers/examples/compose/traefik-volto-plone.md +++ b/docs/install/containers/examples/compose/traefik-volto-plone.md @@ -1,8 +1,8 @@ --- myst: html_meta: - "description": "Simple Plone 6 setup with Traefik, one frontend, and one backend, with data persisted in a Docker volume." - "property=og:description": "Simple Plone 6 setup with Traefik, one frontend, and one backend, with data persisted in a Docker volume." + "description": "Traefik, a frontend, and a single backend in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." + "property=og:description": "Traefik, a frontend, and a single backend in a Plone 6 project for Docker Compose, with data persisted in a Docker volume." "property=og:title": "Traefik, Frontend, Backend container example" "keywords": "Plone 6, Container, Docker, Traefik, Frontend, Backend" --- diff --git a/docs/install/containers/examples/swarm/index.md b/docs/install/containers/examples/swarm/index.md index 87a589b965..51e1d389b6 100644 --- a/docs/install/containers/examples/swarm/index.md +++ b/docs/install/containers/examples/swarm/index.md @@ -1,13 +1,13 @@ --- myst: html_meta: - "description": "Examples of Plone 6 setup with containers" - "property=og:description": "Examples of Plone 6 setup with containers" - "property=og:title": "Examples of Plone 6 using containers" - "keywords": "Plone 6, install, installation, docker, containers" + "description": "Docker Swarm stacks that run Plone 6 behind Traefik, with a scalable frontend and backend." + "property=og:description": "Docker Swarm stacks that run Plone 6 behind Traefik, with a scalable frontend and backend." + "property=og:title": "Docker Swarm examples" + "keywords": "Plone 6, install, installation, Docker, Docker Swarm, containers" --- -# Examples of Plone 6 using containers +# Docker Swarm examples ```{toctree} :maxdepth: 2 @@ -20,7 +20,7 @@ traefik-plone traefik-volto-plone-varnish ``` -Examples of stacks for running Plone with `docker swarm`. +Examples of stacks running Plone on a Docker Swarm cluster. | Stack example | Description | | --- | --- | diff --git a/docs/install/containers/examples/swarm/traefik-plone.md b/docs/install/containers/examples/swarm/traefik-plone.md index a510f361ec..a08d858d1e 100644 --- a/docs/install/containers/examples/swarm/traefik-plone.md +++ b/docs/install/containers/examples/swarm/traefik-plone.md @@ -1,13 +1,13 @@ --- myst: html_meta: - "description": "Plone 6 Classic UI stack for Docker Swarm with Traefik and a backend that stores its data in a Docker volume." - "property=og:description": "Plone 6 Classic UI stack for Docker Swarm with Traefik and a backend that stores its data in a Docker volume." - "property=og:title": "Docker Swarm: Traefik, Plone Classic example" + "description": "Classic UI backend and Traefik in a Plone 6 stack for Docker Swarm, with data stored in a Docker volume." + "property=og:description": "Classic UI backend and Traefik in a Plone 6 stack for Docker Swarm, with data stored in a Docker volume." + "property=og:title": "Traefik, Plone Classic example for Docker Swarm" "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Plone Classic" --- -# Docker Swarm: Traefik, Plone Classic example +# Traefik, Plone Classic example for Docker Swarm This example deploys a Plone 6 site with the Classic UI as a stack on a Docker Swarm cluster. The stack runs the following services. diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md b/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md index 21697959b1..ffdba37d23 100644 --- a/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md +++ b/docs/install/containers/examples/swarm/traefik-volto-plone-postgresql.md @@ -1,13 +1,13 @@ --- myst: html_meta: - "description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend and backend, and a PostgreSQL database." - "property=og:description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend and backend, and a PostgreSQL database." - "property=og:title": "Docker Swarm: Traefik, Frontend, Backend, PostgreSQL example" + "description": "PostgreSQL database, scalable frontend and backend, and Traefik in a Plone 6 stack for Docker Swarm." + "property=og:description": "PostgreSQL database, scalable frontend and backend, and Traefik in a Plone 6 stack for Docker Swarm." + "property=og:title": "Traefik, Frontend, Backend, PostgreSQL example for Docker Swarm" "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Frontend, Backend, PostgreSQL" --- -# Docker Swarm: Traefik, Frontend, Backend, PostgreSQL example +# Traefik, Frontend, Backend, PostgreSQL example for Docker Swarm This example deploys Plone 6 as a stack on a Docker Swarm cluster. The stack runs the following services. diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md b/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md index b1d0f90f6f..af724aa1d2 100644 --- a/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md +++ b/docs/install/containers/examples/swarm/traefik-volto-plone-varnish.md @@ -1,13 +1,13 @@ --- myst: html_meta: - "description": "Plone 6 stack for Docker Swarm with Traefik, Varnish, a scalable frontend and backend, and a ZEO server." - "property=og:description": "Plone 6 stack for Docker Swarm with Traefik, Varnish, a scalable frontend and backend, and a ZEO server." - "property=og:title": "Docker Swarm: Traefik, Frontend, Backend, ZEO, Varnish example" + "description": "Varnish cache, ZEO server, scalable frontend and backend, and Traefik in a Plone 6 stack for Docker Swarm." + "property=og:description": "Varnish cache, ZEO server, scalable frontend and backend, and Traefik in a Plone 6 stack for Docker Swarm." + "property=og:title": "Traefik, Frontend, Backend, ZEO, Varnish example for Docker Swarm" "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Frontend, Backend, ZEO, Varnish" --- -# Docker Swarm: Traefik, Frontend, Backend, ZEO, Varnish example +# Traefik, Frontend, Backend, ZEO, Varnish example for Docker Swarm This example deploys Plone 6 as a stack on a Docker Swarm cluster, with {term}`Varnish` caching the responses of the frontend and the backend. The stack runs the following services. diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md b/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md index 6d03e1b9f4..406a482406 100644 --- a/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md +++ b/docs/install/containers/examples/swarm/traefik-volto-plone-zeo.md @@ -1,13 +1,13 @@ --- myst: html_meta: - "description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend and backend, and a ZEO server." - "property=og:description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend and backend, and a ZEO server." - "property=og:title": "Docker Swarm: Traefik, Frontend, Backend, ZEO example" + "description": "ZEO server, scalable frontend and backend, and Traefik in a Plone 6 stack for Docker Swarm." + "property=og:description": "ZEO server, scalable frontend and backend, and Traefik in a Plone 6 stack for Docker Swarm." + "property=og:title": "Traefik, Frontend, Backend, ZEO example for Docker Swarm" "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Frontend, Backend, ZEO" --- -# Docker Swarm: Traefik, Frontend, Backend, ZEO example +# Traefik, Frontend, Backend, ZEO example for Docker Swarm This example deploys Plone 6 as a stack on a Docker Swarm cluster. The stack runs the following services. diff --git a/docs/install/containers/examples/swarm/traefik-volto-plone.md b/docs/install/containers/examples/swarm/traefik-volto-plone.md index f4d25ac2f9..9c27c4559f 100644 --- a/docs/install/containers/examples/swarm/traefik-volto-plone.md +++ b/docs/install/containers/examples/swarm/traefik-volto-plone.md @@ -1,13 +1,13 @@ --- myst: html_meta: - "description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend, and a backend that stores its data in a Docker volume." - "property=og:description": "Plone 6 stack for Docker Swarm with Traefik, a scalable frontend, and a backend that stores its data in a Docker volume." - "property=og:title": "Docker Swarm: Traefik, Frontend, Backend example" + "description": "Scalable frontend, backend, and Traefik in a Plone 6 stack for Docker Swarm, with data stored in a Docker volume." + "property=og:description": "Scalable frontend, backend, and Traefik in a Plone 6 stack for Docker Swarm, with data stored in a Docker volume." + "property=og:title": "Traefik, Frontend, Backend example for Docker Swarm" "keywords": "Plone 6, Container, Docker, Docker Swarm, Traefik, Frontend, Backend" --- -# Docker Swarm: Traefik, Frontend, Backend example +# Traefik, Frontend, Backend example for Docker Swarm This example deploys Plone 6 as a stack on a Docker Swarm cluster. The stack runs the following services. diff --git a/docs/install/containers/images/index.md b/docs/install/containers/images/index.md index ab3966ebc4..449f466468 100644 --- a/docs/install/containers/images/index.md +++ b/docs/install/containers/images/index.md @@ -15,10 +15,10 @@ The Plone community maintains the following official images: | Image | Description | |-----------------|-------------------------------------------------------------------| -| {doc}`aurora` | Aurora, the future React frontend of Plone. Requires a Plone backend | | {doc}`backend` | Plone backend. Could be used standalone or as a headless CMS | | {doc}`frontend` | Plone default frontend written in React. Requires a Plone backend | | {doc}`zeo` | ZEO server, a specialized database to be used with Plone backend | +| {doc}`aurora` | Aurora, the future React frontend of Plone. Requires a Plone backend | ## Other container images