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
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
We run sparrowdo scenario remotely on the target host like this:
$ sparrowdo --host=$remote-host
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;
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";
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"
);
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"
)
Installing dependencies
All the dependencies are declared via cpanfile:
$ cat cpanfile
requires 'Mojolicious::Lite';
requires 'Mojolicious::Plugin::DigestAuth';
So just run a carton installer:
bash "cd /home/web-application/projects/my-app && carton", %(
description => "update cpan dependencies by carton",
user => "web-application"
);
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
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`
Don't forget to "hide" .htdigest
from Git, so not to commit it occasionally:
$ echo .htdigest >> .gitignore
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'
);
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";
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
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
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
And is accessible by http:
$ curl --digest -uuser:1 127.0.0.1:3000
hello world
Thank you for reading. I hope this post was useful.