{"id":258,"date":"2018-10-17T06:40:24","date_gmt":"2018-10-17T06:40:24","guid":{"rendered":"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/unleash-powerful-linux-container-building-capabilities-with-buildah-red-hat-enterprise-linux-blog\/"},"modified":"2018-10-17T06:40:24","modified_gmt":"2018-10-17T06:40:24","slug":"unleash-powerful-linux-container-building-capabilities-with-buildah-red-hat-enterprise-linux-blog","status":"publish","type":"post","link":"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/unleash-powerful-linux-container-building-capabilities-with-buildah-red-hat-enterprise-linux-blog\/","title":{"rendered":"Unleash powerful Linux container-building capabilities with Buildah \u2013 Red Hat Enterprise Linux Blog"},"content":{"rendered":"<p>Balancing size and features is a universal challenge when building software. So, it\u2019s unsurprising that this holds true when building container images. If you don\u2019t include enough packages in your base image, you end up with images which are difficult to troubleshoot, missing something you need, or just cause different development teams to add the exact same package to layered images (causing duplication). If you build it too big, people complain because it takes too long to download \u2013 especially for quick and dirty projects or demos. This is where Buildah comes in.<\/p>\n<p>In the currently available ecosystem of build tools, there are two main kinds of build tools:<\/p>\n<ol>\n<li> Ones which build container images from scratch.<\/li>\n<li> Those that build layered images. <\/li>\n<\/ol>\n<p>Buildah is unique in that it elegantly blurs the line between both \u2013 and, it has a rich set of capabilities for each. One of those rich capabilities is multi-stage builds.<\/p>\n<p>At Red Hat Summit 2018 in San Francisco, Scott McCarty and I boiled the practice of building production ready containers down into five key tenets \u2013 standardize, minimize, delegate, process, and iterate (<a href=\"https:\/\/www.youtube.com\/watch?v=nizud-1IK9c\">video<\/a> &amp; <a href=\"https:\/\/docs.google.com\/presentation\/d\/12hSt0lsdJ1B3IspaqouQIOtsVYidZydTIFcVG9ntB78\/edit#slide=id.g3547861863_1_3\">presentation<\/a>). <\/p>\n<p>Two tenets in particular are often at odds \u2013 standardize and minimize. It makes sense to standardize on a rich base image, while at the same time minimizing the content in layered builds. Balancing both is tricky, but when done right, reaps the benefits of <a href=\"https:\/\/www.redhat.com\/en\/blog\/open-container-initiative-specifications-reach-10\">OCI image<\/a> layers at scale (lots of applications) and improves registry storage efficiency.<\/p>\n<h2>Multi-stage builds<\/h2>\n<p>A particularly powerful example of how to achieve this balance is the concept of multi-stage builds. Since build dependencies like compilers and package managers are rarely required at runtime, we can exclude them from the final build by breaking it into two parts. We can do the heavy lifting in the first part, then use the build artifacts (think Go binaries or jars) in the second. We will then use the container image from the second build in production.<\/p>\n<p>Using this methodology leverages the power of rich base images, while at the same time, results in a significantly smaller container image. The resultant image isn\u2019t carrying additional dependencies that aren\u2019t used during runtime. The multi-stage build concept became popular last year with the release of Docker v17.05, and OpenShift has long had a similar capability with the concept of <a href=\"https:\/\/docs.openshift.com\/container-platform\/3.5\/dev_guide\/builds\/advanced_build_operations.html#dev-guide-chaining-builds\">chaining builds<\/a>.<\/p>\n<p>OK, multi-stage builds are great, you get it, but to make this work right, the two builds need to be able to copy data between them. Before we tackle this, let\u2019s start with some background.<\/p>\n<h2>Buildah background <\/h2>\n<p>Buildah was a complete rethink of how container image builds could and should work. It follows the Unix philosophy of small, flexible tools. Multi-stage builds were part of the original design and have been possible since its inception. With the release of Buildah 1.0, users can now take advantage of the simplicity of using multi-stage builds with the Dockerfile format. All of this, with a smaller tool, <a href=\"https:\/\/www.redhat.com\/en\/blog\/daemon-haunted-container-world-no-longer-introducing-buildah-10\">no daemon<\/a>, and tons of flexibility during builds (ex. build time volumes).<\/p>\n<p>Below we\u2019ll take a look at how to use Buildah to accomplish multi-stage builds with a Dockerfile and also explore a simpler, yet more sophisticated way to tackle them.<\/p>\n<h3>Using Dockerfiles:<\/h3>\n<p>$buildah bud -t [image:tag] . <\/p>\n<p>\u2026.and that\u2019s it! Assuming your Dockerfile is written for multi-stage builds and in the directory the command is executed, everything will just work. So if this is all you\u2019re looking for, know that it\u2019s now trivial to accomplish this with Buildah in Red Hat Enterprise Linux 7.5.<\/p>\n<p>Now, let\u2019s dig a little deeper and take a look at using Buildah\u2019s native commands to achieve the same outcome and some reasons why this can be a powerful alternative for certain use cases. <\/p>\n<p>For clarity, we\u2019ll start by using <a href=\"https:\/\/blog.alexellis.io\/mutli-stage-docker-builds\/\">Alex Ellis\u2019s blog post<\/a> that demonstrates the benefits of performing multi-stage builds. Use of this example is simply to compare and contrast the Dockerfile version with Buildah\u2019s native capabilities. It\u2019s not an endorsement any underlying technologies such as Alpine Linux or APK. These examples could all be done in Fedora, but that would make the comparison less clear.<\/p>\n<h3>Using Buildah Commands<\/h3>\n<p>Using his <a href=\"https:\/\/github.com\/alexellis\/href-counter\">https:\/\/github.com\/alexellis\/href-counter<\/a> we can convert the included <a href=\"https:\/\/github.com\/alexellis\/href-counter\/blob\/master\/Dockerfile.multi\">Dockerfile.multi<\/a> file to a simple script like this:<\/p>\n<h4>First Build<\/h4>\n<p>#!\/bin\/bash&#xD;<br \/>\n&#xD;<br \/>\n# build container&#xD;<br \/>\n&#xD;<br \/>\nbuildcntr1=$(buildah from golang:1.7.3)&#xD;<br \/>\n&#xD;<br \/>\nbuildmnt1=$(buildah mount $buildcntr)<\/p>\n<p>Using simple variables like this are not required, but they will make the later commands clearer to read so it\u2019s recommended. Think of the <i>buildcntr1<\/i> as a handle which represents the container build, while the variable <i>buildmnt1<\/i> represents a directory which will mount the container.<\/p>\n<p>buildah run $buildcntr1 go get -d -v golang.org\/x\/net\/html<\/p>\n<p>This is the first command verbatim in the original Dockerfile. All that\u2019s needed is to change RUN to <i>run<\/i> and point Buildah to the container we want to execute the command in. Once, the command completes, we are left with a local copy of the go program. Now we can move it wherever we want. Buildah has a native directive to copy the contents out of a container build:<\/p>\n<p>buildah copy $buildcntr1 app.go .<\/p>\n<p>Alternatively, we can use the system command to do the same thing by referencing the mount point:<\/p>\n<p>cp app.go $buildmnt1\/go<\/p>\n<p>For this example both of these lines will accomplish the same thing. We can use buildah\u2019s copy command the same way the COPY command works in a Dockerfile, or we can simply use the host\u2019s cp command to perform the task of copying the binary out of the container. In the rest of this tutorial, we\u2019ll rely on the hosts command.<\/p>\n<p>Now, let\u2019s build the code:<\/p>\n<p>buildah run $buildcntr1 \/bin\/sh -c &#8220;CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .&#8221; <\/p>\n<h4>Second Build<\/h4>\n<p>The same applies to this command. We\u2019re changing RUN to run and executing the command in the same container:<\/p>\n<p># runtime container&#xD;<br \/>\n&#xD;<br \/>\nbuildcntr2=$(buildah from alpine:latest)&#xD;<br \/>\n&#xD;<br \/>\nbuildmnt2=$(buildah mount $buildcntr2)<\/p>\n<p>Now let\u2019s define a separate runtime image that we\u2019ll use to run our application in production with.<\/p>\n<p>buildah run $buildcntr2 apk &#8211;no-cache add ca-certificates<\/p>\n<p>Same tweaks for the RUN command<\/p>\n<p>#buildah copy $buildcntr2 $buildmnt1\/go\/app .<\/p>\n<p>Or: <\/p>\n<p>cp $buildmnt1\/go\/app $buildmnt2<\/p>\n<p>Here we have the same option as above. To bring the compiled application into the second build, we can use the copy command from buildah or the host. <\/p>\n<p>Now, add the default command to the production image.<\/p>\n<p>buildah config &#8211;cmd .\/app $buildcntr2<\/p>\n<p>Finally, we unmount and commit the image, and optionally clean up the environment:<\/p>\n<p>#unmount &amp; commit the image&#xD;<br \/>\n&#xD;<br \/>\nbuildah unmount $buildcntr2&#xD;<br \/>\n&#xD;<br \/>\nbuildah commit $buildcntr2 multi-stage:latest&#xD;<br \/>\n&#xD;<br \/>\n&#xD;<br \/>\n#clean up build&#xD;<br \/>\n&#xD;<br \/>\nbuildah rm $buildcntr1 $buildcntr2<\/p>\n<p>Don\u2019t forget that Buildah can also push the image to your desired registry using \u200bbuildah push`<\/p>\n<p>The beauty of Buildah is that we can continue to leverage the simplicity of the Dockerfile format, but we\u2019re no longer bound by the limitations of it. People do some nasty, nasty things in a Dockerfile to hack everything onto a single line. This can make them hard to read, difficult to maintain, and it\u2019s inelegant.<\/p>\n<p>When you combine the power of being able to manipulate images with native Linux tooling from the build host, you are now free to go beyond the Dockerfile commands! This opens up a ton of new possibilities for the content of container images, the security model involved, and the process for building. <\/p>\n<p>A great example of this was explored in one of <a href=\"https:\/\/www.projectatomic.io\/blog\/2017\/08\/buildah-getting-fit\/\">Tom Sweeney\u2019s blog posts on creating minimal containers.<\/a> Tom\u2019s example of leveraging the build host\u2019s package manager is a great one, and means we no longer require something like \u201cyum\u201d to be available in the final image. <\/p>\n<p>On the security side, we no longer require access to the Docker socket which is a win for performing builds from Kubernetes\/OpenShift. In fairness Buildah currently requires escalated privileges on the host, but soon this will no longer be the case. Finally, on the process side, we can leverage Buildah to augment any existing build process, be it a CI\/CD pipeline or building from a Kubernetes cluster to create simple and production-ready images. <\/p>\n<p>Buildah provides all of the primitives needed to take advantage of the simplicity of Dockerfiles combined with the power of native Linux tooling, and is also paving the way to more secure container builds in OpenShift. If you are running Red Hat Enterprise Linux, or possibly an alternative Linux distribution, I highly recommend taking a look at Buildah and maximizing your container build process for production. <\/p>\n<p> <a href=\"https:\/\/rhelblog.redhat.com\/2018\/07\/11\/unleash-powerful-linux-container-building-capabilities-with-buildah\/\" target=\"_blank\">Source<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Balancing size and features is a universal challenge when building software. So, it\u2019s unsurprising that this holds true when building container images. If you don\u2019t include enough packages in your base image, you end up with images which are difficult to troubleshoot, missing something you need, or just cause different development teams to add the &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.appservgrid.com\/paw92\/index.php\/2018\/10\/17\/unleash-powerful-linux-container-building-capabilities-with-buildah-red-hat-enterprise-linux-blog\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Unleash powerful Linux container-building capabilities with Buildah \u2013 Red Hat Enterprise Linux Blog&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-258","post","type-post","status-publish","format-standard","hentry","category-linux"],"_links":{"self":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/258","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/comments?post=258"}],"version-history":[{"count":0,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/posts\/258\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/media?parent=258"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/categories?post=258"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appservgrid.com\/paw92\/index.php\/wp-json\/wp\/v2\/tags?post=258"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}