{"id":1277,"date":"2024-10-01T14:18:29","date_gmt":"2024-10-01T14:18:29","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2024\/10\/01\/docker-best-practices-using-tags-and-labels-to-manage-docker-image-sprawl\/"},"modified":"2024-10-01T14:18:29","modified_gmt":"2024-10-01T14:18:29","slug":"docker-best-practices-using-tags-and-labels-to-manage-docker-image-sprawl","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2024\/10\/01\/docker-best-practices-using-tags-and-labels-to-manage-docker-image-sprawl\/","title":{"rendered":"Docker Best Practices: Using Tags and Labels to Manage Docker Image Sprawl"},"content":{"rendered":"<p>With many organizations moving to container-based workflows, keeping track of the different versions of your images can become a problem. Even smaller organizations can have hundreds of container images spanning from one-off development tests, through emergency variants to fix problems, all the way to core production images. This leads us to the question: How can we tame our image sprawl while still rapidly iterating our images?<\/p>\n<p>A common misconception is that by using the \u201clatest\u201d tag, you are guaranteeing that you are pulling the \u201clatest<em>\u201d<\/em> version of the image. Unfortunately, this assumption is wrong \u2014 all latest means is \u201cthe last image pushed to this registry.\u201d <\/p>\n<p>Read on to learn more about how to avoid this pitfall when using Docker and how to get a handle on your <a href=\"https:\/\/hub.docker.com\/\" target=\"_blank\" rel=\"noopener\">Docker images<\/a>.<\/p>\n<h2 class=\"wp-block-heading\">Using tags<\/h2>\n<p>One way to address this issue is to use tags when creating an image. Adding one or more tags to an image helps you remember what it is intended for and helps others as well. One approach is always to tag images with their semantic versioning (<a href=\"https:\/\/semver.org\/\" target=\"_blank\" rel=\"noopener\">semver<\/a>), which lets you know what version you are deploying. This sounds like a great approach, and, to some extent, it is, but there is a wrinkle.<\/p>\n<p>Unless you\u2019ve configured your registry for immutable tags, tags can be changed. For example, you could tag my-great-app as v1.0.0 and push the image to the registry. However, nothing stops your colleague from pushing their updated version of the app with tag v1.0.0 as well. Now that tag points to their image, not yours. If you add in the convenience tag latest, things get a bit more murky. <\/p>\n<p>Let\u2019s look at an example:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\nFROM busybox:stable-glibc\n<p># Create a script that outputs the version<br \/>\nRUN echo -e &#8220;#!\/bin\/shn&#8221; &gt; \/test.sh &amp;&amp; <br \/>\n    echo &#8220;echo &#8220;This is version 1.0.0&#8243;&#8221; &gt;&gt; \/test.sh &amp;&amp; <br \/>\n    chmod +x \/test.sh<\/p>\n<p># Set the entrypoint to run the script<br \/>\nENTRYPOINT [&#8220;\/bin\/sh&#8221;, &#8220;\/test.sh&#8221;]\n<\/p><\/div>\n<p>We build the above with docker build -t tagexample:1.0.0 . and run it.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n$ docker run &#8211;rm tagexample:1.0.0<br \/>\nThis is version 1.0.0\n<\/div>\n<p>What if we run it without a tag specified?<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n$ docker run &#8211;rm tagexample<br \/>\nUnable to find image &#8216;tagexample:latest&#8217; locally<br \/>\ndocker: Error response from daemon: pull access denied for tagexample, repository does not exist or may require &#8216;docker login&#8217;.<br \/>\nSee &#8216;docker run &#8211;help&#8217;.\n<\/div>\n<p>Now we build with docker build . without specifying a tag and run it.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n$ docker run &#8211;rm tagexample<br \/>\nThis is version 1.0.0\n<\/div>\n<p>The latest tag is always applied to the most recent push that did not specify a tag. So, in our first test, we had one image in the repository with a tag of 1.0.0, but because we did not have any pushes without a tag, the latest tag did not point to an image. However, once we push an image without a tag, the latest tag is automatically applied to it.<\/p>\n<p>Although it is tempting to always pull the latest tag, it\u2019s rarely a good idea. The logical assumption \u2014 that this points to the most recent version of the image \u2014 is flawed. For example, another developer can update the application to version 1.0.1, build it with the tag 1.0.1, and push it. This results in the following:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n$ docker run &#8211;rm tagexample:1.0.1<br \/>\nThis is version 1.0.1\n<p>$ docker run &#8211;rm tagexample:latest<br \/>\nThis is version 1.0.0\n<\/p><\/div>\n<p>If you made the assumption that latest pointed to the highest version, you\u2019d now be running an out-of-date version of the image.<\/p>\n<p>The other issue is that there is no mechanism in place to prevent someone from inadvertently pushing with the wrong tag. For example, we could create another update to our code bringing it up to 1.0.2. We update the code, build the image, and push it \u2014 but we forget to change the tag to reflect the new version. Although it\u2019s a small oversight, this action results in the following:<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n$ docker run &#8211;rm tagexample:1.0.1<br \/>\nThis is version 1.0.2\n<\/div>\n<p>Unfortunately, this happens all too frequently.<\/p>\n<h2 class=\"wp-block-heading\">Using labels<\/h2>\n<p>Because we can\u2019t trust tags, how should we ensure that we are able to identify our images? This is where the concept of adding metadata to our images becomes important.<\/p>\n<p>The first attempt at using metadata to help manage images was the MAINTAINER instruction. This instruction sets the \u201cAuthor\u201d field (org.opencontainers.image.authors) in the generated image. However, this instruction has been deprecated in favor of the more powerful LABEL instruction. Unlike MAINTAINER, the LABEL instruction allows you to set arbitrary key\/value pairs that can then be read with docker inspect as well as other tooling.<\/p>\n<p>Unlike with tags, labels become part of the image, and when implemented properly, can provide a much better way to determine the version of an image. To return to our example above, let\u2019s see how the use of a label would have made a difference.<\/p>\n<p>To do this, we add the LABEL instruction to the Dockerfile, along with the key version and value 1.0.2.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\nFROM busybox:stable-glibc\n<p>LABEL version=&#8221;1.0.2&#8243;<\/p>\n<p># Create a script that outputs the version<br \/>\nRUN echo -e &#8220;#!\/bin\/shn&#8221; &gt; \/test.sh &amp;&amp; <br \/>\n    echo &#8220;echo &#8220;This is version 1.0.2&#8243;&#8221; &gt;&gt; \/test.sh &amp;&amp; <br \/>\n    chmod +x \/test.sh<\/p>\n<p># Set the entrypoint to run the script<br \/>\nENTRYPOINT [&#8220;\/bin\/sh&#8221;, &#8220;\/test.sh&#8221;]\n<\/p><\/div>\n<p>Now, even if we make the same mistake above where we mistakenly tag the image as version 1.0.1, we have a way to check that does not involve running the container to see which version we are using.<\/p>\n<div class=\"wp-block-syntaxhighlighter-code \">\n$ docker inspect &#8211;format='{{json .Config.Labels}}&#8217; tagexample:1.0.1<br \/>\n{&#8220;version&#8221;:&#8221;1.0.2&#8243;}\n<\/div>\n<h2 class=\"wp-block-heading\">Best practices<\/h2>\n<p>Although you can use any key\/value as a LABEL, there are recommendations. The<a href=\"https:\/\/github.com\/opencontainers\/image-spec\/blob\/main\/annotations.md\" target=\"_blank\" rel=\"noopener\"> OCI<\/a> provides a set of suggested labels within the org.opencontainers.image namespace, as shown in the following table:<\/p>\n<p><strong>Label<\/strong><strong>Content<\/strong>org.opencontainers.image.createdThe date and time on which the image was built (string, RFC 3339 date-time).org.opencontainers.image.authorsContact details of the people or organization responsible for the image (freeform string).org.opencontainers.image.urlURL to find more information on the image (string).org.opencontainers.image.documentationURL to get documentation on the image (string).org.opencontainers.image.sourceURL to the source code for building the image (string).org.opencontainers.image.versionVersion of the packaged software (string).org.opencontainers.image.revisionSource control revision identifier for the image (string).org.opencontainers.image.vendorName of the distributing entity, organization, or individual (string).org.opencontainers.image.licensesLicense(s) under which contained software is distributed (string, SPDX License List).org.opencontainers.image.ref.nameName of the reference for a target (string).org.opencontainers.image.titleHuman-readable title of the image (string).org.opencontainers.image.descriptionHuman-readable description of the software packaged in the image (string).<\/p>\n<p>Because LABEL takes any key\/value, it is also possible to create custom labels. For example, labels specific to a team within a company could use the com.myorg.myteam namespace. Isolating these to a specific namespace ensures that they can easily be related back to the team that created the label.<\/p>\n<h2 class=\"wp-block-heading\">Final thoughts<\/h2>\n<p>Image sprawl is a real problem for organizations, and, if not addressed, it can lead to confusion, rework, and potential production problems. By using tags and labels in a consistent manner, it is possible to eliminate these issues and provide a well-documented set of images that make work easier and not harder.<\/p>\n<h2 class=\"wp-block-heading\">Learn more<\/h2>\n<p>Read the <a href=\"https:\/\/www.docker.com\/blog\/tag\/best-practices\/\" target=\"_blank\" rel=\"noopener\">Docker Best Practices<\/a> collection.<\/p>\n<p>Subscribe to the <a href=\"https:\/\/www.docker.com\/newsletter-subscription\/\" target=\"_blank\" rel=\"noopener\">Docker Newsletter<\/a>.\u00a0<\/p>\n<p>Get the latest release of <a href=\"https:\/\/www.docker.com\/products\/docker-desktop\/\" target=\"_blank\" rel=\"noopener\">Docker Desktop<\/a>.<\/p>\n<p>Vote on what\u2019s next! Check out our <a href=\"https:\/\/github.com\/docker\/roadmap\" target=\"_blank\" rel=\"noopener\">public roadmap<\/a>.<\/p>\n<p>Have questions? The <a href=\"https:\/\/www.docker.com\/community\/\" target=\"_blank\" rel=\"noopener\">Docker community is here to help<\/a>.<\/p>\n<p>New to Docker? <a href=\"https:\/\/docs.docker.com\/desktop\/\" target=\"_blank\" rel=\"noopener\">Get started<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>With many organizations moving to container-based workflows, keeping track of the different versions of your images can become a problem. [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1277","post","type-post","status-publish","format-standard","hentry","category-docker"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/1277","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=1277"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/1277\/revisions"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=1277"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=1277"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=1277"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}