During this meeting Ferenc Erki, the lead developer of Rex taught us to set up and configure his hosts using Rex.

We started from an empty management machine. Installed Rex. Created two Ubuntu-based servers. First we had some basic experimentation with Rex then installed and configured Nginx.

The content

Create Droplet with Ubuntu 20.04 in New York 1. Hostname code-maven-rex

Created ssh keypair on the new machine to be used as a management host. Add the public key to the Digital Ocean admin interface as "Rex" to be used from the management server.

Create 2 Droplets with Ubuntu 20.04 in New York 1 Hostname ubu-1 and ubu-2

Rexfile

During the session we put together a Rexfile with a number of tasks. This is what we got. Whatch the video for explanations.

examples/rex/infra/Rexfile

use Rex -feature => [qw( 1.4 exec_autodie)];

desc 'Just printing hostname';
task 'print_hostname', sub {
    say run('hostname');
};

desc 'Setup nginx';
task setup_nginx => sub {
    pkg 'nginx', ensure => 'present';
};

desc 'Configure new Rex website';
task configure_nginx => sub {
    # copy ubu-1.conf to /etc/nginx/sites-enabled/
    run('rm -f /etc/nginx/sites-enabled/default');
    file '/etc/nginx/sites-enabled/ubu-1.conf',
        source => 'files/ubu-1.conf',
        on_change => sub {
            service 'nginx' => 'reload';
        };

    # copy main.html to /root/rex/index.html
    #file '/root/rex', ensure => 'directory';
    #file '/root/rex/index.html', source => 'files/main.html';

    # copy main.html to /tmp/rex/index.html
    file '/tmp/rex', ensure => 'directory', owner => 'www-data', group => 'www-data', mode => '0755';
    file '/tmp/rex/index.html', source => 'files/main.html';

    # reload nginx
    #service 'nginx' => 'reload';
};

HTML file

examples/rex/infra/files/main.html

<h1>Welcome to Rex</h1>

Nginx config file

examples/rex/infra/files/ubu-1.conf

server {
  listen [::]:80;
  listen 80;
  server_name ubu-1;

  root /tmp/rex;
  location / {
    autoindex on;
  }
}

~/.ssh/config

The SSH config file we created:

examples/rex/infra/config

#Host *
#  StrictHostKeyChecking no

Host ubu2
  Hostname 104.131.169.6

Host ubu1
  Hostname 104.131.167.212

History

This is the history of the shell commands that we used during the meetig. I tried to clean the duplicate entries. I hope having them here makes it easier to reproduce the commands.

examples/rex/infra/history.txt

    1  ssh-keygen
    2  ll .ssh/
    3  cat .ssh/id_rsa.pub
    4  ssh 104.131.169.6
    6  apt-get update
    7  apt-get install rex
    8  rex -v
   10  rex -h | less
   12  rex -H 104.131.169.6 -e 'run( q{hostname} )'
   13  rex -H 104.131.169.6 -e 'run( "hostname" )'
   14  rex -H 104.131.169.6 -e 'run "hostname" '
   15  rex -H 104.131.169.6 -e 'say run "hostname" '
   16  rex -H '104.131.169.6 104.131.167.212' -e 'say run "hostname" '
   17  vim ~/.ssh/config
   18  rex -H 'ubu1 ubu2' -e 'say run "hostname" '
   20  rex -H 'ubu-1 ubu2' -e 'say run "hostname" '
   31  rex -H 'ubu-[1,2]' -e 'say run "hostname" '
   32  rex -H 'ubu-[1..2]' -e 'say run "hostname" '
   33  cat /etc/hosts
   34  mkdir infra
   35  cd infra/
   37  cat ~/.ssh/known_hosts
   42  vim ~/.ssh/config
   49  vim Rexfile
   50  rex -T
   56  apt-cache search nginx | less
   60  rex -H 'ubu1' print_hostname
   78  rex -H 'ubu1' -e 'say run q(ls -l /etc/nginx)'
   79  rex -H 'ubu1' -e 'say for run q(ls -l /etc/nginx)'
   80  rex -H 'ubu1' -e 'say for run qq(ls -l /etc/nginx)'
   81  rex -H 'ubu1' -e 'use Data::Dumper; say [run q(ls -l /etc/nginx)]'
   82  rex -H 'ubu1' -e 'use Data::Dumper; say Dumper [run q(ls -l /etc/nginx)]'
   83  rex -H 'ubu1' -e 'use Data::Dumper; say Dumper [run q(tree /etc/nginx/)]'
   84  rex -H 'ubu1' -e 'use Data::Dumper; say Dumper [run q(ls -l /etc/nginx/sites/enabled)]'
   85  rex -H 'ubu1' -e 'use Data::Dumper; say Dumper [run q(ls -l /etc/nginx/sites-enabled)]'
   87  rex -T
   88  rex -H ubu-1 configure_nginx
   89  curl http://104.131.167.212
   90  curl http://ubu-1
   91  rex -H 'ubu1' -e 'use Data::Dumper; say Dumper [run q(ls -l /etc/nginx/sites-enabled)]'

Links that were mentiond

RexOps - Central Repository for all RexOps projects.

Repology showing which version of Rex is packaged in each Linux distribution.

Rex on MetaCPAN and the list of reverse dependencies, that is CPAN distributions that use Rex.

FErki's answers to questions from the session

What is a meaning of name Rex itself ? :)

Remote execution (see also in the FAQ).

Does each version of Rex have own features ? (about feature flags)

Rex has named feature flags to opt in or out of various behavior based on your exact needs. Versioned feature flags control multiple named feature flags at once, so they can set defaults across the board. That is, the versioned feature flag of 1.4 means "please use all the default from rex-1.4".

A versioned feature flag is only introduced if we think the default behavior should change in some way. In other words, default behavior shouldn't change unless explicitly asked for by specifying a different feature flag.

So better to use cpan Rex ? (after installing with apt from default Ubuntu repositories)

Short answer: Yes, the recommended way to install Rex is from CPAN, because that's the canonical upstream source of releases.

Longer version: It depends on the use case and circumstances. You can get Rex from CPAN, from many different package repositories, or build it from source. Choose the option that fits your use case the best.

Is any issue to use key auth from Windows to Linux machines ?

No, in general no issues.

The important detail is that Rex must use the Net::SSH2 backend for SSH connections on Windows, and that needs different configuration for authentication details. Net::SSH2 is based on libssh2 so the public and private key path need to be specified explicitly.

The other backend, Net::OpenSSH uses the ssh binary, so it doesn't have to rely on Rex configuration, but it doesn't support Windows.

Is there an option to send 'yes' when we run rex? (after being prompted to accept the SSH host key of a remote endpoint)

An important part of the story is that the prompt belongs to SSH, not Rex.

Ideally, the SSH host keys from all the hosts belonging to an infrastructure would be part of the site's inventory. Then the keys would be distributed to those who need to use SSH to connect to these hosts. There's even a dedicated DNS record type to store this information.

Apart from that, there are multiple ways to disable this prompt. Some examples, depending on the security and other requirements you might be looking for:

  • configure SSH to don't check the host key strictly, e.g. specify StrictHostKeyChecking no in ~/.ssh/config or /etc/ssh/ssh_config
  • configure Rex to pass the same option to SSH with Rex::Config->set_openssh_opt( StrictHostKeyChecking => 'no', );

Is it posisble to pass something like ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa

Yes, arbitrary SSH options can be set on the SSH side via ~/.ssh/config or /etc/ssh_ssh_config when Net::OpenSSH is being used.

SSH options (-o) can also be set on the Rex side with Rex::Config->set_openssh_opt();, and identity files (-i) can be set via public_key $path; and private_key $path;

Are there any OS (Linux distributions) dependencies for command like service ?

No explicit dependencies.

Right after connecting to the managed endpoint, Rex discovers which operating system it is supposed to manage, and chooses a service and package manager module accordingly. If systemd is active, it uses that for service management.

So on Ubuntu it might choose upstart and apt, on Gentoo OpenRC and emerge.

is rex IRC channel still active?

The Rex IRC channel is very much alive, yes! Come by and say hi!:)

I’ve never used other common server management software like Ansible or puppet. How does Rex compare?

To get into the right mindset about Rex, maybe the shortest way is to think about it like: "it's possible to implement tools like Ansible and Puppet with Rex, but not the other way around".

In other words, Rex follows a different trade-off by being a framework to build your own tool. It gives you more freedom about what to automate and how to automate, but this also means you have more responsibility to carefully analyse your exact situation and implement your choices.

If you need a tool that pushes changes over SSH from a YAML-based configuration syntax, you can build that. If you need an agent that runs every 30 minutes to pull configuration from a central server, then you can build that too. It's your choice. Any of those systems and approaches can be good enough solutions when applied correctly to the situation at hand.

Is it possible to use Rex for deploying Perl modules to remote machines?

Yes, in fact the original use case of Rex was deployment management (as far as I know).

In the end, any management system boils down to running commands and managing files. If there's a command to run, or a file to configure, it's possible to do that with Rex too. This includes Perl module deployment as well.

How should we share and distribute useful Rex tasks we’ve written with other developers?

Rex code is Perl code, so the same rules apply for distributing the results as well. Like with most things, the "best" method depends on the exact needs.

I prefer the following approaches in general:

  • share small snippets as gists
  • put my code under version control and share the repo with others
  • build a module around a specific scope and publish it on CPAN