Bom, pessoal, comecei a programar há muitos anos e no decorrer desse tempo tenho tentado encontrar um formato adequado para meu sistema. Neste artigo vou tentar mostrar um pouco do estado atual.
Colocando um pouco de contexto, passei a utilizar Linux por volta de 1994. Até 2006, mudei diversas vezes de distribuição, até que conheci o Fedora – que na época se chamava Fedora Core, que utilizo até hoje.
Se você trabalha com Linux, sabe que temos dois modelos de distribuição: as rolling release e as fixed release; a última, fixed release, é a mesma adotada pelo Fedora. Isso significa que de seis em seis meses, mais ou menos, o sistema é atualizado; o que também significa que de seis em seis meses eu formato minha máquina, reinstalo o sistema, ambiente de desenvolvimento, etc.
Formatar a máquina e instalar a distro tendo o menor impacto possível sempre foi problema, já que formatar a máquina e reinstalar o sistema, quando se trabalha com diversos projetos, chaves públicas/privadas, gpg, códigos, ambientes configurados, é bastante problemático; especialmente para voltar a trabalhar.
Para simplificar a restauração do sistema, eu criava diversas partições; essa foi minha primeira medida e esse era meu layout inicial:
swap
/
/boot
/usr
/var/www/html
/src
/opt
/home
Por alguns anos esse foi o layout de partições que eu usei; funcionou bem por algum tempo, especialmente quando eu trabalhava com apenas uma tecnologia e tinha que configurar apenas um ambiente. Mas conforme o tempo foi passando, foi se tornando complicado ter diversos ambientes de desenvolvimento configurados; eu tinha o código, mas precisava – ainda – configurar outras coisas: servidor, banco de dados, linguagens/tecnologias/plataformas, etc.
Imagine só: se você trabalha com php, você precisa de um servidor web, um servidor de banco de dados, além da própria linguagem; aí precisa lidar com alguma coisa no front – ou mesmo renderizar a coisa –, instalar um npm e mais um monte de dependências, mas em determinado projeto você precisa usar Ruby, aí vai lá e instala o Ruby e um Capistrano; aí noutro projeto você precisa usar Ruby numa versão específica e você vai lá e instala um rvm para gerir essas versões. O mesmo ocorre com Java e qualquer que seja a tecnologia de trabalho.
Ter um layout específico para facilitar após uma formatação, deixou de ter o mesmo efeito que no passado, porque agora eu tinha o código, mas também precisava configurar o ambiente, e configurar um ambiente não é uma coisa que se faz rapidamente; muitas vezes essa configuração é complexa, demorada e já me fez desistir algumas vezes de atualizar o sistema, porque eu não queria ter que configurar o ambiente novamente.
Como aquele monte de partição havia perdido razão de ser, ela foi simplificada, e hoje tenho:
/
/boot/efi
/home
/src
Meu código ficou concentrado em /src e uso git com alguns remotes: tenho um remote na minha rede, outro num Github, Gitlab, Bitbucket ou qualquer que seja o caso, etc. Meu /home ficou com minhas coisas pessoais, como chave ssh, gpg, algumas autenticações, etc. Já o ambiente, que antes era complexo e problemático, fica em imagens num registry na minha rede. Por exemplo; vamos supor que eu vá trabalhar com PHP e precise da linguagem, de um servidor web e de um servidor de banco de dados:
mkdir -p /src/sample/php/src cd /src/sample/php git init echo -e "<?php\necho 'PHP é legal.';">src/index.php
A partir daqui, tenho um diretório de trabalho e um arquivo index.php
, mas como acabei de formatar a máquina, não tenho servidor web, banco de dados, nem nada. Em vez do ambiente da linguagem, instalo Docker:
sudo dnf -y install docker docker-compose
Com o compose instalado, vamos por partes; primeiro resolvemos o servidor – no caso, vou usar nginx:
version: "3" services: nginx: image: nginx ports: - "80:80"
Isso basicamente diz que estamos usando a versão 3 do arquivo de configuração do docker-compose e que, entre os serviços, temos um que demos o nome de nginx e usaremos a imagem nginx
. Como não informamos nenhuma tag, é feito o download da latest
, ou última. Estamos dizendo que a porta 80 da nossa máquina é mapeada para a porta 80 do container, ou seja, se acessarmos nosso browser agora, vamos ver a página de boas vindas do nginx:
Agora precisamos fazer o PHP funcionar! Para isso, precisamos:
- 1. Configurar o nginx para trabalhar com o PHP e,
- 2. Configurar o container para usar nosso código PHP:
mkdir nginx vim nginx/site.conf
nginx/site.conf
server { index index.php index.html; root /var/www/html; error_log /dev/stdout; access_log /dev/stdout; location ~ \.php$ { try_files $uri =404; fastcgi_pass php:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } }
Nosso arquivo de configuração do nginx está instruindo o servidor a ter como index, o index.php
ou index.html
; também está direcionando os logs para stdout
– pessoalmente gosto disso, pois me permite ver o que está acontecendo sem precisar acessar o container para ler os logs; finalmente, se o arquivo acessado for “algumacoisa.php
“, ele passa a requisito para o php-fpm – que estará na porta 9000 – lidar com ela.
Com o nginx configurado, o docker-compose.yml
é ajustado:
version: "3" services: nginx: image: nginx ports: - "80:80" links: - php volumes: - ./nginx:/etc/nginx/conf.d:ro - ./src:/var/www/html:ro php: image: php:7.2.8-fpm volumes: - ./src:/var/www/html:z
Agora temos dois serviços: o nginx
e o php
e no nginx
temos duas mudanças:
- 1. Link para o
php
- 2. Mapeamento de volumes.
O links
vai permitir que o container do nginx
consiga conversar com o container do php
; se observar no arquivo de configuração do nginx, verá que fizemos php:9000
; isso só foi possível porque fizemos o link. Na sequência, fizemos o mapeamento dos volumes; o diretório nginx
que tem nosso arquivo de configuração foi montado em /etc/nginx/conf.d
, e nosso src
com o código foi montado em /var/www/html
. Como precisamos que o código seja compartilhado e acessível pelos dois containers, mapeamos em ambos.
Agora, ao acessar nosso localhost no browser, veremos:
Com servidor web e php rodando, falta o MariaDB:
version: "3" services: mariadb: image: mariadb environment: - MYSQL_ROOT_PASSWORD=default123 nginx: image: nginx ports: - "80:80" links: - php volumes: - ./nginx:/etc/nginx/conf.d:ro - ./src:/var/www/html:ro php: image: php:7.2.8-fpm links: - mariadb volumes: - ./src:/var/www/html:z
Assim como o nginx, não usamos uma tag de versão para o mariadb
; a única coisa diferente ali, é que definimos a variável de ambiente MYSQL_ROOT_PASSWORD
com um valor qualquer. Esse valor será utilizado para conexões como root
para instalar o banco; para uma aplicação, são definidas outras variáveis, como MYSQL_DATABASE
, MYSQL_USER
e MYSQL_PASSWORD
, mas para o propósito do artigo, só o MYSQL_ROOT_PASSWORD
resolve.
Bom, agora ajustamos o código PHP para conectar nessa base:
src/index.php
<?php $pdo = new PDO('mysql:host=mariadb;dbname=mysql', 'root', 'default123'); $stm = $pdo->query('SELECT <code>User</code>, <code>Host</code> FROM <code>user</code>;'); foreach ($stm->fetchAll(PDO::FETCH_OBJ) as $row) { printf('%s@%s', $row->User, $row->Host); }
Lembra do link que permitia que o nginx visse o php na porta 9000? Aqui é a mesma coisa: para que o PHP consiga trabalhar com o MariaDB, precisamos fazer o link. Agora, se rodarmos esse código, veremos o seguinte no navegador:
O que aconteceu é que a imagem do PHP que estamos usando não tem a extensão do MySQL que é necessária para trabalhar com o banco de dados. Nesse caso, um Dockerfile
que faz a instalação é necessário:
FROM php:7.2.8-fpm RUN apt-get update && \ docker-php-ext-install pdo_mysql
Com isso, o docker-compose.yml
sofre uma última alteração:
version: "3" services: mariadb: image: mariadb environment: - MYSQL_ROOT_PASSWORD=default123 nginx: image: nginx ports: - "80:80" links: - php volumes: - ./nginx:/etc/nginx/conf.d:ro - ./src:/var/www/html:ro php: image: phpmariadb build: . links: - mariadb volumes: - ./src:/var/www/html:z
Quando o container for levantar, ele vai ver que a imagem phpmariadb
não existe; como temos um build
logo abaixo e um Dockerfile
, ele vai fazer a construção da imagem em vez de procurá-la no registry. Com a imagem construída e a extenção instalada, o código PHP agora funciona:
Agora que tenho uma imagem do PHP 7.2.8 configurada, posso enviá-la para um registry remoto, ou para o meu registry na minha rede local. De qualquer forma, se amanhã eu formatar minha máquina, basta fazer um pull
na imagem que tenho o ambiente PHP configurado e pronto para trabalhar.
O mesmo vai acontecer com Node, Ruby, .NET Core e qualquer que seja a tecnologia que eu precisar trabalhar. Em apenas alguns instantes, consigo ter meu código e meu ambiente prontos para trabalhar – mesmo em máquinas distintas da minha. Basta fazer o pull
, e pronto!