No post anterior (Docker e Docker Compose com PHP), falei como montar um ambiente de desenvolvimento pra trabalhar com PHP usando Docker e Docker Compose. E com várias alternativas. Hoje, vou mostrar como eu uso esse ambiente para trabalhar com PHP e como colocar frameworks (usarei Laravel, Symfony como exemplo) dentro da mistura.

Usando os conteineres

No post anterior, mostrei várias alternativas, mas nesta trabalharei com esta configuração de ambiente (por questões de simplificação, mas funcionaria com qualquer uma delas):

docker-compose.yml

version: "3"
services:
    database:
        image: mysql:5.7.20
        restart: always
        environment:
            MYSQL_ROOT_PASSWORD: 12345
            MYSQL_DATABASE: usuarios
            MYSQL_USER: dbadmin
            MYSQL_PASSWORD: dbpassword
        volumes:
            - "data:/var/lib/mysql"
    webserver:
        image: webdevops/apache:alpine
        depends_on:
            - php
        ports: 
            - "80:80"
            - "443:443"
        volumes: 
            - ".:/var/www/html"
        environment:
            WEB_PHP_SOCKET: "php:9000"
            WEB_PHP_TIMEOUT: 600
            WEB_DOCUMENT_ROOT: "/var/www/html"
    php:
        image: mlalbuquerque/php:7.1
        build:
            context: ./dockerfiles
            dockerfile: php7.1.dockerfile
            args:
                - "UID=$UID"
                - "GID=$GID"
                - "USER=$USER"
        volumes:
            - ".:/var/www/html"
            - "./dockerfiles/config/php.ini:/usr/local/etc/php/php.ini"
            - "./dockerfiles/config/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini"
        environment:
            PATH: "/root/.composer/vendor/bin:${PATH}"
volumes:
    data:

Para ficar mais fácil, a imagem mlalbuquerque/php:7.1 encontra-se já no Docker Hub. Então, nem precisam construir a imagem usando o post anterior. Se quiser, ainda pode usar a imagem mlalbuquerque/php, que por padrão pega usaria a tag latest (mesma imagem anterior).

Neste caso, temos 3 serviços, database, php e webserver. Dependendo da pasta onde este arquivo estiver, ele irá criar 3 conteineres. Os nomes dos conteineres seguirá o seguinte padrão: <NOME_DA_PASTA>_<NOME_DO_SERVIÇO>_1. Ou seja, caso esteja dentro de uma pasta chamada teste, ele criará os seguintes conteineres:

  • teste_database_1
  • teste_php_1
  • teste_webserver_1

Se preferir, podem configurar os nomes dos conteineres no arquivo docker-compose.yml usando a diretiva abaixo em cada um dos serviços:

container_name: NOME_DO_CONTEINER

Assim, podem especificar os nomes dos ocnteineres de cada serviços, ficando mais fácil trabalhar com elas. Mas lembrem-se que neste caso, não poderemos criar várias instâncias do serviço (veremos isso em outros posts).

Nos exemplos, vou trabalhar com o fato de o nomes dos conteineres seguem o padrão e estão na pasta post. Então, relembrando, esses serão os conteineres:

  • post_database_1
  • post_php_1
  • post_webserver_1

Vamos iniciar os serviços:

$ docker-compose up

Agora podemos rodar comandos em qualquer dos 3 serviços pelo Docker!

Rodando Git

O Git está na imagem do PHP, pois tenho o Composer e este precisa do Git pra funcionar. Então, já que temos o Git instalado, porque não usar? Podem tranquilamente usar comandos do Git com o comando Docker abaixo:

$ docker exec -it post_php_1 git [COMANDO_GIT]

Por exemplo, podemos iniciar um repositório Git na pasta post usando o comando abaixo:

$ docker exec -it post_php_1 git init

Como existe o mapeamento da pasta local, ele vai rodar na pasta local o comando Git, iniciando um repositório. Depois, poderá rodar comandos como add, commit e push. SEM PRECISAR INSTALAR GIT NA SUA MÁQUINA!!!

Rodando o Composer

Composer também está na imagem e segue a mesma ideia do Git:

$ docker exec -it post_php_1 composer [COMANDO_COMPOSER]

Por exemplo, podemos atualizar o Composer usando o comando abaixo:

$ docker exec -it post_php_1 composer self-update

Ou instalar as dependências do projeto que estão em composer.json usando:

$ docker exec -it post_php_1 composer install

Criando alias para os comandos

Para facilitar minha vida, e não ter que ficar lembrando o tempo todo do comando completo do Docker, criei alias que direcionam para o uso do Git e Composer:

$ alias git="docker exec -it post_php_1 git"
$ alias composer="docker exec -it post_php_1 composer"

Agora, podemos rodar o comando git e composer dentro da pasta post, onde o projeto se encontra, e eles serão rodados como se fossem aplicativos da máquina hospedeira (afinal, existe o mapeamento para a pasta post no serviço php).

Se quiserem, ainda podem criar outros alias. Até específicos para suas necessidades. Alguns exemplos abaixo:

$ alias php="docker exec -it post_php_1 php"
$ alias php-terminal="docker exec -it post_php_1 php -a"
$ alias php-mods="docker exec -it post_php_1 php -m"
$ alias php-config="docker exec -it post_php_1 php -i"

Alias Globais

Nos exemplos acima sobre alias, eles funcionam apenas na pasta local post. Isto porque os conteineres têm um WORKDIR e os comandos sempre rodam nele. No caso do conteiner PHP post_php_1, seu WORKDIR é /var/www/html e existe um mapeamento desta para post na máquina host.

Então, como fazer alias que possam ser usados globalmente? É até simples:

$ alias git='docker run --rm -it -v $(pwd):/var/www/html mlalbuquerque/php:7.1 git'

Existem diferenças de como estávamos trabalhando até agora. Primeir, estamos usando aspas simples ao invés de aspas duplas na criação do alias. Isso se deve ao fato de que o alis com aspas simples apenas interpreta os comandos no momento da chamada do alias. Então, o mapeamento que estamos usando com -v $(pwd):/var/www/html, interpreta o comando $(pwd) apenas no momento em que é rodado.

Outra diferença é que usamos o comando docker run no lugar de docker exec. Este último é usado para executar comandos dentro de conteineres que já estão rodando. Por isso, usamos docker exec com o conteiner post_php_1 no exemplos na pasta post: os conteineres já estão rodando e simplesmente reaproveitamos.

Já com docker run estamos subindo um ocnteiner baseado numa imagem e rodando um comando nela. Então, no exemplo acima, estamos subindo um conteiner baseado na imagem mlalbuquerque/php:7.1 e rodando o comando git no ocnteiner e ainda por cima estamos mapeando um volume: a pasta onde estamos na máquina host para a pasta /var/www/html no conteiner. Com isso, conseguimos rodar o comando git na pasta local do host.

Por último, percebam que usei --rm no comando. Isso faz com que, depois que o comando rode dentro do conteiner, o conteiner será destruído. Ou seja, usei um conteiner temporário para rodar o comando mapeando para a pasta local do host.

Estou usando o Docker para rodar comandos ao invés de instalar esses mesmos comandos na minha máquina host!!!

Várias versões do PHP

Usando o exemplo acima, podemos, ainda, ter várias versões do PHP como comandos:

$ alias php-7.2="docker run --rm -it php"
$ alias php-7.1="docker run --rm -it php:7.1-cli"
$ alias php-7.0="docker run --rm -it php:7.0-cli"
$ alias php-5.6="docker run --rm -it php:5.6-cli"

Assim, podemos rodar comandos PHP de acordo com a versão que precisamos. Essa mesma ideia vale para outras ferramentas baseadas em PHP, como PHPUnit e Behat. E podemos extrapolar essas ideias para outras linguagens, como Go, Ruby, etc.

Conclusão

Então, não precisamos instalar nada em nossa máquina. Podemos usar comandos como PHP, Git, Java, Node, etc, tudo através do Docker, usando conteineres temporários e alias globais. MAS, nem tudo são flores: sempre veja se vale a pena fazer isso. Existem conteineres com IDEs completas e browsers. Eu acho que para coisas que precisam de GUI, pode instalar na máquina host mesmo, é mais performático. Mas comandos que precisamos no nosso dia-a-dia de trabalho, como no meu caso, PHP, Python, Node, Git, entre outros, podemos usar dessa maneira - “Dockerizando” os comandos - de forma bem tranquila.

Uma última dica: tente colocar mais comandos em um conteiner e tentar separar por contexto. Por exemplo, tem um conteiner que tenho vários comandos relativos a testes com PHP (PHPUnit, Behat, Codeception, etc.) e outro com checagem de código (PMD, Copy & Paste Detector, PDepend, Métricas, etc.). Então, basta criar os alias relativos a cada comando dentro do conteiner.

Espero ter ajudado e que venhas as perguntas, críticas e sugestões do que falar depois. E só para constar, abaixo está marcado o que já falei!

  • Montando um ambiente com PHP, Apache e MySQL (e alternativas) - Docker e Docker Compose com PHP
  • Como trabalhar com este ambiente - Conteinerizando PHP e outras coisas
  • Imagem do PHP já vir com algum framework (Laravel, Symfony e Zend Expressive)
  • Docker Compose para testes automatizados com Selenium
  • Configurar IDE para trabalhar com o XDebug do conteiner (Netbeans e Visual Studio Code)
  • Uso de shell scripts para automatizar mais ainda as tarefas (PHPUnit, Behat, etc)
  • Subir as imagens para o Docker Hub

Até o próximo post!!!