<?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"><channel><title><![CDATA[Blog | Alve Reduan]]></title><description><![CDATA[Blog | Alve Reduan]]></description><link>https://blog.alvereduan.com</link><generator>RSS for Node</generator><lastBuildDate>Sun, 03 May 2026 08:21:08 GMT</lastBuildDate><atom:link href="https://blog.alvereduan.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Dockerfile Walkthrough]]></title><description><![CDATA[In this article, I'll be documenting what each line of a Dockerfile means & why it is there in the first place.
We'll dockerize a FastAPI app. This is the file structure for our app.

And we have a main.py file in the code file. Our Dokcerfile looks ...]]></description><link>https://blog.alvereduan.com/dockerfile-walkthrough</link><guid isPermaLink="true">https://blog.alvereduan.com/dockerfile-walkthrough</guid><category><![CDATA[Docker]]></category><category><![CDATA[Dockerfile]]></category><dc:creator><![CDATA[Alve Reduan]]></dc:creator><pubDate>Tue, 18 Apr 2023 20:17:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1681849033882/3201f721-f687-492e-ab22-e159edf851d0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, I'll be documenting what each line of a Dockerfile means &amp; why it is there in the first place.</p>
<p>We'll dockerize a FastAPI app. This is the file structure for our app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1681813622344/3cb3ed55-dec6-4cd5-b427-9fab85c9c377.png" alt class="image--center mx-auto" /></p>
<p>And we have a main.py file in the code file. Our Dokcerfile looks like this.</p>
<pre><code class="lang-apache"><span class="hljs-attribute">FROM</span> python:<span class="hljs-number">3</span>.<span class="hljs-number">9</span>
<span class="hljs-attribute">COPY</span> ./requirements.txt /code/requirements.txt
<span class="hljs-attribute">RUN</span> pip install --no-cache-dir --upgrade -r /code/requirements.txt
<span class="hljs-attribute">COPY</span> ./code /code
<span class="hljs-attribute">WORKDIR</span> /code
<span class="hljs-attribute">CMD</span><span class="hljs-meta"> ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]</span>
</code></pre>
<p>Let's explain each line of code. :D</p>
<p><strong>FROM python:3.9</strong><br />In this line, we're pulling an image from the docker hub. As we're building a FastAPI app, we need Python. If it were a react or vue app, we might've used node:something. To find out more about available docker images, <a target="_blank" href="https://hub.docker.com/search?q=">Docker Hub</a> is the place to search.</p>
<p><strong>COPY ./requirements.txt</strong><br /><strong>RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt</strong></p>
<p>It may seem a bit odd that, we're first copying only the requirements file and then we're copying the code after installing the requirements. This is to utilize docker's caching mechanism. By default, docker will cache each layer of an image. So, by installing the dependencies first, docker can use the cache for the next time build if requirements haven't changed. We can see the layered history by using the docker history &lt;image_name&gt; command.</p>
<p><strong>COPY ./code /code</strong><br /><strong>WORKDIR /code</strong></p>
<p>After installing the required dependency, now we're copying the code and setting the working directory to /code. So that, when we try to execute any command it'll execute from /code directory.</p>
<p><strong>CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]</strong></p>
<p>Finally, we execute the app by spinning up a uvicorn wsgi server pointed to our app inside the docker.</p>
<p><strong>RUN vs CMD</strong></p>
<p>It might be confusing that we used RUN and CMD. The difference is that RUN will execute the command when building the image but CMD will execute the commands when we run the container. If multiple CMD is written on the Dockerfile, only the last one will take effect.</p>
<p>Also, one thing to mention here is that there are two forms of writing CMD. Shell form [<em>CMD uvicorn main:app</em>] or Exec form [<em>CMD ["uvicorn", "main:app"]</em>]. The difference is that commands written in shell form will be executed through the shell. But exec form commands do not need to spin up another shell process to run as it will run directly. So, it's faster to use the exec form.</p>
<p>We also have a command <strong>ENTRYPOINT</strong>. It does the same thing as CMD but it's a little bit harder to overwrite. For example, when we start the container in interactive mode (mentioned below), we can easily overwrite the CMD. But to overwrite ENTRYPOINT command, we have to use the --entrypoint option.</p>
<h2 id="heading-building-the-image">Building The Image</h2>
<p>We can now build the image by running the command:</p>
<pre><code class="lang-bash">docker build -t &lt;image_name&gt;:tag
</code></pre>
<p>After the building is complete, we can view it by running the command:</p>
<pre><code class="lang-bash">docker images
</code></pre>
<p>We can interact with the container with the following command:</p>
<pre><code class="lang-bash">docker run -it &lt;image_name&gt; sh
</code></pre>
<p>We're saying docker to start the image in interactive mode and run the "shell" program.</p>
<h2 id="heading-running-the-container">Running The Container</h2>
<p>For development, we'll be using the following command to run the container:</p>
<pre><code class="lang-bash">docker run -d -p 8000:80 -v $(<span class="hljs-built_in">pwd</span>)/code:/code &lt;image_name&gt;
</code></pre>
<p>Here,<br />-d is to run the container in detached mode.<br />-p is to map ports from outside to inside.<br />-v is to map our current directory to the container's directory so that when we make any changes to our code, it reflects on the directory inside the container. (-v is to map volume in order to persist data, more on that in another journal).</p>
<p>For production, we'll be using the following command to run the container:</p>
<pre><code class="lang-bash">docker run -d -p 8000:80 &lt;image_name&gt;
</code></pre>
<p>Notice that, we're not using a volume here to track. That's because we want a clean production build.</p>
<h2 id="heading-clean-up">Clean Up</h2>
<p>We can see the list of all the images with this command</p>
<pre><code class="lang-bash">docker images
</code></pre>
<p>And, we might see some images with &lt;none&gt; tag and repository. That means, the images are dangling. They lost their connection with any containers. Those images can be removed with the following command</p>
<pre><code class="lang-bash">docker image prune
</code></pre>
<p>We can also see running containers with the following command:</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p>But it only show all the running containers. To view all the containers, that are also exited, we can run the following command:</p>
<pre><code class="lang-bash">docker ps -a
</code></pre>
<p>We call remove all the stopped containers with the following command:</p>
<pre><code class="lang-bash">docker container prune
</code></pre>
<h2 id="heading-good-to-know">Good To Know</h2>
<h3 id="heading-image">Image</h3>
<p>We can have all the image operations by running the following command:</p>
<pre><code class="lang-bash">docker image
</code></pre>
<p>We can tag any image using the docker tag command. For example:</p>
<pre><code class="lang-bash">docker tag &lt;image_id&gt; &lt;username&gt;/&lt;image_name&gt;:&lt;tag&gt;
</code></pre>
<p>We can push this image to docker hub using the following command.</p>
<pre><code class="lang-bash">docker push &lt;username&gt;/&lt;image_name&gt;:&lt;tag&gt;
</code></pre>
<h3 id="heading-container">Container</h3>
<p>We can run any command in a running container using the following exec command. For example:</p>
<pre><code class="lang-bash">docker <span class="hljs-built_in">exec</span> &lt;container_name&gt; ls
</code></pre>
<p>We can use start/stop/rm to start/stop/remove a container.</p>
<pre><code class="lang-bash">docker start/stop/rm &lt;container_name&gt;
</code></pre>
<h3 id="heading-logs">Logs</h3>
<p>We can see all logs option using the following command:</p>
<pre><code class="lang-bash">docker logs
</code></pre>
]]></content:encoded></item></channel></rss>