Ambiente PHP e Ngnix com Docker

Há alguns dias atrás precisei criar ou recriar um ambiente PHP semelhante ao ambiente de produção, em produção era utilizado o PHP na versão 7.2, com o Nginx na versão 1.14.0 e também o PHP-FPM que é o cara responsável por interpretar o código PHP já que o Nginx não consegue fazer isso, mais isso tudo diretamente pelo sistema usando unix sockets, tudo instalado diretamente no sistema.

Porém tive algumas dificuldades para criar o ambiente usando o fastcgi e então resolvi escrever este artigo para ajudar outras pessoas que venham a precisar e também para o meu eu do futuro que pode vir a precisar fazer algo semelhante.

Dockerfile

Bem, primeiro precisamos criar um Dockerfile na raiz do projeto para termos uma imagem com o nosso projeto php, e já com o php-fpm. Podemos ter o Dockerfile o seguinte conteúdo:

FROM php:7.2.24-fpm
COPY . /var/www/html
WORKDIR /var/www/html
RUN chmod -R 777 /var/www/html
EXPOSE 9000
CMD ["php-fpm"]

Explicando o Dockerfile

  • FROM php:7.2.24-fpm: Com isso vamos ter uma imagem com o php-fpm na versão 7.2.24.

  • COPY . /var/www/html: Aqui fazemos a cópia de todos os arquivos do projeto (.) para a pasta /var/www/html do container.

  • WORKDIR /var/www/html: E precisamos mudar o diretório de trabalho para que seja o primeiro e consigamos ter acesso aos arquivos nesse container.

  • RUN chmod -R 777 /var/www/html: Esse comando é para evitar possíveis erros caso o projeto precise criar ou gerenciar arquivos dentro do container.

  • EXPOSE 9000: Aqui liberamos a porta 9000 para podermos acessar de fora, ou no caso em outros conatiners.

  • CMD ["php-fpm"]: Por fim, executamos o php-fpm.

docker-compose

Agora que temos o Dockerfile, precisamos criar um docker-compose.yml na raiz do projeto para usarmos o Dockerfile criado anteriormente. E nesse docker-compose temos o seguinte conteúdo:

version: '3.7'

services:
  php:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: dev-site
    volumes:
      - ./nginx/php-fpm.conf:/etc/php/7.2/fpm/php-fpm.conf
    networks:
      - site-network

  nginx:
    image: nginx:1.14.0
    container_name: dev-nginx
    ports:
      - "8001:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/sites.conf/:/etc/nginx/conf.d/default.conf
    command: [ nginx-debug, '-g', 'daemon off;' ]
    networks:
      - site-network

networks:
  site-network:
    driver: bridge

Explicando o docker-compose

  • services: Temos dois serviços, um para o PHP e o outro para o Nginx.

  • Serviço php: Nesse bloco fazemos o build na imagem a partir do nosso Dockerfile criado anteriormente. Adicionamos um nome ao container para facilitar a utilização do mesmo. Também fizemos um bind do arquivo de configuração do php-fpm (php-fpm.conf) para o container. E por fim adicionamos a rede que é criada no bloco networks que é o último bloco do arquivo para podermos fazer a comunicação entre ambos os containers.

  • Serviço Nginx: Nesse bloco usamos uma imagem pronta do Nginx na versão 1.14.0 que é a versão que estava sendo executada em produção, também adicionamos um nome para facilitar o uso. E liberamos a porta 8001 da máquina para a porta 80 do container, isso porque podemos já utilizar a porta 80 da máquina por algum outro motivo. E adicionamos os volumes, na verdade fazemos apenas o bind do nginx.conf e do sites.conf que ambos estão dentro da pasta nginx na raiz do projeto com o /etc/nginx/nginx.conf e /etc/nginx/conf.d/default.conf respectivamente. Você pode estar se perguntando sobre esses arquivos, mas calma, ainda vamos ver esses arquivos mais adiante. Também executamos um comando para habilitar o modo de debug do nginx para podemos ver possíveis problemas quando executarmos o mesmo. E por fim a rede para comunicação com o container PHP.

  • networks: Criamos esse bloco apenas para criar a rede e conseguimos comunicar ambos os containers.

Nginx.conf

Como você deve ter visto anteriormente fazemos o bind da configuração do Nginx, isso porque nossa configuração pode ser diferente da configuração default. Mas nesse ponto mantive quase a mesma configuração do Nginx:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;

load_module modules/ngx_http_image_filter_module.so;

events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  65;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}

Não vou entrar muito nessa parte de configuração do Nginx porque penso que foge um pouco do escopo deste artigo.

Sites

E chegamos a outro ponto importante de toda essa configuração que é a comunicação entre o Nginx e o php-fpm, e essa configuração pode ser feita de duas formas.

Proxy reverso

A primeira forma é usando o proxy reverso do Nginx, e para isso utilizamos o proxy_pass para encaminhar a requisição para o nosso outro container.

Então naquele arquivo sites.conf que fizemos o bind para o container:

- ./nginx/sites.conf/:/etc/nginx/conf.d/default.conf

Teríamos o seguinte conteúdo:

upstream container {
    least_conn;
    server dev-site:9000;
}

server {
    listen 80;
    location / {
        proxy_pass http://container;
    }
}

Explicando: Aqui criamos um upstream com o nome container, e esse basicamente aponta para o nosso container, que pode ser acessado usando o nome que criamos lá no docker-compose dev-site e indicando a porta 9000 que foi a porta que liberamos no container (EXPOSE 9000). E em seguida, no bloco server passamos para o proxy_pass o upstream criado acima porém usando o HTTP, isso porque aqui estamos enviando via HTTP.

Fastcgi

A outra forma, e nesse caso a mais interessante é usando o fastcgi que é um protocolo para a comunicação entre o PHP e o Nginx. Também não vou entrar nesse foco aqui. Teremos a seguinte configuração naquele arquivo sites.conf (no lugar da configuração anterior):

server {
    listen 80;

    root /var/www/html;

    location / {
        include fastcgi_params;
        fastcgi_pass dev-site:9000;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
    }
}

Nesse caso não teremos o upstream, apenas o bloco server e os pontos importantes aqui são as linhas:

  • include fastcgi_params;: que inclui os outros parâmetros, que ficam no arquivo fastcgi_params dentro dos arquivos de configuração do Nginx.

  • fastcgi_pass dev-site:9000;: Essa linhas é responsável pelo direcionamento das requisições para o php-fpm, por isso indicamos o nome e a porta do nosso container do php dev-site:9000.

  • fastcgi_param SCRIPT_FILENAME $document_root/index.php;: E aqui indicamos qual o arquivo deve ser executado no container do php.

Conclusão

Agora é só subir os containers usando o comando docker-compose up estando dentro da pasta do projeto e logo em seguida acessar o endereço http://localhost:8001 no navegador.

18