├── .gitignore ├── README.md ├── app ├── Dockerfile ├── index.js ├── package-lock.json └── package.json ├── docker-compose.yml ├── nginx ├── Dockerfile └── nginx.conf └── sample_docker_workflow_diagram.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,node 3 | 4 | ### OSX ### 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | 9 | # Icon must end with two \r 10 | Icon 11 | 12 | 13 | # Thumbnails 14 | ._* 15 | 16 | # Files that might appear in the root of a volume 17 | .DocumentRevisions-V100 18 | .fseventsd 19 | .Spotlight-V100 20 | .TemporaryItems 21 | .Trashes 22 | .VolumeIcon.icns 23 | 24 | # Directories potentially created on remote AFP share 25 | .AppleDB 26 | .AppleDesktop 27 | Network Trash Folder 28 | Temporary Items 29 | .apdisk 30 | 31 | 32 | ### Node ### 33 | # Logs 34 | logs 35 | *.log 36 | npm-debug.log* 37 | 38 | # Runtime data 39 | pids 40 | *.pid 41 | *.seed 42 | 43 | # Directory for instrumented libs generated by jscoverage/JSCover 44 | lib-cov 45 | 46 | # Coverage directory used by tools like istanbul 47 | coverage 48 | 49 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 50 | .grunt 51 | 52 | # node-waf configuration 53 | .lock-wscript 54 | 55 | # Compiled binary addons (http://nodejs.org/api/addons.html) 56 | build/Release 57 | 58 | # Dependency directory 59 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 60 | node_modules 61 | 62 | # Optional npm cache directory 63 | .npm 64 | 65 | # Optional REPL history 66 | .node_repl_history 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## sample docker workflow with Node.js,MongoDB and NGiNX 2 | 3 | ![](https://github.com/atahani/docker-workflow-sample/raw/master/sample_docker_workflow_diagram.png "Docker workflow diagram") 4 | 5 | #### simply clone repo and run docker-compose command 6 | 7 | ``` 8 | git clone https://github.com/atahani/docker-workflow-sample.git 9 | docker-compose up -d 10 | ``` 11 | 12 | #### for more information you can see this youtube video 13 | [![deploy docker workflow](http://img.youtube.com/vi/RrkZ9kMNvbA/0.jpg)](https://www.youtube.com/watch?v=RrkZ9kMNvbA) 14 | 15 | this sample is part of [docker tutorial](https://www.youtube.com/playlist?list=PL-0EQDLPE23MIruFZDPNoVytW_2N7xQ6f) in persian language by me. 16 | 17 | [Docker Tutorial slides](https://goo.gl/3mBvb5) 18 | -------------------------------------------------------------------------------- /app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:9.4.0-slim 2 | MAINTAINER "ahmad.tahani@gmail.com" 3 | 4 | RUN mkdir -p /nodejs/app 5 | 6 | WORKDIR /nodejs/app/ 7 | 8 | #copy app files to WORKDIR 9 | COPY index.js . 10 | COPY package.json . 11 | 12 | #install nodejs dependencies 13 | RUN npm install 14 | 15 | CMD npm install && node index.js 16 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /*** 4 | dependencies 5 | */ 6 | var http = require('http'), 7 | mongoose = require('mongoose'); 8 | 9 | var default_port = 5000; 10 | if(process.env.APP_PORT){ 11 | default_port = process.env.APP_PORT; 12 | } 13 | //define mongoose connection string 14 | //NOTE:since 'our_mongodb' link to this container, use 'our_mongodb' name as ip address of mongodb server 15 | mongoose.connect('mongodb://our_mongodb/foo',{ 16 | useMongoClient: true, 17 | }); 18 | 19 | // mpromise (mongoose's default promise library) was decrypted 20 | mongoose.Promise = require('bluebird'); 21 | 22 | var Counter = mongoose.model('Counter',{ 23 | title:String, 24 | number:{ 25 | type:Number, 26 | default:0 27 | } 28 | }); 29 | 30 | //create server 31 | var server = http.createServer(function(req,res){ 32 | //increment the counter 33 | Counter.update({title:'Number of Request'},{$inc:{number:1}},{upsert: true}) 34 | .exec(function(err,counter){ 35 | if(err){ 36 | res.end('error happend'); 37 | } 38 | else{ 39 | Counter.findOne({title:'Number of Request'},function(err,counter){ 40 | if(err){ 41 | res.end('error happend'); 42 | } 43 | else{ 44 | console.log('the number of request is '+counter.number); 45 | res.end('the number of request is '+counter.number); 46 | } 47 | }); 48 | } 49 | }); 50 | }); 51 | 52 | //our application listen to port for any incomming request 53 | server.listen(default_port,function(){ 54 | console.log('Server listening on port '+default_port); 55 | }); 56 | -------------------------------------------------------------------------------- /app/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "micro_service_sample_app", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "async": { 8 | "version": "2.1.4", 9 | "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", 10 | "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", 11 | "requires": { 12 | "lodash": "4.17.4" 13 | } 14 | }, 15 | "bluebird": { 16 | "version": "3.5.1", 17 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", 18 | "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", 19 | "dev": true 20 | }, 21 | "bson": { 22 | "version": "1.0.4", 23 | "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", 24 | "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=" 25 | }, 26 | "buffer-shims": { 27 | "version": "1.0.0", 28 | "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", 29 | "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" 30 | }, 31 | "core-util-is": { 32 | "version": "1.0.2", 33 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 34 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 35 | }, 36 | "debug": { 37 | "version": "2.6.9", 38 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 39 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 40 | "requires": { 41 | "ms": "2.0.0" 42 | } 43 | }, 44 | "es6-promise": { 45 | "version": "3.2.1", 46 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", 47 | "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" 48 | }, 49 | "hooks-fixed": { 50 | "version": "2.0.2", 51 | "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.2.tgz", 52 | "integrity": "sha512-YurCM4gQSetcrhwEtpQHhQ4M7Zo7poNGqY4kQGeBS6eZtOcT3tnNs01ThFa0jYBByAiYt1MjMjP/YApG0EnAvQ==" 53 | }, 54 | "inherits": { 55 | "version": "2.0.3", 56 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 57 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 58 | }, 59 | "isarray": { 60 | "version": "1.0.0", 61 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 62 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 63 | }, 64 | "kareem": { 65 | "version": "1.5.0", 66 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz", 67 | "integrity": "sha1-4+QQHZ3P3imXadr0tNtk2JXRdEg=" 68 | }, 69 | "lodash": { 70 | "version": "4.17.4", 71 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 72 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" 73 | }, 74 | "lodash.get": { 75 | "version": "4.4.2", 76 | "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", 77 | "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" 78 | }, 79 | "mongodb": { 80 | "version": "2.2.34", 81 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.34.tgz", 82 | "integrity": "sha1-o09Zu+thdUrsQy3nLD/iFSakTBo=", 83 | "requires": { 84 | "es6-promise": "3.2.1", 85 | "mongodb-core": "2.1.18", 86 | "readable-stream": "2.2.7" 87 | } 88 | }, 89 | "mongodb-core": { 90 | "version": "2.1.18", 91 | "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.18.tgz", 92 | "integrity": "sha1-TEYTm986HwMt7ZHbSfOO7AFlkFA=", 93 | "requires": { 94 | "bson": "1.0.4", 95 | "require_optional": "1.0.1" 96 | } 97 | }, 98 | "mongoose": { 99 | "version": "4.13.9", 100 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.13.9.tgz", 101 | "integrity": "sha512-UGkSJR5iYHfHGlAyNNJS/mX5HoukDssQoy2pyJTpdyOXnxSw1ujYvMlxEuiIOQEWH2oZSAmHfjH+/igxG1MXLQ==", 102 | "requires": { 103 | "async": "2.1.4", 104 | "bson": "1.0.4", 105 | "hooks-fixed": "2.0.2", 106 | "kareem": "1.5.0", 107 | "lodash.get": "4.4.2", 108 | "mongodb": "2.2.34", 109 | "mpath": "0.3.0", 110 | "mpromise": "0.5.5", 111 | "mquery": "2.3.3", 112 | "ms": "2.0.0", 113 | "muri": "1.3.0", 114 | "regexp-clone": "0.0.1", 115 | "sliced": "1.0.1" 116 | } 117 | }, 118 | "mpath": { 119 | "version": "0.3.0", 120 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz", 121 | "integrity": "sha1-elj3iem1/TyUUgY0FXlg8mvV70Q=" 122 | }, 123 | "mpromise": { 124 | "version": "0.5.5", 125 | "resolved": "https://registry.npmjs.org/mpromise/-/mpromise-0.5.5.tgz", 126 | "integrity": "sha1-9bJCWddjrMIlewoMjG2Gb9UXMuY=" 127 | }, 128 | "mquery": { 129 | "version": "2.3.3", 130 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.3.tgz", 131 | "integrity": "sha512-NC8L14kn+qxJbbJ1gbcEMDxF0sC3sv+1cbRReXXwVvowcwY1y9KoVZFq0ebwARibsadu8lx8nWGvm3V0Pf0ZWQ==", 132 | "requires": { 133 | "bluebird": "3.5.0", 134 | "debug": "2.6.9", 135 | "regexp-clone": "0.0.1", 136 | "sliced": "0.0.5" 137 | }, 138 | "dependencies": { 139 | "bluebird": { 140 | "version": "3.5.0", 141 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", 142 | "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" 143 | }, 144 | "sliced": { 145 | "version": "0.0.5", 146 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-0.0.5.tgz", 147 | "integrity": "sha1-XtwETKTrb3gW1Qui/GPiXY/kcH8=" 148 | } 149 | } 150 | }, 151 | "ms": { 152 | "version": "2.0.0", 153 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 154 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 155 | }, 156 | "muri": { 157 | "version": "1.3.0", 158 | "resolved": "https://registry.npmjs.org/muri/-/muri-1.3.0.tgz", 159 | "integrity": "sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg==" 160 | }, 161 | "process-nextick-args": { 162 | "version": "1.0.7", 163 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 164 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" 165 | }, 166 | "readable-stream": { 167 | "version": "2.2.7", 168 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", 169 | "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", 170 | "requires": { 171 | "buffer-shims": "1.0.0", 172 | "core-util-is": "1.0.2", 173 | "inherits": "2.0.3", 174 | "isarray": "1.0.0", 175 | "process-nextick-args": "1.0.7", 176 | "string_decoder": "1.0.3", 177 | "util-deprecate": "1.0.2" 178 | } 179 | }, 180 | "regexp-clone": { 181 | "version": "0.0.1", 182 | "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-0.0.1.tgz", 183 | "integrity": "sha1-p8LgmJH9vzj7sQ03b7cwA+aKxYk=" 184 | }, 185 | "require_optional": { 186 | "version": "1.0.1", 187 | "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", 188 | "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", 189 | "requires": { 190 | "resolve-from": "2.0.0", 191 | "semver": "5.4.1" 192 | } 193 | }, 194 | "resolve-from": { 195 | "version": "2.0.0", 196 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", 197 | "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" 198 | }, 199 | "safe-buffer": { 200 | "version": "5.1.1", 201 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 202 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 203 | }, 204 | "semver": { 205 | "version": "5.4.1", 206 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", 207 | "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" 208 | }, 209 | "sliced": { 210 | "version": "1.0.1", 211 | "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", 212 | "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" 213 | }, 214 | "string_decoder": { 215 | "version": "1.0.3", 216 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", 217 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", 218 | "requires": { 219 | "safe-buffer": "5.1.1" 220 | } 221 | }, 222 | "util-deprecate": { 223 | "version": "1.0.2", 224 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 225 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 226 | } 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "micro_service_sample_app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "author": "Ahmad Tahani", 7 | "license": "ISC", 8 | "dependencies": { 9 | "mongoose": "^4.3.6" 10 | }, 11 | "devDependencies": { 12 | "bluebird": "^3.5.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # mongo DB 2 | db: 3 | image: mongo:3.4.10 4 | volumes: 5 | - data:/data/db 6 | - backup:/dump 7 | # nodejs app 1 8 | nodejs_app_1: 9 | build: app/ 10 | links: 11 | - db:our_mongodb 12 | restart: always 13 | # nodejs app 2 14 | nodejs_app_2: 15 | build: app/ 16 | links: 17 | - db:our_mongodb 18 | restart: always 19 | # nodejs app 3 20 | nodejs_app_3: 21 | build: app/ 22 | links: 23 | - db:our_mongodb 24 | restart: always 25 | # nodejs app 4 26 | nodejs_app_4: 27 | build: app/ 28 | links: 29 | - db:our_mongodb 30 | restart: always 31 | 32 | # nginx 33 | nginx: 34 | build: nginx/ 35 | ports: 36 | - "80:80" 37 | links: 38 | - nodejs_app_1:app_1 39 | - nodejs_app_2:app_2 40 | - nodejs_app_3:app_3 41 | - nodejs_app_4:app_4 42 | -------------------------------------------------------------------------------- /nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | #set nginx base image 2 | FROM nginx:1.13 3 | MAINTAINER "ahmad.tahani@gmail.com" 4 | 5 | # copy nginx custom configuration file to nginx configs 6 | COPY nginx.conf /etc/nginx/nginx.conf 7 | -------------------------------------------------------------------------------- /nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | 3 | events { worker_connections 2048; } 4 | 5 | http { 6 | 7 | upstream node-app { 8 | least_conn; 9 | server app_1:5000 weight=10 max_fails=3 fail_timeout=30s; 10 | server app_2:5000 weight=10 max_fails=3 fail_timeout=30s; 11 | server app_3:5000 weight=10 max_fails=3 fail_timeout=30s; 12 | server app_4:5000 weight=10 max_fails=3 fail_timeout=30s; 13 | } 14 | 15 | server { 16 | listen 80; 17 | 18 | location / { 19 | proxy_pass http://node-app; 20 | proxy_http_version 1.1; 21 | proxy_set_header Upgrade $http_upgrade; 22 | proxy_set_header Connection 'upgrade'; 23 | proxy_set_header Host $host; 24 | proxy_cache_bypass $http_upgrade; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sample_docker_workflow_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atahani/docker-workflow-sample/bba23b240c65ed76f37a57223835346b9e075ee1/sample_docker_workflow_diagram.png --------------------------------------------------------------------------------