2018-10-21 Cro as a Service?

I ran Oddmuse 6 for a while now from a little shell script. It set up the environment variables, it checked whether the process was already running, it used `nohup` to run `perl6 service.p6`, it saved the PID in a file, and so on. But it didn’t use `cro run`. And I still don’t know how to do it.

I would like to use `cro run` because I feel that’s what I need to do in the future when I run multiple sites in parallel. So let’s try this:

#!/bin/bash
export ODDMUSE_MENU="Home, Changes, About"
export ODDMUSE_QUESTION="Name a colour of the rainbow."
export ODDMUSE_ANSWER="red, orange, yellow, green, blue, indigo, violet"
export ODDMUSE_SECRET="rainbow-unicorn"
export ODDMUSE_HOST="next.oddmuse.org"
export ODDMUSE_PORT=20000

cd $HOME/oddmuse6
cro run

This script doesn’t work. All I see is this:

▶ Starting oddmuse6 (oddmuse6)
🔌 Endpoint HTTP will be at http://localhost:20000/
♻ Restarting oddmuse6 (oddmuse6)
♻ Restarting oddmuse6 (oddmuse6)
♻ Restarting oddmuse6 (oddmuse6)
♻ Restarting oddmuse6 (oddmuse6)
♻ Restarting oddmuse6 (oddmuse6)

If I replace `cro run` with `perl6 service.p6`, it works.

So now I’m back to this:

#!/bin/bash
export ODDMUSE_MENU="Home, Changes, About"
export ODDMUSE_QUESTION="Name a colour of the rainbow."
export ODDMUSE_ANSWER="red, orange, yellow, green, blue, indigo, violet"
export ODDMUSE_SECRET="rainbow-unicorn"
export ODDMUSE_HOST="next.oddmuse.org"
export ODDMUSE_PORT=20000

cd $HOME/oddmuse6
test -f pid && kill $(cat pid)
perl6 service.p6 &
echo $! > pid

Any ideas?

The `service.p6` file is simple:

use Cro::HTTP::Log::File;
use Cro::HTTP::Server;
use Oddmuse::Routes;

my $logs   = open 'access.log'.IO, :w;
my $errors = open 'error.log'.IO,  :w;

my Cro::Service $http = Cro::HTTP::Server.new(
    http => <1.1>,
    host => %*ENV<ODDMUSE_HOST> ||
        die("Missing ODDMUSE_HOST in environment"),
    port => %*ENV<ODDMUSE_PORT> ||
        die("Missing ODDMUSE_PORT in environment"),
    application => routes(),
    after => [Cro::HTTP::Log::File.new(logs => $logs, errors => $errors)]
);
$http.start;
$logs.say: "Listening at http://%*ENV<ODDMUSE_HOST>:%*ENV<ODDMUSE_PORT>";
react {
    whenever signal(SIGINT) {
        $logs.say: "Shutting down...";
        $http.stop;
        done;
    }
    whenever signal(SIGHUP) {
        $logs.say: "Ignoring SIGHUP...";
    }
}

`Oddmuse::Routes` is available from the Oddmuse 6 repo.

the Oddmuse 6 repo

Hm, what could be the problem? Let’s look at the files:

  total 80
  drwxr-xr-x  6 alex alex 4096 Oct 21 11:40 .
  drwxr-xr-x 57 alex alex 4096 Oct 21 11:57 ..
  -rw-r--r--  1 alex alex  205 Oct  7 18:42 .cro.yml~
  -rw-r--r--  1 alex alex  479 Oct  7 19:23 .cro.yml
  -rw-r--r--  1 alex alex   10 Oct  7 18:42 .dockerignore
  -rw-r--r--  1 alex alex   52 Oct  7 18:42 .gitignore
  -rw-r--r--  1 alex alex  221 Oct  7 18:42 Dockerfile
  -rw-r--r--  1 alex alex  471 Oct  7 18:42 META6.json
  -rw-r--r--  1 alex alex  473 Oct  7 18:42 README.md
  -rw-r--r--  1 alex alex   43 Oct 21 11:49 access.log
  drwxr-xr-x  2 alex alex 4096 Oct 17 10:59 css
  -rw-r--r--  1 alex alex    0 Oct 21 11:49 error.log
  drwxr-xr-x  3 alex alex 4096 Oct  7 19:24 lib
  -rw-------  1 alex alex  445 Oct 21 11:46 nohup.out
  -rw-r--r--  1 alex alex    6 Oct 21 11:49 pid
  -rwxr-xr-x  1 alex alex  149 Oct 15 10:43 run.sh~
  -rwxr-xr-x  1 alex alex  318 Oct 15 10:46 run.sh
  -rw-r--r--  1 alex alex  683 Oct 21 11:19 service.p6~
  -rw-r--r--  1 alex alex  779 Oct 21 11:49 service.p6
  drwxr-xr-x  2 alex alex 4096 Oct 10 20:40 templates
  drwxr-xr-x  4 alex alex 4096 Oct 11 08:32 wiki

`error.log` and `access.log` are the obvious culprits! Changes in the log file will case `cro` to restart. So what I’m going to do is move the log files intoa `logs` subdirectory and add that to the `ignore` section in `.cro.yml`.

OK, so that works. I still feel strange using `nohup`, though.

Shell script:

#!/bin/bash
export ODDMUSE_MENU="Home, Changes, About"
export ODDMUSE_QUESTION="Name a colour of the rainbow."
export ODDMUSE_ANSWER="red, orange, yellow, green, blue, indigo, violet"
export ODDMUSE_SECRET="rainbow-unicorn"
export ODDMUSE_HOST="next.oddmuse.org"
export ODDMUSE_PORT=20000

cd $HOME/oddmuse6

# test -f pid && kill $(cat pid)
# perl6 service.p6 &
# echo $! > pid

nohup cro run &

Service:

use Cro::HTTP::Log::File;
use Cro::HTTP::Server;
use Oddmuse::Routes;

my $logs   = open 'logs/access.log'.IO, :w;
my $errors = open 'logs/error.log'.IO,  :w;

my Cro::Service $http = Cro::HTTP::Server.new(
    http => <1.1>,
    host => %*ENV<ODDMUSE_HOST> ||
        die("Missing ODDMUSE_HOST in environment"),
    port => %*ENV<ODDMUSE_PORT> ||
        die("Missing ODDMUSE_PORT in environment"),
    application => routes(),
    after => [Cro::HTTP::Log::File.new(logs => $logs, errors => $errors)]
);
$http.start;
$logs.say: "Listening at http://%*ENV<ODDMUSE_HOST>:%*ENV<ODDMUSE_PORT>";
react {
    whenever signal(SIGINT) {
        $logs.say: "Shutting down...";
        $http.stop;
        done;
    }
    whenever signal(SIGHUP) {
        $logs.say: "Ignoring SIGHUP...";
    }
}

Me ignoring `SIGHUP` in the service now has no effect because `cro` doesn’t ignore `SIGHUP` which is why the `nohup` wrapper is needed.

OK, so now my shell script starts `nohup cro run &` because `cro` doesn’t daemonize itself. I guess I still find that surprising. And I wonder about the trade-offs. So the benefit is automatic restarts? I also see now that using `cro` run gives me two `moar` processes (one runs `cro`, the other runs my process, `moar` being the virtual machine this Perl 6 runs on).

If there’s nothing else that I’m missing, perhaps running the service using perl6 directly instead of via `cro` isn’t such a bad idea after all. I think I’ll do that.

​#Oddmuse ​#Perl 6 ​#Cro

Comments

(Please contact me if you want to remove your comment.)

As a user I’m expecting a working systemd file provided. Systemd can solve some of these problems and more.

– AlexDaniel 2018-10-22 12:29 UTC

---

If you have more information for what you’d need, I’d love to hear it. Sadly, the only `systemd` thing I ever did was my backup solution...

– Alex Schroeder 2018-10-22 12:35 UTC

---

Thanks for pointing me at this gist.

this gist

NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=read-only
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictRealtime=yes
PrivateTmp=yes
PrivateDevices=yes
PrivateUsers=yes

Other things you mentioned in IRC:

MemoryMax and TasksMax should be used. Restart=always and RestartSec=2 is fine. Watchdog needs to be implemented.

– Alex Schroeder 2018-10-22 12:47 UTC