An installation of Discourse

Part 1: state and coordination project

View source:

In this segment, we’ll talk about:

  • setting up PostgreSQL,
  • setting up Redis,
  • connectivity between Glitch projects, and
  • running multiple processes

Setting up PostgreSQL

The official PostgreSQL project has downloads for Ubuntu PostgreSQL: Linux downloads (Ubuntu), and we’re fortunate enough that they have builds going back as far as 16.04 that we have on our project containers.

We don’t have permission to use apt-get as root, but we can download the .deb files and extract them.

The postgresql-10 package has a lot of PostgreSQL-related dependencies, but it turns out it runs fine without them. For the dependency on the client, it makes sense, and I don’t know why it’s even listed as a dependency. I looked into what the postgresql-common package does, and it seems to be a bunch of wrapper scripts to determine the right version to use. In this project I go without the wrappers, and the scripts access .../usr/lib/postgresql/10/bin/... directly.

This distribution of PostgreSQL normally tries to open a UNIX domain socket in /var/run/postgresql, which we don’t have permission to write to in our project containers. I changed the configuration not to bother with the UNIX domain sockets. We’ll use TCP to connect to it, which I’ll describe later in this segment.

Setting up Redis

I originally tried compiling Redis from source, but I later found that they also publish a prebuilt version for Ubuntu 16.04. Lucky!

I made a copy of the config file (from the wrong version I realize now :person_facepalming:) and edited it not to daemonize (discussed later), to disconnect idle clients (discussed later), and to put its logs and data into the project’s .data directory.

Connectivity between Glitch projects

There won’t be enough RAM, disk, and CPU in one Glitch project to run all of Discourse, so we’ll need to run some components on separate projects and let them access the services in this project. But as far as I know, there is no secret passageway that lets them communicate with each other. That leaves only the great big front door that is the open Internet.

These services have TCP interfaces. In previous work Cramming a Terraria server into a free project container, I showed that Glitch allows arbitrary streaming communication after an HTTP upgrade. I use that technique here too. This time I added some logic to route requests for different paths to different ports.

And because it’ll be connecting over a public HTTP server, I also added some simple authentication. PostgreSQL and Redis probably have their own authentication, and it would be prudent to set those up for defense in depth. But in this project they aren’t, and I really hope I wrote this part correctly :crossed_fingers:. (Disclosure: I am aware that the secret token is not compared in constant time.)

One half of the result is in services/doors/doors.js. The other half is roughly the opposite, and we’ll see it in a later post in this writeup.

An annoying thing about this is that we can’t let connections be idle for too long, or Glitch will close the connection for us. The programs don’t like this. So I’ve added some configuration in various places to have the programs themselves close connections that are idle for ~25 seconds. Maybe a better version of this could add some kind of keep alive system, but you’d have to be careful how you do it, or it could interfere with project container sleep.

Running multiple processes

We have three things, really. There’s a PostgreSQL server, a Redis server, and a little program to accept connections over upgraded HTTP. Now we need to run all three of them from our start script.

I found that there’s already a suite of programs for managing services, runit http://smarden.org/runit/. Glitch uses it to manage some services in the container, and I think it’s not uncommon in the Docker scene where there’s a desire for a simple daemon supervisor. From its manual,

runsv switches to the directory service and starts ./run.

and

runsvdir starts a runsv (8) process for each subdirectory … and restarts a runsv (8) process if it terminates.

How’s that for simple? So we make a bunch of directories and write up some run scripts. That’s the rest of this project in the services directory.

Because runsv expects the program to run until the service dies, I’ve configured the programs not to daemonize.


Thanks! All we have is our best effort, after all.

Thanks! That makes it all worthwhile, which is important because it’s a little unpractical.

Yup, there’s tunneling in the form of HTTP upgrades. And yet it is exposed to the Internet, with only HTTP authentication between us and oblivion. Well, and TLS.


P. S., I think Glitch is sometimes a little un-gentle when it stops your app, and I sometimes see leftover pidfiles. I’ve rigged up the commands in package.json to delete them carelessly :pensive:

9 Likes