<?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: Martin Wimpress</title>
    <description>The latest articles on DEV Community by Martin Wimpress (@wimpress).</description>
    <link>https://dev.to/wimpress</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%2F642284%2F4619e0fe-1eb7-46b0-970a-df489ed2732a.png</url>
      <title>DEV Community: Martin Wimpress</title>
      <link>https://dev.to/wimpress</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wimpress"/>
    <language>en</language>
    <item>
      <title>Creating Production-Ready Containers - Advanced Techniques</title>
      <dc:creator>Martin Wimpress</dc:creator>
      <pubDate>Fri, 18 Jun 2021 14:00:05 +0000</pubDate>
      <link>https://dev.to/wimpress/creating-production-ready-containers-advanced-techniques-4jm3</link>
      <guid>https://dev.to/wimpress/creating-production-ready-containers-advanced-techniques-4jm3</guid>
      <description>&lt;p&gt;Creating production-ready containers for use in commercial-grade apps can be a far cry from the "get started with Node.js and Docker"-type of tutorials that are common around the Internet. Those guides are great for introducing all the advantages of Docker containers in modern cloud-native development, but creating a container that passes muster in a large-scale application in production is a different story. &lt;/p&gt;

&lt;p&gt;For production-ready containers, there are three key things you want to optimise for when creating a container:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Image Size 📦&lt;/li&gt;
&lt;li&gt;Build Speed 🐢&lt;/li&gt;
&lt;li&gt;Security 🔐&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Image size and build speed ensure that your containers can move through CI/CD and test pipelines easily and efficiently. Security is obviously critical in today's software supply chain, and containers have their own set of security issues. Thankfully, reducing container image size actually can alleviate some security issues in containers. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/wimpress/creating-production-ready-containers-the-basics-3k6f"&gt;In my Basics article&lt;/a&gt;, I showed you some easy techniques to improve your &lt;code&gt;Dockerfile&lt;/code&gt; using a sample "Hello World" Node.js application.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/wimpress" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F642284%2F4619e0fe-1eb7-46b0-970a-df489ed2732a.png" alt="wimpress"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/wimpress/creating-production-ready-containers-the-basics-3k6f" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Creating Production-Ready Containers - The Basics&lt;/h2&gt;
      &lt;h3&gt;Martin Wimpress ・ Jun 3 '21&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;These basics address all three optimisations, though they only scratch the surface. &lt;/p&gt;

&lt;p&gt;Let's look at some more advanced techniques for Container Optimisation.&lt;/p&gt;




&lt;p&gt;Keen readers who make it to the end of the article will unlock a bonus! 🎁&lt;/p&gt;




&lt;h2&gt;
  
  
  Alpine Images
&lt;/h2&gt;

&lt;p&gt;The very first thing you'll encounter when looking for techniques to create smaller containers is &lt;a href="https://alpinelinux.org/" rel="noopener noreferrer"&gt;Alpine Linux&lt;/a&gt;. Alpine Linux is an open source project whose goal is to create a bare-bones 🦴 version of Linux that lets developers "build from the ground up." &lt;/p&gt;

&lt;h3&gt;
  
  
  Pros: Transitioning to Alpine can be an easy way to get a smaller container
&lt;/h3&gt;

&lt;p&gt;Reducing image size with Alpine can be incredibly simple - under the right circumstances. For some apps, it's as easy as changing the base image in your &lt;code&gt;Dockerfile&lt;/code&gt;: &lt;/p&gt;

&lt;h4&gt;
  
  
  FROM
&lt;/h4&gt;



&lt;div class="highlight js-code-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; node:16.2.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  TO
&lt;/h4&gt;


&lt;div class="highlight js-code-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; node:16.2.0-alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When we build the new image, we see that the old image was 856MB and the new one is 114MB 🎉&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY          TAG     IMAGE ID        CREATED         SIZE
cotw-node-alpine    latest  2cc7b4a7b09c    2 minutes ago   114MB
cotw-node           latest  873fb9fca53a    3 days ago      856MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Easy, right? Not so fast.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cons: Using Alpine images can lead to build problems, now and in the future
&lt;/h3&gt;

&lt;p&gt;There are some not so obvious gotchas with using Alpine images that don't crop up in our super simple example application, such as: &lt;/p&gt;
&lt;h4&gt;
  
  
  You have to install everything
&lt;/h4&gt;

&lt;p&gt;Those tiny base images have to sacrifice something, right? Alpine users will be installing everything they need, right down to time-zone data or development tools. You won't need your development tools for your production image, most likely, but for most developers, the thought of a server without &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;vim&lt;/code&gt; is a bridge too far.&lt;/p&gt;
&lt;h4&gt;
  
  
  Different compilers and package managers
&lt;/h4&gt;

&lt;p&gt;You'll also be installing any dependencies with the Alpine Package Keeper tool (&lt;code&gt;apk&lt;/code&gt;) instead of the more familiar &lt;code&gt;apt&lt;/code&gt; or &lt;code&gt;rpm&lt;/code&gt;. The differences are small, but can trip up unsuspecting developers. &lt;/p&gt;
&lt;h4&gt;
  
  
  Fewer examples; less documentation
&lt;/h4&gt;

&lt;p&gt;Finally, while Alpine has been around for nine-plus years, it is and likely always will be a smaller and more specialised user base than established Linux distributions such as Ubuntu and Debian. To wit, at the time of this writing the &lt;code&gt;alpine&lt;/code&gt; tag on StackOverflow has just &lt;a href="https://stackoverflow.com/questions/tagged/alpine" rel="noopener noreferrer"&gt;1,280 questions&lt;/a&gt;, compared with &lt;a href="https://stackoverflow.com/questions/tagged/ubuntu" rel="noopener noreferrer"&gt;over 54,000 for Ubuntu&lt;/a&gt;. &lt;/p&gt;
&lt;h2&gt;
  
  
  Multi-stage builds
&lt;/h2&gt;

&lt;p&gt;The next tactic you are likely to encounter when searching for ways to reduce Docker image sizes is multi-stage 🏗 builds. This tactic, &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="noopener noreferrer"&gt;recommended by Docker and many in the Docker community&lt;/a&gt;, is essentially building the image twice. The first set of commands builds your base application image, all things included. The second set of commands builds an image off of that base image, taking only what's needed and leaving out anything that's not. &lt;/p&gt;

&lt;p&gt;With a multi-stage build, our &lt;code&gt;Dockerfile&lt;/code&gt; would look like this. Notice the two &lt;code&gt;FROM&lt;/code&gt; statements. The first builds the application image; the second copies the necessary files from that image into the second, more production-ready version.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:16.2.0-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.js ./&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:16.2.0-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /usr/src/app .&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node","app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Pros: Dev and Prod images can be built separately
&lt;/h3&gt;

&lt;p&gt;When combined with &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt;, this approach gives developers a flexible development environment while reducing bloat in the production images. You can simply use your initial image for dev/test and the final version for productions. Multi-stage builds work especially well for Go containers, significantly reducing image size, but also work well for static Node.js and React-type applications. &lt;/p&gt;
&lt;h3&gt;
  
  
  Cons: Added complexity; use-case specific
&lt;/h3&gt;

&lt;p&gt;Multi-stage builds are still relatively new 🌱 on the scene. For most developers still new to containers, knowing what to copy over to the final production image and what to leave behind is a major barrier to entry. Further, this pattern can run into challenges. &lt;/p&gt;

&lt;p&gt;Since we're already using an Alpine image, the size savings are relatively minor for our "Hello World" example. You'd expect to see greater gains in a full-blown React or Vue application.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY            TAG     IMAGE ID      CREATED        SIZE
cotw-node-multistage  latest  52bc33d14a87  3 minutes ago  114MB
cotw-node-alpine      latest  2cc7b4a7b09c  4 days ago     114MB
cotw-node             latest  873fb9fca53a  7 days ago     856MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Development tools and Distroless
&lt;/h2&gt;

&lt;p&gt;There are several tools - and new ones emerging every day - that look to bypass or automate &lt;code&gt;Dockerfile&lt;/code&gt; authoring to make image creation easier. &lt;a href="https://buildpacks.io/" rel="noopener noreferrer"&gt;&lt;em&gt;Buildpacks&lt;/em&gt;&lt;/a&gt; are the most mature of these technologies, and can be used through tools like &lt;a href="https://buildpacks.io/docs/tools/pack/" rel="noopener noreferrer"&gt;Pack&lt;/a&gt; or &lt;a href="https://www.waypointproject.io/plugins/pack" rel="noopener noreferrer"&gt;Waypoint&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;There are builder options from multiple sources - Heroku, Google, and Paketo are common favourites - and each gives you a slightly different developer experience and final image when used.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pack build cotw-node-bp-google &lt;span class="nt"&gt;--builder&lt;/span&gt; gcr.io/buildpacks/builder:v1
&lt;span class="nv"&gt;$ &lt;/span&gt;pack build cotw-node-bp-heroku &lt;span class="nt"&gt;--builder&lt;/span&gt; heroku/buildpacks:18
&lt;span class="nv"&gt;$ &lt;/span&gt;pack build cotw-node-bp-pb-base &lt;span class="nt"&gt;--builder&lt;/span&gt; paketobuildpacks/builder:base
&lt;span class="nv"&gt;$ &lt;/span&gt;pack build cotw-node-bp-pb-full &lt;span class="nt"&gt;--builder&lt;/span&gt; paketobuildpacks/builder:full
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Pros: When they work, they work
&lt;/h3&gt;

&lt;p&gt;In certain instances, Buildpacks can take the pain out of &lt;code&gt;Dockerfile&lt;/code&gt; authoring and just create container images of your application with no fuss. The pack tool is looking for "app-like" files in your source directory, and automatically figuring out what kind of application is there and how to containerize it. In the case of our Node sample, it sees &lt;code&gt;package.json&lt;/code&gt; and correctly assumes we have a Node.js application.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cons: When they don't…
&lt;/h3&gt;

&lt;p&gt;Given the relative newness of this approach for Docker containers, there are a lot of gotchas with Buildpacks. Non-standard applications or operating systems can struggle, and we've had issues running them successfully on the new Silicon Macbook Pros. The resulting images vary a lot - we saw a range of 200MB to 800MB in our examples - and the results tend to be lower than what you'd get with other techniques. &lt;/p&gt;
&lt;h2&gt;
  
  
  Automate it with DockerSlim
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://dockersl.im" rel="noopener noreferrer"&gt;DockerSlim&lt;/a&gt; open source project was created by &lt;a href="https://slim.ai" rel="noopener noreferrer"&gt;Slim.AI&lt;/a&gt; CTO &lt;a href="https://twitter.com/kcqon" rel="noopener noreferrer"&gt;Kyle Quest&lt;/a&gt; in 2015 as a way to automate container optimisation. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/slimtoolkit" rel="noopener noreferrer"&gt;
        slimtoolkit
      &lt;/a&gt; / &lt;a href="https://github.com/slimtoolkit/slim" rel="noopener noreferrer"&gt;
        slim
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Slim(toolkit): Don't change anything in your container image and minify it by up to 30x (and for compiled languages even more) making it secure too! (free and open source)
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/slimtoolkit/slimassets/images/dslim/logo.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fslimtoolkit%2Fslimassets%2Fimages%2Fdslim%2Flogo.png" alt="SK"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://gitter.im/docker-slim/community" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4b1042dcd43af92da4a9c859abdffffe6c2319b1f7cdd07cae05990dc8954e4e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f636861742d6f6e2532306769747465722d627269676874677265656e2e7376673f7374796c653d666f722d7468652d6261646765" alt="Gitter chat"&gt;&lt;/a&gt;
&lt;a href="https://discord.gg/9tDyxYS" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/61453d2cbb69bc2a9cd8b92fe6880530206059c11f7c7b5d25fe87315bdbe6d8/68747470733a2f2f696d672e736869656c64732e696f2f7374617469632f76312e7376673f6c6162656c3d63686174266d6573736167653d6f6e253230646973636f726426636f6c6f723d373338394438267374796c653d666f722d7468652d6261646765" alt="Discord chat"&gt;&lt;/a&gt;
&lt;a href="https://twitter.com/DockerSlim" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d6d13682e85db00db6b966c10b298114e3e464feff2fd4cc4726929271d2400d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f666f6c6c6f772d6f6e253230747769747465722d2532333144413146322e7376673f7374796c653d666f722d7468652d6261646765266c6f676f436f6c6f723d7768697465" alt="Follow"&gt;&lt;/a&gt;
&lt;a href="https://www.youtube.com/channel/UCy7RHjJlaBhpCCbChrd8POA?sub_confirmation=1" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/be7b8b00240a5a08394b8769658add75cbe54f4af3d4217c8cb0b57e8895649e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f2d596f75547562652d7265643f7374796c653d666f722d7468652d6261646765266c6f676f3d796f7574756265266c6f676f436f6c6f723d7768697465" alt="Youtube"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://gitpod.io/#https://github.com/slimtoolkit/slim" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/11e0450e23b462ea66d07798678898bb5bedcdabbe238b77ec3741379a65c8d6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f476974706f642d72656164792d2d746f2d2d636f64652d3930386138353f6c6f676f3d676974706f64267374796c653d666f722d7468652d6261646765" alt="Gitpod ready-to-code"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/slimtoolkit/slim#installation" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/cdc7dacb8d59b8b73aedf9cd31a191d8bb519fe98adf5dd6955d0b7e81041770/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f696e7374616c6c2d736c696d2d626c75653f7374796c653d666f722d7468652d6261646765" alt="Install SlimToolkit"&gt;&lt;/a&gt;
&lt;a href="https://github.com/slimtoolkit/examples" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5ffd5e56a015377ee6c95fcb9566ce3b032fdfda3dc9cdffaf636e00a6fe50ff/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f736c696d2d6170702532306578616d706c65732d677265656e3f7374796c653d666f722d7468652d6261646765" alt="Get Examples"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://portal.slim.dev/login?invitecode=invite.1s85zlfnYX0p5TT1XKja49pAHbL" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1e130a0e80c66167211bb4f8ab9607a732627f0d10a972fc829c2069f2d49a8f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7472792d536c696d2e4149253230536161532d7265643f7374796c653d666f722d7468652d6261646765" alt="Try Slim.AI SaaS"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Optimize Your Experience with Containers. Make Your Containers Better, Smaller, More Secure and Do Less to Get There (free and open source!)&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Note that &lt;strong&gt;DockerSlim&lt;/strong&gt; is now just &lt;strong&gt;Slim&lt;/strong&gt; (&lt;strong&gt;SlimToolkit&lt;/strong&gt; is the full name, so it's easier to find it online) to show its growing support for additional container tools and runtimes in the cloud native ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Slim&lt;/strong&gt; is now a CNCF Sandbox project. It was created by &lt;a href="https://github.com/kcq" rel="noopener noreferrer"&gt;Kyle&lt;/a&gt; &lt;a href="https://twitter.com/kcqon" rel="nofollow noopener noreferrer"&gt;Quest&lt;/a&gt; and it's been improved by many &lt;a href="https://github.com/slimtoolkit/slim/graphs/contributors" rel="noopener noreferrer"&gt;contributors&lt;/a&gt;. The project is supported by Root.io (formerly known as Slim.AI).&lt;/p&gt;

&lt;p&gt;Here's how Slim and Root work together: Slim helps you build optimized containers, while Root.io automatically fixes vulnerabilities without disrupting your workflows. Use Slim's open source toolkit to optimize containers, then keep them secure with Root's automated vulnerability remediation – from optimization to continuous security in one seamless journey. Learn more at &lt;a href="http://www.root.io" rel="nofollow noopener noreferrer"&gt;www.root.io&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;

&lt;p&gt;SlimToolkit allows developers to inspect, optimize…&lt;/p&gt;
&lt;/div&gt;


&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/slimtoolkit/slim" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;Simply download and run &lt;code&gt;docker-slim build &amp;lt;myimage&amp;gt;&lt;/code&gt; and DockerSlim will examine the image, rebuild it with only the required dependencies, and give you a new image that can be run just like the original. &lt;/p&gt;

&lt;h3&gt;
  
  
  Pros: It's automatic
&lt;/h3&gt;

&lt;p&gt;DockerSlim means you can work with whatever base image you'd like (say, Ubuntu or Debian) and let DockerSlim worry about removing unnecessary tools and files en route to production. The best part is that DockerSlim can be used alongside any of these other techniques. Once tested, it can be integrated into your CI/CD pipeline for automatic container minification, and the reduction in size leads to faster build times and better security. &lt;/p&gt;

&lt;h3&gt;
  
  
  Cons: Steep learning curve
&lt;/h3&gt;

&lt;p&gt;As with any open-source software, DockerSlim can take some time to get working, especially for non-trivial applications. It works best for web-style applications, micro-services and APIs that have defined HTTP/HTTPS ports which the sensor can find and use to observe the container internals. &lt;/p&gt;

&lt;p&gt;For best results, spend some time getting to know the various command flags available to tune your image, and &lt;a href="https://github.com/docker-slim/examples" rel="noopener noreferrer"&gt;take a look at the examples for whatever framework you're using&lt;/a&gt;. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/slimtoolkit" rel="noopener noreferrer"&gt;
        slimtoolkit
      &lt;/a&gt; / &lt;a href="https://github.com/slimtoolkit/examples" rel="noopener noreferrer"&gt;
        examples
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Minified application examples with different languages and stacks for DockerSlim
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Connecting with DockerSlim
&lt;/h2&gt;

&lt;p&gt;There's an &lt;a href="https://discord.gg/9tDyxYS" rel="noopener noreferrer"&gt;active DockerSlim Discord channel&lt;/a&gt; full of experts who can help you triage issues as they arise. &lt;/p&gt;

&lt;p&gt;Follow &lt;a href="https://dev.to/wimpress/"&gt;me here on dev.to&lt;/a&gt;, the &lt;a href="https://twitch.tv/SlimDevOps" rel="noopener noreferrer"&gt;SlimDevOps Twitch 📡 channel&lt;/a&gt; and &lt;a href="https://www.youtube.com/channel/UCdGG73JYGHfQO3n_gjdP-oQ" rel="noopener noreferrer"&gt;SlimDevOps YouTube 📺 channel&lt;/a&gt; where I'll be doing more deep-dives into container optimisation using DockerSlim and the Slim.AI platform in future posts and live-streams. &lt;/p&gt;

&lt;h2&gt;
  
  
  Exclusive Slim.AI early access
&lt;/h2&gt;

&lt;p&gt;And, if you got to the end of this article dear reader, then here is a bonus for you; an &lt;a href="https://slim.ai/blog/closed-beta-signup.html" rel="noopener noreferrer"&gt;&lt;em&gt;early access invite to the Slim.AI private beta&lt;/em&gt;&lt;/a&gt; 🙂&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image credit: &lt;a href="https://unsplash.com/@exdigy" rel="noopener noreferrer"&gt;Dominik Lückmann&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>linux</category>
      <category>dockerslim</category>
    </item>
    <item>
      <title>Creating Production-Ready Containers - The Basics</title>
      <dc:creator>Martin Wimpress</dc:creator>
      <pubDate>Thu, 03 Jun 2021 18:52:51 +0000</pubDate>
      <link>https://dev.to/wimpress/creating-production-ready-containers-the-basics-3k6f</link>
      <guid>https://dev.to/wimpress/creating-production-ready-containers-the-basics-3k6f</guid>
      <description>&lt;p&gt;So you've coded an awesome app and you are ready to deploy it to the cloud. You've heard a lot about &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; and completed a few online tutorials to containerise your app. All set, right? But what do you need to know if you're going to move that app to a production environment on the public Internet? What if you're using it for your job and need to pass security scans and DevOps checks?&lt;/p&gt;

&lt;p&gt;In this series, I introduce some basic concepts for making production ready containers. I also introduce the concept of "slimming" a container. &lt;strong&gt;Slimming&lt;/strong&gt; refers to both optimising and minifying your Docker containers, reducing them in size by up to 80-percent while also making them more secure by decreasing the attack surface. Slimming your container is also a great way to implement &lt;a href="https://www.slim.ai/blog/why-don%E2%80%99t-we-practice-container-best-practices.html" rel="noopener noreferrer"&gt;container best practices&lt;/a&gt; without re-engineering your entire workflow.&lt;/p&gt;

&lt;p&gt;There are many ways to slim a container, from basic security to fully automated open-source tools like &lt;a href="https://dockersl.im/" rel="noopener noreferrer"&gt;DockerSlim&lt;/a&gt;. &lt;em&gt;Full disclosure&lt;/em&gt;: I work for &lt;a href="https://slim.ai" rel="noopener noreferrer"&gt;Slim.AI&lt;/a&gt;, a company founded on the DockerSlim open source project. Let's look at some of the common ways developers create production-ready container images today.&lt;/p&gt;

&lt;p&gt;I'll explore each of these in a separate article using a simple "Hello World" Node.js example that can be found in a number of online tutorials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello World!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Example app listening at http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's get started by simply improving your &lt;code&gt;Dockerfile&lt;/code&gt; to build a better Docker image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Better Dockerfile
&lt;/h2&gt;

&lt;p&gt;Most &lt;code&gt;Dockerfile&lt;/code&gt; examples you'll find are not "production ready" and they aren't meant to be. They are for instructional purposes to help developers successfully build an image. But when one gets into production scenarios, there are a number of "good-to-know" and a few "have-to-know" techniques that will improve build times, security, and reliability. &lt;/p&gt;

&lt;p&gt;Let's look at a typical example that you might run into if you're a Node.js developer looking to get "Hello World" running with Docker. I won't go through building an actual app - there are a lot of great examples out there to show you how to do this - but rather focus on what to do if you were actually going to ship this to production. &lt;/p&gt;

&lt;p&gt;The typical &lt;code&gt;Dockerfile&lt;/code&gt; in a "Hello World" example might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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; node:latest&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json app.js ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It uses the latest version of the official Node.js image, sets a directory and copies your app into the container image, installs dependencies, exposes port 3000, and runs the app via &lt;code&gt;CMD&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While this will run no problem on your local machine, and is great for learning the ropes, this approach is almost certainly going to run into issues when you ship it to production. Let's take a look at some of these in order of severity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Major issues
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Running as Root
&lt;/h4&gt;

&lt;p&gt;Since this example doesn't set a &lt;code&gt;USER&lt;/code&gt; explicitly in the &lt;code&gt;Dockerfile&lt;/code&gt;, Docker runs the build and all commands as the &lt;code&gt;root&lt;/code&gt; user. While not an issue for local development, your friendly neighbourhood SysAdmin will tell you the myriad of issues that come with running applications as root on a server in production. And with Docker, a &lt;a href="https://medium.com/jobteaser-dev-team/docker-user-best-practices-a8d2ca5205f4" rel="noopener noreferrer"&gt;new set of attack methods&lt;/a&gt; can arise. &lt;/p&gt;

&lt;p&gt;Thankfully, most major languages and frameworks have a predefined user for running applications. In Node.js, the user is just &lt;code&gt;node&lt;/code&gt; and can be invoked in the &lt;code&gt;Dockerfile&lt;/code&gt; explicitly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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; node:latest&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json app.js ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Using &lt;code&gt;latest&lt;/code&gt; version
&lt;/h4&gt;

&lt;p&gt;Choosing a version number for your container is often called &lt;em&gt;pinning&lt;/em&gt;. While many tutorials - and even some experts - will counsel newcomers to pin their images to the &lt;code&gt;latest&lt;/code&gt; tag, which means you get whatever the most recently updated version is, using the &lt;code&gt;latest&lt;/code&gt; tag can cause issues in production.&lt;/p&gt;

&lt;p&gt;Containers are meant to be ephemeral, meaning they can be created, destroyed, started, stopped, and reproduced with ease and &lt;em&gt;reliability&lt;/em&gt;. Using the &lt;code&gt;latest&lt;/code&gt; tag means there isn't a single source of truth for your container's "bill of materials". A new version or update of a dependency could introduce a breaking change, which may cause the build to fail somewhere in your CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example &lt;code&gt;Dockerfile&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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; node:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Production &lt;code&gt;Dockerfile&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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; node:16.2.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other tutorials I've seen pin only the major version. For example, using &lt;code&gt;node:14&lt;/code&gt;. This carries the same risks as using &lt;code&gt;latest&lt;/code&gt;, as minor versions can change dependencies as well.&lt;/p&gt;

&lt;p&gt;Now, pinning a specific major and minor version in your &lt;code&gt;Dockerfile&lt;/code&gt; is a trade-off decision - you're choosing to not automatically receive security, fixes or performance improvements that come via new updates - but most DevSecOps teams prefer to employ security scanning and container management software as a way to control updates rather than dealing with the unpredictability that comes with container build failures in production CI/CD pipelines. &lt;/p&gt;

&lt;h3&gt;
  
  
  Performance improvements
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Better layer caching
&lt;/h4&gt;

&lt;p&gt;Docker works on the concept of &lt;em&gt;layer caching&lt;/em&gt;. It builds images sequentially. Layering dependencies on top of each other and only rebuilding them when something in the layer has changed. &lt;/p&gt;

&lt;p&gt;Layer 0 in a Docker image is often the base operating system, which rarely change significantly; although commercial Linux vendors often publish new base images to incorporate security fixes.&lt;/p&gt;

&lt;p&gt;Application code, however, is highly likely to change during the software development cycle, as you iterate on features, refactor, and fix bugs. Dependencies in our core system, installed here by &lt;code&gt;npm install&lt;/code&gt;, change more often than the base OS, but less often than the application code.&lt;/p&gt;

&lt;p&gt;In our example &lt;code&gt;Dockerfile&lt;/code&gt;, we simply need to break the installation of the dependencies into separate instructions on their own lines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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; node:16.0.2&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.js ./&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We actually end up creating another layer by now having two &lt;code&gt;COPY&lt;/code&gt; commands. While adding layers is typically a no-no for build times and image sizes, the tax we pay on this optimisation is going to save us in the long run as we cycle through the QA process, since we aren't reinstalling dependencies if we don’t have to.&lt;/p&gt;

&lt;p&gt;We also opt for the &lt;code&gt;npm ci&lt;/code&gt; command instead of &lt;code&gt;npm install&lt;/code&gt;, which is preferred for automated environments, such as CI/CD, and will help prevent breaking changes from dependencies. Read &lt;a href="https://docs.npmjs.com/cli/v7/commands/npm-ci" rel="noopener noreferrer"&gt;more about &lt;code&gt;npm ci&lt;/code&gt; here&lt;/a&gt;. &lt;/p&gt;

&lt;h4&gt;
  
  
  Use &lt;code&gt;ENTRYPOINT&lt;/code&gt; instead of &lt;code&gt;CMD&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;At a surface level, there isn't a big difference between using &lt;code&gt;ENTRYPOINT&lt;/code&gt; with your app file versus running &lt;code&gt;CMD&lt;/code&gt; using the shell plus your app file. However, web- and API-type containers like Node.js applications are often running as executables in production, and there, proper signal handling - such as graceful shutdowns - are important.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt; provides some flexibility for calling executables with flags or overwriting them, which is common in development. But that generally won't be relevant to production instances and &lt;code&gt;ENTRYPOINT&lt;/code&gt; will likely provide better signal processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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; node:16.0.2&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.js ./&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cleaning up cached files
&lt;/h3&gt;

&lt;p&gt;Most package managers have the ability to clean up their own cache. If you don’t do this, you'll just be moving a bunch of unused files into your container for no reason. It might not save a lot of space depending on your application, but think of it as dropping your unused items at the charity shop &lt;em&gt;before&lt;/em&gt; you move rather than loading them in the moving van. It's not a lot of effort and it's the right thing to do. We do this by adding &lt;code&gt;&amp;amp;&amp;amp; npm cache clean --force&lt;/code&gt; to our &lt;code&gt;RUN&lt;/code&gt; instruction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-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; node:16.0.2&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm cache clean &lt;span class="nt"&gt;--force&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; node&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.js ./&lt;/span&gt;

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["node", "app.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;Improving your &lt;code&gt;Dockerfile&lt;/code&gt; is the first step towards creating a slimmed and optimised container. It closes some major security loopholes that are likely to raise flags with downstream checks and adds baseline optimisations for build time and docker image size. &lt;/p&gt;

&lt;p&gt;If this is all you do to improve your containers prior to shipping to production, you won't be in a bad spot, but there's definitely more - &lt;em&gt;way more&lt;/em&gt; - that you can do to optimise images. We'll explore those techniques in the next article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update
&lt;/h2&gt;

&lt;p&gt;Since publishing this article my colleague and I ran through the techniques presented here in a video.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/yzaNJiy1_wc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Image credit: &lt;a href="https://unsplash.com/@frankiefoto" rel="noopener noreferrer"&gt;Frank McKenna&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>beginners</category>
      <category>node</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
