dylanlott
2/21/2017 - 2:17 AM

Deploy Node.js Apps with Flightplan

Deploy Node.js Apps with Flightplan

Setup your server

Add a Passwordless Deploy user

(wherever it says url.com, use your server's domain or IP)

Login to new server as root, then add a deploy user

sudo useradd --create-home -s /bin/bash deploy
sudo adduser deploy sudo
sudo passwd deploy

And Update the new password

Now login as that user

ssh deploy@url.com

Make directory .ssh on the remote server and log out

mkdir .ssh
exit

Push your ssh key to the authorized_keys file on the remote server

scp ~/.ssh/id_rsa.pub deploy@url.com:~/.ssh/authorized_keys

Note: you might have to manually copy your id_pub.rsa file from your local machine to your vps. Remote copying has been finnicky for me before, but generally works.

Install flightplan

  • npm install -g flightplan
  • in your project folder npm install flightplan --save-dev
  • create a flightplan.js file

flightplan.js

var plan = require('flightplan');
var config = require('./config); //keep sensitive info in a config file that isn't checked into version control.
/**
 * Remote configuration for "production"
 */
plan.target('production', {
  host: config.SSH_HOST, //should be a string
  username: config.SSH_USER, //should also be string
  agent: process.env.SSH_AUTH_SOCK, //this actually needs to be the env variable

  webRoot: '/var/www/testapp.com', // points to where NGINX is serving your static files.
  ownerUser: 'root',
  repository: 'https://github.com/yourgithub/testapp.git',
  branchName: 'master',
  maxDeploys: 10
});

plan.target('dev', {
    host: config.SSH_HOST,
  username: config.SSH_USER,
  agent: process.env.SSH_AUTH_SOCK,

    webRoot: '/var/www/dev.testapp.com/client',
  ownerUser: 'root',
  repository: 'https://github.com/yourgithub/testapp.git',
  branchName: 'master',
  maxDeploys: 10
});

plan.remote('setup', function(remote) {
  remote.hostname();
  remote.sudo('mkdir -p ' + remote.runtime.webRoot);
  remote.with('cd ' + remote.runtime.webRoot, function() {
    remote.sudo('git clone -b ' + remote.runtime.branchName + ' ' + remote.runtime.repository + ' .');
    remote.log('GitHub repo successfully cloned.');
    remote.sudo('npm install -g yarn');
    remote.log('Yarn installed successfully.');
    remote.sudo('yarn');
    remote.log('Environment setup correctly.');
  });
});

plan.local('deploy', function(local) {
    local.hostname();
  local.log('Run build');
  // local.exec('npm run build');

  local.log('Copy files to remote hosts');
  var filesToCopy = local.exec('git ls-files', {silent: true});
  local.transfer(filesToCopy, '/var/www/dev.testapp.com');
});

plan.remote('deploy', function(remote) {
  remote.hostname();

  remote.with('cd ' + remote.runtime.webRoot, function() {
    remote.exec('pwd');
    remote.log('remote work beginning.');
    remote.exec('npm install');
    remote.log('npm install finished');
    remote.log('running npm build');
    remote.exec('npm run build');
    remote.log('Build successful');
  })
})

Using the flightplan

Run fly from the command line in the root of your project.

fly deploy:dev
fly deploy:production
fly setup:dev 
fly setup:production 
  • Note: If you run into an error "no tty present and no askpass program specified", check these out:

From the flightplan docs:

Scenario: 'pstadler' is the user for connecting to the host and 'www' is the user under which you want to execute commands with sudo.

  1. 'pstadler' has to be in the sudo group:
$ groups pstadler
pstadler : pstadler sudo
  1. 'pstadler' needs to be able to run sudo -u 'www' without a password. In order to do this, add the following line to /etc/sudoers:
pstadler ALL=(www) NOPASSWD: ALL
  1. user 'www' needs to have a login shell (e.g. bash, sh, zsh, ...)
$ cat /etc/passwd | grep www
www:x:1002:1002::/home/www:/bin/bash   # GOOD
www:x:1002:1002::/home/www:/bin/false  # BAD

You can get some more info on editing your /etc/sudoers file here. Edit this file very carefully as messing it up can have serious consequences.

Adding new environments and projects

You can easily add a new environment or project using this as a structure. This is a pretty extensible deployment setup that you can maintain with minimal effort.

For example, just add a new target and you can run setup on it and it will provision you a new instance of your app.

plan.target('test', {
    host: config.SSH_HOST,
  username: config.SSH_USER,
  agent: process.env.SSH_AUTH_SOCK,

    webRoot: '/var/www/test.testapp.com',
  ownerUser: 'root',
  repository: 'https://github.com/yourgithub/testapp.git',
  branchName: 'master',
  maxDeploys: 10
});

The flightplan.js npm

Other resources used

http://usersnap.com/blog/deploying-static-websites-flightplan/

https://johnmunsch.com/2015/03/08/shipit-vs-flightplan-for-automated-administration/

https://coderwall.com/p/l1dzfw/flightplan-deploy-like-a-boss

For setting up Nginx

https://geekflare.com/nginx-static-files-node-js/

Stretch Goals / Where you could take this

  • Integrate Docker and auto-build / pull containers with plan.remote()
  • Create a test suite and only push to prod if tests pass
  • Add a CI/CD