{"id":52,"date":"2018-06-30T22:48:33","date_gmt":"2018-06-30T22:48:33","guid":{"rendered":"http:\/\/edspace.com\/blog\/?p=52"},"modified":"2018-07-06T14:52:04","modified_gmt":"2018-07-06T14:52:04","slug":"local-development-with-docker","status":"publish","type":"post","link":"http:\/\/edspace.com\/blog\/2018\/06\/30\/local-development-with-docker\/","title":{"rendered":"Local Development with Docker"},"content":{"rendered":"<p>Alright, I wish I could take back my <a href=\"http:\/\/edspace.com\/blog\/2017\/04\/25\/docker_and_local_dev_env\/\">previous Docker entry<\/a>. It was pretty useless, so I&#8217;m going to take another shot at this at do it right. I&#8217;ve given this talk about Docker about a dozen times in person, done a recorded (sadly, proprietary) teaching session on it, but I still find myself giving it over and over again, so I thought it might be best to just start writing it down. The target audience for this is people who have only really heard of Docker without knowing what it is. At the end of this guide, you should be able to write your own Dockerfile for your project and deploy it locally for testing purposes.<\/p>\n<h2>What is Docker?<\/h2>\n<p>You can think of Docker as yet another layer of virtualization, one that&#8217;s not as heavyweight as full hardware virtualization or paravirtualization. It&#8217;s a level known as &#8220;operating-system-level virtualization,&#8221; where the guest machine shares the same kernel as the host, but gets its own file system to itself and network stack. This allows you to run your application as a process on the host operating system while fooling the guest application into thinking that it has all of its own resources to use.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"57\" data-permalink=\"http:\/\/edspace.com\/blog\/2018\/06\/30\/local-development-with-docker\/docker_overview-2\/\" data-orig-file=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_overview-1.png?fit=457%2C216\" data-orig-size=\"457,216\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Docker Overview\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_overview-1.png?fit=457%2C216\" class=\"alignnone size-full wp-image-57\" src=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_overview-1.png?resize=457%2C216\" alt=\"\" width=\"457\" height=\"216\" srcset=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_overview-1.png?w=457 457w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_overview-1.png?resize=300%2C142 300w\" sizes=\"auto, (max-width: 457px) 100vw, 457px\" \/><\/p>\n<h2>What should I use it for?<\/h2>\n<p>Docker makes it easy to spin up multiple stateless application services onto a cluster. If anything requires storage, e.g. a database, it is much better to use a standard virtual machine with dedicated mounted storage. Docker is not designed to manipulate stored data very efficiently.<\/p>\n<h2>Installation and Example<\/h2>\n<p>The first step, obviously, is to install Docker. Follow <a href=\"https:\/\/docs.docker.com\/install\/\">the directions here<\/a> and find your platform.<\/p>\n<p>After you have it installed, we&#8217;ll get a quick &#8220;Hello, World!&#8221; going. We&#8217;ll execute two lines, <em>docker pull hello-world<\/em> and <em>docker run hello-world<\/em>.<\/p>\n<pre class=\"lang:sh decode:true\" title=\"Hello World\">$ docker pull hello-world\r\nUsing default tag: latest\r\nlatest: Pulling from library\/hello-world\r\n9bb5a5d4561a: Pull complete \r\nDigest: sha256:f5233545e43561214ca4891fd1157e1c3c563316ed8e237750d59bde73361e77\r\nStatus: Downloaded newer image for hello-world:latest\r\n$ docker run hello-world\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 \"hello-world\" image from the Docker Hub.\r\n    (amd64)\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 ID:\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\r\n<\/pre>\n<p>The first line pulls down an image from <a href=\"https:\/\/hub.docker.com\/\">hub.docker.com <\/a>and the second instantiates a container from that image and runs it. Now, this could all be done with the <em>run<\/em> command, but I broke it out into different steps to show two separate steps. The first is to obtain the image, while the second is to create a container from that image.<\/p>\n<p>We&#8217;ll take a look at the two separately with <em>docker images<\/em> and <em>docker container<\/em>.<\/p>\n<pre class=\"lang:sh decode:true\">$ docker images \r\nREPOSITORY          TAG                 IMAGE ID            CREATED             SIZE\r\nhello-world         latest              e38bc07ac18e        2 months ago        1.85kB\r\n$ docker container ls -a\r\nCONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES\r\n79530d8a293c        hello-world         \"\/hello\"            38 minutes ago      Exited (0) 38 minutes ago                       nervous_joliot\r\n<\/pre>\n<p>We see that we have an image that we downloaded from the hub. We also have a container created using said image. It&#8217;s assigned a randomly generated name <em>nervous_joliot<\/em> because we didn&#8217;t bother naming it. You can name your containers when you run them with the <em>&#8211;name<\/em> directive, e.g <em>docker run &#8211;name my_hello_world hello-world<\/em>.<\/p>\n<h2>Images vs. Containers<\/h2>\n<p>Let&#8217;s go into more detail on what images and containers are as they pertain to Docker.<\/p>\n<h3>Images<\/h3>\n<p>Images are immutable data layers that contain portions of the filesystem needed to run your application. Typically, you would start with the base operating system image, add language\/library support, then top it off with your application and ancillary files.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"59\" data-permalink=\"http:\/\/edspace.com\/blog\/2018\/06\/30\/local-development-with-docker\/docker_images\/\" data-orig-file=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_images.png?fit=777%2C330\" data-orig-size=\"777,330\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"docker_images\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_images.png?fit=660%2C280\" class=\"alignnone size-full wp-image-59\" src=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_images.png?resize=660%2C280\" alt=\"\" width=\"660\" height=\"280\" srcset=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_images.png?w=777 777w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_images.png?resize=300%2C127 300w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_images.png?resize=768%2C326 768w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/docker_images.png?resize=700%2C297 700w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/p>\n<p>Each image layer starts by declaring a base image to inherit from. Notice that earlier, when you were pulling the <em>hello-world<\/em> image, it was downloading four images. It was downloading not only the <em>hello-world <\/em>image layer, but also all the layers that it depends on. We&#8217;ll cover this more in depth later on.<\/p>\n<h3>Containers<\/h3>\n<p>Containers are instantiated instances of images that can support execution of the installed application. You can think of images as a class definitions in Object Oriented Programming, an containers are analogous as objects. You can create multiple containers from the same image, allowing you to spin up a cluster of processes with a few simple commands.<\/p>\n<p>When a container is created, a new read\/write layer is introduced on top of the the existing image layers. If a change is made to a file existing in an image layer, that file is copied into the container read\/write layer while the image is untouched.<\/p>\n<h2>Creating Dockerfiles<\/h2>\n<p>A <em>Dockerfile<\/em> is a build descriptor for a Docker image, much like a <em>Makefile<\/em> is used to build your application (if you still write C-code). You would typically include you <em>Dockerfile<\/em> inside your project, run your regular project artifact build, and then run, either manually or via a build target (<em>make<\/em> <em>docker <\/em>or <em>mvn -Pdocker<\/em>, etc) to produce your Docker image.<\/p>\n<p>For this example, we&#8217;ll take a look at <em>Pastr<\/em>, a quick and dirty PasteBin clone I wrote with Python and a Redis storage backend. You can clone the project from here: <a href=\"https:\/\/gitlab.com\/ed11\/pastr\">https:\/\/gitlab.com\/ed11\/pastr<\/a>.<\/p>\n<p>The project uses Flask and Flask-Restful to serve up data stored from a connected Redis database presented with a VueJS UI front-end. (At the time of this writing, it&#8217;s still&#8230; very much lacking in quality; this was just the quickest thing I could slap together for a demo). The application just spins up a Flask WSGI development server for simplicity&#8217;s sake.<\/p>\n<p>Let&#8217;s take a look at the <em>Dockerfile<\/em> to see what we&#8217;re building:<\/p>\n<pre class=\"lang:default decode:true\" title=\"Dockerfile\">FROM python:3.6\r\n\r\nADD pastr \/opt\/pastr\r\nCOPY requirements.txt \/opt\/\r\nRUN pip install -r \/opt\/requirements.txt\r\n\r\nCMD DB_SERVER=$DB_SERVER python \/opt\/pastr\/__init__.py\r\n<\/pre>\n<p>We&#8217;ll break this down line by line, remembering that each line creates its own image layer, as visualized earlier in the <em>Images<\/em> section.<\/p>\n<h5>FROM<\/h5>\n<p>This line tells the <em>Docker<\/em> engine to start our image off by pulling the <em>python<\/em> base image from the official repository (<a href=\"https:\/\/hub.docker.com\/\">hub.docker.com<\/a>). The <em>3.6<\/em> after the colon tells it that we want specifically version 3.6 of Python. This is a <em>tag<\/em> for the image. You can specify tags as a point release for your application or combine it with other text to mean variants (<em>e.g.<\/em> <em>myapp:<\/em><em>1.0-debug<\/em> to indicate that the image runs your application in <em>debug<\/em> mode).<\/p>\n<h5>ADD<\/h5>\n<p>This command copies the contents of the <em>pastr<\/em> directory (in the current project working directory) into the image at <em>\/opt\/<\/em>. Note there are special rules on what <em>ADD<\/em> does. I recommend reading the documentation on the official Docker website:<\/p>\n<p><a href=\"https:\/\/docs.docker.com\/engine\/reference\/builder\/\">https:\/\/docs.docker.com\/engine\/reference\/builder\/<\/a><\/p>\n<h5>COPY<\/h5>\n<p>This command copies a single file (the <em>requirements.txt<\/em> file) into the <em>\/opt<\/em> directory. If you&#8217;re still in doubt on what to use, <strong>USE <em>COPY<\/em><\/strong> instead of <em>ADD<\/em>.<\/p>\n<h5>RUN<\/h5>\n<p>This command starts up a temporary container from the previous image layers, pops open a shell inside the virtual file system, and then begins executing commands. In this case, it simply runs the <em>pip install<\/em> command, which, in a Python project, downloads all the required libraries needed to execute the application. You would normally use this to download third party dependencies, extract tarballs, or change permissions of files to grant execute privileges. After the command is done, it takes the mutable file system layer created by the container and saves it off as an immutable image layer.<\/p>\n<p>Be very mindful of the layer saving when using the RUN command when dealing with large files. For example, if you use this to download a large executable from a third party resource and then change the permissions, you will end up with two layers of the same size. Example:<\/p>\n<pre class=\"lang:default decode:true \" title=\"Dockerfile RUN example BAD\">RUN wget http:\/\/my-file-server\/large-binary-executable\r\nRUN chmod +x large-binary-executable<\/pre>\n<p>Say our <em>large-binary-executable<\/em> is 500MB. The first command will save off an image where the file is not executable, taking up 500MB. The second command will take the 500MB file, change the permissions, and save another image where the 500MB file is executable, essentially taking up 1GB of disk space for anyone who has to download it. Instead, you should run them in one command, like so:<\/p>\n<pre class=\"lang:default decode:true \" title=\"Dockerfile RUN GOOD\">RUN wget http:\/\/my-file-server\/large-binary-executable &amp;&amp; chmod +x large-binary-executable<\/pre>\n<h5>CMD<\/h5>\n<p>The <em>CMD <\/em>directive specifies the command that is to be executed when the container starts up. In our example, we run the <em>python<\/em> command and point it to our application. The <em>DB_SERVER=$DB_SERVER<\/em> is an environment variable that we pass to our application as a rudimentary form of configuration management.<\/p>\n<p>There are actually two ways to specify the container startup command: the <strong>CMD<\/strong> and the <strong>ENTRYPOINT <\/strong>directives. In most cases, these might be interchangeable, but there are nuanced differences on which to use, which are more suitable for a more advanced topic. For now, I will say that semantically, <strong>ENTRYPOINT<\/strong> is generally used to specify the executable and <strong>CMD <\/strong>is used to pass in parameters. The latter can be overridden on the command line prior to starting up.<\/p>\n<h3>Building the Image<\/h3>\n<p>Using the <em>Dockerfile<\/em>, we can build the image manually with the following command:<\/p>\n<pre class=\"lang:sh decode:true \">docker build -t pastr .<\/pre>\n<p>What this command does is build the image using the current working directory (specified by the trailing dot) and naming it <em>pastr<\/em> as indicated by the <em>-t<\/em> directive. We can validate that the image is created by checking the image list.<\/p>\n<pre class=\"lang:sh decode:true\">$ docker images\r\nREPOSITORY         TAG                 IMAGE ID            CREATED             SIZE\r\npastr              latest              6635be8bc083        4 seconds ago       941MB\r\n<\/pre>\n<p>Typically, this would be handled by your build script using a build target plugin, as mentioned earlier.<\/p>\n<h3>Running the Container<\/h3>\n<p>We run the container much like we did with our <em>hello-world<\/em> example above.<\/p>\n<pre class=\"lang:sh decode:true\">docker run --detach --rm --name pastr1 --publish 5000:5000 pastr<\/pre>\n<p>A breakdown of the flags:<\/p>\n<ul>\n<li><em>&#8211;detach <\/em>run the application in the background and return the console back to the user.<\/li>\n<li><em>&#8211;rm<\/em> When the container exits, remove it so it does not linger.<\/li>\n<li><em>&#8211;name<\/em> The name to assign it. If omitted, a random one is generated and assigned.<\/li>\n<li><em>&#8211;publish<\/em> Expose the port on the container, binding it to localhost. In this case, <em>localhost:5000<\/em> on your computer will forward to port 5000 of the container.<\/li>\n<li><em>pastr<\/em> The name of the image to base the container off.<\/li>\n<\/ul>\n<p>From here, we can open a browser up to <em>localhost:5000 <\/em>to view the application.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"67\" data-permalink=\"http:\/\/edspace.com\/blog\/2018\/06\/30\/local-development-with-docker\/mozilla-firefox_015\/\" data-orig-file=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_015.png?fit=1560%2C1248\" data-orig-size=\"1560,1248\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Mozilla Firefox_015\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_015.png?fit=660%2C528\" class=\"alignnone size-full wp-image-67\" src=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_015.png?resize=660%2C528\" alt=\"\" width=\"660\" height=\"528\" srcset=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_015.png?w=1560 1560w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_015.png?resize=300%2C240 300w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_015.png?resize=768%2C614 768w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_015.png?resize=700%2C560 700w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_015.png?w=1320 1320w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/p>\n<p>Of course, if you try typing in anything into the text area and submit, you&#8217;ll get an error indicating that it can&#8217;t connect to the database. So we&#8217;ll have to run a separate Redis database. Let&#8217;s kill off our existing container.<\/p>\n<pre class=\"lang:default decode:true \">$ docker ps\r\nCONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES\r\nadf918f846ef        pastr               \"\/bin\/sh -c 'DB_SERV\u2026\"   About an hour ago   Up About an hour    0.0.0.0:5000-&gt;5000\/tcp   pastr1\r\n$ docker stop pastr1\r\npastr1\r\n<\/pre>\n<p>Now, we&#8217;ll start our Redis database back end server, using the official <em>redis<\/em> image.<\/p>\n<pre class=\"lang:sh decode:true \" title=\"Run Redis\">docker run --detach --rm --name pastedb redis:latest<\/pre>\n<p>With the Redis instance running, we can create our Pastr application and point it to the database.<\/p>\n<pre class=\"lang:sh decode:true \" title=\"Pastr container with DB\">$ docker run --detach --rm --name pastr1 --publish 5000:5000 --link pastedb --env DB_SERVER=pastedb pastr<\/pre>\n<p>You&#8217;ll note that we added a few things to the argument list.<\/p>\n<ul>\n<li><em>&#8211;link<\/em> directs the Docker engine to allow communication between this container and the <em>paste_db<\/em> container, which is the Redis instance we started earlier.<\/li>\n<li><em>&#8211;env<\/em> sets the environment variable the application uses to specify the database server. This is what we specified in the <em>CMD <\/em>line in the <em>Dockerfile<\/em>.<\/li>\n<\/ul>\n<p>From here, we can try again, this time actually pushing the save button.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"69\" data-permalink=\"http:\/\/edspace.com\/blog\/2018\/06\/30\/local-development-with-docker\/mozilla-firefox_016\/\" data-orig-file=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_016.png?fit=1560%2C1248\" data-orig-size=\"1560,1248\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Mozilla Firefox_016\" data-image-description=\"\" data-image-caption=\"\" data-large-file=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_016.png?fit=660%2C528\" class=\"alignnone size-full wp-image-69\" src=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_016.png?resize=660%2C528\" alt=\"\" width=\"660\" height=\"528\" srcset=\"https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_016.png?w=1560 1560w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_016.png?resize=300%2C240 300w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_016.png?resize=768%2C614 768w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_016.png?resize=700%2C560 700w, https:\/\/i0.wp.com\/edspace.com\/blog\/wp-content\/uploads\/2018\/06\/Mozilla-Firefox_016.png?w=1320 1320w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/p>\n<p>It works, end to end, now! Refresh the page and click on the drop down again to see your stored text (bugfix forthcoming).<\/p>\n<p>The problem is, how do we keep track of all the flags that we had to use to get it running?<\/p>\n<h3>Docker Compose<\/h3>\n<p>Docker Compose is an orchestration tool that allows you to create and run Docker containers using a pre-configured YAML file. Let&#8217;s look at our <em>compose<\/em> file.<\/p>\n<pre class=\"lang:default decode:true \" title=\"docker-compose.yml\">version: '3'\r\nservices:\r\n  pastr:\r\n    image: pastr:latest\r\n    build: .\r\n    ports:\r\n      - \"5000:5000\"\r\n    environment:\r\n      - DB_SERVER=database\r\n    links:\r\n      - database\r\n    depends_on:\r\n      - database\r\n  database:\r\n    image: redis:latest\r\n    ports:\r\n      - \"6379:6379\"<\/pre>\n<p>The version field is just so that the <em>docker-compose<\/em> command knows what API set to use. Our application can be found under <em>services<\/em>. You&#8217;ll notice that we have two, the <em>pastr<\/em> app and the backend database. You also may recognize the fields underneath as things we put in the command line to run our containers.<\/p>\n<p>We are already familiar with <em>image, ports<\/em> (which we called <em>publish<\/em>), <em>environment, <\/em> and <em>links<\/em>. We&#8217;ll focus on some of the newer things.<\/p>\n<ul>\n<li><em>build<\/em> the directory to use to build the image if the image does not exist. The build will name it the same as the service, which in this case is <em>pastr<\/em>.<\/li>\n<li><em>depends_on<\/em> this directive will instruct the Docker engine to launch <em>database <\/em>before it starts up <em>pastr<\/em>. Note that it will only affect the orders which containers start, not necessarily wait until the other container application has fully started.<\/li>\n<\/ul>\n<p>If you haven&#8217;t already, now would be a good time to bring down the other containers, as they will conflict with what we are about to do.<\/p>\n<pre class=\"lang:default decode:true\" title=\"docker stop\">docker stop pastr1 pastedb<\/pre>\n<p>We&#8217;ll start by building the <em>pastr<\/em> image using the <em>docker-compose <\/em>command.<\/p>\n<pre class=\"lang:default decode:true \" title=\"docker-compose bulid\">docker-compose build pastr<\/pre>\n<p>From here, we can start up the entire application, including the database.<\/p>\n<pre class=\"lang:default decode:true \" title=\"docker-compose up\">$ docker-compose up -d\r\nCreating network \"pastr_default\" with the default driver\r\nCreating pastr_database_1 ... done\r\nCreating pastr_pastr_1    ... done\r\n<\/pre>\n<p>Again, we use the <em>-d<\/em> flag to detach and run all of our containers in the background. If you ever wish to see the log output of a container, simply run <em>docker-compose logs &lt;container-name&gt;<\/em>.<\/p>\n<pre class=\"lang:default decode:true \">$ docker-compose logs pastr\r\nAttaching to pastr_pastr_1\r\npastr_1     |  * Serving Flask app \"__init__\" (lazy loading)\r\npastr_1     |  * Environment: production\r\npastr_1     |    WARNING: Do not use the development server in a production environment.\r\npastr_1     |    Use a production WSGI server instead.\r\npastr_1     |  * Debug mode: on\r\npastr_1     |  * Running on http:\/\/0.0.0.0:5000\/ (Press CTRL+C to quit)\r\npastr_1     |  * Restarting with stat\r\npastr_1     |  * Debugger is active!\r\npastr_1     |  * Debugger PIN: 133-983-541\r\n<\/pre>\n<p>To shut it all down, issue the <em>stop <\/em>command.<\/p>\n<pre class=\"lang:default decode:true \" title=\"docker-compose down\">$ docker-compose down\r\nStopping pastr_pastr_1    ... done\r\nStopping pastr_database_1 ... done\r\nRemoving pastr_pastr_1    ... done\r\nRemoving pastr_database_1 ... done\r\nRemoving network pastr_default\r\n<\/pre>\n<p>You can also stop and remove individual containers as well as restart containers with the <em>stop, remove, <\/em>and <em>restart<\/em> commands. Give them a try!<\/p>\n<h2>Conclusion<\/h2>\n<p>We have seen what Docker virtualization is and how to run containers manually and through orchestration. In the future, we will learn other things we can do to make local development easier, such as using network bridges and proxies to access multiple containers via the same port.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Alright, I wish I could take back my previous Docker entry. It was pretty useless, so I&#8217;m going to take another shot at this at do it right. I&#8217;ve given this talk about Docker about a dozen times in person, done a recorded (sadly, proprietary) teaching session on it, but I still find myself giving &hellip; <a href=\"http:\/\/edspace.com\/blog\/2018\/06\/30\/local-development-with-docker\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Local Development with Docker<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[5],"tags":[],"class_list":["post-52","post","type-post","status-publish","format-standard","hentry","category-docker"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/paDvvN-Q","jetpack-related-posts":[],"_links":{"self":[{"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/posts\/52","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/comments?post=52"}],"version-history":[{"count":12,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/posts\/52\/revisions"}],"predecessor-version":[{"id":71,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/posts\/52\/revisions\/71"}],"wp:attachment":[{"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/media?parent=52"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/categories?post=52"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/edspace.com\/blog\/wp-json\/wp\/v2\/tags?post=52"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}