{"id":2613,"date":"2016-11-01T13:25:07","date_gmt":"2016-11-01T13:25:07","guid":{"rendered":"http:\/\/blog.designed79.co.uk\/?p=2613"},"modified":"2016-11-01T13:25:07","modified_gmt":"2016-11-01T13:25:07","slug":"connecting-docker-containers-part-two","status":"publish","type":"post","link":"https:\/\/blog.designed79.co.uk\/?p=2613","title":{"rendered":"Connecting Docker Containers, Part Two"},"content":{"rendered":"<p>This post is part two of a miniseries looking at how to connect Docker containers.<\/p>\n<p>In part one, we looked at the bridge network driver that allows us to connect containers that all live on the same Docker host. Specifically, we looked at three basic, older uses of this network driver: port exposure, port binding, and linking.<\/p>\n<p>In this post, we\u2019ll look at a more advanced, and up-to-date use of the bridge network driver.<\/p>\n<p>We\u2019ll also look at using the overlay network driver for connecting Docker containers across multiple hosts.<\/p>\n<p>User-Defined Networks<\/p>\n<p>Docker 1.9.0 was released in early November 2015 and shipped with some exciting new networking features. With these changes, now, for two containers to communicate, all that is required is to place them in the same network or sub-network.<\/p>\n<p>Let\u2019s demonstrate that.<\/p>\n<p>First, let\u2019s see what we already have:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ sudo docker network ls<br \/>\nNETWORK ID NAME DRIVER<br \/>\n362c9d3713cc bridge bridge<br \/>\nfbd276b0df0a singlehost bridge<br \/>\n591d6ac8b537 none null<br \/>\nac7971601441 host host<\/div><\/div>\n<p>Now, let\u2019s create a network :<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ sudo docker network create backend<\/div><\/div>\n<p>If that worked, our network list will show our newly created network:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ sudo docker network ls<br \/>\nNETWORK ID NAME DRIVER<br \/>\n362c9d3713cc bridge bridge<br \/>\nfbd276b0df0a singlehost bridge<br \/>\n591d6ac8b537 none null<br \/>\nac7971601441 host host<br \/>\nd97889cef288 backend bridge<\/div><\/div>\n<p>Here we can see the backend network has been created using the default bridge driver. This is a bridge network, as covered in part one of this miniseries, and is available to all containers on the local host.<\/p>\n<p>We\u2019ll use the client_img and server_img images we created in part one of this miniseries. So, if you don\u2019t already have them set up on your machine, go back and do that now. It won\u2019t take a moment.<\/p>\n<p>Got your images set up? Cool.<\/p>\n<p>Let\u2019s run a server container from the server_img image and put it on the backend network using the &#8211;net option.<\/p>\n<p>Like so:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ sudo docker run -itd --net=backend --name=server server_img \/bin\/bash<\/div><\/div>\n<p>Like before, attach to the container:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ sudo docker attach server<\/div><\/div>\n<p>If you do not see the shell, click the up arrow.<\/p>\n<p>Now start the Apache HTTP server:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ \/etc\/init.d\/apache2 start<\/div><\/div>\n<p>At this point, any container on the backend network will be able to access our Apache HTTP server.<\/p>\n<p>We can test this by starting a client container on a different terminal, and putting it on the backend network.<\/p>\n<p>Like so:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ sudo docker run -itd --net=backend --name=client client_img \/bin\/bash<\/div><\/div>\n<p>Attach to the container:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ sudo docker attach client<\/div><\/div>\n<p>Again, if you do not see the shell, click the up arrow.<\/p>\n<p>Now run:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ curl server<\/div><\/div>\n<p>You should see the default web page HTML. This tells us our network is functioning as expected.<\/p>\n<p>Like mentioned in part one of this miniseries, Docker takes care of setting up the container names as resolvable hostnames, which is why we can curl server directly without knowing the IP address.<\/p>\n<p>Multiple user-defined networks can be created, and containers can be placed in one or more networks, according to application topology. This flexibility, then, is especially useful for anyone wanting to deliver microservices, multitenancy, and micro-segmentation architectures.<\/p>\n<p>Multi-Host Networking<\/p>\n<p>What if you want to create networks that span multiple hosts? Well, since Docker 1.9.0, you can do just that!<\/p>\n<p>So far, we\u2019ve been using the bridge network driver, which has a local scope, meaning bridge networks are local to the Docker host. Docker now provides a new overlay network driver, which has global scope, meaning overlay networks can exist across multiple Docker hosts. And those Docker hosts can exist in different datacenters, or even different cloud providers!<\/p>\n<p>To set up an overlay network, you\u2019ll need:<\/p>\n<p>A host with a 3.16 kernel version or higher<br \/>\nA key-value store (e.g. etcd, Consul, and Apache ZooKeeper)<br \/>\nA cluster of hosts with connectivity to the key-value store<br \/>\nA properly configured Docker Engine daemon on each host in the cluster<br \/>\nLet\u2019s take a look at an example.<\/p>\n<p>For the purposes of this post, I am going to use the multihost-local.sh script with Docker Machine to get three virtual hosts up and running.<\/p>\n<p>This script spins up Virtual Machines (VMs), not containers. We then run Docker on these VMs to simulate a cluster of Docker hosts.<\/p>\n<p>After running the script, here&#8217;s what I have:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ docker-machine ls<br \/>\nNAME ACTIVE DRIVER STATE URL SWARM ERRORS<br \/>\nmhl-consul - virtualbox Running tcp:\/\/192.168.99.100:2376<br \/>\nmhl-demo0 - virtualbox Running tcp:\/\/192.168.99.101:2376<br \/>\nmhl-demo1 - virtualbox Running tcp:\/\/192.168.99.102:2376<\/div><\/div>\n<p>Okay, let\u2019s rewind and look at what just happened.<\/p>\n<p>This script makes use of Docker Machine, which you must have installed. For this post, we used Docker Machine 0.5.2. For instructions on how to download and install 0.5.2 for yourself, see the release notes.<\/p>\n<p>The multihost-local.sh script uses Docker Machine to provision three VirtualBox VMs, installs Docker Engine on them, and configure them appropriately.<\/p>\n<p>Docker Machine works with most major virtualization hypervisors and cloud service providers. It has support for AWS, Digital Ocean, Google Cloud Platform, IBM Softlayer, Microsoft Azure and Hyper-V, OpenStack, Rackspace, VirtualBox, VMware Fusion\u00ae, vCloud\u00ae Air\u2122 and vSphere\u00ae.<\/p>\n<p>We now have three VMs:<\/p>\n<p>mhl-consul: runs Consul<br \/>\nmhl-demo0: Docker cluster node<br \/>\nmhl-demo1: Docker cluster node<br \/>\nThe Docker cluster nodes are configured to coordinate through the VM running Consul, our key-value store. This is how the cluster comes to life.<\/p>\n<p>Cool. Fastforward.<\/p>\n<p>Now, let\u2019s set up an overlay network.<\/p>\n<p>First, we need to grab a console on the mhl-demo0 VM, like so:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ eval $(docker-machine env mhl-demo0)<\/div><\/div>\n<p>Once there, run:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ docker network create -d overlay myapp<\/div><\/div>\n<p>This command creates an overlay network called myapp across all the hosts in the cluster. This is possible because Docker is coordinating with the rest of the cluster through the key-value store.<\/p>\n<p>To confirm this has worked, we can grab a console on each VM in the cluster and list out the Docker networks.<\/p>\n<p>Copy the eval command above, replacing mhl-demo0 with the relevent host name.<\/p>\n<p>Then run:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ docker network ls<br \/>\nNETWORK ID NAME DRIVER<br \/>\n7b9e349b2f01 host host<br \/>\n1f6a49cf5d40 bridge bridge<br \/>\n38e2eba8fbc8 none null<br \/>\n385a8bd92085 myapp overlay<\/div><\/div>\n<p>Here you see the myapp overlay network.<\/p>\n<p>Success!<\/p>\n<p>Remember though: all we\u2019ve done so far is create a cluster of Docker VMs and configure an overlay network which they all share. We\u2019ve not actually created any Docker containers yet. So let\u2019s do that and test the network.<\/p>\n<p>We\u2019re going to:<\/p>\n<p>Run the default nginx image on the mhl-demo0 host (this provides us with a preconfigured Nginx HTTP server)<br \/>\nRun the default busybox image on the mhl-demo1 host (this provides us with a basic OS and tools like GNU Wget)<br \/>\nAdd both containers into the myapp network<br \/>\nTest they can communicate<br \/>\nFirst, grab a console on the mhl-demo0 host:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ eval $(docker-machine env mhl-demo0)<\/div><\/div>\n<p>Then, run the nginx image:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ docker run --name ng1 --net=myapp -d nginx<\/div><\/div>\n<p>To recap, we now have:<\/p>\n<p>A Nginx HTTP server,<br \/>\nRunning in a container called ng1,<br \/>\nIn the myapp network,<br \/>\nOn the mhl-demo0 host<br \/>\nTo test this is working, let\u2019s try to access it from another container on another host.<\/p>\n<p>Grab a console on the mhl-demo1 host this time:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ eval $(docker-machine env mhl-demo1)<\/div><\/div>\n<p>Then run:<\/p>\n<div class=\"codecolorer-container text default\" style=\"overflow:auto;white-space:nowrap;\"><div class=\"text codecolorer\">$ docker run -it --net=myapp busybox wget -qO- ng1<\/div><\/div>\n<p>What this does:<\/p>\n<p>Creates an unnamed container from the busybox image,<br \/>\nAdds it to the myapp network,<br \/>\nRuns the command wget -qO- ng1,<br \/>\nAnd stops the container (we left our other containers running before)<br \/>\nThe ng1 in that Wget command is the the name of our Nginx container. Docker lets us use the container name as a resolvable hostname, even though the container is running on a different Docker host.<\/p>\n<p>If everything is successful, you should see something like this:<\/p>\n<p>Welcome to nginx!<br \/>\nVoila! We have a multi-host container network.<\/p>\n<p>Conclusion<\/p>\n<p>Docker offer the advantages of lightweight self-contained and isolated environments. However, it is crucial that containers are able to communicate with each other and with the host network if they are going to be useful for us.<\/p>\n<p>In this miniseries, we have explored a few ways to connect containers locally and across multiple hosts. We\u2019ve also looked at how to network containers with the host network.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is part two of a miniseries looking at how to connect Docker containers. In part one, we looked at the [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2613","post","type-post","status-publish","format-standard","hentry","category-info-on-tech"],"_links":{"self":[{"href":"https:\/\/blog.designed79.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/2613","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.designed79.co.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.designed79.co.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.designed79.co.uk\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.designed79.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2613"}],"version-history":[{"count":0,"href":"https:\/\/blog.designed79.co.uk\/index.php?rest_route=\/wp\/v2\/posts\/2613\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.designed79.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2613"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.designed79.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2613"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.designed79.co.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2613"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}