Let's get to automating! This video will show you how to use NodeJS to listen for GitHub webhooks, and kick off an automated (zero-downtime!) deployment script when we push to our master branch.We'll setup an SSH keypair and install NodeJS so that the server can listen for web hooks and can push/pull to and from the project repository.
Deploy Hook
On the production server, generate an SSL certificate for the deploy user, and add the resulting public key to Github as a Deploy hook.
Create id_rsa
if not exists, else use id_rsa.pub
:
cd ~/.ssh
ssh-keygen -t rsa -b 4096 -f id_rsa
Paste the contents of the id_rsa.pub
file into the Github repository as a deploy hook within the repository settings.
Web Listener
We need a web listener listening for Github webhooks. Anything that can listen for web hooks will do, but I like NodeJS because small, can listen over HTTP without need of being proxied to from a web server (Nginx, Apache, etc), and can run shell scripts in a solid fashion.
Here's a Node.JS version of a webhook listener!
Install Node.JS
curl -sL https://deb.nodesource.com/setup | sudo bash -
sudo apt-get install -y nodejs
Create Listener
Within our "production" server:
# Log in as user fideloper for sudo
# but use our deployer user's home directory
# Run commands as user "deployer"
sudo -u deployer mkdir /home/deployer/deploy
cd /home/deployer/deploy
sudo -u deployer npm init
sudo -u deployer npm install --save githubhook
Use githubhook
library at /home/deployer/deploy/server.js
var execFile = require('child_process').execFile;
var githubhook = require('githubhook');
var github = githubhook({
host: '0.0.0.0',
port: 3420,
path: '/gitback',
secret: 'my-secret',
logger: console,
});
github.listen();
github.on('deploy-ex:refs/heads/master', function (data) {
// Exec a shell script
var execOptions = {
maxBuffer: 1024 * 1024 // Increase max buffer to 1mb
};
execFile('/home/deployer/deploy/deploy.sh', execOptions, function(error, stdout, stderr) {
if( error )
{
console.log(error)
}
});
});
Within GitHub repository settings, we'll need to add the Webhook settings to point to our production server on the correct port. Add the secret used in the NodeJS script as well:
http://104.236.85.162:3420/gitback
my-secret
Create Deploy Shell Script
This will do basically what we configured Envoy to do, but on the server as a shell script at /home/deployer/deploy/deploy.sh
:
#!/usr/bin/env bash
REPO='git@github.com:Servers-for-Hackers/deploy-ex.git';
RELEASE_DIR='/var/www/releases';
APP_DIR='/var/www/app';
RELEASE="release_`date +%Y%m%d%H%M%s`";
# Fetch Latest Code
[ -d $RELEASE_DIR ] || mkdir $RELEASE_DIR;
cd $RELEASE_DIR;
git clone -b master $REPO $RELEASE;
# Composer
cd $RELEASE_DIR/$RELEASE;
composer install --prefer-dist --no-scripts;
php artisan clear-compiled --env=production;
php artisan optimize --env=production;
# Symlinks
ln -nfs $RELEASE_DIR/$RELEASE $APP_DIR;
chgrp -h www-data $APP_DIR;
## Env File
cd $RELEASE_DIR/$RELEASE;
ln -nfs ../../.env .env;
chgrp -h www-data .env;
## Logs
rm -r $RELEASE_DIR/$RELEASE/storage/logs;
cd $RELEASE_DIR/$RELEASE/storage;
ln -nfs ../../../logs logs;
chgrp -h www-data logs;
## Update Currente Site
ln -nfs $RELEASE_DIR/$RELEASE $APP_DIR;
chgrp -h www-data $APP_DIR;
## PHP
sudo service php5-fpm reload;
Test It
We'll update our code with a new route and then push that to the master branch. If the /deployed
page works, then our automated deployment was a success!
Ensure the NodeJS listener is active:
node /home/deployer/deploy/server.js
Then we add a route to the framework's routes.php
file:
Route::get('/deployed', function() {
return "New Route!";
});
Monitoring NodeJS Listener
We can't leave NodeJS to its own devices - we should install something to make sure it's always up and running.
The video uses Debian 7 with Systemd enabled/installed. For systems with Systemd, we can add a unit file (service) to monitor the NodeJS listener.
Systemd
Add the following service configuration to file /lib/systemd/system/deploy.service
:
[Unit]
Description=Webhook
[Service]
User=deployer
Group=www-data
Restart=on-failure
ExecStart=/usr/bin/node /home/deployer/deploy/server.js`
[Install]
WantedBy=multi-user.target
Then run:
# Enable service unit file
systemctl enable deploy.service
# Check its status and start it
sudo service deploy status
sudo service deploy start
Upstart
On Ubuntu, or any system using Upstart, we can add the following configuration and use that instead:
Add this configuration to file /etc/init/deploy.conf
:
description "NodeJS Webhook Listener"
start on filesystem or runlevel [2345]
stop on runlevel [!2345]
setuid deployer
setgid www-data
respawn
respawn limit 5 2
script
/usr/bin/node /home/deployer/deploy/server.js`
end script
Then get its status and start it:
sudo service deploy status
sudo service deploy start
Firewalls
Lastly we need to ensure Git can send webhooks to our listen. Configure it to accept connections on port 3420:
# Place rule at the 4th position of the INPUT chain
# This assumes the 4th position is before any rule
# potentially dropping tcp traffic over port 3420
sudo iptables -I INPUT 4 -p tcp --dport 3420 -j ACCEPT