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
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:
Neste post, vou mostrar como criar um container Docker para um projeto Django já existente. Como exemplo, resolvi buscar por uma issue aberta no github que estivesse pedindo para ser dockerizada. Criei um PR para a Issue e usei como exemplo aqui.
Por quê você vai querer dockerizar uma aplicação web django que já existe? Bom, existem muitas razões, se você acha que não tem uma, faça pela diversão!
Eu decidi usar o docker em uma das minhas aplicações porque ela estava ficando muito difícil de instalar. Muitos requisitos do sistema, vários bancos de dados, celery, rabbitmq e por aí vai. Sem dockerizar cada vez que uma nova pessoa entra no time é um inferno pra setar tudo porque levava tempo demais.
O mundo ideal é que o programador tenha em seu ambiente de desenvolvimento o mais próximo que puder do ambiente de produção. Se você usa SQLite na sua máquina mas Postgres no servidor pode ser que tenha problemas de dados que são simplesmente truncados localmente mas que vão levantar erros na base de produção. Só para ter ideia de um exemplo.
Se você não sabe o que é o docker, imagine que é um imenso virtualenv que no lugar de ter apenas pacotes python tem o sistema operacional “inteiro”. Isso consegue isolar seu app de tudo que está no seu SO, bancos de dados, workers, etc.
Mão na massa
Ok, falar é fácil, vamos codar um pouco.
Primeiro de tudo, instale o Docker. Fiz isso no Ubuntu e no Mac sem nenhum problema. Já no Windows Home não consegui fazer funcionar.
Para dizer ao docker que sua aplicação é um container você precisa criar um arquivo Dockerfile:
FROM python:3.6
ENV PYTHONUNBUFFERED 1
RUN mkdir /webapps
WORKDIR /webapps
# Installing OS Dependencies
RUN apt-get update && apt-get upgrade -y && apt-get install -y \
libsqlite3-dev
RUN pip install -U pip setuptools
COPY requirements.txt /webapps/
COPY requirements-opt.txt /webapps/
RUN pip install -r /webapps/requirements.txt
RUN pip install -r /webapps/requirements-opt.txt
ADD . /webapps/
# Django service
EXPOSE 8000
Vamos lá, linha a linha:
Docker Images
FROM python:3.6
Aqui estamos usando uma imagem do docker hub. Isto, é um container pré-formatado do docker que permite que você monte sua máquina a partir daquela configuração inicial. Nesse caso, Python 3.6 é um container de um Ubuntu que tem o Python 3.6 instalado nele. Você pode procurar por containers no docker hub.
Environment Variables (Variáveis de ambiente)
Você pode criar todas as variáveis de ambiente que quiser com o ENV.
ENV PYTHONUNBUFFERED 1 # Here we can create all Environment variables for our container
Por exemplo, se você usa variáveis de ambiente para guardar sua secret key do Django é só fazer assim:
import os
SECRET_KEY = os.environ['DJANGO_SECRET_KEY']
Run Commands
Docker Run Commands tem um nome meio óbvio. Você pode rodar um comando “dentro” do seu container. Estou colocando dentro entre aspas porque o docker na verdade cria algo como sub containers para que não precise rodar os mesmos comandos novamente no caso de precisar dar um rebuild do container.
RUN mkdir /webapps
WORKDIR /webapps
# Installing OS Dependencies
RUN apt-get update && apt-get upgrade -y && apt-get install -y \
libsqlite3-dev
RUN pip install -U pip setuptools
COPY requirements.txt /webapps/
COPY requirements-opt.txt /webapps/
RUN pip install -r /webapps/requirements.txt
RUN pip install -r /webapps/requirements-opt.txt
ADD . /webapps/
Aqui estamos criando o diretório que guardará os arquivos do projeto: webapps/.
Workdir é uma instrução para mostrar ao docker em qual diretório ele vai rodar os comandos a partir dali.
Depois disso estou instalando as dependências do sistema operacional. Quando estamos usando requirements.txt no projeto, estamos colocando apenas os requisitos do python e não os do SO. Mais um motivo para querer usar o docker para desenvolver. Quanto maior seu projeto, mais requisitos de sistema operacional vão aparecer.
COPY e ADD
Copy e ADD são basicamente a mesma cosia. Ambos copiam um arquivo do seu computador (o Host) dentro do container (o Guest). No meu exemplo, estou apenas copiando o requirements para dentro do docker, para que eu possa dar pip install nos pacotes.
EXPOSE
Expose é para mapear uma porta do Guest (o Container) para o Host (seu computador)
# Django service
EXPOSE 8000
Ok, e agora? Como podemos adicionar mais containers para rodá-los juntos? E se precisarmos colocar um postgresql para rodar em um container também? Não se preocupe, vamos usar o docker-compose.
Docker-Compose
O compose é uma ferramenta para rodar múltiplos containers do docker. Só precisa criar um arquivo yml na pasta do seu projeto com o nome docker-compose.yml:
Era uma vez o Vagrant. Ele era uma forma de rodar um projeto dentro de uma Máquina Virtual que permitia configurar e mapear portas fácilmente, provisionar requisitos do sistema e compartilhar volumes. Seu computador (o Host) podia compartilhar um volume com a máquina virtual (o Guest, ou convidado). No docker, o volume é exatamente a mesma coisa. Quando você escreve um arquivo em um volume que está compartilhado o arquivo também está sendo escrito dentro do container.
volumes:
- .:/webapps
Nesse o caso, o diretório em que nos encontramos (.) é o que está sendo compartilhado com o container.
LINKS
links:
- db
Você pode se referir a outro container que pertence ao seu arquivo docker-compose utilizando o nome dele. Como criamos um container com o nome db para o Postgres nós podemos criar um link para ele no nosso container chamado web. Pode ver que no settings.py nós colocamos ‘db‘ como host.
DEPENDS_ON
Para rodar sua aplicação, seu banco de dados precisa estar pronto antes do container web, senão vai dar algum pau!
depends_on:
- db
Command
Command é o comando padrão que o docker vai rodar logo depois que você subir, ou seja, colocar os containeres para funcionar.
No nosso exemplo, eu criei um run_web.sh que vai rodar as migrações, coletar o static files e iniciar o servidor de desenvolvimento.
Alguém pode argumentar que migrar assim automaticamente toda vez que subir o container pode não ser uma boa prática. Eu concordo. Você pode (e deve) rodar o migrate direto na máquina web. Você pode acessar seu container para rodar comandos assim (como no bom e velho vagrant ssh):
docker-compose exec web bash
Se você quiser , você pode rodar o comando sem acessar o container mesmo, apenas mudando o último argumento do comando acima:
docker-compose exec web python manage.py migrate
O mesmo para outros comandos:
docker-compose exec web python manage.py test
docker-compose exec web python manage.py shell
Rodando o Docker
Com nosso Dockerfile, docker-compose.yml e o run_web.sh no lugar, vamos rodar tudo junto:
Antes eu estava usando run no lugar de exec. Mas o Bruno FS me mostrou que o exec é melhor pois está acessando exatamente o container que já está rodando e que o run na verdade está criando um novo container.
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.
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.
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.
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).
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:
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:
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.
Tutorial de Deploy por aí é o que não falta, a maioria em inglês. Esse que estou criando é pra engrossar o caldo de deploys em português. Esse é um Guia Definitivo Rápido, ou não tão rápido, para fazer Deploy Django com Python 3. É um deploy para Kids.
A dificuldade de fazer um deploy reside nos detalhes. No fundo é fácil se você está familiarizado com as partes envolvidas. Você precisa saber fazer uma autenticação ssh, estar acostumado com a linha de comando, conhecer linux, saber configurar o projeto, entender o que é servir arquivos estáticos, gunicorn…. tá, tá… nunca é fácil e muito menos rápido, justamente por isso criaram um monte de ferramentas pra deploy. E hoje com Ansible, Docker e whatever kids are using these days fica fácil fazer o deploy mas muito abstrato entender o funcionamento.
Em alguns anos esse post será obsoleto pra sempre, com serverless e tudo mais acho que pouca gente vai querer saber como fazer um deploy django dessa forma. Mas, mesmo assim, se ajudar uma pessoa já está bom. Será um tutorial Old-Style.
O Servidor
Vou imaginar que você não tem um servidor, não tem uma conta na AWS, nem DigitalOcean, nem Linode, nada… Você pode criar uma conta em um deles, lançar uma máquina com as configurações que quiser (Na AWS é tudo mais complicado pra quem não está acostumado, se for sua primeira vez, prefira outro).
Pra esse tutorial estou falando de Ubuntu 16.04, que é o servidor que você mais vai ver por aí nesse momento nesses serviços. Você pode escolher um Debian qualquer também.
Seu projeto pode ter outros requirements do SO pra instalar.
VirtualEnvWrapper para o Python3
Eu gosto muito do VirtualEnvWrapper, acho simples de começar um virtualenv, e deixa todos os meus virtualenvs no mesmo lugar, mas isso é escolha pessoal, se você não gosta, faça como está acostumado
Você instala o virtualenvwrapper, depois define a pasta dos seus virtualenvs (WORKON_HOME).
Para usar com múltiplos Pythons você vai precisar definir VIRTUALENVWRAPPER_PYTHON. No caso estou usando sempre o padrão que defini para o comando python3. Isso não é um problema porque você pode criar um virtualenv depois apontando qual python aquele virtualenv vai usar.
Veja e copie o conteúdo da sua chave pública (id_rsa.pub)
cat ~/.ssh/id_rsa.pub
Depois entre no seu github, em Settings > SSH and GPG Keys. Clique em New SSH Key, dê um nome pra essa chave, como (“chaves do servidor teste”) e em Key cole o conteúdo da chave pública id_rsa.pub
Faça o clone do seu projeto Django
Copie o link SSH do Github para fazer o clone, no caso estou usando um projeto que encontrei agora pra exemplo
Entre na pasta do projeto e instale os requirements do projeto.
Lembre-se de estar no virtual env correto.
cd django-sample-app/
pip install -r requirements.txt
Agora faça as alterações que forem necessárias para o deploy django, como criar um arquivo de settings_local, banco de dados, ou qualquer outra coisa específica do seu projeto.
Depois de tudo certo, você ainda precisa rodar as migrações e gerar os arquivos estáticos (se estiver usando)
Nginx, assim como o Apache, tem um mundo inteiro só deles, nesse momento você precisa conhecer o básico.
Existe um diretório /etc/nginx/sites-available/ onde ficão os arquivos de configuração de sites disponíveis para o nginx servir e existe o diretório /etc/nginx/sites-enabled/ que é a mesmíssima coisa, mas o que estiver aqui o Nginx estará servindo mesmo.
Normalmente você cria o arquivo de configuração no sites-available/ e cria um link simbólico para o sites-enabled/
Nós vamos fazer isso. Primeiramente, vou excluir o site default do nginx
sudo rm /etc/nginx/sites-enabled/default
Agora crie o arquivo de configuração para o seu site. (Se você não está acostumado com o VIM, use troque vi por nano)
sudo vi /etc/nginx/sites-available/meusite
No conteúdo do arquivo, coloque isto, mudando os caminhos necessários:
server {
listen 80;
access_log /home/usuario/logs/access.log;
error_log /home/usuario/logs/error.log;
server_name nome-site.com.br;
location / {
proxy_pass http://127.0.0.1:8000;
#As proximas linhas passam o IP real para o gunicorn nao achar que sao acessos locais
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/usuario/caminho_projeto/static/;
}
}
Mas o que você pretende fazer? Rodar esse comando dentro de um screen e ir embora? Não dá né! Então, você vai usar o Supervisor pra controlar o funcionamento do gunicorn.
Existem muito mais coisas envolvidas no processo de um deploy. Você precisa configurar um firewall, provavelmente precisará servir mais pastas estáticas, etc, etc, etc… Mas precisa começar por algum lugar.
Não acredito que fiz um post inteiro sem colocar nenhum gif no meio, então só pra terminar, PRESTE ATENÇÃO em TODOS os caminhos que eu coloquei acima, você vai ter que usar os seus próprios paths corretamente