- Requirements
- Logic and end-results
- Getting Started
- Folder Structure & Explanation:
- How to build non-production image:
- How to build production image:
- Docker
Since the pdf instructed me to optimize the production image in various ways, I would like to explain my logic behind what I did and why I did it.
-
Install node dependencies for production:
- This step was fairly easy, as using the "--production" flag when installing dependencies and also pruning at the end (just in case) made me confident that the image was as light as possible in its entirety.
-
Using an appropriate lightweight base image from node, My methods for creating the production container were as follows:
-
Using multi-stage Docker builds (This way, our final image won’t have any unnecessary content except the required artifact).
-
Removing development dependencies (as discussed above), and choosing a lightweight base image.
-
Using a .dockerignore file and ignoring local modules and debug logs from being copied onto Docker image. (node_modules and npm-debug.log)
-
Minimizing the amount of layers to install packages in the Dockerfile to avoid increasing the size of the image.
- This last step also was essential in optimizing the run steps so that only the layers that change frequently (those being the RUN steps) are towards the bottom while the steps that changed rarely were more towards the top of the dockerfile. I do believe that I could have made the production image even smaller by using a distroless image(i.e gcr.io/distroless), but I was not sure if I was allowed and I decided to stay with the node:alpine images.
-
*** End Results were as follows ***
non_production_image_nextjs build time: 22s
non_production_image_nextjs size: 799MB
-------------------------------------------
production_image build time: 8s
production_image size: 335MB
REPOSITORY TAG IMAGE ID CREATED SIZE
non_production_image_nextjs latest 1cf772ef1410 2 minutes ago 779MB
production_image latest 18632066a6be 25 minutes ago 335MB
Export this variable in your local environment, or any other version you would like
export NEXT_PUBLIC_APP_VERSION=v1.0.0
SRE <-- Root folder
├── README.md
├── docker-compose.yml <-- docker-compose file for non-production image
└── packages
└── SRE_Test <-- Folder for most of the production level files & deps
├── Dockerfile <-- Production Dockerfile
├── Dockerfile.dev <-- Non-production Dockerfile
├── next.config.js
├── node_modules
├── package-lock.json
├── package.json
├── pages
├── public
└── styles
docker-compose -p [nameOfProject] up
Example:
docker-compose -p non_production_image up
Important that you are in the SRE_Test folder when running these commands... That being /SRE/packages/SRE_Test/
docker build --build-arg NEXT_PUBLIC_APP_VERSION=$NEXT_PUBLIC_APP_VERSION -f Dockerfile -t [nameOfImage] .
Example:
docker build --build-arg NEXT_PUBLIC_APP_VERSION=$NEXT_PUBLIC_APP_VERSION -f Dockerfile -t production_image .
Then run the image. Since the instructions didn't state specific ports for production image, I assumed that it would be up to the user to set them.
docker run -p [portValue]:[portValue] [nameOfImage]
Example:
docker run -p 3000:3000 production_image