Fazendo deploy em produção com Rails, PostgreSQL e Dokku
Tags: rails, ruby, dokku, postgres
Published: 2022-03-24 01:03:06

## Introdução
Disponibilizar uma aplicação na internet para outras pessoas acessarem é uma tarefa razoavelmente simples caso utilizemos o Heroku como PaaS (Plataforma como Serviço), mas se torna uma tarefa um pouco mais complicada quando, querendo baixar custos, passamos a utilizar uma instância na DigitalOcean, Linode, Vultr e etc, e é necessário configurar tudo manualmente, principalmente pra mim, que assunto de infraestrutura eu sou quase um leigo. Mas ainda há esperança para desenvolvedores como eu, que da nomeclatura DevOps, tem só o Dev, e nada de Ops. Eu falo do Dokku, que une a facilitade do Heroku com os baixos custos de uma instância própria. Ele é uma ferramenta de código aberto e gratuita, que resumidamente é um "self-hosted Heroku", um PaaS que tu hospeda no teu próprio servidor, te proporcionando uma maior capacidade de customização e uma redução dos custos.

A seguir vou mostrar como instalar o Dokku numa instância da DigitalOcean (mas tu consegue reproduzir usando o serviço de tua preferência), e implantar uma aplicação real desenvolvida com Ruby on Rails e PostgreSQL. Caso tu não tenha uma aplicação pra seguir o tutorial, pode usar essa aplicação MVP que desenvolvi e que é uma mini-rede social para publicação de customizações do jogo Animal Crossing: New Horizons, jogo do Nintendo Switch lançado em março de 2020. O código está disponível nesse repositório. Use o branch [tutorial](https://github.com/stephannv/customdesigns/tree/tutorial) pois ele já está com alguns ajustes pra facilitar a configuração:

{% embed https://github.com/stephannv/customdesigns %}

## Instalando o Dokku
Antes de tudo é necessário criar uma conta na DigitalOcean, e criar uma instância, termo usado pela DO para se referir às instâncias é droplets, então crie um droplet. Não vai ter imagens desse processo nesse artigo pois a qualquer momento a DigitalOcean pode mudar toda a página e o artigo ficar desatualizado, mas não tem segredo, é só um formulário simples onde tu escolhe as configurações e finaliza o criação da instância. Uma máquina de 2GB de RAM é mais do que suficiente, mas já consegui configurar até usando a de 1GB de RAM.
Após a instância criada, é necessário se conectar nela utilizando o usuário `root`:

```sh
ssh root@IP_DO_TEU_SERVIDOR
```

Esse `IP_DO_TEU_SERVIDOR` se refere ao IP que a DigitalOcean forneceu após a criação do droplet. Caso já tenha configurado um domínio para essa instância, tu pode usar o domínio ao invés do IP.

Agora instale o Dokku com o seguinte comando
```sh
# Para sistemas debian, instale o dokku via apt-get
wget https://raw.githubusercontent.com/dokku/dokku/v0.27.0/bootstrap.sh
sudo DOKKU_TAG=v0.27.0 bash bootstrap.sh
```

A versão `0.27.0` é a mais recente disponível enquanto eu escrevo esse artigo, então confira a documentação oficial no site para verificar se novas versões foram publicadas.

Quando a instalação terminar, visite IP da tua máquina pelo navegador, preencha a chave pública do teu SSH no campo "Public SSH", pois isso te permitirá executar os comandos remotamente que vamos precisar mais pra frente. A sessão de configuração de hostname pode ser ignorada, vamos configurar isso já já. Finalize a instalação e tu estará pronto para começar a configuração da aplicação. 

## Configurando a aplicação

Com o Dokku já instalado, é hora de criar a aplicação. Ainda conectado como root na tua instância, execute:

```sh
dokku apps:create meu_app
```

O `meu_app` é o identificador da tua aplicação, substitua pelo nome do teu projeto, é ele que tu irá se referenciar no restante da configuração.

Uma coisa legal do Dokku é que ele é compátivel com os buildpacks que são utilizados no Heroku, que são conjuntos de scripts que configuram automaticamente várias dependências da aplicação, sem precisar fazer esse processo de forma manual. Então adicione o buildpack do Ruby para seguirmos a configuração:

```sh
dokku buildpacks:add meu_app \
  https://github.com/heroku/heroku-buildpack-ruby.git
```

Para entender mais sobre os buildpacks: https://devcenter.heroku.com/articles/buildpacks

Caso esteja usando as credentials do Rails, tu precisa informar a master.key para que a aplicação possa descriptografar as credenciais:

```sh
dokku config:set meu_app RAILS_MASTER_KEY=tua_chave_mestra
```

Também configure a variável `RAILS_ENV` para ser production:

```sh
dokku config:set meu_app RAILS_ENV=production
```

Com esse comando `dokku config:set`, tu pode definir outras variáveis de ambiente também, caso sejam necessárias.

Como o Heroku, o Dokku também permite que um projeto tenha um arquivo Procfile. Esse arquivo serve para definir os processos que serão executados na inicialização da aplicação. Dois deles são especiais, o `web` e o `release`. O `web` é o único processo que pode receber as requisições externas roteadas pelo Dokku, e o `release` é o comando executado antes de finalizar a implantação da aplicação.

Tu pode ter outros processos e nomeá-los como quiser, por exemplo: depois de configurar um sidekiq na tua aplicação e no Dokku, será necessário colocar um `meus_workers: bundle exec sidekiq -C config/sidekiq.yml` no `Procfile`.

Para o escopo do artigo, precisa apenas do web para subir o servidor com o Puma e do release para executar as migrações e criar os seeds, então crie um arquivo Procfile na raíz do teu projeto com o seguinte conteúdo: 

```sh
web: bundle exec puma -C config/puma.rb
release: bundle exec rails db:migrate db:seed
```

Para entender mais sobre o `Procfile`: https://devcenter.heroku.com/articles/procfile

A partir do Rails 6, por questão de segurança, é necessário informar os domínios permitidos no header das requisições, então no `config/environments/production.rb` informe o IP do teu servidor, teu domínio ou desabilite essa verificação. As opções são essas:

```ruby
config.hosts << "IP_DO_TEU_SERVIDOR"
# ou
config.hosts << "meudominio.com"
# ou caso queira desabilitar a verificação
config.hosts.clear
```

Envie as duas mudanças (do `Procfile` e do `production.rb`) para o teu repositório e vamos seguir para instalar o PostgreSQL.

## Instalando o PostgreSQL

Para instalar o PostgreSQL da maneira mais fácil, pode usar os plugins do Dokku:

```sh
dokku plugin:install \
  https://github.com/dokku/dokku-postgres.git
```

Após isso, crie o banco de dados que será usado na aplicação:
```sh
dokku postgres:create meu_app_db
```

> Observação: A versão do postgres instalada pode não ser a que você deseja, então você pode passar uma flag com uma versão específica: `dokku postgres:create meu_app_db -I 14.1`.


Faça a conexão entre o banco de dados recém-criado e a aplicação com o seguinte comando:
```sh
dokku postgres:link meu_app_db meu_app
```

Esse comando configura uma variável de ambiente chamada `DATABASE_URL` e ela será usada pela aplicação Rails para se conectar com o banco de dados. Uma informação extra: caso por algum motivo tu necessite que o banco seja acessado externamente, esse comando irá expor uma porta aleatória:

```sh
dokku postgres:expose meu_app_db
```

## Configurando o domínio

Esse passo é opcional, caso tu tenha um domínio configurado apontando pro teu servidor e queira que ele se integre com a aplicação, tu precisa adicioná-lo ao meu_app:

```sh
dokku domains:add meu_app meudominio.com

# Caso queira o subdomínio www configurado:
dokku domains:add meu_app www.meudominio.com
```

Lembre-se da configuração que expliquei antes, sobre o `config.hosts`, que é necessário adicionar o `meudominio.com` no `config.hosts` para conseguir navegar na aplicação utilizando o teu domínio.

## Fazendo o deploy

Agora falta pouco para finalizar a implantação da aplicação. Na tua máquina local, na pasta do teu projeto, adicione o repositório remoto apontando para o Dokku do teu servidor com seguinte comando:

```sh
git remote add dokku dokku@IP_DO_TEU_SERVIDOR:meu_app
# ou
git remote add dokku dokku@meudominio.com:meu_app
```

Para finalmente fazer o deploy agora ou toda vez que tu quiser mandar atualizações da aplicação, ainda na pasta do teu projeto execute:

```sh
git push dokku main # ou qualquer outro branch que desejar colocar no ar
```

Esse comando vai demorar um pouco, pois ele irá fazer todo o processo de instalar o Ruby de acordo com o buildpack adicionado lá no início do artigo, vai instalar as gems, instalar as dependências de front-end caso existam, precompilar os assets, rodar os comandos pré e pós deploy e por fim reiniciar o nginx.

Agora tu pode acessar o `http://IP_DO_TEU_SERVIDOR` ou `http://meudominio.com` pelo navegador e se nada inesperado ocorrer, tua aplicação estará no ar, pronta para uso. Mas o ideal é que tua aplicação tenha SSL para dar mais segurança aos teus usuários, principalmente se tiver funcionalidade de autenticação ou se tratar dados mais sensíveis. Na próxima seção explico como configurar o SSL.

## Adicionando o SSL

Caso tu já tenha configurado um domínio pra tua aplicação (caso contrário leia como fazer em Configurando o domínio) e queira configurar o SSL, primeiro instale o plugin do Let's Encrypt:

```sh
dokku plugin:install \
  https://github.com/dokku/dokku-letsencrypt.git
```

Também é necessário configurar o teu e-mail para registro no Let's Encrypt, assim tu receberá notificações do teu certificado:

```sh
 dokku config:set --no-restart meu_app \
   DOKKU_LETSENCRYPT_EMAIL=teu@email.aqui
```

Pra finalizar a configuração de fato o SSL, é só executar o comando letsencrypt e que ele fará todo o trabalho por baixo dos panos de gerar os certificados para cada um dos domínios adicionados:

```sh
dokku letsencrypt:enable meu_app
```

E para ativar a renovação automática de 3 em 3 meses é com o seguinte comando:

```sh
dokku letsencrypt:cron-job --add meu_app
```

Pronto, se tudo tiver dado certo, se tu acessar `https://meudominio.com`, o navegador deverá mostrar na barra de navegação um cadeado sem nenhum erro ou alerta.

## Conclusão

É isso, pode parecer complicado, mas é bem mais fácil que configurar uma máquina "crua" na mão. Com esse conceito de apps, fica fácil adicionar mais aplicações em um mesmo servidor, configurando um subdomínio para cada uma, até mesmo com tecnologias diferentes, pois o Dokku não é apenas para Ruby. É possível usar um Dockerfile ou uma imagem Docker no lugar dos buildpacks para ter uma configuração de ambiente mais customizada. Ele é uma ótima opção para MVPs ou aplicações com um baixo número de usuários, ou até para usar como ambiente de homologação (sandbox, QA).

Caso tenha ficado interessado no Dokku, saiba que há outras alternativas de PaaS self-hosted para tu analisar os pontos fortes e fracos de cada um e escolher o que melhor se adequa para tuas necessidades. Numa pesquisa rápida consigo listar o Flynn, o CapRover, o Convox e o brasileiro Tsuru, mas com uma pesquisa mais aprofundada tu pode encontrar mais opções.

Se tu já tiver usado o Dokku ou qualquer outra opção parecida, deixa nos comentários como foi a experiência, os pontos positivos e negativos, e alguma dica de configuração, caso tenha. Até o próximo artigo.

## TL;DR

```ruby
# Entre na instância
ssh root@IP_DO_TEU_SERVIDOR

# Instale o Dokku. Confira a última versão no site oficial.
wget https://raw.githubusercontent.com/dokku/dokku/v0.27.0/bootstrap.sh
DOKKU_TAG=v0.23.4 bash bootstrap.sh

# Visite o IP_DO_TEU_SERVIDOR pelo navegador, adicione 
# tua chave SSH, ignore as confgurações de hostname 
# e finalize a instalação.

# Ainda conectado via SSH no servidor, crie um app no Dokku
dokku apps:create meu_app

# Adicione o buildpack do Ruby
dokku buildpacks:add meu_app \ 
  https://github.com/heroku/heroku-buildpack-ruby.git

# Configure a variável RAILS_ENV para production
dokku config:set meu_app RAILS_ENV=production

# Caso use as credentials, informe a master.key
dooku config:set meu_app RAILS_MASTER_KEY=tua_senha

# Instale o PostgreSQL
dokku plugin:install \
  https://github.com/dokku/dokku-postgres.git

# Crie um banco de dados
dokku postgres:create meu_app_db

# Conecte o banco de dados com tua aplicação
dokku postgres:link meu_app_db meu_app

# Caso tenha um domínio configurado no servidor:
dokku domains:add meu_app meudominio.com

# Caso queira o subdomínio www configurado:
dokku domains:add meu_app www.meudominio.com

# No código do teu projeto, lembre-se de informar
# o host que tua aplicação irá rodar.
# No arquivo /config/environments/production.rb
config.hosts << "IP_DO_TEU_SERVIDOR"

# Ou
config.hosts << "meudominio.com"

# Ou caso queira desabilitar a verificação
config.hosts.clear


# Na tua máquina local, aponte para o repositório
# remoto do Dokku do teu servidor:
git remote add dokku dokku@IP_DO_TEU_SERVIDOR:meu_app

# Faça o deploy
git push dokku main 

# Acesse http://IP_DO_TEU_SERVIDOR ou
# http://meudominio.com e a aplicação estará no ar.

# Caso queira adicionar SSL na aplicação, instale 
# o letsencrypt
dokku plugin:install \ 
  https://github.com/dokku/dokku-letsencrypt.git

# Configure o e-mail para receber alertas do
# Let's Encrypt:
dokku config:set --no-restart meu_app \
  DOKKU_LETSENCRYPT_EMAIL=teu_email

# Crie os certificados para os domínios da aplicação
dokku letsencrypt meu_app

# Configure a renovação automática
dokku letsencrypt:cron-job --add meu_app

# Acesse https://IP_DO_TEU_SERVIDOR ou
# https://meudominio.com e a aplicação estará no ar
# com SSL
```