Docker Networks - Ou como configurar PHP-FPM+MySQL no Docker com o Nginx externo


Quando eu postei o tutorial completo sobre Docker, muitos anos atrás, algumas pessoas comentaram pedindo mais detalhes a respeito desse trecho aqui que comentei no post:

Esse blog que vos fala, por exemplo, está rodando sob dois containers do Docker, um para o php-fpm, outro para o MySQL. Onde está o Nginx? Está fora, instalado na máquina principal, e aí o Nginx se conecta com o php-fpm para poder renderizar essa página da forma que você vê.

Bom, depois de.. *check notes*, mais de OITO anos, chegou a hora de eu detalhar como funciona isso, afinal, antes tarde do que mais tarde, certo? Então vamos lá.

O Docker, como vocês bem sabem se vocês leram o post referido acima, consegue isolar os processos em containers que compartilham o mesmo kernel, mas cada processo deve ser um container próprio, isolado. Apesar disso, é possível estabelecer um setup onde os containers comunicam entre si, e também com o mundo exterior.

No caso desse blog que vos fala, conforme referido no trecho citado acima, temos três peças (2 rodando em containers) se comunicando entre si:

  1. Nginx - Que é instalado na máquina principal, e fica escutando na porta 80 e 443, e por onde a maior parte dos usuários se conecta
  2. MySQL - Que escuta na clássica porta 3306, mas que NÃO é exposta para fora de container algum, em nenhuma porta. Ficando mais "isolado", por assim dizer.
  3. PHP-FPM - Que escuta na porta 9000 dentro do container, mas é exposta apenas localmente (e isso é importante!) dentro da máquina como porta 2004. A ideia é que o Nginx consiga usar a porta 2004 rodando no host 127.0.0.1 (ou seja, o melhor host, o localhost!) para se comunicar com o PHP-FPM.

Note que em nenhum momento eu mencionei que há um link (que usando o legacy linking system seria algo tipo -l mysql:mysql) entre o PHP-FPM e o MySQL. Mas, como você pode imaginar, por causa do WordPress tal conexão se faz necessária, caso contrário esse blog não funcionaria pois o PHP nunca conseguiria se comunicar com o MySQL (afinal, os containers são isolados, certo?).

Para fazer isso, usamos o que o Docker chama de "Docker User-Defined Network", ou Rede Docker definida pelo usuário, em tradução livre. O que é isso? Imagine que cada container Docker - apesar de rodar um único processo cada - é como se fosse uma rede de computadores. Com base no que sabemos até então, temos a seguinte situação:

Diagrama mostrando você se conectando ao Nginx, que por sua vez se comunica com o PHP-FPM, mas sem se comunicar com o MySQL

Um rede definida pelo usuário, no Docker, nada mais é do que uma rede DENTRO do próprio Docker. No caso desse blog, temos uma rede no Docker chamada fjorgemota-com, e a estrutura fica mais ou menos assim:

Diagrama mostrando você se conectando ao Nginx, que por sua vez se comunica com o PHP-FPM e este se comunica com o MySQL

O que que isso implica, necessariamente? Implica que, assim como no caso da rede LOCAL na sua casa, onde seu celular consegue se comunicar com seu computador e vice-versa, cada container DENTRO da rede fjorgemota-com também consegue se comunicar um com o outro. Porém, da mesma forma que alguém externo à rede local da sua casa NÃO consegue se conectar diretamente ao seu computador ou ao seu celular, um container que esteja FORA da rede fjorgemota-com também não consegue se comunicar nem com o MySQL, e nem com o PHP.

Entretanto, assim como você PODE expor o seu computador conectado na rede interna da sua casa para o mundo exterior, o fato de um container estar dentro de uma rede definida por usuário também não o impossibilita de expor portas da mesma forma, como no caso do container do PHP-FPM cuja porta 9000 é exposta como 2004 dentro do servidor.

Tutorial

Como a configuração do Nginx pode variar DEMAIS de máquina para máquina, vamos fazer uma configuração mais simples aqui, usando o Caddy, que é bem mais simples de configurar (bastando um simples arquivo de configuração!). Vamos ao tutorial:

1) Baixe e instale o Docker (importante ter uma máquina de 64 bits, pois é o único ambiente no qual o Docker efetivamente roda..)

2) Baixe e instale o Caddy para sua máquina (basta clicar no botão de Download na página, você não precisa de nenhum plugin extra)

3) Crie uma rede definida por usuário, usando docker network create tutorial-fjorgemota-com. A saída desse comando deve ser um simples hash, conforme mostrado abaixo:

4) Agora, vamos criar os containers! Primeiro, inicialize o MySQL usando docker run --name bd-tutorial-fjorgemota-com --network tutorial-fjorgemota-com --network-alias mysql -e MYSQL_DATABASE=tutorial_blog -e MYSQL_ROOT_PASSWORD=12345678 -d mysql. A saida do Docker deve ser algo assim:

5) Baixe o WordPress e descompacte-o para uma pasta chamada wordpress. No Linux, você pode usar wget -O wordpress.zip https://br.wordpress.org/latest-pt_BR.zip && unzip wordpress.zip, conforme mostra a saída abaixo:

7) Crie um arquivo chamado Dockerfile para preparar a imagem do PHP-FPM para ter as extensões necessárias para rodar o WordPress. O arquivo deve ter o seguinte conteúdo:

FROM php:8.3-fpm 
RUN apt-get update && apt-get install -y \
    libfreetype6-dev \
    libjpeg62-turbo-dev \
    libpng-dev \
    libxpm-dev \
    libwebp-dev \
    libxslt1-dev \
    libzip-dev \
    libonig-dev \
    libtidy-dev \
    libicu-dev \
    imagemagick \
    libmagickwand-dev \
    less \
    && pecl install imagick \
    && pecl install apcu \
    && docker-php-ext-configure gd --with-freetype --with-jpeg --with-xpm --with-webp \
    && docker-php-ext-install mbstring mysqli zip bcmath exif xsl intl pdo_mysql gd tidy \
    && docker-php-ext-enable imagick apcu \
    && apt-get clean && rm -rf /var/lib/apt/lists/*

Para contextualização, aqui estamos apenas algumas dependêcnias necessárias para a execução do WordPress, uma vez que a imagem php:8.3-fpm vem sem as extensões necessárias instaladas/ativadas.

8) Construa a imagem acima usando docker build -t tutorial-php-fjorgemota-com .

O comando deve mostrar algo assim:

9) Rode o container do PHP-FPM usando essa pasta do wordpress como volume, e mapeando para /wordpress. Sem entrar na pasta wordpress, criada acima, o comando fica assim docker run -v $(pwd)/wordpress:$(pwd)/wordpress --network tutorial-fjorgemota-com -p 127.0.0.1:2004:9000 -d tutorial-php-fjorgemota-com

Esse comando deve ter um simples hash como saída, conforme mostrado abaixo:

Com isso, você deve ter o container rodando PHP-FPM rodando na mesma rede do container do MySQL. E o PHP-FPM deve estar exposto na porta 2004 no seu computador.

7) Agora, vamos configurar o Caddy para se conectar no PHP-FPM na sua máquina. Na mesma pasta onde está a pasta wordpress, crie um arquivo chamado Caddyfile com o seguinte conteúdo:

:8080 {
    root * wordpress
    encode gzip
    php_fastcgi 127.0.0.1:2004
    file_server
}

8) Então, execute caddy run Caddyfile

Se tudo deu certo, você deve ver algo assim:

[fernando@fernando-ryzen ~]$ caddy run
2024/07/07 22:39:08.186	INFO	using adjacent Caddyfile
2024/07/07 22:39:08.187	WARN	Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies	{"adapter": "caddyfile", "file": "Caddyfile", "line": 2}
2024/07/07 22:39:08.188	INFO	admin	admin endpoint started	{"address": "localhost:2019", "enforce_origin": false, "origins": ["//127.0.0.1:2019", "//localhost:2019", "//[::1]:2019"]}
2024/07/07 22:39:08.188	INFO	tls.cache.maintenance	started background certificate maintenance	{"cache": "0xc000429880"}
2024/07/07 22:39:08.188	INFO	http.log	server running	{"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2024/07/07 22:39:08.188	INFO	autosaved config (load with --resume flag)	{"file": "/home/fernando/.local/share/caddy/autosave.json"}
2024/07/07 22:39:08.188	INFO	serving initial configuration
2024/07/07 22:39:08.189	WARN	tls	storage cleaning happened too recently; skipping for now	{"storage": "FileStorage:/home/fernando/.local/share/caddy", "instance": "cf543b53-950d-435f-8cfc-59ea67aff4c5", "try_again": "2024/07/08 22:39:08.189", "try_again_in": 86399.99999981}
2024/07/07 22:39:08.189	INFO	tls	finished cleaning storage units

9) Acesse http://127.0.0.1:8080 no seu navegador. Você verá a tela de instalação do WordPress:

Clique em "Vamos lá!"

10) Nessa tela de configuração do banco de dados:

Informe os seguintes dados:

  • Nome do banco de dados: tutorial_blog
  • Nome de usuário: root
  • Senha: 12345678
  • Servidor do banco de dados: mysql
  • Prefixo da tabela: wp_

Clique em "Enviar"

11) O WordPress te pedirá para criar um arquivo wp-config.php com o código que ele passar:

Entre na pasta wordpress e crie o arquivo wp-config.php com aquele conteúdo informado.

Clique em "Instalar"

12) O WordPress te pedirá mais algumas informações, agora a respeito do site. Preencha com os dados que bem entender:

Clique em "Instalar WordPress"

13) O WordPress estará instalado:

Agora você tem o Caddy conectando ao PHP-FPM rodando dentro de um container Docker, e se comunicando com o MySQL em outro container Docker dentro da mesma rede.

Conclusão

Como dá pra ver, mesmo com o uso de containers, não há a necessidade de se preocupar em colocar TUDO dentro do Docker. É possível ter uma certa flexibilidade, ainda mais em termos de serviços de rede (como o MySQL ou o PHP-FPM).

Naturalmente, entretanto, ter mais containers rodando no Docker tende a facilitar mais a vida em alguns cenários. Em ambientes de desenvolvimento, por exemplo, quando você usa algo como o Docker Compose, todo o processo de instalação e configuração de um projeto se resume a alguns poucos comandos. Não há a necessidade de, por exemplo, pedir pela instalação de softwares adicionais (como o Caddy, no caso desse post), nem de configurações extras.

Portanto, como tudo na vida, decidir como estruturar sua aplicação é uma questão de custo benefício. No meu caso, o blog roda dessa forma (só que usando Nginx em vez do Caddy) pois isso me facilita a instalação e configuração de outros projetos, além do blog. Por exemplo, posso usar o Gitea (que é uma alternativa mais moderna ao Gogs, sobre o qual já falei aqui no blog), o Dokku e outros serviços tendo apenas um servidor web como "porta principal" do servidor. Logo, acaba valendo mais a pena ter tudo centralizado em uma única configuração do Nginx.

E você? Como gosta de estruturar os seus projetos? Compartilhe nos comentários!


Posts relacionados


2 respostas para “Docker Networks - Ou como configurar PHP-FPM+MySQL no Docker com o Nginx externo”

  1. Avatar de Fernando Paladini
    Fernando Paladini

    Por quê você escolheu usar uma imagem do php-fpm ao invés de uma imagem do próprio WordPress? Teria como fazer o mesmo, utilizando uma imagem do WP, mas mesmo assim utilizando o Nginx (ou outro recurso) na máquina host?

    1. Avatar de Fernando

      Então, eu escolhi pois é o setup que eu tenho aqui no blog, e usar a imagem do php-fpm possibilita que você use apenas um container para mais de uma aplicação PHP.

      Vamos dizer que você tenha uma aplicação "symfony" na mesma pasta onde está a pasta "wordpress", o comando para iniciar o php-fpm ficaria assim: docker run -v $(pwd)/wordpress:$(pwd)/wordpress -v $(pwd)/symfony:$(pwd)/symfony --network tutorial-fjorgemota-com -p 127.0.0.1:2004:9000 -d tutorial-php-fjorgemota-com - e bastaria ajustar o Caddyfile (ou a configuração do Nginx) para rodar ou em portas diferentes ou em sub-domínios diferentes:

      :8080 {
          root * wordpress
          encode gzip
          php_fastcgi 127.0.0.1:2004
          file_server
      }
      :8081 {
          root * symfony
          encode gzip
          php_fastcgi 127.0.0.1:2004
          file_server
      }
      

      E tudo continuaria funcionando de maneira usual.

      Teria como fazer o mesmo, utilizando uma imagem do WP, mas mesmo assim utilizando o Nginx (ou outro recurso) na máquina host?

      Ter, tem. Mas teria que usar a variante *-fpm da imagem do WordPress. Além disso, teria que usar algo como -v $(pwd)/wordpress:/var/www/html em vez de -v $(pwd)/wordpress:$(pwd)/wordpress

      Espero que isso tenha respondido suas dúvidas, e valeu pelo comentário!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.