From 7238dc234f607c9686fe6c5c2043f7953ef427b8 Mon Sep 17 00:00:00 2001 From: danmgs Date: Mon, 16 Mar 2020 13:50:52 +0100 Subject: [PATCH] dockerize the apps --- .env | 14 +++-- docker-compose-release.yml | 36 +++++++++++++ docker-compose.yml | 103 ++++++++++++++++--------------------- docker-push.ps1 | 18 +++++++ launch.bat | 55 ++++++++++++++++++++ notes.txt | 58 +++++++++++++++++++++ public/Dockerfile | 26 +++++++--- public/default.conf | 9 ++++ public/package.json | 2 + server/.env | 2 +- server/Dockerfile | 15 ++---- server/package.json | 3 ++ server/src/app.js | 26 ++++++---- 13 files changed, 274 insertions(+), 93 deletions(-) create mode 100644 docker-compose-release.yml create mode 100644 docker-push.ps1 create mode 100644 launch.bat create mode 100644 notes.txt create mode 100644 public/default.conf diff --git a/.env b/.env index b214521..9924893 100644 --- a/.env +++ b/.env @@ -1,14 +1,12 @@ COMPOSE_PROJECT_NAME=my-weather-app -ENV_MONGO_PORT=27017 - -ENV_SERVER_API_PORT=30001 +NODE_ENV=production ENV_SERVER_API_MONGODB_URI=mongodb://mongo-service:27017 ENV_SERVER_API_ALLOW_HOSTS=* +ENV_SERVER_API_PORT=30001 -ENV_CLIENT_WEBAPP_PORT=4200 - -NODE_ENV=production +ENV_WEBAPP_NODE_ENV=prod +ENV_WEBAPP_PORT=80 -ENV_SERVER_API_DOCKER_IMAGE_TAG=danmgs/myweatherapp-server-api:1.0 -ENV_CLIENT_WEBAPP_DOCKER_IMAGE_TAG=danmgs/myweatherapp-client-webapp:1.0 +ENV_FRONTEND_IMAGE_TAG=danmgs/weather-app-frontend +ENV_BACKEND_IMAGE_TAG=danmgs/weather-app-backend diff --git a/docker-compose-release.yml b/docker-compose-release.yml new file mode 100644 index 0000000..f901c0a --- /dev/null +++ b/docker-compose-release.yml @@ -0,0 +1,36 @@ +--- +version: '3' +services: + + mongo-service: + image: mongo:latest + volumes: + - mongodata:/data/db + restart: always + + weather-app-backend: + container_name: weather-app-backend + image: danmgs/weather-app-backend:1.1 + environment: + NODE_ENV: production + ENV_SERVER_API_MONGODB_URI: mongodb://mongo-service:27017 + ENV_SERVER_API_ALLOW_HOSTS: "*" + ENV_SERVER_API_PORT: 30001 + ports: + - "30001:30001" + depends_on: + - mongo-service + restart: always + + weather-app-frontend: + container_name: weather-app-frontend + image: danmgs/weather-app-frontend:1.1 + working_dir: /usr/src/app + ports: + - "80:80" + depends_on: + - weather-app-backend + restart: always + +volumes: + mongodata: diff --git a/docker-compose.yml b/docker-compose.yml index 38f700f..f6804e2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,58 +1,45 @@ -version: '3' - -services: - - mongo-service: - - container_name: mongo - image: mongo - restart: always - ports: - - '$ENV_MONGO_PORT:$ENV_MONGO_PORT' - volumes: - - ./mongo/data/db:/data/db:ro - - server-api-service: - - build: - dockerfile: Dockerfile - context: ./server - image: $ENV_SERVER_API_DOCKER_IMAGE_TAG - working_dir: /usr/src/app - # volumes: - #- ./server:/usr/src/app - #- /usr/src/app/node_modules - # command: './node_modules/nodemon/bin/nodemon' - command: 'node ./src/app.js' - container_name: server-api-service - ports: - - '$ENV_SERVER_API_PORT:$ENV_SERVER_API_PORT' - environment: - - ENV_SERVER_API_MONGODB_URI=$ENV_SERVER_API_MONGODB_URI - - ENV_SERVER_API_ALLOW_HOSTS=$ENV_SERVER_API_ALLOW_HOSTS - - NODE_ENV=$NODE_ENV - restart: always - depends_on: - - mongo-service - - client-webapp-service: - - build: - context: ./public - dockerfile: Dockerfile - # args: - # - NODE_ENV=development # development / staging / production - image: $ENV_CLIENT_WEBAPP_DOCKER_IMAGE_TAG - working_dir: /usr/src/app - # volumes: - # - ./public:/usr/src/app - # - /usr/src/app/node_modules - container_name: client-webapp-service - ports: - - '$ENV_CLIENT_WEBAPP_PORT:$ENV_CLIENT_WEBAPP_PORT' - environment: - - NODE_ENV=$NODE_ENV # development / staging / production - command: "npm start" - restart: always - depends_on: - - server-api-service +--- +version: '3' +services: + + mongo-service: + image: mongo:latest + volumes: + - mongodata:/data/db + restart: always + + weather-app-backend: + build: + context: ./server + dockerfile: Dockerfile + container_name: weather-app-backend + image: ${ENV_BACKEND_IMAGE_TAG} + environment: + NODE_ENV: ${NODE_ENV} + ENV_SERVER_API_MONGODB_URI: ${ENV_SERVER_API_MONGODB_URI} + ENV_SERVER_API_ALLOW_HOSTS: ${ENV_SERVER_API_ALLOW_HOSTS} + ENV_SERVER_API_PORT: ${ENV_SERVER_API_PORT} + ports: + - ${ENV_SERVER_API_PORT}:${ENV_SERVER_API_PORT} + depends_on: + - mongo-service + restart: always + + weather-app-frontend: + build: + context: ./public + dockerfile: Dockerfile + args: + - NODE_ENV=${ENV_WEBAPP_NODE_ENV} # development / staging / prod + container_name: weather-app-frontend + image: ${ENV_FRONTEND_IMAGE_TAG} + working_dir: /usr/src/app + ports: + - ${ENV_WEBAPP_PORT}:${ENV_WEBAPP_PORT} + depends_on: + - weather-app-backend + restart: always + +# https://github.com/docker-library/mongo/issues/74#issuecomment-350034758 +volumes: + mongodata: diff --git a/docker-push.ps1 b/docker-push.ps1 new file mode 100644 index 0000000..c686389 --- /dev/null +++ b/docker-push.ps1 @@ -0,0 +1,18 @@ +# Purpose: script to push image into your docker hub. Must be logged in first. +# Usage: powershell docker-push.ps1 -tag +Param([parameter(Mandatory=$true, + HelpMessage="Enter docker tag version")] + $tag) + +write-output "Entered tag = $tag" + +# Build the images using tag name specify via image attributes in the docker-compose files. +docker-compose build + +# Tags the images +docker tag danmgs/weather-app-frontend danmgs/weather-app-frontend:$tag +docker tag danmgs/weather-app-backend danmgs/weather-app-backend:$tag + +# Push the images +docker push danmgs/weather-app-frontend:$tag +docker push danmgs/weather-app-backend:$tag diff --git a/launch.bat b/launch.bat new file mode 100644 index 0000000..3e965c2 --- /dev/null +++ b/launch.bat @@ -0,0 +1,55 @@ +@ECHO OFF +TITLE launch.bat - Angular WebApp + +SET interactive=1 +IF %ERRORLEVEL% == 0 SET interactive=0 + +IF "%1"=="logs" ( + IF "%2"=="" ( + ECHO Showing logs ^(mongo-service^|^|app-backend^|^|app-frontend^) + GOTO End + ) + IF "%2"=="mongo-service" ( + ECHO Showing logs from the mongo-service container... + docker-compose logs -f mongo-service + GOTO End + ) + IF "%2"=="app-backend" ( + ECHO Showing logs from the app-backend container... + docker-compose logs -f app-backend + GOTO End + ) + IF "%2"=="app-frontend" ( + ECHO Showing logs from the app-frontend container... + docker-compose logs -f app-frontend + GOTO End + )) + + +IF "%1"=="up" ( + ECHO Launching... + ECHO If this is your first time starting the project this might take a minute... + + docker-compose up -d --build + ECHO Opening tabs in browser... + timeout /t 10 /nobreak > NUL + START "" http://localhost + START "" http://localhost:30001/api/serverhealth + GOTO End +) + +IF "%1"=="down" ( + ECHO Stopping and removing running sandbox containers... + docker-compose down + GOTO End +) + +ECHO commands: +ECHO up -^> spin up the sandbox environment +ECHO down -^> tear down the sandbox environment +ECHO. +ECHO logs ^(mongo-service^|^|app-backend^|^|app-frontend^) -^> stream logs for the specified container + +:End +IF "%interactive%"=="0" PAUSE +EXIT /B 0 diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..6f15af7 --- /dev/null +++ b/notes.txt @@ -0,0 +1,58 @@ +# Manual steps notes +# Follow instructions in order: + +# Create the network +docker network create mynetwork +docker network ls + +# Build and run frontend webapp +cd public +docker build -t weather-frontend-app --build-arg NODE_ENV=prod . +docker run --rm -d -p 80:80 --name weather-frontend-app weather-frontend-app + +# Build and run mongo +# https://github.com/docker-library/mongo/issues/74 +docker volume create --name=mongodata +cd server +docker run -d --name mongo-service -v mongodata:/data/db --network=mynetwork mongo + +# Build and run backend server with environment variables settings. +docker build -t weather-backend-app . +docker run -d -p 30001:30001 --name weather-backend-app --network=mynetwork -e NODE_ENV=production -e ENV_SERVER_API_MONGODB_URI=mongodb://mongo-service:27017 -e ENV_SERVER_API_ALLOW_HOSTS=* -e ENV_SERVER_API_PORT=30001 weather-backend-app + +# check network -> shows 2 containers are linked: backend app container can reach mongo container. +docker inspect mynetwork + +# inspect volume and where it lives. +docker inspect mongodata + +# check backend url +http://localhost:30001/api/serverhealth + +# check frontend url +http://localhost + + +# check logs +docker logs + +# other commands + +docker kill mongo-service +docker rm mongo-service + +docker kill weather-frontend-app +docker kill mongo-service +docker kill weather-backend-app + +docker rm weather-frontend-app +docker rm mongo-service +docker rm weather-backend-app + +docker ps + +# show environment variables injected +docker-compose config + +# docker compose +docker-compose -f docker-compose-release.yml up diff --git a/public/Dockerfile b/public/Dockerfile index 1fc97ff..57b8bd7 100644 --- a/public/Dockerfile +++ b/public/Dockerfile @@ -1,13 +1,27 @@ -FROM node:alpine +FROM node:alpine as build -ARG NODE_ENV=development -ENV NODE_ENV $NODE_ENV - -RUN echo $NODE_ENV +# arg with default value if not set. +ARG NODE_ENV=prod WORKDIR /usr/src/app COPY ./package*.json ./ RUN npm install COPY . . -CMD ["npm", "run", "start"] +# generate build +RUN npm run build:$NODE_ENV + +# base image +FROM nginx:1.16.0-alpine + +# copy artifact build from the 'build environment' +COPY --from=build /usr/src/app/dist /usr/share/nginx/html + +# nginx routes config +COPY ./default.conf /etc/nginx/conf.d + +# expose port 80 +EXPOSE 80 + +# run nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/public/default.conf b/public/default.conf new file mode 100644 index 0000000..b7f2f6c --- /dev/null +++ b/public/default.conf @@ -0,0 +1,9 @@ +server { + listen 80; + server_name localhost; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html =404; + } +} diff --git a/public/package.json b/public/package.json index acbaabf..650c188 100644 --- a/public/package.json +++ b/public/package.json @@ -6,6 +6,8 @@ "ng": "ng", "start": "ng serve --host 0.0.0.0 --configuration $NODE_ENV", "build": "ng build", + "build:prod": "ng build --configuration=production --output-path=dist", + "build:development": "ng build --configuration=development --output-path=dist", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" diff --git a/server/.env b/server/.env index 7d4af4c..540d8ee 100644 --- a/server/.env +++ b/server/.env @@ -1,7 +1,7 @@ # https://newsapi.org # https://npm.runkit.com/node-news -SERVER_API_PORT=30001 +ENV_SERVER_API_PORT=30001 ENV_SERVER_API_MONGODB_URI=mongodb://localhost:27017 API_KEY_DARKSKY=3ef106162ed142fc3dc78e058263e0e8 NEWS_ARTICLES= diff --git a/server/Dockerfile b/server/Dockerfile index 4b2228b..0ab0cf6 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,18 +1,11 @@ FROM node:alpine -ARG NODE_ENV=development -ENV NODE_ENV $NODE_ENV - -ARG ENV_SERVER_API_ALLOW_HOSTS=* -ENV ENV_SERVER_API_ALLOW_HOSTS $ENV_SERVER_API_ALLOW_HOSTS - -RUN echo $NODE_ENV -RUN echo $ENV_SERVER_API_ALLOW_HOSTS - WORKDIR /usr/src/app COPY ./package*.json ./ RUN npm install -# RUN npm install -g nodemon COPY . . -CMD ["npm", "run", "start"] +# expose port 30001 +EXPOSE 30001 + +CMD ["npm", "run", "start:docker"] diff --git a/server/package.json b/server/package.json index 9c8e879..84c5e6e 100644 --- a/server/package.json +++ b/server/package.json @@ -8,6 +8,9 @@ "jshint": "jshint src || ECHO.", "lint": "npm run eslint", "start": "nodemon --watch app.js --watch **/*", + "start:prod": "SET NODE_ENV=production && SET ENV_SERVER_API_ALLOW_HOSTS=* && SET ENV_SERVER_API_MONGODB_URI=mongodb://localhost:27017 && SET ENV_SERVER_API_PORT=30001 && node src/app.js", + "start:test": "SET NODE_ENV=test && SET ENV_SERVER_API_ALLOW_HOSTS=* && SET ENV_SERVER_API_MONGODB_URI=mongodb://localhost:27017 && SET ENV_SERVER_API_PORT=30001 && node src/app.js", + "start:docker": "node src/app.js", "test": "cross-env NODE_ENV=test nodemon --exec \"mocha --timeout 20000 \"src/test/**/*\" \"", "test:noverbose": "cross-env NODE_ENV=test nodemon --exec \"mocha --timeout 20000 \"src/test/**/*\" \" -R min" }, diff --git a/server/src/app.js b/server/src/app.js index 15e4324..58d3689 100644 --- a/server/src/app.js +++ b/server/src/app.js @@ -18,20 +18,30 @@ const app = express(); console.log(`Starting server from ${__dirname} ...`); mongoose.Promise = global.Promise; -console.log(`process.env.NODE_ENV = ${process.env.NODE_ENV}`); -if (process.env.NODE_ENV !== 'production') { + +// Init environment variables +const nodeEnv = process.env.NODE_ENV || 'test'; +const mongodbUri = process.env.ENV_SERVER_API_MONGODB_URI || 'mongodb://localhost:27017'; +const allowHosts = process.env.ENV_SERVER_API_ALLOW_HOSTS || '*'; +const port = process.env.ENV_SERVER_API_PORT || '30001'; + +console.log(` > process.env.NODE_ENV = '${process.env.NODE_ENV}', use '${nodeEnv}'`); +console.log(` > process.env.ENV_SERVER_API_MONGODB_URI = '${process.env.ENV_SERVER_API_MONGODB_URI}', use '${mongodbUri}'`); +console.log(` > process.env.ENV_SERVER_API_ALLOW_HOSTS = '${process.env.ENV_SERVER_API_ALLOW_HOSTS}', use '${allowHosts}'`); +console.log(` > process.env.ENV_SERVER_API_PORT = '${process.env.ENV_SERVER_API_PORT}', use '${port}'`); +console.log(' *************************************'); + +if (nodeEnv.trim() !== 'production') { console.log('TEST Config'); } else { console.log('PROD Config'); - const url = `${process.env.ENV_SERVER_API_MONGODB_URI}/weatherapp`; + const url = `${mongodbUri.trim()}/weatherapp`; mongoose.connect(url, { useMongoClient: true }, (error) => { if (!error) console.log(`Connect to mongodb with success with url : ${url} !`); else console.log(`Error connecting to mongodb with url : ${url} !`); }); } -console.log('*************************************'); - // Parsers app.use(bodyParser.json()); // app.use(bodyParser.urlencoded({ extended: true})); @@ -40,9 +50,8 @@ app.use(bodyParser.json()); app.use(express.static(path.join(__dirname, 'dist'))); // CORS Configuration to allow cross domains, to set BEFORE API routing -console.log(`process.env.ENV_SERVER_API_ALLOW_HOSTS = ${process.env.ENV_SERVER_API_ALLOW_HOSTS}`); app.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Origin', allowHosts.trim()); res.setHeader('Access-Control-Allow-Credentials', 'true'); res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS,POST,PUT,DELETE'); res.setHeader('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Access-Control-Allow-Methods, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers'); @@ -79,8 +88,7 @@ app.get('*', (req, res) => { }); // Set Port -const port = process.env.SERVER_API_PORT || '30001'; -app.set('port', port); +app.set('port', port.trim()); const server = http.createServer(app);