2 de out de 2015

Usando Docker em seu Projeto PHP

Introdução

Depois de muito tempo tentando apenas entender o que é o Docker, achei melhor aprender colocando a mão na massa: Fazer um projeto que usa PHP e PostgreSQL rodar no Docker.
O projeto escolhido foi o SiGE, um Sistema de Gerência de Eventos utilizado principalmente na organização do COMSOLiD (evento realizado por alunos e ex-alunos do IFCE Campus Maracanaú).

O que é o Docker?

 


Segundo o site
Docker é uma plataforma de código aberto para construção, implantação e execução de aplicações distribuídas. Ele oferece a programadores, equipes de desenvolvimento e engenheiros um conjunto de ferramentas comum para tirar vantagem da natureza distribuída e em rede de aplicações modernas.

Ou seja, ao montar um ambiente de desenvolvimento fica mais fácil imitar o ambiente de produção e testes. Imagine o seguinte cenário:

O servidor de produção possui o PHP 5.4 instalado juntamente com o PostgreSQL 9.3. Você, desenvolvedor, tem o PHP 5.5 e o PostgreSQL 9.4. Ao realizar uma tarefa você percebe que o código usado só funciona na versão 5.5 do PHP e que na 5.4, versão do servidor, não existe essa funcionalidade ainda. O que acontece é que você só vai perceber o erro quando o servidor estiver em produção, o que com certeza irá causar uma dor de cabeça.

Usando Docker você é capaz de usar exatamente as mesmas versões das ferramentas, tanto no servidor quanto local, sem que elas causem conflitos em sua máquina. O Docker utiliza uma tecnologia chamada LXC ou Linux Containers (na verdade essa é a tecnologia padrão, mas existem outras).

Podemos pensar no LXC como uma máquina virtual leve, isso porque diferente da máquina virtual que sobe uma instância do sistema, o LXC usa o mesmo kernel da máquina host com a vantagem do Kernel namespaces - Componentes virtuais: ipc, uts, mount, pid, network e user.


Instalação

 

No Linux: http://docs.docker.com/linux/step_one/
Além do Docker é ideal instalar também o Docker Compose http://docs.docker.com/compose/install/

Utilização

 

Obervação: Abaixo estão apenas os passos importantes para entender a ferramenta, veja o código completo no código-fonte.

As configurações de uma imagem do Docker devem ser feitas em um arquivo de texto, por padrão chamado de Dockerfile.
O SiGE precisa inicialmente do PHP. Optei por utilizar a imagem nmcteam/php56, ou seja, PHP 5.6.
Para utilizar a imagem basta colocar no seu Dockerfile:

FROM nmcteam/php56

COPY . /usr/share/nginx/html/site

WORKDIR /usr/share/nginx/html/site

RUN php -r "readfile('https://getcomposer.org/installer');" | php
RUN ["/bin/bash", "-c", "php composer.phar install"]
 
O arquivo acima mostra que nossa imagem vai ser criada a partir de nmcteam/php56 (FROM). Tudo que estiver no diretório atual (.) deve ser colocado em /usr/share/nginx/html/site (COPY). Ao executar comandos, execute a partir de /usr/share/nginx/html/site (WORKDIR). Em seguida o SIGE utiliza o composer do PHP (Não confundir com o Docker Compose!), para isso é preciso instalá-lo (RUN).
No final das contas esse arquivo é como um script bash que executa alguns comandos para configurar o sistema.
Para fazer o build da imagem execute:

docker build -t main/sige .
 
A opção -t é o nome da sua imagem, dê o nome adequado.
Arquivo completo com outros comandos necessários somente para o SiGE: https://github.com/comsolid/sige/blob/master/Dockerfile
Em seguida criei um Dockerfile separado para o PostgreSQL. Quando usar o Docker tente sempre separar os componentes do sistema em pequenos serviços que funcionam independentes um do outro (Micro Services!).

FROM postgres:9.4.4

COPY docker-entrypoint-initdb.d/database.sql /docker-entrypoint-initdb.d/database.sql

RUN pg_createcluster 9.4 main
 
A equipe do PostgreSQL oferece uma imagem oficial. Ao subir uma instância dessa imagem (container), um script irá executar qualquer arquivo .sql dentro do diretório docker-entrypoint-initdb.d/, use quando precisar subir uma base de dados populada (COPY).
Usaremos também a imagem do Nginx, mas como ela não precisa de muita configuração usaremos apenas declarada no Docker Compose.

Arquivo docker-compose.yml

 

O Docker Compose utiliza uma linguagem chamada YML para seu arquivo docker-compose.yml (arquivo padrão do Docker Compose).
É uma linguagem simples como JSON. Abaixo um exemplo para iniciar um serviço Nginx:

web:
  image: nginx
  ports:
    - "8080:8080"
 
Iniciamos com uma label (web), a partir da imagem nginx, e mapeando a porta 8080 para 8080 (a primeira indica a porta que você vai utilizar, a segunda é a porta interna, usada pelo container).
Em seguida podemos colocar nossa imagem do PHP 5.6 e PostgreSQL 9.4 fazendo uso de links, que faz com que um container converse com outro:

web:
  image: nginx
  ports:
    - "8080:8080"
  links:
    - php

php:
  image: main/sige
  links:
    - pg

pg:
  image: sige/pg
 
Veja que fizemos links de um serviço com outro para que eles conversem, ou seja, um serviço Nginx não conhece a linguagem PHP então ele pede que o serviço PHP execute o código para ele e entrega de volta o resultado.
Da mesmo forma quando um trecho de código PHP tenta acessar o banco de dados do serviço PostgreSQL.
Para subir todos os serviços de uma vez basta executar:

docker-compose up -d
 
Como foi dito anteriormente o Docker utiliza LXC. Se você executar ifconfig em seu terminal vai ver várias interfaces de rede configuradas. É assim que os containers se comunicam. Para saber o IP principal basta procurar pela inteface docker0.
Para ver os serviços rodando basta acessar (no meu caso): 172.17.42.1:8080

Conclusão

 

Lembrando que os trechos acima estão incompletos mas são essenciais. Se eu fosse mostrar passo a passo você provavelmente ia cansar de ler e desistir. Então se você leu e se interessou abaixo estão os links de todos os arquivo envolvidos:
Você provavelmente não precisará mexer muita coisa, mas por exemplo meu arquivo https://github.com/comsolid/sige/blob/master/docker/nginx/vhost.conf está configurado para uso do Zend 1.12 (que usa reescrita (rewrite) da URI). Basta lembrar que a tecnologia não mudou, apenas aumentamos um nível de acesso. Quer dizer, se você tem um arquivo vhost.conf que roda fora do Docker, ele pode ser usado dentro do Docker sem problemas.
É isso aí, até a próxima!