{"id":1139,"date":"2016-11-16T23:11:17","date_gmt":"2016-11-17T07:11:17","guid":{"rendered":"http:\/\/technofovea.com\/blog\/?p=1139"},"modified":"2016-11-16T23:16:39","modified_gmt":"2016-11-17T07:16:39","slug":"refreshing-my-lamp-stack-with-docker","status":"publish","type":"post","link":"http:\/\/technofovea.com\/blog\/archives\/1139","title":{"rendered":"Refreshing my LAMP stack with Docker"},"content":{"rendered":"<p>Over the years my home desktop has acquired many layers of development tools, to the extent where I keep things around &#8220;just in case&#8221; but in reality the project or experiment is definitely over. (Who needs disk-images\u00c2\u00a0of a Slackware server from 2001?) I&#8217;ve decided to do some spring-cleaning and\u00c2\u00a0install new versions.<\/p>\n<p>Part of that means (re)installing Docker on Windows 7, and I&#8217;ve decided to journal this process mainly so I can refer to what I did (or didn&#8217;t) do later. \u00c2\u00a0I&#8217;m going to assume that if you&#8217;re still reading this, you have a vague idea what Docker is and where it stands relative to Vagrant, Virtualbox, and the development benefits of virtual-machinery in general.<\/p>\n<h3>Step one: Install Docker Toolbox (and VirtualBox)<\/h3>\n<p>For Windows 7 you&#8217;ll want\u00c2\u00a0<a href=\"https:\/\/docs.docker.com\/toolbox\/toolbox_install_windows\/\">Docker Toolbox<\/a>, since it seems the flagship Docker Platform\u00c2\u00a0is designed around features in Windows 10. (And for various reasons, I myself do not want to upgrade.)<\/p>\n<p>When you install Docker Toolbox (in my case, v1.12.2) it will automatically include <a href=\"https:\/\/www.virtualbox.org\/\">VirtualBox<\/a>\u00c2\u00a0(5.1.6) as a workhorse behind the scenes. Effectively it has to hose a teeny tiny VM which, in turn, can host the core components of Docker. In the process I was prompted to install some device drivers from Oracle for things like USB support and network drivers.<\/p>\n<p>When it&#8217;s done, you should have Start Menu entries for VirtualBox (which can be used on its own without Docker) and\u00c2\u00a0a shortcut called &#8220;Docker Quickstart Terminal&#8221; should exist on your desktop. Run it.<\/p>\n<p>You may be prompted to allow VirtualBox to make some changes &#8212; it&#8217;s probably setting up all the network-adapter magic that lets your virtual machines talk to one-another and reach out to download things from the internet. (Often necessary, when installing.)<\/p>\n<p>Once it&#8217;s done, you should see an ASCII-art whale (with ominous growths on its back) and a prompt. So let&#8217;s run the its standard &#8220;Hello World&#8221; test image in a new container:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/\r\n$ docker run hello-world\r\nUnable to find image 'hello-world:latest' locally\r\nlatest: Pulling from library\/hello-world\r\nc04b14da8d14: Pull complete\r\nDigest: sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9\r\nStatus: Downloaded newer image for hello-world:latest\r\n\r\nHello from Docker!\r\nThis message shows that your installation appears to be working correctly.\r\n\r\nTo generate this message, Docker took the following steps:\r\n 1. The Docker client contacted the Docker daemon.\r\n 2. The Docker daemon pulled the &quot;hello-world&quot; image from the Docker Hub.\r\n 3. The Docker daemon created a new container from that image which runs the\r\n    executable that produces the output you are currently reading.\r\n 4. The Docker daemon streamed that output to the Docker client, which sent it\r\n    to your terminal.\r\n\r\nTo try something more ambitious, you can run an Ubuntu container with:\r\n $ docker run -it ubuntu bash\r\n\r\nShare images, automate workflows, and more with a free Docker Hub account:\r\n https:\/\/hub.docker.com\r\n\r\nFor more examples and ideas, visit:\r\n https:\/\/docs.docker.com\/engine\/userguide\/\r\n<\/pre>\n<h3>Step two:\u00c2\u00a0A simple docker-compose<\/h3>\n<p>Create a new folder somewhere for the project, and create a new file called\u00c2\u00a0<em>docker-compose.yml\u00c2\u00a0<\/em>\u00c2\u00a0, which will define which host(s) we need for a full installation of, well, whatever.<\/p>\n<p>Initially, let&#8217;s choose one PHP web node, running a pre-release copy of PHP-7.1, and let&#8217;s structure it so that we can customize that image a little bit.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nversion: '2'\r\nservices:   \r\n   webserver:\r\n     build:\r\n      context: .\/image_web\r\n     ports:\r\n       - &quot;8000:80&quot;\r\n     restart: always\r\n     volumes:  \r\n       - .\/src\/:\/var\/www\r\n<\/pre>\n<p>This sample defines a new host called\u00c2\u00a0<em>webserver<\/em>, and in order to create it\u00c2\u00a0<em>docker-compose<\/em> will go try to build\u00c2\u00a0.\/<em>image_web\/Dockerfile<\/em>\u00c2\u00a0, and when it runs the data in <em>.\/src\/<\/em> will be mounted.<\/p>\n<p>So go ahead and create the folder\u00c2\u00a0<em>image_web<\/em>, and inside place a\u00c2\u00a0<em>Dockerfile<\/em> that just says:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nFROM php:7.1-rc-apache\r\n<\/pre>\n<p>At the moment this is a pointless level of indirection, but later it&#8217;ll help us layer on customizations.<\/p>\n<p>Next, create the folders and file for <em>src\/html\/index.php<\/em> , the &#8220;html&#8221; portion is due to the server setup defined by the existing <em>php:7.1-rc-apache<\/em> image. To test that PHP is running right, try:<\/p>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\nHello dockerized world!\r\n&lt;?php\r\nphpinfo();\r\n<\/pre>\n<h3>Step three:\u00c2\u00a0Bringing everything up and testing<\/h3>\n<pre>Back to the command-line, let's check that we have nothing currently running:<\/pre>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/docker-demo\r\n$ docker ps\r\nCONTAINER ID        IMAGE               COMMAND             CREATED\r\nSTATUS              PORTS               NAMES\r\n<\/pre>\n<p>So let&#8217;s ask docker-compose to build everything and start\u00c2\u00a0up our tiny world of one webserver. You&#8217;ll probably see much\u00c2\u00a0<em>much<\/em> more output than this as your computer downloads what it needs for the\u00c2\u00a0<em>php:7.1-rc-apache\u00c2\u00a0<\/em>image.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/docker-demo\r\n$ docker-compose up -d\r\nBuilding webserver\r\nStep 1 : FROM php:7.1-rc-apache\r\n ---&gt; dd39d6dff6fd\r\nSuccessfully built dd39d6dff6fd\r\nWARNING: Image for service webserver was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.\r\ndockerphp_webserver_1 is up-to-date\r\n<\/pre>\n<p>To figure out what URL to use in your browser, first we should find the IP address that the docker virtual-machine (one-layer out from the containers) is using. You can do that with:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/docker-demo\r\n$ docker-machine.exe ip\r\n192.168.99.100\r\n<\/pre>\n<p>As for the port number, remember the <em>docker-compose.yml\u00c2\u00a0<\/em> file? It contained a line saying that the container&#8217;s port-80 should be mapper to port-8000 on the outside, so that means our combined URL is <em>http:\/\/192.168.99.100:8000\/<\/em><\/p>\n<p>Visiting that, you ought to see\u00c2\u00a0&#8220;<em>Hello dockerized world!<\/em>&#8221; and a PHPInfo output.<\/p>\n<p>Now, we used the -d argument to\u00c2\u00a0<em>docker-compose<\/em> which means the webserver will keep running. To turn it off, simply go:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/docker-demo\r\n$ docker-compose stop\r\nStopping dockerphp_webserver_1 ... done\r\n<\/pre>\n<h3>Epilogue: Cleaning up<\/h3>\n<p>What if you want to get rid of this stuff and try some things over? First, you can get a list of the images docker knows about like so:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/docker-demo\/image_web\r\n$ docker images\r\nREPOSITORY            TAG                 IMAGE ID            CREATED             SIZE\r\ndockerphp_webserver   latest              dd39d6dff6fd        2 days ago          406.3 MB\r\nphp                   7.1-rc-apache       dd39d6dff6fd        2 days ago          406.3 MB\r\nhello-world           latest              c54a2cc56cbb        4 months ago        1.848 kB\r\n<\/pre>\n<p>Let&#8217;s try removing the <em>hello-world\u00c2\u00a0<\/em>stuff from before:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/docker-demo\r\n$ docker rmi hello-world:latest\r\nError response from daemon: conflict: unable to remove repository reference &quot;hello-world:latest&quot; (must force) - container 560e895d696c is using its referenced i\r\nmage c54a2cc56cbb\r\n<\/pre>\n<p>Uh oh, what happened? Well, the hello-world container we created is still around, even if it isn&#8217;t actively running. We can see it here:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/docker-demo\r\n$ docker ps -a\r\nCONTAINER ID        IMAGE               COMMAND                CREATED             STATUS                     PORTS               NAMES\r\ncf5dbd9936a7        dd39d6dff6fd        &quot;apache2-foreground&quot;   22 minutes ago      Exited (0) 6 minutes ago                       dockerphp_webserver_1\r\n560e895d696c        hello-world         &quot;\/hello&quot;               46 hours ago        Exited (0) 46 hours ago                        small_pasteur\r\n<\/pre>\n<p>And remove it with:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/docker-demo\r\n$ docker rm 560e895d696c\r\n560e895d696c\r\n<\/pre>\n<p>Now when we remove the image, things are smoother:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nUser@Desktop MINGW64 ~\/docker-demo\r\n$ docker rmi hello-world:latest\r\nUntagged: hello-world:latest\r\nUntagged: hello-world@sha256:0256e8a36e2070f7bf2d0b0763dbabdd67798512411de4cdcf9431a1feb60fd9\r\nDeleted: sha256:c54a2cc56cbb2f04003c1cd4507e118af7c0d340fe7e2720f70976c4b75237dc\r\nDeleted: sha256:a02596fdd012f22b03af6ad7d11fa590c57507558357b079c3e8cebceb4262d7\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Over the years my home desktop has acquired many layers of development tools, to the extent where I keep things around &#8220;just in case&#8221; but in reality the project or experiment is definitely over. (Who needs disk-images\u00c2\u00a0of a Slackware server from 2001?) I&#8217;ve decided to do some spring-cleaning and\u00c2\u00a0install new versions. Part of that means [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[4],"tags":[33,34],"_links":{"self":[{"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/posts\/1139"}],"collection":[{"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/comments?post=1139"}],"version-history":[{"count":15,"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/posts\/1139\/revisions"}],"predecessor-version":[{"id":1155,"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/posts\/1139\/revisions\/1155"}],"wp:attachment":[{"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/media?parent=1139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/categories?post=1139"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/technofovea.com\/blog\/wp-json\/wp\/v2\/tags?post=1139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}