<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Itamar Turner-Trauring</title>
    <description>The latest articles on DEV Community by Itamar Turner-Trauring (@itamarstpythonspeed).</description>
    <link>https://dev.to/itamarstpythonspeed</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F237998%2F7a82a12d-164f-47c2-ab2c-83a447e9403f.png</url>
      <title>DEV Community: Itamar Turner-Trauring</title>
      <link>https://dev.to/itamarstpythonspeed</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itamarstpythonspeed"/>
    <language>en</language>
    <item>
      <title>Broken by default: why you should avoid most Dockerfile examples</title>
      <dc:creator>Itamar Turner-Trauring</dc:creator>
      <pubDate>Fri, 27 Mar 2020 15:14:54 +0000</pubDate>
      <link>https://dev.to/itamarstpythonspeed/broken-by-default-why-you-should-avoid-most-dockerfile-examples-10n3</link>
      <guid>https://dev.to/itamarstpythonspeed/broken-by-default-why-you-should-avoid-most-dockerfile-examples-10n3</guid>
      <description>&lt;p&gt;When it's time to package up your Python application into a Docker image, the natural thing to do is search the web for some examples. And a quick search will provide you with plenty of simple, easy examples.&lt;/p&gt;

&lt;p&gt;Unfortunately, these simple, easy examples are often broken in a variety of ways, some obvious, some less so. To demonstrate just some of the ways they're broken, I'm going to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with an example &lt;code&gt;Dockerfile&lt;/code&gt; that comes up fairly high on some Google searches.&lt;/li&gt;
&lt;li&gt;Show how it's broken.&lt;/li&gt;
&lt;li&gt;Give some suggestions on how to make it less broken.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Broken by default
&lt;/h2&gt;

&lt;p&gt;Consider the following &lt;code&gt;Dockerfile&lt;/code&gt;, which I found by searching for Python Dockerization examples. I've made some minor changes to disguise its origin, but otherwise it is the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# DO NOT USE THIS DOCKERFILE AS AN EXAMPLE, IT IS BROKEN&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; yourscript.py /&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;flask

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "python", "./yourscript.py" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Some of the problems with this Dockerfile
&lt;/h2&gt;

&lt;p&gt;How many different problems can you spot in this image?&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem #1: Non-reproducible builds re Python version
&lt;/h3&gt;

&lt;p&gt;The first thing to notice is that this &lt;code&gt;Dockerfile&lt;/code&gt; is based off of the &lt;code&gt;python:3&lt;/code&gt; image. At the time of writing this will install Python 3.7, but at some point it will switch to installing Python 3.8.&lt;/p&gt;

&lt;p&gt;At that point rebuilding the image will switch to a different version of Python, which might break the software: a minor change in your code can lead to a deploy that breaks production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Use &lt;code&gt;python:3.7.3-stretch&lt;/code&gt; as the base image, to pin the version and OS. Or, &lt;code&gt;python:3.7-stretch&lt;/code&gt; if you're feeling less worried about point releases. See my article for &lt;a href="https://pythonspeed.com/articles/base-image-python-docker-images/"&gt;choosing a base image for Python&lt;/a&gt; for more details on image variants.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem #2: Non-reproducible builds re dependencies.
&lt;/h3&gt;

&lt;p&gt;Similarly, &lt;code&gt;flask&lt;/code&gt; is installed with no versioning, so each time the image is rebuilt potentially a new version of &lt;code&gt;flask&lt;/code&gt; (or one of its dependencies, or one of its dependencies' dependencies) will change. If they're compatible, great, but there's no guarantee that is the case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Create &lt;code&gt;requirements.txt&lt;/code&gt; with transitively-pinned versions of all dependencies, e.g. &lt;a href="https://pythonspeed.com/articles/pipenv-docker/"&gt;by using &lt;code&gt;pip-tools&lt;/code&gt;, &lt;code&gt;poetry&lt;/code&gt;, or &lt;code&gt;Pipenv&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem #3: Changes to source code invalidate the build cache
&lt;/h3&gt;

&lt;p&gt;If you want fast builds, you want to rely on Docker's layer caching. But by copying in the file before running &lt;code&gt;pip install&lt;/code&gt;, all later layers are invalidated—this image will be rebuilt from scratch every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; &lt;a href="https://pythonspeed.com/articles/docker-caching-model/"&gt;Copy in files only when they're first needed&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem #4: Running as root, which is insecure
&lt;/h3&gt;

&lt;p&gt;By default Docker containers run as root, which is a security risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; It's much better to &lt;a href="https://pythonspeed.com/articles/root-capabilities-docker-security/"&gt;run as a non-root user&lt;/a&gt;, and do so in the image itself so that you don't listen on ports&amp;lt;1024 or do other operations that require a subset of root's permissions.&lt;/p&gt;

&lt;h2&gt;
  
  
  A somewhat better image
&lt;/h2&gt;

&lt;p&gt;Here's a somewhat better—though still not ideal—Dockerfile that addresses the issues above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.7.3-stretch&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt /tmp/&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /tmp/requirements.txt

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;--create-home&lt;/span&gt; appuser
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/appuser&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; yourscript.py .&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "python", "./yourscript.py" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Even if the resulting image was something you'd want to run in production—and it almost certainly isn't!—the image is still insufficient on its own.&lt;/p&gt;

&lt;p&gt;For example, you also need to regularly update &lt;code&gt;requirements.txt&lt;/code&gt; in a controlled manner, in order to get security updates and bug fixes, and you'll need to &lt;a href="https://pythonspeed.com/articles/docker-cache-insecure-images/"&gt;regularly rebuild your images without caching to get security updates&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And there are many more improvements you could make to get this closer to a &lt;a href="https://pythonspeed.com/docker/"&gt;production-ready Python container&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be careful what you learn from
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A broken Docker image can lead to production outages, and building best-practices images is a lot harder than it seems.&lt;/strong&gt; So don't just copy the first example you find on the web: do your research, and spend some time &lt;a href="https://pythonspeed.com/docker/"&gt;reading about best practices&lt;/a&gt;.&lt;/p&gt;




&lt;blockquote&gt;
&lt;h3&gt;Too much to learn, and don't know where to start?&lt;br&gt;&lt;br&gt;Learn practical software engineering techniques every week by &lt;a href="https://pythonspeed.com/#newsletter"&gt;signing up for my newsletter&lt;/a&gt;.&lt;/h3&gt;
&lt;/blockquote&gt;

</description>
      <category>python</category>
      <category>docker</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
