-
Couldn't load subscription status.
- Fork 2
Optimize Docker builds and E2E #33
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
Conversation
package.json
Outdated
| "docker:up": "docker compose --env-file .env.docker up", | ||
| "docker:down": "docker compose --env-file .env.docker down --rmi all --volumes" | ||
| "docker:down": "docker compose --env-file .env.docker down" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The thing is that all our sample app e2e tests in the core app rely on running the docker:up to run a MB based on a locally built MB uberjar.
The same way, they rely on docker:down to completely kill everything related to the container (container, image)
So changing the behavior of these commands will break the core app e2e.
But we can override docker:up/docker:down commands in core app for each app to run something different
So:
- let's introduce
docker:e2e:upanddocker:e2e:downcommands that behave like before - the new docker-compose-e2e file for e2e should be added that behaves as before, i.e. it builds an image based on Dockerfile, Dockerfile should be restored. If we can extend the
docker-compose.ymlin it and just overrideimagewithbuild- would be nice - this docker-compose-e2e should be used in
docker:e2e:up/docker:e2e:down docker:e2e:upshould be used in CI workflow in this repo (instead ofdocker:up)- core-app changes (the usage of
docker:e2e:up/docker:e2e:downcan be handled by our team (me), but this PR must be merged ONLY after we merge a core-app PR, otherwise we break CI in core app
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense to me, though I think we can still avoid the build by directly mounting the JAR into the container instead? This should give us a decent speedup.
We could introduce a new docker-compose.e2e.yaml (or whatever) file, like so:
services:
metabase:
volumes:
- ./local-dist/metabase.jar:/app/metabase.jarThen, introduce a docker:e2e:up command that runs:
docker compose -f docker-compose.yml -f docker-compose.e2e.yml --env-file .env.docker up
It's not strictly the same as before but should still work I think. And we can add the docker:e2e:down command as you suggested.
This has the added benefit of failing early if we're running an E2E test without a custom JAR, which I think should never happen but is technically possible with the current setup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me check tomorrow if this approach will work with Shoppy (we want to have a consistent setup for all Sample Apps).
At the first glance - it should but i want to be 100% sure, that there won't be issues with a custom entrypoint (defined in docker-compose-e2e)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
services: metabase: volumes: - ./local-dist/metabase.jar:/app/metabase.jar
Just this - won't work well if the metabase.jar is missing, and it may be missing. When a container is running, an empty metabase.jar folder is created and it looks weird and annoying.
But we can use a long syntax with create_host_path: false
services:
metabase:
volumes:
- type: bind
source: ./local-dist/metabase.jar
target: /app/metabase.jar
read_only: true
bind:
create_host_path: false
It also has cons: ideally if a file is not here - we want just to avoid the creation of a bind volume. With the approach above for a missing file it will throw an error. But after 57 it's fine, because to test SDK locally we anyway MUST copy a locally built metabase.jar because it will contain the SDK hosted bundle code.
Based on Shoppy adjustments, the proposal:
docker:up,docker:downcommands keep them as in your PR currently- rename your
rmintodocker:rm - add
docker:e2e:upfor e2e on the level of this repo - for this repo and for Nextjs app it should just calldocker:upunder the hood. (For Shoppy it will run an additionaldocker-compose.e2e.ymlwith overrides) - add
docker:local-dist:upcommand. It should rundocker compose -f docker-compose.yml -f docker-compose.local-dist.yml --env-file .env.docker up - add
docker-compose.local-dist.ymlfile with the content:
services:
metabase:
volumes:
- type: bind
source: ./local-dist/metabase.jar
target: /app/metabase.jar
read_only: true
bind:
create_host_path: false
- in
e2e-tests.ymllet's runnpm run docker:e2e:upinstead ofdocker:up
So we will have the docker:e2e:up command for e2e CI workflows of this repo, that currently behaves just as a regular docker:up command
We will have the docker:local-dist:up command that will be used locally AND in e2e on the level of the core app to start containers based on the locally built metabase.jar and Embedding SDK package dist (note, that locally it can be either a local metabase.jar OR a local SDK dist, or both).
Wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also i created a Shoppy PR with changes mentioned above
metabase/shoppy#161
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense to me. Most of the complexity (conditionally using a local or not) seems to be caused by using Docker Compose, since it doesn't support logic for mounting volumes. At some point it might be worth moving away from Compose and directly calling the Docker API/CLI, though definitely not in this PR :)
I don't work on these codebases enough to have an opinion on whether the increased complexity is worth it, so I'll defer to you to go ahead and merge this PRs and the others you've created. Thanks for taking this onboard @sanex3339!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see comment
In #14, we introduced the
yarn startrun-command, which is now the documented way to run this sample.I noticed a few improvement opportunities for the current setup:
Don't build a Metabase image
Building a Metabase image is slow, unnecessary and error-prone. This sample should not create its own Metabase image, and instead only use external ones. If the core monorepo depends on this behavior for testing, it should be accommodated some other way such as a separate Compose file, CLI arguments, etc. There should not be implicit behavior such as changing what gets built if a JAR happens to be in some directory.
I've not investigated yet what the implications of this change are for the core monorepo, but wanted to get some validation on this approach before I do that.
Also, instead of building the
config.ymlinto the image, we now just mount it into the Metabase container.Client image gets rebuilt always
Running
yarn run startwould always result in theclientimage being built, even when no changes were made. This was because the.dockerignorewas not targeting the nestednode_modulesdirectory, which would always get changed during a build, which caused the next build to not get cached. Fixed by adding glob patterns to the.dockerignore.Don't purge everything on
docker:downCurrently,
yarn run docker:downpurges everything, including built images and volumes. This is usually unnecessary, makes the nextyarn run startmuch slower, and is confusing if you expect this command to behave similar todocker compose downwith no arguments.I changed
yarn run docker:downto just dodocker compose down. Removing volumes had no effect anyway, since there are no persistent volumes in this setup. Removing images is unnecessary, since we're rebuilding them on startup if needed. This should have no effect in CI.Because image build caching now works correctly, we also add
--buildtoyarn run start, so that images are always rebuilt if necessary on startup.Fix race condition and use healthcheck in E2E tests
The E2E tests only check if the client app accepts TCP connections using a hardcoded port, but nothing about if it's healthy or not, or if other containers are healthy. Instead, I've added a healthcheck to the
clientcontainer, and usedocker compose up --waitto wait until all healthchecks succeed before running the E2E tests.