<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gabriel Lima</title>
    <description>The latest articles on DEV Community by Gabriel Lima (@limagbz).</description>
    <link>https://dev.to/limagbz</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%2F1169687%2Fa3cc5a62-c316-4682-9a94-7b2fbfe06830.jpeg</url>
      <title>DEV Community: Gabriel Lima</title>
      <link>https://dev.to/limagbz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/limagbz"/>
    <language>en</language>
    <item>
      <title>You get what you Measure: Understanding your applications health with Grafana, Loki and Prometheus</title>
      <dc:creator>Gabriel Lima</dc:creator>
      <pubDate>Wed, 24 Apr 2024 14:55:00 +0000</pubDate>
      <link>https://dev.to/limagbz/you-get-what-you-measure-understanding-your-applications-health-with-grafana-loki-and-prometheus-1ldi</link>
      <guid>https://dev.to/limagbz/you-get-what-you-measure-understanding-your-applications-health-with-grafana-loki-and-prometheus-1ldi</guid>
      <description>&lt;p&gt;Applications will fail, this is a reality from the universe. But these fails are not always catastrophic, sometimes they are just silent: a system that starts to answer incorrectly but just for some users, maybe responses start to get slow for an specific region.&lt;/p&gt;

&lt;p&gt;Everyone that worked deploying systems enough have a story about how things "suddenly" went wrong on an Friday night after 10pm (a nightmare that haunts every DevOps dreams).&lt;/p&gt;

&lt;p&gt;One way to avoid these kinds of problems (and others too) is to establish a good monitoring environment to track your application's health and availability. The objective of this tutorial is to help you create and understand this environment with some of the most famous tools for the task: Grafana, Loki and Prometheus.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fezz3c1iko7xbl57gelrz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fezz3c1iko7xbl57gelrz.png" alt="Monitoring Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;This tutorial uses Kubernetes to deploy the applications, however most of the content is focusing on the tools and related concepts rather than on deployment. So this should be good for everyone.&lt;/p&gt;

&lt;p&gt;If you want hands-on practice you should have a running Kubernetes cluster (I used &lt;a href="https://microk8s.io/" rel="noopener noreferrer"&gt;MicroK8s&lt;/a&gt; for this tutorial) and Helm (see how to install on &lt;a href="https://helm.sh/docs/intro/install/" rel="noopener noreferrer"&gt;Installing Helm&lt;/a&gt; tutorial). It is important that you understand the basics of these tools to fully understand.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that this is not an exhaustive list of features from the tools. See the sources section for a deeper understanding.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Capturing your metrics with Prometheus
&lt;/h2&gt;

&lt;p&gt;One way to understand the health of our applications is to understand the &lt;a href="https://prometheus.io/docs/introduction/overview/#what-are-metrics" rel="noopener noreferrer"&gt;metrics&lt;/a&gt; they produce. For example, if your database is slow you may want to know if it is not reaching the boundaries of your CPU/RAM by monitoring your CPU/RAM usage metric. If your web server is slow you may want to know if you are not receiving more requests that your system can handle. When you have one or two applications this can be easily done by getting these metrics manually for each tool. However when you have dozens or even hundreds of applications things start to get very difficult to manage.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works
&lt;/h3&gt;

&lt;p&gt;Prometheus solves this problem by providing a centralized way to store and get these metrics from the application. For it to work each application should expose a port (by default 9090) that will contain its generated metrics in a specific human-readable format following the &lt;a href="https://prometheus.io/docs/concepts/data_model/#data-model" rel="noopener noreferrer"&gt;Prometheus Data Model&lt;/a&gt;. What Prometheus do is to use &lt;strong&gt;crawlers&lt;/strong&gt; that will regularly get this information from the applications and send to the Prometheus instance. This is call a pull architecture.&lt;/p&gt;

&lt;p&gt;
    &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprometheus.io%2Fassets%2Farchitecture.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprometheus.io%2Fassets%2Farchitecture.png" alt="Prometheus Architecture"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Push Gateway
&lt;/h4&gt;

&lt;p&gt;You can also work with a push architecture in Prometheus by using the Push Gateway. As the name says it works by the your application pushing its metrics to Prometheus (you can read more about how it works on &lt;a href="https://prometheus.io/docs/instrumenting/pushing/" rel="noopener noreferrer"&gt;"Pushing metrics"&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;This is not a thing to use always, in fact, the official documentation recommends to use that only in limited cases that you can understand more reading &lt;a href="https://prometheus.io/docs/practices/pushing/" rel="noopener noreferrer"&gt;"When to use the Pushgateway"&lt;/a&gt;. &lt;/p&gt;

&lt;h4&gt;
  
  
  Exporters
&lt;/h4&gt;

&lt;p&gt;As I said before, Prometheus requires that the data is available for the crawlers in an specific format. But as you may be expecting not all tools follow theses rules. To solve this problem Prometheus have &lt;a href="https://prometheus.io/docs/instrumenting/exporters/" rel="noopener noreferrer"&gt;&lt;strong&gt;exporters&lt;/strong&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Basically What an exporter do is implement a way to convert metrics from third-party systems to the Prometheus data format. One famous example is the JMX exporter can convert JVM-based application metrics. Another one is the Kubernetes Node Exporter that exports information about your Linux Kubernetes Nodes to the tool.&lt;/p&gt;

&lt;h4&gt;
  
  
  Querying the metrics
&lt;/h4&gt;

&lt;p&gt;But with the metrics being on Prometheus, how can we actually see and use them? The answer is on &lt;a href="https://prometheus.io/docs/prometheus/latest/querying/basics/" rel="noopener noreferrer"&gt;PromQL&lt;/a&gt;, a query language that enables us to select and aggregate these metrics data. &lt;/p&gt;

&lt;p&gt;In fact (and a little bit of a spoiler), this is what Grafana uses to query metrics from Prometheus and create the dashboards.&lt;/p&gt;

&lt;h4&gt;
  
  
  Alerting
&lt;/h4&gt;

&lt;p&gt;Now, we just need to look for every log on the system and see if there is a problem, right? No, there is no need for that since Prometheus has also a built-in alert manager that will alert everything you want based on some metrics thresholds. For example, you can set an alert that triggers every time the response time is greater than 200ms.&lt;/p&gt;

&lt;p&gt;Note that Grafana has a similar feature, so it is a good practice to just use one of these tools for alerts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Prometheus
&lt;/h3&gt;

&lt;p&gt;Prometheus can be deployed using the &lt;a href="https://github.com/prometheus-community/helm-charts" rel="noopener noreferrer"&gt;Prometheus Helm Chart&lt;/a&gt;. This helm chart contains a lot of features such as the already mentioned Push Gateway, Alert Manager and so on. For simplicity reasons of this tutorial I will not show all the Helm chart configuration but you can see a real example used by me &lt;a href="https://github.com/limagbz/data-mesh-yelp/blob/main/infra/monitoring/prometheus/values.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Crawlers and Scraping
&lt;/h4&gt;

&lt;p&gt;As briefly explained before, Prometheus use crawlers to get information from the applications that generate these metrics. On Kubernetes these is done by specifying jobs on the &lt;code&gt;scrape_configs&lt;/code&gt; section. You can see an example below:&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;serverFiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;prometheus.yml&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;rule_files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/config/recording_rules.yml&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/config/alerting_rules.yml&lt;/span&gt;
    &lt;span class="na"&gt;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prometheus&lt;/span&gt;
            &lt;span class="s"&gt;static_configs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;localhost:9090&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kube-state-metrics&lt;/span&gt;
          &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
          &lt;span class="na"&gt;honor_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prometheus-kube-state-metrics.monitoring.svc.cluster.local:8080&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes-apiservers&lt;/span&gt;
            &lt;span class="s"&gt;kubernetes_sd_configs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;endpoints&lt;/span&gt;
            &lt;span class="na"&gt;scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https&lt;/span&gt;
            &lt;span class="na"&gt;tls_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;ca_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/run/secrets/kubernetes.io/serviceaccount/ca.crt&lt;/span&gt;
                &lt;span class="na"&gt;insecure_skip_verify&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
            &lt;span class="na"&gt;bearer_token_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/run/secrets/kubernetes.io/serviceaccount/token&lt;/span&gt;
            &lt;span class="na"&gt;relabel_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;source_labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;
                        &lt;span class="nv"&gt;__meta_kubernetes_namespace&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
                        &lt;span class="nv"&gt;__meta_kubernetes_service_name&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
                        &lt;span class="nv"&gt;__meta_kubernetes_endpoint_port_name&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
                    &lt;span class="pi"&gt;]&lt;/span&gt;
                &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep&lt;/span&gt;
                &lt;span class="na"&gt;regex&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default;kubernetes;https&lt;/span&gt;
            &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the example you can see that the first 2 jobs specify an static target (&lt;code&gt;static_configs.target&lt;/code&gt;) that the crawlers should look for metrics. The first one is to scrape the Prometheus application itself on &lt;code&gt;localhost&lt;/code&gt; and the second will look for metrics in the &lt;code&gt;prometheus-kube-state-metrics.monitoring.svc.cluster.local:8080&lt;/code&gt; address.&lt;/p&gt;

&lt;p&gt;However the &lt;code&gt;kubernetes-apiservers&lt;/code&gt; job acts differently. It uses a &lt;strong&gt;service-dicovery mechanism&lt;/strong&gt; that will automatically find the metrics endpoints that respect some rules defined by the mechanism. Here we are using the &lt;code&gt;kubernetes_sd_configs&lt;/code&gt; but others such as &lt;code&gt;azure_sd_configs&lt;/code&gt; or &lt;code&gt;http_sd_configs&lt;/code&gt; can be used. You can see more of this configuration on the &lt;a href="https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config" rel="noopener noreferrer"&gt;scrape_config documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you go to your option &lt;code&gt;status &amp;gt; targets&lt;/code&gt; inside you Prometheus instance you can see all the application (targets) that are being crawled and their status. It should look similar to this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fqd5e4nsbw2lugjh6hfwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fqd5e4nsbw2lugjh6hfwl.png" alt="Prometheus Targets"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Capturing your logs with Grafana Loki (and Promtail)
&lt;/h2&gt;

&lt;p&gt;But is not only about metrics that the SRE lives on. Another part of understanding how our applications are behaving is to read the logs that they generate, this is why you need Loki. &lt;/p&gt;

&lt;p&gt;Loki works similar to Prometheus by having &lt;strong&gt;agents&lt;/strong&gt; that scrape logs from the application (like Prometheus Crawlers), add metadata (labels) and streams them to a Loki instance. You can now read these logs using some tool, for example Grafana or the Loki Cli.&lt;/p&gt;

&lt;p&gt;But differently from Prometheus, Loki does not scrape information automatically, so you need a way of sending you need a way of sending these logs to it. One of the most famous agents is &lt;a href="https://grafana.com/docs/loki/latest/send-data/promtail/" rel="noopener noreferrer"&gt;Promtail&lt;/a&gt;. In a Kubernetes environment what it does is that it deploys a Daemon on every deployed node in the cluster. This Daemon reads the local logs generated by the pods and send them to a Loki instance (for those unfamiliar with Kubernetes, in a simplified way, this is similar to create a machine that will be deployed alongside your application machine and will be used to read all the logs).&lt;/p&gt;

&lt;p&gt;There are a lot of agents that you can use such as FluentBit, Logstash and many others.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Loki
&lt;/h3&gt;

&lt;p&gt;For Loki you can use the official &lt;a href="https://artifacthub.io/packages/helm/grafana/loki" rel="noopener noreferrer"&gt;Loki helm chart by Grafana&lt;/a&gt;. Again, you can see a real example by looking at this &lt;a href="https://github.com/limagbz/data-mesh-yelp/blob/main/infra/monitoring/loki/values.yaml" rel="noopener noreferrer"&gt;values.yaml&lt;/a&gt; file. Loki can be deployed in &lt;strong&gt;single binary&lt;/strong&gt; where all the write, read and backend will be done in the same machine (monolithic). You can specify the values on &lt;code&gt;write.replicas&lt;/code&gt;,  &lt;code&gt;read.replicas&lt;/code&gt; and &lt;code&gt;backend.replicas&lt;/code&gt; to deploy in &lt;strong&gt;distributed mode&lt;/strong&gt;. You can also store the logs in a file format or using a s3-like storage. In the example I use a MinIO deployed with other helm chart, but this helm chart provides a way to deploy a MinIO instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Promtail
&lt;/h3&gt;

&lt;p&gt;To deploy &lt;a href="https://grafana.com/docs/loki/latest/send-data/promtail/" rel="noopener noreferrer"&gt;Promtail&lt;/a&gt;, again you can use this &lt;a href="https://github.com/limagbz/data-mesh-yelp/blob/main/infra/monitoring/promtail/values.yaml" rel="noopener noreferrer"&gt;values.yaml&lt;/a&gt; file as a base.&lt;/p&gt;

&lt;p&gt;Promtail works with the concept of pipelines that will transform a log line, its labels and its timestamps in a way that is better for Loki. There are a lot of built-in stages such as&lt;br&gt;
regex, json and many others. You can see more about pipelines and stages &lt;a href="https://grafana.com/docs/loki/latest/send-data/promtail/pipelines/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://grafana.com/docs/loki/latest/send-data/promtail/stages/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Visualize all with Grafana
&lt;/h2&gt;

&lt;p&gt;Finally, to make these metrics and logs actionable we need to have a way to visualize, aggregate and correlate them in a way that is easy for humans to understand. We also need to be alerted in case things go wrong. Grafana is a visualization and analytics software that can help us create visualizations (and alerts) from time-series databases. On our example these databases will be Prometheus and Loki, but many others could be used.&lt;/p&gt;
&lt;h3&gt;
  
  
  Deploy Grafana
&lt;/h3&gt;

&lt;p&gt;Grafana is deployed by using the official Grafana Helm Chart. You can find more information about the chart on &lt;a href="https://grafana.com/docs/grafana/latest/setup-grafana/installation/kubernetes/" rel="noopener noreferrer"&gt;Deploy Grafana on Kubernetes&lt;/a&gt;. Again you can see a real example &lt;a href="https://github.com/limagbz/data-mesh-yelp/blob/main/infra/monitoring/grafana/values.yaml" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Data Sources
&lt;/h4&gt;

&lt;p&gt;What a Data Source do is specify where Grafana should get the data from, the example below shows that we are getting data from Prometheus and Loki. There are a lot of supported Data Sources that you can find &lt;a href="https://grafana.com/docs/grafana/latest/datasources/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Configuring a data source can be done directly via the UI or via the helm chart:&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;datasources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;datasources.yaml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;datasources&lt;/span&gt;&lt;span class="pi"&gt;:&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;Prometheus&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;prometheus&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://prometheus-server.monitoring.svc.cluster.local&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;Loki&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;loki&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://loki-gateway.monitoring.svc.cluster.local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Dashboards
&lt;/h4&gt;

&lt;p&gt;We also want to visualize the information in ways that make it actionable, this is done by creating dashboards to show graphs and metrics for your applications. You can create your own dashboards via the UI or json files, however there are also a lot of community dashboards that you can install directly into Grafana (more information &lt;a href="https://grafana.com/docs/grafana/latest/dashboards/" rel="noopener noreferrer"&gt;here&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dashboardProviders&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dashboardproviders.yaml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="pi"&gt;:&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;default&lt;/span&gt;
        &lt;span class="na"&gt;orgId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&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;file&lt;/span&gt;
        &lt;span class="na"&gt;disableDeletion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;editable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/grafana/dashboards/default&lt;/span&gt;
&lt;span class="na"&gt;dashboards&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Ref: https://grafana.com/grafana/dashboards/13332-kube-state-metrics-v2/&lt;/span&gt;
    &lt;span class="na"&gt;kube-state-metrics-v2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;gnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;13332&lt;/span&gt;
      &lt;span class="na"&gt;revision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt;
      &lt;span class="na"&gt;datasource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prometheus&lt;/span&gt;
    &lt;span class="c1"&gt;# Ref: https://grafana.com/grafana/dashboards/12660-kubernetes-persistent-volumes/&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes-persistent-volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;gnetId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12660&lt;/span&gt;
      &lt;span class="na"&gt;revision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;datasource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prometheus&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dashboards are configured in YAML by setting a dashboard provider (read more on &lt;a href="https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards" rel="noopener noreferrer"&gt;Dashboards&lt;/a&gt;) and the dashboards that you want to load, this can be a local dashboard configuration loaded as a json file or as the example shows above a dashboard found on the &lt;a href="https://grafana.com/grafana/dashboards/" rel="noopener noreferrer"&gt;dashboards repository&lt;/a&gt; by selecting the id and the revision. For example, the first dashboard on the example loads the 12 revision of &lt;code&gt;kube-state-metrics-v2&lt;/code&gt; (id 13332) using the&lt;br&gt;
Prometheus datasource. This should result in a dashboard that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F80m1xhl937ppxjmn8sw4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F80m1xhl937ppxjmn8sw4.png" alt="Grafana example dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://grafana.com/docs/grafana/latest/" rel="noopener noreferrer"&gt;Grafana Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grafana.com/docs/loki/latest/send-data/promtail/" rel="noopener noreferrer"&gt;Promtail Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://grafana.com/docs/loki/latest/" rel="noopener noreferrer"&gt;Grafana Loki Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://prometheus.io/docs/introduction/overview/" rel="noopener noreferrer"&gt;Prometheus Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>monitoring</category>
      <category>tutorial</category>
      <category>devops</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Setup your Python Environment with Pyenv and Poetry</title>
      <dc:creator>Gabriel Lima</dc:creator>
      <pubDate>Wed, 01 Nov 2023 14:30:00 +0000</pubDate>
      <link>https://dev.to/limagbz/setup-your-python-environment-with-pyenv-and-poetry-egc</link>
      <guid>https://dev.to/limagbz/setup-your-python-environment-with-pyenv-and-poetry-egc</guid>
      <description>&lt;p&gt;Dependency management in Python is very painful. Everyone that uses Python in multiple projects someday had a problem with package versions across the machine and even between team members. To solve these problems a lot of solutions appeared: venv, conda environments, Pipenv and many other tools that aim to create an isolated environment for you projects.&lt;/p&gt;

&lt;p&gt;The objective of this quick tutorial is to help you easily setup a reproducible and isolated virtual environment. using &lt;strong&gt;Pyenv&lt;/strong&gt; to manage your python versions and &lt;strong&gt;Poetry&lt;/strong&gt; to manage your packages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing your Python Versions: PyEnv
&lt;/h2&gt;

&lt;p&gt;The objective of &lt;a href="https://github.com/pyenv/pyenv"&gt;PyEnv&lt;/a&gt; is to help you install and switch between multiple Python versions in your system without worrying about breaking things.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing PyEnv
&lt;/h3&gt;

&lt;p&gt;To install PyEnv you can follow their &lt;a href="https://github.com/pyenv/pyenv#installation"&gt;Installation&lt;/a&gt; tutorial. Personally I like to use their official automatic installer found &lt;a href="https://github.com/pyenv/pyenv-installer"&gt;here&lt;/a&gt;. For any problem I also recommend reading the &lt;a href="https://github.com/pyenv/pyenv/wiki/Common-build-problems"&gt;Common build problems&lt;/a&gt; specially the prerequisites section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing the Python Version
&lt;/h3&gt;

&lt;p&gt;With PyEnv installed you can run the command below to obtain the list of possible Python versions that you can install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyenv &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command above shows a huge list of python versions, you can choose whichever you need. For example, you can install Python 3.11.3 by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyenv &lt;span class="nb"&gt;install &lt;/span&gt;3.11.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To know if everything went well you can check all the installed versions running:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You should see an output similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;system
&lt;span class="k"&gt;*&lt;/span&gt; 3.11.3 &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set &lt;/span&gt;by /usr/local/pyenv/version&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that are 2 python versions installed into your machine. The &lt;strong&gt;system&lt;/strong&gt; is the one that comes with your OS. The other is the versions that we installed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing the versions
&lt;/h3&gt;

&lt;p&gt;The * symbol means that this is the default version used whenever you call python (i.e. the global version). You can change the global version by running the command &lt;code&gt;pyenv global&lt;/code&gt;. For example, to set 3.11.3 as the global version you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyenv global 3.11.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also set the Python version in a folder, this is useful when you need a different versions of python for many projects. To do this use the &lt;code&gt;pyenv local&lt;/code&gt; command. Following our example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyenv &lt;span class="nb"&gt;local &lt;/span&gt;3.11.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Managing your packages: Poetry
&lt;/h2&gt;

&lt;p&gt;With Python installed in the correct version now you need a way to easily manage your packages in an isolated environment. &lt;a href="https://python-poetry.org/"&gt;Poetry&lt;/a&gt; is a tool to help manage your packages and its dependencies. It provides packaging and dependency resolution features (we are going to focus on this one) for you not to worry more about conflicts in your dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Poetry
&lt;/h3&gt;

&lt;p&gt;First you need to install the tool by following the &lt;a href="https://python-poetry.org/docs/#installation"&gt;installation&lt;/a&gt; guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup your project
&lt;/h3&gt;

&lt;p&gt;Now you need to &lt;strong&gt;go to your project folder&lt;/strong&gt; and run the &lt;code&gt;poetry init&lt;/code&gt; command. This command will prompt you for some information including name of the project, authors, packages that you want to install from the beginning and other information. Note that this command will use the Python version setup by PyEnv giving preference to the local version.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;You will notice that the command will create 2 new files. The &lt;code&gt;pyproject.toml&lt;/code&gt; is a human-friendly file introduced on &lt;a href="https://peps.python.org/pep-0518/"&gt;PEP518&lt;/a&gt; and expanded in some other PEPs (&lt;a href="https://peps.python.org/pep-0517/"&gt;&lt;strong&gt;PEP 517&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://peps.python.org/pep-0621/"&gt;&lt;strong&gt;PEP 621&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://peps.python.org/pep-0660/"&gt;&lt;strong&gt;PEP 660&lt;/strong&gt;&lt;/a&gt;). This file store many information about your project that can be used by many tools of the Python ecosystem.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;poetry.lock&lt;/code&gt; files contains the installed packages (including their dependencies) and whatever is necessary for them to work. You should not edit this file manually. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If your project already have a &lt;code&gt;pyproject.toml&lt;/code&gt; inside with &lt;code&gt;[tool.poetry]&lt;/code&gt; sections it means that someone already setup the tool. You can skip the &lt;code&gt;poetry init&lt;/code&gt; and go directly to run &lt;code&gt;poetry install&lt;/code&gt; to install all the required packages in your machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Adding Packages
&lt;/h3&gt;

&lt;p&gt;To add packages to the environment you just need to run the &lt;code&gt;poetry add&lt;/code&gt; command. The command accepts a flag &lt;code&gt;--group&lt;/code&gt; that is a way to organize the dependencies into groups. A common example is if you want to differ between production packages and development ones (e.g. test packages, code style and so on). In this case you can create a group called &lt;code&gt;dev&lt;/code&gt; for the non-production environment. See an example below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry add &lt;span class="nv"&gt;pandas&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;2.1.1             &lt;span class="c"&gt;# Add pandas 2.1.1 on prod environment&lt;/span&gt;
poetry add &lt;span class="nv"&gt;pandas&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;2.1.1 &lt;span class="nt"&gt;--group&lt;/span&gt; dev &lt;span class="c"&gt;# Add pandas 2.1.1 on dev environment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using your environment
&lt;/h3&gt;

&lt;p&gt;With your environment configured, you need to activate it to use every time you call python. This is as simple as running:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you don't want to activate the shell you can also run any python command using the environment with &lt;code&gt;poetry run&lt;/code&gt; inside the project folder. For example, to run an example python script using the environment you can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poetry run python some-script.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;For more Poetry commands and usage, see &lt;a href="https://python-poetry.org/docs/"&gt;Poetry Docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Since I started my python journey I had used many tools to manage packages: venv, Pipenv, requirements, conda environments. This setup was the one that I had less headaches with the many projects and versions that I worked on. This tutorial should help you achieve that. &lt;/p&gt;

&lt;p&gt;Comment below if you use any other setup, if this tutorial helped you or any other comment that you want. Thanks for reading!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pip.pypa.io/en/stable/reference/build-system/pyproject-toml/"&gt;pip: pyproject.toml&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://python-poetry.org/docs/"&gt;Poetry Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pyenv/pyenv"&gt;Pyenv&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Creating an isolated and reproducible development environment: A tutorial on DevContainers</title>
      <dc:creator>Gabriel Lima</dc:creator>
      <pubDate>Wed, 18 Oct 2023 13:35:07 +0000</pubDate>
      <link>https://dev.to/limagbz/creating-an-isolated-and-reproducible-development-environment-a-tutorial-on-devcontainers-3ajo</link>
      <guid>https://dev.to/limagbz/creating-an-isolated-and-reproducible-development-environment-a-tutorial-on-devcontainers-3ajo</guid>
      <description>&lt;p&gt;You started on a new project, went through all the onboarding process, met your team, discovered the details of your new challenge and is now able to start the coding. Sounds exciting right?&lt;/p&gt;

&lt;p&gt;But you know that before you start to define any variable in your IDE you need to setup all the tools, languages and environments for the project to work. And now the first challenge of you project is not to develop a new useful feature for the client, but to debug the tools that are not working as expected in your machine because of some computer magic.&lt;/p&gt;

&lt;p&gt;This all sounds familiar. Right?&lt;/p&gt;

&lt;h2&gt;
  
  
  What are a Development Containers (or DevContainers)?
&lt;/h2&gt;

&lt;p&gt;Development Containers are a specification that allows you to use containers as a full-featured development environment (read more on &lt;a href="https://containers.dev/"&gt;containers.dev&lt;/a&gt;). This enables users and teams to create a consistent, shareable, reproducible and isolated development environment.&lt;/p&gt;

&lt;p&gt;
    &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zX5Awfbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://code.visualstudio.com/assets/docs/devcontainers/containers/architecture-containers.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zX5Awfbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://code.visualstudio.com/assets/docs/devcontainers/containers/architecture-containers.png" alt="DevContainers Architecture" width="800" height="332"&gt;&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;Some benefits of using DevContainers are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A faster onboarding for anyone that wants to join your project. Now a single command is sufficient to get people ready to contribute;&lt;/li&gt;
&lt;li&gt;A shareable and reproducible environment that reduces problems related to different versions of packages and tools between team members;&lt;/li&gt;
&lt;li&gt;An isolated environment from your OS that prevents your local OS from interfering with your project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you are going to learn here?
&lt;/h2&gt;

&lt;p&gt;This tutorial will help you set a DevContainer for VSCode using the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers"&gt;official extension&lt;/a&gt; by Microsoft. At the end you will be able to create your own custom container, use it on VSCode and personalize it without interfering in your team's environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;DevContainers work on Windows and Linux (including WSL2). For this tutorial to work you will need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VSCode&lt;/strong&gt;: You can install following this &lt;a href="https://code.visualstudio.com/"&gt;link&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Engine&lt;/strong&gt;: You can install following the steps on &lt;a href="https://docs.docker.com/engine/install/"&gt;Docker Engine Install&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup your DevContainer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 01: Setup your .devcontainer folder
&lt;/h3&gt;

&lt;p&gt;For VSCode to autodetect the container you should have at least a &lt;code&gt;Dockerfile&lt;/code&gt; with your container's specification and a &lt;code&gt;devcontainer.json&lt;/code&gt; file containing metadata and settings for the environment. All these files should be in a &lt;code&gt;.devcontainer&lt;/code&gt; folder on the root of your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;├── .devcontainer
│   ├── ...                 &amp;lt;- Any other related file that you want
│   ├── Dockerfile          &amp;lt;- Container Dockerfile definition
│   └── devcontainer.json   &amp;lt;- DevContainer's metadata and settings
└── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 02: Creating the Dockerfile
&lt;/h3&gt;

&lt;p&gt;Here you can create a common Dockerfile with any base image, ARGs, ENVs and commands that you want your container to do. The example below will create a Ubuntu container with a python environment using Pyenv and Poetry. We also use the &lt;code&gt;root&lt;/code&gt; user but if you want to use a non-root user you can follow &lt;a href="https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user"&gt;this link&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:22.04&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; PYENV_VERSION=2.3.18&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; POETRY_VERSION=1.5.1&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; PYTHON_VERSION=3.11.3&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DEBIAN_FRONTEND=noninteractive&lt;/span&gt;

&lt;span class="c"&gt;# Copy custom scripts and set the required permissions.&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; custom-scripts/ /tmp/scripts/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /tmp/scripts/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Basic packages and requirements&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; git curl ca-certificates gnupg lsb-release locales locales-all nano jq wget

&lt;span class="c"&gt;# Setting Locales&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LC_ALL en_US.UTF-8&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LANG en_US.UTF-8&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; LANGUAGE en_US.UTF-8&lt;/span&gt;

&lt;span class="c"&gt;# Install Pyenv and Python&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYENV_ROOT="/usr/local/pyenv" PYENV_GIT_TAG=v${PYENV_VERSION}&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="${PATH}:${PYENV_ROOT}/bin"&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev curl libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl https://pyenv.run | bash &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pyenv &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pyenv global &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PYTHON_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Install Poetry&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;unset &lt;/span&gt;PYENV_VERSION &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pyenv &lt;span class="nb"&gt;exec &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;&lt;span class="nv"&gt;poetry&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;POETRY_VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Lifecycle Scripts
&lt;/h4&gt;

&lt;p&gt;You might have noticed the snippet in the Dockerfile above&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;# Copy custom scripts and set the required permissions&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; custom-scripts/ /tmp/scripts/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /tmp/scripts/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it does is to copy some files from the &lt;code&gt;custom-scripts&lt;/code&gt; folder located inside the &lt;code&gt;.devcontainer&lt;/code&gt; and give the necessary permissions for them to be executed. This will be useful later on  where we are going to run these scripts in some phases of the Docker lifecycle (e.g. when it creates, every time it starts and so on).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 03: The devcontainer.json file
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;devcontainer.json&lt;/code&gt; file is where you specify configuration for VSCode to run your containers. You can see the complete specification of settings and metadata on &lt;a href="https://containers.dev/implementors/json_reference/"&gt;Development Containers metadata reference&lt;/a&gt;.&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="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;"DevContainer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&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;"dockerfile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dockerfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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;"PYENV_VERSION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.3.18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"POETRY_VERSION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.5.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;"PYTHON_VERSION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.11.3"&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;"mounts"&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="s2"&gt;"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind,consistency=default"&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;"postStartCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/bin/bash /tmp/scripts/poststart.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"customizations"&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;"vscode"&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;"extensions"&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="s2"&gt;"ms-python.vscode-pylance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"njpwerner.autodocstring"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"bungcip.better-toml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"ms-python.black-formatter"&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;"settings"&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;"editor.rulers"&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="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;110&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="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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;build&lt;/code&gt; information is responsible to setup the information for VSCode to build your container. This includes args, target, caches, the path to the Dockerfile and so on. The snippet below defines the path of the Dockerfile and set the args. See more on &lt;a href="https://containers.dev/implementors/json_reference/#image-specific"&gt;Image or Dockerfile specific properties&lt;/a&gt;.&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&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;"dockerfile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dockerfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"args"&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;"PYENV_VERSION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.3.18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"POETRY_VERSION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.5.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;"PYTHON_VERSION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.11.3"&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="err"&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;h3&gt;
  
  
  postStartCommand and Lifecycle Scripts
&lt;/h3&gt;

&lt;p&gt;Lifecycle scripts is a feature by DevContainers where you can make some commands run in some phases of the Docker Lifecycle. For example, on our example we use the &lt;code&gt;postStartCommand&lt;/code&gt; that runs everytime we start the container. This is useful for example to always update your OS or to make all your python dependencies installed. But there are more, you can use for example &lt;code&gt;onCreateCommand&lt;/code&gt; that will execute only when the container is created. You can read more about this on &lt;a href="https://containers.dev/implementors/json_reference/#lifecycle-scripts"&gt;Lifecycle scripts&lt;/a&gt; and &lt;a href="https://code.visualstudio.com/remote/advancedcontainers/start-processes"&gt;Start a process when the container starts&lt;/a&gt;.&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"postStartCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/bin/bash /tmp/scripts/poststart.sh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;h3&gt;
  
  
  Customizations
&lt;/h3&gt;

&lt;p&gt;The customizations property is where DevContainers define information specific for your IDE. For VSCode we have two properties &lt;code&gt;vscode.settings&lt;/code&gt; and &lt;code&gt;vscode.extensions&lt;/code&gt; that defines the default settings and installed extensions for the environment. The example &lt;code&gt;devcontainer.json&lt;/code&gt; adds some extensions such as pylance, autodocstring, better-toml and black-formatter to the container and sets two editor rules for 79 (PEP8) and 110 characters.&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"customizations"&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;"vscode"&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;"extensions"&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="s2"&gt;"ms-python.vscode-pylance"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"njpwerner.autodocstring"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"bungcip.better-toml"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s2"&gt;"ms-python.black-formatter"&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;"settings"&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;"editor.rulers"&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="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;110&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="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="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;Some tips for making this configuration easier:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For &lt;strong&gt;extensions&lt;/strong&gt;, you can go to any extension in the tab, expand the options on the &lt;code&gt;...&lt;/code&gt; (three dots) and click on the option &lt;code&gt;add to devcontainer.json&lt;/code&gt; that the file will be automatically managed.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;settings&lt;/strong&gt; you can open the command pallet on &lt;code&gt;Ctrl + Shift + P&lt;/code&gt; and go to any configuration that you wish to alter, click on the &lt;code&gt;More actions&lt;/code&gt; represented by a gear icon, &lt;code&gt;copy setting as json&lt;/code&gt; and add this to the json file.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Mounts (and Docker-From-Docker)
&lt;/h3&gt;

&lt;p&gt;Mounts is the metadata used to specify your container's volumes as in any docker container. A common example of mount that is used is when you want to run docker within your container. Here we are setting up the Docker-from-Docker (or Docker-outside-of-Docker) approach where we mount the host's docker socket into the container so any build or docker command will actually execute on the host. See more on &lt;a href="https://containers.dev/implementors/json_reference/#general-properties"&gt;General devcontainer.json properties&lt;/a&gt;&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="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mounts"&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="s2"&gt;"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind,consistency=default"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;h2&gt;
  
  
  Personalizing your environment with dotfiles
&lt;/h2&gt;

&lt;p&gt;Each Linux user has their own set of settings for their software, some use a personalized shell like zshell, other use Vim with some tweaks as their main editor. To maintain the productivity of everyone it is important to maintain these settings across environments. Many Linux software uses dotfiles as their main way to store their settings and this is what we are going to explore.&lt;/p&gt;

&lt;p&gt;One of the features of the DevContainer extension on VSCode is that we can pull dotfiles directly from a Github repository (public or private). This can be done by adding the following settings on your VSCode Settings (JSON) (or looking for similar settings in the UI).&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="nl"&gt;"dotfiles.repository"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-github-id/your-dotfiles-repo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
  &lt;/span&gt;&lt;span class="nl"&gt;"dotfiles.targetPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/dotfiles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dotfiles.installCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/dotfiles/install.sh"&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;What this means is that VSCode will copy all the content of the &lt;code&gt;dotfiles.repository&lt;/code&gt; into the folder &lt;code&gt;dotfiles.targetPath&lt;/code&gt; inside the container and will execute the &lt;code&gt;dotfiles.installCommand&lt;/code&gt;. You can omit the &lt;code&gt;installCommand&lt;/code&gt; and it will default to a &lt;code&gt;install.sh&lt;/code&gt; script inside the &lt;code&gt;targetpath&lt;/code&gt;. You can read more on&lt;br&gt;
&lt;a href="https://code.visualstudio.com/docs/devcontainers/containers#_personalizing-with-dotfile-repositories"&gt;Personalizing with dotfile repositories&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A real example of a &lt;code&gt;install.sh&lt;/code&gt; (used by me) is presented below. This script will install zshell, &lt;a href="https://github.com/romkatv/powerlevel10k"&gt;Powerlevel10K&lt;/a&gt; theme and create symbolic links of my personalized settings into the required ones in the system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;DOTFILES_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/dotfiles"&lt;/span&gt;    
&lt;span class="nb"&gt;export &lt;/span&gt;DOTFILES_LOCATION&lt;span class="p"&gt;;&lt;/span&gt;

apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; zsh fonts-powerline fzf

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.oh-my-zsh"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"oh-my-zsh is already installed&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;--unattended&lt;/span&gt; &lt;span class="c"&gt;# Zshell&lt;/span&gt;
  git clone &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 https://github.com/romkatv/powerlevel10k.git &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ZSH_CUSTOM&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="p"&gt;/.oh-my-zsh/custom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/themes/powerlevel10k
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"creating symlinks for zsh"&lt;/span&gt;
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOTFILES_LOCATION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/zsh/.zshrc"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.zshrc"&lt;/span&gt;
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOTFILES_LOCATION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/zsh/.p10k.zsh"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.p10k.zsh"&lt;/span&gt;
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOTFILES_LOCATION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/zsh/.zprofile.zsh"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.zprofile.zsh"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that everything in this section is completely optional, and you can use the default configuration for each software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;Learn about DevContainers were a turning point in the projects that I worked with reducing problems related to manual configuration of a series of tools on different environments. This tutorial should help you achieve the same benefits as I had by setting your custom development environment.&lt;/p&gt;

&lt;p&gt;Thanks for Reading! Please, feel free to drop any suggestions, discussions, tips on the section below!&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/devcontainers/containers"&gt;Developing inside a Container&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/remote/advancedcontainers/overview"&gt;Advanced container configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://containers.dev/implementors/json_reference/"&gt;Dev Container metadata reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>vscode</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
