<?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: Pablo Romeo</title>
    <description>The latest articles on DEV Community by Pablo Romeo (@pabloromeo).</description>
    <link>https://dev.to/pabloromeo</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%2F368559%2F8d858a20-b764-4ec3-ba87-645ae93b3add.jpg</url>
      <title>DEV Community: Pablo Romeo</title>
      <link>https://dev.to/pabloromeo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pabloromeo"/>
    <language>en</language>
    <item>
      <title>Implementing a DAO in a growing software development company</title>
      <dc:creator>Pablo Romeo</dc:creator>
      <pubDate>Tue, 22 Nov 2022 13:26:48 +0000</pubDate>
      <link>https://dev.to/cloudx/implementing-a-dao-in-a-growing-software-development-company-1mo1</link>
      <guid>https://dev.to/cloudx/implementing-a-dao-in-a-growing-software-development-company-1mo1</guid>
      <description>&lt;p&gt;Co-written with &lt;a href="https://github.com/pablitxn" rel="noopener noreferrer"&gt;Pablo Coronel&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if for the activities you do on your day-to-day you were &lt;strong&gt;rewarded with tokens&lt;/strong&gt; that would allow you to participate in company decisions? Sounds great, right? It is possible. We might not all be part of the Roy family from Succession, but we can have access to certain decisions of our company depending on our participation in it.&lt;/p&gt;

&lt;p&gt;In this article we will tell you about the idea that led us to build our own &lt;strong&gt;Decentralized Autonomous Organization (DAO)&lt;/strong&gt;, and how we are implementing it within CloudX.&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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdao-succession.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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdao-succession.png" alt="The Roy family from Succession series"&gt;&lt;/a&gt;&lt;/p&gt;
The Roy family from Succession series



&lt;h2&gt;
  
  
  First things first… What is a DAO?
&lt;/h2&gt;

&lt;p&gt;A DAO is an organization that runs on blockchain and is controlled in its entirety by computational algorithms—aka &lt;strong&gt;smart contracts&lt;/strong&gt;. Once published to the blockchain, smart contracts execute autonomously and determine how the DAO's participants should behave. A DAO is coordinated in a &lt;strong&gt;decentralized, horizontal&lt;/strong&gt; fashion, meaning that each one of its participants has an equal right to make decisions. Additionally, if any modification needs to be made to the DAO, all of its members must reach &lt;strong&gt;consensus&lt;/strong&gt; in order to apply the change.&lt;/p&gt;

&lt;p&gt;There are infinite use cases for DAOs, but they are specially being implemented &lt;strong&gt;in the financial world&lt;/strong&gt;. In LatAm there are many projects exploring DAOs application in other fields, for example to provide transparency in nonprofit organizations, or in social verification methods such as &lt;a href="https://github.com/Proof-Of-Humanity" rel="noopener noreferrer"&gt;Proof of Humanity&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now, what is our motivation?
&lt;/h2&gt;

&lt;p&gt;Usually, we start most of the initiatives of the Research &amp;amp; Development team in CloudX with a simple question: &lt;em&gt;“I wonder if «some crazy idea» is possible to do?”&lt;/em&gt;. This case was not different.&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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdao-dr-malcolm.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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdao-dr-malcolm.png" alt="Dr Malcolm from Jurassic Park saying "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In CloudX, we frequently open the decision making process to the whole team. How? Through &lt;strong&gt;polls in Slack&lt;/strong&gt;. That made us think: would it be possible to implement &lt;strong&gt;a similar participation system&lt;/strong&gt; but based on crypto tokens? This simple question took us down a rabbit-hole to a final outcome: let's create a &lt;strong&gt;Decentralized Autonomous Organization (DAO)&lt;/strong&gt; for internal use within CloudX.&lt;/p&gt;

&lt;p&gt;Our main goals are to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have &lt;strong&gt;active participation&lt;/strong&gt; of the team in the decision making process.&lt;/li&gt;
&lt;li&gt;Have &lt;strong&gt;weighted governance&lt;/strong&gt; while voting for company initiatives.&lt;/li&gt;
&lt;li&gt;Add &lt;strong&gt;gamification&lt;/strong&gt; and &lt;strong&gt;fun&lt;/strong&gt; to it, with rewards for employees.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By implementing a DAO in our company, not only we are expecting to meet these goals, but also we get to familiarize our team with the crypto ecosystem, while learning about DAOs, meta-transactions and dApps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing a DAO in a software company
&lt;/h2&gt;

&lt;p&gt;In our DAO, users are rewarded with &lt;strong&gt;governance tokens&lt;/strong&gt; for different activities they can engage in within CloudX. There is a vast spectrum of activities across the different areas of the company, for example, publishing a tech article in Dev.to, interviewing candidates, giving a tech talk, preparing a course or workshop, assisting other coworkers, reaching milestones, and so on.&lt;/p&gt;

&lt;p&gt;This brings &lt;strong&gt;Gamification&lt;/strong&gt; to the table, because when it's time to vote for new proposals, votes are weighted proportionally to the amount of tokens the voter holds.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;proposals&lt;/strong&gt; we are thinking about voting via our DAO are those that involve different team members of the company in daily activities. They can be very simple things, such as deciding on which date and time we should hold an all-hands meeting, proposing topics for internal workshops, or deciding what our company merch should be.&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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdao-journey.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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdao-journey.png" alt="A DAO user collecting tokens and then using them to vote for a proposal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nevertheless, we naturally ran into an interesting problem. In the blockchain ecosystem—and DAOs are no different—every transaction has a &lt;strong&gt;fee&lt;/strong&gt;. We absolutely don't want CloudX members having to spend money from their wallets in order to vote for initiatives. This problem led us to think of a solution that allowed CloudX to cover transaction fees in a transparent way.&lt;/p&gt;

&lt;p&gt;To solve this problem we came up with the use of &lt;strong&gt;meta-transactions&lt;/strong&gt;, which are transactions that are signed off-chain—outside the blockchain itself—by our DAO's users. These meta-transactions are then embedded into another transaction which, in turn, is submitted and &lt;strong&gt;paid by a different account&lt;/strong&gt; that will ultimately take care of the costs. In consequence, this enables the participants of our DAO to interact with it but not having to worry about the network's fees.&lt;/p&gt;

&lt;h2&gt;
  
  
  How are we doing it?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We are creating a dApp&lt;/strong&gt;—decentralized application—, which is a type of application where operations do not depend on centralized control points or servers, but works based on a &lt;strong&gt;decentralized network&lt;/strong&gt;. Our DAO will run on &lt;strong&gt;Ethereum&lt;/strong&gt; blockchain.&lt;/p&gt;

&lt;p&gt;For the development we chose &lt;strong&gt;Solidity&lt;/strong&gt;, which is a language with a large community and an extensive tool set. It also gives us the possibility to make our smart contracts compatible with many of the most popular and widely used networks.&lt;/p&gt;

&lt;p&gt;For the operation of our DAO we have two smart contracts that articulate it as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GovernorContract&lt;/strong&gt;: Manages the governance of our DAO, the creation and execution of proposals, the voting and quorum required for approval, and the duration of each proposal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GovernanceToken&lt;/strong&gt;: Manages votes and participation. Each participant of the DAO holds governance tokens in their wallets.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Some of &lt;strong&gt;the tools&lt;/strong&gt; we are using for this development are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenZeppelin&lt;/strong&gt; provides us with a set of smart contracts that are audited and approved by its community, which makes it completely secure for implementation. In our case, the governor contracts and the governance tokens were created with this library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Foundry&lt;/strong&gt; is a suite of tools for the development of smart contracts that allows us to manage dependencies, compile our contracts, run tests, deploy them and interact with them through scripts. All of this uses Solidity in its entirety.&lt;/li&gt;
&lt;li&gt;We are considering using &lt;strong&gt;OpenGSN&lt;/strong&gt; as the protocol to provide us with the infrastructure to handle our users' meta-transactions. It provides us with different relayers that will receive the meta-transactions from our users, execute the final transaction and pay the network cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;As our DAO starts to gain adoption within CloudX, we will continue to improve it, adding new features in order to make it more robust and empower people to participate in it.&lt;/p&gt;

&lt;p&gt;Some of the ideas we have in mind for future iterations are to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable the DAO to be &lt;strong&gt;self-managed&lt;/strong&gt; and able to define the weight (amount of tokens) per activity.&lt;/li&gt;
&lt;li&gt;Depending on the level of adoption, analyze the need to apply some adjustment mechanism to &lt;strong&gt;balance&lt;/strong&gt; longstanding holders versus new members of the organization.&lt;/li&gt;
&lt;li&gt;Evaluate the possibility to offer &lt;strong&gt;buy-back&lt;/strong&gt; to holders, meaning that somebody who owns tokens can sell them back to CloudX.&lt;/li&gt;
&lt;li&gt;Brainstorm additional &lt;strong&gt;integrations&lt;/strong&gt; that enable new activities that can mint tokens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Host&lt;/strong&gt; our DAO in a decentralized way via a protocol such as IPFS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we do in many projects, this internal initiative from the Research &amp;amp; Development team of CloudX is a sort of &lt;strong&gt;“proof of concept”&lt;/strong&gt;. All the time we are coming up with ideas to use technology to improve our daily tasks.&lt;/p&gt;

&lt;p&gt;In CloudX we are passionate about what we do and we think of &lt;strong&gt;technology as an end in itself&lt;/strong&gt;. And, why not, we also like to have fun with it and transform our organization with a gamification process—that has also a lot to do with learning—and that has, as a result, the benefit of making more people participate in our decision making process.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>dao</category>
      <category>solidity</category>
      <category>metatx</category>
    </item>
    <item>
      <title>Taming cAdvisor's high CPU usage</title>
      <dc:creator>Pablo Romeo</dc:creator>
      <pubDate>Wed, 02 Feb 2022 18:50:58 +0000</pubDate>
      <link>https://dev.to/cloudx/taming-cadvisors-high-cpu-usage-1nm5</link>
      <guid>https://dev.to/cloudx/taming-cadvisors-high-cpu-usage-1nm5</guid>
      <description>&lt;p&gt;When running multiple docker containers, be it on a single server, a Kubernetes cluster or in Docker Swarm, it is very important to be able to monitor their resource usage.&lt;/p&gt;

&lt;p&gt;That's where &lt;a href="https://github.com/google/cadvisor" rel="noopener noreferrer"&gt;cAdvisor&lt;/a&gt; comes in. It provides useful metrics to build Monitoring Dashboards using Grafana and Prometheus:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fcadvisor-high-cpu-grafana-dashboard.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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fcadvisor-high-cpu-grafana-dashboard.png" alt="Grafana Dashboard showing CPU and Memory metrics"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But there is one big problem: &lt;strong&gt;cAdvisor's default settings&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The consequence of running the default configuration is very high CPU usage. Especially noticeable in low-powered devices such as the Raspberry Pi's from the cluster in the graph above.&lt;/p&gt;

&lt;p&gt;cAdvisor uses more CPU than the containers it is monitoring! 😭&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Thankfully, it's quite simple. The parameters that make the biggest impact are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;housekeeping_interval&lt;/li&gt;
&lt;li&gt;docker_only&lt;/li&gt;
&lt;li&gt;disable_metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A detailed explanation of each can be found &lt;a href="https://github.com/google/cadvisor/blob/master/docs/runtime_options.md" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker-compose Swarm example
&lt;/h2&gt;

&lt;p&gt;Below I'm defining a &lt;code&gt;housekeeping_interval&lt;/code&gt; of 30 seconds, setting &lt;code&gt;docker_only&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;, and disabling metrics I'm not interested in, using &lt;code&gt;disable_metrics&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;NOTE&lt;/em&gt;: Beware that I'm using an ARM version of cAdvisor, change the image/tag accordingly.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.4'&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
  &lt;span class="na"&gt;cadvisor&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;zcube/cadvisor:latest&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;published&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9102&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9102&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--port=9102"&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--housekeeping_interval=30s"&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--docker_only=true"&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--disable_metrics=percpu,sched,tcp,udp,disk,diskIO,accelerator,hugetlb,referenced_memory,cpu_topology,resctrl"&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/lib/docker/:/var/lib/docker:ro&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/dev/disk/:/dev/disk:ro&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/sys:/sys:ro&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run:/var/run:ro&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/:/rootfs:ro&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/sys/fs/cgroup:/cgroup:ro&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/machine-id:/etc/machine-id:ro&lt;/span&gt;&lt;br&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/etc/localtime:/etc/localtime:ro&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;global&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;update_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stop-first&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
        &lt;span class="na"&gt;reservations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
          &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;80M&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wget --quiet --tries=1 --spider &lt;a href="http://localhost:9102/healthz" rel="noopener noreferrer"&gt;http://localhost:9102/healthz&lt;/a&gt; || exit &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Did it help?&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Check this out:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fcadvisor-high-cpu-usage-improvement.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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fcadvisor-high-cpu-usage-improvement.png" alt="Lower CPU usage after changing settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm sure you can guess at what time the new settings kicked in 😏. From &lt;strong&gt;0.68&lt;/strong&gt; vCPU to &lt;strong&gt;0.08&lt;/strong&gt; vCPU!&lt;br&gt;
Not bad at all.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>containers</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Multi-arch docker images the easy way, with Github Actions</title>
      <dc:creator>Pablo Romeo</dc:creator>
      <pubDate>Wed, 12 Jan 2022 13:56:35 +0000</pubDate>
      <link>https://dev.to/cloudx/multi-arch-docker-images-the-easy-way-with-github-actions-4k54</link>
      <guid>https://dev.to/cloudx/multi-arch-docker-images-the-easy-way-with-github-actions-4k54</guid>
      <description>&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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdocker-multi-arch-images-small.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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdocker-multi-arch-images-small.png" alt="multi-arch docker image example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may have come across Docker images like the one above ☝️. In this example, you can see that the &lt;code&gt;latest&lt;/code&gt; tag supports two different architectures. Meaning there are two different images for the same tag, and docker will pull the appropriate one depending on the target architecture. But...how are they built?&lt;/p&gt;

&lt;h2&gt;
  
  
  The old way
&lt;/h2&gt;

&lt;p&gt;Back in the day, the only option was to manually built each separate image using the &lt;code&gt;ARCH&lt;/code&gt; build-arg, push it, and then use the &lt;code&gt;docker manifest&lt;/code&gt; command to group the images together under the same manifest.&lt;br&gt;
This manual process is described in depth in &lt;a href="https://www.docker.com/blog/multi-arch-build-and-images-the-simple-way/" rel="noopener noreferrer"&gt;this post by the Docker team&lt;/a&gt;. But now there's even an easier way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Buildx to the rescue
&lt;/h2&gt;

&lt;p&gt;Thankfully &lt;a href="https://docs.docker.com/buildx/working-with-buildx/" rel="noopener noreferrer"&gt;Buildx&lt;/a&gt; automates this process and simplifies it quite a bit. A single command can do all that for you.&lt;br&gt;
But who wants to be building images locally anyway? 😁&lt;br&gt;
Plus, automating it with Github Actions is a great excuse to also implement a proper tagging and versioning strategy as well as exploring multiple Container Registries.&lt;br&gt;
Let's get to it! 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Github Actions build
&lt;/h2&gt;

&lt;p&gt;The overall process is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Checkout the code&lt;/li&gt;
&lt;li&gt;Setup &lt;a href="https://www.qemu.org/" rel="noopener noreferrer"&gt;QEMU&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Setup Buildx&lt;/li&gt;
&lt;li&gt;Log into the target container registry&lt;/li&gt;
&lt;li&gt;Docker Metadata tag voodoo magic &lt;em&gt;(optional)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Build and push&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's assume we have a Github repo with a &lt;code&gt;main&lt;/code&gt; and a &lt;code&gt;dev&lt;/code&gt; branch, PRs, as well as &lt;a href="https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository" rel="noopener noreferrer"&gt;versioned releases&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll have a github actions YAML similar to the following:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;

&lt;span class="c1"&gt;# Controls when the workflow will run&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;workflow_dispatch&lt;/span&gt;
  &lt;span class="s"&gt;push&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;main'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dev'&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*.*.*'&lt;/span&gt;
&lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;main'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dev'&lt;/span&gt;

&lt;span class="c1"&gt;# permissions are needed if pushing to ghcr.io&lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
  &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&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="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Steps 1,2 and 3
&lt;/h3&gt;

&lt;p&gt;These are quite straightforward:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

      &lt;span class="c1"&gt;# Get the repository's code&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;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="c1"&gt;# https://github.com/docker/setup-qemu-action&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;Set up QEMU&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-qemu-action@v1&lt;/span&gt;
      &lt;span class="c1"&gt;# https://github.com/docker/setup-buildx-action&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;Set up Docker Buildx&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v1&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Step 4
&lt;/h3&gt;

&lt;p&gt;For Github's Container Registry you can use the following:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;Login to GHCR&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event_name != 'pull_request'&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.repository_owner }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And for Dockerhub:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;Login to Docker Hub&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event_name != 'pull_request'&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_USERNAME }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DOCKERHUB_TOKEN }}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Create a &lt;a href="https://docs.docker.com/docker-hub/access-tokens/" rel="noopener noreferrer"&gt;dockerhub access token&lt;/a&gt; and set the corresponding &lt;a href="https://docs.github.com/en/actions/security-guides/encrypted-secrets" rel="noopener noreferrer"&gt;github secrets&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5
&lt;/h3&gt;

&lt;p&gt;We could skip this step and just use the &lt;code&gt;latest&lt;/code&gt; docker tag, but in real world scenarios you'll likely want a more elaborate image tagging strategy. Such as sha, branch, &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;Semantic Versioning&lt;/a&gt;, etc.&lt;/p&gt;

&lt;p&gt;So we'll use the extremely useful &lt;code&gt;docker/metadata-action@v3&lt;/code&gt; for preparing those tags for us.&lt;br&gt;
Check out &lt;a href="https://github.com/docker/metadata-action" rel="noopener noreferrer"&gt;https://github.com/docker/metadata-action&lt;/a&gt; for more example tagging strategies. The documentation is great!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;Docker meta&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;meta_id&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# you'll use this in the next step&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/metadata-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# list of Docker images to use as base name for tags&lt;/span&gt;
          &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;&amp;lt;specify the image name&amp;gt;&lt;/span&gt;
          &lt;span class="c1"&gt;# Docker tags based on the following events/attributes&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;type=schedule&lt;/span&gt;
            &lt;span class="s"&gt;type=ref,event=branch&lt;/span&gt;
            &lt;span class="s"&gt;type=ref,event=pr&lt;/span&gt;
            &lt;span class="s"&gt;type=semver,pattern={{version}}&lt;/span&gt;
            &lt;span class="s"&gt;type=semver,pattern={{major}}.{{minor}}&lt;/span&gt;
            &lt;span class="s"&gt;type=semver,pattern={{major}}&lt;/span&gt;
            &lt;span class="s"&gt;type=sha&lt;/span&gt;


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

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;This prepares image tags for branch, PR, major, major.minor, version, and sha, which you should adapt according to your own release process.&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;meta_id&lt;/code&gt; with something more appropriate. It will be used in the next step.&lt;/li&gt;
&lt;li&gt;Specify the image name under the &lt;code&gt;images:&lt;/code&gt; section. For ex: &lt;code&gt;ghcr.io/pabloromeo/foo&lt;/code&gt; for GHCR or &lt;code&gt;pabloromeo/foo&lt;/code&gt; for dockerhub.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 6
&lt;/h3&gt;

&lt;p&gt;And the final step is to actually build for each target platform and push to the registry.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;Build and push&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;platforms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux/amd64,linux/arm/v7&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event_name != 'pull_request' }}&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.&amp;lt;meta_id&amp;gt;.outputs.tags }}&lt;/span&gt;
          &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.&amp;lt;meta_id&amp;gt;.outputs.labels }}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Change the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The target architecture platforms to build, using &lt;code&gt;platforms:&lt;/code&gt; (in this example AMD64 and ARMv7)&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;&amp;lt;meta_id&amp;gt;&lt;/code&gt; with the proper id used previously.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final comments
&lt;/h2&gt;

&lt;p&gt;This turned out to be a lengthy post, but the actual build definition is quite simple when put together, and you'll not only  get multi-arch docker images, but nice semver versioning and tagging as well. 😄&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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdocker-multi-arch-semver-tags.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%2Fraw.githubusercontent.com%2Fcloudx-labs%2Fposts%2Fmain%2Fposts%2Fpabloromeo%2Fassets%2Fdocker-multi-arch-semver-tags.png" alt="Multi-arch images with SemVer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To see full working examples of this we can take a look at the builds of two pet projects of mine: &lt;a href="https://github.com/pabloromeo/docker-dogecoin" rel="noopener noreferrer"&gt;Docker-Dogecoin&lt;/a&gt; and &lt;a href="https://github.com/pabloromeo/clusterplex" rel="noopener noreferrer"&gt;ClusterPlex&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/pabloromeo/docker-dogecoin/blob/master/Dockerfile" rel="noopener noreferrer"&gt;Docker-Dogecoin's Dockerfile&lt;/a&gt; there's an example of how to write conditional logic as part of the docker build process depending on the target architecture to install different binaries for each.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://github.com/pabloromeo/clusterplex/blob/master/.github/workflows/main.yml" rel="noopener noreferrer"&gt;Clusterplex's build&lt;/a&gt; it actually pushes to BOTH ghcr.io and Dockerhub simultaneously, as well as updating Dockerhub's Description page to the content of the README.md in the repo.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>github</category>
      <category>githubactions</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
