Utilizando uma imagem base para suas aplicações PHP
Tags: docker, php, laravel
Published: 2022-07-28 18:04:00

Para garantir a padronização das aplicações e facilitar o trabalho do dia a dia, na [Convenia](convenia.com.br), utilizamos uma imagem base para todas as aplicações. O proposito desse post é mostrar as vantagens dessa abordagem e como criar suas aplicações a partir dessa imagem.

## Porque padronizar a imagem base?

Na [Convenia](convenia.com.br) temos uma stack bem homogênea quase que inteiramente em [PHP](https://www.php.net/), manter uma imagem diferente em cada projeto aumenta a chance de aparecer um problema específico com uma versão específica de uma biblioteca que está instalada apenas em um projeto, o desenvolvedor que mantém esse projeto terá que resolver esse problema e não terá como reaproveitar o conhecimento dos amigos visto que esse problema ocorreu apenas no projeto dele.

Seguindo a mesma linha de raciocínio imagine o quão diferente os setups podem parecer sem uma padronização, manter essa padronização ajuda bastante quando o desenvolvedor muda de projeto ou mesmo quando ele faz um "merge request" em um outro projeto, dessa forma ganhamos agilidade por não ter que estudar especificidades de cada projeto.

Novas features e correções de bugs são centralizadas em apenas uma imagem ao invés de serem feitas em cada projeto.

Bom e finalmente eliminamos o famoso "na minha maquina funciona". Já que estamos utilizando um container padronizado no ambiente de desenvolvimento porque não utilizar o mesmo container em ambiente de produção(com alguns cuidados claro)? Isso praticamente erradica a possibilidade de ocorrer um erro proveniente da discrepância entre uma biblioteca visto que quase todas as bibliotecas vão ser idênticas. Esse risco só não é nulo porque inevitavelmente vamos ter que instalar alguma extensão antes de enviar para produção(opcache por exemplo).

Em algum momento teremos que gerar uma nova imagem a partir da imagem base contendo a aplicação para rodar em produção. Se todos os projetos tem a necessidade de ter um webserver por exemplo porque não adicionar esse webserver já na imagem base? Isso vai tornar a etapa de build do pipeline muito mais rápida visto que passamos a maioria das coisas comuns aos projetos para a imagem base, temos um deploy mais ágil!


## Utilizando a imagem base

Para rodar uma aplicações PHP da forma convencional são necessários 2 containers, o [PHP-FPM](https://www.php.net/manual/pt_BR/install.fpm.php) e um servidor web como [nginx](https://www.nginx.com/) ou [apache](https://httpd.apache.org/). Rodar esses 2 componentes separadamente, apesar de ser o recomendado, demanda que o desenvolvedor mergulhe em detalhes referentes à comunicação entre os containers e até mesmo configuração de volumes, muitas vezes esses detalhes não são interessantes no momento, desenvolvedores iniciantes se sentem bem perdidos nesses detalhes.

Analisando outras stacks como nodejs vamos perceber que é relativamente mais simples containerizar esse tipo de aplicação, pois o próprio processo do node já é capaz de servir os requests sendo necessário apenas um container, mas e se podessemos rodar uma aplicação PHP com a mesma simplicidade? Essa é a proposta da imagem [PHP Full](https://hub.docker.com/r/convenia/php-full) que utilizamos aqui na convenia como imagem base de todas as aplicações PHP.

Vamos executar a imagem PHP Full com o seguinte comando:

```
docker run --rm -p 80:80 convenia/php-full:latest
```

O comando acima executa um container apartir da image `convenia/php-full:latest` e binda a porta 80 do container à porta 80 do host, logo ao entrar no nosso localhost pelo browser(em windows e macOS pode ser necessário utilizar o IP da vm do docker, dependendo do seu setup) devemos ver a página da documentação:

![Página padrão do container contendo a documentação](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ic7jr0iph4i64wxx0ovf.png)

Essa documentação é um arquivo PHP, isso significa que a imagem já está pronta para servir nossa aplicação, tudo que precisamos fazer é colocar nossa aplicação dentro do diretório `/var/www/app` que é onde a imagem procura por padrão.

Para fazer um lab bem rápido vamos executar um comando no terminal para instalar uma aplicação [Laravel](https://laravel.com/) fresh:

```
docker run --user 1000 --rm -v $(pwd):/app composer create-project laravel/laravel
```

Isso deve utilizar o [composer](https://getcomposer.org/) para criar uma aplicação Laravel bem na pasta em que executamos o comando. Em seguida vamos executar o seguinte comando para criar um container "colocando" a aplicação Laravel que criamos no passo anterior dentro da pasta `/var/www/app`, é nessa pasta que o webserver dentro do container olha para servir a aplicação:

```
docker run --rm -p 80:80 -v $(pwd)/laravel:/var/www/app convenia/php-full:latest
```

Após executar o comando podemos entrar no nosso browser novamente porém agora o resultado é esse:

![Página padrão do Laravel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/paknc650djxtq735n2wo.png)

Pronto finalmente estamos rodando nossa aplicação PHP dentro do container, e tudo que fizemos foi copiar a aplicação para uma pasta específica dentro do container, esse é o propósito dessa imagem, prover uma forma simples e segura para executar aplicações PHP podendo ser utilizado em ambiente de desenvolvimento e produção.

## Criando um arquivo Docker Compose

Já sabemos como utilizar nossa imagem porém em um ambiente real podemos sentir a necessidade de rodar mais de um container, containers de serviços como mysql e redis para nossa aplicação e o próprio container da nossa aplicação pode começar a ficar complexo pois pode surgir a necessidade de utilizar mais volumes, podemos definir uma network para isolar nossa aplicação. Logo o comando para rodar esse container vai ficando cada vez mais complexo e rodar a stack completa se torna uma tarefa árdua. Para resolver esse problema utilizamos um arquivo `docker-compose.yml` que deve ser criado na raiz do projeto:

```yml
version: '3.3'
services:
  app:
    image: convenia/php-full:latest
    container_name: application
    volumes:
      - .:/var/www/app
    ports:
      - 80:80
```

Esse arquivo contém todos os detalhes que incluímos via linha de comando quando rodamos nosso container e ele é versionado junto com a aplicação, dessa forma qualquer pessoa que pegar o projeto consegue executar a stack toda com um único comando:

```
docker-compose up -d
```

Após executar esse comando devemos ver o mesmo resultado de quando utilizamos o comando anterior, devemos ver a aplicação rodando no browser.

## Conclusão

Com certeza padronizar uma imagem base nos seus projetos não vai resolver todos os seus problemas mas ajuda a diminuir a complexidade na implantação e evita silos de conhecimento relacionados ao setup local, espero ter contribuído com esse post, deixe sua opinião nos comentários ou me da um grito no [twitter](https://twitter.com/ConveniaTech).