Setting up Jelastic at infomaniak

First things first, you need to create environments. I like renaming them once the have been created.

Once the environments have been created, we will connect them to a git repo such that it gets the latest version from the selected branch master by default. You can press the vcs button Deployment, and it will bring a popup where you input the git url of your repository on github or bitbucket or whatever. You will then expand the bottom part to add your password (hoping that it is not stored...). Once that is done you can press the checkbox for 1 min update (it will check the repo for changes and redeploy them).

In my experience re-deployments maintain the files in the /home/jelastic/ROOT dir and overwrite only those that are coming from the repo, so you can add a .env file in your root and it will not be trashed by redeployments (unless you track it with vcs).

Our setup

If you are working with Graphql and a federated Gateway, you don't want to do the encryption between the gateway and the graphql nodes. Therefore you need to use HTTP for the nodes and SSL for the gateway. Everything is pretty straight forward. For example lets say we have 2 nodes and a gateway that stitches them together:

  1. Node 1 (nodejs + mariaDB ; noSSL)
  2. Node 2 (nodejs + mariaDB ; noSSL)
  3. Gateway (nodejs, SSL)

The mariaDB instances are created for you with a root user and password that you receive in your email. That email also specifies the host to use in your apps.

It is a best practice to create a new user and database, with restricted powers (only those required by the nodejs app) and use those credentials instead of the root. Do that by accessing the mariaDB phpMyAdmin tool (the link is near the mariaDB node). There you can create a user.

Our goal being to let nodejs know about the db connection data, and that data being different depending on the environment, we will create a .env file which is not tracked by git (add .env to your .gitignore). You should create one for your dev machine (which you probably already have) and a different one directly in production machine (here jelastic environment). Log in to to a web shell or using SSH, and simply vim /home/jelastic/ROOT/.env.

Our repositories have a usual structure with a root folder hosting the package.json and a subdirectory ./src with all the uncompiled typescript code.

Building

In order to run our app we need to compile the code. By default the typescript version available is really old, around the 1.x.x. If we try to compile, it will fail, so the best bet is to install the latest version globally with:

npm i -g typescript@latest

this will output something like:

/opt/.nvm/versions/node/v13.9.0/bin/tsc # -> /opt......

use this complete path, either to create an alias or directly to compile your code:

cd /home/jelastic/ROOT && /opt/.nvm/versions/node/v13.9.0/bin/tsc

Now if everything is respected in our tsconfig.json, all the code should be available as javascript in /home/jelastic/ROOT/build/src

Running the server forever

When we run our script using either node with : node /home/jelastic/ROOT/build/src/index.js or npm start, as soon as we exit the terminal that started the server, the process is killed and the website is down. There is a quick way to fix this with nohup node /home/jelastic/ROOT/build/src/index.js &, but if the server goes down for some reason, it will not automatically restart the process. That's why forever npm package is used: prevents killing as soon as leaving the terminal, and reboots.

npm Forever

To setup forever you have to create two files:

  1. /home/jelastic/ROOT/forever.json aka forever.json

    {
       "uid": "civili",
       "append": true,
       "watch": true,
       "script": "index.js",
       // tells forever where to find `script` (here index.js)
       // it also uses this path to look for .foreverignore
       "sourceDir": "/home/jelastic/ROOT/build/src",
       // tells forever where to find .env files
       "workingDir": "/home/jelastic/ROOT"
     }
    
  2. /home/jelastic/ROOT/build/src/.foreverignore aka .foreverignore

    # we only care about changes to javascript files
    # in the src folder
    !src/*.js
    

Start

cd /home/jelastic/ROOT
forever start forever.json

then if everything goes right you should see your process running when you do (otherwise see possible errors below):

forever list
# info:    Forever processes running
# data: uid         command                                  script   forever pid id  logfile                            uptime
# data: [2] civili  /opt/.nvm/versions/node/v13.9.0/bin/node index.js 10900   10911   /home/jelastic/.forever/civili.log 0:6:54:6.619999999998981

See the 10911 is the pid here, and you should use that in order to refer to the process when you stop with forever stop 10911 or restart etc.

Forever errors

Error 1:

(node:14545) Warning: Accessing non-existent property 'padLevels' of module exports inside circular dependency

Solution: use an older lower version of node (< 14). Go to Change Environment Topology and choose a different forever-node version for example Node-13.9.0 aliased forever-13.9.0. Here is a clue to the solution

Error 2:

error: Could not read .foreverignore file.

Solution: forever is not finding the .foreverignore. Make sure to add one inside the sourceDir specified in forever.json solution thanks to

Error 3: process.env.SOME_VARIABLE not found Solution: . When this happens it's because forever does not know where to look for the .env file. Make sure to add the ROOT dir as workingDir in forever.json

.foreverignore

Add the .foreverignore at the sourceDir folder level. It should not get deleted by running tsc, since it will only overwrite existing files. In case it does, simply add a script to create it after build time. See package.json

Scripts

In order to streamline the deployment process on new commits to your repository, I recommend adding these scripts to your package.json of your projects:

{
  "build:jelastic": "/opt/.nvm/versions/node/v13.9.0/bin/tsc && echo '!*.js' >> /home/jelastic/ROOT/build/src/.foreverignore",
}

Environment intercommunication

Let's come back to our "microservices" architecture. We need to let our gateway federate its nodes (node 1 and node 2). To achieve this, you have to open the Settings (a yellow workbench button to the right of the environment name), and there configure the firewall. The rules we want are:

  1. grant Gateway access to Node 1 and Node 2 through HTTP (no need to encrypt and decrypt)
    • Go to Node 1 Settings > Firewall and there open the Inbound rules:
      • Press "+ Add" and select: HTTP + the specific port of your nodejs server (i have for example 3555)
        • Unchanged: Name: HTTP, Protocol: TCP
        • Change: Port 80 to your desired port number (the one you wrote in app.listen(<PORT>))
        • For "Source": select "Environment nodes" and add the gateway "Application server"
        • forbid access from everywhere else (just make sure not to block other useful stuff)
    • Repeat steps in Node 2
  2. grant users access to the Gateway endpoint
    • Go to the Gateway firewall config and allow accessing from the outside:
      • Press "+ Add"

Ports in the gateway

In order to connect the gateway to the other nodes, you should make sure before hand that they are accessible. To do so, you should check it from within the gateway environment. Within the Gateway environment shell use curl:

If you are using a path for your graphql endpoint, for example I use /graphql you can test it directly with

curl http://node12312-name.jelastic.ik-server.com:3500/graphql
# expected output:
GET query missing

If you see this, you are all good. Just add the url with the port and path to wherever you tell the gateway federation to get its links, I do it in the .env file.

Troubleshooting

If you don't get the above output, you can also try without the path whether you at least get a Cannot GET / message, which means the firewall is setup properly and that you are querying the proper host.

curl http://node12312-name.jelastic.ik-server.com:3500
# outputs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /</pre>
</body>
</html>

If you see this Cannot GET, you should be really happy because you are only a path away from the graphql endpoint, there must be another error, but it is not a Firewall issue.

Exposing the gateway to the outer world

Again go to your Gateway environment Settings > Firewall and open view inbound traffic rules. Do the same process as for Node 1 and Node 2, except this time you want to use HTTPS on the port you want (for example 8097) and set the Source to All.

Important: make sure that when you created the environment you toggled SSL switch on.

Using a custom domain

Since we are exposing the Gateway to the outer world, it would be much nicer if we were able to give it our own top level domain.

The reason is very simple: CORS. You want to avoid handling CORS stuff if possible and for that you simply.

Warning: if you are going to be doing SSL on that domain, you will have to use a paid option and a load balancer, since Jelastic does not support HTTPS on nodejs. With the help of a load balancer it can be done.

To add the custom domain, we simply go to Settings > Custom Domain and from there we add a custom domain. You will probably be using a subdomain, so add it there and go to your registrar and add the appropriate CNAME for that subdomain as indicated there.

Using SSL

If you want to have HTTPS, you will have to make a choice, either:

  1. FREE: use HTTPS on the generated node's hostname that jelastic provides you with (may be a problem for CORS (need to verify))
  2. PAID: add a custom domain, a load balancer and lets encrypt module