To build your own Docker image, you need to create a Dockerfile. Here's an example:
# This Dockerfile is BROKEN: it's an EXAMPLE, read on
FROM debian
MAINTAINER gorr@example.com
EXPOSE 80
RUN echo "APT::Install-Recommends \"false\";" > /etc/apt/apt.conf.d/02norecommendssuggests
RUN echo "APT::Install-Suggests \"false\";" >> /etc/apt/apt.conf.d/02norecommendssuggests
RUN apt-get update
RUN apt-get -y install nginx
COPY html /usr/share/html
To construct your Docker image, run docker build -t basicweb .
in the folder that contains the Dockerfile. You can simply run docker build .
but this results in an anonymous hexadecimal-labeled image: named images are a lot nicer to deal with. The commands in the Dockerfile are reasonably self-evident: FROM says what base image to start with (see my previous docker entry on how to find images in the repository. MAINTAINER isn't required, but is good form. EXPOSE is used to make port 80 available within the Docker infrastructure: making it available to the outside world takes another step that we'll get to shortly. RUN is used to apply changes to the image. Note that apt-get install <package>
has to be run with -y because otherwise apt-get gives an interactive prompt that will wait forever. I've also chosen to disable the installation of "recommended" and "suggested" apt packages to reduce the size of the image (this is done before any apt-get commands). And COPY copies files "from the local context" (Dockers' documentation, which I take to mean "within the folder containing the Dockerfile, not parent folders") to the image. In this case, I have a folder full of working HTML files.
This image should build successfully. To run it, use docker run -d -p 80:80 basicweb
- but at this point we find out it will immediately exit. As I understand it, this is because Docker containers only continue to run if they have a foreground service. So we add a line a couple lines:
FROM debian
MAINTAINER gorr@example.com
EXPOSE 80
RUN echo "APT::Install-Recommends \"false\";" > /etc/apt/apt.conf.d/02norecommendssuggests
RUN echo "APT::Install-Suggests \"false\";" >> /etc/apt/apt.conf.d/02norecommendssuggests
RUN apt-get update
RUN apt-get -y install nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# source material must be in context, ie. in the current directory, not outside it
# only the contents of a source directory are copied, not the dir itself
COPY html /usr/share/html
CMD nginx
Now the image runs fine and can be seen in the Docker process table with docker ps
. But the web page at http://localhost/ is the default Debian Nginx page because I've copied the HTML documents to the wrong location. Let's kill it and start it again with a window into it:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ff48a4074d12 basicweb "/bin/sh -c nginx" 4 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp stoic_albattani
$ docker kill stoic_albattani
stoic_albattani
$ docker run -ti basicweb /bin/bash
root@99d6bf38aab7:/# less /etc/nginx/sites-enabled/default
bash: less: command not found
root@99d6bf38aab7:/# cat /etc/nginx/sites-enabled/default
From examining this file, I find that the default HTML file location is root /var/www/html;
. So I corrected the COPY line to COPY html /var/www/html
and when run again the container now provides the HTML documents I expect on port 80 of localhost.
Creating Dockerfiles is a slow iterative process of testing and refinement - usually with a lot more cycles than I've shown above. But this was a very simple example.
Here's another mostly complete Dockerfile, this one the result of a previously mentioned learning lab on the subject. You can trace its design by reading the lab notes, in which the instructor carefully has you construct the following file line by line so you understand everything in it:
FROM centos:6
RUN yum install -y httpd
MAINTAINER gorr@example.com
CMD /usr/sbin/apachectl -DFOREGROUND -k start
EXPOSE 80
RUN yum install -y tar bzip2
COPY owncloud-7.0.6.tar.bz2 /var/www/html/
RUN cd /var/www/html/ && tar xvfj owncloud-7.0.6.tar.bz2 && rm -f owncloud-7.0.6.tar.bz2
RUN yum install -y php php-dom php-mbstring php-pdo php-gd
VOLUME /data
COPY config.php /var/www/html/owncloud/config/config.php
RUN chown -R apache:apache /var/www/html/owncloud /data
In this case, you'll need to download the owncloud-7.0.6.tar.bz2 tarball to the same directory as the Dockerfile. You'll need to have a /data/ folder on the machine running the Docker instance. You'll need a config.php for OwnCloud, which is most easily constructed by running OwnCloud in the Docker instance, doing the base configuration, and then copying out the resulting file (docker cp ecstatic_tesla:/var/www/html/owncloud/config/config.php .
to copy a file out of a running container). I also suspect that the installation hauls in a database engine and then doesn't put a master password on it: as it's in a container this isn't disastrous - but it's far from best practice.
Managing Docker Images
It's worth checking occasionally how much space your Docker Images occupy:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
basicweb latest e15fb217de1d 10 hours ago 176.1 MB
<none> <none> f8db3b4002fb 11 hours ago 176.1 MB
<none> <none> b839fbee95cb 11 hours ago 176.1 MB
<none> <none> 339576126c97 11 hours ago 176.1 MB
owncloud latest 90f5388c296d 6 days ago 352.6 MB
<none> <none> 03793ab59c43 6 days ago 185.2 MB
<none> <none> 15ba0a3ed852 6 days ago 125.1 MB
debian latest 031143c1c662 12 days ago 125.1 MB
hello-world latest c54a2cc56cbb 10 weeks ago 1.848 kB
As it turns out, this is somewhat deceptive. The unnamed ones are (I think - take this with a grain of salt) actually intermediate mis-steps, slices of Docker image file systems that have been abandoned. So 339576126c97 was a part of what eventually became basicweb, and it would occupy 176MB if I were using that entire Docker image. Still, it doesn't hurt to clean up - especially if you've completely abandoned a line of development.
$ docker rmi 339576126c97
Error response from daemon: conflict: unable to delete 339576126c97 (must be forced) - image is being used by stopped container 4d989f6204eb
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f26b271cfd33 basicweb "/bin/sh -c nginx" 10 hours ago Exited (137) 10 hours ago trusting_khorana
4e0263d585da basicweb "-d nginx" 10 hours ago Created distracted_lumiere
99d6bf38aab7 f8db3b4002fb "/bin/bash" 11 hours ago Exited (0) 10 hours ago ecstatic_lamport
ff48a4074d12 f8db3b4002fb "/bin/sh -c nginx" 11 hours ago Exited (137) 11 hours ago stoic_albattani
6dc0c2bc27d6 430a5a61f2cd "-d nginx" 11 hours ago Created drunk_davinci
9eba61da1cd9 b839fbee95cb "/bin/sh -c nginx" 11 hours ago Exited (0) 11 hours ago silly_archimedes
4d989f6204eb 339576126c97 "/bin/sh -c 'service " 11 hours ago Exited (0) 11 hours ago elated_poitras
90d80b110813 430a5a61f2cd "/bin/bash" 11 hours ago Exited (0) 11 hours ago sharp_swanson
b06e441837d9 430a5a61f2cd "/bin/bash" 11 hours ago Exited (0) 11 hours ago zen_roentgen
$ docker rm zen_roentgen
zen_roentgen
$ docker rm sharp_swanson elated_poitras silly_archimedes drunk_davinci stoic_albattani ecstatic_lamport distracted_lumiere trusting_khorana
sharp_swanson
elated_poitras
silly_archimedes
drunk_davinci
stoic_albattani
ecstatic_lamport
distracted_lumiere
trusting_khorana
$ docker rmi 339576126c97
Deleted: sha256:339576126c97838b9d7d2c891ffb60087ae956e4ed9dee4ca5d3944210308e30
$ docker rmi 15ba0a3ed852 03793ab59c43 b839fbee95cb f8db3b4002fb
Deleted: sha256:15ba0a3ed852ce3a53a0c915dd1970977f5509a772cb3c343120bd92a9f1b752
Deleted: sha256:52d9be9d22771383b0bce52176cbe4aeb1be18ff963328db81e246c78f160003
Deleted: sha256:03793ab59c43b9cb321f9fdcd736921a9aef1894a7c10fc7e92c8a6a43bdb98b
Deleted: sha256:7e46d22e7f2a12208576a86ba8824f9cde05afbbeac8594d836758674f650fc9
Deleted: sha256:b0323516ad75942dbac79a348b38713dbffcf68e4263b0da22cff59c21e2f94f
Deleted: sha256:a9887ef5524e5c4df5e48a3290f72924d4bc1520c334592bc94054faceb57f0f
Deleted: sha256:379e3fed830e573dc497159e8fdc197a937fbaa461b0ff0f4435e1e07ee2fad7
Deleted: sha256:985b95036aa499a0667194367f4f2bf13fe8002adde01ead02ac6bce650e521c
Deleted: sha256:6c354589940d2e890204bcfb0ed166134eeb193e14ed985382ad8f6ed9612911
Deleted: sha256:e6796d0d00581e5a42077815aff604b43b3070ebcf70110d2ff6fd470dcb784e
Deleted: sha256:270d88ba6382496182d6876abc9de8730616856b68c50b0fc6ae4e1f5e74a62f
Deleted: sha256:ab98df599990a30aed499f8d2c4f9fcbbe29a300ffe111eeb9a73be3cfc5f344
Deleted: sha256:b839fbee95cbcb1a5d87cfd588fa14f724540c0d5a824687474a8ae10e6c8037
Deleted: sha256:430a5a61f2cda448707341491cda6107b096f742597bb5c20bf769714cc8bd9c
Deleted: sha256:7c3f524c58452abc5dbdffe28e3222e290da523d0a9820694e664e167e989b6e
Deleted: sha256:f8db3b4002fb377038c96347320d9dcc6bc118ecb0897755395257d57cf56916
Deleted: sha256:a81d2b379ae36d2038de9bbe32e5437cfd9ccd97508e1efd85d77b7ff7e48bbd
Deleted: sha256:b2d5eb9f30c73645b9af48b5ebe5cb6c9f8a399d36234528f776dd8ed288e347
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
basicweb latest e15fb217de1d 11 hours ago 176.1 MB
owncloud latest 90f5388c296d 6 days ago 352.6 MB
debian latest 031143c1c662 12 days ago 125.1 MB
hello-world latest c54a2cc56cbb 10 weeks ago 1.848 kB
This only cleared about 100MB of space. But it feels tidier. Notice the distinction between docker rm <container>
and docker rmi <image>
.