Deployment
This chapter describes how to put in place a mechanism to deploy the app into a remote server.
We will use ShipIt to have a really easy and fast way to have deployments.
Project Configuration
We need to configure shipit in the projects
Backend
Make sure that package.json file has a repository url property. If https then it will ask your passwd on each release, twice :(, otherwise use ssh access.
Example
...
"repository": {
"type": "git",
"url": "[email protected]:MiOrg/myproject.git"
},
...
Shipit config
Lets create a script to run deploys easily, in the same file, under
"deploy": "shipit staging deploy"
Now install shipit packages
npm install --save-dev shipit-cli shipit-deploy shipit-npm shipit-pm2
Create a file named shipitfile.js with the following content
var pkg = require('./package.json');
module.exports = function (shipit) {
require('shipit-deploy')(shipit);
require('shipit-npm')(shipit);
require('shipit-pm2')(shipit);
var config = require('./shipitfile.config.json')
shipit.initConfig({
default: {
workspace: '/tmp/myproject/backend',
deployTo: '/var/www/myproject/backend',
repositoryUrl: pkg.repository.url,
branch: 'master',
ignores: ['.git'],
keepReleases: 2,
deleteOnRollback: false,
shallowClone: true
},
staging: {
servers: {
host: config.staging.host,
user: config.staging.user,
},
key: config.staging.sshKey
}
});
};
Of course this can be customized, if you would like different folders, or steps. You will need to change
- default.workspace | deployTo to replace "myproject" with the name of your project (if you want better names for the folders on the server where the app is going to be deployed.
Now this file requires a configuration file shipitconfig.config.json
{
"staging": {
"host": "myserver.com",
"user": "ubuntu",
"sshKey": "/blah/bleh/mykey"
}
}
Of course you should update this content. And you probably want to ignore this file from git.
echo "shipitfile.config.json" >> .gitignore
A good practice is to save a template file for this file with the name shipitconfig.config.json.example but without real values. Then your team can copy the file and change it for running the deploy.
PM2 config
We will use PM2 to monitor start/stop/restart our app in the server (we will install it later in the server). But it also makes things simpler later if our app already has a file to describe itself as a pm2 application.
So lets create
vi app.json
With this content
{
"name" : "myproject-backend",
"script" : "index.js",
"cwd" : "/var/www/myproject/backend/current",
"exec_interpreter" : "/var/www/myproject/backend/current/node_modules/.bin/babel-node",
"env": {
"NODE_ENV": "production",
"PORT": 3001
}
}
That's it, commit and push all changes.
We now need to setup the server before we can run the deploy
Frontend
// TODO:
Server Setup
We will need to setup a server instance with all the required software and configuration.
Install Mongodb
Follow instructions for example for Ubuntu 16.04 https://docs.mongodb.com/v3.2/tutorial/install-mongodb-on-ubuntu/
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927
echo "deb http://repo.mongodb.org/apt/ubuntu xenial/mongodb-org/3.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.2.list
sudo apt-get update
sudo apt-get install -y mongodb-org
sudo service mongod start
Install nginx
Follow https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-16-04
sudo apt-get install nginx
Then check with a browser
http://miserver.com
(or whatever IP / hostname you have)
Node
We will use node 6.x following this instructions https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-16-04
curl -sL https://deb.nodesource.com/setup_6.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
sudo apt-get install nodejs
PM 2
Follow the pm2 section from here https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-16-04
sudo npm install -g pm2
Now in order to have the logs rotated every day at 00:00:00 we must install a plugin
pm2 install pm2-logrotate
That's it. More info on how to customize the logs and rotation here
Install Python 2.7
We need python 2.7 for node-gyp (notice that it seems to require 2.7 and not 3.x !!) We will use a prebuilt package as it is the easiest solution.
sudo add-apt-repository ppa:fkrull/deadsnakes
sudo apt-get update
sudo apt-get install python2.7
Configure python in npm (probably it will also work if you create an alias "python" and make sure it is in $PATH)
npm config set python /usr/bin/python2.7
Create Remote folder
We need to create a remote folder for the releases that will be served by nginx
Log to the server
sudo mkdir /var/www/myproject
sudo chown -R ubuntu: /var/www/myproject
(or whatever user you want to use)
Configure Nginx Proxy
We will put nginx on front of the backend module and also serving the frontend.
Our deployment with shipit, if well configured would end up creating a structure like this in the server
/var/www
├── html
│ └── index.nginx-debian.html
└── myproject
├── backend
│ ├── current -> releases/20161110194400
│ └── releases
└── frontend
├── current -> releases/20161110205627
└── releases
Serve Backend
Now we will configure nginx to act as proxy for the backend
sudo vi /etc/nginx/sites-available/default
And add the following location
location /api/ {
proxy_pass http://127.0.0.1:3001/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
(Notice the ending slash in api/ ! otherwise you will lose time :P )
This expects the backend to be in the same host as nginx, and using port 3001 (default).
Now restart nginx
sudo systemctl restart nginx
Now go to a browser http://myserver.com/api/
Serve Frontend
The front end once deployed is just regular static files (js, css, etc). So we only need to configure nginx to serve files from that folder
Again
sudo vi /etc/nginx/sites-available/default
Comment the root directive and add a new one pointing to our front end release folder. Also change the / location to look as here below
#root /var/www/html;
root /var/www/myproject/frontend/current;
location / {
root /var/www/nes/frontend/current/;
try_files $uri $uri/ /index.html;
}
Restart
sudo systemctl restart nginx
And now access http://myserver.com/
(notice the slash at the end !)
Note
Of course nginx can be configured in different ways. For example you could prefer to create specific config file for this virtual server, etc. We just picked one here, the one that seemed the easiest but probably not the better for real production. (anyway, for production it would be better to have a docker based deployment :))
Deploying
That's it, we are ready. Now to deploy lets say the backend we just need to run
npm run deploy