├── scripts
├── run
├── rebuild
├── shell
├── open
└── README.md
├── Procfile
├── Dockerfile
├── app.json
├── test
└── index.js
├── package.json
├── circle.yml
├── app.js
├── docker-compose.yml
├── LICENSE
└── README.md
/scripts/run:
--------------------------------------------------------------------------------
1 | docker-compose up web
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: npm install; node app.js
2 |
--------------------------------------------------------------------------------
/scripts/rebuild:
--------------------------------------------------------------------------------
1 | docker-compose build
2 |
--------------------------------------------------------------------------------
/scripts/shell:
--------------------------------------------------------------------------------
1 | docker-compose run --service-ports web bash
2 |
--------------------------------------------------------------------------------
/scripts/open:
--------------------------------------------------------------------------------
1 | open "http://$(docker-machine ip default):8080"
2 |
--------------------------------------------------------------------------------
/scripts/README.md:
--------------------------------------------------------------------------------
1 | # scripts
2 | Just some scripts to shorten the docker commands.
3 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM heroku/nodejs
2 | RUN apt-get update && apt-get install -y postgresql-client-9.3
3 |
4 |
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nodejs-heroku-docker",
3 | "description": "A simple docker heroku example for nodejs",
4 | "image": "heroku/nodejs",
5 | "addons": []
6 | }
7 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | var assert = require("assert");
2 |
3 | describe("heroku-docker-nodejs", function() {
4 | it("should pass test :)", function() {
5 | assert(true);
6 | });
7 | });
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "heroku-docker-nodejs",
3 | "private": true,
4 | "version": "0.0.0",
5 | "dependencies": {
6 | "express": "^4.13.3",
7 | "mocha": "^2.3.3"
8 | },
9 | "scripts": {
10 | "test": "mocha test/index.js"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | services:
3 | - docker
4 |
5 | dependencies:
6 | pre:
7 | - sudo pip install --upgrade docker-compose==1.3.0
8 | override:
9 | - docker-compose build
10 |
11 | test:
12 | override:
13 | - docker-compose run --service-ports web npm test
14 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | var express = require('express');
2 | var app = express();
3 |
4 | app.get('/', function (req, res) {
5 | res.send("Hello from container land!");
6 | });
7 |
8 | var server = app.listen(process.env.PORT, function () {
9 | var port = server.address().port;
10 | console.log('Example app listening at http://localhost:%s', port);
11 | });
12 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | web:
2 | build: .
3 | command: 'bash -c ''npm install; node app.js'''
4 | working_dir: /app/user
5 | volumes:
6 | - ./:/app/user/
7 | - /app/user/node_modules
8 | environment:
9 | PORT: 8080
10 | DATABASE_URL: 'postgres://postgres:@herokuPostgresql:5432/postgres'
11 | ports:
12 | - '8080:8080'
13 | links:
14 | - herokuPostgresql
15 | shell:
16 | build: .
17 | command: bash
18 | working_dir: /app/user
19 | environment:
20 | PORT: 8080
21 | DATABASE_URL: 'postgres://postgres:@herokuPostgresql:5432/postgres'
22 | ports:
23 | - '8080:8080'
24 | links:
25 | - herokuPostgresql
26 | volumes:
27 | - '.:/app/user'
28 | herokuPostgresql:
29 | image: postgres
30 |
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jamie Blair
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # heroku-docker-nodejs
2 | A simple heroku-docker example for [nodejs](https://nodejs.org) with development, CI and deployment instructions
3 |
4 | This README acts as a quick start guide to setting up and using heroku-docker, the repo itself contains the final result.
5 |
6 | [][circleci]
7 | [][dm-prod]
8 | [][dm-dev]
9 |
10 | [circleci]: https://circleci.com/gh/orangemug/heroku-docker-nodejs
11 | [dm-prod]: https://david-dm.org/orangemug/heroku-docker-nodejs
12 | [dm-dev]: https://david-dm.org/orangemug/heroku-docker-nodejs#info=devDependencies
13 |
14 |
15 | ## Prerequisites
16 | Install some deps
17 |
18 | * [docker-compose](https://docs.docker.com/compose/install)
19 | * [heroku toolbelt](https://toolbelt.heroku.com/)
20 |
21 | Via the CLI, install the `heroku-docker` plugin
22 |
23 | heroku plugins:install heroku-docker
24 |
25 | We'll also assume you'll have this code in a repo on [github](http://github.com)
26 |
27 |
28 | ## Setup
29 | Create a `package.json`
30 |
31 | {
32 | "name": "heroku-docker-nodejs",
33 | "private": true,
34 | "version": "0.0.0",
35 | "dependencies": {
36 | "express": "^4.13.3"
37 | }
38 | }
39 |
40 | ...an `app.js`
41 |
42 | var express = require('express');
43 | var app = express();
44 |
45 | app.get('/', function (req, res) {
46 | res.send("Hello from container land!");
47 | });
48 |
49 | var server = app.listen(process.env.PORT, function () {
50 | var port = server.address().port;
51 | console.log('Example app listening at http:/localhost:%s', port);
52 | });
53 |
54 | ...an app.json describing your app
55 |
56 | {
57 | "name": "nodejs-heroku-docker",
58 | "description": "A simple docker heroku example for nodejs",
59 | "image": "heroku/nodejs",
60 | "addons": []
61 | }
62 |
63 | ...and a `Procfile` to start our app with[^](https://ddollar.github.io/foreman)
64 |
65 | web: npm install; node app.js
66 |
67 | Now lets get the heroku toolbelt to generate us some heroku config
68 |
69 | $ heroku docker:init
70 | Wrote Dockerfile
71 | Wrote docker-compose.yml
72 |
73 | This will have created 2 files which _docker-compose_ uses to create and run your containers. Now go and add some additional bits to your `docker-compose.yml`, this will allow you to share files between the host and the container during dev.
74 |
75 | web:
76 | volumes:
77 | - ./:/app/user/
78 | - /app/user/node_modules
79 |
80 | Then run
81 |
82 | $ docker-compose build
83 |
84 | Note if the `Dockerfile` has changed then you'll have to rerun the above command, to rebuild the container
85 |
86 |
87 | ## Development
88 | When running the app in development the main requirement is quick restarts of your app. So instead of reloading the VM each time, we'll start a bash shell in the container and run the app manually.
89 |
90 | $ docker-compose run --service-ports web bash
91 |
92 | The important part above is `docker-compose run web` which will run a command in our docker container. `--service-ports` tells _docker-compose_ that we want to setup the port mappings. You should now be seeing a bash prompt where you can start your app.
93 |
94 | root@52697b69237b:/code# npm install
95 | root@52697b69237b:/code# npm start
96 |
97 | Open another terminal and run the following to hit the web server from a browser
98 |
99 | $ open "http://$(docker-machine ip default):8080"
100 |
101 |
102 | ## Deployment
103 | To deploy the VM to production first off create an app on heroku
104 |
105 | heroku create [optional name]
106 |
107 | Now instead of running `git push heroku` as you'd normally do with heroku, we instead run
108 |
109 | heroku docker:release
110 |
111 | As normal you can now open the app in your browser
112 |
113 | heroku open
114 |
115 |
116 | ## Testing
117 | Next up lets setup some CI with [circleci](http://circleci.com), first off [create an account](https://circleci.com/signup) if you haven't already got one.
118 |
119 | Create a simple test (ok it doesn't actually test anything but you'll get the idea)
120 |
121 | // ./test/index.js
122 | var assert = require("assert");
123 |
124 | describe("heroku-docker-nodejs", function() {
125 | it("should pass test :)", function() {
126 | assert(true);
127 | });
128 | });
129 |
130 | Add the test script and `mocha` dependency to your `package.json`
131 |
132 | {
133 | "dependencies": {
134 | "mocha": "^2.3.3"
135 | },
136 | "scripts": {
137 | "test": "mocha test/index.js"
138 | }
139 | }
140 |
141 | Next up add a `circle.yml` to the base of the repo and _push this to github_
142 |
143 | machine:
144 | services:
145 | - docker
146 |
147 | dependencies:
148 | pre:
149 | - sudo pip install --upgrade docker-compose==1.3.0
150 | override:
151 | - docker-compose build
152 |
153 | test:
154 | override:
155 | - docker-compose run --service-ports web npm test
156 |
157 | Then [add the project](https://circleci.com/add-projects) in circleci and you should very shortly see passing test :)
158 |
159 |
160 | ## Add ons
161 | We'll add postgres as an example here as it's probably the most common addon for heroku. First off add the following to your `docker-compose.yml`
162 |
163 | web:
164 | environment:
165 | DATABASE_URL: 'postgres://postgres:@herokuPostgresql:5432/postgres'
166 | links:
167 | - herokuPostgresql
168 | shell:
169 | environment:
170 | DATABASE_URL: 'postgres://postgres:@herokuPostgresql:5432/postgres'
171 | links:
172 | - herokuPostgresql
173 | # This defines a new service called herokuPostgresql
174 | herokuPostgresql:
175 | image: postgres
176 |
177 | `herokuPostgresql` defines a new service which uses the [postgres image from dockerhub](https://hub.docker.com/_/postgres/), this is linked from the web/shell services. When you setup a link the service will be added to `/etc/hosts`
178 |
179 | So the host `herokuPostgresql` will point to our postgres server running in another container, cool huh!
180 |
181 | Now we'll add `psql` so we can connect to postgres from within our `web` service. To do this append the following to the `Dockerfile`
182 |
183 | RUN apt-get update && apt-get install -y postgresql-client-9.3
184 |
185 | Now rebuild the image
186 |
187 | docker-compose build
188 |
189 | We can now start a shell and test that we can connect to postgres.
190 |
191 | $ docker-compose run --service-ports web bash
192 | $ psql $DATABASE_URL
193 |
194 | Note: The `DATABASE_URL` was defined in the `docker-compose-yml` above.
195 |
196 | You should now be connected to the postgres server! If you want to know more about the postgres docker image see
197 |
198 |
199 | ## Extras
200 | I've also added a few scripts (just shorthands really) to the [./scripts](./scripts) directory
201 |
202 | * [shell](./scripts/shell) - to run in development
203 | * [open](./scripts/open) - to open in a browser
204 | * [rebuild](./scripts/rebuild) - rebuild the container
205 | * [run](./scripts/run) - to run in a production like way
206 |
207 |
208 | ## References
209 |
210 | *
211 | *
212 | *
213 | *
214 |
215 |
216 | ## Thanks!
217 | To [samgiles](https://github.com/samgiles) and [oliverbrooks](https://github.com/oliverbrooks) for the pointers
218 |
219 |
220 | ## License
221 | [MIT](LICENSE)
222 |
--------------------------------------------------------------------------------