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 email@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 firstname.lastname@example.org 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 email@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>.