This week I have been learning about Docker… Here is what I’ve learned so far.

Note: I have set up functions/aliases for several of these in my dotfiles – the commands in brackets refer to those shortcuts.

Installing Docker on Ubuntu

sudo apt-get install docker docker-compose
sudo usermod -aG docker $USER

Security warning: Any user in the docker group effectively has full root permissions on the host machine.

Installing Docker on Windows

Download and install Docker Toolbox. (Note: The newer “Docker for Windows” only works on Windows Professional with Hyper-V – not Windows Home or VirtualBox.)

Initialise a new virtual machine (dinitdm = docker-machine):

docker-machine create --driver virtualbox default

Set environment variables to allow communication with Docker Machine (denv):

eval $(docker-machine env)

To support interactive commands under Cygwin, install WinPTY for Cygwin and prefix commands with “winpty“. I have set up Bash functions to do that automatically when required.

Running Docker interactively

Run Bash in a minimal Ubuntu container (dr = docker run):

docker run -it ubuntu
# or
docker run -it ubuntu bash

Run the latest version of PHP interactively:

docker run -it php

Run a given command in latest version of PHP (the parameters are just passed to the php executable):

docker run php -r 'echo PHP_VERSION . "\n";'

Run a specific version of PHP:

docker run -it php:5.6

Any version from 5.3 to 7.1 seems to be available, even though they’re not listed on Docker Hub – including some minor versions:

docker run -it php:5.6.1

Some (older?) versions don’t automatically pass extra parameters to PHP so require the executable name to be specified:

docker run php:5.3 php -r 'echo PHP_VERSION . "\n";'

List stopped containers:

docker ps -a

Resume a stopped container:

docker start -ai <id>

Resume the last exited container (dresume):

docker start -ai $(docker ps -qlf status=exited)

Run Bash in any container, overriding the default entry point:

docker run -it --entrypoint bash <image>

Run Bash, including SSH agent forwarding via Docker Machine:

docker-machine ssh default -At docker run -it --volume '$SSH_AUTH_SOCK:/tmp/ssh-agent' \
    --env SSH_AUTH_SOCK=/tmp/ssh-agent --entrypoint bash <image>

On Cygwin use this version to avoid layout issues by bypassing docker-machine and winpty (dsh):

ssh -Ati "$HOME/.docker/machine/machines/default/id_rsa" "docker@$(docker-machine ip)" \
    docker run -it --volume '$SSH_AUTH_SOCK:/tmp/ssh-agent' \
    --env SSH_AUTH_SOCK=/tmp/ssh-agent --entrypoint bash <image>

Running daemons in Docker

Start a container in the background and map port 80 to the host port 80:

docker run --name webserver -d -p80:80 nginx

Then open http://localhost/ in a web browser. (On Windows, run docker-machine ip to get the host IP then open http://<ip>/ instead.)

List running containers:

docker ps

Stop a running container:

docker stop webserver

Resume a stopped container:

docker start webserver

Connect to a container to see it’s input/output (e.g. access logs) – resumes it if it’s not already running:

docker start -ai webserver

(Note: Press Ctrl-C to disconnect. This doesn’t stop the server/container.)

Stop the most recently started container (dstop):

docker stop $(docker ps -ql)

Stop all containers (dstopall):

docker stop $(docker ps -q)

Forcibly stop a container / all containers (dkill, dkillall):

docker kill webserver
docker kill $(docker ps -ql)
docker kill $(docker ps -q)

Delete a container:

docker rm webserver

Delete all stopped containers (dclean):

docker container prune

Building images

Create a Dockerfile, preferably in a new directory, switch to that directory and run (db = docker build):

docker build -t <name> .

(Here are two examples I created: PHP 7.1 CI base image and Dotfiles setup script test image. I also recommend reading the Best Practices guide.)

Then run it as described above:

docker run -it <name>

List local images / tags:

docker images

Publish to Docker Hub:

docker login
docker tag <name> <username>/<repo>:<version>
docker push <username>/<repo>:<version>

Delete all unused and unnamed images (e.g. old versions of a build) (dclean):

docker image prune