Archive for marzo, 2021


… and you want to run an ASP.Net Core application on it (be it a web API, an MVC project or whatever).
Suppose you have a supported model, like a model 4B Pi with 8 Gigs of RAM (seems familiar? Well maybe…).

If you’re fine running Kestrel and accessing it directly on port 5000, good for you. Just look at the Microsoft docs on how to set up a Systemd unit to have your app run automatically at startup and you’re good to go.

But maybe you want to run Kestrel behind a reverse proxy, like nginx.

Cool.

Again, for a simple configuration where your app runs at the root of the site, the Microsoft docs are actually fine. They also instruct you on how to forward the headers from nginx to Kestrel and how to modify your application accordingly.

But maybe you want to run your app as a subdirectory (subdirectory, not a subdomain, which should be a simpler task, as the app will reside at the root of the subdomain) of your main site.

Cool, but there are some caveats.

The first one is that missing a single trailing ‘/’ in the configuration file of nginx makes the difference between a working configuration and a non-working configuration.

After some trials and errors I got to the following sample configuration file (which is the “default” file in the “sites-available” / “sites-enabled” directory from which nginx read it’s config) .

server {
        listen 80;

        index index.html index.htm index.nginx-debian.html;

        server_name your-raspberry-name.your-domain;

        location / {
                root            /var/www/html;
                try_files       $uri $uri/ =404;
        }

        location /apps {
                alias           /var/www/apps;
                try_files       $uri $uri/ =404;
        }

        location /apps/your-app-name/ {
                proxy_pass              http://localhost:5000/;
                proxy_http_version      1.1;
                proxy_set_header        Upgrade $http_upgrade;
                proxy_set_header        Connection keep-alive;
                proxy_set_header        Host $host;
                proxy_cache_bypass      $http_upgrade;
                proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header        X-Forwarded-Proto $scheme;
        }
}

The part of interest is, of course, the ‘location /apps/your-app-name/’ (in my case, my app is located in a sub-subdirectory because… why not?) section.
Notice the trailing ‘/’s in both the location and the proxy_pass directive.

But as I wrote, there are some caveats, not just one.

The second caveat is that, as the app is running in Kestrel, it thinks it’s running at the root of the site, not in a subdirectory (which tecnically is true, from a certain point of view). If you have an MVC app or a SPA which tries to load assets (stylesheets, scripts, images, etc.) from the path it believes it’s running from, it will fail. The result will be a crippled app, where the page will load without any asset (or, in the case of a SPA, it will not load at all).

After a lot of googling and reading every q&a on Stack Overflow even remotely related to this problem, I finally found a comment by Scott Hanselman on some random github issue that pointed me in the right direction (to be honest, that comment also pointed to the correct page to the related Microsoft docs).
The solution consist of a whopping 4 (yeah… four) LOC that have to be put inside the ‘Configure’ method:

app.Use((context, next) => {
    context.Request.PathBase = new PathString("/apps/your-app-name");
    return next.Invoke();
});

of course the argument of the PathString constructor can be retrieved from a configuration file or whatever, but for clarity sake, I have hardcoded the string in this example.
Notice the missing trailing ‘/’ in the argument.

With those two configurations I got a sample app (an app created from one ot the standard templates) running as expected inside a subdirectory with nginx and Kestrel.

Bye


Mar 5

… and you want to run PostgreSQL on it.
Suppose you have a model 4B Pi with 8 Gigs of RAM.
Considering that, up to fall 2019, I was maintaing some actively used business web applications on a 2 vCORE, 4GB of vRAM Windows Server 2008 R2 VM on which were running:

  • SQL Server 2008 R2 – not exactly a lightweight RDBMS
  • IIS
  • a bunch of ASP.Net and ASP.Net Core applications (some of which were really bad performers)

I would say that a 1.5 GHz quad core ARM CPU and 8 GB of DDR4 RAM are more than enough to run PostgreSQL – and even some Kestrel istances – for some small home projects, if not for one small detail: storage.

I wouldn’t dare to run PostgreSQL on an SD card, so there are two options:

  • an external storage medium (maybe a fancy USB3 SSD?)
  • a network-mounted drive

I have a NAS at home, so the second option seems more practical.
Specifically I have a NAS that support iSCSI, so… why not? No file permissions to manage, block level access to the LUN…

So, first thing I did was to configure a LUN on the NAS and expose it as an iSCSI target: takes a total of 2 minutes, while drinking coffee.
Then I tought it was as simple as following any guide about open-iscsi on Debian… or not?

Well, in my case, after executing as root (after installing the open-iscsi package):

# iscsiadm -m discovery -t st -p ip.of.my.nas

I got three result, with the same target (i mean, the same iqn) exposed to three different IP addresses… two of which are on subnets not reachable by my raspberry (having a NAS with multiple interfaces is funny!).

To prevent some strange errors at boot, I decided to go (as root) inside the /etc/iscsi/nodes/##nasIdentifier##/ and delete everything with an ip different from the one I wanted to connect to.
Note that the content of the /etc/iscsi/nodes directory is written by the iscsi-adm utility.

The last step I performed to automatically connect to the iscsi target at boot was to modify the default file inside the /etc/iscsi/nodes/##nasidentifier/ip.of.the.target,3260/ directory and set

node.startup = automatic

at the next reboot I had a /dev/sda device available, which was the LUN of my iSCSI target.
Using GParted i created a partition on the device and then created an fstab entry to automatically mount it at boot (always remember to use the _netdev flag in fstab when dealing with network devices, like iSCSI, SMB, NFS, ecc.).

The fun starts now…

I wouldn’t enter the details of installing PostgreSQL on Debian, configuring it for at least the postgres user or setting up the pg_hba.conf and postgresql.conf to allow for access to the server via network.
After moving the data directory to a directory created on the iscsi mount, the following behaviour started to happen:

at boot, PostgreSQL failed to start -according to systemd – but when checking with

# systemctl status postgresql

after login, the result was that postgresql was running, but a quick

netstat -tuna

told a different story: no process was listening on the 5432 port.
But, when doing a

# systemctl restart postgresql

everything was back to normal and PostgreSQL was happily listening on its port.

after having a lot of fun reading the output of

journalctl -xe

I found out the problem was (you already guessed, right?) that PostgreSQL was trying to start before the iSCSI initiator had finished connecting to the iSCSI target.

As

# systemctl list-dependencies

Shows that the postgresql.service unit starts after the remote-fs.target, this shouldn’t happen.
Also, after editing the unit file for PostgreSQL with

# systemctl edit --full postgresql.service

by adding the following line

After=open-iscsi.service ##name-of-the-unit-for-remote-fs-mount##.mount

Nothing changed at the next reboot.

Looking better at the output of journalctl, I discovered an interesting fact: there are two units for PostgreSQL (and only one is shown by the “list-dependencies” command of systemctl… that little S.O.B.).

The unit postgresql@11-main.service is responsible for starting the PostgreSQL cluster, which is the one trying to access the files before the mount point is ready.

So after editing the correct unit by modifying the After line as following

After=network.target remote-fs.target

and adding the mount point of the data directory to the RequiresMountsFor line, at the next reboot PostgreSQL started normally and was already listening on port 5432 right after the Raspberry booted up.
I would say that there’s a lot of documentation about issues related to iscsi and postgresql… all related to CentOS and RHEL.
lucky me, uh?

Bye