Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modernize docker compose #71

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

jsma
Copy link
Contributor

@jsma jsma commented Nov 11, 2023

This work in progress attempts to modernize and cleanup the docker configuration to address several issues.

I've based this off two other open PRs I've created (#69 & #70) but can either officially bundle those small fixes into this branch or break this PR into tinier pieces if necessary.

I have not made any significant changes to the frontend container as the existing approach may need a rethink (active discussion in #64).

  • Move database connection parameters to an environment file to avoid duplication
  • Changed database connection parameters to match vanilla bakerydemo
  • Update to modern command docker compose vs docker-compose
  • Remove the version number from the file since this is ignored by docker and causes IDEs to attempt to validate
  • Removed mount of node_modules volume in web container since the web container does not have node installed
  • Removed :delegated,rw since this was the incorrect option (it meant that the container had the authoritative view of the files, leading to long delays in synchronizing with the host which caused issues when trying to detect changes for migrations, etc.), and is no longer necessary in modern docker
  • Added health checks for containers to avoid warnings/errors
  • Updated base image to python:3.9-bullseye to at least match the version used in bakerydemo's docker-compose.yml
  • Updated base image to postgres:14.1 to match bakerydemo's docker-compose.yml

@saevarom
Copy link
Collaborator

Thank you for taking the time to help @jsma ! At the time delegated seemed to mitigate some performance problems in docker which were subsequently fixed about a year later, in 2021. So, if this works better then I'm all for it. I will test it when I get the chance and I know @lb- is excited to get a better setup for node :)

Dockerfile Outdated Show resolved Hide resolved
@saevarom
Copy link
Collaborator

@jsma you probably need to undo the changes to bakerydemo-settings-local.py.example and setup.sh since #70 has been merged.

@lb- I am inclined to approve this and would be curious to know if this solves your npm/node problems?

@lb-
Copy link
Member

lb- commented Nov 11, 2023

I'll do my best to test this out today or tomorrow.

Another request might be to update to Node 20 in this PR also.

See #64

@saevarom
Copy link
Collaborator

I'll do my best to test this out today or tomorrow.

Another request might be to update to Node 20 in this PR also.

See #64

When this PR is rebased to main, it will get the node 20 upgrade :)

* Move database connection parameters to an environment file to avoid duplication
* Update to modern command `docker compose` vs `docker-compose`
* Remove the version number from the file since this is ignored by docker and causes IDEs to attempt to validate
* Removed mount of `node_modules` volume in `web` container since the `web` container does not have `node` installed
* Removed `:delegated,rw` since this was the incorrect option (it meant that the container had the authoritative view of the files, leading to long delays in synchronizing with the host which caused issues when trying to detect changes for migrations, etc.), and is no longer necessary in modern docker
* Added health checks for containers to avoid warnings/errors
* Updated base image to python:3.9-bullseye to at least match the version used in `bakerydemo`'s `docker-compose.yml`
* Updated base image to postgres:14.1 to match `bakerydemo`'s `docker-compose.yml`
@jsma
Copy link
Contributor Author

jsma commented Nov 11, 2023

I've cleaned up the branch and rebased from main.

Renames docker-compose.yml => compose.yaml which is the new preferred name going forward
Moves Dockerfiles to subdirectory, ensuring the filenames end with Dockerfile (for syntax highlighting in IDEs)
Renamed the `web` service to `app`, since this is typical for Docker examples and is also what bakerydemo uses
Various tricks and tips to optimize the build cache to speed up subsequent builds

The new Dockerfile was partially based on the output of `docker init`, which includes some best practices:
* create non-root users to run the applications
* use bind-mounts for pip & npm installs (this means that when adding a new requirement, it will only download the new package and install everything else from the cache)

NOTE: the wagtail and libs directories are no longer COPY'd into the container for doing a `pip install`, but installing wagtail and django-modelcluster creates .egg-info directories, so the bind mounts need write access.

Added custom ignore files for each Dockerfile to restrict the build context to the absolute minimum

Added a custom requirements file to avoid relying on bakerydemo's requirements which results in duplicate installs.
The previous setup was installing bakerydemo requirements, which installed wagtail, django-modelcluster, and Willow, and then a few steps later, reinstalling these packages from the local sources. Now they are only installed once.

The CMDs for both `app` and `frontend` are now wrapped with `dumb-init` (node is not designed to run as PID 1) which handles killing the processes. Previously attempting the Ctrl-C the running containers would hang waiting for `npm` to give up the ghost, now it exits immediately.
@jsma
Copy link
Contributor Author

jsma commented Nov 13, 2023

I've pushed a rather significant update. I spent a bit more time with the Docker docs and took this "modernization" project more seriously than just borrowing from what I was doing a couple years ago ;)

Let me know if you have questions about some of the changes and I'll do my best to explain while it's fresh in my mind.

There may be room for some additions related to compose watch, e.g. we could trigger a rebuild of the frontend container if someone edits package.json, but I'll defer to front-end devs and their workflows (e.g., perhaps they don't want it constantly rebuilding if they are in the middle of pasting new entries into package.json).

At any rate, I've got this tuned as well as I can. It builds, starts, and stops much more quickly now. I've only lightly tested the front-end, changing the background color of the sidebar-wagtail-branding and see my changes reflected on reload:

Screenshot 2023-11-12 at 5 38 48 PM

That said, it's very slow to rebuild. I'm not super familiar with webpack (I'm still old school using django-compressor just for scss, no Javascript build step), so I'm not sure what can be done to optimize. Although any optimizations would likely need to happen upstream in Wagtail.

What I've observed is that changing a single scss file results in three rounds of [webpack.Progress] going from 0-100%. I've also noticed that if I touch a Python file in the wagtail directory e.g. touch wagtail/wagtail/admin/views/workflows.py this also triggers a rebuild:

wagtail-dev-frontend-1  | <s> [webpack.Progress] 0%
...
wagtail-dev-frontend-1  | <s> [webpack.Progress] 100%
wagtail-dev-frontend-1  |
wagtail-dev-frontend-1  | assets by status 12.5 MiB [cached] 92 assets
wagtail-dev-frontend-1  | cached modules 4.07 MiB (javascript) 25 KiB (css/mini-extract) 127 KiB (runtime) [cached] 762 modules
wagtail-dev-frontend-1  | orphan modules 812 KiB [orphan] 1 module
wagtail-dev-frontend-1  | cacheable modules 50 bytes (javascript) 232 KiB (css/mini-extract)
wagtail-dev-frontend-1  |   ./wagtail/admin/static_src/wagtailadmin/scss/core.scss 50 bytes [built]
wagtail-dev-frontend-1  |   css ../../node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[2].use[1]!../../node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[2].use[2]!../../node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[2].use[3]!./wagtail/admin/static_src/wagtailadmin/scss/core.scss 232 KiB [built]
wagtail-dev-frontend-1  | webpack compiled successfully in 3038 ms
wagtail-dev-frontend-1  | <s> [webpack.Progress] 0%
...
wagtail-dev-frontend-1  | <s> [webpack.Progress] 99%
wagtail-dev-frontend-1  |
wagtail-dev-frontend-1  | assets by status 12.5 MiB [cached] 92 assets
wagtail-dev-frontend-1  | cached modules 4.07 MiB (javascript) 257 KiB (css/mini-extract) 127 KiB (runtime) [cached] 758 modules
wagtail-dev-frontend-1  | webpack compiled successfully in 113 ms
wagtail-dev-frontend-1  | <s> [webpack.Progress] 99% cache store build dependencies
wagtail-dev-frontend-1  | <s> [webpack.Progress] 99% cache store build dependencies
wagtail-dev-frontend-1  | <s> [webpack.Progress] 99% cache begin idle
wagtail-dev-frontend-1  | <s> [webpack.Progress] 99% cache begin idle
wagtail-dev-frontend-1  | <s> [webpack.Progress] 100%

Seems like it should not be doing this ;)

I realize this is out of scope a bit, but any objection to including ipython in the requirements here for development purposes? I find the default python shell completely unusable.

@jsma
Copy link
Contributor Author

jsma commented Nov 13, 2023

I can fix the path to the correct Dockerfile for the .github workflow, but quick question: why does this workflow exist? As a smoke test that it builds? The existing setup was not doing anything with the built image and it is not building the frontend container. If it's not serving a purpose, perhaps the .github folder should be removed.

@saevarom
Copy link
Collaborator

@jsma It is as you say, a smoke test to verify that the app container builds so we can at least verify that a contribution is not breaking the setup and build process.
The plan was to make it more complete but that has not been done. I'll create an issue for that.

So if you could update your PR with the correct path then that would be great.

@saevarom
Copy link
Collaborator

@jsma I could not get the frontend container to run correctly. Here are the steps I took:

  • Deleted the containers and volumes from Docker desktop.
  • Rebuilt with docker compose build
  • Ran the setup-db.sh script
  • Started everything with docker compose up

I get an error that I did not get with the previous version of this PR:

docker-wagtail-develop-frontend-1  | /code/wagtail/node_modules/loader-runner/lib/LoaderRunner.js:146
docker-wagtail-develop-frontend-1  | 		if(isError) throw e;
docker-wagtail-develop-frontend-1  | 		            ^
docker-wagtail-develop-frontend-1  |
docker-wagtail-develop-frontend-1  | Error: error:0308010C:digital envelope routines::unsupported
docker-wagtail-develop-frontend-1  |     at new Hash (node:internal/crypto/hash:68:19)
docker-wagtail-develop-frontend-1  |     at Object.createHash (node:crypto:138:10)
docker-wagtail-develop-frontend-1  |     at BulkUpdateDecorator.hashFactory (/code/wagtail/node_modules/webpack/lib/util/createHash.js:144:18)
docker-wagtail-develop-frontend-1  |     at BulkUpdateDecorator.digest (/code/wagtail/node_modules/webpack/lib/util/createHash.js:79:21)
docker-wagtail-develop-frontend-1  |     at NormalModule._initBuildHash (/code/wagtail/node_modules/webpack/lib/NormalModule.js:843:53)
docker-wagtail-develop-frontend-1  |     at /code/wagtail/node_modules/webpack/lib/NormalModule.js:882:10
docker-wagtail-develop-frontend-1  |     at processResult (/code/wagtail/node_modules/webpack/lib/NormalModule.js:683:12)
docker-wagtail-develop-frontend-1  |     at /code/wagtail/node_modules/webpack/lib/NormalModule.js:778:5
docker-wagtail-develop-frontend-1  |     at /code/wagtail/node_modules/loader-runner/lib/LoaderRunner.js:399:11
docker-wagtail-develop-frontend-1  |     at /code/wagtail/node_modules/loader-runner/lib/LoaderRunner.js:251:18
docker-wagtail-develop-frontend-1  |     at context.callback (/code/wagtail/node_modules/loader-runner/lib/LoaderRunner.js:124:13)
docker-wagtail-develop-frontend-1  |     at Object.loader (/code/wagtail/node_modules/ts-loader/dist/index.js:17:9)
docker-wagtail-develop-frontend-1  |     at LOADER_EXECUTION (/code/wagtail/node_modules/loader-runner/lib/LoaderRunner.js:132:14)
docker-wagtail-develop-frontend-1  |     at runSyncOrAsync (/code/wagtail/node_modules/loader-runner/lib/LoaderRunner.js:133:4)
docker-wagtail-develop-frontend-1  |     at iterateNormalLoaders (/code/wagtail/node_modules/loader-runner/lib/LoaderRunner.js:250:2)
docker-wagtail-develop-frontend-1  |     at Array.<anonymous> (/code/wagtail/node_modules/loader-runner/lib/LoaderRunner.js:223:4)
docker-wagtail-develop-frontend-1  |     at runCallbacks (/code/wagtail/node_modules/webpack/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:27:15)
docker-wagtail-develop-frontend-1  |     at /code/wagtail/node_modules/webpack/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:200:4
docker-wagtail-develop-frontend-1  |     at /code/wagtail/node_modules/graceful-fs/graceful-fs.js:123:16
docker-wagtail-develop-frontend-1  |     at FSReqCallback.readFileAfterClose [as oncomplete] (node:internal/fs/read/context:68:3) {
docker-wagtail-develop-frontend-1  |   opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
docker-wagtail-develop-frontend-1  |   library: 'digital envelope routines',
docker-wagtail-develop-frontend-1  |   reason: 'unsupported',
docker-wagtail-develop-frontend-1  |   code: 'ERR_OSSL_EVP_UNSUPPORTED'
docker-wagtail-develop-frontend-1  | }
docker-wagtail-develop-frontend-1  |
docker-wagtail-develop-frontend-1  | Node.js v20.9.0

@jsma
Copy link
Contributor Author

jsma commented Nov 13, 2023

If you are working in an existing checkout of this project, have you done the equivalent of update.sh to get the latest wagtail? Also, you may need to copy environment variables from .env.example to .env in the root directory. I just had Docker completely erase all data, did a fresh checkout in a tmp directory and it builds and starts correctly. That specific error appears (based on a quick Stack Overflow search) to be due to some older version of something being installed by webpack that isn't happy running in Node v20.

@saevarom
Copy link
Collaborator

saevarom commented Nov 13, 2023

I had the v5.2 tag checked out in wagtail to test something else but now I'm on main.

I redid all the steps, took care to destroy all containers, images and volumes, used docker compose build --no-cache and docker compose up --force-recreate.

Still, I get the same error and somehow the database still persists, there are no migrations to perform 🤔 Do you have any idea about why the postgres volume would persist even if I delete it from Docker Desktop? It seems like stale data is persisting both in postgres and the node volume.

@lb-
Copy link
Member

lb- commented Nov 21, 2023

Hey, sorry, I am also having trouble getting this branch to work.

wagtail-dev on  pr/71-wagtail-modernize-docker-compose 
➜ make build
docker compose build app
WARN[0000] The "DATABASE_URL" variable is not set. Defaulting to a blank string. 
WARN[0000] The "DATABASE_NAME" variable is not set. Defaulting to a blank string. 
WARN[0000] The "DATABASE_USER" variable is not set. Defaulting to a blank string. 
WARN[0000] The "DATABASE_PASSWORD" variable is not set. Defaulting to a blank string. 
[+] Building 58.1s (12/13)                                                                                                                                                                     
 => [internal] load build definition from app.Dockerfile                                                                                                                                  0.0s
 => => transferring dockerfile: 2.13kB                                                                                                                                                    0.0s
 => [internal] load .dockerignore                                                                                                                                                         0.0s
 => => transferring context: 35B                                                                                                                                                          0.0s
 => resolve image config for docker.io/docker/dockerfile:1                                                                                                                                3.8s
 => docker-image://docker.io/docker/dockerfile:1@sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021                                                                  1.7s
 => => resolve docker.io/docker/dockerfile:1@sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021                                                                      0.0s
 => => sha256:19c37871da0fa7da4b2a871455ccea62d3b08eb94a7f6f0cf310fe02f14f5089 1.27kB / 1.27kB                                                                                            0.0s
 => => sha256:cf80d9b4bd1c75ee551ce5ff6f950a178fbb62e661c02e42d41b8c772d1efc1d 11.02MB / 11.02MB                                                                                          1.6s
 => => sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021 8.40kB / 8.40kB                                                                                            0.0s
 => => sha256:e929b0d024894103bb3a8577aad825a7df006a8f6767747bffd373d804c3ee67 482B / 482B                                                                                                0.0s
 => => extracting sha256:cf80d9b4bd1c75ee551ce5ff6f950a178fbb62e661c02e42d41b8c772d1efc1d                                                                                                 0.1s
 => [internal] load build definition from app.Dockerfile                                                                                                                                  0.0s
 => [internal] load metadata for docker.io/library/python:3.12.0-slim-bookworm                                                                                                            4.0s
 => [base 1/6] FROM docker.io/library/python:3.12.0-slim-bookworm@sha256:2d4aea774065e001d817c030f1790e2289866589530fb18cb764331df9eaa646                                                 4.8s
 => => resolve docker.io/library/python:3.12.0-slim-bookworm@sha256:2d4aea774065e001d817c030f1790e2289866589530fb18cb764331df9eaa646                                                      0.0s
 => => sha256:4473e8f8c12ce7c3b1fcc281aacff508562fdc29119e9767fc2a3a74c4cfbac1 11.92MB / 11.92MB                                                                                          4.1s
 => => sha256:2d4aea774065e001d817c030f1790e2289866589530fb18cb764331df9eaa646 1.65kB / 1.65kB                                                                                            0.0s
 => => sha256:cf04935d7603f277a59d703571447b1284ca90c6d8904fc9db129fc52bcb5fe9 1.37kB / 1.37kB                                                                                            0.0s
 => => sha256:896433661b299985f6aa2c11e56d898b36bda146e6fe066934700919aaaa0566 6.73kB / 6.73kB                                                                                            0.0s
 => => sha256:2c6d21737d8318aa15c4cc838475029a5efc36c0429e3d8da80d97d0b96d9aaf 29.18MB / 29.18MB                                                                                          3.3s
 => => sha256:b4906d053db500056f4d92f6658d5c6d9db98074ff2543087ba68e029166b327 3.33MB / 3.33MB                                                                                            2.4s
 => => sha256:65a8689d9dc85c93d849bd46bd3f5093ea663f1f0d481ba43c4a44e62d68b769 244B / 244B                                                                                                3.3s
 => => sha256:0d1ab692db8c9f5a572637ed9dd6d2c91fec2a49b5f002b04333496e25caf2eb 2.96MB / 2.96MB                                                                                            4.7s
 => => extracting sha256:2c6d21737d8318aa15c4cc838475029a5efc36c0429e3d8da80d97d0b96d9aaf                                                                                                 0.7s
 => => extracting sha256:b4906d053db500056f4d92f6658d5c6d9db98074ff2543087ba68e029166b327                                                                                                 0.1s
 => => extracting sha256:4473e8f8c12ce7c3b1fcc281aacff508562fdc29119e9767fc2a3a74c4cfbac1                                                                                                 0.2s
 => => extracting sha256:65a8689d9dc85c93d849bd46bd3f5093ea663f1f0d481ba43c4a44e62d68b769                                                                                                 0.0s
 => => extracting sha256:0d1ab692db8c9f5a572637ed9dd6d2c91fec2a49b5f002b04333496e25caf2eb                                                                                                 0.1s
 => [internal] load build context                                                                                                                                                         3.5s
 => => transferring context: 4.80MB                                                                                                                                                       3.4s
 => [base 2/6] RUN apt-get update -y && apt-get install -y     dumb-init     libenchant-2-dev     postgresql-client                                                                      25.9s
 => [base 3/6] RUN adduser     --disabled-password     --gecos ""     --home "/nonexistent"     --shell "/sbin/nologin"     --no-create-home     --uid "10001"     appuser                0.4s
 => [base 4/6] WORKDIR /code/                                                                                                                                                             0.0s
 => ERROR [base 5/6] RUN --mount=type=cache,target=/root/.cache/pip     --mount=type=bind,source=requirements,target=/code/requirements     --mount=type=bind,source=libs,target=/code/  17.2s
------
 > [base 5/6] RUN --mount=type=cache,target=/root/.cache/pip     --mount=type=bind,source=requirements,target=/code/requirements     --mount=type=bind,source=libs,target=/code/libs,rw     --mount=type=bind,source=wagtail,target=/code/wagtail,rw     python -m pip install --cache-dir=/root/.cache/pip -U pip     && python -m pip install --cache-dir=/root/.cache/pip -r /code/requirements/development.txt:
#0 1.202 Requirement already satisfied: pip in /usr/local/lib/python3.12/site-packages (23.2.1)
#0 2.022 Collecting pip
#0 2.022   Obtaining dependency information for pip from https://files.pythonhosted.org/packages/47/6a/453160888fab7c6a432a6e25f8afe6256d0d9f2cbd25971021da6491d899/pip-23.3.1-py3-none-any.whl.metadata
#0 2.456   Downloading pip-23.3.1-py3-none-any.whl.metadata (3.5 kB)
#0 2.625 Downloading pip-23.3.1-py3-none-any.whl (2.1 MB)
#0 15.11    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.1/2.1 MB 176.3 kB/s eta 0:00:00
#0 15.16 Installing collected packages: pip
#0 15.16   Attempting uninstall: pip
#0 15.16     Found existing installation: pip 23.2.1
#0 15.18     Uninstalling pip-23.2.1:
#0 15.23       Successfully uninstalled pip-23.2.1
#0 15.93 Successfully installed pip-23.3.1
#0 15.93 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
#0 16.71 Obtaining file:///code/libs/django-modelcluster (from -r /code/requirements/development.txt (line 2))
#0 16.71   Preparing metadata (setup.py): started
#0 17.08   Preparing metadata (setup.py): finished with status 'done'
#0 17.09 Obtaining file:///code/libs/Willow (from -r /code/requirements/development.txt (line 3))
#0 17.09 ERROR: file:///code/libs/Willow (from -r /code/requirements/development.txt (line 3)) does not appear to be a Python project: neither 'setup.py' nor 'pyproject.toml' found.
------
failed to solve: executor failed running [/bin/sh -c python -m pip install --cache-dir=/root/.cache/pip -U pip     && python -m pip install --cache-dir=/root/.cache/pip -r /code/requirements/development.txt]: exit code: 1
make: *** [build] Error 17

@jsma
Copy link
Contributor Author

jsma commented Nov 22, 2023

I'm sorry I haven't had a chance to update this branch to wrap things up for now. There are currently issues with editing frontend files and having these reflected in Wagtail. Leaving for a holiday but hope to wrap this up next weekend.

As far as the errors, there are new environment variables that only get copied over if you don't already have a .env file (both at the root of this project and in the bakerydemo) so you may need to manually set those, based on the .env.example files. Additionally, the bakerydemo/wagtail/Willow/django-modelcluster checkouts should be up to date. It looks like specifically the error is due to your copy of Willow being older. The pyproject.toml file was introduced in that project in June of this year.

That said, I'll let you know when I've pushed my "final" version of this branch. It's not worth your time at the moment with the current issues with the frontend container.

@lb-
Copy link
Member

lb- commented Nov 22, 2023

Thank you @jsma - your help here is really appreciated. Enjoy the break.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants