Deploy a user-defined node.js application

Deploy a user-defined node.js application

This is an example
juju
charm to deploy a user-defined node app
directly from revision control.

This charm will be maintained with a general set of hooks
for various services that can be used with node apps
(like mongodb).

Using this charm

First, edit config.yaml to add info about your app.

Then deploy some basic services

juju deploy node-app myapp
juju deploy mongodb
juju deploy haproxy

relate them

juju add-relation mongodb myapp
juju add-relation myapp haproxy

scale up your app (to 10 nodes for example)

juju add-unit -n 10 myapp

open it up to the outside world

juju expose haproxy

Find the haproxy instance's public URL from

juju status

(or attach it to an elastic IP via the aws console)
and open it up in a browser.

What the formula does

During the install hook,

  • installs node/npm
  • clones your node app from the repo specified in app_repo
  • runs npm if your app contains package.json
  • configures networking if your app contains config/config.js
  • waits to be joined to a mongodb service

when related to a mongodb service, the formula

  • configures db access if your app contains config/config.js
  • starts your node app as a service

Charm configuration

Configurable aspects of the charm are listed in config.yaml
and can be set by either editing the default values directly
in the yaml file or passing a myapp.yaml configuration
file during deployment

juju deploy --config ~/myapp.yaml node-app myapp

Some of these parameters are used directly by the charm,
and some are passed through to the node app using config/config.js.

Application configuration

The formula looks for config/config.js in your app which
starts off looking something like this

module.exports = config = {
  "name" : "mynodeapp"
  ,"listen_port" : 8000
  ,"mongo_host" : "localhost"
  ,"mongo_port" : 27017
}

and gets modified with contextually correct configuration information during
either deployment (via the install hook) or relation to another service
(relation-changed hook).

This config can be used from within
your application using snippets like

...
var config = require('./config/config')
...
  new mongo.Server(config.mongo_host, config.mongo_port, {}),
...
server.listen(config.listen_port);
...

Alternatively you could use a "Procfile" in root directory like this:

web: node app.js

and then get the environment variables from the running environment like this:

app.set('port', process.env.PORT);

The defined environment variables are:

NAME
PORT
NODE_ENV
MONGO_HOST
MONGO_PORT
MONGO_REPLSET

Network access

This charm does not open any public ports itself.
The intention is to relate it to a proxy service like
haproxy, which will in turn open port 80 to the outside world.
This allows for instant horizontal scalability.

If your node app is itself a proxy and you want it directly exposed,
this can easily be done by adding

open-port $app_port

to the bottom of the install hook, and then once your stack
is started, you expose

juju expose myapp

it to the outside world.

By default, juju services within the same environment
can talk to each other on any port over
internal network interfaces.

Making this work with your node.js app

This charm makes some strong assumptions
about the structure of the node application
(config/config.js) that might not apply to your app.
Please treat this formula as a template that
you can fork and modify to suit your needs.

The biggest difference between how the charm
behaves for different kind of apps is application
startup. A simple application will want to start
upon install (startup code goes in the install hook),
whereas some applications will not want
to start up until a database has be associated
(startup code goes in the db-relation-joined hooks).

Mirrored

lp:charm/node-app

Configuration

app_url
(string)
                            Application repository URL
                        
http://github.com/mmm/testnode.git
app_scm
(string)
                            Repository type (git/bzr/...)
                        
git
app_name
(string)
                            Application Name
                        
myapp
install_root
(string)
                            Install the application in app_name under this directory
                        
/opt
app_node_env
(string)
                            The node environment to run as ("production", "development", "test" etc.).
                        
production
polling_schedule
(string)
                            Set to a cron schedule string (e.g. "*/5 * * * *" poll each 5 minutes) for polling repo for updates and deploy if new branch contents is found. Set to empty to disable polling.
                        
app_port
(int)
                            Port where the application will handle requests
                        
8000
app_branch
(string)
                            Application repository deployment branch. Might also be a revision ID (e.g. tag or SHA1 for git) to upgrade/downgrade application.
                        
master
app_user
(string)
                            run-as user
                        
ubuntu