<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:hashnode="https://hashnode.com/rss"><channel><title><![CDATA[kunmidevOpstories]]></title><description><![CDATA[kunmidevOpstories]]></description><link>https://kunmidevopstories.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 03 Dec 2024 05:49:39 GMT</lastBuildDate><atom:link href="https://kunmidevopstories.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><atom:link rel="next" href="https://kunmidevopstories.hashnode.dev/rss.xml?page=2"/><atom:link rel="previous" href="https://kunmidevopstories.hashnode.dev/rss.xml"/><item><title><![CDATA[How to Achieve Real Zero-Downtime in Kubernetes Rolling Deployments: Avoiding broken client connections]]></title><description><![CDATA[The popular idiom nothing is constant except change was from a Greek philosopher named Heraclitus. Although Heraclitus lived around 500 BCE, this quote remains valid. Thanks to super-efficient orchestration tools like Kubernetes, making changes to ou...]]></description><link>https://kunmidevopstories.hashnode.dev/how-to-achieve-real-zero-downtime-in-kubernetes-rolling-deployments-avoiding-broken-client-connections</link><guid isPermaLink="true">https://kunmidevopstories.hashnode.dev/how-to-achieve-real-zero-downtime-in-kubernetes-rolling-deployments-avoiding-broken-client-connections</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[container orchestration]]></category><category><![CDATA[Zero Downtime]]></category><category><![CDATA[deployment]]></category><dc:creator><![CDATA[Tokede Akinkunmi]]></dc:creator><pubDate>Fri, 16 Feb 2024 15:10:55 GMT</pubDate><content:encoded>&lt;![CDATA[&lt;p&gt;The popular idiom &lt;em&gt;nothing is constant except change&lt;/em&gt; was from a Greek philosopher named Heraclitus. Although Heraclitus lived around 500 BCE, this quote remains valid. Thanks to super-efficient orchestration tools like Kubernetes, making changes to our applications has become more seamless.&lt;/p&gt;&lt;p&gt;In software engineering, we make changes almost every day, but how do we avoid these changes from impacting users negatively? One of the negative impacts on users is through broken connections. I would have loved to discuss the impact of broken client connections, but not in this article.&lt;/p&gt;&lt;p&gt;By default, the Kubernetes deployment strategy involves rolling deployments. Yes! Rolling deployment sounds very interesting, but there is more to that. We need to ask ourselves some questions. What happens during rolling deployments?&lt;/p&gt;&lt;p&gt;Rolling deployments means incrementally replacing the current pods with new ones. During this process, there is always downtime spanning from microseconds to seconds. It might be insignificant for applications with a low user base. But it is significant for big applications, especially payment gateways, where every second counts.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: There are other ways to achieve zero downtime while deploying to production in Kubernetes, such as utilising a service mesh like Istio or implementing a blue-green deployment. These options consume more resources than rolling deployments, leading to an increase in infrastructural costs.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The question, What happens during rolling deployments? can be broken into two.&lt;/p&gt;&lt;p&gt;First, what happens when a pod starts up, and what happens when a pod shuts down?&lt;/p&gt;&lt;p&gt;Before we continue, here are the pre-requisites for this tutorial:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Kubernetes knowledge&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Experience using Docker&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h1 id=&quot;heading-the-startup-stage-of-a-pod&quot;&gt;&lt;strong&gt;The startup stage of a pod&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;When a pod starts in a rolling deployment without the readiness probe configured, the endpoint controller updates the corresponding service objects with the pods endpoints, which means the pod starts receiving traffic even though the pod is not ready. The absence of a readiness probe makes the application unstable.&lt;/p&gt;&lt;p&gt;Having a readiness probe set is recommended for applications. The implication is that it only receives traffic when it is ready; the endpoint controller continues to monitor the pods based on the pods readiness probe result. When the probe is successful, the endpoints are updated on the service objects to receive traffic.&lt;/p&gt;&lt;p&gt;Below is an example of a Kubernetes deployment file with a readiness probe configured:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;my-highly-available-app&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Define desired number of replicas&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available-image:latest&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;containerPort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;readinessProbe:&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Readiness probe configuration&lt;/span&gt;          &lt;span class=&quot;hljs-attr&quot;&gt;httpGet:&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/health-check&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;port:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;initialDelaySeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Wait 5 seconds before starting probes&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;periodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Check every 10 seconds&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;failureThreshold:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Mark unhealthy after 3 consecutive failures&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have established what happens during the startup stage of a pod; it is time to analyse what happens in the shutdown stage.&lt;/p&gt;&lt;h1 id=&quot;heading-the-shutdown-stage-of-a-pod&quot;&gt;&lt;strong&gt;The shutdown stage of a pod&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;It is crucial to understand that components in a Kubernetes cluster are more like micro-services and not monolithic. The way micro-services work is different from how monolithic processes run. In microservices, it takes more time for all the components to sync.&lt;/p&gt;&lt;p&gt;When the API server receives a notification of a pod deletion from a client or during rolling deployment, it first modifies the state of the pod in etcd, then notifies the endpoint controller and the Kubelet. Upon receiving the pod deletion notification from the API server, the endpoint controller removes the pod endpoint from every Service with which the pod is associated.&lt;/p&gt;&lt;p&gt;The endpoint controller on the control plane does this by sending a REST API to the API server. The API server then notifies its watchers, of which Kube-proxies are one; the Kube-proxies update the iptables rules to reflect the changes in the set of endpoints associated with that Service. Updating the iptables rules will prevent new traffic from being directed to the terminating pods.&lt;/p&gt;&lt;p&gt;The scenario described above is where the downtime occurs because it takes more time to update the iptables rules than for the containers to be terminated by the Kubelet. These phases happen concurrently. When a request to delete a pod is received from a client or during a rolling deployment, this request reaches the API server on the control plane. The Kubelet and the endpoint controller watch the API server for changes once the Kubelet and the endpoint controller receive a deletion notification. The Kubelet immediately sends a SIGTERM signal to the container, and the endpoints controller sends a request back to the API server for the pod endpoints to be removed from all service objects, a task performed by Kube-proxies on the worker nodes.&lt;/p&gt;&lt;p&gt;The reason for this downtime is that the containers would have been terminated by Kubelet (which is a shorter process and therefore takes less time) before the pod endpoints are updated on the corresponding Service (which involves more processes and thus takes more time). Due to the difference in task completion time, Services still route traffic to the endpoints of the terminating pods, leading to messages like connection error or connection refused.&lt;/p&gt;&lt;p&gt;The diagram below provides a pictorial view of what happens internally within the Kubernetes architecture.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1708002342236/9f754cdf-c0a4-4d4d-8928-bc45c4fb5d9c.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;We have been able to establish reasons for broken connections during rolling deployments; how then do we solve this issue?&lt;/p&gt;&lt;h2 id=&quot;heading-the-solution&quot;&gt;&lt;strong&gt;The Solution&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Kubernetes was never designed as a plug and play orchestration tool; it requires proper configuration to suit every use case accordingly. Since we have discovered that the difference in task completion time is the main issue, the simple solution is to define a wait time for the proxies to update the iptables.&lt;/p&gt;&lt;p&gt;We can achieve this by adding a &lt;strong&gt;preStop&lt;/strong&gt; hook to the deployment configuration. Before the container shuts down completely, we will configure the container to wait for 20 seconds. It is a synchronous action, which means the container will only shut down when this wait time is complete. By then, the Kube-proxies would have updated the iptables, and new connections would be routed to running pods instead of terminating pods.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: The preStop hook is a mechanism used in pod lifecycle management to execute a specific command or action before a pod terminates&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;It is crucial to understand that when the iptables are updated, connections to the old pods (pods that are terminating) are still maintained, and client connections are not broken until all processes are complete and the pod shuts down gracefully, but new connections are directed to stable pods.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Below is an example of a deployment file with the &lt;strong&gt;preStop&lt;/strong&gt; hook configured:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;my-highly-available-app&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Define desired number of replicas&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available-image:latest&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;containerPort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;readinessProbe:&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Readiness probe configuration&lt;/span&gt;          &lt;span class=&quot;hljs-attr&quot;&gt;httpGet:&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/health-check&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;port:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;initialDelaySeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Wait 5 seconds before starting probes&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;periodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Check every 10 seconds&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;failureThreshold:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Mark unhealthy after 3 consecutive failures&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;lifecycle:&lt;/span&gt;          &lt;span class=&quot;hljs-attr&quot;&gt;preStop:&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;exec:&lt;/span&gt;              &lt;span class=&quot;hljs-attr&quot;&gt;command:&lt;/span&gt; [&lt;span class=&quot;hljs-string&quot;&gt;&quot;/bin/bash&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;-c&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;sleep 20&quot;&lt;/span&gt;]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With the configuration above, rolling deployments will no longer cause downtime on our infrastructure.&lt;/p&gt;&lt;p&gt;Finally, we should always ensure that the sleep time given is less than &lt;strong&gt;terminationGracePeriodSeconds,&lt;/strong&gt; which is 30 seconds by default. A higher value will only cause the container to shut down forcefully.&lt;/p&gt;&lt;h2 id=&quot;heading-conclusion&quot;&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;To sum up, we have made significant progress in ensuring stable user connections during rolling deployments, regardless of the number of deployment versions released daily. We have modified our deployment file to include a &lt;em&gt;readiness probe and a pre-stop hook&lt;/em&gt;. These changes enable us to manage traffic during pod startup and shutdown more effectively.&lt;/p&gt;&lt;p&gt;It has indeed been a productive time with you. Happy automating!&lt;/p&gt;]]&gt;</content:encoded><hashnode:content>&lt;![CDATA[&lt;p&gt;The popular idiom &lt;em&gt;nothing is constant except change&lt;/em&gt; was from a Greek philosopher named Heraclitus. Although Heraclitus lived around 500 BCE, this quote remains valid. Thanks to super-efficient orchestration tools like Kubernetes, making changes to our applications has become more seamless.&lt;/p&gt;&lt;p&gt;In software engineering, we make changes almost every day, but how do we avoid these changes from impacting users negatively? One of the negative impacts on users is through broken connections. I would have loved to discuss the impact of broken client connections, but not in this article.&lt;/p&gt;&lt;p&gt;By default, the Kubernetes deployment strategy involves rolling deployments. Yes! Rolling deployment sounds very interesting, but there is more to that. We need to ask ourselves some questions. What happens during rolling deployments?&lt;/p&gt;&lt;p&gt;Rolling deployments means incrementally replacing the current pods with new ones. During this process, there is always downtime spanning from microseconds to seconds. It might be insignificant for applications with a low user base. But it is significant for big applications, especially payment gateways, where every second counts.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: There are other ways to achieve zero downtime while deploying to production in Kubernetes, such as utilising a service mesh like Istio or implementing a blue-green deployment. These options consume more resources than rolling deployments, leading to an increase in infrastructural costs.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;The question, What happens during rolling deployments? can be broken into two.&lt;/p&gt;&lt;p&gt;First, what happens when a pod starts up, and what happens when a pod shuts down?&lt;/p&gt;&lt;p&gt;Before we continue, here are the pre-requisites for this tutorial:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Kubernetes knowledge&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Experience using Docker&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h1 id=&quot;heading-the-startup-stage-of-a-pod&quot;&gt;&lt;strong&gt;The startup stage of a pod&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;When a pod starts in a rolling deployment without the readiness probe configured, the endpoint controller updates the corresponding service objects with the pods endpoints, which means the pod starts receiving traffic even though the pod is not ready. The absence of a readiness probe makes the application unstable.&lt;/p&gt;&lt;p&gt;Having a readiness probe set is recommended for applications. The implication is that it only receives traffic when it is ready; the endpoint controller continues to monitor the pods based on the pods readiness probe result. When the probe is successful, the endpoints are updated on the service objects to receive traffic.&lt;/p&gt;&lt;p&gt;Below is an example of a Kubernetes deployment file with a readiness probe configured:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;my-highly-available-app&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Define desired number of replicas&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available-image:latest&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;containerPort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;readinessProbe:&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Readiness probe configuration&lt;/span&gt;          &lt;span class=&quot;hljs-attr&quot;&gt;httpGet:&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/health-check&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;port:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;initialDelaySeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Wait 5 seconds before starting probes&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;periodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Check every 10 seconds&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;failureThreshold:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Mark unhealthy after 3 consecutive failures&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We have established what happens during the startup stage of a pod; it is time to analyse what happens in the shutdown stage.&lt;/p&gt;&lt;h1 id=&quot;heading-the-shutdown-stage-of-a-pod&quot;&gt;&lt;strong&gt;The shutdown stage of a pod&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;It is crucial to understand that components in a Kubernetes cluster are more like micro-services and not monolithic. The way micro-services work is different from how monolithic processes run. In microservices, it takes more time for all the components to sync.&lt;/p&gt;&lt;p&gt;When the API server receives a notification of a pod deletion from a client or during rolling deployment, it first modifies the state of the pod in etcd, then notifies the endpoint controller and the Kubelet. Upon receiving the pod deletion notification from the API server, the endpoint controller removes the pod endpoint from every Service with which the pod is associated.&lt;/p&gt;&lt;p&gt;The endpoint controller on the control plane does this by sending a REST API to the API server. The API server then notifies its watchers, of which Kube-proxies are one; the Kube-proxies update the iptables rules to reflect the changes in the set of endpoints associated with that Service. Updating the iptables rules will prevent new traffic from being directed to the terminating pods.&lt;/p&gt;&lt;p&gt;The scenario described above is where the downtime occurs because it takes more time to update the iptables rules than for the containers to be terminated by the Kubelet. These phases happen concurrently. When a request to delete a pod is received from a client or during a rolling deployment, this request reaches the API server on the control plane. The Kubelet and the endpoint controller watch the API server for changes once the Kubelet and the endpoint controller receive a deletion notification. The Kubelet immediately sends a SIGTERM signal to the container, and the endpoints controller sends a request back to the API server for the pod endpoints to be removed from all service objects, a task performed by Kube-proxies on the worker nodes.&lt;/p&gt;&lt;p&gt;The reason for this downtime is that the containers would have been terminated by Kubelet (which is a shorter process and therefore takes less time) before the pod endpoints are updated on the corresponding Service (which involves more processes and thus takes more time). Due to the difference in task completion time, Services still route traffic to the endpoints of the terminating pods, leading to messages like connection error or connection refused.&lt;/p&gt;&lt;p&gt;The diagram below provides a pictorial view of what happens internally within the Kubernetes architecture.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1708002342236/9f754cdf-c0a4-4d4d-8928-bc45c4fb5d9c.png&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;We have been able to establish reasons for broken connections during rolling deployments; how then do we solve this issue?&lt;/p&gt;&lt;h2 id=&quot;heading-the-solution&quot;&gt;&lt;strong&gt;The Solution&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;Kubernetes was never designed as a plug and play orchestration tool; it requires proper configuration to suit every use case accordingly. Since we have discovered that the difference in task completion time is the main issue, the simple solution is to define a wait time for the proxies to update the iptables.&lt;/p&gt;&lt;p&gt;We can achieve this by adding a &lt;strong&gt;preStop&lt;/strong&gt; hook to the deployment configuration. Before the container shuts down completely, we will configure the container to wait for 20 seconds. It is a synchronous action, which means the container will only shut down when this wait time is complete. By then, the Kube-proxies would have updated the iptables, and new connections would be routed to running pods instead of terminating pods.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Note: The preStop hook is a mechanism used in pod lifecycle management to execute a specific command or action before a pod terminates&lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;It is crucial to understand that when the iptables are updated, connections to the old pods (pods that are terminating) are still maintained, and client connections are not broken until all processes are complete and the pod shuts down gracefully, but new connections are directed to stable pods.&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Below is an example of a deployment file with the &lt;strong&gt;preStop&lt;/strong&gt; hook configured:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;my-highly-available-app&lt;/span&gt;&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Define desired number of replicas&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;highly-available-image:latest&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;containerPort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;readinessProbe:&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Readiness probe configuration&lt;/span&gt;          &lt;span class=&quot;hljs-attr&quot;&gt;httpGet:&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/health-check&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;port:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;initialDelaySeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Wait 5 seconds before starting probes&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;periodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Check every 10 seconds&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;failureThreshold:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Mark unhealthy after 3 consecutive failures&lt;/span&gt;        &lt;span class=&quot;hljs-attr&quot;&gt;lifecycle:&lt;/span&gt;          &lt;span class=&quot;hljs-attr&quot;&gt;preStop:&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;exec:&lt;/span&gt;              &lt;span class=&quot;hljs-attr&quot;&gt;command:&lt;/span&gt; [&lt;span class=&quot;hljs-string&quot;&gt;&quot;/bin/bash&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;-c&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;sleep 20&quot;&lt;/span&gt;]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With the configuration above, rolling deployments will no longer cause downtime on our infrastructure.&lt;/p&gt;&lt;p&gt;Finally, we should always ensure that the sleep time given is less than &lt;strong&gt;terminationGracePeriodSeconds,&lt;/strong&gt; which is 30 seconds by default. A higher value will only cause the container to shut down forcefully.&lt;/p&gt;&lt;h2 id=&quot;heading-conclusion&quot;&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;To sum up, we have made significant progress in ensuring stable user connections during rolling deployments, regardless of the number of deployment versions released daily. We have modified our deployment file to include a &lt;em&gt;readiness probe and a pre-stop hook&lt;/em&gt;. These changes enable us to manage traffic during pod startup and shutdown more effectively.&lt;/p&gt;&lt;p&gt;It has indeed been a productive time with you. Happy automating!&lt;/p&gt;]]&gt;</hashnode:content><hashnode:coverImage>https://cdn.hashnode.com/res/hashnode/image/upload/v1708006680961/7d13cfc6-e438-4da1-8ff5-2f72577a5a76.png</hashnode:coverImage></item><item><title><![CDATA[HOW TO RUN DOCKER IN DOCKER [Leveraging on Unix Sockets]]]></title><description><![CDATA[This helps to run docker inside docker easily]]></description><link>https://kunmidevopstories.hashnode.dev/how-to-run-docker-in-docker-leveraging-on-unix-sockets</link><guid isPermaLink="true">https://kunmidevopstories.hashnode.dev/how-to-run-docker-in-docker-leveraging-on-unix-sockets</guid><category><![CDATA[Docker]]></category><category><![CDATA[containerization]]></category><category><![CDATA[Jenkins]]></category><category><![CDATA[AWS]]></category><category><![CDATA[AWS ECR]]></category><category><![CDATA[container orchestration]]></category><dc:creator><![CDATA[Tokede Akinkunmi]]></dc:creator><pubDate>Wed, 02 Aug 2023 23:00:00 GMT</pubDate><content:encoded>&lt;![CDATA[&lt;p&gt;In a world where everyone seeks convenience, complications can arise when attempting to simplify processes. However, its interesting to discover that providing solutions to these complications can make the whole process a lot easier and more exciting.&lt;/p&gt;&lt;p&gt;Containerization represents a significant improvement over the traditional way of running applications. The advantages of containerization cannot be overstated, from its ability to run applications in any environment to efficient versioning through multiple available registries and more.&lt;/p&gt;&lt;p&gt;In this tutorial, we will focus on a specific use case that commonly appears in production environments.&lt;/p&gt;&lt;p&gt;Heres the scenario: we have pulled a Jenkins image from the official Docker registry, and its running in a container on port 8080. Jenkins will be utilized for continuous integration and deployment of our applications. As mentioned earlier, we have decided to containerize our applications, so in the Jenkins pipeline, we will need to push each version of our application to AWS ECR (Elastic Container Registry).&lt;/p&gt;&lt;p&gt;In the given scenario, the Jenkins agent running in Docker needs to execute Docker commands to build the Docker image and push it to AWS ECR. This requires having Docker present in that container. So, how can we achieve this?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Ubuntu 20.04 AMI on AWS&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Root SSH access or sudo privileges&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Knowledgeable about Jenkins and Docker&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h1 id=&quot;heading-step-1-install-docker-on-the-host-machine&quot;&gt;&lt;strong&gt;Step 1: Install Docker on the Host Machine&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;We shouldnt forget that the host machine is where the Jenkins container will be running. The following commands will help you with the installation of Docker on Ubuntu.&lt;/p&gt;&lt;p&gt;Lets update the package index and install all necessary dependencies. Run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo apt-get updatesudo apt install apt-transport-https ca-certificates curl software-properties-common&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, add Dockers GPG key and repository to your host machine&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu focal stable&quot;&lt;/span&gt; | sudo tee /etc/apt/sources.list.d/docker.list&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Note: The command above is specifically for Ubuntu 20.04&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Finally, install the Docker engine. Run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo apt-get updatesudo apt-get install docker-ce&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We will need to enable and start the Docker service. Run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo systemctl start dockersudo systemctl &lt;span class=&quot;hljs-built_in&quot;&gt;enable&lt;/span&gt; docker&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yes! We have Docker running on our host machine. Lets verify it by running:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;docker --version&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A result similar to this should display:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;ubuntu@ip-45-67-23:$ docker --versionDocker version 24.0.4, build 3713ee1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The next step is quite optional, but its recommended. If you wish to run Docker commands on your host machine without preceding those commands with sudo every time, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo usermod -aG docker &lt;span class=&quot;hljs-variable&quot;&gt;$USER&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;heading-step-2-customize-the-official-jenkins-image&quot;&gt;&lt;strong&gt;Step 2: Customize the official Jenkins Image&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;One of the greatest advantages of containerization is the ability to pull an image from a Docker repository to be used directly or as a base image for further customization to suit a specific use case. For the use case stated above, this is how our Dockerfile will look like:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-dockerfile&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; jenkins/jenkins:latest&lt;span class=&quot;hljs-keyword&quot;&gt;USER&lt;/span&gt; root&lt;span class=&quot;hljs-comment&quot;&gt;# Install sudo&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;bash&quot;&gt; apt-get update &amp;amp;&amp;amp; \    apt-get install -y sudo&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Install dependencies&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;bash&quot;&gt; apt-get update &amp;amp;&amp;amp; \    apt-get install -y unzip &amp;amp;&amp;amp; \    apt-get install -y python3-pip &amp;amp;&amp;amp; \    pip3 install --upgrade pip&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Install AWS CLI&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;bash&quot;&gt; curl &lt;span class=&quot;hljs-string&quot;&gt;&quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot;&lt;/span&gt; -o &lt;span class=&quot;hljs-string&quot;&gt;&quot;awscliv2.zip&quot;&lt;/span&gt; &amp;amp;&amp;amp; \Unzip awscliv2.zip and \    ./aws/install&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;USER&lt;/span&gt; jenkins&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above Dockerfile indicates that we pulled the official Jenkins image and then switched the user to root just to install all required dependencies, after which we switched back to the Jenkins user to ensure Jenkins ran with non-root privileges for security purposes.&lt;/p&gt;&lt;p&gt;To build the Jenkins image from the above Dockerfile, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo docker build -t test_jenkins:latest &amp;lt;path to dockerfile&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above Docker command builds a customized Jenkins image with test_jenkins as its name and tags it as latest. It is important for us to indicate the path to our Dockerfile.&lt;/p&gt;&lt;p&gt;To verify if we have successfully built our Jenkins image, lets run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo docker images&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After running the command above, you should see a result similar to this in your shell:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;ubuntu@ip-174-42-12-23:$ sudo docker imagesREPOSITORY        TAG          IMAGE ID       CREATED        SIZEtest_jenkins     latest       63f37aa302   2 minutes ago   1.2GB&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The output above shows the name of the image, the tag, the image ID, which is also very useful, the creation time, and the size. The output gives a summary of the image built.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: Docker images take up a lot of space on the host machine over time after building several images. In a production environment, it is advisable to set up a cron job to help clean up the unused Docker images at certain intervals.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&lt;h1 id=&quot;heading-step-3-perform-the-magic&quot;&gt;&lt;strong&gt;Step 3: Perform the Magic&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;Here is the most critical part. We are about to perform magic! It is not magic per se, but just a smart move. Let us take a little dive into how Docker works.&lt;br /&gt;When we install Docker on our host machine, in our case, Ubuntu AMI is our host machine. It creates a Unix socket in this location: /var/run/docker.sock . This Unix socket is used for communication between the Docker client and the Docker daemon.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: The Unix socket is a special file-based communication mechanism in Unix-like operating systems.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Okay, lets break it down: the Docker client allows us to interact with Docker using docker commands, while the Docker daemon listens to requests from the Docker client and manages Docker objects such as images, containers, volumes, and so on. What facilitates communication between the Docker client and the Docker daemon is the &lt;em&gt;Unix socket.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Below is a diagram from Docker&apos;s official documentation on Docker architecture:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1708003733278/a90fcefc-72c8-45cf-9118-c3ceba473087.webp&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;From the explanation given above, we now know that /var/run/docker.sock is the most important location for docker communication. Therefore, we will be mapping it as a volume to the container. This implies that when we run a docker command in the container, the container has access to the socket /var/run/docker.sock, which will help pass the request to the Docker daemon. Remember that the Docker daemon manages Docker objects such as images, volumes, and so on.&lt;/p&gt;&lt;p&gt;We will be using the docker run command to start our container and map the necessary volumes. Lets run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo docker run -d --name jenkins -p 8080:8080 -v /var/run/docker.sock:/var/run/docker.sock -v jenkins_data:/var/jenkins_home test_jenkins:latest&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;It is important to note that in the command above, I created an extra Docker volume called jenkins_data mapped to the Jenkins_home directory for data persistence in case the container dies and we need to run a new one. External volume mapping is a good example of best practices.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;To confirm if our Docker container is running or not, lets run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo docker ps&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This should return an output similar to this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;ubuntu@ip-176-45-67-23:$ sudo docker psCONTAINER ID   IMAGE                  COMMAND                 CREATED       STATUS       PORTS                                                                                      NAMES9ed41r31067w test_jenkins:latest &lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/bin/tini -- /u...&quot;&lt;/span&gt; 1 minute ago   1 minute ago 0.0.0.0:8080-&amp;gt;8080/tcp, :::8080-&amp;gt;8080/tcp,   jenkins&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Well done if you can see an output similar to the one shown above. Finally, to ensure this works perfectly, let us make this location executable: /var/run/docker.sock. The purpose of this tutorial is not only to provide a solution but also to educate you on how it works, as most articles out there wont explain the underlying principles. To achieve this, on the host machine, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;chmod +x /var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The command above allows us to run docker commands from the container without any errors. We can therefore proceed with writing our pipeline scripts to push to AWS ECR (Elastic Container Registry).&lt;/p&gt;&lt;p&gt;Congratulations! You now have a clear understanding of how to achieve Docker in Docker (DinD) functionality. By mapping the Docker daemons Unix socket into a Docker container using the &lt;strong&gt;&lt;em&gt;-v /var/run/docker.sock:/var/run/docker.sock&lt;/em&gt;&lt;/strong&gt; option, you enable the container&apos;s Docker client to interact with the Docker daemon running on the host machine. This allows you to manage sibling containers and run Docker commands within the container itself. Happy containerizing!&lt;/p&gt;]]&gt;</content:encoded><hashnode:content>&lt;![CDATA[&lt;p&gt;In a world where everyone seeks convenience, complications can arise when attempting to simplify processes. However, its interesting to discover that providing solutions to these complications can make the whole process a lot easier and more exciting.&lt;/p&gt;&lt;p&gt;Containerization represents a significant improvement over the traditional way of running applications. The advantages of containerization cannot be overstated, from its ability to run applications in any environment to efficient versioning through multiple available registries and more.&lt;/p&gt;&lt;p&gt;In this tutorial, we will focus on a specific use case that commonly appears in production environments.&lt;/p&gt;&lt;p&gt;Heres the scenario: we have pulled a Jenkins image from the official Docker registry, and its running in a container on port 8080. Jenkins will be utilized for continuous integration and deployment of our applications. As mentioned earlier, we have decided to containerize our applications, so in the Jenkins pipeline, we will need to push each version of our application to AWS ECR (Elastic Container Registry).&lt;/p&gt;&lt;p&gt;In the given scenario, the Jenkins agent running in Docker needs to execute Docker commands to build the Docker image and push it to AWS ECR. This requires having Docker present in that container. So, how can we achieve this?&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Ubuntu 20.04 AMI on AWS&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Root SSH access or sudo privileges&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Knowledgeable about Jenkins and Docker&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h1 id=&quot;heading-step-1-install-docker-on-the-host-machine&quot;&gt;&lt;strong&gt;Step 1: Install Docker on the Host Machine&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;We shouldnt forget that the host machine is where the Jenkins container will be running. The following commands will help you with the installation of Docker on Ubuntu.&lt;/p&gt;&lt;p&gt;Lets update the package index and install all necessary dependencies. Run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo apt-get updatesudo apt install apt-transport-https ca-certificates curl software-properties-common&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, add Dockers GPG key and repository to your host machine&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu focal stable&quot;&lt;/span&gt; | sudo tee /etc/apt/sources.list.d/docker.list&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Note: The command above is specifically for Ubuntu 20.04&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Finally, install the Docker engine. Run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo apt-get updatesudo apt-get install docker-ce&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;We will need to enable and start the Docker service. Run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo systemctl start dockersudo systemctl &lt;span class=&quot;hljs-built_in&quot;&gt;enable&lt;/span&gt; docker&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yes! We have Docker running on our host machine. Lets verify it by running:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;docker --version&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A result similar to this should display:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;ubuntu@ip-45-67-23:$ docker --versionDocker version 24.0.4, build 3713ee1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The next step is quite optional, but its recommended. If you wish to run Docker commands on your host machine without preceding those commands with sudo every time, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo usermod -aG docker &lt;span class=&quot;hljs-variable&quot;&gt;$USER&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&quot;heading-step-2-customize-the-official-jenkins-image&quot;&gt;&lt;strong&gt;Step 2: Customize the official Jenkins Image&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;One of the greatest advantages of containerization is the ability to pull an image from a Docker repository to be used directly or as a base image for further customization to suit a specific use case. For the use case stated above, this is how our Dockerfile will look like:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-dockerfile&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;FROM&lt;/span&gt; jenkins/jenkins:latest&lt;span class=&quot;hljs-keyword&quot;&gt;USER&lt;/span&gt; root&lt;span class=&quot;hljs-comment&quot;&gt;# Install sudo&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;bash&quot;&gt; apt-get update &amp;amp;&amp;amp; \    apt-get install -y sudo&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Install dependencies&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;bash&quot;&gt; apt-get update &amp;amp;&amp;amp; \    apt-get install -y unzip &amp;amp;&amp;amp; \    apt-get install -y python3-pip &amp;amp;&amp;amp; \    pip3 install --upgrade pip&lt;/span&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# Install AWS CLI&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;RUN&lt;/span&gt;&lt;span class=&quot;bash&quot;&gt; curl &lt;span class=&quot;hljs-string&quot;&gt;&quot;https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip&quot;&lt;/span&gt; -o &lt;span class=&quot;hljs-string&quot;&gt;&quot;awscliv2.zip&quot;&lt;/span&gt; &amp;amp;&amp;amp; \Unzip awscliv2.zip and \    ./aws/install&lt;/span&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;USER&lt;/span&gt; jenkins&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above Dockerfile indicates that we pulled the official Jenkins image and then switched the user to root just to install all required dependencies, after which we switched back to the Jenkins user to ensure Jenkins ran with non-root privileges for security purposes.&lt;/p&gt;&lt;p&gt;To build the Jenkins image from the above Dockerfile, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo docker build -t test_jenkins:latest &amp;lt;path to dockerfile&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above Docker command builds a customized Jenkins image with test_jenkins as its name and tags it as latest. It is important for us to indicate the path to our Dockerfile.&lt;/p&gt;&lt;p&gt;To verify if we have successfully built our Jenkins image, lets run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo docker images&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After running the command above, you should see a result similar to this in your shell:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;ubuntu@ip-174-42-12-23:$ sudo docker imagesREPOSITORY        TAG          IMAGE ID       CREATED        SIZEtest_jenkins     latest       63f37aa302   2 minutes ago   1.2GB&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The output above shows the name of the image, the tag, the image ID, which is also very useful, the creation time, and the size. The output gives a summary of the image built.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: Docker images take up a lot of space on the host machine over time after building several images. In a production environment, it is advisable to set up a cron job to help clean up the unused Docker images at certain intervals.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&lt;h1 id=&quot;heading-step-3-perform-the-magic&quot;&gt;&lt;strong&gt;Step 3: Perform the Magic&lt;/strong&gt;&lt;/h1&gt;&lt;p&gt;Here is the most critical part. We are about to perform magic! It is not magic per se, but just a smart move. Let us take a little dive into how Docker works.&lt;br /&gt;When we install Docker on our host machine, in our case, Ubuntu AMI is our host machine. It creates a Unix socket in this location: /var/run/docker.sock . This Unix socket is used for communication between the Docker client and the Docker daemon.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;Note: The Unix socket is a special file-based communication mechanism in Unix-like operating systems.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Okay, lets break it down: the Docker client allows us to interact with Docker using docker commands, while the Docker daemon listens to requests from the Docker client and manages Docker objects such as images, containers, volumes, and so on. What facilitates communication between the Docker client and the Docker daemon is the &lt;em&gt;Unix socket.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Below is a diagram from Docker&apos;s official documentation on Docker architecture:&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://cdn.hashnode.com/res/hashnode/image/upload/v1708003733278/a90fcefc-72c8-45cf-9118-c3ceba473087.webp&quot; alt class=&quot;image--center mx-auto&quot; /&gt;&lt;/p&gt;&lt;p&gt;From the explanation given above, we now know that /var/run/docker.sock is the most important location for docker communication. Therefore, we will be mapping it as a volume to the container. This implies that when we run a docker command in the container, the container has access to the socket /var/run/docker.sock, which will help pass the request to the Docker daemon. Remember that the Docker daemon manages Docker objects such as images, volumes, and so on.&lt;/p&gt;&lt;p&gt;We will be using the docker run command to start our container and map the necessary volumes. Lets run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo docker run -d --name jenkins -p 8080:8080 -v /var/run/docker.sock:/var/run/docker.sock -v jenkins_data:/var/jenkins_home test_jenkins:latest&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;It is important to note that in the command above, I created an extra Docker volume called jenkins_data mapped to the Jenkins_home directory for data persistence in case the container dies and we need to run a new one. External volume mapping is a good example of best practices.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;To confirm if our Docker container is running or not, lets run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;sudo docker ps&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This should return an output similar to this:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;ubuntu@ip-176-45-67-23:$ sudo docker psCONTAINER ID   IMAGE                  COMMAND                 CREATED       STATUS       PORTS                                                                                      NAMES9ed41r31067w test_jenkins:latest &lt;span class=&quot;hljs-string&quot;&gt;&quot;/usr/bin/tini -- /u...&quot;&lt;/span&gt; 1 minute ago   1 minute ago 0.0.0.0:8080-&amp;gt;8080/tcp, :::8080-&amp;gt;8080/tcp,   jenkins&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Well done if you can see an output similar to the one shown above. Finally, to ensure this works perfectly, let us make this location executable: /var/run/docker.sock. The purpose of this tutorial is not only to provide a solution but also to educate you on how it works, as most articles out there wont explain the underlying principles. To achieve this, on the host machine, run:&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;lang-bash&quot;&gt;chmod +x /var/run/docker.sock&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The command above allows us to run docker commands from the container without any errors. We can therefore proceed with writing our pipeline scripts to push to AWS ECR (Elastic Container Registry).&lt;/p&gt;&lt;p&gt;Congratulations! You now have a clear understanding of how to achieve Docker in Docker (DinD) functionality. By mapping the Docker daemons Unix socket into a Docker container using the &lt;strong&gt;&lt;em&gt;-v /var/run/docker.sock:/var/run/docker.sock&lt;/em&gt;&lt;/strong&gt; option, you enable the container&apos;s Docker client to interact with the Docker daemon running on the host machine. This allows you to manage sibling containers and run Docker commands within the container itself. Happy containerizing!&lt;/p&gt;]]&gt;</hashnode:content><hashnode:coverImage>https://cdn.hashnode.com/res/hashnode/image/upload/v1708005722284/62be4224-8009-4f34-b4a6-116ee799dd4f.webp</hashnode:coverImage></item></channel></rss>