<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Aldo Vázquez</title>
    <description>The latest articles on Forem by Aldo Vázquez (@aldorvv__).</description>
    <link>https://forem.com/aldorvv__</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F809711%2Fc67a83cc-f939-4dad-825c-dbd1db4c6393.jpg</url>
      <title>Forem: Aldo Vázquez</title>
      <link>https://forem.com/aldorvv__</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/aldorvv__"/>
    <language>en</language>
    <item>
      <title>Set up a multi-container app with docker compose</title>
      <dc:creator>Aldo Vázquez</dc:creator>
      <pubDate>Sat, 11 Feb 2023 22:10:15 +0000</pubDate>
      <link>https://forem.com/aldorvv__/set-up-a-multi-container-app-with-docker-compose-1n77</link>
      <guid>https://forem.com/aldorvv__/set-up-a-multi-container-app-with-docker-compose-1n77</guid>
      <description>&lt;p&gt;Usually, we use Docker to build a whole application environment in different machines, this can be on an AWS instance, a VM, or even on your localhost.&lt;/p&gt;

&lt;p&gt;Of course, you can write a giant Dockerfile to run all the components of your application, database, backend, web server configuration, even a cache service, this is a solution, with some disadvantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building the image will take so long&lt;/li&gt;
&lt;li&gt;If any component requires an update, the whole image will be rebuilt&lt;/li&gt;
&lt;li&gt;Debugging any component will be a nightmare&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, the other option is to build many Dockerfiles to actually manage multiple containers, this solution of course solves all the issues above, since we can work with each component individually.&lt;/p&gt;

&lt;p&gt;Yet not everything is perfect, now we’ll need to handle many containers manually, run &lt;code&gt;docker run ...&lt;/code&gt; for each component, creating a network to connect all our containers, setup many environment variables, and passing aliases to each container to communicate them is quite another nightmare.&lt;/p&gt;

&lt;p&gt;How do we fix this? Maybe creating an &lt;code&gt;init.sh&lt;/code&gt; file with each &lt;code&gt;docker run ...&lt;/code&gt; instruction is a suitable approach, but there is another solution offered by &lt;code&gt;Docker&lt;/code&gt; to handle this kind of situations, it is called &lt;code&gt;docker compose&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;docker compose&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Compose is a tool for defining and running multi-container applications. It allows us to define all the services in a single file called &lt;code&gt;docker-compose.yml&lt;/code&gt; file and then start, or stop, all of them with a single command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;Most of the newer versions of Docker Desktop already comes with docker compose command, although, you can always check the installation instructions at their official &lt;a href="https://github.com/docker/compose" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Post’s code
&lt;/h3&gt;

&lt;p&gt;For this docker compose tutorial, I will use a simple REST API written in Go with a MySQL database. Since the app’s functionality is not in our scope I won’t explain it, yet if you have any question related to the example app, please don’t hesitate to reach me out by &lt;a href="//mailto:me@aldorvv.dev"&gt;email&lt;/a&gt; or on &lt;a href="https://twitter.com/aldorvv__" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can find all the code used in this post &lt;a href="https://github.com/aldorvv/BlogCode/tree/main/MultiContainerWithDockerCompose" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;docker-compose.yml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://github.com/aldorvv/BlogCode/tree/main/MultiContainerWithDockerCompose" rel="noopener noreferrer"&gt;repository for this post&lt;/a&gt; you will find a file named &lt;code&gt;docker-compose.yml&lt;/code&gt; this file will be the “entry point” for our application, let’s take a look into it.&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./db&lt;/span&gt;
    &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux/x86_64&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;todos-db&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_PASSWORD=root&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_DATABASE=todos&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db:1.0&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;volume&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db-volume&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/data/mysql&lt;/span&gt;

  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./app&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;todos-api&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_URI=root:root@tcp(db:3306)/todos?parseTime=true&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api:1.0&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:8080"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;docker-compose.yml&lt;/code&gt; schema requires the &lt;code&gt;docker compose schema version&lt;/code&gt; at the top of the file, the latest version is &lt;code&gt;3.8&lt;/code&gt; but any &lt;code&gt;3&lt;/code&gt; version will work for this project in particular, if you are eager to learn more about the changes between versions, the change-log is published &lt;a href="https://docs.docker.com/compose/compose-file/compose-versioning/#version-3" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Immediately, we need to define our services, each of them require a name, for example, my database service is named &lt;code&gt;db&lt;/code&gt; this names also &lt;strong&gt;works as aliases within the network&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s take a closer look at db service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./db&lt;/span&gt;
  &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux/x86_64&lt;/span&gt;
  &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;todos-db&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_ROOT_PASSWORD=root&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;MYSQL_DATABASE=todos&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db:1.0&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;volume&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db-volume&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/data/mysql&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, now field by field:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;build&lt;/code&gt; specifies which Dockerfile to use to create this service, in the project’s repository, we have a &lt;code&gt;db&lt;/code&gt; folder, and within in we have a &lt;code&gt;Dockerfile&lt;/code&gt; with some customizations for our database&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;platform&lt;/code&gt; is not actually necessary, but a good tip in case you are working with an Apple Silicon Processor.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;container_name&lt;/code&gt; as you might have guessed, specifies the name of the container created, is equivalent to &lt;code&gt;docker run -n&lt;/code&gt; flag&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;environment&lt;/code&gt; allows us to specify environment variables used by the container, is equivalent to &lt;code&gt;docker run -e&lt;/code&gt; flag&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;image&lt;/code&gt; is for specifying the tag for this image. Is equivalent to &lt;code&gt;docker build -t&lt;/code&gt; flag.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;volumes&lt;/code&gt; is a list of the &lt;code&gt;docker volumes&lt;/code&gt; being used by this container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pretty straightforward, right? There are many more fields that can be specified, &lt;code&gt;docker compose&lt;/code&gt; allows customizing each aspect of the containers, this configuration is a pretty simple, but solid, one.&lt;/p&gt;

&lt;p&gt;But we have another service, app is for defining my REST API application container, let’s take a look at it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./app&lt;/span&gt;
  &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;todos-api&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_URI=root:root@tcp(db:3306)/todos?parseTime=true&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api:1.0&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;80:8080"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And again, let’s explore each field:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;depends_on&lt;/code&gt; is a quite useful field, it allows us to make our container wait until the service specified here is up, since we depend on a database to store To-dos, it is important to wait until the &lt;code&gt;db&lt;/code&gt; service is up.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;build&lt;/code&gt; again, receives the context to build a new image, within the app folder we have a Dockerfile to build the API&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;container_name&lt;/code&gt; as you might have guessed, again, is for naming our container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;environment&lt;/code&gt; allows us to specify environment variables used in our containers, this time we only use one variable, but as you can see, the URI points to &lt;code&gt;db:3306&lt;/code&gt; remember when I said &lt;em&gt;this names also works as aliases within the network&lt;/em&gt;? &lt;code&gt;docker compose&lt;/code&gt; creates a network with each service connected to it; therefore, we can point each service using its alias.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;image&lt;/code&gt; again is for tagging our images&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ports&lt;/code&gt; is a list of the ports exposed by this container and the mapping to the host’s actual ports, is equivalent to &lt;code&gt;docker run -p&lt;/code&gt; flag&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;docker compose up&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Ok, but now what? As I said at the beginning of this post, &lt;code&gt;docker compose&lt;/code&gt; allows us to run all our containers with a single command, this command will build all the images (if they don't exist) for our application and also run all the required containers.&lt;/p&gt;

&lt;p&gt;But that’s not it, as I mentioned, it also creates a new &lt;code&gt;docker network&lt;/code&gt; and attach all the containers to it, same thing for all the volumes specified in the volumes field at &lt;code&gt;docker-compose.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s then run it using the &lt;code&gt;-d&lt;/code&gt; flag to run our containers in the background.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If the images don’t exist, all the building log will be displayed; otherwise, the output should be something similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[+] Running 2/0
 ⠿ Container todos-db   Running                                                                                                                   0.0s
 ⠿ Container todos-api  Running   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the &lt;code&gt;*.container_name&lt;/code&gt; field is already reflected here.&lt;/p&gt;

&lt;p&gt;Then if you list your images you should see something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED             SIZE
api          1.0       b5c4cf711443   49 minutes ago      18.1MB
db           1.0       53c16ac987e5   About an hour ago   517MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, the field &lt;code&gt;*.image&lt;/code&gt; was used to name our images and add the version of the image to it.&lt;/p&gt;

&lt;p&gt;But let’s see our containers! The output will be something similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                  NAMES
806258079a07   api:1.0   "./server"               4 minutes ago   Up 4 minutes   0.0.0.0:80-&amp;gt;8080/tcp   todos-api
d01357663cea   db:1.0    "docker-entrypoint.s…"   4 minutes ago   Up 4 minutes   3306/tcp, 33060/tcp    todos-db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where you can see the port mapping that we used in the app.ports field the name in &lt;code&gt;*.container_name&lt;/code&gt; fields and even the &lt;code&gt;*.image&lt;/code&gt; name.&lt;/p&gt;

&lt;p&gt;Finally, let’s take a look at our Docker networks!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker network ls
NETWORK ID     NAME                    DRIVER    SCOPE
f8d14f7ddf0b   dockercompose_default   bridge    local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a new network, being used for this specific project, and we didn’t need to do nothing! It was automatically created and all the containers are already attached to it.&lt;/p&gt;

&lt;p&gt;By the way, you can test the API with these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a new TODO&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://127.0.0.1/todos &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt;  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"description": "Write a comment!"}'&lt;/span&gt;

&lt;span class="c"&gt;# Get TODOs&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"http://127.0.0.1/todos"&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"http://127.0.0.1/todos/1"&lt;/span&gt;

&lt;span class="c"&gt;# Mark as complete&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"http://127.0.0.1/todos/1?completed=1"&lt;/span&gt;

&lt;span class="c"&gt;# Delete a TODO&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; DELETE &lt;span class="s2"&gt;"http://127.0.0.1/todos/1"&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;docker compose&lt;/code&gt; is a great tool, even it is not the best option to be used in production environments, is really useful to replicate different environments in different hosts, quick, easy to read and easy to share. I encourage you to build your next project using a &lt;code&gt;docker-compose.yml&lt;/code&gt; file!&lt;/p&gt;

&lt;h4&gt;
  
  
  Note from the OP
&lt;/h4&gt;

&lt;p&gt;Hi! Long time no see, I recently moved so I was very &lt;strong&gt;very&lt;/strong&gt; busy, but I’m back with many ideas and drafts for new posts!&lt;/p&gt;

&lt;p&gt;Thank you so much for all your support and reading my content, I’ll really appreciate any feedback or suggestion!&lt;/p&gt;

</description>
      <category>writing</category>
    </item>
    <item>
      <title>Lighter Docker images</title>
      <dc:creator>Aldo Vázquez</dc:creator>
      <pubDate>Fri, 27 Jan 2023 04:26:52 +0000</pubDate>
      <link>https://forem.com/aldorvv__/lighter-docker-images-17k1</link>
      <guid>https://forem.com/aldorvv__/lighter-docker-images-17k1</guid>
      <description>&lt;p&gt;Sometimes, using a custom Dockerfile results in massive images, and, yes, they are a bunch of layers that will reproduce an isolated environment, their huge size makes sense.&lt;/p&gt;

&lt;p&gt;Today, I am going to show you a way to save many space with a simple and cool Docker trick.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dockerfiles
&lt;/h2&gt;

&lt;p&gt;When it comes to create a custom image, you will need to write a Dockerfile, as you might know, each sentence in your file represents a new layer, let’s see a quick example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:latest # Pulls latest ubuntu image&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="c"&gt;# Updates apt repositories&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;figlet &lt;span class="c"&gt;# Installs figlet&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["figlet", "-c", "Hello from dockerized ubuntu!"]&lt;/span&gt;
 &lt;span class="c"&gt;# Runs!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you try to build and run the previous Dockerfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; hello &lt;span class="nb"&gt;.&lt;/span&gt;
docker run hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will be something like&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frkcadxnhe9in6njuxw9d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frkcadxnhe9in6njuxw9d.png" alt="Docker run hello output" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, the output is pretty, but let’s see how much space does my &lt;code&gt;hello&lt;/code&gt; image use.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The output is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        latest    3d0b79dcdd1f   10 minutes ago   109MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;109MB&lt;/code&gt; to print a hello world in my console using Ubuntu, this is actually huge, now, think about how much space a real-life application Docker image will use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single-stage Dockerfile
&lt;/h2&gt;

&lt;p&gt;For this example, we will use a super simple Go REST API, the code is already done, and you can find it &lt;a href="https://github.com/aldorvv/BlogCode/tree/main/MultiStageDockerfiles" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Since the scope of this blog post is how to reduce the size of your Docker images, I will not explain the Go code, but I promise to create a post about that in the future.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://github.com/aldorvv/BlogCode/tree/main/MultiStageDockerfiles" rel="noopener noreferrer"&gt;repository&lt;/a&gt; for this post you will find two Dockerfiles, let’s take a look at &lt;code&gt;Dockerfile&lt;/code&gt; first&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; golang:latest&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /server&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download
&lt;span class="k"&gt;RUN &lt;/span&gt;go build &lt;span class="nt"&gt;-o&lt;/span&gt; server .

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./server"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The image built from this Dockerfile will&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull the latest &lt;code&gt;golang&lt;/code&gt; Docker image&lt;/li&gt;
&lt;li&gt;Create a directory named &lt;code&gt;/server&lt;/code&gt; and change the context to it&lt;/li&gt;
&lt;li&gt;Copy all the files in the current directory into our container&lt;/li&gt;
&lt;li&gt;Execute &lt;code&gt;go mod download&lt;/code&gt; to install all the project’s dependency&lt;/li&gt;
&lt;li&gt;Execute &lt;code&gt;go build -o server .&lt;/code&gt; To build the app and rename the bin to &lt;code&gt;server&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Finally, execute the &lt;code&gt;server&lt;/code&gt; binary every time we run a container from this image&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Quite straightforward, right? Let’s build the image and take a look at the size, for this you can run the following commands.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The output should be something similar to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        latest    3d0b79dcdd1f   12 minutes ago   109MB
GoServer     latest    34e3ad5acd86   21 minutes ago   1.02GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;1.02GB&lt;/code&gt; is being used to isolate my server environment, it can be shocking, but let’s take a look at what it contains.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All my project dependencies&lt;/li&gt;
&lt;li&gt;All Go dependencies&lt;/li&gt;
&lt;li&gt;A lightweight Linux distro&lt;/li&gt;
&lt;li&gt;The code of my app&lt;/li&gt;
&lt;li&gt;The final binary of my app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, it actually stores many things that we only use once, for example, since I already compiled my project, maybe storing all the dependencies is actually unnecessary, just as all the code of my app, and all the Go dependencies.&lt;/p&gt;

&lt;p&gt;Then, we can get rid of some items of the above list and keep it like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;del&gt;All my project dependencies&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;All Go dependencies&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;A lightweight Linux distro&lt;/li&gt;
&lt;li&gt;&lt;del&gt;The code of my app&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;The final binary of my app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But, how do we achieve this? &lt;/p&gt;

&lt;p&gt;For this, Docker actually has a solution, a different strategy of building Dockerfiles, splitting them in &lt;code&gt;STAGES&lt;/code&gt; to reduce the quantity of layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-stage Dockerfiles
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://github.com/aldorvv/BlogCode/tree/main/MultiStageDockerfiles" rel="noopener noreferrer"&gt;project’s repository&lt;/a&gt; you have another Dockerfile, called &lt;code&gt;Dockerfile.multi&lt;/code&gt; the extension &lt;code&gt;.multi&lt;/code&gt; is not actually necessary, I just used it to differentiate it from the previous Dockerfile, let’s take a look at it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;golang:latest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /build&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;go mod download

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nv"&gt;GOOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;linux go build &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-installsuffix&lt;/span&gt; cgo &lt;span class="nt"&gt;-o&lt;/span&gt; server .

&lt;span class="c"&gt;# Run stage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:latest&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /server&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /build/server .&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["./server"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might look really complex, since it uses &lt;code&gt;FROM&lt;/code&gt; twice, but it is actually smaller and easier to read (with practice).&lt;/p&gt;

&lt;p&gt;What will this Dockerfile do when we build the image?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull latest &lt;code&gt;golang&lt;/code&gt; Docker image and give it the &lt;code&gt;builder&lt;/code&gt; alias&lt;/li&gt;
&lt;li&gt;Create a directory named &lt;code&gt;/build&lt;/code&gt; and change the context to it&lt;/li&gt;
&lt;li&gt;Copy all the files in the current directory into our container&lt;/li&gt;
&lt;li&gt;Execute &lt;code&gt;go mod download&lt;/code&gt; to install all the project’s dependency&lt;/li&gt;
&lt;li&gt;Execute &lt;code&gt;go build -o server .&lt;/code&gt; To build the app and rename the bin to server, with some &lt;code&gt;GO FLAGS&lt;/code&gt; enabled to make our binary executable in different environments.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok, we will take a break here, at this point in the Dockerfile&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We copied the code&lt;/li&gt;
&lt;li&gt;Installed the project dependencies&lt;/li&gt;
&lt;li&gt;Compiled our project
Many of the files that we want to get rid of were used in this &lt;strong&gt;First stage&lt;/strong&gt;, named &lt;strong&gt;builder&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, ready? Let's see the &lt;strong&gt;Second stage.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull latest &lt;code&gt;alpine&lt;/code&gt; Docker image, a lightweight linux distro&lt;/li&gt;
&lt;li&gt;Create a directory named &lt;code&gt;/server&lt;/code&gt; and change the context to it&lt;/li&gt;
&lt;li&gt;Copy the final binary &lt;code&gt;server&lt;/code&gt; extracted from the previous &lt;code&gt;builder&lt;/code&gt; stage, we do this using the &lt;code&gt;from=&lt;/code&gt; key argument&lt;/li&gt;
&lt;li&gt;Finally, execute the &lt;code&gt;server&lt;/code&gt; binary&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was a longer road, but it worths it, let’s see the space used by our images using the same two commands as before, with a couple of modifications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; GoLight &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.multi &lt;span class="nb"&gt;.&lt;/span&gt;
docker image &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, the output is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        latest    3d0b79dcdd1f   12 minutes ago   109MB
GoServer     latest    34e3ad5acd86   21 minutes ago   1.02GB
GoLight      latest    309467e3d24e   6 minutes ago    17.5MB

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

&lt;/div&gt;



&lt;p&gt;It worked! Now our server image uses only &lt;code&gt;17.5MB&lt;/code&gt; even less than the &lt;code&gt;hello&lt;/code&gt; image, but why?&lt;/p&gt;

&lt;p&gt;Multistage Dockerfiles allows us to extract files and data from specific points on the creation of our images.&lt;/p&gt;

&lt;p&gt;Therefore, we can get rid of many unnecessary things and save a lot of disk space without sacrificing functionality.&lt;/p&gt;

&lt;p&gt;Don’t believe me? Let’s take a brief look at the execution of a container using each image.&lt;/p&gt;

&lt;p&gt;We will use the heavier image first&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 GoServer:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This container then, creates a web server and starts to listen at &lt;code&gt;:8080&lt;/code&gt;, currently our API only has one endpoint, so let’s perform a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;http://127.0.0.1:8080/songs&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET http://127.0.0.1:8080/songs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output should be the next one&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alabaster"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Foals"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"album"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Total Life Forever"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bravery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Human Tetris"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"album"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"River Pt. 1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Lately"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Metronomy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"album"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Metronomy Forever"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Paranoid Android"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"artist"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Radiohead"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"album"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OK Computer"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok! The heavy container is working properly and using &lt;code&gt;1.02GB&lt;/code&gt; of my disk, now let’s try the same using the lighter image (Remember to stop the current container)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 GoLight:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will create a container but using the lighter image this time, the behavior is the same; therefore, we can perform the same &lt;code&gt;GET&lt;/code&gt; request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET http://127.0.0.1:8080/songs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output is the same as before, give it a try!&lt;/p&gt;

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

&lt;p&gt;Docker is a great tool and widely used in the market these days, although it has some disadvantages.&lt;/p&gt;

&lt;p&gt;Of course, massive images are still lighter than virtual machines, but we can use Docker in a better way to exploit its full potential.&lt;/p&gt;

&lt;p&gt;Multistage Dockerfiles work better with compiled languages, since you can use a stage for build and compile and another to actually execute the binary, yet this is not a limitation.&lt;/p&gt;

&lt;p&gt;Please stay tuned for more Docker posts!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>Writing a sequence class in Python</title>
      <dc:creator>Aldo Vázquez</dc:creator>
      <pubDate>Sat, 21 Jan 2023 23:45:16 +0000</pubDate>
      <link>https://forem.com/aldorvv__/writing-a-sequence-class-in-python-l74</link>
      <guid>https://forem.com/aldorvv__/writing-a-sequence-class-in-python-l74</guid>
      <description>&lt;h3&gt;
  
  
  Python data model
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Why &lt;code&gt;len&lt;/code&gt; and not &lt;code&gt;list.length&lt;/code&gt;?
&lt;/h4&gt;

&lt;p&gt;This is a recurrent question when it comes to writing Python code, and if you are familiar with OOP, you probably asked the same question before.&lt;/p&gt;

&lt;p&gt;To talk about this, we should take a look into &lt;em&gt;special methods&lt;/em&gt; some other languages, like Ruby, call them &lt;em&gt;magic methods&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;These methods are not intended to be invoked directly by the user; instead, the interpreter invokes them.&lt;/p&gt;

&lt;p&gt;As a result, we can deduce that the truth of the matter is &lt;code&gt;len(sequence)&lt;/code&gt; built-in function calls &lt;code&gt;sequence.__len__()&lt;/code&gt; behind the scenes.&lt;/p&gt;

&lt;p&gt;That being said, our question remains unanswered, but actually, Python has a pretty good explanation for this behavior.&lt;/p&gt;

&lt;p&gt;A user defined &lt;code&gt;__len__&lt;/code&gt; method to calculate the length of a sequence might be very custom, and vary from user to user.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;__len__&lt;/code&gt; method of Python built-in types, like &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;tuple&lt;/code&gt;, &lt;code&gt;str&lt;/code&gt; or any other sequence is more complicated, and faster, than that.&lt;/p&gt;

&lt;p&gt;Since Python is written in C, invoking &lt;code&gt;len&lt;/code&gt; function with a built-in sequence type, Python, rather than iterating over the sequence and count how many items are inside it, looks for a C data structure field called &lt;code&gt;ob_size&lt;/code&gt;, which is way faster.&lt;/p&gt;

&lt;p&gt;This is because, &lt;code&gt;__len__&lt;/code&gt; method of the built-in sequences is written to do so.&lt;/p&gt;

&lt;p&gt;We can conclude, Python is quite flexible; therefore, we are free to create any custom type, customizing each special method of our classes as much as we need.&lt;/p&gt;

&lt;p&gt;In this article, I will show you how defining a couple of special methods, you will be able to create classes which behaves as any built-in Python sequence.&lt;/p&gt;

&lt;h4&gt;
  
  
  Writing a Playlist class
&lt;/h4&gt;

&lt;p&gt;To show you, how to create a &lt;code&gt;Playlist&lt;/code&gt; class which will behave as a Python builtin &lt;code&gt;sequence&lt;/code&gt; we will create a new &lt;code&gt;Song&lt;/code&gt; &lt;a href="https://docs.python.org/3/library/collections.html#collections.namedtuple" rel="noopener noreferrer"&gt;namedtuple&lt;/a&gt; , after all, what is a &lt;code&gt;Playlist&lt;/code&gt; without songs?&lt;/p&gt;

&lt;h4&gt;
  
  
  Why a &lt;code&gt;namedtuple&lt;/code&gt; and not a &lt;code&gt;class&lt;/code&gt;?
&lt;/h4&gt;

&lt;p&gt;Since my &lt;code&gt;Song&lt;/code&gt; is only a “vessel” that don’t require any special logic and its data will remain the same during all the program execution, a &lt;code&gt;namedtuple&lt;/code&gt; is a better option to use, they also have a nice human-readable string representation, which is easier to read when we use &lt;code&gt;print&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;namedtuple&lt;/span&gt;

&lt;span class="n"&gt;song_attrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;album&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Song&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;namedtuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Song&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;song_attrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, now our &lt;code&gt;Song&lt;/code&gt; type is done, then, let’s start with the &lt;code&gt;Playlist&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Playlist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__songs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, we created a &lt;code&gt;Playlist&lt;/code&gt; class which has a &lt;code&gt;songs&lt;/code&gt; private attribute, this attribute is specifically a &lt;code&gt;list&lt;/code&gt; of &lt;code&gt;Song&lt;/code&gt; instances.&lt;/p&gt;

&lt;p&gt;The next step is to implement a &lt;code&gt;__len__&lt;/code&gt; and a &lt;code&gt;__getitem__&lt;/code&gt; methods as well, Python does not use &lt;code&gt;interfaces&lt;/code&gt;, but it actually requires implementing at least those two methods in order to considere your class as a &lt;code&gt;sequence&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Playlist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__songs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__songs&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_song&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;songs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above’s example, we defined a &lt;code&gt;property&lt;/code&gt; which is actually optional, this is an equivalent of a &lt;code&gt;getter&lt;/code&gt;, the advantage of using properties is, since we didn’t define a &lt;code&gt;setter&lt;/code&gt; trying to set the &lt;code&gt;songs&lt;/code&gt; property will raise an exception rather than allowing the user to override the full attribute, making it “read-only”.&lt;/p&gt;

&lt;p&gt;On the other hand, we defined a &lt;code&gt;__len__&lt;/code&gt; method, which actually returns the length of the &lt;code&gt;list&lt;/code&gt; of songs, then we can now use the next snippet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;my_playlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Playlist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;album&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Soft Sounds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;artist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Delta Sleep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Strongthany,Dotwork,Camp adventure,Sans Soleil&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;my_playlist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_song&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Song&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;album&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_playlist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Out: 4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we defined a &lt;code&gt;__getitem__&lt;/code&gt; as well, this method, “unlocks” the &lt;code&gt;sequence[index]&lt;/code&gt; syntax; therefore, we can access any of our songs by index.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;my_playlist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="c1"&gt;# Out: Strongthany
&lt;/span&gt;
&lt;span class="n"&gt;my_playlist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Out: Song(name='Sans Soleil', album='Soft Sounds', artist='Delta Sleep')
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But that’s not all! It also “unlocks” &lt;code&gt;slicing&lt;/code&gt; making us able to use the following syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;my_playlist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Out: [Song(name='Dotwork', album='Soft Sounds', artist='Delta Sleep'), Song(name='Camp adventure', album='Soft Sounds', artist='Delta Sleep')]
&lt;/span&gt;
&lt;span class="n"&gt;my_playlist&lt;/span&gt;&lt;span class="p"&gt;[::&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Out: [Song(name='Sans Soleil', album='Soft Sounds', artist='Delta Sleep'), Song(name='Camp adventure', album='Soft Sounds', artist='Delta Sleep'), Song(name='Dotwork', album='Soft Sounds', artist='Delta Sleep'), Song(name='Strongthany', album='Soft Sounds', artist='Delta Sleep')]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ability, allows us to iterate over our &lt;code&gt;sequence&lt;/code&gt; using a &lt;code&gt;for&lt;/code&gt; sentence, as if the &lt;code&gt;Playlist&lt;/code&gt; instances were built-in &lt;code&gt;list&lt;/code&gt; instances, this is because, we actually &lt;strong&gt;delegated&lt;/strong&gt; all the &lt;code&gt;sequence&lt;/code&gt; implementation to our &lt;code&gt;__songs&lt;/code&gt; attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;song&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;my_playlist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;by &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Out: 
# Strongthany by  Delta Sleep
# Dotwork by  Delta Sleep
# Camp adventure by  Delta Sleep
# Sans Soleil by  Delta Sleep
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although, no, it is not all yet! Many of the Python built-in modules, require a &lt;code&gt;sequence&lt;/code&gt; to work, and, since our &lt;code&gt;Playlist&lt;/code&gt; class is technically a &lt;code&gt;sequence&lt;/code&gt;, we can use them with our instances.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;song&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_playlist&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;by &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;song&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Out: 
# Sans Soleil by  Delta Sleep
# Camp adventure by  Delta Sleep
# Dotwork by  Delta Sleep
# Strongthany by  Delta Sleep
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;choice&lt;/span&gt;

&lt;span class="nf"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_playlist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Out: Song(name='Dotwork', album='Soft Sounds', artist='Delta Sleep')
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;Python data model provides much flexibility, I haven’t worked (yet) with other language that actually allows you to emulate its built-in types, Go &lt;code&gt;slices&lt;/code&gt; are useful, yes, but you cannot create a custom structure that fulfills your program necessities and behave as a built-in &lt;code&gt;slice&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As you might have guessed, yes, you can emulate any other built-in type in Python.&lt;/p&gt;

&lt;p&gt;For this article, I based myself on &lt;a href="https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/" rel="noopener noreferrer"&gt;“Fluent Python, 2nd Edition”&lt;/a&gt; a great book written by &lt;a href="https://github.com/ramalho" rel="noopener noreferrer"&gt;Luciano Ramalho&lt;/a&gt; one of my biggest influences to become a &lt;em&gt;Pythonista&lt;/em&gt;, please take a look into the book! You’ll learn many new things, just as I did.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>Monitoring Linux instances with Prometheus and Grafana</title>
      <dc:creator>Aldo Vázquez</dc:creator>
      <pubDate>Fri, 20 Jan 2023 23:52:29 +0000</pubDate>
      <link>https://forem.com/aldorvv__/monitoring-linux-instances-with-prometheus-and-grafana-441i</link>
      <guid>https://forem.com/aldorvv__/monitoring-linux-instances-with-prometheus-and-grafana-441i</guid>
      <description>&lt;p&gt;If you want to know how is everything going in your servers, you must monitor them, yeah, sometimes read htop or similar commands output can be hard to understand, there are a lot of alternatives, and today I am going to show you how to set up a monitoring suite to always know what is going on your servers, linux instances, and more.&lt;/p&gt;

&lt;p&gt;In this tutorial, I will show you how to mount a complete, flexible, and scalable monitoring system, using two great (and free) tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are we going to use?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Prometheus&lt;/strong&gt; is a powerful, open-source and easy-to-use monitoring solution, it is going to help us to collect and fetch a lot of information from your Linux instances.&lt;br&gt;
&lt;strong&gt;Grafana&lt;/strong&gt; on the other hand, is the most popular technology to create monitoring dashboards in a fast and easy way, also it has a lot of plugins and integrations, therefore, if it exists, you can create a Grafana panel for it.&lt;/p&gt;

&lt;p&gt;Let's start!&lt;/p&gt;
&lt;h3&gt;
  
  
  Installing node_exporter
&lt;/h3&gt;

&lt;p&gt;First of all, we will need to install a Prometheus light-weight daemon in all your servers that you want to monitor, this daemon is node_exporter.&lt;br&gt;
Let's install it.&lt;br&gt;
Connect to your server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh user@target-instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Download the node_exporter files&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wget https://github.com/prometheus/node_exporter/releases/download/v1.0.1/node_exporter-1.0.1.linux-amd64.tar.gz
Uncompress and rename the folder
$ tar xvf node_exporter-1.0.1.linux-amd64.tar.gz
$ mv node_exporter-1.0.1.linux-amd64 ne_files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create a new user&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo useradd --no-create-home -s /bin/false node_exporter
Change the node_exporter binary ownership to your new user
$ sudo chown node_exporter:node_exporter ./ne_files/node_exporter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add node_exporter to your local binaries&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo mv ./ne_files/node_exporter /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are adding node_exporter as a new service&lt;br&gt;
Create a service file for &lt;code&gt;node_exporter&lt;/code&gt; with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo vim /etc/systemd/system/node_exporter.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, add this to the new file, do not forget to save your changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=node_exporter
After=network-online.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter
[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload the services daemon to see your new service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl start node_exporter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, but how do I know it is working?&lt;br&gt;
You can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl status node_exporter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or go to your favorite browser and navigate to&lt;br&gt;
&lt;code&gt;http://&amp;lt;target-instance&amp;gt;:9100/metrics&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If everything is working, you should see something similar to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0.000108492
go_gc_duration_seconds{quantile="0.25"} 0.000108492
go_gc_duration_seconds{quantile="0.5"} 0.000108492
go_gc_duration_seconds{quantile="0.75"} 0.000108492
go_gc_duration_seconds{quantile="1"} 0.000108492
go_gc_duration_seconds_sum 0.000108492
go_gc_duration_seconds_count 1
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 8
# HELP go_info Information about the Go environment.
# TYPE go_info gauge
go_info{version="go1.14.4"} 1
# HELP go_memstats_alloc_bytes Number of bytes allocated and still in use.
# TYPE go_memstats_alloc_bytes gauge
go_memstats_alloc_bytes 1.846384e+06
# HELP go_memstats_alloc_bytes_total Total number of bytes allocated, even if freed.
# TYPE go_memstats_alloc_bytes_total counter
go_memstats_alloc_bytes_total 3.689864e+06
# HELP go_memstats_buck_hash_sys_bytes Number of bytes used by the profiling bucket hash table.
# TYPE go_memstats_buck_hash_sys_bytes gauge
go_memstats_buck_hash_sys_bytes 1.445623e+06
# HELP go_memstats_frees_total Total number of frees.
# TYPE go_memstats_frees_total counter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember, you need to repeat this process within every Linux instance that you want to monitor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Prometheus
&lt;/h3&gt;

&lt;p&gt;Once all your target instances are running node_exporter, we will install Prometheus to scrape and understand your server's metrics. &lt;br&gt;
You can install it either in your &lt;code&gt;localhost&lt;/code&gt; or on another server. In this tutorial, we are installing Prometheus in a new server that will monitor everything. Don't worry if you want to install the monitoring suite in your &lt;code&gt;localhost&lt;/code&gt;; both processes are very similar.&lt;/p&gt;

&lt;p&gt;Connect to your monitoring server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh user@&amp;lt;monitoring-server&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download the Prometheus files from here&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wget https://github.com/prometheus/prometheus/releases/download/v2.23.0/prometheus-2.23.0.linux-amd64.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's &lt;code&gt;untar&lt;/code&gt; and rename the Prometheus files&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tar xvf prometheus-2.23.0.linux-amd64.tar.gz
$ mv prometheus-2.23.0.linux-amd64 p_files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new user to use Prometheus&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo useradd --no-create-home -s /bin/false prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the Prometheus binaries ownership&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo chown prometheus:prometheus p_files/prometheus
$ sudo chown prometheus:prometheus p_files/promtool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move Prometheus binaries to your local binaries&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo mv p_files/prometheus /usr/local/bin
$ sudo mv p_files/promtool /usr/local/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create folders to store Prometheus configuration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo mkdir /etc/prometheus
$ sudo mkdir /var/lib/prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not forget to change the directories ownership&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo chown prometheus:prometheus /etc/prometheus
$ sudo chown prometheus:prometheus /var/lib/prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy configurations to your new folders&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo cp -r p_files/consoles /etc/prometheus
$ sudo cp -r p_files/console_libraries /etc/prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you might guess, yes, you have to change the directories ownership&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo chown -R prometheus:prometheus /etc/prometheus/consoles
$ sudo chown -R prometheus:prometheus /etc/prometheus/console_libraries
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a prometheus.yml file within &lt;code&gt;/etc/prometheus&lt;/code&gt; folder&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo vim /etc/prometheus/prometheus.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to &lt;code&gt;prometheus.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;global:
 scrape_interval: 10s
scrape_configs:
 - job_name: 'prometheus'
 scrape_interval: 5s
 static_configs:
 - targets: ['&amp;lt;target-instance-1&amp;gt;:9090', '&amp;lt;target-instance-2:9100&amp;gt;']
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a basic Prometheus configuration file. You must add every instance with the node_exporter daemon running in the "targets" array.&lt;br&gt;
Once the &lt;code&gt;prometheus.yml&lt;/code&gt; file is ready, change its ownership&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo chown prometheus:prometheus /etc/prometheus/prometheus.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's create a Prometheus service file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo vim /etc/systemd/system/prometheus.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following configuration to &lt;code&gt;prometheus.service&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=Prometheus
Wants=network-online.target
After=network-online.target
[Service]
User=prometheus
Group=prometheus
Type=simple
ExecStart=/usr/local/bin/prometheus \
 --config.file /etc/prometheus/prometheus.yml \
 --storage.tsdb.path /var/lib/prometheus/ \
 --web.console.templates=/etc/prometheus/consoles \
 --web.console.libraries=/etc/prometheus/console_libraries
[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload the service daemon&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl daemon-reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's time to run your server!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl start prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your service status&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl status prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, &lt;strong&gt;let's check the Prometheus Web UI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open your favorite browser and navigate to&lt;br&gt;
&lt;code&gt;http://&amp;lt;monitoring-server-ip&amp;gt;:9090/&lt;/code&gt;&lt;br&gt;
Now, you should see something like this,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn2irp523toohdlwwu99.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbn2irp523toohdlwwu99.png" alt="Prometheus web ui" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's run a simple query to see if Prometheus is retrieving your instances metrics correctly.&lt;br&gt;
Copy the following query into Prometheus Expression input, then click &lt;code&gt;Execute&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sum by (mode, instance) (irate(node_cpu_seconds_total{job="Prometheus",mode="user"}[30s]))*100/4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you should see a similar output&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ztp7zm0exdkuw7htx19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ztp7zm0exdkuw7htx19.png" alt="Query output" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, Prometheus also lists all the instances you added in the targets array before.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Grafana
&lt;/h3&gt;

&lt;p&gt;We have installed node_exporter in your instance to export its metrics.&lt;br&gt;
Then we installed Prometheus on a different server to interpret the instance's metrics. Now, we will install Grafana to create dashboards and charts to make the information easier to read.&lt;br&gt;
The Grafana installation is pretty short. If you want (or need) to install it using the standalone binaries, following the previous steps should give you an idea.&lt;br&gt;
Grafana has a "ready to download and install" deb package; using the following snippet should install Grafana as a new service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt-get install -y adduser libfontconfig1
$ wget https://dl.grafana.com/oss/release/grafana_7.3.4_amd64.deb
$ sudo dpkg -i grafana_7.3.4_amd64.deb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once Grafana installation is done, run the service with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl start grafana-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open a new tab on your browser and navigate to&lt;br&gt;
&lt;code&gt;http://&amp;lt;monitoring-server&amp;gt;:3000&lt;/code&gt;&lt;br&gt;
You should see a login screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffx36ps9ugvg8assh2ych.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffx36ps9ugvg8assh2ych.png" alt="Grafana login screen" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The default username and password are always "admin" after you log in, Grafana will ask you to change your admin password. Remember to use a strong password, please.&lt;/p&gt;

&lt;p&gt;Now, let's go to setup a new Data Source to fetch information from our Prometheus service.&lt;/p&gt;

&lt;p&gt;Click the gear icon on the left bar, then click the Data Sources section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmchveyqli6vr22ayp1kw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmchveyqli6vr22ayp1kw.png" alt="Data sources config" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click the Add data source button&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpa1h2m3w6d8j5hy5s8j4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpa1h2m3w6d8j5hy5s8j4.png" alt="Data Sources page" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you might guess, yes, we will add a new Prometheus data source&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybplw4qcblrglrkccvfa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybplw4qcblrglrkccvfa.png" alt="Add Prometheus" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see a similar screen&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwstvih3msltz3ywe6io.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwstvih3msltz3ywe6io.png" alt="Data Source configuration" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the HTTP URL to use your Prometheus service, by default it is set to localhost, which is true in this case, because Grafana and Prometheus are running in the same host.&lt;/p&gt;

&lt;p&gt;Then scroll down and click on "Save and test"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6hfy20g710bznaxo0bnk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6hfy20g710bznaxo0bnk.png" alt="Save the data source" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, navigate to Manage dashboard sections, click the dashboard button on the left bar, then click on "Manage" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7eyo1sy65f0fv7qb6vto.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7eyo1sy65f0fv7qb6vto.png" alt="Prometheus Data Source is already working" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this screen, click on Import&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ippktbm69c2eg1tq4uw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ippktbm69c2eg1tq4uw.png" alt="Dashboards Page" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should get this screen, as you can see, there are some ways to import dashboards, click on Upload JSON file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6cbw7nt2h46g6o4wx5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6cbw7nt2h46g6o4wx5x.png" alt="Import a dashboard JSON file" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find a lot of examples on internet or, if you prefer, you can create your own custom dashboard. For this tutorial we are using &lt;a href="https://grafana.com/api/dashboards/159/revisions/5/download" rel="noopener noreferrer"&gt;this&lt;/a&gt; dashboard, developed by &lt;strong&gt;Grafana&lt;/strong&gt; Labs.&lt;br&gt;
Download the &lt;a href="https://grafana.com/api/dashboards/159/revisions/5/download" rel="noopener noreferrer"&gt;JSON&lt;/a&gt; file and select it at the file explorer, then this window should be displayed.&lt;br&gt;
Note: You can find a lot of dashboards &lt;a href="https://grafana.com/grafana/dashboards/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43f0tjykf0rroptfaz2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43f0tjykf0rroptfaz2m.png" alt="Dashboard configuration" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just keep going with the dashboard settings, remember to choose the right Prometheus dashboard at the "Prometheus" dropdown, then, click on import.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro4ld6rpno28f4vesy5l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fro4ld6rpno28f4vesy5l.png" alt="Dashboard" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now, your dashboard is ready!&lt;br&gt;
At the upper left corner, is a "Host" dropdown, there you can select any of your instances added to the Prometheus configuration file.&lt;br&gt;
Yes, those are a lot of charts, Grafana allows you to remove or add as many as you want, so, get fun creating your own monitoring dashboard!&lt;br&gt;
Thanks for reading, this is my first tutorial/blog ever! I will appreciate any feedback or comment.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>deployment</category>
      <category>showdev</category>
    </item>
    <item>
      <title>First steps with Ansible.</title>
      <dc:creator>Aldo Vázquez</dc:creator>
      <pubDate>Fri, 20 Jan 2023 23:26:17 +0000</pubDate>
      <link>https://forem.com/aldorvv__/first-steps-with-ansible-5dje</link>
      <guid>https://forem.com/aldorvv__/first-steps-with-ansible-5dje</guid>
      <description>&lt;p&gt;&lt;a href="https://www.ansible.com/" rel="noopener noreferrer"&gt;Ansible&lt;/a&gt; is one of the most recent tools released by Red Hat and one of the best choices for Developers and DevOps engineers to automate almost everything inside a new server. It is free and open-source, and trust me, configuring remote servers has never been so easy.&lt;/p&gt;

&lt;p&gt;In this tutorial, I intend to show you how to install and start to use this powerful tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing Ansible
&lt;/h2&gt;

&lt;p&gt;First, you will need an Ansible controller host to manage all your nodes. This host could be your localhost, don't worry, Ansible will work no matter which operating system you use. I am using Ubuntu, but the installation steps for any other os are almost the same.&lt;/p&gt;

&lt;p&gt;Let's install Ansible using apt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install ansible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the installation finished, run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ansible --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the package installation was successful, you should see something like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqopp9qnzix9huh1ji44v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqopp9qnzix9huh1ji44v.png" alt="Image description" width="800" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ansible nodes
&lt;/h2&gt;

&lt;p&gt;Now your ansible controller is working, pretty easy right?&lt;br&gt;
Let's set up your nodes, open the &lt;code&gt;/etc/ansible/hosts&lt;/code&gt; file with your preferred editor, you should see this template&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This is the default ansible 'hosts' file.
#
# It should live in /etc/ansible/hosts
#
#   - Comments begin with the '#' character
#   - Blank lines are ignored
#   - Groups of hosts are delimited by [header] elements
#   - You can enter hostnames or ip addresses
#   - A hostname/ip can be a member of multiple groups
# Ex 1: Ungrouped hosts, specify before any group headers.
#green.example.com
#blue.example.com
#192.168.100.1
#192.168.100.10
# Ex 2: A collection of hosts belonging to the 'webservers' group#[webservers]
#alpha.example.org
#beta.example.org
#192.168.1.100
#192.168.1.110
# If you have multiple hosts following a pattern you can specify
# them like this:
#www[001:006].example.com# Ex 3: A collection of database servers in the 'dbservers' group
#[dbservers]
#
#db01.intranet.mydomain.net
#db02.intranet.mydomain.net
#10.25.1.56
#10.25.1.57# Here's another example of host ranges, this time there are no
# leading 0s:#db-[99:101]-node.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can add your hosts at the bottom of the file or delete all the content and replace it with your hosts. The only rule is to follow the syntax below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Linux host
# host_alias ansible_host=&amp;lt;host_ip_or_url&amp;gt; ansible_connection=ssh ansible_user=&amp;lt;host_user&amp;gt; ansible_ssh_pass=&amp;lt;ssh_password_for_user&amp;gt;
# Windows host
# host_alias ansible_host=&amp;lt;host_ip_or_url&amp;gt; ansible_connection=winrm ansible_user=&amp;lt;host_user&amp;gt; ansible_password=&amp;lt;password_for_user&amp;gt;
# I am adding two servers, both of them are Linux servers
web1 ansible_host=my.webserver.com ansible_connection=ssh ansible_user=root ansible_ssh_pass=Passw0rd
db1 ansible_host=my.databaseserver.com ansible_connection=ssh ansible_user=root ansible_ssh_server=Passw0rd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, add some groups for your servers. Groups let you run commands and scripts on multiple hosts simultaneously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[web_nodes]
web1 ansible_host=my.webserver.com ansible_connection=ssh ansible_user=root ansible_ssh_pass=Passw0rd
[db_nodes]
db1 ansible_host=my.databaseserver.com ansible_connection=ssh ansible_user=root ansible_ssh_server=Passw0rd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save your changes and close the file.&lt;/p&gt;

&lt;p&gt;Ansible depends on sshpass package, install it running the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install sshpass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only remaining step is to connect to your hosts through ssh; this is for adding your host's fingerprints to the &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh root@my.webserver.com
$ ssh root@my.databaseserver.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, try your hosts by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ # Ansible has an 'all' group which contains all your defined hosts
$ ansible all -m ping
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is going well, you will see a similar output&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iipj9f15le9mr6d83ah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iipj9f15le9mr6d83ah.png" alt="Image description" width="796" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running playbooks
&lt;/h2&gt;

&lt;p&gt;Your hosts are ready to be used through Ansible. Please create a new folder in your home directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mdkir ~/ansible-tutorial
$ cd ~/ansible-tutorial
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ansible's scripts are just YAML files. If you have worked with YAML before, this step will be straightforward.&lt;br&gt;
Open a new playbook.yml file and add the following lines&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: My playbook
  hosts: web1
  tasks:
  - name: Runnig date
    command: date
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; is an alias for the playbook&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hosts&lt;/code&gt; defines the hosts to run the playbook, can be a group&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tasks&lt;/code&gt; is an array that holds the actions or modules to use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save your changes and close the file, now run it with&lt;br&gt;
&lt;code&gt;$ ansible-playbook playbook.yml&lt;/code&gt;&lt;br&gt;
You should see a similar output as below&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhwumywrcbzbhicrl4ko.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhwumywrcbzbhicrl4ko.png" alt="Image description" width="800" height="223"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A simple, real-life example
&lt;/h2&gt;

&lt;p&gt;You learned some new things so far&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install and configure an Ansible controller&lt;/li&gt;
&lt;li&gt;Add a new playbook&lt;/li&gt;
&lt;li&gt;Run it on your hosts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now it is time to learn how we can use Ansible in a real-life scenario.&lt;br&gt;
For this example, you are going to install and run an Nginx webserver.&lt;br&gt;
Let's create a new playbook&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vim nginx-playbook.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start to write this playbook, add a name and the host where the Nginx should be running (remember the identation)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: 'Install and deploy nginx'
  hosts: web1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, add the tasks, in this case you will need to define two actions.&lt;br&gt;
Install the latest Nginx version&lt;br&gt;
Start Nginx server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tasks:
  - name: 'Install Nginx latest version'
    apt:
      name: nginx
      state: latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This task looks for Nginx on the target host. If Ansible finds Nginx's latest version, the installation won't happen.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: 'Run Nginx'
    service:
      name: nginx
      state: started
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this task checks if the Nginx server is already running. If it is not, then the step is skipped.&lt;br&gt;
Your complete nginx-playbook.yml should look like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- name: 'Install and deploy nginx'
  hosts: web1
  tasks:
    - name: 'Install Nginx latest version'
      apt:
        name: nginx
        state: latest
    - name: 'Run Nginx'
      service:
        name: nginx
        state: started
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save your changes, and now, try it!&lt;br&gt;
Since we are installing a package in your host, you will need to add the '-b' flag to the playbook command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ansible-playbook nginx-playbook.yml -b 
$ # -b or --become grants root privileges to ansible user.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n2qgxp3dtwjo578bq93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n2qgxp3dtwjo578bq93.png" alt="Image description" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally let's check the browser!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8ry7njzete8r5z7jvdu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8ry7njzete8r5z7jvdu.png" alt="Image description" width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Ansible is a great ally; it makes it easy to install and deploy things in all your servers. Sure, this is just an introductory tutorial, but this tool has one module for everything you need to automate. You can find all always available &lt;a href="https://docs.ansible.com/ansible/2.8/modules/list_of_all_modules.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Every feedback, comment, or sharing is highly appreciated!&lt;/p&gt;

</description>
      <category>career</category>
      <category>education</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
