Working with Docker Images
##orignal is always the best
In the introduction we've discovered that Docker images are the basis of containers. In the previoussections we've used Docker images that already exist, for example the ubuntu
image and thetraining/webapp
image.
We've also discovered that Docker stores downloaded images on the Docker host. If an image isn't already present on the host then it'll be downloaded from a registry: by default the Docker Hub public registry.
In this section we're going to explore Docker images a bit more including:
- Managing and working with images locally on your Docker host;
- Creating basic images;
- Uploading images to Docker Hub.
Listing images on the host
Let's start with listing the images we have locally on our host. You can do this using the docker images
command like so:
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
training/webapp latest fc77f57ad303 3 weeks ago 280.5 MB
ubuntu 13.10 5e019ab7bf6d 4 weeks ago 180 MB
ubuntu saucy 5e019ab7bf6d 4 weeks ago 180 MB
ubuntu 12.04 74fe38d11401 4 weeks ago 209.6 MB
ubuntu precise 74fe38d11401 4 weeks ago 209.6 MB
ubuntu 12.10 a7cf8ae4e998 4 weeks ago 171.3 MB
ubuntu quantal a7cf8ae4e998 4 weeks ago 171.3 MB
ubuntu 14.04 99ec81b80c55 4 weeks ago 266 MB
ubuntu latest 99ec81b80c55 4 weeks ago 266 MB
ubuntu trusty 99ec81b80c55 4 weeks ago 266 MB
ubuntu 13.04 316b678ddf48 4 weeks ago 169.4 MB
ubuntu raring 316b678ddf48 4 weeks ago 169.4 MB
ubuntu 10.04 3db9c44f4520 4 weeks ago 183 MB
ubuntu lucid 3db9c44f4520 4 weeks ago 183 MB
We can see the images we've previously used in our user guide. Each has been downloaded from Docker Hub when we launched a container using that image.
We can see three crucial pieces of information about our images in the listing.
- What repository they came from, for example
ubuntu
. - The tags for each image, for example
14.04
. - The image ID of each image.
A repository potentially holds multiple variants of an image. In the case of our ubuntu
image we can see multiple variants covering Ubuntu 10.04, 12.04, 12.10, 13.04, 13.10 and 14.04. Each variant is identified by a tag and you can refer to a tagged image like so:
ubuntu:14.04
So when we run a container we refer to a tagged image like so:
$ sudo docker run -t -i ubuntu:14.04 /bin/bash
If instead we wanted to build an Ubuntu 12.04 image we'd use:
$ sudo docker run -t -i ubuntu:12.04 /bin/bash
If you don't specify a variant, for example you just use ubuntu
, then Docker will default to using theubuntu:latest
image.
Tip: We recommend you always use a specific tagged image, for example
ubuntu:12.04
. That way you always know exactly what variant of an image is being used.
Getting a new image
So how do we get new images? Well Docker will automatically download any image we use that isn't already present on the Docker host. But this can potentially add some time to the launch of a container. If we want to pre-load an image we can download it using the docker pull
command. Let's say we'd like to download the centos
image.
$ sudo docker pull centos
Pulling repository centos
b7de3133ff98: Pulling dependent layers
5cc9e91966f7: Pulling fs layer
511136ea3c5a: Download complete
ef52fb1fe610: Download complete
. . .
Status: Downloaded newer image for centos
We can see that each layer of the image has been pulled down and now we can run a container from this image and we won't have to wait to download the image.
$ sudo docker run -t -i centos /bin/bash
bash-4.1#
Finding images
One of the features of Docker is that a lot of people have created Docker images for a variety of purposes. Many of these have been uploaded to Docker Hub. We can search these images on the Docker Hubwebsite.
We can also search for images on the command line using the docker search
command. Let's say our team wants an image with Ruby and Sinatra installed on which to do our web application development. We can search for a suitable image by using the docker search
command to find all the images that contain the term sinatra
.
$ sudo docker search sinatra
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
training/sinatra Sinatra training image 0 [OK]
marceldegraaf/sinatra Sinatra test app 0
mattwarren/docker-sinatra-demo 0 [OK]
luisbebop/docker-sinatra-hello-world 0 [OK]
bmorearty/handson-sinatra handson-ruby + Sinatra for Hands on with D... 0
subwiz/sinatra 0
bmorearty/sinatra 0
. . .
We can see we've returned a lot of images that use the term sinatra
. We've returned a list of image names, descriptions, Stars (which measure the social popularity of images - if a user likes an image then they can "star" it), and the Official and Automated build statuses. Official repositories are built and maintained by the Stackbrew project, and Automated repositories are Automated Builds that allow you to validate the source and content of an image.
We've reviewed the images available to use and we decided to use the training/sinatra
image. So far we've seen two types of images repositories, images like ubuntu
, which are called base or root images. These base images are provided by Docker Inc and are built, validated and supported. These can be identified by their single word names.
We've also seen user images, for example the training/sinatra
image we've chosen. A user image belongs to a member of the Docker community and is built and maintained by them. You can identify user images as they are always prefixed with the user name, here training
, of the user that created them.
Pulling our image
We've identified a suitable image, training/sinatra
, and now we can download it using the docker pull
command.
$ sudo docker pull training/sinatra
The team can now use this image by run their own containers.
$ sudo docker run -t -i training/sinatra /bin/bash
root@a8cb6ce02d85:/#
Creating our own images
The team has found the training/sinatra
image pretty useful but it's not quite what they need and we need to make some changes to it. There are two ways we can update and create images.
- We can update a container created from an image and commit the results to an image.
- We can use a
Dockerfile
to specify instructions to create an image.
To learn more, check out the Dockerfile tutorial.
Updating and committing an image
To update an image we first need to create a container from the image we'd like to update.
$ sudo docker run -t -i training/sinatra /bin/bash
root@0b2616b0e5a8:/#
Note: Take note of the container ID that has been created,
0b2616b0e5a8
, as we'll need it in a moment.
Inside our running container let's add the json
gem.
root@0b2616b0e5a8:/# gem install json
Once this has completed let's exit our container using the exit
command.
Now we have a container with the change we want to make. We can then commit a copy of this container to an image using the docker commit
command.
$ sudo docker commit -m="Added json gem" -a="Kate Smith"
0b2616b0e5a8 ouruser/sinatra:v2
4f177bd27a9ff0f6dc2a830403925b5360bfe0b93d476f7fc3231110e7f71b1c
Here we've used the docker commit
command. We've specified two flags: -m
and -a
. The -m
flag allows us to specify a commit message, much like you would with a commit on a version control system. The -a
flag allows us to specify an author for our update.
We've also specified the container we want to create this new image from, 0b2616b0e5a8
(the ID we recorded earlier) and we've specified a target for the image:
ouruser/sinatra:v2
Let's break this target down. It consists of a new user, ouruser
, that we're writing this image to. We've also specified the name of the image, here we're keeping the original image name sinatra
. Finally we're specifying a tag for the image: v2
.
We can then look at our new ouruser/sinatra
image using the docker images
command.
$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
training/sinatra latest 5bc342fa0b91 10 hours ago 446.7 MB
ouruser/sinatra v2 3c59e02ddd1a 10 hours ago 446.7 MB
ouruser/sinatra latest 5db5f8471261 10 hours ago 446.7 MB
To use our new image to create a container we can then:
$ sudo docker run -t -i ouruser/sinatra:v2 /bin/bash
root@78e82f680994:/#
Building an image from a Dockerfile
Using the docker commit
command is a pretty simple way of extending an image but it's a bit cumbersome and it's not easy to share a development process for images amongst a team. Instead we can use a new command, docker build
, to build new images from scratch.
To do this we create a Dockerfile
that contains a set of instructions that tell Docker how to build our image.
Let's create a directory and a Dockerfile
first.
$ mkdir sinatra
$ cd sinatra
$ touch Dockerfile
Each instruction creates a new layer of the image. Let's look at a simple example now for building our own Sinatra image for our development team.
# This is a comment
FROM ubuntu:14.04
MAINTAINER Kate Smith <ksmith@example.com>
RUN apt-get update && apt-get install -y ruby ruby-dev
RUN gem install sinatra
Let's look at what our Dockerfile
does. Each instruction prefixes a statement and is capitalized.
INSTRUCTION statement
Note: We use
#
to indicate a comment
The first instruction FROM
tells Docker what the source of our image is, in this case we're basing our new image on an Ubuntu 14.04 image.
Next we use the MAINTAINER
instruction to specify who maintains our new image.
Lastly, we've specified three RUN
instructions. A RUN
instruction executes a command inside the image, for example installing a package. Here we're updating our APT cache, installing Ruby and RubyGems and then installing the Sinatra gem.
Note: There are a lot more instructions available to us in a Dockerfile.
Now let's take our Dockerfile
and use the docker build
command to build an image.
$ sudo docker build -t="ouruser/sinatra:v2" .
Sending build context to Docker daemon 2.048 kB
Sending build context to Docker daemon
Step 0 : FROM ubuntu:14.04
---> e54ca5efa2e9
Step 1 : MAINTAINER Kate Smith <ksmith@example.com>
---> Using cache
---> 851baf55332b
Step 2 : RUN apt-get update && apt-get install -y ruby ruby-dev
---> Running in 3a2558904e9b
Selecting previously unselected package libasan0:amd64.
(Reading database ... 11518 files and directories currently installed.)
Preparing to unpack .../libasan0_4.8.2-19ubuntu1_amd64.deb ...
Unpacking libasan0:amd64 (4.8.2-19ubuntu1) ...
Selecting previously unselected package libatomic1:amd64.
Preparing to unpack .../libatomic1_4.8.2-19ubuntu1_amd64.deb ...
Unpacking libatomic1:amd64 (4.8.2-19ubuntu1) ...
Selecting previously unselected package libgmp10:amd64.
Preparing to unpack .../libgmp10_2%3a5.1.3+dfsg-1ubuntu1_amd64.deb ...
Unpacking libgmp10:amd64 (2:5.1.3+dfsg-1ubuntu1) ...
Selecting previously unselected package libisl10:amd64.
Preparing to unpack .../libisl10_0.12.2-1_amd64.deb ...
Unpacking libisl10:amd64 (0.12.2-1) ...
Selecting previously unselected package libcloog-isl4:amd64.
Preparing to unpack .../libcloog-isl4_0