<?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: Kostas Kalafatis</title>
    <description>The latest articles on DEV Community by Kostas Kalafatis (@kalkwst).</description>
    <link>https://dev.to/kalkwst</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%2F55227%2Fe91bf4bf-777b-4937-add8-f6710f9b94c8.jpg</url>
      <title>DEV Community: Kostas Kalafatis</title>
      <link>https://dev.to/kalkwst</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kalkwst"/>
    <language>en</language>
    <item>
      <title>Using Semaphores for Producer-Consumer Problems</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Mon, 25 Nov 2024 14:52:52 +0000</pubDate>
      <link>https://dev.to/kalkwst/using-semaphores-for-producer-consumer-problems-3d0p</link>
      <guid>https://dev.to/kalkwst/using-semaphores-for-producer-consumer-problems-3d0p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Producer-Consumer&lt;/strong&gt; problem is a classic set of scenarios in concurrent programming, first described by Edsger W. Dijkstra in 1965. The problem shows the need of synchronizing several threads or processes sharing a common resource.&lt;/p&gt;

&lt;p&gt;In this problem, there are two kinds of entities that interact with a common shared resource. Producers, who create data, and Consumers that process these data. The difficulty occurs when producers and consumers operate at different rates, potentially leading to circumstances in which producers overrun the buffer (filling it) or consumers exhaust it (emptying it).&lt;/p&gt;

&lt;p&gt;To overcome this, synchronization techniques are required to ensure that the shared resource is used safely and orderly. For example, when the buffer is filled, producers must wait for consumers to remove elements from the buffer, allowing more space for newer items. Consumers must wait when the buffer is empty since there are no data points to process. If we let the producers and consumers run without supervision, the threads may encounter race conditions, inconsistent states, or even deadlocks, all of which compromising the reliability of our system.&lt;/p&gt;

&lt;p&gt;This challenge replicates real-world situations including work scheduling, logging systems, or data streaming pipelines rather than only a theoretical exercise. In multithreaded systems, the producer-consumer pattern is fundamental since it captures the complexity of organizing independent entities with a dependence.&lt;/p&gt;

&lt;h3&gt;
  
  
  But where is my &lt;code&gt;ConcurrentQueue&amp;lt;T&amp;gt;&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;This post focuses on using &lt;code&gt;lock&lt;/code&gt; to build a producer-consumer system, instead of using higher-level tools like &lt;code&gt;ConcurrentQueue&amp;lt;T&amp;gt;&lt;/code&gt;. While &lt;code&gt;ConcurrentQueue&amp;lt;T&amp;gt;&lt;/code&gt; makes it easy to create thread-safe systems, and is something you should use 99.9999% of the time in your production code, it hides how synchronization actually works. By using &lt;code&gt;lock&lt;/code&gt;, you can see how to protect shared resources and prevent threads from interacting with each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Problem
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Producer-Consumer&lt;/strong&gt; problem can be boiled down to the following definition. We have a finite-size buffer and two classes of threads, &lt;em&gt;producers&lt;/em&gt; and &lt;em&gt;consumers&lt;/em&gt;, that put items into the buffer (&lt;em&gt;producers&lt;/em&gt;) and take items out of the buffer (&lt;em&gt;consumers&lt;/em&gt;). Let's break it down:&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Shared Buffer?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;shared buffer&lt;/strong&gt; is a common data structure, more often than not a queue, that acts as the intermediary between producers and consumers, that allows us to decouple them. Producers add items (or data) in the shared buffer, while consumers remove and process these items. The shared buffer has a fixed size meaning that it &lt;strong&gt;does not&lt;/strong&gt; have infinite space, meaning that only a fixed number of items can exist at any point inside the buffer. Thus a buffer can become &lt;strong&gt;full&lt;/strong&gt; or &lt;strong&gt;empty&lt;/strong&gt; which can cause issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When the Buffer is Full&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
A full buffer means no more items can be added until space is freed. If a &lt;strong&gt;producer&lt;/strong&gt; tries to add an item to a full buffer, several things can go wrong. The buffer might throw an exception, disrupting the system and potentially causing a crash. In extreme cases, the producer might overwrite memory it doesn’t own, leading to unpredictable and catastrophic behavior. Alternatively, the buffer might silently drop the item, resulting in data loss and leaving consumers in an inconsistent state. Finally, the producer could block indefinitely while waiting for space, and if consumers are also stalled, this can lead to a &lt;strong&gt;deadlock&lt;/strong&gt;, where neither producers nor consumers can make progress.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;When the Buffer is Empty&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
An empty buffer means there are no items available for consumers to process. If a &lt;strong&gt;consumer&lt;/strong&gt; tries to fetch data from an empty buffer, it might throw an exception or return invalid data, potentially crashing the thread or propagating errors. Without proper safeguards, the consumer could also process uninitialized or corrupted data, leading to incorrect behavior. Worse, the consumer might block indefinitely while waiting for items that will never arrive, resulting in a &lt;strong&gt;deadlock&lt;/strong&gt;, as both producers and consumers remain stuck and unable to continue.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is a Consumer?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Consumer&lt;/strong&gt; is a key component in a producer-consumer system, responsible for retrieving items from a shared buffer and perform operations on them. A consumer's role is to process the data or tasks provided by the producer, usually finishing the workflow by providing results, storing information, or initiating downstream actions. Consumers in a logging system, for example, write log entries to a file or database; in a data processing pipeline, they analyze or transform incoming data; and in a task queue, they perform tasks such as image rendering or transaction processing. &lt;/p&gt;

&lt;p&gt;A consumer relies only on the shared buffer for input. If the buffer goes empty, the consumer must either wait for more items to arrive or gracefully deal with the lack of data. The way a consumer deals with this is determined by how it is designed. Blocking consumers will pause execution until the items are available, but non-blocking consumers may skip processing or retry later. A badly designed consumer may even crash or consume erroneous data if it is not correctly synced with the buffer.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Producer?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Producer&lt;/strong&gt; is an essential component in a producer-consumer system, responsible for generating data or tasks and placing them into a shared buffer for consumers to process. Producers act as the starting point of the workflow, supplying the system with the necessary input to keep it running. In a logging system, a producer may generate log messages; in a data processing pipeline, a producer may read sensor data or retrieve information from an external source. The primary responsibility of a producer is to guarantee that relevant data or tasks are regularly made available to consumers without overwhelming the system.&lt;/p&gt;

&lt;p&gt;A producer relies only on the shared buffer for input. It must also handle situations in which the buffer is full. Depending on the design, the producer may block until space becomes available, drop the item, or activate backpressure mechanisms to slow its generation rate. The choice is based on the system's requirements and tolerance for delays or data loss. For example, a real-time system may favor speed over dependability and drop data, but a critical system may stall the producer in order to ensure that every item is processed.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Semaphore?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Semaphore&lt;/strong&gt; is a synchronization primitive used in computer science to control access to a common resource by multiple threads and avoid critical section problems in a concurrent system. It's like a manager that orchestrates which threads can enter a section of code that shouldn't be accessed by multiple threads at the same time. A semaphore uses a counter to track how many threads or processes can access the resource simultaneously. The counter begins with a specified initial value, representing the initial number of "permits" for accessing the resource. When a thread wants to use a resource, it decrements the counter, effectively "acquiring" a permit. If the counter is greater than zero, the thread can proceed. However, if the counter reaches zero, the thread blocks and waits until another thread releases a permit by incrementing the counter.&lt;/p&gt;

&lt;p&gt;To understand how it works in practice, consider a semaphore initialized with a count of 3, representing three available slots for accessing a shared resource.&lt;/p&gt;

&lt;p&gt;When processes interact with the semaphore, the count is decremented or incremented based on whether they are acquiring or releasing a resource. For instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initial Semaphore Count: 3

Process A --wait()-&amp;gt; Decrease Count: 2
Process B --wait()-&amp;gt; Decrease Count: 1
Process C --wait()-&amp;gt; Decrease Count: 0 (No more available slots)

Process A --signal()-&amp;gt; Increase Count: 1 (Resource released) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s say three processes—Process A, Process B, and Process C—attempt to access the resource sequentially. When &lt;strong&gt;Process A&lt;/strong&gt; calls &lt;code&gt;wait()&lt;/code&gt;, it decrements the semaphore’s count from 3 to 2 and proceeds to access the resource. Similarly, &lt;strong&gt;Process B&lt;/strong&gt; calls &lt;code&gt;wait()&lt;/code&gt;, further decreasing the count to 1, and gains access. When &lt;strong&gt;Process C&lt;/strong&gt; calls &lt;code&gt;wait()&lt;/code&gt;, it reduces the count to 0, taking the last available slot. At this point, the semaphore’s counter indicates that no slots are left, and any subsequent process calling &lt;code&gt;wait()&lt;/code&gt; will block until a slot becomes available.&lt;/p&gt;

&lt;p&gt;Once &lt;strong&gt;Process A&lt;/strong&gt; completes its work, it calls &lt;code&gt;signal()&lt;/code&gt; to release its slot, increasing the semaphore’s count from 0 to 1. This signals that a resource is now free, allowing another waiting process to proceed. The flow of these operations ensures that only up to three processes can access the resource simultaneously, while additional processes are queued and served in turn as resources are freed.&lt;/p&gt;

&lt;p&gt;In a producer-consumer system, semaphores play a critical role in managing the flow of data. For instance, a semaphore can track the number of items in the buffer, blocking consumers if the buffer is empty, or track the available slots, blocking producers if the buffer is full. This coordination ensures that producers and consumers interact with the shared resource in an orderly and efficient manner, preventing race conditions or data corruption.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Mutex?
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;mutex&lt;/strong&gt; (short for mutual exclusion) is a synchronization tool that allows only one thread or process to access a shared resource at a time. Unlike a semaphore, which can manage multiple simultaneous accesses, a mutex ensures &lt;strong&gt;exclusive access&lt;/strong&gt; by locking the resource when one thread acquires it. Other threads attempting to acquire the mutex will block until the resource is released. This behavior prevents race conditions and ensures thread safety when accessing critical sections of code or shared data.&lt;/p&gt;

&lt;p&gt;To understand how a mutex works, imagine a scenario where multiple threads compete to access a shared resource, such as a log file or a database connection. When a thread acquires the mutex, it effectively locks the resource, preventing other threads from interfering. Once the thread completes its task, it releases the mutex, unlocking the resource for the next waiting thread.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initial Mutex State: Unlocked

Thread A --acquire()-&amp;gt; Mutex is Locked
Thread B --acquire()-&amp;gt; Mutex is Locked, wait
Thread C --acquire()-&amp;gt; Mutex is Locked, wait

Thread A --release()-&amp;gt; Mutex is Unlocked
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This flow ensures that only one thread can access the shared resource at any given time. Unlike a semaphore, which tracks permits with a counter, a mutex is binary—it is either &lt;strong&gt;locked&lt;/strong&gt; or &lt;strong&gt;unlocked&lt;/strong&gt;. Additionally, mutexes are typically &lt;strong&gt;owned&lt;/strong&gt; by the thread that locks them, meaning only the owning thread can release the mutex. This ownership guarantees that other threads cannot accidentally unlock a mutex they did not acquire, ensuring safe and predictable behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using  Semaphores and Locks
&lt;/h2&gt;

&lt;p&gt;To solve the producer-consumer problem, we can use &lt;strong&gt;two semaphores&lt;/strong&gt; and a &lt;strong&gt;lock&lt;/strong&gt;. Each plays a distinct role in ensuring safe and efficient synchronization.&lt;/p&gt;

&lt;p&gt;First we are going to use two semaphores for tracking the items in the buffer. The &lt;strong&gt;ConsumerSemaphore&lt;/strong&gt; tracks the number of available items in the buffer. Consumers wait on this semaphore when the buffer is empty. The &lt;strong&gt;ProducerSemaphore&lt;/strong&gt; tracks the number of available slots in the buffer for producers. Producers wait on this semaphore when the buffer is full.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;BufferLock&lt;/strong&gt; is used to ensure mutual exclusion when we are modifying our shared buffer. Both producers and consumers must acquire the lock before accessing or modifying the buffer.&lt;/p&gt;

&lt;p&gt;The solution works by coordinating the actions of producers and consumers using semaphores and locks. Semaphores ensure that threads only proceed when specific conditions are met. For example, a producer is blocked from adding items if the buffer is full, as the space semaphore’s count will be zero. Similarly, a consumer cannot remove an item from an empty buffer, as the item semaphore’s count will prevent them from proceeding. These semaphores act as gatekeepers, ensuring producers and consumers interact with the buffer only when it is in a valid state.&lt;/p&gt;

&lt;p&gt;The lock ensures mutual exclusion when modifying the shared buffer. Without it, multiple threads could access the buffer simultaneously, leading to race conditions, data corruption, or other undefined behavior. By requiring each thread to acquire the lock before modifying the buffer, we ensure that only one thread at a time can perform operations like adding or removing items. This protects the integrity of the buffer and prevents conflicts between producers and consumers.&lt;/p&gt;

&lt;p&gt;Finally, the combination of semaphores and locks maintains a balanced workflow between producers and consumers. The semaphores dynamically manage the flow of threads, allowing producers to proceed only when there is space in the buffer and consumers to act only when there are items to process. This synchronization mechanism prevents deadlocks and ensures that both producers and consumers can operate efficiently without wasting resources.&lt;/p&gt;

&lt;p&gt;Now, lets see the &lt;code&gt;Produce&lt;/code&gt; method, that is used by producers to add data to the buffer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PROCEDURE Produce();
BEGIN
    Wait(ProducerSemaphore);            # Wait for space in the buffer.
    Lock(BufferLock);                   # Lock the buffer for safe access.

    Add(item);                          # Add an item in the buffer.

    Unlock(BufferLock);                 # Unlock the buffer.
    Signal(ConsumerSemaphore);          # Signal that an item is available.
END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wait for Space&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Wait(ProducerSemaphore)&lt;/code&gt; ensures that there is available space in the buffer before proceeding. If the buffer is full, the producer will block until space becomes available.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock the Buffer&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Lock(BufferLock)&lt;/code&gt; acquires a lock on the shared buffer to prevent simultaneous access by other threads (producers or consumers). This ensures mutual exclusion.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add an Item&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Add(item)&lt;/code&gt; safely places a new item into the buffer while the lock is held.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unlock the Buffer&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Unlock(BufferLock)&lt;/code&gt; releases the lock, allowing other threads to access the buffer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal Availability&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Signal(ConsumerSemaphore)&lt;/code&gt; increments the semaphore, notifying consumers that a new item is now available in the buffer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;Consume&lt;/code&gt; method, is used by consumers to get data from the buffer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PROCEDURE Consume();
BEGIN
    WAIT(ConsumerSemaphore);            # Wait for items in the buffer.
    Lock(BufferLock);                   # Lock the buffer for safe access.

    Remove(item);                       # Remove an item from the buffer.

    Unlock(BufferLock);                 # Unlock the buffer.
    Signal(ProducerSemaphore);          # Signal that a slot is available
END;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Wait for Items&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;WAIT(ConsumerSemaphore)&lt;/code&gt; ensures that there is at least one item in the buffer before proceeding. If the buffer is empty, the consumer will block until an item becomes available.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lock the Buffer&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Lock(BufferLock)&lt;/code&gt; acquires a lock on the shared buffer to ensure mutual exclusion, preventing other threads (producers or consumers) from accessing the buffer simultaneously.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove an Item&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Remove(item)&lt;/code&gt; safely retrieves an item from the buffer while holding the lock. This operation ensures that no other thread modifies the buffer during the removal.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unlock the Buffer&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Unlock(BufferLock)&lt;/code&gt; releases the lock, allowing other threads to access the buffer.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal Slot Availability&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Signal(ProducerSemaphore)&lt;/code&gt; increments the semaphore, notifying producers that a slot in the buffer is now free and available for new items.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With that out of the way, lets move to the actual C# implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Now that we've discussed the concept of solving the producer-consumer problem using semaphores and locks, let's dive into the implementation. Our goal is to create a system where multiple producers can generate items and add them into a shared buffer, while multiple consumers can retrieve and process those items, all in a thread-safe manner.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Shared Buffer
&lt;/h3&gt;

&lt;p&gt;In this implementation, we are going to use a &lt;strong&gt;queue&lt;/strong&gt; as the shared buffer to hold items produced by the producers until they are consumed. The queue allows us to demonstrate the concepts of synchronization and coordination in a straightforward and easily understandable manner. Specifically, the queue serves as a simple first-in, first-out (FIFO) data structure, making it intuitive to see how producers add items and consumers retrieve them.&lt;/p&gt;

&lt;p&gt;However, it’s important to note that in production environments, you should almost always use a &lt;strong&gt;concurrent data structure&lt;/strong&gt;, such as &lt;code&gt;ConcurrentQueue&lt;/code&gt; in .NET. These data structures are optimized for multi-threaded scenarios and handle thread safety internally, making them more efficient and less error-prone. While our implementation adds locks and semaphores to make a standard queue thread-safe, using a &lt;code&gt;ConcurrentQueue&lt;/code&gt; would eliminate the need for such explicit synchronization logic. This approach ensures better performance and maintainability in real-world applications.&lt;/p&gt;

&lt;p&gt;For the purpose of this demonstration, we are intentionally using a manually synchronized queue to showcase how semaphores and locks work together to solve the producer-consumer problem. This allows you to understand the mechanics behind concurrency before relying on higher-level abstractions in production code.&lt;/p&gt;

&lt;p&gt;The shared resource is designed to manage a queue in a thread-safe way, allowing producers to add items and consumers to retrieve them without conflicts. To achieve this, we use a combination of a queue, semaphores, and a lock.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SharedResource&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;  &lt;/span&gt;
    &lt;span class="c1"&gt;/// The underlying queue that holds the data.  &lt;/span&gt;
    &lt;span class="c1"&gt;/// Access is synchronized with the &amp;lt;see cref="_lock"/&amp;gt; object.  &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;  &lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_queue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;  &lt;/span&gt;
    &lt;span class="c1"&gt;/// A Semaphore that tracks the number of items currently available in the queue.  &lt;/span&gt;
    &lt;span class="c1"&gt;/// Ensures consumers block when the queue is empty.  &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;  &lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SemaphoreSlim&lt;/span&gt; &lt;span class="n"&gt;_emptyQueueSemaphore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;  &lt;/span&gt;
    &lt;span class="c1"&gt;/// A Semaphore that tracks the number of slots currently available in the queue.  &lt;/span&gt;
    &lt;span class="c1"&gt;/// Ensures producers block when the queue is full.  &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;  &lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SemaphoreSlim&lt;/span&gt; &lt;span class="n"&gt;_fullQueueSemaphore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;  &lt;/span&gt;
    &lt;span class="c1"&gt;/// Lock object to ensure thread-safe access to the &amp;lt;see cref="_queue"/&amp;gt;.  &lt;/span&gt;
    &lt;span class="c1"&gt;/// This prevents race conditions during enqueue and dequeue operations.  &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;  &lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;_lock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  

    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt;  &lt;/span&gt;
    &lt;span class="c1"&gt;/// The capacity of our queue.  &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt;  &lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_maxQueueSize&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_queue&lt;/code&gt; variable is the actual queue where items are stored. It acts as the shared buffer between producers and consumers. Since multiple threads can access this queue at the same time, we need to protect it using a lock to avoid issues like race conditions.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;_emptyQueueSemaphore&lt;/code&gt; keeps track of how many items are currently in the queue. If the queue is empty, consumers will block on this semaphore until a producer adds an item. It ensures consumers don’t try to take something that isn’t there.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;_fullQueueSemaphore&lt;/code&gt; manages the available space in the queue. If the queue is full, producers will block on this semaphore until a consumer removes an item, making space available. It ensures the queue never overflows.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;_lock&lt;/code&gt; is a simple mechanism to ensure only one thread accesses the queue at a time. This is critical when adding or removing items to avoid data corruption or unexpected behavior.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;_maxQueueSize&lt;/code&gt; is the maximum number of items the queue can hold. It defines the size of the buffer and ensures that the system respects the queue’s limits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Shared&lt;/span&gt; &lt;span class="n"&gt;Resource&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Variable declaration...&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SharedResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentOutOfRangeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"Capacity must be greater than zero."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
        &lt;span class="p"&gt;}&lt;/span&gt;  
        &lt;span class="n"&gt;_maxQueueSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
        &lt;span class="n"&gt;_queue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
        &lt;span class="n"&gt;_emptyQueueSemaphore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SemaphoreSlim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_fullQueueSemaphore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SemaphoreSlim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&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;The constructor sets everything up to make the queue functional and safe for concurrent use.&lt;/p&gt;

&lt;p&gt;The constructor ensures the &lt;code&gt;capacity&lt;/code&gt; is greater than zero. If someone tries to create a queue with zero or negative capacity, it throws an error. After all, a queue with no capacity wouldn’t make sense, and the &lt;code&gt;_queue&lt;/code&gt; is created with the given capacity. This prepares the queue to handle items up to the specified limit.&lt;/p&gt;

&lt;p&gt;Also the two semaphores are initialized. The &lt;code&gt;_emptyQueueSemaphore&lt;/code&gt; starts with a count of &lt;code&gt;0&lt;/code&gt;, meaning that the queue is initially empty, so consumers will wait until producers add items. The &lt;code&gt;_fullQueueSemaphore&lt;/code&gt; starts with a count equal to the queue's capacity, indicating that the queue has all its slots available for producers to use.&lt;/p&gt;

&lt;p&gt;With the shared resource in place, we can now define the methods for producing and consuming items. These methods handle the core logic of adding and removing items from the queue, ensuring thread safety and synchronization through semaphores and locks.&lt;/p&gt;

&lt;p&gt;Both methods are implemented as &lt;strong&gt;asynchronous&lt;/strong&gt; to prevent blocking the execution of other tasks. Without this, producers or consumers waiting for semaphores to release (e.g., when the queue is full or empty) would block the entire thread, reducing the overall responsiveness and scalability of the system. By using asynchronous methods, threads can perform other work while waiting, making the application more efficient, especially in scenarios involving multiple producers and consumers.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;ProduceAsync&lt;/code&gt; method
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;ProduceAsync&lt;/code&gt; method is responsible for generating items and adding them to the queue, waiting if necessary for space to become available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ProduceAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_fullQueueSemaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Wait for space.  &lt;/span&gt;
    &lt;span class="k"&gt;lock&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_lock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Enter critical section.  &lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="n"&gt;_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Produced: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Exit critical section.  &lt;/span&gt;
    &lt;span class="n"&gt;_emptyQueueSemaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Signal that there are items available in the queue.  &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ProduceAsync&lt;/code&gt; method is the core logic for adding items to the shared queue in a thread-safe and synchronized manner. It ensures that producers only add items when there is space available and that access to the queue is properly coordinated with other threads. &lt;/p&gt;

&lt;p&gt;The method starts by awaiting &lt;code&gt;_fullQueueSemaphore.WaitAsync&lt;/code&gt;. This semaphore tracks the available slots in the queue. If the queue is full, the producer will wait until a consumer removes an item, freeing up space. The asynchronous nature of &lt;code&gt;WaitAsync&lt;/code&gt; ensures that the thread isn’t blocked while waiting, allowing other operations to proceed.&lt;/p&gt;

&lt;p&gt;A lock on &lt;code&gt;_lock&lt;/code&gt; is used to enter the critical section where the producer interacts with the queue. This guarantees that only one thread can modify the queue at a time, preventing race conditions. Inside the critical section, the item (value) is added to the queue using &lt;code&gt;_queue.Enqueue(value)&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Once the item is added, the lock is released, allowing other threads (producers or consumers) to access the queue.&lt;/p&gt;

&lt;p&gt;Finally, the producer calls &lt;code&gt;_emptyQueueSemaphore.Release&lt;/code&gt; to signal that an item is now available in the queue. This semaphore notifies waiting consumers, allowing them to proceed with consuming the item.&lt;/p&gt;

&lt;h4&gt;
  
  
  The &lt;code&gt;ConsumeAsync&lt;/code&gt; method
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;&lt;code&gt;ConsumeAsync&lt;/code&gt;&lt;/strong&gt; method handles retrieving items from the shared queue in a thread-safe manner. It ensures that consumers only access the queue when there are items available and coordinates access using semaphores and locks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ConsumeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_emptyQueueSemaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="k"&gt;lock&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_lock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dequeue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Consumed: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="n"&gt;_fullQueueSemaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;value&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;The method starts by awaiting &lt;code&gt;_emptyQueueSemaphore.WaitAsync&lt;/code&gt;. This semaphore tracks the number of items currently available in the queue. If the queue is empty, the consumer will block here, waiting for a producer to add an item. By using the asynchronous version (&lt;code&gt;WaitAsync&lt;/code&gt;), the thread is not blocked, allowing other tasks to execute while the consumer waits.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;lock&lt;/code&gt; on &lt;code&gt;_lock&lt;/code&gt; ensures mutual exclusion while interacting with the queue. Once inside the critical section, the consumer safely retrieves an item using &lt;code&gt;_queue.Dequeue()&lt;/code&gt;. This ensures no other threads (producers or consumers) can modify the queue while the current thread is working. After retrieving the item, the lock is released, allowing other threads to access the queue.&lt;/p&gt;

&lt;p&gt;The consumer calls &lt;code&gt;_fullQueueSemaphore.Release&lt;/code&gt;, signaling that a slot in the queue is now available for producers to use. This step ensures producers don’t remain blocked when the queue has room for new items.&lt;/p&gt;

&lt;p&gt;Finally, the method returns the value retrieved from the queue, allowing the caller to process the consumed item.&lt;/p&gt;

&lt;p&gt;Finally, we have two helper methods &lt;code&gt;AvailableSpaces&lt;/code&gt; and &lt;code&gt;AvailableElements&lt;/code&gt;, that return the current count of the semaphores:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;AvailableSpaces&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_fullQueueSemaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;AvailableElements&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_emptyQueueSemaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CurrentCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Consumer Class
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;&lt;code&gt;Consumer&lt;/code&gt;&lt;/strong&gt; class represents an entity that retrieves and processes items from the shared resource in a thread-safe and synchronized manner. Its role is to repeatedly consume items as they become available, ensuring no race conditions occur during the interaction with the shared resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SharedResource&lt;/span&gt; &lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SharedResource&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="n"&gt;_resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&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;The constructor takes a single parameter of the &lt;code&gt;SharedResource&lt;/code&gt; type. This is the shared buffer (queue) that the consumer will interact with.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;code&gt;ConsumeAsync&lt;/code&gt;&lt;/strong&gt; method performs the actual consumption of items in an infinite loop, which is gracefully terminated using a &lt;code&gt;CancellationToken&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ConsumeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="p"&gt;{&lt;/span&gt;   
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AvailableElements&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Consumer: No available elements, waiting for semaphore access"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
        &lt;span class="p"&gt;}&lt;/span&gt;  

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConsumeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Consumer: Processed &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&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;Before attempting to consume an item, the method checks the shared resource’s &lt;code&gt;AvailableElements&lt;/code&gt; property. If the queue is empty, it logs a message indicating that the consumer is waiting for semaphore access.&lt;/p&gt;

&lt;p&gt;The consumer calls the shared resource’s &lt;code&gt;ConsumeAsync&lt;/code&gt; method to dequeue an item. This method handles all synchronization (waiting for the semaphore, locking the queue, and signaling availability to producers). Once an item is successfully retrieved, the consumer logs the action with its identifier (&lt;code&gt;Consumer&lt;/code&gt;) and the item value. This provides clear visibility into the actions of consumers.&lt;/p&gt;

&lt;p&gt;The loop continues until the &lt;code&gt;CancellationToken&lt;/code&gt; signals a request to stop. This allows the system to gracefully terminate consumers when needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Producer Class
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;&lt;code&gt;Producer&lt;/code&gt;&lt;/strong&gt; class represents an entity that generates and adds items to the shared resource. Its role is to repeatedly produce items while ensuring thread-safe interactions with the shared buffer. Like the &lt;code&gt;Consumer&lt;/code&gt; class, it operates continuously and can be gracefully terminated using a &lt;code&gt;CancellationToken&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Producer&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SharedResource&lt;/span&gt; &lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SharedResource&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="n"&gt;_resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ProduceAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
    &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
        &lt;span class="p"&gt;{&lt;/span&gt;    
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AvailableSpaces&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
            &lt;span class="p"&gt;{&lt;/span&gt;  
                &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Producer: No available spaces, waiting for semaphore access"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
            &lt;span class="p"&gt;}&lt;/span&gt;  

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Producer: Adding &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProduceAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;++,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
        &lt;span class="p"&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;The &lt;strong&gt;&lt;code&gt;ProduceAsync&lt;/code&gt;&lt;/strong&gt; method contains the logic for generating and adding items to the shared resource. The method uses a local variable &lt;code&gt;item&lt;/code&gt; to track the current value being produced. This value increments with each iteration of the loop, ensuring each item added to the queue is unique.&lt;/p&gt;

&lt;p&gt;Before attempting to produce an item, the method checks the shared resource’s &lt;code&gt;AvailableSpaces&lt;/code&gt; property to determine if the queue has room. If the queue is full, it logs a message indicating that the producer is waiting for space. The method logs the item being added, providing a clear view of the producer's activity in the system. &lt;/p&gt;

&lt;p&gt;The producer calls the shared resource’s &lt;code&gt;ProduceAsync&lt;/code&gt; method, passing the item and the cancellation token. This method handles synchronization (waiting for the semaphore, locking the queue, and signaling availability to consumers). &lt;/p&gt;

&lt;p&gt;The loop continues producing items until the &lt;code&gt;CancellationToken&lt;/code&gt; signals a request to stop. This ensures the producer can be gracefully terminated when the system needs to shut down or adjust workload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demonstrating the Producer-Consumer System
&lt;/h3&gt;

&lt;p&gt;To see the producer-consumer system in action, we need a &lt;strong&gt;main method&lt;/strong&gt; to tie everything together. This method will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initialize the Shared Resource&lt;/strong&gt;: Set up the queue with a fixed capacity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create Producers and Consumers&lt;/strong&gt;: Instantiate multiple producers and consumers to simulate real-world behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run Asynchronous Tasks&lt;/strong&gt;: Start the producers and consumers on separate tasks so they can work concurrently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful Shutdown&lt;/strong&gt;: Use a &lt;code&gt;CancellationTokenSource&lt;/code&gt; to allow stopping the system by user input.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;bufferSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Define the size of the queue&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;producerCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Number of producers&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;consumerCount&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Number of consumers&lt;/span&gt;

    &lt;span class="c1"&gt;// Create the shared resource&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sharedResource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SharedResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bufferSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a cancellation token for stopping the system&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cts&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;CancellationTokenSource&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Create and start producer tasks&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;producers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;producerCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Producer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sharedResource&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;producer&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;producer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProduceAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Create and start consumer tasks&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;consumers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumerCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sharedResource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consumer&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConsumeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Token&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Press any key to stop the system..."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;cts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Signal cancellation to stop all tasks&lt;/span&gt;

    &lt;span class="c1"&gt;// Wait for all tasks to complete&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WhenAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;producers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;consumers&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"System shut down gracefully."&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;h2&gt;
  
  
  Enhancements
&lt;/h2&gt;

&lt;p&gt;The producer-consumer implementation we’ve discussed is robust and demonstrates key synchronization principles. However, there are a few enhancements worth noting to make the example more realistic and to clarify its behavior under certain conditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Random Delays to Simulate Real-Life Behavior
&lt;/h3&gt;

&lt;p&gt;In real-world scenarios, producers and consumers rarely operate at consistent, predictable rates. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A producer might experience delays due to external factors like data acquisition time or resource availability.&lt;/li&gt;
&lt;li&gt;A consumer might take longer to process certain items depending on their complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To simulate this variability, we can introduce random delays into the producer and consumer loops. This creates a more dynamic system and allows us to observe how the semaphores and locks handle non-deterministic behavior.&lt;/p&gt;

&lt;p&gt;Here's how we can modify the methods:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Producer&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AvailableSpaces&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Producer: No available spaces, waiting for semaphore access"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Producer: Adding &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProduceAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;++,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Random delay between 100ms and 1000ms&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Consumer&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsCancellationRequested&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;   
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AvailableElements&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Consumer: No available elements, waiting for semaphore access"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ConsumeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Consumer: Processed &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Random delay between 500ms and 1500ms&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By introducing these delays, the producer-consumer system more closely resembles real-world conditions where production and consumption rates fluctuate. It also showcases how the synchronization mechanisms handle unpredictable workloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Illusion of First-Come, First-Served
&lt;/h3&gt;

&lt;p&gt;While the implementation might seem like it operates on a &lt;strong&gt;First-Come, First-Served (FCFS)&lt;/strong&gt; basis, this is not strictly true. The order in which threads are scheduled and granted access to semaphores is determined by the operating system’s thread scheduler, which prioritizes threads based on several factors, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread priority.&lt;/li&gt;
&lt;li&gt;CPU time availability.&lt;/li&gt;
&lt;li&gt;The system’s overall load.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that even if Producer A tries to enqueue an item before Producer B, the OS might schedule Producer B’s thread first, allowing it to acquire the semaphore and the lock before Producer A. Similarly, consumers might not process items in the exact order they arrive, depending on how the OS schedules their threads.&lt;/p&gt;

&lt;p&gt;If two consumers are waiting for items, you might observe that the one which started waiting last gets to consume first, even though the queue maintains the correct order of items. This is because semaphore access is not inherently fair—it does not guarantee that threads are served in the order they began waiting.&lt;/p&gt;

&lt;p&gt;Understanding this nuance is important when designing systems where strict fairness or predictable thread execution order is critical. If FCFS behavior is a strict requirement, additional mechanisms, such as a &lt;strong&gt;per-thread semaphore&lt;/strong&gt; or &lt;strong&gt;queueing requests&lt;/strong&gt;, would need to be implemented.&lt;/p&gt;

&lt;p&gt;If strict First-Come, First-Served (FCFS) behavior is critical for your use case, additional mechanisms such as per-thread semaphores or request queues can be implemented. If you’re interested in exploring solutions to enforce true FCFS order in a producer-consumer system, let me know in the comments—I’d be happy to dive into it in a future post!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The producer-consumer problem is a cornerstone of concurrent programming, demonstrating how multiple threads can safely interact with shared resources. In this post, we explored how to solve this problem using semaphores and locks, breaking down the core concepts into an intuitive, step-by-step approach. We built a shared resource with a thread-safe queue, implemented producers and consumers that operate asynchronously, and demonstrated how semaphores and locks work together to ensure synchronization and prevent race conditions.&lt;/p&gt;

&lt;p&gt;Beyond the implementation, we delved into enhancements that bring the system closer to real-world scenarios, such as adding random delays to simulate varying workloads. We also discussed the nuances of thread scheduling and why, despite appearances, our solution isn’t strictly First-Come, First-Served (FCFS)—a limitation determined by the operating system’s thread scheduler.&lt;/p&gt;

&lt;p&gt;This journey through the producer-consumer problem has provided both practical and conceptual insights. Whether you’re implementing this pattern in a real-world application or simply looking to deepen your understanding of concurrency, this post should serve as a solid foundation.&lt;/p&gt;

&lt;p&gt;If you have questions, suggestions, or would like to see specific aspects—such as enforcing true FCFS order—covered in future posts, let me know in the comments.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Configuration of Services</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Mon, 26 Aug 2024 06:00:00 +0000</pubDate>
      <link>https://dev.to/kalkwst/configuration-of-services-2ikj</link>
      <guid>https://dev.to/kalkwst/configuration-of-services-2ikj</guid>
      <description>&lt;p&gt;In cloud-native applications, we typically use environment variables to manage configuration settings. These variables offer a flexible way to adjust settings without modifying the source code. They are stored on Linux-based systems and can be accessed by applications to control their behavior.&lt;/p&gt;

&lt;p&gt;For example, suppose your application relies on a &lt;code&gt;LOG_LEVEL&lt;/code&gt; environment variable to determine the level of detail in its logs. If you change this variable from &lt;code&gt;INFO&lt;/code&gt; to &lt;code&gt;DEBUG&lt;/code&gt; and restart the application, you'll start seeing more detailed logs, which can help you diagnose issues more effectively.&lt;/p&gt;

&lt;p&gt;This approach is also handy for deploying the same application across different environments, such as staging, testing, and production. You can simply adjust the environment variables for each environment to meet its specific needs.&lt;/p&gt;

&lt;p&gt;Similarly, when using Docker Compose to manage containers, you configure services by setting environment variables for each container. This keeps your configurations consistent and easy to manage across different environments.&lt;/p&gt;

&lt;p&gt;There are three methods of defining environment variables in Docker Compose arranged from the most stable, with the least frequent changes, to the most dynamic, with the most frequent changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using the &lt;em&gt;Compose&lt;/em&gt; file&lt;/li&gt;
&lt;li&gt;Using shell environment variables&lt;/li&gt;
&lt;li&gt;Using the environment file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the environment variables you need for your containers don’t change often, it's a good idea to keep them in your &lt;code&gt;docker-compose.yaml&lt;/code&gt; file. For sensitive information like passwords, it’s safer to set these variables directly in your shell environment before running the &lt;code&gt;docker-compose&lt;/code&gt; command. However, if you have a lot of variables or if they differ between your testing, staging, and production environments, it's more manageable to put them in &lt;code&gt;.env&lt;/code&gt; files and then reference those in your &lt;code&gt;docker-compose.yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;services&lt;/code&gt; section of your &lt;code&gt;docker-compose.yaml&lt;/code&gt; file, you can specify environment variables for each service. For instance, you can set environment variables like &lt;code&gt;LOG_LEVEL&lt;/code&gt; and &lt;code&gt;METRICS_PORT&lt;/code&gt; for the &lt;code&gt;server&lt;/code&gt; service like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-custom&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;LOG_LEVEL=debug&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;METRICS_PORT=8445&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the environment variables are not defined directly in the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file, Docker Compose can still retrieve their values from the shell environment. For example, if you want the &lt;code&gt;HOSTNAME&lt;/code&gt; environment variable for the &lt;code&gt;server&lt;/code&gt; service to be set from the shell, you can ensure it's available by exporting it in your shell before running Docker Compose. Here's how it might look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-custom&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HOSTNAME&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And exporting the &lt;code&gt;HOSTNAME&lt;/code&gt; variable to the shell:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HOSTNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myhost
docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the shell running the &lt;code&gt;docker-compose&lt;/code&gt; command doesn't have a value set for the &lt;code&gt;HOSTNAME&lt;/code&gt; environment variable, the container will start with &lt;code&gt;HOSTNAME&lt;/code&gt; as an empty environment variable. &lt;/p&gt;

&lt;p&gt;You can also store environment variables in &lt;code&gt;.env&lt;/code&gt; files and configure them in your &lt;code&gt;docker-compose.yaml&lt;/code&gt; files. For example, you might have a &lt;code&gt;database.env&lt;/code&gt; file that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;localhost
&lt;span class="nv"&gt;DB_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5432
&lt;span class="nv"&gt;DB_USER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;myuser
&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mypassword
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file, you can specify the &lt;code&gt;.env&lt;/code&gt; file for each service using the &lt;code&gt;env_file&lt;/code&gt; field. Here's how you would configure it for a service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;my-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-image&lt;/span&gt;
        &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./database.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Docker Compose sets up the &lt;code&gt;server&lt;/code&gt; service, it will apply all the environment variables specified in the &lt;code&gt;database.env&lt;/code&gt; file to the container. In the following example, you'll get hands-on experience configuring an application using all three methods for setting environment variables in Docker Compose.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Services with Docker Compose
&lt;/h3&gt;

&lt;p&gt;In this example, you'll set up a Docker Compose application using various methods for configuring environment variables. You’ll start by defining two environment variables in a file named &lt;code&gt;print.env&lt;/code&gt;. Next, you'll add one environment variable directly in the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file and set another one from the Terminal on the fly. By the end, you’ll see how these four environment variables from different sources are combined and used in your container.&lt;/p&gt;

&lt;p&gt;Create a folder named &lt;code&gt;configuration-server&lt;/code&gt; and navigate into it using the &lt;code&gt;cd&lt;/code&gt; command&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="nb"&gt;mkdir &lt;/span&gt;configuration-server
&lt;span class="nb"&gt;cd &lt;/span&gt;configuration-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create an &lt;code&gt;.env&lt;/code&gt; file with the name &lt;code&gt;display.env&lt;/code&gt; and the following content, using your favorite code editor. I am going to use VSCode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;VAR_FROM_ENV_FILE_1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;HELLO
&lt;span class="nv"&gt;VAR_FROM_ENV_FILE_2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;WORLD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create a &lt;code&gt;docker-compose.yaml&lt;/code&gt; file similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sh -c 'sleep 5 &amp;amp;&amp;amp; env'&lt;/span&gt;
        &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;display.env&lt;/span&gt;
        &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ENV_FROM_COMPOSE_FILE=HELLO&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ENV_FROM_SHELL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;docker-compose.yaml&lt;/code&gt; file defines a &lt;code&gt;print&lt;/code&gt; service using the &lt;code&gt;busybox&lt;/code&gt; image. It runs a command that sleeps for 5 seconds and then prints the environment variables. Environment variables are configured in three ways: from the &lt;code&gt;print.env&lt;/code&gt; file, which contains key-value pairs; directly in the Compose file with &lt;code&gt;ENV_FROM_COMPOSE_FILE=HELLO&lt;/code&gt;; and via shell environment variables with &lt;code&gt;ENV_FROM_SHELL&lt;/code&gt;, which will be set at runtime if available. &lt;/p&gt;




&lt;p&gt;Export the &lt;code&gt;ENV_FROM_SHELL&lt;/code&gt; variable to the shell with the following command:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ENV_FROM_SHELL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;WORLD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Use the &lt;code&gt;docker-compose up&lt;/code&gt; command to start the app. The output should be similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up
error during connect: this error may indicate that the docker daemon is not running: Get "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.24/containers/json?all=1&amp;amp;filters=%7B%22label%22%3A%7B%22com.docker.compose.config-hash%22%3Atrue%2C%22com.docker.compose.project%3Dconfiguration-server%22%3Atrue%7D%7D": open //./pipe/docker_engine: The system cannot find the file specified.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this moment of embarrassment,  you will remember to start the actual Docker daemon, and the output should be the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;display Pulling
 ec562eabd705 Already exists
 display Pulled
 Network configuration-server_default  Creating
 Network configuration-server_default  Created
 Container configuration-server-display-1  Creating
 Container configuration-server-display-1  Created
Attaching to configuration-server-display-1
configuration-server-display-1  | HOSTNAME=c791b58e8a00
configuration-server-display-1  | SHLVL=1
configuration-server-display-1  | HOME=/root
configuration-server-display-1  | VAR_FROM_ENV_FILE_1=HELLO
configuration-server-display-1  | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
configuration-server-display-1  | VAR_FROM_ENV_FILE_2=WORLD
configuration-server-display-1  | ENV_FROM_COMPOSE_FILE=HELLO
configuration-server-display-1  | ENV_FROM_SHELL=WORLD
configuration-server-display-1  | PWD=/
configuration-server-display-1 exited with code 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output comes from the &lt;code&gt;display&lt;/code&gt; container defined in the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file, which runs the &lt;code&gt;env&lt;/code&gt; command to display its environment variables. The results show two variables from the &lt;code&gt;display.env&lt;/code&gt; file: &lt;code&gt;VAR_FROM_ENV_FILE_1&lt;/code&gt; with the value &lt;code&gt;HELLO&lt;/code&gt;, and &lt;code&gt;VAR_FROM_ENV_FILE_2&lt;/code&gt; with the value &lt;code&gt;WORLD&lt;/code&gt;. Additionally, the &lt;code&gt;ENV_FROM_COMPOSE_FILE&lt;/code&gt; variable defined directly in the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file has the value &lt;code&gt;HELLO&lt;/code&gt;. Finally, the &lt;code&gt;ENV_FROM_SHELL&lt;/code&gt; variable, set in the shell before running Docker Compose, appears with the value &lt;code&gt;WORLD&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this example, you set up a Docker Compose application using various methods for configuration, such as Docker Compose files, environment definition files, and shell-exported values. This flexibility allows you to deploy the same application across different platforms.&lt;/p&gt;

&lt;p&gt;Since Docker Compose handles multi-container setups, it's essential to define how these containers depend on each other. The next section will cover how to manage these interdependencies within Docker Compose applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Service Dependency
&lt;/h2&gt;

&lt;p&gt;Docker Compose is used to manage multi-container applications, which are defined in &lt;code&gt;docker-compose.yaml&lt;/code&gt; files. Even though the containers operate as separate microservices, it's common to have them depend on each other. &lt;/p&gt;

&lt;p&gt;For example, in a two-tier application with a PostgreSQL database and a Java backend, the backend needs the PostgreSQL service to be running before it can connect and function correctly. Docker Compose allows you to control the startup and shutdown order of these services, ensuring that dependencies are handled properly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
&lt;span class="na"&gt;  pre&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
&lt;span class="na"&gt;    depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - init&lt;/span&gt;
&lt;span class="na"&gt;  main&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
&lt;span class="na"&gt;    depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - pre&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, the &lt;strong&gt;main&lt;/strong&gt; container depends on the &lt;strong&gt;pre&lt;/strong&gt; container, and the &lt;strong&gt;pre&lt;/strong&gt; container depends on the &lt;strong&gt;init&lt;/strong&gt; container. Docker Compose will start the containers in the order of &lt;strong&gt;init&lt;/strong&gt;, &lt;strong&gt;pre&lt;/strong&gt; and &lt;strong&gt;main&lt;/strong&gt;. Also, the containers will be stopped in reverse order: &lt;strong&gt;main&lt;/strong&gt;, &lt;strong&gt;pre&lt;/strong&gt; and then &lt;strong&gt;init&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the following example, the order of containers will be used to fill a file and serve it with a web server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Service Dependency with Docker Compose
&lt;/h3&gt;

&lt;p&gt;In this exercise, you'll configure a Docker Compose application with four containers. The first three containers will run in sequence to generate a static file, which will then be served by the fourth container. This setup demonstrates how to manage and coordinate dependencies between services in Docker Compose.&lt;/p&gt;

&lt;p&gt;Create a folder named &lt;code&gt;service-dependency-example&lt;/code&gt; and navigate into it using the &lt;code&gt;cd&lt;/code&gt; command&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="nb"&gt;mkdir &lt;/span&gt;service-dependency-example
&lt;span class="nb"&gt;cd &lt;/span&gt;service-dependency-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create your &lt;code&gt;docker-compose.yaml&lt;/code&gt; similar to the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This is the main configuration file for Docker Compose&lt;/span&gt;
&lt;span class="c1"&gt;# It defines the services that are to be run, and how they should be configured.&lt;/span&gt;
&lt;span class="c1"&gt;# Each service is defined as a key-value pair, where the key is the name of the service,&lt;/span&gt;
&lt;span class="c1"&gt;# and the value is a dictionary of configuration options.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# The services section is where you define the services that you want to run.&lt;/span&gt;
&lt;span class="c1"&gt;# Each service is defined as a key-value pair, where the key is the name of the service,&lt;/span&gt;
&lt;span class="c1"&gt;# and the value is a dictionary of configuration options.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# The configuration options for a service can include things like the image to use,&lt;/span&gt;
&lt;span class="c1"&gt;# the command to run, the ports to expose, and the volumes to mount.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# In this file, we have defined four services:&lt;/span&gt;
&lt;span class="c1"&gt;# - clean: This service simply cleans out the static volume.&lt;/span&gt;
&lt;span class="c1"&gt;# - init: This service initializes the static volume with a simple HTML file.&lt;/span&gt;
&lt;span class="c1"&gt;# - pre: This service simply adds data to the HTML file&lt;/span&gt;
&lt;span class="c1"&gt;# - server: This service runs an Nginx server, which serves the content from the static volume.&lt;/span&gt;
&lt;span class="c1"&gt;#&lt;/span&gt;
&lt;span class="c1"&gt;# The volumes section is where you define the volumes that are to be used by the services.&lt;/span&gt;
&lt;span class="c1"&gt;# In this case, we have defined a single volume called "static", which is mounted by all three services.&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  clean&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
&lt;span class="na"&gt;    command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rm -rf /data/*&lt;/span&gt;
&lt;span class="na"&gt;    volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - static:/data&lt;/span&gt;
&lt;span class="s"&gt; &lt;/span&gt;
&lt;span class="na"&gt;  init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
&lt;span class="na"&gt;    command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sh&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-c&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;init&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/data/hello.html'"&lt;/span&gt;
&lt;span class="na"&gt;    volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - static:/data&lt;/span&gt;
&lt;span class="na"&gt;    depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - clean&lt;/span&gt;

&lt;span class="na"&gt;  pre&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
&lt;span class="na"&gt;    command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sh&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-c&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;'echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pre&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/data/hello.html'"&lt;/span&gt;
&lt;span class="na"&gt;    volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - static:/data&lt;/span&gt;
&lt;span class="na"&gt;    depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - init&lt;/span&gt;

&lt;span class="na"&gt;  server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;span class="na"&gt;    volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - static:/usr/share/nginx/html&lt;/span&gt;
&lt;span class="na"&gt;    ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - "8080:80"&lt;/span&gt;
&lt;span class="na"&gt;    depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - pre&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  static&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Docker Compose file defines four services and a single volume named &lt;code&gt;static&lt;/code&gt;. The volume is shared among all services. The &lt;code&gt;clean&lt;/code&gt; service starts by removing the &lt;code&gt;index.html&lt;/code&gt; file from the volume. The &lt;code&gt;init&lt;/code&gt; service then creates and populates &lt;code&gt;index.html&lt;/code&gt;. Afterward, the &lt;code&gt;pre&lt;/code&gt; service appends an additional line to &lt;code&gt;index.html&lt;/code&gt;. Finally, the &lt;code&gt;server&lt;/code&gt; service serves the content from the &lt;code&gt;static&lt;/code&gt; volume.&lt;/p&gt;




&lt;p&gt;Start the application with the &lt;code&gt;docker-compose up&lt;/code&gt; command. The output should look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Container service-dependency-clean-1  Created
 Container service-dependency-init-1  Recreate
 Container service-dependency-init-1  Recreated
 Container service-dependency-pre-1  Recreate
 Container service-dependency-pre-1  Recreated
 Container service-dependency-server-1  Recreate
 Container service-dependency-server-1  Recreated
Attaching to service-dependency-clean-1, service-dependency-init-1, service-dependency-pre-1, service-dependency-server-1
service-dependency-clean-1 exited with code 0
service-dependency-init-1 exited with code 0
service-dependency-pre-1 exited with code 0
service-dependency-server-1  | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
service-dependency-server-1  | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
service-dependency-server-1  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
service-dependency-server-1  | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
service-dependency-server-1  | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
service-dependency-server-1  | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
service-dependency-server-1  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
service-dependency-server-1  | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
service-dependency-server-1  | /docker-entrypoint.sh: Configuration complete; ready for start up
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: using the "epoll" event method
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: nginx/1.27.0
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: OS: Linux 5.10.102.1-microsoft-standard-WSL2
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker processes
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 29
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 30
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 31
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 32
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 33
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 34
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 35
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 36
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 37
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 38
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 39
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 40
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 41
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 42
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 43
service-dependency-server-1  | 2024/08/13 06:53:43 [notice] 1#1: start worker process 44
service-dependency-server-1  | 172.21.0.1 - - [13/Aug/2024:06:53:48 +0000] "GET / HTTP/1.1" 200 31 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" "-"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output shows that Docker Compose creates the containers in the order of &lt;strong&gt;clean&lt;/strong&gt;, &lt;strong&gt;init&lt;/strong&gt;, &lt;strong&gt;pre&lt;/strong&gt; and &lt;strong&gt;server&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;Open &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; in the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fskc0fzbgl2m4197ysnze.png" class="article-body-image-wrapper"&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%2Farticles%2Fskc0fzbgl2m4197ysnze.png" alt="Image description" width="578" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output shows that the &lt;strong&gt;clean&lt;/strong&gt;, &lt;strong&gt;init&lt;/strong&gt; and &lt;strong&gt;pre&lt;/strong&gt; containers work in the expected order.&lt;/p&gt;




&lt;p&gt;Return to your terminal and hit Ctrl + C to close the application &lt;em&gt;gracefully&lt;/em&gt;. You will notice that in the logs, that the containers stop in the reverse order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gracefully stopping... (press Ctrl+C again to force)
Aborting on container exit...
 Container service-dependency-server-1  Stopping
 Container service-dependency-server-1  Stopped
 Container service-dependency-pre-1  Stopping
 Container service-dependency-pre-1  Stopped
 Container service-dependency-init-1  Stopping
 Container service-dependency-init-1  Stopped
 Container service-dependency-clean-1  Stopping
 Container service-dependency-clean-1  Stopped
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we set up a Docker Compose application with interdependent services to demonstrate how Docker Compose manages the startup and operation of containers in a specific order. This feature is crucial for building and orchestrating complex multi-container applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this post, we explored the use of environment variables to manage configurations in cloud-native applications, specifically within Docker Compose. We demonstrated how to configure applications using environment variables defined in Docker Compose files, shell environments, and &lt;code&gt;.env&lt;/code&gt; files, allowing for flexible and adaptable deployments across different environments like staging, testing, and production.&lt;/p&gt;

&lt;p&gt;We also delved into the management of service dependencies in Docker Compose, showing how to control the order in which containers start and stop. We created a Docker Compose application with four interdependent services that worked together to generate and serve a static file, highlighting the importance of managing dependencies in complex multi-container applications.&lt;/p&gt;

&lt;p&gt;And with that we covered how to compose environments using Docker Compose. In the next few posts we are going to delve into Docker Networking.&lt;/p&gt;

&lt;p&gt;See you next Monday!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Using Docker Compose to Build Environments</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Mon, 19 Aug 2024 05:23:42 +0000</pubDate>
      <link>https://dev.to/kalkwst/using-docker-compose-to-build-environments-4f3c</link>
      <guid>https://dev.to/kalkwst/using-docker-compose-to-build-environments-4f3c</guid>
      <description>&lt;p&gt;In our previous posts, we explored how Docker containers and Dockerfiles can be used to package applications neatly. But what happens when your applications become more complex, with multiple components and intricate configurations? Imagine building an online store, where you have separate microservices for the frontend, backend, payment processing, order management and analytics. Each of these microservices might be developed using different programming languages and technologies, and they all need to be built, packaged, and configured correctly.&lt;/p&gt;

&lt;p&gt;This is where Docker Compose comes to the rescue. It's a powerful tool specifically designed for managing applications that run in multiple Docker containers. You can define your entire application stack in a single YAML file, including each microservice, its configuration, and how they all interact with each other. With Docker Compose, you can spin up your entire complex application with just a single command, making it super convenient for development, testing, CI/CD pipelines, and even production environments.&lt;/p&gt;

&lt;p&gt;The essential features of Docker Compose can be grouped into three categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation:&lt;/strong&gt; Imagine running multiple instances of your application, each completely isolated from the others. Docker Compose makes this possible, allowing you to replicate your entire application stack on various environments like developer machines, CI servers, or shared hosts. This not only optimizes resource utilization but also simplifies management by reducing operational complexity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateful Data Management:&lt;/strong&gt; Say your application needs to store data on disk, like a database. Docker Compose takes care of managing the data volumes associated with your containers, ensuring that your data persists across different runs. This makes it much easier to work with applications that rely on persistent storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterative Design:&lt;/strong&gt; Docker Compose works with a clear configuration that defines all the containers in your application. You can easily add new containers to this configuration without disrupting existing ones. For example, if you have two containers running and decide to add a third, Docker Compose won't touch the first two. It will simply create and connect the new container, making it a breeze to expand your application iteratively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docker Compose's ability to isolate multiple instances of your application, manage stateful data, and support iterative development makes it a must-have tool for handling complex applications with multiple containers. In this chapter, we'll take a closer look at how Docker Compose can help you manage the entire lifecycle of your application, from setup to deployment.&lt;/p&gt;

&lt;p&gt;We'll start by diving deep into the Docker Compose command-line interface (CLI) and the structure of Compose files. Then, we'll explore different ways to configure your applications using Compose and how to define dependencies between different services within your application stack.&lt;/p&gt;

&lt;p&gt;Since Docker Compose is a core tool in the Docker ecosystem, gaining both technical knowledge and practical experience with it will be a valuable addition to your skillset. By the end of this chapter, you'll be well-equipped to handle even the most intricate multi-container applications with ease.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Compose CLI
&lt;/h2&gt;

&lt;p&gt;Docker Compose works hand-in-hand with Docker Engine to orchestrate multi-container applications. It uses a command-line tool called &lt;code&gt;docker-compose&lt;/code&gt; to communicate with the Engine. On Mac and Windows, this tool is conveniently bundled with Docker Desktop. However, if you're running a Linux system, you'll need to install &lt;code&gt;docker-compose&lt;/code&gt; separately after installing Docker Engine. The good news is that it's a single executable, making the installation quite straightforward.&lt;/p&gt;

&lt;p&gt;You can find instructions on installing the Compose Plugin &lt;a href="https://docs.docker.com/compose/install/#scenario-two-install-the-compose-plugin" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker Compose CLI Commands
&lt;/h3&gt;

&lt;p&gt;The docker-compose command can handle all aspects of an application's life cycle that use multiple containers. It is possible to start, stop, and restart services with the subcommands. You can also see what's going on with the running stacks and get their logs. The following posts  will give you hands-on examples with the most important commands. In the same way, the following command can be used to see a sample of all the features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you should see something like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Usage:  docker compose [OPTIONS] COMMAND

Define and run multi-container applications with Docker.

Options:
      --ansi string                Control when to print ANSI control
                                   characters ("never"|"always"|"auto")
                                   (default "auto")
      --compatibility              Run compose in backward compatibility mode
      --dry-run                    Execute command in dry run mode
      --env-file stringArray       Specify an alternate environment file.
  -f, --file stringArray           Compose configuration files
      --parallel int               Control max parallelism, -1 for
                                   unlimited (default -1)
      --profile stringArray        Specify a profile to enable
      --progress string            Set type of progress output (auto,
                                   tty, plain, quiet) (default "auto")
      --project-directory string   Specify an alternate working directory
                                   (default: the path of the, first
                                   specified, Compose file)
  -p, --project-name string        Project name

Commands:
  build       Build or rebuild services
  config      Parse, resolve and render compose file in canonical format
  cp          Copy files/folders between a service container and the local filesystem
  create      Creates containers for a service.
  down        Stop and remove containers, networks
  events      Receive real time events from containers.
  exec        Execute a command in a running container.
  images      List images used by the created containers
  kill        Force stop service containers.
  logs        View output from containers
  ls          List running compose projects
  pause       Pause services
  port        Print the public port for a port binding.
  ps          List containers
  pull        Pull service images
  push        Push service images
  restart     Restart service containers
  rm          Removes stopped service containers
  run         Run a one-off command on a service.
  scale       Scale services
  start       Start services
  stop        Stop services
  top         Display the running processes
  unpause     Unpause services
  up          Create and start containers
  version     Show the Docker Compose version information
  wait        Block until the first service container stops
  watch       Watch build context for service and rebuild/refresh containers when files are updated

Run 'docker compose COMMAND --help' for more information on a command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three key &lt;code&gt;docker-compose&lt;/code&gt; commands crucial for managing the lifecycle of applications. Here's a breakdown of these commands and how they fit into the overall lifecycle:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fu49oj2ev4og36tc4co3c.jpg" class="article-body-image-wrapper"&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%2Farticles%2Fu49oj2ev4og36tc4co3c.jpg" alt="Image description" width="800" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker-compose up&lt;/code&gt;: The &lt;code&gt;docker-compose up&lt;/code&gt; command initializes and starts the containers as defined in your configuration file. You can either build container images from scratch or use pre-built images available in a registry. For long-running services, like web servers, it’s often practical to run the containers in detached mode by using the &lt;code&gt;-d&lt;/code&gt; or &lt;code&gt;--detach&lt;/code&gt; flags. This allows the containers to run in the background, freeing up your terminal for other tasks. For a full list of options and flags available with this command, you can use &lt;code&gt;docker-compose up --help&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker-compose ps&lt;/code&gt;: The &lt;code&gt;docker-compose ps&lt;/code&gt; command provides a snapshot of the containers and their current status. This is particularly useful for diagnosing issues and performing health checks on your containers. For example, if you have a two-container setup with a backend and a frontend, you can use &lt;code&gt;docker-compose ps&lt;/code&gt; to see the status of each container. It helps you identify whether any of your containers are down, not responding to health checks, or have failed to start due to misconfiguration.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker-compose down&lt;/code&gt;: The &lt;code&gt;docker-compose down&lt;/code&gt; command stops and removes all the resources, including containers, networks, images and volumes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Docker Compose File
&lt;/h2&gt;

&lt;p&gt;The Docker Compose CLI manages and configures multi-container applications. Typically, these settings are saved in a file called &lt;code&gt;docker-compose.yml&lt;/code&gt;. Docker Compose is a powerful tool, but its efficacy is dependent on the quality of the configuration. As a result, understanding how to create and fine-tune these &lt;code&gt;docker-compose.yml&lt;/code&gt; files is critical, requiring careful attention to detail.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;docker-compose.yaml&lt;/code&gt; file consists of four main sections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Name
&lt;/h4&gt;

&lt;p&gt;Docker Compose specifies that the top-level name property will be used as the project name if you don't set one yourself. You can change this name, and if the top-level name element is not set, Docker Compose will set a default project name that will be used. Also note that the project &lt;code&gt;name&lt;/code&gt; is exposed for interpolation and we can use the &lt;code&gt;COMPOSE_PROJECT_NAME&lt;/code&gt; to access this &lt;code&gt;name&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;some-service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "I'm running ${COMPOSE_PROJECT_NAME}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Services
&lt;/h4&gt;

&lt;p&gt;A service is a general term for a computer resource in an app that can be changed or grown without affecting other parts of the app. A group of containers backs up the services. The platform runs the containers based on replication needs and placement restrictions. A Docker image and a set of runtime arguments describe a service because it is backed by a container. With these arguments, all containers in a service are made in the same way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8080:80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section of the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file defines a service named server, which is set up to run a Nginx web server. The image: &lt;code&gt;nginx:latest&lt;/code&gt; directive instructs Docker to utilize the latest version of the Nginx image from Docker Hub. The ports section maps port &lt;code&gt;8080&lt;/code&gt; on the host system to port &lt;code&gt;80&lt;/code&gt; within the container. This configuration allows you to reach the Nginx server via &lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt; on your host machine.&lt;/p&gt;

&lt;h4&gt;
  
  
  Networks
&lt;/h4&gt;

&lt;p&gt;Networks let services communicate with each other. By default Δοψκερ Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by the service's name. The top-level &lt;code&gt;networks&lt;/code&gt; element lets you configure named networks that can be reused across multiple services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example/webapp&lt;/span&gt;
&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;front-tier&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;back-tier&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This section of the &lt;code&gt;docker-compose.yaml&lt;/code&gt; file creates a service named frontend and uses the Docker image &lt;code&gt;example/webapp&lt;/code&gt;. This image is anticipated to include the application code and environment required to operate the frontend component of a web application. The networks section states that this service is connected to two networks: &lt;code&gt;front-tier&lt;/code&gt; and &lt;code&gt;back-tier&lt;/code&gt;. By connecting the frontend service to these networks, it can communicate with other services on the same networks, allowing for easier interaction across different components of a multi-container application.&lt;/p&gt;

&lt;h4&gt;
  
  
  Volumes
&lt;/h4&gt;

&lt;p&gt;Volumes in Docker are used to handle persistent data that must survive the lifecycle of a container. Volumes in Docker Compose provide a common way for services to mount and use persistent storage areas. You can construct named volumes that are reusable across several services in your application by specifying them at the top level in a &lt;code&gt;docker-compose.yaml&lt;/code&gt; file. This configuration not only helps to guarantee data consistency and integrity, but it also simplifies storage resource management by allowing volumes to be configured and allocated centrally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example/database&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db-data:/etc/data&lt;/span&gt;

  &lt;span class="na"&gt;backup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backup-service&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db-data:/var/lib/backup/data&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this &lt;code&gt;docker-compose.yaml&lt;/code&gt; configuration, the &lt;code&gt;volumes&lt;/code&gt; section defines a named volume called &lt;code&gt;db-data&lt;/code&gt;. This volume is then used by two different services: &lt;code&gt;backend&lt;/code&gt; and &lt;code&gt;backup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;backend&lt;/code&gt; service uses the &lt;code&gt;example/database&lt;/code&gt; image and mounts the &lt;code&gt;db-data&lt;/code&gt; volume to the &lt;code&gt;/etc/data&lt;/code&gt; directory inside the container. This setup allows the &lt;code&gt;backend&lt;/code&gt; service to store and persist data in the &lt;code&gt;db-data&lt;/code&gt; volume.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;backup&lt;/code&gt; service, which uses the &lt;code&gt;backup-service&lt;/code&gt; image, also mounts the same &lt;code&gt;db-data&lt;/code&gt; volume but maps it to a different directory inside its container, &lt;code&gt;/var/lib/backup/data&lt;/code&gt;. This configuration enables the &lt;code&gt;backup&lt;/code&gt; service to access and potentially back up the data stored by the &lt;code&gt;backend&lt;/code&gt; service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Web Server with Docker Compose
&lt;/h2&gt;

&lt;p&gt;Web servers running within containers frequently require various initial operations before they can begin serving content. These tasks could include setting up configurations, downloading files, or installing dependencies. Docker Compose makes this process easier by allowing you to specify all of these operations as part of a multi-container application configuration. In this exercise, you will build a preparation container specifically for creating static files such as &lt;code&gt;index.html&lt;/code&gt;. Following that, a server container will be configured to serve the static files. With the proper network configuration, this server will be accessible from your host system. You'll also manage the full application lifecycle with various Docker Compose commands, which will make it easier to set up and run your containers.&lt;/p&gt;

&lt;p&gt;Create a folder named &lt;code&gt;compose-server&lt;/code&gt; and navigate to it:&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="nb"&gt;mkdir &lt;/span&gt;compose-server
&lt;span class="nb"&gt;cd &lt;/span&gt;compose-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create a folder named &lt;code&gt;init&lt;/code&gt; and navigate to it:&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="nb"&gt;mkdir &lt;/span&gt;init
&lt;span class="nb"&gt;cd &lt;/span&gt;init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create a bash script to generate a simple HTML page&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="c"&gt;#!/usr/bin/env sh&lt;/span&gt;

&lt;span class="c"&gt;# Check if a directory named 'data' exists&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"data"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# If it does, delete it and all its contents&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing existing 'data' directory..."&lt;/span&gt;
    &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; data
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Create a new directory named 'data'&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating a new 'data' directory..."&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;data

&lt;span class="c"&gt;# Create a new file named 'index.html' inside the 'data' directory&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating a new file 'index.html' inside the 'data' directory..."&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;data/index.html

&lt;span class="c"&gt;# Append HTML content to the 'index.html' file&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Appending HTML content to the 'index.html' file..."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;h1&amp;gt;Welcome to Docker Compose! &amp;lt;/h1&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; data/index.html
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;img src='https://www.docker.com/wp-content/uploads/2021/10/Moby-logo-sm.png' /&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; data/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create a &lt;strong&gt;Dockerfile&lt;/strong&gt; with the name &lt;code&gt;Dockerfile&lt;/code&gt; and the following content:&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="c"&gt;# Use the busybox base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; busybox&lt;/span&gt;

&lt;span class="c"&gt;# Copy the prepare.sh script to /usr/bin/prepare.sh in the image&lt;/span&gt;
&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; prepare.sh /usr/bin/prepare.sh&lt;/span&gt;

&lt;span class="c"&gt;# Make the prepare.sh script executable&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/bin/prepare.sh

&lt;span class="c"&gt;# Set the entry point for the container to be the prepare.sh script&lt;/span&gt;
&lt;span class="c"&gt;# This means that when the container starts, it will run the prepare.sh script&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["sh", "/usr/bin/prepare.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile starts by using the &lt;code&gt;busybox&lt;/code&gt; base image, which is a minimal and lightweight Linux distribution commonly used for small utilities and embedded systems. It then adds a script named &lt;code&gt;prepare.sh&lt;/code&gt; from the build context to the &lt;code&gt;/usr/bin/prepare.sh&lt;/code&gt; path within the image. To ensure that this script can be executed, the Dockerfile uses the &lt;code&gt;RUN&lt;/code&gt; command to modify its permissions, making it executable with &lt;code&gt;chmod +x&lt;/code&gt;. Finally, the &lt;code&gt;ENTRYPOINT&lt;/code&gt; instruction is specified to define the default command that will be run when the container starts. In this case, it sets the entry point to execute the &lt;code&gt;prepare.sh&lt;/code&gt; script using the &lt;code&gt;sh&lt;/code&gt; shell. This setup means that every time the container is launched, it will automatically run &lt;code&gt;prepare.sh&lt;/code&gt;, allowing for pre-defined setup or configuration tasks to be carried out as the container starts.&lt;/p&gt;




&lt;p&gt;Change the directory to the parent folder with the &lt;code&gt;cd ..&lt;/code&gt; command and create a &lt;code&gt;docker-compose.yaml&lt;/code&gt; file with the following content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;compose-server"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;      context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./init&lt;/span&gt;
&lt;span class="na"&gt;    volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - static:/data&lt;/span&gt;

&lt;span class="na"&gt;  server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;    image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
&lt;span class="na"&gt;    volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - static:/usr/share/nginx/html&lt;/span&gt;
&lt;span class="na"&gt;    ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="s"&gt;      - "8080:80"&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="na"&gt;  static&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Docker Compose file sets up a multi-container application with two services: &lt;code&gt;init&lt;/code&gt; and &lt;code&gt;server&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Service Definitions:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;init&lt;/code&gt;:&lt;/strong&gt; This service builds an image from a Dockerfile located in the &lt;code&gt;./init&lt;/code&gt; directory. The Dockerfile in this context likely contains instructions to prepare or generate static files. The &lt;code&gt;volumes&lt;/code&gt; section maps a named volume, &lt;code&gt;static&lt;/code&gt;, to the &lt;code&gt;/data&lt;/code&gt; directory inside the container. This allows the &lt;code&gt;init&lt;/code&gt; service to write data to this volume, which can then be accessed by other services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;server&lt;/code&gt;:&lt;/strong&gt; This service uses the official &lt;code&gt;nginx&lt;/code&gt; image to run an NGINX web server. It also mounts the same named volume, &lt;code&gt;static&lt;/code&gt;, but this time to the &lt;code&gt;/usr/share/nginx/html&lt;/code&gt; directory inside the container. This is where NGINX expects to find static files to serve. The &lt;code&gt;ports&lt;/code&gt; section maps port 80 of the container to port 8080 on the host machine, making the web server accessible via &lt;code&gt;http://localhost:8080&lt;/code&gt; on the host.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volumes:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;volumes&lt;/code&gt; section at the bottom defines a named volume called &lt;code&gt;static&lt;/code&gt;. Named volumes are managed by Docker and provide persistent storage that is shared among containers. In this case, the &lt;code&gt;static&lt;/code&gt; volume is used to transfer static files generated by the &lt;code&gt;init&lt;/code&gt; service to the &lt;code&gt;server&lt;/code&gt; service.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Start the application with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;--detach&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you should see an output similar to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[+] Running 8/8
 ✔ server 7 layers [⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled                                                               41.0s
   ✔ efc2b5ad9eec Pull complete                                                                                   32.9s
   ✔ 8fe9a55eb80f Pull complete                                                                                   29.3s
   ✔ 045037a63be8 Pull complete                                                                                    0.9s
   ✔ 7111b42b4bfa Pull complete                                                                                    1.6s
   ✔ 3dfc528a4df9 Pull complete                                                                                    3.0s
   ✔ 9e891cdb453b Pull complete                                                                                    5.3s
   ✔ 0f11e17345c5 Pull complete                                                                                    6.2s
[+] Building 5.3s (9/9) FINISHED                                                                         docker:default
 =&amp;gt; [init internal] load build definition from Dockerfile                                                          0.1s
 =&amp;gt; =&amp;gt; transferring dockerfile: 457B                                                                               0.0s
 =&amp;gt; [init internal] load .dockerignore                                                                             0.1s
 =&amp;gt; =&amp;gt; transferring context: 2B                                                                                    0.0s
 =&amp;gt; [init internal] load metadata for docker.io/library/busybox:latest                                             4.5s
 =&amp;gt; [init auth] library/busybox:pull token for registry-1.docker.io                                                0.0s
 =&amp;gt; [init internal] load build context                                                                             0.0s
 =&amp;gt; =&amp;gt; transferring context: 771B                                                                                  0.0s
 =&amp;gt; CACHED [init 1/3] FROM docker.io/library/busybox@sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85  0.0s
 =&amp;gt; =&amp;gt; resolve docker.io/library/busybox@sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7   0.0s
 =&amp;gt; [init 2/3] ADD prepare.sh /usr/bin/prepare.sh                                                                  0.0s
 =&amp;gt; [init 3/3] RUN chmod +x /usr/bin/prepare.sh                                                                    0.5s
 =&amp;gt; [init] exporting to image                                                                                      0.1s
 =&amp;gt; =&amp;gt; exporting layers                                                                                            0.0s
 =&amp;gt; =&amp;gt; writing image sha256:8d2660840cab8d1e01e7cb9ad823fb2a2e3141b73c6cc0cce53c91355a6d52a6                       0.0s
 =&amp;gt; =&amp;gt; naming to docker.io/library/compose-server-init                                                             0.0s
[+] Running 4/4
 ✔ Network compose-server_default     Created                                                                      0.3s
 ✔ Volume "compose-server_static"     Created                                                                      0.0s
 ✔ Container compose-server-init-1    Started                                                                      0.1s
 ✔ Container compose-server-server-1  Started                                                                      0.1s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates and starts the containers in &lt;strong&gt;detached&lt;/strong&gt; mode. It starts by creating the &lt;code&gt;compose-server_default&lt;/code&gt; network and the &lt;code&gt;compose-server_static&lt;/code&gt; volume. It then builds the &lt;code&gt;init&lt;/code&gt; container using the &lt;strong&gt;Dockerfile&lt;/strong&gt; from the previous step, downloads &lt;code&gt;nginx&lt;/code&gt;, and starts the containers.&lt;/p&gt;




&lt;p&gt;Check the status of the application with the &lt;code&gt;docker-compose ps&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you should see the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME                      IMAGE     COMMAND                                          SERVICE   CREATED         STATUS         PORTS
compose-server-server-1   nginx     "/docker-entrypoint.sh nginx -g 'daemon off;'"   server    3 minutes ago   Up 3 minutes   0.0.0.0:8080-&amp;gt;80/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Open the &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080/&lt;/a&gt; on your browser. If everything went as planned you should see the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fi3oa1qniirai4aig6782.png" class="article-body-image-wrapper"&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%2Farticles%2Fi3oa1qniirai4aig6782.png" alt="Image description" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Stop and remove all the resources with the following command if you do not need the application up and running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you should see the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[+] Running 3/3
 ✔ Container compose-server-server-1  Removed                                                                      0.6s
 ✔ Container compose-server-init-1    Removed                                                                      0.0s
 ✔ Network compose-server_default     Removed                                                                      0.3s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this exercise, we successfully created and configured a multi-container application using Docker Compose. The &lt;code&gt;docker-compose.yaml&lt;/code&gt; file was employed to define both networking and volume options, which are crucial for inter-container communication and data persistence. We demonstrated how to use Docker Compose CLI commands to build and manage the application. These commands included starting and stopping containers, checking their status, and removing them when no longer needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This blog post highlighted Docker Compose's ability to manage and orchestrate multi-container applications effectively. Building on our earlier explorations of Docker containers and Dockerfiles, we looked at how Docker Compose simplifies the setup and administration of complicated applications with various components. Docker Compose, which defines your whole application stack in a single YAML file, enables you to spin up complex environments with a single command, making it ideal for development, testing, and production.  &lt;/p&gt;

&lt;p&gt;We've highlighted Docker Compose's major capabilities, such as its ability to separate container instances, handle stateful data using named volumes, and facilitate iterative development. The practical examples offered demonstrated how to effectively develop, manage, and troubleshoot applications using Docker Compose CLI commands. We also went over the key components of a docker-compose.yaml file, like service definitions, network setups, and volume management.  &lt;/p&gt;

&lt;p&gt;Looking ahead, our next post will go over the configuration choices accessible in the Docker Compose environment. We'll go over advanced settings and approaches for further optimizing and tailoring your application installations, ensuring that you can fully utilize Docker Compose's capabilities to meet your individual requirements.&lt;/p&gt;

&lt;p&gt;See you next Monday!&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Multi-Stage Dockerfiles</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Mon, 12 Aug 2024 06:00:00 +0000</pubDate>
      <link>https://dev.to/kalkwst/multi-stage-dockerfiles-3e90</link>
      <guid>https://dev.to/kalkwst/multi-stage-dockerfiles-3e90</guid>
      <description>&lt;p&gt;In the previous posts, we explored Docker registries, covering both private and public options. We set up our private Docker registry to manage and store our Docker images securely. Additionally, we covered configuring access and storing Docker images on Docker Hub. Now, in this next chapter, we'll delve into the concept of multi-stage Dockerfiles. This technique allows us to streamline Docker image builds by using multiple stages to optimize build efficiency and reduce image size.&lt;/p&gt;

&lt;p&gt;Multi-stage Dockerfiles, introduced in Docker version 17.05, are designed to enhance Docker image optimization for production environments by minimizing image size. This approach involves creating multiple intermediate Docker images during the build process. Each stage focuses on specific tasks and selectively copies essential artifacts to subsequent stages, thereby streamlining the final image composition.&lt;/p&gt;

&lt;p&gt;Before the advent of multi-stage builds, the builder pattern was employed to achieve similar optimization goals. However, this method required two Dockerfiles and a shell script, adding complexity to the build process. &lt;/p&gt;

&lt;p&gt;We'll begin this post by examining traditional Docker builds and the challenges they pose. Then, we'll explore the builder pattern's implementation for optimizing image size, highlighting its limitations and complexities. Finally, we'll delve into multi-stage Dockerfiles, demonstrating how they effectively address these issues, offering a more streamlined approach to Docker image optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Normal Docker Builds
&lt;/h2&gt;

&lt;p&gt;We can use &lt;strong&gt;Dockerfiles&lt;/strong&gt; to create our own Docker images. As we discussed in the previous posts, a &lt;strong&gt;Dockerfile&lt;/strong&gt; is a text file that instructs docker on how to make an image. But when we run these Docker images in production environments. it is important to have images that are as small as possible. &lt;/p&gt;

&lt;p&gt;Consider an example where we build a simple Java application. We are going to deploy a &lt;code&gt;HelloWorld&lt;/code&gt; application written in Java using the following Dockerfile:&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="c"&gt;# Use the official openjdk image as a base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:latest&lt;/span&gt;

&lt;span class="c"&gt;# Set the working directory in the Docker container&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy the HelloWorld.java file into the Docker container&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; HelloWorld.java .&lt;/span&gt;

&lt;span class="c"&gt;# Compile the HelloWorld.java file&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;javac HelloWorld.java

&lt;span class="c"&gt;# Specify the command to run when the Docker container starts&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["java", "HelloWorld"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile is designed to build and run a Java application inside a Docker container. It starts by specifying the base image as &lt;code&gt;openjdk:latest&lt;/code&gt;, which provides an environment with OpenJDK 11 installed. The &lt;code&gt;WORKDIR /app&lt;/code&gt; instruction sets the working directory inside the container to &lt;code&gt;/app&lt;/code&gt;, ensuring all subsequent commands are executed relative to this directory.&lt;/p&gt;

&lt;p&gt;Next, the &lt;code&gt;COPY HelloWorld.java&lt;/code&gt; command copies the &lt;code&gt;HelloWorld.java&lt;/code&gt; file from the host machine into the &lt;code&gt;/app&lt;/code&gt; directory of the Docker container. This file contains the source code for the Java program.&lt;/p&gt;

&lt;p&gt;Following that, the &lt;code&gt;RUN javac HelloWorld.java&lt;/code&gt; command compiles the &lt;code&gt;HelloWorld.java&lt;/code&gt; file within the Docker container using the Java compiler (&lt;code&gt;javac&lt;/code&gt;). This step generates the corresponding bytecode (&lt;code&gt;HelloWorld.class&lt;/code&gt;) necessary to run the Java application.&lt;/p&gt;

&lt;p&gt;Lastly, the &lt;code&gt;CMD ["java", "HelloWorld"]&lt;/code&gt; instruction specifies the command that should be executed when the Docker container starts. Here, it runs the Java Virtual Machine (&lt;code&gt;java&lt;/code&gt;) with &lt;code&gt;HelloWorld&lt;/code&gt; as the main class. This runs the compiled Java program, printing "Hello, World!" or any other specified output defined within &lt;code&gt;HelloWorld.java&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The following is the content of the &lt;code&gt;HelloWorld.java&lt;/code&gt; file. This is a simple file that will print the text &lt;strong&gt;"Hello World!"&lt;/strong&gt; when executed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the Dockerfile is ready, we can build the Docker image using the &lt;code&gt;docker image build&lt;/code&gt; command. This image will be tagged as &lt;code&gt;helloworld:v1&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image build &lt;span class="nt"&gt;-t&lt;/span&gt; helloworld:v1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, observe the built image with the &lt;code&gt;docker image ls&lt;/code&gt;. You will get an output similar to the following&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
helloworld                                      v1        3dbb42c8d45c   2 days ago     470MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a look at the image size—a whopping 470MB! That's a huge Docker image for a production environment. Not only will it hog bandwidth and time when you're pushing and pulling it across networks, but it's also a potential security risk. Why? Because it likely contains a bunch of build tools that could be vulnerable to attacks.&lt;/p&gt;

&lt;p&gt;To make your image leaner and meaner, the best practice is to strip it down to the bare essentials: the compiled code and the runtime environment it needs to function. Think of it this way: you need a hammer to build a house, but once it's built, you don't need to carry the hammer around everywhere you go.&lt;/p&gt;

&lt;p&gt;Take Java, for instance. The Java compiler is essential for building the application, but it's unnecessary baggage once the app is up and running. Ideally, you want a minimal Docker image that only contains the Java runtime and nothing else. This reduces the attack surface, making it harder for malicious actors to exploit vulnerabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Builder Pattern
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;builder pattern&lt;/strong&gt; (which is different from the &lt;a href="https://dev.to/kalkwst/the-builder-pattern-in-c-5bcc"&gt;builder design pattern&lt;/a&gt;) is a clever technique for creating Docker images that are as small as possible. It involves two Docker images working together to selectively copy only the essential parts of your application from one image to the other.&lt;/p&gt;

&lt;p&gt;The first image is called the &lt;strong&gt;build image&lt;/strong&gt;. It's like your workshop, where you have all the tools you need to build your application from the source code. This includes things like compilers, build tools, and any libraries or dependencies your application needs during the build process.&lt;/p&gt;

&lt;p&gt;The second image, aptly named the &lt;strong&gt;runtime image&lt;/strong&gt;, is where the magic happens. It's the environment where your compiled application actually runs. This image is a minimalist, containing only the essential components: your executable files, any necessary runtime dependencies, and the runtime tools needed to execute your code.&lt;/p&gt;

&lt;p&gt;To get these essentials from the build image to the runtime image, a shell script comes into play. It uses the &lt;code&gt;docker container cp&lt;/code&gt; command to selectively copy only the required files, leaving behind the bulky build tools and unnecessary dependencies.&lt;/p&gt;

&lt;p&gt;The entire process of building the image using the builder pattern consists of the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the &lt;strong&gt;Build&lt;/strong&gt; Docker image.&lt;/li&gt;
&lt;li&gt;Create a container from the &lt;strong&gt;Build&lt;/strong&gt; Docker image.&lt;/li&gt;
&lt;li&gt;Copy the artifacts from the &lt;strong&gt;Build&lt;/strong&gt; Docker image to the local filesystem.&lt;/li&gt;
&lt;li&gt;Build the &lt;strong&gt;Runtime&lt;/strong&gt; Docker image using copied artifacts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fhb7n3vcsmioqrzjdnx9z.png" class="article-body-image-wrapper"&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%2Farticles%2Fhb7n3vcsmioqrzjdnx9z.png" alt="Image description" width="800" height="732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram clearly demonstrates the builder pattern workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build Container&lt;/strong&gt;: The &lt;em&gt;Build Dockerfile&lt;/em&gt; is responsible for creating the build container. This container comes packed with all the tools needed to compile your source code, like compilers, build tools, and development dependencies. Think it as a well-equipped workshop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shell Script Action&lt;/strong&gt;: Once the &lt;em&gt;Build Container&lt;/em&gt; is up and running, a handy shell script takes charge. It uses the &lt;code&gt;docker container cp&lt;/code&gt; command to carefully transfer only the necessary executable files from the build container to the Docker host (a.k.a., your local machine).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime Container&lt;/strong&gt;: Finally, the &lt;em&gt;Runtime Dockerfile&lt;/em&gt; comes into play. This Dockerfile is specifically designed to create a lean, mean runtime container. It takes the executable files copied over from the build container and packages them together with any essential runtime dependencies. This lightweight runtime container is optimized for deployment end efficient execution of your application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This two-step process allows us to keep the final Docker image small and secure by leaving behind the bulky build tools and unnecessary dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a Docker Image with the Build Pattern
&lt;/h3&gt;

&lt;p&gt;In this example, we are going to optimize our Docker image using the builder pattern&lt;/p&gt;

&lt;p&gt;Create a new directory named &lt;code&gt;builder-pattern&lt;/code&gt; for this example&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="nb"&gt;mkdir &lt;/span&gt;builder-pattern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Navigate to the newly created &lt;code&gt;builder-pattern&lt;/code&gt; directory&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="nb"&gt;cd &lt;/span&gt;builder-pattern
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Within the &lt;code&gt;builder-pattern&lt;/code&gt; directory, create a file named &lt;code&gt;Welcome.java&lt;/code&gt;. This file will be copied to the Docker image at build time&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code Welcome.java
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Add the following content to the &lt;code&gt;Welcome.java&lt;/code&gt; file, and then save and exit this file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Welcome&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; 
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello From Docker!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple &lt;code&gt;Hello World&lt;/code&gt; application written in Java. This will output &lt;strong&gt;"Hello From Docker!"&lt;/strong&gt; once executed.&lt;/p&gt;




&lt;p&gt;Within the &lt;code&gt;builder-pattern&lt;/code&gt; directory create a file named &lt;code&gt;Dockerfile.build&lt;/code&gt;. This file will contain all the instructions that you are going to use to create the &lt;code&gt;build&lt;/code&gt; Docker image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code Dockerfile.build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Add the following content to the &lt;code&gt;Dockerfile.build&lt;/code&gt; file and save the file&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; openjdk:latest&lt;/span&gt;

&lt;span class="c"&gt;# Set working directory inside the builder image&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy the Java source file into the builder image&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; HelloWorld.java /app&lt;/span&gt;

&lt;span class="c"&gt;# Compile the Java source file&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;javac HelloWorld.java

&lt;span class="c"&gt;# Create the JAR file&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;jar cvf HelloWorld.jar HelloWorld.class
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It starts by selecting the &lt;code&gt;openjdk:latest&lt;/code&gt; image from Docker Hub, which provides the latest version of OpenJDK, an open-source implementation of the Java Platform, Standard Edition. This choice ensures that the Docker environment includes all necessary tools and libraries to compile and run Java applications.&lt;/p&gt;

&lt;p&gt;Next, the working directory inside the Docker container is set to &lt;code&gt;/app&lt;/code&gt; using the &lt;code&gt;WORKDIR /app&lt;/code&gt; command. This directory will serve as the context for all subsequent commands in the Dockerfile, simplifying file operations and ensuring that files are copied and created in the correct location.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;COPY HelloWorld.java /app&lt;/code&gt; command copies the &lt;code&gt;HelloWorld.java&lt;/code&gt; source file from the host machine into the &lt;code&gt;/app&lt;/code&gt; directory of the Docker container. This file contains the Java source code for the application that we want to compile and package.&lt;/p&gt;

&lt;p&gt;To compile the Java source code into bytecode, the Dockerfile uses the &lt;code&gt;RUN javac HelloWorld.java&lt;/code&gt; command. This command executes the Java compiler (&lt;code&gt;javac&lt;/code&gt;) inside the Docker container, transforming the human-readable Java source code (&lt;code&gt;HelloWorld.java&lt;/code&gt;) into machine-readable bytecode (&lt;code&gt;HelloWorld.class&lt;/code&gt;). The resulting &lt;code&gt;.class&lt;/code&gt; file contains the compiled Java application ready for execution.&lt;/p&gt;

&lt;p&gt;Finally, the Dockerfile creates a JAR file (&lt;code&gt;HelloWorld.jar&lt;/code&gt;) from the compiled &lt;code&gt;HelloWorld.class&lt;/code&gt; file using the &lt;code&gt;RUN jar cvf HelloWorld.jar HelloWorld.class&lt;/code&gt; command. Here, the &lt;code&gt;jar&lt;/code&gt; command is invoked with options &lt;code&gt;cvf&lt;/code&gt;, where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;c&lt;/code&gt;: create a new JAR file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;v&lt;/code&gt;: generate verbose output (optional but helpful for debugging)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f&lt;/code&gt;: specify the filename for the JAR file (&lt;code&gt;HelloWorld.jar&lt;/code&gt;) The command packages the compiled Java application into a standalone JAR file, encapsulating all necessary resources and dependencies for the application.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Next, create the &lt;code&gt;Dockerfile&lt;/code&gt; for the runtime container. Within the &lt;code&gt;builder-pattern&lt;/code&gt; directory, create a file named &lt;code&gt;Dockerfile&lt;/code&gt;. This file will contain all the instructions that we are going to use to create the runtime Docker image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Add the following content to the &lt;code&gt;Dockerfile&lt;/code&gt; and save it&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; openjdk:11-jdk-slim&lt;/span&gt;

&lt;span class="c"&gt;# Set working directory inside the builder image&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy the Java source file&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; HelloWorld.jar /app/HelloWorld.jar&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "HelloWorld.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile is designed to run a Java application packaged as a JAR file within a Docker container. It begins by selecting the &lt;code&gt;openjdk:11-jdk-slim&lt;/code&gt; image from Docker Hub. This image provides a minimalistic Java Development Kit (JDK) environment based on OpenJDK 11, optimized for smaller container size and efficient resource usage.&lt;/p&gt;

&lt;p&gt;The working directory inside the Docker container is set to &lt;code&gt;/app&lt;/code&gt; using the &lt;code&gt;WORKDIR /app&lt;/code&gt; command. This directory will serve as the context for all subsequent commands in the Dockerfile, ensuring that files are copied and executed from the correct location.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;COPY HelloWorld.jar /app/HelloWorld.jar&lt;/code&gt; command copies the &lt;code&gt;HelloWorld.jar&lt;/code&gt; file from the host machine into the &lt;code&gt;/app&lt;/code&gt; directory of the Docker container. This JAR file is assumed to contain the compiled Java application (&lt;code&gt;HelloWorld.class&lt;/code&gt; and its dependencies) that we want to run.&lt;/p&gt;

&lt;p&gt;The Dockerfile concludes with the &lt;code&gt;CMD ["java", "-jar", "HelloWorld.jar"]&lt;/code&gt; command. This command specifies the default command to run when the Docker container starts. Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;java&lt;/code&gt; is the command to execute the Java Virtual Machine (JVM).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-jar&lt;/code&gt; indicates that the next argument (&lt;code&gt;HelloWorld.jar&lt;/code&gt;) is the path to the JAR file that contains the Java application to be executed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the Docker container is launched, it will execute the specified &lt;code&gt;java -jar HelloWorld.jar&lt;/code&gt; command, which starts the JVM and runs the Java application encapsulated within the &lt;code&gt;HelloWorld.jar&lt;/code&gt; file. &lt;/p&gt;




&lt;p&gt;Now, let's create a shell script to copy the executables between Docker containers. Within the &lt;code&gt;builder-pattern&lt;/code&gt; directory, create a file named &lt;code&gt;build.sh&lt;/code&gt;. This file will contain the steps to coordinate the build process between the two Docker containers&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Add the following content to the shell script and save the file&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="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-x&lt;/span&gt;

&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"welcomeapp"&lt;/span&gt;
&lt;span class="nv"&gt;BUILDER_IMAGE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;&lt;span class="s2"&gt;"-builder"&lt;/span&gt;
&lt;span class="nv"&gt;IMAGE_TAG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"latest"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Building &lt;/span&gt;&lt;span class="nv"&gt;$BUILDER_IMAGE_NAME&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Clean up the existing build&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; target

&lt;span class="c"&gt;# Build the builder image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$BUILDER_IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.build &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Failed to build &lt;/span&gt;&lt;span class="nv"&gt;$BUILDER_IMAGE_NAME&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Create the container&lt;/span&gt;
docker create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$BUILDER_IMAGE_NAME&lt;/span&gt; &lt;span class="nv"&gt;$BUILDER_IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Failed to create &lt;/span&gt;&lt;span class="nv"&gt;$BUILDER_IMAGE_NAME&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Copy the build output into the Docker host&lt;/span&gt;
docker &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$BUILDER_IMAGE_NAME&lt;/span&gt;:/app/HelloWorld.jar ./HelloWorld.jar

&lt;span class="c"&gt;# Clean up the container&lt;/span&gt;
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nv"&gt;$BUILDER_IMAGE_NAME&lt;/span&gt;
docker rmi &lt;span class="nv"&gt;$BUILDER_IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt;

&lt;span class="c"&gt;# Build the actual image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nv"&gt;$IMAGE_NAME&lt;/span&gt;:&lt;span class="nv"&gt;$IMAGE_TAG&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script automates the creation and deployment of a Dockerized application named "welcomeapp." Here's how it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Setup:&lt;/strong&gt; It defines names and tags for the Docker images involved: one for building (&lt;code&gt;BUILDER_IMAGE_NAME&lt;/code&gt;) and one for the final application (&lt;code&gt;IMAGE_NAME&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Builder Image Creation:&lt;/strong&gt; It builds a builder image using a specific Dockerfile (&lt;code&gt;Dockerfile.build&lt;/code&gt;), presumably containing instructions for compiling the application. It cleans up any previous build artifacts before starting and checks for build errors, stopping if any occur.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifact Extraction:&lt;/strong&gt; A temporary Docker container is created from the builder image. This container is used to copy the compiled application files (from &lt;code&gt;/app/&lt;/code&gt;) out of the container and into the host system's file system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleanup:&lt;/strong&gt; The script tidies up by removing the temporary container and deleting the builder image, as they're no longer needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Image Creation:&lt;/strong&gt; Finally, it builds the actual application image (&lt;code&gt;IMAGE_NAME&lt;/code&gt;) using a different Dockerfile, likely incorporating the extracted application files. This image is ready for deployment and running the "welcomeapp."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a nutshell, this script simplifies the whole process by dividing the build into two steps (builder and application), ensuring a clean and efficient way to package and deploy the "welcomeapp" in a Docker container.&lt;/p&gt;




&lt;p&gt;Add execution permissions to the &lt;code&gt;build.sh&lt;/code&gt; shell script&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="nb"&gt;chmod&lt;/span&gt; +x build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Now that you have the two Dockerfiles and the shell script, build the Docker image by executing the &lt;code&gt;build.sh&lt;/code&gt; shell script&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;++ IMAGE_NAME=helloapp
++ BUILDER_IMAGE_NAME=helloapp-builder
++ IMAGE_TAG=latest
++ echo 'Building helloapp-builder:latest'
Building helloapp-builder:latest
++ rm -rf target
++ docker build -t helloapp-builder:latest -f Dockerfile.build .
#0 building with "default" instance using docker driver

#1 [internal] load .dockerignore
#1 transferring context: 2B done
#1 DONE 0.0s

#2 [internal] load build definition from Dockerfile.build
#2 transferring dockerfile: 344B done
#2 DONE 0.0s

#3 [internal] load metadata for docker.io/library/openjdk:latest
#3 ...

#4 [auth] library/openjdk:pull token for registry-1.docker.io
#4 DONE 0.0s

#3 [internal] load metadata for docker.io/library/openjdk:latest
#3 DONE 3.6s

#5 [1/5] FROM docker.io/library/openjdk:latest@sha256:9b448de897d211c9e0ec635a485650aed6e28d4eca1efbc34940560a480b3f1f
#5 DONE 0.0s

#6 [internal] load build context
#6 transferring context: 169B done
#6 DONE 0.0s

#7 [4/5] RUN javac HelloWorld.java
#7 CACHED

#8 [2/5] WORKDIR /app
#8 CACHED

#9 [3/5] COPY HelloWorld.java /app
#9 CACHED

#10 [5/5] RUN jar cvf HelloWorld.jar HelloWorld.class
#10 CACHED

#11 exporting to image
#11 exporting layers done
#11 writing image sha256:2d5900c70ddee5681fafde163931dc1536f6812e2caeccbc172794b13c99552b done
#11 naming to docker.io/library/helloapp-builder:latest done
#11 DONE 0.0s

What's Next?
  View a summary of image vulnerabilities and recommendations → docker scout quickview
++ '[' 0 -ne 0 ']'
++ docker create --name helloapp-builder helloapp-builder:latest
bf4df7ae3e2fed2dc97b2f1952656819c87e37454108c7ef6a9e3747895f0adb
++ '[' 0 -ne 0 ']'
++ docker cp helloapp-builder:/app/HelloWorld.jar ./HelloWorld.jar
++ docker rm helloapp-builder
helloapp-builder
++ docker rmi helloapp-builder:latest
Untagged: helloapp-builder:latest
Deleted: sha256:2d5900c70ddee5681fafde163931dc1536f6812e2caeccbc172794b13c99552b
++ docker build -t helloapp:latest -f Dockerfile .
#0 building with "default" instance using docker driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 243B done
#1 DONE 0.0s

#2 [internal] load .dockerignore
#2 transferring context: 2B done
#2 DONE 0.0s

#3 [internal] load metadata for docker.io/library/openjdk:11-jdk-slim
#3 DONE 0.8s

#4 [1/3] FROM docker.io/library/openjdk:11-jdk-slim@sha256:868a4f2151d38ba6a09870cec584346a5edc8e9b71fde275eb2e0625273e2fd8
#4 DONE 0.0s

#5 [2/3] WORKDIR /app
#5 CACHED

#6 [internal] load build context
#6 transferring context: 798B done
#6 DONE 0.0s

#7 [3/3] COPY HelloWorld.jar /app/HelloWorld.jar
#7 DONE 0.0s

#8 exporting to image
#8 exporting layers 0.0s done
#8 writing image sha256:73f70ee8012a258801314c0280f1b2788afbffbf57c6235a09f00e30406e3404 done
#8 naming to docker.io/library/helloapp:latest done
#8 DONE 0.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Use the &lt;code&gt;docker image ls&lt;/code&gt; command to list all the Docker images available&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get a list of all the available Docker images as shown below&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
helloapp                                        latest    73f70ee8012a   About a minute ago   274MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see the image created has a size of 274MB, while our previous image had a size of 470 MB.&lt;/p&gt;

&lt;p&gt;While the builder pattern is a handy way to slim down your Docker images, it does come with some overhead. You have to juggle two Dockerfiles and a shell script to make everything work smoothly. Fortunately, there's a more streamlined approach: multi-stage Dockerfiles.&lt;/p&gt;

&lt;p&gt;In the next section, we'll ditch the multiple files and shell scripts and explore how multi-stage Dockerfiles can simplify the process while still achieving the same goal of creating leaner and more efficient images. Get ready to discover a simpler and more elegant way to optimize your Docker builds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Multi-Stage Dockerfiles
&lt;/h2&gt;

&lt;p&gt;Multi-stage Dockerfiles offer a streamlined way to create optimized Docker images within a single file. They achieve this by incorporating multiple stages, each potentially using a different base image and serving a specific purpose. Similar to the builder pattern we explored earlier, these stages typically include a builder stage for compiling source code and a runtime stage for executing the resulting application.&lt;/p&gt;

&lt;p&gt;The key difference lies in the use of multiple &lt;code&gt;FROM&lt;/code&gt; directives within a single Dockerfile, where each directive marks the beginning of a new stage. This allows for greater flexibility and efficiency, as you can selectively copy only the essential files from one stage to another, leaving behind any unnecessary build tools or dependencies.&lt;/p&gt;

&lt;p&gt;Before the introduction of multi-stage builds, the builder pattern was the go-to method for achieving this optimization. However, multi-stage builds offer a more elegant and concise solution within a single Dockerfile, eliminating the need for multiple files and external scripts.&lt;/p&gt;

&lt;p&gt;Multi-stage Docker builds offer a significant advantage over the builder pattern by allowing you to create equally small and efficient Docker images without the hassle of managing multiple files and scripts.&lt;/p&gt;

&lt;p&gt;While the builder pattern requires maintaining two Dockerfiles and a shell script, multi-stage builds consolidate everything into a single Dockerfile, simplifying the process considerably. Moreover, the builder pattern necessitates copying executables to the Docker host as an intermediate step before incorporating them into the final image. This extra step isn't needed with multi-stage builds, as you can directly transfer files between different stages within the same Dockerfile using the &lt;code&gt;--from&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;In essence, multi-stage builds eliminate the extra work and potential for errors associated with the builder pattern, making it a more streamlined and efficient approach to creating optimized Docker images.&lt;/p&gt;

&lt;p&gt;The key distinction between a regular Dockerfile and a multi-stage Dockerfile is the use of multiple &lt;code&gt;FROM&lt;/code&gt; directives. Each &lt;code&gt;FROM&lt;/code&gt; statement kicks off a new phase or stage in the build process, starting with a fresh base image. This means that each stage is isolated from the previous one, except for the specific files or directories you explicitly copy over.&lt;/p&gt;

&lt;p&gt;Think of it like building a house in stages. You start with a foundation (the base image), then build the frame, add walls, wiring, plumbing, and so on. Each stage builds upon the previous one, but you don't need all the tools and materials from earlier stages once you've moved on to the next.&lt;/p&gt;

&lt;p&gt;In a multi-stage Dockerfile, you use the &lt;code&gt;COPY --from=n&lt;/code&gt; instruction to selectively copy the necessary files (like your compiled application) from one stage to another. The number &lt;code&gt;0&lt;/code&gt; refers to the first stage (stages are numbered starting from 0). This allows you to build your application in one stage with all the necessary tools and dependencies, and then create a leaner final image in a later stage that only contains the essential runtime components.&lt;/p&gt;

&lt;p&gt;In a multi-stage Dockerfile, you can refer to each stage by a number, starting from 0. However, to make your Dockerfiles easier to read and maintain, it's often a good practice to give each stage a meaningful name.&lt;/p&gt;

&lt;p&gt;You can do this by adding the &lt;code&gt;AS&lt;/code&gt; keyword after the &lt;code&gt;FROM&lt;/code&gt; directive, followed by the name you want to give the stage. For example:&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:18&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;build&lt;/span&gt;
&lt;span class="c"&gt;# ... build stage instructions&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;nginx: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;runtime&lt;/span&gt;
&lt;span class="c"&gt;# ... runtime stage instructions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we've named the first stage &lt;code&gt;build&lt;/code&gt; and the second stage &lt;code&gt;runtime&lt;/code&gt;. This makes it clearer what each stage is for and can be helpful when you're referencing stages later in the Dockerfile.&lt;/p&gt;

&lt;p&gt;When working with multi-stage Dockerfiles, you might not always need to build all the way to the final stage. Let's imagine you have a Dockerfile with two stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Development Stage:&lt;/strong&gt; Packed with all the build and debugging tools you need for coding and testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Stage:&lt;/strong&gt; A leaner version that only includes the necessary runtime tools for deployment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;During development, you might only want to build up to the development stage to test your code and catch any bugs. In this case, the &lt;code&gt;--target&lt;/code&gt; flag in the &lt;code&gt;docker build&lt;/code&gt; command becomes your best friend. It lets you specify which stage should be the final one for the resulting image.&lt;/p&gt;

&lt;p&gt;For example, if you want to stop at the development stage, you'd run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--target&lt;/span&gt; developement &lt;span class="nt"&gt;-t&lt;/span&gt; my-image:dev &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;development&lt;/code&gt; is the name we gave to the development stage in the Dockerfile, and &lt;code&gt;my-image:dev&lt;/code&gt; is the name and tag we're giving to the resulting image.&lt;/p&gt;

&lt;p&gt;This way, you can create multiple images from a single Dockerfile, each tailored to a specific purpose in your development workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a Docker Image with a Multi-Stage Docker Build
&lt;/h3&gt;

&lt;p&gt;In this example, we are going to create a Go application using a Multi-Stage Docker Build. Create a new directory named &lt;code&gt;multi-stage-build&lt;/code&gt; for this example:&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="nb"&gt;mkdir &lt;/span&gt;multi-stage-build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And navigate to it:&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="nb"&gt;cd &lt;/span&gt;multi-stage-build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create the following &lt;code&gt;hellodocker.go&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Docker! I am built using a multi-stage build."&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;Now create the multi-stage Dockerfile&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;golang:latest&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; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; hellodocker.go .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; hello hellodocker.go

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; scratch&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/hello .&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["./hello"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile employs a multi-stage build process, optimizing the resulting image size. It begins with a builder stage, leveraging the &lt;code&gt;golang:latest&lt;/code&gt; image to create an environment suitable for compiling Go code. Within this stage, the &lt;code&gt;hellodocker.go&lt;/code&gt; source file is copied into the &lt;code&gt;/app&lt;/code&gt; directory, and the Go compiler (&lt;code&gt;go build&lt;/code&gt;) generates an executable named &lt;code&gt;hello&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The subsequent stage uses the &lt;code&gt;scratch&lt;/code&gt; image, a bare-bones image devoid of an operating system, as its foundation. This minimalistic environment is ideal for running the compiled application. The &lt;code&gt;hello&lt;/code&gt; executable, created in the previous builder stage, is copied into the &lt;code&gt;/app&lt;/code&gt; directory of this scratch image, ensuring that only the essential binary and its runtime dependencies are included.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;ENTRYPOINT ["./hello"]&lt;/code&gt; instruction dictates that upon container creation, the &lt;code&gt;hello&lt;/code&gt; binary will be executed, starting the application. This multi-stage approach effectively minimizes the final image size, making it more efficient for deployment and execution.&lt;/p&gt;




&lt;p&gt;Build the Docker image using the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; hello-multistage:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get an output similar to the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#0 building with "default" instance using docker driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 233B done
#1 DONE 0.0s

#2 [internal] load .dockerignore
#2 transferring context: 2B done
#2 DONE 0.0s

#3 [internal] load metadata for docker.io/library/golang:latest
#3 ...

#4 [auth] library/golang:pull token for registry-1.docker.io
#4 DONE 0.0s

#3 [internal] load metadata for docker.io/library/golang:latest
#3 DONE 5.1s

#5 [internal] load build context
#5 DONE 0.0s

#6 [stage-1 1/2] WORKDIR /app
#6 DONE 0.0s

#7 [builder 1/4] FROM docker.io/library/golang:latest@sha256:829eff99a4b2abffe68f6a3847337bf6455d69d17e49ec1a97dac78834754bd6
#7 resolve docker.io/library/golang:latest@sha256:829eff99a4b2abffe68f6a3847337bf6455d69d17e49ec1a97dac78834754bd6 0.0s done
#7 ...

#5 [internal] load build context
#5 transferring context: 159B done
#5 DONE 0.0s

#7 [builder 1/4] FROM docker.io/library/golang:latest@sha256:829eff99a4b2abffe68f6a3847337bf6455d69d17e49ec1a97dac78834754bd6
[...truncated...]

#8 [builder 2/4] WORKDIR /app
#8 DONE 1.0s

#9 [builder 3/4] COPY hellodocker.go .
#9 DONE 0.0s

#10 [builder 4/4] RUN go build -o hello hellodocker.go
#10 DONE 3.7s

#11 [stage-1 2/2] COPY --from=builder /app/hello .
#11 DONE 0.1s

#12 exporting to image
#12 exporting layers 0.1s done
#12 writing image sha256:6ff5d8bfd14ee1893ea5d1e315aa8c394edbc879369cd3d59326dd6256184625 done
#12 naming to docker.io/library/hello-multistage:v1 done
#12 DONE 0.1s

What's Next?
  View a summary of image vulnerabilities and recommendations → docker scout quickview
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Use the &lt;code&gt;docker image ls&lt;/code&gt; command to list all docker images available on your computer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker images &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY                                      TAG       IMAGE ID       CREATED         SIZE
hello-multistage                                v1        6ff5d8bfd14e   9 minutes ago   1.89MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we discussed how to use a multi-stage Dockerfile to build optimized Docker images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Multi-stage builds, introduced in Docker version 17.05, aim to streamline the Docker image creation process by using multiple stages within a single Dockerfile. Each stage focuses on specific tasks and allows for selective copying of artifacts between stages, ultimately optimizing the final image size and efficiency.&lt;/p&gt;

&lt;p&gt;Before multi-stage builds, we often relied on the builder pattern, which involved using separate Dockerfiles and shell scripts to achieve similar optimization goals. While effective, this approach added complexity to the build process.&lt;/p&gt;

&lt;p&gt;In this post, we started by discussing the challenges of traditional Docker builds, especially in production environments where image size and build efficiency are critical. Then, we introduced the builder pattern as a precursor to multi-stage builds, explaining its use of two Docker images—one for building and one for runtime—with selective artifact copying between them.&lt;/p&gt;

&lt;p&gt;Finally, we delved into multi-stage Dockerfiles as a more integrated solution. Here, you define multiple stages within a single Dockerfile using &lt;code&gt;FROM&lt;/code&gt; directives. Each stage builds upon the previous one but allows you to copy only the necessary files, eliminating the need for multiple Dockerfiles and external scripts. This approach simplifies the build process while ensuring smaller, more efficient Docker images for deployment.&lt;/p&gt;

&lt;p&gt;Overall, multi-stage Dockerfiles offer a streamlined and elegant solution for optimizing Docker image builds, reducing complexity while enhancing efficiency—a significant benefit for anyone managing Docker images in production environments.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Storing and Publishing your Docker Images</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Mon, 05 Aug 2024 06:00:00 +0000</pubDate>
      <link>https://dev.to/kalkwst/storing-and-publishing-your-docker-images-4m39</link>
      <guid>https://dev.to/kalkwst/storing-and-publishing-your-docker-images-4m39</guid>
      <description>&lt;p&gt;From the get-go, Docker's popularity stemmed from having a central hub where users could grab images, tweak them, and share their creations with others. Docker Hub has become a go-to resource for developers looking for pre-built images to kickstart their projects. Despite some security hiccups along the way, it's still the default starting point for many.&lt;/p&gt;

&lt;p&gt;As a public repository, Docker Hub is a treasure trove of open-source images, perfect for streamlining your next project. It's also a great platform for companies and developers to share their work with the community.&lt;/p&gt;

&lt;p&gt;But hold on, there's more to the image-sharing game than just Docker Hub. When it comes to production environments, especially for larger teams, relying on a public repository might not be the most secure or efficient option.&lt;/p&gt;

&lt;p&gt;Nowadays, you've got cloud-based options like Amazon ECR or Google Container Registry, which offer more control and security for your images. And if you're feeling adventurous, I'll even show you how to set up your own local registry later on.&lt;/p&gt;

&lt;p&gt;In this post, we'll get hands-on with transferring Docker images between different machines. We'll start by exploring how to share your creations on Docker Hub, making them publicly available to the world. Then, for a more private setup, we'll dive into setting up your own local Docker registry right on your development machine. This gives you more control and security over your images, perfect for team collaboration or sensitive projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Moving Docker Images Manually
&lt;/h3&gt;

&lt;p&gt;In certain situations, like dealing with firewalls or network restrictions, you might need to directly transfer a Docker image between machines without using a registry. Luckily, Docker has you covered! This example will walk you through how to move an image from one system to another without relying on a registry.&lt;/p&gt;

&lt;p&gt;We are going to use the image created in the previous post, and the &lt;code&gt;docker save&lt;/code&gt; command. We'll save it as a &lt;code&gt;.tar&lt;/code&gt; file with a specific filename and directory. To do that use the following command. Make sure that you replace the user, image name, and tag of the image with what you used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker save &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/base-image.tar base-image:1.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now find the image you just saved sitting in your &lt;code&gt;/tmp&lt;/code&gt; directory, packaged up as a TAR file (&lt;code&gt;base-image.tar&lt;/code&gt;). This is because the &lt;code&gt;docker save&lt;/code&gt; command always creates a TAR archive. But here's a fun fact: the file extension itself doesn't matter all that much. You could rename it to &lt;code&gt;.tar.gz&lt;/code&gt; or even &lt;code&gt;.myimage&lt;/code&gt; if you wanted to! The content inside is what's important, and Docker will recognize it as an image file regardless of the extension.&lt;/p&gt;




&lt;p&gt;Use the &lt;code&gt;du&lt;/code&gt; command to verify that the &lt;code&gt;base-image.tar&lt;/code&gt; file has data in it&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="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; /tmp/base-image.tar
22M     base-image.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Now that you've saved your Docker image as a TAR file, you're free to move it around however you like. You can use tools like &lt;code&gt;rsync&lt;/code&gt;, &lt;code&gt;scp&lt;/code&gt;, or even a simple &lt;code&gt;cp&lt;/code&gt; command. If you want to save a bit of space during the transfer, you can even compress the TAR file into a ZIP archive.&lt;/p&gt;

&lt;p&gt;For this example, let's just remove the image from your current system to clean things up. You'll need the image ID for this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then remove the image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rmi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;b5e88b0b4601&lt;/span&gt;&lt;span class="w"&gt;                                                         

&lt;/span&gt;&lt;span class="n"&gt;Untagged:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;base-image:1.1.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Deleted:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sha256:b5e88b0b4601183bb9425186cd95fee52e22be5f1ec8c58b95a50f42e78c2004&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Imagine that you've successfully transferred your image to a new machine, let's load it back into Docker so you can use it again. You'll need the &lt;code&gt;docker load&lt;/code&gt; command for this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/tmp/base-image.tar&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get output like the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Loaded image: base-image:1.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Use the &lt;code&gt;docker image&lt;/code&gt; command to bring up the image you just loaded into your local environment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get output similar to the following&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
base-image                                      1.1.0     b5e88b0b4601   About an hour ago   22MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example was a straightforward demonstration, but it aimed to highlight that even without access to a registry, you can still transport your Docker images across systems.&lt;/p&gt;

&lt;p&gt;Now, let's shift our focus to the more common methods of storing, sharing, and distributing your Docker images. We'll delve into using Docker Hub and other registries to streamline your workflow and collaborate effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storing and Deleting Images from Docker Hub
&lt;/h2&gt;

&lt;p&gt;While Docker Hub offers a free tier for working with Docker images, it's important to note that you only get one private repository for free. If you need more than that, you'll have to opt for a paid monthly plan.&lt;/p&gt;

&lt;p&gt;For most teams, a single private repository is rarely enough. So, if you're considering Docker Hub, be prepared to factor in the cost of a subscription if you need multiple private repositories.&lt;/p&gt;

&lt;p&gt;However, if your needs are limited and you're okay with your images being public, then a free account gives you an unlimited number of public repositories to work with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Storing Docker Images in Docker Hub
&lt;/h3&gt;

&lt;p&gt;Alright, let's get your &lt;code&gt;base-image&lt;/code&gt; image up on Docker Hub. We'll walk through creating a new repository, pushing your image to it, and then deleting the repository when we're done. This will give you a taste of how to manage your Docker images in the cloud.&lt;/p&gt;

&lt;p&gt;To follow along with these Docker shenanigans, you'll need a Docker Hub account. Don't worry, I'm not made of money either, so we'll stick to the free stuff (unless you want to sponsor me, of course). If you haven't signed up yet, head over to &lt;a href="https://hub.docker.com/signup" rel="noopener noreferrer"&gt;Docker Signup&lt;/a&gt; and create your free account. Then come back here and let's get this Docker party started!&lt;/p&gt;

&lt;p&gt;First things first, head over to your Docker Hub account. Once you're logged in, look for the "Repositories" section. You should see a blue button on the right side of the screen that says "Create Repository". Click that button to start setting up a new home for your &lt;code&gt;base-image&lt;/code&gt; image.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fptwtvze5k7wy1ln5gmji.png" class="article-body-image-wrapper"&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%2Farticles%2Fptwtvze5k7wy1ln5gmji.png" alt="Image description" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;When creating a new repository, you'll be presented with a page like the one that follows. Fill in the Name of the repository, which is usually the name of the image or service you are storing (in this case, basic-app). You also have the option to set the repository as Public or Private, and in this instance, select Public:&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Feroe40tc5gqkeaqbw8dj.png" class="article-body-image-wrapper"&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%2Farticles%2Feroe40tc5gqkeaqbw8dj.png" alt="Image description" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Once your new repository is created, it will provide details on how to start pushing your images to your new repository. Tag your image with /:tag to let Docker know where it will be pushing the image and which repository Docker will be pushing it to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;base-image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cyberduckling/base-image:v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Great, Docker is now aware of where to send your image. Before you can push the image, you'll need to log in to your Docker Hub account through the Docker CLI. Use the &lt;code&gt;docker login&lt;/code&gt; command and enter the same username and password you used when you logged into Docker Hub earlier. Then, let's push it to Docker Hub using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cyberduckling/base-image:v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Alright! Now, if you head back to your Docker Hub account and look at your newly created &lt;code&gt;base-image&lt;/code&gt; repository, you should see your image version (&lt;code&gt;v1&lt;/code&gt;) listed there. It's officially up in the cloud and ready to be shared or used in your projects&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2F2x8hm37qxis2a20ub5jr.png" class="article-body-image-wrapper"&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%2Farticles%2F2x8hm37qxis2a20ub5jr.png" alt="Image description" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you look at the previous screenshot, you'll see a button on the right side of the screen labeled "Public View." This lets you preview exactly what the public will see when they search for your image on Docker Hub. Go ahead and click it! You should see a screen similar to this&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fia40fg9jd9nkxnk7eiah.png" class="article-body-image-wrapper"&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%2Farticles%2Fia40fg9jd9nkxnk7eiah.png" alt="Image description" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This exercise demonstrates how Docker Hub can be a great way to share your Docker images with the world, making it easy for others to collaborate on your projects or build upon your work. While a public repository might not always be the perfect fit for a large company, it offers a similar platform for collaboration and code sharing as GitHub does for software development. By publishing your images on Docker Hub, you're contributing to a thriving community of developers and making your own projects more accessible and impactful.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Docker Registry
&lt;/h2&gt;

&lt;p&gt;Docker Registry is like a personal storage space for your Docker images. In most cases, these registries are kept private, only accessible by your team. There are many great options out there, and one of them is a ready-made registry image maintained by Docker itself.&lt;/p&gt;

&lt;p&gt;There are many reasons why you might want to run your own Docker registry. Maybe you have concerns about security, want to keep your work under wraps until it's ready, or simply prefer the convenience of having your images right there on your own machine.&lt;/p&gt;

&lt;p&gt;In this section, we'll guide you through setting up a Docker registry on your development environment and start storing your valuable images there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Local Docker Registry
&lt;/h3&gt;

&lt;p&gt;In this hands-on example, we'll create a private Docker registry that runs directly on your machine. Don't worry, this won't be accessible to your team or the public – it's just for you to experiment and decide if a local registry is the right fit for your needs. We'll even set up a custom domain name to make it feel like a real registry&lt;/p&gt;

&lt;p&gt;To give your local registry a custom domain name, you'll need to edit your system's hosts file. Here's how you can find it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows:&lt;/strong&gt; &lt;code&gt;C:\Windows\System32\drivers\etc\hosts&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux/macOS:&lt;/strong&gt; &lt;code&gt;/etc/hosts&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open the &lt;code&gt;hosts&lt;/code&gt; file in a text editor (you might need administrator privileges on Windows) and add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1    my-registry.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line tells your computer to resolve the domain name &lt;code&gt;my-registry.local&lt;/code&gt; to the local IP address &lt;code&gt;127.0.0.1&lt;/code&gt;, which is where your registry will be running.&lt;/p&gt;




&lt;p&gt;Pull the latest &lt;code&gt;registry&lt;/code&gt; image down from Docker Hub&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;registry&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Great, you've set up the domain! Now, let's fire up your local Docker registry using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;8954:5000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;always&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-registry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;registry:2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am using port &lt;code&gt;8954&lt;/code&gt; because I have other things running on &lt;code&gt;5000&lt;/code&gt;, but feel free to use any port you like.&lt;/p&gt;




&lt;p&gt;Run the &lt;code&gt;docker ps&lt;/code&gt; command to see the &lt;code&gt;registry&lt;/code&gt; container running on your system&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ps&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see an output similar to the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS          PORTS                    NAMES
ba187cd38a11   registry:2   "/entrypoint.sh /etc…"   42 seconds ago   Up 41 seconds   0.0.0.0:8954-&amp;gt;5000/tcp   my-registry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Now run the &lt;code&gt;docker tag&lt;/code&gt; command to tag existing images with the registry hostname and port &lt;code&gt;my-registry:8954&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;cyberduckling/base-image:v1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-registry:8954/cyberduckling/base-image:v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Delete the original images using the &lt;code&gt;docker image remove&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-registry:8954/cyberduckling/base-image:v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Finally, pull down the image from your local registry&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;ocker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;my-registry:8954/cyberduckling/base-image:v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wraps up our exploration of setting up a local Docker registry on your system. While this simple registry isn't officially supported, it provides a hands-on way to understand how registries work and how they can benefit your team. It's a great stepping stone for evaluating whether a private registry is the right solution for your needs.&lt;/p&gt;

&lt;p&gt;If you're looking for a more comprehensive, supported option, Docker also offers Docker Trusted Registry, a commercial product designed to meet enterprise-level requirements for image management and security. This could be a good next step to consider if you need a more robust registry solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This post dives into the world of Docker image sharing, starting with a brief overview of Docker Hub's history and benefits. It then guides you through hands-on examples of how to move Docker images between machines, both manually (using a TAR file) and by leveraging Docker Hub's repository system. You'll learn how to create a repository on Docker Hub, push your images to it, and even view your published images from a public perspective.&lt;/p&gt;

&lt;p&gt;But the fun doesn't stop there! The post also explores setting up a local Docker registry on your development machine, giving you a taste of how to create a private repository and manage your images in a controlled environment. This is a great option for teams or individuals who want more control over their image storage and security.&lt;/p&gt;

&lt;p&gt;Whether you're a beginner looking to share your Docker creations or a seasoned pro exploring private registry options, this post offers valuable insights and practical examples to help you master Docker image management.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Docker Image Naming and Tagging</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Mon, 29 Jul 2024 06:00:00 +0000</pubDate>
      <link>https://dev.to/kalkwst/docker-image-naming-and-tagging-1pg9</link>
      <guid>https://dev.to/kalkwst/docker-image-naming-and-tagging-1pg9</guid>
      <description>&lt;p&gt;As we get more acquainted with Docker, let's explore a little bit more what image tags are all about, even if we have discussed them before. All told, a tag is essentially a label you apply to your Docker image. Like a little sticky note informing you—and everyone else using the image—something significant about it, including what version it is or what type of software it contains. Consider it as a means of maintaining easily comprehensible and orderly Docker images.&lt;/p&gt;

&lt;p&gt;Working on our Docker images, we had assumed that we are all by ourselves. That won't always be the case, though, particularly as our development team expands. The following post will help us to understand how best to tag and name our images for a team environment. Following these guidelines will help us to maintain our Docker images clear and understandable for every project participant.&lt;/p&gt;

&lt;p&gt;There are two basic approaches for labeling and naming Docker images. When you build an image from a &lt;strong&gt;Dockerfile&lt;/strong&gt;, you can either use the &lt;code&gt;-t&lt;/code&gt; option, or the &lt;code&gt;docker tag&lt;/code&gt; command. When you use the &lt;code&gt;docker tag&lt;/code&gt; command, you specify the &lt;strong&gt;source repository name&lt;/strong&gt; you are going to use as the base and the &lt;strong&gt;target name&lt;/strong&gt; and &lt;strong&gt;tag&lt;/strong&gt; you'll be creating&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;source_repository_name&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;target_repository_name&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you use the &lt;code&gt;docker build&lt;/code&gt; command, to name your image, you will use the &lt;strong&gt;Dockerfile&lt;/strong&gt; as the source and then the &lt;code&gt;-t&lt;/code&gt; option to name and tag your images&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;target_repository_name&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Dockerfile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Occasionally you may find a repository name beginning with a hostname. This is only a means of informing Docker where the repository resides online; and it is absolutely optional. In future posts in this series we are going to create our own Docker Registry, and see how this works. If you're uploading your images to Docker Hub, though, you'll definitely need to add your Docker Hub username to the beginning of the repository name, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;built&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;dockerhub_user&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;target_repository_name&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Dockerfile&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the following example, we are going through the process of tagging Docker images.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tagging Docker Images
&lt;/h3&gt;

&lt;p&gt;In this example, we are going to work to use a new image, using the lightweight &lt;code&gt;busybox&lt;/code&gt; image to demonstrate the process of tagging and start to implement tags in our projects. BusyBox is used to combine mini versions of many common UNIX utilities into a single small executable.&lt;/p&gt;

&lt;p&gt;Run the &lt;code&gt;docker rmi&lt;/code&gt; command to clear up the images you currently have on your system, so you don't get confused with a large number of images around:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rmi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;On the command line, run the &lt;code&gt;docker pull&lt;/code&gt; command to download the latest &lt;code&gt;busybox&lt;/code&gt; container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;busybox&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the &lt;code&gt;docker images&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give us the information we need to start putting some tag commands together&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
busybox                                         latest    65ad0d468eb1   13 months ago   4.26MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Okay, let's give your image a name and tag it so it's easier to find and work with later. We can do this using either the image's unique ID or its repository name.&lt;/p&gt;

&lt;p&gt;For now, we'll use the image ID. Keep in mind that your image ID will be different from the one I use in the example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;65ad0d468eb1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mybusybox:v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will create a new tag called &lt;code&gt;v1&lt;/code&gt; for your image in the &lt;code&gt;mybusybox&lt;/code&gt; repository. Now you can refer to your image using either its ID or the more human-readable name &lt;code&gt;mybusybox:v1&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;Now that we've tagged our image, let's create a new repository using your name and a new version tag. Since we've already tagged the image as &lt;code&gt;mybusybox:v1&lt;/code&gt;, we can use that to create a new tag with the updated version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;mybusybox:v1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kalkost/busybox:v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the &lt;code&gt;docker images&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see an output similar to the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kalkost/busybox                                 v1        65ad0d468eb1   13 months ago   4.26MB
busybox                                         latest    65ad0d468eb1   13 months ago   4.26MB
mybusybox                                       v1        65ad0d468eb1   13 months ago   4.26MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Let's put together a Dockerfile to create a basic image, using the &lt;code&gt;mybusybox&lt;/code&gt; image we tagged earlier. We'll also include a tag for the new image we're creating, since Docker might try to use the &lt;code&gt;latest&lt;/code&gt; tag if we don't specify one, and that could cause errors if it doesn't exist.&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FROM mybusybox:v1"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the &lt;code&gt;docker build&lt;/code&gt; command to create the image while naming and tagging it at the same time&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;built_image:v1.1.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that when I tried the above command, the Dockerfile was created with a UTF-16 encoding causing the very user friendly &lt;code&gt;ERROR: failed to solve: Internal: Internal: Internal: stream terminated by RST_STREAM with error code: INTERNAL_ERROR&lt;/code&gt; error to appear. If you encounter this issue, then open the Dockerfile with any editor of your choosing and change the encoding to &lt;code&gt;UTF-8&lt;/code&gt;&lt;/p&gt;




&lt;p&gt;Now, run the &lt;code&gt;docker images&lt;/code&gt; command to see the four images available on your system&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something similar to the following&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
built-image                                     v1.1.1    0534e936c658   13 months ago   4.26MB
busybox                                         latest    65ad0d468eb1   13 months ago   4.26MB
mybusybox                                       v1        65ad0d468eb1   13 months ago   4.26MB
kalkost/busybox                                 v1        65ad0d468eb1   13 months ago   4.26MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tagging your Docker images with a meaningful version number that makes sense for your team or company is actually pretty simple, especially once you've done it a few times. The tips we've covered here will help you avoid the default &lt;code&gt;latest&lt;/code&gt; tag, which can cause problems down the line. In the next section, we'll dive into those potential issues and why using specific tags is a smart move.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Latest Tag in Docker
&lt;/h2&gt;

&lt;p&gt;As we've been working with tags, I've mentioned a few times to avoid using the default &lt;code&gt;latest&lt;/code&gt; tag. It might seem convenient, but it can actually cause a lot of headaches, especially when you're deploying images to production. Let's dive into why it's best to steer clear of &lt;code&gt;latest&lt;/code&gt; and use specific tags instead.&lt;/p&gt;

&lt;p&gt;The first thing to understand is that the &lt;code&gt;latest&lt;/code&gt; tag is just a regular tag, like the &lt;code&gt;v1&lt;/code&gt; we used earlier. It doesn't automatically mean the newest version of your code. It actually refers to the most recent image you built without specifying a tag.&lt;/p&gt;

&lt;p&gt;Here's the thing about using the &lt;code&gt;latest&lt;/code&gt; tag: it can really mess things up when you have a big team constantly deploying updates to different environments. It's like trying to keep track of a moving target – you never quite know which version you're actually dealing with. And if something goes wrong, good luck rolling back to a previous version without a clear history.&lt;/p&gt;

&lt;p&gt;Remember, Docker doesn't automatically pull the most recent image when you use &lt;code&gt;latest&lt;/code&gt;. It just grabs the most recent one you've built or pulled &lt;em&gt;without specifying a tag&lt;/em&gt;. This can lead to all sorts of confusion and unexpected behavior, especially in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issues when Using Latest
&lt;/h3&gt;

&lt;p&gt;Since you might still getting familiar with Docker and tagging, you might not have run into any problems using the &lt;code&gt;latest&lt;/code&gt; tag yet. But don't worry, this next exercise will give you a firsthand look at how &lt;code&gt;latest&lt;/code&gt; can throw a wrench in your development process. You'll understand why it's best to avoid it and start using specific tags instead.&lt;/p&gt;

&lt;p&gt;We're going to build on the previous Dockerfile in this example and explore the potential pitfalls of relying on the &lt;code&gt;latest&lt;/code&gt; tag. Get ready to see the &lt;code&gt;latest&lt;/code&gt; tag in action (or inaction, as the case may be).&lt;/p&gt;

&lt;p&gt;Let's update the that Dockerfile we created to include a simple version script.&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; mybusybox:v1&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; /version.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;
    echo '#!/bin/sh' &amp;gt;&amp;gt; /version.sh &amp;amp;&amp;amp; \ 
    echo 'echo "Version: 1.0"' &amp;gt;&amp;gt; /version.sh &amp;amp;&amp;amp; \ 
    chmod +x /version.sh

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["sh", "/version.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Build the image and name it whatever you want&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kalkost/test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the image using the &lt;code&gt;docker run&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kalkost/test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the output of the &lt;code&gt;version.sh&lt;/code&gt; script&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Version: 1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Use the &lt;code&gt;docker tag&lt;/code&gt; command to tag this image as &lt;code&gt;v1&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kalkost/test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kalkost/test:version1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Change the value of the version of the &lt;strong&gt;Dockerfile&lt;/strong&gt; in line 5&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; mybusybox:v1&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; /version.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'#!/bin/sh'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /version.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'echo "Version: 2.0"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /version.sh &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;chmod&lt;/span&gt; +x /version.sh

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["sh", "/version.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Build the amended &lt;strong&gt;Dockerfile&lt;/strong&gt; and tag it with &lt;code&gt;v2&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kalkost/test:v2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the amended image using the &lt;code&gt;docker run&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run kalkost/test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you might think that you are going to see the version changed, but you will see the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Version: 1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hold on a second! This isn't the version we expected, right? It seems Docker pulled the most recent version of the image that was tagged with &lt;code&gt;latest&lt;/code&gt; instead of the one we just built with the &lt;code&gt;1.0&lt;/code&gt; tag in step 3. This is a perfect example of how using the &lt;code&gt;latest&lt;/code&gt; tag can lead to unexpected results and confusion.&lt;/p&gt;




&lt;p&gt;Now, run both images with &lt;code&gt;latest&lt;/code&gt; and &lt;code&gt;v2&lt;/code&gt; tags&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;kalkost/test:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Version:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run kalkost/test:v2
Version: 2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You probably guessed it - to run the updated version of the code with the version script, we need to specify the &lt;code&gt;1.0&lt;/code&gt; tag that we used when building the image. If you don't, Docker defaults to the most recent image with the &lt;code&gt;latest&lt;/code&gt; tag, which might not be what you want.&lt;/p&gt;

&lt;p&gt;This example clearly shows how using the &lt;code&gt;latest&lt;/code&gt; tag can create confusion, especially in a team setting where multiple developers are pushing images to a shared repository. It's like playing a guessing game with which version is actually running in production.&lt;/p&gt;

&lt;p&gt;Things can get even messier if you're using orchestration tools (like Kubernetes) and pulling the &lt;code&gt;latest&lt;/code&gt; image. You could end up with a mix of different versions running in your production environment, which is a recipe for unexpected behavior and potential bugs.&lt;/p&gt;

&lt;p&gt;The key takeaway here is that using specific tags is crucial for maintaining control and predictability in your Docker workflow. It ensures that everyone is on the same page and helps avoid those frustrating &lt;code&gt;latest&lt;/code&gt; tag surprises.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Image Tagging Policies
&lt;/h2&gt;

&lt;p&gt;As development teams grow and projects become more complex, having a standard tagging policy for your Docker images becomes increasingly important. Without a clear and consistent approach to tagging, you risk running into the kind of confusion and problems we've explored in earlier sections.&lt;/p&gt;

&lt;p&gt;Deciding on a tagging policy early in the game is a smart move to avoid these headaches. There's no one-size-fits-all answer, but the key is to choose a policy that works for your team and stick to it. This section will cover some different tagging strategies you can use, along with examples of how to implement them. Remember, the goal is to find a policy that everyone on your team agrees on and can easily follow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic versioning&lt;/strong&gt; is a popular and reliable system that can also be used for tagging your Docker images. If you haven't heard of it before, it's a simple three-part number system (&lt;code&gt;major&lt;/code&gt;.&lt;code&gt;minor&lt;/code&gt;.&lt;code&gt;patch&lt;/code&gt;). For example, a version like &lt;code&gt;2.1.0&lt;/code&gt; would indicate a major release of version &lt;code&gt;2&lt;/code&gt;, a minor release of &lt;code&gt;1&lt;/code&gt;, and no patches. It's a great way to keep track of your image versions, especially in automated build environments where it can be easily automated.&lt;/p&gt;

&lt;p&gt;Another option is to use a &lt;em&gt;hash value&lt;/em&gt;, such as the &lt;strong&gt;git commit hash&lt;/strong&gt; for your code. This allows you to easily link the image tag back to the specific code changes in your repository, making it super easy for anyone to see what's been updated.&lt;/p&gt;

&lt;p&gt;Using a &lt;strong&gt;date value&lt;/strong&gt; is another simple option that can be automated. It gives you a clear timestamp for each image, which can be helpful for tracking and troubleshooting.&lt;/p&gt;

&lt;p&gt;The key takeaway here is that automating your tagging policy is the best way to ensure consistency, clarity, and adherence across your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automating Image Tagging
&lt;/h3&gt;

&lt;p&gt;Alright, let's explore how we can automate tagging your Docker images. This will minimize manual intervention and streamline your workflow. We'll continue working with the familiar &lt;code&gt;base-image&lt;/code&gt; for this example.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;base-image&lt;/code&gt; using the following &lt;strong&gt;Dockerfile&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; alpine:latest&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk upgrade &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add wget curl

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; VERSION=0.0.0&lt;/span&gt;

&lt;span class="c"&gt;# Copy the version.txt file to the image&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; version.txt /version.txt&lt;/span&gt;

&lt;span class="c"&gt;# Command to read and display the version&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["cat", "/version.txt"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create a &lt;code&gt;version.txt&lt;/code&gt; file with the following content&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Now, create the following &lt;code&gt;build.sh&lt;/code&gt;&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="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Read current version from version.txt&lt;/span&gt;
&lt;span class="nv"&gt;current_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;version.txt&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Extract the major and minor versions, set patch to 0&lt;/span&gt;
&lt;span class="nv"&gt;major_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$current_version&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;minor_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$current_version&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;new_minor_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;minor_version &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="nv"&gt;new_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$major_version&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$new_minor_version&lt;/span&gt;&lt;span class="s2"&gt;.0"&lt;/span&gt;

&lt;span class="c"&gt;# Update version.txt with the new version&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$new_version&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; version.txt

&lt;span class="c"&gt;# Build the Docker image with the new version as a build argument&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; base-image:&lt;span class="nv"&gt;$new_version&lt;/span&gt; &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$new_version&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down what the script does&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="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Read current version from version.txt&lt;/span&gt;
&lt;span class="nv"&gt;current_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;version.txt&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part reads the current version number from a file named &lt;code&gt;version.txt&lt;/code&gt; and stores it in the variable &lt;code&gt;current_version&lt;/code&gt;. In a more real-life setting you would probably use Git to update the versions. If you are using Git, change the above command with the following&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="c"&gt;# Get the current version from Git tags &lt;/span&gt;
&lt;span class="nv"&gt;current_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git describe &lt;span class="nt"&gt;--tags&lt;/span&gt; &lt;span class="nt"&gt;--abbrev&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Extract the major and minor versions, set patch to 0&lt;/span&gt;
&lt;span class="nv"&gt;major_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$current_version&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;minor_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$current_version&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;new_minor_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;minor_version &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="nv"&gt;new_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$major_version&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$new_minor_version&lt;/span&gt;&lt;span class="s2"&gt;.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These lines use &lt;code&gt;awk&lt;/code&gt; to parse the &lt;code&gt;current_version&lt;/code&gt; string (which is assumed to be in &lt;code&gt;MAJOR.MINOR.PATCH&lt;/code&gt; format, e.g., &lt;code&gt;1.2.3&lt;/code&gt;) and extract the &lt;code&gt;MAJOR&lt;/code&gt; and &lt;code&gt;MINOR&lt;/code&gt; version components into variables &lt;code&gt;major_version&lt;/code&gt; and &lt;code&gt;minor_version&lt;/code&gt; respectively. Then it increments the &lt;code&gt;MINOR&lt;/code&gt; version by 1 to get the new &lt;code&gt;MINOR&lt;/code&gt; version number. &lt;/p&gt;

&lt;p&gt;Finally the script constructs the &lt;code&gt;new_version&lt;/code&gt;. &lt;code&gt;new_version&lt;/code&gt; is constructed by combining the &lt;code&gt;major_version&lt;/code&gt;, incremented &lt;code&gt;minor_version&lt;/code&gt;, and setting the &lt;code&gt;PATCH&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;. This forms a new version number, e.g., if &lt;code&gt;current_version&lt;/code&gt; is &lt;code&gt;1.2.3&lt;/code&gt;, then &lt;code&gt;new_version&lt;/code&gt; will become &lt;code&gt;1.3.0&lt;/code&gt;.&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="c"&gt;# Update version.txt with the new version&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$new_version&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; version.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This line updates the &lt;code&gt;version.txt&lt;/code&gt; file with the newly created &lt;code&gt;new_version&lt;/code&gt;. It overwrites the existing version number stored in &lt;code&gt;version.txt&lt;/code&gt; with the incremented version.&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="c"&gt;# Build the Docker image with the new version as a build argument&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; base-image:&lt;span class="nv"&gt;$new_version&lt;/span&gt; &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$new_version&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, this command builds a Docker image named &lt;code&gt;base-image&lt;/code&gt; with the tag &lt;code&gt;$new_version&lt;/code&gt; (e.g., &lt;code&gt;base-image:1.3.0&lt;/code&gt;). The &lt;code&gt;--build-arg VERSION=$new_version&lt;/code&gt; option passes the &lt;code&gt;new_version&lt;/code&gt; as a build argument to the Docker build process. The &lt;code&gt;.&lt;/code&gt; at the end specifies the current directory as the build context.&lt;/p&gt;




&lt;p&gt;Make sure that you have permissions to run the script using the &lt;code&gt;chmod&lt;/code&gt; command&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="nb"&gt;chmod&lt;/span&gt; +x build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Now, if you run &lt;code&gt;build.sh&lt;/code&gt; you will see that the script calculates the new version of the image and builds the Docker image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./build.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output should be something similar to the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#0 building with "default" instance using docker driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 120B done
#1 DONE 0.0s

#2 [internal] load .dockerignore
#2 transferring context: 2B done
#2 DONE 0.0s

#3 [internal] load metadata for docker.io/library/alpine:latest
#3 ...

#4 [auth] library/alpine:pull token for registry-1.docker.io
#4 DONE 0.0s

#3 [internal] load metadata for docker.io/library/alpine:latest
#3 DONE 4.1s

#5 [1/2] FROM docker.io/library/alpine:latest@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0
#5 DONE 0.0s

#6 [2/2] RUN apk upgrade --no-cache &amp;amp;&amp;amp; apk update &amp;amp;&amp;amp; apk add wget curl
#6 CACHED

#7 exporting to image
#7 exporting layers done
#7 writing image sha256:b5e88b0b4601183bb9425186cd95fee52e22be5f1ec8c58b95a50f42e78c2004 done
#7 naming to docker.io/library/base-image:1.1.0 done
#7 DONE 0.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;View the image using the &lt;code&gt;docker images&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should reflect the name and tags created as part of the build script&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
base-image                                      1.1.0     b5e88b0b4601   29 minutes ago   22MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We've discussed the importance of image tags in Docker and provided guidelines for tagging and naming them, especially in a team environment. We saw two approaches for labeling and naming Docker images: using the &lt;code&gt;-t&lt;/code&gt; option or the &lt;code&gt;docker tag&lt;/code&gt; command. &lt;/p&gt;

&lt;p&gt;We also discussed examples of tagging Docker images and highlighted the potential issues with using the &lt;code&gt;latest&lt;/code&gt; tag. We then saw the need for a clear tagging policy and suggested using semantic versioning, hash values, or date values for image tags. &lt;/p&gt;

&lt;p&gt;Finally, we discussed the importance of automating the tagging policy to ensure consistency and clarity.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dockerfile Best Practices Explained - Interlude Post 2</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Wed, 24 Jul 2024 06:00:00 +0000</pubDate>
      <link>https://dev.to/kalkwst/dockerfile-best-practices-explained-interlude-post-2-3elj</link>
      <guid>https://dev.to/kalkwst/dockerfile-best-practices-explained-interlude-post-2-3elj</guid>
      <description>&lt;p&gt;In this post, we'll dive into the best practices for writing Dockerfiles that will help you create efficient, secure, and maintainable Docker images while reducing build times. &lt;/p&gt;

&lt;h2&gt;
  
  
  Using an Appropriate Parent Image
&lt;/h2&gt;

&lt;p&gt;One of the most important things you can do to build efficient Docker images is to choose the right base image. It's always a good idea to start with official images from Docker Hub as your foundation when building custom images. These official images are well-maintained, follow best practices, have comprehensive documentation, and receive regular security updates.&lt;/p&gt;

&lt;p&gt;For example, if your application needs the Java Development Kit (JDK), it's more efficient and secure to use an official Docker image like &lt;code&gt;eclipse-temurin&lt;/code&gt; instead of starting from scratch with a generic &lt;code&gt;ubuntu&lt;/code&gt; image and manually installing the JDK. This way, you can leverage the work that has already been done to ensure the JDK is properly configured and secure within the Docker environment.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Inefficient Dockerfile&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Efficient Dockerfile&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FROM ubuntu&lt;br&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y openjdk-11-jdk&lt;/td&gt;
&lt;td&gt;FROM eclipse-temurin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Secondly, it's crucial to avoid using the &lt;code&gt;latest&lt;/code&gt; tag when specifying the parent image in your Dockerfile, especially for production environments. The &lt;code&gt;latest&lt;/code&gt; tag is a moving target – it always points to the most recent version of the image on Docker Hub. However, as newer versions are released, the &lt;code&gt;latest&lt;/code&gt; tag gets updated, and those updates might not be compatible with your application. This can result in unexpected errors and failures in your production environment.&lt;/p&gt;

&lt;p&gt;To prevent this, always use a specific versioned tag when referring to the parent image in your Dockerfile. This ensures that you're building on a stable foundation and avoids the risk of unintended changes caused by automatic updates to the &lt;code&gt;latest&lt;/code&gt; tag.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Inefficient Dockerfile&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Efficient Dockerfile&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FROM eclipse-temurin:latest&lt;/td&gt;
&lt;td&gt;FROM eclipse-temurin:8u412-b08-jre-windowsservercore-ltsc2022&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And finally, to really slim down your Docker images, it's a good idea to use the minimal version of your base image whenever possible. Many official Docker images on Docker Hub offer a "slim" or "alpine" variant that's specifically designed to be lightweight.&lt;/p&gt;

&lt;p&gt;These minimal versions are often based on Alpine Linux, a stripped-down distribution known for its small size and security focus. By using a minimal base image, you can significantly reduce the overall size of your final image, making it faster to build, push, pull, and deploy.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Inefficient Dockerfile&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Efficient Dockerfile&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FROM eclipse-temurin:8u412-b08-jre-windowsservercore-ltsc2022&lt;/td&gt;
&lt;td&gt;FROM eclipse-temurin:8u412-b08-jre-alpine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;eclipse-temurin:8u412-b08-jre-alpine&lt;/code&gt; image will be only 51.27 MB in size, whereas &lt;code&gt;eclipse-temurin:8u412-b08-jre-windowsservercore-ltsc2022&lt;/code&gt; will be 2.06 GB in size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a Non-Root User for Better Security
&lt;/h2&gt;

&lt;p&gt;By default, Docker containers run with the power of the root user. This lets them do all sorts of things, like tweaking system settings, installing software, or using special network ports. But here's the problem: that kind of power can be dangerous. If someone manages to hack into your application running inside the container, they might also gain root access to your whole system—not good!&lt;/p&gt;

&lt;p&gt;That's why it's considered a security best practice to run containers as a regular, non-root user whenever possible. This follows the principle of least privilege, which means only giving the application the bare minimum permissions it needs to do its job. It's like locking up your valuables when you're not using them—better safe than sorry!&lt;/p&gt;

&lt;p&gt;There are two main ways to achieve this in Docker:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;--user&lt;/code&gt; (or &lt;code&gt;-u&lt;/code&gt;) flag:&lt;/strong&gt; You can use this flag when running a container with &lt;code&gt;docker run&lt;/code&gt; to specify a different user ID (UID) or username to use inside the container.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;USER&lt;/code&gt; directive:&lt;/strong&gt; You can add this directive to your Dockerfile to set the default user for any containers created from that image.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By using either of these methods, you can significantly reduce the risk of a security breach in case your container is compromised.&lt;/p&gt;

&lt;p&gt;If you want to switch things up and run a Docker container as a different user than the default root user, you can use the &lt;code&gt;--user&lt;/code&gt; (or its shorthand &lt;code&gt;-u&lt;/code&gt;) flag when you start the container. This flag gives you the flexibility to specify either the username or the user ID (UID) of the user you want the container to run as.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1234 ubuntu:focal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous command, we set the user ID to 1234 when starting the Docker container. If we're using a user ID instead of a username, the actual user with that ID doesn't need to exist inside the container.&lt;/p&gt;

&lt;p&gt;Alternatively, you can use the &lt;code&gt;USER&lt;/code&gt; directive directly in your Dockerfile to specify the default user for your containers. This can be convenient, but keep in mind that you can always override this default setting later on by using the &lt;code&gt;--user&lt;/code&gt; flag when you run the container.&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; ubuntu:focal&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;useradd demo-user

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; demo-user&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; whoami&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous example, the &lt;code&gt;USER&lt;/code&gt; directive was added to the Dockerfile to set the default user as &lt;code&gt;demo-user&lt;/code&gt;. This indicates that any commands that follow the &lt;code&gt;USER&lt;/code&gt; directive in the Dockerfile will be executed as if the &lt;code&gt;demo-user&lt;/code&gt; is running them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using .dockerignore
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.dockerignore&lt;/code&gt; file is like a "do not disturb" list for Docker. It's a special text file you create in the same folder as your &lt;code&gt;Dockerfile&lt;/code&gt;. Inside this file, you list all the files and folders you &lt;em&gt;don't&lt;/em&gt; want Docker to include when it's building your image.&lt;/p&gt;

&lt;p&gt;Why is this important? Well, when you run &lt;code&gt;docker build&lt;/code&gt;, Docker first gathers everything in your project folder (called the "build context") and packages it up into a TAR archive. It then sends this archive to the Docker daemon, which actually builds the image.&lt;/p&gt;

&lt;p&gt;The first line you usually see in the &lt;code&gt;docker build&lt;/code&gt; output is "Sending build context to Docker daemon." This means Docker is uploading that whole package of files. If your project has a lot of files that aren't actually needed to build the image (like temporary files, logs, or large binaries), this can make the build process unnecessarily slow and bloat the size of your final image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sending build content to Docker daemon 18.6MB
Step 1/5 : FROM ubuntu:focal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever you build a Docker image, the entire build context—that's everything in your project's directory—gets sent over to the Docker daemon. This can take time and bandwidth, so it's smart to streamline things by excluding unnecessary files.&lt;/p&gt;

&lt;p&gt;That's where the &lt;code&gt;.dockerignore&lt;/code&gt; file comes in handy. It acts as a filter, telling Docker which files and folders to leave behind when packaging your build context. Not only does this save time and bandwidth, but it also helps you keep sensitive information like passwords and keys out of your Docker images.&lt;/p&gt;

&lt;p&gt;Creating a &lt;code&gt;.dockerignore&lt;/code&gt; file is easy – just place it in the root directory of your project (the same level as your &lt;code&gt;Dockerfile&lt;/code&gt;). Docker will automatically look for it and use its instructions to exclude unwanted files when building the image.&lt;/p&gt;

&lt;p&gt;The following is the content of a sample &lt;code&gt;.dockerignore&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Ignore the node_modules 
directory node_modules 

# Ignore log files 
*.log 

# Ignore test files 
**/*.test.js 

# Ignore configuration files 
.env 

# Ignore hidden files 
.* 

# Ignore build directories 
build/ 

# Ignore the Dockerfile itself (optional, but good practice) 
Dockerfile* 

# Ignore specific files 
temp.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example &lt;code&gt;.dockerignore&lt;/code&gt; file excludes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;node_modules&lt;/code&gt; directory (commonly used for Node.js dependencies).&lt;/li&gt;
&lt;li&gt;Any file ending in &lt;code&gt;.log&lt;/code&gt; (log files).&lt;/li&gt;
&lt;li&gt;Any JavaScript file ending in &lt;code&gt;.test.js&lt;/code&gt; (test files).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.env&lt;/code&gt; file (environment variables).&lt;/li&gt;
&lt;li&gt;All hidden files (files starting with a dot).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;build/&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;Any Dockerfile (optional, but recommended to prevent accidental inclusion).&lt;/li&gt;
&lt;li&gt;The file &lt;code&gt;temp.txt&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Minimizing Layers
&lt;/h2&gt;

&lt;p&gt;Every line in your Dockerfile translates to a new layer in your Docker image, and each layer adds to the overall size. To keep things lean, it's a good idea to create as few layers as possible. One way to do this is by combining multiple &lt;code&gt;RUN&lt;/code&gt; instructions whenever you can.&lt;/p&gt;

&lt;p&gt;Let's take a look at an example Dockerfile that installs &lt;code&gt;redis-server&lt;/code&gt; and &lt;code&gt;nginx&lt;/code&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; ubuntu:latest&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; redis-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we have three separate &lt;code&gt;RUN&lt;/code&gt; instructions: one to update the package list, one to install &lt;code&gt;nginx&lt;/code&gt; and another to install &lt;code&gt;redis-server&lt;/code&gt;. This creates three separate layers in the image.&lt;/p&gt;

&lt;p&gt;To optimize this, we can combine the three &lt;code&gt;RUN&lt;/code&gt; instructions into one:&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; ubuntu:latest&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; redis-server nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; operator, we tell Docker to execute all three commands in a single layer, reducing the overall size of the image. This is a simple yet effective way to make your Docker images more efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't Install Unnecessary Tools
&lt;/h2&gt;

&lt;p&gt;Avoiding unnecessary tools and dependencies is a key strategy for creating efficient Docker images. For example, if you're building an image for a production environment, you likely don't need debugging tools like &lt;code&gt;vim&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;, or &lt;code&gt;telnet&lt;/code&gt;. By leaving these out, you can significantly reduce the size of your image.&lt;/p&gt;

&lt;p&gt;Similarly, be mindful of the dependencies that your package manager installs. Some package managers, such as &lt;code&gt;apt&lt;/code&gt;, will automatically install additional "recommended" or "suggested" packages along with the ones you explicitly request. This can bloat your image unnecessarily. To prevent this, you can use the &lt;code&gt;--no-install-recommends&lt;/code&gt; flag with the &lt;code&gt;apt-get install&lt;/code&gt; command. This tells &lt;code&gt;apt&lt;/code&gt; to install only the essential packages you need and skip any optional extras.&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; ubuntu:latest&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous example, using the &lt;code&gt;--no-install-recommends&lt;/code&gt; flag when installing the &lt;code&gt;nginx&lt;/code&gt; package already helped us trim down the image size by about 10MB. But we can squeeze even more savings out of it!&lt;/p&gt;

&lt;p&gt;Besides avoiding recommended packages, we can also clear out the &lt;code&gt;apt&lt;/code&gt; package manager's cache to further shrink the final Docker image. This cache stores information about available packages, and while it's helpful during installation, it's not needed once the package is installed.&lt;/p&gt;

&lt;p&gt;To remove this cache, we can simply add a command to the end of 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; ubuntu:latest&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; nginx &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;rm -rf /var/lib/apt/lists/*&lt;/code&gt; part removes the cache directory and its contents. By doing this, we're getting rid of files that take up space but aren't essential for the image to function.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Creating Base Docker Images</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Mon, 22 Jul 2024 06:00:00 +0000</pubDate>
      <link>https://dev.to/kalkwst/creating-base-docker-images-5ao3</link>
      <guid>https://dev.to/kalkwst/creating-base-docker-images-5ao3</guid>
      <description>&lt;p&gt;Creating your base Docker image is quite straightforward. Just as we used the &lt;code&gt;docker commit&lt;/code&gt; command previously to create an image from a running container, we can also create an image from a system or server where our applications are originally hosted. However, it's crucial to keep the base image small and lightweight. Simply moving existing applications from traditional servers to Docker isn't enough.&lt;/p&gt;

&lt;p&gt;If you're using a production server, the resulting image could end up quite large. For smaller virtual machines that seem ideal for a base image, you can follow these steps to create one. Similar to the &lt;code&gt;docker commit&lt;/code&gt; command, this method works for any system you have access to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Custom Base Docker Images
&lt;/h3&gt;

&lt;p&gt;Let's take the Docker image we're already using (basic-server) and transform it into a custom base image. This is a simple demo, but the same process works for bigger, more complex setups.&lt;/p&gt;

&lt;p&gt;First, we need to start the container and connect to it at the same time. Here's how&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; basic-server sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Use the &lt;code&gt;docker image save&lt;/code&gt; command to create a backup of the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-o&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;basebackup.tar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;basic-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create a new image using the &lt;code&gt;docker load&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\basebackup.tar&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;This example has demonstrated how to generate a base image from an existing running system or environment. However, if your goal is to create a minimal base image, the next section will explain how to utilize the scratch image.&lt;/p&gt;

&lt;h2&gt;
  
  
  The SCRATCH Image
&lt;/h2&gt;

&lt;p&gt;The scratch image is Docker's secret weapon for building incredibly lean and efficient images. It's essentially a blank slate, devoid of any operating system or pre-installed software, making it the perfect starting point for running compiled binary applications like those written in Java or C++. These binaries are self-contained and don't rely on external libraries or dependencies, allowing them to run directly on the minimal environment provided by the scratch image.&lt;/p&gt;

&lt;p&gt;By using the &lt;code&gt;FROM scratch&lt;/code&gt; directive in your Dockerfile, you signal to Docker that you want to build an image with the absolute minimum footprint. This results in a container image that is remarkably small and lightweight, as it doesn't include any unnecessary overhead from an operating system or additional software layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the SCRATCH Image
&lt;/h3&gt;

&lt;p&gt;In this example, we'll craft a simple C application to showcase how to build one of the most minimal Docker base images possible. While prior knowledge of C programming isn't required, this hands-on experience will demonstrate the process of creating a highly efficient and compact image using Docker's "scratch" image as our foundation. By leveraging the scratch image, we can eliminate any unnecessary dependencies and produce an image with a remarkably small footprint. Let's get started.&lt;/p&gt;

&lt;p&gt;Try getting the scratch image using the &lt;code&gt;docker pull&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scratch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will notice that you will get an error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Using default tag: latest
Error response from daemon: 'scratch' is a reserved name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create a C program that we will build into the image to use in our &lt;strong&gt;Dockerfile&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;christmas-tree.c&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Add the following C code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spaces&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spaces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;spaces&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;spaces&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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;This C program crafts a Christmas tree composed of asterisks based on user-specified height. It employs a series of nested loops to achieve this. The outer loop controls the number of rows, starting from the top of the tree and iterating downwards. For each row, an inner loop determines the number of spaces preceding the asterisks, ensuring the tree's triangular shape.&lt;/p&gt;

&lt;p&gt;Another inner loop handles the actual printing of asterisks, with the number of asterisks increasing by two with each descending row. This creates the tree's wider base. A newline character is printed after each row, moving the cursor to the next line for the subsequent row's output. The result is a beautifully constructed Christmas tree in the console, ready to spread festive cheer!&lt;/p&gt;




&lt;p&gt;Build the image form the command line by running the following command to build the C program&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcc &lt;span class="nt"&gt;-o&lt;/span&gt; christmas-tree christmas-tree.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Now lets create the &lt;strong&gt;Dockerfile&lt;/strong&gt;. The &lt;strong&gt;Dockerfile&lt;/strong&gt; will be pretty minimal but needs to start with &lt;code&gt;FROM scratch&lt;/code&gt;. The rest of the file will add the C program and then run it&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; scratch&lt;/span&gt;

&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; christmas-tree.exe /&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/christmas-tree.exe"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Build a new image. In this instance, call the image &lt;code&gt;scratch-test&lt;/code&gt; using the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scratch-test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the image from the command line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scratch-test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the &lt;code&gt;docker images&lt;/code&gt; command for your new image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scratch-test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will show some pretty impressive results with a size of &lt;code&gt;41.2kB&lt;/code&gt; in size&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
scratch-test   latest    817c50b9dbe6   3 minutes ago   41.2kB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;View the layers of the image using the &lt;code&gt;docker history&lt;/code&gt; command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;scratch-test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see a similar output to the following one&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IMAGE          CREATED         CREATED BY                            SIZE      COMMENT
817c50b9dbe6   5 minutes ago   CMD ["./christmas-tree.exe"]          0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      5 minutes ago   CMD ["ls"]                            0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      5 minutes ago   ADD christmas-tree.exe / # buildkit   41.2kB    buildkit.dockerfile.v0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, the scratch image we whipped up in this exercise shows how to make a Docker image that's both super-lean and still gets the job done. It also proves that if you put some thought into your goals, you can easily speed up your builds and shrink those images down to size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Creating a base Docker image is a straightforward process, but it's important to keep the image small and lightweight. You can create a custom base image from an existing running system or environment using the &lt;code&gt;docker save&lt;/code&gt; and &lt;code&gt;docker load&lt;/code&gt; commands. &lt;/p&gt;

&lt;p&gt;Additionally, Docker's "scratch" image is a powerful tool for building minimal and efficient images, especially for compiled binary applications. By using the &lt;code&gt;FROM scratch&lt;/code&gt; directive in your Dockerfile, you can create a container image with the absolute minimum footprint. This results in a remarkably small and lightweight image that eliminates unnecessary dependencies.&lt;/p&gt;

&lt;p&gt;Overall, by following these techniques, you can optimize your Docker images for better performance and efficiency.&lt;/p&gt;

&lt;p&gt;In the next post, let's take a breather from building for a moment and dive into the world of naming and tagging our Docker creations. After all, organizing our work is just as important as creating it&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Understanding CMD, ENTRYPOINT and RUN in Docker - An Interlude Post</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Fri, 19 Jul 2024 10:46:43 +0000</pubDate>
      <link>https://dev.to/kalkwst/interlude-1-cmd-entrypoint-and-run-2926</link>
      <guid>https://dev.to/kalkwst/interlude-1-cmd-entrypoint-and-run-2926</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclaimer: This post is heavily inspired by &lt;a href="https://www.docker.com/blog/docker-best-practices-choosing-between-run-cmd-and-entrypoint/?ref=dailydev" rel="noopener noreferrer"&gt;this post on the Docker Blog&lt;/a&gt;. It is also seeks to answer questions raised by the community in previous posts in this series.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Docker is undeniably a powerful tool for containerization, but its versatility and extensive feature set can be overwhelming, especially for newcomers. The platform offers multiple ways to achieve similar goals, making it crucial for users to carefully weigh the advantages and disadvantages of each option to determine the best approach for their specific projects. This decision-making process requires a solid understanding of Docker's underlying mechanisms and the trade-offs involved in different strategies.&lt;/p&gt;

&lt;p&gt;Docker can be a bit of a maze, especially when it comes to the nitty-gritty of instructions like &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt;, and &lt;code&gt;ENTRYPOINT&lt;/code&gt;. These three instructions often cause confusion, as they seem to have overlapping functions. But fear not! In this article, we'll unravel the mystery, clearly explaining the differences between them and highlighting when to use each one effectively. By the end, you'll be a Dockerfile ninja, wielding these instructions with confidence and precision!&lt;/p&gt;

&lt;h3&gt;
  
  
  The RUN Directive
&lt;/h3&gt;

&lt;p&gt;In Docker, the &lt;code&gt;RUN&lt;/code&gt; directive is a key instruction used in Dockerfiles to run commands while building your Docker image. It's your go-to tool for setting up the environment inside the image, allowing you to install packages, update dependencies, configure settings, and basically do anything you need to make your image ready to use. Think of it as the stage where you get everything in place before the curtain goes up on your running container.&lt;/p&gt;

&lt;p&gt;Here is an example of the &lt;code&gt;RUN&lt;/code&gt; directive in practice:&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; ubuntu:20.04&lt;/span&gt;

&lt;span class="c"&gt;# Update package lists and install necessary packages&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    curl &lt;span class="se"&gt;\
&lt;/span&gt;    wget &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we use the &lt;code&gt;RUN&lt;/code&gt; instruction to update the package lists on your system and install &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;wget&lt;/code&gt;. These are handy tools for downloading files and interacting with websites from the command line.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;/code&gt; part is added for cleanup. It removes the cached package lists after the installation, making your final Docker image smaller and leaner. Since these lists aren't needed to run your application, it's good practice to get rid of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CMD Directive
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;CMD&lt;/code&gt; instruction in your Dockerfile sets the default command that will run when you start a container from your image. It's like a pre-set option for what your container should do when it "boots up." But here's the key: you can easily change this default behavior by providing different command-line arguments when you run the container using &lt;code&gt;docker run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt; is perfect for situations where you want to provide a sensible default behavior for your container, but also give users the flexibility to customize it. Think of it as setting up a suggested starting point, but allowing users to take the wheel if they want to go in a different direction.&lt;/p&gt;

&lt;p&gt;It's a common practice to use &lt;code&gt;CMD&lt;/code&gt; in Docker images to define default parameters or configurations that can be easily overridden by the user when running the container.&lt;/p&gt;

&lt;p&gt;For example, by default, you might want to start a web server to start, but also allow users to override this and run a shell instead:&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:alpine&lt;/span&gt;

[...dockerfile truncated...]

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  The ENTRYPOINT Directive
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ENTRYPOINT&lt;/code&gt; instruction sets the main executable for your container. It's similar to &lt;code&gt;CMD&lt;/code&gt; but with a key difference: when you run a container with &lt;code&gt;docker run&lt;/code&gt;, the command you provide doesn't replace the &lt;code&gt;ENTRYPOINT&lt;/code&gt; command. Instead, your command gets &lt;em&gt;added to&lt;/em&gt; the &lt;code&gt;ENTRYPOINT&lt;/code&gt; command as arguments.&lt;/p&gt;

&lt;p&gt;Think of it like this: &lt;code&gt;ENTRYPOINT&lt;/code&gt; sets the core command your container is designed to run, while any arguments you provide with &lt;code&gt;docker run&lt;/code&gt; become extra instructions for that command.&lt;/p&gt;

&lt;p&gt;For example, the following Dockerfile will run the webserver no matter what the users provide as arguments:&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:alpine&lt;/span&gt;

[...dockerfile truncated...]

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  So CMD or ENTRYPOINT?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CMD&lt;/code&gt; and &lt;code&gt;ENTRYPOINT&lt;/code&gt; are special instructions in a Dockerfile. While other instructions execute when you &lt;em&gt;build&lt;/em&gt; the image, these two come into play when you actually &lt;em&gt;run&lt;/em&gt; a container from that image.&lt;/p&gt;

&lt;p&gt;Essentially, when a container starts, Docker needs to know what it should do – what program to run, and how to run it. That's where &lt;code&gt;CMD&lt;/code&gt; and &lt;code&gt;ENTRYPOINT&lt;/code&gt; step in. They tell Docker what the main process inside the container is and how to start it.&lt;/p&gt;

&lt;p&gt;Now, the difference between them is a bit tricky, and many people don't fully grasp it. Luckily, most of the time, your container will work fine even if you don't use them perfectly. However, understanding the nuances can make things a lot smoother and less confusing.&lt;/p&gt;

&lt;p&gt;To get a better handle on this, let's break down a typical Linux command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping &lt;span class="nt"&gt;-c&lt;/span&gt; 10 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;ping&lt;/code&gt; is the command itself, and the rest (&lt;code&gt;-c 10 127.0.0.1&lt;/code&gt;) are the parameters or arguments we're giving to that command.&lt;/p&gt;

&lt;p&gt;Now, back to Docker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ENTRYPOINT&lt;/code&gt;:&lt;/strong&gt; This is where you define the &lt;em&gt;command&lt;/em&gt; part of the expression. It's the core thing you want your container to do when it starts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CMD&lt;/code&gt;:&lt;/strong&gt; This is where you define the &lt;em&gt;parameters&lt;/em&gt; for that command. They're the additional instructions you want to give it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, a Dockerfile that uses Alpine Linux as the base image and wants to run the &lt;code&gt;ping&lt;/code&gt; command could look 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; alpine:latest&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["ping"]&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["-c", "10", "127.0.0.1"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now build an image called &lt;code&gt;pinger&lt;/code&gt; from the preceding Dockerfile, as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image build &lt;span class="nt"&gt;-t&lt;/span&gt; pinger &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can run a container from the &lt;code&gt;pinger&lt;/code&gt; image we just created like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pinger
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.047 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.056 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.038 ms
64 bytes from 127.0.0.1: seq=3 ttl=64 time=0.055 ms
64 bytes from 127.0.0.1: seq=4 ttl=64 time=0.037 ms
64 bytes from 127.0.0.1: seq=5 ttl=64 time=0.036 ms
64 bytes from 127.0.0.1: seq=6 ttl=64 time=0.053 ms
64 bytes from 127.0.0.1: seq=7 ttl=64 time=0.058 ms
64 bytes from 127.0.0.1: seq=8 ttl=64 time=0.048 ms
64 bytes from 127.0.0.1: seq=9 ttl=64 time=0.053 ms

--- 127.0.0.1 ping statistics ---
10 packets transmitted, 10 packets received, 0% packet loss
round-trip min/avg/max = 0.036/0.048/0.058 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The great thing about this setup is that you can easily override the default &lt;code&gt;CMD&lt;/code&gt; parameters you've set in the Dockerfile. If you remember, we originally defined &lt;code&gt;CMD ["-c", "10", "127.0.0.1"]&lt;/code&gt; to ping a specific address three times.&lt;/p&gt;

&lt;p&gt;But now, when you create a new container, you can simply add different values at the end of your &lt;code&gt;docker run&lt;/code&gt; command to change the ping target or the number of pings. This gives you a lot of flexibility while still keeping a consistent base command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker container run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pinger &lt;span class="nt"&gt;-w&lt;/span&gt; 5 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: seq=0 ttl=64 time=0.034 ms
64 bytes from 127.0.0.1: seq=1 ttl=64 time=0.053 ms
64 bytes from 127.0.0.1: seq=2 ttl=64 time=0.040 ms
64 bytes from 127.0.0.1: seq=3 ttl=64 time=0.038 ms
64 bytes from 127.0.0.1: seq=4 ttl=64 time=0.056 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will cause the container to ping the loopback IP address (127.0.0.1) for 5 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Directive Description and Use Cases
&lt;/h3&gt;

&lt;p&gt;The following table provides an overview of these commands and use cases.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Directive&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Description&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Syntax Example&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RUN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes commands during the image build process. It is used to install software packages, configure settings, or perform other setup tasks. The result is part of the image layers.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y nginx&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;- Installing software packages&lt;br&gt;- Configuring files or repositories&lt;br&gt;- Setting up environment variables or system settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CMD&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Specifies the default command to run when a container starts from the image. It can be overridden by providing a command in &lt;code&gt;docker run&lt;/code&gt;. It is generally used to provide default arguments to &lt;code&gt;ENTRYPOINT&lt;/code&gt; or to set a default command.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CMD ["nginx", "-g", "daemon off;"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;- Setting a default command for the container&lt;br&gt;- Providing default arguments to &lt;code&gt;ENTRYPOINT&lt;/code&gt;&lt;br&gt;- Running a service or application by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ENTRYPOINT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Defines the main command that is always executed when a container starts. It is not overridden by arguments provided in &lt;code&gt;docker run&lt;/code&gt; unless you use the &lt;code&gt;--entrypoint&lt;/code&gt; flag. Useful for ensuring a specific command is always run.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ENTRYPOINT ["nginx", "-g", "daemon off;"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ensuring a specific command is always executed&lt;br&gt;- Running a primary process or service&lt;br&gt;- Used in combination with &lt;code&gt;CMD&lt;/code&gt; to provide default arguments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Unix Signaling and the PID 1
&lt;/h2&gt;

&lt;p&gt;In the world of Unix-like systems, including Docker containers, PID 1 is a special process – the very first one that starts up. Every other process inside the system is its child, creating a family tree of processes with PID 1 at the top.&lt;/p&gt;

&lt;p&gt;In Docker, this PID 1 process is really important because it's responsible for managing everything else inside the container. One of its critical roles is handling signals (like &lt;code&gt;SIGTERM&lt;/code&gt;) from the host system, which are essentially messages that tell the container to do something (like gracefully shut down).&lt;/p&gt;

&lt;p&gt;Now, when you use the shell form for a Docker command, a shell process (usually &lt;code&gt;/bin/sh -c&lt;/code&gt;) takes over as PID 1. The problem is that this shell process isn't very good at handling signals. It might not pass them along to your actual application, which can lead to issues like containers not shutting down cleanly.&lt;/p&gt;

&lt;p&gt;On the other hand, the exec form lets you run your command directly as PID 1, without any shell in between. This way, the command itself receives and handles signals directly, making the whole thing much more reliable.&lt;/p&gt;

&lt;p&gt;So, if your container needs to react to signals promptly and gracefully, the exec form is the way to go. It's particularly crucial for applications that need to respond to events or interruptions, ensuring that everything shuts down properly and your data stays safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shell and exec forms
&lt;/h2&gt;

&lt;p&gt;When you're working with Dockerfiles and setting commands for &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt;, and &lt;code&gt;ENTRYPOINT&lt;/code&gt;, you have two ways to do it: the &lt;code&gt;shell&lt;/code&gt; form and the &lt;code&gt;exec&lt;/code&gt; form. Each has its own strengths and weaknesses, and understanding the difference is key to effectively managing your Docker containers.&lt;/p&gt;

&lt;h4&gt;
  
  
  Shell Form
&lt;/h4&gt;

&lt;p&gt;The shell form is like writing commands the old-school way, similar to what you'd type in a terminal. When Docker sees a command in shell form, it essentially runs it through a shell (usually &lt;code&gt;/bin/sh -c&lt;/code&gt; for Unix-based images). This means the command gets interpreted by the shell, which unlocks some handy features like using environment variables and chaining commands together.&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;CMD&lt;/span&gt;&lt;span class="s"&gt; echo "Hello, World!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way of writing commands, using the shell form, means that Docker executes the command &lt;code&gt;echo "Hello, World!"&lt;/code&gt; through the default shell of your container. This can be handy for simple commands or when you need to use special shell features. However, it has a drawback: it doesn't handle signals (like those used for managing processes) the same way as the exec form.&lt;/p&gt;

&lt;p&gt;In other words, if your command needs to respond properly to Unix signals for things like stopping or restarting processes gracefully, the shell form might not be the best choice. It's a bit like having a middleman who might not deliver your messages as reliably as you'd like.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exec Form
&lt;/h3&gt;

&lt;p&gt;Now, let's talk about the exec form. It's a more specific and reliable way to define commands in your Dockerfile. Instead of writing the whole command as a single line, you break it up into a JSON array, where each part of the command (the command itself and its arguments) is a separate element in the array.&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;CMD&lt;/span&gt;&lt;span class="s"&gt; ["echo", "Hello, World!"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, instead of using a shell as a middleman, Docker directly executes the command &lt;code&gt;echo&lt;/code&gt; with the argument &lt;code&gt;"Hello, World!"&lt;/code&gt;. This direct execution method is more reliable because it doesn't depend on the shell's interpretation.&lt;/p&gt;

&lt;p&gt;This has a few key advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Better Signal Handling:&lt;/strong&gt; The command itself can receive and respond to Unix signals directly. This is crucial for ensuring your application can gracefully shut down or restart when needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Precise Argument Passing:&lt;/strong&gt; You can be confident that the arguments you provide are passed directly to your command without any potential modifications or interference from the shell.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Differences between Shell and Exec
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Shell Form&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Exec Form&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Form&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Commands without square brackets (&lt;code&gt;[]&lt;/code&gt;). Run by the container's shell&lt;/td&gt;
&lt;td&gt;Commands with square brackets (&lt;code&gt;[]&lt;/code&gt;). Run directly, not through a shell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Variable Substitution&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Inherits environment variables from the shell, such as &lt;code&gt;$HOME&lt;/code&gt; and &lt;code&gt;$PATH&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Does not inherit shell environment variables but behaves the same for &lt;code&gt;ENV&lt;/code&gt; instruction variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Shell Features&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Supports sub-commands, piping output, chaining commands, I/O redirection, etc.&lt;/td&gt;
&lt;td&gt;Does not support shell features&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Signal Trapping &amp;amp; Forwarding&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Most shells do not forward process signals to child processes.&lt;/td&gt;
&lt;td&gt;Directly traps and forwards signals like &lt;code&gt;SIGINT&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Usage with ENTRYPOINT&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Can cause issues with signal forwarding&lt;/td&gt;
&lt;td&gt;Recommended due to better signal handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Usage as ENTRYPOINT Params&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Not possible with the shell form&lt;/td&gt;
&lt;td&gt;If the first item in the array is not a command, all items are used as parameters for the &lt;code&gt;ENTRYPOINT&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The diagram below, will help you decide if you need &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt; or &lt;code&gt;ENTRYPOINT&lt;/code&gt; in your Dockerfile.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fm7r7n2uz44rqcgv8fv1f.jpg" class="article-body-image-wrapper"&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%2Farticles%2Fm7r7n2uz44rqcgv8fv1f.jpg" alt="Image description" width="800" height="254"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram below, will help you decide if you need &lt;code&gt;shell&lt;/code&gt; or &lt;code&gt;exec&lt;/code&gt; form for your commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fape4z5dbd3efez7o96me.jpg" class="article-body-image-wrapper"&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%2Farticles%2Fape4z5dbd3efez7o96me.jpg" alt="Image description" width="800" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find high resolution diagrams &lt;a href="https://github.com/Kalkwst/Docker-Workshop-Repository/tree/master/diagrams" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running CMD and ENTRYPOINT
&lt;/h2&gt;

&lt;p&gt;The following examples will walk you through the high-level differences between &lt;code&gt;CMD&lt;/code&gt; and &lt;code&gt;ENTRYPOINT&lt;/code&gt;, through an example&lt;/p&gt;

&lt;p&gt;First lets create our Dockerfile&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="c"&gt;# Use the Ubuntu 20.04 image as the base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:20.04&lt;/span&gt;

&lt;span class="c"&gt;# Update the image and install traceroute&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; traceroute

&lt;span class="c"&gt;# Set the default command&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; traceroute&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then build your image using the &lt;code&gt;docker build -t tracert&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the container image with &lt;code&gt;CMD traceroute&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Without passing any arguments, we get the following output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run tracert

Usage:
  traceroute &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-46dFITnreAUDV&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; first_ttl &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; gate,... &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; device &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; max_ttl &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-N&lt;/span&gt; squeries &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; port &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; tos &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; flow_label &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; MAX,HERE,NEAR &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; nqueries &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; src_addr &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; sendwait &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;--fwmark&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;num &lt;span class="o"&gt;]&lt;/span&gt; host &lt;span class="o"&gt;[&lt;/span&gt; packetlen &lt;span class="o"&gt;]&lt;/span&gt;
Options:
  &lt;span class="nt"&gt;-4&lt;/span&gt;                          Use IPv4
  &lt;span class="nt"&gt;-6&lt;/span&gt;                          Use IPv6
  &lt;span class="nt"&gt;-d&lt;/span&gt;  &lt;span class="nt"&gt;--debug&lt;/span&gt;                 Enable socket level debugging
  &lt;span class="nt"&gt;-F&lt;/span&gt;  &lt;span class="nt"&gt;--dont-fragment&lt;/span&gt;         Do not fragment packets
  &lt;span class="nt"&gt;-f&lt;/span&gt; first_ttl  &lt;span class="nt"&gt;--first&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;first_ttl
                              Start from the first_ttl hop &lt;span class="o"&gt;(&lt;/span&gt;instead from 1&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-g&lt;/span&gt; gate,...  &lt;span class="nt"&gt;--gateway&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gate,...
                              Route packets through the specified gateway
                              &lt;span class="o"&gt;(&lt;/span&gt;maximum 8 &lt;span class="k"&gt;for &lt;/span&gt;IPv4 and 127 &lt;span class="k"&gt;for &lt;/span&gt;IPv6&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-I&lt;/span&gt;  &lt;span class="nt"&gt;--icmp&lt;/span&gt;                  Use ICMP ECHO &lt;span class="k"&gt;for &lt;/span&gt;tracerouting
  &lt;span class="nt"&gt;-T&lt;/span&gt;  &lt;span class="nt"&gt;--tcp&lt;/span&gt;                   Use TCP SYN &lt;span class="k"&gt;for &lt;/span&gt;tracerouting &lt;span class="o"&gt;(&lt;/span&gt;default port is 80&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nt"&gt;-i&lt;/span&gt; device  &lt;span class="nt"&gt;--interface&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;device
                              Specify a network interface to operate with
  &lt;span class="nt"&gt;-m&lt;/span&gt; max_ttl  &lt;span class="nt"&gt;--max-hops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;max_ttl
                              Set the max number of hops &lt;span class="o"&gt;(&lt;/span&gt;max TTL to be
                              reached&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Default is 30
&lt;span class="o"&gt;[&lt;/span&gt;...output truncated...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, if we provide an IP address to test, we will get an error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run tracert www.dev.to                                                      docker: Error response from daemon: failed to create task &lt;span class="k"&gt;for &lt;/span&gt;container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: &lt;span class="nb"&gt;exec&lt;/span&gt;: &lt;span class="s2"&gt;"www.dev.to"&lt;/span&gt;: executable file not found &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$PATH&lt;/span&gt;: unknown.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that the string you're providing on the command line, &lt;code&gt;www.dev.to&lt;/code&gt;, is completely replacing the &lt;code&gt;CMD&lt;/code&gt; instruction in your Dockerfile. Since that URL isn't a valid command, it's causing an error.&lt;/p&gt;

&lt;p&gt;To fix this, you need to specify the actual command you want to run alongside the URL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run tracert traceroute www.dev.to
traceroute to www.dev.to &lt;span class="o"&gt;(&lt;/span&gt;104.18.26.242&lt;span class="o"&gt;)&lt;/span&gt;, 30 hops max, 60 byte packets
&lt;span class="o"&gt;[&lt;/span&gt;...output truncated...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run the container image with &lt;code&gt;ENTRYPOINT traceroute&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;In this updated version, we'll make a change to the Dockerfile. We will remove the original &lt;code&gt;CMD&lt;/code&gt; instruction and replace it with &lt;code&gt;ENTRYPOINT ["traceroute"]&lt;/code&gt;. This means the &lt;code&gt;traceroute&lt;/code&gt; command is now the main command that this container will run when it starts.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ENTRYPOINT&lt;/code&gt; instruction works a bit differently than &lt;code&gt;CMD&lt;/code&gt;. With &lt;code&gt;ENTRYPOINT&lt;/code&gt;, you can't simply override the command by typing something different after &lt;code&gt;docker run&lt;/code&gt;. Instead, any extra arguments you add after &lt;code&gt;docker run&lt;/code&gt; are treated as arguments for the &lt;code&gt;traceroute&lt;/code&gt; command itself.&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="c"&gt;# Use the Ubuntu 20.04 image as the base image&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:20.04&lt;/span&gt;

&lt;span class="c"&gt;# Update the image and install traceroute&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; traceroute

&lt;span class="c"&gt;# Set the default command&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["traceroute"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see what happens when we try that&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run tracert www.dev.to
traceroute to www.dev.to &lt;span class="o"&gt;(&lt;/span&gt;104.18.26.242&lt;span class="o"&gt;)&lt;/span&gt;, 30 hops max, 60 byte packets
&lt;span class="o"&gt;[&lt;/span&gt;...output truncated...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key point here is to use &lt;code&gt;ENTRYPOINT&lt;/code&gt; when you want to ensure that a specific executable is always run when your container starts, regardless of any additional commands or arguments the user might provide. It gives you a way to create containers that behave like self-contained tools, with a clearly defined main purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Deciding when to use &lt;code&gt;RUN&lt;/code&gt;, &lt;code&gt;CMD&lt;/code&gt;, or &lt;code&gt;ENTRYPOINT&lt;/code&gt;, and whether to choose the shell or exec form, demonstrates the level of detail and flexibility Docker offers. Each of these commands plays a distinct role in the Docker ecosystem, influencing how containers are constructed, behave, and interact with their environment.&lt;/p&gt;

&lt;p&gt;By carefully selecting the right command and form for each specific situation, developers can create Docker images that are more dependable, secure, and optimized for efficiency. Mastering these Docker commands and their formats is essential for unlocking the full potential of Docker. When these best practices are followed, applications deployed within Docker containers can achieve peak performance across diverse environments, enhancing both development workflows and production deployments.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Natural Numbers</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Wed, 17 Jul 2024 07:20:00 +0000</pubDate>
      <link>https://dev.to/kalkwst/natural-numbers-4em4</link>
      <guid>https://dev.to/kalkwst/natural-numbers-4em4</guid>
      <description>&lt;p&gt;What is a number?&lt;/p&gt;

&lt;p&gt;In mathematics, there are several ways to approach this question. We can look at it &lt;strong&gt;semantically&lt;/strong&gt;, by understanding what numbers mean. Alternatively, we can take the &lt;strong&gt;axiomatic&lt;/strong&gt; approach, focusing on their fundamental properties and behaviors. Or, we can answer the question &lt;strong&gt;constructively&lt;/strong&gt; by looking at how numbers can be constructed from simpler objects. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Semantic Approach
&lt;/h2&gt;

&lt;p&gt;Let's begin with semantics. What do numbers really mean? Many people assume that numbers are just a tool for counting things, but their meaning is more nuanced. Numbers can serve two different purposes depending on their context.&lt;/p&gt;

&lt;p&gt;There are two main types of numbers: &lt;em&gt;cardinal&lt;/em&gt; and &lt;em&gt;ordinal&lt;/em&gt;. For example, when we see the number &lt;code&gt;3&lt;/code&gt;, it's meaning is not immediately clear. It could mean &lt;code&gt;3&lt;/code&gt; as in &lt;em&gt;I have three eggplants&lt;/em&gt;, or it could mean &lt;code&gt;3&lt;/code&gt; as in &lt;em&gt;I want the third eggplant&lt;/em&gt;. The &lt;code&gt;3&lt;/code&gt; in &lt;em&gt;three eggplants&lt;/em&gt; is a &lt;strong&gt;cardinal&lt;/strong&gt; number, and the &lt;code&gt;3&lt;/code&gt; in &lt;em&gt;third eggplant&lt;/em&gt; is an &lt;strong&gt;ordinal&lt;/strong&gt; number.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;cardinal&lt;/em&gt; number counts &lt;strong&gt;how many objects&lt;/strong&gt; there are in a group. When we say &lt;em&gt;I want **three&lt;/em&gt;* eggplants*, that &lt;em&gt;three&lt;/em&gt; is a cardinal. An &lt;em&gt;ordinal&lt;/em&gt; number counts where a particular object is in a group. When we say &lt;em&gt;I want the **third&lt;/em&gt;* eggplant*, that &lt;em&gt;three&lt;/em&gt; is an ordinal.&lt;/p&gt;

&lt;p&gt;The cardinal/ordinal distinction really starts to make sense when you talk about the set theoretic basis of math. For now, the basic idea is enough: &lt;em&gt;cardinals&lt;/em&gt; &lt;strong&gt;count objects&lt;/strong&gt;, &lt;em&gt;ordinals&lt;/em&gt; &lt;strong&gt;position them&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Axiomatic Approach
&lt;/h2&gt;

&lt;p&gt;The axiomatic part is a lot more interesting. Here, we define concepts like numbers using a set of rules, known as &lt;em&gt;axioms&lt;/em&gt;. These axioms lay out how the numbers (or whatever we're working with) act. Mathematicians love axiomatic definitions because they eliminate any confusion about what's possible and how it works. While they might not be as intuitive, they're incredibly precise and perfect for logical reasoning.&lt;/p&gt;

&lt;p&gt;Let's begin our exploration of numbers with the most basic kind: the natural (or counting) numbers. These are the whole numbers we first learn as kids. They start with zero and going on forever: 0, 1, 2, 3, 4, and so on. They are like the building blocks of all numbers, and for computer scientists, they're particularly special because everything we can compute is ultimately based on them. Formally, these numbers are defined by a set of rules called &lt;strong&gt;Peano arithmetic&lt;/strong&gt;, which lays out exactly how they work.&lt;/p&gt;

&lt;p&gt;Peano arithmetic specifies a list of axioms that define the natural numbers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Initial Value Rule&lt;/em&gt;: There is one special object called &lt;code&gt;0&lt;/code&gt;, and &lt;code&gt;0&lt;/code&gt; is a natural number.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Successor Rule&lt;/em&gt;: Every natural number &lt;code&gt;n&lt;/code&gt; has a successor in the natural numbers, called its &lt;em&gt;successor&lt;/em&gt;, &lt;code&gt;s(n)&lt;/code&gt;. Zero is the only natural number that is not the successor of any natural number.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Uniqueness Rule&lt;/em&gt;: If the successor of two natural numbers is the same, then the two original numbers are the same.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Equality Rules&lt;/em&gt;: While these are not considered to be part of the Peano axioms in modern treatments, the following rules describe the equality relation:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Reflexive&lt;/em&gt;: For every natural number &lt;code&gt;x&lt;/code&gt; then &lt;code&gt;x = x&lt;/code&gt;. Or in other words, every natural number is equal to itself.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Symmetric&lt;/em&gt;: For all natural numbers &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt;, if &lt;code&gt;x = y&lt;/code&gt; then &lt;code&gt;y = x&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Transitive&lt;/em&gt;: For all natural numbers &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt; and &lt;code&gt;z&lt;/code&gt;, if &lt;code&gt;x = y&lt;/code&gt; and &lt;code&gt;y = z&lt;/code&gt;, then &lt;code&gt;x = z&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Closed under equality&lt;/em&gt;: For all &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;, if &lt;code&gt;b&lt;/code&gt; is a natural number and &lt;code&gt;a = b&lt;/code&gt;, then &lt;code&gt;a&lt;/code&gt; is also a natural number.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;em&gt;Induction Rule&lt;/em&gt;: For some statement &lt;code&gt;P&lt;/code&gt;, &lt;code&gt;P&lt;/code&gt; is true for all natural numbers if

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;P&lt;/code&gt; is true about 0 (that is, &lt;code&gt;P(0)&lt;/code&gt; is true)&lt;/li&gt;
&lt;li&gt;If you &lt;em&gt;assume&lt;/em&gt; &lt;code&gt;P&lt;/code&gt; is true for a natural number &lt;code&gt;n&lt;/code&gt; (&lt;code&gt;P(n)&lt;/code&gt; is true), then you can &lt;em&gt;prove&lt;/em&gt; that &lt;code&gt;P&lt;/code&gt; is true for the successor &lt;code&gt;s(n)&lt;/code&gt; of &lt;code&gt;n&lt;/code&gt; (or &lt;code&gt;P(s(n))&lt;/code&gt; is true)&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;And of the above is just a fancy way of saying that natural numbers are numbers with no fractional part starting at &lt;code&gt;0&lt;/code&gt;. Most people get the Peano rules right away, except for the last one about induction. It's a tricky concept – it can feel a bit circular at first. But it's crucial because natural numbers go on forever. Induction is the tool that lets us take what we know about a finite number of things and apply it to the whole infinite set.&lt;/p&gt;

&lt;p&gt;Forgetting the technical jargon, the induction rule basically says: if something works for your first number, and you have a way to describe what happens when you add 1 to it, you can apply this pattern to all the numbers that follow. This pattern lets us write proofs that hold true for all natural numbers, or even define things that work for all of them. We can use similar techniques to tackle all integers, fractions, or even all real numbers. Let's start with an example of a definition, which is easier to understand than a proof.&lt;/p&gt;

&lt;p&gt;Let's illustrate how induction is used in a definition by looking at addition. Addition is simply a function "+" that takes two natural numbers and produces another natural number, their sum. We can formally define addition with the following rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Commutativity&lt;/em&gt;: For any pair of natural numbers &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;,


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n+m=m+nn + m = m + n
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;m&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;m&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;or in simple terms, it means you can swap the numbers around, and the result stays the same.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Identity&lt;/em&gt;: For any natural number &lt;code&gt;n&lt;/code&gt;,

&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n+0=0+n=nn + 0 = 0 + n = n
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;or in simple terms, adding &lt;code&gt;0&lt;/code&gt; to any number doesn't change the value.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Recursion&lt;/em&gt;: For any natural number &lt;code&gt;n&lt;/code&gt;,

&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;m+s(n)=s(m+n)m + s(n) = s (m + n)
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;m&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;m&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last rule is based on a concept called recursion. It can be a bit tricky if you're not familiar with it, so let's break it down. &lt;/p&gt;

&lt;p&gt;Essentially, we're defining addition by using the Peano arithmetic concept of adding 1 to a number. If we rewrite the rule slightly, it becomes: &lt;br&gt;

&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;m+n=1+(m+(n−1)).
m + n = 1 + (m + (n - 1)). 
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;m&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;m&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;−&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mclose"&gt;))&lt;/span&gt;&lt;span class="mord"&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
&lt;/p&gt;

&lt;p&gt;Remember, this is a definition, not a procedure. It explains what addition means, not how to calculate it. &lt;/p&gt;

&lt;p&gt;This rule works because of the Peano induction rule. Without it, we wouldn't have a way to define addition for any two numbers. Induction gives us a way to express the meaning of addition for any pair of natural numbers. &lt;/p&gt;

&lt;p&gt;Now, let's tackle a proof. I know proofs can seem intimidating, but don't worry! They're not so bad, and we'll start with a very simple one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Peano's Induction
&lt;/h3&gt;

&lt;p&gt;Let's have some fun with a simple proof using natural numbers and addition. Suppose we have a natural number N. Can you guess the sum of all the integers from 1 to N? It's actually N times (N + 1) divided by 2. Let's prove this using induction.&lt;/p&gt;

&lt;p&gt;First, we start with a base case, a starting point we can prove on its own. In this case, our base case is &lt;code&gt;0&lt;/code&gt; because the first part of the induction rule requires us to show it works for &lt;code&gt;0&lt;/code&gt;. Luckily, this is easy: &lt;br&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;(0∗(0+1))/2=0.
(0 * (0 + 1)) / 2 = 0. 
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;∗&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mclose"&gt;))&lt;/span&gt;&lt;span class="mord"&gt;/2&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;0.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;So, our equation holds true when &lt;code&gt;N&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, for the inductive part. Let's assume the rule is true for a number &lt;code&gt;N&lt;/code&gt;. We need to prove it's also true for &lt;code&gt;N + 1&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This is where the magic of induction happens. We want to show that if the rule works for &lt;code&gt;0&lt;/code&gt;, it must work for &lt;code&gt;1&lt;/code&gt;, and if it works for &lt;code&gt;1&lt;/code&gt;, it must work for &lt;code&gt;2&lt;/code&gt;, and so on. We don't want to prove each of these cases individually, so we simply say, "&lt;em&gt;If it's true for N, it must be true for N + 1.&lt;/em&gt;" &lt;/p&gt;

&lt;p&gt;By using a variable in this inductive structure, we're essentially saying, "&lt;em&gt;If it's true for &lt;code&gt;0&lt;/code&gt;, then it's true for &lt;code&gt;1&lt;/code&gt;; if it's true for &lt;code&gt;1&lt;/code&gt;, then it's true for &lt;code&gt;2&lt;/code&gt;&lt;/em&gt;", and so on.&lt;/p&gt;

&lt;p&gt;Here's what we want to prove:&lt;br&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;(0+1+2+3+...+n+n+1)=(n+1)(n+2)2
(0 + 1 + 2 + 3 + ... + n + n + 1) = \frac{(n + 1)(n + 2)}{2}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;...&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;To begin, we know that&lt;br&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;(0+1+2+3+...+n)=n(n+1)2
(0 + 1 + 2 + 3 + ... + n) = \frac{n(n+1)}{2}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;0&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;...&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;So we can substitute that in get this&lt;br&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n(n+1)2+n+1=(n+1)(n+2)n
\frac{n(n+1)}{2} + n + 1 = \frac{(n+1)(n+2)}{n}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Now, we can expand the multiplication on both sides&lt;br&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n2+n2+(n+1)=n2+3n+22
\frac{n^2 + n}{2} + (n + 1) = \frac{n^2 + 3n + 2}{2}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Get the common denominator on the left side&lt;br&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n2+n+2n+22=n2+3n+22
\frac{n^2 + n + 2n + 2}{2} = \frac{n^2 + 3n + 2}{2}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;Finally, simplify the left side&lt;br&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n2+3n+22=n2+3n+22
\frac{n^2 + 3n + 2}{2} = \frac{n^2 + 3n + 2}{2}
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;3&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;+&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;And there you have it! We've proven the equation holds true for all natural numbers. So, that's the axiomatic definition of natural numbers – numbers greater than or equal to zero, each with a successor, and where you can use induction. It's amazing how almost everything we do with natural numbers, including basic arithmetic we learn as kids, can be built upon this foundation.&lt;/p&gt;

&lt;p&gt;So, can we now define what a number is? Well, kind of. One thing we learn in math is that numbers don't have just one meaning. There are many types of numbers: natural numbers, integers, rational numbers, real numbers, complex numbers, and so on. The entire universe of numbers begins with what we just explored: the natural numbers. And ultimately, the meaning of these numbers boils down to either quantity or position. &lt;/p&gt;

&lt;p&gt;They're all either cardinal numbers (representing quantity) or ordinal numbers (representing position), or combinations of both. In essence, a number is a construct that represents either a quantity or a position.&lt;/p&gt;

</description>
      <category>mathematics</category>
    </item>
    <item>
      <title>Managing Docker Images</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Mon, 15 Jul 2024 06:00:00 +0000</pubDate>
      <link>https://dev.to/kalkwst/managing-docker-images-875</link>
      <guid>https://dev.to/kalkwst/managing-docker-images-875</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In the previous posts of the series, we discussed in depth about Docker images. As we've seen, we've been able to take existing images, provided to the general public in Docker Hub, and run them or reuse them for our purposes. The image itself helps us streamline our processes and reduce the work we need to do.&lt;/p&gt;

&lt;p&gt;In the next few posts, we are going to take a more in-depth look at images and how to work with them on our system. We're going to discuss how images can be better organized and tagged, understand how different layers of images work, and set up registries that are both public and private to further reuse the images we have created. &lt;/p&gt;

&lt;p&gt;Docker images are also ideal for application development because each image contains a complete, self-contained version of the application along with all its dependencies. This enables developers to create an image locally and deploy it in development or testing environments to verify compatibility with other parts of the application. If testing is successful, the same image can be pushed to the production environment for users to use. It's crucial to maintain consistency when using these images, especially when collaborating within larger developer teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker Layers and Caching
&lt;/h2&gt;

&lt;p&gt;A registry is a way to store and distribute Docker images. When you pull a Docker image from a registry, you might notice that the image is pulled in pieces and not as a single image. The same thing happens when you build an image on your system.&lt;/p&gt;

&lt;p&gt;This is because Docker images are structured in layers, each representing a stage of the image's construction. Each layer of an image represents a specific action or change made when building the image with a Dockerfile. These layers are organized on top of a base image, capturing every change to the filesystem that occurs with each instruction in the Dockerfile. This setup is structured in a way that Docker can use caching efficiently. &lt;/p&gt;

&lt;p&gt;When you instantiate an image as a container, Docker adds a writable layer on top of the existing read-only layers. This writable layer, often referred to as the container layer, allows the container to modify and persist changes during runtime without affecting the underlying image.&lt;/p&gt;

&lt;p&gt;As we will see in the following examples, when you build a Docker container from a &lt;strong&gt;Dockerfile&lt;/strong&gt;, Docker shows the execution of each command specified in the Dockerfile. These commands contribute to creating layers in the Docker image, each represented by a unique ID generated during the build process. After successfully building the image, we can inspect the layers using the &lt;code&gt;docker history&lt;/code&gt; command, which provides a detailed view including the image name or ID alongside the commands that formed each layer.&lt;/p&gt;

&lt;p&gt;It's important to note, that as you setup your build environment and progress in development, the number of layers in the Docker image grows. More layers mean larger image sizes, which can lead to longer build times. &lt;/p&gt;

&lt;p&gt;When you build an image from a &lt;strong&gt;Dockerfile&lt;/strong&gt;, each instruction contributes to the creation of layers in the image. Layers are created explicitly when commands like &lt;strong&gt;RUN&lt;/strong&gt;, &lt;strong&gt;ADD&lt;/strong&gt; and &lt;strong&gt;COPY&lt;/strong&gt; are executed. These commands make changes to the filesystem within the image, resulting in new layers being added.&lt;/p&gt;

&lt;p&gt;On the other hand, commands like &lt;strong&gt;FROM&lt;/strong&gt;, &lt;strong&gt;ENV&lt;/strong&gt;, &lt;strong&gt;WORKDIR&lt;/strong&gt; and &lt;strong&gt;CMD&lt;/strong&gt; do not directly create filesystem changes. Instead, they modify the environment or configure settings within the image without altering the filesystem itself. As a result, these commands generate &lt;strong&gt;intermediate layers&lt;/strong&gt;. These layers have a size of 0 bytes because they don't introduce any new filesystem change. They serve as metadata or configuration layers that help define how an image behaves or is structured, but they don't increase the size of the final Docker image.&lt;/p&gt;

&lt;p&gt;When building our Docker images, we can use the &lt;code&gt;docker history&lt;/code&gt; command and the image name or ID to see the layers used to create the image. The output will provide details on commands being used to generate the layer as well as the size of the layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;image_name&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;image_id&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;docker image inspect&lt;/code&gt; command is useful in providing further details on where the layers of our images are located:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;inspect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;image_id&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Working with Docker Image Layers
&lt;/h3&gt;

&lt;p&gt;In this example, we are going to work with some basic &lt;strong&gt;Dockerfiles&lt;/strong&gt; to see how Docker uses layers to build images. We will start by creating a &lt;strong&gt;Dockerfile&lt;/strong&gt; and building a new image. We will then rebuild the image to see the advantages of caching and how the build time is reduced due to its use.&lt;/p&gt;

&lt;p&gt;Create a file named &lt;strong&gt;Dockerfile&lt;/strong&gt; and add the following directives:&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; alpine&lt;/span&gt;



&lt;span class="k"&gt;RUN &lt;/span&gt;apk update

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add wget
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Save the &lt;strong&gt;Dockerfile&lt;/strong&gt; and then, from the command line, make sure you are in the same directory as the &lt;strong&gt;Dockerfile&lt;/strong&gt; you are created. Use the &lt;code&gt;docker build&lt;/code&gt; command to create the new image using the &lt;code&gt;-t&lt;/code&gt; option to name it &lt;code&gt;basic-example&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;basic-example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the image is built successfully, you should see an output similar to the following. Rach step is built as an intermediate layer and if it completes successfully, it is then transferred to a read-only layer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[+] Building 9.0s (8/8) FINISHED                                                                         docker:default
 =&amp;gt; [internal] load .dockerignore                                                                                  0.1s
 =&amp;gt; =&amp;gt; transferring context: 2B                                                                                    0.0s
 =&amp;gt; [internal] load build definition from Dockerfile                                                               0.1s
 =&amp;gt; =&amp;gt; transferring dockerfile: 84B                                                                                0.0s
 =&amp;gt; [internal] load metadata for docker.io/library/alpine:latest                                                   3.6s
 =&amp;gt; [auth] library/alpine:pull token for registry-1.docker.io                                                      0.0s
 =&amp;gt; [1/3] FROM docker.io/library/alpine@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0    1.1s
 =&amp;gt; =&amp;gt; resolve docker.io/library/alpine@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0    0.1s
 =&amp;gt; =&amp;gt; sha256:a606584aa9aa875552092ec9e1d62cb98d486f51f389609914039aabd9414687 1.47kB / 1.47kB                     0.0s
 =&amp;gt; =&amp;gt; sha256:ec99f8b99825a742d50fb3ce173d291378a46ab54b8ef7dd75e5654e2a296e99 3.62MB / 3.62MB                     0.6s
 =&amp;gt; =&amp;gt; sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0 1.85kB / 1.85kB                     0.0s
 =&amp;gt; =&amp;gt; sha256:dabf91b69c191a1a0a1628fd6bdd029c0c4018041c7f052870bb13c5a222ae76 528B / 528B                         0.0s
 =&amp;gt; =&amp;gt; extracting sha256:ec99f8b99825a742d50fb3ce173d291378a46ab54b8ef7dd75e5654e2a296e99                          0.2s
 =&amp;gt; [2/3] RUN apk update                                                                                           2.1s
 =&amp;gt; [3/3] RUN apk add wget                                                                                         1.7s
 =&amp;gt; exporting to image                                                                                             0.1s
 =&amp;gt; =&amp;gt; exporting layers                                                                                            0.1s
 =&amp;gt; =&amp;gt; writing image sha256:2fc965ee555abcf548a268e3622ee031366479ddefb080e6920847c46a8848b9                       0.0s
 =&amp;gt; =&amp;gt; naming to docker.io/library/basic-example  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Use the &lt;code&gt;docker history&lt;/code&gt; command along with the image name of &lt;code&gt;basic-example&lt;/code&gt; to see the different layers of the image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;basic-example&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The history gives you creation details, including the size of each layer&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
2fc965ee555a   7 minutes ago   RUN /bin/sh -c apk add wget # buildkit          3.07MB    buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      7 minutes ago   RUN /bin/sh -c apk update # buildkit            2.32MB    buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      5 days ago      /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
&amp;lt;missing&amp;gt;      5 days ago      /bin/sh -c #(nop) ADD file:33ebe56b967747a97…   7.8MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;docker history&lt;/code&gt; command shows the layer of the original image used as part of the &lt;strong&gt;Dockerfile FROM&lt;/strong&gt; command as &lt;code&gt;&amp;lt;missing&amp;gt;&lt;/code&gt;. It is showing as &lt;code&gt;missing&lt;/code&gt; as it was created by a different system and then pulled into ours.&lt;/p&gt;




&lt;p&gt;Run the build again without making any changes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;basic-example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will show you the build is done using the layers stored in the Docker image cache, thereby speeding up our build. Although this is a small image, a much larger image would show a significant increase&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[+] Building 4.3s (8/8) FINISHED                                                                         docker:default
 =&amp;gt; [internal] load .dockerignore                                                                                  0.0s
 =&amp;gt; =&amp;gt; transferring context: 2B                                                                                    0.0s
 =&amp;gt; [internal] load build definition from Dockerfile                                                               0.0s
 =&amp;gt; =&amp;gt; transferring dockerfile: 84B                                                                                0.0s
 =&amp;gt; [internal] load metadata for docker.io/library/alpine:latest                                                   4.1s
 =&amp;gt; [auth] library/alpine:pull token for registry-1.docker.io                                                      0.0s
 =&amp;gt; [1/3] FROM docker.io/library/alpine@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0    0.0s
 =&amp;gt; CACHED [2/3] RUN apk update                                                                                    0.0s
 =&amp;gt; CACHED [3/3] RUN apk add wget                                                                                  0.0s
 =&amp;gt; exporting to image                                                                                             0.0s
 =&amp;gt; =&amp;gt; exporting layers                                                                                            0.0s
 =&amp;gt; =&amp;gt; writing image sha256:2fc965ee555abcf548a268e3622ee031366479ddefb080e6920847c46a8848b9                       0.0s
 =&amp;gt; =&amp;gt; naming to docker.io/library/basic-example                                                                   0.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Lets add the &lt;code&gt;curl&lt;/code&gt; package as part of our image creation, and modify the &lt;strong&gt;Dockerfile&lt;/strong&gt; as follows&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; alpine&lt;/span&gt;



&lt;span class="k"&gt;RUN &lt;/span&gt;apk update

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add wget

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add curl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Build the image again, and now you'll see the image was created with a mix of cached and new layers&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;basic-example&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command should create the following output&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[+] Building 7.1s (9/9) FINISHED                                                                                                                                                                                                                               docker:default
 =&amp;gt; [internal] load .dockerignore                                                                                                                                                                                                                                        0.0s
 =&amp;gt; =&amp;gt; transferring context: 2B                                                                                                                                                                                                                                          0.0s
 =&amp;gt; [internal] load build definition from Dockerfile                                                                                                                                                                                                                     0.0s
 =&amp;gt; =&amp;gt; transferring dockerfile: 102B                                                                                                                                                                                                                                     0.0s
 =&amp;gt; [internal] load metadata for docker.io/library/alpine:latest                                                                                                                                                                                                         4.0s
 =&amp;gt; [auth] library/alpine:pull token for registry-1.docker.io                                                                                                                                                                                                            0.0s
 =&amp;gt; [1/4] FROM docker.io/library/alpine@sha256:b89d9c93e9ed3597455c90a0b88a8bbb5cb7188438f70953fede212a0c4394e0                                                                                                                                                          0.0s
 =&amp;gt; CACHED [2/4] RUN apk update                                                                                                                                                                                                                                          0.0s
 =&amp;gt; CACHED [3/4] RUN apk add wget                                                                                                                                                                                                                                        0.0s
 =&amp;gt; [4/4] RUN apk add curl                                                                                                                                                                                                                                               2.8s
 =&amp;gt; exporting to image                                                                                                                                                                                                                                                   0.1s
 =&amp;gt; =&amp;gt; exporting layers                                                                                                                                                                                                                                                  0.1s
 =&amp;gt; =&amp;gt; writing image sha256:f2bb77eb9898954a27c2bd12838c3ed0fdfe19ed78a7440189c28bdb0cbfbf8d                                                                                                                                                                             0.0s
 =&amp;gt; =&amp;gt; naming to docker.io/library/basic-example  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the &lt;code&gt;docker image&lt;/code&gt; command again&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will now notice an image named and tagged as &lt;code&gt;&amp;lt;none&amp;gt;&lt;/code&gt; to show we have now created a dangling image&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
basic-example                                   latest    f2bb77eb9898   52 seconds ago   18.9MB
&amp;lt;none&amp;gt;                                          &amp;lt;none&amp;gt;    2fc965ee555a   16 hours ago     13.2MB
onbuild-child-example                           latest    9fb3629a292e   5 days ago       222MB
onbuild-parent-example                          latest    4a6360882fb6   6 days ago       222MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Docker, dangling images are those that are represented by &lt;code&gt;&amp;lt;none&amp;gt;&lt;/code&gt; in the image list. These images occur when a layer in the image hierarchy no longer corresponds to any tagged or referenced image in the system. Essentially, they are orphaned or unused layers that have lost their connection to any active image.&lt;/p&gt;

&lt;p&gt;Dangling images can accumulate over time as you build and prune Docker images, and they occupy disk space without serving any meaningful purpose. Even though each individual dangling image might be relatively small, such as the example of 7.48 MB, these sizes can accumulate significantly over time, especially in development and production environments where frequent image builds and updates occur.&lt;/p&gt;




&lt;p&gt;Run the &lt;code&gt;docker image inspect&lt;/code&gt; command using the image ID to see the location of where the dangling images are located in the system&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;inspect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2fc965ee555a&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you should get an output similar to the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"Data"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"LowerDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"/var/lib/docker/overlay2/p9vnb2sakx8pcxhnabhpbbghs/diff:/var/lib/docker/overlay2/f41cbe299d47005328bcfbf0aa9c958ead5148eca0e0f65679aaabb38f9db96a/diff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"MergedDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"/var/lib/docker/overlay2/sbm1ykpcezy3eydzy6eyxhz08/merged"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"UpperDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"/var/lib/docker/overlay2/sbm1ykpcezy3eydzy6eyxhz08/diff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"WorkDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"/var/lib/docker/overlay2/sbm1ykpcezy3eydzy6eyxhz08/work"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of our images are located in the same location. So any dangling images would waste space on our system.&lt;/p&gt;




&lt;p&gt;Run the &lt;code&gt;docker images&lt;/code&gt; command again using the &lt;code&gt;-a&lt;/code&gt; option&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will also show the intermediate layers used when our image is build&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;basic-example                                   latest    f2bb77eb9898   23 minutes ago   18.9MB
&amp;lt;none&amp;gt;                                          &amp;lt;none&amp;gt;    2fc965ee555a   17 hours ago     13.2MB
onbuild-child-example                           latest    9fb3629a292e   5 days ago       222MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the &lt;code&gt;docker image prune&lt;/code&gt; command to remove all the dangling images. We could use &lt;code&gt;docker rmi&lt;/code&gt; but the &lt;code&gt;docker image prune&lt;/code&gt; command is the easier way to do it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;prune&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get an output looking like the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:2fc965ee555abcf548a268e3622ee031366479ddefb080e6920847c46a8848b9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the &lt;code&gt;docker images&lt;/code&gt; command again&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see we no longer have the dangling image in our list of images&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
basic-example                                   latest    f2bb77eb9898   28 minutes ago   18.9MB
onbuild-child-example                           latest    9fb3629a292e   5 days ago       222MB
onbuild-parent-example                          latest    4a6360882fb6   6 days ago       222MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example was on smaller image sizes, but this is definitely something to keep in mind when running production and development environments. In the next example, we will look further at our layers and caching to see how they can be used to speed up the image build process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Increasing Build Speed and Reducing Layers
&lt;/h3&gt;

&lt;p&gt;We've been working with small projects up until now. As our apps get bigger and more complex, though, we'll want to start thinking about the size and number of layers in our Docker images, along with how quickly we're building them. In this example, we'll focus on speeding up build times, shrinking those image sizes, and using the &lt;code&gt;--cache-from&lt;/code&gt; option to make things even faster.&lt;/p&gt;

&lt;p&gt;First, let's clean up any existing images on your system. We'll use the &lt;code&gt;docker rmi -f $(docker images -a -q)&lt;/code&gt; command, which will force-remove all images currently on your system. This will give you a clean slate to work with.&lt;/p&gt;




&lt;p&gt;Create a new &lt;strong&gt;Dockerfile&lt;/strong&gt; with the following content. It will simulate a simple web server, as well as print the output of our &lt;strong&gt;Dockerfile&lt;/strong&gt; during the build process&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; alpine&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk update
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add wget curl

&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; randomdata.txt https://github.com/Kalkwst/Docker-Workshop-Repository/blob/master/Dockerfiles/create-base-image2/random_data.txt

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; mkdir /var/www/&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; mkdir /var/www/html/&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /var/www/html/&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Dockerfile.tar.gz /tmp/&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-zxvf&lt;/span&gt; /tmp/Dockerfile.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; /var/www/html
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/Dockerfile.tar.gz

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Download the &lt;code&gt;Alpine&lt;/code&gt; base image using &lt;code&gt;docker pull&lt;/code&gt; so that we can start with the same image for each test we do&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pull&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Create a TAR file to be added to our image&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="nb"&gt;tar &lt;/span&gt;zcvf Dockerfile.tar.gz Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Build a new image using the name of &lt;code&gt;basic-server&lt;/code&gt;. We are going to use the &lt;code&gt;time&lt;/code&gt; command at the start of the command to allow us to gauge the time it takes to build the image&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="nb"&gt;time &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; basic-server &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will return something similar to the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#0 building with "default" instance using docker driver

#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 451B done
#1 DONE 0.0s

#2 [internal] load .dockerignore
#2 transferring context: 2B done
#2 DONE 0.1s

#3 [internal] load metadata for docker.io/library/alpine:latest
#3 DONE 0.0s

#4 [1/9] FROM docker.io/library/alpine
#4 DONE 0.0s

#5 [2/9] RUN apk update
#5 CACHED

#6 [internal] load build context
#6 transferring context: 396B done
#6 DONE 0.0s

#7 [3/9] RUN apk add wget curl
#7 0.323 fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/main/x86_64/APKINDEX.tar.gz
#7 1.054 fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/community/x86_64/APKINDEX.tar.gz
#7 2.267 (1/12) Installing ca-certificates (20240226-r0)
#7 2.393 (2/12) Installing brotli-libs (1.1.0-r2)
#7 2.638 (3/12) Installing c-ares (1.28.1-r0)
#7 2.723 (4/12) Installing libunistring (1.2-r0)
#7 3.061 (5/12) Installing libidn2 (2.3.7-r0)
#7 3.155 (6/12) Installing nghttp2-libs (1.62.1-r0)
#7 3.240 (7/12) Installing libpsl (0.21.5-r1)
#7 3.316 (8/12) Installing zstd-libs (1.5.6-r0)
#7 3.526 (9/12) Installing libcurl (8.8.0-r0)
#7 3.709 (10/12) Installing curl (8.8.0-r0)
#7 3.831 (11/12) Installing pcre2 (10.43-r0)
#7 4.008 (12/12) Installing wget (1.24.5-r0)
#7 4.157 Executing busybox-1.36.1-r29.trigger
#7 4.160 Executing ca-certificates-20240226-r0.trigger
#7 4.183 OK: 14 MiB in 26 packages
#7 DONE 4.3s

#8 [4/9] RUN wget -O randomdata.txt https://github.com/Kalkwst/Docker-Workshop-Repository/blob/master/Dockerfiles/create-base-image2/random_data.txt
#8 0.404 --2024-07-01 07:34:51--  https://github.com/Kalkwst/Docker-Workshop-Repository/blob/master/Dockerfiles/create-base-image2/random_data.txt
#8 0.414 Resolving github.com (github.com)... 140.82.121.3
#8 0.561 Connecting to github.com (github.com)|140.82.121.3|:443... connected.
#8 0.685 HTTP request sent, awaiting response... 200 OK
#8 1.109 Length: unspecified [text/html]
#8 1.109 Saving to: 'randomdata.txt'
#8 1.109
#8 1.109      0K .......... .......... .......... .......... ..........  437K
#8 1.223     50K .......... .......... .......... .......... ..........  881K
#8 1.280    100K .......... .......... .......... .......... .......... 2.02M
#8 1.304    150K .......... .......... .......... .......... ..........  720K
#8 1.373    200K .......... .......... .......... .......... .......... 35.1M
#8 1.375    250K .......... .......... .......                           481K=0.3s
#8 1.432
#8 1.432 2024-07-01 07:34:52 (858 KB/s) - 'randomdata.txt' saved [284319]
#8 1.432
#8 DONE 1.5s

#9 [5/9] WORKDIR /var/www/html/
#9 DONE 0.0s

#10 [6/9] COPY Dockerfile.tar.gz /tmp/
#10 DONE 0.0s

#11 [7/9] RUN tar -zxvf /tmp/Dockerfile.tar.gz -C /var/www/html
#11 0.365 Dockerfile
#11 DONE 0.4s

#12 [8/9] RUN rm /tmp/Dockerfile.tar.gz
#12 DONE 0.5s

#13 [9/9] RUN cat Dockerfile
#13 0.450 FROM alpine
#13 0.450
#13 0.450 RUN apk update
#13 0.450 RUN apk add wget curl
#13 0.450
#13 0.450 RUN wget -O randomdata.txt https://github.com/Kalkwst/Docker-Workshop-Repository/blob/master/Dockerfiles/create-base-image2/random_data.txt
#13 0.450
#13 0.450 CMD mkdir /var/www/
#13 0.450 CMD mkdir /var/www/html/
#13 0.450
#13 0.450 WORKDIR /var/www/html/
#13 0.450
#13 0.450 COPY Dockerfile.tar.gz /tmp/
#13 0.450
#13 0.450 RUN tar -zxvf /tmp/Dockerfile.tar.gz -C /var/www/html
#13 0.450 RUN rm /tmp/Dockerfile.tar.gz
#13 0.450
#13 0.450 RUN cat Dockerfile
#13 DONE 0.5s

#14 exporting to image
#14 exporting layers
#14 exporting layers 0.2s done
#14 writing image sha256:971477cab5a251188e82e14baaf9268de83ecdd04429e5818c617ffe0803921c done
#14 naming to docker.io/library/basic-server done
#14 DONE 0.2s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the time will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
real    0m10.468s
user    0m0.060s
sys     0m0.276s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the &lt;code&gt;docker history&lt;/code&gt; command over the new &lt;code&gt;basic-app&lt;/code&gt; image&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker history basic-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should be something like the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
971477cab5a2   8 minutes ago   RUN /bin/sh -c cat Dockerfile # buildkit        0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 minutes ago   RUN /bin/sh -c rm /tmp/Dockerfile.tar.gz # b…   0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 minutes ago   RUN /bin/sh -c tar -zxvf /tmp/Dockerfile.tar…   412B      buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 minutes ago   COPY Dockerfile.tar.gz /tmp/ # buildkit         350B      buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 minutes ago   WORKDIR /var/www/html/                          0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 minutes ago   CMD ["/bin/sh" "-c" "mkdir /var/www/html/"]     0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 minutes ago   CMD ["/bin/sh" "-c" "mkdir /var/www/"]          0B        buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 minutes ago   RUN /bin/sh -c wget -O randomdata.txt https:…   284kB     buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      8 minutes ago   RUN /bin/sh -c apk add wget curl # buildkit     8.75MB    buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      4 days ago      RUN /bin/sh -c apk update # buildkit            2.32MB    buildkit.dockerfile.v0
&amp;lt;missing&amp;gt;      10 days ago     /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
&amp;lt;missing&amp;gt;      10 days ago     /bin/sh -c #(nop) ADD file:33ebe56b967747a97…   7.8MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see there are 12 layers in our new image. As you can see, the &lt;strong&gt;RUN&lt;/strong&gt;, &lt;strong&gt;COPY&lt;/strong&gt; and &lt;strong&gt;ADD&lt;/strong&gt; commands in our &lt;strong&gt;Dockerfile&lt;/strong&gt; are creating layers of a particular size relevant to the command being run or files being added, and all of the other commands are of size &lt;code&gt;0 B&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;We can slim down our image by merging some of the commands in the Dockerfile we created earlier. Combine the &lt;code&gt;RUN&lt;/code&gt; commands from lines 3 and 4, and then merge the &lt;code&gt;CMD&lt;/code&gt; commands from lines 8 and 9. This will reduce the number of layers in our image, making it more efficient.&lt;/p&gt;

&lt;p&gt;After making these changes, your Dockerfile should look 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; alpine&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add wget curl
&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; randomdata.txt https://github.com/Kalkwst/Docker-Workshop-Repository/blob/master/Dockerfiles/create-base-image2/random_data.txt

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; mkdir -p /var/www/html/&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /var/www/html/&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Dockerfile.tar.gz /tmp/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-zxvf&lt;/span&gt; /tmp/Dockerfile.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; /var/www/hmtl/
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/Dockerfile.tar.gz

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we rebuild our Docker image now, we'll see that the number of layers has decreased from 12 to 9. This is because we combined several commands into single lines, even though the same actions are still being performed.&lt;/p&gt;




&lt;p&gt;We can further optimize our Dockerfile by replacing lines 11, 12, and 13 with a single &lt;code&gt;ADD&lt;/code&gt; command. This eliminates the need for separate &lt;strong&gt;COPY&lt;/strong&gt;, &lt;strong&gt;RUN&lt;/strong&gt;, and &lt;strong&gt;RUN&lt;/strong&gt; commands to unzip and remove the archived file. Here's the updated Dockerfile snippet:&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; alpine&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add wget curl
&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; randomdata.txt https://github.com/Kalkwst/Docker-Workshop-Repository/blob/master/Dockerfiles/create-base-image2/random_data.txt

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; mkdir -p /var/www/html/&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /var/www/html/&lt;/span&gt;

&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; Dockerfile.tar.gz -C /var/www/hmtl/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; /tmp/Dockerfile.tar.gz

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Rebuilding our Docker image with the &lt;strong&gt;ADD&lt;/strong&gt; command in place reduces the number of layers from 9 to 8, making it even more streamlined.&lt;/p&gt;

&lt;p&gt;You might have noticed that a significant portion of the build time is spent running &lt;code&gt;apk update&lt;/code&gt;, installing &lt;code&gt;wget&lt;/code&gt; and &lt;code&gt;curl&lt;/code&gt;, and fetching content from websites (as seen in lines 3 and 5 of our Dockerfile). While this isn't a major issue for a few builds, it can become a bottleneck when creating multiple images.&lt;/p&gt;

&lt;p&gt;To address this, we can create a base image that already includes these tools and dependencies. By using this base image as our starting point, we can eliminate these lines from our Dockerfile altogether, further improving build times and image efficiency.&lt;/p&gt;




&lt;p&gt;All right, let's create a dedicated base image to streamline our Docker builds. First, navigate to a new directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;mkdir&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;base-image&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;base-image&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create a new Dockerfile in this directory with the following contents:&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; alpine&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; wget curl
&lt;span class="k"&gt;RUN &lt;/span&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; randomdata.txt https://github.com/Kalkwst/Docker-Workshop-Repository/blob/master/Dockerfiles/create-base-image2/random_data.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Dockerfile does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pulls the base image:&lt;/strong&gt; It starts with the &lt;code&gt;alpine:latest&lt;/code&gt; image as its foundation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runs the apk commands:&lt;/strong&gt; It updates the package index and installs &lt;code&gt;wget&lt;/code&gt; and &lt;code&gt;curl&lt;/code&gt; using &lt;code&gt;apk add --no-cache&lt;/code&gt;. The &lt;code&gt;--no-cache&lt;/code&gt; option prevents apk from storing the downloaded packages in the local cache, keeping the image smaller.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runs the wget command&lt;/strong&gt;: It finally downloads the &lt;code&gt;randomdata.txt&lt;/code&gt; file from the external directory.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Build the new image from the previous &lt;strong&gt;Dockerfile&lt;/strong&gt; and name it &lt;code&gt;basic-base&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;basic-base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Perfect! Now that we have a base image ready, let's update our original Dockerfile. First, head back to the directory where your original Dockerfile resides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="nx"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;original_project_directory&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, open your Dockerfile and make the following changes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Remove line 3:&lt;/strong&gt; Delete the line that starts with &lt;code&gt;RUN apk update...&lt;/code&gt; since these commands are now handled in our base image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Update the &lt;code&gt;FROM&lt;/code&gt; command:&lt;/strong&gt; Change the base image from &lt;code&gt;alpine:latest&lt;/code&gt; to the name you'll give your custom base image (i.e., &lt;code&gt;basic-base&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove the &lt;code&gt;apk&lt;/code&gt; commands from line 3 (now line 2):&lt;/strong&gt; Delete the remaining part of the line that installed &lt;code&gt;wget&lt;/code&gt; and &lt;code&gt;curl&lt;/code&gt;.
Your updated &lt;strong&gt;Dockerfile&lt;/strong&gt; should now look similar to this
&lt;/li&gt;
&lt;/ol&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; basic-base&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; mkdir -p /var/www/html/&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /var/www/html/&lt;/span&gt;

&lt;span class="k"&gt;ADD&lt;/span&gt;&lt;span class="s"&gt; Dockerfile.tar.gz /var/www/html/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;Run the build again for your new &lt;strong&gt;Dockerfile&lt;/strong&gt;. Using the &lt;code&gt;time&lt;/code&gt; command again, you should see the build complete in just under 3 seconds&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="nb"&gt;time &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; basic-server &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll likely notice a significant speed improvement compared to our previous builds. This is because we've offloaded the time-consuming package installations to our base image, streamlining the build process for our main image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;real    0m2.924s
user    0m0.000s
sys     0m0.262s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Throughout this example, we've seen firsthand how the build cache and image layers work together to significantly speed up the Docker build process. We've been starting our builds by pulling images from Docker Hub, but you have the flexibility to start with your own custom images. This gives you even more control over the build process and allows for further optimization.&lt;/p&gt;

&lt;p&gt;By creating and maintaining your own base images, you can tailor them to your specific needs, pre-installing common dependencies and configurations. This not only reduces build times but also ensures consistency across your projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This post demonstrated how Docker allows users to work with images to package their applications together with a working environment to be moved across different environments. You've also seen how Docker uses layers and caching to improve build speed and ensure you can also work with these layers to reserve resources or disk space.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>docker</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Mathematics of Algorithms</title>
      <dc:creator>Kostas Kalafatis</dc:creator>
      <pubDate>Fri, 12 Jul 2024 06:00:00 +0000</pubDate>
      <link>https://dev.to/kalkwst/algorithmic-thinking-4n9p</link>
      <guid>https://dev.to/kalkwst/algorithmic-thinking-4n9p</guid>
      <description>&lt;p&gt;One of the most crucial considerations when selecting an algorithm is the speed with which it is likely to complete. Predicting this speed involves using mathematical methods. This post delves into the mathematical tools, aiming to clarify the terms commonly used in this series and the rest of the literature that describes algorithms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Instance Size
&lt;/h2&gt;

&lt;p&gt;Think of the problem you are giving to an algorithm implementation as a recipe you are following. An &lt;em&gt;instance&lt;/em&gt; of that problem is like the number of portions your recipe will create. Most of the time, the more portions (or the larger the dataset), the longer it takes to cook the dish (or for the implementation to run). On the other hand, trying to compress the dataset might include unnecessary operations that will eventually slow down the execution. Finding this sweet spot in how you represent the data to the computer is surprisingly challenging. This is because real-world problems aren't naturally in a format computers understand. They need to be translated into a suitable representation, and there are many ways to do that.&lt;/p&gt;

&lt;p&gt;When we judge how good an implementation is at solving a problem, we want to focus on the program's core logic (the algorithm), not how the problem was presented. We don't want the way we formatted the data to be the main thing that decides how fast or well the program works. The way you represent the problem should really just depend on what the program needs to &lt;em&gt;do&lt;/em&gt; with the data. For example, if you need to find the shortest route between cities, the best way to represent the map data is different than if you're sorting a list of names.&lt;/p&gt;

&lt;p&gt;Designing efficient algorithms often starts with picking the right tools for the job. In this case, the tools are data structures – how you organize and store the information in the computer's memory. By choosing the right data structures, you set the stage for a program that solves the problem quickly and smoothly.&lt;/p&gt;

&lt;p&gt;Since there's no single, universal way to define the "size" of a problem for a computer program, we usually rely on common sense and practical conventions. For instance, when dealing with a task like sorting numbers, we typically assume that each number can be stored in a single 32-bit word (a standard unit of computer memory). So, if we're sorting 100 numbers, we say the size of the problem is 100.&lt;/p&gt;

&lt;p&gt;Now, sometimes a number might be too big to fit into one word. But if it takes a consistent, fixed number of words (say, 2 or 3), our estimate of the problem size is only off by a small factor. For instance, an algorithm that works with 64-bit numbers might take roughly twice as long as one working with 32-bit numbers, even if they're doing the same kind of sorting.&lt;/p&gt;

&lt;p&gt;The key point is this: we make these assumptions about how data is encoded so we can compare different algorithms fairly. By focusing on the size of the problem (like the number of items to be sorted) and assuming a reasonable way to store the data, we can get a good idea of how well an algorithm performs without getting bogged down in the details of data representation.&lt;/p&gt;

&lt;p&gt;In the world of algorithm design, researchers accept that it's impossible to perfectly predict how the choice of data representation will impact the performance of an algorithm when implemented. To simplify things, they consider performance differences that are only a constant multiple of each other as being essentially the same, especially when dealing with very large problem sizes. This concept is called asymptotic equivalence.&lt;/p&gt;

&lt;p&gt;Let's take an example. We know that working with 64-bit integers is generally slower than 32-bit integers. But if we have a good algorithm designed for sorting a million 32-bit integers, it's reasonable to assume it will also be a good algorithm for sorting a million 64-bit integers. The difference in processing time, while existing, becomes less significant as the problem size increases.&lt;/p&gt;

&lt;p&gt;Of course, in real-life situations, nobody would be happy to pay a bill that's 1000 times larger than expected! But this simplification, where we disregard constant multiplicative factors, is a widely accepted way to compare the efficiency of different algorithms in theory. It helps researchers focus on the core logic of the algorithm and avoid getting bogged down in the details of implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best, Average and Worst Case Analysis
&lt;/h2&gt;

&lt;p&gt;For a lot of problems, there's no one-size-fits-all solution. The best algorithm to choose depends on several factors&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem Itself&lt;/strong&gt;: What exactly are you trying to solve? Different problems demand different approaches. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Data&lt;/strong&gt;: What kind of data are you working with? How are they distributed? Algorithms can be optimized for specific types of data and distributions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Algorithms&lt;/strong&gt;: How do the different algorithms your are considering behave under different circumstances? Some might be faster for datasets, while others are more appropriate for larger ones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To help navigate this decision-making process, algorithms are usually presented with three different common scenarios in mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Worst Case&lt;/strong&gt;: This identifies a category of problem instances where an algorithm performs at its absolute slowest. Instead of pinpointing a single, specific input, algorithm designers usually describe characteristics of the input data that hinder the algorithm's efficiency. This could be things like a large input size , specific data patterns, or extreme outlier values. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Average Case&lt;/strong&gt;: This focuses on the expected performance of an algorithm when faced with typical, randomly generated problem instances. While some specific cases might take longer due to unique characteristics, most instances should fall within a predictable range of execution time. This measure reflects the performance that a typical user can anticipate when using the algorithm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Case&lt;/strong&gt;: The best-case scenario represents a special class of problem instances that allow an algorithm to shine, performing at its absolute fastest with minimal work. These are often idealized or uncommon scenarios that rarely occur in practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Worst Case
&lt;/h3&gt;

&lt;p&gt;When dealing with a fixed-size input, a computer program's execution time can still vary greatly depending on the specific data it receives. The worst-case execution time is the absolute longest it could take to run, considering all possible input scenarios of that size.&lt;/p&gt;

&lt;p&gt;We focus on the worst-case scenario because it represents the upper limit of a program's slowness. This is important for applications where timeliness is critical. Additionally, analyzing the worst case is often easier than dealing with the average or best cases, providing valuable insights into overall efficiency.&lt;/p&gt;

&lt;p&gt;To express it more formally, let's consider 

&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SnS_n&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 as the set of all problem instances 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;sis_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 with a size of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. The function 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t(si)t(s_i)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 measures the amount of work (computational steps or time) an algorithm takes to process a specific instance 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;sis_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. The worst-case performance of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SnS_n&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, denoted as 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Twc(n)T_{wc}(n)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;w&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, is the maximum value of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t(si)t(s_i)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 across all instances of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SnS_n&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
.&lt;/p&gt;

&lt;p&gt;In simpler terms, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Twc(n)T_{wc}(n)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;w&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 represents the absolute longest time the algorithm could possibly take to solve any problem instance of size n. This worst-case behavior provides an upper bound on the algorithm's running time, guaranteeing that it will never take longer than this amount of time, regardless of the specific input. The rate at which 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Twc(n)T_{wc}(n)&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;w&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 grows as n increases defines the worst-case time complexity of the algorithm.&lt;/p&gt;
&lt;h3&gt;
  
  
  Average Case
&lt;/h3&gt;

&lt;p&gt;Imagine a massive telephone system built to handle a huge number of phones (let's say 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 phones). In the absolute worst-case scenario, we need to be prepared for a situation where half the people (
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n2\frac{n}{2}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
) decide to call the other half 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n2\frac{n}{2}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mopen nulldelimiter"&gt;&lt;/span&gt;&lt;span class="mfrac"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="frac-line"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose nulldelimiter"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 simultaneously. This scenario would put immense strain on the system, and while it might not crash completely, building a system to handle this extreme case would be incredibly costly.&lt;/p&gt;

&lt;p&gt;In reality, the chances of this exact scenario happening are very, very slim. It's highly unlikely that everyone would call a completely different person at the same time. Instead of building an exorbitantly expensive system to handle this improbable worst-case scenario, we can take a more practical approach. We can design a cheaper system and then use mathematical tools to analyze the likelihood of it crashing due to overload under more realistic conditions.&lt;/p&gt;

&lt;p&gt;To do this, we assign probabilities to different scenarios or &lt;em&gt;instances&lt;/em&gt; of phone calls. Each instance 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;sis_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 has a size 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 (representing the total number of phones), and we give each instance a probability between 0 and 1. The sum of all probabilities for all possible instances of size 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 must equal 1.&lt;/p&gt;

&lt;p&gt;For each set of problem instances with size 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 (represented as 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SnS_n&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
), we assign a probability distribution 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;PrsiPr{s_i}&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. This means each individual instance 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;sis_i&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 within 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SnS_n&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 gets a probability value between 0 and 1. These probability values reflect the likelihood of encountering each specific instance. Importantly, the sum of all these probabilities across all instances in 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SnS_n&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 must add up to 1, ensuring that we've covered all possible scenarios.&lt;/p&gt;

&lt;p&gt;More formally, we can express this relationship as:&lt;br&gt;

&lt;/p&gt;
&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;∑si∈SnPr{si}=1
\sum_{s_i \in S_n} Pr \lbrace s_i \rbrace = 1
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mop op-limits"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size3 size1 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mrel mtight"&gt;∈&lt;/span&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size3 size1 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="mop op-symbol large-op"&gt;∑&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;{&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;}&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;This equation simply states that when you add up the probabilities of all possible instances within a given set Sn, the total probability must equal 1 (or 100%).&lt;/p&gt;

&lt;p&gt;If  
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t()t() &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 measures the work done by an algorithm on each instance, then the average case work done by an algorithm on  
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SnS_n &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is:&lt;/p&gt;


&lt;div class="katex-element"&gt;
  &lt;span class="katex-display"&gt;&lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Tac(n)=∑si∈Snt(si)Pr{si}
T_{ac}(n) =  \sum_{s_i \in S_n} t(s_i) Pr \lbrace s_i \rbrace
&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;a&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;=&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mop op-limits"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size3 size1 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mrel mtight"&gt;∈&lt;/span&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size3 size1 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span&gt;&lt;span class="mop op-symbol large-op"&gt;∑&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;{&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/div&gt;


&lt;p&gt;In simpler terms, the average-case work done by an algorithm on a set of problem instances 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;SnS_n &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;S&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is calculated by taking the work done on each specific instance (
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t(si)t(s_i) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
) and multiplying it by the probability of that instance actually occurring (
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Pr{si}Pr \lbrace s_i \rbrace &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;P&lt;/span&gt;&lt;span class="mord mathnormal"&gt;r&lt;/span&gt;&lt;span class="mopen"&gt;{&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;s&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;i&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mclose"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
). If the probability of an instance is zero, it doesn't contribute to the overall average work.&lt;/p&gt;

&lt;p&gt;The average-case work, denoted as 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Tac(n)T_{ac}(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;a&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, gives us a more realistic picture of how an algorithm performs in real-world scenarios. It's a weighted average that takes into account the likelihood of encountering different problem instances. The rate at which 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Tac(n)T_{ac}(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;T&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;a&lt;/span&gt;&lt;span class="mord mathnormal mtight"&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 grows as 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 increases defines the average-case complexity of the algorithm, indicating how the algorithm's performance scales with the size of the input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Case
&lt;/h3&gt;

&lt;p&gt;Understanding the best-case scenario for an algorithm, even though it rarely happens in real life, can be quite insightful. It gives us a glimpse into the ideal conditions that would allow the algorithm to perform optimally.&lt;/p&gt;

&lt;p&gt;Let's take &lt;code&gt;Sequential Search&lt;/code&gt; as an example. The best-case scenario for this algorithm is when the value you're looking for (let's call it &lt;code&gt;v&lt;/code&gt;) happens to be the very first element in the list. It finds it right away, making it super efficient in this specific situation.&lt;/p&gt;

&lt;p&gt;Now, imagine a slightly different approach called &lt;code&gt;Counting Search&lt;/code&gt;. This algorithm counts how many times the value &lt;code&gt;v&lt;/code&gt; appears in the list. If the count is zero, it means the item wasn't found. Otherwise, it's there.&lt;/p&gt;

&lt;p&gt;Here's the key difference: &lt;code&gt;Counting Search&lt;/code&gt; always goes through the entire list, even if it finds &lt;code&gt;v&lt;/code&gt; early on. So, even though its worst-case performance is the same as &lt;code&gt;Sequential Search&lt;/code&gt; (O(n)), its best-case performance is also O(n). This means &lt;code&gt;Counting Search&lt;/code&gt; isn't able to take advantage of situations where it could potentially finish faster, like when the value is found at the beginning.&lt;/p&gt;

&lt;p&gt;In essence, while the best-case scenario might be rare, it reveals the potential for an algorithm to be even more efficient under optimal conditions. Understanding this can guide us in making decisions about which algorithm to choose for a particular task, especially if we know something about the characteristics of the data we're dealing with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lower and Upper Bounds
&lt;/h3&gt;

&lt;p&gt;To simplify how we talk about "Big O" notation in this series, we'll focus on classifying how an algorithm behaves as it tackles problems of growing size, represented by 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. We're going to use the notation 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;O(f(n))O(f(n))&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;O&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;f&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, where 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;f(n)f(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;f&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is typically a function like 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n2n^2 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n3n^3 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, or 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;OnO^n &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;O&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mathnormal mtight"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. &lt;/p&gt;

&lt;p&gt;Let's, for example, consider an algorithm where the worst-case performance grows in direct proportion to the size of the input data, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, once that input gets big enough. This means there's a positive constant 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;cc &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and a threshold size 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n0n_0 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 such that the time it takes for the algorithm to run, represented by 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t(n)t(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, is always less than or equal to 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;cc &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 multiplied by 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 for all values of 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 greater than 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n0n_0 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, or 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t(n)≤c×nt(n) \leq c \times n  &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≤&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;c&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;∀n&amp;gt;n0\forall  n \gt n_0&lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;∀&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;&amp;gt;&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. In simpler terms, the algorithm's runtime is "linear", meaning that its execution time increases proportionally with the input size. It implies that as the size of the input data grows, the time it takes to process that data also grows linearly.&lt;/p&gt;

&lt;p&gt;In this scenario, we classify the algorithm's worst-case performance as 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;O(n)O(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;O&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, where 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;nn &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 represents the input size. This notation, called Big O notation, describes the upper bound of the algorithm's time complexity.&lt;/p&gt;

&lt;p&gt;Now, imagine that the same algorithm's best-case performance is also directly proportional to the input size. This means there's a different positive constant 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;cc &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and a different threshold 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n0n_0 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 such that 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t(n)t(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 is always greater than or equal to 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;O(n)O(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;O&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 multiplied by 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;O(n)O(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;O&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 for all 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;O(n)O(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;O&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 greater than 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;n0n_0 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, or in mathematical terms 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;t(n)≥c×nt(n) \geq c \times n &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;t&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≥&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;c&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mbin"&gt;×&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;∀n≥n0\forall n \ge n_0 &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;∀&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;span class="mrel"&gt;≥&lt;/span&gt;&lt;span class="mspace"&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="msupsub"&gt;&lt;span class="vlist-t vlist-t2"&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;span class="pstrut"&gt;&lt;/span&gt;&lt;span class="sizing reset-size6 size3 mtight"&gt;&lt;span class="mord mtight"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-s"&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class="vlist-r"&gt;&lt;span class="vlist"&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
. In other words, the algorithm's runtime never gets faster than linear, even in the best-case scenario.&lt;/p&gt;

&lt;p&gt;In this situation, we classify the algorithm's best-case performance as  
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Ω(n)Ω(n) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;Ω&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
, using Big Omega notation. This indicates the lower bound of the algorithm's time complexity.&lt;/p&gt;

&lt;p&gt;To summarize, the actual formal notation is as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;lower bound&lt;/em&gt; for the execution time of an algorithm is classified as 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;Ω(f(n))Ω(f(n)) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord"&gt;Ω&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;f&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and corresponds to the best-case scenario.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;upper bound&lt;/em&gt; for execution time of an algorithm is classified as 
&lt;span class="katex-element"&gt;
  &lt;span class="katex"&gt;&lt;span class="katex-mathml"&gt;O(f(n))O(f(n)) &lt;/span&gt;&lt;span class="katex-html"&gt;&lt;span class="base"&gt;&lt;span class="strut"&gt;&lt;/span&gt;&lt;span class="mord mathnormal"&gt;O&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;f&lt;/span&gt;&lt;span class="mopen"&gt;(&lt;/span&gt;&lt;span class="mord mathnormal"&gt;n&lt;/span&gt;&lt;span class="mclose"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;/span&gt;
 and corresponds to the worst-case scenario.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this post we discussed how we analyze algorithms and what is the Big O Notation. In the next post we are going to discuss performance families of the algorithms. See you on the next post!&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
