Docker com Symfony e Apache

Você já passou por aquele momento em que termina de desenvolver uma feature, com todo amor e carinho, faz seu commit, atualiza o servidor de homologação e: no servidor não funciona? Afinal, o que muda da sua máquina de desenvolvimento pra máquina de produção/homologação?

Nesse post, vamos aprender como evitar aquele famoso caso do “funciona na minha máquina não sei porque não funcionou no deploy” =)

Garantindo reprodução de ambientes

As vezes, você desenvolve em um Mac e o servidor é um Linux. As vezes, você pode até ter o mesmo sistema operacional mas suas dependências não são as mesmas do servidor. As vezes, é o servidor quem não tem as mesmas dependências que você!

A verdade é que entre a sua máquina de desenvolvimento e qualquer outra máquina (inclusive servidores), existem diversas diferenças e seria muito difícil garantir manualmente que ambas fossem idênticas justamente porque temos muitas variáveis nesse processo.

Então não tem jeito?

Se a gente pudesse criar nossas próprias máquinas talvez esse processo fosse mais fácil. Afinal, se foi você quem criou a máquina toda, é você quem decide qual sistema operacional irá rodar nela, em qual versão, com quais dependências e até qual versão do PHP virá instalada.

Porém, não podemos fazer isso com máquinas físicas. Afinal, formatar sua máquina seria um trabalhão e ninguém quer passar por esse tipo de coisa pra ter um bom ambiente de desenvolvimento.

Usando VMs

Nossa solução seria criar máquinas virtuais (Virtual Machine). Ou seja, precisamos criar um ambiente dentro da nossa máquina que seja capaz de reproduzir exatamente um sistema operacional, com o PHP e o Symfony dentro.

A verdade é que esse tipo de solução não é nenhuma novidade e plataformas para utilização de máquinas virtuais como o grande VirtualBox são utilizadas há bastante tempo.

Porém, criar um sistema operacional todo, dentro de outro sistema operacional tem um grande custo de processamento. Porque agora você está consumindo o dobro da sua máquina e isso pode prejudicar bastante a escalabilidade da aplicação.

Reduzindo o peso das VMs

Aqui, o ideal, seria reduzir as funcionalidades dessa máquina virtual pro mínimo viável.

Tanto porque, a gente só quer usar ela pra rodar um sistema com o ambiente. Não precisamos de nenhuma parte gráfica, por exemplo. Um terminal já resolveria o problema e gastaria muito menos memória.

Nesse contexto, surgiram os containers: Máquinas virtuais simples com acesso ao terminal.

E como eu crio um container?

Por isso, em 2013, a galera criou o famoso Docker:

Uma forma de criar containers extremamente leves que nos dão a capacidade de encapsular completamente os ambientes de desenvolvimento, teste, homologação e produção.

E se eu precisar de mais de um container?

É bem comum, e uma boa prática, a gente ir além de criar apenas um container pra toda a aplicação.

Afinal, se você separa o PHP em uma máquina virtual e o Mysql em outra fica muito mais fácil decidir, por exemplo, quando derrubar/reiniciar uma delas independentemente da outra.

Pra isso, existe o docker-compose, uma forma de relacionar e gerenciar containers em Docker =)

Beleza, e como eu uso?

O primeiro passo é instalar o Docker e o Docker-Compose na sua máquina. Se você usa Windows ou Mac é só baixar o Docker Desktop executar o arquivo e ser feliz.

Se você usa Linux, eu recomendo dar uma olhada no tutorial da Digital Ocean de como instalar o Docker porque no linux temos alguns detalhes de permissão e atualização dos pacotes. Tendo o Docker instalado é importante instalar, também, o docker-compose.

Pra garantir que tudo deu certo, basta verificar a versão do docker e do docker-compose no terminal:

verificando a versão do docker-compose
verificando a versão do docker

Criando containers

Agora que temos tudo instalado, podemos começar a definir containers com tudo que a gente precisa para executar uma aplicação em Symfony.

PHP

Pra começar, vamos precisar de um container que rode o PHP. Afinal, sem o PHP não temos Symfony.

Para definir exatamente qual versão do PHP vamos usar, quais bibliotecas vamos precisar, etc. precisamos criar o famoso Dockerfile.

No nosso caso, vamos criar o Dockerfile do PHP dentro da pasta .docker/php:

criando o arquivo Dockerfile do PHP

Além disso, seria legal já deixar disponível nessa pasta os arquivos de configuração do PHP, como o php.ini e o php-fpm-pool.conf.

Adicionando os arquivos de configuração

Assim, podemos copiar esses arquivos e injetar eles dentro dos nossos containers. E, caso algum detalhe mude na configuração do php.ini em algum momento, é só alterar lá =)

Conteúdo do Dockerfile do PHP comentado

Apache

Além disso, pra distribuir a aplicação com um servidor e acessar, vamos precisar de um container responsável por rodar o Apache ou Nginx.

Do mesmo jeito que a gente fez com o PHP, vamos precisar de um Dockerfile do Apache também.

Aqui também é interessante já trazer arquivos de configuração relacionados ao apache. Como as configurações de virtual hosts do Symfony no diretório vhosts/sf4.conf:

Criando o Dockerfile do apache e trazendo o arquivo de configuração do apache pro Symfony

E o Mysql?

No caso do Mysql, é muito raro a gente querer alguma configuração específica pra ele. Normalmente, as imagens disponíveis lá no docker são suficientes =)

Juntando tudo

Já que o responsável por gerenciar os containers, no nosso caso, é o docker-compose, vamos precisar criar um arquivo de configuração informando quais containers precisamos e de onde ele vai puxar a configuração de cada um deles.

Configurando o docker-compose

Para configurar o docker-compose, podemos criar um arquivo chamado docker-compose.yml na pasta raiz do projeto:

Criando docker-compose.yml

E, finalmente, podemos definir e apontar tudo relacionado aos nossos containers:

docker-compose.yml com comentários explicando a estrutura do arquivo

Aqui, é extremamente importante dar uma olhada lá na documentação do docker-compose pra se aprofundar no poder da ferramenta =)

E pra executar isso tudo?

Agora que temos tudo instalado e configurado com nossos containers, basta rodar o comando:

docker-compose build

Na pasta raiz do projeto para baixar todas as dependências e instalar tudo relacionado ao projeto:

Executando o comando docker-compose build

Ao executar esse comando, o docker-compose vai olhar lá pro nosso arquivo de configuração docker-compose.yml que também está na pasta raiz do projeto para consultar todas as dependências que a gente deixou configurado.

Após baixar tudo, podemos subir as máquinas do docker com o comando:

docker-compose up

ou

docker-compose up -d

Caso você queira executar os containers sem ocupar o terminal =)

Executando o comando docker-compose up

E como eu mudo de dev pra prod?

Para alterar o ambiente, basta alterar o arquivo de configuração .env do próprio Symfony na chave APP_ENV.

Além disso, é importante lembrar de apontar sua string de conexão com o mysql para o container, usando os usuários e senhas definidos lá no docker-compose.yml:

.env configurado para usar o docker

O arquivo de configuração anterior está no ambiente de desenvolvimento, se a gente quiser trocar pra produção, basta alterar:

APP_ENV=prod

E, pronto!

Agora, é só acessar a porta que a gente apontou o apache lá no docker-compose.yml. No nosso caso é a porta 80:

Sistema em Symfony rodando na porta 80 do apache do Docker

Para derrubar os containers, basta rodar o comando:

docker-compose down

Na pasta raiz do projeto

Derrubando os containers

Com isso, conseguimos executar o Symfony no docker =)

Porém, o processo não seria muito diferente para outros frameworks MVC em outras linguagens.

Isso porque, provavelmente, teríamos uma configuração muito parecida dos containers. Afinal, boa parte das aplicações web precisam de um banco de dados e um servidor =)

A maior dificuldade nesse processo todo é configurar o Docker a primeira vez, escolher as imagens, as bibliotecas, etc.

Considerações finais

Agora temos o poder de executar não só a aplicação, mas todo o ambiente ao redor dela. Isso significa que o servidor de produção e o nosso são executados em cima da mesma máquina, com as mesmas dependências nas mesmas versões.

E, com isso, com certeza nunca mais vamos presenciar o bug mágico que só acontece em produção.

No mínimo, se acontecer em prod também vai acontecer em dev ;)

E ai, o que você achou de usar o Docker para executar uma aplicação em Symfony? Já usou o Docker em outros contextos? Compartilha aqui com a gente nos comentários =)