From 4e1eec2ea4634e408f809e625310e00b67281645 Mon Sep 17 00:00:00 2001 From: Max Ottenhof Date: Sat, 19 Nov 2022 20:59:52 +0100 Subject: [PATCH] Docker flow implementation: Local development Production --- .env.example | 17 +++++++ Dockerfile | 92 ++++++++++++++++++++++++++++++++++++ README.md | 13 +++++ docker-compose.override.yaml | 24 ++++++++++ docker-compose.prod.yaml | 34 +++++++++++++ docker-compose.yaml | 10 ++++ 6 files changed, 190 insertions(+) create mode 100644 .env.example create mode 100644 Dockerfile create mode 100644 docker-compose.override.yaml create mode 100644 docker-compose.prod.yaml create mode 100644 docker-compose.yaml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..0679698 --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +# Required +DIR=E:\Users\Max\Documents\simple-typescript-starter + +# Optional +PORT=3000 +PINO_LOG_LEVEL=debug + +# Secrets +TEST_FILE=/run/secrets/TEST_FILE +TEST2_FILE=/run/secrets/OPTIONAL_FILE + +# Database +POSTGRES_HOST=127.0.0.1 +POSTGRES_PORT=8090 +POSTGRES_USER=postgres +POSTGRES_PASSWORD=mysecretpassword +POSTGRES_DATABASE=postgres \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2f8bd7a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,92 @@ +################### +# BUILD FOR LOCAL DEVELOPMENT +################### + +FROM node:18-alpine As development + +# Set port 3000 to open +# This is commented out because not all application need an open port +#ARG PORT=3000 +#ENV PORT $PORT + +# Create app directory +WORKDIR /usr/src/app + +# Copy application dependency manifests to the container image. +# A wildcard is used to ensure copying both package.json AND package-lock.json (when available). +# Copying this first prevents re-running npm install on every code change. +COPY --chown=node:node package*.json ./ + +# Install app dependencies using the `npm ci` command instead of `npm install` +RUN npm ci + +# Bundle app source +COPY --chown=node:node . . + +# Use the node user from the image (instead of the root user) +USER node + +################### +# BUILD FOR PRODUCTION +################### + +FROM node:18-alpine As build + +WORKDIR /usr/src/app + +COPY --chown=node:node package*.json ./ + +COPY --chown=node:node --from=development /usr/src/app/node_modules ./node_modules + +COPY --chown=node:node . . + +# Run the test commands +RUN npm run test +# Enable lint in this line +# RUN npm run lint + +# Run the build command which creates the production bundle +RUN npm run build + +# Set NODE_ENV environment variable +ENV NODE_ENV production + +# Running `npm ci` removes the existing node_modules directory. +# Passing in --only=production ensures that only the production dependencies are installed. +# This ensures that the node_modules directory is as optimized as possible. +RUN npm ci --only=production && npm cache clean --force + +USER node +################### +# PRODUCTION DEPLOYMENT +################### + +# Use apline package to keep it small +FROM node:18-alpine As production + +# set our node environment, either development or production +# defaults to production, compose overrides this to development on build and run +ARG NODE_ENV=production +ENV NODE_ENV $NODE_ENV + +# Set port 3000 to open +# This is commented out because not all application need an open port +#ARG PORT=3000 +#ENV PORT $PORT + +# Copy the bundled code from the build stage to the production image +COPY --chown=node:node --from=build /usr/src/app/node_modules ./node_modules +COPY --chown=node:node --from=build /usr/src/app/build ./build + +#Expose port 3000 +# This is commented out because not all application need an open port +#EXPOSE ${PORT} + +# check every 30s to ensure this service returns HTTP 200 +# This is commented out because not all application have a healthcheck endpoint +#HEALTHCHECK --interval=30s CMD wget --no-verbose --tries=1 --spider http://0.0.0.0:$PORT/v1/healthz #todo + +USER node + +# Start the server using the production build +CMD [ "node", "--max_old_space_size=450", "build/index.js" ] diff --git a/README.md b/README.md index 07b6343..8184ddf 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,19 @@ ### Scripts + +#### `docker compose build` + +Builds the docker image for development environment + +#### `docker compose up` + +Starts the application in development using docker compose with hot reloading + +#### `docker build -t simple-type-script-starter:1.0.0-node18-alpine --target production .` + +Builds the docker image for production environment + #### `npm run start:dev` Starts the application in development using `nodemon` and `ts-node` to do hot reloading. diff --git a/docker-compose.override.yaml b/docker-compose.override.yaml new file mode 100644 index 0000000..9e1c305 --- /dev/null +++ b/docker-compose.override.yaml @@ -0,0 +1,24 @@ +networks: + example-backend-dev: + name: example-backend-dev +#this config is automatically applied when using docker compose up +#override service template for local development +#automatically restarts the application after changing source files +#mounts local src folder to service src folder +services: # docker compose up --build + simple-type-script-starter: # docker compose up --build simple-type-script-starter + restart: unless-stopped + build: + context: ${DIR} + dockerfile: Dockerfile + target: development + container_name: simple-type-script-starter + image: simple-type-script-starter:dev-1.0.0-node18-alpine + command: npm run start:dev + volumes: + - ${DIR}:/usr/src/app + - /usr/src/app/node_modules + ports: + - ${PORT}:${PORT} + networks: + - example-backend-dev \ No newline at end of file diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml new file mode 100644 index 0000000..42c0eff --- /dev/null +++ b/docker-compose.prod.yaml @@ -0,0 +1,34 @@ +version: '3.8' + +networks: + backend: + external: true + name: backend + +services: + simple-type-script-starter: + deploy: + mode: replicated + replicas: 1 + placement: + max_replicas_per_node: 2 + # preferences: + # - spread: node.id + constraints: # only on manager/worker node (pick one) + - node.role == manager + restart_policy: + condition: on-failure + delay: 15s + max_attempts: 5 + window: 10s + update_config: + parallelism: 1 + delay: 5s + resources: + limits: + memory: 512M + cpus: '0.4' + reservations: + memory: 64M + networks: + - backend \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..292320f --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,10 @@ +version: '3.8' + +services: + simple-type-script-starter: + image: simple-type-script-starter:1.0.0-node18-alpine + deploy: + mode: replicated + replicas: 1 + env_file: + - .env \ No newline at end of file