August 12, 2017

Development Workflow with Docker

We cover how to make running complex Docker commands quickly and easily.

We saw how we could use docker-compose to easily get a dev environment up and running.

However, we had to run some commands that get pretty long. For example:

docker run -it --rm \
    -v $(pwd)/application:/opt \
    -w /opt \
    --network=zondaroad_appnet \
    shippingdocker/php \
    php artisan make:auth

This gets to be a huge pain when running commands often, such as running phpunit or artisan commands to generate scaffolding.

A Makefile

To combat the pain of writing long Docker commands, we can create a script to help us out. Bash can be used here, but I like using a Makefile, as the syntax is easy to get started with.

For example, which a file named Makefile, we can create command "foo":

foo:
    bar

And use it:

make foo

Pretty easy!

Let's make a simple one:

.PHONY: up down

up:
    docker-compose up

down:
    docker-compose down

We can run make up, make down.

What we've done:

  1. Let Make know that the listed commands are "phony" - they don't generate artifacts (files)
  2. Command up will start our services
  3. Command down will stop our services

More Advanced Makefile

Let's add onto this:

.PHONY: up down

# Set dir of Makefile to a variable to use later
MAKEPATH := $(abspath $(lastword $(MAKEFILE_LIST)))
PWD := $(dir $(MAKEPATH))

up:
    docker-compose up

down:
    docker-compose down

test:
    docker run -it --rm \
        -v $(PWD):/opt \
        -w /opt \
        --network=zondaroad_appnet \
        shippingdocker/php:latest \
        ./vendor/bin/phpunit

We created a command make test which will spin up a PHP container within our network and run phpunit.

Logs

We can tail some logs too. I spent some time to try to get colorized output of log files too:

.PHONY: up down log

# Modified From http://bytefreaks.net/gnulinux/bash/tail-logs-with-color-for-monolog
log:
    tail -f $(PWD)storage/logs/laravel.log | awk '\
        {matched=0}\
        /INFO:/    {matched=1; print "\033[0;37m" $$0 "\033[0m"}\
        /WARNING:/ {matched=1; print "\033[0;34m" $$0 "\033[0m"}\
        /ERROR:/   {matched=1; print "\033[0;31m" $$0 "\033[0m"}\
        /Next/     {matched=1; print "\033[0;31m" $$0 "\033[0m"}\
        /ALERT:/   {matched=1; print "\033[0;35m" $$0 "\033[0m"}\
        /Stack trace:/ {matched=1; print "\033[0;35m" $$0 "\033[0m"}\
        matched==0            {print "\033[0;33m" $$0 "\033[0m"}\
    '

We can use make log to tail the laravel.log file, and use awk to give it some color based on output.

Artisan

Let'see how we can use artisan more easily.

Tinker

I use tinker a bunch, and we can start tinker from the Makefile as well:

.PHONY: up down log tinker

tinker:
    docker run -it --rm \
        -e "HOME=/home" \
        -v $(PWD).tinker:/home/.config \
        -v $(PWD):/opt \
        -w /opt \
        --network=zondaroad_appnet \
        shippingdocker/php:latest \
        php artisan tinker

Similar to our test command, we spin up a container and run tinker. Just type make tinker.

Other Commands

This one is the hairiest, and perhaps warrants a bash script over Makefile, but we can still accomplish our goal of arbitrary artisan commands.

.PHONY: up down log tinker artisan

ART=""
artisan:
    docker run -it --rm \
        -e "HOME=/home" \
        -v $(PWD).tinker:/home/.config \
        -v $(PWD):/opt \
        -w /opt \
        --network=zondaroad_appnet \
        shippingdocker/php:latest \
        php artisan $(ART)

We create a variable ART and give it an empty string value. We also spin up a container and run artisan commands within it, passing the ART altho as our artisan command.

Try make artisan to get a listing of available commands. Use make artisan ART=route:list to get a listing of routes.

It's a little rough, but not too bad.

The Complete Makefile

Here's the complete Makefile:

.PHONY: up down log tinker artisan test

# Set dir of Makefile to a variable to use later
MAKEPATH := $(abspath $(lastword $(MAKEFILE_LIST)))
PWD := $(dir $(MAKEPATH))

up:
    docker-compose up -d

down:
    docker-compose down

log:
    tail -f $(PWD)storage/logs/laravel.log | awk '\
        {matched=0}\
        /INFO:/    {matched=1; print "\033[0;37m" $$0 "\033[0m"}\
        /WARNING:/ {matched=1; print "\033[0;34m" $$0 "\033[0m"}\
        /ERROR:/   {matched=1; print "\033[0;31m" $$0 "\033[0m"}\
        /Next/     {matched=1; print "\033[0;31m" $$0 "\033[0m"}\
        /ALERT:/   {matched=1; print "\033[0;35m" $$0 "\033[0m"}\
        /Stack trace:/ {matched=1; print "\033[0;35m" $$0 "\033[0m"}\
        matched==0            {print "\033[0;33m" $$0 "\033[0m"}\
    '

tinker:
    docker run -it --rm \
        -e "HOME=/home" \
        -v $(PWD).tinker:/home/.config \
        -v $(PWD):/opt \
        -w /opt \
        --network=zondaroad_appnet \
        shippingdocker/php:latest \
        php artisan tinker

ART=""
artisan:
    docker run -it --rm \
        -e "HOME=/home" \
        -v $(PWD).tinker:/home/.config \
        -v $(PWD):/opt \
        -w /opt \
        --network=zondaroad_appnet \
        shippingdocker/php:latest \
        php artisan $(ART)

test:
    docker run -it --rm \
        -v $(PWD):/opt \
        -w /opt \
        --network=zondaroad_appnet \
        shippingdocker/php:latest \
        ./vendor/bin/phpunit

Exec

Lastly, we can also use Docker's exec to make it all a tad easier. Here we'll run bash inside if our already-running PHP container:

docker exec -it  bash
> cd /var/www/html
> php artisan

Looking for a deeper dive into Docker?

Sign up here to get a preview of the Shipping Docker course! Learn how to integrate Docker into your applications and develop a workflow to make using Docker a breeze!

All Topics