Deploy de aplicações SSR utilizando o Supervisord

Se você está construindo aplicações que renderizam do lado do servidor, o famoso SSR, utiliznado React (com o next.js) ou Vue.js (com o nuxt.js), você vai precisar de alguma ferramenta para controle do processos quando for fazer o deploy. Eu escolhi fazer o deploy de aplicações SSR utilizando o Supervisord, apesar de ter visto em vários sites o pessoal utilizando PM2 para este tipo de aplicação, acredito que o Supervisord é mais conhecido em um contexto geral, não só de quem é do mundo do node.
Se você já viu meu Guia para Deploy Django e Python 3, você já usou o Supervisor.

A razão número 1 para você ter um aplicativo React ou Vue utilizando SSR é por causa do SEO. O Googlebot não trabalha bem com páginas renderizadas do lado do cliente (CSR, o contrário do SSR) e por conta disso pode nem indexar suas páginas. Então, ter uma aplicação SSR dessas rodando no seu servidor, significa que você vai servir a aplicação utilizando o node pra rodar os javascripts que você criou. E para manter o seu comando node rodando, você não pode simplesmente abrir em um screen e torcer para que continue rodando. Você precisa colocar seu app em uma ferramenta de controle de processos como o Supervisord para inicializar sua aplicação caso o servidor reinicie ou sua própria aplicação dê algum pau.

Instalando o Supervisord:

sudo apt-get install supervisor

Agora, vamos criar um arquivo de configuração para a aplicação SSR:

sudo vi /etc/supervisor/conf.d/my-ssr-app.conf

That’s the content:


[program:myappname]
directory=/home/username/yourproject/
command=npm run start
user=username
autostart=true
autorestart=true

Então, precisamos avisar o supervisord que existe um novo processo para ele controlar


sudo supervisorctl reread
sudo supervisorctl update

E futuramente, quando precisar restartar apenas o seu app, use o nome que você colocou no arquivo de configuração.


sudo supervisorctl restart myappname

E é isso aí. Agora você sabe como fazer deploy de aplicações SSR utilizando o Supervisord.

I Know Kung Fu GIF from The Matrix (Now you know how to deploy ssr applications using supervisord)

Alguns links:

Post original em inglês

https://www.reddit.com/r/reactjs/comments/an16mx/how_does_seo_work_with_react/

https://medium.com/walmartlabs/the-benefits-of-server-side-rendering-over-client-side-rendering-5d07ff2cefe8

https://dev.to/stereobooster/server-side-rendering-or-ssr-what-is-it-for-and-when-to-use-it-2cpg

Deploy SSR applications using Supervisord

If you’re building Server-Side Rendering applications with React (next.js) or Vue.js (nuxt.js) you will have to deploy it using some process control tool to keep it running. I have seen that a lot of websites are teaching how to do this with PM2, but I decided to deploy SSR applications using Supervisord. It will work in the same way and it’s a very common tool, so chances are you already have Supervisord in your server. Especially you’ve followed the Deploy for Kids tutorial.

The number one reason to have a React or Vue.js SSR app is SEO. Google Bot doesn’t work well with CSR (Client-Side Rendering) and it can’t index your pages in that way. So, having an SSR app running in your server means you have node.js running some program that you’ve built in Javascript. But you can’t just run node in a screen and get out of it, you must have some process control tool to keep it running if the server restarts or if the application crashes for some reason.

Installing Supervisord:

sudo apt-get install supervisor

Now, create a new configuration file for your SSR application:

sudo vi /etc/supervisor/conf.d/my-ssr-app.conf

That’s the content:


[program:myappname]
directory=/home/username/yourproject/
command=npm run start
user=username
autostart=true
autorestart=true

Now, you have to tell Supervisord about this new process:


sudo supervisorctl reread
sudo supervisorctl update

And if in the future you need to restart just your app, use the name in the conf file:


sudo supervisorctl restart myappname

That’s it. Now you know how to deploy SSR applications using Supervisord.

I Know Kung Fu GIF from The Matrix (Now you know how to deploy ssr applications using supervisord)

Interesting links:

https://www.reddit.com/r/reactjs/comments/an16mx/how_does_seo_work_with_react/

https://medium.com/walmartlabs/the-benefits-of-server-side-rendering-over-client-side-rendering-5d07ff2cefe8

https://dev.to/stereobooster/server-side-rendering-or-ssr-what-is-it-for-and-when-to-use-it-2cpg

Deploy for kids – Guide for deploying Django Python 3

crianças fazendo deploy

There is a lot of tutorials out there, especially in English. Here it goes another one. I wrote it originally in Portuguese.

The reason many people has problems deploying is that they don’t pay enough attention to details. Deploying is easy when you are familiarized with all parts involved. You must know how to authenticate through ssh, be used to command line and Linux, understand how to configure and set up your project, have an idea of what serving static files is, what is Gunicorn… Ok, it’s not that simple. That’s why there is a lot of deploy tools, kits, and tutorials. Currently, with Ansible, Docker and whatever kids are using these days it’s easier to deploy, but what happens under the hood gets more abstract.

Maybe in a couple of years, this post is going to be obsolete if it’s not already with serverless and everything else. Anyway, just a few people want to learn how to deploy Django as I’ll show here, but if it helps at least one person, I’ll be satisfied.

Enjoy this Old-Style guide!

The Server

I presume you don’t have a server or AWS account, DigitalOcean, Linode… Nothing! You have to create an account in one of them and launch a server with the distro you want. If it’s your first time, don’t go with AWS because it’s way more complicated than the others.

In this tutorial, I’m using an Ubuntu 16.04, the most common distro you’ll see around. You can also pick a Debian if you like.

Initial Set Up

Configure server timezone

sudo locale-gen --no-purge --lang pt_BR  # I'm using pt_BR, because HUE HUE BR BR
sudo dpkg-reconfigure tzdata

Update and upgrade OS Packages:

sudo apt-get update 
sudo apt-get -y upgrade

Installing Python 3.6 over Python 3.5

Replace Python 3.5 which is default on our distro with Python 3.6.

sudo apt-get update
sudo add-apt-repository ppa:jonathonf/python-3.6
sudo apt-get install python3.6
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.5 1
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 2

You can choose which Python version the OS will call when you type python3.

sudo update-alternatives --config python3

Having trouble, take a look here:

How to Install Python 3.6.1 in Ubuntu 16.04 LTS

Install OS requirements

sudo apt-get install python3-pip nginx supervisor git git-core libpq-dev python-dev 
python-virtualenv

If your project has more OS requirements, install them as well.

VirtualEnvWrapper for Python3

I’m a fan of VirtualEnvWrapper. It’s super easy and creates all my virtual environments in the same place. That’s a personal choice, if you don’t like it, use what you know how to use.

First, you install virtualenvwrapper, and then define where to put your virtualenvs. (WORKON_HOME).

If you need to use it with multiple Python versions, you must define VIRTUALENVWRAPPER_PYTHON. Here I’m using always with python3. It’s not a problem since you can create a virtualenv pointing which Python that env will use.

sudo pip3 install virtualenvwrapper
echo 'export WORKON_HOME=~/Envs' >> ~/.bashrc
echo ‘export VIRTUALENVWRAPPER_PYTHON=`which python3`’ >> ~/.bashrc
echo 'source /usr/local/bin/virtualenvwrapper.sh' >> ~/.bashrc
source ~/.bashrc

Now, create your virtualenv and define what Python is going to use.

mkvirtualenv name_venv --python=python3

VirtualEnvWrapper is really easy to use. If you want to activate a virtual env, you can use workon.

workon name_venv

To deactivate this virtualenv:

deactivate

To remove a virtualenv:

rmvirtualenv name_venv

Generate SSH for GitHub Authentication

You don’t want (neither should) write your password to git pull your project on the server.

Generating SSH Keys:

cd ~/.ssh
ssh-keygen -t rsa -b 4096 -C "[email protected]"
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_rsa

See and copy the content of your public key (id_rsa.pub)

cat ~/.ssh/id_rsa.pub

Then sign in your GitHub account and go to Settings > SSH and GPG Keys. Click on New SSH Key, give it a name, like (“test server keys”) and in Key paste the content of your  id_rsa.pub

Clone your Django Project

Copy the SSH link from GitHub to clone your project. In this case, I’m using a project that I just have found as an example.

git clone [email protected]:kirpit/django-sample-app.git

In the project folder, install the project requirements.

Remember that you have to be in your virtual environment

cd django-sample-app/
pip install -r requirements.txt

Now, make the necessary alterations for your deploy, such as create a settings_local.py file, change database settings or anything specific to your project.

After you’re done, run your migrations and collect your static files (if you’re using it).

python manage.py migrate
python manage.py collectstatic

Configuring NGINX

Nginx, like Apache, is an entirely separate world. Right now, you just need the basics.

/etc/nginx/sites-available/ is a directory where you have to put the config files of available sites. There is another directory, /etc/nginx/sites-enabled/ that shows which sites are enabled. They are the same thing, but what is put on enabled will be served by Nginx.

It’s usual to create your config file on sites-available and create just a symlink to sites-enabled.

First of all, I’ll remove the default site from Nginx.

sudo rm /etc/nginx/sites-enabled/default

Now, create the config file for your site. (If you don’t know how to use VIM, use nano instead of vi)

sudo vi /etc/nginx/sites-available/mysite

Past this on your file, changing the necessary paths:

server {
 listen 80;
 access_log /home/username/logs/access.log;
 error_log /home/username/logs/error.log;

 server_name nome-site.com.br;

 location / {
 proxy_pass http://127.0.0.1:8000; 

 proxy_pass_header Server;
 proxy_set_header X-Forwarded-Host $server_name;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxy_set_header Host $http_host;


 }

 location /static {

   alias /home/username/project_path/static/;

 }

And create a symlink to sites-enabled:

sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite

Restart Nginx:

sudo service nginx restart

Ok, if you made it till here, if you access your website you will see a 502 Bad Gateway from Nginx. That’s because it’s nothing here: http://127.0.0.1:8000

Now, configure the website to run on 8000 port.

Configuring Gunicorn

Are you guys alive? Don’t give up, we’re almost there.

In your virtualenv (remember workon name_env?) install Gunicorn

pip install gunicorn

In your project’s directory, make a gunicorn_conf file:

bind = "127.0.0.1:8000"
logfile = "/home/username/logs/gunicorn.log"
workers = 3

Now, if you run Gunicorn you will see your website working!

/home/username/Envs/name_venv/bin/gunicorn project.wsgi:application -c gunicorn_conf

But what are you going to do? Run this command inside a screen and walk away? Of course not! You’ll use Supervisord to control Gunicorn.

Configuring Supervisor

Now create a gunicorn.conf:

sudo vi /etc/supervisor/conf.d/gunicorn.conf

That’s the content:

[program:gunicorn]
command=/home/username/Envs/name_venv/bin/gunicorn project.wsgi:application -c /home/username/project/project_django/gunicorn_conf
directory=/home/username/project/project-django
user=username
autostart=true
autorestart=true
redirect_stderr=true

And now, you just tell Supervisor that there is a new process in town and Supervisord will take care of it:

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart gunicorn

And voilá! A new running you will have.

Conclusion

There is a lot of things involved in a deploy process. You have to configure a firewall, probably you’ll have to serve more than one static folder, etc, etc… But you have to start somewhere.

I can’t believe I wrote a whole post without using any GIF. So, just to finish, pay attention to all paths I’ve used here.

Oops..