Deploy Laravel 6 utilizando a ferramenta Deployer

Aprenda a fazer o deploy da sua aplicação Laravel utilizando uma ferramenta de deploy

Deploy Laravel 6 utilizando a ferramenta Deployer

Fala pessoal tudo beleza?

Mais um artigo hoje, no formato tutorial. Nesse artigo aprenderemos como fazer o deploy de uma aplicação desenvolvida com o Laravel, usando uma ferramenta de deploy.

Deployer

Deployer é uma ferramenta desenvolvida em PHP e que faz todo o trabalho pesado em realizar o deploy da sua aplicação PHP, seja usando um framework ou não.

Todos os passos a seguir, foram executados usando o sistema operacional Linux.

Pré requisitos:

  • Conta no Bitbucket (ou em outro repositório git)
  • Uma VPS (estou usando a Digital Ocean)
  • Servidor com Nginx, PHP e Mysql instalados
  • Node e NPM instalados no servidor
  • Acesso root (ou sudo) ao servidor via SSH

Te indico a leitura de 3 artigos do meu blog que pode te ajudar em caso de alguma dúvida de alguns tópicos desse tutorial, são eles:

  1. Configuração de um servidor web usando LEMP no Debian 9. Criando um usuário e configurando o SSH
  2. Aprenda a configurar um servidor web usando LEMP no Debian 9. Iremos instalar o Nginx, MariaDB e PHP 7.2
  3. Aprenda a instalação e a configuração básica de como criar um server block com o servidor web Nginx

Serão criados dois ambientes, de produção e homologação. Para acessarmos a aplicação, nesse tutorial iremos utilizar o IP do servidor, mas caso queira pode ser usado um domínio válido e configurado. Por exemplo, caso esteja usando um domínio, crie um subdomínio com o prefixo homolog.seusite.com.br. Esse será o endereço para o ambiente de homologação, e o seusite.com.br o endereço de produção.

Para o meu caso, irei usar o IP e definir as portas para diferenciar os ambientes, por exemplo, a porta 80 para produção e 81 para homologação.

Instalando o Deployer

A instalação deve ser feita na máquina de desenvolvimento. Para isso digite os 3 comandos abaixo em seu terminal.

curl -LO https://deployer.org/deployer.phar
mv deployer.phar /usr/local/bin/dep
chmod +x /usr/local/bin/dep

No comando acima, é feito o download do arquivo deployer.phar e em seguinda movemos o arquivo para o diretório /usr/local/bin renomeando para dep, e para finalizar concedemos permissão de execução para o arquivo.

Para testar o deployer, execute no seu terminal o comando:

dep --version

Se aparecer a versão do Deployer, sucesso!

Instalando o Laravel

Nesse exemplo iremos baixar uma aplicação Laravel usando o composer. Não terá nenhuma regra de negócio na aplicação, apenas irei criar a autenticação padrão do Laravel e uma implementação a mais para que o deploy funcione corretamente.

mkdir -p ~/projetos/deployer/
cd ~/projetos/deployer
composer create-project --prefer-dist laravel/laravel blog

Com o projeto criado, entre no diretório da aplicação e execute os comandos abaixo para criar a autenticação padrão do Laravel 6 (Esse passo é somente para o Laravel 6+).

cd blog
composer require laravel/ui --dev
php artisan ui vue --auth
npm install && npm run dev

Para que esse exemplo funcione, é necessário criar um controller e alterar a rota barra / do Laravel. Durante o deploy é executado o comando artisan config:cache, e esse comando não funciona se existir uma rota que esteja usando uma função anônima, conhecida também como closure, ou seja, a rota inicial do Laravel utiliza uma função anônima.

Route::get('/', function() {
    return view('welcome');
});

Para resolver, crie um controller chamado WelcomeController e altere a rota para:

Route::get('/', 'WelcomeController@index');

No arquivo WelcomeController, adicione no método index(), o return para a view welcome.

<?php

namespace App\Http\Controllers;

class WelcomeController extends Controller
{
    public function index()
    {
        return view('welcome');
    }
}

No arquivo de rotas da API routes/api.php também existe uma função anônima, nesse caso basta remover a rota que o problema é resolvido.

Até esse momento temos nossa aplicação Laravel funcionando e com a autenticação padrão gerada, apenas faltando a conexão com o banco de dados, mas agora não será preciso se preocupar com isso. Vamos fazer isso mais para frente.

Conhecendo o Deployer

Antes de começar com o deployer, vamos conhecer alguns de seus comandos. Digite no terminal:

dep list

Você verá os comandos disponíveis e dentre eles existe o comando init. Essa opção criará o template inicial de uma configuração, é possível também passar um parâmetro informando qual template gostaria de usar. Vamos ver na prática o comando dep init.

Dentro do diretório do seu projeto, digite o comando abaixo:

dep init

Veja o retorno do comando:

Welcome to the Deployer config generator
This utility will walk you through creating a deploy.php file.
It only covers the most common items, and tries to guess sensible defaults.

Press ^C at any time to quit.

Please select your project type [Common]:
[0 ] Common
[1 ] Laravel
[2 ] Symfony
[3 ] Yii
[4 ] Yii2 Basic App
[5 ] Yii2 Advanced App
[6 ] Zend Framework
[7 ] CakePHP
[8 ] CodeIgniter
[9 ] Drupal
[10] TYPO3
>

O resultado desse comando é uma lista de Tipos de Projeto disponíveis para o uso, o padrão é uma aplicação Common, que seria uma aplicação comum em PHP, sem o uso de frameworks.

Selecione a opção 1 para escolher o template Laravel. Em seguida será solicitado que informe o repositório de onde está seu projeto, apenas dê enter, iremos configurar depois. Em seguida ele pergunta se deseja contribuir com a ferramenta enviando informações anônimas, escolha sim ou não.

Finalizado esses passos, veja que no diretório raiz da sua aplicação foi criado um arquivo chamado deploy.php.

Dica: Podemos criar o mesmo arquivo deploy.php informando o parâmetro -t seguido do nome do template. Veja como ficaria para nosso exemplo: dep init -t Laravel

Configurando o Deployer

Abra o arquivo deploy.php criado na raiz do seu projeto e vamos brincar um pouco.

No trecho abaixo, informamos o endereço do repositório onde a aplicação se encontra. Caso você não tenha essa url ainda, no tópico Versionando a aplicação eu falo sobre isso, deixe como está nesse momento, depois basta atualizar essa informação.

<?php

// Project repository
set('repository', 'git@bitbucket.org:username/deployer-laravel.git');

Dica importante: O repositório fica disponível através de dois endereços: git@bitbucket.org e https://username@bitbucket. Existem diferenças entre eles, para que a primeira opção funcione é necessário ter cadastrado a chave pública do SSH no Bitbucket. Na segunda opção é necessário informar a senha para cada requisição realizada.

Na próxima configuração, definimos o host da aplicação, podemos ter um, dois ou mais hosts configurados. Nesse exemplo criaremos dois, de produção e homologação.

<?php

// Hosts
host('production')
    ->hostname('deploy@174.138.55.236')
    ->port(33458)
    ->stage('prod')
    ->set('keep_releases', 6)
    ->set('branch', 'master')
    ->set('deploy_path', '/var/www/html/production-my-app');

host('homolog')
    ->hostname('deploy@174.138.55.236')
    ->port(33458)
    ->stage('hml')
    ->set('keep_releases', 3)
    ->set('branch', 'develop')
    ->set('deploy_path', '/var/www/html/homolog-my-app');

O código em si é bem simples de entender, mas vou explicar o que cada linha significa:

  1. host: é o nome que identifica aquela conexão
  2. hostname: usuário que será utilizado para fazer o deploy e o IP do servidor
  3. port: porta SSH utilizada (se a porta for a padrão 22, essa opção pode ser omitida)
  4. stage: um alias para o host (sempre utilize)
  5. keep_releases: número de versões que será mantido no servidor
  6. branch: branch que será utilizado ao realizar o deploy
  7. deploy_path: diretório onde se encontra a aplicação

O parâmetro stage eu recomendo utilizar porque utilizando o stage com a opção abaixo, o default_stage, quer dizer que sempre ao realizar um deploy (sem informar o host como parâmetro) será realizado em homologação, assim evitando que seja feito deploy para produção sem querer.

<?php

// Stage default
set('default_stage', 'hml');

O restante das informações são bem auto explicativas, caso tiver com dúvidas deixe sua pergunta nos comentários, e não deixe de olhar a documentação do deployer, vale a pena.

Versionando a aplicação

Crie um repositório no bitbucket e copie a url do seu repositório, com essa url você poderá atualizar o arquivo deploy.php conforme mencionei no tópico anterior.

Dentro do diretório do seu projeto, vamos utilizar o git para versionar e enviar para o bitbucket.

cd ~/projetos/deployer/blog
git init
git add .
git commit -m “Primeiro commit”
git remote add origin https://username@bitbucket.org/username/deployer-laravel.git
git push -u origin master

Acabamos de enviar para o repositório nossa aplicação.

Branch develop

Vamos criar um branch chamado develop, que será nosso branch de homologação. Também iremos enviar para o bitbucket.

git checkout -b develop
git push -u origin develop

Pronto! Até o momento temos nossa aplicação Laravel versionada no Bitbucket com dois branchs (master e develop).

Criando banco de dados e usuário Mysql

Agora os passos a seguir serão executados no servidor.

Acesse o servidor via SSH para criarmos o banco de dados da nossa aplicação. Digite no terminal os comandos abaixo para criar o banco:

mysql -uroot -psenha
CREATE DATABASE laravel;
CREATE USER 'deployer'@'localhost' IDENTIFIED BY 'senhaforteaqui';
GRANT ALL PRIVILEGES ON laravel . * TO 'deployer'@'localhost';
FLUSH PRIVILEGES;

Pronto, criamos nosso banco de dados chamado laravel e criamos também um usuário com todos os privilégios para que possamos conectar no banco.

Configurando usuário deploy

Ainda logado no servidor, crie um usuário shell que será responsável em realizar o deploy da sua aplicação:

# Informe uma senha forte
adduser deploy

# Adicionando o deploy no grupo www-data
usermod -aG www-data deploy

Diretório WEB

Ainda no servidor, vamos criar dois diretórios que irão hospedar nossa aplicação web e alterar o dono para o usuário deploy. Digite os seguintes comandos:

mkdir -p /var/www/html/production-my-app
mkdir -p /var/www/html/homolog-my-app
chown deploy:deploy /var/www/html/production-my-app
chown deploy:deploy /var/www/html/homolog-my-app

Configurando arquivo host do Nginx

Entre no diretório de configuração de sites do nginx e crie dois arquivos de configuração.

cd /etc/nginx/sites-available
touch production.conf homolog.conf

Agora copie e cole o conteúdo abaixo dentro do arquivo production.conf.

Lembre-se de alterar as informações referente ao seu host, por exemplo o IP e a porta caso necessário.

# Arquivo production.conf
server {

    listen 80;
    listen [::]:80;       

    server_name 174.138.55.236;

    # Log
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # Directory root
    root /var/www/html/production-my-app/current/public;
    index index.php index.html;

    client_max_body_size 128M;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # pass PHP scripts to FastCGI server
    location ~ \.php$ {
        # Note que aqui definimos a versao que iremos utilizar do PHP
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_split_path_info ^(.+\.php)(.*)$;
        include /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
    }

    # deny access to .htaccess files
    location ~ /\.ht {
        deny all;
    }

    location ~ /\.well-known {
        allow all;
    }

    # Set header expirations on per-project basis
    location ~* \.(?:ico|css|js|jpe?g|JPG|png|svg|woff)$ {
        expires 365d;
    }
}

Abra o arquivo homolog.conf e cole o conteúdo abaixo:

# Arquivo homolog.conf
server {

    listen 81;
    listen [::]:81;       

    server_name 174.138.55.236;

    # Log
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # Directory root
    root /var/www/html/homolog-my-app/current/public;
    index index.php index.html;

    client_max_body_size 128M;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # pass PHP scripts to FastCGI server
    location ~ \.php$ {
        # Note que aqui definimos a versao que iremos utilizar do PHP
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_split_path_info ^(.+\.php)(.*)$;
        include /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
    }

    # deny access to .htaccess files
    location ~ /\.ht {
        deny all;
    }

    location ~ /\.well-known {
        allow all;
    }

    # Set header expirations on per-project basis
    location ~* \.(?:ico|css|js|jpe?g|JPG|png|svg|woff)$ {
        expires 365d;
    }
}

Ative os dois sites que acabamos de configurar:

ln -s /etc/nginx/sites-available/production.conf /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/homolog.conf /etc/nginx/sites-enabled/
service nginx restart

Pronto. Arquivos vhost do nginx configurados. Se você tentar acessar agora, não funcionará, pois ainda falta realizar o deploy da aplicação, calma que vamos chegar lá.

Configurando chaves SSH no Bitbucket

Para que o deploy funcione sem a necessidade de informar a senha, vamos criar uma chave SSH para o usuário deploy.

Logue no servidor com o usuário deploy criado neste tutorial e digite o comando:

# Acessando o servidor com o usuário deploy
ssh deploy@174.138.55.236

# Gerar chaves SSH
ssh-keygen

Confirme as informações, quando solicitar uma senha, não é necessário informar, apenas dê enter até finalizar.

Será gerado dois arquivos em /home/deploy/.ssh chamado id_rsa (chave privada) e id_rsa.pub (chave pública).

Copie todo o conteúdo do arquivo id_rsa.pub e siga os passos:

  1. Abra sua conta do bitbucket
  2. Clique sobre sua foto de perfil e em Bitbucket settings
  3. No painel da esquerda clique em SSH Keys
  4. Clique no botão Add key e cole o conteúdo no campo key
  5. Dê um nome para identificar essa chave
  6. Clique em Add key para salvar as informações

Nesse ponto a comunicação do servidor e bitbucket já pode ser feita através das chaves SSH de maneira segura e sem a necessidade de informar uma senha.

Apenas para efeito de teste, vamos clonar nosso repositório no servidor. Copie a url do repositório que pode ser obtida no próprio repositório, e dentro do diretório /home/deploy (no servidor) cole a url do repositório e dê enter:

git clone git@bitbucket.org:username/deployer-laravel.git

Irá aparecer uma mensagem para que confirme o host e que seja adicionado na lista de hosts confiáveis, informe yes para continuar.

Sucesso. Seu repositório foi clonado.

Ufa, chegamos no deploy!

Bom, vamos ver como ficou o nosso arquivo de deploy? Após nossas configurações o arquivo deve estar parecido com esse:

<?php

namespace Deployer;

require 'recipe/laravel.php';

// Project name
set('application', 'My APP');

// Project repository
set('repository', 'git@bitbucket.org:username/deployer-laravel.git');

// [Optional] Allocate tty for git clone. Default value is false.
set('git_tty', true);

// Shared files/dirs between deploys
add('shared_files', []);
add('shared_dirs', []);

// Writable dirs by web server
add('writable_dirs', []);

// Stage default
set('default_stage', 'hml');

// Hosts
host('production')
    ->hostname('deploy@174.138.55.236')
    ->port(33458)
    ->stage('prod')
    ->set('keep_releases', 6)
    ->set('branch', 'master')
    ->set('deploy_path', '/var/www/html/production-my-app');

host('homolog')
    ->hostname('deploy@174.138.55.236')
    ->port(33458)
    ->stage('hml')
    ->set('keep_releases', 3)
    ->set('branch', 'develop')
    ->set('deploy_path', '/var/www/html/homolog-my-app');

// Tasks

task('npm_run', function () {
    // Descomente esse if após o primeiro deploy
    //if (has('previous_release')) {
    //    run('cp -R {{previous_release}}/node_modules {{release_path}}/node_modules');
    //}

    run('cd {{release_path}} && npm install');
    run('cd {{release_path}} && npm run prod');
});

// [Optional] if deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');

// Migrate database before symlink new release.

// Descomente essa linha após o primeiro deploy
// before('deploy:symlink', 'artisan:migrate');

Um detalhe muito importante para fazermos antes do primeiro deploy é comentar a última linha, ela é responsável em executar a migrate, se fizermos o deploy com a migrate sem antes configurar o arquivo .env no servidor, irá dar erro de conexão com o banco de dados e isso é fato.

Mais um teste rápido antes de iniciar, dentro do seu projeto local, digite no terminal o comando dep ssh:

Comando dep ssh
Executar login SSH no servidor

Viu que bacana? O deployer identificou que foi configurado dois hosts e ele te deu a opção de escolher em qual host deseja conectar via SSH. Informei o host de homologação e ele realizou o login no servidor.

A mensagem de erro dizendo que o diretório current não existe é normal, pois ainda não realizamos o deploy e a estrutura de diretórios que a ferramenta de deploy cria, não existe no momento.

Chega de enrolação, bora fazer esse deploy. Se ainda continua logado no servidor, digite exit para sair e voltar para sua máquina. Dentro do diretório do seu projeto digite o comando dep deploy. Veja que não passamos nenhum parâmetro, nesse caso o deploy será realizado no host de homologação.

Comando dep deploy
Realizando o deploy da aplicação

Agora precisamos configurar o arquivo .env, se tentar acessar o endereço IP na porta 81 irá receber um erro 500.

Logando no servidor através do comando dep ssh, dentro do diretório current de homologação (diretório atual quando se faz login usando o dep ssh), se abrirmos o arquivo .env verá que está em branco. Copie o conteúdo do arquivo .env.example para .env:

cp .env.example .env

Agora abra o .env e adicione as informações de acesso ao Mysql que criamos no início do tutorial. Salve as alterações e saia do arquivo.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=deployer
DB_PASSWORD=senhaforteaqui

É necessário gerar a chave de segurança do artisan para que a aplicação funcione corretamente, execute os comandos:

php artisan config:clear
php artisan key:generate

Nesse momento sua aplicação deve estar funcionando, acesse seu endereço IP na porta 81 e verifique se a página inicial do Laravel irá aparecer. Se não deu certo, verifique o log do Laravel para identificar o problema.

Vamos continuar pois ainda não acabou. Agora que configuramos nosso arquivo .env com as informações de acesso ao Mysql, vamos executar o deploy novamente agora com a opção de executar a migrate. Lembra que comentamos a linha responsável em executar a migrate, vamos descomentar esse linha e aproveite para descomentar a linha da tarefa do npm_run, ficando assim:

<?php

// Descomente o if da tarefa npm_run
if (has('previous_release')) {
    run('cp -R {{previous_release}}/node_modules {{release_path}}/node_modules');
}

// Descomente o artisan migrate
before('deploy:symlink', 'artisan:migrate');

Ou seja, antes da tarefa symlink, será executado as migrations. Vamos executar o deploy novamente:

Comando dep deploy com migrations
Deploy da migration realizado com sucesso

Veja que lindo que foi o resultado. Foi executada uma tarefa nova agora, a tarefa responsável em criar as migrations, veja a linha artisan:migrate no log acima.

Bom, agora se você acessar sua aplicação e realizar o cadastro de um usuário verá que funcionará lindamente.

Deploy em produção

Configuramos o deployer para que seja feito em dois ambientes, em homologação e produção. Ao executar o comando dep deploy é feito o deploy em homologação. Para que seja feito em produção é necessário informar um parâmetro a mais no comando, veja:

dep deploy prod

O prod na frente do comando vem do stage definido no arquivo deploy.php. Agora que você já sabe como fazer o deploy em homologação que tal fazer a mesma coisa para produção?

Execute os mesmos passos que você fez para homolog, crie um banco de dados para produção e após o primeiro deploy configure seu .env com os dados de acesso ao Mysql.

A implantação de uma ferramenta de deploy apesar de parecer simples, se torna um pouco complicado se o seu ambiente não está preparado ainda, por exemplo, as configurações essenciais do servidor como o Mysql, Nginx, PHP, etc...

Fico a disposição caso tenham alguma dúvida ou algum problema.

Até a próxima!