<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Play with Docker classroom</title>
    <description>Play with docker tutorials
</description>
    <link>https://training.play-with-docker.com/</link>
    <atom:link href="https://training.play-with-docker.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 21 Jan 2026 20:31:32 +0000</pubDate>
    <lastBuildDate>Wed, 21 Jan 2026 20:31:32 +0000</lastBuildDate>
    <generator>Jekyll v3.10.0</generator>
    
      <item>
        <title>Medical physics with Geant4 + Docker = ❤️</title>
        <description>&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcj6lhk6srj31lc0ksdoa.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this lab we will learn about the basics of low and mid-energy radiation-matter interaction with the help of &lt;a href=&quot;https://geant4.web.cern.ch/geant4/&quot;&gt;Geant4&lt;/a&gt;, the Geant4 binding for Python (&lt;a href=&quot;https://gitlab.cern.ch/geant4/geant4/blob/master/environments/g4py/README.md&quot;&gt;g4py&lt;/a&gt;),  and of course, our beloved Docker. This lab may fall into the recent (and praiseworthy) trend related to the so called &lt;em&gt;&lt;a href=&quot;https://www.researchgate.net/profile/Carl_Boettiger/publication/266477818_An_introduction_to_Docker_for_reproducible_research_with_examples_from_the_R_environment/links/56e81ce508ae9aecadbaca56/An-introduction-to-Docker-for-reproducible-research-with-examples-from-the-R-environment.pdf?_sg%5B0%5D=98jcI0oukjCz_zwDtOe9nXl_rTsULpFEgS5NSqVYu6vTMAD9OSLNamM_AikeK9fZ8EQ8jJOikPLNUbCNslzckw.7kVkDKbcq_mR5Oxx2Fs0VjUrVXGcbn1-3XGJCLgznKN9YJW_O4NeIa2Sq__GbpSILXrYgBnO92d3jNdJPN6v6g&amp;amp;_sg%5B1%5D=Uc-IV8Be_BJSbvZve1HCV6YsdFeB1yzhuQp4jEuuqecUOxUyKQt1Samh4Ad_xRNNG1jLlI2Txx1oscSYWTeCMF819aMxk5wl2zx6X5sGL52B.7kVkDKbcq_mR5Oxx2Fs0VjUrVXGcbn1-3XGJCLgznKN9YJW_O4NeIa2Sq__GbpSILXrYgBnO92d3jNdJPN6v6g&amp;amp;_iepl=&quot;&gt;reproducible research&lt;/a&gt;&lt;/em&gt; and &lt;em&gt;&lt;a href=&quot;https://ieeexplore.ieee.org/document/7555277&quot;&gt;Open Educational Resources as a Service&lt;/a&gt;&lt;/em&gt;. This PWD/Docker-based experience has been carried out with huge success at &lt;strong&gt;Universidad Internacional de La Rioja (UNIR)&lt;/strong&gt;, as described in this &lt;a href=&quot;https://iopscience.iop.org/article/10.1088/1361-6404/ab5011&quot;&gt;&lt;em&gt;research paper&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;simulating-the-building-blocks-of-reality&quot;&gt;Simulating the building blocks of Reality&lt;/h2&gt;

&lt;p&gt;To begin with, let’s start by summarising what is &lt;strong&gt;Geant4&lt;/strong&gt; and why it is so important in todays particle physics research. As very well stated by &lt;a href=&quot;https://www.sciencedirect.com/science/article/pii/S0168900203013688&quot;&gt;Agostinelli et al.&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;img src=&quot;https://www.fib.upc.edu/sites/fib/files/logo-cern.jpg&quot; style=&quot;float:right;width:170px;height:142px;&quot; /&gt;&lt;em&gt;Geant4 is a toolkit for simulating the &lt;strong&gt;passage of particles through matter&lt;/strong&gt;. Its functionalities include &lt;strong&gt;tracking&lt;/strong&gt;, &lt;strong&gt;geometry&lt;/strong&gt;, &lt;strong&gt;physics&lt;/strong&gt; models and &lt;strong&gt;hits&lt;/strong&gt;. The physics processes take into account &lt;strong&gt;electromagnetic&lt;/strong&gt;, &lt;strong&gt;hadronic&lt;/strong&gt; and &lt;strong&gt;optical&lt;/strong&gt; processes, as well as the surrounding &lt;strong&gt;materials&lt;/strong&gt;, over a &lt;strong&gt;wide energy range&lt;/strong&gt;. The toolkit is the result of a worldwide collaboration of &lt;strong&gt;physicists&lt;/strong&gt; and software &lt;strong&gt;engineers&lt;/strong&gt;. It has been implemented in the &lt;strong&gt;C++&lt;/strong&gt; programming language. It has been used in applications in &lt;strong&gt;Particle Physics&lt;/strong&gt;, &lt;strong&gt;Nuclear Physics&lt;/strong&gt;, accelerator design, &lt;strong&gt;Space&lt;/strong&gt; Engineering and &lt;strong&gt;Medical Physics&lt;/strong&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&quot;https://nukephysik101.files.wordpress.com/2016/01/screenshot-from-2016-01-30-003434.png?w=584&quot; style=&quot;float:right;width:50%;padding:6px;&quot; /&gt;In other words, Geant4 implements our deepest (and best) level of knowledge about Nature… in the form of computer (C++) code. This &lt;em&gt;deepest level&lt;/em&gt; is no other thing but the &lt;a href=&quot;https://en.wikipedia.org/wiki/Standard_Model&quot;&gt;&lt;strong&gt;Standard Model&lt;/strong&gt;&lt;/a&gt; of physics. This picture describes the &lt;strong&gt;building blocks of Reality&lt;/strong&gt; (the subatomic particles) as well as the &lt;strong&gt;fundamental forces&lt;/strong&gt; that govern their interactions. Geant4 acts as some sort of &lt;em&gt;magnifying lens&lt;/em&gt; that amplifies what happens when a given particle (an electron, a kaon, a muon, or a photon) hits or, better said, &lt;em&gt;interacts&lt;/em&gt; with its surroundings. This computer toolbox is able to simulate almost all possible scenarios, no matter the actors involved, the geometry or the energy of the event.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcRYMxNXRP_19ElbAi0mICSSq9vdLhWSmITg929t_nVKzfbM3CG5&quot; style=&quot;float:right;width:25%;padding:0px;&quot; /&gt;This turns out perfect if we happen to be particle physicists, but it may be felt as &lt;em&gt;over complicated&lt;/em&gt; for those who just need to model &lt;em&gt;humbler&lt;/em&gt; atomic interactions for specific fields. One if these specific fields is &lt;strong&gt;Medical Physics&lt;/strong&gt; and, in this case, Geant4 has a wonderful and easy to use &lt;strong&gt;Python binding&lt;/strong&gt;, g4py. With the help of Docker, g4py is even easier to use.&lt;/p&gt;

&lt;h2 id=&quot;particle-physics-in-medicine&quot;&gt;Particle Physics in Medicine&lt;/h2&gt;

&lt;p&gt;The field of Medical Physics has its own complexity. Subatomic particles can be used to treat cancer or tumours or can be used to &lt;strong&gt;&lt;em&gt;image&lt;/em&gt; (probe) a patient’s body&lt;/strong&gt;, i.e., to &lt;em&gt;see&lt;/em&gt; its inner parts. This last application is the one that will be tackled in this interactive lab. Normally, the type of imaging particle will be the (X-ray) photon, although you’ll be able to play with other &lt;em&gt;probing rays&lt;/em&gt; such as electrons or positrons. Photons can be defined as &lt;em&gt;particles of light&lt;/em&gt; and were originally proposed by Einstein in one of his &lt;a href=&quot;http://astro1.panet.utoledo.edu/~ljc/PE_eng.pdf&quot;&gt;1905 paper&lt;/a&gt; &lt;em&gt;An Heuristic Point of View Toward the Emission and Transformation of Light&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://www.ous-research.no/medicalphysics/images/PET_1.JPG&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In Medical Imaging, mid-energy photons (a few keV) are &lt;em&gt;projected&lt;/em&gt; against the patient. Some of these electromagnetic entities are completely absorbed (&lt;a href=&quot;https://en.wikipedia.org/wiki/Photoelectric_effect&quot;&gt;&lt;strong&gt;photoelectric&lt;/strong&gt;&lt;/a&gt; effect), others are diverted (&lt;a href=&quot;https://en.wikipedia.org/wiki/Compton_scattering&quot;&gt;&lt;strong&gt;Compton&lt;/strong&gt;&lt;/a&gt; scattering) by atoms, and others (if their energy is high enough) can even transform themselves into a pair of new objects composed of a electron and an anti-electron (a &lt;em&gt;positron&lt;/em&gt;). This last process is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Pair_production&quot;&gt;&lt;strong&gt;pair production&lt;/strong&gt;&lt;/a&gt;. In turn, when a positron accidentally hits and electron, they both disappear back into two photons which travel in opposite directions. This is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Electron–positron_annihilation&quot;&gt;&lt;strong&gt;annihilation&lt;/strong&gt;&lt;/a&gt; and it’s the foundation of &lt;a href=&quot;https://en.wikipedia.org/wiki/Positron_emission_tomography&quot;&gt;&lt;strong&gt;PET&lt;/strong&gt; imaging&lt;/a&gt; (which produces beautiful and lifesaving images such as the one shown above).&lt;/p&gt;

&lt;h2 id=&quot;geant4-and-medical-physics&quot;&gt;Geant4 and Medical Physics&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/pammacdotnet/FFRepo/raw/master/simulation.gif?sanitize=true&quot; style=&quot;float:right;width:60%;padding:6px;&quot; /&gt;As stated above, Geant4 has a &lt;em&gt;difficult-to-grasp&lt;/em&gt; C++ heart, but &lt;strong&gt;thanks to g4py&lt;/strong&gt;, its easy to design simple scenarios related to the field of &lt;strong&gt;Medical Physics&lt;/strong&gt; which &lt;strong&gt;radiologists&lt;/strong&gt; and &lt;strong&gt;doctors&lt;/strong&gt; can even used in their daily practice. However, installing and configuring Geant4 and g4py can turn out awkward. We can &lt;em&gt;alleviate&lt;/em&gt; this problem with Docker and better yet, we can &lt;em&gt;play&lt;/em&gt; with a simple, yet interesting simulation thanks to Play with Docker. This simulation consists of the &lt;strong&gt;&lt;em&gt;following ingredients&lt;/em&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A &lt;strong&gt;world&lt;/strong&gt;, or the &lt;em&gt;simulation limits&lt;/em&gt;. This world has the shape of an empty &lt;em&gt;elongated cuboid&lt;/em&gt;. The longest axis is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z&lt;/code&gt; (or longitudinal) axis.&lt;/li&gt;
  &lt;li&gt;A &lt;strong&gt;beam&lt;/strong&gt; of particles that are initially projected along the aforementioned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z&lt;/code&gt;-axis. This beam has a nature (type of particles), an energy (in &lt;em&gt;mega-electronvolts&lt;/em&gt; or MeV, for short) and a count (i.e., how many particles). In a real particle physics experiment or setup, generating (and conforming) such a beam has its complexities. However, in a simulated world, we don’t have to worry about those.&lt;/li&gt;
  &lt;li&gt;A &lt;strong&gt;target&lt;/strong&gt;, or something the particles can &lt;em&gt;hit&lt;/em&gt; in their way. This target has also cuboid shape, with a width (thickness) and location (both also defined along the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z&lt;/code&gt;-axis as the world) and is composed of a given (and selectable) material. In Medical Physics, the target is also commonly known as &lt;em&gt;phantom&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can take a 3D look at this world in the following image:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/0082zybpgy1gc7cxrbxxdj315m0k50xx.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Or you can also surf a &lt;strong&gt;live WebGL version&lt;/strong&gt; by clicking in &lt;a href=&quot;https://htmlpreview.github.io/?https://github.com/pammacdotnet/FFRepo/blob/master/simulation%20example.html&quot; target=&quot;_blank&quot;&gt;this link&lt;/a&gt;. A quick (and tiny) animation is also displayed above.&lt;/p&gt;

&lt;p&gt;We will now launch &lt;strong&gt;our own simulations&lt;/strong&gt;. In order to achieve so, let us first &lt;strong&gt;download the Docker image&lt;/strong&gt; that contains the necessary infrastructure. Remember, you can just &lt;strong&gt;click in the code cells to have them&lt;/strong&gt; (one by one) &lt;strong&gt;executed&lt;/strong&gt; ▶️ in the terminal on the left side of this page:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker pull pammacdotnet/g4lpwd
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It may take a minute 🕐 or so to download the whole image… so please, be (slightly) patient.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;download-the-simulation-code-from-github&quot;&gt;Download the simulation code from Github&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;code&lt;/strong&gt; of the simple simulation described above can be downloaded by clicking in the next code cell:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl https://raw.githubusercontent.com/pammacdotnet/FFRepo/master/simulation.py -o simulation.py -s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We recommend you &lt;strong&gt;take a quick look&lt;/strong&gt; (even if you are not a Python or particle physics enthusiast) so that you realise how it is very easy to experiment with modern computer simulations thanks to awesome tools such as g4py:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;pip install pygments
pygmentize -g simulation.py
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Also, download (and enable the execution of) this simple Python script to &lt;strong&gt;convert from &lt;a href=&quot;https://en.wikipedia.org/wiki/VRML&quot;&gt;WRL&lt;/a&gt; data to &lt;a href=&quot;https://www.x3dom.org&quot;&gt;x3dom&lt;/a&gt;-enabled HTML&lt;/strong&gt; content:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl https://raw.githubusercontent.com/pammacdotnet/FFRepo/master/wrl2html.py -o wrl2html.py -s
chmod +x wrl2html.py 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will allow us to visualise &lt;strong&gt;3D scenes&lt;/strong&gt; (like the one linked above) in the same Web browser. BTW, this last command  (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wrl2html.py&lt;/code&gt;) uses &lt;a href=&quot;https://doc.instantreality.org/tools/x3d_encoding_converter/&quot;&gt;this online converter&lt;/a&gt; made by the company &lt;a href=&quot;https://www.instantreality.org&quot;&gt;instantreality&lt;/a&gt; (part of the &lt;a href=&quot;http://www.igd.fraunhofer.de/vcst/&quot;&gt;Visual Computing System Technologies Group&lt;/a&gt; which, in turn, belongs to the reputed &lt;a href=&quot;https://www.fraunhofer.de&quot;&gt;Fraunhofer&lt;/a&gt; Institute).&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;first-simulation&quot;&gt;First simulation&lt;/h2&gt;

&lt;p&gt;Let’s run our first simulation. It will consist of a beam of 20 photons, each of 1 MeV that hits a water filled target (with a thickness of 20 cm):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -v ${PWD}:/root pammacdotnet/g4lpwd
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You’ll see a bunch of information appearing in the terminal:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;**************************************************************
 Geant4 version Name: geant4-10-04-patch-02    (25-May-2018)
                       Copyright : Geant4 Collaboration
                      References : NIM A 506 (2003), 250-303
                                 : IEEE-TNS 53 (2006), 270-278
                                 : NIM A 835 (2016), 186-225
                             WWW : http://geant4.org/
**************************************************************

Visualization Manager instantiating with verbosity &quot;warnings (3)&quot;...
/tracking/storeTrajectory 1
/tracking/storeTrajectory 2

phot:   for  gamma    SubType= 12  BuildTable= 0
      LambdaPrime table from 200 keV to 100 TeV in 61 bins 
      ===== EM models for the G4Region  DefaultRegionForTheWorld ======
       PhotoElectric :  Emin=        0 eV    Emax=      100 TeV   AngularGenSauterGavrila

compt:   for  gamma    SubType= 13  BuildTable= 1
      Lambda table from 100 eV  to 1 MeV, 7 bins per decade, spline: 1
      LambdaPrime table from 1 MeV to 100 TeV in 56 bins 
      ===== EM models for the G4Region  DefaultRegionForTheWorld ======
…
…
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is Geant4 telling you all the calculations it has had to carry out for you in behind the scenes in order to generate a final (and more intuitive) file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simulation.wrl&lt;/code&gt; (default name). It is now time to launch a &lt;strong&gt;simple web server&lt;/strong&gt; that will let us download or view the results:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;python3 -m http.server 80 &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt; symbol we state we want the web server to run in the background and with the expression  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt; /dev/null 2&amp;gt;&amp;amp;1&lt;/code&gt; we guarantee that its output won’t bother us. Once started, the previous file can be downloaded from &lt;a href=&quot;/simulation.wrl&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;here&lt;/a&gt; ⬇️. It’ll take some time because a VRLM file can be quite big.&lt;/p&gt;

&lt;p&gt;Once in your hard drive, you can 3D-navigate/fly through this file (or any VRML scene) with any of the following software:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.instantreality.org/downloads/&quot;&gt;Instant Player&lt;/a&gt;, by &lt;a href=&quot;https://www.instantreality.org&quot;&gt;instantreality&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://castle-engine.io/view3dscene.php&quot;&gt;view3dscene&lt;/a&gt;, by &lt;a href=&quot;https://castle-engine.io/index.php&quot;&gt;Castle Game Engine&lt;/a&gt;,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.paraview.org&quot;&gt;Paraview&lt;/a&gt;, by &lt;a href=&quot;https://www.kitware.com&quot;&gt;Kitware&lt;/a&gt; and,&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.blender.org&quot;&gt;Blender&lt;/a&gt; (among many others).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here you can see a screenshot of &lt;strong&gt;view3dscene&lt;/strong&gt; (Windows version):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcj9r1xo84j31f60i2guf.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;However, it is better to &lt;strong&gt;transform the WRL file into WebGL-enabled HTML&lt;/strong&gt; with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wrl2html.py&lt;/code&gt; command tackled above:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;./wrl2html.py 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By default, this last command assumes the input WRL file to have the name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simulation.wrl&lt;/code&gt; and outputs an HTML file called, by default, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simulation.html&lt;/code&gt;. You previous instruction is equivalent to:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;./wrl2html.py --wrl simulation.wrl --html simulation.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can fly through this 3D HTML version by clicking &lt;a href=&quot;/simulation.html&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;here&lt;/a&gt; ↗️, or you can &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;&lt;strong&gt;list all files&lt;/strong&gt;&lt;/a&gt; 📁.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;more-simulations&quot;&gt;More simulations&lt;/h2&gt;

&lt;p&gt;Let’s explore more simulations! You achieve this by altering any of the configurable parameters:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Particle type&lt;/strong&gt;. Available with the option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--type&lt;/code&gt;. Possible values are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e-&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e+&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gamma&lt;/code&gt;. The default value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gamma&lt;/code&gt; (also known as photons or, simply, light).&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Particle energy&lt;/strong&gt;. Available with the option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--energy&lt;/code&gt;. All values are expressed in MeV and should be integer values. The default values is 1 (MeV). Bear in mind that X-rays have more or less 0.1 MeV of energy.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phantom material&lt;/strong&gt;. Available with the option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--material&lt;/code&gt;. Geant4 defines many materials: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Al&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Si&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Ar&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Cu&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Fe&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Ge&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Ag&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_W&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Au&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Pb&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_AIR&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_Galactic&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_WATER&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_CESIUM_IODIDE&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_SODIUM_IODIDE&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_PLASTIC_SC_VINYLTOLUENE&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_MYLAR&lt;/code&gt;, etc. The default value in our simulations is water (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;G4_WATER&lt;/code&gt;). Do you remember that the human body is composed of 70% water?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Phantom thickness&lt;/strong&gt;. Available with the option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--size&lt;/code&gt;. It is the size (along the longitudinal axis) of the phantom, that is, how much matter the beam of particle will have to traverse.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Output file&lt;/strong&gt;. Available with the option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--wrl&lt;/code&gt;. It represents the name of the VRML file with a 3D representation of the simulation. The default value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simulation.wrl&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Particle count&lt;/strong&gt;. Available with the option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--count&lt;/code&gt;. It is the number of particles within the simulated beam. The default value is 20. We suggest that you keep this value under 💯. Otherwise you’ll end up with a huge WRL file, very difficult to manage and visually analyse.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;antimatter-to-the-rescue&quot;&gt;Antimatter to the rescue!&lt;/h3&gt;

&lt;p&gt;For instance, the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -v ${PWD}:/root pammacdotnet/g4lpwd --type=e+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;will &lt;strong&gt;exchange photons for positrons&lt;/strong&gt;. Positrons are the antiparticles of electrons. The major difference from electrons is their positive charge. You can either download again the &lt;a href=&quot;/simulation.wrl&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;WRL file&lt;/a&gt; ⬇️ or generate a new HTML file. If you run again the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wrl2html.py&lt;/code&gt; command with no arguments, the original &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;simulation.html&lt;/code&gt; file will be overwritten ⚠️. However you can change the name of the resulting HTML file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;./wrl2html.py --html simulation2.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This file is &lt;a href=&quot;/simulation2.html&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;still browsable&lt;/a&gt; ↗️.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.physicscentral.com/explore/action/images/antics-img2.gif&quot; style=&quot;float:right;width:20%&quot; /&gt;Positrons tend to &lt;strong&gt;annihilate with electrons&lt;/strong&gt;. When this happens, &lt;strong&gt;two photons emerge&lt;/strong&gt; in opposite directions.  As you can see from the following picture, the positrons (yellow traces) suddenly seem to disappear. What is happening is that those positrons find a corresponding electron (attached to the atoms that compose the material) and the mass of both particles is transformed into two photons emerging in any random direction (but opposite ways).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcdmiq6fkyj30k906qq3l.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;light-seems-to-zigzag&quot;&gt;Light seems to zigzag!&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;https://i.pinimg.com/originals/15/ec/8d/15ec8d1c8d7dded0592b39ebb9075289.jpg&quot; style=&quot;float:right;width:20%&quot; /&gt;Another effect that you will see in the previous simulation is that light rays seem to bend with sharp angles. This is due to the &lt;strong&gt;Compton scattering&lt;/strong&gt;. In this process, the path of a photon is diverted by a charged particle, usually an electron. In each scattering event, the ray of light loses energy (decreases its frequency) and its finally &lt;em&gt;absorbed&lt;/em&gt; by the material.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcdmw5bh1lj30jy06zjrs.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;superman-o-supergirl-approved-shielding&quot;&gt;Superman (o Supergirl)-approved shielding!&lt;/h3&gt;

&lt;p&gt;Let’s now change the particle type to a more common one (electrons) and let’s have them collide against a lead target:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -v ${PWD}:/root pammacdotnet/g4lpwd --type=e- --material=G4_Pb --wrl simulation3.wrl
./wrl2html.py --wrl simulation3.wrl --html simulation3.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The results are displayed &lt;a href=&quot;/simulation3.html&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;here&lt;/a&gt; ↗️. Again, you can specify a new WRL file (with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--wrl&lt;/code&gt; option). As you can see, 1 MeV electrons are unable to penetrate the material. On the contrary, some of them seem to &lt;em&gt;bounce back&lt;/em&gt;. This is due to electrostatic repulsion against the electronic cloud covering this lead shield.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcdo3cw7pwj30l6080dgc.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;beware-of-radiation&quot;&gt;Beware of radiation!&lt;/h3&gt;

&lt;p&gt;Let’s repeat the same simulation with a (virtual) water target and with a greater energy:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -v ${PWD}:/root pammacdotnet/g4lpwd --type=e- --energy=100 --wrl simulation4.wrl
./wrl2html.py --wrl simulation4.wrl --html simulation4.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;https://web2.uwindsor.ca/courses/physics/high_schools/2006/Medical_Imaging/x%20ray%20brem.gif&quot; style=&quot;float:right;width:30%&quot; /&gt;If you carefully observe the &lt;a href=&quot;/simulation4.html&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;simulation results&lt;/a&gt; ↗️, you’ll notice the famous &lt;em&gt;electromagnetic showers&lt;/em&gt;. This is the same process that occurs when a cosmic ray hits the upper atmosphere and triggers the generation of new particles. The primary process that we are watching here is &lt;strong&gt;&lt;em&gt;bremsstrahlung&lt;/em&gt;&lt;/strong&gt;, which consists in the production of electromagnetic radiation (a photon, represented by a white line) when an electron &lt;em&gt;decelerates&lt;/em&gt; by the presence of matter.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcdq6dpi0ij31d40ncq7t.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Remember you can either download the resulting WRL file or navigate through the x3dom-enabled HTML file.&lt;/p&gt;

&lt;h3 id=&quot;ionization&quot;&gt;Ionization&lt;/h3&gt;

&lt;p&gt;Many of the already proposed simulations exhibit ionisations. In this type of interaction, a photon &lt;em&gt;hits&lt;/em&gt; an electron (attached to an atom) and produces an ion (i.e., an atom &lt;em&gt;negatively charged&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcdspm2qgzj30yq0d6jrw.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The previous simulation was obtained with the following command (and whose results can be explored &lt;a href=&quot;/simulation5.html&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;here&lt;/a&gt; ↗️):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -v ${PWD}:/root pammacdotnet/g4lpwd --type=gamma --energy=10 --material=G4_Al --wrl simulation5.wrl
./wrl2html.py --wrl simulation5.wrl --html simulation5.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ionisations can also take place when a moving electron hits an atom at rest (well, one of its orbiting electrons), that is, the ionisation particle can be of the same nature as the removed one.&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;simulation-limits-and-photoelectric-absorption&quot;&gt;Simulation limits and photoelectric absorption&lt;/h2&gt;

&lt;p&gt;Let’s go back a water-filled cuboid and low energies, but let’s &lt;em&gt;insanely&lt;/em&gt; increase the number of particles (photons). Run the following command in the terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -v ${PWD}:/root pammacdotnet/g4lpwd --type=gamma --energy=2 --count=100 --wrl simulation6.wrl
./wrl2html.py --wrl simulation6.wrl --html simulation6.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You’ll see an &lt;a href=&quot;/simulation6.html&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;scenario&lt;/a&gt; ↗️ very similar to this one, where the photons, after traveling in straight lines and/or interacting with matter, seem to disappear:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcdta9jpymj31os0mk0zt.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Well, where did all the photons go? Well, some of them reach the &lt;em&gt;world limits&lt;/em&gt;… Do you remember the movie &lt;em&gt;&lt;a href=&quot;https://www.imdb.com/title/tt0139809/&quot;&gt;The 13th Floor&lt;/a&gt;&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://66.media.tumblr.com/e1e51070e9065fb3cc74d24e8d59c2cf/tumblr_omq4gg2LUn1w4t58uo1_400.gifv&quot; style=&quot;float:right;width:50%;padding:6px;&quot; /&gt;When this happens, Geant4 stops calculating any further interactions and any particle trace that crosses the boundaries of the simulation seems to suddenly vanish. However, even inside the simulation realm, some photons seem to &lt;em&gt;evaporate&lt;/em&gt;. What’s happening? The answer is simple: they are absorbed by the material. With more detail, these absorption processes correspond to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Photoelectric_effect&quot;&gt;&lt;strong&gt;photoelectric effect&lt;/strong&gt;&lt;/a&gt;: photons are completely absorbed by electrons.&lt;/p&gt;

&lt;p&gt;Here you have a second simulation that better shows the previous processes:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcexurwrk1j30rw0a3jt5.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This scene can be recreated with the following command, where a beam of just a few electrons hits an iron target:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -v ${PWD}:/root pammacdotnet/g4lpwd --type=e- --material=G4_Fe --count=10 --energy=50 --wrl simulation7.wrl
./wrl2html.py --wrl simulation7.wrl --html simulation7.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each electron produces in turn an &lt;a href=&quot;https://en.wikipedia.org/wiki/Particle_shower#Electromagnetic_showers&quot;&gt;&lt;strong&gt;&lt;em&gt;electromagnetic shower&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;. These showers consist of a bunch of photons that end up &lt;em&gt;photoelectrically absorbed&lt;/em&gt;. Take &lt;a href=&quot;/simulation7.html&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;a look&lt;/a&gt; ↗️ for yourself (of course, after running the simulation)!&lt;/p&gt;

&lt;h2 id=&quot;energy-is-a-kind-of-matter&quot;&gt;Energy is &lt;em&gt;a kind of&lt;/em&gt; matter&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;https://j.gifs.com/vOJj9P.gif&quot; style=&quot;float:right;width:40%;padding:6px;&quot; /&gt;Yes, energy can be transformed into matter (and back!)… as discovered by Einstein in his famous &lt;a href=&quot;http://hermes.ffn.ub.es/luisnavarro/nuevo_maletin/Einstein_1905_relativity.pdf&quot;&gt;1905 paper&lt;/a&gt;: &lt;em&gt;On the Electrodynamics of Moving Bodies&lt;/em&gt;. BTW, Einstein was also responsible for the explanation of the photoelectric effect, as commented in the introduction above.&lt;/p&gt;

&lt;p&gt;(Not that Einstein!)&lt;/p&gt;

&lt;p&gt;Run the following simulation:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -v ${PWD}:/root pammacdotnet/g4lpwd --type=gamma --material=G4_WATER --count=10 --energy=15 --wrl simulation8.wrl
./wrl2html.py --wrl simulation8.wrl --html simulation8.html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You &lt;a href=&quot;/simulation8.html&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;will see&lt;/a&gt; ↗️ some events like the following, where a photon (with no mass and thus made of pure energy), after colliding with an atom, transfers all its momentum to the creation of two particles: an electron and anti-electron (positron). &lt;img src=&quot;https://tva1.sinaimg.cn/large/00831rSTgy1gcex54zkp3j31240bm3z0.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That is: energy (&lt;em&gt;E&lt;/em&gt;) is eventually converted into matter (&lt;em&gt;m&lt;/em&gt;), following the famous Einstein equation: &lt;em&gt;E=m·c·c&lt;/em&gt;, where &lt;em&gt;c&lt;/em&gt; is the speed of light. This process is known as &lt;strong&gt;&lt;em&gt;pair production&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;final-words&quot;&gt;Final words&lt;/h2&gt;

&lt;p&gt;Now it’s time for you to play with your own simulations. This includes other materials, energies, particles, etc. As stated above, you can also increase the number of particles, but we suggest you keep this number below 100. This lab is based on the &lt;a href=&quot;https://iopscience.iop.org/article/10.1088/1361-6404/ab5011&quot;&gt;article&lt;/a&gt; &lt;em&gt;X-ray imaging virtual online laboratory for engineering undergraduates&lt;/em&gt; (by &lt;a href=&quot;http://twitter.com/albertcorbi&quot;&gt;Alberto Corbi&lt;/a&gt;, &lt;a href=&quot;http://www.danielburgos.eu&quot;&gt;Daniel Burgos&lt;/a&gt;, &lt;a href=&quot;https://www.bangor.ac.uk/computer-science-and-electronic-engineering/staff/franck-vidal/en&quot;&gt;Frank Vidal&lt;/a&gt;, &lt;a href=&quot;https://webific.ific.uv.es/web/en/content/albiol-colomer-francisco-javier&quot;&gt;Francisco Albiol&lt;/a&gt; and &lt;a href=&quot;http://www.upv.es/ficha-personal/alalbiol&quot;&gt;Alberto Albiol&lt;/a&gt;) and it has been carried out with success in the first semesters of the &lt;a href=&quot;https://www.unir.net/ingenieria/grado-informatica/549200001509/&quot;&gt;Computing Science degree&lt;/a&gt; at the &lt;a href=&quot;https://www.unir.net/ingenieria/&quot;&gt;School of Engineering&lt;/a&gt; of Universidad Internacional de La Rioja (&lt;a href=&quot;https://www.unir.net&quot;&gt;UNIR&lt;/a&gt;).&lt;/p&gt;
</description>
        <pubDate>Sun, 23 Feb 2020 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/geant4lab/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/geant4lab/</guid>
        
        <category>beginner</category>
        
        <category>Geant4</category>
        
        <category>particle physics</category>
        
        <category>medical physics</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>.NET Conf 2019</title>
        <description>&lt;p&gt;Welcome to Docker’s &lt;a href=&quot;https://www.dotnetconf.net&quot;&gt;.NET Conf&lt;/a&gt; challenge!&lt;/p&gt;

&lt;p&gt;This lab gets you using .NET Core in Docker containers. You’ll experience compiling code,  packaging apps and running containers, using the latest .NET Core 3.0 release.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;: Beginner (assumes no familiarity with Docker)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt;: Approximately 5 minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Tasks&lt;/strong&gt;:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_1&quot;&gt;Task 1: Grab the code&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_2&quot;&gt;Task 2: Build the application image&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_3&quot;&gt;Task 3: Run a .NET Core container!&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;task-1-grab-the-code&quot;&gt;&lt;a name=&quot;Task_1&quot;&gt;&lt;/a&gt;Task 1: Grab the code&lt;/h2&gt;

&lt;p&gt;The Play-with-Docker environment is already set up with Docker and Git, so you’re good to go.&lt;/p&gt;

&lt;h3 id=&quot;clone-the-source-code-from-github&quot;&gt;Clone the source code from GitHub&lt;/h3&gt;

&lt;p&gt;Clone the application source into your local session:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Just click the text in these boxes to send the command to your terminal&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git clone https://github.com/dockersamples/dotnetconf19.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now browse to the source code folder &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnetconf19&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cd dotnetconf19
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In there you’ll see a folder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;src&lt;/code&gt; which contains the .NET code and a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; whch contains the instructions to build and package the app:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ls
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;task-2-build-the-application-image&quot;&gt;&lt;a name=&quot;Task_2&quot;&gt;&lt;/a&gt;Task 2: Build the application image&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; has &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnet&lt;/code&gt; commands to restore NuGet packages and publish the app:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;Even if you’re not familiar with the &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot;&gt;Dockerfile syntax&lt;/a&gt;, you can kind of work out that this is a script to compile the app and package it up to run&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You run the script with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image build&lt;/code&gt; command, which produces a container package called a &lt;em&gt;Docker image&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build --tag dotnetconf:19 .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You’ll see lots of download progress bars, and some familiar output from MSBuild.&lt;/p&gt;

&lt;p&gt;The final message &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Successfully tagged dotnetconf:19&lt;/code&gt; tells you the image has been built and given the tag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dotnetconf:19&lt;/code&gt; - which is just the image name.&lt;/p&gt;

&lt;h2 id=&quot;task-3-run-a-net-core-container&quot;&gt;&lt;a name=&quot;Task_3&quot;&gt;&lt;/a&gt;Task 3: Run a .NET Core container!&lt;/h2&gt;

&lt;p&gt;A Docker image is a complete packaged app. You can share it on &lt;a href=&quot;https://hub.docker.com&quot;&gt;Docker Hub&lt;/a&gt;, which is how thousands of open-source and commercial projects now distribute their software.&lt;/p&gt;

&lt;p&gt;Your image contains the .NET Core 3.0 runtime, together with the assemblies and configuration for the demo app.&lt;/p&gt;

&lt;p&gt;You run the app by running a container from the image:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run dotnetconf:19
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;Scroll up to read the message from .NET Bot - that’s your .NET Conf code!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want to learn more about what’s happened here and how you can use Docker to build cross-platform apps that run on Windows, Linux, Itenal and Arm - check out this blog post:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://blog.docker.com/2019/09/docker-arm-virtual-meetup-multi-arch-with-buildx/&quot;&gt;Docker + Arm Virtual Meetup Recap: Building Multi-arch Apps with Buildx&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;youre-done&quot;&gt;You’re done!&lt;/h2&gt;

&lt;p&gt;Enjoy .NET Conf :)&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://training.play-with-docker.com&quot;&gt;Play with Docker Training site&lt;/a&gt; is always on, and there are plenty more labs you can try at home.&lt;/p&gt;
</description>
        <pubDate>Wed, 04 Sep 2019 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/dotnet-conf/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/dotnet-conf/</guid>
        
        <category>beginner</category>
        
        <category>linux</category>
        
        <category>developer</category>
        
        <category>swarm</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker for Beginners - Linux</title>
        <description>&lt;p&gt;In this lab, we will look at some basic Docker commands and a simple build-ship-run workflow. We’ll start by running some simple containers, then we’ll use a Dockerfile to build a custom app. Finally, we’ll look at how to use bind mounts to modify a running container as you might if you were actively developing using Docker.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;: Beginner (assumes no familiarity with Docker)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt;: Approximately 30 minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Tasks&lt;/strong&gt;:&lt;/p&gt;

&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_0&quot;&gt;Task 0: Prerequisites&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_1&quot;&gt;Task 1: Run some simple Docker containers&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_2&quot;&gt;Task 2: Package and run a custom app using Docker&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_3&quot;&gt;Task 3: Modify a Running Website&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;task-0-prerequisites&quot;&gt;&lt;a name=&quot;task0&quot;&gt;&lt;/a&gt;Task 0: Prerequisites&lt;/h2&gt;

&lt;p&gt;You will need all of the following to complete this lab:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A clone of the lab’s GitHub repo.&lt;/li&gt;
  &lt;li&gt;A DockerID.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;clone-the-labs-github-repo&quot;&gt;Clone the Lab’s GitHub Repo&lt;/h3&gt;

&lt;p&gt;Use the following command to clone the lab’s repo from GitHub (you can click the command or manually type it). This will make a copy of the lab’s repo in a new sub-directory called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux_tweet_app&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;    git clone https://github.com/dockersamples/linux_tweet_app
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;make-sure-you-have-a-dockerid&quot;&gt;Make sure you have a DockerID&lt;/h3&gt;

&lt;p&gt;If you do not have a DockerID (a free login used to access Docker Hub), please visit &lt;a href=&quot;https://hub.docker.com&quot;&gt;Docker Hub&lt;/a&gt; and register for one. You will need this for later steps.&lt;/p&gt;

&lt;h2 id=&quot;task-1-run-some-simple-docker-containers&quot;&gt;&lt;a name=&quot;Task_1&quot;&gt;&lt;/a&gt;Task 1: Run some simple Docker containers&lt;/h2&gt;

&lt;p&gt;There are different ways to use containers. These include:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;To run a single task:&lt;/strong&gt; This could be a shell script or a custom app.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Interactively:&lt;/strong&gt; This connects you to the container similar to the way you SSH into a remote server.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;In the background:&lt;/strong&gt; For long-running services like websites and databases.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this section you’ll try each of those options and see how Docker manages the workload.&lt;/p&gt;

&lt;h3 id=&quot;run-a-single-task-in-an-alpine-linux-container&quot;&gt;Run a single task in an Alpine Linux container&lt;/h3&gt;

&lt;p&gt;In this step we’re going to start a new container and tell it to run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostname&lt;/code&gt; command. The container will start, execute the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostname&lt;/code&gt; command, then exit.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Run the following command in your Linux console.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container run alpine hostname
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;The output below shows that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alpine:latest&lt;/code&gt; image could not be found locally. When this happens, Docker automatically &lt;em&gt;pulls&lt;/em&gt; it from Docker Hub.&lt;/p&gt;

    &lt;p&gt;After the image is pulled, the container’s hostname is displayed (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;888e89a3b36b&lt;/code&gt; in the example below).&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; Unable to find image &apos;alpine:latest&apos; locally
 latest: Pulling from library/alpine
 88286f41530e: Pull complete
 Digest: sha256:f006ecbb824d87947d0b51ab8488634bf69fe4094959d935c0c103f4820a417d
 Status: Downloaded newer image for alpine:latest
 888e89a3b36b
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Docker keeps a container running as long as the process it started inside the container is still running. In this case the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hostname&lt;/code&gt; process exits as soon as the output is written. This means the container stops. However, Docker doesn’t delete resources by default, so the container still exists in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Exited&lt;/code&gt; state.&lt;/p&gt;

    &lt;p&gt;List all containers.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container ls --all
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;Notice that your Alpine Linux container is in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Exited&lt;/code&gt; state.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS            PORTS               NAMES
 888e89a3b36b        alpine              &quot;hostname&quot;          50 seconds ago      Exited (0) 49 seconds ago                       awesome_elion
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;blockquote&gt;
      &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The container ID &lt;em&gt;is&lt;/em&gt; the hostname that the container displayed. In the example above it’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;888e89a3b36b&lt;/code&gt;.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Containers which do one task and then exit can be very useful. You could build a Docker image that executes a script to configure something. Anyone can execute that task just by running the container - they don’t need the actual scripts or configuration information.&lt;/p&gt;

&lt;h3 id=&quot;run-an-interactive-ubuntu-container&quot;&gt;Run an interactive Ubuntu container&lt;/h3&gt;

&lt;p&gt;You can run a container based on a different version of Linux than is running on your Docker host.&lt;/p&gt;

&lt;p&gt;In the next example, we are going to run an Ubuntu Linux container on top of an Alpine Linux Docker host (Play With Docker uses Alpine Linux for its nodes).&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Run a Docker container and access its shell.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container run --interactive --tty --rm ubuntu bash
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;In this example, we’re giving Docker three parameters:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--interactive&lt;/code&gt; says you want an interactive session.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--tty&lt;/code&gt; allocates a pseudo-tty.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--rm&lt;/code&gt; tells Docker to go ahead and remove the container when it’s done executing.&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;The first two parameters allow you to interact with the Docker container.&lt;/p&gt;

    &lt;p&gt;We’re also telling the container to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; as its main process (PID 1).&lt;/p&gt;

    &lt;p&gt;When the container starts you’ll drop into the bash shell with the default prompt &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root@&amp;lt;container id&amp;gt;:/#&lt;/code&gt;. Docker has attached to the shell in the container, relaying input and output between your local session and the shell session in the container.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Run the following commands in the container.&lt;/p&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls /&lt;/code&gt; will list the contents of the root directory in the container, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ps aux&lt;/code&gt; will show running processes in the container, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat /etc/issue&lt;/code&gt; will show which Linux distro the container is running, in this case Ubuntu 20.04.3 LTS.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ls /
&lt;/code&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ps aux
&lt;/code&gt;&lt;/pre&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat /etc/issue
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt; to leave the shell session. This will terminate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash&lt;/code&gt; process, causing the container to exit.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; exit
&lt;/code&gt;&lt;/pre&gt;

    &lt;blockquote&gt;
      &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; As we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--rm&lt;/code&gt; flag when we started the container, Docker removed the container when it stopped. This means if you run another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls --all&lt;/code&gt; you won’t see the Ubuntu container.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;For fun, let’s check the version of our host VM.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; cat /etc/issue
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;You should see:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; Welcome to Alpine Linux 3.8
 Kernel \r on an \m (\l)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that our host VM is running Alpine Linux, yet we were able to run an Ubuntu container. As previously mentioned, the distribution of Linux inside the container does not need to match the distribution of Linux running on the Docker host.&lt;/p&gt;

&lt;p&gt;However, Linux containers require the Docker host to be running a Linux kernel. For example, Linux containers cannot run directly on Windows Docker hosts. The same is true of Windows containers - they need to run on a Docker host with a Windows kernel.&lt;/p&gt;

&lt;p&gt;Interactive containers are useful when you are putting together your own image. You can run a container and verify all the steps you need to deploy your app, and capture them in a Dockerfile.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You &lt;em&gt;can&lt;/em&gt; &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/commit/&quot;&gt;commit&lt;/a&gt; a container to make an image from it - but you should avoid that wherever possible. It’s much better to use a repeatable &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot;&gt;Dockerfile&lt;/a&gt; to build your image. You’ll see that shortly.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;run-a-background-mysql-container&quot;&gt;Run a background MySQL container&lt;/h3&gt;

&lt;p&gt;Background containers are how you’ll run most applications. Here’s a simple example using MySQL.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Run a new MySQL container with the following command.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container run \
 --detach \
 --name mydb \
 -e MYSQL_ROOT_PASSWORD=my-secret-pw \
 mysql:latest
&lt;/code&gt;&lt;/pre&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--detach&lt;/code&gt; will run the container in the background.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--name&lt;/code&gt; will name it &lt;strong&gt;mydb&lt;/strong&gt;.&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e&lt;/code&gt; will use an environment variable to specify the root password (NOTE: This should never be done in production).&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;As the MySQL image was not available locally, Docker automatically pulled it from Docker Hub.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; Unable to find image &apos;mysql:latest&apos; locallylatest: Pulling from library/mysql
 aa18ad1a0d33: Pull complete
 fdb8d83dece3: Pull complete
 75b6ce7b50d3: Pull complete
 ed1d0a3a64e4: Pull complete
 8eb36a82c85b: Pull complete
 41be6f1a1c40: Pull complete
 0e1b414eac71: Pull complete
 914c28654a91: Pull complete
 587693eb988c: Pull complete
 b183c3585729: Pull complete
 315e21657aa4: Pull complete
 Digest: sha256:0dc3dacb751ef46a6647234abdec2d47400f0dfbe77ab490b02bffdae57846ed
 Status: Downloaded newer image for mysql:latest
 41d6157c9f7d1529a6c922acb8167ca66f167119df0fe3d86964db6c0d7ba4e0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;As long as the MySQL process is running, Docker will keep the container running in the background.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;List the running containers.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container ls
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;Notice your container is running.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS            NAMES
 3f4e8da0caf7        mysql:latest        &quot;docker-entrypoint...&quot;   52 seconds ago      Up 51 seconds       3306/tcp            mydb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You can check what’s happening in your containers by using a couple of built-in Docker commands: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container logs&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container top&lt;/code&gt;.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container logs mydb
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;This shows the logs from the MySQL Docker container.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   &amp;lt;output truncated&amp;gt;
   2017-09-29T16:02:58.605004Z 0 [Note] Executing &apos;SELECT * FROM INFORMATION_SCHEMA.TABLES;&apos; to get a list of tables using the deprecated partition engine. You may use the startup option &apos;--disable-partition-engine-check&apos; to skip this check.
   2017-09-29T16:02:58.605026Z 0 [Note] Beginning of list of non-natively partitioned tables
   2017-09-29T16:02:58.616575Z 0 [Note] End of list of non-natively partitioned tables
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Let’s look at the processes running inside the container.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;   docker container top mydb
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;You should see the MySQL daemon (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysqld&lt;/code&gt;) is running in the container.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; PID                 USER                TIME                COMMAND
 2876                999                 0:00                mysqld
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Although MySQL is running, it is isolated within the container because no network ports have been published to the host. Network traffic cannot reach containers from the host unless ports are explicitly published.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;List the MySQL version using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container exec&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container exec&lt;/code&gt; allows you to run a command inside a container. In this example, we’ll use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container exec&lt;/code&gt; to run the command-line equivalent of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mysql --user=root --password=$MYSQL_ROOT_PASSWORD --version&lt;/code&gt; inside our MySQL container.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker exec -it mydb \
 mysql --user=root --password=$MYSQL_ROOT_PASSWORD --version
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;You will see the MySQL version number, as well as a handy warning.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; mysql: [Warning] Using a password on the command line interface can be insecure.
 mysql  Ver 14.14 Distrib 5.7.19, for Linux (x86_64) using  EditLine wrapper
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You can also use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container exec&lt;/code&gt; to connect to a new shell process inside an already-running container. Executing the command below will give you an interactive shell (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt;) inside your MySQL container.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker exec -it mydb sh
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;Notice that your shell prompt has changed. This is because your shell is now connected to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh&lt;/code&gt; process running inside of your container.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Let’s check the version number by running the same command again, only this time from within the new shell session in the container.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; mysql --user=root --password=$MYSQL_ROOT_PASSWORD --version
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;Notice the output is the same as before.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt; to leave the interactive shell session.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; exit
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;task-2-package-and-run-a-custom-app-using-docker&quot;&gt;&lt;a name=&quot;Task_2&quot;&gt;&lt;/a&gt;Task 2: Package and run a custom app using Docker&lt;/h2&gt;

&lt;p&gt;In this step you’ll learn how to package your own apps as Docker images using a &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot;&gt;Dockerfile&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Dockerfile syntax is straightforward. In this task, we’re going to create a simple NGINX website from a Dockerfile.&lt;/p&gt;

&lt;h3 id=&quot;build-a-simple-website-image&quot;&gt;Build a simple website image&lt;/h3&gt;

&lt;p&gt;Let’s have a look at the  Dockerfile we’ll be using, which builds a simple website that allows you to send a tweet.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Make sure you’re in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux_tweet_app&lt;/code&gt; directory.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; cd ~/linux_tweet_app
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Display the contents of the Dockerfile.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; cat Dockerfile
&lt;/code&gt;&lt;/pre&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; FROM nginx:latest

 COPY index.html /usr/share/nginx/html
 COPY linux.png /usr/share/nginx/html

 EXPOSE 80 443     

 CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Let’s see what each of these lines in the Dockerfile do.&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/reference/builder/#from&quot;&gt;FROM&lt;/a&gt; specifies the base image to use as the starting point for this new image you’re creating. For this example we’re starting from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nginx:latest&lt;/code&gt;.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/reference/builder/#copy&quot;&gt;COPY&lt;/a&gt; copies files from the Docker host into the image, at a known location. In this example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; is used to copy two files into the image: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt;. and a graphic that will be used on our webpage.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/reference/builder/#expose&quot;&gt;EXPOSE&lt;/a&gt; documents which ports the application uses.&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/reference/builder/#cmd&quot;&gt;CMD&lt;/a&gt; specifies what command to run when a container is started from the image. Notice that we can specify the command, as well as run-time arguments.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In order to make the following commands more copy/paste friendly, export an environment variable containing your DockerID (if you don’t have a DockerID you can get one for free via &lt;a href=&quot;https://hub.docker.com&quot;&gt;Docker Hub&lt;/a&gt;).&lt;/p&gt;

    &lt;p&gt;You will have to manually type this command as it requires your unique DockerID.&lt;/p&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export DOCKERID=&amp;lt;your docker id&amp;gt;&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Echo the value of the variable back to the terminal to ensure it was stored correctly.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; echo $DOCKERID
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image build&lt;/code&gt; command to create a new Docker image using the instructions in the Dockerfile.&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--tag&lt;/code&gt; allows us to give the image a custom name. In this case it’s comprised of our DockerID, the application name, and a version. Having the Docker ID attached to the name will allow us to store it on Docker Hub in a later step&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt; tells Docker to use the current directory as the build context&lt;/li&gt;
    &lt;/ul&gt;

    &lt;p&gt;Be sure to include period (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt;) at the end of the command.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker image build --tag $DOCKERID/linux_tweet_app:1.0 .
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;The output below shows the Docker daemon executing each line in the Dockerfile&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; Sending build context to Docker daemon  32.77kB
 Step 1/5 : FROM nginx:latest
 latest: Pulling from library/nginx
 afeb2bfd31c0: Pull complete
 7ff5d10493db: Pull complete
 d2562f1ae1d0: Pull complete
 Digest: sha256:af32e714a9cc3157157374e68c818b05ebe9e0737aac06b55a09da374209a8f9
 Status: Downloaded newer image for nginx:latest
 ---&amp;gt; da5939581ac8
 Step 2/5 : COPY index.html /usr/share/nginx/html
 ---&amp;gt; eba2eec2bea9
 Step 3/5 : COPY linux.png /usr/share/nginx/html
 ---&amp;gt; 4d080f499b53
 Step 4/5 : EXPOSE 80 443
 ---&amp;gt; Running in 47232cb5699f
 ---&amp;gt; 74c968a9165f
 Removing intermediate container 47232cb5699f
 Step 5/5 : CMD nginx -g daemon off;
 ---&amp;gt; Running in 4623761274ac
 ---&amp;gt; 12045a0df899
 Removing intermediate container 4623761274ac
 Successfully built 12045a0df899
 Successfully tagged &amp;lt;your docker ID&amp;gt;/linux_tweet_app:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run&lt;/code&gt; command to start a new container from the image you created.&lt;/p&gt;

    &lt;p&gt;As this container will be running an NGINX web server, we’ll use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--publish&lt;/code&gt; flag to publish port 80 inside the container onto port 80 on the host. This will allow traffic coming in to the Docker host on port 80 to be directed to port 80 in the container. The format of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--publish&lt;/code&gt; flag is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;host_port&lt;/code&gt;:&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;container_port&lt;/code&gt;.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container run \
 --detach \
 --publish 80:80 \
 --name linux_tweet_app \
 $DOCKERID/linux_tweet_app:1.0
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;Any external traffic coming into the server on port 80 will now be directed into the container on port 80.&lt;/p&gt;

    &lt;p&gt;In a later step you will see how to map traffic from two different ports - this is necessary when two containers use the same port to communicate since you can only expose the port once on the host.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;Click here to load the website&lt;/a&gt; which should be running.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Once you’ve accessed your website, shut it down and remove it.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container rm --force linux_tweet_app
&lt;/code&gt;&lt;/pre&gt;

    &lt;blockquote&gt;
      &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--force&lt;/code&gt; parameter to remove the running container without shutting it down. This will ungracefully shutdown the container and permanently remove it from the Docker host.&lt;/p&gt;

      &lt;p&gt;In a production environment you may want to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container stop&lt;/code&gt; to gracefully stop the container and leave it on the host. You can then use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container rm&lt;/code&gt; to permanently remove it.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;task-3-modify-a-running-website&quot;&gt;&lt;a name=&quot;Task_3&quot;&gt;&lt;/a&gt;Task 3: Modify a running website&lt;/h2&gt;

&lt;p&gt;When you’re actively working on an application it is inconvenient to have to stop the container, rebuild the image, and run a new version every time you make a change to your source code.&lt;/p&gt;

&lt;p&gt;One way to streamline this process is to mount the source code directory on the local machine into the running container. This will allow any changes made to the files on the host to be immediately reflected in the container.&lt;/p&gt;

&lt;p&gt;We do this using something called a &lt;a href=&quot;https://docs.docker.com/engine/admin/volumes/bind-mounts/&quot;&gt;bind mount&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When you use a bind mount, a file or directory on the host machine is mounted into a container running on the same host.&lt;/p&gt;

&lt;h3 id=&quot;start-our-web-app-with-a-bind-mount&quot;&gt;Start our web app with a bind mount&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Let’s start the web app and mount the current directory into the container.&lt;/p&gt;

    &lt;p&gt;In this example we’ll use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--mount&lt;/code&gt; flag to mount the current directory on the host into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/share/nginx/html&lt;/code&gt; inside the container.&lt;/p&gt;

    &lt;p&gt;Be sure to run this command from within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux_tweet_app&lt;/code&gt; directory on your Docker host.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container run \
 --detach \
 --publish 80:80 \
 --name linux_tweet_app \
 --mount type=bind,source=&quot;$(pwd)&quot;,target=/usr/share/nginx/html \
 $DOCKERID/linux_tweet_app:1.0
&lt;/code&gt;&lt;/pre&gt;

    &lt;blockquote&gt;
      &lt;p&gt;Remember from the Dockerfile, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/share/nginx/html&lt;/code&gt; is where the html files are stored for the web app.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;The &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;website&lt;/a&gt; should be running.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;modify-the-running-website&quot;&gt;Modify the running website&lt;/h3&gt;

&lt;p&gt;Bind mounts mean that any changes made to the local file system are immediately reflected in the running container.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Copy a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; into the container.&lt;/p&gt;

    &lt;p&gt;The Git repo that you pulled earlier contains several different versions of an index.html file. You can manually run an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; command from within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/linux_tweet_app&lt;/code&gt; directory to see a list of them. In this step we’ll replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index-new.html&lt;/code&gt;.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; cp index-new.html index.html
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Go to the running &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;website&lt;/a&gt; and &lt;strong&gt;refresh the page&lt;/strong&gt;. Notice that the site has changed.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;If you are comfortable with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vi&lt;/code&gt; you can use it to load the local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; file and make additional changes. Those too would be reflected when you reload the webpage.
If you are really adventurous, why not try using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; to access the running container and modify the files stored there.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Even though we’ve modified the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; local filesystem and seen it reflected in the running container, we’ve not actually changed the Docker image that the container was started from.&lt;/p&gt;

&lt;p&gt;To show this, stop the current container and re-run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0&lt;/code&gt; image without a bind mount.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Stop and remove the currently running container.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker rm --force linux_tweet_app
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Rerun the current version without a bind mount.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container run \
 --detach \
 --publish 80:80 \
 --name linux_tweet_app \
 $DOCKERID/linux_tweet_app:1.0
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Notice  the &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;website&lt;/a&gt; is back to the original version.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Stop and remove the current container&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker rm --force linux_tweet_app
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;update-the-image&quot;&gt;Update the image&lt;/h3&gt;

&lt;p&gt;To persist the changes you made to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; file into the image, you need to build a new version of the image.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Build a new image and tag it as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.0&lt;/code&gt;&lt;/p&gt;

    &lt;p&gt;Remember that you previously modified the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; file on the Docker hosts local filesystem. This means that running another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image build&lt;/code&gt; command will build a new image with the updated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt;&lt;/p&gt;

    &lt;p&gt;Be sure to include the period (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.&lt;/code&gt;) at the end of the command.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker image build --tag $DOCKERID/linux_tweet_app:2.0 .
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;Notice how fast that built! This is because Docker only modified the portion of the image that changed vs. rebuilding the whole image.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Let’s look at the images on the system.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker image ls
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;You now have both versions of the web app on your host.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZE
 &amp;lt;docker id&amp;gt;/linux_tweet_app    2.0                 01612e05312b        16 seconds ago      108MB
 &amp;lt;docker id&amp;gt;/linux_tweet_app    1.0                 bb32b5783cd3        4 minutes ago       108MB
 mysql                          latest              b4e78b89bcf3        2 weeks ago         412MB
 ubuntu                         latest              2d696327ab2e        2 weeks ago         122MB
 nginx                          latest              da5939581ac8        3 weeks ago         108MB
 alpine                         latest              76da55c8019d        3 weeks ago         3.97MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;test-the-new-version&quot;&gt;Test the new version&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Run a new container from the new version of the image.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container run \
 --detach \
 --publish 80:80 \
 --name linux_tweet_app \
 $DOCKERID/linux_tweet_app:2.0
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Check the new version of the &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;website&lt;/a&gt; (&lt;strong&gt;You may need to refresh your browser to get the new version to load&lt;/strong&gt;).&lt;/p&gt;

    &lt;p&gt;The web page will have an orange background.&lt;/p&gt;

    &lt;p&gt;We can run both versions side by side. The only thing we need to be aware of is that we cannot have two containers using port 80 on the same host.&lt;/p&gt;

    &lt;p&gt;As we’re already using port 80 for the container running from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.0&lt;/code&gt; version of the image, we will start a new container and publish it on port 8080. Additionally, we need to give our container a unique name (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;old_linux_tweet_app&lt;/code&gt;)&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Run another new container, this time from the old version of the image.&lt;/p&gt;

    &lt;p&gt;Notice that this command maps the new container to port 8080 on the host. This is because two containers cannot map to the same port on a single Docker host.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container run \
 --detach \
 --publish 8080:80 \
 --name old_linux_tweet_app \
 $DOCKERID/linux_tweet_app:1.0
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;View the old version of the &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;website&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;push-your-images-to-docker-hub&quot;&gt;Push your images to Docker Hub&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;List the images on your Docker host.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker image ls -f reference=&quot;$DOCKERID/*&quot;
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;You will see that you now have two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linux_tweet_app&lt;/code&gt; images - one tagged as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0&lt;/code&gt; and the other as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.0&lt;/code&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; REPOSITORY                     TAG                 IMAGE ID            CREATED             SIZE
 &amp;lt;docker id&amp;gt;/linux_tweet_app    2.0                 01612e05312b        3 minutes ago       108MB
 &amp;lt;docker id&amp;gt;/linux_tweet_app    1.0                 bb32b5783cd3        7 minutes ago       108MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;These images are only stored in your Docker hosts local repository. Your Docker host will be deleted after the workshop. In this step we’ll push the images to a public repository so you can run them from any Linux machine with Docker.&lt;/p&gt;

    &lt;p&gt;Distribution is built into the Docker platform. You can build images locally and push them to a public or private &lt;a href=&quot;https://docs.docker.com/registry/&quot;&gt;registry&lt;/a&gt;, making them available to other users. Anyone with access can pull that image and run a container from it. The behavior of the app in the container will be the same for everyone, because the image contains the fully-configured app - the only requirements to run it are Linux and Docker.&lt;/p&gt;

    &lt;p&gt;&lt;a href=&quot;https://hub.docker.com&quot;&gt;Docker Hub&lt;/a&gt; is the default public registry for Docker images.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Before you can push your images, you will need to log into Docker Hub.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker login
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;You will need to supply your Docker ID credentials when prompted.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; Username: &amp;lt;your docker id&amp;gt;
 Password: &amp;lt;your docker id password&amp;gt;
 Login Succeeded
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Push version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1.0&lt;/code&gt; of your web app using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image push&lt;/code&gt;.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker image push $DOCKERID/linux_tweet_app:1.0
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;You’ll see the progress as the image is pushed up to Docker Hub.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; The push refers to a repository [docker.io/&amp;lt;your docker id&amp;gt;/linux_tweet_app]
 910e84bcef7a: Pushed
 1dee161c8ba4: Pushed
 110566462efa: Pushed
 305e2b6ef454: Pushed
 24e065a5f328: Pushed
 1.0: digest: sha256:51e937ec18c7757879722f15fa1044cbfbf2f6b7eaeeb578c7c352baba9aa6dc size: 1363
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Now push version &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.0&lt;/code&gt;.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker image push $DOCKERID/linux_tweet_app:2.0
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;Notice that several lines of the output say &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Layer already exists&lt;/code&gt;. This is because Docker will leverage read-only layers that are the same as any previously uploaded image layers.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; The push refers to a repository [docker.io/&amp;lt;your docker id&amp;gt;/linux_tweet_app]
 0b171f8fbe22: Pushed
 70d38c767c00: Pushed
 110566462efa: Layer already exists
 305e2b6ef454: Layer already exists
 24e065a5f328: Layer already exists
 2.0: digest: sha256:7c51f77f90b81e5a598a13f129c95543172bae8f5850537225eae0c78e4f3add size: 1363
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can browse to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://hub.docker.com/r/&amp;lt;your docker id&amp;gt;/&lt;/code&gt; and see your newly-pushed Docker images. These are public repositories, so anyone can pull the image - you don’t even need a Docker ID to pull public images. Docker Hub also supports private repositories.&lt;/p&gt;

&lt;h3 id=&quot;next-step&quot;&gt;Next Step&lt;/h3&gt;

&lt;p&gt;Check out the introduction to a multi-service application stack orchestration in the &lt;a href=&quot;/microservice-orchestration&quot;&gt;Application Containerization and Microservice Orchestration&lt;/a&gt; tutorial.&lt;/p&gt;

&lt;hr /&gt;
</description>
        <pubDate>Thu, 01 Aug 2019 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/beginner-linux/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/beginner-linux/</guid>
        
        <category>beginner</category>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Application Containerization and Microservice Orchestration</title>
        <description>&lt;p&gt;In this tutorial we will learn about basic application containerization using Docker and running various components of an application as microservices.
We will utilize &lt;a href=&quot;https://docs.docker.com/compose/&quot;&gt;Docker Compose&lt;/a&gt; for orchestration during the development.
This tutorial is targeted for beginners who have the basic familiarity with Docker.
If you are new to Docker, we recommend you check out &lt;a href=&quot;/beginner-linux&quot;&gt;Docker for Beginners&lt;/a&gt; tutorial first.&lt;/p&gt;

&lt;p&gt;We will start from a basic Python script that scrapes links from a given web page and gradually evolve it into a multi-service application stack.
The demo code is available in the &lt;a href=&quot;https://github.com/ibnesayeed/linkextractor&quot;&gt;Link Extractor&lt;/a&gt; repo.
The code is organized in steps that incrementally introduce changes and new concepts.
After completion, the application stack will contain the following microservices:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A web application written in PHP and served using Apache that takes a URL as the input and summarizes extracted links from it&lt;/li&gt;
  &lt;li&gt;The web application talks to an API server written in Python (and Ruby) that takes care of the link extraction and returns a JSON response&lt;/li&gt;
  &lt;li&gt;A Redis cache that is used by the API server to avoid repeated fetch and link extraction for pages that are already scraped&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The API server will only load the page of the input link from the web if it is not in the cache.
The stack will eventually look like the figure below:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/linkextractor-microservice-diagram.png&quot; alt=&quot;A Microservice Architecture of the Link Extractor Application&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This tutorial was initially developed for a colloquium in the &lt;a href=&quot;https://odu.edu/compsci&quot;&gt;Computer Science Department&lt;/a&gt; of the &lt;a href=&quot;https://www.odu.edu/&quot;&gt;Old Dominion University&lt;/a&gt;, Norfolk, Virginia. A &lt;a href=&quot;https://www.youtube.com/watch?v=Y_X0F2FgYm8&quot;&gt;video recording&lt;/a&gt;, &lt;a href=&quot;https://www.slideshare.net/ibnesayeed/introducing-docker-application-containerization-service-orchestration&quot;&gt;presentation slides&lt;/a&gt;, and a brief description of the talk can be found in &lt;a href=&quot;https://ws-dl.blogspot.com/2017/12/2017-12-03-introducing-docker.html&quot;&gt;a blog post&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#stage-setup&quot; id=&quot;markdown-toc-stage-setup&quot;&gt;Stage Setup&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#step-0-basic-link-extractor-script&quot; id=&quot;markdown-toc-step-0-basic-link-extractor-script&quot;&gt;Step 0: Basic Link Extractor Script&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#step-1-containerized-link-extractor-script&quot; id=&quot;markdown-toc-step-1-containerized-link-extractor-script&quot;&gt;Step 1: Containerized Link Extractor Script&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#step-2-link-extractor-module-with-full-uri-and-anchor-text&quot; id=&quot;markdown-toc-step-2-link-extractor-module-with-full-uri-and-anchor-text&quot;&gt;Step 2: Link Extractor Module with Full URI and Anchor Text&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#step-3-link-extractor-api-service&quot; id=&quot;markdown-toc-step-3-link-extractor-api-service&quot;&gt;Step 3: Link Extractor API Service&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#step-4-link-extractor-api-and-web-front-end-services&quot; id=&quot;markdown-toc-step-4-link-extractor-api-and-web-front-end-services&quot;&gt;Step 4: Link Extractor API and Web Front End Services&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#step-5-redis-service-for-caching&quot; id=&quot;markdown-toc-step-5-redis-service-for-caching&quot;&gt;Step 5: Redis Service for Caching&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#step-6-swap-python-api-service-with-ruby&quot; id=&quot;markdown-toc-step-6-swap-python-api-service-with-ruby&quot;&gt;Step 6: Swap Python API Service with Ruby&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#conclusions&quot; id=&quot;markdown-toc-conclusions&quot;&gt;Conclusions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;stage-setup&quot;&gt;Stage Setup&lt;/h2&gt;

&lt;p&gt;Let’s get started by first cloning the demo code repository, changing the working directory, and checking the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;demo&lt;/code&gt; branch out.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git clone https://github.com/ibnesayeed/linkextractor.git
cd linkextractor
git checkout demo
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;step-0-basic-link-extractor-script&quot;&gt;Step 0: Basic Link Extractor Script&lt;/h2&gt;

&lt;p&gt;Checkout the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step0&lt;/code&gt; branch and list files in it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout step0
tree
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── README.md
└── linkextractor.py

0 directories, 2 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor.py&lt;/code&gt; file is the interesting one here, so let’s look at its contents:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat linkextractor.py
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env python
&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bs4&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BeautifulSoup&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;soup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BeautifulSoup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;html.parser&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;soup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a simple Python script that imports three packages: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sys&lt;/code&gt; from the standard library and two popular third-party packages &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bs4&lt;/code&gt;.
User-supplied command line argument (which is expected to be a URL to an HTML page) is used to fetch the page using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt; package, then parsed using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BeautifulSoup&lt;/code&gt;.
The parsed object is then iterated over to find all the anchor elements (i.e., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags) and print the value of their &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;href&lt;/code&gt; attribute that contains the hyperlink.&lt;/p&gt;

&lt;p&gt;However, this seemingly simple script might not be the easiest one to run on a machine that does not meet its requirements.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;README.md&lt;/code&gt; file suggests how to run it, so let’s give it a try:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;./linkextractor.py http://example.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bash: ./linkextractor.py: Permission denied
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When we tried to execute it as a script, we got the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Permission denied&lt;/code&gt; error.
Let’s check the current permissions on this file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ls -l linkextractor.py
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-rw-r--r--    1 root     root           220 Sep 23 16:26 linkextractor.py
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This current permission &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-rw-r--r--&lt;/code&gt; indicates that the script is not set to be executable.
We can either change it by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod a+x linkextractor.py&lt;/code&gt; or run it as a Python program instead of a self-executing script as illustrated below:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;python3 linkextractor.py
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;Traceback&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;most&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;recent&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;call&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;last&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;File&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;linkextractor.py&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bs4&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BeautifulSoup&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;ImportError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;No&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;named&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bs4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here we got the first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ImportError&lt;/code&gt; message because we are missing the third-party package needed by the script.
We can install that Python package (and potentially other missing packages) using one of the many techniques to make it work, but it is too much work for such a simple script, which might not be obvious for those who are not familiar with Python’s ecosystem.&lt;/p&gt;

&lt;p&gt;Depending on which machine and operating system you are trying to run this script on, what software is already installed, and how much access you have, you might face some of these potential difficulties:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Is the script executable?&lt;/li&gt;
  &lt;li&gt;Is Python installed on the machine?&lt;/li&gt;
  &lt;li&gt;Can you install software on the machine?&lt;/li&gt;
  &lt;li&gt;Is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; installed?&lt;/li&gt;
  &lt;li&gt;Are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requests&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;beautifulsoup4&lt;/code&gt; Python libraries installed?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where application containerization tools like Docker come in handy.
In the next step we will try to containerize this script and make it easier to execute.&lt;/p&gt;

&lt;h2 id=&quot;step-1-containerized-link-extractor-script&quot;&gt;Step 1: Containerized Link Extractor Script&lt;/h2&gt;

&lt;p&gt;Checkout the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step1&lt;/code&gt; branch and list files in it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout step1
tree
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── Dockerfile
├── README.md
└── linkextractor.py

0 directories, 3 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have added one new file (i.e., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;) in this step.
Let’s look into its contents:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       python:3&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;LABEL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;      maintainer=&quot;Sawood Alam &amp;lt;@ibnesayeed&amp;gt;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;RUN        &lt;/span&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;beautifulsoup4
&lt;span class=&quot;k&quot;&gt;RUN        &lt;/span&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;requests

&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       linkextractor.py /app/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;a+x linkextractor.py

&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;./linkextractor.py&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; we can prepare a Docker image for this script.
We start from the official &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt; Docker image that contains Python’s run-time environment as well as necessary tools to install Python packages and dependencies.
We then add some metadata as labels (this step is not essential, but is a good practice nonetheless).
Next two instructions run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install&lt;/code&gt; command to install the two third-party packages needed for the script to function properly.
We then create a working directory &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/app&lt;/code&gt;, copy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor.py&lt;/code&gt; file in it, and change its permissions to make it an executable script.
Finally, we set the script as the entrypoint for the image.&lt;/p&gt;

&lt;p&gt;So far, we have just described how we want our Docker image to be like, but didn’t really build one.
So let’s do just that:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -t linkextractor:step1 .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This command should yield an output as illustrated below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Sending build context to Docker daemon  171.5kB
Step 1/8 : FROM       python:3

... [OUTPUT REDACTED] ...

Successfully built 226196ada9ab
Successfully tagged linkextractor:step1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have created a Docker image named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor:step1&lt;/code&gt; based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; illustrated above.
If the build was successful, we should be able to see it in the list of image:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image ls
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
linkextractor       step1               e067c677be37        2 seconds ago       931MB
python              3                   a9d071760c82        2 weeks ago         923MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This image should have all the necessary ingredients packaged in it to run the script anywhere on a machine that supports Docker.
Now, let’s run a one-off container with this image and extract links from some live web pages:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -it --rm linkextractor:step1 http://example.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This outputs a single link that is present in the simple &lt;a href=&quot;http://example.com/&quot;&gt;example.com&lt;/a&gt; web page:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http://www.iana.org/domains/example
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s try it on a web page with more links in it:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -it --rm linkextractor:step1 https://training.play-with-docker.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/
/about/
#ops
#dev
/ops-stage1
/ops-stage2
/ops-stage3
/dev-stage1
/dev-stage2
/dev-stage3
/alacart
https://twitter.com/intent/tweet?text=Play with Docker Classroom&amp;amp;url=https://training.play-with-docker.com/&amp;amp;via=docker&amp;amp;related=docker
https://facebook.com/sharer.php?u=https://training.play-with-docker.com/
https://plus.google.com/share?url=https://training.play-with-docker.com/
http://www.linkedin.com/shareArticle?mini=true&amp;amp;url=https://training.play-with-docker.com/&amp;amp;title=Play%20with%20Docker%20Classroom&amp;amp;source=https://training.play-with-docker.com
https://2018.dockercon.com/
https://2018.dockercon.com/
https://success.docker.com/training/
https://community.docker.com/registrations/groups/4316
https://docker.com
https://www.docker.com
https://www.facebook.com/docker.run
https://twitter.com/docker
https://www.github.com/play-with-docker/play-with-docker.github.io
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This looks good, but we can improve the output.
For example, some links are relative, we can convert them into full URLs and also provide the anchor text they are linked to.
In the next step we will make these changes and some other improvements to the script.&lt;/p&gt;

&lt;h2 id=&quot;step-2-link-extractor-module-with-full-uri-and-anchor-text&quot;&gt;Step 2: Link Extractor Module with Full URI and Anchor Text&lt;/h2&gt;

&lt;p&gt;Checkout the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step2&lt;/code&gt; branch and list files in it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout step2
tree
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── Dockerfile
├── README.md
└── linkextractor.py

0 directories, 3 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this step the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor.py&lt;/code&gt; script is updated with the following functional changes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Paths are normalized to full URLs&lt;/li&gt;
  &lt;li&gt;Reporting both links and anchor texts&lt;/li&gt;
  &lt;li&gt;Usable as a module in other scripts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s have a look at the updated script:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat linkextractor.py
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env python
&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;requests&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;bs4&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BeautifulSoup&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;urllib.parse&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urljoin&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;extract_links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;soup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BeautifulSoup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;html.parser&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# TODO: Update base if a &amp;lt;base&amp;gt; element is present with the href attribute
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;soup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;[IMG]&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;urljoin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Usage:&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;{} &amp;lt;URL&amp;gt;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extract_links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;[{}]({})&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The link extraction logic is abstracted into a function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extract_links&lt;/code&gt; that accepts a URL as a parameter and returns a list of objects containing anchor texts and normalized hyperlinks.
This functionality can now be imported into other scripts as a module (which we will utilize in the next step).&lt;/p&gt;

&lt;p&gt;Now, let’s build a new image and see these changes in effect:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -t linkextractor:step2 .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We have used a new tag &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor:step2&lt;/code&gt; for this image so that we don’t overwrite the image from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step1&lt;/code&gt; to illustrate that they can co-exist and containers can be run using either of these images.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image ls
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
linkextractor       step2               be2939eada96        3 seconds ago        931MB
linkextractor       step1               673d045a822f        About a minute ago   931MB
python              3                   a9d071760c82        2 weeks ago          923MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running a one-off container using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor:step2&lt;/code&gt; image should now yield an improved output:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -it --rm linkextractor:step2 https://training.play-with-docker.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[Play with Docker classroom](https://training.play-with-docker.com/)
[About](https://training.play-with-docker.com/about/)
[IT Pros and System Administrators](https://training.play-with-docker.com/#ops)
[Developers](https://training.play-with-docker.com/#dev)
[Stage 1: The Basics](https://training.play-with-docker.com/ops-stage1)
[Stage 2: Digging Deeper](https://training.play-with-docker.com/ops-stage2)
[Stage 3: Moving to Production](https://training.play-with-docker.com/ops-stage3)
[Stage 1: The Basics](https://training.play-with-docker.com/dev-stage1)
[Stage 2: Digging Deeper](https://training.play-with-docker.com/dev-stage2)
[Stage 3: Moving to Staging](https://training.play-with-docker.com/dev-stage3)
[Full list of individual labs](https://training.play-with-docker.com/alacart)
[[IMG]](https://twitter.com/intent/tweet?text=Play with Docker Classroom&amp;amp;url=https://training.play-with-docker.com/&amp;amp;via=docker&amp;amp;related=docker)
[[IMG]](https://facebook.com/sharer.php?u=https://training.play-with-docker.com/)
[[IMG]](https://plus.google.com/share?url=https://training.play-with-docker.com/)
[[IMG]](http://www.linkedin.com/shareArticle?mini=true&amp;amp;url=https://training.play-with-docker.com/&amp;amp;title=Play%20with%20Docker%20Classroom&amp;amp;source=https://training.play-with-docker.com)
[[IMG]](https://2018.dockercon.com/)
[DockerCon 2018 in San Francisco](https://2018.dockercon.com/)
[training.docker.com](https://success.docker.com/training/)
[Register here](https://community.docker.com/registrations/groups/4316)
[Docker, Inc.](https://docker.com)
[[IMG]](https://www.docker.com)
[[IMG]](https://www.facebook.com/docker.run)
[[IMG]](https://twitter.com/docker)
[[IMG]](https://www.github.com/play-with-docker/play-with-docker.github.io)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running a container using the previous image &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor:step1&lt;/code&gt; should still result in the old output:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -it --rm linkextractor:step1 https://training.play-with-docker.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;So far, we have learned how to containerize a script with its necessary dependencies to make it more portable.
We have also learned how to make changes in the application and build different variants of Docker images that can co-exist.
In the next step we will build a web service that will utilize this script and will make the service run inside a Docker container.&lt;/p&gt;

&lt;h2 id=&quot;step-3-link-extractor-api-service&quot;&gt;Step 3: Link Extractor API Service&lt;/h2&gt;

&lt;p&gt;Checkout the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step3&lt;/code&gt; branch and list files in it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout step3
tree
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── Dockerfile
├── README.md
├── linkextractor.py
├── main.py
└── requirements.txt

0 directories, 5 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following changes have been made in this step:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Added a server script &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.py&lt;/code&gt; that utilizes the link extraction module written in the last step&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; is updated to refer to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.py&lt;/code&gt; file instead&lt;/li&gt;
  &lt;li&gt;Server is accessible as a WEB API at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://&amp;lt;hostname&amp;gt;[:&amp;lt;prt&amp;gt;]/api/&amp;lt;url&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Dependencies are moved to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirements.txt&lt;/code&gt; file&lt;/li&gt;
  &lt;li&gt;Needs port mapping to make the service accessible outside of the container (the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flask&lt;/code&gt; server used here listens on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5000&lt;/code&gt; by default)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s first look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; for changes:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       python:3&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;LABEL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;      maintainer=&quot;Sawood Alam &amp;lt;@ibnesayeed&amp;gt;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       requirements.txt /app/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN        &lt;/span&gt;pip &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; requirements.txt

&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       *.py /app/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;a+x &lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.py

&lt;span class=&quot;k&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;        [&quot;./main.py&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since we have started using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirements.txt&lt;/code&gt; for dependencies, we no longer need to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install&lt;/code&gt; command for individual packages.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENTRYPOINT&lt;/code&gt; directive is replaced with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMD&lt;/code&gt; and it is referring to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.py&lt;/code&gt; script that has the server code it because we do not want to use this image for one-off commands now.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor.py&lt;/code&gt; module remains unchanged in this step, so let’s look into the newly added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.py&lt;/code&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env python
&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;flask&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;flask&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;flask&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;linkextractor&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extract_links&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Usage: http://&amp;lt;hostname&amp;gt;[:&amp;lt;prt&amp;gt;]/api/&amp;lt;url&amp;gt;&quot;&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/api/&amp;lt;path:url&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;utf-8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;?&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extract_links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;0.0.0.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, we are importing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extract_links&lt;/code&gt; function from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor&lt;/code&gt; module and converting the returned list of objects into a JSON response.&lt;/p&gt;

&lt;p&gt;It’s time to build a new image with these changes in place:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -t linkextractor:step3 .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then run the container in detached mode (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; flag) so that the terminal is available for other commands while the container is still running.
Note that we are mapping the port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5000&lt;/code&gt; of the container with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5000&lt;/code&gt; of the host (using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-p 5000:5000&lt;/code&gt; argument) to make it accessible from the host.
We are also assigning a name (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--name=linkextractor&lt;/code&gt;) to the container to make it easier to see logs and kill or remove the container.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -d -p 5000:5000 --name=linkextractor linkextractor:step3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If things go well, we should be able to see the container being listed in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Up&lt;/code&gt; condition:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUSPORTS                    NAMES
d69c0150a754        linkextractor:step3   &quot;./main.py&quot;         9 seconds ago       Up 8 seconds0.0.0.0:5000-&amp;gt;5000/tcp   linkextractor
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can now make an HTTP request in the form &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/api/&amp;lt;url&amp;gt;&lt;/code&gt; to talk to this server and fetch the response containing extracted links:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl -i http://localhost:5000/api/http://example.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;HTTP/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Content-Type:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Content-Length:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;78&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Server:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Werkzeug/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.14&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Python/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.7&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Date:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Sun,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Sep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;52&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;56&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;GMT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://www.iana.org/domains/example&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;More information...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, we have the API service running that accepts requests in the form &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/api/&amp;lt;url&amp;gt;&lt;/code&gt; and responds with a JSON containing hyperlinks and anchor texts of all the links present in the web page at give &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;url&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since the container is running in detached mode, so we can’t see what’s happening inside, but we can see logs using the name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor&lt;/code&gt; we assigned to our container:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container logs linkextractor
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;* Serving Flask app &quot;main&quot; (lazy loading)
* Environment: production
  WARNING: Do not use the development server in a production environment.
  Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
172.17.0.1 - - [23/Sep/2018 20:52:56] &quot;GET /api/http://example.com/ HTTP/1.1&quot; 200 -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can see the messages logged when the server came up, and an entry of the request log when we ran the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl&lt;/code&gt; command.
Now we can kill and remove this container:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container rm -f linkextractor
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this step we have successfully ran an API service listening on port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5000&lt;/code&gt;.
This is great, but APIs and JSON responses are for machines, so in the next step we will run a web service with a human-friendly web interface in addition to this API service.&lt;/p&gt;

&lt;h2 id=&quot;step-4-link-extractor-api-and-web-front-end-services&quot;&gt;Step 4: Link Extractor API and Web Front End Services&lt;/h2&gt;

&lt;p&gt;Checkout the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step4&lt;/code&gt; branch and list files in it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout step4
tree
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── README.md
├── api
│   ├── Dockerfile
│   ├── linkextractor.py
│   ├── main.py
│   └── requirements.txt
├── docker-compose.yml
└── www
    └── index.php

2 directories, 7 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this step the following changes have been made since the last step:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The link extractor JSON API service (written in Python) is moved in a separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./api&lt;/code&gt; folder that has the exact same code as in the previous step&lt;/li&gt;
  &lt;li&gt;A web front-end application is written in PHP under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./www&lt;/code&gt; folder that talks to the JSON API&lt;/li&gt;
  &lt;li&gt;The PHP application is mounted inside the official &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php:7-apache&lt;/code&gt; Docker image for easier modification during the development&lt;/li&gt;
  &lt;li&gt;The web application is made accessible at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://&amp;lt;hostname&amp;gt;[:&amp;lt;prt&amp;gt;]/?url=&amp;lt;url-encoded-url&amp;gt;&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;An environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;API_ENDPOINT&lt;/code&gt; is used inside the PHP application to configure it to talk to the JSON API server&lt;/li&gt;
  &lt;li&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file is written to build various components and glue them together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this step we are planning to run two separate containers, one for the API and the other for the web interface.
The latter needs a way to talk to the API server.
For the two containers to be able to talk to each other, we can either map their ports on the host machine and use that for request routing or we can place the containers in a single private network and access directly.
Docker has excellent support for networking and provides helpful commands for dealing with networks.
Additionally, in a Docker network containers identify themselves using their names as hostnames to avoid hunting for their IP addresses in the private network.
However, we are not going to do any of this manually, instead we will be using Docker Compose to automate many of these tasks.&lt;/p&gt;

&lt;p&gt;So let’s look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file we have:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&apos;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;linkextractor-api:step4-python&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./api&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;5000:5000&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;php:7-apache&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;80:80&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;API_ENDPOINT=http://api:5000/api/&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./www:/var/www/html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a simple YAML file that describes the two services &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt;.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api&lt;/code&gt; service will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor-api:step4-python&lt;/code&gt; image that is not built yet, but will be built on-demand using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./api&lt;/code&gt; directory.
This service will be exposed on the port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5000&lt;/code&gt; of the host.&lt;/p&gt;

&lt;p&gt;The second service named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt; will use official &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php:7-apache&lt;/code&gt; image directly from the DockerHub, that’s why we do not have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; for it.
The service will be exposed on the default HTTP port (i.e., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;80&lt;/code&gt;).
We will supply an environment variable named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;API_ENDPOINT&lt;/code&gt; with the value &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://api:5000/api/&lt;/code&gt; to tell the PHP script where to connect to for the API access.
Notice that we are not using an IP address here, instead, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api:5000&lt;/code&gt; is being used because we will have a dynamic hostname entry in the private network for the API service matching its service name.
Finally, we will bind mount the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./www&lt;/code&gt; folder to make the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.php&lt;/code&gt; file available inside of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt; service container at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/html&lt;/code&gt;, which is the default web root for the Apache web server.&lt;/p&gt;

&lt;p&gt;Now, let’s have a look at the user-facing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www/index.php&lt;/code&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat www/index.php
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a long file that mainly contains all the markup and styles of the page.
However, the important block of code is in the beginning of the file as illustrated below:&lt;/p&gt;

&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$api_endpoint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;API_ENDPOINT&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;http://localhost:5000/api/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$_GET&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;$json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;file_get_contents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$api_endpoint&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$json&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Something is wrong with the URL: &quot;&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$links&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;json_decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$domains&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$links&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nb&quot;&gt;array_push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$domains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;parse_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;PHP_URL_HOST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$domainct&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;array_count_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$domains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;arsort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$domainct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$api_endpoint&lt;/code&gt; variable is initialized with the value of the environment variable supplied from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$_ENV[&quot;API_ENDPOINT&quot;]&lt;/code&gt; (otherwise falls back to a default value of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://localhost:5000/api/&lt;/code&gt;).
The request is made using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;file_get_contents&lt;/code&gt; function that uses the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$api_endpoint&lt;/code&gt; variable and user supplied URL from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$_GET[&quot;url&quot;]&lt;/code&gt;.
Some analysis and transformations are performed on the received response that are later used in the markup to populate the page.&lt;/p&gt;

&lt;p&gt;Let’s bring these services up in detached mode using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose&lt;/code&gt; utility:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose up -d --build
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Creating network &quot;linkextractor_default&quot; with the default driver
Pulling web (php:7-apache)...
7-apache: Pulling from library/php

... [OUTPUT REDACTED] ...

Status: Downloaded newer image for php:7-apache
Building api
Step 1/8 : FROM       python:3

... [OUTPUT REDACTED] ...

Successfully built 1f419be1c2bf
Successfully tagged linkextractor-api:step4-python
Creating linkextractor_web_1 ... done
Creating linkextractor_api_1 ... done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This output shows that Docker Compose automatically created a network named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor_default&lt;/code&gt;, pulled &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php:7-apache&lt;/code&gt; image from DockerHub, built &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api:python&lt;/code&gt; image using our local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;, and finally, spun two containers &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor_web_1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor_api_1&lt;/code&gt; that correspond to the two services we have defined in the YAML file above.&lt;/p&gt;

&lt;p&gt;Checking for the list of running containers confirms that the two services are indeed running:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS   PORTS                    NAMES
268b021b5a2c        php:7-apache                     &quot;docker-php-entrypoi…&quot;   3 minutes ago       Up 3 minutes        0.0.0.0:80-&amp;gt;80/tcp       linkextractor_web_1
5bc266b4e43d        linkextractor-api:step4-python   &quot;./main.py&quot;              3 minutes ago       Up 3 minutes        0.0.0.0:5000-&amp;gt;5000/tcp   linkextractor_api_1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We should now be able to talk to the API service as before:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl -i http://localhost:5000/api/http://example.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To access the web interface &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;click to open the Link Extractor&lt;/a&gt;.
Then fill the form with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://training.play-with-docker.com/&lt;/code&gt; (or any HTML page URL of your choice) and submit to extract links from it.&lt;/p&gt;

&lt;p&gt;We have just created an application with microservice architecture, isolating individual tasks in separate services as opposed to monolithic applications where everything is put together in a single unit.
Microservice applications are relatively easier to scale, maintains, and move around.
They also allow easy swapping of components with an equivalent service.
More on that later.&lt;/p&gt;

&lt;p&gt;Now, let’s modify the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www/index.php&lt;/code&gt; file to replace all occurrences of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Link Extractor&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Super Link Extractor&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;sed -i &apos;s/Link Extractor/Super Link Extractor/g&apos; www/index.php
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Reloading the web interface of the application (or &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;clicking here&lt;/a&gt;) should now reflect this change in the title, header, and footer.
This is happening because the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./www&lt;/code&gt; folder is bind mounted inside of the container, so any changes made outside will reflect inside the container or the vice versa.
This approach is very helpful in development, but in the production environment we would prefer our Docker images to be self-contained.
Let’s revert these changes now to clean the Git tracking:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git reset --hard
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Before we move on to the next step we need to shut these services down, but Docker Compose can help us take care of it very easily:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Stopping linkextractor_api_1 ... done
Stopping linkextractor_web_1 ... done
Removing linkextractor_api_1 ... done
Removing linkextractor_web_1 ... done
Removing network linkextractor_default
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the next step we will add one more service to our stack and will build a self-contained custom image for our web interface service.&lt;/p&gt;

&lt;h2 id=&quot;step-5-redis-service-for-caching&quot;&gt;Step 5: Redis Service for Caching&lt;/h2&gt;

&lt;p&gt;Checkout the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step5&lt;/code&gt; branch and list files in it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout step5
tree
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── README.md
├── api
│   ├── Dockerfile
│   ├── linkextractor.py
│   ├── main.py
│   └── requirements.txt
├── docker-compose.yml
└── www
    ├── Dockerfile
    └── index.php

2 directories, 8 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Some noticeable changes from the previous step are as following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; is added in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./www&lt;/code&gt; folder for the PHP web application to build a self-contained image and avoid live file mounting&lt;/li&gt;
  &lt;li&gt;A Redis container is added for caching using the official Redis Docker image&lt;/li&gt;
  &lt;li&gt;The API service talks to the Redis service to avoid downloading and parsing pages that were already scraped before&lt;/li&gt;
  &lt;li&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REDIS_URL&lt;/code&gt; environment variable is added to the API service to allow it to connect to the Redis cache&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s first inspect the newly added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./www&lt;/code&gt; folder:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat www/Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       php:7-apache&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;LABEL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;      maintainer=&quot;Sawood Alam &amp;lt;@ibnesayeed&amp;gt;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;        API_ENDPOINT=&quot;http://localhost:5000/api/&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       . /var/www/html/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a rather simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; that uses the official &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;php:7-apache&lt;/code&gt; image as the base and copies all the files from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./www&lt;/code&gt; folder into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/www/html/&lt;/code&gt; folder of the image.
This is exactly what was happening in the previous step, but that was bind mounted using a volume, while here we are making the code part of the self-contained image.
We have also added the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;API_ENDPOINT&lt;/code&gt; environment variable here with a default value, which implicitly suggests that this is an important information that needs to be present in order for the service to function properly (and should be customized at run time with an appropriate value).&lt;/p&gt;

&lt;p&gt;Next, we will look at the API server’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api/main.py&lt;/code&gt; file where we are utilizing the Redis cache:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat api/main.py
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The file has many lines, but the important bits are as illustrated below:&lt;/p&gt;

&lt;div class=&quot;language-py highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;redis_conn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;REDIS_URL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;redis://localhost:6379&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# ...
&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;jsonlinks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonlinks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;extract_links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;jsonlinks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;indent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonlinks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time the API service needs to know how to connect to the Redis instance as it is going to use it for caching.
This information can be made available at run time using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REDIS_URL&lt;/code&gt; environment variable.
A corresponding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENV&lt;/code&gt; entry is also added in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; of the API service with a default value.&lt;/p&gt;

&lt;p&gt;A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis&lt;/code&gt; client instance is created using the hostname &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis&lt;/code&gt; (same as the name of the service as we will see later) and the default Redis port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6379&lt;/code&gt;.
We are first trying to see if a cache is present in the Redis store for a given URL, if not then we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extract_links&lt;/code&gt; function as before and populate the cache for future attempts.&lt;/p&gt;

&lt;p&gt;Now, let’s look into the updated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&apos;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;linkextractor-api:step5-python&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./api&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;5000:5000&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;REDIS_URL=redis://redis:6379&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;linkextractor-web:step5-php&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./www&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;80:80&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;API_ENDPOINT=http://api:5000/api/&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api&lt;/code&gt; service configuration largely remains the same as before, except the updated image tag and added environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REDIS_URL&lt;/code&gt; that points to the Redis service.
For the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt; service, we are using the custom &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor-web:step5-php&lt;/code&gt; image that will be built using the newly added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./www&lt;/code&gt; folder.
We are no longer mounting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./www&lt;/code&gt; folder using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;volumes&lt;/code&gt; config.
Finally, a new service named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis&lt;/code&gt; is added that will use the official image from DockerHub and needs no specific configurations for now.
This service is accessible to the Python API using its service name, the same way the API service is accessible to the PHP front-end service.&lt;/p&gt;

&lt;p&gt;Let’s boot these services up:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose up -d --build
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;... [OUTPUT REDACTED] ...

Creating linkextractor_web_1   ... done
Creating linkextractor_api_1   ... done
Creating linkextractor_redis_1 ... done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, that all three services are up, access the web interface by &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;clicking the Link Extractor&lt;/a&gt;.
There should be no visual difference from the previous step.
However, if you extract links from a page with a lot of links, the first time it should take longer, but the successive attempts to the same page should return the response fairly quickly.
To check whether or not the Redis service is being utilized, we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose exec&lt;/code&gt; followed by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis&lt;/code&gt; service name and the Redis CLI’s &lt;a href=&quot;https://redis.io/commands/monitor&quot;&gt;monitor&lt;/a&gt; command:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose exec redis redis-cli monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, try to extract links from some web pages using the web interface and see the difference in Redis log entries for pages that are scraped the first time and those that are repeated.
Before continuing further with the tutorial, stop the interactive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;monitor&lt;/code&gt; stream as a result of the above &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;redis-cli&lt;/code&gt; command by pressing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl + C&lt;/code&gt; keys while the interactive terminal is in focus.&lt;/p&gt;

&lt;p&gt;Now that we are not mounting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/www&lt;/code&gt; folder inside the container, local changes should not reflect in the running service:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;sed -i &apos;s/Link Extractor/Super Link Extractor/g&apos; www/index.php
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Verify that the changes made locally do not reflect in the running service by reloading the web interface and then revert changes:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git reset --hard
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, shut these services down and get ready for the next step:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Stopping linkextractor_web_1   ... done
Stopping linkextractor_redis_1 ... done
Stopping linkextractor_api_1   ... done
Removing linkextractor_web_1   ... done
Removing linkextractor_redis_1 ... done
Removing linkextractor_api_1   ... done
Removing network linkextractor_default
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have successfully orchestrated three microservices to compose our Link Extractor application.
We now have an application stack that represents the architecture illustrated in the figure shown in the introduction of this tutorial.
In the next step we will explore how easy it is to swap components from an application with the microservice architecture.&lt;/p&gt;

&lt;h2 id=&quot;step-6-swap-python-api-service-with-ruby&quot;&gt;Step 6: Swap Python API Service with Ruby&lt;/h2&gt;

&lt;p&gt;Checkout the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step6&lt;/code&gt; branch and list files in it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout step6
tree
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── README.md
├── api
│   ├── Dockerfile
│   ├── Gemfile
│   └── linkextractor.rb
├── docker-compose.yml
├── logs
└── www
    ├── Dockerfile
    └── index.php

3 directories, 7 files
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Some significant changes from the previous step include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The API service written in Python is replaced with a similar Ruby implementation&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;API_ENDPOINT&lt;/code&gt; environment variable is updated to point to the new Ruby API service&lt;/li&gt;
  &lt;li&gt;The link extraction cache event (HIT/MISS) is logged and is persisted using volumes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./api&lt;/code&gt; folder does not contain any Python scripts, instead, it now has a Ruby file and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile&lt;/code&gt; to manage dependencies.&lt;/p&gt;

&lt;p&gt;Let’s have a quick walk through the changed files:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat api/linkextractor.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-rb highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#!/usr/bin/env ruby&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# encoding: utf-8&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;sinatra&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;open-uri&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;uri&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;nokogiri&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;json&quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;redis&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:protection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:except&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:path_traversal&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;redis&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;url: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDIS_URL&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;redis://localhost:6379&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;mkdir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;logs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;exist?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;logs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;Usage: http://&amp;lt;hostname&amp;gt;[:&amp;lt;prt&amp;gt;]/api/&amp;lt;url&amp;gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;/api/*&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;splat&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;query_string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:empty?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cache_status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;HIT&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;jsonlinks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonlinks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cache_status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;MISS&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;jsonlinks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;pretty_generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;extract_links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonlinks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;cache_log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;logs/extraction.log&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cache_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_i&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache_status&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;cache_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;close&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;content-type&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;application/json&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;jsonlinks&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;extract_links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Nokogiri&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;HTML&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;doc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;css&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;a&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;text: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;empty?&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;[IMG]&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;ss&quot;&gt;href: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;URI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;links&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This Ruby file is almost equivalent to what we had in Python before, except, in addition to that it also logs the link extraction requests and corresponding cache events.
In a microservice architecture application swapping components with an equivalent one is easy as long as the expectations of consumers of the component are maintained.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat api/Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       ruby:2.6&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;LABEL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;      maintainer=&quot;Sawood Alam &amp;lt;@ibnesayeed&amp;gt;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;        LANG C.UTF-8&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;        REDIS_URL=&quot;redis://localhost:6379&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;    /app&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       Gemfile /app/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN        &lt;/span&gt;bundle &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;       linkextractor.rb /app/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod &lt;/span&gt;a+x linkextractor.rb

&lt;span class=&quot;k&quot;&gt;CMD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;        [&quot;./linkextractor.rb&quot;, &quot;-o&quot;, &quot;0.0.0.0&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Above &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; is written for the Ruby script and it is pretty much self-explanatory.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3&apos;&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;api&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;linkextractor-api:step6-ruby&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./api&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;4567:4567&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;REDIS_URL=redis://redis:6379&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./logs:/app/logs&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;web&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;linkextractor-web:step6-php&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./www&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;80:80&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;API_ENDPOINT=http://api:4567/api/&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;redis&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redis&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file has a few minor changes in it.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;api&lt;/code&gt; service image is now named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;linkextractor-api:step6-ruby&lt;/code&gt;, the port mapping is changed from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5000&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;4567&lt;/code&gt; (which is the default port for Sinatra server), and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;API_ENDPOINT&lt;/code&gt; environment variable in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt; service is updated accordingly so that the PHP code can talk to it.&lt;/p&gt;

&lt;p&gt;With these in place, let’s boot our service stack:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose up -d --build
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;... [OUTPUT REDACTED] ...

Successfully built b713eef49f55
Successfully tagged linkextractor-api:step6-ruby
Creating linkextractor_web_1   ... done
Creating linkextractor_api_1   ... done
Creating linkextractor_redis_1 ... done
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We should now be able to access the API (using the updated port number):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl -i http://localhost:4567/api/http://example.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;err&quot;&gt;HTTP/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;OK&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Content-Type:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;application/json&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Content-Length:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;96&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;X-Content-Type-Options:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;nosniff&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Server:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;WEBrick/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;1.4&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;(Ruby/&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.5&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2018-03-29&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Date:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Mon,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Sep&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2018&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;01&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;35&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;GMT&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Connection:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;Keep-Alive&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;text&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;More information...&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;http://www.iana.org/domains/example&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, open the web interface by &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;clicking the Link Extractor&lt;/a&gt; and extract links of a few URLs.
Also, try to repeat these attempts for some URLs.
If everything is alright, the web application should behave as before without noticing any changes in the API service (which is completely replaced).&lt;/p&gt;

&lt;p&gt;We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt; command with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-f&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--follow&lt;/code&gt; option to follow the log output live.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;tail -f logs/extraction.log
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Try a few more URLs in the web interface. You should see the new log entries appear in the terminal.&lt;/p&gt;

&lt;p&gt;To stop following the log, press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ctrl + C&lt;/code&gt; keys while the interactive terminal is in focus.&lt;/p&gt;

&lt;p&gt;We can shut the stack down now:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Since we have persisted logs, they should still be available after the services are gone:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat logs/extraction.log
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;1537753295      MISS    http://example.com/
1537753600      HIT     http://example.com/
1537753635      MISS    https://training.play-with-docker.com/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This illustrates that the caching is functional as the second attempt to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://example.com/&lt;/code&gt; resulted in a cache &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HIT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this step we explored the possibility of swapping components of an application with microservice architecture with their equivalents without impacting rest of the parts of the stack.
We have also explored data persistence using bind mount volumes that persists even after the containers writing to the volume are gone.&lt;/p&gt;

&lt;p&gt;So far, we have used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose&lt;/code&gt; utility to orchestrate the application stack, which is good for development environment, but for production environment we use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker stack deploy&lt;/code&gt; command to run the application in a &lt;a href=&quot;/swarm-stack-intro&quot;&gt;Docker Swarm Cluster&lt;/a&gt;.
It is left for you as an assignment to deploy this application in a Docker Swarm Cluster.&lt;/p&gt;

&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;We started this tutorial with a simple Python script that scrapes links from a given web page URL.
We demonstrated various difficulties in running the script.
We then illustrated how easy to run and portable the script becomes onces it is containerized.
In the later steps we gradually evolved the script into a multi-service application stack.
In the process we explored various concepts of microservice architecture and how Docker tools can be helpful in orchestrating a multi-service stack.
Finally, we demonstrated the ease of microservice component swapping and data persistence.&lt;/p&gt;

&lt;p&gt;The next step would be to learn how to deploy such service stacks in a &lt;a href=&quot;/swarm-stack-intro&quot;&gt;Docker Swarm Cluster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As an aside, here are some introductory Docker slides.&lt;/p&gt;

&lt;iframe src=&quot;//www.slideshare.net/slideshow/embed_code/key/qAjE65X2E8tkxe&quot; width=&quot;700&quot; height=&quot;450&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot; style=&quot;border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;&quot; allowfullscreen=&quot;&quot;&gt; &lt;/iframe&gt;
&lt;div style=&quot;margin-bottom:5px&quot;&gt; &lt;strong&gt; &lt;a href=&quot;//www.slideshare.net/ibnesayeed/introducing-docker-application-containerization-service-orchestration&quot; title=&quot;Introducing Docker - Application Containerization &amp;amp; Service Orchestration&quot; target=&quot;_blank&quot;&gt;Introducing Docker - Application Containerization &amp;amp; Service Orchestration&lt;/a&gt; &lt;/strong&gt; by &lt;strong&gt;&lt;a href=&quot;//www.slideshare.net/ibnesayeed&quot; target=&quot;_blank&quot;&gt;Sawood Alam&lt;/a&gt;&lt;/strong&gt; &lt;/div&gt;

&lt;hr /&gt;
</description>
        <pubDate>Sat, 22 Sep 2018 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/microservice-orchestration/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/microservice-orchestration/</guid>
        
        <category>beginner</category>
        
        <category>linux</category>
        
        <category>developer</category>
        
        <category>microservice</category>
        
        <category>orchestration</category>
        
        <category>linkextractor</category>
        
        <category>api</category>
        
        <category>python</category>
        
        <category>php</category>
        
        <category>ruby</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Node.js with SQL Server on Docker</title>
        <description>&lt;p&gt;This lab walks through the evolution of a simple Node.js bulletin board application, running on Docker. You’ll start with a simple app that uses hard-coded data, then add SQL Server for persistent storage, and a proxy to improve web performance.&lt;/p&gt;

&lt;p&gt;You’ll learn about packaging applications in Docker images, running distributed applications across multiple containers, and adding instrumentation to your containers so you can see the health of your application. You’ll use the Docker command line, Docker Compose and Docker swarm for running the app.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;: Beginner (assumes no familiarity with Docker)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt;: Approximately 60 minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Tasks&lt;/strong&gt;:&lt;/p&gt;

&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_0&quot;&gt;Task 0: Prerequisites&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_1&quot;&gt;Task 1: Run v1 of the app in a container&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_2&quot;&gt;Task 2: Add a SQL Server database container for storage&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_3&quot;&gt;Task 3: Switch to high availability in swarm mode&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_4&quot;&gt;Task 4: Add a reverse proxy to improve performance&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#Task_5&quot;&gt;Task 5: Add monitoring and an application dashboard&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;task-0-prerequisites&quot;&gt;&lt;a name=&quot;task0&quot;&gt;&lt;/a&gt;Task 0: Prerequisites&lt;/h2&gt;

&lt;p&gt;You will need:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;a copy of the application source code&lt;/li&gt;
  &lt;li&gt;a Docker ID&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;clone-the-source-code-from-github&quot;&gt;Clone the source code from GitHub&lt;/h3&gt;

&lt;p&gt;Use the following command to clone the application source code from GitHub (you can click the command or manually type it). This will make a copy of the lab’s repo in a new sub-directory called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node-bulletin-board&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git clone https://github.com/dockersamples/node-bulletin-board.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And browse to the source code folder:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cd node-bulletin-board
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;save-your-docker-id&quot;&gt;Save your Docker ID&lt;/h3&gt;

&lt;p&gt;You need a Docker ID to push your images to Docker Hub. If you don’t have one, &lt;a href=&quot;https://hub.docker.com&quot;&gt;create a free Docker ID at Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now save your Docker ID in an environment variable - &lt;strong&gt;you need to type this command manually with your own Docker ID&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;export dockerId=&apos;your-docker-id&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;Be sure to use your own Docker ID. Mine is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sixeyed&lt;/code&gt;, so the command I run is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;export dockerId=&apos;sixeyed&apos;&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Check your Docker ID gets displayed when you read the variable:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo $dockerId
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;task-1-run-v1-of-the-app-in-a-container&quot;&gt;&lt;a name=&quot;Task_1&quot;&gt;&lt;/a&gt;Task 1: Run v1 of the app in a container&lt;/h2&gt;

&lt;p&gt;The first version of the application uses a single container, running the Node.js application, and the data is only stored on the client’s browser.&lt;/p&gt;

&lt;p&gt;Switch to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1&lt;/code&gt; source code branch:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout v1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now build the Docker image, which uses this &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v1/bulletin-board-app/Dockerfile&quot;&gt;Dockerfile&lt;/a&gt; to package the source code on top of the official Node.js image:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build --tag $dockerId/bb-app:v1 --file bulletin-board-app/Dockerfile ./bulletin-board-app
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When that completes you will have version 1 of the app in an image stored locally. Run a container from that image to start the app:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run --detach --publish 8080:8080 $dockerId/bb-app:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Docker will start a container from the application image, which runs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm start&lt;/code&gt; to start the app. You can browse to the application on port 8080:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;Click here for v1 of the app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll see the bulletin board application, and you can add and remove events:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/node-sql-server-docker-bulletin-board.jpg&quot; alt=&quot;Bulletin Board sample app&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you make some changes and refresh the browser, you’ll see your changes get lost. That’s because the events are only stored in memory on the client.&lt;/p&gt;

&lt;p&gt;In the next step you’ll fix that.&lt;/p&gt;

&lt;h2 id=&quot;task-2-add-a-sql-server-database-container-for-storage&quot;&gt;&lt;a name=&quot;Task_2&quot;&gt;&lt;/a&gt;Task 2: Add a SQL Server database container for storage&lt;/h2&gt;

&lt;p&gt;Storing data in client memory is only good for proof-of-concept applications. In this step you’ll build and deploy version 2 of the app, which uses a SQL Server database to store the events.&lt;/p&gt;

&lt;p&gt;First switch to the v2 code branch:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout v2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now clear up all the containers from the previous part:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container rm --force $(docker container ls --quiet)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You’ll use Docker Compose to build and run the application. The compose file specifies the database and application containers to run, and how to configure them.&lt;/p&gt;

&lt;p&gt;The compose file also contains the path to the &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v2/bulletin-board-app/Dockerfile&quot;&gt;application Dockerfile&lt;/a&gt; and to the &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v2/bulletin-board-db/Dockerfile&quot;&gt;database Dockerfile&lt;/a&gt;, so you can build the database and application images with one command:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose build
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When that completes, you’ll have two Docker images:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;your-docker-id&amp;gt;/bulletin-board-db:v2&lt;/code&gt; - which is based on Microsoft’s SQL Server image and packages the database schema for the bulletin board&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;your-docker-id&amp;gt;/bulletin-board-app:v2&lt;/code&gt; - which is the new version of the Node.js application, using SQL Server to store events&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can start the whole app with Docker Compose:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You’ll see compose starts the database first, because it’s specified as a dependency for the application container. Then it starts the app container.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you list all containers, you’ll see there are two instances of the app container. One container started before the database was ready, so it failed - and then Docker Compose started a replacement container, which did connect to the database.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls --all
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;Click here for v2 of the app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll see it’s the same user interface, but now you can add and delete events and when you refresh the page they’re still there. The data is persisted in SQL Server.&lt;/p&gt;

&lt;p&gt;The SQL Server database is not publicly available. In the &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v2/docker-compose.yml&quot;&gt;docker-compose.yml&lt;/a&gt; file, the web container, the port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8080&lt;/code&gt; is published so you can send traffic in, but no ports are published for the database. It’s only available to other containers and to Docker.&lt;/p&gt;

&lt;h2 id=&quot;task-3-switch-to-high-availability-in-swarm-mode&quot;&gt;&lt;a name=&quot;Task_3&quot;&gt;&lt;/a&gt;Task 3: Switch to high availability in swarm mode&lt;/h2&gt;

&lt;p&gt;Swarm mode lets you join several Docker servers together and treat them as a single unit. You deploy your app as services to the swarm, and Docker runs containers across all the servers.&lt;/p&gt;

&lt;p&gt;You can run multiple instances of a container to deal with scale, and if a server goes down and you lose containers, Docker starts replacement containers on other servers.&lt;/p&gt;

&lt;p&gt;First clear down all the containers from part 2:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container rm --force $(docker container ls --quiet)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now switch to swarm mode:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr $(hostname -i)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This creates a single-node swarm. The output of the command shows you how to join other Docker servers to the swarm - all you need are more servers running Docker in the same network. You can scale Docker swarm up to hundreds of nodes.&lt;/p&gt;

&lt;p&gt;The normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker&lt;/code&gt; commands still work in swarm mode. Switch to the v3 source code branch:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout v3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And build the application with Docker Compose:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose build
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Version 3 has the same source code, but the &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v3/bulletin-board-app/Dockerfile&quot;&gt;Dockerfile for v3&lt;/a&gt; of the the web app includes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HEALTHCHECK&lt;/code&gt; instruction. That tells Docker how to test if the application is healthy, and unhealthy containers are stopped and replaced with new ones.&lt;/p&gt;

&lt;p&gt;You use the same Docker Compose file format to deploy in swarm mode, and there are some additional options available. Deploy version 3 of the app using the &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v3/docker-stack.yml&quot;&gt;docker-stack.yml&lt;/a&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy -c docker-stack.yml bb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;A stack is a way to group many services together, so you can manage them as one unit. You can see the services in the stack, which tells you if the application is up:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack services bb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;Click here for v3 of the app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll see the application behaviour is exactly the same - containers are running from the same Docker images, but now they’re being scheduled by Docker swarm.&lt;/p&gt;

&lt;p&gt;Docker swarm also supports rolling updates for applications running as stacks. In the next part you’ll add more functionality to the app, by running a web proxy.&lt;/p&gt;

&lt;h2 id=&quot;task-4-add-a-reverse-proxy-to-improve-performance&quot;&gt;&lt;a name=&quot;Task_4&quot;&gt;&lt;/a&gt;Task 4: Add a reverse proxy to improve performance&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://nodejs.org/en/&quot;&gt;Node.js&lt;/a&gt; is a good server platform, but it’s easy to improve performance by putting a reverse proxy in front of the Node.js application. The proxy is the public entrypoint to the app, and it handles requests from users.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://nginx.org&quot;&gt;Nginx&lt;/a&gt; is a popular open source web server which you can easily configure as a reverse proxy. The &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v4/bulletin-board-proxy/nginx.conf&quot;&gt;Nginx configuration&lt;/a&gt; in this part makes use of browser and server caching, which reduces the load on the web application and improves performance.&lt;/p&gt;

&lt;p&gt;Switch to the v4 code branch:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout v4
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And use Docker Compose to build the application:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose build
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now you have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v4&lt;/code&gt; Docker images for all the application parts, you can upgrade the running stack using the new &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v4/docker-stack.yml&quot;&gt;docker-stack.yml&lt;/a&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy -c docker-stack.yml bb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Version 4 adds a proxy server to the stack which publishes port &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;80&lt;/code&gt;, so now you can browse to the app on the standard HTTP port:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;Click here for v4 of the app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The web application looks the same, but behind the scenes all the hard work is being done by the Nginx proxy. You can open developer tools on your browser and inspect the network responses - Nginx has added browser caching hints, and it’s also using a local cache to reduce traffic to the Node.js app.&lt;/p&gt;

&lt;p&gt;There are also several instances of the proxy container running - Docker swarm load-balances incoming requests between those containers. If you had multiple servers in the swarm, you would be able to scale up to handle your incoming workload.&lt;/p&gt;

&lt;p&gt;In the final part you’ll add monitoring to the application, so you can see what the Node.js container is doing.&lt;/p&gt;

&lt;h2 id=&quot;task-5-add-monitoring-and-an-application-dashboard&quot;&gt;&lt;a name=&quot;Task_5&quot;&gt;&lt;/a&gt;Task 5: Add monitoring and an application dashboard&lt;/h2&gt;

&lt;p&gt;Docker swarm makes it super easy to scale containers, but before you go to production witrh a Dockerized application, you’ll want monitoring in place so you can see what all those containers are doing.&lt;/p&gt;

&lt;p&gt;Two open-source technologies are very popular in the Docker ecosystem for monitoring containers. &lt;a href=&quot;https://prometheus.io&quot;&gt;Prometheus&lt;/a&gt; is an instrumentation server that collects and stores metrics from your containers, and &lt;a href=&quot;https://grafana.com&quot;&gt;Grafana&lt;/a&gt; is an analytics UI that plugs into Prometheus to show dashboards.&lt;/p&gt;

&lt;p&gt;In this part you’ll add Prometheus and Grafana to your application.&lt;/p&gt;

&lt;p&gt;First switch to the v5 code branch:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git checkout v5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now build the application, which will build images from &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v5/bulletin-board-metrics/Dockerfile&quot;&gt;the Prometheus Dockerfile&lt;/a&gt; and &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v5/bulletin-board-dashboard/Dockerfile&quot;&gt;the Grafana Dockerfile&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose build
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v5&lt;/code&gt; images for all the application components now. Upgrade the stack to the v5 &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v5/docker-stack.yml&quot;&gt;docker-stack.yml&lt;/a&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker stack deploy -c docker-stack.yml bb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;Click here for v5 of the app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The UX is the same, but now the Prometheus container is scraping metrics from the Node.js container, every 5 seconds.&lt;/p&gt;

&lt;p&gt;To see the application metrics in Grafana, you need to configure the dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;3000&quot;&gt;Click here for Grafana&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log in to Grafana with the credentials &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin&lt;/code&gt; / &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;admin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add a new data source with the following details:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Name: &lt;strong&gt;prometheus&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Type: &lt;strong&gt;Prometheus&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;URL: &lt;strong&gt;http://bb-metrics:9090&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;../images/node-sql-server-docker-grafana-data-source.jpg&quot; alt=&quot;Grafana data source&quot; /&gt;&lt;/p&gt;

&lt;p&gt;From the Grafana icon, click &lt;em&gt;Dashboards… Import&lt;/em&gt; and load the JSON dashboard file from &lt;a href=&quot;https://github.com/dockersamples/node-bulletin-board/blob/v5/bulletin-board-dashboard/dashboard.json&quot;&gt;v5 /dashboard.json&lt;/a&gt;. Select the Prometheus data store.&lt;/p&gt;

&lt;p&gt;You’ll now see the application dashboard - send some load into the app by refreshing the browser, and the graphs will be populated:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;img/grafana-dashboard.jpg&quot; alt=&quot;Grafana dashboard&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;cleanup&quot;&gt;&lt;a name=&quot;Cleanup&quot;&gt;&lt;/a&gt;Cleanup&lt;/h2&gt;

&lt;p&gt;You can easily remove the whole application by removing the stack:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack rm bb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And you can leave swarm mode to return to a single-server Docker host:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm leave --force
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Thanks for completing the Node.js and SQL Server lab! You’ve learned how to build and run applications with Docker and Docker Compose, how to achieve high availability with Docker Swarm and how to get your application production ready by adding a proxy and a metrics dashboard.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;http://training.play-with-docker.com&quot;&gt;Play with Docker Training site&lt;/a&gt; is always on, and there are plenty more labs you can try at home.&lt;/p&gt;
</description>
        <pubDate>Tue, 28 Nov 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/node-sql-server-docker/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/node-sql-server-docker/</guid>
        
        <category>beginner</category>
        
        <category>linux</category>
        
        <category>developer</category>
        
        <category>swarm</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker for IT Pros and System Administrators Stage 1</title>
        <description>&lt;p&gt;Welcome to the first stage of your Docker learning journey. This stage will&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Get you familiar with the core concepts of Docker&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Help you understand why other IT leaders are rapidly adopting Docker&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Help you see how Docker can help your organization&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an introduction to Docker specifically geared towards for sys admins and IT Pros, check out Mike Coleman’s talk &lt;em&gt;Docker?!? But I’m a SYSADMIN!&lt;/em&gt;&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/S3iKweF41Cc&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;!-- 5-7 minute animated PPT to describe the benefits TODO: Build this --&gt;

&lt;!--  Build - Ship - Run recorded demo TODO: Record New One --&gt;

&lt;!-- Docker 101 Webinar TODO: Find right video --&gt;

&lt;h2 id=&quot;hands-on-learning&quot;&gt;Hands-on Learning&lt;/h2&gt;
&lt;p&gt;Give Docker a try with these three beginning tutorials:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/ops-s1-hello&quot;&gt;Your First Linux Containers&lt;/a&gt;: In this lab you will explore the basics of running containers: pulling images from a registry, running an containerized application, and container instances and isolation.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/ops-s1-images&quot;&gt;Customizing Docker Images&lt;/a&gt;: Move on to building your own custom Docker images and explore the Docker concept of image layers and the Dockerfile.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/ops-s1-swarm-intro&quot;&gt;Deploy and Managing Multiple Containers&lt;/a&gt;: Real apps consist of multiple components. In this lab you will begin to explore running multiple services as a single stack with Docker Swarm.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;more-reading-ebook&quot;&gt;More Reading: Ebook&lt;/h2&gt;
&lt;p&gt;To understand the differences between containers and VMs, check out this ebook: &lt;a href=&quot;https://goto.docker.com/docker-for-the-virtualization-admin.html&quot;&gt;Docker for the Virtualization Admin&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Tue, 19 Sep 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/ops-stage1/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/ops-stage1/</guid>
        
        <category>windows</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
      </item>
    
      <item>
        <title>Doing More With Docker Images</title>
        <description>&lt;p&gt;In the previous exercise you pulled down images from &lt;a href=&quot;https://store.docker.com&quot;&gt;Docker Store&lt;/a&gt; to run in your containers. Then you ran multiple instances and noted how each instance was isolated from the others. We hinted that this is used in many production IT environments every day but obviously we need a few more tools in our belt to get to the point where Docker can become a true time &amp;amp; money saver.&lt;/p&gt;

&lt;p&gt;First thing you may want to do is figure out how to create our own images. While there are over 700K images on &lt;a href=&quot;https://store.docker.com&quot;&gt;Docker Store&lt;/a&gt; it is almost certain that none of them are exactly what you run in your data center today. Even something as common as a Windows OS image would get its own tweaks before you actually run it in production. In the &lt;a href=&quot;/ops-s1-hello&quot;&gt;first lab&lt;/a&gt;, we created a file called “hello.txt” in one of our container instances. If that instance of our Alpine container was something we wanted to re-use in future containers and share with others, we would need to create a custom image that everyone could use.&lt;/p&gt;

&lt;p&gt;We will start with the simplest form of image creation, in which we simply &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;commit&lt;/code&gt; one of our container instances as an image. Then we will explore a much more powerful and useful method for creating images: the Dockerfile.&lt;/p&gt;

&lt;p&gt;We will then see how to get the details of an image through the inspection and explore the filesystem to have a better understanding of what happens under the hood.&lt;/p&gt;

&lt;h2 id=&quot;image-creation-from-a-container&quot;&gt;Image creation from a container&lt;/h2&gt;

&lt;p&gt;Let’s start by running an interactive shell in a ubuntu container:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -ti ubuntu bash
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you know from earlier labs, you just grabbed the image called “ubuntu” from Docker Store and are now running the bash shell inside that container.&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;To customize things a little bit we will install a package called &lt;a href=&quot;http://www.figlet.org&quot; title=&quot;make large letters out of ordinary text&quot;&gt;figlet&lt;/a&gt; in this container. Your container should still be running so type the following commands at your ubuntu container command line:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;apt-get update
apt-get install -y figlet
figlet &quot;hello docker&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should see the words “hello docker” printed out in large ascii characters on the screen. Go ahead and exit from this container&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;exit
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now let us pretend this new figlet application is quite useful and you want to share it with the rest of your team. You &lt;em&gt;could&lt;/em&gt; tell them to do exactly what you did above and install figlet in to their own container, which is simple enough in this example. But if this was a real world application where you had just installed several packages and run through a number of configuration steps the process could get cumbersome and become quite error prone. Instead, it would be easier to create an &lt;em&gt;image&lt;/em&gt; you can share with your team.&lt;/p&gt;

&lt;p&gt;To start, we need to get the ID of this container using the ls command (do not forget the -a option as the non running container are not returned by the ls command).&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls -a
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Before we create our own image, we might want to inspect all the changes we made. Try typing the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container diff &amp;lt;container ID&amp;gt;&lt;/code&gt; for the container you just created. You should see a list of all the files that were added to or changed in the container when you installed figlet. Docker keeps track of all of this information for us. This is part of the &lt;em&gt;layer&lt;/em&gt; concept we will explore in a few minutes.&lt;/p&gt;

&lt;p&gt;Now, to create an image we need to “commit” this container. Commit creates an image locally on the system running the Docker engine. Run the following command, using the container ID you retrieved, in order to commit the container and create an image out of it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker container commit CONTAINER_ID
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s it - you have created your first image! Once it has been commited, we can see the newly created image in the list of available images.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should see something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
&amp;lt;none&amp;gt;              &amp;lt;none&amp;gt;              a104f9ae9c37        46 seconds ago      160MB
ubuntu              latest              14f60031763d        4 days ago          120MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that the image we pulled down in the first step (ubuntu) is listed here along with our own custom image. Except our custom image has no information in the REPOSITORY or TAG columns, which would make it tough to identify exactly what was in this container if we wanted to share amongst multiple team members.&lt;/p&gt;

&lt;p&gt;Adding this information to an image is known as &lt;em&gt;tagging&lt;/em&gt; an image. From the previous command, get the ID of the newly created image and tag it so it’s named &lt;strong&gt;ourfiglet&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker image tag &amp;lt;IMAGE_ID&amp;gt; ourfiglet
docker image ls
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we have the more friendly name “ourfiglet” that we can use to identify our image.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ourfiglet           latest              a104f9ae9c37        5 minutes ago       160MB
ubuntu              latest              14f60031763d        4 days ago          120MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is a graphical view of what we just completed:
&lt;img src=&quot;/images/ops-images-commit.svg&quot; alt=&quot;commit container to image&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now we will run a container based on the newly created &lt;em&gt;ourfiglet&lt;/em&gt; image:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run ourfiglet figlet hello
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As the figlet package is present in our &lt;em&gt;ourfiglet&lt;/em&gt; image, the command returns the following output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; _          _ _
| |__   ___| | | ___
| &apos;_ \ / _ \ | |/ _ \
| | | |  __/ | | (_) |
|_| |_|\___|_|_|\___/

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This example shows that we can create a container, add all the libraries and binaries in it and then commit it in order to create an image. We can then use that image just as we would for images pulled down from the Docker Store. We still have a slight issue in that our image is only stored locally. To share the image we would want to &lt;em&gt;push&lt;/em&gt; the image to a registry somewhere. This is beyond the scope of this lab (and you should not enter any personal login information in these labs) but you can get a free Docker ID, run these labs, and push to the &lt;a href=&quot;https://hub.docker.com/&quot;&gt;Docker Community Hub&lt;/a&gt; from your own system using &lt;a href=&quot;https://www.docker.com/docker-windows&quot;&gt;Docker for Windows&lt;/a&gt; or &lt;a href=&quot;https://www.docker.com/docker-mac&quot;&gt;Docker for Mac&lt;/a&gt; if you want to try this out.&lt;/p&gt;

&lt;p&gt;As mentioned above, this approach of manually installing software in a container and then committing it to a custom image is just one way to create an image. It works fine and is quite common. However, there is a more powerful way to create images. In the following exercise we will see how images are created using a &lt;em&gt;Dockerfile&lt;/em&gt;, which is a text file that contains all the instructions to build an image.&lt;/p&gt;

&lt;h2 id=&quot;image-creation-using-a-dockerfile&quot;&gt;Image creation using a Dockerfile&lt;/h2&gt;

&lt;p&gt;Instead of creating a static binary image, we can use a file called a &lt;em&gt;Dockerfile&lt;/em&gt; to create an image. The final result is essentially the same, but with a Dockerfile we are supplying the instructions for building the image, rather than just the raw binary files. This is useful because it becomes much easier to manage changes, especially as your images get bigger and more complex.&lt;/p&gt;

&lt;p&gt;For example, if a new version of figlet is released we would either have to re-create our image from scratch, or run our image and upgrade the installed version of figlet. In contrast, a Dockerfile would include the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get&lt;/code&gt; commands we used to install figlet so that we - or anybody using the Dockerfile - could simply recompose the image using those instructions.&lt;/p&gt;

&lt;p&gt;It is kind of like the old adage:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Give a sysadmin an image and their app will be up-to-date for a day, give a sysadmin a Dockerfile and their app will always be up-to-date&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ok, maybe that’s a bit of a stretch but Dockerfiles are powerful because they allow us to manage &lt;em&gt;how&lt;/em&gt; an image is built, rather than just managing binaries. In practice, Dockerfiles can be managed the same way you might manage source code: they are simply text files so almost any version control system can be used to manage Dockerfiles over time.&lt;/p&gt;

&lt;p&gt;We will use a simple example in this section and build a “hello world” application in Node.js. Do not be concerned if you are not familiar with Node.js: Docker (and this exercise) does not require you to know all these details.&lt;/p&gt;

&lt;p&gt;We will start by creating a file in which we retrieve the hostname and display it. 
NOTE: You should be at the Docker host’s command line (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$&lt;/code&gt;). If you see a command line that looks similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root@abcd1234567:/#&lt;/code&gt; then you are probably still inside your ubuntu container from the previous exercise. Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt; to return to the host command line.&lt;/p&gt;

&lt;p&gt;Type the following content into a file named &lt;em&gt;index.js&lt;/em&gt;. You can use vi, vim or several other Linux editors in this exercise. If you need assistance with the Linux editor commands to do this follow this footnote&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var os = require(&quot;os&quot;);
var hostname = os.hostname();
console.log(&quot;hello from &quot; + hostname);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The file we just created is the javascript code for our server. As you can probably guess, Node.js will simply print out a “hello” message. We will Docker-ize this application by creating a Dockerfile. We will use &lt;strong&gt;alpine&lt;/strong&gt; as the base OS image, add a Node.js runtime and then copy our source code in to the container. We will also specify the default command to be run upon container creation.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;em&gt;Dockerfile&lt;/em&gt; and copy the following content into it. Again, help creating this file with Linux editors is here &lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM alpine
RUN apk update &amp;amp;&amp;amp; apk add nodejs
COPY . /app
WORKDIR /app
CMD [&quot;node&quot;,&quot;index.js&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s build our first image out of this Dockerfile and name it &lt;em&gt;hello:v0.1&lt;/em&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -t hello:v0.1 .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what you just completed:
&lt;img src=&quot;/images/ops-images-dockerfile.svg&quot; alt=&quot;build container from dockerfile&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We then start a container to check that our applications runs correctly:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run hello:v0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should then have an output similar to the following one (the ID will be different though).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hello from 92d79b6de29f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What just happened?&lt;/strong&gt; 
We created two files: our application code (index.js) is a simple bit of javascript code that prints out a message. And the Dockerfile is the instructions for Docker engine to create our custom container. This Dockerfile does the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Specifies a base image to pull &lt;strong&gt;FROM&lt;/strong&gt; - the &lt;em&gt;alpine&lt;/em&gt; image we used in earlier labs.&lt;/li&gt;
  &lt;li&gt;Then it &lt;strong&gt;RUN&lt;/strong&gt;s two commands (&lt;em&gt;apk update&lt;/em&gt; and &lt;em&gt;apk add&lt;/em&gt;) inside that container which installs the Node.js server.&lt;/li&gt;
  &lt;li&gt;Then we told it to &lt;strong&gt;COPY&lt;/strong&gt; files from our working directory in to the container. The only file we have right now is our &lt;em&gt;index.js&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;Next we specify the &lt;strong&gt;WORKDIR&lt;/strong&gt; - the directory the container should use when it starts up&lt;/li&gt;
  &lt;li&gt;And finally, we gave our container a command (&lt;strong&gt;CMD&lt;/strong&gt;) to run when the container starts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Recall that in previous labs we put commands like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo &quot;hello world&quot;&lt;/code&gt; on the command line. With a Dockerfile we can specify precise commands to run for everyone who uses this container. Other users do not have to build the container themselves once you push your container up to a repository (which we will cover later) or even know what commands are used. The &lt;em&gt;Dockerfile&lt;/em&gt; allows us to specify &lt;em&gt;how&lt;/em&gt; to build a container so that we can repeat those steps precisely everytime and we can specify &lt;em&gt;what&lt;/em&gt; the container should do when it runs. There are actually multiple methods for specifying the commands and accepting parameters a container will use, but for now it is enough to know that you have the tools to create some pretty powerful containers.&lt;/p&gt;

&lt;h2 id=&quot;image-layers&quot;&gt;Image layers&lt;/h2&gt;
&lt;p&gt;There is something else interesting about the images we build with Docker. When running they appear to be a single OS and application. But the images themselves are actually built in &lt;strong&gt;&lt;em&gt;layers&lt;/em&gt;&lt;/strong&gt;. If you scroll back and look at the output from your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image build&lt;/code&gt; command you will notice that there were 5 steps and each step had several tasks. You should see several “fetch” and “pull” tasks where Docker is grabbing various bits from Docker Store or other places. These bits were used to create one or more container &lt;em&gt;layers&lt;/em&gt;. Layers are an important concept. To explore this, we will go through another set of exercises.&lt;/p&gt;

&lt;p&gt;First, check out the image you created earlier by using the &lt;em&gt;history&lt;/em&gt; command (remember to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image ls&lt;/code&gt; command from earlier exercises to find your image IDs):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker image history &amp;lt;image ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;!-- add image of container layer example here--&gt;
&lt;p&gt;What you see is the list of intermediate container images that were built along the way to creating your final Node.js app image. Some of these intermediate images will become &lt;em&gt;layers&lt;/em&gt; in your final container image. In the history command output, the original Alpine layers are at the bottom of the list and then each customization we added in our Dockerfile is its own step in the output. This is a powerful concept because it means that if we need to make a change to our application, it may only affect a single layer! To see this, we will modify our app a bit and create a new image.&lt;/p&gt;

&lt;p&gt;Type the following in to your console window:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &quot;console.log(\&quot;this is v0.2\&quot;);&quot; &amp;gt;&amp;gt; index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will add a new line to the bottom of your &lt;em&gt;index.js&lt;/em&gt; file from earlier so your application will output one additional line of text. Now we will build a new image using our updated code. We will also tag our new image to mark it as a new version so that anybody consuming our images later can identify the correct version to use:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -t hello:v0.2 .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should see output similar to this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Sending build context to Docker daemon  86.15MB
Step 1/5 : FROM alpine
 ---&amp;gt; 7328f6f8b418
Step 2/5 : RUN apk update &amp;amp;&amp;amp; apk add nodejs
 ---&amp;gt; Using cache
 ---&amp;gt; 2707762fca63
Step 3/5 : COPY . /app
 ---&amp;gt; 07b2e2127db4
Removing intermediate container 84eb9c31320d
Step 4/5 : WORKDIR /app
 ---&amp;gt; 6630eb76312c
Removing intermediate container ee6c9e7a5337
Step 5/5 : CMD node index.js
 ---&amp;gt; Running in e079fb6000a3
 ---&amp;gt; e536b9dadd2f
Removing intermediate container e079fb6000a3
Successfully built e536b9dadd2f
Successfully tagged hello:v0.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice something interesting in the build steps this time. In the output it goes through the same five steps, but notice that in some steps it says &lt;strong&gt;Using cache&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ops-images-cache.svg&quot; alt=&quot;layers and cache&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Docker recognized that we had already built some of these layers in our earlier image builds and since nothing had changed in those layers it could simply use a cached version of the layer, rather than pulling down code a second time and running those steps. Docker’s layer management is very useful to IT teams  when patching systems, updating or upgrading to the latest version of code, or making configuration changes to applications. Docker is intelligent enough to build the container in the most efficient way possible, as opposed to repeatedly building an image from the ground up each and every time.&lt;/p&gt;

&lt;h2 id=&quot;image-inspection&quot;&gt;Image Inspection&lt;/h2&gt;

&lt;p&gt;Now let us reverse our thinking a bit. What if we get a container from Docker Store or another registry and want to know a bit about what is inside the container we are consuming? Docker has an  &lt;strong&gt;inspect&lt;/strong&gt; command for images and it returns details on the container image, the commands it runs, the OS and more.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;alpine&lt;/em&gt; image should already be present locally from the exercises above (use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image ls&lt;/code&gt; to confirm), if it’s not, run the following command to pull it down:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image pull alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once we are sure it is there let’s inspect it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image inspect alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There is a lot of information in there:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;the layers the image is composed of&lt;/li&gt;
  &lt;li&gt;the driver used to store the layers&lt;/li&gt;
  &lt;li&gt;the architecture / OS it has been created for&lt;/li&gt;
  &lt;li&gt;metadata of the image&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will not go into all the details here but we can use some filters to just inspect particular details about the image. You may have noticed that the image information is in JSON format. We can take advantage of that to use the inspect command with some filtering info to just get specific data from the image.&lt;/p&gt;

&lt;p&gt;Let’s get the list of layers:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image inspect --format &quot;{{ json .RootFS.Layers }}&quot; alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Alpine is just a small base OS image so there’s just one layer:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[&quot;sha256:60ab55d3379d47c1ba6b6225d59d10e1f52096ee9d5c816e42c635ccc57a5a2b&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now let’s look at our custom Hello image. You will need the image ID (use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image ls&lt;/code&gt; if you need to look it up):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker image inspect --format &quot;{{ json .RootFS.Layers }}&quot; &amp;lt;image ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our Hello image is a bit more interesting (your sha256 hashes will vary):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[&quot;sha256:5bef08742407efd622d243692b79ba0055383bbce12900324f75e56f589aedb0&quot;,&quot;sha256:5ac283aaea742f843c869d28bbeaf5000c08685b5f7ba01431094a207b8a1df9&quot;,&quot;sha256:2ecb254be0603a2c76880be45a5c2b028f6208714aec770d49c9eff4cbc3cf25&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We have three layers in our application. Recall that we had the base Alpine image (the FROM command in our Dockerfile), then we had a RUN command to install some packages, then we had a COPY command to add in our javascript code. Those are our layers! If you look closely, you can even see that both &lt;em&gt;alpine&lt;/em&gt; and &lt;em&gt;hello&lt;/em&gt; are using the same base layer, which we know because they have the same sha256 hash.&lt;/p&gt;

&lt;p&gt;The tools and commands we explored in this lab are just the beginning. Docker Enterprise Edition includes private Trusted Registries with Security Scanning and Image Signing capabilities so you can further inspect and authenticate your images. In addition, there are policy controls to specify which users have access to various images, who can push and pull images, and much more.&lt;/p&gt;

&lt;p&gt;Another important note about layers: each layer is immutable. As an image is created and successive layers are added, the new layers keep track of the changes from the layer below. When you start the container running there is an additional layer used to keep track of any changes that occur as the application runs (like the “hello.txt” file we created in the earlier exercises). This design principle is important for both security and data management. If someone mistakenly or maliciously changes something in a running container, you can very easily revert back to its original state because the base layers cannot be changed. Or you can simply start a new container instance which will start fresh from your pristine image. And applications that create and store data (databases, for example) can store their data in a special kind of Docker object called a &lt;strong&gt;&lt;em&gt;volume&lt;/em&gt;&lt;/strong&gt;, so that data can persist and be shared with other containers. We will explore volumes in a later lab.&lt;/p&gt;

&lt;p&gt;Up next, we will look at more sophisticated applications that run across several containers and use Docker Compose and Docker Swarm to define our architecture and manage it.&lt;/p&gt;

&lt;h2 id=&quot;terminology&quot;&gt;Terminology&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Layers&lt;/em&gt; - A Docker image is built up from a series of layers. Each layer represents an instruction in the image’s Dockerfile. Each layer except the last one is read-only.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Dockerfile&lt;/em&gt; - A text file that contains all the commands, in order, needed to build a given image. The &lt;a href=&quot;https://docs.docker.com/engine/reference/builder&quot;&gt;Dockerfile reference&lt;/a&gt; page lists the various commands and format details for Dockerfiles.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Volumes&lt;/em&gt; - A special Docker container layer that allows data to persist and be shared separately from the container itself. Think of volumes as a way to abstract and manage your persistent data separately from the application itself.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;A note on images and the public Docker Store (AKA Docker Hub): Docker registries are subdivided in to many &lt;em&gt;repositories&lt;/em&gt;. This is the same for both our public registries like Docker Store / Docker Hub, as well as Docker Trusted Registries that you might run in your own environment. Image names must be unique and are specified in the format &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;repository&amp;gt;/&amp;lt;image&amp;gt;:&amp;lt;tag&amp;gt;&lt;/code&gt;. In our exercises, we pulled images called “ubuntu” and “alpine”. Since there is no repository specified we pulled from a default public repository called “library” which is maintained by us at Docker. And since we did not specify a tag, the default is to look for a tag named “latest” and use that. The tags generally specify versions (although this is not a requirement). &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vi index.js&lt;/code&gt; then once the editor loads hit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; key. You can now type each of the commands as shown in the example. When you are finished hit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;esc&amp;gt;&lt;/code&gt; key then type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:wq&lt;/code&gt; and that will save the file and take you back to the command prompt. You can type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; at the command prompt to ensure your &lt;em&gt;index.js&lt;/em&gt; file is there or type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat index.js&lt;/code&gt; to make sure all the code is in the file. If you make a mistake in the editor and you have a hard time navigating the editor it might be easier to start fresh: simply type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;esc&amp;gt;&lt;/code&gt; and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:wq&lt;/code&gt; if you are in the editor and then when you are back to the command line type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rm index.js&lt;/code&gt; to delete the file and then start again. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vi Dockerfile&lt;/code&gt; then once the editor loads hit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; key. Type in each line of the Dockerfile code as shown in the example - capitalization is important! - then hit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;esc&amp;gt;&lt;/code&gt; key followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:wq&lt;/code&gt;. To verify your Dockerfile exists and is correct type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat Dockerfile&lt;/code&gt;. If you make a mistake in the editor and you have a hard time navigating the editor it might be easier to start fresh: simply type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;esc&amp;gt;&lt;/code&gt; and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:wq&lt;/code&gt; if you are in the editor and then when you are back to the command line type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rm Dockerfile&lt;/code&gt; and then start again. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</description>
        <pubDate>Tue, 19 Sep 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/ops-s1-images/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/ops-s1-images/</guid>
        
        <category>developer</category>
        
        <category>operations</category>
        
        <category>linux</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>First Alpine Linux Containers</title>
        <description>&lt;p&gt;In this lab you will run a popular, free, lightweight container and explore the basics of how containers work, how the Docker Engine executes and isolates containers from each other. If you already have experience running containers and basic Docker commands you can probably skip this intro exercise.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Concepts in this exercise:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Docker engine&lt;/li&gt;
  &lt;li&gt;Containers &amp;amp; images&lt;/li&gt;
  &lt;li&gt;Image registries and Docker Hub&lt;/li&gt;
  &lt;li&gt;Container isolation&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;Tips:&lt;/p&gt;

&lt;p&gt;Code snippets are shown in one of three ways throughout this environment:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Code that looks like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this&lt;/code&gt; is sample code snippets that is usually part of an explanation.&lt;/li&gt;
  &lt;li&gt;Code that appears in box like the one below can be clicked on and it will automatically be typed in to the appropriate terminal window:
    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;uname -a
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;Code appearing in windows like the one below is code that you should type in yourself. Usually there will be a unique ID or other bit your need to enter which we cannot supply. Items appearing in &amp;lt;&amp;gt; are the pieces you should substitute based on the instructions.
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker container start &amp;lt;container ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;10-running-your-first-container&quot;&gt;1.0 Running your first container&lt;/h2&gt;
&lt;p&gt;It’s time to get your hands dirty! As with all things technical, a “hello world” app is good place to start. Type or click the code below to run your first Docker container:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That’s it: your first container. The &lt;em&gt;hello-world&lt;/em&gt; container output tells you a bit about what just happened. Essentially, the Docker engine running in your terminal tried to find an &lt;strong&gt;image&lt;/strong&gt; named hello-world. Since you just got started there are no images stored locally (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Unable to find image...&lt;/code&gt;) so Docker engine goes to its default &lt;strong&gt;Docker registry&lt;/strong&gt;, which is &lt;a href=&quot;https://hub.docker.com&quot;&gt;Docker Hub&lt;/a&gt;, to look for an image named “hello-world”. It finds the image there, pulls it down, and then runs it in a container. And hello-world’s only function is to output the text you see in your terminal, after which the container exits.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ops-basics-hello-world.svg&quot; alt=&quot;Hello world explainer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you are familiar with VMs, you may be thinking this is pretty much just like running a virtual machine, except with a central repository of VM images. And in this simple example, that is basically true. But as you go through these exercises you will start to see important ways that Docker and containers differ from VMs. For now, the simple explanation is this:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The VM is a &lt;em&gt;hardware&lt;/em&gt; abstraction: it takes physical CPUs and RAM from a host, and divides and shares it across several smaller virtual machines. There is an OS and application running inside the VM, but the virtualization software usually has no real knowledge of that.&lt;/li&gt;
  &lt;li&gt;A container is an &lt;em&gt;application&lt;/em&gt; abstraction: the focus is really on the OS and the application, and not so much the hardware abstraction.
Many customers actually use both VMs and containers today in their environments and, in fact, may run containers inside of VMs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;11-docker-images&quot;&gt;1.1 Docker Images&lt;/h2&gt;
&lt;p&gt;In this rest of this lab, you are going to run an &lt;a href=&quot;http://www.alpinelinux.org/&quot;&gt;Alpine Linux&lt;/a&gt; container. Alpine is a lightweight Linux distribution so it is quick to pull down and run, making it a popular starting point for many other images.&lt;/p&gt;

&lt;p&gt;To get started, let’s run the following in our terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image pull alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pull&lt;/code&gt; command fetches the alpine &lt;strong&gt;image&lt;/strong&gt; from the &lt;strong&gt;Docker registry&lt;/strong&gt; and saves it in our system. In this case the registry is &lt;strong&gt;&lt;a href=&quot;https://hub.docker.com&quot;&gt;Docker Hub&lt;/a&gt;&lt;/strong&gt;. You can change the registry, but that’s a different lab.&lt;/p&gt;

&lt;p&gt;You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image&lt;/code&gt; command to see a list of all images on your system.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;REPOSITORY              TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
alpine                 latest              c51f86c28340        4 weeks ago         1.109 MB
hello-world             latest              690ed74de00f        5 months ago        960 B
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;11-docker-container-run&quot;&gt;1.1 Docker Container Run&lt;/h3&gt;
&lt;p&gt;Great! Let’s now run a Docker &lt;strong&gt;container&lt;/strong&gt; based on this image. To do that you are going to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run&lt;/code&gt; command.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run alpine ls -l
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;total 48
drwxr-xr-x    2 root     root          4096 Mar  2 16:20 bin
drwxr-xr-x    5 root     root           360 Mar 18 09:47 dev
drwxr-xr-x   13 root     root          4096 Mar 18 09:47 etc
drwxr-xr-x    2 root     root          4096 Mar  2 16:20 home
drwxr-xr-x    5 root     root          4096 Mar  2 16:20 lib
......
......
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;While the output of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; command may not be all that exciting, behind the scenes quite a few things just took place. When you call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt;, the Docker client finds the image (alpine in this case), creates the container and then runs a command in that container. When you run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run alpine&lt;/code&gt;, you provided a command (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls -l&lt;/code&gt;), so Docker executed this command inside the container for which you saw the directory listing. After the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; command finished, the container shut down.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ops-basics-run-details.svg&quot; alt=&quot;docker run explainer&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The fact that the container exited after running our command is important, as you will start to see. Let’s try something more exciting. Type in the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run alpine echo &quot;hello from alpine&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you should get the following output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hello from alpine
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this case, the Docker client dutifully ran the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo&lt;/code&gt; command inside our alpine container and then exited. If you noticed, all of that happened pretty quickly and again our container exited. As you will see in a few more steps, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo&lt;/code&gt; command ran in a separate container instance. Imagine booting up a virtual machine (VM), running a command and then killing it; it would take a minute or two just to boot the VM before running the command. A VM has to emulate a full hardware stack, boot an operating system, and then launch your app - it’s a virtualized &lt;em&gt;hardware&lt;/em&gt; environment. Docker containers function at the application layer so they skip most of the steps VMs require and just run what is required for the app. Now you know why they say containers are fast!&lt;/p&gt;

&lt;p&gt;Try another command.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run alpine /bin/sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wait, nothing happened! Is that a bug? No! In fact, something did happen. You started a 3rd instance of the alpine container and it ran the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/sh&lt;/code&gt; and then exited. You did not supply any additional commands to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/sh&lt;/code&gt; so it just launched the shell, exited the shell, and then stopped the container. What you might have &lt;em&gt;expected&lt;/em&gt; was an interactive shell where you could type some commands. Docker has a facility for that by adding a flag to run the container in an interactive terminal. For this example, type the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker container run -it alpine /bin/sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You are now inside the container running a Linux shell and you can try out a few commands like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls -l&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uname -a&lt;/code&gt; and others. Note that Alpine is a small Linux OS so several commands might be missing. Exit out of the shell and container by typing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Ok, we said that we had run each of our commands above in a separate container instance. We can see these instances using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls&lt;/code&gt; command. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls&lt;/code&gt; command by itself shows you all containers that are currently running:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since no containers are running, you see a blank line. Let’s try a more useful variant: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls -a&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls -a
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
36171a5da744        alpine              &quot;/bin/sh&quot;                5 minutes ago       Exited (0) 2 minutes ago                        fervent_newton
a6a9d46d0b2f        alpine             &quot;echo &apos;hello from alp&quot;    6 minutes ago       Exited (0) 6 minutes ago                        lonely_kilby
ff0a5c3750b9        alpine             &quot;ls -l&quot;                   8 minutes ago       Exited (0) 8 minutes ago                        elated_ramanujan
c317d0a9e3d2        hello-world         &quot;/hello&quot;                 34 seconds ago      Exited (0) 12 minutes ago                       stupefied_mcclintock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What you see now is a list of all containers that you ran. Notice that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STATUS&lt;/code&gt; column shows that these containers exited some time ago.&lt;/p&gt;

&lt;p&gt;Here is the same output of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls -a&lt;/code&gt; command, shown diagrammatically (note that your container IDs and names will be different):&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ops-basics-instances.svg&quot; alt=&quot;Docker container instances&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It makes sense to spend some time getting comfortable with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; commands. To find out more about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;run&lt;/code&gt;, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run --help&lt;/code&gt; to see a list of all flags it supports. As you proceed further, we’ll see a few more variants of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run&lt;/code&gt; but feel free to experiment here before proceeding.&lt;/p&gt;

&lt;h3 id=&quot;12-container-isolation&quot;&gt;1.2 Container Isolation&lt;/h3&gt;
&lt;p&gt;In the steps above we ran several commands via container instances with the help of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls -a&lt;/code&gt; command showed us that there were several containers listed. Why are there so many containers listed if they are all from the &lt;em&gt;alpine&lt;/em&gt; image?&lt;/p&gt;

&lt;p&gt;This is a critical security concept in the world of Docker containers! Even though each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run&lt;/code&gt; command used the same alpine &lt;strong&gt;&lt;em&gt;image&lt;/em&gt;&lt;/strong&gt;, each execution was a separate, isolated &lt;strong&gt;&lt;em&gt;container&lt;/em&gt;&lt;/strong&gt;. Each container has a separate filesystem and runs in a different namespace; by default a container has no way of interacting with other containers, even those from the same image. Let’s try another exercise to learn more about isolation.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -it alpine /bin/ash
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/ash&lt;/code&gt; is another type of shell available in the alpine image. Once the container launches and you are at the container’s command prompt type the following commands:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; echo &quot;hello world&quot; &amp;gt; hello.txt

 ls
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo&lt;/code&gt; command creates a file called “hello.txt” with the words “hello world” inside it. The second command gives you a directory listing of the files and should show your newly created “hello.txt” file. Now type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt; to leave this container.&lt;/p&gt;

&lt;p&gt;To show how isolation works, run the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run alpine ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It is the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; command we used inside the container’s interactive ash shell, but this time, did you notice that your “hello.txt” file is missing? That’s isolation! Your command ran in a new and separate &lt;em&gt;instance&lt;/em&gt;, even though it is based on the same &lt;em&gt;image&lt;/em&gt;. The 2nd instance has no way of interacting with the 1st instance because the Docker Engine keeps them separated and we have not setup any extra parameters that would enable these two instances to interact.&lt;/p&gt;

&lt;p&gt;In every day work, Docker users take advantage of this feature not only for security, but to test the effects of making application changes. Isolation allows users to quickly create separate, isolated test copies of an application or service and have them run side-by-side without interfering with one another. In fact, there is a whole lifecycle where users take their changes and move them up to production using this basic concept and the built-in capabilities of Docker Enteprise. We will explore more of that in later exercises.&lt;/p&gt;

&lt;p&gt;Right now, the obvious question is “how do I get back to the container that has my ‘hello.txt’ file?”&lt;/p&gt;

&lt;p&gt;Once again run the&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls -a
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;command again and you should see output similar to the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
36171a5da744        alpine              &quot;ls&quot;                     2 minutes ago       Exited (0) 2 minutes ago                        distracted_bhaskara
3030c9c91e12        alpine              &quot;/bin/ash&quot;               5 minutes ago       Exited (0) 2 minutes ago                        fervent_newton
a6a9d46d0b2f        alpine             &quot;echo &apos;hello from alp&quot;    6 minutes ago       Exited (0) 6 minutes ago                        lonely_kilby
ff0a5c3750b9        alpine             &quot;ls -l&quot;                   8 minutes ago       Exited (0) 8 minutes ago                        elated_ramanujan
c317d0a9e3d2        hello-world         &quot;/hello&quot;                 34 seconds ago      Exited (0) 12 minutes ago                       stupefied_mcclintock
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Graphically this is what happened on our Docker Engine:
&lt;img src=&quot;/images/ops-basics-isolation.svg&quot; alt=&quot;Docker container isolation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The container in which we created the “hello.txt” file is the same one where we used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/bin/ash&lt;/code&gt; shell, which we can see listed in the “COMMAND” column. The &lt;em&gt;Container ID&lt;/em&gt; number from the first column uniquely identifies that particular container instance. In the sample output above the container ID is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;3030c9c91e12&lt;/code&gt;. We can use a slightly different command to tell Docker to run this specific container instance. Try typing:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker container start &amp;lt;container ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Instead of using the full container ID you can use just the first few characters, as long as they are enough to uniquely ID a container. So we could simply use “3030” to identify the container instance in the example above, since no other containers in this list start with these characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls&lt;/code&gt; command again to list the running containers.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                      PORTS               NAMES
3030c9c91e12        alpine              &quot;/bin/ash&quot;                2 minutes ago       Up 14 seconds                        distracted_bhaskara
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice this time that our container instance is still running. We used the ash shell this time so the rather than simply exiting the way /bin/sh did earlier, ash waits for a command. We can send a command in to the container to run by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; command, as follows:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker container exec &amp;lt;container ID&amp;gt; ls
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This time we get a directory listing and it shows our “hello.txt” file because we used the container instance where we created that file.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/ops-basics-exec.svg&quot; alt=&quot;Docker container exec command&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now you are starting to see some of the important concepts of containers. In the next exercise we will start to see how you can create your own Docker images and how to use a Dockerfile to standardize images such that you can create larger, more complex images in a simple, automated manner.&lt;/p&gt;

&lt;h3 id=&quot;13-terminology&quot;&gt;1.3 Terminology&lt;/h3&gt;
&lt;p&gt;In the last section, you saw a lot of Docker-specific jargon which might be confusing to some. So before you go further, let’s clarify some terminology that is used frequently in the Docker ecosystem.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Images&lt;/em&gt; - The file system and configuration of our application which are used to create containers. To find out more about a Docker image, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image inspect alpine&lt;/code&gt;. In the demo above, you used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image pull&lt;/code&gt; command to download the &lt;strong&gt;alpine&lt;/strong&gt; image. When you executed the command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run hello-world&lt;/code&gt;, it also did a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image pull&lt;/code&gt; behind the scenes to download the &lt;strong&gt;hello-world&lt;/strong&gt; image.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Containers&lt;/em&gt; - Running instances of Docker images — containers run the actual applications. A container includes an application and all of its dependencies. It shares the kernel with other containers, and runs as an isolated process in user space on the host OS. You created a container using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; which you did using the alpine image that you downloaded. A list of running containers can be seen using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls&lt;/code&gt; command.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Docker daemon&lt;/em&gt; - The background service running on the host that manages building, running and distributing Docker containers.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Docker client&lt;/em&gt; - The command line tool that allows the user to interact with the Docker daemon.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Docker Hub&lt;/em&gt; - Store is, among other things, a &lt;a href=&quot;https://store.docker.com/&quot;&gt;registry&lt;/a&gt; of Docker images. You can think of the registry as a directory of all available Docker images. You’ll be using this later in this tutorial.&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;quiz&quot;&gt;Where do images get pulled from by default when not found locally?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;( ) Docker Trusted Registry&lt;/li&gt;
  &lt;li&gt;(x) Docker Hub&lt;/li&gt;
  &lt;li&gt;( ) There is no default&lt;/li&gt;
  &lt;li&gt;( ) Docker Store&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;quiz&quot;&gt;Which command lists your Docker images?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;(x) docker image ls&lt;/li&gt;
  &lt;li&gt;( ) docker run&lt;/li&gt;
  &lt;li&gt;( ) docker container ls&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 19 Sep 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/ops-s1-hello/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/ops-s1-hello/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Swarm Mode Introduction for IT Pros</title>
        <description>&lt;p&gt;So far we have explored using single instances of containers running on a single host, much like a developer might do when working on a single service application or like an IT adminstrator might do on a test rig. Production applications are usually much more complex and this single server model will not work to coordinate 10s or 100s of containers and the network connections amongst them, not to mention the need to ensure availability and the ability to scale.&lt;/p&gt;

&lt;p&gt;For real applications IT users and app teams need more sophisticated tools. Docker supplies two such tools: &lt;strong&gt;&lt;em&gt;Docker Compose&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;Docker Swarm Mode&lt;/em&gt;&lt;/strong&gt;. The two tools have some similarities but some important differences:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Compose&lt;/strong&gt; is used to control multiple containers on a single system. Much like the &lt;em&gt;Dockerfile&lt;/em&gt; we looked at to build an image, there is a text file that describes the application: which images to use, how many instances, the network connections, etc. But &lt;em&gt;Compose&lt;/em&gt; only runs on a single system so while it is useful, we are going to skip &lt;em&gt;Compose&lt;/em&gt;&lt;sup id=&quot;a1&quot;&gt;&lt;a href=&quot;#fn-compose&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; and go straight to &lt;em&gt;Docker Swarm Mode&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Swarm Mode&lt;/strong&gt; tells Docker that you will be running many Docker engines and you want to coordinate operations across all of them. Swarm mode combines the ability to not only define the application architecture, like Compose, but to define and maintain high availability levels, scaling, load balancing, and more. With all this functionality, &lt;em&gt;Swarm mode&lt;/em&gt; is used more often in production environments than its more simplistic cousin, Compose.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-application&quot;&gt;The application&lt;/h2&gt;

&lt;p&gt;The voting app is a multi-container application often used for demo purposes during Docker meetups and conferences. It basically allow users to vote between two choices, the default being “cat” and “dog”, but could be “space” or “tab”, too, if you feel like it. This application is available on Github and updated very frequently when new features are developed.&lt;/p&gt;

&lt;h2 id=&quot;initialize-your-swarm&quot;&gt;Initialize Your Swarm&lt;/h2&gt;

&lt;p&gt;First thing we need to do is tell our Docker hosts we want to use Docker Swarm Mode. Swarms &lt;em&gt;can&lt;/em&gt; be just a single node, but that is unusual as you would have no high availability capabilities and you would severely limit your scalability. Most production swarms have at least three &lt;em&gt;manager&lt;/em&gt; nodes in them and many &lt;em&gt;worker&lt;/em&gt; nodes. Three managers is the minimum to have a true high-availability cluster with quorum. Note that manager nodes can run your container tasks the same as a worker node, but this functionality can also be separated so that managers only perform the management tasks.&lt;/p&gt;

&lt;p&gt;Initializing Docker Swarm Mode is easy. In the first terminal window labeled [node1] enter the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr $(hostname -i)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That’s it - you now have your first Swarm manager and it is listening on the IP address returned by the (hostname -i) command. You should see some output that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Swarm initialized: current node (tjocs7ul557phkmp6mkpjmu3f) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token &amp;lt;token&amp;gt; &amp;lt;host&amp;gt;

To add a manager to this swarm, run &apos;docker swarm join-token manager&apos; and follow the instructions.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the output of your swarm init, you are given a command in the middle that looks like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm join -token SWMTKN-X-abcdef.....&lt;/code&gt; which you use to join workers nodes to the swarm. You are also given a second command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm join-token manager&lt;/code&gt; for adding additional managers.&lt;/p&gt;

&lt;p&gt;We are going to add a worker. Copy the “docker swarm join…” command from your manager’s output and paste it in the 2nd terminal window on your screen. &lt;em&gt;Make sure to copy the entire command - it’s likely to break across multiple lines - and do not copy the sample command above because your token will be different&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You now officially have a Docker Swarm! Currently, you have one manager and one worker. As hinted at above, you would almost always have 3 or more manager nodes and several worker nodes in order to maintain high availability and scalability, but one of each is enough to get started.&lt;/p&gt;

&lt;h2 id=&quot;show-swarm-members&quot;&gt;Show Swarm Members&lt;/h2&gt;

&lt;p&gt;From the first terminal window, check the number of nodes in the swarm (running this command from the second terminal worker node will fail as swarm related commands need to be issued against a swarm manager).&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above command should output 2 nodes, the first one being the manager, and the second one a worker. You should see that your manager node is also the “Leader”. This is because you only have one manager node. The Leader is just what it sounds like: the main control node for all the managers. If your Leader node goes down for some reason, the other manager nodes will elect a new leader; just one reason why you would always have multiple manager nodes in true production.&lt;/p&gt;

&lt;p&gt;Here is a view of managers and workers in Docker Swarm Mode. In our exercise, we have just one manager and one worker, but you can see how multiple managers and workers interact in the diagram:
&lt;img src=&quot;/images/ops-swarm-arch.svg&quot; alt=&quot;Swarm architecture&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;clone-the-voting-app&quot;&gt;Clone the Voting App&lt;/h2&gt;

&lt;p&gt;Now to do something interesting we will retrieve the sample voting app code from Github.&lt;/p&gt;

&lt;p&gt;Ensure you are in the first terminal - the manager - and enter the following two commands:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git clone https://github.com/docker/example-voting-app
cd example-voting-app
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;deploy-a-stack&quot;&gt;Deploy a Stack&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;&lt;em&gt;stack&lt;/em&gt;&lt;/strong&gt; is a group of &lt;strong&gt;&lt;em&gt;services&lt;/em&gt;&lt;/strong&gt; that are deployed together: multiple containerized components of an application that run in separate instances. Each individual &lt;em&gt;service&lt;/em&gt; can actually be made up of one or more containers, called &lt;strong&gt;&lt;em&gt;tasks&lt;/em&gt;&lt;/strong&gt; and then all the tasks &amp;amp; services together make up a &lt;em&gt;stack&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;As with Dockerfiles and the Compose files, the file that defines a stack is a plain text file that is easy to edit and track. In our exercise, there is a file called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-stack.yml&lt;/code&gt; in the current folder which will be used to deploy the voting app as a stack. Enter the following to investigate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-stack.yml&lt;/code&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat docker-stack.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This YAML file defines our entire stack: the architecture of the services, number of instances, how everything is wired together, how to handle updates to each service. It is the source code for our application design. A few items of particular note:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Near the top of the file you will see the line “services:”. These are the individual application components. In the voting app we have redis, db, vote, result, worker, and visualizer as our services.&lt;/li&gt;
  &lt;li&gt;Beneath each service are lines that specify how that service should run:
    &lt;ul&gt;
      &lt;li&gt;Notice the familiar term &lt;em&gt;image&lt;/em&gt; from earlier labs? Same idea here: this is the container image to use for a particular service.&lt;/li&gt;
      &lt;li&gt;&lt;em&gt;Ports&lt;/em&gt; and &lt;em&gt;networks&lt;/em&gt; are mostly self-explanatory although it is worth pointing out that these networks and ports can be privately used within the stack or they can allow external communication to and from a stack.&lt;sup id=&quot;a2&quot;&gt;&lt;a href=&quot;#fn-network&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
      &lt;li&gt;Note that some services have a line labeled &lt;em&gt;replicas&lt;/em&gt;: this indicates the number of instances, or tasks, of this service that the Swarm managers should start when the stack is brought up. The Docker engine is intelligent enough to automatically load balance between multiple replicas using built-in load balancers. (The built-in load balancer can, of course, be swapped out for something else.)&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ensure you are in the [node1] manager terminal and do the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy --compose-file=docker-stack.yml voting_stack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can see if the stack deployed from the [node1] manager terminal&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output should be the following. It indicates the 6 services of the voting app’s stack (named voting_stack) have been deployed.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NAME          SERVICES
voting_stack  6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can get details on each service within the stack with the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack services voting_stack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output should be similar to the following, although naturally your IDs will be unique:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME                     MODE        REPLICAS  IMAGE
10rt1wczotze  voting_stack_visualizer  replicated  1/1       dockersample
s/visualizer:stable
8lqj31k3q5ek  voting_stack_redis       replicated  1/1       redis:alpine
nhb4igkkyg4y  voting_stack_result      replicated  1/1       dockersample
s/examplevotingapp_result:before
nv8d2z2qhlx4  voting_stack_db          replicated  1/1       postgres:9.4
ou47zdyf6cd0  voting_stack_vote        replicated  2/2       dockersample
s/examplevotingapp_vote:before
rpnxwmoipagq  voting_stack_worker      replicated  1/1       dockersample
s/examplevotingapp_worker:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you see that there are 0 replicas just wait a few seconds and enter the command again. The Swarm will eventually get all the replicas running for you. Just like our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-stack&lt;/code&gt; file specified, there are two replicas of the &lt;em&gt;voting_stack_vote&lt;/em&gt; service and one of each of the others.&lt;/p&gt;

&lt;p&gt;Let’s list the tasks of the vote service.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps voting_stack_vote
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should get an output like the following one where the 2 tasks (replicas) of the service are listed.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME                 IMAGE
      NODE   DESIRED STATE  CURRENT STATE           ERROR  PORTS
my7jqgze7pgg  voting_stack_vote.1  dockersamples/examplevotingapp_vote:be
fore  node1  Running        Running 56 seconds ago
3jzgk39dyr6d  voting_stack_vote.2  dockersamples/examplevotingapp_vote:be
fore  node2  Running        Running 58 seconds ago
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From the NODE column, we can see one task is running on each node. This app happens to have a built-in &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;SWARM VISUALIZER&lt;/a&gt; to show you how the app is setup and running. You can also access the &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;5000&quot;&gt;front-end web UI&lt;/a&gt; of the app to cast your vote for dogs or cats, and track how the votes are going on the &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;5001&quot;&gt;result&lt;/a&gt; page. Try opening the front-end several times so you can cast multiple votes. You should see that the “container ID” listed at the bottom of the voting page changes since we have two replicas running.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;SWARM VISUALIZER&lt;/a&gt; gives you the physical layout of the stack, but here is a logical interpretation of how stacks, services and tasks are inter-related:
&lt;img src=&quot;/images/ops-swarm-stack-service-task.svg&quot; alt=&quot;Stack, services and tasks&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;scaling-an-application&quot;&gt;Scaling An Application&lt;/h2&gt;
&lt;p&gt;Let us pretend that our cats vs. dogs vote has gone viral and our two front-end web servers are no longer able to handle the load. How can we tell our app to add more replicas of our &lt;em&gt;vote&lt;/em&gt; service? In production you might automate it through Docker’s APIs but for now we will do it manually. You could also edit the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-stack.yml&lt;/code&gt; file and change the specs if you wanted to make the scale size more permanent. Type the following at the [node1] terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service scale voting_stack_vote=5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now enter your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker stack services voting_stack&lt;/code&gt; command again. You should see the number of replicas for the vote service increase to 5 and in a few seconds Swarm will have all of them running. Go back to your &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;5000&quot;&gt;front-end voting UI&lt;/a&gt; and refresh the page a few times. You should see the &lt;em&gt;container ID&lt;/em&gt; listed at the bottom cycle through all 5 of your containers. If you go back and refresh your &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;SWARM VISUALIZER&lt;/a&gt; you should see your updated architecture there as well.&lt;/p&gt;

&lt;p&gt;Here’s our new architecture after scaling:
&lt;img src=&quot;/images/ops-swarm-scale.svg&quot; alt=&quot;Swarm scaling&quot; /&gt;&lt;/p&gt;

&lt;p&gt;That’s all there is to it! Docker Swarm can easily and quickly scale your application’s services up and down as needs require. Again, in many situations you would probably want to automate this rather than manually scaling, which is pretty easy through the Docker APIs. You also have the option to swap out the built-in load balancer for something with additional controls, like an &lt;a href=&quot;https://store.docker.com/images/f5networks-asp&quot;&gt;F5&lt;/a&gt; or &lt;a href=&quot;https://store.docker.com/images/netscaler-cpx-express&quot;&gt;Citrix NetScaler&lt;/a&gt; or some other software you prefer.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Using only a couple of commands enables you to deploy a stack of services using Docker Swarm Mode to orchestrate the entire stack, all maintained in the simple, human readable Docker Compose file format.&lt;/p&gt;

&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;/h2&gt;
&lt;p&gt;Hopefully these first few labs have given you some familiarity with Docker, containers, and Docker Swarm Mode orchestration. We encourage you to keep Playing With Docker to learn more. There are several things you can do to continue your learning:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Keep learning right here in the Play With Docker classroom with &lt;a href=&quot;/ops-landing&quot;&gt;Phase 2 and Phase 3&lt;/a&gt; of the Docker for IT Pros and System Administrators learning path.
    &lt;ul&gt;
      &lt;li&gt;In Phase 2 you will learn more about orchestration, security, and networking&lt;/li&gt;
      &lt;li&gt;Phase 3 will bring it all together as you implement a proof-of-concept application&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Run the labs locally on your machine. You can download the free &lt;a href=&quot;https://www.docker.com/docker-mac&quot;&gt;Docker for Mac&lt;/a&gt; or &lt;a href=&quot;https://www.docker.com/docker-windows&quot;&gt;Docker for Windows&lt;/a&gt; clients and run some of these labs right on your own machine.
    &lt;ul&gt;
      &lt;li&gt;You can also sign up for a free &lt;a href=&quot;https://store.docker.com/signup?next=%2F&quot;&gt;Docker Store&lt;/a&gt; account. You get a free Docker repository of your very own so that you can try taking the images we created in these labs and storing them in the community hub.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes:&lt;/h2&gt;

&lt;p&gt;&lt;b id=&quot;fn-compose&quot;&gt;1&lt;/b&gt;: If you want to go through a lab using &lt;em&gt;Docker Compose&lt;/em&gt; the &lt;a href=&quot;/orchestration-workshop-part1&quot;&gt;Orchestration, Part 1&lt;/a&gt; lab on play-with-docker will guide you through the basics.&lt;a href=&quot;#a1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;b id=&quot;fn-network&quot;&gt;2&lt;/b&gt;: Networking is an important concept and there are many more details available on &lt;a href=&quot;https://docs.docker.com/engine/userguide/networking/&quot;&gt;Docker Docs&lt;/a&gt; &lt;a href=&quot;#a2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;

&lt;p class=&quot;quiz&quot;&gt;What is a stack?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;(x) a multi-service app running on a Swarm&lt;/li&gt;
  &lt;li&gt;( ) a way of creating multiple nodes&lt;/li&gt;
  &lt;li&gt;( ) a method of using multiple compose files to run an app&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;quiz&quot;&gt;A stack can:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;(x) be deployed from the commandline&lt;/li&gt;
  &lt;li&gt;(x) use the compose file format to deploy&lt;/li&gt;
  &lt;li&gt;( ) run a Dockerfile&lt;/li&gt;
  &lt;li&gt;( ) be used to manage your hosts&lt;/li&gt;
  &lt;li&gt;(x) be used to manage services over multiple nodes&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 12 Sep 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/ops-s1-swarm-intro/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/ops-s1-swarm-intro/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker for IT Pros and System Administrators</title>
        <description>&lt;p&gt;This self-paced learning journey is designed for IT Pros and System Administrators and will introduce you to Docker containers and the ways IT organizations like yours have started using Docker in their environment. We’ve designed three stages to guide you through this journey:&lt;/p&gt;

&lt;h2 id=&quot;stage-1-the-basics&quot;&gt;&lt;a href=&quot;/ops-stage1&quot;&gt;Stage 1: The Basics&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;This stage will&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Get you familiar with the core concepts of Docker&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Help you understand the fundamental value proposition for Docker&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Help you see how Docker can help your organization&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;!-- I&apos;d suggest hiding stages 2 &amp; 3 for now...maybe we list them with no links as a preview of what&apos;s to come --&gt;
&lt;h2 id=&quot;stage-2-digging-deeper&quot;&gt;&lt;a href=&quot;/ops-stage2&quot;&gt;Stage 2: Digging Deeper&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;This stage will help you&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Understand the architecture of Docker, and the core features&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Understand how to integrate Docker into your existing application infrastructure&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Develop a proof of concept application deployment&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;stage-3-moving-to-production&quot;&gt;&lt;a href=&quot;/ops-stage3&quot;&gt;Stage 3: Moving to Production&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;This final stage will help you&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Prepare to implement a full proof of concept application&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Develop a strategy for integrating Docker into your existing production 
  environment&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Become recognized as a leader in your organization on implementing Docker&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 25 Jul 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/ops-landing/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/ops-landing/</guid>
        
        <category>windows</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
      </item>
    
      <item>
        <title>Reducing nodejs Docker images size by %50 using multi-stage builds and Zeit pkg.</title>
        <description>&lt;p&gt;This is a followup interactive tutorial from this &lt;a href=&quot;https://medium.com/@marcosnils/reducing-nodejs-docker-images-size-by-47-using-multi-sage-builds-and-zeit-pkg-360ab8b6c6d2&quot;&gt;blogpost&lt;/a&gt;
to reduce docker nodejs images by 50% using Zeit &lt;a href=&quot;https://github.com/zeit/pkg&quot;&gt;pkg&lt;/a&gt; and docker multi-stage builds.&lt;/p&gt;

&lt;h2 id=&quot;the-traditional-way&quot;&gt;The traditional way&lt;/h2&gt;

&lt;p&gt;Before jumping into the magic trick, we’ll build a nodejs docker image as we would do traditionally to show the
space that we’re saving by using this new approach.&lt;/p&gt;

&lt;p&gt;Let’s create a simple node application:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &apos;console.log(&quot;Hello, pkg&quot;)&apos; | tee index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We’ll now package it using a very basic dockerfile&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &apos;FROM node:boron-slim
COPY . /app
CMD [&quot;node&quot;, &quot;/app/index.js&quot;] &apos; | tee Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, we’ll build our image and check it’s size:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker build -t myapp .
docker image ls | grep myapp
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can see that our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myapp&lt;/code&gt; is 200+MB&lt;/p&gt;

&lt;h2 id=&quot;using-pkg-and-multi-stage-builds&quot;&gt;Using PKG and multi-stage builds&lt;/h2&gt;

&lt;p&gt;Let’s create now a new Dockerfile that bundles our app with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pkg&lt;/code&gt; and uses multi-stage builds to package
it in a smaller docker image&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &apos;FROM node:boron
RUN npm install -g pkg pkg-fetch

ENV NODE node6
ENV PLATFORM linux
ENV ARCH x64

RUN /usr/local/bin/pkg-fetch ${NODE} ${PLATFORM} ${ARCH}

COPY . /app
WORKDIR /app
RUN /usr/local/bin/pkg --targets ${NODE}-${PLATFORM}-${ARCH} index.js

FROM debian:jessie-slim
COPY --from=0  /app/index /
CMD [&quot;/index&quot;]&apos; | tee Dockerfile-pkg
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We’ll build our app the same way as before but using our new Dockerfile:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker build -t myapp_pkg -f Dockerfile-pkg .
docker image ls | grep myapp_pkg
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, our new docker image is around 100MB and it works as expected:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run myapp_pkg
&lt;/code&gt;&lt;/pre&gt;

</description>
        <pubDate>Thu, 04 May 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/node-zeit-pkg/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/node-zeit-pkg/</guid>
        
        <category>linux</category>
        
        <category>development</category>
        
        <category>nodejs</category>
        
        <category>community</category>
        
        
        <category>community</category>
        
      </item>
    
      <item>
        <title>Service Discovery under Docker Swarm Mode</title>
        <description>&lt;p&gt;Service Discovery under Swarm Mode.&lt;/p&gt;

&lt;h2 id=&quot;purpose&quot;&gt;Purpose&lt;/h2&gt;

&lt;p&gt;The purpose of this lab is to illustrate how Service Discovery works under Swarm Mode.&lt;/p&gt;

&lt;h2 id=&quot;the-application&quot;&gt;The application&lt;/h2&gt;

&lt;p&gt;WordPress is an open-source content management system (CMS) based on PHP and MySQL. 
It is a very simple containers application often used for demo purposes during meetup and conferences.&lt;/p&gt;

&lt;h2 id=&quot;init-your-swarm&quot;&gt;Init your swarm&lt;/h2&gt;

&lt;p&gt;Let’s create a Docker Swarm first. Open up the first instance and initiate Swarm mode cluster.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr $(hostname -i)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This node becomes a master node. The output displays a command to add a worker node to this swarm as shown below:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Swarm initialized: current node (xf323rkhg80qy2pywkjkxqusp) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-089phhmfamjor1o1qj8s0l4wdhyvegphg6vtt9p3s8c35upltk-eecvhhtz1f2vpjhvc70v6v
vzb \
    10.0.50.3:2377

To add a manager to this swarm, run &apos;docker swarm join-token manager&apos; and follow the instructi
ons.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above token ID is unique for every swarm mode cluster and hence might differ for your setup.
From the output above, copy the join command (&lt;em&gt;watch out for newlines&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Next, Open up the new instance and paste the below command. This should join the new node to the swarm mode cluster and this new node becomes a worker node. In my case, the command would look something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; docker swarm join \
    --token SWMTKN-1-089phhmfamjor1o1qj8s0l4wdhyvegphg6vtt9p3s8c35upltk-eecvhhtz1f2vpjhvc70v6v
vzb \
    10.0.50.3:2377
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ docker swarm join --token SWMTKN-1-089phhmfamjor1o1qj8s0l4wdhyvegphg6vtt9p3s8c35upltk-eecvhh
tz1f2vpjhvc70v6vvzb 10.0.50.3:2377
This node joined a swarm as a worker.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;show-members-of-swarm&quot;&gt;Show members of swarm&lt;/h2&gt;

&lt;p&gt;Type the below command in the first terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output shows you both the manager and worker node indicating 2-node cluster:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
xf323rkhg80qy2pywkjkxqusp *  node1     Ready   Active        Leader
za75md1p0hpc2qswefj8uyktk    node2     Ready   Active
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you try to execute an administrative command in a non-leader node &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker&lt;/code&gt;, you’ll get an error. Try it here:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;create-an-overlay-network&quot;&gt;Create an overlay network&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network create -d overlay net1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above command generates an ID:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;4md6wyy0pdpdzku6dj2z7yxjf
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;list-out-the-newly-created-overlay-network-using-the-below-command&quot;&gt;List out the newly created overlay network using the below command:&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output should show the newly added network called “net1” holding swarm scope .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NETWORK ID          NAME                DRIVER              SCOPE
c30f13d9c242        bridge              bridge              local
990fa0ad6ab6        docker_gwbridge     bridge              local
c60123ff7abf        host                host                local
v7sp7ev6xfoo        ingress             overlay             swarm
4md6wyy0pdpd        net1                overlay             swarm
333c7d045239        none                null
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;creating-mysql-service&quot;&gt;Creating MYSQL service&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service create \
           --replicas 1 \
           --name wordpressdb \
           --network net1 \
           --env MYSQL_ROOT_PASSWORD=mysql123 \
           --env MYSQL_DATABASE=wordpress \
          mysql:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above command creates a service named “wordpressdb” which belongs to “net1” network which runs a single replica of the container. It displays service ID as an output as shown:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ip9a8zl9rke256q92itgrm8ov
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run the below command to list out the service:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ls 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output should be like the following one (your ID will display different though).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                  NAME                MODE                REPLICAS            IMAGE
ip9a8zl9rke2        wordpressdb         replicated          1/1                 mysql:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s list the tasks of the wordpressdb service.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps wordpressdb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should get an output like the following one where the 1 task  of the service are listed.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                  NAME                IMAGE               NODE                DESIRED STATE
      CURRENT STATE                ERROR               PORTS
puoe9lvfkcia        wordpressdb.1       mysql:latest        node1               Running
      Running about a minute ago
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;creating-wordpress-service&quot;&gt;Creating WordPress service&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service create \
           --replicas 4 \
           --name wordpressapp \
           --network net1 \
           --env WORDPRESS_DB_HOST=wordpressdb \
           --env WORDPRESS_DB_PASSWORD=mysql123 \
          wordpress:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above command creates a service named “wordpressapp” which belongs to “net1” network which runs 4 copies of wordpressapp container.
As output, this command displays a service ID as:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;m4hca6rliz8wer2aojayv01r5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Listing out the services:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                  NAME                MODE                REPLICAS            IMAGE
ID                  NAME                MODE                REPLICAS            IMAGE
ip9a8zl9rke2        wordpressdb         replicated          1/1                 mysql:latest
m4hca6rliz8w        wordpressapp        replicated          4/4                 wordpress:late
st
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can list the tasks of the wordpressapp service using the command:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps wordpressapp
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                  NAME                IMAGE               NODE                DESIRED STATE
      CURRENT STATE                ERROR               PORTS
zg7wpvs1rbki        wordpressapp.1      wordpress:latest    node2               Running
      Running 58 seconds ago
8rybe5m4urik        wordpressapp.2      wordpress:latest    node1               Running
      Running about a minute ago
scia4v5i1znj        wordpressapp.3      wordpress:latest    node2               Running
      Running 58 seconds ago
4avyixggcb8n        wordpressapp.4      wordpress:latest    node1               Running
      Running about a minute ago
      
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;service-discovery&quot;&gt;Service Discovery&lt;/h3&gt;

&lt;p&gt;Let us try to discover wordpressdb service from within one of wordpressapp container. Open up the manager node instance and run the below command:&lt;/p&gt;

&lt;p&gt;Open up instance of worker node and verify what containers are running:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This should display number of tasks(containers) running on the worker node locally:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS
           PORTS               NAMES
52f16028e12c        wordpress:latest    &quot;docker-entrypoint...&quot;   2 minutes ago       Up 2 minu
tes        80/tcp              wordpressapp.1.zg7wpvs1rbkiy4zwo71yk031i
f3271e89d54e        wordpress:latest    &quot;docker-entrypoint...&quot;   2 minutes ago       Up 2 minu
tes        80/tcp              wordpressapp.3.scia4v5i1znj378gujluad2ku

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As shown above, there are 2 instances of wordpressapp task(container) running on the worker node.&lt;/p&gt;

&lt;p&gt;Now, Open up manager node and confirm what task are running:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS
           PORTS               NAMES
b68d99cad3da        wordpress:latest    &quot;docker-entrypoint...&quot;   5 minutes ago       Up 4 minu
tes        80/tcp              wordpressapp.2.8rybe5m4urikqsqje6hcpou9t
657cff3e37d5        wordpress:latest    &quot;docker-entrypoint...&quot;   5 minutes ago       Up 4 minu
tes        80/tcp              wordpressapp.4.4avyixggcb8neej1h395ognt2
e71c164c36b3        mysql:latest        &quot;docker-entrypoint...&quot;   10 minutes ago      Up 10 min
utes       3306/tcp            wordpressdb.1.puoe9lvfkciavkrzrkbrhrl6e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As we notice, there are 2 instances of wordpressapp task(container) running on the manager node(shown above) and 1 instance of wordpressdb.&lt;/p&gt;

&lt;p&gt;Let’s pick up the wordpressdb task running on the manager node and try to reach out to wordpressapp running on the remote worker node. Because the container is missing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping&lt;/code&gt; command we need to install it first:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker exec -it e71 bash -c &quot;apt update &amp;amp;&amp;amp; apt -y install iputils-ping&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once installed, we can ping wordpressapp as shown below:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker exec -it e71 ping wordpressapp
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This should work successfully and be able to ping the wordpressapp as service name.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PING wordpressapp (10.0.0.4): 56 data bytes
64 bytes from 10.0.0.4: icmp_seq=0 ttl=64 time=0.052 ms
^C--- wordpressapp ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.052/0.052/0.052/0.000 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let us try to reach out to remote wordpressapp container from one of the wordpressdb instance running on the worker node by its hostname:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker exec -it e71 ping wordpressapp.3.scia4v5i1znj378gujluad2ku
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PING wordpressapp.3.scia4v5i1znj378gujluad2ku (10.0.0.5): 56 data bytes
64 bytes from 10.0.0.5: icmp_seq=0 ttl=64 time=6.175 ms
64 bytes from 10.0.0.5: icmp_seq=1 ttl=64 time=0.131 ms
^C--- wordpressapp.3.scia4v5i1znj378gujluad2ku ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.131/3.153/6.175/3.022 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Voila ! We are able to ping wordpressapp service from container(running wordpresdb task) using the service name.Also, we were successful in reaching out to remote wordpressapp container using its hostname from one of wordpressdb container running in maanager node.&lt;/p&gt;

&lt;p class=&quot;quiz&quot;&gt;What features are available under Docker Swarm Mode:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;Service Discovery&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;Routing Mesh&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;Load Balancing&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;Orchestration&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;All of the above&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 12 Apr 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/swarm-service-discovery/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/swarm-service-discovery/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>swarm</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Docker volume sshfs</title>
        <description>&lt;h2 id=&quot;docker-volume-plugins&quot;&gt;Docker volume plugins&lt;/h2&gt;

&lt;p&gt;Docker Engine volume plugins enable Engine deployments to be integrated with external storage systems such as Amazon EBS, and enable data volumes to persist beyond the lifetime of a single Docker host. See &lt;a href=&quot;https://docs.docker.com/engine/extend/legacy_plugins/&quot;&gt;the plugin documentation&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h2 id=&quot;setting-up&quot;&gt;Setting up&lt;/h2&gt;

&lt;p&gt;First we install openssh and configure test user with password &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testpassword&lt;/code&gt; in the second node&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;apk add --no-cache openssh
adduser -D test
echo &quot;test:testpassword&quot; | chpasswd
ssh-keygen -N &quot;&quot; -t rsa -f /etc/ssh/ssh_host_rsa_key
/usr/sbin/sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can safely ignore any &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Could not load host key&lt;/code&gt; error messages.&lt;/p&gt;

&lt;p&gt;Now, let’s install the plugin and create a docker volume with it&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;mkdir /var/lib/docker/plugins # Shouldn&apos;t have to do this if graph folder is somewhere else
docker plugin install --grant-all-permissions vieux/sshfs
docker volume create -d vieux/sshfs -o sshcmd=test@node2:/home/test -o password=testpassword sshvolume
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can now use the plugin volume in a container as usual&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -it -v sshvolume:/test alpine sh -c &apos;echo &quot;Hello world&quot; &amp;gt; /test/somefile&apos;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now we check that the file has been created in the second node&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;cat /home/test/somefile
&lt;/code&gt;&lt;/pre&gt;

</description>
        <pubDate>Fri, 07 Apr 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/docker-volume-sshfs/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/docker-volume-sshfs/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>plugins</category>
        
        <category>volumes</category>
        
        <category>community</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Swarm synchronous services</title>
        <description>&lt;h2 id=&quot;synchronous-service-create-and-service-update&quot;&gt;Synchronous service create and service update&lt;/h2&gt;

&lt;p&gt;A nice &lt;a href=&quot;https://github.com/docker/docker/pull/31144#issuecomment-291354685&quot;&gt;PATCH&lt;/a&gt; has been merged into Docker a few minutes ago that allows
service creations and updated to be executed synchronously.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you don’t see the progress bars in the terminal on the right, try resizing the pane to make the term bigger.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;creating-a-synchronous-service&quot;&gt;Creating a synchronous service&lt;/h2&gt;

&lt;p&gt;Initialize your swarm&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr eth1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create a new synchronous serivce using the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-d&lt;/code&gt; flag&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service create -d=false --name top --replicas 5 busybox top
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should an output similar to the following in the terminal:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mmsdrpbigre7ls9vp6mhig3vz
overall progress: 5 out of 5 tasks
1/5: running   [==================================================&amp;gt;]
2/5: running   [==================================================&amp;gt;]
3/5: running   [==================================================&amp;gt;]
4/5: running   [==================================================&amp;gt;]
5/5: running   [==================================================&amp;gt;]
verify: Waiting 1 seconds to verify that tasks are stable...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see, a nice progress bar will display the status of the overall deployment.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you press Ctrl+C while the service is being created, it will be sent to background automatically&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We’ll check how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;update&lt;/code&gt; also works with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--detach&lt;/code&gt; paramter now.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service update -d=false --force --update-parallelism 0 top
&lt;/code&gt;&lt;/pre&gt;
</description>
        <pubDate>Mon, 03 Apr 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/synchronous-services/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/synchronous-services/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>swarm</category>
        
        <category>community</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>WebApps with Docker Flow Proxy</title>
        <description>&lt;h3 id=&quot;before-anything-how-to-use-this-course&quot;&gt;Before anything How to use this course&lt;/h3&gt;

&lt;p&gt;Please, validate the Google capcha to activate the shell on the right.
Then, you can either copy the commands yourself, or simply click on the grey boxes to automatically copy commands into the terminal.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &apos;execute command on node1!!&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;echo &apos;execute command on node2!!&apos;
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;Please note that this platform is not secure and you should not store personal datas&lt;br /&gt;
the instance will be removed after few hours&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;predictive-load-balancing-name-using-docker-flow-proxy&quot;&gt;Predictive Load-balancing name using Docker Flow Proxy&lt;/h2&gt;

&lt;p&gt;In this course, we will leverage the power of Docker Swarm Mode, released with Docker 1.13, and the great features of vfarcic &lt;strong&gt;&lt;a href=&quot;http://proxy.dockerflow.com/swarm-mode-stack/&quot;&gt;Docker Flow Proxy&lt;/a&gt;&lt;/strong&gt; which provide an easy way to reconfigure proxy every time a new service is deployed, or when a service is scaled. It uses docker &lt;strong&gt;service labels&lt;/strong&gt; to define the metadata and rules for its dynamically-configured routing rules to send traffic from the PRoxy to real applications (regardless of the host they are within a Docker Swarm Cluster).&lt;/p&gt;

&lt;p&gt;Docker Flow Proxy is composed on two parts :&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/vfarcic/docker-flow-swarm-listener&quot;&gt;swarm-listener&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/vfarcic/docker-flow-proxy&quot;&gt;Docker Flow: Proxy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The purpose of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swarm-listener&lt;/code&gt; is to monitore swarm services (add, remove, scale..) and to send requests to the proxy whenever a service is created or destroyed.
It must be running on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Swarm Manager&lt;/code&gt; and will queries Docker API in search for newly created services.&lt;/p&gt;

&lt;p&gt;It uses docker &lt;strong&gt;service’s labels&lt;/strong&gt; (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.df.*&lt;/code&gt;) to define the metadata and rules for dynamically configure routing rules of the Proxy.&lt;/p&gt;

&lt;h3 id=&quot;first-we-will-enable-the-swarm-mode&quot;&gt;First we will enable the Swarm mode&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;In this tutorial, we will only use a 2 node swarm cluster, but it will work exactly the same way with more nodes!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr=$(hostname -i)
docker swarm join-token manager
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;Copy the join command output and paste it in the other terminal to form a 2 node swarm cluster.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;show-members-of-the-swarm&quot;&gt;show members of the swarm&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you correctly execute, the above command, you must see 2 nodes:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
$ docker node ls
ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
7p167ggf1wi3ox52z8ga2myu6 *  node1     Ready   Active        Leader
og1irjjh2fjtwt7dko7ht0qnq    node2     Ready   Active        Reachable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;create-docker-flow-proxy-docker-containers&quot;&gt;Create Docker Flow Proxy Docker Containers&lt;/h3&gt;

&lt;p&gt;We will start by creating a Docker Compose file named proxy.yml, which will defines our 2 services &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swarm-listener&lt;/code&gt; of our Docker Flow Proxy stack :&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You can Click on the grey box to automatically copy the content on the terminal (don’t mess with the order of commands ;) )&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat &amp;lt;&amp;lt;EOF &amp;gt; proxy.yml
version: &quot;3&quot;

services:


  proxy:
    image: vfarcic/docker-flow-proxy
    ports:
      - &quot;80:80&quot;
      - &quot;443:443&quot;
      - &quot;8080:8080&quot;
    environment:
      - LISTENER_ADDRESS=swarm-listener
      - MODE=swarm
    networks:
      - public
    deploy:
      replicas: 2
      restart_policy:
        condition: on-failure


  swarm-listener:
    image: vfarcic/docker-flow-swarm-listener
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - public
    environment:
      - DF_NOTIFY_CREATE_SERVICE_URL=http://proxy:8080/v1/docker-flow-proxy/reconfigure
      - DF_NOTIFY_REMOVE_SERVICE_URL=http://proxy:8080/v1/docker-flow-proxy/remove
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]		                                                   
      restart_policy:
        condition: on-failure
      
networks:
  public:
    driver: overlay
    ipam:
      driver: default
      config:
      - subnet: 10.1.0.0/24

EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;ul&gt;
  &lt;li&gt;We are using version 3 of compose file (mandatory for docker stack deploy)&lt;/li&gt;
  &lt;li&gt;We are using image from vfarcic on docker Hub&lt;/li&gt;
  &lt;li&gt;Docker will create an overlay networks named &lt;strong&gt;public&lt;/strong&gt;, on which will will add each container we want to publish&lt;/li&gt;
  &lt;li&gt;We uses constraints to deploy the swarm-listener service on a swarm manager (as it needs to listen to swarm events)&lt;/li&gt;
  &lt;li&gt;We gives the proxy service the address of the swarm-listener&lt;/li&gt;
  &lt;li&gt;We gives the swarm-listener 2 API endpoint to reconfigure the Proxy through environment variables.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DF_NOTIFY_*&lt;/code&gt; environments variables defines the url of the Proxy API for reconfiguration.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;launch-the-docker-containers&quot;&gt;Launch the Docker Containers&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy proxy --compose-file proxy.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The proxy container is configured to listen on port 80 and 443 for the standard HTTP traffic, and will listen privately on the internal network on port 8080 for the reconfiguration API requests.&lt;/p&gt;

&lt;h3 id=&quot;check-docker-networks&quot;&gt;Check docker networks&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should see that a network named &lt;strong&gt;proxy_public&lt;/strong&gt; has been created with Driver &lt;strong&gt;overlay&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Later If we want others containers to be able to be accessible through the proxy load balancer we will need to &lt;strong&gt;attached them&lt;/strong&gt; to this network.&lt;/p&gt;

&lt;h3 id=&quot;see-your-docker-swarm-stack&quot;&gt;See Your Docker Swarm Stack&lt;/h3&gt;

&lt;p&gt;List all your deployed stacks, and view detailed on a specific stack&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack ls
docker stack ps proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We must have 2 Proxy Running and 1 swarm-listener Running&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Since we have set 2 replicas for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;proxy&lt;/code&gt; service it will be deployed on both nodes while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swarm-listener&lt;/code&gt; must be on one manager node&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;view-logs-of-our-proxy-service&quot;&gt;View logs of our Proxy service&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service logs --tail=10 proxy_proxy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View logs of our swarm-listener service&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service logs --tail=10 proxy_swarm-listener
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;scaling-the-proxy-service&quot;&gt;Scaling the Proxy service&lt;/h4&gt;

&lt;p&gt;Normally, creating a new instance of the proxy service, means that it will starts without any state, as a result, the new instances would not have any knowledge of our already deployed services.
Fortunately docker-flow provides an environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LISTEN_ADDRESS=swarm-listener&lt;/code&gt; which tells the proxy the adress of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swarm-listener&lt;/code&gt; to resend notifications for all the services. As a result, each proxy instance will soon have the same state as the other :)&lt;/p&gt;

&lt;h3 id=&quot;deploy-our-first-service-and-connect-it-to-the-docker-flow-proxy&quot;&gt;Deploy our first service and connect it to the Docker Flow Proxy&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;swarm-listener&lt;/strong&gt; service is listening to &lt;strong&gt;docker swarm events&lt;/strong&gt; informations, and will reconfigure the &lt;strong&gt;proxy&lt;/strong&gt; service based on the service’s metadatas, we need to configure thoses metadata as docker service labels:&lt;/p&gt;

&lt;h4 id=&quot;configure-service-with-routing-based-on-url-path&quot;&gt;Configure service with routing based on URL Path&lt;/h4&gt;

&lt;p&gt;We can set a label to inform the proxy to route the traffic according to the target service URI Path using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.df.*&lt;/code&gt; rules labels:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat &amp;lt;&amp;lt;EOF &amp;gt; http.yml
version: &quot;3&quot;

services:
  http:
    image: emilevauge/whoami
    networks:
      - proxy_public
    deploy:
      replicas: 3
      labels:
        - com.df.notify=true
        - com.df.distribute=true
        - com.df.servicePath=/http/
        - com.df.reqPathSearch=/http/
        - com.df.reqPathReplace=/renamed/
        - com.df.port=80

networks:
  proxy_public:
    external: true

EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;!!Note: Because we are working with Docker Swarm Mode, labels must be set at the &lt;strong&gt;service&lt;/strong&gt; level in the &lt;strong&gt;deploy&lt;/strong&gt; section, instead of at &lt;strong&gt;container&lt;/strong&gt; level!!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notify&lt;/code&gt; label ask &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;swarm-listener&lt;/code&gt; to re-configure &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Flow Proxy&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;distribute&lt;/code&gt; label means that reconfiguration should be applied to all Proxy instances.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;We are using Docker 1.13 networking features (routing mesh, and VIP) So that Docker takes care of load balancing on all instances of our service, and so that there is no need to reconfigure the proxy every time a new instance is deployed. (We configure our docker’s VIP service IP adress in the proxy, so 1 IP per service)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;launch-the-container&quot;&gt;launch the container&lt;/h4&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy http --compose-file http.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The proxy container should have been attached the &lt;strong&gt;proxy_public&lt;/strong&gt; network, which we can verify by inspecting the network:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network inspect proxy_public
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;check-service-status&quot;&gt;check Service status&lt;/h4&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack ps http
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;request-the-service&quot;&gt;Request the service&lt;/h4&gt;

&lt;blockquote&gt;
  &lt;p&gt;We have defined that our service will be receive the request if an incoming request starts with the path &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/http&lt;/code&gt;. This was done using the rule in the service’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;com.df.servicePath=/http&lt;/code&gt; label&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We may now be able to reach our service from any host :
from node1&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl http://localhost/http/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;from node2&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;curl http://localhost/http/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We should see a response that was generated by the service. The Url on the service site may have been rewritten (see the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/rewrited/&lt;/code&gt; in the GET parameter)&lt;/p&gt;

&lt;p&gt;You can request the service in your Browser:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/http/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;Link to http service&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can request the logs of the Proxy Load Balancer:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker service logs --tail=10 -f proxy_proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can request the logs of the application&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service logs --tail=10 http_http
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;scaling-service&quot;&gt;Scaling Service&lt;/h3&gt;

&lt;p&gt;Swarm is continuously monitoring containers health. If one of them fails, it will redeployed to one of available nodes. If a whole node fails, or if we ask to drain all containers out of a node for maintenance, Swarm will recreate all the containers that were running on that node.
In production we need to reach zero down time, and so to guarentee our nodes will be available, we need to scale our services, so that we have many instances of our service running on severals nodes. That way, while we are waiting for one instance to recuperate from a failure, others can take over the load.&lt;/p&gt;

&lt;p&gt;We can use docker swarm to scale the services of our applications: Exemple, scale our http service to use 5 instances:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service scale http_http=5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Check that you have 5 instances of the service :&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps http_http
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can make local calls to the http service and see the loadbalancing :&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl http://localhost/http/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;On every request, it’s a different docker container that will respond!&lt;/p&gt;

&lt;h3 id=&quot;retrieve-proxy-configuration&quot;&gt;Retrieve Proxy Configuration&lt;/h3&gt;

&lt;p&gt;If we have activated the admin port (8080), then we can request the proxy to retrieve the configuration
Docker Flow Proxy is base on HAProxy so what we retrieve here is the HAProxy configuration&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl http://localhost:8080/v1/docker-flow-proxy/config
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;deploy-a-microservice-application&quot;&gt;Deploy a Microservice Application&lt;/h2&gt;

&lt;p&gt;We have see how we can leverage Docker labels to dynamically customize our LoadBalancing routing rules, and how docker-compose can be used to create and link services together.&lt;/p&gt;

&lt;p&gt;Now let’s try to launch a &lt;strong&gt;more complicated&lt;/strong&gt; Microservice application.&lt;/p&gt;

&lt;p&gt;We will uses &lt;strong&gt;Docker’s vote&lt;/strong&gt; microservice application with custom labels to be used within our Docker Flow Proxy loadbalancer.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://github.com/allamand/example-voting-app/raw/master/proxy_voting.png&quot; width=&quot;600&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The voting application is composed of :&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A Python webapp which lets you vote between two options&lt;/li&gt;
  &lt;li&gt;A Redis queue which collects new votes&lt;/li&gt;
  &lt;li&gt;A Java worker which consumes votes and stores them in…&lt;/li&gt;
  &lt;li&gt;A Postgres database backed by a Docker volume&lt;/li&gt;
  &lt;li&gt;A Node.js webapp which shows the results of the voting in real time&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;run-voting-microservice-application&quot;&gt;Run voting microservice application&lt;/h3&gt;

&lt;p&gt;First you need to Retrieve voting-app application&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git clone https://github.com/allamand/example-voting-app.git
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Go to the stack directory&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cd example-voting-app
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and launch the app using docker-compose file, you can view the &lt;strong&gt;docker-compose-flow-proxy.yml&lt;/strong&gt; file&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy cloud -c docker-compose-flow-proxy.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;This command will build each part of the microservice from sources.
It may take a little time to get all services up &amp;amp; running (time to download images..)
You can take a coffee since this may take a little to finish ;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;rewriting-paths&quot;&gt;Rewriting Paths&lt;/h4&gt;

&lt;p&gt;In this example, we need the incoming requests that starts with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/vote/&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/result/&lt;/code&gt; to be routed to the according services by the proxy.
But each of our service needs traffic to be send on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt;, so we need the Proxy to &lt;strong&gt;rewrite&lt;/strong&gt; the Path while sending the request.&lt;/p&gt;

&lt;p&gt;For that we are using specific docker-flow labels &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqPathSearch&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;reqPathReplace&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;      labels:
        - com.df.notify=true
        - com.df.distribute=true
        - com.df.servicePath=/vote/
        - com.df.reqPathSearch=/vote/
        - com.df.reqPathReplace=/
        - com.df.port=80

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To monitor the setup state, you can use:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack ps cloud
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;Be carreful, the Output shows two state columns :&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;strong&gt;Desired State&lt;/strong&gt; which represents what you are asking to swarm&lt;/li&gt;
    &lt;li&gt;&lt;strong&gt;Current State&lt;/strong&gt; which is the current state of the container (which may be stuck in Preparing for a moment while downloading the images).&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once all containers are in the &lt;strong&gt;Running&lt;/strong&gt; state, you can start test the application.&lt;/p&gt;

&lt;p&gt;While the application is working you can take a look at the docker-compose file we are deploying :&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat docker-compose-flow-proxy.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can view the updated configuration on the proxy API&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl http://localhost:8080/v1/docker-flow-proxy/config
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;you-can-now-make-your-vote&quot;&gt;You can now make your Vote!!&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/vote/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;Link to vote service&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;and-see-the-results-of-votes&quot;&gt;And See the results of votes&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/result/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;Link to result service&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the logs of the services :&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service logs --tail=10 cloud_vote
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;You are now able to deploy any stack on Docker Swarm Mode using docker-compose and &lt;strong&gt;Docker Flow Proxy&lt;/strong&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;bonus&quot;&gt;Bonus&lt;/h2&gt;

&lt;p&gt;We can add a Swarm visualizer service :&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat &amp;lt;&amp;lt;EOF &amp;gt; visualizer.yml
version: &quot;3&quot;

services:
  visu:
    image: dockersamples/visualizer
    volumes:
      - &quot;/var/run/docker.sock:/var/run/docker.sock&quot;
    networks:
      - proxy_public
    ports:
      - 81:8080
    deploy:
      replicas: 1
      placement:
        constraints: [node.role == manager]		                                                   
      restart_policy:
        condition: on-failure

networks:
  proxy_public:
    external: true

EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;launch-the-container-1&quot;&gt;launch the container&lt;/h4&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy visu --compose-file visualizer.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps visu_visu
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
  &lt;p&gt;wait few second for the service to start&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can now target directly the port 81 of our swarm cluster and docker will direclty reach our Visualizer service&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;81&quot;&gt;Link to Visualizer service&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This should be something like :&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/visualizer.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;free-resources&quot;&gt;Free resources&lt;/h2&gt;

&lt;p&gt;When you are finished with this tutorial, please free the unused resources:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack rm cloud
docker stack rm visu
docker stack rm http
docker stack rm proxy
&lt;/code&gt;&lt;/pre&gt;

</description>
        <pubDate>Sat, 01 Apr 2017 05:21:47 +0000</pubDate>
        <link>https://training.play-with-docker.com/docker-flow-proxy/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/docker-flow-proxy/</guid>
        
        <category>docker</category>
        
        <category>operations</category>
        
        <category>community</category>
        
        
        <category>community</category>
        
      </item>
    
      <item>
        <title>Multi-stage builds</title>
        <description>&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;This tutorial will need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;master&lt;/code&gt; build of Docker, which is available on Play With Docker.&lt;/p&gt;

&lt;h2 id=&quot;multi-staged-builds&quot;&gt;Multi-staged builds&lt;/h2&gt;

&lt;p&gt;A common pipe-line for building applications in Docker involves adding SDKs and runtimes, followed by adding code and building it. The most efficient way to get a small image tends to be to use 2-3 Dockerfiles with different filenames where each one takes the output of the last. This is referred to as the &lt;em&gt;Builder pattern&lt;/em&gt; in the Docker community.&lt;/p&gt;

&lt;p&gt;This lab explores a new bleeding-edge feature called Multi-stage builds. It is not yet released into a Docker version, but when it is available on the Docker Hub/Cloud and for all the Docker editions it will mean we can use a single Dockerfile with multiple stages instead of the &lt;em&gt;Builder pattern&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let’s build a simple Golang application which counts internal/external facing anchor tags to help us come up with an SEO rating.&lt;/p&gt;

&lt;p&gt;Let’s try out the href-counter Docker image from the hub, then look at how to re-build from the Github repository:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -e url=https://news.ycombinator.com alexellis2/href-counter
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{&quot;internal&quot;:197,&quot;external&quot;:32}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You get a JSON object returned giving the total amount of internal vs external links.&lt;/p&gt;

&lt;p&gt;Let’s clone the source:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git clone https://github.com/alexellis/href-counter
cd href-counter
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Cloning into &apos;href-counter&apos;...
remote: Counting objects: 24, done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 24 (delta 7), reused 12 (delta 1), pack-reused 0
Unpacking objects: 100% (24/24), done.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-old-way-of-doing-things&quot;&gt;The old way of doing things&lt;/h2&gt;

&lt;p&gt;Let’s build the Docker image with all the Golang toolchain and see how big the image comes out as:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker build -t alexellis2/href-counter:sdk . -f Dockerfile.build
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now check the size of the image:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker images |grep href-counter
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker images |grep href-counter
href-counter        sdk                 131c782e8c35        30 second
s ago      692MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker history&lt;/code&gt; command will show you that the layers we added during the build are only a small part of the resulting image (about 20MB +/-):&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker history alexellis2/href-counter:sdk |head -n 4
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;IMAGE               CREATED             CREATED BY
                   SIZE                COMMENT
f8b1953fb9c7        1 second ago        /bin/sh -c CGO_ENABLED=0 GOOS=linux go bui...   5.64MB
5d24895500e8        9 seconds ago       /bin/sh -c #(nop) COPY file:d3eec1f1fefbec...   1.71kB
d83dc0785057        9 seconds ago       /bin/sh -c go get -d -v golang.org/x/net/html   13.6MB
c6f59b210906        11 seconds ago      /bin/sh -c #(nop) WORKDIR /go/src/github.c...   0B
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The image is quite large, but this Golang package can be built into a very small binary with no external dependencies, then added to an Alpine Linux base image.&lt;/p&gt;

&lt;h2 id=&quot;the-builder-pattern&quot;&gt;The builder pattern&lt;/h2&gt;

&lt;p&gt;Type in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat builder.sh&lt;/code&gt; so you can see how the builder pattern uses two separate Dockerfiles. This will help us get the context for the next step where we will use a single Dockerfile.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;./build.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#!/bin/sh
echo Building alexellis2/href-counter:build

docker build -t alexellis2/href-counter:build . -f Dockerfile.build
docker create --name extract alexellis2/href-counter:build
docker cp extract:/go/src/github.com/alexellis/href-counter/app ./app
docker rm -f extract

echo Building alexellis2/href-counter:latest
docker build -t alexellis2/href-counter:latest .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see there are quite a few intermediate steps required to create an optimized image using the &lt;em&gt;Builder pattern&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let’s see how big the Docker image came out as:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker images |grep alexellis2/href-counter
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is much smaller than when we built our first image with the Golang SDK included.&lt;/p&gt;

&lt;h2 id=&quot;multi-stage-build-example&quot;&gt;Multi-stage build example&lt;/h2&gt;

&lt;p&gt;While the builder pattern helps us achieve a small image, it does require extra leg-work for every piece of software we want to package in Docker.&lt;/p&gt;

&lt;p&gt;Here is where multi-stage builds help us out. Instead of using a shell script to orchestrate two separate Dockerfiles, we can just use one and define stages throughout.&lt;/p&gt;

&lt;p&gt;To use the Dockerfile.multi file in the Github repository to do a multi-stage build, then check the size of the resulting image against that of the image created by the Golang SDK base and the builder pattern.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker build -t alexellis2/href-counter:multi . -f Dockerfile.multi
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker images |grep href-counter
alexellis2/href-counter   multi               44852229a1cc        2 minutes ago       10.3MB
alexellis2/href-counter   latest              bb997f819fbb        3 minutes ago       10.3MB
alexellis2/href-counter   build               298d3b970412        4 minutes ago       692MB
alexellis2/href-counter   sdk                 298d3b970412        4 minutes ago       692MB
alexellis2/href-counter   &amp;lt;none&amp;gt;              b0a73b688243        13 days ago         11.6MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here is an example of building “Hello World” in Go.&lt;/p&gt;

&lt;p&gt;Create a new folder:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;mkdir hello
cd hello
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create the app.go file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &apos;package main

import &quot;fmt&quot;

func main() {
    fmt.Println(&quot;Hello world!&quot;)
}
&apos; | tee app.go
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Create a Dockerfile with the following contents:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &apos;
FROM golang:1.7.3
COPY app.go .
RUN go build -o app app.go

FROM scratch
COPY --from=0 /go/app .
CMD [&quot;./app&quot;]
&apos; | tee Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now build and run the Dockerfile:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker build -t hello-world-lab .
docker run hello-world-lab
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The resulting size of hello-world is very small:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker images |grep hello-world-lab
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;more-about-the-lab&quot;&gt;More about the lab&lt;/h2&gt;

&lt;p&gt;This lab was built from a &lt;a href=&quot;http://blog.alexellis.io/mutli-stage-docker-builds/&quot;&gt;blog post&lt;/a&gt;. by &lt;a href=&quot;https://twitter.com/alexellisuk&quot;&gt;Alex Ellis&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Blog: &lt;a href=&quot;http://blog.alexellis.io/mutli-stage-docker-builds/&quot;&gt;Builder pattern vs. Multi-stage builds in Docker&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note: We do not recommend moving over to multi-stage builds until they are fully available on the Docker Hub/Cloud and all editions of Docker. The example in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build.sh&lt;/code&gt; provides an interim solution for using separate Dockerfiles to build and ship code.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
        <pubDate>Sun, 26 Mar 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/multi-stage/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/multi-stage/</guid>
        
        <category>developer</category>
        
        <category>operations</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Orchestration, part 1: from Compose to Swarm</title>
        <description>&lt;p&gt;This lab is longer than the other ones. It features a demo app
built around a microservices architecture, defined through
a Compose file.&lt;/p&gt;

&lt;p&gt;You will run this app on a single node. Then, to scale
that app on a cluster, you will learn the concepts powering
SwarmKit, Docker’s native orchestration system. Armed
with this new knowledge, you will setup your own
Swarm cluster across five nodes, and use it to deploy
the demo app.&lt;/p&gt;

&lt;p&gt;We recommend that you open two tabs or two windows
for that lab: one for the materials, another for
the Play-With-Docker environment.&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://jpetazzo.github.io/orchestration-workshop/&quot;&gt;Introduction&lt;/a&gt;
(recommended reading)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://jpetazzo.github.io/orchestration-workshop/#part-1&quot;&gt;Part 1&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://play-with-docker.com/&quot;&gt;Play-With-Docker&lt;/a&gt; environment&lt;/li&gt;
&lt;/ul&gt;

</description>
        <pubDate>Mon, 13 Mar 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/orchestration-workshop-part1/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/orchestration-workshop-part1/</guid>
        
        <category>linux</category>
        
        <category>developer</category>
        
        <category>operations</category>
        
        <category>landing</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Orchestration, part 2: securing and operating Swarm</title>
        <description>&lt;p&gt;This lab is longer than the other ones. It is designed
to follow the part 1 (“from Compose to Swarm”) but can
also be done independently (it provides jump-start
instructions if you want to skip part 1).&lt;/p&gt;

&lt;p&gt;You will learn about the security features of Swarm:
secrets, cluster locking, network encryption, and more.&lt;/p&gt;

&lt;p&gt;You will also see how to implement logging, metrics,
and will given a galore of tips and tricks to operate
Swarm.&lt;/p&gt;

&lt;p&gt;We recommend that you open two tabs or two windows
for that lab: one for the materials, another for
the Play-With-Docker environment.&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://jpetazzo.github.io/orchestration-workshop/&quot;&gt;Introduction&lt;/a&gt;
(recommended reading)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://jpetazzo.github.io/orchestration-workshop/#part-2&quot;&gt;Part 2&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://play-with-docker.com/&quot;&gt;Play-With-Docker&lt;/a&gt; environment&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 13 Mar 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/orchestration-workshop-part2/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/orchestration-workshop-part2/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>landing</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Security Lab: Capabilities</title>
        <description>&lt;h1 id=&quot;lab-capabilities&quot;&gt;Lab: Capabilities&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;: Advanced&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt;: Approximately 30 minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this lab you’ll learn the basics of capabilities in the Linux kernel. You’ll learn how they work with Docker, some basic commands to view and manage them, as well as how to add and remove capabilities in new containers.&lt;/p&gt;

&lt;p&gt;You will complete the following steps as part of this lab.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#cap_intro&quot;&gt;Step 1 - Introduction to capabilities&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#docker_cap&quot;&gt;Step 2 - Working with Docker and capabilities&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#test_docker&quot;&gt;Step 3 - Testing Docker capabilities&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#extra&quot;&gt;Step 4 - Extra for experts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;step-1-introduction-to-capabilities&quot;&gt;&lt;a name=&quot;cap_intro&quot;&gt;&lt;/a&gt;Step 1: Introduction to capabilities&lt;/h1&gt;

&lt;p&gt;In this step you’ll learn the basics of capabilities.&lt;/p&gt;

&lt;p&gt;The Linux kernel is able to break down the privileges of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; user into distinct units referred to as &lt;strong&gt;capabilities&lt;/strong&gt;. For example, the CAP_CHOWN capability is what allows the root user to make arbitrary changes to file UIDs and GIDs. The CAP_DAC_OVERRIDE capability allows the root user to bypass kernel permission checks on file read, write and execute operations. Almost all of the special powers associated with the Linux root user are broken down into individual capabilities.&lt;/p&gt;

&lt;p&gt;This breaking down of root privileges into granular capabilities allows you to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Remove individual capabilities from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; user account, making it less powerful/dangerous.&lt;/li&gt;
  &lt;li&gt;Add privileges to non-root users at a very granular level.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Capabilities apply to both files and threads. File capabilities allow users to execute programs with higher privileges. This is similar to the way the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setuid&lt;/code&gt; bit works. Thread capabilities keep track of the current state of capabilities in running programs.&lt;/p&gt;

&lt;p&gt;The Linux kernel lets you set capability &lt;em&gt;bounding sets&lt;/em&gt; that impose limits on the capabilities that a file/thread can gain.&lt;/p&gt;

&lt;p&gt;Docker imposes certain limitations that make working with capabilities much simpler. For example, file capabilities are stored within a file’s extended attributes, and extended attributes are stripped out when Docker images are built. This means you will not normally have to concern yourself too much with file capabilities in containers.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It is of course possible to get file capabilities into containers at runtime, however this is not recommended.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In an environment without file based capabilities, it’s not possible for applications to escalate their privileges beyond the &lt;em&gt;bounding set&lt;/em&gt; (a set beyond which capabilities cannot grow). Docker sets the &lt;em&gt;bounding set&lt;/em&gt; before starting a container. You can use Docker commands to add or remove capabilities to or from the &lt;em&gt;bounding set&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;By default, Docker drops all capabilities except &lt;a href=&quot;https://github.com/moby/moby/blob/5f17312653c3e4dc5474f86692b09f06262a1ebd/oci/defaults.go#L14-L31&quot;&gt;those needed&lt;/a&gt;, using a whitelist approach.&lt;/p&gt;

&lt;h1 id=&quot;step-2-working-with-docker-and-capabilities&quot;&gt;&lt;a name=&quot;docker_cap&quot;&gt;&lt;/a&gt;Step 2: Working with Docker and capabilities&lt;/h1&gt;

&lt;p&gt;In this step you’ll learn the basic approach to managing capabilities with Docker. You’ll also learn the Docker commands used to manage capabilities for a container’s root account.&lt;/p&gt;

&lt;p&gt;As of Docker 1.12 you have 3 high level options for using capabilities:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Run containers as root with a large set of capabilities and try to manage capabilities within your container manually.&lt;/li&gt;
  &lt;li&gt;Run containers as root with limited capabilities and never change them within a container.&lt;/li&gt;
  &lt;li&gt;Run containers as an unprivileged user with no capabilities.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Option 2 as the most realistic as of Docker 1.12. Option 3 would be ideal but not realistic. Option 1 should be avoided wherever possible.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Another option may be added in future versions of Docker that will allow you to run containers as a non-root user with added capabilities. The correct way of doing this requires &lt;em&gt;ambient capabilities&lt;/em&gt; which was added to the Linux kernel in version 4.3. Whether it is possible for Docker to approximate this behavior in older kernels requires more research.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the following commands, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$CAP&lt;/code&gt; will be used to indicate one or more individual capabilities. We’ll test these out in the next section.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;To drop capabilities from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; account of a container.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run --rm -it --cap-drop $CAP alpine sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To add capabilities to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; account of a container.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run --rm -it --cap-add $CAP alpine sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;To drop all capabilities and then explicitly add individual capabilities to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; account of a container.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run --rm -it --cap-drop ALL --cap-add $CAP alpine sh
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Linux kernel prefixes all capability constants with “CAP_”. For example, CAP_CHOWN, CAP_NET_ADMIN, CAP_SETUID, CAP_SYSADMIN etc. Docker capability constants are not prefixed with “CAP_” but otherwise match the kernel’s constants.&lt;/p&gt;

&lt;p&gt;For more information on capabilities, including a full list, see the &lt;a href=&quot;http://man7.org/linux/man-pages/man7/capabilities.7.html&quot;&gt;capabilities man page&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;step-3-testing-docker-capabilities&quot;&gt;&lt;a name=&quot;test_docker&quot;&gt;&lt;/a&gt;Step 3: Testing Docker capabilities&lt;/h1&gt;

&lt;p&gt;In this step you will start various new containers. Each time you will use the commands learned in the previous step to tweak the capabilities associated with the account used to run the container.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Start a new container and prove that the container’s root account can change the ownership of files.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker run --rm -it alpine chown nobody /
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;The command gives no return code indicating that the operation succeeded. The command works because the default behavior is for new containers to be started with a root user. This root user has the CAP_CHOWN capability by default.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Start another new container and drop all capabilities for the containers root account other than the CAP_CHOWN capability. Remember that Docker does not use the “CAP_” prefix when addressing capability constants.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker run --rm -it --cap-drop ALL --cap-add CHOWN alpine chown nobody /
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;This command also gives no return code, indicating a successful run. The operation succeeds because although you dropped all capabilities for the container’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;root&lt;/code&gt; account, you added the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chown&lt;/code&gt; capability back. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chown&lt;/code&gt; capability is all that is needed to change the ownership of a file.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Start another new container and drop only the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CHOWN&lt;/code&gt; capability form its root account.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker run --rm -it --cap-drop CHOWN alpine chown nobody /
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; chown: /: Operation not permitted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;This time the command returns an error code indicating it failed. This is because the container’s root account does not have the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CHOWN&lt;/code&gt; capability and therefore cannot change the ownership of a file or directory.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Create another new container and try adding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CHOWN&lt;/code&gt; capability to the non-root user called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nobody&lt;/code&gt;. As part of the same command try and change the ownership of a file or folder.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt; docker run --rm -it --cap-add chown -u nobody alpine chown nobody /
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chown: /: Operation not permitted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;The above command fails because Docker does not yet support adding capabilities to non-root users.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this step you have added and removed capabilities to a range of new containers. You have seen that capabilities can be added and removed from the root user of a container at a very granular level. You also learned that Docker does not currently support adding capabilities to non-root users.&lt;/p&gt;

&lt;h1 id=&quot;step-4-extra-for-experts&quot;&gt;&lt;a name=&quot;extra&quot;&gt;&lt;/a&gt;Step 4: Extra for experts&lt;/h1&gt;

&lt;p&gt;The remainder of this lab will show you additional tools for working with capabilities form the Linux shell.&lt;/p&gt;

&lt;p&gt;There are two main sets of tools for managing capabilities:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;libcap&lt;/strong&gt; focuses on manipulating capabilities.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;libcap-ng&lt;/strong&gt; has some useful tools for auditing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below are some useful commands from both.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You may need to manually install the packages required for some of these commands.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;libcap&quot;&gt;&lt;strong&gt;libcap&lt;/strong&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;capsh&lt;/code&gt; - lets you perform capability testing and limited debugging&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setcap&lt;/code&gt; - set capability bits on a file&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;getcap&lt;/code&gt; - get the capability bits from a file&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;libcap-ng&quot;&gt;&lt;strong&gt;libcap-ng&lt;/strong&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pscap&lt;/code&gt; - list the capabilities of running processes&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filecap&lt;/code&gt; - list the capabilities of files&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;captest&lt;/code&gt; - test capabilities as well as list capabilities for current process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The remainder of this step will show you some examples of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libcap&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libcap-ng&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;listing-all-capabilities&quot;&gt;Listing all capabilities&lt;/h3&gt;

&lt;p&gt;The following command will start a new container using Alpine Linux, install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;libcap&lt;/code&gt; package and then list capabilities.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;    docker run --rm -it alpine sh -c &apos;apk add -U libcap; capsh --print&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   (1/1) Installing libcap (2.25-r0)
   Executing busybox-1.24.2-r9.trigger
   OK: 5 MiB in 12 packages
   Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+eip
   Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
   Securebits: 00/0x0/1&apos;b0
    secure-noroot: no (unlocked)
    secure-no-suid-fixup: no (unlocked)
    secure-keep-caps: no (unlocked)
   uid=0(root)
   gid=0(root)
   groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Current&lt;/strong&gt; is multiple sets separated by spaces. Multiple capabilities within the same set are separated by commas &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;,&lt;/code&gt;. The letters following the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt; at the end of each set are as follows:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e&lt;/code&gt; is effective&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i&lt;/code&gt; is inheritable&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; is permitted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For information on what these mean, see the &lt;a href=&quot;http://man7.org/linux/man-pages/man7/capabilities.7.html&quot;&gt;capabilities manpage&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;experimenting-with-capabilities&quot;&gt;Experimenting with capabilities&lt;/h3&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;capsh&lt;/code&gt; command can be useful for experimenting with capabilities. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;capsh --help&lt;/code&gt; shows how to use the command:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run --rm -it alpine sh -c &apos;apk add -U libcap;capsh --help&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.ta
r.gz
(1/1) Installing libcap (2.25-r1)
Executing busybox-1.25.1-r0.trigger
OK: 4 MiB in 12 packages
usage: capsh [args ...]
  --help         this message (or try &apos;man capsh&apos;)
  --print        display capability relevant state
  --decode=xxx   decode a hex string to a list of caps
  --supports=xxx exit 1 if capability xxx unsupported
  --drop=xxx     remove xxx,.. capabilities from bset
  --caps=xxx     set caps as per cap_from_text()
  --inh=xxx      set xxx,.. inheritiable set
  --secbits=&amp;lt;n&amp;gt;  write a new value for securebits
  --keep=&amp;lt;n&amp;gt;     set keep-capabability bit to &amp;lt;n&amp;gt;
  --uid=&amp;lt;n&amp;gt;      set uid to &amp;lt;n&amp;gt; (hint: id &amp;lt;username&amp;gt;)
  --gid=&amp;lt;n&amp;gt;      set gid to &amp;lt;n&amp;gt; (hint: id &amp;lt;username&amp;gt;)
  --groups=g,... set the supplemental groups
  --user=&amp;lt;name&amp;gt;  set uid,gid and groups to that of user
  --chroot=path  chroot(2) to this path
  --killit=&amp;lt;n&amp;gt;   send signal(n) to child
  --forkfor=&amp;lt;n&amp;gt;  fork and make child sleep for &amp;lt;n&amp;gt; sec
  ==             re-exec(capsh) with args as for --
  --             remaing arguments are for /bin/bash
                 (without -- [capsh] will simply exit(0))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;Warning:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--drop&lt;/code&gt; sounds like what you want to do, but it only affects the bounding set. This can be very confusing because it doesn’t actually take away the capability from the effective or inheritable set. You almost always want to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--caps&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;modifying-capabilities&quot;&gt;Modifying capabilities&lt;/h3&gt;

&lt;p&gt;Libcap and libcap-ng can both be used to modify capabilities.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Use libcap to modify the capabilities on a file.&lt;/p&gt;

    &lt;p&gt;The command below shows how to set the CAP_NET_RAW capability as &lt;em&gt;effective&lt;/em&gt; and &lt;em&gt;permitted&lt;/em&gt; on the file represented by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$file&lt;/code&gt;. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setcap&lt;/code&gt; command calls on libcap to do this.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;setcap cap_net_raw=ep $file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Use libcap-ng to set the capabilities of a file.&lt;/p&gt;

    &lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filecap&lt;/code&gt; command calls on libcap-ng.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;filecap /absolute/path net_raw
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filecap&lt;/code&gt; requires absolute path names. Shortcuts like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./&lt;/code&gt; are not permitted.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;auditing&quot;&gt;Auditing&lt;/h3&gt;

&lt;p&gt;There are multiple ways to read out the capabilities from a file.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Using libcap:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;getcap $file

$file = cap_net_raw+ep
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Using libcap-ng:&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ filecap /absolue/path/to/file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;file                     capabilities
/absolute/path/to/file        net_raw
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Using extended attributes (attr package):&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;getfattr -n security.capability $file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# file: $file
security.capability=0sAQAAAgAgAAAAAAAAAAAAAAAAAAA=
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;tips&quot;&gt;Tips&lt;/h3&gt;

&lt;p&gt;Docker images cannot have files with capability bits set. This reduces the risk of Docker containers using capabilities to escalate privileges. However, it is possible to mount volumes that contain files with capability bits set into containers. Therefore you should use caution if doing this.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;You can audit directories for capability bits with the following commands:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# with libcap
getcap -r /

# with libcap-ng
filecap -a
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;To remove capability bits you can use.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# with libcap
setcap -r $file

# with libcap-ng
filecap /path/to/file none
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further reading:&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.kernel.org/doc/ols/2008/ols2008v1-pages-163-172.pdf&quot;&gt;This article&lt;/a&gt; explains capabilities in a lot of detail. It will help you understand how capability sets interact with each other, and is very useful if you plan to run privileged docker containers and manage capabilities manually inside of them.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://man7.org/linux/man-pages/man7/capabilities.7.html&quot;&gt;This is the man page for capabilities&lt;/a&gt;. Most of the complex interactions between capability sets don’t affect Docker containers as long as there are no files with capability bits set.&lt;/p&gt;
</description>
        <pubDate>Mon, 06 Mar 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/security-capabilities/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/security-capabilities/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>security</category>
        
        
        <category>advanced</category>
        
      </item>
    
      <item>
        <title>Security Lab: Seccomp</title>
        <description>&lt;h1 id=&quot;lab-seccomp&quot;&gt;Lab: Seccomp&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;: Advanced&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt;: Approximately 20 minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;seccomp is a sandboxing facility in the Linux kernel that acts like a firewall for system calls (syscalls). It uses Berkeley Packet Filter (BPF) rules to filter syscalls and control how they are handled. These filters can significantly limit a containers access to the Docker Host’s Linux kernel - especially for simple containers/applications.&lt;/p&gt;

&lt;p&gt;You will complete the following steps as part of this lab.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#clone&quot;&gt;Step 1 - Clone the labs GitHub repo&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#test&quot;&gt;Step 2 - Test a seccomp profile&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#no-default&quot;&gt;Step 3 - Run a container with no seccomp profile&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#chmod&quot;&gt;Step 4 - Selectively remove syscalls&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#write&quot;&gt;Step 5 - Write a seccomp profile&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#gotchas&quot;&gt;Step 6 - A few Gotchas&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h1&gt;

&lt;p&gt;With this lab in Play With Docker you have all you need to complete the lab. If you are running this on another environment, you will need:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A Linux-based Docker Host with seccomp enabled&lt;/li&gt;
  &lt;li&gt;Docker 1.10 or higher (preferably 1.12 or higher)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following commands show you how to check if seccomp is enabled in your system’s kernel:&lt;/p&gt;

&lt;p&gt;Check from Docker 1.12 or higher&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   $ docker info | grep seccomp
   Security Options: apparmor seccomp   
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If the above output does not return a line with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;seccomp&lt;/code&gt; then your system does not have seccomp enabled in its kernel.&lt;/p&gt;

&lt;p&gt;Check from the Linux command line&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   $ grep SECCOMP /boot/config-$(uname -r)
   CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
   CONFIG_SECCOMP_FILTER=y
   CONFIG_SECCOMP=y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;seccomp-and-docker&quot;&gt;Seccomp and Docker&lt;/h3&gt;

&lt;p&gt;Docker has used seccomp since version 1.10 of the Docker Engine.&lt;/p&gt;

&lt;p&gt;Docker uses seccomp in &lt;em&gt;filter mode&lt;/em&gt; and has its own JSON-based DSL that allows you to define &lt;em&gt;profiles&lt;/em&gt; that compile down to seccomp filters. When you run a container it gets the default seccomp profile unless you override this by passing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--security-opt&lt;/code&gt; flag to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The following example command starts an interactive container based off the Alpine image and starts a shell process. It also applies the seccomp profile described by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;profile&amp;gt;.json&lt;/code&gt; to it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   docker run -it --rm --security-opt seccomp=&amp;lt;profile&amp;gt;.json alpine sh ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The above command sends the JSON file from the client to the daemon where it is compiled into a BPF program using a &lt;a href=&quot;https://github.com/seccomp/libseccomp-golang&quot;&gt;thin Go wrapper around libseccomp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Docker seccomp profiles operate using a whitelist approach that specifies allowed syscalls. &lt;em&gt;Only&lt;/em&gt; syscalls on the whitelist are permitted.&lt;/p&gt;

&lt;p&gt;Docker supports many security related technologies. It is possible for other security related technologies to interfere with your testing of seccomp profiles. For this reason, the best way to test the effect of seccomp profiles is to add all &lt;em&gt;capabilities&lt;/em&gt; and disable &lt;em&gt;apparmor&lt;/em&gt;. This gives you the confidence the behavior you see in the following steps is solely due to seccomp changes.&lt;/p&gt;

&lt;p&gt;The following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; flags add all &lt;em&gt;capabilities&lt;/em&gt; and disable &lt;em&gt;apparmor&lt;/em&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cap-add ALL --security-opt apparmor=unconfined&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;step-1-clone-the-labs-github-repo&quot;&gt;&lt;a name=&quot;clone&quot;&gt;&lt;/a&gt;Step 1: Clone the labs GitHub repo&lt;/h1&gt;

&lt;p&gt;In this step you will clone the lab’s GitHub repo so that you have the seccomp profiles that you will use for the remainder of this lab.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Clone the labs GitHub repo.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git clone https://github.com/docker/labs
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Change into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;labs/security/seccomp&lt;/code&gt; directory.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cd labs/security/seccomp
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The remaining steps in this lab will assume that you are running commands from this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;labs/security/seccomp&lt;/code&gt; directory. This will be important when referencing the seccomp profiles on the various &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; commands throughout the lab.&lt;/p&gt;

&lt;h1 id=&quot;step-2-test-a-seccomp-profile&quot;&gt;&lt;a name=&quot;test&quot;&gt;&lt;/a&gt;Step 2: Test a seccomp profile&lt;/h1&gt;

&lt;p&gt;In this step you will use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deny.json&lt;/code&gt; seccomp profile included the lab guides repo. This profile has an empty syscall whitelist meaning all syscalls will be blocked. As part of the demo you will add all &lt;em&gt;capabilities&lt;/em&gt; and effectively disable &lt;em&gt;apparmor&lt;/em&gt; so that you know that only your seccomp profile is preventing the syscalls.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; command to try to start a new container with all capabilities added, apparmor unconfined, and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;seccomp-profiles/deny.json&lt;/code&gt; seccomp profile applied.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run --rm -it --cap-add ALL --security-opt apparmor=unconfined --security-opt seccomp=seccomp-profiles/deny.json alpine sh
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker: Error response from daemon: cannot start a stopped process: unknown.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this scenario, Docker doesn’t actually have enough syscalls to start the container!&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Inspect the contents of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;seccomp-profiles/deny.json&lt;/code&gt; profile.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat seccomp-profiles/deny.json
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
     &quot;defaultAction&quot;: &quot;SCMP_ACT_ERRNO&quot;,
     &quot;architectures&quot;: [
             &quot;SCMP_ARCH_X86_64&quot;,
             &quot;SCMP_ARCH_X86&quot;,
             &quot;SCMP_ARCH_X32&quot;
     ],
     &quot;syscalls&quot;: [
     ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;Notice that there are no &lt;strong&gt;syscalls&lt;/strong&gt; in the whitelist. This means that no syscalls will be allowed from containers started with this profile.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this step you removed &lt;em&gt;capabilities&lt;/em&gt; and &lt;em&gt;apparmor&lt;/em&gt; from interfering, and started a new container with a seccomp profile that had no syscalls in its whitelist. You saw how this prevented all syscalls from within the container or to let it start in the first place.&lt;/p&gt;

&lt;h1 id=&quot;step-3-run-a-container-with-no-seccomp-profile&quot;&gt;&lt;a name=&quot;no-default&quot;&gt;&lt;/a&gt;Step 3: Run a container with no seccomp profile&lt;/h1&gt;

&lt;p&gt;Unless you specify a different profile, Docker will apply the &lt;a href=&quot;https://github.com/docker/docker/blob/master/profiles/seccomp/default.json&quot;&gt;default seccomp profile&lt;/a&gt; to all new containers. In this step you will see how to force a new container to run without a seccomp profile.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Start a new container with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--security-opt seccomp=unconfined&lt;/code&gt; flag so that no seccomp profile is applied to it.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run --rm -it --security-opt seccomp=unconfined debian:jessie sh
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;From the terminal of the container run a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami&lt;/code&gt; command to confirm that the container works and can make syscalls back to the Docker Host.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;whoami
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;To prove that we are not running with the default seccomp profile, try running a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unshare&lt;/code&gt; command, which runs a shell process in a new namespace:
    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;  unshare --map-root-user --user
  whoami
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  root
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Exit the new shell and the container.
    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;exit
exit
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Run the following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; command from your Docker Host to see a list of the syscalls used by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami&lt;/code&gt; program.&lt;/p&gt;

    &lt;p&gt;Your Docker Host will need the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; package installed.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;apk add --update strace
strace -c -f -S name whoami 2&amp;gt;&amp;amp;1 1&amp;gt;/dev/null | tail -n +3 | head -n -2 | awk &apos;{print $(NF)}&apos;
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;access
arch_prctl
brk
close
connect
execve
&amp;lt;SNIP&amp;gt;
socket
write
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can also run the following simpler command and get a more verbose output.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;   strace whoami
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;   execve(&quot;/usr/bin/whoami&quot;, [&quot;whoami&quot;, &quot;-qq&quot;], [/* 21 vars */]) = 0
   brk(0)                                  = 0x1980000
   &amp;lt;SNIP&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can substitute &lt;strong&gt;whoami&lt;/strong&gt; for any other program.&lt;/p&gt;

&lt;p&gt;In this step you started a new container with no seccomp profile and verified that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami&lt;/code&gt; program could execute. You also used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; program to list the syscalls made by a particular run of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;whoami&lt;/code&gt; program.&lt;/p&gt;

&lt;h1 id=&quot;step-4-selectively-remove-syscalls&quot;&gt;&lt;a name=&quot;chmod&quot;&gt;&lt;/a&gt;Step 4: Selectively remove syscalls&lt;/h1&gt;

&lt;p&gt;In this step you will see how applying changes to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.json&lt;/code&gt; profile can be a good way to fine-tune which syscalls are available to containers.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default-no-chmod.json&lt;/code&gt; profile is a modification of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.json&lt;/code&gt; profile with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fchmod()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmodat()&lt;/code&gt; syscalls removed from its whitelist.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Start a new container with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default-no-chmod.json&lt;/code&gt; profile and attempt to run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 777 / -v&lt;/code&gt; command.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run --rm -it --security-opt seccomp=./seccomp-profiles/default-no-chmod.json alpine sh
&lt;/code&gt;&lt;/pre&gt;
    &lt;p&gt;and then from inside the container:&lt;/p&gt;
    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;chmod 777 / -v
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;chmod: /: Operation not permitted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The command fails because the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 777 / -v&lt;/code&gt; command uses some of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fchmod()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmodat()&lt;/code&gt; syscalls that have been removed from the whitelist of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default-no-chmod.json&lt;/code&gt; profile.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Exit the container.
    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;exit
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Start another new container with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.json&lt;/code&gt; profile and run the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod 777 / -v&lt;/code&gt;.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run --rm -it --security-opt seccomp=./seccomp-profiles/default.json alpine sh
&lt;/code&gt;&lt;/pre&gt;
    &lt;p&gt;and then from inside the container:&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;chmod 777 / -v
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mode of &apos;/&apos; changed to 0777 (rwxrwxrwx)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The command succeeds this time because the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.json&lt;/code&gt; profile has the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fchmod()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmodat&lt;/code&gt; syscalls included in its whitelist.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Exit the container.
    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;exit
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Check both profiles for the presence of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmod()&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fchmod()&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chmodat()&lt;/code&gt; syscalls.&lt;/p&gt;

    &lt;p&gt;Be sure to perform these commands from the command line of your Docker Host and not from inside of the container created in the previous step.&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat ./seccomp-profiles/default.json | grep chmod
&lt;/code&gt;&lt;/pre&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;name&quot;: &quot;chmod&quot;,
&quot;name&quot;: &quot;fchmod&quot;,
&quot;name&quot;: &quot;fchmodat&quot;,
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat ./seccomp-profiles/default-no-chmod.json | grep chmod
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;The output above shows that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default-no-chmod.json&lt;/code&gt; profile contains no &lt;strong&gt;chmod&lt;/strong&gt; related syscalls in the whitelist.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this step you saw how removing particular syscalls from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;default.json&lt;/code&gt; profile can be a powerful way to start fine tuning the security of your containers.&lt;/p&gt;

&lt;h1 id=&quot;step-5-write-a-seccomp-profile&quot;&gt;&lt;a name=&quot;write&quot;&gt;&lt;/a&gt;Step 5: Write a seccomp profile&lt;/h1&gt;

&lt;p&gt;It is possible to write Docker seccomp profiles from scratch. You can also edit existing profiles. In this step you will learn about the syntax and behavior of Docker seccomp profiles.&lt;/p&gt;

&lt;p&gt;The layout of a Docker seccomp profile looks like the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    &quot;defaultAction&quot;: &quot;SCMP_ACT_ERRNO&quot;,
    &quot;architectures&quot;: [
        &quot;SCMP_ARCH_X86_64&quot;,
        &quot;SCMP_ARCH_X86&quot;,
        &quot;SCMP_ARCH_X32&quot;
    ],
    &quot;syscalls&quot;: [
        {
            &quot;name&quot;: &quot;accept&quot;,
            &quot;action&quot;: &quot;SCMP_ACT_ALLOW&quot;,
            &quot;args&quot;: []
        },
        {
            &quot;name&quot;: &quot;accept4&quot;,
            &quot;action&quot;: &quot;SCMP_ACT_ALLOW&quot;,
            &quot;args&quot;: []
        },
        ...
    ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The most authoritative source for how to write Docker seccomp profiles is the structs used to deserialize the JSON.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/docker/engine-api/blob/c15549e10366236b069e50ef26562fb24f5911d4/types/seccomp.go&quot;&gt;https://github.com/docker/engine-api/blob/c15549e10366236b069e50ef26562fb24f5911d4/types/seccomp.go&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/opencontainers/runtime-spec/blob/6be516e2237a6dd377408e455ac8b41faf48bdf6/specs-go/config.go#L502&quot;&gt;https://github.com/opencontainers/runtime-spec/blob/6be516e2237a6dd377408e455ac8b41faf48bdf6/specs-go/config.go#L502&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The table below lists the possible &lt;em&gt;actions&lt;/em&gt; in order of precedence. Higher actions overrule lower actions.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Action&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;SCMP_ACT_KILL&lt;/td&gt;
      &lt;td&gt;Kill with a exit status of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x80 + 31 (SIGSYS) = 159&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SCMP_ACT_TRAP&lt;/td&gt;
      &lt;td&gt;Send a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SIGSYS&lt;/code&gt; signal without executing the system call&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SCMP_ACT_ERRNO&lt;/td&gt;
      &lt;td&gt;Set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;errno&lt;/code&gt; without executing the system call&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SCMP_ACT_TRACE&lt;/td&gt;
      &lt;td&gt;Invoke a ptracer to make a decision or set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;errno&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ENOSYS&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;SCMP_ACT_ALLOW&lt;/td&gt;
      &lt;td&gt;Allow&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The most important actions for Docker users are &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SCMP_ACT_ERRNO&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SCMP_ACT_ALLOW&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Profiles can contain more granular filters based on the value of the arguments to the system call.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    ...
    &quot;syscalls&quot;: [
        {
            &quot;name&quot;: &quot;accept&quot;,
            &quot;action&quot;: &quot;SCMP_ACT_ALLOW&quot;,
            &quot;args&quot;: [
                {
                    &quot;index&quot;: 0,
                    &quot;op&quot;: &quot;SCMP_CMP_MASKED_EQ&quot;,
                    &quot;value&quot;: 2080505856,
                    &quot;valueTwo&quot;: 0
                }
            ]
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index&lt;/code&gt; is the index of the system call argument&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;op&lt;/code&gt; is the operation to perform on the argument. It can be one of:
    &lt;ul&gt;
      &lt;li&gt;SCMP_CMP_NE - not equal&lt;/li&gt;
      &lt;li&gt;SCMP_CMP_LT - less than&lt;/li&gt;
      &lt;li&gt;SCMP_CMP_LE - less than or equal to&lt;/li&gt;
      &lt;li&gt;SCMP_CMP_EQ - equal to&lt;/li&gt;
      &lt;li&gt;SCMP_CMP_GE - greater or equal to&lt;/li&gt;
      &lt;li&gt;SCMP_CMP_GT - greater than&lt;/li&gt;
      &lt;li&gt;SCMP_CMP_MASKED_EQ - masked equal: true if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(value &amp;amp; arg == valueTwo)&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;value&lt;/code&gt; is a parameter for the operation&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;valueTwo&lt;/code&gt; is used only for SCMP_CMP_MASKED_EQ&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule only matches if &lt;strong&gt;all&lt;/strong&gt; args match. Add multiple rules to achieve the effect of an OR.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;strace&lt;/code&gt; can be used to get a list of all system calls made by a program.
It’s a very good starting point for writing seccomp policies.
Here’s an example of how we can list all system calls made by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;strace -c -f -S name ls 2&amp;gt;&amp;amp;1 1&amp;gt;/dev/null | tail -n +3 | head -n -2 | awk &apos;{print $(NF)}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;access
arch_prctl
brk
close
execve
&amp;lt;SNIP&amp;gt;
statfs
write
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output above shows the syscalls that will need to be enabled for a container running the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; program to work, in addition to the syscalls required to start a container.&lt;/p&gt;

&lt;p&gt;In this step you learned the format and syntax of Docker seccomp profiles. You also learned the order of preference for actions, as well as how to determine the syscalls needed by an individual program.&lt;/p&gt;

&lt;h1 id=&quot;step-6-a-few-gotchas&quot;&gt;&lt;a name=&quot;test&quot;&gt;&lt;/a&gt;Step 6: A few gotchas&lt;/h1&gt;

&lt;p&gt;The remainder of this lab will walk you through a few things that are easy to miss when using seccomp with Docker.&lt;/p&gt;

&lt;h4 id=&quot;timing-of-a-seccomp-profile-application&quot;&gt;Timing of a seccomp profile application&lt;/h4&gt;

&lt;p&gt;In versions of Docker prior to 1.12, seccomp polices tended to be applied very early in the container creation process. This resulted in you needing to add syscalls to your profile that were required for the container creation process but not required by your container. This was not ideal. See:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/docker/docker/issues/22252&quot;&gt;https://github.com/docker/docker/issues/22252&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/opencontainers/runc/pull/789&quot;&gt;https://github.com/opencontainers/runc/pull/789&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good way to avoid this issue in Docker 1.12+ can be to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--security-opt no-new-privileges&lt;/code&gt; flag when starting your container. However, this will also prevent you from gaining privileges through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setuid&lt;/code&gt; binaries.&lt;/p&gt;

&lt;h4 id=&quot;truncation&quot;&gt;Truncation&lt;/h4&gt;

&lt;p&gt;When writing a seccomp filter, there may be unused or randomly set bits on 32-bit arguments when using a 64-bit operating system after the filter has run.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When checking values from args against a blacklist, keep in mind that
arguments are often silently truncated before being processed, but
after the seccomp check.  For example, this happens if the i386 ABI
is used on an x86-64 kernel: although the kernel will normally not
look beyond the 32 lowest bits of the arguments, the values of the
full 64-bit registers will be present in the seccomp data.  A less
surprising example is that if the x86-64 ABI is used to perform a
system call that takes an argument of type int, the more-significant
half of the argument register is ignored by the system call, but
visible in the seccomp data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt&quot;&gt;https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt&lt;/a&gt;&lt;/p&gt;

&lt;h4 id=&quot;seccomp-escapes&quot;&gt;seccomp escapes&lt;/h4&gt;

&lt;p&gt;Syscall numbers are architecture dependent. This limits the portability of BPF filters. Fortunately Docker profiles abstract this issue away, so you don’t need to worry about it if using Docker seccomp profiles.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptrace&lt;/code&gt; is disabled by default and you should avoid enabling it. This is because it allows bypassing of seccomp. You can use &lt;a href=&quot;https://gist.github.com/thejh/8346f47e359adecd1d53&quot;&gt;this script&lt;/a&gt; to test for seccomp escapes through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptrace&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;differences-between-docker-versions&quot;&gt;Differences between Docker versions&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Seccomp is supported as of Docker 1.10.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--privileged&lt;/code&gt; flag when creating a container with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; disables seccomp in all versions of docker - even if you explicitly specify a seccomp profile. In general you should avoid using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--privileged&lt;/code&gt; flag as it does too many things. You can achieve the same goal with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cap-add ALL --security-opt apparmor=unconfined --security-opt seccomp=unconfined&lt;/code&gt;. If you need access to devices use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-ice&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In docker 1.10-1.12 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker exec --privileged&lt;/code&gt; does not bypass seccomp. This may change in future versions (see  &lt;a href=&quot;https://github.com/docker/docker/issues/21984&quot;&gt;https://github.com/docker/docker/issues/21984&lt;/a&gt;).&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In docker 1.12 and later, adding a capability may enable some appropriate system calls in the default seccomp profile. However, it does not disable apparmor.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;using-multiple-filters&quot;&gt;Using multiple filters&lt;/h3&gt;

&lt;p&gt;The only way to use multiple seccomp filters, as of Docker 1.12, is to load additional filters within your program at runtime. The kernel supports layering filters.&lt;/p&gt;

&lt;p&gt;When using multiple layered filters, all filters are always executed starting with the most recently added. The highest precedence action returned is taken. See the man page for all the details: &lt;a href=&quot;http://man7.org/linux/man-pages/man2/seccomp.2.html&quot;&gt;http://man7.org/linux/man-pages/man2/seccomp.2.html&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;misc&quot;&gt;Misc&lt;/h3&gt;

&lt;p&gt;There is no easy way to use seccomp in a mode that reports errors without crashing the program. However, there are several round-about ways to accomplish this. One such way is to use SCMP_ACT_TRAP and write your code to handle SIGSYS and report the errors in a useful way. Here is some information on &lt;a href=&quot;https://wiki.mozilla.org/Security/Sandbox/Seccomp&quot;&gt;how Firefox handles seccomp violations&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;further-reading&quot;&gt;Further reading:&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;Very comprehensive presentation about seccomp that goes into more detail than this document.
    &lt;ul&gt;
      &lt;li&gt;Article: &lt;a href=&quot;https://lwn.net/Articles/656307/&quot;&gt;https://lwn.net/Articles/656307/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Slides: &lt;a href=&quot;http://man7.org/conf/lpc2015/limiting_kernel_attack_surface_with_seccomp-LPC_2015-Kerrisk.pdf&quot;&gt;http://man7.org/conf/lpc2015/limiting_kernel_attack_surface_with_seccomp-LPC_2015-Kerrisk.pdf&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Chrome’s DSL for generating seccomp BPF programs:
&lt;a href=&quot;https://cs.chromium.org/chromium/src/sandbox/linux/bpf_dsl/bpf_dsl.h?sq=package:chromium&amp;amp;dr=CSs&quot;&gt;https://cs.chromium.org/chromium/src/sandbox/linux/bpf_dsl/bpf_dsl.h?sq=package:chromium&amp;amp;dr=CSs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Mon, 06 Mar 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/security-seccomp/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/security-seccomp/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>security</category>
        
        
        <category>advanced</category>
        
      </item>
    
      <item>
        <title>Docker registry for Linux Part 1</title>
        <description>&lt;h1 id=&quot;introduction&quot;&gt;Introduction&lt;/h1&gt;

&lt;p&gt;A registry is a service for storing and accessing Docker images. &lt;a href=&quot;https://hub.docker.com&quot;&gt;Docker Hub&lt;/a&gt; and &lt;a href=&quot;https://store.docker.com&quot;&gt;Docker Store&lt;/a&gt; are the best-known hosted registries, which you can use to store public and private images. You can also run your own registry using the open-source &lt;a href=&quot;https://docs.docker.com/registry&quot;&gt;Docker Registry&lt;/a&gt;, which is a Go application in a Alpine Linux container.&lt;/p&gt;

&lt;h2 id=&quot;what-you-will-learn&quot;&gt;What You Will Learn&lt;/h2&gt;

&lt;p&gt;You’ll learn how to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;run a local registry in a container and configure your Docker engine to use the registry;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;generate SSL certificates (using Docker!) and run a secure local registry with a friendly domain name;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;generate encrypted passwords (using Docker!) and run an authenticated, secure local registry over HTTPS with basic auth.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note. The open-source registry does not have a Web UI, so there’s no interface like &lt;a href=&quot;https://hub.docker.com&quot;&gt;Docker Hub&lt;/a&gt; or &lt;a href=&quot;https://store.docker.com&quot;&gt;Docker Store&lt;/a&gt;. Instead there is a &lt;a href=&quot;https://docs.docker.com/registry/spec/api/&quot;&gt;REST API&lt;/a&gt; you can use to query the registry. For a local registry which has a Web UI and role-based access control, Docker, Inc. has the &lt;a href=&quot;https://www.docker.com/sites/default/files/Docker%20Trusted%20Registry.pdf&quot;&gt;Trusted Registry&lt;/a&gt; product.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You’ll need Docker running on in this tutorial, or on a Linux machine and be familiar with the key Docker concepts, and with Docker volumes:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/understanding-docker/&quot;&gt;Docker concepts&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/tutorials/dockervolumes/&quot;&gt;Docker volumes&lt;/a&gt;
    &lt;h1 id=&quot;part-1---running-a-registry-container-in-linux&quot;&gt;Part 1 - Running a Registry Container in Linux&lt;/h1&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several ways to run a registry container. The simplest is to run an insecure registry over HTTP, but for that we need to configure Docker to explicitly allow insecure access to the registry.&lt;/p&gt;

&lt;p&gt;Docker expects all registries to run on HTTPS. The next section of this lab will introduce a secure version of our registry container, but for this part of the tutorial we will run a version on HTTP. When registering a image, Docker returns an error message like this:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;http: server gave HTTP response to HTTPS client
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The Docker Engine needs to be explicitly setup to use HTTP for the insecure registry. For this sample it has already been done, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:5000&lt;/code&gt; has already been added to the daemon.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;** Running on your own Linux machine instead of in this browser window **&lt;/em&gt;
Edit or create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/docker/docker&lt;/code&gt; file:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;vi /etc/docker/docker

# add this line
DOCKER_OPTS=&quot;--insecure-registry 127.0.0.1:5000&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Close and save the file, then restart the docker daemon.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;pkill dockerd
dockerd &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;em&gt;** If you’re running on your own Mac or Windows machine instead of in this browser window **&lt;/em&gt;
In Docker for Mac, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Preferences&lt;/code&gt; menu lets you set the address for an insecure registry under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Daemon&lt;/code&gt; panel:
&lt;img src=&quot;/images/docker_osx_insecure_registry.png&quot; alt=&quot;MacOS menu&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In Docker for Windows, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Settings&lt;/code&gt; menu lets you set the address for an insecure registry under the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Daemon&lt;/code&gt; panel:
&lt;img src=&quot;/images/docker_windows_insecure_registry.png&quot; alt=&quot;MacOS menu&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;testing-the-registry-image&quot;&gt;Testing the Registry Image&lt;/h2&gt;
&lt;p&gt;First we’ll test that the registry image is working correctly, by running it without any special configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -d -p 5000:5000 --name registry registry:2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;understanding-image-names&quot;&gt;Understanding Image Names&lt;/h2&gt;
&lt;p&gt;Typically we work with images from Docker Store, which is the default registry for Docker. Commands using just the image repository name work fine, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker pull hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello-world&lt;/code&gt; is the repository name, which we are using as a short form of the full image name. The full name is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker.io/hello-world:latest&lt;/code&gt;. That breaks down into three parts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker.io&lt;/code&gt; - the hostname of the registry which stores the image;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello-world&lt;/code&gt; - the repository name, in this case in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{imageName}&lt;/code&gt; format;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest&lt;/code&gt; - the image tag.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a tag isn’t specified, then the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;latest&lt;/code&gt; is used. If a registry hostname isn’t specified then the default &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker.io&lt;/code&gt; for Docker Store is used. If you want to use images with any other registry, you need to explicitly specify the hostname - the default is always Docker Store.&lt;/p&gt;

&lt;p&gt;With a local registry, the hostname and the custom port used by the registry is the full registry address, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:5000&lt;/code&gt;. In this sample we’ll just be using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:5000&lt;/code&gt; as that’s already been added to the daemon.&lt;/p&gt;

&lt;h2 id=&quot;pushing-and-pulling-from-the-local-registry&quot;&gt;Pushing and Pulling from the Local Registry&lt;/h2&gt;

&lt;p&gt;Docker uses the hostname from the full image name to determine which registry to use. We can build images and include the local registry hostname in the image tag, or use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker tag&lt;/code&gt; command to add a new tag to an existing image.&lt;/p&gt;

&lt;p&gt;These commands pull a public image from Docker Store, tag it for use in the private registry with the full name &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:5000/hello-world&lt;/code&gt;, and then push it to the registry:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker tag hello-world 127.0.0.1:5000/hello-world
docker push 127.0.0.1:5000/hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When you push the image to your local registry, you’ll see similar output to when you push a public image to the Hub:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;The push refers to a repository [127.0.0.1:5000/hello-world]
a55ad2cda2bf: Pushed
cfbe7916c207: Pushed
fe4c16cbf7a4: Pushed
latest: digest: sha256:79e028398829da5ce98799e733bf04ac2ee39979b238e4b358e321ec549da5d6 size: 948
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;On your machine, you can remove the new image tag and the original image, and pull it again from the local registry to verify it was correctly stored:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker rmi 127.0.0.1:5000/hello-world
docker rmi hello-world
docker pull 127.0.0.1:5000/hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That exercise shows the registry works correctly, but at the moment it’s not very useful because all the image data is stored in the container’s writable storage area, which will be lost when the container is removed. To store the data outside of the container, we need to mount a host directory when we start the container.&lt;/p&gt;

&lt;h2 id=&quot;running-a-registry-container-with-external-storage&quot;&gt;Running a Registry Container with External Storage&lt;/h2&gt;
&lt;p&gt;Remove the existing registry container by removing the container which holds the storage layer. Any images pushed will be deleted:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker kill registry
docker rm registry
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, the new container will use a host-mounted Docker volume. When the registry server in the container writes image layer data, it appears to be writing to a local directory in the container but it will be writing to a directory on the host.&lt;/p&gt;

&lt;p&gt;Create the registry:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;mkdir registry-data
docker run -d -p 5000:5000 --name registry -v $(pwd)/registry-data:/var/lib/registry registry
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tag and push the container with the new IP address of the registry.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker pull hello-world
docker tag hello-world 127.0.0.1:5000/hello-world
docker push 127.0.0.1:5000/hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Repeating the previous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker push&lt;/code&gt; command uploads an image to the registry container, and the layers will be stored in the container’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/var/lib/registry&lt;/code&gt; directory, which is actually mapped to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(pwd)/registry-data&lt;/code&gt; directory on your machine. Storing data outside of the container means we can build a new version of the registry image and replace the old container with a new one using the same host mapping - so the new registry container has all the images stored by the previous container.&lt;/p&gt;

&lt;p&gt;Using an insecure registry isn’t practical in multi-user scenarios. Effectively there’s no security so anyone can push and pull images if they know the registry hostname. The registry server supports authentication, but only over a secure SSL connection. We’ll run a secure version of the registry server in a container next.&lt;/p&gt;
</description>
        <pubDate>Tue, 28 Feb 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/linux-registry-part1/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/linux-registry-part1/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Docker registry for Linux Parts 2 &amp; 3</title>
        <description>&lt;h1 id=&quot;part-2---running-a-secured-registry-container-in-linux&quot;&gt;Part 2 - Running a Secured Registry Container in Linux&lt;/h1&gt;

&lt;p&gt;We saw how to run a simple registry container in Part 1, using the official Docker registry image. The registry server can be configured to serve HTTPS traffic on a known domain, so it’s straightforward to run a secure registry for private use with a self-signed SSL certificate.&lt;/p&gt;

&lt;h2 id=&quot;generating-the-ssl-certificate-in-linux&quot;&gt;Generating the SSL Certificate in Linux&lt;/h2&gt;

&lt;p&gt;The Docker docs explain how to &lt;a href=&quot;https://docs.docker.com/registry/insecure/#/using-self-signed-certificates&quot;&gt;generate a self-signed certificate&lt;/a&gt; on Linux using OpenSSL:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;mkdir -p certs 
openssl req -newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key -x509 -days 365 -out certs/domain.crt
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Generating a 4096 bit RSA private key
........++
............................................................++
writing new private key to &apos;certs/domain.key&apos;
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter &apos;.&apos;, the field will be left blank.
-----
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Docker
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:127.0.0.1
Email Address []:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you are running the registry locally, be sure to use your host name as the CN.&lt;/p&gt;

&lt;p&gt;To get the docker daemon to trust the certificate, copy the domain.crt file.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;mkdir /etc/docker/certs.d
mkdir /etc/docker/certs.d/127.0.0.1:5000 
cp $(pwd)/certs/domain.crt /etc/docker/certs.d/127.0.0.1:5000/ca.crt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure to restart the docker daemon.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;pkill dockerd
dockerd &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;em&gt;/dev/null&lt;/em&gt; part is to avoid the output logs from docker daemon.&lt;/p&gt;

&lt;p&gt;Now we have an SSL certificate and can run a secure registry.&lt;/p&gt;

&lt;h2 id=&quot;running-the-registry-securely&quot;&gt;Running the Registry Securely&lt;/h2&gt;

&lt;p&gt;The registry server supports several configuration switches as environment variables, including the details for running securely. We can use the same image we’ve already used, but configured for HTTPS.&lt;/p&gt;

&lt;p&gt;For the secure registry, we need to run a container which has the SSL certificate and key files available, which we’ll do with an additional volume mount (so we have one volume for registry data, and one for certs). We also need to specify the location of the certificate files, which we’ll do with environment variables:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;mkdir registry-data
docker run -d -p 5000:5000 --name registry \
  --restart unless-stopped \
  -v $(pwd)/registry-data:/var/lib/registry -v $(pwd)/certs:/certs \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  registry
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The new parts to this command are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--restart unless-stopped&lt;/code&gt; - restart the container when it exits, unless it has been explicitly stopped. When the host restarts, Docker will start the registry container, so it’s always available.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v $pwd\certs:c:\certs&lt;/code&gt; - mount the local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;certs&lt;/code&gt; folder into the container, so the registry server can access the certificate and key files;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e REGISTRY_HTTP_TLS_CERTIFICATE&lt;/code&gt; - specify the location of the SSL certificate file;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e REGISTRY_HTTP_TLS_KEY&lt;/code&gt; - specify the location of the SSL key file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll let Docker assign a random IP address to this container, because we’ll be accessing it by host name. The registry is running securely now, but we’ve used a self-signed certificate for an internal domain name.&lt;/p&gt;

&lt;h2 id=&quot;accessing-the-secure-registry&quot;&gt;Accessing the Secure Registry&lt;/h2&gt;

&lt;p&gt;We’re ready to push an image into our secure registry.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker pull hello-world
docker tag hello-world 127.0.0.1:5000/hello-world
docker push 127.0.0.1:5000/hello-world
docker pull 127.0.0.1:5000/hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can go one step further with the open-source registry server, and add basic authentication - so we can require users to securely log in to push and pull images.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;** We have added Part 3 to the end of this section to allow you to continue to use the set-up we have above **&lt;/em&gt;&lt;/p&gt;

&lt;h1 id=&quot;part-3---using-basic-authentication-with-a-secured-registry-in-linux&quot;&gt;Part 3 - Using Basic Authentication with a Secured Registry in Linux&lt;/h1&gt;

&lt;p&gt;From Part 2 we have a registry running in a Docker container, which we can securely access over HTTPS from any machine in our network. We used a self-signed certificate, which has security implications, but you could buy an SSL from a CA instead, and use that for your registry. With secure communication in place, we can set up user authentication.&lt;/p&gt;

&lt;h2 id=&quot;usernames-and-passwords&quot;&gt;Usernames and Passwords&lt;/h2&gt;

&lt;p&gt;The registry server and the Docker client support &lt;a href=&quot;https://en.wikipedia.org/wiki/Basic_access_authentication&quot;&gt;basic authentication&lt;/a&gt; over HTTPS. The server uses a file with a collection of usernames and encrypted passwords. The file uses Apache’s htpasswd.&lt;/p&gt;

&lt;p&gt;Create the password file with an entry for user “moby” with password “gordon”;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;mkdir auth
docker run --entrypoint htpasswd registry:latest -Bbn moby gordon &amp;gt; auth/htpasswd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The options are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;–entrypoint Overwrite the default ENTRYPOINT of the image&lt;/li&gt;
  &lt;li&gt;-B Use bcrypt encryption (required)&lt;/li&gt;
  &lt;li&gt;-b run in batch mode&lt;/li&gt;
  &lt;li&gt;-n display results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can verify the entries have been written by checking the file contents - which shows the user names in plain text and a cipher text password:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat auth/htpasswd
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;moby:$2y$05$Geu2Z4LN0QDpUJBHvP5JVOsKOLH/XPoJBqISv1D8Aeh6LVGvjWWVC
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;running-an-authenticated-secure-registry&quot;&gt;Running an Authenticated Secure Registry&lt;/h2&gt;

&lt;p&gt;Adding authentication to the registry is a similar process to adding SSL - we need to run the registry with access to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htpasswd&lt;/code&gt; file on the host, and configure authentication using environment variables.&lt;/p&gt;

&lt;p&gt;As before, we’ll remove the existing container and run a new one with authentication configured:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker kill registry
docker rm registry
docker run -d -p 5000:5000 --name registry \
  --restart unless-stopped \
  -v $(pwd)/registry-data:/var/lib/registry \
  -v $(pwd)/certs:/certs \
  -v $(pwd)/auth:/auth \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  -e REGISTRY_AUTH=htpasswd \
  -e &quot;REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm&quot; \
  -e &quot;REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd&quot; \
  registry
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The options for this container are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v $(pwd)/auth:/auth&lt;/code&gt; - mount the local &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;auth&lt;/code&gt; folder into the container, so the registry server can access &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htpasswd&lt;/code&gt; file;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e REGISTRY_AUTH=htpasswd&lt;/code&gt; - use the registry’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htpasswd&lt;/code&gt; authentication method;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e REGISTRY_AUTH_HTPASSWD_REALM=&apos;Registry Realm&apos;&lt;/code&gt; - specify the authentication realm;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd&lt;/code&gt; - specify the location of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;htpasswd&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now the registry is using secure transport and user authentication.&lt;/p&gt;

&lt;h2 id=&quot;authenticating-with-the-registry&quot;&gt;Authenticating with the Registry&lt;/h2&gt;

&lt;p&gt;With basic authentication, users cannot push or pull from the registry unless they are authenticated. If you try and pull an image without authenticating, you will get an error:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker pull 127.0.0.1:5000/hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Using default tag: latest
Error response from daemon: Get https://127.0.0.1:5000/v2/hello-world/manifests/latest: no basic auth credentials
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The result is the same for valid and invalid image names, so you can’t even check a repository exists without authenticating. Logging in to the registry is the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker login&lt;/code&gt; command you use for Docker Store, specifying the registry hostname:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker login 127.0.0.1:5000
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Username: moby
Password:
Login Succeeded
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you use the wrong password or a username that doesn’t exist, you get a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;401&lt;/code&gt; error message:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Error response from daemon: login attempt to https://registry.local:5000/v2/ failed with status: 401 Unauthorized
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now you’re authenticated, you can push and pull as before:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker pull 127.0.0.1:5000/hello-world
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Using default tag: latest
latest: Pulling from hello-world
Digest: sha256:961497c5ca49dc217a6275d4d64b5e4681dd3b2712d94974b8ce4762675720b4
Status: Image is up to date for registry.local:5000/hello-world:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note. The open-source registry does not support the same authorization model as Docker Store or Docker Trusted Registry. Once you are logged in to the registry, you can push and pull from any repository, there is no restriction to limit specific users to specific repositories.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/registry/&quot;&gt;Docker Registry&lt;/a&gt; is a free, open-source application for storing and accessing Docker images. You can run the registry in a container on your own network, or in a virtual network in the cloud, to host private images with secure access. For Linux hosts, there is an &lt;a href=&quot;https://hub.docker.com/_/registry/&quot;&gt;official registry image&lt;/a&gt; on Docker Hub.&lt;/p&gt;

&lt;p&gt;We’ve covered all the options, from running an insecure registry, through adding SSL to encrypt traffic, and finally adding basic authentication to restrict access. By now you know how to set up a usable registry in your own environment, and you’ve also used some key Docker patterns - using containers as build agents and to run basic commands, without having to install software on your host machines.&lt;/p&gt;

&lt;p&gt;There is still more you can do with Docker Registry - using a different &lt;a href=&quot;https://docs.docker.com/registry/storage-drivers/&quot;&gt;storage driver&lt;/a&gt; so the image data is saved to reliable share storage, and setting up your registry as a &lt;a href=&quot;https://docs.docker.com/registry/recipes/mirror/&quot;&gt;caching proxy for Docker Store&lt;/a&gt; are good next steps.&lt;/p&gt;
</description>
        <pubDate>Mon, 27 Feb 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/linux-registry-part2/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/linux-registry-part2/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Go + Docker = ♥</title>
        <description>&lt;p&gt;This is a short collection of tips and tricks showing how Docker
can be useful when working with Go code. For instance, I’ll show
you how to compile Go code with different versions of the Go
toolchain, how to cross-compile to a different platform (and test
the result!), or how to produce really small container images.&lt;/p&gt;

&lt;p&gt;The following article assumes that you have Docker installed on
your system. It doesn’t have to be a recent version (we’re not
going to use any fancy feature here).&lt;/p&gt;

&lt;h2 id=&quot;go-without-go&quot;&gt;Go without &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;… And by that, we mean “Go without installing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go&lt;/code&gt;”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you write Go code, or if you have even the slightest interest
into the Go language, you certainly have the Go compiler and toolchain installed,
so you might be wondering “what’s the point?”; but there are
a few scenarios where you want to compile Go without installing Go.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;You still have this old Go 1.2 on your machine (that you can’t
or won’t upgrade), and you have to work on this codebase that
requires a newer version of the toolchain.&lt;/li&gt;
  &lt;li&gt;You want to play with cross compilation features of Go 1.5
(for instance, to make sure that you can create OS X binaries
from a Linux system).&lt;/li&gt;
  &lt;li&gt;You want to have multiple versions of Go side-by-side, but don’t
want to completely litter your system.&lt;/li&gt;
  &lt;li&gt;You want to be 100% sure that your project and all its dependencies
download, build, and run fine on a clean system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of this is relevant to you, then let’s call Docker to the rescue!&lt;/p&gt;

&lt;h3 id=&quot;compiling-a-program-in-a-container&quot;&gt;Compiling a program in a container&lt;/h3&gt;

&lt;p&gt;When you have installed Go, you can do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get -v github.com/user/repo&lt;/code&gt; 
to download, build, and install a library. (The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-v&lt;/code&gt; flag is just
here for verbosity, you can remove it if you prefer your
toolchain to be swift and silent!)&lt;/p&gt;

&lt;p&gt;You can also do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get github.com/user/repo/...&lt;/code&gt; (yes, that’s 
three dots) to download, build, and install all the things in 
that repo (including libraries and binaries).&lt;/p&gt;

&lt;p&gt;We can do that in a container!&lt;/p&gt;

&lt;p&gt;Try this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run golang go get -v github.com/golang/example/hello/...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This will pull the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;golang&lt;/code&gt; image (unless you have it already;
then it will start right away), and create a container based on
that image. In that container, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go&lt;/code&gt; will download a little
“hello world” example, build it, and install it. But it will
install it in the container … So how do we run that program now?&lt;/p&gt;

&lt;h3 id=&quot;running-our-program-in-a-container&quot;&gt;Running our program in a container&lt;/h3&gt;

&lt;p&gt;One solution is to &lt;em&gt;commit&lt;/em&gt; the container that we just built,
i.e. “freeze” it into a new image:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container commit $(docker container ls -lq) awesomeness
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container ls -lq&lt;/code&gt; outputs the ID (and only the ID!) of
the last container that was executed. If you are the only
user on your machine, and if you haven’t created another
container since the previous command, that container
should be the one in which we just built the “hello world”
example.&lt;/p&gt;

&lt;p&gt;Now, we can run our program in a container based on
the image that we just built:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run awesomeness hello
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output should be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello, Go examples!&lt;/code&gt;.&lt;/p&gt;

&lt;h4 id=&quot;bonus-points&quot;&gt;Bonus points&lt;/h4&gt;

&lt;p&gt;When creating the image with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container commit&lt;/code&gt;, you can
use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--change&lt;/code&gt; flag to specify arbitrary &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot;&gt;Dockerfile&lt;/a&gt; commands.
For instance, you could use a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CMD&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ENTRYPOINT&lt;/code&gt; command
so that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run awesomeness&lt;/code&gt; automatically executes
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;running-in-a-throwaway-container&quot;&gt;Running in a throwaway container&lt;/h3&gt;

&lt;p&gt;What if we don’t want to create an extra image just to
run this Go program?&lt;/p&gt;

&lt;p&gt;We got you covered:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run --rm golang sh -c \
    &quot;go get github.com/golang/example/hello/... &amp;amp;&amp;amp; exec hello&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wait a minute, what are all those bells and whistles?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--rm&lt;/code&gt; tells to the Docker CLI to automatically issue a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container rm&lt;/code&gt; command once the container exits. That way,
we don’t leave anything behind ourselves.&lt;/li&gt;
  &lt;li&gt;We chain together the build step (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get&lt;/code&gt;) and the
execution step (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec hello&lt;/code&gt;) using the shell logical
operator &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt;. If you’re not a shell aficionado, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt;
means “and”. It will run the first part &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get...&lt;/code&gt;,
and if (and only if!) that part is successful, it will run
the second part (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec hello&lt;/code&gt;). If you wonder why this
is like that: it works like a lazy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;and&lt;/code&gt; evaluator,
which needs to evaluate the right hand side
only if the left hand side evaluates to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;We pass our commands to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sh -c&lt;/code&gt;, because if we were to
simply do &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container run golang &quot;go get ... &amp;amp;&amp;amp; hello&quot;&lt;/code&gt;,
Docker would try to execute the program named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go SPACE get
SPACE etc.&lt;/code&gt; and that wouldn’t work. So instead, we start
a shell and instruct the shell to execute the command
sequence.&lt;/li&gt;
  &lt;li&gt;We use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec hello&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt;: this will replace
the current process (the shell that we started) with the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt; program. This ensures that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt; will be PID 1
in the container, instead of having the shell as PID 1
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt; as a child process. This is totally useless
for this tiny example, but when we will run more useful
programs, this will allow them to receive external signals
properly, since external signals are delivered to PID 1 of
the container. What kind of signal, you might be wondering?
A good example is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker container stop&lt;/code&gt;, which sends &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SIGTERM&lt;/code&gt; to
PID 1 in the container.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;using-a-different-version-of-go&quot;&gt;Using a different version of Go&lt;/h3&gt;

&lt;p&gt;When you use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;golang&lt;/code&gt; image, Docker expands that to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;golang:latest&lt;/code&gt;, which (as you might guess) will map to
the latest version available on the Docker Hub.&lt;/p&gt;

&lt;p&gt;If you want to use a specific version of Go, that’s very
easy: specify that version as a &lt;em&gt;tag&lt;/em&gt; after the image name.&lt;/p&gt;

&lt;p&gt;For instance, to use Go 1.5, change the example above
to replace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;golang&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;golang:1.5&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run --rm golang:1.5 sh -c \
    &quot;go get github.com/golang/example/hello/... &amp;amp;&amp;amp; exec hello&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can see all the versions (and variants) available on the
&lt;a href=&quot;https://hub.docker.com/r/library/golang/tags/&quot;&gt;Golang image page&lt;/a&gt;
on the Docker Hub.&lt;/p&gt;

&lt;h3 id=&quot;installing-on-our-system&quot;&gt;Installing on our system&lt;/h3&gt;

&lt;p&gt;OK, so what if we want to run the compiled program on our
system, instead of in a container?&lt;/p&gt;

&lt;p&gt;We could copy the compiled binary out of the container.
Note, however, that this will work only if our container
architecture matches our host architecture; in other words,
if we run Docker on Linux. (I’m leaving out people who
might be running Windows Containers!)&lt;/p&gt;

&lt;p&gt;The easiest way to get the binary out of the container
is to map the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$GOPATH/bin&lt;/code&gt; directory to a local directory.
In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;golang&lt;/code&gt; container, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$GOPATH&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/go&lt;/code&gt;. So we can do
the following:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -v /tmp/bin:/go/bin \
  golang go get github.com/golang/example/hello/...
/tmp/bin/hello
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you are on Linux, you should see the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello, Go examples!&lt;/code&gt; message.
But if you are, for instance, on a Mac, you will probably see:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;/tmp/test/hello: cannot execute binary file
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;What can we do about it?&lt;/p&gt;

&lt;h3 id=&quot;cross-compilation&quot;&gt;Cross-compilation&lt;/h3&gt;

&lt;p&gt;Go 1.5 comes with &lt;a href=&quot;http://dave.cheney.net/2015/08/22/cross-compilation-with-go-1-5&quot;&gt;outstanding out-of-the-box cross-compilation abilities&lt;/a&gt;, so if your
container operating system and/or architecture doesn’t match your system’s,
it’s no problem at all!&lt;/p&gt;

&lt;p&gt;To enable cross-compilation, you need to set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GOOS&lt;/code&gt; and/or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GOARCH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For instance, assuming that you are on a 64 bits Mac:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -e GOOS=darwin -e GOARCH=amd64 -v /tmp/crosstest:/go/bin \
  golang go get github.com/golang/example/hello/...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output of cross-compilation is not directly in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$GOPATH/bin&lt;/code&gt;,
but in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$GOPATH/bin/$GOOS_$GOARCH&lt;/code&gt;. In other words, to run the
program, you have to execute &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp/crosstest/darwin_amd64/hello&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;installing-straight-to-the-path&quot;&gt;Installing straight to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PATH&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;If you are on Linux, you can even install directly to your system
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bin&lt;/code&gt; directories:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -v /usr/local/bin:/go/bin \
  golang go get github.com/golang/example/hello/...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;However, on a Mac, trying to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr&lt;/code&gt; as a volume will not
mount your Mac’s filesystem to the container. It will mount
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr&lt;/code&gt; directory of the Moby VM (the small Linux VM
hidden behind the Docker whale icon in your toolbar).&lt;/p&gt;

&lt;p&gt;You can, however, use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/tmp&lt;/code&gt; or something in your home
directory, and then copy it from there.&lt;/p&gt;

&lt;h2 id=&quot;building-lean-images&quot;&gt;Building lean images&lt;/h2&gt;

&lt;p&gt;The Go binaries that we produced with this technique are
&lt;em&gt;statically linked&lt;/em&gt;. This means that they embed all the code
that they need to run, including all dependencies. This
contrasts with &lt;em&gt;dynamically linked&lt;/em&gt; programs, which don’t
contain some basic libraries (like the “libc”) and use a system-wide
copy which is resolved at run time.&lt;/p&gt;

&lt;p&gt;This means that we can drop our Go compiled program in
a container, &lt;em&gt;without anything else&lt;/em&gt;, and it should work.&lt;/p&gt;

&lt;p&gt;Let’s try this!&lt;/p&gt;

&lt;h3 id=&quot;the-scratch-image&quot;&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scratch&lt;/code&gt; image&lt;/h3&gt;

&lt;p&gt;There is a special image in the Docker ecosystem: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scratch&lt;/code&gt;.
This is an empty image. It doesn’t need to be created or
downloaded, since by definition, it is empty.&lt;/p&gt;

&lt;p&gt;Let’s create a new, empty directory for our new Go lean image.&lt;/p&gt;

&lt;p&gt;In this new directory, create the following Dockerfile:&lt;/p&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; scratch&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ./hello /hello&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/hello&quot;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This means:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;start &lt;em&gt;from scratch&lt;/em&gt; (an empty image),&lt;/li&gt;
  &lt;li&gt;add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt; file to the root of the image,&lt;/li&gt;
  &lt;li&gt;define this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt; program to be the default thing to execute
when starting this container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, produce our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt; binary as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -v $(pwd):/go/bin --rm \
  golang go get github.com/golang/example/hello/...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note: we don’t need to set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GOOS&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GOARCH&lt;/code&gt; here, because
precisely, we want a binary that will run &lt;em&gt;in a Docker container&lt;/em&gt;,
not on our host system. So leave those variables alone!&lt;/p&gt;

&lt;p&gt;Then, we can build the image:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -t hello .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And test it:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run hello
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;(This should display &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Hello, Go examples!&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;Last but not least, check the image’s size:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image ls hello
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If we did everything right, this image should be about 2 MB. Not bad!&lt;/p&gt;

&lt;h3 id=&quot;building-something-without-pushing-to-github&quot;&gt;Building something without pushing to GitHub&lt;/h3&gt;

&lt;p&gt;Of course, if we had to push to GitHub each time we wanted to compile,
we would waste a lot of time.&lt;/p&gt;

&lt;p&gt;When you want to work on a piece of code and build it within a container,
you can mount a local directory to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/go&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;golang&lt;/code&gt; container, so that the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$GOPATH&lt;/code&gt; is persisted across invocations: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -v $HOME/go:/go golang ...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But you can also mount local directories to specific paths, to “override” some
packages (the ones that you have edited locally). Here is a complete example:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;# Adapt the two following environment variables if you are not running on a Mac
export GOOS=darwin GOARCH=amd64
mkdir go-and-docker-is-love
cd go-and-docker-is-love
git clone git://github.com/golang/example
cat example/hello/hello.go
sed -i .bak s/olleH/eyB/ example/hello/hello.go
docker container run --rm \
  -v $(pwd)/example:/go/src/github.com/golang/example \
  -v $(pwd):/go/bin/${GOOS}_${GOARCH} \
  -e GOOS -e GOARCH \
  golang go get github.com/golang/example/hello/...
./hello
# Should display &quot;Bye, Go examples!&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;the-special-case-of-the-net-package-and-cgo&quot;&gt;The special case of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net&lt;/code&gt; package and CGo&lt;/h2&gt;

&lt;p&gt;Before diving into real-world Go code, we have to confess something:
we lied a little bit about the static binaries. If you are using CGo,
or if you are using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net&lt;/code&gt; package, the Go linker will generate
a dynamic binary. In the case of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;net&lt;/code&gt; package (which a &lt;em&gt;lot&lt;/em&gt;
of useful Go programs out there are using indeed!), the main culprit
is the DNS resolver. Most systems out there have a fancy, modular name
resolution system (like the &lt;em&gt;Name Service Switch&lt;/em&gt;) which relies on
plugins which are, technically, dynamic libraries. By default,
Go will try to use that; and to do so, it will produce dynamic
libraries.&lt;/p&gt;

&lt;p&gt;How do we work around that?&lt;/p&gt;

&lt;h3 id=&quot;re-using-another-distros-libc&quot;&gt;Re-using another distro’s libc&lt;/h3&gt;

&lt;p&gt;One solution is to use a base image that &lt;em&gt;has&lt;/em&gt; the essential
libraries needed by those Go programs to function. Almost any
“regular” Linux distro based on the GNU libc will do the trick.
So instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM scratch&lt;/code&gt;, you would use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM debian&lt;/code&gt; or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM fedora&lt;/code&gt;, for instance. The resulting image will be much
bigger now; but at least, the bigger bits will be shared with
other images on your system.&lt;/p&gt;

&lt;p&gt;Note: you &lt;em&gt;cannot&lt;/em&gt; use Alpine
in that case, since Alpine is using the musl library instead 
of the GNU libc.&lt;/p&gt;

&lt;h3 id=&quot;bring-your-own-libc&quot;&gt;Bring your own libc&lt;/h3&gt;

&lt;p&gt;Another solution is to surgically extract the files needed,
and place them in your container with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt;. The resulting
container will be small. However, this extraction process
leaves the author with the uneasy impression of a really
dirty job, and they would rather not go into more details.&lt;/p&gt;

&lt;p&gt;If you want to see for yourself, look around &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ldd&lt;/code&gt; and the
Name Service Switch plugins mentioned earlier.&lt;/p&gt;

&lt;h3 id=&quot;producing-static-binaries-with-netgo&quot;&gt;Producing static binaries with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netgo&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;We can also instruct Go to &lt;em&gt;not&lt;/em&gt; use the system’s libc, and
substitute Go’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;netgo&lt;/code&gt; library, which comes with a native
DNS resolver.&lt;/p&gt;

&lt;p&gt;To use it, just add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-tags netgo -installsuffix netgo&lt;/code&gt; to
the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get&lt;/code&gt; options.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-tags netgo&lt;/code&gt; instructs the toolchain to use netgo.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-installsuffix netgo&lt;/code&gt; will make sure that the resulting
libraries (if any) are placed in a different, non-default
directory. This will avoid conflicts between code built
with and without netgo, if you do multiple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go get&lt;/code&gt;
(or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;go build&lt;/code&gt;) invocations. If you build in containers 
like we have shown so far, this is not strictly necessary,
since there will be no other Go code compiled in this
container, ever; but it’s a good idea to get used to it,
or at least know that this flag exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-special-case-of-ssl-certificates&quot;&gt;The special case of SSL certificates&lt;/h2&gt;

&lt;p&gt;There is one more thing that you have to worry about if
your code has to validate SSL certificates; for instance
if it will connect to external APIs over HTTPS. In that
case, you need to put the root certificates in your
container too, because Go won’t bundle those into your
binary.&lt;/p&gt;

&lt;h3 id=&quot;installing-the-ssl-certificates&quot;&gt;Installing the SSL certificates&lt;/h3&gt;

&lt;p&gt;Three again, there are multiple options available, but
the easiest one is to use a package from an existing
distribution.&lt;/p&gt;

&lt;p&gt;Alpine is a good candidate here because it’s so tiny.
The following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt; will give you a base image
that is small, but has an up-to-date bundle of root
certificates:&lt;/p&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; alpine:3.4&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apk add &lt;span class=&quot;nt&quot;&gt;--no-cache&lt;/span&gt; ca-certificates apache2-utils
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Check it out; the resulting image is only 6 MB!&lt;/p&gt;

&lt;p&gt;Note: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--no-cache&lt;/code&gt; option tells &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apk&lt;/code&gt; (the Alpine
package manager) to get the list of available packages
from Alpine’s distribution mirrors, without saving it
to disk. You might have seen Dockerfiles doing something
like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get update &amp;amp;&amp;amp; apt-get install ... &amp;amp;&amp;amp; rm -rf /var/cache/apt/*&lt;/code&gt;;
this achieves something equivalent (i.e. not leave package
caches in the final image) with a single flag.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;As an added bonus,&lt;/em&gt; putting your application in a container
based on the Alpine image gives you access to a ton of really
useful tools: now you can drop a shell into your container
and poke around while it’s running, if you need to!&lt;/p&gt;

&lt;h2 id=&quot;wrapping-it-up&quot;&gt;Wrapping it up&lt;/h2&gt;

&lt;p&gt;We saw how Docker can help us to compile Go code in a clean,
isolated environment; how to use different versions of the
Go toolchain; and how to cross-compile between different
operating systems and platforms.&lt;/p&gt;

&lt;p&gt;We also saw how Go can help us to build small, lean container
images for Docker, and described a number of associated
subtleties linked (no pun intended) to static libraries
and network dependencies.&lt;/p&gt;

&lt;p&gt;Beyond the fact that Go is really good fit for a project
that Docker, we hope that we showed you how Go and Docker
can benefit from each other and work really well together!&lt;/p&gt;

&lt;h3 id=&quot;acknowledgements&quot;&gt;Acknowledgements&lt;/h3&gt;

&lt;p&gt;This was initially presented during the hack day at GopherCon 2016.&lt;/p&gt;

&lt;p&gt;I would like to thank all the people who proofread this material
and gave ideas and suggestions to make it better; including but
not limited to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Aaron Lehmann&lt;/li&gt;
  &lt;li&gt;Stephen Day&lt;/li&gt;
  &lt;li&gt;AJ Bowen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All mistakes and typos are my own; all the good stuff is theirs! ☺&lt;/p&gt;

</description>
        <pubDate>Thu, 23 Feb 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/go-docker/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/go-docker/</guid>
        
        <category>linux</category>
        
        <category>developer</category>
        
        <category>golang</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Live Debugging Node.js with Docker</title>
        <description>&lt;p&gt;&lt;strong&gt;Note: This tutorial requires you to run your app locally on your own computer&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;pre-requisites&quot;&gt;Pre-requisites&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.docker.com/products/docker&quot;&gt;Docker for OSX, Docker for Windows, or Docker for Linux&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h3&gt;

&lt;p&gt;The first thing to notice is that you don’t actually need to have Node.js installed on your machine. You can just use Docker and your IDE. In this case we’re going to show you how to use Visual Studio Code.&lt;/p&gt;

&lt;p&gt;We’ve created a simple application which includes an error. You can see the app in the &lt;a href=&quot;https://github.com/docker/labs/tree/master/developer-tools/nodejs-debugging/app&quot;&gt;app/ directory&lt;/a&gt; of this repository. You can either clone this repository, or create the files yourself. Make sure they’re all in the same directory. You will need the following files:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;app.js&lt;/li&gt;
  &lt;li&gt;package.json&lt;/li&gt;
  &lt;li&gt;index.html&lt;/li&gt;
  &lt;li&gt;Dockerfile&lt;/li&gt;
  &lt;li&gt;docker-compose.yml&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.js&lt;/code&gt; defines a simple node app. It serves up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt;, refreshing every 2 seconds with quote from an array. Here’s what it looks like:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/browser-broken.gif&quot; alt=&quot;Image of Browser with quotations from app&quot; title=&quot;Image of a green background with quotes cycling through. Last image is just two quotation marks&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Let’s take a look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM node:5.11.0-slim

WORKDIR /code

RUN npm install -g nodemon

COPY package.json /code/package.json
RUN npm install &amp;amp;&amp;amp; npm ls
RUN mv /code/node_modules /node_modules

COPY . /code

CMD [&quot;npm&quot;, &quot;start&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see it installs &lt;a href=&quot;http://nodemon.io/&quot;&gt;nodemon&lt;/a&gt;, a utility that will monitor for any changes in your source and automatically restart your server.&lt;/p&gt;

&lt;p&gt;You’ll start the app with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;version: &quot;3&quot;

services:
  web:
    build: .
    command: nodemon --debug=5858
    volumes:
      - .:/code
    ports:
      - &quot;8000:8000&quot;
      - &quot;5858:5858&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A few things are going on here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;It defines a service called “web”, which uses the image built from the Dockerfile in the current directory.&lt;/li&gt;
  &lt;li&gt;It overrides the command specified in the Dockerfile to enable the remote debugging feature built into Node.js. We do that here because when you ship this application’s container image to production, you don’t want the debugger enabled – it’s a development-only override.&lt;/li&gt;
  &lt;li&gt;It overwrites the application code in the container by mounting the current directory as a volume. This means that the code inside the running container will update whenever you update the local files on your hard drive. This is very useful, as it means you don’t have to rebuild the image every time you make a change to the application.&lt;/li&gt;
  &lt;li&gt;It maps port 8000 inside the container to port 8000 on localhost, so you can actually visit the application.&lt;/li&gt;
  &lt;li&gt;Finally, it maps port 5858 inside the container to the same port on localhost, so you can connect to the remote debugger.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;run-the-app&quot;&gt;Run the app&lt;/h3&gt;
&lt;p&gt;Using your terminal, navigate to the directory with the app files and start up the app:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ docker-compose up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Docker Compose will build the image and start a container for the app. You should see this output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Creating network &quot;nodeexample_default&quot; with the default driver
Creating nodeexample_web_1
Attaching to nodeexample_web_1
web_1  | [nodemon] 1.9.2
web_1  | [nodemon] to restart at any time, enter `rs`
web_1  | [nodemon] watching: *.*
web_1  | [nodemon] starting `node --debug=5858 app.js`
web_1  | Debugger listening on port 5858
web_1  | HTTP server listening on port 80
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The app is now running. Open up &lt;a href=&quot;http://localhost:8000&quot;&gt;http://localhost:8000/&lt;/a&gt; to see it in action, and take a moment to appreciate the poetry.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/browser-broken.gif&quot; alt=&quot;Image of Browser with quotations from app&quot; title=&quot;Image of a green background with quotes cycling through. Last image is just two quotation marks&quot; /&gt;&lt;/p&gt;

&lt;p&gt;It’s undoubtedly beautiful, but the problem is obvious: we’re outputting a blank message at the end before cycling back to the first line. It’s time to debug.&lt;/p&gt;

&lt;h3 id=&quot;start-debugging&quot;&gt;Start debugging&lt;/h3&gt;
&lt;p&gt;Open up the app directory in VSCode. Head over to the debugger by clicking the bug icon in the left-hand sidebar.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/debugger-icon.png&quot; alt=&quot;Image of VS Code with debugger icon highlighted&quot; title=&quot;Image of Visual Studio Code with debugger icon highlighted&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Create a boilerplate debugger config by clicking the gear icon and selecting “Node.js” in the dropdown.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/gear-icon.png&quot; alt=&quot;Image of VS Code with gear icon highlighted&quot; title=&quot;Image of Visual Studio Code with gear icon highlighted&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/dropdown.png&quot; alt=&quot;Image of VS Code dropdown list&quot; title=&quot;Image of Visual Studio Code dropdown list&quot; /&gt;&lt;/p&gt;

&lt;p&gt;A JSON file will be created and displayed. Replace its contents with the following:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    &quot;version&quot;: &quot;0.2.0&quot;,
    &quot;configurations&quot;: [
        {
            &quot;name&quot;: &quot;Attach&quot;,
            &quot;type&quot;: &quot;node&quot;,
            &quot;request&quot;: &quot;attach&quot;,
            &quot;port&quot;: 5858,
            &quot;address&quot;: &quot;localhost&quot;,
            &quot;restart&quot;: true,
            &quot;sourceMaps&quot;: false,
            &quot;outDir&quot;: null,
            &quot;localRoot&quot;: &quot;${workspaceRoot}&quot;,
            &quot;remoteRoot&quot;: &quot;/code&quot;
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;There are three important changes here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The whole “Launch” config has been deleted – you’re using Compose to launch the app, not VSCode, so it’s unnecessary.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;restart&lt;/code&gt; is set to true, so that the debugger re-attaches when the app restarts.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remoteRoot&lt;/code&gt; is set to the path of the code directory inside the container, because it’s almost certainly different than the path to the code on your machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the “Attach” config selected, click the “play” button to start the debugger.
&lt;img src=&quot;../images/attach.png&quot; alt=&quot;Image of VS Code attach icon&quot; title=&quot;Image of Visual Studio Code attach icon&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now go back to app.js and find the line that reads &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lineIndex += 1&lt;/code&gt; line, just after we initialize the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;message&lt;/code&gt; variable. Set a breakpoint by clicking in the gutter, just to the left of the line number.
&lt;img src=&quot;../images/breakpoint.png&quot; alt=&quot;Image of VS Code breakpoint&quot; title=&quot;Image of Visual Studio Code breakpoint&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If your browser window is still open and refreshing, in a second or two you should see it hit the breakpoint. If not, go back and refresh it – VSCode will pop back to the front as soon as the debugger hits it.&lt;/p&gt;

&lt;p&gt;Hit the Play button at the top to resume code execution. It’ll hit the breakpoint every time the browser refreshes, which is every 2 seconds. You can see it cycling through the lines, and then the bug shows up – right after the last line, message gets set to undefined.
&lt;img src=&quot;../images/hitting-breakpoint.gif&quot; alt=&quot;Animated image of VS Code hititng breakpoint&quot; title=&quot;Animated image of VS Code hititng breakpoint&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The reason becomes clear if you open up the “Closure” section under “VARIABLES”: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lineIndex&lt;/code&gt; has incremented to 4 – the length of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;LINES&lt;/code&gt; array – when it should have been reset after getting to 3. We’ve got an off-by-one error.
&lt;img src=&quot;../images/variables.png&quot; alt=&quot;Image of VS Code lineIndex value&quot; title=&quot;Image of Visual Studio Code lineIndex value&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;fix-the-bug&quot;&gt;Fix the bug&lt;/h3&gt;
&lt;p&gt;Replace the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt; &lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;=&lt;/code&gt; in the conditional on the next line:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/fixing-line.png&quot; alt=&quot;Image of VS Code line 24 to fix&quot; title=&quot;Image of Visual Studio Code line 24 to fix&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now save the file. A second or two later, you should see the debugger detach and then reattach (the yellow line highlighting the breakpoint will disappear and reappear). This is because several things have just happened:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Upon saving the file, Docker detected the filesystem change event and proxied it through to the container.&lt;/li&gt;
  &lt;li&gt;nodemon detected the event and restarted the application. You can confirm this by looking at your terminal: there should be a line that reads “restarting due to changes…”&lt;/li&gt;
  &lt;li&gt;Finally, VSCode detected that the remote debugger had gone away and reattached.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The debugger is now attached again. However, your browser tab might have errored out – go refresh it if so.&lt;/p&gt;

&lt;p&gt;You can now step through the debugger once again and see that the lines cycle properly – no more &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/attach.png&quot; alt=&quot;Animated image of VS Code hitting the breakpoint with line fixed&quot; title=&quot;Animated image of Visual Studio Code hitting the breakpoint with line fixed&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Remove the breakpoint and detach the debugger by clicking the stop button. Go back to the browser window and enjoy the updated experience.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/attach.png&quot; alt=&quot;Animated image of browser without error&quot; title=&quot;Animated image of browser without error&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And that’s it, you’re done!&lt;/p&gt;

&lt;p class=&quot;quiz&quot;&gt;True or false: You have to restart a container after you make changes to the code or they won’t be reflected in the application&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;( ) True&lt;/li&gt;
  &lt;li&gt;(x) False&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;quiz&quot;&gt;True or false: Debugging a NodeJS app running in a container requires a special plugin for the IDE&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;( ) True&lt;/li&gt;
  &lt;li&gt;(x) False&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 22 Feb 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/nodejs-live-debugging/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/nodejs-live-debugging/</guid>
        
        <category>desktop</category>
        
        <category>linux</category>
        
        <category>windows</category>
        
        <category>developer</category>
        
        <category>nodejs</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>In-container Java Development: Netbeans</title>
        <description>&lt;p&gt;&lt;strong&gt;Note: This tutorial requires you to run your app locally on your own computer&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;pre-requisites&quot;&gt;Pre-requisites&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.docker.com/products/docker&quot;&gt;Docker for OSX or Docker for Windows&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://netbeans.org/downloads/&quot;&gt;NetBeans IDE&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&quot;&gt;Java Development Kit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h3&gt;

&lt;p&gt;Using your git client clone the repository.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/docker/labs
cd labs/developer-tools/java-debugging
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Open NetBeans IDE, Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open Project...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_open_project_menu.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app&lt;/code&gt; and click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open Project&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_open_project_app.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;building-the-application&quot;&gt;Building the application&lt;/h3&gt;

&lt;p&gt;The application is a basic Spring MVC application that receives user input from a form, writes the data to a database, and queries the database.&lt;/p&gt;

&lt;p&gt;The application is built using Maven. To build the application click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Build Project&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_build_project_menu.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The results of the build will be displayed in the console.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_build_project_console.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;running-the-application&quot;&gt;Running the application&lt;/h3&gt;

&lt;p&gt;Open a terminal and go to the application directory. Start the application with docker-compose&lt;/p&gt;

&lt;pre&gt;&amp;gt; docker-compose up &lt;/pre&gt;

&lt;p&gt;Docker will build the images for Apache Tomcat and MySQL and start the containers. It will also mount the application directory (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./app/target/UserSignup&lt;/code&gt;) as a data volume on the host system to the Tomcat webapps directory in the web server container.&lt;/p&gt;

&lt;p&gt;Open a browser window and go to:
‘localhost:8080’; you should see the Tomcat home page&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/tomcat_home3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When the Tomcat image was built, the user roles were also configured. Click on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Manager App&lt;/code&gt; button to see the deployed applications. When prompted for username and password, enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manager&lt;/code&gt; respectively to log into the Tomcat Web Application Manager page.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/tomcat_web_application_manager3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can use the Manager page to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Start&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stop&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reload&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Undeploy&lt;/code&gt; web applications.&lt;/p&gt;

&lt;p&gt;To go to the application, Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/UserSignup&lt;/code&gt; link.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_index_page3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;debugging-the-application&quot;&gt;Debugging the Application&lt;/h3&gt;

&lt;p&gt;In the application, click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Signup&lt;/code&gt; to create a new user. Fill out the registration form and click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Submit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_signup2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Yes&lt;/code&gt; to confirm.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_signup_confirm.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Test out the login.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_login2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oh no!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_login_fail2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;configure-remote-debugging&quot;&gt;Configure Remote Debugging&lt;/h4&gt;

&lt;p&gt;In the menu click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debug&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Attach Debugger...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_debug_attach_debugger_menu.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Make sure that the port is set to 8000, click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OK&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_debug_attach_debugger_configure.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;finding-the-error&quot;&gt;Finding the Error&lt;/h4&gt;

&lt;p&gt;Since the problem is with the password, lets see how the password is set in the User class. In the User class, the setter for password is scrambled using &lt;a href=&quot;https://en.wikipedia.org/wiki/ROT13&quot;&gt;rot13&lt;/a&gt; before it is saved to the database.&lt;/p&gt;

&lt;p&gt;Since we enabled remote debugging earlier, you should see the Daemon Threads for Tomcat in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debugging&lt;/code&gt; window. Set a breakpoint for in the User class where the password is set.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_debug_User_breakpoint.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Register a new user with the username of ‘Moby’ and with ‘m0by’ as the password, click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Submit&lt;/code&gt;, click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_register_moby2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;NetBeans will display the code at the breakpoint and the value of password in the variables window. Note that the value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m0by&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_debug_User_moby.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Continue&lt;/code&gt; icon or press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt; to let the code run.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_debug_resume.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next, set a breakpoint on the getPassword in the User class to see the value returned for password. You can also toggle off the breakpoint for setPassword. Try to log into the application. Look at the value for password in the NetBeans variables window, note that it is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z0ol&lt;/code&gt; which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m0by&lt;/code&gt; using ROT13.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_debug_User_show_user.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this MVC application the UserController uses the findByLogin method in the UserServiceImpl class which uses the findByUsername method to retrieve the information from the database. It then checks to see if the password from the form matches the user password. Since the password from the login form is not scrambled using ROT13, it does not match the user password and you cannot log into the application.&lt;/p&gt;

&lt;p&gt;To fix this, apply ROT13 to the password by adding&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import com.docker.UserSignup.utit.Rot13

String passwd = Rot13.rot13(password);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;../images/netbeans_debug_UserServiceImpl_code.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Set a breakpoint in UserServiceImpl on the findByLogin method. Press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F11&lt;/code&gt; or click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Build Project&lt;/code&gt; to update the deployed code. Log in again and look at the values for the breakpoint. The ‘passwd’ variable is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z0ol&lt;/code&gt; which matches the password for the user moby.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/netbeans_debug_UserServiceImpl_values.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Continue (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F5&lt;/code&gt;) and you should successfully log in.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_success.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;quiz&quot;&gt;True or false: You have to restart a container after you make changes to the code or they won’t be reflected in the application&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;( ) True&lt;/li&gt;
  &lt;li&gt;(x) False&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;quiz&quot;&gt;True or false: Debugging a Java app running in a container requires a special plugin for the IDE&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;( ) True&lt;/li&gt;
  &lt;li&gt;(x) False&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 22 Feb 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/java-debugging-netbeans/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/java-debugging-netbeans/</guid>
        
        <category>desktop</category>
        
        <category>linux</category>
        
        <category>windows</category>
        
        <category>developer</category>
        
        <category>java</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>In-container Java Development: Intellij</title>
        <description>&lt;p&gt;&lt;strong&gt;Note: This tutorial requires you to run your app locally on your own computer&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;pre-requisites&quot;&gt;Pre-requisites&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.docker.com/products/docker&quot;&gt;Docker for OSX or Docker for Windows&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.jetbrains.com/idea/download/&quot;&gt;IntelliJ Community Edition&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&quot;&gt;Java Development Kit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h3&gt;

&lt;p&gt;In IntelliJ, clone the repository. Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Check out from Version Control&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Github&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intelliJ_git_open_project.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If this the first time to use IntelliJ with Github, log into your Github account.
&lt;img src=&quot;../images/intelliJ_git_login.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On the command line clone the &lt;a href=&quot;https://github.com/docker/labs&quot;&gt;docker/labs&lt;/a&gt; repository&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intelliJ_git_clone_repository1.png&quot; alt=&quot;&quot; /&gt;
Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Import project from external model&lt;/code&gt;, select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Maven&lt;/code&gt;. Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_github_import_maven.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Search for projects recursively&lt;/code&gt;. Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_github_import_maven_configure.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select the project and click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_github_import_maven_select.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select the JDK(set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JDK home path&lt;/code&gt;) and click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_github_import_select_sdk.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Finish&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_github_import_project_finish.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Project View&lt;/code&gt; to open the project.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intelliJ_git_open_project_gui.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;building-the-application&quot;&gt;Building the application&lt;/h3&gt;

&lt;p&gt;The application is a basic Spring MVC application that receives user input from a form, writes the data to a database, and queries the database.&lt;/p&gt;

&lt;p&gt;The application is built using Maven. To build the application click on icon on the bottom left of the IntelliJ window and select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Maven Projects&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_maven_setup.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Maven Projects&lt;/code&gt; window will open on the right side. Maven goals of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install&lt;/code&gt; need to be set to build the application.&lt;/p&gt;

&lt;p&gt;To set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean&lt;/code&gt; goal, click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Lifecycle&lt;/code&gt; to display the tree of goals. Right click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean&lt;/code&gt; and select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create &apos;UserSignup [clean]&apos;...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_maven_goal_clean.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OK&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create Run/Debug Configuration&lt;/code&gt; window.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_maven_goal_clean_menu.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Configure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install&lt;/code&gt; goal similarly. Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install&lt;/code&gt; in the Lifecycle tree. Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create &apos;UserSignup[install]&apos;...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_maven_goal_install.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OK&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Create Run/Debug Configuration&lt;/code&gt; window.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intelligj_maven_goal_install_menu.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To build the application run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_maven_goal_clean_run.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Then run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;install&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_maven_goal_install_run.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When the application builds, you will see a success message in the log window.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_maven_goal_install_log.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;running-the-application&quot;&gt;Running the application&lt;/h3&gt;

&lt;p&gt;Open a terminal and go to the application directory. Start the application with docker-compose&lt;/p&gt;

&lt;pre&gt;&amp;gt; docker-compose up &lt;/pre&gt;

&lt;p&gt;Docker will build the images for Apache Tomcat and MySQL then start the containers. It will also mount the application directory (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./app/target/UserSignup&lt;/code&gt;) as a data volume on the host system to the Tomcat webapps directory in the web server container.&lt;/p&gt;

&lt;p&gt;Open a browser window and go to:
‘localhost:8080’; you should see the Tomcat home page&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/tomcat_home3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When the Tomcat image was built, the user roles were also configured. Click on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Manager App&lt;/code&gt; button to see the deployed applications. When prompted for username and password, enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manager&lt;/code&gt; respectively to log into the Tomcat Web Application Manager page.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/tomcat_web_application_manager3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can use the Manager page to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Start&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stop&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reload&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Undeploy&lt;/code&gt; web applications.&lt;/p&gt;

&lt;p&gt;To go to the application, Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/UserSignup&lt;/code&gt; link.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_index_page3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;debugging-the-application&quot;&gt;Debugging the Application&lt;/h3&gt;

&lt;p&gt;In the application, click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Signup&lt;/code&gt; to create a new user. Fill out the registration form and click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Submit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_signup2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Yes&lt;/code&gt; to confirm.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_signup_confirm.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Test out the login.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_login2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oh no!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_login_fail2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;configure-remote-debugging&quot;&gt;Configure Remote Debugging&lt;/h4&gt;

&lt;p&gt;Tomcat supports remote debugging the Java Platform Debugger Architecture (JPDA). Remote debugging was enabled when the tomcat image (registration-webserver) was built.&lt;/p&gt;

&lt;p&gt;To configure remote debugging in IntelliJ, click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Edit Configuration ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intelij_debug_run_edit_configurations.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Add a new remote configuration.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_add_remote_configuration.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run\Debug Configurations&lt;/code&gt; window, set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Name&lt;/code&gt; of the configuration as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker tomcat&lt;/code&gt; and in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Settings&lt;/code&gt; set the port to ‘8000’ as the default Tomcat JPDA debuging port. Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OK&lt;/code&gt; to save the configuration.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_tomcat_remote_settings.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;finding-the-error&quot;&gt;Finding the Error&lt;/h4&gt;

&lt;p&gt;Since the problem is with the password, let’s see how the password is set in the User class. In the User class, the setter for password is scrambled using &lt;a href=&quot;https://en.wikipedia.org/wiki/ROT13&quot;&gt;rot13&lt;/a&gt; before it is saved to the database.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_User_password.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Try registering a new user using the debugger. In the menu click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debug...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_run_debug.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Choose the remote Tomcat debug configuration. The Debugger console will be displayed at the bottom of the IntelliJ window.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_choose_remote_tomcat.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Set a breakpoint in the User class where the password is set.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_set_breakpoint_password.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Register a new user with the username of ‘Moby’ and with ‘m0by’ as the password, click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Submit&lt;/code&gt;, click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_register_moby2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;IntelliJ will display the code at the breakpoint and the value of password in the variables window. Note that the value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m0by&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_User_moby.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Resume Program&lt;/code&gt; to let the code run or press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F8&lt;/code&gt; to step over the breakpoint.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_resume.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next, set a breakpoint on the getPassword in the User class to see the value returned for password. You can also toggle off the breakpoint for setPassword.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_User_getPassword.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Try to log into the application. Look at the value for password in the debugging console, note that it is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z0ol&lt;/code&gt; which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m0by&lt;/code&gt; using ROT13.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_User_show_user.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this MVC application the UserController uses the findByLogin method in the UserServiceImpl class which uses the findByUsername method to retrieve the information from the database. It then checks to see if the password from the form matches the user password. Since the password from the login form is not scrambled using ROT13, it does not match the user password and you cannot log into the application.&lt;/p&gt;

&lt;p&gt;To fix this, apply ROT13 to the password by adding&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import com.docker.UserSignup.utit.Rot13

String passwd = Rot13.rot13(password);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_UserServiceImpl_code.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Set a breakpoint in UserServiceImpl on the findByLogin method. Log in again and look at the values for the breakpoint. The ‘passwd’ variable is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z0ol&lt;/code&gt; which matches the password for the user moby.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/intellij_debug_UserServiceImpl_values.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Continue (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F8&lt;/code&gt;) and you should successfully log in.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_success.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;quiz&quot;&gt;True or false: You have to restart a container after you make changes to the code or they won’t be reflected in the application&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;( ) True&lt;/li&gt;
  &lt;li&gt;(x) False&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;quiz&quot;&gt;True or false: Debugging a Java app running in a container requires a special plugin for the IDE&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;( ) True&lt;/li&gt;
  &lt;li&gt;(x) False&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 22 Feb 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/java-debugging-intellij/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/java-debugging-intellij/</guid>
        
        <category>desktop</category>
        
        <category>linux</category>
        
        <category>windows</category>
        
        <category>developer</category>
        
        <category>java</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>In-container Java Development: Eclipse</title>
        <description>&lt;p&gt;&lt;strong&gt;Note: This tutorial requires you to run your app locally on your own computer&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;pre-requisites&quot;&gt;Pre-requisites&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.docker.com/products/docker&quot;&gt;Docker for OSX or Docker for Windows&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.eclipse.org/downloads/&quot;&gt;Eclipse&lt;/a&gt; (install Eclipse IDE for Java EE Developers)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html&quot;&gt;Java Development Kit&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.eclipse.org/m2e/&quot;&gt;Maven for Eclipse&lt;/a&gt; (see instructions for adding the Maven plug-in to Eclipse)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h3&gt;

&lt;p&gt;On the command line clone the &lt;a href=&quot;https://github.com/docker/labs&quot;&gt;registration-docker&lt;/a&gt; repository&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/docker/labs
cd labs/developer-tools/java-debugging
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In Eclipse, import the app directory of that project as an existing maven project&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;File&lt;/code&gt;&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Import&lt;/code&gt; Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Maven&lt;/code&gt;&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Existing Maven Projects&lt;/code&gt;&amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Next&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_import_existing_maven_project_1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select the app subdirectory of the directory where you cloned the project.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_import_existing_maven_project_2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select the pom.xml from the app directory, click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Finish&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_import_existing_maven_project_3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;building-the-application&quot;&gt;Building the application&lt;/h3&gt;

&lt;p&gt;The application is a basic Spring MVC application that receives user input from a form, writes the data to a database, and queries the database.&lt;/p&gt;

&lt;p&gt;The application is built using Maven. To build the application click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run configurations&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_maven_run_config3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Maven build&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;New&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_maven_build_new.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Enter a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Name&lt;/code&gt; for the configuration.&lt;/p&gt;

&lt;p&gt;Set the base direct of the application &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;path&amp;gt;/registration-docker/app&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Set the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Goals&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clean install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Apply&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_maven_run_config_apply.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The results of the build will be displayed in the console.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_maven_console_build_result.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;running-the-application&quot;&gt;Running the application&lt;/h3&gt;

&lt;p&gt;Open a terminal and go to the application directory. Start the application with docker-compose&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Docker will build the images for Apache Tomcat and MySQL and start the containers. It will also mount the application directory (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;./app/target/UserSignup&lt;/code&gt;) as a data volume on the host system to the Tomcat webapps directory in the web server container.&lt;/p&gt;

&lt;p&gt;Open a browser window and go to:
‘localhost:8080’; you should see the Tomcat home page&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/tomcat_home3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When the Tomcat image was built, the user roles were also configured. Click on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Manager App&lt;/code&gt; button to see the deployed applications. When prompted for username and password, enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;system&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manager&lt;/code&gt; respectively to log into the Tomcat Web Application Manager page.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/tomcat_web_application_manager3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can use the Manager page to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Start&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Stop&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Reload&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Undeploy&lt;/code&gt; web applications.&lt;/p&gt;

&lt;p&gt;To go to the application, Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/UserSignup&lt;/code&gt; link.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_index_page3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;debugging-the-application&quot;&gt;Debugging the Application&lt;/h3&gt;

&lt;p&gt;In the application, click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Signup&lt;/code&gt; to create a new user. Fill out the registration form and click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Submit&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_signup2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Yes&lt;/code&gt; to confirm.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_signup_confirm.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Test out the login.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_login2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Oh no!&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_login_fail2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;configure-remote-debugging&quot;&gt;Configure Remote Debugging&lt;/h4&gt;

&lt;p&gt;Tomcat supports remote debugging the Java Platform Debugger Architecture (JPDA). Remote debugging was enabled when the tomcat image (registration-webserver) was built.&lt;/p&gt;

&lt;p&gt;To configure remote debugging in Eclipse, click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Run&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debug Configurations ...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_configure2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Remote Java Application&lt;/code&gt; and click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Launch New Configuration&lt;/code&gt; icon&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_configure_new.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Enter a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Name&lt;/code&gt; for the configuration. Select the project using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;browse&lt;/code&gt; button. Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Apply&lt;/code&gt; to save the configuration and click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debug&lt;/code&gt; to start the debugging connection between Tomcat and Eclipse.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_configure_docker.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;finding-the-error&quot;&gt;Finding the Error&lt;/h4&gt;

&lt;p&gt;Since the problem is with the password, lets see how the password is set in the User class. In the User class, the setter for password is scrambled using &lt;a href=&quot;https://en.wikipedia.org/wiki/ROT13&quot;&gt;rot13&lt;/a&gt; before it is saved to the database.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_User_password.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Try registering a new user using the debugger. In Eclipse, change the view or Perspective to the debugger by clicking on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Window&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Perspective&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Open Perspective&lt;/code&gt; &amp;gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Debug&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_perspective.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Eclipse will switch to the debug perspective. Since we enable remote debugging earlier, you should see the Daemon Threads for Tomcat in the debug window. Set a breakpoint for in the User class where the password is set.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_User_breakpoint.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Register a new user with the username of ‘Moby’ and with ‘m0by’ as the password, click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Submit&lt;/code&gt;, click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;yes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_register_moby2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Eclipse will display the code at the breakpoint and the value of password in the variables window. Note that the value is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m0by&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_User_moby.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Click on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resume&lt;/code&gt; or press &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F8&lt;/code&gt; to let the code run.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_resume.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Next, set a breakpoint on the getPassword in the User class to see the value returned for password. You can also toggle off the breakpoint for setPassword.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_User_getPassword.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Try to log into the application. Look at the value for password in the Eclipse variables window, note that it is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z0ol&lt;/code&gt; which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m0by&lt;/code&gt; using ROT13.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_User_show_user.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this MVC application the UserController uses the findByLogin method in the UserServiceImpl class which uses the findByUsername method to retrieve the information from the database. It then checks to see if the password from the form matches the user password. Since the password from the login form is not scrambled using ROT13, it does not match the user password and you cannot log into the application.&lt;/p&gt;

&lt;p&gt;To fix this, apply ROT13 to the password by adding&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import com.docker.UserSignup.utit.Rot13

String passwd = Rot13.rot13(password);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_UserServiceImpl_code.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Set a breakpoint in UserServiceImpl on the findByLogin method. Log in again and look at the values for the breakpoint. The ‘passwd’ variable is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;z0ol&lt;/code&gt; which matches the password for the user moby.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/eclipse_debug_UserServiceImpl_values.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Continue (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;F8&lt;/code&gt;) and you should successfully log in.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../images/app_debug_success.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p class=&quot;quiz&quot;&gt;True or false: You have to restart a container after you make changes to the code or they won’t be reflected in the application&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;( ) True&lt;/li&gt;
  &lt;li&gt;(x) False&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;quiz&quot;&gt;True or false: Debugging a Java app running in a container requires a special plugin for the IDE&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;( ) True&lt;/li&gt;
  &lt;li&gt;(x) False&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Wed, 22 Feb 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/java-debugging-eclipse/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/java-debugging-eclipse/</guid>
        
        <category>desktop</category>
        
        <category>linux</category>
        
        <category>windows</category>
        
        <category>developer</category>
        
        <category>java</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Swarm stack introduction</title>
        <description>&lt;p&gt;Let’s deploy the voting app stack on a swarm.&lt;/p&gt;

&lt;h2 id=&quot;purpose&quot;&gt;Purpose&lt;/h2&gt;

&lt;p&gt;The purpose of this lab is to illustrate how to deploy a stack (multi services application) against a Swarm using a docker compose file.&lt;/p&gt;

&lt;h2 id=&quot;the-application&quot;&gt;The application&lt;/h2&gt;

&lt;p&gt;The voting app is a very handy multi containers application often used for demo purposes during meetup and conferences.&lt;/p&gt;

&lt;p&gt;It basically allow users to vote between cat and dog (but could be “space” or “tab” too if you feel like it).&lt;/p&gt;

&lt;p&gt;This application is available on Github and updated very frequently when new features are developed.&lt;/p&gt;

&lt;h2 id=&quot;init-your-swarm&quot;&gt;Init your swarm&lt;/h2&gt;

&lt;p&gt;There should be two terminals displayed. The first accesses the swarm &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manager&lt;/code&gt; node and the second accesses the swarm &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker&lt;/code&gt; node once the swarm is created.&lt;/p&gt;

&lt;p&gt;Let’s create a Docker Swarm first. Copy the below command into the first terminal.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr $(hostname -i)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;From the output above, copy the join command (&lt;em&gt;watch out for newlines&lt;/em&gt;) and paste it in the other terminal.&lt;/p&gt;

&lt;h2 id=&quot;show-members-of-swarm&quot;&gt;Show members of swarm&lt;/h2&gt;

&lt;p&gt;From the first terminal, check the number of nodes in the swarm (running this command from the second terminal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker&lt;/code&gt; will fail as swarm related commands need to be issued against a swarm manager).&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The above command should output 2 nodes, the first one being the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;manager&lt;/code&gt;, and the second one a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;clone-the-voting-app&quot;&gt;Clone the voting-app&lt;/h2&gt;

&lt;p&gt;Let’s retrieve the voting app code from Github and go into the application folder.&lt;/p&gt;

&lt;p&gt;Ensure you are in the first terminal and do the below:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git clone https://github.com/docker/example-voting-app
cd example-voting-app
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;deploy-a-stack&quot;&gt;Deploy a stack&lt;/h2&gt;

&lt;p&gt;A stack is a group of services that are deployed together.
The docker-stack.yml in the current folder will be used to deploy the voting app as a stack.&lt;/p&gt;

&lt;p&gt;Ensure you are in the first terminal and do the below:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy --compose-file=docker-stack.yml voting_stack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note: being able to create a stack from a docker compose file is a great feature added in Docker 1.13.&lt;/p&gt;

&lt;p&gt;Check the stack deployed from the first terminal&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output should be the following one. It indicates the 6 services of the voting app’s stack (named voting_stack) have been deployed.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NAME          SERVICES
voting_stack  6
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s check the service within the stack&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack services voting_stack
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output should be like the following one (your ID should be different though).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME                     MODE        REPLICAS  IMAGE
10rt1wczotze  voting_stack_visualizer  replicated  1/1       dockersample
s/visualizer:stable
8lqj31k3q5ek  voting_stack_redis       replicated  2/2       redis:alpine
nhb4igkkyg4y  voting_stack_result      replicated  2/2       dockersample
s/examplevotingapp_result:before
nv8d2z2qhlx4  voting_stack_db          replicated  1/1       postgres:9.4
ou47zdyf6cd0  voting_stack_vote        replicated  2/2       dockersample
s/examplevotingapp_vote:before
rpnxwmoipagq  voting_stack_worker      replicated  1/1       dockersample
s/examplevotingapp_worker:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s list the tasks of the vote service.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps voting_stack_vote
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should get an output like the following one where the 2 tasks (replicas) of the service are listed.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME                 IMAGE
      NODE   DESIRED STATE  CURRENT STATE           ERROR  PORTS
my7jqgze7pgg  voting_stack_vote.1  dockersamples/examplevotingapp_vote:be
fore  node1  Running        Running 56 seconds ago
3jzgk39dyr6d  voting_stack_vote.2  dockersamples/examplevotingapp_vote:be
fore  node2  Running        Running 58 seconds ago
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From the NODE column, we can see one task is running on each node.&lt;/p&gt;

&lt;p&gt;Finally, we can check that our &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;5000&quot;&gt;APP&lt;/a&gt; is running, the &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;5001&quot;&gt;RESULT&lt;/a&gt; page is also available as well as &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;SWARM VISUALIZER&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Using only a couple of commands enables to deploy a stack of services on a Docker Swarm using the really great Docker Compose file format.&lt;/p&gt;

&lt;p class=&quot;quiz&quot;&gt;What is a stack?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;(x) a multi-service app running on a Swarm&lt;/li&gt;
  &lt;li&gt;( ) a way of creating multiple nodes&lt;/li&gt;
  &lt;li&gt;( ) a method of using multiple compose files to run an app&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;quiz&quot;&gt;A stack can:&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;be deployed from the commandline&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;can use the compose file format to deploy&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;can run a Dockerfile&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; /&gt;be used to manage your hosts&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;can be used to manage services over multiple nodes&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sat, 28 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/swarm-stack-intro/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/swarm-stack-intro/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>swarm</category>
        
        <category>community</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker images deeper dive</title>
        <description>&lt;h1 id=&quot;lets-have-fun-with-docker-images&quot;&gt;Let’s have fun with Docker images&lt;/h1&gt;

&lt;p&gt;In this lab we will see how to create an image from a container. Even if this is not used very often it’s interesting to try it at least once.
Then we will focus on the image creation using a Dockerfile. We will then see how to get the details of an image through the inspection and explore the filesystem to have a better understanding of what happens behind the hood. We will end this lab with the image API.&lt;/p&gt;

&lt;h2 id=&quot;image-creation-from-a-container&quot;&gt;Image creation from a container&lt;/h2&gt;

&lt;p&gt;Let’s start by running an interactive shell in a ubuntu container.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -ti ubuntu bash
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As we’ve done in the previous lab, we will install the figlet package in this container.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;apt-get update
apt-get install -y figlet
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We then exit from this container&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;exit
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Get the ID of this container using the ls command (do not forget the -a option as the non running container are not returned by the ls command).&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls -a
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Run the following command, using the ID retreived, in order to commit the container and create an image out of it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker container commit CONTAINER_ID
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once it has been commited, we can see the newly created image in the list of available images.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;From the previous command, get the ID of the newly created image and tag it so it’s named &lt;strong&gt;ourfiglet&lt;/strong&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker image tag IMAGE_ID ourfiglet
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we will run a container based on the newly created image named &lt;strong&gt;ourfiglet&lt;/strong&gt;, and specify the command to be ran such as it uses the figlet package.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run ourfiglet figlet hello
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As figlet is present in our &lt;strong&gt;ourfiglet&lt;/strong&gt; image, the command ran returns the following output.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; _          _ _
| |__   ___| | | ___
| &apos;_ \ / _ \ | |/ _ \
| | | |  __/ | | (_) |
|_| |_|\___|_|_|\___/

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This example shows that we can create a container, add all the libraries and binaries in it and then commit this one in order to create an image. We can then use that image as we would do for any other images. This approach is not the recommended one as it is not very portable.&lt;/p&gt;

&lt;p&gt;In the following we will see how images are usually created, using a Dockerfile, which is a text file that contains all the instructions to build an image.&lt;/p&gt;

&lt;h2 id=&quot;image-creation-using-a-dockerfile&quot;&gt;Image creation using a Dockerfile&lt;/h2&gt;

&lt;p&gt;We will use a simple example in this section and build a hello world application in Node.js. We will start by creating a file in which we retrieve the hostname and display it.&lt;/p&gt;

&lt;p&gt;Copy the following content into index.js file.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;var os = require(&quot;os&quot;);
var hostname = os.hostname();
console.log(&quot;hello from &quot; + hostname);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We will dockerrize this application and start by creating a Dockerfile for this purpose. We will use &lt;strong&gt;alpine&lt;/strong&gt; as the base image, add a Node.js runtime and then copy our source code. We will also specify the default command to be ran upon container creation.&lt;/p&gt;

&lt;p&gt;Create a file named Dockerfile and copy the following content into it.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM alpine
RUN apk update &amp;amp;&amp;amp; apk add nodejs
COPY . /app
WORKDIR /app
CMD [&quot;node&quot;,&quot;index.js&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s build our first image out of this Dockerfile, we will name it hello:v0.1&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -t hello:v0.1 .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We then create a container to check it is running fine.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run hello:v0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should then have an output similar to the following one (the ID will be different though).&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hello from 92d79b6de29f
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are always several ways to write a Dockerfile, we can start from a Linux distribution and then install a runtime (as we did above) or use images where this has already been done for us.&lt;/p&gt;

&lt;p&gt;To illustrate that, we will now create a new Dockerfile but we will use the &lt;strong&gt;mhart/alpine-node:6.9.4&lt;/strong&gt; image. This is not an official image but it’s a very well known and used one.&lt;/p&gt;

&lt;p&gt;Create a new Dockerfile named Dockerfile-v2 and make sure it has the following content.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM mhart/alpine-node:6.9.4
COPY . /app
WORKDIR /app
CMD [&quot;node&quot;,&quot;index.js&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Basically, it is not that different from the previous one, it just uses a base image that embeds alpine and a Node.js runtime so we do not have to install it ourself. In this example, installing Node.js is not a big deal, but it is really helpful to use image where a runtime (or else) is already packages when using more complex environments.&lt;/p&gt;

&lt;p&gt;We will now create a new image using this Dockerfile.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -f Dockerfile-v2 -t hello:v0.2 .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note: as we do not use the default name for our Dockerfile, we use the -f option to point towards the one we need to use.&lt;/p&gt;

&lt;p&gt;We now run a container from this image.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run hello:v0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once again, the output will look like the following.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;hello from 4094ff6bffbd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;entrypoint-vs-command&quot;&gt;ENTRYPOINT vs COMMAND&lt;/h2&gt;

&lt;p&gt;In the 2 previous Dockerfile, we used CMD to define the command to be ran when a container is launched. As we have seen, there are several ways to define the command, using ENTRYPOINT and/or CMD.
We will illustrate this on a new Dockerfile, named Dockerfile-v3, that as the following content.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM alpine
ENTRYPOINT [&quot;ping&quot;]
CMD [&quot;localhost&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here, we define the &lt;strong&gt;ping&lt;/strong&gt; command as the ENTRYPOINT and the &lt;strong&gt;localhost&lt;/strong&gt; as the CMD, the command that will be ran by default is the concatenation of ENTRYPOINT and CMD: &lt;strong&gt;ping localhost&lt;/strong&gt;.  This command can be seen as a wrapper around the &lt;strong&gt;ping&lt;/strong&gt; utility to which we can change the address we provide as a parameter.&lt;/p&gt;

&lt;p&gt;Let’s create an image based on this new file.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -f Dockerfile-v3 -t ping:v0.1 .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can run this image without specifying any command:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run ping:v0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That should give a result like the following one.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PING localhost (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.046 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.046 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.046 ms
64 bytes from 127.0.0.1: seq=3 ttl=64 time=0.046 ms
64 bytes from 127.0.0.1: seq=4 ttl=64 time=0.047 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also override the default CMD indicating another IP address. We will use &lt;strong&gt;8.8.8.8&lt;/strong&gt; which is the IP of a Google’s DNS.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run ping:v0.1 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That should return the following.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=38 time=9.235 ms
64 bytes from 8.8.8.8: seq=1 ttl=38 time=8.590 ms
64 bytes from 8.8.8.8: seq=2 ttl=38 time=8.585 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;image-inspection&quot;&gt;Image Inspection&lt;/h2&gt;

&lt;p&gt;As we have already seen with containers, and as we will see with other Docker’s components (volume, network, …), the &lt;strong&gt;inspect&lt;/strong&gt; command is available for the image API and it returns all the information of the image provided.&lt;/p&gt;

&lt;p&gt;The alpine image should already be present locally, if it’s not, run the following command to pull it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image pull alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once we are sure it is there let’s inspect it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image inspect alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There is a lot of information in there:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;the layers the image is composed of&lt;/li&gt;
  &lt;li&gt;the driver used to store the layers&lt;/li&gt;
  &lt;li&gt;the architecture / os it has been created for&lt;/li&gt;
  &lt;li&gt;metadata of the image&lt;/li&gt;
  &lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will not go into all the details now but it’s interesing to see an example of the Go template notation that enables to extract the part of information we need in just a simple command.&lt;/p&gt;

&lt;p&gt;Let’s get the list of layers (only one for alpine)&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image inspect --format &quot;{{ json .RootFS.Layers }}&quot; alpine | python -m json.tool
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
    &quot;sha256:60ab55d3379d47c1ba6b6225d59d10e1f52096ee9d5c816e42c635ccc57a5a2b&quot;
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s try another example to query only the Architecture information&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image inspect --format &quot;{{ .Architecture }}&quot; alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This should return &lt;strong&gt;amd64&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to play with the Go template format and get familiar with it as it’s really handy.&lt;/p&gt;

&lt;h2 id=&quot;filesystem-exploration&quot;&gt;Filesystem exploration&lt;/h2&gt;

&lt;p&gt;We first stop and remove all containers from your host (you might not be able to remove images if containers are using some of the layers).&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container stop $(docker container ls -aq)
docker container rm $(docker container ls -aq)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note: containers can also be removed in a non graceful way:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker container rm -f $(docker container ls -aq)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We also remove all the images&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image rm $(docker image ls -q)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We will now have a look inside the &lt;strong&gt;/var/lib/docker/overlay2&lt;/strong&gt; folder where the image and container layers are stored.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ls /var/lib/docker/overlay2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As we do not have any images yet, there should not be anything in this folder.&lt;/p&gt;

&lt;p&gt;Let’s pull an nginx image&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image pull nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should get something like the following where we can see that 3 layers are pulled.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Using default tag: latest
latest: Pulling from library/nginx
5040bd298390: Pull complete
d7a91cdb22f0: Pull complete
9cac4850e5df: Pull complete
Digest: sha256:33ff28a2763feccc1e1071a97960b7fef714d6e17e2d0ff573b74825d0049303
Status: Downloaded newer image for nginx:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we have a look to the changes that occurs in the /var/lib/docker/overlay2 folder&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ls /var/lib/docker/overlay2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;we can see the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;261fed39e3aca63326758681c96cad5bfe7eeeabafda23408bee0f5ae365d3fd
28f7998921ca5e4b28231b59b619394ba73571b5127a9c28cc9bacb3db706d2a
backingFsBlockDev
c1ae1be1c1c62dbaacf26bb9a5cde02e30d5364e06a437d0626f31c55af82a58
l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Some folders, with names that looks like hash, were created. Those are the layers which, merged together, build the image filesystem.&lt;/p&gt;

&lt;p&gt;Let’s run a container based on nginx.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -d nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can now see 2 additional folders (ID, ID-init), those ones correspond to the read-write layer of the running container.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;11995e6da1dc5acab33aceacea3656d3795a4fb136c3a65b37d40b97747b5f84
11995e6da1dc5acab33aceacea3656d3795a4fb136c3a65b37d40b97747b5f84-init
261fed39e3aca63326758681c96cad5bfe7eeeabafda23408bee0f5ae365d3fd
28f7998921ca5e4b28231b59b619394ba73571b5127a9c28cc9bacb3db706d2a
backingFsBlockDev
c1ae1be1c1c62dbaacf26bb9a5cde02e30d5364e06a437d0626f31c55af82a58
l
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Feel free to go into those folder and explore their filesystems.&lt;/p&gt;
</description>
        <pubDate>Fri, 27 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/docker-images/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/docker-images/</guid>
        
        <category>developer</category>
        
        <category>operations</category>
        
        <category>linux</category>
        
        <category>community</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker Volumes</title>
        <description>&lt;h1 id=&quot;understanding-what-is-this-volume-thing&quot;&gt;Understanding what is this volume thing&lt;/h1&gt;

&lt;p&gt;In this lab, we will illustrate the concept of volume. We will see how to use volume&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;in a Dockerfile&lt;/li&gt;
  &lt;li&gt;at runtime with the -v option&lt;/li&gt;
  &lt;li&gt;using the volume API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will also see what is bind-mounting on a simple example.&lt;/p&gt;

&lt;h2 id=&quot;data-persistency-without-a-volume-&quot;&gt;Data persistency without a volume ?&lt;/h2&gt;

&lt;p&gt;We will first illustrate how data is &lt;strong&gt;not&lt;/strong&gt; persisted outside of a container by default.&lt;/p&gt;

&lt;p&gt;Let’s run an interactive shell within an alpine container named c1.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run --name c1 -ti alpine sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We will create the /data folder and a dummy hello.txt file in it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;mkdir /data &amp;amp;&amp;amp; cd /data &amp;amp;&amp;amp; touch hello.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We will then check how the read-write layer (container layer) is accessible from the host.&lt;/p&gt;

&lt;p&gt;Let exit the container first.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;exit
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let’s inspect our container in order to get the location of the container’s layer.
We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inspect&lt;/code&gt; command and then scroll into the output until the &lt;strong&gt;GraphDriver&lt;/strong&gt; key, like the following.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container inspect c1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or we can directly use the Go template notation and get the content of the &lt;strong&gt;GraphDriver&lt;/strong&gt; keys right away.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container inspect -f &quot;{{ json .GraphDriver }}&quot; c1 | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should then get an output like the following (the ID will not be the same though)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;{
    &quot;Data&quot;: {
        &quot;LowerDir&quot;: &quot;/var/lib/docker/overlay2/55922a6b646ba6681c5eca253a19e90270e3872329a239a82877b2f8c505c9a2-init/diff:/var/lib/docker/overlay2/30474f5fc34277d1d9e5ed5b48e2fb979eee9805a61a0b2c4bf33b766ba65a16/diff&quot;,
        &quot;MergedDir&quot;: &quot;/var/lib/docker/overlay2/55922a6b646ba6681c5eca253a19e90270e3872329a239a82877b2f8c505c9a2/merged&quot;,
        &quot;UpperDir&quot;: &quot;/var/lib/docker/overlay2/55922a6b646ba6681c5eca253a19e90270e3872329a239a82877b2f8c505c9a2/diff&quot;,
        &quot;WorkDir&quot;: &quot;/var/lib/docker/overlay2/55922a6b646ba6681c5eca253a19e90270e3872329a239a82877b2f8c505c9a2/work&quot;
    },
    &quot;Name&quot;: &quot;overlay2&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From our host, if we inspect the folder which path is specified in &lt;strong&gt;UpperDir&lt;/strong&gt;, we can see our /data and the hello.txt file we created are there.&lt;/p&gt;

&lt;p&gt;Try the below command, to see the contents of the /data folder:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ls /var/lib/docker/overlay2/[YOUR_ID]/diff/data
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What happen if we remove our c1 container now ? Let’s try.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container rm c1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It seems the folder defined in the &lt;strong&gt;UpperDir&lt;/strong&gt; above does not exist anymore. Do you confirm that ? Try running the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; command again and see the results.&lt;/p&gt;

&lt;p&gt;This shows that data created in a container is not persisted. It’s removed with the container’s layer when the container is deleted.&lt;/p&gt;

&lt;h2 id=&quot;defining-a-volume-in-a-dockerfile&quot;&gt;Defining a volume in a Dockerfile&lt;/h2&gt;

&lt;p&gt;We will now see how volumes come into the picture to handle the data persistency.&lt;/p&gt;

&lt;p&gt;We will start by creating a Dockerfile based on alpine and define the /data as a volume.
This means that anything written by a container in /data will be persisted outside of the Union filesystem.&lt;/p&gt;

&lt;p&gt;Create a Dockerfile with the following content&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM alpine
VOLUME [&quot;/data&quot;]
ENTRYPOINT [&quot;/bin/sh&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: we specify &lt;strong&gt;/bin/sh&lt;/strong&gt; as the ENTRYPOINT so that if no command is provided in interactive mode we will end up in a shell inside our container.&lt;/p&gt;

&lt;p&gt;Let’s build an image from this Dockerfile.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker image build -t img1 .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We will then create a container in interactive mode (using -ti flags) from this image and name it c2.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run --name c2 -ti img1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We should then end up in a shell within the container. From there, we will go into /data and create a hello.txt file.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cd /data
touch hello.txt
ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let’s exit the container making sure it remains running: use the Control-P / Control-Q combination for this.
Use the following command to make sure it’s still running.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note: the container, named c2, should be listed there.&lt;/p&gt;

&lt;p&gt;We will now inspect this container in order to get the location of the volume (defined on /data) on the host.
We can use the inspect command and then scroll into the output until we find the &lt;strong&gt;Mounts&lt;/strong&gt; key…&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container inspect c2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or we can directly use the Go template notation and get the content of the &lt;strong&gt;Mounts&lt;/strong&gt; keys right away.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container inspect -f &quot;{{ json .Mounts }}&quot;  c2 | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You should then get an output like the following (the ID will not be the same though)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
    {
        &quot;Destination&quot;: &quot;/data&quot;,
        &quot;Driver&quot;: &quot;local&quot;,
        &quot;Mode&quot;: &quot;&quot;,
        &quot;Name&quot;: &quot;2f5b7c6b77494934293fc7a09198dd3c20406f05272121728632a4aab545401c&quot;,
        &quot;Propagation&quot;: &quot;&quot;,
        &quot;RW&quot;: true,
        &quot;Source&quot;: &quot;/var/lib/docker/volumes/2f5b7c6b77494934293fc7a09198dd3c20406f05272121728632a4aab545401c/_data&quot;,
        &quot;Type&quot;: &quot;volume&quot;
    }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This output shows that the volume defined in /data is stored in &lt;strong&gt;/var/lib/docker/volumes/2f5…01c/_data&lt;/strong&gt; on the host (removing part of the ID for a better readability).&lt;/p&gt;

&lt;p&gt;Copy your own path (the one under the &lt;strong&gt;Source&lt;/strong&gt; key) and make sure the &lt;strong&gt;hello.txt&lt;/strong&gt; file we created (from within the container) is there.&lt;/p&gt;

&lt;p&gt;We now remove the c2 container.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container stop c2 &amp;amp;&amp;amp; docker container rm c2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Check that the folder defined under the &lt;strong&gt;Source&lt;/strong&gt; key is still there and contains &lt;strong&gt;hello.txt&lt;/strong&gt; file.&lt;/p&gt;

&lt;p&gt;From the above, we can see that a volume bypasses the union filesystem and is not dependent on a container’s lifecycle.&lt;/p&gt;

&lt;h2 id=&quot;defining-a-volume-at-runtime&quot;&gt;Defining a volume at runtime&lt;/h2&gt;

&lt;p&gt;We have seen volume defined in a Dockerfile, we will see they can also be defined at runtime using the &lt;strong&gt;-v&lt;/strong&gt; flag of the &lt;strong&gt;docker container run&lt;/strong&gt; command.&lt;/p&gt;

&lt;p&gt;Let’s create a container from the alpine image, we’ll use the -d option so it runs in background and also define a volume on /data as we’ve done previously.
In order the PID 1 process remains active, we use the following command that pings Google DNS and log the output in a file within the /data folder.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ping 8.8.8.8 &amp;gt; /data/ping.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The container is ran that way:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run --name c3 -d -v /data alpine sh -c &apos;ping 8.8.8.8 &amp;gt; /data/ping.txt&apos;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let’s inspect the container and get the &lt;strong&gt;Mounts&lt;/strong&gt; key using the Go template notation.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container inspect -f &quot;{{ json .Mounts }}&quot; c3 | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We have pretty much the same output as we had when we defined the volume in the Dockerfile.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
  {
    &quot;Type&quot;: &quot;volume&quot;,
    &quot;Name&quot;: &quot;af621cde2717307e5bf91be850c5a00474d58b8cdc8d6e37f2e373631c2f1331&quot;,
    &quot;Source&quot;: &quot;/var/lib/docker/volumes/af621cde2717307e5bf91be850c5a00474d58b8cdc8d6e37f2e373631c2f1331/_data&quot;,
    &quot;Destination&quot;: &quot;/data&quot;,
    &quot;Driver&quot;: &quot;local&quot;,
    &quot;Mode&quot;: &quot;&quot;,
    &quot;RW&quot;: true,
    &quot;Propagation&quot;: &quot;&quot;
  }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we use the folder defined in the &lt;strong&gt;Source&lt;/strong&gt; key, and check the content of the ping.txt within the /data folder, we get something similar to the following.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tail -f /var/lib/docker/volumes/OUR_ID/_data/ping.txt
64 bytes from 8.8.8.8: seq=34 ttl=37 time=0.462 ms
64 bytes from 8.8.8.8: seq=35 ttl=37 time=0.436 ms
64 bytes from 8.8.8.8: seq=36 ttl=37 time=0.512 ms
64 bytes from 8.8.8.8: seq=37 ttl=37 time=0.487 ms
64 bytes from 8.8.8.8: seq=38 ttl=37 time=0.409 ms
64 bytes from 8.8.8.8: seq=39 ttl=37 time=0.438 ms
64 bytes from 8.8.8.8: seq=40 ttl=37 time=0.477 ms
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The ping.txt file is updated regularly by the command running in the &lt;strong&gt;c3&lt;/strong&gt; container.&lt;/p&gt;

&lt;p&gt;Stopping and removing the container will obviously stop the ping command but the /data/ping.txt file will still be there. Give it a try :)&lt;/p&gt;

&lt;h2 id=&quot;usage-of-the-volume-api&quot;&gt;Usage of the Volume API&lt;/h2&gt;

&lt;p&gt;The volume API introduced in Docker 1.9 enables to perform operations on volume very easily.&lt;/p&gt;

&lt;p&gt;First have a look at the commands available in the volume API.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker volume --help
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We will start with the create command, and create a volume named &lt;strong&gt;html&lt;/strong&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker volume create --name html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If we list the existing volume, our &lt;strong&gt;html&lt;/strong&gt; volume should be the only one.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker volume ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output should be something like&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;DRIVER              VOLUME NAME
[other previously created volumes]
local               html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the volume API, like for almost all the other Docker’s API, there is an &lt;strong&gt;inspect&lt;/strong&gt; command. Let’s use it against the &lt;strong&gt;html&lt;/strong&gt; volume.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker volume inspect html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The output should be the following one.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
    {
        &quot;Driver&quot;: &quot;local&quot;,
        &quot;Labels&quot;: {},
        &quot;Mountpoint&quot;: &quot;/var/lib/docker/volumes/html/_data&quot;,
        &quot;Name&quot;: &quot;html&quot;,
        &quot;Options&quot;: {},
        &quot;Scope&quot;: &quot;local&quot;
    }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;Mountpoint&lt;/strong&gt; defined here is the path on the Docker host where the volume can be accessed. We can note that this path uses the name of the volume instead of the auto-generated ID we saw in the example above.&lt;/p&gt;

&lt;p&gt;We can now use this volume and mount it on a specific path of a container. We will use a Nginx image and mount the &lt;strong&gt;html&lt;/strong&gt; volume onto &lt;strong&gt;/usr/share/nginx/html&lt;/strong&gt; folder within the container.&lt;/p&gt;

&lt;p&gt;Note: /usr/share/nginx/html is the default folder served by nginx. It contains 2 files: index.html and 50x.html&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run --name www -d -p 8080:80 -v html:/usr/share/nginx/html nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Note: we use the -p option to map the nginx default port (80) to a port on the host (8080). We will come back to this in the lesson dedicated to the networking.&lt;/p&gt;

&lt;p&gt;From the host, let’s have a look at the content of the volume.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ls /var/lib/docker/volumes/html/_data
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The content of the &lt;strong&gt;/usr/share/nginx/html&lt;/strong&gt; folder of the &lt;strong&gt;www&lt;/strong&gt; container has been copied into the &lt;strong&gt;/var/lib/docker/volumes/html/_data&lt;/strong&gt; folder on the host.&lt;/p&gt;

&lt;p&gt;Let’s have a look at the nginx’s &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;welcome page&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From our host, we can now modify the index.html file and verify the changes are taken into account within the container.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat&amp;lt;&amp;lt;END &amp;gt;/var/lib/docker/volumes/html/_data/index.html
SOMEONE HERE ?
END
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let’s have a look at the nginx’s &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;welcome page&lt;/a&gt;. We can see the changes we have done in the index.html.&lt;/p&gt;

&lt;p&gt;Note: please reload the page if you cannot see the changes.&lt;/p&gt;

&lt;h2 id=&quot;mount-hosts-folder-into-a-container&quot;&gt;Mount host’s folder into a container&lt;/h2&gt;

&lt;p&gt;The last item we will talk about is named bind-mount and consist of mounting a host’s folder into a container’s folder. This is done using the &lt;strong&gt;-v&lt;/strong&gt; option of the &lt;strong&gt;docker container run&lt;/strong&gt; command. Instead of specifying one single path (as we did when defining volumes) we will specified 2 paths separated by a column.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker container run -v HOST_PATH:CONTAINER_PATH [OPTIONS] IMAGE [CMD]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note: HOST_PATH and CONTAINER_PATH can be a folder or file. None of the Paths have to exist before starting the Container as they will be created automatically during the start.&lt;/p&gt;

&lt;h3 id=&quot;1st-case&quot;&gt;1st case&lt;/h3&gt;

&lt;p&gt;Let’s run an alpine container bind mounting the local /tmp folder inside the container /data folder.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -ti -v /tmp:/data alpine sh
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We end up in a shell inside our container. By default, there is no /data folder in an alpine distribution. What is the impact of the bind-mount ?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ls /data
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The /data folder has been created inside the container and it contains the content of the /tmp folder of the host. We can now, from the container, change files on the host and the other way round.&lt;/p&gt;

&lt;h3 id=&quot;2nd-case&quot;&gt;2nd case&lt;/h3&gt;

&lt;p&gt;Let’s run a nginx container bind mounting the local /tmp folder inside the /usr/share/nginx/html folder of the container.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -ti -v /tmp:/usr/share/nginx/html nginx bash
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Are the default index.html and 50x.html files still there in the container’s /usr/share/nginx/html folder ?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ls /usr/share/nginx/html
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;No ! The content of the container’s folder has been overridden with the content of the host folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bind-mounting&lt;/strong&gt; is very usefull in development as it enables, for instance, to share source code on the host with the container.&lt;/p&gt;
</description>
        <pubDate>Wed, 25 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/docker-volumes/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/docker-volumes/</guid>
        
        <category>linux</category>
        
        <category>developer</category>
        
        <category>operations</category>
        
        <category>community</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Swarm mode introduction</title>
        <description>&lt;p&gt;This tutorial will show you how to setup a swarm and deploy your first services.&lt;/p&gt;

&lt;h2 id=&quot;init-your-swarm&quot;&gt;Init your swarm&lt;/h2&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr $(hostname -i)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Copy the join command (&lt;em&gt;watch out for newlines&lt;/em&gt;) output and paste it in the other terminal.&lt;/p&gt;

&lt;h2 id=&quot;show-members-of-swarm&quot;&gt;Show members of swarm&lt;/h2&gt;

&lt;p&gt;Type the below command in the first terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That last line will show you a list of all the nodes, something like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGE
R STATUS
kytp4gq5mrvmdbb0qpifdxeiv *  node1     Ready   Active        Leader
lz1j4d6290j8lityk4w0cxls5    node2     Ready   Active
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you try to execute an administrative command in a non-leader node &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;worker&lt;/code&gt;, you’ll get an error. Try it here:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;creating-services&quot;&gt;Creating services&lt;/h2&gt;

&lt;p&gt;The next step is to create a service and list out the services. This creates a single service called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt; that runs the latest nginx, type the below commands in the first terminal:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service create -p 80:80 --name web nginx:latest
docker service ls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can check that nginx is running by executing the following command:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl http://localhost:80
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;scaling-up&quot;&gt;Scaling up&lt;/h2&gt;

&lt;p&gt;We will be performing these actions in the first terminal. Next let’s inspect the service:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service inspect web
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That’s lots of info! Now, let’s scale the service:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service scale web=15
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Docker has spread the 15 services evenly over all of the nodes&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps web
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;updating-nodes&quot;&gt;Updating nodes&lt;/h2&gt;

&lt;p&gt;You can also drain a particular node, that is remove all services from that node. The services will automatically be rescheduled on other nodes.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node update --availability drain node2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps web
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can check out the nodes and see that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node2&lt;/code&gt; is still active but drained.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;scaling-down&quot;&gt;Scaling down&lt;/h2&gt;

&lt;p&gt;You can also scale down the service&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service scale web=10
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Lets check our service status&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps web
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now bring &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node2&lt;/code&gt; back online and show it’s new availability&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node update --availability active node2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node inspect node2 --pretty
&lt;/code&gt;&lt;/pre&gt;

&lt;p class=&quot;quiz&quot;&gt;Which of these can you do with Docker Swarm Mode?&lt;/p&gt;
&lt;ul class=&quot;task-list&quot;&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;add a node&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;start a service&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;end a service&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;list all service&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;scale up the number of replicas of a service&lt;/li&gt;
  &lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; class=&quot;task-list-item-checkbox&quot; disabled=&quot;disabled&quot; checked=&quot;checked&quot; /&gt;take a node out of the swarm&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Tue, 24 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/swarm-mode-intro/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/swarm-mode-intro/</guid>
        
        <category>linux</category>
        
        <category>operations</category>
        
        <category>swarm</category>
        
        <category>community</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker compose with swarm secrets</title>
        <description>&lt;p&gt;Start securing your swarm services using the latest compose reference that allows to specify secrets in your application stack&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;

&lt;p&gt;Make sure your daemon is in swarm mode or initialize it as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr $(hostname -i)
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;automatic-provision&quot;&gt;Automatic provision&lt;/h3&gt;

&lt;p&gt;In this example we’ll let compose automatically create our secrets and provision them through compose with the defined secret file&lt;/p&gt;

&lt;p&gt;Create a new secret and store it in a file:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &quot;shh, this is a secret&quot; &amp;gt; mysecret.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Use your secret file in your compose stack as follows:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &apos;version: &apos;\&apos;3.1\&apos;&apos;
services:
    test:
        image: &apos;\&apos;alpine\&apos;&apos;
        command: &apos;\&apos;cat /run/secrets/my_secret \&apos;&apos;
        secrets: 
            - my_secret

secrets:
    my_secret:
        file: ./mysecret.txt
&apos; &amp;gt; docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Deploy your stack service:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy -c docker-compose.yml secret
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Results in the below output:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Creating network secret_default
Creating secret secret_my_secret
Creating service secret_test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After your stack is deployed you can check your service output:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service logs -f secret_test
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Results in the below output (below values after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;secret_test.1.&lt;/code&gt; may vary):&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;secret_test.1.lcygnppmzfdp@node1    | shhh, this is a secret
secret_test.1.mg1420w2i3x4@node1    | shhh, this is a secret
secret_test.1.8osraz8yxjrb@node1    | shhh, this is a secret
secret_test.1.byh5b9uik6db@node1    | shhh, this is a secret
.
.
.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;using-existing-secrets&quot;&gt;Using existing secrets&lt;/h3&gt;

&lt;p&gt;Create a new secret using the docker CLI:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &quot;some other secret&quot; | docker secret create manual_secret - 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Define your secret as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;external&lt;/code&gt; in your compose file so it’s not created while deploying the stack&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &apos;version: &apos;\&apos;3.1\&apos;&apos;
services:
    test:
        image: &apos;\&apos;alpine\&apos;&apos;
        command: &apos;\&apos;cat /run/secrets/manual_secret \&apos;&apos;
        secrets: 
            - manual_secret

secrets:
    manual_secret:
        external: true
&apos; &amp;gt; external-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Deploy your stack as you did in the automatic section:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker stack deploy -c external-compose.yml external_secret
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Validate your secret is there by checking the service logs&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service logs -f external_secret_test
&lt;/code&gt;&lt;/pre&gt;

</description>
        <pubDate>Mon, 23 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/swarm-compose-secrets/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/swarm-compose-secrets/</guid>
        
        <category>developer</category>
        
        <category>operations</category>
        
        <category>linux</category>
        
        <category>swarm</category>
        
        <category>community</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker swarm config files</title>
        <description>&lt;p&gt;This tutorials showcases the &lt;a href=&quot;https://github.com/moby/moby/pull/32336&quot;&gt;config&lt;/a&gt; swarm feature that allow config objects to be attached to services. Config files can be mounted inside services’ containers, avoiding the need to bake configuration into images.&lt;/p&gt;

&lt;p&gt;Configuration files are similar to secrets, and in fact the CLI and API show few differences between the two. The principal differences so far are:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Secrets are always redacted at the API level, so the payload cannot be obtained through an API call after they are created.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Secrets are restricted to the /run/secrets directory inside the container, as a design choice. Config files can be mounted anywhere.
Start securing your swarm services using the latest compose reference that allows to specify secrets in your application stack&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;requirements&quot;&gt;Requirements&lt;/h2&gt;

&lt;p&gt;Docker 17.06+&lt;/p&gt;

&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;

&lt;p&gt;Initialize your swarm:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr $(hostname -i)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Let’s peak the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;config&lt;/code&gt; options:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker config --help
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see the API is very similar to the &lt;a href=&quot;./2017-01-23-swarm-compose-secrets.markdown&quot;&gt;docker secrets&lt;/a&gt;. Let’s create our first config object&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;echo &quot;this is some crazy config stuff&quot; | docker config create my_config -
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As stated before, unlike secrets, you can actually see the content of the config objects directly from the CLI. Let’s check this:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker config inspect my_config
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wait, what?, where’s my config?. Docker hides the config information by default to prevent unnecessary large outputs; in order to display 
the config value the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--pretty&lt;/code&gt; flag needs to added&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker config inspect --pretty my_config
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, let’s deploy a service using our recently created config&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service create --name test_cfg --config my_config alpine cat /my_config
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can check your service logs to see your configuration.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service logs test_cfg
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, as we didn’t specify any destination mountpoint, by default configs will be located at the root path. However, with configs
you can place them wherever you need.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service create --name test_cfg_mount --config source=my_config,target=/tmp/cfg alpine cat /tmp/cfg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same as before, check your service logs to see the expected configuration:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service logs test_cfg_mount
&lt;/code&gt;&lt;/pre&gt;

</description>
        <pubDate>Sun, 22 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/swarm-config/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/swarm-config/</guid>
        
        <category>developer</category>
        
        <category>operations</category>
        
        <category>linux</category>
        
        <category>swarm</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Windows Containers Setup</title>
        <description>&lt;h1 id=&quot;setup&quot;&gt;Setup&lt;/h1&gt;

&lt;p&gt;There are four environments you can use to set-up Windows Containers. We have provided separate guides for each:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#win10&quot;&gt;Windows 10 with the Anniversary Update&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#azure&quot;&gt;Windows Server 2016 on Azure&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#aws&quot;&gt;Windows Server 2016 on AWS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#vm&quot;&gt;Windows Server 2016 on bare metal or in VM&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;see-also&quot;&gt;See also:&lt;/h1&gt;
&lt;p&gt;See the &lt;a href=&quot;https://msdn.microsoft.com/virtualization/windowscontainers/containers_welcome&quot; title=&quot;Microsoft documentation&quot;&gt;Microsoft documentation for more comprehensive instructions&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;setup---windows-10&quot;&gt;&lt;a name=&quot;win10&quot;&gt;&lt;/a&gt;Setup - Windows 10&lt;/h1&gt;

&lt;p&gt;This chapter explores setting up a Windows environment to properly use Windows containers on Windows 10.&lt;/p&gt;

&lt;h2 id=&quot;windows-10-with-anniversary-update&quot;&gt;Windows 10 with Anniversary Update&lt;/h2&gt;

&lt;p&gt;For developers, Windows 10 is a great place to run Docker Windows containers and containerization support was added to the Windows 10 kernel with the &lt;a href=&quot;https://blogs.windows.com/windowsexperience/2016/08/02/how-to-get-the-windows-10-anniversary-update/&quot;&gt;Anniversary Update&lt;/a&gt; (note that container images can only be based on Windows Server Core and Nanoserver, not Windows 10). All that’s missing is the Windows-native Docker Engine and some image base layers.&lt;/p&gt;

&lt;p&gt;The simplest way to get a Windows Docker Engine is by installing the &lt;a href=&quot;https://docs.docker.com/docker-for-windows/&quot; title=&quot;Docker for Windows&quot;&gt;Docker for Windows&lt;/a&gt; public beta (&lt;a href=&quot;https://download.docker.com/win/beta/InstallDocker.msi&quot;&gt;direct download link&lt;/a&gt;). Docker for Windows used to only setup a Linux-based Docker development environment, but the public beta version now sets up both Linux and Windows Docker development environments, and we’re working on improving Windows container support and Linux/Windows container interoperability.&lt;/p&gt;

&lt;p&gt;With the public beta installed, the Docker for Windows tray icon has an option to switch between Linux and Windows container development.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/docker-for-windows-switch.gif&quot; alt=&quot;Image of switching between Linux and Windows development environments&quot; title=&quot;Image of switching between Linux and Windows development environments&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can skip the rest of this lab.&lt;/p&gt;

&lt;h1 id=&quot;setup---azure&quot;&gt;&lt;a name=&quot;azure&quot;&gt;&lt;/a&gt;Setup - Azure&lt;/h1&gt;

&lt;p&gt;This chapter explores setting up a Windows environment to properly use Windows containers on Microsoft Azure.&lt;/p&gt;

&lt;h2 id=&quot;windows-server-2016-on-azure&quot;&gt;Windows Server 2016 on Azure&lt;/h2&gt;

&lt;p&gt;Microsoft Azure has a pre-baked VM image with Docker engine and base images pre-loaded. To get started (requires Azure account):&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Create a &lt;a href=&quot;https://azure.microsoft.com/en-us/marketplace/partners/microsoft/windowsserver2016datacenterwithcontainers/&quot;&gt;Windows Server 2016 Datacenter - with Containers&lt;/a&gt; VM. This VM image has Docker pre-installed and the Windows base layers pre-loaded.&lt;/li&gt;
  &lt;li&gt;Select “Classic” deployment model and hit “Create”&lt;/li&gt;
  &lt;li&gt;Input setup parameters
    &lt;ul&gt;
      &lt;li&gt;Default settings are good&lt;/li&gt;
      &lt;li&gt;Creating a new resource group is fine&lt;/li&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DS2_V2&lt;/code&gt; instance type has good performance for development and testing&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Check the Summary and hit “OK”. Setup will take a couple of minutes&lt;/li&gt;
  &lt;li&gt;Once the VM is running, select “Connect” to open a remote desktop connection. If using macOS, get the free &lt;a href=&quot;https://itunes.apple.com/us/app/microsoft-remote-desktop/id715768417?mt=12&quot;&gt;Remote Desktop app in the Mac App Store&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Login with the username and password configured during setup&lt;/li&gt;
  &lt;li&gt;Start PowerShell&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Start-Service docker&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Check that Docker is running with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker version&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/images/Azure-ws2016-Create-Virtual-Machine.PNG&quot; alt=&quot;Creating Azure Virtual Machine&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/Azure-ws2016-Connect.PNG&quot; alt=&quot;Connecting to Azure Virtual Machine&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can skip the rest of this lab.&lt;/p&gt;

&lt;h1 id=&quot;setup---aws&quot;&gt;&lt;a name=&quot;aws&quot;&gt;&lt;/a&gt;Setup - AWS&lt;/h1&gt;

&lt;p&gt;This chapter explores setting up a Windows environment to properly use Windows containers on Amazon Web Services (AWS).&lt;/p&gt;

&lt;h2 id=&quot;windows-server-2016-on-aws&quot;&gt;&lt;a name=&quot;vm&quot;&gt;&lt;/a&gt;Windows Server 2016 on AWS&lt;/h2&gt;

&lt;p&gt;AWS has a pre-baked AMI with Docker Engine already installed. To start an instance, do the following (requires AWS account):&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open the &lt;a href=&quot;https://us-west-1.console.aws.amazon.com/ec2/v2/home#LaunchInstanceWizard&quot;&gt;EC2 launch-instance wizard&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Select the “Microsoft Windows Server 2016 Base with Containers” AMI&lt;/li&gt;
  &lt;li&gt;Input setup parameters
    &lt;ul&gt;
      &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c4.large&lt;/code&gt; has good performance for development and testing&lt;/li&gt;
      &lt;li&gt;The default AWS security group settings will let you connect with Remote Desktop&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Select “Review and Launch”&lt;/li&gt;
  &lt;li&gt;Once the VM is up, hit “Connect”. If using macOS, get the free &lt;a href=&quot;https://itunes.apple.com/us/app/microsoft-remote-desktop/id715768417?mt=12&quot;&gt;Remote Desktop app in the Mac App Store&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;See details on &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/connecting_to_windows_instance.html&quot;&gt;getting the initial Windows Administrator password for your AWS instance&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Start PowerShell&lt;/li&gt;
  &lt;li&gt;Check that Docker is running with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker version&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/images/aws-connect.PNG&quot; alt=&quot;Connecting to AWS Virtual Machine&quot; /&gt;&lt;/p&gt;

&lt;p&gt;You can skip the rest of this lab.&lt;/p&gt;

&lt;h1 id=&quot;setup---windows-server-2016&quot;&gt;Setup - Windows Server 2016&lt;/h1&gt;

&lt;p&gt;This chapter explores setting up a Windows environment to properly use Windows containers on Windows Server 2016, on bare metal or in VM.&lt;/p&gt;

&lt;h2 id=&quot;windows-server-2016-on-bare-metal-or-in-vm&quot;&gt;Windows Server 2016 on bare metal or in VM&lt;/h2&gt;

&lt;p&gt;Windows Server 2016 is where Docker Windows containers should be deployed for production. For developers planning to do lots of Docker Windows container development, it may also be worth setting up a Windows Server 2016 dev system (in a VM, for example), at least until Windows 10 and Docker for Windows support for Windows containers matures. Running a VM with Windows Server 2016 is also a great way to do Docker Windows container development on macOS and older Windows versions.&lt;/p&gt;

&lt;p&gt;Once Windows Server 2016 is running, log in, run Windows Update (use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sconfig&lt;/code&gt; on Windows Server Core) to ensure all the latest updates are installed and install the Windows-native Docker Engine (that is, don’t use “Docker for Windows”). There are two options: Install using a Powershell Package (recommended) or with DSC.&lt;/p&gt;

&lt;h3 id=&quot;powershell-package-provider-recommended&quot;&gt;PowerShell Package Provider (recommended)&lt;/h3&gt;

&lt;p&gt;Microsoft maintains a &lt;a href=&quot;https://www.powershellgallery.com/packages/DockerMsftProvider&quot;&gt;PowerShell package provider&lt;/a&gt; that lets easily install Docker on Windows Server 2016.&lt;/p&gt;

&lt;p&gt;Run the following in an Administrative PowerShell prompt:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Install-Module -Name DockerMsftProvider -Force
Install-Package -Name docker -ProviderName DockerMsftProvider -Force
Restart-Computer -Force
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;powershell-desired-state-configuration&quot;&gt;PowerShell Desired State Configuration&lt;/h3&gt;

&lt;p&gt;If interested in experimenting with &lt;a href=&quot;https://msdn.microsoft.com/en-us/powershell/dsc/overview&quot;&gt;Windows PowerShell Desired State Configuration&lt;/a&gt;, Daniel Scott-Raynsford has built a &lt;a href=&quot;https://www.powershellgallery.com/packages/Install-DockerOnWS2016UsingDSC/1.0.1/DisplayScript&quot;&gt;prototype script that uses DSC to install Docker Engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how to use it:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Install-Script -Name Install-DockerOnWS2016UsingDSC
Install-DockerOnWS2016UsingDSC.ps1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;See Daniel’s blog post for &lt;a href=&quot;https://dscottraynsford.wordpress.com/2016/10/15/install-docker-on-windows-server-2016-using-dsc/&quot;&gt;details on installing Docker with DSC&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Whether using the PowerShell Package Provider or DSC, Docker Engine is now running as a Windows service, listening on the default Docker named pipe.&lt;/p&gt;

&lt;p&gt;For development VMs running (for example) in a Hyper-V VM on Windows 10, it might be advantageous to make the Docker Engine running in the Windows Server 2016 VM available to the Windows 10 host:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Open firewall port 2375
netsh advfirewall firewall add rule name=&quot;docker engine&quot; dir=in action=allow protocol=TCP localport=2375

# Configure Docker daemon to listen on both pipe and TCP (replaces docker --register-service invocation above)
Stop-Service docker
dockerd --unregister-service
dockerd -H npipe:// -H 0.0.0.0:2375 --register-service
Start-Service docker
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Windows Server 2016 Docker engine can now be used from the VM host by setting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DOCKER_HOST&lt;/code&gt;:
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$env:DOCKER_HOST = &quot;&amp;lt;ip-address-of-vm&amp;gt;:2375&quot;&lt;/code&gt;&lt;/p&gt;
</description>
        <pubDate>Sun, 22 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/windows-containers-setup/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/windows-containers-setup/</guid>
        
        <category>windows</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Windows Containers Basics</title>
        <description>&lt;h2 id=&quot;getting-started-with-windows-containers&quot;&gt;Getting Started with Windows Containers&lt;/h2&gt;

&lt;p&gt;This chapter will cover the basics of using Windows Containers with Docker.&lt;/p&gt;

&lt;p&gt;##Running Windows containers&lt;/p&gt;

&lt;p&gt;First, make sure the Docker installation is working:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;gt; docker version
Client:
 Version:      1.12.2
 API version:  1.24
 Go version:   go1.6.3
 Git commit:   bb80604
 Built:        Tue Oct 11 05:27:08 2016
 OS/Arch:      windows/amd64
 Experimental: true

Server:
 Version:      1.12.2-cs2-ws-beta
 API version:  1.25
 Go version:   go1.7.1
 Git commit:   050b611
 Built:        Tue Oct 11 02:35:40 2016
 OS/Arch:      windows/amd64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, pull a base image that’s compatible with the evaluation build, re-tag it and do a test-run:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker pull microsoft/windowsservercore:10.0.14393.321
docker tag microsoft/windowsservercore:10.0.14393.321 microsoft/windowsservercore
docker run microsoft/windowsservercore hostname
69c7de26ea48
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;building-and-pushing-windows-container-images&quot;&gt;Building and pushing Windows container images&lt;/h2&gt;

&lt;p&gt;Pushing images to Docker Cloud requires a &lt;a href=&quot;https://cloud.docker.com/&quot; title=&quot;Click to create a Docker ID&quot;&gt;free Docker ID&lt;/a&gt;. Storing images on Docker Cloud is a great way to save build artifacts for later user, to share base images with co-workers or to create build-pipelines that move apps from development to production with Docker.&lt;/p&gt;

&lt;p&gt;Docker images are typically built with &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/build/&quot; title=&quot;docker build reference&quot;&gt;docker build&lt;/a&gt; from a &lt;a href=&quot;https://docs.docker.com/engine/reference/builder/&quot; title=&quot;Dockerfile reference&quot;&gt;Dockerfile&lt;/a&gt; recipe, but for this example, we’re going to just create an image on the fly in PowerShell.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&quot;FROM microsoft/windowsservercore `n CMD echo Hello World!&quot; | docker build -t &amp;lt;docker-id&amp;gt;/windows-test-image -
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Test the image:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &amp;lt;docker-id&amp;gt;/windows-test-image
Hello World!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Login with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker login&lt;/code&gt; and then push the image:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker push &amp;lt;docker-id&amp;gt;/windows-test-image
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Images stored on Docker Cloud are available in the web interface and public images can be pulled by other Docker users in the &lt;a href=&quot;https://store.docker.com&quot;&gt;Docker Store&lt;/a&gt;.&lt;/p&gt;
</description>
        <pubDate>Sat, 21 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/windows-containers-basics/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/windows-containers-basics/</guid>
        
        <category>windows</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker Orchestration Hands-on Lab</title>
        <description>&lt;p&gt;In this lab you will play around with the container orchestration features of Docker. You will deploy a simple application to a single host and learn how that works. Then, you will configure Docker Swarm Mode, and learn to deploy the same simple application across multiple hosts. You will then see how to scale the application and move the workload across different hosts easily.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;: Beginner&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt;: Approximately 30 minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Tasks&lt;/strong&gt;:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;#basics&quot;&gt;Section #1 - What is Orchestration&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#start-cluster&quot;&gt;Section #2 - Configure Swarm Mode&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#multi-application&quot;&gt;Section #3 - Deploy applications across multiple hosts&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#scale-application&quot;&gt;Section #4 - Scale the application&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#recover-application&quot;&gt;Section #5 - Drain a node and reschedule the containers&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#cleanup&quot;&gt;Cleaning Up&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;section-1-what-is-orchestration&quot;&gt;&lt;a name=&quot;basics&quot;&gt;&lt;/a&gt;Section 1: What is Orchestration&lt;/h1&gt;

&lt;p&gt;So, what is Orchestration anyways? Well, Orchestration is probably best described using an example. Let’s say that you have an application that has high traffic along with high-availability requirements. Due to these requirements, you typically want to deploy across at least 3+ machines, so that in the event a host fails, your application will still be accessible from at least two others. Obviously, this is just an example and your use-case will likely have its own requirements, but you get the idea.&lt;/p&gt;

&lt;p&gt;Deploying your application without Orchestration is typically very time consuming and error prone, because you would have to manually SSH into each machine, start up your application, and then continually keep tabs on things to make sure it is running as you expect.&lt;/p&gt;

&lt;p&gt;But, with Orchestration tooling, you can typically off-load much of this manual work and let automation do the heavy lifting. One cool feature of Orchestration with Docker Swarm, is that you can deploy an application across many hosts with only a single command (once Swarm mode is enabled). Plus, if one of the supporting nodes dies in your Docker Swarm, other nodes will automatically pick up load, and your application will continue to hum along as usual.&lt;/p&gt;

&lt;p&gt;If you are typically only using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; to deploy your applications, then you could likely really benefit from using Docker Compose, Docker Swarm mode, or both Docker Compose and Swarm.&lt;/p&gt;

&lt;h1 id=&quot;section-2-configure-swarm-mode&quot;&gt;&lt;a name=&quot;start-cluster&quot;&gt;&lt;/a&gt;Section 2: Configure Swarm Mode&lt;/h1&gt;

&lt;p&gt;Real-world applications are typically deployed across multiple hosts as discussed earlier. This improves application performance and availability, as well as allowing individual application components to scale independently. Docker has powerful native tools to help you do this.&lt;/p&gt;

&lt;p&gt;An example of running things manually and on a single host would be to create a new container on &lt;strong&gt;node1&lt;/strong&gt; by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -dt ubuntu sleep infinity&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -dt ubuntu sleep infinity
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Unable to find image &apos;ubuntu:latest&apos; locally
latest: Pulling from library/ubuntu
d54efb8db41d: Pull complete
f8b845f45a87: Pull complete
e8db7bf7c39f: Pull complete
9654c40e9079: Pull complete
6d9ef359eaaa: Pull complete
Digest: sha256:dd7808d8792c9841d0b460122f1acf0a2dd1f56404f8d1e56298048885e45535
Status: Downloaded newer image for ubuntu:latest
846af8479944d406843c90a39cba68373c619d1feaa932719260a5f5afddbf71
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command will create a new container based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu:latest&lt;/code&gt; image and will run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sleep&lt;/code&gt; command to keep the container running in the background. You can verify our example container is up by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt; on &lt;strong&gt;node1&lt;/strong&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
044bea1c2277        ubuntu              &quot;sleep infinity&quot;    2 seconds ago       Up 1 second                             distracted_mayer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But, this is only on a single node. What happens if this node goes down? Well, our application just dies and it is never restarted. To restore service, we would have to manually log into this machine, and start tweaking things to get it back up and running. So, it would be helpful if we had some type of system that would allow us to run this “sleep” application/service across many machines.&lt;/p&gt;

&lt;p&gt;In this section you will configure &lt;em&gt;Swarm Mode&lt;/em&gt;. This is a new optional mode in which multiple Docker hosts form a self-orchestrating group of engines called a &lt;em&gt;swarm&lt;/em&gt;. Swarm mode enables new features such as &lt;em&gt;services&lt;/em&gt; and &lt;em&gt;bundles&lt;/em&gt; that help you deploy and manage multi-container apps across multiple Docker hosts.&lt;/p&gt;

&lt;p&gt;You will complete the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Configure &lt;em&gt;Swarm mode&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Run the app&lt;/li&gt;
  &lt;li&gt;Scale the app&lt;/li&gt;
  &lt;li&gt;Drain nodes for maintenance and reschedule containers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the remainder of this lab we will refer to &lt;em&gt;Docker native clustering&lt;/em&gt; as &lt;strong&gt;&lt;em&gt;Swarm mode&lt;/em&gt;&lt;/strong&gt;. The collection of Docker engines configured for Swarm mode will be referred to as the &lt;em&gt;swarm&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A swarm comprises one or more &lt;em&gt;Manager Nodes&lt;/em&gt; and one or more &lt;em&gt;Worker Nodes&lt;/em&gt;. The manager nodes maintain the state of swarm and schedule application containers. The worker nodes run the application containers. As of Docker 1.12, no external backend, or 3rd party components, are required for a fully functioning swarm - everything is built-in!&lt;/p&gt;

&lt;p&gt;In this part of the demo you will use all three of the nodes in your lab. &lt;strong&gt;node1&lt;/strong&gt; will be the Swarm manager, while &lt;strong&gt;node2&lt;/strong&gt; and &lt;strong&gt;node3&lt;/strong&gt; will be worker nodes. Swarm mode supports highly available redundant manager nodes, but for the purposes of this lab you will only deploy a single manager node.&lt;/p&gt;

&lt;h2 id=&quot;step-21---create-a-manager-node&quot;&gt;Step 2.1 - Create a Manager node&lt;/h2&gt;

&lt;p&gt;In this step you’ll initialize a new Swarm, join a single worker node, and verify the operations worked.&lt;/p&gt;

&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm init&lt;/code&gt; on &lt;strong&gt;node1&lt;/strong&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr $(hostname -i)
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Swarm initialized: current node (6dlewb50pj2y66q4zi3egnwbi) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-1wxyoueqgpcrc4xk2t3ec7n1poy75g4kowmwz64p7ulqx611ih-68pazn0mj8p4p4lnuf4ctp8xy \
    10.0.0.5:2377

To add a manager to this swarm, run &apos;docker swarm join-token manager&apos; and follow the instructions.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker info&lt;/code&gt; command to verify that &lt;strong&gt;node1&lt;/strong&gt; was successfully configured as a swarm manager node.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker info
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Containers: 2
 Running: 0
 Paused: 0
 Stopped: 2
Images: 2
Server Version: 17.03.1-ee-3
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Backing Filesystem: extfs
 Dirs: 13
 Dirperm1 Supported: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
 Volume: local
 Network: bridge host macvlan null overlay
Swarm: active
 NodeID: rwezvezez3bg1kqg0y0f4ju22
 Is Manager: true
 ClusterID: qccn5eanox0uctyj6xtfvesy2
 Managers: 1
 Nodes: 1
 Orchestration:
  Task History Retention Limit: 5
 Raft:
  Snapshot Interval: 10000
  Number of Old Snapshots to Retain: 0
  Heartbeat Tick: 1
  Election Tick: 3
 Dispatcher:
  Heartbeat Period: 5 seconds
 CA Configuration:
  Expiry Duration: 3 months
 Node Address: 10.0.0.5
 Manager Addresses:
  10.0.0.5:2377
&amp;lt;Snip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The swarm is now initialized with &lt;strong&gt;node1&lt;/strong&gt; as the only Manager node. In the next section you will add &lt;strong&gt;node2&lt;/strong&gt; and &lt;strong&gt;node3&lt;/strong&gt; as &lt;em&gt;Worker nodes&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-22---join-worker-nodes-to-the-swarm&quot;&gt;Step 2.2 - Join Worker nodes to the Swarm&lt;/h2&gt;

&lt;p&gt;You will perform the following procedure on &lt;strong&gt;node2&lt;/strong&gt; and &lt;strong&gt;node3&lt;/strong&gt;. Towards the end of the procedure you will switch back to &lt;strong&gt;node1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, take the entire &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm join ...&lt;/code&gt; command we copied earlier from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;node1&lt;/code&gt; where it was displayed as terminal output. We need to paste the copied command into the terminal of &lt;strong&gt;node2&lt;/strong&gt; and &lt;strong&gt;node3&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It should look something like this for &lt;strong&gt;node2&lt;/strong&gt;. By the way, if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm join ...&lt;/code&gt; command scrolled off your screen already, you can run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm join-token worker&lt;/code&gt; command on the Manager node to get it again.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Remember, the tokens displayed here are not the actual tokens you will use. Copy the command from the output on &lt;strong&gt;node1&lt;/strong&gt;. On &lt;strong&gt;node2&lt;/strong&gt; and &lt;strong&gt;node3&lt;/strong&gt; it should look like this:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker swarm join \
    --token SWMTKN-1-1wxyoueqgpcrc4xk2t3ec7n1poy75g4kowmwz64p7ulqx611ih-68pazn0mj8p4p4lnuf4ctp8xy \
    10.0.0.5:2377
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker swarm join \
    --token SWMTKN-1-1wxyoueqgpcrc4xk2t3ec7n1poy75g4kowmwz64p7ulqx611ih-68pazn0mj8p4p4lnuf4ctp8xy \
    10.0.0.5:2377
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once you have run this on &lt;strong&gt;node2&lt;/strong&gt; and &lt;strong&gt;node3&lt;/strong&gt;, switch back to &lt;strong&gt;node1&lt;/strong&gt;, and run a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker node ls&lt;/code&gt; to verify that both nodes are part of the Swarm. You should see three nodes, &lt;strong&gt;node1&lt;/strong&gt; as the Manager node and &lt;strong&gt;node2&lt;/strong&gt; and &lt;strong&gt;node3&lt;/strong&gt; both as Worker nodes.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
6dlewb50pj2y66q4zi3egnwbi *  node1   Ready   Active        Leader
ym6sdzrcm08s6ohqmjx9mk3dv    node3   Ready   Active
yu3hbegvwsdpy9esh9t2lr431    node2   Ready   Active
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker node ls&lt;/code&gt; command shows you all of the nodes that are in the swarm as well as their roles in the swarm. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt; identifies the node that you are issuing the command from.&lt;/p&gt;

&lt;p&gt;Congratulations! You have configured a swarm with one manager node and two worker nodes.&lt;/p&gt;

&lt;h1 id=&quot;section-3-deploy-applications-across-multiple-hosts&quot;&gt;&lt;a name=&quot;multi-application&quot;&gt;&lt;/a&gt;Section 3: Deploy applications across multiple hosts&lt;/h1&gt;

&lt;p&gt;Now that you have a swarm up and running, it is time to deploy our really simple sleep application.&lt;/p&gt;

&lt;p&gt;You will perform the following procedure from &lt;strong&gt;node1&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-31---deploy-the-application-components-as-docker-services&quot;&gt;Step 3.1 - Deploy the application components as Docker services&lt;/h2&gt;

&lt;p&gt;Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sleep&lt;/code&gt; application is becoming very popular on the internet (due to hitting Reddit and HN). People just love it. So, you are going to have to scale your application to meet peak demand. You will have to do this across multiple hosts for high availability too. We will use the concept of &lt;em&gt;Services&lt;/em&gt; to scale our application easily and manage many containers as a single entity.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Services&lt;/em&gt; were a new concept in Docker 1.12. They work with swarms and are intended for long-running containers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You will perform this procedure from &lt;strong&gt;node1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s deploy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sleep&lt;/code&gt; as a &lt;em&gt;Service&lt;/em&gt; across our Docker Swarm.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service create --name sleep-app ubuntu sleep infinity
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;of5rxsxsmm3asx53dqcq0o29c
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verify that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;service create&lt;/code&gt; has been received by the Swarm manager.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME       MODE        REPLICAS  IMAGE
of5rxsxsmm3a  sleep-app  replicated  1/1       ubuntu:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The state of the service may change a couple times until it is running. The image is being downloaded from Docker Store to the other engines in the Swarm. Once the image is downloaded the container goes into a running state on one of the three nodes.&lt;/p&gt;

&lt;p&gt;At this point it may not seem that we have done anything very differently than just running a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run ...&lt;/code&gt;. We have again deployed a single container on a single host. The difference here is that the container has been scheduled on a swarm cluster.&lt;/p&gt;

&lt;p&gt;Well done. You have deployed the sleep-app to your new Swarm using Docker services.&lt;/p&gt;

&lt;h1 id=&quot;section-4-scale-the-application&quot;&gt;&lt;a name=&quot;scale-application&quot;&gt;&lt;/a&gt;Section 4: Scale the application&lt;/h1&gt;

&lt;p&gt;Demand is crazy! Everybody loves your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sleep&lt;/code&gt; app! It’s time to scale out.&lt;/p&gt;

&lt;p&gt;One of the great things about &lt;em&gt;services&lt;/em&gt; is that you can scale them up and down to meet demand. In this step you’ll scale the service up and then back down.&lt;/p&gt;

&lt;p&gt;You will perform the following procedure from &lt;strong&gt;node1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Scale the number of containers in the &lt;strong&gt;sleep-app&lt;/strong&gt; service to 7 with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker service update --replicas 7 sleep-app&lt;/code&gt; command. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replicas&lt;/code&gt; is the term we use to describe identical containers providing the same service.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service update --replicas 7 sleep-app
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The Swarm manager schedules so that there are 7 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sleep-app&lt;/code&gt; containers in the cluster. These will be scheduled evenly across the Swarm members.&lt;/p&gt;

&lt;p&gt;We are going to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker service ps sleep-app&lt;/code&gt; command. If you do this quick enough after using the  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--replicas&lt;/code&gt; option you can see the containers come up in real time.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps sleep-app
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME         IMAGE          NODE     DESIRED STATE  CURRENT STATE          ERROR  PORTS
7k0flfh2wpt1  sleep-app.1  ubuntu:latest  node1  Running        Running 9 minutes ago
wol6bzq7xf0v  sleep-app.2  ubuntu:latest  node3  Running        Running 2 minutes ago
id50tzzk1qbm  sleep-app.3  ubuntu:latest  node2  Running        Running 2 minutes ago
ozj2itmio16q  sleep-app.4  ubuntu:latest  node3  Running        Running 2 minutes ago
o4rk5aiely2o  sleep-app.5  ubuntu:latest  node2  Running        Running 2 minutes ago
35t0eamu0rue  sleep-app.6  ubuntu:latest  node2  Running        Running 2 minutes ago
44s8d59vr4a8  sleep-app.7  ubuntu:latest  node1  Running        Running 2 minutes ago
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that there are now 7 containers listed. It may take a few seconds for the new containers in the service to all show as &lt;strong&gt;RUNNING&lt;/strong&gt;.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NODE&lt;/code&gt; column tells us on which node a container is running.&lt;/p&gt;

&lt;p&gt;Scale the service back down to just four containers with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker service update --replicas 4 sleep-app&lt;/code&gt; command.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service update --replicas 4 sleep-app
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Verify that the number of containers has been reduced to 4 using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker service ps sleep-app&lt;/code&gt; command.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps sleep-app
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME         IMAGE          NODE     DESIRED STATE  CURRENT STATE           ERROR  PORTS
7k0flfh2wpt1  sleep-app.1  ubuntu:latest  node1  Running        Running 13 minutes ago
wol6bzq7xf0v  sleep-app.2  ubuntu:latest  node3  Running        Running 5 minutes ago
35t0eamu0rue  sleep-app.6  ubuntu:latest  node2  Running        Running 5 minutes ago
44s8d59vr4a8  sleep-app.7  ubuntu:latest  node1  Running        Running 5 minutes ago
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You have successfully scaled a swarm service up and down.&lt;/p&gt;

&lt;h1 id=&quot;section-5-drain-a-node-and-reschedule-the-containers&quot;&gt;&lt;a name=&quot;recover-application&quot;&gt;&lt;/a&gt;Section 5: Drain a node and reschedule the containers&lt;/h1&gt;

&lt;p&gt;Your sleep-app has been doing amazing after hitting Reddit and HN. It’s now number 1 on the App Store! You have scaled up during the holidays and down during the slow season. Now you are doing maintenance on one of your servers so you will need to gracefully take a server out of the swarm without interrupting service to your customers.&lt;/p&gt;

&lt;p&gt;Take a look at the status of your nodes again by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker node ls&lt;/code&gt; on &lt;strong&gt;node1&lt;/strong&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
6dlewb50pj2y66q4zi3egnwbi *  node1   Ready   Active        Leader
ym6sdzrcm08s6ohqmjx9mk3dv    node3   Ready   Active
yu3hbegvwsdpy9esh9t2lr431    node2   Ready   Active
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You will be taking &lt;strong&gt;node2&lt;/strong&gt; out of service for maintenance.&lt;/p&gt;

&lt;p&gt;Let’s see the containers that you have running on &lt;strong&gt;node2&lt;/strong&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE                                                                            COMMAND             CREATED             STATUS              PORTS               NAMES
4e7ea1154ea4        ubuntu@sha256:dd7808d8792c9841d0b460122f1acf0a2dd1f56404f8d1e56298048885e45535   &quot;sleep infinity&quot;    9 minutes ago       Up 9 minutes                            sleep-app.6.35t0eamu0rueeozz0pj2xaesi
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can see that we have one of the slepp-app containers running here (your output might look different though).&lt;/p&gt;

&lt;p&gt;Now let’s jump back to &lt;strong&gt;node1&lt;/strong&gt; (the Swarm manager) and take &lt;strong&gt;node2&lt;/strong&gt; out of service. To do that, let’s run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker node ls&lt;/code&gt; again.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
6dlewb50pj2y66q4zi3egnwbi *  node1   Ready   Active        Leader
ym6sdzrcm08s6ohqmjx9mk3dv    node3   Ready   Active
yu3hbegvwsdpy9esh9t2lr431    node2   Ready   Active
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We are going to take the &lt;strong&gt;ID&lt;/strong&gt; for &lt;strong&gt;node2&lt;/strong&gt; and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker node update --availability drain yournodeid&lt;/code&gt;. We are using the &lt;strong&gt;node2&lt;/strong&gt; host &lt;strong&gt;ID&lt;/strong&gt; as input into our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;drain&lt;/code&gt; command. Replace yournodeid with the id of &lt;strong&gt;node2&lt;/strong&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker node update --availability drain yournodeid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Check the status of the nodes&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
6dlewb50pj2y66q4zi3egnwbi *  node1   Ready   Active        Leader
ym6sdzrcm08s6ohqmjx9mk3dv    node3   Ready   Active
yu3hbegvwsdpy9esh9t2lr431    node2   Ready   Drain
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Node &lt;strong&gt;node2&lt;/strong&gt; is now in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Drain&lt;/code&gt; state.&lt;/p&gt;

&lt;p&gt;Switch back to &lt;strong&gt;node2&lt;/strong&gt; and see what is running there by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;node2&lt;/strong&gt; does not have any containers running on it.&lt;/p&gt;

&lt;p&gt;Lastly, check the service again on &lt;strong&gt;node1&lt;/strong&gt; to make sure that the container were rescheduled. You should see all four containers running on the remaining two nodes.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps sleep-app
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME             IMAGE          NODE     DESIRED STATE  CURRENT STATE           ERROR  PORTS
7k0flfh2wpt1  sleep-app.1      ubuntu:latest  node1  Running        Running 25 minutes ago
wol6bzq7xf0v  sleep-app.2      ubuntu:latest  node3  Running        Running 18 minutes ago
s3548wki7rlk  sleep-app.6      ubuntu:latest  node3  Running        Running 3 minutes ago
35t0eamu0rue   \_ sleep-app.6  ubuntu:latest  node2  Shutdown       Shutdown 3 minutes ago
44s8d59vr4a8  sleep-app.7      ubuntu:latest  node1  Running        Running 18 minutes ago
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;cleaning-up&quot;&gt;&lt;a name=&quot;cleanup&quot;&gt;&lt;/a&gt;Cleaning Up&lt;/h1&gt;

&lt;p&gt;Execute the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker service rm sleep-app&lt;/code&gt; command on &lt;strong&gt;node1&lt;/strong&gt; to remove the service called &lt;em&gt;myservice&lt;/em&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service rm sleep-app
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Execute the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt; command on &lt;strong&gt;node1&lt;/strong&gt; to get a list of running containers.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
044bea1c2277        ubuntu              &quot;sleep infinity&quot;    17 minutes ago      17 minutes ag                           distracted_mayer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker kill &amp;lt;CONTAINER ID&amp;gt;&lt;/code&gt; command on &lt;strong&gt;node1&lt;/strong&gt; to kill the sleep container we started at the beginning.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker kill yourcontainerid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, let’s remove node1, node2, and node3 from the Swarm. We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm leave --force&lt;/code&gt; command to do that.&lt;/p&gt;

&lt;p&gt;Lets run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm leave --force&lt;/code&gt; on &lt;strong&gt;node1&lt;/strong&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm leave --force
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm leave --force&lt;/code&gt; on &lt;strong&gt;node2&lt;/strong&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker swarm leave --force
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Finally, run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm leave --force&lt;/code&gt; on &lt;strong&gt;node3&lt;/strong&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term3&quot;&gt;docker swarm leave --force
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Congratulations! You’ve completed this lab. You now know how to build a swarm, deploy applications as collections of services, and scale individual services up and down.&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/orchestration-hol/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/orchestration-hol/</guid>
        
        <category>operations</category>
        
        <category>networking</category>
        
        <category>swarm</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker Networking Hands-on Lab</title>
        <description>&lt;p&gt;In this lab you will learn about key Docker Networking concepts. You will get your hands dirty by going through examples of a few basic networking concepts, learn about Bridge networking, and finally Overlay networking.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;: Beginner to Intermediate&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt;: Approximately 45 minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Tasks&lt;/strong&gt;:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;#task1&quot;&gt;Section #1 - Networking Basics&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#task2&quot;&gt;Section #2 - Bridge Networking&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#task3&quot;&gt;Section #3 - Overlay Networking&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#cleanup&quot;&gt;Cleaning Up&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;section-1---networking-basics&quot;&gt;&lt;a name=&quot;Task 1&quot;&gt;&lt;/a&gt;Section #1 - Networking Basics&lt;/h1&gt;

&lt;h2 id=&quot;step-1-the-docker-network-command&quot;&gt;&lt;a name=&quot;list_networks&quot;&gt;&lt;/a&gt;Step 1: The Docker Network Command&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network&lt;/code&gt; command is the main command for configuring and managing container networks. Run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network&lt;/code&gt; command from the first terminal.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
Usage:	docker network COMMAND

Manage networks

Options:
      --help   Print usage

Commands:
  connect     Connect a container to a network
  create      Create a network
  disconnect  Disconnect a container from a network
  inspect     Display detailed information on one or more networks
  ls          List networks
  prune       Remove all unused networks
  rm          Remove one or more networks

Run &apos;docker network COMMAND --help&apos; for more information on a command.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The command output shows how to use the command as well as all of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network&lt;/code&gt; sub-commands. As you can see from the output, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network&lt;/code&gt; command allows you to create new networks, list existing networks, inspect networks, and remove networks. It also allows you to connect and disconnect containers from networks.&lt;/p&gt;

&lt;h2 id=&quot;step-2-list-networks&quot;&gt;&lt;a name=&quot;list_networks&quot;&gt;&lt;/a&gt;Step 2: List networks&lt;/h2&gt;

&lt;p&gt;Run a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network ls&lt;/code&gt; command to view existing container networks on the current Docker host.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NETWORK ID          NAME                DRIVER              SCOPE
3430ad6f20bf        bridge              bridge              local
a7449465c379        host                host                local
06c349b9cc77        none                null                local
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The output above shows the container networks that are created as part of a standard installation of Docker.&lt;/p&gt;

&lt;p&gt;New networks that you create will also show up in the output of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network ls&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;You can see that each network gets a unique &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ID&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NAME&lt;/code&gt;. Each network is also associated with a single driver. Notice that the “bridge” network and the “host” network have the same name as their respective drivers.&lt;/p&gt;

&lt;h2 id=&quot;step-3-inspect-a-network&quot;&gt;&lt;a name=&quot;inspect&quot;&gt;&lt;/a&gt;Step 3: Inspect a network&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect&lt;/code&gt; command is used to view network configuration details. These details include; name, ID, driver, IPAM driver, subnet info, connected containers, and more.&lt;/p&gt;

&lt;p&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect &amp;lt;network&amp;gt;&lt;/code&gt; to view configuration details of the container networks on your Docker host. The command below shows the details of the network called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bridge&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network inspect bridge
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
    {
        &quot;Name&quot;: &quot;bridge&quot;,
        &quot;Id&quot;: &quot;3430ad6f20bf1486df2e5f64ddc93cc4ff95d81f59b6baea8a510ad500df2e57&quot;,
        &quot;Created&quot;: &quot;2017-04-03T16:49:58.6536278Z&quot;,
        &quot;Scope&quot;: &quot;local&quot;,
        &quot;Driver&quot;: &quot;bridge&quot;,
        &quot;EnableIPv6&quot;: false,
        &quot;IPAM&quot;: {
            &quot;Driver&quot;: &quot;default&quot;,
            &quot;Options&quot;: null,
            &quot;Config&quot;: [
                {
                    &quot;Subnet&quot;: &quot;172.17.0.0/16&quot;,
                    &quot;Gateway&quot;: &quot;172.17.0.1&quot;
                }
            ]
        },
        &quot;Internal&quot;: false,
        &quot;Attachable&quot;: false,
        &quot;Containers&quot;: {},
        &quot;Options&quot;: {
            &quot;com.docker.network.bridge.default_bridge&quot;: &quot;true&quot;,
            &quot;com.docker.network.bridge.enable_icc&quot;: &quot;true&quot;,
            &quot;com.docker.network.bridge.enable_ip_masquerade&quot;: &quot;true&quot;,
            &quot;com.docker.network.bridge.host_binding_ipv4&quot;: &quot;0.0.0.0&quot;,
            &quot;com.docker.network.bridge.name&quot;: &quot;docker0&quot;,
            &quot;com.docker.network.driver.mtu&quot;: &quot;1500&quot;
        },
        &quot;Labels&quot;: {}
    }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The syntax of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect&lt;/code&gt; command is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect &amp;lt;network&amp;gt;&lt;/code&gt;, where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;network&amp;gt;&lt;/code&gt; can be either network name or network ID. In the example above we are showing the configuration details for the network called “bridge”. Do not confuse this with the “bridge” driver.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;step-4-list-network-driver-plugins&quot;&gt;&lt;a name=&quot;list_drivers&quot;&gt;&lt;/a&gt;Step 4: List network driver plugins&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker info&lt;/code&gt; command shows a lot of interesting information about a Docker installation.&lt;/p&gt;

&lt;p&gt;Run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker info&lt;/code&gt; command and locate the list of network plugins.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker info
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 0
Server Version: 17.03.1-ee-3
Storage Driver: aufs
&amp;lt;Snip&amp;gt;
Plugins:
 Volume: local
 Network: bridge host macvlan null overlay
Swarm: inactive
Runtimes: runc
&amp;lt;Snip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output above shows the &lt;strong&gt;bridge&lt;/strong&gt;, &lt;strong&gt;host&lt;/strong&gt;,&lt;strong&gt;macvlan&lt;/strong&gt;, &lt;strong&gt;null&lt;/strong&gt;, and &lt;strong&gt;overlay&lt;/strong&gt; drivers.&lt;/p&gt;

&lt;h1 id=&quot;section-2---bridge-networking&quot;&gt;&lt;a name=&quot;Task #2&quot;&gt;&lt;/a&gt;Section #2 - Bridge Networking&lt;/h1&gt;

&lt;h2 id=&quot;step-1-the-basics&quot;&gt;&lt;a name=&quot;connect-container&quot;&gt;&lt;/a&gt;Step 1: The Basics&lt;/h2&gt;

&lt;p&gt;Every clean installation of Docker comes with a pre-built network called &lt;strong&gt;bridge&lt;/strong&gt;. Verify this with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network ls&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NETWORK ID          NAME                DRIVER              SCOPE
3430ad6f20bf        bridge              bridge              local
a7449465c379        host                host                local
06c349b9cc77        none                null                local
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output above shows that the &lt;strong&gt;bridge&lt;/strong&gt; network is associated with the &lt;em&gt;bridge&lt;/em&gt; driver. It’s important to note that the network and the driver are connected, but they are not the same. In this example the network and the driver have the same name - but they are not the same thing!&lt;/p&gt;

&lt;p&gt;The output above also shows that the &lt;strong&gt;bridge&lt;/strong&gt; network is scoped locally. This means that the network only exists on this Docker host. This is true of all networks using the &lt;em&gt;bridge&lt;/em&gt; driver - the &lt;em&gt;bridge&lt;/em&gt; driver provides single-host networking.&lt;/p&gt;

&lt;p&gt;All networks created with the &lt;em&gt;bridge&lt;/em&gt; driver are based on a Linux bridge (a.k.a. a virtual switch).&lt;/p&gt;

&lt;p&gt;Install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brctl&lt;/code&gt; command and use it to list the Linux bridges on your Docker host. You can do this by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo apt-get install bridge-utils&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;apk update
apk add bridge
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then, list the bridges on your Docker host, by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brctl show&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;brctl show
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bridge name	bridge id		STP enabled	interfaces
docker0		8000.024252ed52f7	no
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output above shows a single Linux bridge called &lt;strong&gt;docker0&lt;/strong&gt;. This is the bridge that was automatically created for the &lt;strong&gt;bridge&lt;/strong&gt; network. You can see that it has no interfaces currently connected to it.&lt;/p&gt;

&lt;p&gt;You can also use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ip a&lt;/code&gt; command to view details of the &lt;strong&gt;docker0&lt;/strong&gt; bridge.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;ip a
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;Snip&amp;gt;
3: docker0: &amp;lt;NO-CARRIER,BROADCAST,MULTICAST,UP&amp;gt; mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:52:ed:52:f7 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-2-connect-a-container&quot;&gt;&lt;a name=&quot;connect-container&quot;&gt;&lt;/a&gt;Step 2: Connect a container&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;bridge&lt;/strong&gt; network is the default network for new containers. This means that unless you specify a different network, all new containers will be connected to the &lt;strong&gt;bridge&lt;/strong&gt; network.&lt;/p&gt;

&lt;p&gt;Create a new container by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -dt ubuntu sleep infinity&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run -dt ubuntu sleep infinity
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Unable to find image &apos;ubuntu:latest&apos; locally
latest: Pulling from library/ubuntu
d54efb8db41d: Pull complete
f8b845f45a87: Pull complete
e8db7bf7c39f: Pull complete
9654c40e9079: Pull complete
6d9ef359eaaa: Pull complete
Digest: sha256:dd7808d8792c9841d0b460122f1acf0a2dd1f56404f8d1e56298048885e45535
Status: Downloaded newer image for ubuntu:latest
846af8479944d406843c90a39cba68373c619d1feaa932719260a5f5afddbf71
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This command will create a new container based on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ubuntu:latest&lt;/code&gt; image and will run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sleep&lt;/code&gt; command to keep the container running in the background. You can verify our example container is up by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
846af8479944        ubuntu              &quot;sleep infinity&quot;    55 seconds ago      Up 54 seconds                           heuristic_boyd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As no network was specified on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run&lt;/code&gt; command, the container will be added to the &lt;strong&gt;bridge&lt;/strong&gt; network.&lt;/p&gt;

&lt;p&gt;Run the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;brctl show&lt;/code&gt; command again.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;brctl show
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;bridge name	bridge id		STP enabled	interfaces
docker0		8000.024252ed52f7	no		vethd630437
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how the &lt;strong&gt;docker0&lt;/strong&gt; bridge now has an interface connected. This interface connects the &lt;strong&gt;docker0&lt;/strong&gt; bridge to the new container just created.&lt;/p&gt;

&lt;p&gt;You can inspect the &lt;strong&gt;bridge&lt;/strong&gt; network again, by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect bridge&lt;/code&gt;, to see the new container attached to it.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network inspect bridge
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;Snip&amp;gt;
        &quot;Containers&quot;: {
            &quot;846af8479944d406843c90a39cba68373c619d1feaa932719260a5f5afddbf71&quot;: {
                &quot;Name&quot;: &quot;heuristic_boyd&quot;,
                &quot;EndpointID&quot;: &quot;1265c418f0b812004d80336bafdc4437eda976f166c11dbcc97d365b2bfa91e5&quot;,
                &quot;MacAddress&quot;: &quot;02:42:ac:11:00:02&quot;,
                &quot;IPv4Address&quot;: &quot;172.17.0.2/16&quot;,
                &quot;IPv6Address&quot;: &quot;&quot;
            }
        },
&amp;lt;Snip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-3-test-network-connectivity&quot;&gt;&lt;a name=&quot;ping_local&quot;&gt;&lt;/a&gt;Step 3: Test network connectivity&lt;/h2&gt;

&lt;p&gt;The output to the previous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect&lt;/code&gt; command shows the IP address of the new container. In the previous example it is “172.17.0.2” but yours might be different.&lt;/p&gt;

&lt;p&gt;Ping the IP address of the container from the shell prompt of your Docker host by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping -c5 &amp;lt;IPv4 Address&amp;gt;&lt;/code&gt;. Remember to use the IP of the container in &lt;strong&gt;your&lt;/strong&gt; environment.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ping -c5 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.031 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.034 ms
64 bytes from 172.17.0.2: icmp_seq=4 ttl=64 time=0.041 ms
64 bytes from 172.17.0.2: icmp_seq=5 ttl=64 time=0.047 ms

--- 172.17.0.2 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4075ms
rtt min/avg/max/mdev = 0.031/0.041/0.055/0.011 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The replies above show that the Docker host can ping the container over the &lt;strong&gt;bridge&lt;/strong&gt; network. But, we can also verify the container can connect to the outside world too. Lets log into the container, install the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping&lt;/code&gt; program, and then ping &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;www.github.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, we need to get the ID of the container started in the previous step. You can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt; to get that.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
846af8479944        ubuntu              &quot;sleep infinity&quot;    7 minutes ago       Up 7 minutes                            heuristic_boyd
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, lets run a shell inside that ubuntu container, by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker exec -it &amp;lt;CONTAINER ID&amp;gt; /bin/bash&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker exec -it yourcontainerid /bin/bash
root@846af8479944:/#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, we need to install the ping program. So, lets run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apt-get update &amp;amp;&amp;amp; apt-get install -y iputils-ping&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;apt-get update &amp;amp;&amp;amp; apt-get install -y iputils-ping
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Lets ping www.github.com by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping -c5 www.github.com&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;  ping -c5 www.github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PING www.docker.com (104.239.220.248) 56(84) bytes of data.
64 bytes from 104.239.220.248: icmp_seq=1 ttl=45 time=38.1 ms
64 bytes from 104.239.220.248: icmp_seq=2 ttl=45 time=37.3 ms
64 bytes from 104.239.220.248: icmp_seq=3 ttl=45 time=37.5 ms
64 bytes from 104.239.220.248: icmp_seq=4 ttl=45 time=37.5 ms
64 bytes from 104.239.220.248: icmp_seq=5 ttl=45 time=37.5 ms

--- www.docker.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4003ms
rtt min/avg/max/mdev = 37.372/37.641/38.143/0.314 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, lets disconnect our shell from the container, by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;exit
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We should also stop this container so we clean things up from this test, by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker stop &amp;lt;CONTAINER ID&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker stop yourcontainerid
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This shows that the new container can ping the internet and therefore has a valid and working network configuration.&lt;/p&gt;

&lt;h2 id=&quot;step-4-configure-nat-for-external-connectivity&quot;&gt;&lt;a name=&quot;nat&quot;&gt;&lt;/a&gt;Step 4: Configure NAT for external connectivity&lt;/h2&gt;

&lt;p&gt;In this step we’ll start a new &lt;strong&gt;NGINX&lt;/strong&gt; container and map port 8080 on the Docker host to port 80 inside of the container. This means that traffic that hits the Docker host on port 8080 will be passed on to port 80 inside the container.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; If you start a new container from the official NGINX image without specifying a command to run, the container will run a basic web server on port 80.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Start a new container based off the official NGINX image by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run --name web1 -d -p 8080:80 nginx&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker run --name web1 -d -p 8080:80 nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Unable to find image &apos;nginx:latest&apos; locally
latest: Pulling from library/nginx
6d827a3ef358: Pull complete
b556b18c7952: Pull complete
03558b976e24: Pull complete
9abee7e1ef9d: Pull complete
Digest: sha256:52f84ace6ea43f2f58937e5f9fc562e99ad6876e82b99d171916c1ece587c188
Status: Downloaded newer image for nginx:latest
4e0da45b0f169f18b0e1ee9bf779500cb0f756402c0a0821d55565f162741b3e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Review the container status and port mappings by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                           NAMES
4e0da45b0f16        nginx               &quot;nginx -g &apos;daemon ...&quot;   2 minutes ago       Up 2 minutes        443/tcp, 0.0.0.0:8080-&amp;gt;80/tcp   web1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The top line shows the new &lt;strong&gt;web1&lt;/strong&gt; container running NGINX. Take note of the command the container is running as well as the port mapping - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0.0.0.0:8080-&amp;gt;80/tcp&lt;/code&gt; maps port 8080 on all host interfaces to port 80 inside the &lt;strong&gt;web1&lt;/strong&gt; container. This port mapping is what effectively makes the containers web service accessible from external sources (via the Docker hosts IP address on port 8080).&lt;/p&gt;

&lt;p&gt;Now that the container is running and mapped to a port on a host interface you can test connectivity to the NGINX web server.&lt;/p&gt;

&lt;p&gt;To complete the following task you will need the IP address of your Docker host. This will need to be an IP address that you can reach (e.g. your lab is hosted in Azure so this will be the instance’s Public IP - the one you SSH’d into). Just point your web browser to the IP and port 8080 of your Docker host. Also, if you try connecting to the same IP address on a different port number it will fail.&lt;/p&gt;

&lt;p&gt;If for some reason you cannot open a session from a web broswer, you can connect from your Docker host using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;curl 127.0.0.1:8080&lt;/code&gt; command.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;curl 127.0.0.1:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;Snip&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;
    &amp;lt;Snip&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Thank you for using nginx.&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you try and curl the IP address on a different port number it will fail.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The port mapping is actually port address translation (PAT).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;section-3---overlay-networking&quot;&gt;&lt;a name=&quot;Task #2&quot;&gt;&lt;/a&gt;Section #3 - Overlay Networking&lt;/h1&gt;

&lt;h2 id=&quot;step-1-the-basics-1&quot;&gt;&lt;a name=&quot;connect-container&quot;&gt;&lt;/a&gt;Step 1: The Basics&lt;/h2&gt;

&lt;p&gt;In this step you’ll initialize a new Swarm, join a single worker node, and verify the operations worked.&lt;/p&gt;

&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm init --advertise-addr $(hostname -i)&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm init --advertise-addr $(hostname -i)
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Swarm initialized: current node (rzyy572arjko2w0j82zvjkc6u) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join \
    --token SWMTKN-1-69b2x1u2wtjdmot0oqxjw1r2d27f0lbmhfxhvj83chln1l6es5-37ykdpul0vylenefe2439cqpf \
    10.0.0.5:2377

To add a manager to this swarm, run &apos;docker swarm join-token manager&apos; and follow the instructions.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the first terminal copy the entire &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm join ...&lt;/code&gt; command that is displayed as part of the output from your terminal output. Then, paste the copied command into the second terminal.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker swarm join \
&amp;gt;     --token SWMTKN-1-69b2x1u2wtjdmot0oqxjw1r2d27f0lbmhfxhvj83chln1l6es5-37ykdpul0vylenefe2439cqpf \
&amp;gt;     10.0.0.5:2377
This node joined a swarm as a worker.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker node ls&lt;/code&gt; to verify that both nodes are part of the Swarm.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker node ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
ijjmqthkdya65h9rjzyngdn48    node2   Ready   Active
rzyy572arjko2w0j82zvjkc6u *  node1   Ready   Active        Leader
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ID&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HOSTNAME&lt;/code&gt; values may be different in your lab. The important thing to check is that both nodes have joined the Swarm and are &lt;em&gt;ready&lt;/em&gt; and &lt;em&gt;active&lt;/em&gt;.&lt;/p&gt;

&lt;h2 id=&quot;step-2-create-an-overlay-network&quot;&gt;&lt;a name=&quot;create_network&quot;&gt;&lt;/a&gt;Step 2: Create an overlay network&lt;/h2&gt;

&lt;p&gt;Now that you have a Swarm initialized it’s time to create an &lt;strong&gt;overlay&lt;/strong&gt; network.&lt;/p&gt;

&lt;p&gt;Create a new overlay network called “overnet” by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network create -d overlay overnet&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network create -d overlay overnet
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wlqnvajmmzskn84bqbdi1ytuy
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network ls&lt;/code&gt; command to verify the network was created successfully.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NETWORK ID          NAME                DRIVER              SCOPE
3430ad6f20bf        bridge              bridge              local
a4d584350f09        docker_gwbridge     bridge              local
a7449465c379        host                host                local
8hq1n8nak54x        ingress             overlay             swarm
06c349b9cc77        none                null                local
wlqnvajmmzsk        overnet             overlay             swarm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The new “overnet” network is shown on the last line of the output above. Notice how it is associated with the &lt;strong&gt;overlay&lt;/strong&gt; driver and is scoped to the entire Swarm.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; The other new networks (ingress and docker_gwbridge) were created automatically when the Swarm cluster was created.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network ls&lt;/code&gt; command from the second terminal.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker network ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NETWORK ID          NAME                DRIVER              SCOPE
55f10b3fb8ed        bridge              bridge              local
b7b30433a639        docker_gwbridge     bridge              local
a7449465c379        host                host                local
8hq1n8nak54x        ingress             overlay             swarm
06c349b9cc77        none                null                local
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that the “overnet” network does &lt;strong&gt;not&lt;/strong&gt; appear in the list. This is because Docker only extends overlay networks to hosts when they are needed. This is usually when a host runs a task from a service that is created on the network. We will see this shortly.&lt;/p&gt;

&lt;p&gt;Use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect &amp;lt;network&amp;gt;&lt;/code&gt; command to view more detailed information about the “overnet” network. You will need to run this command from the first terminal.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network inspect overnet
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
    {
        &quot;Name&quot;: &quot;overnet&quot;,
        &quot;Id&quot;: &quot;wlqnvajmmzskn84bqbdi1ytuy&quot;,
        &quot;Created&quot;: &quot;0001-01-01T00:00:00Z&quot;,
        &quot;Scope&quot;: &quot;swarm&quot;,
        &quot;Driver&quot;: &quot;overlay&quot;,
        &quot;EnableIPv6&quot;: false,
        &quot;IPAM&quot;: {
            &quot;Driver&quot;: &quot;default&quot;,
            &quot;Options&quot;: null,
            &quot;Config&quot;: []
        },
        &quot;Internal&quot;: false,
        &quot;Attachable&quot;: false,
        &quot;Containers&quot;: null,
        &quot;Options&quot;: {
            &quot;com.docker.network.driver.overlay.vxlanid_list&quot;: &quot;4097&quot;
        },
        &quot;Labels&quot;: null
    }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;step-3-create-a-service&quot;&gt;&lt;a name=&quot;create_service&quot;&gt;&lt;/a&gt;Step 3: Create a service&lt;/h2&gt;

&lt;p&gt;Now that we have a Swarm initialized and an overlay network, it’s time to create a service that uses the network.&lt;/p&gt;

&lt;p&gt;Execute the following command from the first terminal to create a new service called &lt;em&gt;myservice&lt;/em&gt; on the &lt;em&gt;overnet&lt;/em&gt; network with two tasks/replicas.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service create --name myservice \
--network overnet \
--replicas 2 \
ubuntu sleep infinity
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ov30itv6t2n7axy2goqbfqt5e
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Verify that the service is created and both replicas are up by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker service ls&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME       MODE        REPLICAS  IMAGE
ov30itv6t2n7  myservice  replicated  2/2       ubuntu:latest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2/2&lt;/code&gt; in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;REPLICAS&lt;/code&gt; column shows that both tasks in the service are up and running.&lt;/p&gt;

&lt;p&gt;Verify that a single task (replica) is running on each of the two nodes in the Swarm by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker service ps myservice&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service ps myservice
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ID            NAME         IMAGE          NODE     DESIRED STATE  CURRENT STATE               ERROR  PORTS
riicggj5tuta  myservice.1  ubuntu:latest  node2  Running        Running about a minute ago
nlozn82wsttv  myservice.2  ubuntu:latest  node1  Running        Running about a minute ago
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ID&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NODE&lt;/code&gt; values might be different in your output. The important thing to note is that each task/replica is running on a different node.&lt;/p&gt;

&lt;p&gt;Now that the second node is running a task on the “overnet” network it will be able to see the “overnet” network. Lets run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network ls&lt;/code&gt; from the second terminal to verify this.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker network ls
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NETWORK ID          NAME                DRIVER              SCOPE
55f10b3fb8ed        bridge              bridge              local
b7b30433a639        docker_gwbridge     bridge              local
a7449465c379        host                host                local
8hq1n8nak54x        ingress             overlay             swarm
06c349b9cc77        none                null                local
wlqnvajmmzsk        overnet             overlay             swarm
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can also run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect overnet&lt;/code&gt; on the second terminal to get more detailed information about the “overnet” network and obtain the IP address of the task running on the second terminal.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker network inspect overnet
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
    {
        &quot;Name&quot;: &quot;overnet&quot;,
        &quot;Id&quot;: &quot;wlqnvajmmzskn84bqbdi1ytuy&quot;,
        &quot;Created&quot;: &quot;2017-04-04T09:35:47.526642642Z&quot;,
        &quot;Scope&quot;: &quot;swarm&quot;,
        &quot;Driver&quot;: &quot;overlay&quot;,
        &quot;EnableIPv6&quot;: false,
        &quot;IPAM&quot;: {
            &quot;Driver&quot;: &quot;default&quot;,
            &quot;Options&quot;: null,
            &quot;Config&quot;: [
                {
                    &quot;Subnet&quot;: &quot;10.0.0.0/24&quot;,
                    &quot;Gateway&quot;: &quot;10.0.0.1&quot;
                }
            ]
        },
        &quot;Internal&quot;: false,
        &quot;Attachable&quot;: false,
        &quot;Containers&quot;: {
            &quot;fbc8bb0834429a68b2ccef25d3c90135dbda41e08b300f07845cb7f082bcdf01&quot;: {
                &quot;Name&quot;: &quot;myservice.1.riicggj5tutar7h7sgsvqg72r&quot;,
                &quot;EndpointID&quot;: &quot;8edf83ebce77aed6d0193295c80c6aa7a5b76a08880a166002ecda3a2099bb6c&quot;,
                &quot;MacAddress&quot;: &quot;02:42:0a:00:00:03&quot;,
                &quot;IPv4Address&quot;: &quot;10.0.0.3/24&quot;,
                &quot;IPv6Address&quot;: &quot;&quot;
            }
        },
        &quot;Options&quot;: {
            &quot;com.docker.network.driver.overlay.vxlanid_list&quot;: &quot;4097&quot;
        },
        &quot;Labels&quot;: {},
        &quot;Peers&quot;: [
            {
                &quot;Name&quot;: &quot;node1-f6a6f8e18a9d&quot;,
                &quot;IP&quot;: &quot;10.0.0.5&quot;
            },
            {
                &quot;Name&quot;: &quot;node2-507a763bed93&quot;,
                &quot;IP&quot;: &quot;10.0.0.6&quot;
            }
        ]
    }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You should note that as of Docker 1.12, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect&lt;/code&gt; only shows containers/tasks running on the local node. This means that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.0.3&lt;/code&gt; is the IPv4 address of the container running on the second node. Make a note of this IP address for the next step (the IP address in your lab might be different than the one shown here in the lab guide).&lt;/p&gt;

&lt;h2 id=&quot;step-4-test-the-network&quot;&gt;&lt;a name=&quot;test&quot;&gt;&lt;/a&gt;Step 4: Test the network&lt;/h2&gt;

&lt;p&gt;To complete this step you will need the IP address of the service task running on &lt;strong&gt;node2&lt;/strong&gt; that you saw in the previous step (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.0.3&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Execute the following commands from the first terminal.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker network inspect overnet
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
    {
        &quot;Name&quot;: &quot;overnet&quot;,
        &quot;Id&quot;: &quot;wlqnvajmmzskn84bqbdi1ytuy&quot;,
        &quot;Created&quot;: &quot;2017-04-04T09:35:47.362263887Z&quot;,
        &quot;Scope&quot;: &quot;swarm&quot;,
        &quot;Driver&quot;: &quot;overlay&quot;,
        &quot;EnableIPv6&quot;: false,
        &quot;IPAM&quot;: {
            &quot;Driver&quot;: &quot;default&quot;,
            &quot;Options&quot;: null,
            &quot;Config&quot;: [
                {
                    &quot;Subnet&quot;: &quot;10.0.0.0/24&quot;,
                    &quot;Gateway&quot;: &quot;10.0.0.1&quot;
                }
            ]
        },
        &quot;Internal&quot;: false,
        &quot;Attachable&quot;: false,
        &quot;Containers&quot;: {
            &quot;d676496d18f76c34d3b79fbf6573a5672a81d725d7c8704b49b4f797f6426454&quot;: {
                &quot;Name&quot;: &quot;myservice.2.nlozn82wsttv75cs9vs3ju7vs&quot;,
                &quot;EndpointID&quot;: &quot;36638a55fcf4ada2989650d0dde193bc2f81e0e9e3c153d3e9d1d85e89a642e6&quot;,
                &quot;MacAddress&quot;: &quot;02:42:0a:00:00:04&quot;,
                &quot;IPv4Address&quot;: &quot;10.0.0.4/24&quot;,
                &quot;IPv6Address&quot;: &quot;&quot;
            }
        },
        &quot;Options&quot;: {
            &quot;com.docker.network.driver.overlay.vxlanid_list&quot;: &quot;4097&quot;
        },
        &quot;Labels&quot;: {},
        &quot;Peers&quot;: [
            {
                &quot;Name&quot;: &quot;node1-f6a6f8e18a9d&quot;,
                &quot;IP&quot;: &quot;10.0.0.5&quot;
            },
            {
                &quot;Name&quot;: &quot;node2-507a763bed93&quot;,
                &quot;IP&quot;: &quot;10.0.0.6&quot;
            }
        ]
    }
]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that the IP address listed for the service task (container) running is different to the IP address for the service task running on the second node. Note also that they are on the same “overnet” network.&lt;/p&gt;

&lt;p&gt;Run a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt; command to get the ID of the service task so that you can log in to it in the next step.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE                                                                            COMMAND                  CREATED             STATUS              PORTS                           NAMES
d676496d18f7        ubuntu@sha256:dd7808d8792c9841d0b460122f1acf0a2dd1f56404f8d1e56298048885e45535   &quot;sleep infinity&quot;         10 minutes ago      Up 10 minutes                                       myservice.2.nlozn82wsttv75cs9vs3ju7vs
&amp;lt;Snip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Log on to the service task. Be sure to use the container &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ID&lt;/code&gt; from your environment as it will be different from the example shown below. We can do this by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker exec -it &amp;lt;CONTAINER ID&amp;gt; /bin/bash&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker exec -it yourcontainerid /bin/bash
root@d676496d18f7:/#
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Install the ping command and ping the service task running on the second node where it had a IP address of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.0.3&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker network inspect overnet&lt;/code&gt; command.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;apt-get update &amp;amp;&amp;amp; apt-get install -y iputils-ping
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, lets ping &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.0.3&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@d676496d18f7:/# ping -c5 10.0.0.3
PING 10.0.0.3 (10.0.0.3) 56(84) bytes of data.
^C
--- 10.0.0.3 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 2998ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output above shows that both tasks from the &lt;strong&gt;myservice&lt;/strong&gt; service are on the same overlay network spanning both nodes and that they can use this network to communicate.&lt;/p&gt;

&lt;h2 id=&quot;step-5-test-service-discovery&quot;&gt;&lt;a name=&quot;discover&quot;&gt;&lt;/a&gt;Step 5: Test service discovery&lt;/h2&gt;

&lt;p&gt;Now that you have a working service using an overlay network, let’s test service discovery.&lt;/p&gt;

&lt;p&gt;If you are not still inside of the container, log back into it with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker exec -it &amp;lt;CONTAINER ID&amp;gt; /bin/bash&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat /etc/resolv.conf&lt;/code&gt; form inside of the container.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker exec -it yourcontainerid /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cat /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;search ivaf2i2atqouppoxund0tvddsa.jx.internal.cloudapp.net
nameserver 127.0.0.11
options ndots:0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The value that we are interested in is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nameserver 127.0.0.11&lt;/code&gt;. This value sends all DNS queries from the container to an embedded DNS resolver running inside the container listening on 127.0.0.11:53. All Docker container run an embedded DNS server at this address.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; Some of the other values in your file may be different to those shown in this guide.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Try and ping the “myservice” name from within the container by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping -c5 myservice&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@d676496d18f7:/# ping -c5 myservice
PING myservice (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=0.020 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.052 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.044 ms
64 bytes from 10.0.0.2: icmp_seq=4 ttl=64 time=0.042 ms
64 bytes from 10.0.0.2: icmp_seq=5 ttl=64 time=0.056 ms

--- myservice ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4001ms
rtt min/avg/max/mdev = 0.020/0.042/0.056/0.015 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The output clearly shows that the container can ping the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myservice&lt;/code&gt; service by name. Notice that the IP address returned is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.0.2&lt;/code&gt;. In the next few steps we’ll verify that this address is the virtual IP (VIP) assigned to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;myservice&lt;/code&gt; service.&lt;/p&gt;

&lt;p&gt;Type the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exit&lt;/code&gt; command to leave the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; container session and return to the shell prompt of your Docker host.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;root@d676496d18f7:/# exit
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Inspect the configuration of the “myservice” service by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker service inspect myservice&lt;/code&gt;. Lets verify that the VIP value matches the value returned by the previous &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping -c5 myservice&lt;/code&gt; command.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service inspect myservice
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[
    {
        &quot;ID&quot;: &quot;ov30itv6t2n7axy2goqbfqt5e&quot;,
        &quot;Version&quot;: {
            &quot;Index&quot;: 19
        },
        &quot;CreatedAt&quot;: &quot;2017-04-04T09:35:47.009730798Z&quot;,
        &quot;UpdatedAt&quot;: &quot;2017-04-04T09:35:47.05475096Z&quot;,
        &quot;Spec&quot;: {
            &quot;Name&quot;: &quot;myservice&quot;,
            &quot;TaskTemplate&quot;: {
                &quot;ContainerSpec&quot;: {
                    &quot;Image&quot;: &quot;ubuntu:latest@sha256:dd7808d8792c9841d0b460122f1acf0a2dd1f56404f8d1e56298048885e45535&quot;,
                    &quot;Args&quot;: [
                        &quot;sleep&quot;,
                        &quot;infinity&quot;
                    ],
&amp;lt;Snip&amp;gt;
        &quot;Endpoint&quot;: {
            &quot;Spec&quot;: {
                &quot;Mode&quot;: &quot;vip&quot;
            },
            &quot;VirtualIPs&quot;: [
                {
                    &quot;NetworkID&quot;: &quot;wlqnvajmmzskn84bqbdi1ytuy&quot;,
                    &quot;Addr&quot;: &quot;10.0.0.2/24&quot;
                }
            ]
        },
&amp;lt;Snip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Towards the bottom of the output you will see the VIP of the service listed. The VIP in the output above is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;10.0.0.2&lt;/code&gt; but the value may be different in your setup. The important point to note is that the VIP listed here matches the value returned by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping -c5 myservice&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Feel free to create a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker exec&lt;/code&gt; session to the service task (container) running on &lt;strong&gt;node2&lt;/strong&gt; and perform the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ping -c5 service&lt;/code&gt; command. You will get a response form the same VIP.&lt;/p&gt;

&lt;h1 id=&quot;cleaning-up&quot;&gt;&lt;a name=&quot;cleanup&quot;&gt;&lt;/a&gt;Cleaning Up&lt;/h1&gt;

&lt;p&gt;Hopefully you were able to learn a little about how Docker Networking works during this lab. Lets clean up the service we created, the containers we started, and finally disable Swarm mode.&lt;/p&gt;

&lt;p&gt;Execute the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker service rm myservice&lt;/code&gt; command to remove the service called &lt;em&gt;myservice&lt;/em&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker service rm myservice
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Execute the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker ps&lt;/code&gt; command to get a list of running containers.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                           NAMES
846af8479944        ubuntu              &quot;sleep infinity&quot;         17 minutes ago      Up 17 minutes                                       heuristic_boyd
4e0da45b0f16        nginx               &quot;nginx -g &apos;daemon ...&quot;   12 minutes ago      Up 12 minutes       443/tcp, 0.0.0.0:8080-&amp;gt;80/tcp   web1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker kill &amp;lt;CONTAINER ID ...&amp;gt;&lt;/code&gt; command to kill the ubunut and nginx containers we started at the beginning.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker kill yourcontainerid1 yourcontainerid2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finally, lets remove node1 and node2 from the Swarm. We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm leave --force&lt;/code&gt; command to do that.&lt;/p&gt;

&lt;p&gt;Lets run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm leave --force&lt;/code&gt; on node1.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker swarm leave --force
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Lets also run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker swarm leave --force&lt;/code&gt; on node2.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term2&quot;&gt;docker swarm leave --force
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Congratulations! You’ve completed this lab!&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/docker-networking-hol/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/docker-networking-hol/</guid>
        
        <category>operations</category>
        
        <category>networking</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Continuous Integration With Docker Cloud</title>
        <description>&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Difficulty&lt;/strong&gt;: Beginner&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt;: Approximately 20 minutes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this lab, you will learn how to configure a continuous integration (CI) pipeline for a web application using Docker Cloud’s automated build features. You will complete the following tasks as part of the lab:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href=&quot;#prerequisites&quot;&gt;Task 0: Configure the prerequisites&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#deploy_app&quot;&gt;Task 1: Configure Docker Cloud to Automatically Build Docker Images&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#autobuild&quot;&gt;Task 1.1: Configure Docker Cloud Autobuilds&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&quot;#test_autobuild&quot;&gt;Task 1.2: Trigger an Autobuild&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;what-is-docker-cloud&quot;&gt;What is Docker Cloud?&lt;/h2&gt;

&lt;p&gt;Docker Cloud is Docker’s cloud platform for individual developers and teams to build, ship, and run containerized applications. Docker Cloud enables teams to come together to collaborate on their projects and automate complex continuous delivery flows, which enables you to focus on improving your app, and leave the rest up to Docker Cloud.&lt;/p&gt;

&lt;h2 id=&quot;document-conventions&quot;&gt;Document conventions&lt;/h2&gt;

&lt;p&gt;When you encounter a phrase in between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;gt;&lt;/code&gt;  you are meant to substitute in a different value.&lt;/p&gt;

&lt;p&gt;For instance if you see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh &amp;lt;username&amp;gt;@&amp;lt;hostname&amp;gt;&lt;/code&gt; you would actually type something like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh ubuntu@node0-gvs0mgc0216.southcentralus.cloudapp.azure.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You may be asked to SSH into various nodes. These nodes are referred to as &lt;strong&gt;node0&lt;/strong&gt;, &lt;strong&gt;node1&lt;/strong&gt; etc. These tags correspond to the very beginning of the hostnames you will find in your welcome email.&lt;/p&gt;

&lt;h2 id=&quot;task-0-prerequisites&quot;&gt;&lt;a name=&quot;prerequisites&quot;&gt;&lt;/a&gt;Task 0: Prerequisites&lt;/h2&gt;

&lt;p&gt;In order to complete this lab, you will need the following:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A Docker ID&lt;/li&gt;
  &lt;li&gt;A management host (for this lab you’ll need a Linux named &lt;strong&gt;node0&lt;/strong&gt;)&lt;/li&gt;
  &lt;li&gt;A GitHub account&lt;/li&gt;
  &lt;li&gt;Git installed&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://docker.com/getdocker&quot;&gt;Docker installed&lt;/a&gt; at the command-line&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;obtain-a-docker-id&quot;&gt;Obtain a Docker ID&lt;/h3&gt;

&lt;p&gt;If you do not already have a Docker ID, you will need to create one now. Creating a Docker ID is free, and allows you to use &lt;a href=&quot;https://cloud.docker.com&quot;&gt;Docker Cloud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you already have a Docker ID, skip to the next prerequisite.&lt;/p&gt;

&lt;p&gt;To create a Docker ID:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Use your web browser to visit &lt;a href=&quot;https://cloud.docker.com&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://cloud.docker.com&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;On the right hand side of the screen, enter your preferred Docker ID, supply your email address, and choose a password.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Sign up&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Check your email (&lt;strong&gt;including your spam folder&lt;/strong&gt;) for a confirmation email.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Follow the steps outlined in the email.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;You should be redirected back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://cloud.docker.com&lt;/code&gt;.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You now have a Docker ID. Remember to keep the password safe and secure.&lt;/p&gt;

&lt;h3 id=&quot;github-account&quot;&gt;GitHub account&lt;/h3&gt;

&lt;p&gt;In order to complete the CI portions of this lab, you will need an account on GitHub. If you do not already have one, you can create one for free at &lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Continue with the lab as soon as you have completed the prerequisites.&lt;/p&gt;

&lt;h1 id=&quot;task-1-configure-docker-cloud-to-automatically-build-docker-images&quot;&gt;&lt;a name=&quot;deploy_app&quot;&gt;&lt;/a&gt;Task 1: Configure Docker Cloud to Automatically Build Docker Images&lt;/h1&gt;

&lt;p&gt;One of the most powerful features of Docker Cloud is the ability to define end-to-end CI/CD pipelines. In this part of the lab, you’re going to link your GitHub account to Docker Cloud to facilitate seamless application delivery.&lt;/p&gt;

&lt;p&gt;Let’s start by linking your Docker Cloud account to your GitHub account:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Using your web browser, go to &lt;a href=&quot;https://cloud.docker.com&quot;&gt;https://cloud.docker.com&lt;/a&gt; and sign in with your Docker ID.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click the &lt;strong&gt;Cloud Settings&lt;/strong&gt; link in the menu on the left hand side of the Docker Cloud web UI.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you cannot see menu on the left, un-select &lt;strong&gt;Swarm Mode&lt;/strong&gt; at the top of the screen.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
  &lt;li&gt;Scroll down to the &lt;strong&gt;Source providers&lt;/strong&gt; section. Click the &lt;strong&gt;power plug&lt;/strong&gt; icon next to GitHub, and follow the procedure to link your GitHub account.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/images/power_socket.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now that you’ve got Docker Cloud linked to your GitHub account, we’ll start by forking a demo repo.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;In your web browser, navigate to &lt;a href=&quot;https://github.com/Cloud-Demo-Team/voting-demo.git&quot;&gt; https://github.com/Cloud-Demo-Team/voting-demo.git&lt;/a&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click the &lt;strong&gt;Fork&lt;/strong&gt; button in the upper right hand corner to create your own copy of the source repository.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next, we’ll clone the repository into our local Docker environment. The following commands will be executed in the terminal or command window from Linux &lt;strong&gt;node0&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;At the command line, change into the directory where you want to clone code.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Clone the repository (you will need to have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; installed and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git&lt;/code&gt; binary present in your PATH).&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; $ git clone https://github.com/&amp;lt;your github user name&amp;gt;/voting-demo.git

 Cloning into &apos;voting-demo&apos;...
 remote: Counting objects: 481, done.
 remote: Total 481 (delta 0), reused 0 (delta 0), pack-reused 481
 Receiving objects: 100% (481/481), 105.01 KiB | 0 bytes/s, done.
 Resolving deltas: 100% (246/246), done.
 Checking connectivity... done.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This will create a copy of the forked repo in a directory called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;voting-demo&lt;/code&gt; within your home directory.&lt;/p&gt;

&lt;h1 id=&quot;task-11-configure-autobuilds&quot;&gt;&lt;a name=&quot;autobuild&quot;&gt;&lt;/a&gt;Task 1.1: Configure Autobuilds&lt;/h1&gt;

&lt;p&gt;Docker Cloud can automatically build new images when updates are pushed to a repository on GitHub.&lt;/p&gt;

&lt;p&gt;In this step, you’re going to build two GitHub repositories - one for the &lt;strong&gt;voting&lt;/strong&gt; part of the app and one for the &lt;strong&gt;results&lt;/strong&gt; part. You’ll configure two new repositories in Docker Cloud so that each time a change is pushed to the source repo an updated Docker image will be built.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;In your web browser, return to Docker Cloud and click the &lt;strong&gt;Repositories&lt;/strong&gt; link on the left hand side.&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/images/repositories.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click the &lt;strong&gt;+&lt;/strong&gt; icon near the top right of the page and select &lt;strong&gt;Repository&lt;/strong&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Enter the following information in the &lt;strong&gt;Create Repository&lt;/strong&gt; section:&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: results&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Description&lt;/strong&gt;: Results service for the Docker voting app&lt;/li&gt;
      &lt;li&gt;&lt;strong&gt;Visibilty&lt;/strong&gt;: public&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;In the &lt;strong&gt;Build Settings&lt;/strong&gt; section, you should see that your GitHub account is connected. Click the &lt;strong&gt;GitHub icon&lt;/strong&gt;.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Make sure the your appropriate GitHub organization is populated from the drop down list, and select &lt;strong&gt;voting-demo&lt;/strong&gt; for repository.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Select “Click here to customize the build settings” to configure the build rules.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt; at the bottom of the page.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will be taken to the repository page.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Right now Docker Cloud doesn’t let you specify the build context when you create a repository, so you need to update the settings&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
  &lt;li&gt;Navigate to the Builds page and click ‘Configure Automated Builds’. Scroll down to Build Rule, and set the &lt;strong&gt;Build Context&lt;/strong&gt; to &lt;strong&gt;/results&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/images/update-automated-build.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;create-a-second-repository&quot;&gt;Create a second repository&lt;/h3&gt;
&lt;p&gt;Repeat steps 1-8 with the following modifications:&lt;/p&gt;

&lt;p&gt;Create Repo (Step 3)&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: voting&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Description&lt;/strong&gt;: Voting service for the Docker voting app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Specify the Dockerfile path (in Step 7):&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Enter &lt;strong&gt;/voting/Dockerfile&lt;/strong&gt; for the &lt;strong&gt;Dockerfile Path&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;check-to-make-sure-the-repositories-were-created&quot;&gt;Check to make sure the repositories were created&lt;/h3&gt;
&lt;p&gt;If you click the &lt;strong&gt;Repositories&lt;/strong&gt; menu on the left you should see both the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;voting&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;results&lt;/code&gt; respositories were created.&lt;/p&gt;

&lt;p&gt;Well done! You’ve created two new repos and configured them to autobuild whenever new changes are pushed to the associated GitHub repos.&lt;/p&gt;

&lt;h1 id=&quot;task-12-trigger-an-autobuild&quot;&gt;&lt;a name=&quot;test_autobuild&quot;&gt;&lt;/a&gt;Task 1.2: Trigger an Autobuild&lt;/h1&gt;

&lt;p&gt;Switch back the command line of your VM.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;If you have not already log into your Azure VM. For example (be sure to use the actual node name supplied in your email):&lt;/p&gt;

    &lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ssh ubuntu@node0-gvs0mgc0216.southcentralus.cloudapp.azure.com&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Change to the directory containing the voting app.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; $ cd ~/voting-demo/voting
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Use vi or your favorite text editor to open &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;app.py&lt;/code&gt;.
    &lt;ul&gt;
      &lt;li&gt;To use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vi&lt;/code&gt; on Linux: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$ vi app.py&lt;/code&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Scroll down to find the lines containing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;optionA&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;optionB&lt;/code&gt;, and change &lt;strong&gt;Dev&lt;/strong&gt; and &lt;strong&gt;Ops&lt;/strong&gt; to &lt;strong&gt;Futbol&lt;/strong&gt; and &lt;strong&gt;Soccer&lt;/strong&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; optionA = &quot;Futbol&quot;
 optionB = &quot;Soccer&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Save your changes.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Commit changes to the repository and push to GitHub using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git add&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push&lt;/code&gt;.&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; $ git add *
 $ git commit -m &quot;changing the voting options&quot;
 [master 2ab640a] changing the voting options
 		1 file changed, 3 insertions(+), 2 deletions(-)
 		$ git push origin master
 		Counting objects: 4, done.
 Delta compression using up to 8 threads.
 Compressing objects: 100% (4/4), done.
 Writing objects: 100% (4/4), 380 bytes | 0 bytes/s, done.
 Total 4 (delta 3), reused 0 (delta 0)
 To https://github.com/&amp;lt;your github repo&amp;gt;/voting-demo.git
	c1788a1..2ab640a  master -&amp;gt; master
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You may be prompted to set your email and name when you attempt to commit your changes. If this is the case, simply follow the instructions provided on your screen.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you have two factor authentication (2FA) configured on your GitHub account you will need to enter your personal access token (PAT) instead of your password when prompted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;In the Docker Cloud web UI, navigate back to the &lt;strong&gt;voting&lt;/strong&gt; repo and notice that the status is &lt;strong&gt;BUILDING&lt;/strong&gt;.&lt;/p&gt;

    &lt;blockquote&gt;
      &lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: It can take several minutes for a build job to complete.&lt;/p&gt;
    &lt;/blockquote&gt;

    &lt;p&gt;&lt;img src=&quot;/images/building.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click the &lt;strong&gt;Timeline&lt;/strong&gt; tab near the top of the screen.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Click &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Build in master:/voting&lt;/code&gt;.&lt;/p&gt;

    &lt;p&gt;Here you can see the status of the build process:&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/images/build_status.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Congratulations! You have successfully configured Docker Cloud to automatically build a new Docker image each time you push a change to your application’s repository on GitHub.&lt;/p&gt;

&lt;h1 id=&quot;learn-more&quot;&gt;&lt;a name=&quot;autobuild&quot;&gt;&lt;/a&gt;Learn More&lt;/h1&gt;

&lt;p&gt;To learn more about Docker Cloud’s continuous integration (CI) capabilities and how to bring more automation and collaboration to your application pipeline, check out:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Video &lt;a href=&quot;https://www.youtube.com/watch?v=sl2mfyjnkXk&quot;&gt;Automated Builds with Docker Cloud&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Docs&lt;/strong&gt;: &lt;a href=&quot;https://docs.docker.com/docker-cloud/builds/automated-build/&quot;&gt;Automated Builds&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/ci-docker-cloud-hol/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/ci-docker-cloud-hol/</guid>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
        <category>intermediate</category>
        
      </item>
    
      <item>
        <title>Windows Containers Multi-Container Applications</title>
        <description>&lt;h2 id=&quot;multi-container-applications&quot;&gt;Multi-Container Applications&lt;/h2&gt;

&lt;p&gt;This tutorial will walk you through using the sample Music Store application with Windows containers. The Music Store application is a standard .NET sample application, available in the &lt;a href=&quot;https://github.com/aspnet/MusicStore&quot; title=&quot;Music Store application&quot;&gt;aspnet GitHub repository&lt;/a&gt;. We’ve &lt;a href=&quot;https://github.com/friism/MusicStore&quot; title=&quot;link to forked version of Music Store App&quot;&gt;forked it&lt;/a&gt; to use Windows Containers.&lt;/p&gt;

&lt;h2 id=&quot;using-docker-compose-on-windows&quot;&gt;Using docker-compose on Windows&lt;/h2&gt;
&lt;p&gt;Docker Compose is a great way develop complex multi-container consisting of databases, queues and web frontends.&lt;/p&gt;

&lt;p&gt;To develop with Docker Compose on a Windows Server 2016 system, install compose too (this is not required on Windows 10 with Docker for Windows installed):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Invoke-WebRequest https://github.com/docker/compose/releases/download/1.11.1/docker-compose-Windows-x86_64.exe -UseBasicParsing -OutFile $env:ProgramFiles\docker\docker-compose.exe
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To try out Compose on Windows, clone a variant of the ASP.NET Core MVC MusicStore app, backed by a SQL Server Express 2016 database.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git clone https://github.com/dockersamples/dotnet-musicstore.git
...
cd dotnet-musicstore
docker-compose -f .\docker-compose.windows.yml build
...
docker-compose -f .\docker-compose.windows.yml up
...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To access the running app from the host running the containers (for example when running on Windows 10 or if opening browser on Windows Server 2016 system running Docker engine) use the container IP and port 5000. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;localhost&lt;/code&gt; will not work:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker inspect -f {{ .NetworkSettings.IPAddress }} dotnet-musicstore_web_1
172.21.124.54
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If using Windows Server 2016 and accessing from outside the VM or host, simply use the VM or host IP and port 5000.&lt;/p&gt;

&lt;h3 id=&quot;whats-happening-here&quot;&gt;What’s happening here?&lt;/h3&gt;
&lt;p&gt;Take a closer look at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.windows.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;version: &apos;2.1&apos;
services:
  db:
    image: microsoft/mssql-server-windows-express
    environment:
      sa_password: &quot;Password1&quot;
    ports:
      - &quot;1433:1433&quot; # for debug. Remove this for production

  web:
    build:
      context: .
      dockerfile: Dockerfile.windows
    environment:
      - &quot;Data:DefaultConnection:ConnectionString=Server=db,1433;Database=MusicStore;User Id=sa;Password=Password1;MultipleActiveResultSets=True&quot;
    depends_on:
      - &quot;db&quot;
    ports:
      - &quot;5000:5000&quot;

networks:
  default:
    external:
      name: nat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can find more details in the &lt;a href=&quot;https://docs.docker.com/compose/&quot; title=&quot;Docker Compose documentation&quot;&gt;Docker Compose documentation&lt;/a&gt;, but basically here’s what is happening.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Two services are defined, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db&lt;/code&gt; is a Microsoft SQL Express image official image from Microsoft.
    &lt;ul&gt;
      &lt;li&gt;The password for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db&lt;/code&gt; is set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Password1&lt;/code&gt; (obviously only for a developer environment).&lt;/li&gt;
      &lt;li&gt;Port 1433 on the host is mapped to the exposed port 1433 in the container which is used for debugging.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt; is built from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile.windows&lt;/code&gt;. 
    - Compose passes along an environment variable which defines where the database is and how to connect to it. Notice that we can just refer to the database as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;db&lt;/code&gt; and Compose will allow &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;web&lt;/code&gt; to discover the service there.
    - The port 5000 is mapped to the exposed port 5000 in the container.&lt;/li&gt;
  &lt;li&gt;The two services are added to an existing Docker network, named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nat&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile.windows&lt;/code&gt; to understand it a bit better.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM microsoft/dotnet:1.1-sdk-msbuild-nanoserver
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This pulls in the official microsoft .NET Core image based on Nano Server&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;SHELL [&quot;powershell&quot;, &quot;-Command&quot;, &quot;$ErrorActionPreference = &apos;Stop&apos;;&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This sets the shell to powershell.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;RUN set-itemproperty -path &apos;HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters&apos; -Name ServerPriorityTimeLimit -Value 0 -Type DWord
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This disables the Windows DNS cache. The cache is too aggressive, and turning it off ensures the web container always queries the DNS server running in Docker to get the address of the database container.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;RUN New-Item -Path \MusicStore\samples\MusicStore.Standalone -Type Directory
WORKDIR MusicStore
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This creates a new directory in the container and makes it the working directory. Everything else that happens after this point will use MusicStore as the base directory.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;COPY samples/MusicStore.Standalone/project.json samples/MusicStore.Standalone/project.json
COPY NuGet.config .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The command adds the project file (detailing NuGet package dependencies) and the NuGet config file.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;RUN dotnet restore --no-cache .\samples\MusicStore.Standalone
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This pulls in the right dependencies to the project.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;COPY samples samples
RUN dotnet build .\samples\MusicStore.Standalone
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This adds the rest of the app source code to the container, and compiles the project.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;EXPOSE 5000
ENV ASPNETCORE_URLS http://0.0.0.0:5000
CMD dotnet run -p .\samples\MusicStore.Standalone
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;These last three commands expose correct ports and set the default run command for the container to run the MusicStore app.&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/windows-containers-multicontainer/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/windows-containers-multicontainer/</guid>
        
        <category>windows</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        <category>swarm</category>
        
        
        <category>beginner</category>
        
      </item>
    
      <item>
        <title>Docker for IT Pros and System Administrators Stage 3</title>
        <description>&lt;p&gt;This final stage will help you&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Implement a full proof of concept application&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Develop a strategy for integrating Docker into your existing production 
  environment&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Become recognized as a leader in your organization on implementing Docker&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this stage we have a series of design and reference architectures that help you understand the best ways to structure your apps with Docker. And a good video overview of the process at the end.
&lt;!-- ## Use case-focused example architectures
CI/CD TODO: Create
Deploying Multi-OS Apps to EE TODO: Create --&gt;&lt;/p&gt;

&lt;h2 id=&quot;design-and-reference-architectures&quot;&gt;Design and Reference Architectures&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://success.docker.com/Architecture/Docker_Reference_Architecture%3A_Docker_EE_Best_Practices_and_Design_Considerations&quot;&gt;Docker EE DDC Implementation Considerations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://success.docker.com/Architecture/Docker_Reference_Architecture%3A_Service_Discovery_and_Load_Balancing_with_Docker_Universal_Control_Plane_(UCP)&quot;&gt;Service Discovery and Load Balancing with Docker Universal Control Plane&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://success.docker.com/Architecture/An_Introduction_to_Storage_Solutions_for_Docker_CaaS&quot;&gt;An Introduction to Storage Solutions for Docker CaaS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://success.docker.com/Architecture/Docker_Reference_Architecture%3A_Designing_Scalable%2C_Portable_Docker_Container_Networks&quot;&gt;Docker Reference Architecture: Designing Scalable, Portable Docker Container Networks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://success.docker.com/Architecture/Docker_Reference_Architecture%3A_Securing_Docker_EE_and_Security_Best_Practices&quot;&gt;Docker Reference Architecture: Securing Docker EE and Security Best Practices&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://success.docker.com/Architecture/Docker_Reference_Architecture%3A_Docker_EE_Best_Practices_and_Design_Considerations&quot;&gt;Docker Reference Architecture: Docker EE Best Practices and Design Considerations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://success.docker.com/Architecture/Docker_Reference_Architecture%3A_Development_Pipeline_Best_Practices_Using_Docker_EE&quot;&gt;Docker Reference Architecture: Development Pipeline Best Practices Using Docker EE&lt;/a&gt;&lt;/p&gt;

&lt;!-- ## Docker White Papers
Docker / Microsoft Security White Paper TODO: create
“Integration” White Papers --&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/ZdUcKtg84T8?list=PLkA60AVN3hh8_lyxE2jjGaGyr0UoqIv4K&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;Once you’ve finished the three stages, you can&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://docker.com/get-docker&quot;&gt;Download Docker&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://community.docker.com&quot;&gt;Sign up for the Docker Community&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://docs.docker.com&quot;&gt;Explore the Docker documentation&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/ops-stage3/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/ops-stage3/</guid>
        
        <category>windows</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        <category>landing</category>
        
        
      </item>
    
      <item>
        <title>Docker for IT Pros and System Administrators Stage 2</title>
        <description>&lt;p&gt;This stage will help you&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Understand the architecture of Docker, and the core features&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Understand how to integrate Docker into your existing application infrastructure&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Develop a proof of concept application deployment&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;hands-on-learning&quot;&gt;Hands-on Learning&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;&lt;em&gt;Dig deeper into some hands-on learning in the browser&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h3 id=&quot;docker-enterprise-edition&quot;&gt;Docker Enterprise Edition&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://trial.docker.com&quot;&gt;Try the Docker Enterprise Edition Hosted Trial&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;security&quot;&gt;Security&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/security-seccomp&quot;&gt;Seccomp profiles&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/security-capabilities&quot;&gt;Linux Kernel Capabilities and Docker&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;networking&quot;&gt;Networking&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/docker-networking-hol&quot;&gt;Docker Networking Hands-on Lab&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;orchestration&quot;&gt;Orchestration&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/orchestration-hol&quot;&gt;Docker Orchestration Hands-on Lab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;&lt;em&gt;And for a bonus, you can work through a comprehensive Orchestration Workshop&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/orchestration-workshop-part1&quot;&gt;Part 1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/orchestration-workshop-part2&quot;&gt;Part 2&lt;/a&gt;&lt;/p&gt;

&lt;!-- Storage TODO: Needs to be created --&gt;

&lt;h2 id=&quot;bonus-materials&quot;&gt;Bonus materials&lt;/h2&gt;
&lt;p&gt;As a bonus, check out these videos. The first video is from DockerCon 2017 in Austin and features Brandon Royal showing you how to port traditional monolithic applications to modern Docker based applications.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/IK3l9UhwOGU&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Next we have Nico Kabar discussing design considerations, tools and best practices to address a globally distributed hybrid cloud infrastructure with hundreds or even thousands of apps using Docker.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/TYN-XgVmITE&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;!-- EE Video Demo TODO: Needs Updating
EE Walkthrough Guide TODO: Create --&gt;

&lt;p&gt;And in this playlist, learn from Docker Customers how they use Docker at their companies.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/videoseries?list=PLkA60AVN3hh-nubLYLr8CgXotVoAkC24n&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/ops-stage2/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/ops-stage2/</guid>
        
        <category>windows</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        <category>landing</category>
        
        
      </item>
    
      <item>
        <title>Docker for IT Pros and System Administrators Stage 1</title>
        <description>&lt;p&gt;This stage will&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Get you familiar with the core concepts of Docker&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Help you understand the fundamental value proposition for Docker&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Help you see how Docker can help your organization&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an introduction to Docker specifically for sys admins and IT Pros, check out Mike Coleman’s talk &lt;em&gt;Docker?!? But I’m a SYSADMIN!&lt;/em&gt;&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/M7ZBF-JJWVU&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;!-- 5-7 minute animated PPT to describe the benefits TODO: Build this --&gt;

&lt;!--  Build - Ship - Run recorded demo TODO: Record New One --&gt;

&lt;!-- Docker 101 Webinar TODO: Find right video --&gt;

&lt;h2 id=&quot;hands-on-learning&quot;&gt;Hands-on Learning&lt;/h2&gt;
&lt;p&gt;Give Docker a try with these two beginning tutorials:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/beginner-linux&quot;&gt;Docker for Beginners - Linux&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/swarm-stack-intro&quot;&gt;Deploy a multi-service application&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;more-reading-ebook&quot;&gt;More Reading: Ebook&lt;/h2&gt;
&lt;p&gt;To understand the differences between containers and VMs, check out this ebook: &lt;a href=&quot;https://goto.docker.com/docker-for-the-virtualization-admin.html&quot;&gt;Docker for the Virtualization Admin&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/ops-stage1/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/ops-stage1/</guid>
        
        <category>windows</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        <category>landing</category>
        
        
      </item>
    
      <item>
        <title>Docker for Developers Stage 3</title>
        <description>&lt;p&gt;This final stage will help you&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Deploy an application to a staging environment&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Manage your staging environment with Docker Swarm Mode&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Learn how to build a secure application&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this stage you’ll learn from the experts as we have a variety of videos with in depth content.&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/a8Z3MncihLg?list=PLkA60AVN3hh8_lyxE2jjGaGyr0UoqIv4K&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/yHLAaA4gPxw?list=PLkA60AVN3hh8_lyxE2jjGaGyr0UoqIv4K&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/tjxkxVI_PVU?list=PLkA60AVN3hh8_lyxE2jjGaGyr0UoqIv4K&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/f0jOoIm1dbs?list=PLkA60AVN3hh-HFXhOCZXyIi-du9FxliCN&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;Once you’ve finished the three stages, you can&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://docker.com/get-docker&quot;&gt;Download Docker&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://community.docker.com&quot;&gt;Sign up for the Docker Community&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;https://docs.docker.com&quot;&gt;Explore the Docker documentation&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/dev-stage3/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/dev-stage3/</guid>
        
        <category>developer</category>
        
        <category>landing</category>
        
        
      </item>
    
      <item>
        <title>Docker for Developers Stage 2</title>
        <description>&lt;p&gt;This stage show you how to&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;This stage show you how to incorporate Docker into your entire developer workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;self-guided-on-desktop-labs&quot;&gt;Self-guided on desktop labs&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;In-container development - learn how to integrate Docker into your IDE
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;/java-debugging-eclipse/&quot;&gt;Java Development: Eclipse&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/java-debugging-intellij/&quot;&gt;Java Development: IntelliJ&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/java-debugging-netbeans/&quot;&gt;Java Development: Netbeans&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/nodejs-live-debugging/&quot;&gt;Live Debugging Node.js with Docker and Visual Studio Code&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Windows Containers
    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;/windows-containers-setup/&quot;&gt;Windows Container Setup&lt;/a&gt; Setup up your environment to work with Windows Containers&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/windows-containers-basics/&quot;&gt;Windows Container Basics&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;/windows-containers-multicontainer/&quot;&gt;Windows Containers Multi-Container Applications&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;videos&quot;&gt;Videos&lt;/h2&gt;
&lt;p&gt;&lt;span&gt;&lt;em&gt;In the following video, Docker Captain John Zaccone describes how Docker can help with the entire developer workflow, including setting up a continuous integration and staging environment&lt;/em&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/y9IYnEDSVEc?list=PLkA60AVN3hh8_lyxE2jjGaGyr0UoqIv4K&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;&lt;span&gt;&lt;em&gt;In this video, Abby Fuller goes deep into creating effective Docker Images&lt;/em&gt;&lt;/span&gt;&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/pPsREQbf3PA?list=PLkA60AVN3hh8_lyxE2jjGaGyr0UoqIv4K&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/dev-stage2/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/dev-stage2/</guid>
        
        <category>developer</category>
        
        <category>landing</category>
        
        
      </item>
    
      <item>
        <title>Docker for Developers Stage 1</title>
        <description>&lt;p&gt;This stage will&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Get you familiar with the core concepts of Docker&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Show you how to build and deploy basic applications&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;self-guided-in-browser-tutorials&quot;&gt;Self-guided in-browser tutorials&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/beginner-linux&quot;&gt;Docker for Beginners - Linux&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/microservice-orchestration&quot;&gt;Application Containerization and Microservice Orchestration&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href=&quot;/swarm-stack-intro&quot;&gt;Deploying a Multi-Service App in Docker Swarm Mode&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;!-- ## Videos
 TODO: Add Docker 101 video --&gt;

&lt;!-- ## Videos
 TODO: Add Docker 101 video --&gt;
</description>
        <pubDate>Fri, 20 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/dev-stage1/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/dev-stage1/</guid>
        
        <category>developer</category>
        
        <category>landing</category>
        
        
      </item>
    
      <item>
        <title>Modernizing Traditional Java Applications</title>
        <description>&lt;p&gt;In this Hands-on Lab, we will show you how to take a traditional Java EE app, and compile it to run in a lightweight Java container. The app we’ve chosen is an old Java EE demo called Movie Plex 7 that was originally written to run in Wildfly 3. You can find our fork of the repo in our &lt;a href=&quot;https://github.com/dockersamples/javaee-demo&quot;&gt;javaee-demo&lt;/a&gt; repository on GitHub. In fact, if you were to take that repository, and lift the &lt;a href=&quot;https://github.com/dockersamples/javaee-demo/blob/master/ear/movieplex7-1.0-SNAPSHOT.ear&quot;&gt;movieplex7-1.0-SNAPSHOT&lt;/a&gt; file, you could run that in a Java Application Server, such as WebLogic, Wildfly, or WebSphere.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you aren’t yet familiar with the basic concepts of Docker, you may wish to start with the Docker 101 lab before running this lab.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#0&quot;&gt;Task 0: The Play with Docker Lab Environment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#1&quot;&gt;Task 1: Building the App in a Container&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#2&quot;&gt;Task 2: Adding a New Front-end&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#3&quot;&gt;Task 3: Moving from Wildfly to Tomcat EE&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-0-the-play-with-docker-lab-environment&quot;&gt;&lt;a name=&quot;0&quot;&gt;&lt;/a&gt;Step 0: The Play with Docker Lab Environment&lt;/h2&gt;

&lt;p&gt;The terminal window on the right hand side of this web page is a Linux-based Docker host running in the Play with Docker (PWD) lab environment. This is where you will run all of the commands in this lab.&lt;/p&gt;

&lt;p&gt;There are two ways of running commands in the terminal:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Single-clicking any command will paste it into the terminal and execute it&lt;/li&gt;
  &lt;li&gt;You can manually type each command into the terminal&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;step-1-building-the-app-in-a-container&quot;&gt;&lt;a name=&quot;1&quot;&gt;&lt;/a&gt;Step 1: Building the App in a Container&lt;/h2&gt;

&lt;p&gt;In this section of the lab, you are going to take a Java EE web app running on Wildfly, move it to Tomcat EE, and run it in a Docker container.&lt;/p&gt;

&lt;p&gt;The easiest way to move an enterprise Java app into Docker is create a WAR file and deploy it to the app server.&lt;/p&gt;

&lt;p&gt;Clone the lab repo into the PWD lab. Remember, you can click the command or manually type it into the terminal window.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;git clone https://github.com/dockersamples/javaee-demo
cd javaee-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You will build the application using Maven and deploy it to container to see how it looks. Building and deploying the application can be done without having either Maven or an application server on your computer.&lt;/p&gt;

&lt;p&gt;First, look at how the original application works. You can do this by building the application in Docker using a Wildfly container which was the app server the project first used. Although the GitHub repo contains an EAR file, you will build the application from source and deploy it using a multi-stage build Dockerfile. Here’s the text of the two-stage &lt;a href=&quot;https://github.com/dockersamples/javaee-demo/blob/master/movieplex7/Dockerfile.wildfly&quot;&gt;Dockerfile.wildfly&lt;/a&gt; file that you can find in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movieplex7&lt;/code&gt; directory.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM maven:3-jdk-7 AS app
WORKDIR /usr/src/movieplex7
COPY pom.xml .
RUN mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
COPY . .
RUN mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests

FROM jboss/wildfly
RUN /opt/jboss/wildfly/bin/add-user.sh admin Admin --silent
COPY --from=app /usr/src/movieplex7/target/movieplex7-1.0-SNAPSHOT.war .
EXPOSE 8080 9990
CMD [&quot;/opt/jboss/wildfly/bin/standalone.sh&quot;, &quot;-b&quot;, &quot;0.0.0.0&quot;, &quot;-bmanagement&quot;, &quot;0.0.0.0&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first part of the Dockerfile uses a maven container to build the war file. Instead of building the application locally, it is built inside the container with the tool chain needed to compile the application. In the second part of the Dockerfile, you use a Wildfly container and copy the war file built in the previous container to the app server for deployment.&lt;/p&gt;

&lt;p&gt;There are a few additional commands to expose the ports and start Wildfly.&lt;/p&gt;

&lt;p&gt;Here’s how to build the app:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;cd movieplex7
docker image build -t movieplex7 -f Dockerfile.wildfly .
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This creates a Docker image with the application deployed on Wildfly.&lt;/p&gt;

&lt;p&gt;To run the application:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container run -d -p 8080:8080 --name movieplex7 movieplex7
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;/movieplex7-1.0-SNAPSHOT&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;Click here to see it running&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/wildfly-movieplex7.png&quot; alt=&quot;Movieplex7 Landing Page in Wildfly&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Your app is working just fine in Docker now, with no code changes and running in the original app server. Feel free to play around with the functionality of the site to understand the app better.&lt;/p&gt;

&lt;h2 id=&quot;step-2-adding-a-new-front-end&quot;&gt;&lt;a name=&quot;2&quot;&gt;&lt;/a&gt;Step 2: Adding a New Front End&lt;/h2&gt;

&lt;p&gt;One of the big advantages of containers is that they can ease the process of updating older applications by replacing parts of the application as needed. Java EE also makes it easy to consume the output of your application in different ways. This allows you to easily add a different client to an application.&lt;/p&gt;

&lt;h3 id=&quot;react-client&quot;&gt;React Client&lt;/h3&gt;

&lt;p&gt;In this section, you will update the Movie Plex 7 application by adding more attributes to the movie entity and also updating the presentation layer by writing a new client using &lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt;. Don’t worry, all the code is provided, you don’t need to know React.&lt;/p&gt;

&lt;p&gt;As you saw earlier, the JavaServer Faces client was rather sparse and old-fashioned looking. You want to make the movie listing to include movie posters as well as more information about each movie. To do that, you will add a few attributes to the Movie entity, and include a path to a movie poster, and more information about the cast and the movie rating.&lt;/p&gt;

&lt;p&gt;The Movie Plex 7 application includes a REST interface for querying movies. This simplifies writing a new client because you can use the API and get data via REST. The javaScript client is a bit more descriptive than the original JavaServer Faces client.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/react-movieplex7-2.png&quot; alt=&quot;Movieplex7 App with a React JS client displaying movie posters&quot; /&gt;&lt;/p&gt;

&lt;p&gt;To get a separation of the new client from the existing client and server, you will deploy the React client in a separate container.&lt;/p&gt;

&lt;p&gt;To get ready, stop and remove the running container, and then change the directory from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movieplex7&lt;/code&gt; to the root of the project. Due to a quirk in how React is built, and the base url of the Play with Docker lab, before you build the React client you need to run a script to inject the host URL into the Dockerfile:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container stop movieplex7
docker container rm movieplex7
cd ..
./add_ee_pwd_host.sh
more react-client/Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM node:latest
ENV API_HOST=pwd10-0-16-3-8080.host4.labs.play-with-docker.com
WORKDIR /usr/src/react-client
COPY . .
RUN npm install
RUN npm run build
EXPOSE 3000
CMD [&quot;npm&quot;, &quot;run&quot;, &quot;start&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;React uses Node.js to build a static site for the interface. The Dockerfile is much simpler than the one for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movieplex7&lt;/code&gt; app. It uses a single-stage build, which passes the environment variable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;API_HOST&lt;/code&gt; to the builder. When it runs, it will start a simple Node server that serves the React pages, and exposes them on port 3000. We’ll deploy it in the next section.&lt;/p&gt;

&lt;h2 id=&quot;step-3-moving-from-wildfly-to-tomcat-ee&quot;&gt;&lt;a name=&quot;3&quot;&gt;&lt;/a&gt;Step 3: Moving from Wildfly to Tomcat EE&lt;/h2&gt;

&lt;p&gt;In Step 1, you compiled and deployed the Movie Plex 7 application from source by building it in a Maven container and then deploying it in a Wildfly container. The JavaServer Faces front-end was integrated into the the same application image.&lt;/p&gt;

&lt;p&gt;In this step you will make three changes. First, as described in the previous step, you will use a React front-end deployed in a second container. Second, you will deploy the app in a Tomcat EE application server instead of Wildfly. Last, you will deploy both at the same time using a Docker Compose file.&lt;/p&gt;

&lt;p&gt;The new front-end is described in the last section, so take a look at the new Dockerfile that deploys your app using the Tomcat Server.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;more movieplex7/Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;FROM maven:latest AS app
WORKDIR /usr/src/movieplex7
COPY pom.xml .
RUN mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve
COPY . .
RUN mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTest

FROM tomee:8-jre-7.0.2-plume

# tomcat-users.xml sets up user accounts for the Tomcat manager GUI
# and script access for Maven deployments
WORKDIR /usr/local/tomee/
ADD tomcat/tomcat-users.xml conf/
ADD tomcat/web.xml conf/
# copy and deploy application
WORKDIR /usr/local/tomee/webapps/
COPY --from=app /usr/src/movieplex7/target/movieplex7-1.0-SNAPSHOT.war .
# start tomcat7
EXPOSE 8080
CMD [&quot;catalina.sh&quot;, &quot;run&quot;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You’ll see that the first stage is very similar to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Dockerfile.wildfly&lt;/code&gt;. Only instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maven:3-jdk-7&lt;/code&gt; as the base image, it uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;maven:latest&lt;/code&gt;. There’s still no code change, you are still compiling the code into a WAR file. The second stage is a bit different, because it uses a different application server. But the end result is the same application running on both.&lt;/p&gt;

&lt;p&gt;You now have two Dockerfiles, one to build the Java Movie Plex 7 application and another to build the React javaScript client. Running a build for each will result in two images. You can individually run each image with the appropriate flags, but a simpler and more consistent solution is to use Docker Compose to start both images as containers that are connected by a network defined in Docker. Another nice thing about Docker Compose is that you can also build the images without having to do a separate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image build&lt;/code&gt; command for each image. This is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-compose.yml&lt;/code&gt; file in the project root:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;version: &quot;3.3&quot;

services:
  movieplex7:
    build:
      context: ./movieplex7
    image: movieplex7-tomee
    ports:
      - &quot;8080:8080&quot;
    networks:
      - www

  react-client:
    build:
      context: ./react-client
    image: react-client
    ports:
      - &quot;80:3000&quot;
    networks:
      - www

networks:
    www:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Compose file defines each service and the ports that they are mapped to. Of note is the react-client, which maps the public port 80 to the private in-container port of 3000.&lt;/p&gt;

&lt;p&gt;To run and build the images use:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker-compose up --build -d
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once it’s completed building, which the first time can take awhile, we can check on the status of the containers:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker container ls
&lt;/code&gt;&lt;/pre&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                    NAMES
02c1ee2be5ff        movieplex7-tomee    &quot;catalina.sh run&quot;   26 minutes ago      Up 26 minutes       0.0.0.0:8080-&amp;gt;8080/tcp   javaeedemo_movieplex7_1
2a65e6f08819        react-client        &quot;npm run start&quot;     26 minutes ago      Up 26 minutes       0.0.0.0:80-&amp;gt;3000/tcp     javaeedemo_react-client_1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This shows that the containers are running. Since there is overhead to start and deploy the war file you can look at the log files of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movieplex7&lt;/code&gt; container to check if it’s started&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-.term1&quot;&gt;docker logs javaeedemo_movieplex7_1
&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;...
02-Oct-2017 21:01:22.069 INFO [main] sun.reflect.DelegatingMethodAccessorImpl.invoke Starting ProtocolHandler [http-nio-8080]
02-Oct-2017 21:01:22.084 INFO [main] sun.reflect.DelegatingMethodAccessorImpl.invoke Starting ProtocolHandler [ajp-nio-8009]
02-Oct-2017 21:01:22.088 INFO [main] sun.reflect.DelegatingMethodAccessorImpl.invoke Server startup in 15073 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that the application server has started, you can click &lt;a href=&quot;/&quot; data-term=&quot;.term1&quot; data-port=&quot;80&quot;&gt;here&lt;/a&gt; and try out the new interface.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/images/react-movieplex7.png&quot; alt=&quot;Movieplex7 React Front-end&quot; /&gt;&lt;/p&gt;

&lt;p&gt;And the old interface is still there, you can see it by clicking on &lt;a href=&quot;/movieplex7-1.0-SNAPSHOT&quot; data-term=&quot;.term1&quot; data-port=&quot;8080&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
        <pubDate>Sun, 01 Jan 2017 00:00:00 +0000</pubDate>
        <link>https://training.play-with-docker.com/java-mta/</link>
        <guid isPermaLink="true">https://training.play-with-docker.com/java-mta/</guid>
        
        <category>windows</category>
        
        <category>operations</category>
        
        <category>developer</category>
        
        
        <category>intermediate</category>
        
      </item>
    
  </channel>
</rss>
