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:
- Node 1 (nodejs + mariaDB ; noSSL)
- Node 2 (nodejs + mariaDB ; noSSL)
- 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:
-
/home/jelastic/ROOT/forever.json
akaforever.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" }
-
/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:
- 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 inapp.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)
- Press "+ Add" and select: HTTP + the specific port of your nodejs server (i have for example 3555)
- Repeat steps in Node 2
- Go to Node 1 Settings > Firewall and there open the Inbound rules:
- grant users access to the Gateway endpoint
- Go to the Gateway firewall config and allow accessing from the outside:
- Press "+ Add"
- Go to the Gateway firewall config and allow accessing from the outside:
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:
- FREE: use HTTPS on the generated node's hostname that jelastic provides you with (may be a problem for CORS (need to verify))
- PAID: add a custom domain, a load balancer and lets encrypt module