{"id":3325,"date":"2020-10-25T17:09:36","date_gmt":"2020-10-25T22:09:36","guid":{"rendered":"http:\/\/osric.com\/chris\/accidental-developer\/?p=3325"},"modified":"2020-10-25T17:09:36","modified_gmt":"2020-10-25T22:09:36","slug":"using-buildah-to-build-containers-for-docker-and-podman","status":"publish","type":"post","link":"https:\/\/osric.com\/chris\/accidental-developer\/2020\/10\/using-buildah-to-build-containers-for-docker-and-podman\/","title":{"rendered":"Using Buildah to build containers for Docker and Podman"},"content":{"rendered":"<p>One of my colleagues pointed me to an article on using <a href=\"https:\/\/buildah.io\/\">Buildah<\/a> to create container images: <a href=\"https:\/\/opensource.com\/article\/19\/3\/tips-tricks-rootless-buildah\">How rootless Buildah works: Building containers in unprivileged environments<\/a>.<\/p>\n<p>I decided to test it out! In this case, I just wanted to build a container using the shell script method described in the article, rather than using a Dockerfile. Although the rootless aspect is interesting to me, I believe that requires newer versions of Buildah than what is available by default on CentOS 7. A project for another day!<\/p>\n<p>First, I needed a test case. I decided to use my NLTK chatbot container image, which can be found at:<\/p>\n<ul>\n<li><a href=\"https:\/\/hub.docker.com\/repository\/docker\/cherdt\/nltk-chatbot\">https:\/\/hub.docker.com\/repository\/docker\/cherdt\/nltk-chatbot<\/a> (container image)<\/li>\n<li><a href=\"https:\/\/github.com\/cherdt\/docker-nltk-chatbot\">https:\/\/github.com\/cherdt\/docker-nltk-chatbot<\/a> (Git repo including Dockerfile)<\/li>\n<\/ul>\n<p><!--more--><\/p>\n<p>The existing container image, built from the Dockerfile, was 499 MB. I wanted to note this since one of my goals was to keep unnecessary packages out of the container image. For example, when building uWSGI inside a container, it requires a C compiler (<code>gcc<\/code>) and Python headers (<code>python3-devel<\/code>). However, those aren&#8217;t needed in the final container. Why not just use the existing compiler and headers in the build environment and avoid putting them in the container image at all?<\/p>\n<p>The shell script example from the article had a couple of bugs, which I ironed out to create this shell script:<\/p>\n<pre><code>#!\/bin\/sh\r\n\r\n# Build the container using buildah instead of Docker\r\n\r\n# Retrieve container\r\nCONTAINER=$(buildah from centos:centos7)\r\necho $CONTAINER\r\n\r\n# Mount the container filesystem\r\nMNT=$(buildah mount $CONTAINER)\r\necho $MNT\r\n\r\n# Install python3 within the container path\r\nyum --assumeyes install --installroot $MNT python3\r\n\r\n# Install python libraries within the container path\r\npip3 install --prefix=$MNT\/usr\/ Flask flask-cors nltk pyyaml requests uwsgi\r\n\r\n# Remove unnecessary cache files\r\nrm -rf $MNT\/var\/cache $MNT\/var\/log\/*\r\n\r\n# Copy chatbot app files\r\ncp chatbot.py $MNT\/\r\ncp chat.html $MNT\/\r\n\r\n# Set container config options (port, entrypoint)\r\nbuildah config --port 9500\/tcp $CONTAINER\r\nbuildah config --entrypoint 'uwsgi --http :9500 --manage-script-name --mount \/=chatbot:app' $CONTAINER\r\n\r\n# Commit the changes to nltk-eliza\r\nbuildah commit $CONTAINER cherdt\/nltk-chatbot<\/code><\/pre>\n<p>One of the interesting things is that <code>buildah<\/code> simply mounts the container filesystem, and then the script uses that as a target for <code>yum<\/code> installs and <code>pip<\/code> installs. Using buildah was helpful to me to emphasize that a container image is really just a series of files with some metadata\/configuration included.<\/p>\n<p>Once the container image was built, I tested it using podman:<\/p>\n<pre><code>podman run -d -p 9500:9500 --name nltk_eliza nltk-eliza<\/code><\/pre>\n<p>It worked!<\/p>\n<p>When I examined the new container size, I found that I had shaved off 189 MB:<\/p>\n<pre><code># buildah images\r\nREPOSITORY                      TAG       IMAGE ID       CREATED         SIZE\r\nlocalhost\/cherdt\/nltk-chatbot   latest    8e508778df12   47 hours ago    310 MB<\/code><\/pre>\n<p><strong>Shell scripts versus Dockerfile<\/strong><br \/>\nWhat are the advantages or disadvantages of this method over using a Dockerfile?<\/p>\n<ul>\n<li>Advantage: keep unnecessary packages and libraries out of the container image.<\/li>\n<li>Advantage: smaller file size.<\/li>\n<li>Disadvantage: those packages and libraries now need to be installed on your build environment, which was not previously necessary since everything happened in the containers themselves.<\/li>\n<li>Disadvantage: the build host <em>probably<\/em> needs to be similar. I haven&#8217;t tested this, but I created a container image based on CentOS 7 on a CentOS 7 host. I&#8217;m not sure if it would work trying to create the same CentOS 7 image on, say, an Ubuntu host on the same x86_64 architecture.<\/li>\n<li>Disadvantage: since this was my first time using a shell script to build a container image, I ran into a lot of errors along the way. Every time I re-ran the script, it built the entire container image over again. This was time-consuming. Building with Dockerfiles creates intermediate container images and makes changes to those images only when needed. There may be a way to accomplish the same thing using buildah, but I haven&#8217;t explored that yet.<\/li>\n<\/ul>\n<p>Buildah can create container images using Dockerfiles (using <code>buildah bud<\/code>). Podman can do the same (using <code>podman build<\/code>). Worth pointing out in case you want to explore alternatives to Docker without changing how you build container images!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You don&#8217;t need a Dockerfile to build a container image! In this post, I take an existing container image and rebuild it using a shell script with buildah. The result is a smaller container image because it doesn&#8217;t need to contain packages and libraries used to build the required applications. But is that reason enough to switch from using Dockerfiles?<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[451],"tags":[550,450,449,530],"class_list":["post-3325","post","type-post","status-publish","format-standard","hentry","category-docker","tag-buildah","tag-containers","tag-docker","tag-podman"],"_links":{"self":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/3325","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/comments?post=3325"}],"version-history":[{"count":13,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/3325\/revisions"}],"predecessor-version":[{"id":3342,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/3325\/revisions\/3342"}],"wp:attachment":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/media?parent=3325"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/categories?post=3325"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/tags?post=3325"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}