<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>devops Archives - Tech Chronicles</title>
	<atom:link href="http://kostacipo.stream/tag/devops/feed/" rel="self" type="application/rss+xml" />
	<link>http://kostacipo.stream/tag/devops/</link>
	<description>Ramblings of a Tech Dude</description>
	<lastBuildDate>Tue, 16 Feb 2021 21:34:22 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.2</generator>

<image>
	<url>https://kostacipo.stream/wp-content/uploads/2019/12/cropped-profile-32x32.jpg</url>
	<title>devops Archives - Tech Chronicles</title>
	<link>http://kostacipo.stream/tag/devops/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>What is DevOps?</title>
		<link>http://kostacipo.stream/what-is-devops/</link>
					<comments>http://kostacipo.stream/what-is-devops/#respond</comments>
		
		<dc:creator><![CDATA[Majordomo]]></dc:creator>
		<pubDate>Tue, 16 Feb 2021 21:34:22 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[Software Development]]></category>
		<category><![CDATA[ci/cd]]></category>
		<category><![CDATA[devops]]></category>
		<guid isPermaLink="false">http://kostacipo.stream/?p=2045</guid>

					<description><![CDATA[<p>DevOps is the simplification or automation of established IT processes. Here&#8217;s a brief tutorial to understand and get started with DevOps. DevOps… CI/CD… Docker… Kubernetes… I&#8217;m sure you&#8217;ve been bombarded with these words a lot the past year. Seems like the entire world is talking about it. The rate at which this segment is progressing, [&#8230;]</p>
<p>The post <a href="http://kostacipo.stream/what-is-devops/">What is DevOps?</a> appeared first on <a href="http://kostacipo.stream">Tech Chronicles</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h3>DevOps is the simplification or automation of established IT processes.</h3>
<h5>Here&#8217;s a brief tutorial to understand and get started with DevOps.</h5>
<p data-selectable-paragraph="">DevOps… CI/CD… Docker… Kubernetes… I&#8217;m sure you&#8217;ve been bombarded with these words a lot the past year. Seems like the entire world is talking about it. The rate at which this segment is progressing, it won&#8217;t be long before we reach the stage of NoOps. It&#8217;s about time we break down what DevOps really is.</p>
<p data-selectable-paragraph="">The objective of this article is to set a solid foundation for you to build on top of. So let&#8217;s start with the obvious question.</p>
<h2 data-selectable-paragraph="">What is DevOps?</h2>
<blockquote>
<p data-selectable-paragraph="">DevOps is the simplification or automation of <strong>established IT processes.</strong></p>
</blockquote>
<p data-selectable-paragraph="">I&#8217;ve seen so many people start this journey to adopt DevOps to only find themselves lost. There seems to be a pattern to this.</p>
<p data-selectable-paragraph="">It usually starts with a video on how a fancy tech startup has automated its release cycle. Deployments happen automatically once all the tests pass. Rollbacks, in case of failures, is automatic. Thousands of simultaneous A/B test is driving up customer engagement.</p>
<p data-selectable-paragraph="">We all are tired of releasing a new version like it&#8217;s a rollercoaster ride.</p>
<figure>
<div tabindex="0"><img decoding="async" class="fr-fic fr-dib lazyloaded" src="https://dzone.com/storage/temp/14354587-devops-rollercoaster.png" alt="The DevOps rollercoaster" data-image="true" data-new="false" data-sizeformatted="179.5 kB" data-mimetype="image/png" data-creationdate="1611506359500" data-creationdateformatted="01/24/2021 04:39 PM" data-type="temp" data-url="/storage/temp/14354587-devops-rollercoaster.png" data-modificationdate="null" data-size="179456" data-name="devops-rollercoaster.png" data-id="14354587" data-src="/storage/temp/14354587-devops-rollercoaster.png" /></div>
</figure>
<p data-selectable-paragraph="">Unfortunately, DevOps doesn&#8217;t work that way. DevOps isn&#8217;t a magic wand that can solve all your problems in an instant.</p>
<p data-selectable-paragraph="">Instead, it is a systematic process of choosing the right tools and technology to get the job done.</p>
<h3 data-selectable-paragraph="">All of This Starts With a Process</h3>
<p data-selectable-paragraph="">It doesn&#8217;t matter what the process is. It could be simplifying the deployment of your app or automate testing. Whatever your process is, the act of making your life easier is what DevOps is all about.</p>
<p data-selectable-paragraph="">But you always need to start with a process.</p>
<p data-selectable-paragraph="">In fact, <em>if your process cannot be done manually (on a smaller scale), you should probably re-examine your process.</em></p>
<p data-selectable-paragraph="">I mean it.</p>
<p data-selectable-paragraph="">Enough talking. Let&#8217;s take a real-world example to understand things better.</p>
<h2 data-selectable-paragraph="">Let&#8217;s Take a Real DevOps Example</h2>
<p data-selectable-paragraph="">Let&#8217;s take a simple example of <em>making a </em><a href="https://nodejs.org/en/" target="_blank" rel="noopener nofollow"><em>Nodejs</em></a><em> app live on a VM in the cloud</em>.</p>
<h3 data-selectable-paragraph="">The Process</h3>
<p data-selectable-paragraph="">Here&#8217;s what our process looks like:</p>
<ul>
<li data-selectable-paragraph=""><strong>Start with the source code:</strong> This is our source of truth. We can run our process from anywhere as long as we have access to the source code.</li>
<li data-selectable-paragraph=""><strong>Build an Artifact:</strong> We then package our source code to build an <a href="https://en.wikipedia.org/wiki/Artifact_(software_development)" target="_blank" rel="noopener nofollow">Artifact</a>. In the case of a compiled language, the compiled output (JAR file, in the case of JAVA) would be our artifact. In our case, our source code itself is the artifact to be released.</li>
<li data-selectable-paragraph=""><strong>Publish to an Artifact Repository:</strong> Next, we push our artifact to a repository. This is a location from where our target environment can pull the artifact from. We could stick with something like <a href="https://github.com/" target="_blank" rel="noopener nofollow">Github</a> since we are working with source code here.</li>
<li data-selectable-paragraph=""><strong>Pull and run your app:</strong> Finally, we pull the artifact onto our VM and schedule a Nodejs process by running <code>npm start</code>.</li>
</ul>
<figure>
<div tabindex="0"><img decoding="async" class="fr-fic fr-dib lazyloaded" src="https://dzone.com/storage/temp/14354591-devops-deploy-node-app-on-vm.png" alt="process diagram" data-image="true" data-new="false" data-sizeformatted="52.5 kB" data-mimetype="image/png" data-creationdate="1611506405487" data-creationdateformatted="01/24/2021 04:40 PM" data-type="temp" data-url="/storage/temp/14354591-devops-deploy-node-app-on-vm.png" data-modificationdate="null" data-size="52531" data-name="devops-deploy-node-app-on-vm.png" data-id="14354591" data-src="/storage/temp/14354591-devops-deploy-node-app-on-vm.png" /></div>
</figure>
<p data-selectable-paragraph="">It&#8217;s okay if you do things a slightly different way. We are here to focus on the journey and not the destination.</p>
<h2 data-selectable-paragraph="">Our First DevOps Project</h2>
<p data-selectable-paragraph="">Let&#8217;s not do anything fancy here.</p>
<p data-selectable-paragraph="">The easiest way to automate this process would be to write a simple <a href="https://en.wikipedia.org/wiki/Shell_script" target="_blank" rel="noopener nofollow">shell script</a> to run all the commands in sequence.</p>
<p data-selectable-paragraph=""><strong>Congratulations!!! That&#8217;s our first DevOps project!!!</strong></p>
<p data-selectable-paragraph="">I know shell scripts sounds too simple to be taken seriously. I suspect you already have such scripts in place. But believe me, that&#8217;s DevOps!</p>
<p data-selectable-paragraph="">Don’t worry; we will get to the fancy stuff in a minute. But it&#8217;s important to understand that this is how DevOps works.</p>
<h2 data-selectable-paragraph="">Importance of Repeatability</h2>
<p data-selectable-paragraph="">Let me ask you one question. Which one of these would you prefer?</p>
<ul>
<li data-selectable-paragraph="">An automated deployments pipeline which works 60% of the time, or,</li>
<li data-selectable-paragraph="">A boring shell script that gets the job done every time it&#8217;s executed.</li>
</ul>
<p data-selectable-paragraph="">If you have dealt with production failures in the middle of the night, you&#8217;ll choose the shell script.</p>
<p data-selectable-paragraph="">The reason is simple.</p>
<p data-selectable-paragraph=""><strong>Reliability is far more critical than the degree of automation.</strong></p>
<p data-selectable-paragraph="">In other words,</p>
<blockquote>
<p data-selectable-paragraph="">A DevOps process must be able to produce consistent results every time it&#8217;s run.</p>
</blockquote>
<h3 data-selectable-paragraph="">Making Our Process Repeatable</h3>
<p data-selectable-paragraph="">Let&#8217;s take the example of our shell script.</p>
<p data-selectable-paragraph="">Currently, our shell script depends on Node.js to be installed on the VM we want to deploy the app to.</p>
<p data-selectable-paragraph="">What would happen if the Nodejs runtime was missing? These days, an incorrect version of the runtime is enough to break our application.</p>
<p data-selectable-paragraph="">This problem only gets worse in <a href="https://en.wikipedia.org/wiki/Polyglot_(computing)" target="_blank" rel="noopener nofollow">polyglot environments</a> where we deal with multiple programming languages.</p>
<p data-selectable-paragraph="">A simple solution would be to <em>archive the Nodejs runtime along with our source code</em> in a zip file. The zip file can then be sent to the VM. This way, the VM can use the local Nodejs runtime present in the archive to run our app.</p>
<p data-selectable-paragraph="">Luckily, there is a tool to make our lives easier.</p>
<h2 data-selectable-paragraph="">In Comes Docker and Containers</h2>
<p data-selectable-paragraph="">If you are new to this, think of <a href="https://www.docker.com/" target="_blank" rel="noopener nofollow">Docker</a> as a way to package your artifact along with all its OS dependencies, including Nodejs, into a container image.</p>
<p data-selectable-paragraph="">Using containers, we can deploy any application on a VM which has Docker installed.</p>
<p data-selectable-paragraph="">With Docker, our flow will look something like this:</p>
<figure>
<div tabindex="0"><img decoding="async" class="fr-fic fr-dib lazyloaded" src="https://dzone.com/storage/temp/14354592-devops-deployment-process-with-docker.png" alt="Docker flow should look something like this." data-image="true" data-new="false" data-sizeformatted="59.4 kB" data-mimetype="image/png" data-creationdate="1611506421880" data-creationdateformatted="01/24/2021 04:40 PM" data-type="temp" data-url="/storage/temp/14354592-devops-deployment-process-with-docker.png" data-modificationdate="null" data-size="59437" data-name="devops-deployment-process-with-docker.png" data-id="14354592" data-src="/storage/temp/14354592-devops-deployment-process-with-docker.png" /></div>
</figure>
<p data-selectable-paragraph="">There is a lot more to containers than just this. However, this was one of the reasons why containers got so popular.</p>
<h3 data-selectable-paragraph="">Docker Vs. Containers</h3>
<p data-selectable-paragraph="">Let me clarify this. Docker and containers are not the same things anymore.</p>
<blockquote>
<p data-selectable-paragraph="">Docker is a set of utility tools to build and ship container images which container runtimes like <a href="https://containerd.io/" target="_blank" rel="noopener nofollow">containerd</a> use to make and run containers.</p>
</blockquote>
<p data-selectable-paragraph="">Many are concerned about the future of Docker, given the <a href="https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/" target="_blank" rel="noopener nofollow">recent events</a> which have taken place.</p>
<p data-selectable-paragraph="">It is important to understand that Docker is not going anywhere anytime soon. It <em>provides the best DX</em> and will <em>continue to play a major role in building and shipping container images</em>.</p>
<h2 data-selectable-paragraph="">Getting Serious With DevOps</h2>
<p data-selectable-paragraph="">We have made some serious progress already. Hopefully, we understand how Docker fits into the DevOps process.</p>
<p data-selectable-paragraph="">It&#8217;s time to take things to the next level.</p>
<h3 data-selectable-paragraph="">Triggering Deployment Based on Events</h3>
<p data-selectable-paragraph="">Our script looks pretty solid, but it&#8217;s still triggered manually.</p>
<figure>
<div tabindex="0"><img decoding="async" class="fr-fic fr-dib lazyloaded" src="https://dzone.com/storage/temp/14354593-devops-trigger-automation-manual.png" alt="Developer &gt; Shell Script &gt; VM" data-image="true" data-new="false" data-sizeformatted="49.5 kB" data-mimetype="image/png" data-creationdate="1611506443799" data-creationdateformatted="01/24/2021 04:40 PM" data-type="temp" data-url="/storage/temp/14354593-devops-trigger-automation-manual.png" data-modificationdate="null" data-size="49537" data-name="devops-trigger-automation-manual.png" data-id="14354593" data-src="/storage/temp/14354593-devops-trigger-automation-manual.png" /></div>
</figure>
<p data-selectable-paragraph="">Wouldn&#8217;t it be great if we could trigger this script automatically whenever someone pushes code on GitHub? In other words, we want to trigger this script on an event.</p>
<p data-selectable-paragraph="">GitHub can <a href="https://docs.github.com/en/developers/webhooks-and-events" target="_blank" rel="noopener nofollow">invoke webhooks</a> on a certain set of events.</p>
<p data-selectable-paragraph="">To achieve this, we need to make a simple HTTP server that executes our shell script whenever its endpoint is hit. We can configure GitHub to hit our endpoint on the <a href="https://docs.github.com/en/developers/webhooks-and-events/github-event-types#pushevent" target="_blank" rel="noopener nofollow">Push Event</a>.</p>
<p data-selectable-paragraph="">Let&#8217;s call this server <em>Colorful Daemons</em> or <em>CD</em>.</p>
<p data-selectable-paragraph="">Our new flow will look something like this:</p>
<figure>
<div tabindex="0"><img decoding="async" class="fr-fic fr-dib lazyloaded" src="https://dzone.com/storage/temp/14354594-devops-trigger-automation-cd.png" alt="Source Code to Server diagram" data-image="true" data-new="false" data-sizeformatted="62.4 kB" data-mimetype="image/png" data-creationdate="1611506463327" data-creationdateformatted="01/24/2021 04:41 PM" data-type="temp" data-url="/storage/temp/14354594-devops-trigger-automation-cd.png" data-modificationdate="null" data-size="62420" data-name="devops-trigger-automation-cd.png" data-id="14354594" data-src="/storage/temp/14354594-devops-trigger-automation-cd.png" /></div>
</figure>
<p data-selectable-paragraph="">Congratulations! You just set up what we call a <em>CD pipeline</em>.</p>
<p data-selectable-paragraph="">And no… I don&#8217;t mean Colorful Daemons. I&#8217;m talking about <a href="https://en.wikipedia.org/wiki/Continuous_deployment" target="_blank" rel="noopener nofollow">Continous Deployments</a>.</p>
<blockquote>
<p data-selectable-paragraph="">Continuous Deployments is a piece of software responsible for taking your app from something like GitHub all the way to your target environment where it finally gets deployed.</p>
</blockquote>
<p data-selectable-paragraph="">This is basically the CI/CD stuff you keep hearing about. When people talk about tools like Jenkins and CircleCI, they are usually referring to CI/CD.</p>
<p data-selectable-paragraph="">What we just made with Colorful Daemons was a continuous deployments pipeline. Don&#8217;t confuse it with continuous integration or delivery. We&#8217;ll get to those some other day.</p>
<h2 data-selectable-paragraph="">The DevOps Pattern</h2>
<p data-selectable-paragraph="">I guess you&#8217;ve already found a pattern here. We start with a process, find a section we aren’t happy with and then introduce some software component to simplify or automate it.</p>
<p data-selectable-paragraph="">That&#8217;s getting the <em>dev in ops</em>. And that&#8217;s all that there is to it.</p>
<p data-selectable-paragraph="">This is the real answer to the question, &#8216;<em>What is DevOps?.&#8217;</em></p>
<h2 data-selectable-paragraph="">Introducing Container Orchestration</h2>
<p data-selectable-paragraph="">Let&#8217;s finish up by making one small improvement.</p>
<p data-selectable-paragraph="">Till now, we have been dealing with deploying our app to a single VM or a single node. What if we wanted to <em>deploy our app to multiple nodes?</em></p>
<p data-selectable-paragraph="">The easiest way to achieve this would be to modify our CD server to ssh into all the VMs and deploy our container to each one of them.</p>
<figure>
<div tabindex="0"><img decoding="async" class="fr-fic fr-dib lazyloaded" src="https://dzone.com/storage/temp/14354596-devops-deploy-to-multiple-vms-cd.png" alt="Deployment from Github" data-image="true" data-new="false" data-sizeformatted="69.4 kB" data-mimetype="image/png" data-creationdate="1611506491877" data-creationdateformatted="01/24/2021 04:41 PM" data-type="temp" data-url="/storage/temp/14354596-devops-deploy-to-multiple-vms-cd.png" data-modificationdate="null" data-size="69429" data-name="devops-deploy-to-multiple-vms-cd.png" data-id="14354596" data-src="/storage/temp/14354596-devops-deploy-to-multiple-vms-cd.png" /></div>
</figure>
<p data-selectable-paragraph="">While this method works, <strong>we&#8217;ll need to change our script every time our infrastructure changes</strong>. In a world where <em>applications are always autoscaling,</em> and <em>VMs are considered disposable</em>, this is unacceptable.</p>
<p data-selectable-paragraph="">A better way would be to make another HTTP server to track infrastructure changes. We can call this server <em>&#8216;</em><em>Pilot.&#8217;</em></p>
<p data-selectable-paragraph="">This server will be responsible for performing health checks on the various VMs in our cluster to maintain a list of active VMs. It could even communicate with the cloud vendor to make things more robust.</p>
<figure>
<div tabindex="0"><img decoding="async" class="fr-fic fr-dib lazyloaded" src="https://dzone.com/storage/temp/14354597-devops-deploy-to-multiple-vms-pilot.png" alt="Pilot diagram" data-image="true" data-new="false" data-sizeformatted="65.2 kB" data-mimetype="image/png" data-creationdate="1611506510994" data-creationdateformatted="01/24/2021 04:41 PM" data-type="temp" data-url="/storage/temp/14354597-devops-deploy-to-multiple-vms-pilot.png" data-modificationdate="null" data-size="65214" data-name="devops-deploy-to-multiple-vms-pilot.png" data-id="14354597" data-src="/storage/temp/14354597-devops-deploy-to-multiple-vms-pilot.png" /></div>
</figure>
<p data-selectable-paragraph="">Pilot will expose an endpoint as well to accept the details of the container to spawn. It can then talk to the various VMs to get the job done.</p>
<p data-selectable-paragraph="">Now, our CD server can simply request Pilot instead of talking to each VM individually.</p>
<p data-selectable-paragraph="">Our new flow will look something like this:</p>
<figure>
<div tabindex="0"><img decoding="async" class="fr-fic fr-dib lazyloaded" src="https://dzone.com/storage/temp/14354598-devops-deploy-to-multiple-vms-process.png" alt="Another new flow, to two servers!" data-image="true" data-new="false" data-sizeformatted="84.0 kB" data-mimetype="image/png" data-creationdate="1611506532707" data-creationdateformatted="01/24/2021 04:42 PM" data-type="temp" data-url="/storage/temp/14354598-devops-deploy-to-multiple-vms-process.png" data-modificationdate="null" data-size="83976" data-name="devops-deploy-to-multiple-vms-process.png" data-id="14354598" data-src="/storage/temp/14354598-devops-deploy-to-multiple-vms-process.png" /></div>
</figure>
<p data-selectable-paragraph="">The second server, Pilot, is called a container orchestrator. That&#8217;s what <a href="https://kubernetes.io/" target="_blank" rel="noopener nofollow">Kubernetes</a> is!</p>
<p data-selectable-paragraph="">You just designed a mini version of Kubernetes!</p>
<p data-selectable-paragraph="">Also, Kubernetes is greek for Pilot. Isn&#8217;t that a pleasant co-incidence?</p>
<h2 data-selectable-paragraph="">Where to Start?</h2>
<p data-selectable-paragraph="">We covered quite a few tools together. This brings me to my last point. Ever wondered why the DevOps space is so fragmented?</p>
<p data-selectable-paragraph="">If you think about it, there are so many tools out there, making it hard to decide: what&#8217;s the right choice or where you should even start?</p>
<figure>
<div tabindex="0"><img decoding="async" class="fr-fic fr-dib lazyloaded" src="https://dzone.com/storage/temp/14354599-devops-landscape.png" alt="Where do you start? Roadmap" data-image="true" data-new="false" data-sizeformatted="97.4 kB" data-mimetype="image/png" data-creationdate="1611506544158" data-creationdateformatted="01/24/2021 04:42 PM" data-type="temp" data-url="/storage/temp/14354599-devops-landscape.png" data-modificationdate="null" data-size="97371" data-name="devops-landscape.png" data-id="14354599" data-src="/storage/temp/14354599-devops-landscape.png" /></div>
</figure>
<p data-selectable-paragraph="">Every organisation has its own way, its own process to do things. And since their paths are different, the tools they need to use are different.</p>
<p data-selectable-paragraph="">Your job is not to find which tool is the best. <em>Your job is to find what process works for you best.</em> Once you have that figured out, the tools are just a google search away.</p>
<p data-selectable-paragraph="">So now you know where to start. It&#8217;s not with the tools out there.</p>
<blockquote>
<p data-selectable-paragraph="">Start by understanding how your company and teams do things.</p>
</blockquote>
<p data-selectable-paragraph="">I&#8217;m literally asking you to open up a Word document and copy-paste the commands you need to run to do stuff.</p>
<h2 data-selectable-paragraph="">Wrapping Up</h2>
<p data-selectable-paragraph="">I hope this post has been helpful in understanding how the DevOps field is arranged and how different tools depend and coexist with each other.</p>
<p data-selectable-paragraph="">I&#8217;d like to add:</p>
<blockquote>
<p data-selectable-paragraph="">Your DevOps process is only as strong as its foundation.</p>
</blockquote>
<p data-selectable-paragraph="">So work on the underlying process. It&#8217;s okay if you need to tweak your current process a bit.</p>
<p data-selectable-paragraph="">An excellent foundation to build upon could be using tools like <a href="https://spaceuptech.com/" target="_blank" rel="noopener nofollow">SpaceCloud</a>. Space Cloud is a Kubernetes-based serverless platform that helps you develop, deploy and secure cloud-native applications.</p>
<p data-selectable-paragraph="">In a nutshell, SpaceCloud gives you <em>an excellent starting point to build your DevOps practices on top of</em>. It makes performing rolling upgrades, canary deployments, and autoscaling your applications easy. You can configure everything using the <code>space-cli</code> or REST APIs.</p>
<p>The post <a href="http://kostacipo.stream/what-is-devops/">What is DevOps?</a> appeared first on <a href="http://kostacipo.stream">Tech Chronicles</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>http://kostacipo.stream/what-is-devops/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Kubernetes 101: Pods, Nodes, Containers, and Clusters</title>
		<link>http://kostacipo.stream/kubernetes-101-pods-nodes-containers-and-clusters/</link>
					<comments>http://kostacipo.stream/kubernetes-101-pods-nodes-containers-and-clusters/#respond</comments>
		
		<dc:creator><![CDATA[Majordomo]]></dc:creator>
		<pubDate>Fri, 21 Feb 2020 13:13:56 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[kubernetes]]></category>
		<guid isPermaLink="false">http://www.kostacipo.stream/?p=1753</guid>

					<description><![CDATA[<p>&#160; Kubernetes is quickly becoming the new standard for deploying and managing software in the cloud. With all the power Kubernetes provides, however, comes a steep learning curve. As a newcomer, trying to parse the&#160;official documentation&#160;can be overwhelming. There are many different pieces that make up the system, and it can be hard to tell [&#8230;]</p>
<p>The post <a href="http://kostacipo.stream/kubernetes-101-pods-nodes-containers-and-clusters/">Kubernetes 101: Pods, Nodes, Containers, and Clusters</a> appeared first on <a href="http://kostacipo.stream">Tech Chronicles</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>&nbsp;</p>
<p id="5e66" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">Kubernetes is quickly becoming the new standard for deploying and managing software in the cloud. With all the power Kubernetes provides, however, comes a steep learning curve. As a newcomer, trying to parse the&nbsp;<a class="cq fw gv gw gx gy" href="https://kubernetes.io/docs/concepts/" target="_blank" rel="noopener nofollow noreferrer">official documentation&nbsp;</a>can be overwhelming. There are many different pieces that make up the system, and it can be hard to tell which ones are relevant for your use case. This blog post will provide a simplified view of Kubernetes, but it will attempt to give a high-level overview of the most important components and how they fit together.</p>
<p id="2c24" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">First, lets look at how hardware is represented</p>
<h1 id="baff" class="gz ha du at as hb dw hc dy hd he hf hg hh hi hj hk">Hardware</h1>
<h2 id="6b4c" class="hl ha du at as hb hm hn ho hp hq hr hs ht hu hv hw">Nodes</h2>
<p id="34b1" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm"><img decoding="async" src="https://miro.medium.com/max/6930/1*uyMd-QxYaOk_APwtuScsOg.png">A&nbsp;<a class="cq fw gv gw gx gy" href="https://kubernetes.io/docs/concepts/architecture/nodes/" target="_blank" rel="noopener nofollow noreferrer">node</a>&nbsp;is the smallest unit of computing hardware in Kubernetes. It is a representation of a single machine in your cluster. In most production systems, a node will likely be either a physical machine in a datacenter, or virtual machine hosted on a cloud provider like&nbsp;<a class="cq fw gv gw gx gy" href="https://cloud.google.com/" target="_blank" rel="noopener nofollow noreferrer">Google Cloud Platform</a>. Don’t let conventions limit you, however; in theory, you can make a node out of&nbsp;almost&nbsp;anything.</p>
<p id="dea0" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">Thinking of a machine as a “node” allows us to insert a layer of abstraction. Now, instead of worrying about the unique characteristics of any individual machine, we can instead simply view each machine as a set of CPU and RAM resources that can be utilized. In this way, any machine can substitute any other machine in a Kubernetes cluster.</p>
<h2 id="8eb5" class="hl ha du at as hb hm hn ho hp hq hr hs ht hu hv hw">The Cluster</h2>
<p id="60fa" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm"><img decoding="async" src="https://miro.medium.com/max/6540/1*KoMzLETQeN-c63x7xzSKPw.png">Although working with individual nodes can be useful, it’s not the Kubernetes way. In general, you should think about the cluster as a whole, instead of worrying about the state of individual nodes.</p>
<p id="82aa" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">In Kubernetes, nodes pool together their resources to form a more powerful machine. When you deploy programs onto the cluster, it intelligently handles distributing work to the individual nodes for you. If any nodes are added or removed, the cluster will shift around work as necessary. It shouldn’t matter to the program, or the programmer, which individual machines are actually running the code.</p>
<p id="ed84" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">If this kind of hivemind-like system reminds you of the&nbsp;<a class="cq fw gv gw gx gy" href="http://memory-alpha.wikia.com/wiki/Borg" target="_blank" rel="noopener nofollow noreferrer">Borg from Star Trek</a>, you’re not alone; “Borg” is the name for the&nbsp;<a class="cq fw gv gw gx gy" href="http://blog.kubernetes.io/2015/04/borg-predecessor-to-kubernetes.html" target="_blank" rel="noopener nofollow noreferrer">internal Google project</a>&nbsp;Kubernetes was based on.</p>
<h2 id="bfa3" class="hl ha du at as hb hm hn ho hp hq hr hs ht hu hv hw">Persistent Volumes</h2>
<p id="3b4c" class="gh gi du at gj b gk iw gm ix go iy gq iz gs ja gu dm">Because programs running on your cluster aren’t guaranteed to run on a specific node, data can’t be saved to any arbitrary place in the file system. If a program tries to save data to a file for later, but is then relocated onto a new node, the file will no longer be where the program expects it to be. For this reason, the traditional local storage associated to each node is treated as a temporary cache to hold programs, but any data saved locally can not be expected to persist.</p>
<p id="6784" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm"><img decoding="async" src="https://miro.medium.com/max/6874/1*kF57zE9a5YCzhILHdmuRvQ.png">To store data permanently, Kubernetes uses&nbsp;<a class="cq fw gv gw gx gy" href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/" target="_blank" rel="noopener nofollow noreferrer">Persistent Volumes</a>. While the CPU and RAM resources of all nodes are effectively pooled and managed by the cluster, persistent file storage is not. Instead, local or cloud drives can be attached to the cluster as a Persistent Volume. This can be thought of as plugging an external hard drive in to the cluster. Persistent Volumes provide a file system that can be mounted to the cluster, without being associated with any particular node.</p>
<h1 id="9af2" class="gz ha du at as hb dw hc dy hd he hf hg hh hi hj hk">Software</h1>
<h2 id="8ca8" class="hl ha du at as hb hm hn ho hp hq hr hs ht hu hv hw">Containers</h2>
<p id="0f71" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm"><img decoding="async" src="https://miro.medium.com/max/10000/1*ILinzzMdnD5oQ6Tu2bfBgQ.png">Programs running on Kubernetes are packaged as&nbsp;<a class="cq fw gv gw gx gy" href="https://www.docker.com/what-container" target="_blank" rel="noopener nofollow noreferrer">Linux containers</a>. Containers are a widely accepted standard, so there are already many&nbsp;<a class="cq fw gv gw gx gy" href="https://hub.docker.com/explore/" target="_blank" rel="noopener nofollow noreferrer">pre-built images</a>&nbsp;that can be deployed on Kubernetes.</p>
<p id="6c91" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">Containerization allows you to create self-contained Linux execution environments. Any program and all its dependencies can be bundled up into a single file and then shared on the internet. Anyone can download the container and deploy it on their infrastructure with very little setup required. Creating a container can be done programmatically, allowing powerful&nbsp;CI and CD&nbsp;pipelines to be formed.</p>
<p id="b764" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">Multiple programs can be added into a single container, but you should limit yourself to one process per container if at all possible. It’s better to have many small containers than one large one. If each container has a tight focus, updates are easier to deploy and issues are easier to diagnose.</p>
<h2 id="2bd6" class="hl ha du at as hb hm hn ho hp hq hr hs ht hu hv hw">Pods</h2>
<p id="efba" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm"><img decoding="async" src="https://miro.medium.com/max/12000/1*8OD0MgDNu3Csq0tGpS8Obg.png">Unlike other systems you may have used in the past, Kubernetes doesn’t run containers directly; instead it wraps one or more containers into a higher-level structure called a&nbsp;<a class="cq fw gv gw gx gy" href="https://kubernetes.io/docs/concepts/workloads/pods/pod/" target="_blank" rel="noopener nofollow noreferrer">pod</a>. Any containers in the same pod will share the same resources and local network. Containers can easily communicate with other containers in the same pod as though they were on the same machine while maintaining a degree of isolation from others.</p>
<p id="64c4" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">Pods are used as the unit of replication in Kubernetes. If your application becomes too popular and a single pod instance can’t carry the load, Kubernetes can be configured to deploy new replicas of your pod to the cluster as necessary. Even when not under heavy load, it is standard to have multiple copies of a pod running at any time in a production system to allow load balancing and failure resistance.</p>
<p id="3fb7" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">Pods can hold multiple containers, but you should limit yourself when possible. Because pods are scaled up and down as a unit, all containers in a pod must scale together, regardless of their individual needs. This leads to wasted resources and an expensive bill. To resolve this, pods should remain as small as possible, typically holding only a main process and its tightly-coupled helper containers (these helper containers are typically referred to as “side-cars”).</p>
<h2 id="f5c6" class="hl ha du at as hb hm hn ho hp hq hr hs ht hu hv hw">Deployments</h2>
<p id="54b8" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm"><img decoding="async" src="https://miro.medium.com/max/8068/1*iTAVk3glVD95hb-X3HiCKg.png">Although pods are the basic unit of computation in Kubernetes, they are not typically directly launched on a cluster. Instead, pods are usually managed by one more layer of abstraction: the&nbsp;<a class="cq fw gv gw gx gy" href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/" target="_blank" rel="noopener nofollow noreferrer">deployment</a>.</p>
<p id="aeef" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">A deployment’s primary purpose is to declare how many replicas of a pod should be running at a time. When a deployment is added to the cluster, it will automatically spin up the requested number of pods, and then monitor them. If a pod dies, the deployment will automatically re-create it.</p>
<p id="2bb1" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">Using a deployment, you don’t have to deal with pods manually. You can just declare the desired state of the system, and it will be managed for you automatically.</p>
<h2 id="4a14" class="hl ha du at as hb hm hn ho hp hq hr hs ht hu hv hw">Ingress</h2>
<p id="85af" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm"><img decoding="async" src="https://miro.medium.com/max/6564/1*tBJ-_g4Mk5OkfzLEHrRsRw.png">Using the concepts described above, you can create a cluster of nodes, and launch deployments of pods onto the cluster. There is one last problem to solve, however: allowing external traffic to your application.</p>
<p id="d52a" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">By default, Kubernetes provides isolation between pods and the outside world. If you want to communicate with a service running in a pod, you have to open up a channel for communication. This is referred to as ingress.</p>
<p id="727e" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">There are multiple ways to add ingress to your cluster. The most common ways are by adding either an&nbsp;<a class="cq fw gv gw gx gy" href="https://kubernetes.io/docs/concepts/services-networking/ingress/" target="_blank" rel="noopener nofollow noreferrer">Ingress</a>&nbsp;controller, or a&nbsp;<a class="cq fw gv gw gx gy" href="https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/" target="_blank" rel="noopener nofollow noreferrer">LoadBalancer</a>. The exact tradeoffs between these two options are out of scope for this post, but you must be aware that ingress is something you need to handle before you can experiment with Kubernetes.</p>
<h2 id="ef85" class="hl ha du at as hb hm hn ho hp hq hr hs ht hu hv hw">What’s Next</h2>
<p id="3a4d" class="gh gi du at gj b gk iw gm ix go iy gq iz gs ja gu dm">What’s described above is an oversimplified version of Kubernetes, but it should give you the basics you need to start experimenting. </p>
<p id="55de" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">To experiment with Kubernetes locally,&nbsp;<a class="cq fw gv gw gx gy" href="https://kubernetes.io/docs/getting-started-guides/minikube/" target="_blank" rel="noopener nofollow noreferrer">Minikube</a> will create a virtual cluster on your personal hardware. If you’re ready to try out a cloud service, <a class="cq fw gv gw gx gy" href="https://cloud.google.com/kubernetes-engine/" target="_blank" rel="noopener nofollow noreferrer">Google Kubernetes Engine</a>&nbsp;has a collection of&nbsp;<a class="cq fw gv gw gx gy" href="https://cloud.google.com/kubernetes-engine/docs/tutorials/" target="_blank" rel="noopener nofollow noreferrer">tutorials</a>&nbsp;to get you started.</p>
<p id="9358" class="gh gi du at gj b gk gl gm gn go gp gq gr gs gt gu dm">If you are new to the world of containers and web infrastructure, I suggest reading up on the<a class="cq fw gv gw gx gy" href="https://12factor.net/" target="_blank" rel="noopener nofollow noreferrer">&nbsp;12 Factor App methodology</a>. This describes some of the best practices to keep in mind when designing software to run in an environment like Kubernetes.</p>
<p>The post <a href="http://kostacipo.stream/kubernetes-101-pods-nodes-containers-and-clusters/">Kubernetes 101: Pods, Nodes, Containers, and Clusters</a> appeared first on <a href="http://kostacipo.stream">Tech Chronicles</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>http://kostacipo.stream/kubernetes-101-pods-nodes-containers-and-clusters/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Infrastructure as Code and Six Key Automation Concepts</title>
		<link>http://kostacipo.stream/infrastructure-as-code-and-six-key-automation-concepts/</link>
					<comments>http://kostacipo.stream/infrastructure-as-code-and-six-key-automation-concepts/#respond</comments>
		
		<dc:creator><![CDATA[Majordomo]]></dc:creator>
		<pubDate>Fri, 10 Jan 2020 12:27:17 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[devops]]></category>
		<guid isPermaLink="false">http://www.kostacipo.stream/?p=1645</guid>

					<description><![CDATA[<p>&#160; As an industry, we aspire to something that delivers on our collective vision of fully-automated, self-governing infrastructure. In the 00s we called it autonomic computing, then software defined data center (SDDC) and, now, IaC. Sadly, IaC is not it—or, more specifically, it’s not it alone. The actual definition of IaC is way smaller than [&#8230;]</p>
<p>The post <a href="http://kostacipo.stream/infrastructure-as-code-and-six-key-automation-concepts/">Infrastructure as Code and Six Key Automation Concepts</a> appeared first on <a href="http://kostacipo.stream">Tech Chronicles</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>&nbsp;</p>
<p>As an industry, we aspire to something that delivers on our collective vision of fully-automated, self-governing infrastructure. In the 00s we called it autonomic computing, then software defined data center (SDDC) and, now, IaC. Sadly, IaC is not it—or, more specifically, it’s not it alone.</p>
<p>The actual definition of IaC is way smaller than what people want it to be.</p>
<p>My take-away from IaC discussions is they are built around six key automation themes. Let’s take a few minutes to examine each of the themes. Then we’ll have a basis for answering “If that’s not IaC, then what is it?”</p>
<h2><b>Programmatic Configuration (IaC)</b></h2>
<p>Programmatic configuration captures the idea that we can define our infrastructure in a machine readable format. For example, you describe a planned cluster build in YAML to execution by a tool such as Terraform that requests the resources from your dynamic infrastructure and, hopefully, tracks that resource descriptors you get back from that request. Basically, it’s just your IaaS input format.</p>
<h2><b>Dynamic Infrastructure (or IaaS)</b></h2>
<p>It describes a system that allows operators to change the resource allocations via an API. It’s a critical part of the discussion because there must be a dependable way to pragmatically change the infrastructure. Some would consider this cloud; however, we should not assumes either managed services or VMs because dynamic infrastructure is much broader.</p>
<h2><b>Desired State (or State Reconciler)</b></h2>
<p>Desired state actively compares dynamic infrastructure to programmatic manifest and makes automatic adjustments to keep them synchronized. Typically, the idea of maintaining state is separated from configuring systems. Kubernetes and other Autoscaling cluster managers perform this role.</p>
<h2><b>Deployment from Source Control (or Immutability)</b></h2>
<p>It implies that all of the configuration components of the system can be stored as versioned artifacts and deployed from source. A Dockerfile that builds a container is a good example of an immutable process and the generated artifact. The repeatability offered by deploying from source control is transformative compared to past generations of operations that relied on either manual or scripted changes to production systems. Immutability is not IaC: We are focused on an generated deployment artifact rather than managing the infrastructure.</p>
<h2><b>Composable (or Modular) Automation</b></h2>
<p>It allows us to manage complex structures through layers of abstraction. In concept, composability is as simple as using platform plugin to script against an API.</p>
<h2><b>CI/CD&nbsp;</b><b>Pipeline</b></h2>
<p>CI/CD pipeline is the glue that pulls all these pieces together to create a resilient environment by continuously adjusting the system components based on the operators’ IaC descriptions. The pipeline relies on having dynamic infrastructure managed to desired state as a target; however, it adds the critical concepts of staged changes&nbsp;over time to the system. Since we need to create systems that can adapt and improve over time, the CI/CD pipeline provides mechanism&nbsp;for controlled implementation change over time.</p>
<p>Together, these six concepts reflect an integrated new way to think about the infrastructure that is beyond IaC. They allow us to maintain and control our systems over time in a way that is resilient and automatic. More importantly, they bring complex infrastructure problems back into terms that people can comprehend by creating more deterministic and predictable chains of change. That allows us to keep our hands out of the infrastructure and our minds focused on what we are delivering: a continuously integrated data center.</p>
<p>Since all six concepts are interdependent, continuously integrated data center (CI DC) completely captures our aspirations for IaC. The term intuitively builds from source code into a fully operational infrastructure. More importantly, CI DC demands that we acknowledge the ongoing maintenance required. For that reason, I’ve embraced CI DC to replace IaC.</p>
<p>The post <a href="http://kostacipo.stream/infrastructure-as-code-and-six-key-automation-concepts/">Infrastructure as Code and Six Key Automation Concepts</a> appeared first on <a href="http://kostacipo.stream">Tech Chronicles</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>http://kostacipo.stream/infrastructure-as-code-and-six-key-automation-concepts/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Gotchas in Writing Dockerfile</title>
		<link>http://kostacipo.stream/gotchas-in-writing-dockerfile/</link>
					<comments>http://kostacipo.stream/gotchas-in-writing-dockerfile/#respond</comments>
		
		<dc:creator><![CDATA[Majordomo]]></dc:creator>
		<pubDate>Tue, 24 Dec 2019 12:08:47 +0000</pubDate>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[General]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[docker]]></category>
		<guid isPermaLink="false">http://www.kostacipo.stream/?p=1519</guid>

					<description><![CDATA[<p>&#160; &#160; Why do we need to use a Dockerfile? Dockerfile is not yet-another shell. Dockerfile has its special mission:&#160;automation of Docker image creation. Once, you write build instructions into Dockerfile, you can build the same image just with&#160;docker build&#160;command. Dockerfile is also useful to tell the knowledge of what a job the container does [&#8230;]</p>
<p>The post <a href="http://kostacipo.stream/gotchas-in-writing-dockerfile/">Gotchas in Writing Dockerfile</a> appeared first on <a href="http://kostacipo.stream">Tech Chronicles</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>&nbsp;</p>
<p>&nbsp;</p>
<h2>Why do we need to use a Dockerfile?</h2>
<p>Dockerfile is not yet-another shell. Dockerfile has its special mission:&nbsp;<strong>automation of Docker image creation.</strong></p>
<p>Once, you write build instructions into Dockerfile, you can build the same image just with&nbsp;<code>docker build</code>&nbsp;command.</p>
<p>Dockerfile is also useful to tell the knowledge of what a job the container does to somebody else. Your teammates can tell what the container is supposed to do just by reading Dockerfile. They don’t need to know login to the container and figure out what the container is doing by using ps command.</p>
<p>For these reasons, you&nbsp;<strong>must</strong>&nbsp;use Dockerfile when you build images. However, writing Dockerfile is sometimes painful. In this post, I will write a few tips and gochas in writing Dockerfile so that you love the tool.</p>
<h2>ADD and understanding context in Dockerfile</h2>
<p><strong><em>ADD</em></strong>&nbsp;is the instruction to add local files to Docker image. The basic usage is very simple. Suppose you want to add a local file called&nbsp;<em>myfile.txt</em>&nbsp;to&nbsp;<em>/myfile.txt</em>&nbsp;of image.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>ls
</span><span class="line">Dockerfile  mydir  myfile.txt
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>Then your Dockerfile looks like this.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line">ADD myfile.txt /
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>Very simple. However, if you want to add&nbsp;<em>/home/vagrant/myfile.txt</em>, you can’t do this.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="c"># Your have this in your Dockerfile</span>
</span><span class="line"><span class="c"># ADD /home/vagrant/myfile.txt /</span>
</span>
<span class="line"><span class="nv">$ </span>docker build -t blog .
</span><span class="line">Uploading context 10240 bytes
</span><span class="line">Step 1 : FROM ubuntu
</span><span class="line"> ---&gt; 8dbd9e392a96
</span><span class="line">Step 2 : ADD /home/vagrant/myfile.txt /
</span><span class="line">Error build: /home/vagrant/myfile.txt: no such file or directory
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>You got&nbsp;<code>no such file or directory</code>&nbsp;error even if you have the file. Why? This is because&nbsp;<em>/home/vagrant/myfile.txt</em>&nbsp;is not added to the&nbsp;<strong>context</strong>&nbsp;of Dockerfile. Context in Dockerfile means files and directories available to the Dockerfile instructions. Only files and directories in the context can be added during build. Files and sub directories under the current directory are added to the context. You can see this when you run build command.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>docker build -t blog .
</span><span class="line">Uploading context 10240 bytes
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>What’s happening here is Docker client makes tarball of entries under the current directory and send it to Docker daemon. The reason why thiis is required is because your Docker daemon may be running on remote machine. That’s why the above command says&nbsp;<em>Uploading</em>.</p>
<p>There is a pitfall, though. Since automatically entries under current directories are added to the context, it tries to upload huge files and take longer time for build even if you don’t add the file.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>ls
</span><span class="line">Dockerfile  very_huge_file
</span>
<span class="line"><span class="nv">$ </span>docker build -t blog .
</span><span class="line">Uploading context xxxxxx bytes
</span><span class="line">..... <span class="c"># Takes very long time</span>
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>So the best practice is only placing files and directories that you need to add to image under current directory.</p>
<h2>Treat your container like a binary with CMD</h2>
<p>By using CMD instruction in Dockerfile, your container acts like a single executable binary. Suppose you have these instructions in your Dockerfile.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="c"># Suppose you have run.sh in the same directory as the Dockerfile</span>
</span><span class="line">ADD run.sh /usr/local/bin/run.sh
</span><span class="line">CMD <span class="o">[</span><span class="s2">"/usr/local/bin/run.sh"</span><span class="o">]</span>
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>When you build a container from this Dockerfile and run with&nbsp;<code>docker run -i run_image</code>, it runs&nbsp;<code>/usr/local/bin/run.sh</code>&nbsp;script and exists.</p>
<p>If you don’t use&nbsp;<code>CMD</code>, you always have to pass the command to the argument:&nbsp;<code>docker run -i run_image /usr/local/bin/run.sh</code>.</p>
<p>This is not just cumbersome, but also considered to be a bad practice from the perspective of operation.</p>
<p>If you have&nbsp;<code>CMD</code>&nbsp;instruction, the purpose of the container becomes explicit: all what the container wants to do is running the command.</p>
<p>But, if you don’t have the instruction, anybody except the person who made the container need to rely on external documentation to know how to run the container properly.</p>
<p>So, in general, you should have&nbsp;<code>CMD</code>&nbsp;instruction in your Dockerfile.</p>
<h2>Difference between CMD and ENTRYPOINT</h2>
<p><code>CMD</code>&nbsp;and&nbsp;<code>ENTRYPOINT</code>&nbsp;are confusing.</p>
<p>Every commands, either passed as an argument or specified from&nbsp;<code>CND</code>&nbsp;instruction are passed as argument of binary specified in&nbsp;<code>ENTRYPOINT</code>.</p>
<p><code>/bin/sh -c</code>&nbsp;is the default entrypoint. So if you specify&nbsp;<code>CMD date</code>&nbsp;without specifying entrypoint, Docker executes it as&nbsp;<code>/bin/sh -c date</code>.</p>
<p>By using entrypoint, you can change the behaviour of your container at run time that makes container operation a bit more flexible.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line">ENTRYPOINT <span class="o">[</span><span class="s2">"/bin/date"</span><span class="o">]</span>
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>With the entrypoint above, the container prints out current date with different format.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>docker run -i clock_container +<span class="s2">"%s"</span>
</span><span class="line">1404214000
</span>
<span class="line"><span class="nv">$ </span>docker run -i clock_container +<span class="s2">"%F"</span>
</span><span class="line">2014-07-01
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<h3>exec format error</h3>
<p>There is one caveat in default entrypoint. For example, you want to execute the following shell script.</p>
<p><strong><em>/usr/local/bin/run.sh</em></strong></p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nb">echo</span> <span class="s2">"hello, world"</span>
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p><strong><em>Dockerfile</em></strong></p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line">ADD run.sh /usr/local/bin/run.sh
</span><span class="line">RUN chmod +x /usr/local/bin/run.sh
</span><span class="line">CMD <span class="o">[</span><span class="s2">"/usr/local/bin/run.sh"</span><span class="o">]</span>
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>When you run the container, your expectation is the container prints out&nbsp;<code>hello, world</code>. However, what you will get is a error message that doesn’t make sense.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>docker run -i hello_world_image
</span><span class="line">2014/07/01 10:53:57 <span class="nb">exec </span>format error
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>You see this message when you didn’t put shebang in your script, and because of that, default entrypoint&nbsp;<code>/bin/sh -c</code>&nbsp;does not know how to run the script.</p>
<p>To fix this, you can either add shebang</p>
<p><strong><em>/usr/local/bin/run.sh</em></strong></p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="c">#!/bin/bash</span>
</span><span class="line"><span class="nb">echo</span> <span class="s2">"hello, world"</span>
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>or you can specify from command line.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>docker run -entrypoint<span class="o">=</span><span class="s2">"/bin/bash"</span> -i hello_world_image
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<h2>Build caching: what invalids cache and not?</h2>
<p>Docker creates a commit for each line of instruction in Dockerfile. As long as you don’t change the instruction, Docker thinks it doesn’t need to change the image, so use cached image which is used by the next instruction as a parent image. This is the reason why&nbsp;<code>docker build</code>&nbsp;takes long time in the first time, but immediately finishes in the second time.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
<span class="line-number">17</span>
<span class="line-number">18</span>
<span class="line-number">19</span>
<span class="line-number">20</span>
<span class="line-number">21</span>
<span class="line-number">22</span>
<span class="line-number">23</span>
<span class="line-number">24</span>
<span class="line-number">25</span>
<span class="line-number">26</span>
<span class="line-number">27</span>
<span class="line-number">28</span>
<span class="line-number">29</span>
<span class="line-number">30</span>
<span class="line-number">31</span>
<span class="line-number">32</span>
<span class="line-number">33</span>
<span class="line-number">34</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span><span class="nb">time </span>docker build -t blog .
</span><span class="line">Uploading context 10.24 kB
</span><span class="line">Step 1 : FROM ubuntu
</span><span class="line"> ---&gt; 8dbd9e392a96
</span><span class="line">Step 2 : RUN apt-get update
</span><span class="line"> ---&gt; Running in 15705b182387
</span><span class="line">Ign http://archive.ubuntu.com precise InRelease
</span><span class="line">Hit http://archive.ubuntu.com precise Release.gpg
</span><span class="line">Hit http://archive.ubuntu.com precise Release
</span><span class="line">Hit http://archive.ubuntu.com precise/main amd64 Packages
</span><span class="line">Get:1 http://archive.ubuntu.com precise/main i386 Packages <span class="o">[</span>1641 kB<span class="o">]</span>
</span><span class="line">Get:2 http://archive.ubuntu.com precise/main TranslationIndex <span class="o">[</span>3706 B<span class="o">]</span>
</span><span class="line">Get:3 http://archive.ubuntu.com precise/main Translation-en <span class="o">[</span>893 kB<span class="o">]</span>
</span><span class="line">Fetched 2537 kB in 7s <span class="o">(</span>351 kB/s<span class="o">)</span>
</span>
<span class="line"> ---&gt; a8e9f7328cc4
</span><span class="line">Successfully built a8e9f7328cc4
</span>
<span class="line">real  0m8.589s
</span><span class="line">user  0m0.008s
</span><span class="line">sys   0m0.012s
</span>
<span class="line"><span class="nv">$ </span><span class="nb">time </span>docker build -t blog .
</span><span class="line">Uploading context 10.24 kB
</span><span class="line">Step 1 : FROM ubuntu
</span><span class="line"> ---&gt; 8dbd9e392a96
</span><span class="line">Step 2 : RUN apt-get update
</span><span class="line"> ---&gt; Using cache
</span><span class="line"> ---&gt; a8e9f7328cc4
</span><span class="line">Successfully built a8e9f7328cc4
</span>
<span class="line">real  0m0.067s
</span><span class="line">user  0m0.012s
</span><span class="line">sys   0m0.000s
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>However, when cache is used and what invalids cache are sometimes not very clear. Here is a few cases that I found worth to note.</p>
<h4>Cache invalidation at one instruction invalids cache of all subsequent instructions</h4>
<p>This is the basic rule of caching. If you cause cache invalidation at one instruction, subsequent instructions doesn’t use cache.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="c"># Before</span>
</span><span class="line">From ubuntu
</span><span class="line">Run apt-get install ruby
</span><span class="line">Run <span class="nb">echo </span><span class="k">done</span>!
</span>
<span class="line"><span class="c"># After</span>
</span><span class="line">From ubuntu
</span><span class="line">Run apt-get update
</span><span class="line">Run apt-get install ruby
</span><span class="line">Run <span class="nb">echo </span><span class="k">done</span>!
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>Since you add&nbsp;<em>Run apt-get update</em>&nbsp;instruction,&nbsp;<strong>all</strong>&nbsp;instructions after that have to be done from the scratch even if they are not changed. This is inevitable because Dockerfile uses the image built by the previous instruction as a parent image to execute next instruction. So, if you insert an instruction that creates a new parent image, all subsequent instructions cannot use cache because now parent image differs.</p>
<h4>Cache is invalid even when adding commands that don’t do anything</h4>
<p><em>This invalidates caching.</em>&nbsp;For example,</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="c"># Before</span>
</span><span class="line">Run apt-get update
</span>
<span class="line"><span class="c"># After</span>
</span><span class="line">Run apt-get update <span class="o">&amp;&amp;</span> <span class="nb">true</span>
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>Even if&nbsp;<code>true</code>&nbsp;command doesn’t change anything of the image, Docker invalids the cache.</p>
<h4>Cache is invalid when you add spaces between command and arguments inside instruction</h4>
<p><em>This invalids cache</em></p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="c"># Before</span>
</span><span class="line">Run apt-get update
</span>
<span class="line"><span class="c"># After</span>
</span><span class="line">Run apt-get               update
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<h4>Cache is used when you add spaces around commands inside instruction</h4>
<p><em>Cache is valid even if you add space around commands</em></p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="c"># Before</span>
</span><span class="line">Run apt-get update
</span>
<span class="line"><span class="c"># After</span>
</span><span class="line">Run                apt-get update
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<h4>Cache is used for non-idempotent instructions</h4>
<p>This is kind of pitfall of build caching. What I mean by non-idempotent instructions is the execution of commands that may return different result each time. For example,&nbsp;<code>apt-get update</code>&nbsp;is not idempotent because the content of updates changes as time goes by.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line">From ubuntu
</span><span class="line">Run apt-get update
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>You made this Dockerfile and create image. 3 months later, Ubuntu made some security updates to their repository, so you rebuild the image by using the same Dockerfile hoping your new image includes the security updates. However, this doesn’t pick up the updates. Since no instructions or files are changed, Docker uses cache and skips doing&nbsp;<code>apt-get update</code>.</p>
<p>If you don’t want to use cache, just pass&nbsp;<code>-no-cache</code>&nbsp;option to build.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>docker build -no-cache .
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<h4>Instructions after ADD never cached (Only versions prior to 0.7.3)</h4>
<p>If you use Docker before v7.3, watch out!</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line">From ubuntu
</span><span class="line">Add myfile /
</span><span class="line">Run apt-get update
</span><span class="line">Run apt-get install openssh-server
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>If you have Dockerfile like this,&nbsp;<strong><em>Run apt-get update</em></strong>&nbsp;and&nbsp;<strong><em>Run apt-get install openssh-server</em></strong>&nbsp;will never be cached.</p>
<p>The behavior is changed from v7.3. It caches even if you have ADD instruction, but invalids cache if file content is changed.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"Jeff Beck"</span> &gt; rock.you
</span>
<span class="line">From ubuntu
</span><span class="line">Add rock.you /
</span><span class="line">Run add rock.you
</span>
<span class="line"><span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"Eric Clapton"</span> &gt; rock.you
</span>
<span class="line">From ubuntu
</span><span class="line">Add rock.you /
</span><span class="line">Run add rock.you
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>Since you change&nbsp;<em>rock.you</em>&nbsp;file, instructions after Add doesn’t use cache.</p>
<h2>Hack to run container in the background</h2>
<p>If you want to simplify the way to run containers, you should run your container on background with&nbsp;<code>docker run -d image your-command</code>. Instead of running with&nbsp;<code>docker run -i -t image your-command</code>, using&nbsp;<code>-d</code>&nbsp;is recommended because you can run your container with just one command and you don’t need to detach terminal of container by hitting&nbsp;<code>Ctrl + P + Q</code>.</p>
<p>However, there is a problem with&nbsp;<code>-d</code>&nbsp;option. Your container immediately stops unless the commands are not running on foreground.</p>
<p>Let me explain this by using case where you want to run apache service on a container. The intuitive way of doing this is</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>docker run -d apache-server apachectl start
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>However, the container stops immediately after it is started. This is because&nbsp;<code>apachectl</code>&nbsp;exits once it detaches apache daemon.</p>
<p>Docker doesn’t like this. Docker requires your command to keep running in the foreground. Otherwise, it thinks that your applications stops and shutdown the container.</p>
<p>You can solve this by directly running apache executable with foreground option.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>docker run -e <span class="nv">APACHE_RUN_USER</span><span class="o">=</span>www-data <span class="se">\</span>
</span><span class="line">                    -e <span class="nv">APACHE_RUN_GROUP</span><span class="o">=</span>www-data <span class="se">\</span>
</span><span class="line">                    -e <span class="nv">APACHE_PID_FILE</span><span class="o">=</span>/var/run/apache2.pid <span class="se">\</span>
</span><span class="line">                    -e <span class="nv">APACHE_RUN_DIR</span><span class="o">=</span>/var/run/apache2 <span class="se">\</span>
</span><span class="line">                    -e <span class="nv">APACHE_LOCK_DIR</span><span class="o">=</span>/var/lock/apache2 <span class="se">\</span>
</span><span class="line">                    -e <span class="nv">APACHE_LOG_DIR</span><span class="o">=</span>/var/log/apache2 <span class="se">\</span>
</span><span class="line">                    -d apache-server /usr/sbin/apache2 -D NO_DETACH -D FOREGROUND
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>Here we are manually doing what&nbsp;<code>apachectl</code>&nbsp;does for us and run apache executable. With this approach, apache keeps running on foreground.</p>
<p>The problem is that some application does not run in the foreground. Also, we need to do extra works such as exporting environment variables by ourselves. How can we make it easier?</p>
<p>In this situation, you can add&nbsp;<code>tail -f /dev/null</code>&nbsp;to your command. By doing this, even if your main command runs in the background, your container doesn’t stop because&nbsp;<code>tail</code>&nbsp;is keep running in the foreground. We can use this technique in the apache case.</p>
<figure class="code"><figcaption></figcaption><div class="highlight">
<table>
<tbody>
<tr>
<td class="gutter">
<pre class="line-numbers"><span class="line-number">1</span>
</pre>
</td>
<td class="code">
<pre><code class="bash"><span class="line"><span class="nv">$ </span>docker run -d apache-server apachectl start <span class="o">&amp;&amp;</span> tail -f /dev/null
</span></code></pre>
</td>
</tr>
</tbody>
</table>
</div>
</figure>
<p>Much better, right? Since&nbsp;<code>tail -f /dev/null</code>&nbsp;doesn’t do any harm, you can use this hack to any applications.</p>
<p>The post <a href="http://kostacipo.stream/gotchas-in-writing-dockerfile/">Gotchas in Writing Dockerfile</a> appeared first on <a href="http://kostacipo.stream">Tech Chronicles</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>http://kostacipo.stream/gotchas-in-writing-dockerfile/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
