Deploying Perl5 Applications by Sparrowdo

melezhik

Alexey Melezhik

Posted on November 14, 2017

Deploying Perl5 Applications by Sparrowdo

Sparrowdo - is a configuration management tool written on Perl6. It's an efficient tool to automate servers deployments. Have you written a Perl5 application? Don't bother yourself with finding proper deployment tool. Sparrowdo to the rescue!

In the rest of the post I am going to give a step by step instruction on how to use Sparrowdo to deploy Mojolicious application with a source code fetched from Git. What we're going to do is:

  • Fetch the source code from GitHub repository.
  • Install CPAN dependencies by using Carton/cpanfile.
  • Keep all the files and run the processes under dedicated system user.
  • Set up systemd script to run web application as a daemon.
  • Set up a file with passwords to be used in application's digest access authentication.

Are you ready? Off we go.

Sparrowdo Install

Sparrowdo is Perl6 module so is installed by zef manager:

$ zef install Sparrowdo

Enter fullscreen mode Exit fullscreen mode

Once Sparrowdo is installed we should start writing Sparrowdo scenario, let's create a file named sparrowfile, located in the current working directory:

$ nano sparrowfile
Enter fullscreen mode Exit fullscreen mode

We run sparrowdo scenario remotely on the target host like this:

$ sparrowdo --host=$remote-host

Enter fullscreen mode Exit fullscreen mode

Sparrowdo console client supports many options, follow documentation for details.

The minimum of options is a remote host being deployed or --localhost_mode if we run deploy at the localhost.

Application source code

This is a simple Mojolicios application with a single endpoint which if a user is successfully authenticated returns "hello world":

#!/usr/bin/perl

use Mojolicious::Lite;
use strict;

plugin 'digest_auth', 
  realm   => 'app',
  expires => 120,
  allow   => '.htdigest'
;

get '/' => sub {
  my $c = shift;
  return unless $c->digest_auth;
  $c->render( text => 'hello world' );
};

app->start;

Enter fullscreen mode Exit fullscreen mode

Dedicated system user

We're going to keep all the data and run our application under a dedicated system user, so let's create it:

user "web-application";
Enter fullscreen mode Exit fullscreen mode

Fetching the source code from SCM

It is simple just add this:

git-scm 'git@github.com:melezhik/my-app.git', %( 
  to   => '/home/web-application/projects', 
  user => "web-application"
);
Enter fullscreen mode Exit fullscreen mode

To make this work we also need to create a directory ~/projects, so let's add this line beforehand:

directory "/home/web-application/projects", %(
    owner => "web-application",
    group => "web-application"
)
Enter fullscreen mode Exit fullscreen mode

Installing dependencies

All the dependencies are declared via cpanfile:

$ cat cpanfile
requires 'Mojolicious::Lite';
requires 'Mojolicious::Plugin::DigestAuth';

Enter fullscreen mode Exit fullscreen mode

So just run a carton installer:

bash "cd /home/web-application/projects/my-app && carton", %(
  description => "update cpan dependencies by carton",
  user => "web-application"
);
Enter fullscreen mode Exit fullscreen mode

To install all declared CPAN dependencies into ./local directory.

Populating a file with passwords

We use digest access authentication in our application. This, first of all we need to create a local file with credentials:

$ htdigest -c .htdigest $realm $username
Enter fullscreen mode Exit fullscreen mode

Where $real - authentication realm is specific for an application, and the real command to create a login/password entry for login user would be:

$ htdigest -c .htdigest app user # let's set password as `1`
Enter fullscreen mode Exit fullscreen mode

Don't forget to "hide" .htdigest from Git, so not to commit it occasionally:

$ echo .htdigest >> .gitignore
Enter fullscreen mode Exit fullscreen mode

All is left to do is to populate password file to remote server by sparrowdo,
we also have to store the file under the application root:

file '/home/web-application/projects/.htdigest', %( 
  content => slurp '.htdigest', 
  owner => 'web-application', 
  group => 'web-application' 
);
Enter fullscreen mode Exit fullscreen mode

We are almost all set, the last thing we need to is set up systemd service

Set up systemd script and run application service

With sparrowdo it's just as simple as adding few lines of code:

systemd-service "my-app", %(
  user => "my-app",
  workdir => "/home/web-application/projects/my-app",
  command => "/bin/bash --login -c 'cd /home/web-application/projects/my-app && carton exec ./app.pl daemon --listen http://0.0.0.0:3000'"
);

service-restart "my-app";
Enter fullscreen mode Exit fullscreen mode

This code ensures that our application will be accessible as 0.0.0.0:3000.

Let's give it a run

Here is an example of sparrowdo report for the localhost deployment:

$ sparrowdo --local_mode  --format=production

Enter fullscreen mode Exit fullscreen mode

The output:

running sparrow tasks on 127.0.0.1 ... 
target OS is - ubuntu
push [task] create user web-application OK
push [task] create directory /home/web-application/projects OK
push [task] fetch from git source: https://github.com/melezhik ... OK
push [task] create file /home/web-application/projects/my-app/.htdigest OK
push [task] update cpan dependencies by carton ... OK
push [task] create template /etc/systemd/system/my-app.service OK
push [task] restart service my-app OK
SPL file /opt/sparrow/sparrow.list is empty
get index updates from SparrowHub ... OK
set up task box file - /home/melezhik/.sparrowdo//opt/sparrow/task-box.json - OK
public@user is uptodate (0.2.1)
public@directory is uptodate (0.1.5)
public@bash is uptodate (0.1.7)
public@file is uptodate (0.0.6)
public@templater is uptodate (0.0.11)
Installing modules using /opt/sparrow/plugins/public/templater/cpanfile
Complete! Modules were installed into /opt/sparrow/plugins/public/templater/local
public@service is uptodate (0.1.15)
running task box from /opt/sparrow/sparrow-cache/task-box.json ... 
2017-11-14 16:11:03 : [task] create user web-application [path] modules/change/
2017-11-14 16:11:03 : [task] create directory /home/web-application/projects [path] modules/create/
2017-11-14 16:11:03 : [task] fetch from git source: https://github.com/melezhik ... [path] modules/bash-command/ [params] envvars:
2017-11-14 16:11:04 : [task] create file /home/web-application/projects/my-app/.htdigest [path] /
2017-11-14 16:11:05 : [task] update cpan dependencies by carton ... [path] modules/bash-command/ [params] envvars:
2017-11-14 16:11:05 : [task] create template /etc/systemd/system/my-app.service [path] modules/generate-content/
2017-11-14 16:11:05 : [task] create template /etc/systemd/system/my-app.service [path] /
2017-11-14 16:11:06 : [task] restart service my-app [path] modules/stop/ [params] os:debian service:my-app
2017-11-14 16:11:06 : [task] restart service my-app [path] modules/start/ [params] os:debian service:my-app
Enter fullscreen mode Exit fullscreen mode

Checks

Now lets check our web app, it should be visible as process:

$ sudo ps aux | grep  web-application
web-app+ 29642  0.0  0.0  17808  7180 ?        Ss   16:11   0:00 /bin/bash --login -c cd /home/web-application/projects/my-app && carton exec ./app.pl daemon --listen http://0.0.0.0:3000
Enter fullscreen mode Exit fullscreen mode

And is accessible by http:

$ curl --digest -uuser:1 127.0.0.1:3000
hello world
Enter fullscreen mode Exit fullscreen mode

Thank you for reading. I hope this post was useful.

💖 💪 🙅 🚩
melezhik
Alexey Melezhik

Posted on November 14, 2017

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related