Skip to content

add nginx, multistage dockerfile, testing, hot reloading#1

Draft
jeffzyliu wants to merge 2 commits intotmonfre:mainfrom
jeffzyliu:main
Draft

add nginx, multistage dockerfile, testing, hot reloading#1
jeffzyliu wants to merge 2 commits intotmonfre:mainfrom
jeffzyliu:main

Conversation

@jeffzyliu
Copy link

sending a draft PR just to give you a pretty diff to look at/comment on

Comment on lines +17 to +22
RUN npm ci
COPY src src
COPY webpack* .
COPY .eslintrc.json .
COPY .babelrc .
COPY tsconfig.json .
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose to not copy all; this pretty much just excludes default.conf from the dependencies of the builder image. This way, you can change the nginx stuff without forcing webpack to rebuild the same source code. Super fast.

Comment on lines +11 to +13
FROM dev AS test
COPY . .
CMD ["npm", "run", "test"]
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may look like major overkill here, but the purpose is for ci/cd environments where you can spin up a separate lint and unit/integration test image without making the dev or prod images depend on it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually better practice might be to use RUN instead of CMD so it blocks building of the image if checks dont pass. ¯_(ツ)_/¯

Comment on lines +26 to +27
# this still installs all the non-dev dependencies; removing react etc would be better
RUN npm ci --production
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you were to run this as an express server, the cleanest way would be to put everything in devDependencies so this only installs express. That's kind of bad coding practices though so it's a pick your poison thing. These images are already way smaller when the devDependencies are pruned so it's not like we have a lack of space right now.

Comment on lines +32 to +33
FROM nginx:mainline-alpine AS nginx
COPY --from=builder /app/build /usr/share/nginx/html
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the magic of multistage dockerfiles: we use an entirely separate image(nginx) to host the web server. This image literally doesnt even have node, and it has none of the things in this repo actually except for default.conf (and the webpack output). We use the --from=builder option to let Docker know that this image depends on the builder image, so it builds the builder before this one.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multistage dockerfiles also allow for parallelism; it finds layers that have no dependencies and runs them simultaneously, blocking when there's a dependency such as copy --from

Comment on lines +16 to +17
# redirects any sub-routes to root for anything containing extension except html
rewrite ([^\/]*)\.(?!html)(.*)$ /$1.$2;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really hobbled together; it does the same thing as your app.use in ./app.js where it translates all requests for whatever/whatever.ext to just ./whatever.ext. Except for html, which is handled in the location / thing.

Comment on lines -10 to +12
- ".:/app"
depends_on:
- server
- "./src:/app/src"
- "./app.js:/app/app.js"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This I already mentioned. These are the only source files you'll be editing that need hot reloading so it should suffice.

Comment on lines +4 to +11
build:
context: .
target: nginx
# target: prod
restart: unless-stopped
ports:
- "9090:9090"
depends_on:
- db
# - "8080:8080"
- "80:80"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swap between prod and nginx to test both approaches

@@ -0,0 +1,6 @@
version: '3.8'
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chose to keep this as a docker compose even though it's technically redundant bc it makes the rebuild interface a bit cleaner

"clean": "rimraf build",
"docker:debug": "docker-compose -f docker-compose.debug.yml -p example_webapp_debug up",
"docker:prod": "docker-compose -f docker-compose.prod.yml -p example_webapp_prod up"
"dev": "npm run build:watch & npm run build:run",
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the hacky solution: doing build:watch asyncronously and build:run in the foreground. So webpack watch AND nodemon are both running, each hot reloading the part it's responsible for. Never need to install node modules on the host machine at all!

Well you need to do that for vscode, but I intend to figure out how to develop in the container too so you really don't touch the host.

Comment on lines +14 to +17
"docker:clean": "docker image prune",
"docker:debug": "docker-compose -f docker-compose.debug.yml -p example_webapp_debug up --build",
"docker:prod": "docker-compose -f docker-compose.prod.yml -p example_webapp_prod up --build",
"docker:test": "docker-compose -f docker-compose.test.yml -p example_webapp_test up --build"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the three compose instructions: --build rebuilds the image (using the cache ofc) which allows you to develop faster bc it responds to any source code/config/dependency changes without you having to manually rebuild the images.

This leaves a bunch of hollow image aliases though bc it doesnt delete the old ones when you rebuild. Docker image prune will delete the deprecated images to clean up space.

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.

1 participant